animehunter-mongo_ext 0.3 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
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
+ }
@@ -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.3'
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.'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: animehunter-mongo_ext
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.3"
4
+ version: "0.4"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Dirolf