diff --git a/ext/cbson/cbson.c b/ext/cbson/cbson.c index f81bf58..4177f9d 100644 --- a/ext/cbson/cbson.c +++ b/ext/cbson/cbson.c @@ -353,7 +353,72 @@ static VALUE method_serialize(VALUE self, VALUE doc) { return result; } +static VALUE get_value(const char* buffer, int* position, int type) { + VALUE value; + switch (type) { + case 1: + { + double d; + memcpy(&d, buffer + *position, 8); + value = DBL2NUM(d); + *position += 8; + break; + } + case 2: + { + *position += 4; + int value_length = strlen(buffer + *position); + value = rb_str_new(buffer+ *position, value_length); + *position += value_length + 1; + break; + } + case 8: + { + value = buffer[(*position)++] ? Qtrue : Qfalse; + break; + } + case 16: + { + int i; + memcpy(&i, buffer + *position, 4); + value = INT2FIX(i); + *position += 4; + break; + } + default: + rb_raise(rb_eTypeError, "no c decoder for this type yet (%d)", type); + break; + } + return value; +} + +static VALUE elements_to_hash(const char* buffer, int max) { + VALUE hash = rb_hash_new(); + int position = 0; + while (position < max) { + int type = (int)buffer[position++]; + int name_length = strlen(buffer + position); + VALUE name = rb_str_new(buffer + position, name_length); + position += name_length + 1; + VALUE value = get_value(buffer, &position, type); + rb_hash_aset(hash, name, value); + } + return hash; +} + +static VALUE method_deserialize(VALUE self, VALUE bson) { + const char* buffer = RSTRING(bson)->ptr; + int remaining = RSTRING(bson)->len; + + // NOTE we just swallow the size and end byte here + buffer += 4; + remaining -= 5; + + return elements_to_hash(buffer, remaining); +} + void Init_cbson() { VALUE CBson = rb_define_module("CBson"); rb_define_module_function(CBson, "serialize", method_serialize, 1); + rb_define_module_function(CBson, "deserialize", method_deserialize, 1); } diff --git a/lib/mongo/util/bson.rb b/lib/mongo/util/bson.rb index 7f5d132..2f619ef 100644 --- a/lib/mongo/util/bson.rb +++ b/lib/mongo/util/bson.rb @@ -131,73 +131,86 @@ class BSON end end - def deserialize(buf=nil, parent=nil) - # If buf is nil, use @buf, assumed to contain already-serialized BSON. - # This is only true during testing. - if buf.is_a? String - @buf = ByteBuffer.new(buf) if buf - else - @buf = ByteBuffer.new(buf.to_a) if buf - end - @buf.rewind - @buf.get_int # eat message size - doc = OrderedHash.new - while @buf.more? - type = @buf.get - case type - when STRING, CODE - key = deserialize_cstr(@buf) - doc[key] = deserialize_string_data(@buf) - when SYMBOL - key = deserialize_cstr(@buf) - doc[key] = deserialize_string_data(@buf).intern - when NUMBER - key = deserialize_cstr(@buf) - doc[key] = deserialize_number_data(@buf) - when NUMBER_INT - key = deserialize_cstr(@buf) - doc[key] = deserialize_number_int_data(@buf) - when OID - key = deserialize_cstr(@buf) - doc[key] = deserialize_oid_data(@buf) - when ARRAY - key = deserialize_cstr(@buf) - doc[key] = deserialize_array_data(@buf, doc) - when REGEX - key = deserialize_cstr(@buf) - doc[key] = deserialize_regex_data(@buf) - when OBJECT - key = deserialize_cstr(@buf) - doc[key] = deserialize_object_data(@buf, doc) - when BOOLEAN - key = deserialize_cstr(@buf) - doc[key] = deserialize_boolean_data(@buf) - when DATE - key = deserialize_cstr(@buf) - doc[key] = deserialize_date_data(@buf) - when NULL - key = deserialize_cstr(@buf) - doc[key] = nil - when UNDEFINED - key = deserialize_cstr(@buf) - doc[key] = Undefined.new - when REF - key = deserialize_cstr(@buf) - doc[key] = deserialize_dbref_data(@buf, key, parent) - when BINARY - key = deserialize_cstr(@buf) - doc[key] = deserialize_binary_data(@buf) - when CODE_W_SCOPE - # TODO CODE_W_SCOPE unimplemented; may be removed - raise "unimplemented type #{type}" - when EOO - break + begin + require 'mongo/ext/cbson' + def deserialize(buf=nil, parent=nil) + if buf.is_a? String + @buf = ByteBuffer.new(buf) if buf else - raise "Unknown type #{type}, key = #{key}" + @buf = ByteBuffer.new(buf.to_a) if buf end + @buf.rewind + CBson.deserialize(@buf.to_s) + end + rescue LoadError + def deserialize(buf=nil, parent=nil) + # If buf is nil, use @buf, assumed to contain already-serialized BSON. + # This is only true during testing. + if buf.is_a? String + @buf = ByteBuffer.new(buf) if buf + else + @buf = ByteBuffer.new(buf.to_a) if buf + end + @buf.rewind + @buf.get_int # eat message size + doc = OrderedHash.new + while @buf.more? + type = @buf.get + case type + when STRING, CODE + key = deserialize_cstr(@buf) + doc[key] = deserialize_string_data(@buf) + when SYMBOL + key = deserialize_cstr(@buf) + doc[key] = deserialize_string_data(@buf).intern + when NUMBER + key = deserialize_cstr(@buf) + doc[key] = deserialize_number_data(@buf) + when NUMBER_INT + key = deserialize_cstr(@buf) + doc[key] = deserialize_number_int_data(@buf) + when OID + key = deserialize_cstr(@buf) + doc[key] = deserialize_oid_data(@buf) + when ARRAY + key = deserialize_cstr(@buf) + doc[key] = deserialize_array_data(@buf, doc) + when REGEX + key = deserialize_cstr(@buf) + doc[key] = deserialize_regex_data(@buf) + when OBJECT + key = deserialize_cstr(@buf) + doc[key] = deserialize_object_data(@buf, doc) + when BOOLEAN + key = deserialize_cstr(@buf) + doc[key] = deserialize_boolean_data(@buf) + when DATE + key = deserialize_cstr(@buf) + doc[key] = deserialize_date_data(@buf) + when NULL + key = deserialize_cstr(@buf) + doc[key] = nil + when UNDEFINED + key = deserialize_cstr(@buf) + doc[key] = Undefined.new + when REF + key = deserialize_cstr(@buf) + doc[key] = deserialize_dbref_data(@buf, key, parent) + when BINARY + key = deserialize_cstr(@buf) + doc[key] = deserialize_binary_data(@buf) + when CODE_W_SCOPE + # TODO CODE_W_SCOPE unimplemented; may be removed + raise "unimplemented type #{type}" + when EOO + break + else + raise "Unknown type #{type}, key = #{key}" + end + end + @buf.rewind + doc end - @buf.rewind - doc end # For debugging.