diff --git a/lib/mongo/util/bson.rb b/lib/mongo/util/bson.rb index 6a96206..a7afaa0 100644 --- a/lib/mongo/util/bson.rb +++ b/lib/mongo/util/bson.rb @@ -47,6 +47,7 @@ class BSON CODE_W_SCOPE = 15 NUMBER_INT = 16 TIMESTAMP = 17 + NUMBER_LONG = 18 MAXKEY = 127 if RUBY_VERSION >= '1.9' @@ -180,6 +181,9 @@ class BSON when NUMBER_INT key = deserialize_cstr(@buf) doc[key] = deserialize_number_int_data(@buf) + when NUMBER_LONG + key = deserialize_cstr(@buf) + doc[key] = deserialize_number_long_data(@buf) when OID key = deserialize_cstr(@buf) doc[key] = deserialize_oid_data(@buf) @@ -263,6 +267,12 @@ class BSON unsigned >= 2**32 / 2 ? unsigned - 2**32 : unsigned end + def deserialize_number_long_data(buf) + # same note as above applies here... + unsigned = buf.get_long + unsigned >= 2 ** 64 / 2 ? unsigned - 2**64 : unsigned + end + def deserialize_object_data(buf) size = buf.get_int buf.position -= 4 @@ -394,15 +404,23 @@ class BSON end def serialize_number_element(buf, key, val, type) - buf.put(type) - self.class.serialize_cstr(buf, key) if type == NUMBER + buf.put(type) + self.class.serialize_cstr(buf, key) buf.put_double(val) else - if val > 2**32 / 2 - 1 or val < -2**32 / 2 - raise RangeError.new("MongoDB can only handle 4-byte ints - try converting to a double before saving") + if val > 2**64 / 2 - 1 or val < -2**64 / 2 + raise RangeError.new("MongoDB can only handle 8-byte ints") + end + if val > 2**32 / 2 - 1 or val < -2**32 / 2 + buf.put(NUMBER_LONG) + self.class.serialize_cstr(buf, key) + buf.put_long(val) + else + buf.put(type) + self.class.serialize_cstr(buf, key) + buf.put_int(val) end - buf.put_int(val) end end diff --git a/tests/test_bson.rb b/tests/test_bson.rb index e63fc5b..8651ca3 100644 --- a/tests/test_bson.rb +++ b/tests/test_bson.rb @@ -215,18 +215,32 @@ class BSONTest < Test::Unit::TestCase end def test_overflow - doc = {"x" => 2**45} + doc = {"x" => 2**75} assert_raise RangeError do @b.serialize(doc) end - doc = {"x" => 2147483647} + doc = {"x" => 9223372036854775} + assert_equal doc, @b.deserialize(@b.serialize(doc).to_a) + + doc = {"x" => 9223372036854775807} assert_equal doc, @b.deserialize(@b.serialize(doc).to_a) doc["x"] = doc["x"] + 1 assert_raise RangeError do @b.serialize(doc) end + + doc = {"x" => -9223372036854775} + assert_equal doc, @b.deserialize(@b.serialize(doc).to_a) + + doc = {"x" => -9223372036854775808} + assert_equal doc, @b.deserialize(@b.serialize(doc).to_a) + + doc["x"] = doc["x"] - 1 + assert_raise RangeError do + @b.serialize(doc) + end end def test_do_not_change_original_object diff --git a/tests/test_db_api.rb b/tests/test_db_api.rb index 044e8a2..e1b7715 100644 --- a/tests/test_db_api.rb +++ b/tests/test_db_api.rb @@ -639,6 +639,12 @@ class DBAPITest < Test::Unit::TestCase assert_equal 2, @@coll.count end + def test_save_long + @@coll.clear + @@coll.insert("x" => 9223372036854775807) + assert_equal 9223372036854775807, @@coll.find_first()["x"] + end + def test_find_by_oid @@coll.clear