static VALUE method_deserialize(VALUE self, VALUE bson) { const char* buffer = RSTRING_PTR(bson); int remaining = RSTRING_LENINT(bson); // NOTE we just swallow the size and end byte here buffer += 4; remaining -= 5; return elements_to_hash(buffer, remaining); }
static VALUE method_deserialize(int argc, VALUE *argv, VALUE self) { VALUE bson; VALUE symbolize_keys = 0; rb_scan_args(argc, argv, "11", &bson, &symbolize_keys); const char* buffer = RSTRING_PTR(bson); int remaining = RSTRING_LEN(bson); // NOTE we just swallow the size and end byte here buffer += 4; remaining -= 5; return elements_to_hash(buffer, remaining, symbolize_keys); }
static VALUE method_deserialize(VALUE self, VALUE bson, VALUE opts) { const char* buffer = RSTRING_PTR(bson); int remaining = RSTRING_LENINT(bson); struct deserialize_opts deserialize_opts; deserialize_opts.compile_regex = 1; if (rb_funcall(opts, rb_intern("has_key?"), 1, ID2SYM(rb_intern("compile_regex"))) == Qtrue && rb_hash_aref(opts, ID2SYM(rb_intern("compile_regex"))) == Qfalse) { deserialize_opts.compile_regex = 0; } // NOTE we just swallow the size and end byte here buffer += 4; remaining -= 5; return elements_to_hash(buffer, remaining, &deserialize_opts); }
static VALUE get_value(const char* buffer, int* position, int type) { VALUE value; switch (type) { case -1: { value = rb_class_new_instance(0, NULL, MinKey); break; } case 1: { double d; memcpy(&d, buffer + *position, 8); value = rb_float_new(d); *position += 8; break; } case 2: case 13: { int value_length; value_length = *(int*)(buffer + *position) - 1; *position += 4; value = STR_NEW(buffer + *position, value_length); *position += value_length + 1; break; } case 3: { int size; memcpy(&size, buffer + *position, 4); if (strcmp(buffer + *position + 5, "$ref") == 0) { // DBRef int offset = *position + 10; VALUE argv[2]; int collection_length = *(int*)(buffer + offset) - 1; char id_type; offset += 4; argv[0] = STR_NEW(buffer + offset, collection_length); offset += collection_length + 1; id_type = buffer[offset]; offset += 5; argv[1] = get_value(buffer, &offset, (int)id_type); value = rb_class_new_instance(2, argv, DBRef); } else { value = elements_to_hash(buffer + *position + 4, size - 5); } *position += size; break; } case 4: { int size, end; memcpy(&size, buffer + *position, 4); end = *position + size - 1; *position += 4; value = rb_ary_new(); while (*position < end) { int type = (int)buffer[(*position)++]; int key_size = (int)strlen(buffer + *position); VALUE to_append; *position += key_size + 1; // just skip the key, they're in order. to_append = get_value(buffer, position, type); rb_ary_push(value, to_append); } (*position)++; break; } case 5: { int length, subtype; VALUE data, st; VALUE argv[2]; memcpy(&length, buffer + *position, 4); subtype = (unsigned char)buffer[*position + 4]; if (subtype == 2) { data = rb_str_new(buffer + *position + 9, length - 4); } else { data = rb_str_new(buffer + *position + 5, length); } st = INT2FIX(subtype); argv[0] = data; argv[1] = st; value = rb_class_new_instance(2, argv, Binary); *position += length + 5; break; } case 6: { value = Qnil; break; } case 7: { VALUE str = rb_str_new(buffer + *position, 12); VALUE oid = rb_funcall(str, unpack_method, 1, rb_str_new2("C*")); value = rb_class_new_instance(1, &oid, ObjectId); *position += 12; break; } case 8: { value = buffer[(*position)++] ? Qtrue : Qfalse; break; } case 9: { long long millis; memcpy(&millis, buffer + *position, 8); value = rb_time_new(millis / 1000, (millis % 1000) * 1000); value = rb_funcall(value, utc_method, 0); *position += 8; break; } case 10: { value = Qnil; break; } case 11: { int pattern_length = (int)strlen(buffer + *position); VALUE pattern = STR_NEW(buffer + *position, pattern_length); int flags_length, flags = 0, i = 0; VALUE argv[3]; *position += pattern_length + 1; flags_length = (int)strlen(buffer + *position); for (i = 0; i < flags_length; i++) { char flag = buffer[*position + i]; if (flag == 'i') { flags |= IGNORECASE; } else if (flag == 'm') { flags |= MULTILINE; } else if (flag == 's') { flags |= MULTILINE; } else if (flag == 'x') { flags |= EXTENDED; } } argv[0] = pattern; argv[1] = INT2FIX(flags); value = rb_class_new_instance(2, argv, Regexp); *position += flags_length + 1; break; } case 12: { int collection_length; VALUE collection, str, oid, id, argv[2]; collection_length = *(int*)(buffer + *position) - 1; *position += 4; collection = STR_NEW(buffer + *position, collection_length); *position += collection_length + 1; str = rb_str_new(buffer + *position, 12); oid = rb_funcall(str, unpack_method, 1, rb_str_new2("C*")); id = rb_class_new_instance(1, &oid, ObjectId); *position += 12; argv[0] = collection; argv[1] = id; value = rb_class_new_instance(2, argv, DBRef); break; } case 14: { int value_length; memcpy(&value_length, buffer + *position, 4); value = ID2SYM(rb_intern(buffer + *position + 4)); *position += value_length + 4; break; } case 15: { int code_length, scope_size; VALUE code, scope, argv[2]; *position += 4; code_length = *(int*)(buffer + *position) - 1; *position += 4; code = STR_NEW(buffer + *position, code_length); *position += code_length + 1; memcpy(&scope_size, buffer + *position, 4); scope = elements_to_hash(buffer + *position + 4, scope_size - 5); *position += scope_size; argv[0] = code; argv[1] = scope; value = rb_class_new_instance(2, argv, Code); break; } case 16: { int i; memcpy(&i, buffer + *position, 4); value = LL2NUM(i); *position += 4; break; } case 17: { int sec, inc; VALUE argv[2]; memcpy(&inc, buffer + *position, 4); memcpy(&sec, buffer + *position + 4, 4); argv[0] = INT2FIX(sec); argv[1] = INT2FIX(inc); value = rb_class_new_instance(2, argv, Timestamp); *position += 8; break; } case 18: { long long ll; memcpy(&ll, buffer + *position, 8); value = LL2NUM(ll); *position += 8; break; } case 127: { value = rb_class_new_instance(0, NULL, MaxKey); break; } default: { rb_raise(rb_eTypeError, "no c decoder for this type yet (%d)", type); break; } } return value; }
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 = rb_float_new(d); *position += 8; break; } case 2: case 13: { *position += 4; int value_length = strlen(buffer + *position); value = rb_str_new(buffer+ *position, value_length); *position += value_length + 1; break; } case 3: { int size; memcpy(&size, buffer + *position, 4); if (strcmp(buffer + *position + 5, "$ref") == 0) { // DBRef int offset = *position + 14; VALUE argv[2]; int collection_length = strlen(buffer + offset); argv[0] = rb_str_new(buffer + offset, collection_length); offset += collection_length + 1; char id_type = buffer[offset]; offset += 5; argv[1] = get_value(buffer, &offset, (int)id_type); value = rb_class_new_instance(2, argv, DBRef); } else { value = elements_to_hash(buffer + *position + 4, size - 5); } *position += size; break; } case 4: { int size; memcpy(&size, buffer + *position, 4); int end = *position + size - 1; *position += 4; value = rb_ary_new(); while (*position < end) { int type = (int)buffer[(*position)++]; int key_size = strlen(buffer + *position); *position += key_size + 1; // just skip the key, they're in order. VALUE to_append = get_value(buffer, position, type); rb_ary_push(value, to_append); } (*position)++; break; } case 5: { int length; memcpy(&length, buffer + *position, 4); int subtype = (unsigned char)buffer[*position + 4]; VALUE data; if (subtype == 2) { data = rb_str_new(buffer + *position + 9, length - 4); } else { data = rb_str_new(buffer + *position + 5, length); } VALUE st = INT2FIX(subtype); VALUE argv[2] = {data, st}; value = rb_class_new_instance(2, argv, Binary); *position += length + 5; break; } case 6: { value = rb_class_new_instance(0, NULL, Undefined); break; } case 7: { VALUE str = rb_str_new(buffer + *position, 12); VALUE oid = rb_funcall(str, rb_intern("unpack"), 1, rb_str_new2("C*")); value = rb_class_new_instance(1, &oid, ObjectID); *position += 12; break; } case 8: { value = buffer[(*position)++] ? Qtrue : Qfalse; break; } case 9: { long long millis; memcpy(&millis, buffer + *position, 8); VALUE seconds = INT2NUM(millis / 1000); VALUE microseconds = INT2NUM((millis % 1000) * 1000); value = rb_funcall(Time, rb_intern("at"), 2, seconds, microseconds); *position += 8; break; } case 10: { value = Qnil; break; } case 11: { int pattern_length = strlen(buffer + *position); VALUE pattern = rb_str_new(buffer + *position, pattern_length); *position += pattern_length + 1; int flags_length = strlen(buffer + *position); int i = 0; int flags = 0; char extra[10]; extra[0] = 0; for (i = 0; i < flags_length; i++) { char flag = buffer[*position + i]; if (flag == 'i') { flags |= IGNORECASE; } else if (flag == 'm') { flags |= MULTILINE; } else if (flag == 'x') { flags |= EXTENDED; } else if (strlen(extra) < 9) { strncat(extra, &flag, 1); } } VALUE argv[3] = { pattern, INT2FIX(flags), rb_str_new2(extra) }; value = rb_class_new_instance(3, argv, RegexpOfHolding); *position += flags_length + 1; break; } case 12: { *position += 4; int collection_length = strlen(buffer + *position); VALUE collection = rb_str_new(buffer + *position, collection_length); *position += collection_length + 1; VALUE str = rb_str_new(buffer + *position, 12); VALUE oid = rb_funcall(str, rb_intern("unpack"), 1, rb_str_new2("C*")); VALUE id = rb_class_new_instance(1, &oid, ObjectID); *position += 12; VALUE argv[2] = {collection, id}; value = rb_class_new_instance(2, argv, DBRef); break; } case 14: { int value_length; memcpy(&value_length, buffer + *position, 4); value = ID2SYM(rb_intern(buffer + *position + 4)); *position += value_length + 4; break; } case 15: { *position += 8; int code_length = strlen(buffer + *position); VALUE code = rb_str_new(buffer + *position, code_length); *position += code_length + 1; int scope_size; memcpy(&scope_size, buffer + *position, 4); VALUE scope = elements_to_hash(buffer + *position + 4, scope_size - 5); *position += scope_size; VALUE argv[2] = {code, scope}; value = rb_class_new_instance(2, argv, Code); break; } case 16: { int i; memcpy(&i, buffer + *position, 4); value = LL2NUM(i); *position += 4; break; } case 17: { int i; int j; memcpy(&i, buffer + *position, 4); memcpy(&j, buffer + *position + 4, 4); value = rb_ary_new3(2, LL2NUM(i), LL2NUM(j)); *position += 8; break; } default: { rb_raise(rb_eTypeError, "no c decoder for this type yet (%d)", type); break; } } return value; }
static VALUE get_value(const char* buffer, int* position, unsigned char type, struct deserialize_opts * opts) { VALUE value; switch (type) { case 255: { value = rb_class_new_instance(0, NULL, MinKey); break; } case 1: { double d; memcpy(&d, buffer + *position, 8); value = rb_float_new(d); *position += 8; break; } case 2: case 13: { int value_length; value_length = *(int*)(buffer + *position) - 1; *position += 4; value = STR_NEW(buffer + *position, value_length); *position += value_length + 1; break; } case 3: { int size; memcpy(&size, buffer + *position, 4); if (strcmp(buffer + *position + 5, "$ref") == 0) { // DBRef VALUE argv[2]; unsigned char id_type; int offset = *position + 10; int collection_length = *(int*)(buffer + offset) - 1; offset += 4; argv[0] = STR_NEW(buffer + offset, collection_length); offset += collection_length + 1; id_type = (unsigned char)buffer[offset]; offset += 5; argv[1] = get_value(buffer, &offset, id_type, opts); value = rb_class_new_instance(2, argv, DBRef); } else { value = elements_to_hash(buffer + *position + 4, size - 5, opts); } *position += size; break; } case 4: { int size, end; memcpy(&size, buffer + *position, 4); end = *position + size - 1; *position += 4; value = rb_ary_new(); while (*position < end) { VALUE to_append; unsigned char type = (unsigned char)buffer[(*position)++]; int key_size = (int)strlen(buffer + *position); *position += key_size + 1; // just skip the key, they're in order. to_append = get_value(buffer, position, type, opts); rb_ary_push(value, to_append); } (*position)++; break; } case 5: { int length, subtype; VALUE data, st; VALUE argv[2]; memcpy(&length, buffer + *position, 4); subtype = (unsigned char)buffer[*position + 4]; if (subtype == 2) { data = rb_str_new(buffer + *position + 9, length - 4); } else { data = rb_str_new(buffer + *position + 5, length); } st = INT2FIX(subtype); argv[0] = data; argv[1] = st; value = rb_class_new_instance(2, argv, Binary); *position += length + 5; break; } case 6: { value = Qnil; break; } case 7: { VALUE str = rb_str_new(buffer + *position, 12); VALUE oid = rb_funcall(str, unpack_method, 1, rb_str_new2("C*")); value = rb_class_new_instance(1, &oid, ObjectId); *position += 12; break; } case 8: { value = buffer[(*position)++] ? Qtrue : Qfalse; break; } case 9: { int64_t millis; memcpy(&millis, buffer + *position, 8); // Support 64-bit time values in 32 bit environments in Ruby > 1.9 // Note: rb_time_num_new is not available pre Ruby 1.9 #if RUBY_API_VERSION_CODE >= 10900 #define add(x,y) (rb_funcall((x), '+', 1, (y))) #define mul(x,y) (rb_funcall((x), '*', 1, (y))) #define quo(x,y) (rb_funcall((x), rb_intern("quo"), 1, (y))) VALUE d, timev; d = LL2NUM(1000LL); timev = add(LL2NUM(millis / 1000), quo(LL2NUM(millis % 1000), d)); value = rb_time_num_new(timev, Qnil); #else value = rb_time_new(millis / 1000, (millis % 1000) * 1000); #endif value = rb_funcall(value, utc_method, 0); *position += 8; break; } case 10: { value = Qnil; break; } case 11: { int pattern_length = (int)strlen(buffer + *position); VALUE pattern = STR_NEW(buffer + *position, pattern_length); int flags_length; VALUE argv[3], flags_str; *position += pattern_length + 1; flags_length = (int)strlen(buffer + *position); flags_str = STR_NEW(buffer + *position, flags_length); argv[0] = pattern; argv[1] = flags_str; value = rb_class_new_instance(2, argv, BSONRegex); if (opts->compile_regex == 1) { value = rb_funcall(value, rb_intern("try_compile"), 0); } *position += flags_length + 1; break; } case 12: { int collection_length; VALUE collection, str, oid, id, argv[2]; collection_length = *(int*)(buffer + *position) - 1; *position += 4; collection = STR_NEW(buffer + *position, collection_length); *position += collection_length + 1; str = rb_str_new(buffer + *position, 12); oid = rb_funcall(str, unpack_method, 1, rb_str_new2("C*")); id = rb_class_new_instance(1, &oid, ObjectId); *position += 12; argv[0] = collection; argv[1] = id; value = rb_class_new_instance(2, argv, DBRef); break; } case 14: { int value_length; memcpy(&value_length, buffer + *position, 4); value = ID2SYM(rb_intern(buffer + *position + 4)); *position += value_length + 4; break; } case 15: { int code_length, scope_size; VALUE code, scope, argv[2]; *position += 4; code_length = *(int*)(buffer + *position) - 1; *position += 4; code = STR_NEW(buffer + *position, code_length); *position += code_length + 1; memcpy(&scope_size, buffer + *position, 4); scope = elements_to_hash(buffer + *position + 4, scope_size - 5, opts); *position += scope_size; argv[0] = code; argv[1] = scope; value = rb_class_new_instance(2, argv, Code); break; } case 16: { int i; memcpy(&i, buffer + *position, 4); value = LL2NUM(i); *position += 4; break; } case 17: { unsigned int sec, inc; VALUE argv[2]; memcpy(&inc, buffer + *position, 4); memcpy(&sec, buffer + *position + 4, 4); argv[0] = UINT2NUM(sec); argv[1] = UINT2NUM(inc); value = rb_class_new_instance(2, argv, Timestamp); *position += 8; break; } case 18: { long long ll; memcpy(&ll, buffer + *position, 8); value = LL2NUM(ll); *position += 8; break; } case 127: { value = rb_class_new_instance(0, NULL, MaxKey); break; } default: { rb_raise(rb_eTypeError, "no c decoder for this type yet (%d)", type); break; } } return value; }