Encoder number handling cleanup

This commit is contained in:
Kyle Banker 2010-10-01 16:29:31 -04:00
parent 694f011087
commit 3b5aac6de6
4 changed files with 162 additions and 180 deletions

Binary file not shown.

View File

@ -207,7 +207,7 @@ public class RubyBSONEncoder extends BSONEncoder {
return _buf.getPosition() - start;
}
protected void _putObjectField( String name , Object val ){
protected void _putObjectField( String name , Object val ) {
if ( name.equals( "_transientFields" ) )
return;
@ -217,7 +217,7 @@ public class RubyBSONEncoder extends BSONEncoder {
System.out.println( "\t class : " + val.getClass().getName() );
}
if ( name.equals( "$where") && val instanceof String ){
if ( name.equals( "$where") && val instanceof String ) {
_put( CODE , name );
_putValueString( val.toString() );
return;
@ -227,10 +227,21 @@ public class RubyBSONEncoder extends BSONEncoder {
putString(name, val.toString() );
else if ( val instanceof Number ) {
if (((Number)val).longValue() >= Integer.MIN_VALUE && ((Number)val).longValue() <= Integer.MAX_VALUE)
putNumber(name, ((Number)val).intValue() );
else
putNumber(name, (Number)val );
if ( ( val instanceof Float ) || ( val instanceof Double ) ) {
_put( NUMBER , name );
_buf.writeDouble( ((Number)val).doubleValue() );
}
else {
long longVal = ((Number)val).longValue();
if (longVal >= Integer.MIN_VALUE && longVal <= Integer.MAX_VALUE) {
_put( NUMBER_INT , name );
_buf.writeInt( (int)longVal );
}
else {
_put( NUMBER_LONG , name );
_buf.writeLong( longVal );
}
}
}
else if ( val instanceof Boolean )
@ -260,16 +271,22 @@ public class RubyBSONEncoder extends BSONEncoder {
else if ( val instanceof RubyFixnum ) {
long jval = ((RubyFixnum)val).getLongValue();
if (jval >= Integer.MIN_VALUE && jval <= Integer.MAX_VALUE) {
putNumber(name, (int)jval );
_put( NUMBER_INT , name );
_buf.writeInt( (int)jval );
}
else {
_put( NUMBER_LONG , name );
_buf.writeLong( jval );
}
else
putNumber(name, (Number)jval );
}
else if ( val instanceof RubyFloat ) {
double jval = ((RubyFloat)val).getValue();
putNumber(name, (Number)jval );
double doubleValue = ((RubyFloat)val).getValue();
_put( NUMBER , name );
_buf.writeDouble( doubleValue );
}
else if ( val instanceof JavaProxy ) {
@ -286,9 +303,8 @@ public class RubyBSONEncoder extends BSONEncoder {
else if ( val instanceof RubyNil )
putNull(name);
else if ( val instanceof RubyTime ) {
else if ( val instanceof RubyTime )
putDate( name , ((RubyTime)val).getDateTime().getMillis() );
}
else if ( val instanceof RubyBoolean )
putBoolean(name, (Boolean)((RubyBoolean)val).toJava(Boolean.class));
@ -303,7 +319,7 @@ public class RubyBSONEncoder extends BSONEncoder {
}
else {
long jval = big.longValue();
putNumber(name, (Number)jval );
putLong(name, (Number)jval );
}
}
@ -446,25 +462,6 @@ public class RubyBSONEncoder extends BSONEncoder {
_buf.writeLong( millis );
}
protected void putNumber( String name , Number n ){
if ( n instanceof Integer ||
n instanceof Short ||
n instanceof Byte ||
n instanceof AtomicInteger ){
_put( NUMBER_INT , name );
_buf.writeInt( n.intValue() );
}
else if ( n instanceof Long ||
n instanceof AtomicLong ) {
_put( NUMBER_LONG , name );
_buf.writeLong( n.longValue() );
}
else {
_put( NUMBER , name );
_buf.writeDouble( n.doubleValue() );
}
}
private void putRubyBinary( String name , RubyObject binary ) {
RubyArray rarray = (RubyArray)JavaEmbedUtils.invokeMethod(_runtime,
binary, "to_a", new Object[] {}, Object.class);

View File

@ -21,6 +21,25 @@ class BSONTest < Test::Unit::TestCase
include BSON
# This setup allows us to change the decoders for
# cross-coder compatibility testing
def setup
@encoder = BSON::BSON_CODER
@decoder = @encoder
end
def assert_doc_pass(doc, options={})
bson = @encoder.serialize(doc)
if options[:debug]
puts "DEBUGGIN DOC:"
p bson.to_a
puts "DESERIALIZES TO:"
p @decoder.deserialize(bson)
end
assert_equal @decoder.serialize(doc).to_a, bson.to_a
assert_equal doc, @decoder.deserialize(bson)
end
def test_require_hash
assert_raise_error InvalidDocument, "takes a Hash" do
BSON.serialize('foo')
@ -35,56 +54,25 @@ class BSONTest < Test::Unit::TestCase
end
end
def test_read_bson_io_document
doc = {'doc' => 'hello, world'}
bson = BSON.serialize(doc)
io = StringIO.new
io.write(bson.to_s)
io.rewind
assert_equal BSON.deserialize(bson), BSON.read_bson_document(io)
end
def test_serialize_returns_byte_buffer
doc = {'doc' => 'hello, world'}
bson = BSON.serialize(doc)
assert bson.is_a?(ByteBuffer)
end
def test_deserialize_from_string
doc = {'doc' => 'hello, world'}
bson = BSON.serialize(doc)
string = bson.to_s
assert_equal doc, BSON.deserialize(string)
end
def test_deprecated_bson_module
doc = {'doc' => 'hello, world'}
bson = BSON.serialize(doc)
assert_equal doc, BSON.deserialize(bson)
end
def test_string
doc = {'doc' => 'hello, world'}
bson = bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
assert_doc_pass(doc)
end
def test_valid_utf8_string
doc = {'doc' => 'aé'}
bson = bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
assert_doc_pass(doc)
end
def test_valid_utf8_key
doc = {'aé' => 'hello'}
bson = bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
assert_doc_pass(doc)
end
def test_document_length
doc = {'name' => 'a' * 5 * 1024 * 1024}
assert_raise InvalidDocument do
assert BSON::BSON_CODER.serialize(doc)
assert @encoder.serialize(doc)
end
end
@ -96,7 +84,7 @@ class BSONTest < Test::Unit::TestCase
string = Iconv.conv('iso-8859-1', 'utf-8', 'aé')
doc = {'doc' => string}
assert_raise InvalidStringEncoding do
BSON::BSON_CODER.serialize(doc)
@encoder.serialize(doc)
end
end
@ -104,7 +92,7 @@ class BSONTest < Test::Unit::TestCase
key = Iconv.conv('iso-8859-1', 'utf-8', 'aé')
doc = {key => 'hello'}
assert_raise InvalidStringEncoding do
BSON::BSON_CODER.serialize(doc)
@encoder.serialize(doc)
end
end
else
@ -140,38 +128,31 @@ class BSONTest < Test::Unit::TestCase
def test_code
doc = {'$where' => Code.new('this.a.b < this.b')}
bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
assert_doc_pass(doc)
end
def test_code_with_scope
doc = {'$where' => Code.new('this.a.b < this.b', {'foo' => 1})}
bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
assert_doc_pass(doc)
end
def test_number
doc = {'doc' => 41.99}
bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
def test_double
doc = {'doc' => 41.25}
assert_doc_pass(doc)
end
def test_int
doc = {'doc' => 42}
bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
assert_doc_pass(doc)
doc = {"doc" => -5600}
bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
assert_doc_pass(doc)
doc = {"doc" => 2147483647}
bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
assert_doc_pass(doc)
doc = {"doc" => -2147483648}
bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
assert_doc_pass(doc)
end
def test_ordered_hash
@ -180,72 +161,57 @@ class BSONTest < Test::Unit::TestCase
doc["a"] = 2
doc["c"] = 3
doc["d"] = 4
bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
assert_doc_pass(doc)
end
def test_object
doc = {'doc' => {'age' => 42, 'name' => 'Spongebob', 'shoe_size' => 9.5}}
assert_doc_pass(doc)
bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
end
def test_oid
doc = {'doc' => ObjectId.new}
bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
assert_doc_pass(doc)
end
def test_array
doc = {'doc' => [1, 2, 'a', 'b']}
bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
assert_doc_pass(doc)
end
def test_regex
doc = {'doc' => /foobar/i}
bson = BSON::BSON_CODER.serialize(doc)
doc2 = BSON::BSON_CODER.deserialize(bson)
assert_equal doc, doc2
r = doc2['doc']
assert_kind_of Regexp, r
doc = {'doc' => r}
bson_doc = BSON::BSON_CODER.serialize(doc)
doc2 = nil
doc2 = BSON::BSON_CODER.deserialize(bson_doc)
assert_equal doc, doc2
assert_doc_pass(doc)
end
def test_boolean
doc = {'doc' => true}
bson = BSON::BSON_CODER.serialize(doc)
assert_equal doc, BSON::BSON_CODER.deserialize(bson)
assert_doc_pass(doc)
end
def test_date
doc = {'date' => Time.now}
bson = BSON::BSON_CODER.serialize(doc)
doc2 = BSON::BSON_CODER.deserialize(bson)
bson = @encoder.serialize(doc)
doc2 = @decoder.deserialize(bson)
# Mongo only stores up to the millisecond
assert_in_delta doc['date'], doc2['date'], 0.001
end
def test_date_returns_as_utc
doc = {'date' => Time.now}
bson = BSON::BSON_CODER.serialize(doc)
doc2 = BSON::BSON_CODER.deserialize(bson)
bson = @encoder.serialize(doc)
doc2 = @decoder.deserialize(bson)
assert doc2['date'].utc?
end
def test_date_before_epoch
begin
doc = {'date' => Time.utc(1600)}
bson = BSON::BSON_CODER.serialize(doc)
doc2 = BSON::BSON_CODER.deserialize(bson)
bson = @encoder.serialize(doc)
doc2 = @decoder.deserialize(bson)
# Mongo only stores up to the millisecond
assert_in_delta doc['date'], doc2['date'], 0.001
assert_in_delta doc['date'], doc2['date'], 2
rescue ArgumentError
# some versions of Ruby won't let you create pre-epoch Time instances
#
@ -273,16 +239,23 @@ class BSONTest < Test::Unit::TestCase
oid = ObjectId.new
doc = {}
doc['dbref'] = DBRef.new('namespace', oid)
bson = BSON::BSON_CODER.serialize(doc)
doc2 = BSON::BSON_CODER.deserialize(bson)
bson = @encoder.serialize(doc)
doc2 = @decoder.deserialize(bson)
# Java doesn't deserialize to DBRefs
if RUBY_PLATFORM =~ /java/
assert_equal 'namespace', doc2['dbref']['$ns']
assert_equal oid, doc2['dbref']['$id']
else
assert_equal 'namespace', doc2['dbref'].namespace
assert_equal oid, doc2['dbref'].object_id
end
end
def test_symbol
doc = {'sym' => :foo}
bson = BSON::BSON_CODER.serialize(doc)
doc2 = BSON::BSON_CODER.deserialize(bson)
bson = @encoder.serialize(doc)
doc2 = @decoder.deserialize(bson)
assert_equal :foo, doc2['sym']
end
@ -291,8 +264,8 @@ class BSONTest < Test::Unit::TestCase
'binstring'.each_byte { |b| bin.put(b) }
doc = {'bin' => bin}
bson = BSON::BSON_CODER.serialize(doc)
doc2 = BSON::BSON_CODER.deserialize(bson)
bson = @encoder.serialize(doc)
doc2 = @decoder.deserialize(bson)
bin2 = doc2['bin']
assert_kind_of Binary, bin2
assert_equal 'binstring', bin2.to_s
@ -302,8 +275,8 @@ class BSONTest < Test::Unit::TestCase
def test_binary_with_string
b = Binary.new('somebinarystring')
doc = {'bin' => b}
bson = BSON::BSON_CODER.serialize(doc)
doc2 = BSON::BSON_CODER.deserialize(bson)
bson = @encoder.serialize(doc)
doc2 = @decoder.deserialize(bson)
bin2 = doc2['bin']
assert_kind_of Binary, bin2
assert_equal 'somebinarystring', bin2.to_s
@ -314,33 +287,36 @@ class BSONTest < Test::Unit::TestCase
bin = Binary.new([1, 2, 3, 4, 5], Binary::SUBTYPE_USER_DEFINED)
doc = {'bin' => bin}
bson = BSON::BSON_CODER.serialize(doc)
doc2 = BSON::BSON_CODER.deserialize(bson)
bson = @encoder.serialize(doc)
doc2 = @decoder.deserialize(bson)
bin2 = doc2['bin']
assert_kind_of Binary, bin2
assert_equal [1, 2, 3, 4, 5], bin2.to_a
assert_equal Binary::SUBTYPE_USER_DEFINED, bin2.subtype
end
# Java doesn't support binary subtype 0 yet
if !(RUBY_PLATFORM =~ /java/)
def test_binary_subtype_0
bin = Binary.new([1, 2, 3, 4, 5], Binary::SUBTYPE_SIMPLE)
doc = {'bin' => bin}
bson = BSON::BSON_CODER.serialize(doc)
doc2 = BSON::BSON_CODER.deserialize(bson)
bson = @encoder.serialize(doc)
doc2 = @decoder.deserialize(bson)
bin2 = doc2['bin']
assert_kind_of Binary, bin2
assert_equal [1, 2, 3, 4, 5], bin2.to_a
assert_equal Binary::SUBTYPE_SIMPLE, bin2.subtype
end
end
def test_binary_byte_buffer
bb = Binary.new
5.times { |i| bb.put(i + 1) }
doc = {'bin' => bb}
bson = BSON::BSON_CODER.serialize(doc)
doc2 = BSON::BSON_CODER.deserialize(bson)
bson = @encoder.serialize(doc)
doc2 = @decoder.deserialize(bson)
bin2 = doc2['bin']
assert_kind_of Binary, bin2
assert_equal [1, 2, 3, 4, 5], bin2.to_a
@ -351,52 +327,55 @@ class BSONTest < Test::Unit::TestCase
val = BSON::OrderedHash.new
val['not_id'] = 1
val['_id'] = 2
roundtrip = BSON::BSON_CODER.deserialize(BSON::BSON_CODER.serialize(val, false, true).to_a)
roundtrip = @decoder.deserialize(@encoder.serialize(val, false, true).to_s)
assert_kind_of BSON::OrderedHash, roundtrip
assert_equal '_id', roundtrip.keys.first
val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'}
roundtrip = BSON::BSON_CODER.deserialize(BSON::BSON_CODER.serialize(val, false, true).to_a)
roundtrip = @decoder.deserialize(@encoder.serialize(val, false, true).to_s)
assert_kind_of BSON::OrderedHash, roundtrip
assert_equal '_id', roundtrip.keys.first
end
def test_nil_id
doc = {"_id" => nil}
assert_equal doc, BSON::BSON_CODER.deserialize(bson = BSON::BSON_CODER.serialize(doc, false, true).to_a)
assert_doc_pass(doc)
end
if !(RUBY_PLATFORM =~ /java/)
def test_timestamp
val = {"test" => [4, 20]}
assert_equal val, BSON::BSON_CODER.deserialize([0x13, 0x00, 0x00, 0x00,
assert_equal val, @decoder.deserialize([0x13, 0x00, 0x00, 0x00,
0x11, 0x74, 0x65, 0x73,
0x74, 0x00, 0x04, 0x00,
0x00, 0x00, 0x14, 0x00,
0x00, 0x00, 0x00])
end
end
def test_overflow
doc = {"x" => 2**75}
assert_raise RangeError do
bson = BSON::BSON_CODER.serialize(doc)
bson = @encoder.serialize(doc)
end
doc = {"x" => 9223372036854775}
assert_equal doc, BSON::BSON_CODER.deserialize(BSON::BSON_CODER.serialize(doc).to_a)
assert_doc_pass(doc)
doc = {"x" => 9223372036854775807}
assert_equal doc, BSON::BSON_CODER.deserialize(BSON::BSON_CODER.serialize(doc).to_a)
assert_doc_pass(doc)
doc["x"] = doc["x"] + 1
assert_raise RangeError do
bson = BSON::BSON_CODER.serialize(doc)
bson = @encoder.serialize(doc)
end
doc = {"x" => -9223372036854775}
assert_equal doc, BSON::BSON_CODER.deserialize(BSON::BSON_CODER.serialize(doc).to_a)
assert_doc_pass(doc)
doc = {"x" => -9223372036854775808}
assert_equal doc, BSON::BSON_CODER.deserialize(BSON::BSON_CODER.serialize(doc).to_a)
assert_doc_pass(doc)
doc["x"] = doc["x"] - 1
assert_raise RangeError do
@ -408,7 +387,7 @@ class BSONTest < Test::Unit::TestCase
[BigDecimal.new("1.0"), Complex(0, 1), Rational(2, 3)].each do |type|
doc = {"x" => type}
begin
BSON::BSON_CODER.serialize(doc)
@encoder.serialize(doc)
rescue => e
ensure
assert_equal InvalidDocument, e.class
@ -422,12 +401,12 @@ class BSONTest < Test::Unit::TestCase
val['not_id'] = 1
val['_id'] = 2
assert val.keys.include?('_id')
BSON::BSON_CODER.serialize(val)
@encoder.serialize(val)
assert val.keys.include?('_id')
val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'}
assert val.keys.include?(:_id)
BSON::BSON_CODER.serialize(val)
@encoder.serialize(val)
assert val.keys.include?(:_id)
end
@ -442,50 +421,48 @@ class BSONTest < Test::Unit::TestCase
dup = {"_id" => "foo", :_id => "foo"}
one = {"_id" => "foo"}
assert_equal BSON::BSON_CODER.serialize(one).to_a, BSON::BSON_CODER.serialize(dup).to_a
assert_equal @encoder.serialize(one).to_a, @encoder.serialize(dup).to_a
end
def test_no_duplicate_id_when_moving_id
dup = {"_id" => "foo", :_id => "foo"}
one = {:_id => "foo"}
assert_equal BSON::BSON_CODER.serialize(one, false, true).to_s, BSON::BSON_CODER.serialize(dup, false, true).to_s
assert_equal @encoder.serialize(one, false, true).to_s, @encoder.serialize(dup, false, true).to_s
end
def test_null_character
doc = {"a" => "\x00"}
assert_equal doc, BSON::BSON_CODER.deserialize(BSON::BSON_CODER.serialize(doc).to_a)
assert_doc_pass(doc)
assert_raise InvalidDocument do
BSON::BSON_CODER.serialize({"\x00" => "a"})
@encoder.serialize({"\x00" => "a"})
end
assert_raise InvalidDocument do
BSON::BSON_CODER.serialize({"a" => (Regexp.compile "ab\x00c")})
@encoder.serialize({"a" => (Regexp.compile "ab\x00c")})
end
end
def test_max_key
doc = {"a" => MaxKey.new}
assert_equal doc, BSON::BSON_CODER.deserialize(BSON::BSON_CODER.serialize(doc).to_a)
assert_doc_pass(doc)
end
def test_min_key
doc = {"a" => MinKey.new}
assert_equal doc, BSON::BSON_CODER.deserialize(BSON::BSON_CODER.serialize(doc).to_a)
assert_doc_pass(doc)
end
def test_invalid_object
o = Object.new
assert_raise InvalidDocument do
BSON::BSON_CODER.serialize({:foo => o})
@encoder.serialize({:foo => o})
end
assert_raise InvalidDocument do
BSON::BSON_CODER.serialize({:foo => Date.today})
@encoder.serialize({:foo => Date.today})
end
end
@ -498,10 +475,14 @@ class BSONTest < Test::Unit::TestCase
assert_equal ")\000\000\000\020_id\000\001\000\000\000\002text" +
"\000\004\000\000\000abc\000\002key\000\004\000\000\000abc\000\000",
BSON::BSON_CODER.serialize(a, false, true).to_s
@encoder.serialize(a, false, true).to_s
# Java doesn't support this. Isn't actually necessary.
if !(RUBY_PLATFORM =~ /java/)
assert_equal ")\000\000\000\002text\000\004\000\000\000abc\000\002key" +
"\000\004\000\000\000abc\000\020_id\000\001\000\000\000\000",
BSON::BSON_CODER.serialize(a, false, false).to_s
@encoder.serialize(a, false, false).to_s
end
end
def test_move_id_with_nested_doc
@ -515,11 +496,15 @@ class BSONTest < Test::Unit::TestCase
assert_equal ">\000\000\000\020_id\000\003\000\000\000\002text" +
"\000\004\000\000\000abc\000\003hash\000\034\000\000" +
"\000\002text\000\004\000\000\000abc\000\020_id\000\002\000\000\000\000\000",
BSON::BSON_CODER.serialize(c, false, true).to_s
@encoder.serialize(c, false, true).to_s
# Java doesn't support this. Isn't actually necessary.
if !(RUBY_PLATFORM =~ /java/)
assert_equal ">\000\000\000\002text\000\004\000\000\000abc\000\003hash" +
"\000\034\000\000\000\002text\000\004\000\000\000abc\000\020_id" +
"\000\002\000\000\000\000\020_id\000\003\000\000\000\000",
BSON::BSON_CODER.serialize(c, false, false).to_s
@encoder.serialize(c, false, false).to_s
end
end
# Mocking this class for testing
@ -531,12 +516,12 @@ class BSONTest < Test::Unit::TestCase
embedded['_id'] = ObjectId.new
doc['_id'] = ObjectId.new
doc['embedded'] = [embedded]
BSON::BSON_CODER.serialize(doc, false, true).to_a
@encoder.serialize(doc, false, true).to_a
assert doc.has_key?("_id")
assert doc['embedded'][0].has_key?("_id")
doc['_id'] = ObjectId.new
BSON::BSON_CODER.serialize(doc, false, true).to_a
@encoder.serialize(doc, false, true).to_a
assert doc.has_key?("_id")
end