animehunter-mongo_ext 0.3 → 0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ext/cbson/cbson.c +421 -1
- data/mongo-extensions.gemspec +1 -1
- metadata +1 -1
data/ext/cbson/cbson.c
CHANGED
|
@@ -344,4 +344,424 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
|
|
|
344
344
|
break;
|
|
345
345
|
}
|
|
346
346
|
if (strcmp(cls, "XGen::Mongo::Driver::ObjectID") == 0) {
|
|
347
|
-
VALUE as_array = rb_funcall(value, rb_intern("to_a
|
|
347
|
+
VALUE as_array = rb_funcall(value, rb_intern("to_a"), 0);
|
|
348
|
+
int i;
|
|
349
|
+
write_name_and_type(buffer, key, 0x07);
|
|
350
|
+
for (i = 0; i < 12; i++) {
|
|
351
|
+
char byte = (char)FIX2INT(RARRAY_PTR(as_array)[i]);
|
|
352
|
+
buffer_write_bytes(buffer, &byte, 1);
|
|
353
|
+
}
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
if (strcmp(cls, "XGen::Mongo::Driver::DBRef") == 0) {
|
|
357
|
+
int start_position, length_location, obj_length;
|
|
358
|
+
VALUE ns, oid;
|
|
359
|
+
write_name_and_type(buffer, key, 0x03);
|
|
360
|
+
|
|
361
|
+
start_position = buffer->position;
|
|
362
|
+
|
|
363
|
+
// save space for length
|
|
364
|
+
length_location = buffer_save_bytes(buffer, 4);
|
|
365
|
+
|
|
366
|
+
ns = rb_funcall(value, rb_intern("namespace"), 0);
|
|
367
|
+
write_element(rb_str_new2("$ref"), ns, pack_extra(buffer, Qfalse));
|
|
368
|
+
oid = rb_funcall(value, rb_intern("object_id"), 0);
|
|
369
|
+
write_element(rb_str_new2("$id"), oid, pack_extra(buffer, Qfalse));
|
|
370
|
+
|
|
371
|
+
// write null byte and fill in length
|
|
372
|
+
buffer_write_bytes(buffer, &zero, 1);
|
|
373
|
+
obj_length = buffer->position - start_position;
|
|
374
|
+
memcpy(buffer->buffer + length_location, &obj_length, 4);
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
if (strcmp(cls, "XGen::Mongo::Driver::Undefined") == 0) {
|
|
378
|
+
write_name_and_type(buffer, key, 0x06);
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
case T_DATA:
|
|
383
|
+
{
|
|
384
|
+
// TODO again, is this really the only way to do this?
|
|
385
|
+
const char* cls = rb_class2name(RBASIC(value)->klass);
|
|
386
|
+
if (strcmp(cls, "Time") == 0) {
|
|
387
|
+
double t = NUM2DBL(rb_funcall(value, rb_intern("to_f"), 0));
|
|
388
|
+
long long time_since_epoch = (long long)round(t * 1000);
|
|
389
|
+
write_name_and_type(buffer, key, 0x09);
|
|
390
|
+
buffer_write_bytes(buffer, (const char*)&time_since_epoch, 8);
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
case T_REGEXP:
|
|
395
|
+
{
|
|
396
|
+
int length = RREGEXP_SRC_LEN(value);
|
|
397
|
+
char* pattern = (char*)RREGEXP_SRC_PTR(value);
|
|
398
|
+
long flags = RREGEXP(value)->ptr->options;
|
|
399
|
+
VALUE has_extra;
|
|
400
|
+
|
|
401
|
+
write_name_and_type(buffer, key, 0x0B);
|
|
402
|
+
|
|
403
|
+
buffer_write_bytes(buffer, pattern, length);
|
|
404
|
+
buffer_write_bytes(buffer, &zero, 1);
|
|
405
|
+
|
|
406
|
+
if (flags & IGNORECASE) {
|
|
407
|
+
char ignorecase = 'i';
|
|
408
|
+
buffer_write_bytes(buffer, &ignorecase, 1);
|
|
409
|
+
}
|
|
410
|
+
if (flags & MULTILINE) {
|
|
411
|
+
char multiline = 'm';
|
|
412
|
+
buffer_write_bytes(buffer, &multiline, 1);
|
|
413
|
+
}
|
|
414
|
+
if (flags & EXTENDED) {
|
|
415
|
+
char extended = 'x';
|
|
416
|
+
buffer_write_bytes(buffer, &extended, 1);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
has_extra = rb_funcall(value, rb_intern("respond_to?"), 1, rb_str_new2("extra_options_str"));
|
|
420
|
+
if (TYPE(has_extra) == T_TRUE) {
|
|
421
|
+
VALUE extra = rb_funcall(value, rb_intern("extra_options_str"), 0);
|
|
422
|
+
int old_position = buffer->position;
|
|
423
|
+
buffer_write_bytes(buffer, RSTRING_PTR(extra), RSTRING_LEN(extra));
|
|
424
|
+
qsort(buffer->buffer + old_position, RSTRING_LEN(extra), sizeof(char), cmp_char);
|
|
425
|
+
}
|
|
426
|
+
buffer_write_bytes(buffer, &zero, 1);
|
|
427
|
+
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
default:
|
|
431
|
+
{
|
|
432
|
+
rb_raise(rb_eTypeError, "no c encoder for this type yet (%d)", TYPE(value));
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return ST_CONTINUE;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
static int write_element(VALUE key, VALUE value, VALUE extra) {
|
|
440
|
+
return write_element_allow_id(key, value, extra, 0);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
static void write_doc(bson_buffer* buffer, VALUE hash, VALUE check_keys) {
|
|
444
|
+
int start_position = buffer->position;
|
|
445
|
+
int length_location = buffer_save_bytes(buffer, 4);
|
|
446
|
+
int length;
|
|
447
|
+
|
|
448
|
+
VALUE key = rb_str_new2("_id");
|
|
449
|
+
if (rb_funcall(hash, rb_intern("has_key?"), 1, key) == Qtrue) {
|
|
450
|
+
VALUE id = rb_hash_aref(hash, key);
|
|
451
|
+
write_element_allow_id(key, id, pack_extra(buffer, check_keys), 1);
|
|
452
|
+
}
|
|
453
|
+
key = ID2SYM(rb_intern("_id"));
|
|
454
|
+
if (rb_funcall(hash, rb_intern("has_key?"), 1, key) == Qtrue) {
|
|
455
|
+
VALUE id = rb_hash_aref(hash, key);
|
|
456
|
+
write_element_allow_id(key, id, pack_extra(buffer, check_keys), 1);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// we have to check for an OrderedHash and handle that specially
|
|
460
|
+
if (strcmp(rb_class2name(RBASIC(hash)->klass), "OrderedHash") == 0) {
|
|
461
|
+
VALUE keys = rb_funcall(hash, rb_intern("keys"), 0);
|
|
462
|
+
int i;
|
|
463
|
+
for(i = 0; i < RARRAY_LEN(keys); i++) {
|
|
464
|
+
VALUE key = RARRAY_PTR(keys)[i];
|
|
465
|
+
VALUE value = rb_hash_aref(hash, key);
|
|
466
|
+
|
|
467
|
+
write_element(key, value, pack_extra(buffer, check_keys));
|
|
468
|
+
}
|
|
469
|
+
} else {
|
|
470
|
+
rb_hash_foreach(hash, write_element, pack_extra(buffer, check_keys));
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// write null byte and fill in length
|
|
474
|
+
buffer_write_bytes(buffer, &zero, 1);
|
|
475
|
+
length = buffer->position - start_position;
|
|
476
|
+
memcpy(buffer->buffer + length_location, &length, 4);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
static VALUE method_serialize(VALUE self, VALUE doc, VALUE check_keys) {
|
|
480
|
+
VALUE result;
|
|
481
|
+
bson_buffer* buffer = buffer_new();
|
|
482
|
+
assert(buffer);
|
|
483
|
+
|
|
484
|
+
write_doc(buffer, doc, check_keys);
|
|
485
|
+
|
|
486
|
+
result = rb_str_new(buffer->buffer, buffer->position);
|
|
487
|
+
buffer_free(buffer);
|
|
488
|
+
return result;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
static VALUE get_value(const char* buffer, int* position, int type) {
|
|
492
|
+
VALUE value;
|
|
493
|
+
switch (type) {
|
|
494
|
+
case 1:
|
|
495
|
+
{
|
|
496
|
+
double d;
|
|
497
|
+
memcpy(&d, buffer + *position, 8);
|
|
498
|
+
value = rb_float_new(d);
|
|
499
|
+
*position += 8;
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
case 2:
|
|
503
|
+
case 13:
|
|
504
|
+
{
|
|
505
|
+
int value_length;
|
|
506
|
+
*position += 4;
|
|
507
|
+
value_length = strlen(buffer + *position);
|
|
508
|
+
value = rb_str_new(buffer+ *position, value_length);
|
|
509
|
+
*position += value_length + 1;
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
case 3:
|
|
513
|
+
{
|
|
514
|
+
int size;
|
|
515
|
+
memcpy(&size, buffer + *position, 4);
|
|
516
|
+
if (strcmp(buffer + *position + 5, "$ref") == 0) { // DBRef
|
|
517
|
+
int offset = *position + 14;
|
|
518
|
+
VALUE argv[2];
|
|
519
|
+
int collection_length = strlen(buffer + offset);
|
|
520
|
+
char id_type;
|
|
521
|
+
|
|
522
|
+
argv[0] = rb_str_new(buffer + offset, collection_length);
|
|
523
|
+
offset += collection_length + 1;
|
|
524
|
+
id_type = buffer[offset];
|
|
525
|
+
offset += 5;
|
|
526
|
+
argv[1] = get_value(buffer, &offset, (int)id_type);
|
|
527
|
+
value = rb_class_new_instance(2, argv, DBRef);
|
|
528
|
+
} else {
|
|
529
|
+
value = elements_to_hash(buffer + *position + 4, size - 5);
|
|
530
|
+
}
|
|
531
|
+
*position += size;
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
case 4:
|
|
535
|
+
{
|
|
536
|
+
int size, end;
|
|
537
|
+
memcpy(&size, buffer + *position, 4);
|
|
538
|
+
end = *position + size - 1;
|
|
539
|
+
*position += 4;
|
|
540
|
+
|
|
541
|
+
value = rb_ary_new();
|
|
542
|
+
while (*position < end) {
|
|
543
|
+
int type = (int)buffer[(*position)++];
|
|
544
|
+
int key_size = strlen(buffer + *position);
|
|
545
|
+
VALUE to_append;
|
|
546
|
+
|
|
547
|
+
*position += key_size + 1; // just skip the key, they're in order.
|
|
548
|
+
to_append = get_value(buffer, position, type);
|
|
549
|
+
rb_ary_push(value, to_append);
|
|
550
|
+
}
|
|
551
|
+
(*position)++;
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
case 5:
|
|
555
|
+
{
|
|
556
|
+
int length, subtype;
|
|
557
|
+
VALUE data, st;
|
|
558
|
+
VALUE argv[2];
|
|
559
|
+
memcpy(&length, buffer + *position, 4);
|
|
560
|
+
subtype = (unsigned char)buffer[*position + 4];
|
|
561
|
+
data;
|
|
562
|
+
if (subtype == 2) {
|
|
563
|
+
data = rb_str_new(buffer + *position + 9, length - 4);
|
|
564
|
+
} else {
|
|
565
|
+
data = rb_str_new(buffer + *position + 5, length);
|
|
566
|
+
}
|
|
567
|
+
st = INT2FIX(subtype);
|
|
568
|
+
argv[0] = data;
|
|
569
|
+
argv[1] = st;
|
|
570
|
+
value = rb_class_new_instance(2, argv, Binary);
|
|
571
|
+
*position += length + 5;
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
case 6:
|
|
575
|
+
{
|
|
576
|
+
value = rb_class_new_instance(0, NULL, Undefined);
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
case 7:
|
|
580
|
+
{
|
|
581
|
+
VALUE str = rb_str_new(buffer + *position, 12);
|
|
582
|
+
VALUE oid = rb_funcall(str, rb_intern("unpack"), 1, rb_str_new2("C*"));
|
|
583
|
+
value = rb_class_new_instance(1, &oid, ObjectID);
|
|
584
|
+
*position += 12;
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
case 8:
|
|
588
|
+
{
|
|
589
|
+
value = buffer[(*position)++] ? Qtrue : Qfalse;
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
case 9:
|
|
593
|
+
{
|
|
594
|
+
long long millis;
|
|
595
|
+
VALUE seconds, microseconds;
|
|
596
|
+
memcpy(&millis, buffer + *position, 8);
|
|
597
|
+
seconds = INT2NUM(millis / 1000);
|
|
598
|
+
microseconds = INT2NUM((millis % 1000) * 1000);
|
|
599
|
+
|
|
600
|
+
value = rb_funcall(Time, rb_intern("at"), 2, seconds, microseconds);
|
|
601
|
+
value = rb_funcall(value, rb_intern("utc"), 0);
|
|
602
|
+
*position += 8;
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
605
|
+
case 10:
|
|
606
|
+
{
|
|
607
|
+
value = Qnil;
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
case 11:
|
|
611
|
+
{
|
|
612
|
+
int pattern_length = strlen(buffer + *position);
|
|
613
|
+
VALUE pattern = rb_str_new(buffer + *position, pattern_length);
|
|
614
|
+
int flags_length, flags = 0, i = 0;
|
|
615
|
+
char extra[10];
|
|
616
|
+
VALUE argv[3];
|
|
617
|
+
*position += pattern_length + 1;
|
|
618
|
+
|
|
619
|
+
flags_length = strlen(buffer + *position);
|
|
620
|
+
extra[0] = 0;
|
|
621
|
+
for (i = 0; i < flags_length; i++) {
|
|
622
|
+
char flag = buffer[*position + i];
|
|
623
|
+
if (flag == 'i') {
|
|
624
|
+
flags |= IGNORECASE;
|
|
625
|
+
}
|
|
626
|
+
else if (flag == 'm') {
|
|
627
|
+
flags |= MULTILINE;
|
|
628
|
+
}
|
|
629
|
+
else if (flag == 'x') {
|
|
630
|
+
flags |= EXTENDED;
|
|
631
|
+
}
|
|
632
|
+
else if (strlen(extra) < 9) {
|
|
633
|
+
strncat(extra, &flag, 1);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
argv[0] = pattern;
|
|
637
|
+
argv[1] = INT2FIX(flags);
|
|
638
|
+
argv[2] = rb_str_new2(extra);
|
|
639
|
+
value = rb_class_new_instance(3, argv, RegexpOfHolding);
|
|
640
|
+
*position += flags_length + 1;
|
|
641
|
+
break;
|
|
642
|
+
}
|
|
643
|
+
case 12:
|
|
644
|
+
{
|
|
645
|
+
int collection_length;
|
|
646
|
+
VALUE collection, str, oid, id, argv[2];
|
|
647
|
+
*position += 4;
|
|
648
|
+
collection_length = strlen(buffer + *position);
|
|
649
|
+
collection = rb_str_new(buffer + *position, collection_length);
|
|
650
|
+
*position += collection_length + 1;
|
|
651
|
+
|
|
652
|
+
str = rb_str_new(buffer + *position, 12);
|
|
653
|
+
oid = rb_funcall(str, rb_intern("unpack"), 1, rb_str_new2("C*"));
|
|
654
|
+
id = rb_class_new_instance(1, &oid, ObjectID);
|
|
655
|
+
*position += 12;
|
|
656
|
+
|
|
657
|
+
argv[0] = collection;
|
|
658
|
+
argv[1] = id;
|
|
659
|
+
value = rb_class_new_instance(2, argv, DBRef);
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
case 14:
|
|
663
|
+
{
|
|
664
|
+
int value_length;
|
|
665
|
+
memcpy(&value_length, buffer + *position, 4);
|
|
666
|
+
value = ID2SYM(rb_intern(buffer + *position + 4));
|
|
667
|
+
*position += value_length + 4;
|
|
668
|
+
break;
|
|
669
|
+
}
|
|
670
|
+
case 15:
|
|
671
|
+
{
|
|
672
|
+
int code_length, scope_size;
|
|
673
|
+
VALUE code, scope, argv[2];
|
|
674
|
+
*position += 8;
|
|
675
|
+
code_length = strlen(buffer + *position);
|
|
676
|
+
code = rb_str_new(buffer + *position, code_length);
|
|
677
|
+
*position += code_length + 1;
|
|
678
|
+
|
|
679
|
+
memcpy(&scope_size, buffer + *position, 4);
|
|
680
|
+
scope = elements_to_hash(buffer + *position + 4, scope_size - 5);
|
|
681
|
+
*position += scope_size;
|
|
682
|
+
|
|
683
|
+
argv[0] = code;
|
|
684
|
+
argv[1] = scope;
|
|
685
|
+
value = rb_class_new_instance(2, argv, Code);
|
|
686
|
+
break;
|
|
687
|
+
}
|
|
688
|
+
case 16:
|
|
689
|
+
{
|
|
690
|
+
int i;
|
|
691
|
+
memcpy(&i, buffer + *position, 4);
|
|
692
|
+
value = LL2NUM(i);
|
|
693
|
+
*position += 4;
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
case 17:
|
|
697
|
+
{
|
|
698
|
+
int i;
|
|
699
|
+
int j;
|
|
700
|
+
memcpy(&i, buffer + *position, 4);
|
|
701
|
+
memcpy(&j, buffer + *position + 4, 4);
|
|
702
|
+
value = rb_ary_new3(2, LL2NUM(i), LL2NUM(j));
|
|
703
|
+
*position += 8;
|
|
704
|
+
break;
|
|
705
|
+
}
|
|
706
|
+
default:
|
|
707
|
+
{
|
|
708
|
+
rb_raise(rb_eTypeError, "no c decoder for this type yet (%d)", type);
|
|
709
|
+
break;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return value;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
static VALUE elements_to_hash(const char* buffer, int max) {
|
|
716
|
+
VALUE hash = rb_class_new_instance(0, NULL, OrderedHash);
|
|
717
|
+
int position = 0;
|
|
718
|
+
while (position < max) {
|
|
719
|
+
int type = (int)buffer[position++];
|
|
720
|
+
int name_length = strlen(buffer + position);
|
|
721
|
+
VALUE name = rb_str_new(buffer + position, name_length);
|
|
722
|
+
VALUE value;
|
|
723
|
+
position += name_length + 1;
|
|
724
|
+
value = get_value(buffer, &position, type);
|
|
725
|
+
rb_funcall(hash, rb_intern("[]="), 2, name, value);
|
|
726
|
+
}
|
|
727
|
+
return hash;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
static VALUE method_deserialize(VALUE self, VALUE bson) {
|
|
731
|
+
const char* buffer = RSTRING_PTR(bson);
|
|
732
|
+
int remaining = RSTRING_LEN(bson);
|
|
733
|
+
|
|
734
|
+
// NOTE we just swallow the size and end byte here
|
|
735
|
+
buffer += 4;
|
|
736
|
+
remaining -= 5;
|
|
737
|
+
|
|
738
|
+
return elements_to_hash(buffer, remaining);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
void Init_cbson() {
|
|
742
|
+
VALUE driver, CBson;
|
|
743
|
+
Time = rb_const_get(rb_cObject, rb_intern("Time"));
|
|
744
|
+
|
|
745
|
+
driver = rb_const_get(rb_const_get(rb_const_get(rb_cObject,
|
|
746
|
+
rb_intern("XGen")),
|
|
747
|
+
rb_intern("Mongo")),
|
|
748
|
+
rb_intern("Driver"));
|
|
749
|
+
rb_require("mongo/types/binary");
|
|
750
|
+
Binary = rb_const_get(driver, rb_intern("Binary"));
|
|
751
|
+
rb_require("mongo/types/undefined");
|
|
752
|
+
Undefined = rb_const_get(driver, rb_intern("Undefined"));
|
|
753
|
+
rb_require("mongo/types/objectid");
|
|
754
|
+
ObjectID = rb_const_get(driver, rb_intern("ObjectID"));
|
|
755
|
+
rb_require("mongo/types/dbref");
|
|
756
|
+
DBRef = rb_const_get(driver, rb_intern("DBRef"));
|
|
757
|
+
rb_require("mongo/types/code");
|
|
758
|
+
Code = rb_const_get(driver, rb_intern("Code"));
|
|
759
|
+
rb_require("mongo/types/regexp_of_holding");
|
|
760
|
+
RegexpOfHolding = rb_const_get(driver, rb_intern("RegexpOfHolding"));
|
|
761
|
+
rb_require("mongo/util/ordered_hash");
|
|
762
|
+
OrderedHash = rb_const_get(rb_cObject, rb_intern("OrderedHash"));
|
|
763
|
+
|
|
764
|
+
CBson = rb_define_module("CBson");
|
|
765
|
+
rb_define_module_function(CBson, "serialize", method_serialize, 2);
|
|
766
|
+
rb_define_module_function(CBson, "deserialize", method_deserialize, 1);
|
|
767
|
+
}
|
data/mongo-extensions.gemspec
CHANGED
|
@@ -7,7 +7,7 @@ TEST_FILES = []
|
|
|
7
7
|
|
|
8
8
|
Gem::Specification.new do |s|
|
|
9
9
|
s.name = 'mongo_ext'
|
|
10
|
-
s.version = '0.
|
|
10
|
+
s.version = '0.4'
|
|
11
11
|
s.platform = Gem::Platform::RUBY
|
|
12
12
|
s.summary = 'C extensions for the MongoDB Ruby driver'
|
|
13
13
|
s.description = 'C extensions to accelerate the MondoDB Ruby driver. For more information about Mongo, see http://www.mongodb.org.'
|