mongodb-mongo_ext 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rubygems/specification'
3
+ require 'fileutils'
4
+ require 'rake'
5
+ require 'rake/testtask'
6
+ require 'rake/gempackagetask'
7
+ begin
8
+ require 'rake/contrib/rubyforgepublisher'
9
+ rescue LoadError
10
+ end
11
+ require 'rbconfig'
12
+ include Config
13
+
14
+ # NOTE: some of the tests assume Mongo is running
15
+ Rake::TestTask.new do |t|
16
+ t.test_files = FileList['tests/test*.rb']
17
+ end
18
+
19
+ desc "Generate documentation"
20
+ task :rdoc do
21
+ FileUtils.rm_rf('html')
22
+ system "rdoc --main README.rdoc --op html --inline-source --quiet README.rdoc `find lib -name '*.rb'`"
23
+ end
24
+
25
+ desc "Publish documentation to mongo.rubyforge.org"
26
+ task :publish => [:rdoc] do
27
+ # Assumes docs are in ./html
28
+ Rake::RubyForgePublisher.new(GEM, RUBYFORGE_USER).upload
29
+ end
30
+
31
+ namespace :gem do
32
+
33
+ desc "Install the gem locally"
34
+ task :install do
35
+ sh <<EOS
36
+ gem build mongo-ruby-driver.gemspec &&
37
+ sudo gem install mongo-*.gem &&
38
+ rm mongo-*.gem
39
+ EOS
40
+ end
41
+
42
+ desc "Install the optional c extensions"
43
+ task :install_extensions do
44
+ sh <<EOS
45
+ gem build mongo-extensions.gemspec &&
46
+ sudo gem install mongo_ext-*.gem &&
47
+ rm mongo_ext-*.gem
48
+ EOS
49
+ end
50
+
51
+ end
52
+
53
+ task :default => :list
54
+
55
+ task :list do
56
+ system 'rake -T'
57
+ end
data/ext/cbson/cbson.c ADDED
@@ -0,0 +1,656 @@
1
+ /*
2
+ * Copyright 2009 10gen, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ /*
18
+ * This file contains C implementations of some of the functions needed by the
19
+ * bson module. If possible, these implementations should be used to speed up
20
+ * BSON encoding and decoding.
21
+ */
22
+
23
+ #include "ruby.h"
24
+ #include "st.h"
25
+ #include "regex.h"
26
+ #include <assert.h>
27
+
28
+ #define INITIAL_BUFFER_SIZE 256
29
+
30
+ static VALUE Binary;
31
+ static VALUE Undefined;
32
+ static VALUE Time;
33
+ static VALUE ObjectID;
34
+ static VALUE DBRef;
35
+ static VALUE Code;
36
+ static VALUE RegexpOfHolding;
37
+ static VALUE OrderedHash;
38
+
39
+ typedef struct {
40
+ char* buffer;
41
+ int size;
42
+ int position;
43
+ } bson_buffer;
44
+
45
+ static char zero = 0;
46
+ static char one = 1;
47
+
48
+ static int cmp_char(const void* a, const void* b) {
49
+ return *(char*)a - *(char*)b;
50
+ }
51
+
52
+ static void write_doc(bson_buffer* buffer, VALUE hash);
53
+ static int write_element(VALUE key, VALUE value, VALUE extra);
54
+ static VALUE elements_to_hash(const char* buffer, int max);
55
+
56
+ static bson_buffer* buffer_new(void) {
57
+ bson_buffer* buffer;
58
+ buffer = ALLOC(bson_buffer);
59
+ assert(buffer);
60
+
61
+ buffer->size = INITIAL_BUFFER_SIZE;
62
+ buffer->position = 0;
63
+ buffer->buffer = ALLOC_N(char, INITIAL_BUFFER_SIZE);
64
+ assert(buffer->buffer);
65
+
66
+ return buffer;
67
+ }
68
+
69
+ static void buffer_free(bson_buffer* buffer) {
70
+ assert(buffer);
71
+ assert(buffer->buffer);
72
+
73
+ free(buffer->buffer);
74
+ free(buffer);
75
+ }
76
+
77
+ static void buffer_resize(bson_buffer* buffer, int min_length) {
78
+ int size = buffer->size;
79
+ if (size >= min_length) {
80
+ return;
81
+ }
82
+ while (size < min_length) {
83
+ size *= 2;
84
+ }
85
+ buffer->buffer = REALLOC_N(buffer->buffer, char, size);
86
+ assert(buffer->buffer);
87
+ buffer->size = size;
88
+ }
89
+
90
+ static void buffer_assure_space(bson_buffer* buffer, int size) {
91
+ if (buffer->position + size <= buffer->size) {
92
+ return;
93
+ }
94
+ buffer_resize(buffer, buffer->position + size);
95
+ }
96
+
97
+ /* returns offset for writing */
98
+ static int buffer_save_bytes(bson_buffer* buffer, int size) {
99
+ buffer_assure_space(buffer, size);
100
+ int position = buffer->position;
101
+ buffer->position += size;
102
+ return position;
103
+ }
104
+
105
+ static void buffer_write_bytes(bson_buffer* buffer, const char* bytes, int size) {
106
+ buffer_assure_space(buffer, size);
107
+
108
+ memcpy(buffer->buffer + buffer->position, bytes, size);
109
+ buffer->position += size;
110
+ }
111
+
112
+ static void write_name_and_type(bson_buffer* buffer, VALUE name, char type) {
113
+ buffer_write_bytes(buffer, &type, 1);
114
+ buffer_write_bytes(buffer, RSTRING(name)->ptr, RSTRING(name)->len);
115
+ buffer_write_bytes(buffer, &zero, 1);
116
+ }
117
+
118
+ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow_id) {
119
+ bson_buffer* buffer = (bson_buffer*)extra;
120
+
121
+ if (TYPE(key) == T_SYMBOL) {
122
+ // TODO better way to do this... ?
123
+ key = rb_str_new2(rb_id2name(SYM2ID(key)));
124
+ }
125
+
126
+ if (TYPE(key) != T_STRING) {
127
+ rb_raise(rb_eTypeError, "keys must be strings or symbols");
128
+ }
129
+
130
+ if (!allow_id && strcmp("_id", RSTRING(key)->ptr) == 0) {
131
+ return ST_CONTINUE;
132
+ }
133
+
134
+ switch(TYPE(value)) {
135
+ case T_BIGNUM:
136
+ {
137
+ write_name_and_type(buffer, key, 0x10);
138
+ VALUE as_f = rb_funcall(value, rb_intern("to_f"), 0);
139
+ int int_value = NUM2LL(as_f);
140
+ buffer_write_bytes(buffer, (char*)&int_value, 4);
141
+ break;
142
+ }
143
+ case T_FIXNUM:
144
+ {
145
+ write_name_and_type(buffer, key, 0x10);
146
+ int int_value = FIX2INT(value);
147
+ buffer_write_bytes(buffer, (char*)&int_value, 4);
148
+ break;
149
+ }
150
+ case T_TRUE:
151
+ {
152
+ write_name_and_type(buffer, key, 0x08);
153
+ buffer_write_bytes(buffer, &one, 1);
154
+ break;
155
+ }
156
+ case T_FALSE:
157
+ {
158
+ write_name_and_type(buffer, key, 0x08);
159
+ buffer_write_bytes(buffer, &zero, 1);
160
+ break;
161
+ }
162
+ case T_FLOAT:
163
+ {
164
+ write_name_and_type(buffer, key, 0x01);
165
+ double d = NUM2DBL(value);
166
+ buffer_write_bytes(buffer, (char*)&d, 8);
167
+ break;
168
+ }
169
+ case T_NIL:
170
+ {
171
+ write_name_and_type(buffer, key, 0x0A);
172
+ break;
173
+ }
174
+ case T_HASH:
175
+ {
176
+ write_name_and_type(buffer, key, 0x03);
177
+ write_doc(buffer, value);
178
+ break;
179
+ }
180
+ case T_ARRAY:
181
+ {
182
+ write_name_and_type(buffer, key, 0x04);
183
+ int start_position = buffer->position;
184
+
185
+ // save space for length
186
+ int length_location = buffer_save_bytes(buffer, 4);
187
+
188
+ int items = RARRAY_LEN(value);
189
+ VALUE* values = RARRAY_PTR(value);
190
+ int i;
191
+ for(i = 0; i < items; i++) {
192
+ char* name;
193
+ asprintf(&name, "%d", i);
194
+ VALUE key = rb_str_new2(name);
195
+ write_element(key, values[i], (VALUE)buffer);
196
+ free(name);
197
+ }
198
+
199
+ // write null byte and fill in length
200
+ buffer_write_bytes(buffer, &zero, 1);
201
+ int obj_length = buffer->position - start_position;
202
+ memcpy(buffer->buffer + length_location, &obj_length, 4);
203
+ break;
204
+ }
205
+ case T_STRING:
206
+ {
207
+ if (strcmp(rb_class2name(RBASIC(value)->klass),
208
+ "XGen::Mongo::Driver::Code") == 0) {
209
+ write_name_and_type(buffer, key, 0x0F);
210
+
211
+ int start_position = buffer->position;
212
+ int length_location = buffer_save_bytes(buffer, 4);
213
+
214
+ int length = RSTRING(value)->len + 1;
215
+ buffer_write_bytes(buffer, (char*)&length, 4);
216
+ buffer_write_bytes(buffer, RSTRING(value)->ptr, length - 1);
217
+ buffer_write_bytes(buffer, &zero, 1);
218
+ write_doc(buffer, rb_funcall(value, rb_intern("scope"), 0));
219
+
220
+ int total_length = buffer->position - start_position;
221
+ memcpy(buffer->buffer + length_location, &total_length, 4);
222
+
223
+ break;
224
+ } else {
225
+ write_name_and_type(buffer, key, 0x02);
226
+ int length = RSTRING(value)->len + 1;
227
+ buffer_write_bytes(buffer, (char*)&length, 4);
228
+ buffer_write_bytes(buffer, RSTRING(value)->ptr, length - 1);
229
+ buffer_write_bytes(buffer, &zero, 1);
230
+ break;
231
+ }
232
+ }
233
+ case T_SYMBOL:
234
+ {
235
+ write_name_and_type(buffer, key, 0x0E);
236
+ const char* str_value = rb_id2name(SYM2ID(value));
237
+ int length = strlen(str_value) + 1;
238
+ buffer_write_bytes(buffer, (char*)&length, 4);
239
+ buffer_write_bytes(buffer, str_value, length);
240
+ break;
241
+ }
242
+ case T_OBJECT:
243
+ {
244
+ // TODO there has to be a better way to do these checks...
245
+ const char* cls = rb_class2name(RBASIC(value)->klass);
246
+ if (strcmp(cls, "XGen::Mongo::Driver::Binary") == 0 ||
247
+ strcmp(cls, "ByteBuffer") == 0) {
248
+ write_name_and_type(buffer, key, 0x05);
249
+ const char subtype = strcmp(cls, "ByteBuffer") ?
250
+ (const char)FIX2INT(rb_funcall(value, rb_intern("subtype"), 0)) : 2;
251
+ VALUE string_data = rb_funcall(value, rb_intern("to_s"), 0);
252
+ int length = RSTRING(string_data)->len;
253
+ if (subtype == 2) {
254
+ const int other_length = length + 4;
255
+ buffer_write_bytes(buffer, (const char*)&other_length, 4);
256
+ buffer_write_bytes(buffer, &subtype, 1);
257
+ }
258
+ buffer_write_bytes(buffer, (const char*)&length, 4);
259
+ if (subtype != 2) {
260
+ buffer_write_bytes(buffer, &subtype, 1);
261
+ }
262
+ buffer_write_bytes(buffer, RSTRING(string_data)->ptr, length);
263
+ break;
264
+ }
265
+ if (strcmp(cls, "XGen::Mongo::Driver::ObjectID") == 0) {
266
+ write_name_and_type(buffer, key, 0x07);
267
+ VALUE as_array = rb_funcall(value, rb_intern("to_a"), 0);
268
+ int i;
269
+ for (i = 0; i < 12; i++) {
270
+ char byte = (char)FIX2INT(RARRAY(as_array)->ptr[i]);
271
+ buffer_write_bytes(buffer, &byte, 1);
272
+ }
273
+ break;
274
+ }
275
+ if (strcmp(cls, "XGen::Mongo::Driver::DBRef") == 0) {
276
+ write_name_and_type(buffer, key, 0x03);
277
+
278
+ int start_position = buffer->position;
279
+
280
+ // save space for length
281
+ int length_location = buffer_save_bytes(buffer, 4);
282
+
283
+ VALUE ns = rb_funcall(value, rb_intern("namespace"), 0);
284
+ write_element(rb_str_new2("$ref"), ns, (VALUE)buffer);
285
+ VALUE oid = rb_funcall(value, rb_intern("object_id"), 0);
286
+ write_element(rb_str_new2("$id"), oid, (VALUE)buffer);
287
+
288
+ // write null byte and fill in length
289
+ buffer_write_bytes(buffer, &zero, 1);
290
+ int obj_length = buffer->position - start_position;
291
+ memcpy(buffer->buffer + length_location, &obj_length, 4);
292
+ break;
293
+ }
294
+ if (strcmp(cls, "XGen::Mongo::Driver::Undefined") == 0) {
295
+ write_name_and_type(buffer, key, 0x06);
296
+ break;
297
+ }
298
+ }
299
+ case T_DATA:
300
+ {
301
+ // TODO again, is this really the only way to do this?
302
+ const char* cls = rb_class2name(RBASIC(value)->klass);
303
+ if (strcmp(cls, "Time") == 0) {
304
+ write_name_and_type(buffer, key, 0x09);
305
+ double t = NUM2DBL(rb_funcall(value, rb_intern("to_f"), 0));
306
+ long long time_since_epoch = (long long)(t * 1000);
307
+ buffer_write_bytes(buffer, (const char*)&time_since_epoch, 8);
308
+ break;
309
+ }
310
+ }
311
+ case T_REGEXP:
312
+ {
313
+ write_name_and_type(buffer, key, 0x0B);
314
+
315
+ int length = RREGEXP(value)->len;
316
+ char* pattern = RREGEXP(value)->str;
317
+ buffer_write_bytes(buffer, pattern, length);
318
+ buffer_write_bytes(buffer, &zero, 1);
319
+
320
+ long flags = RREGEXP(value)->ptr->options;
321
+ if (flags & RE_OPTION_IGNORECASE) {
322
+ char ignorecase = 'i';
323
+ buffer_write_bytes(buffer, &ignorecase, 1);
324
+ }
325
+ if (flags & RE_OPTION_MULTILINE) {
326
+ char multiline = 'm';
327
+ buffer_write_bytes(buffer, &multiline, 1);
328
+ }
329
+ if (flags & RE_OPTION_EXTENDED) {
330
+ char extended = 'x';
331
+ buffer_write_bytes(buffer, &extended, 1);
332
+ }
333
+
334
+ VALUE has_extra = rb_funcall(value, rb_intern("respond_to?"), 1, rb_str_new2("extra_options_str"));
335
+ if (TYPE(has_extra) == T_TRUE) {
336
+ VALUE extra = rb_funcall(value, rb_intern("extra_options_str"), 0);
337
+ int old_position = buffer->position;
338
+ buffer_write_bytes(buffer, RSTRING(extra)->ptr, RSTRING(extra)->len);
339
+ qsort(buffer->buffer + old_position, RSTRING(extra)->len, sizeof(char), cmp_char);
340
+ }
341
+ buffer_write_bytes(buffer, &zero, 1);
342
+
343
+ break;
344
+ }
345
+ default:
346
+ {
347
+ rb_raise(rb_eTypeError, "no c encoder for this type yet (%d)", TYPE(value));
348
+ break;
349
+ }
350
+ }
351
+ return ST_CONTINUE;
352
+ }
353
+
354
+ static int write_element(VALUE key, VALUE value, VALUE extra) {
355
+ return write_element_allow_id(key, value, extra, 0);
356
+ }
357
+
358
+ static void write_doc(bson_buffer* buffer, VALUE hash) {
359
+ int start_position = buffer->position;
360
+ int length_location = buffer_save_bytes(buffer, 4);
361
+
362
+ VALUE key = rb_str_new2("_id");
363
+ VALUE id = rb_hash_aref(hash, key);
364
+ if (TYPE(id) != T_NIL) {
365
+ write_element_allow_id(key, id, (VALUE)buffer, 1);
366
+ }
367
+ key = ID2SYM(rb_intern("_id"));
368
+ id = rb_hash_aref(hash, key);
369
+ if (TYPE(id) != T_NIL) {
370
+ write_element_allow_id(key, id, (VALUE)buffer, 1);
371
+ }
372
+
373
+
374
+ // we have to check for an OrderedHash and handle that specially
375
+ if (strcmp(rb_class2name(RBASIC(hash)->klass), "OrderedHash") == 0) {
376
+ VALUE keys = rb_funcall(hash, rb_intern("keys"), 0);
377
+ int i;
378
+ for(i = 0; i < RARRAY(keys)->len; i++) {
379
+ VALUE key = RARRAY(keys)->ptr[i];
380
+ VALUE value = rb_hash_aref(hash, key);
381
+ write_element(key, value, (VALUE)buffer);
382
+ }
383
+ } else {
384
+ rb_hash_foreach(hash, write_element, (VALUE)buffer);
385
+ }
386
+
387
+ // write null byte and fill in length
388
+ buffer_write_bytes(buffer, &zero, 1);
389
+ int length = buffer->position - start_position;
390
+ memcpy(buffer->buffer + length_location, &length, 4);
391
+ }
392
+
393
+ static VALUE method_serialize(VALUE self, VALUE doc) {
394
+ bson_buffer* buffer = buffer_new();
395
+ assert(buffer);
396
+
397
+ write_doc(buffer, doc);
398
+
399
+ VALUE result = rb_str_new(buffer->buffer, buffer->position);
400
+ buffer_free(buffer);
401
+ return result;
402
+ }
403
+
404
+ static VALUE get_value(const char* buffer, int* position, int type) {
405
+ VALUE value;
406
+ switch (type) {
407
+ case 1:
408
+ {
409
+ double d;
410
+ memcpy(&d, buffer + *position, 8);
411
+ value = rb_float_new(d);
412
+ *position += 8;
413
+ break;
414
+ }
415
+ case 2:
416
+ case 13:
417
+ {
418
+ *position += 4;
419
+ int value_length = strlen(buffer + *position);
420
+ value = rb_str_new(buffer+ *position, value_length);
421
+ *position += value_length + 1;
422
+ break;
423
+ }
424
+ case 3:
425
+ {
426
+ int size;
427
+ memcpy(&size, buffer + *position, 4);
428
+ if (strcmp(buffer + *position + 5, "$ref") == 0) { // DBRef
429
+ int offset = *position + 14;
430
+ VALUE argv[2];
431
+ int collection_length = strlen(buffer + offset);
432
+ argv[0] = rb_str_new(buffer + offset, collection_length);
433
+ offset += collection_length + 1;
434
+ char id_type = buffer[offset];
435
+ offset += 5;
436
+ argv[1] = get_value(buffer, &offset, (int)id_type);
437
+ value = rb_class_new_instance(2, argv, DBRef);
438
+ } else {
439
+ value = elements_to_hash(buffer + *position + 4, size - 5);
440
+ }
441
+ *position += size;
442
+ break;
443
+ }
444
+ case 4:
445
+ {
446
+ int size;
447
+ memcpy(&size, buffer + *position, 4);
448
+ int end = *position + size - 1;
449
+ *position += 4;
450
+
451
+ value = rb_ary_new();
452
+ while (*position < end) {
453
+ int type = (int)buffer[(*position)++];
454
+ int key_size = strlen(buffer + *position);
455
+ *position += key_size + 1; // just skip the key, they're in order.
456
+ VALUE to_append = get_value(buffer, position, type);
457
+ rb_ary_push(value, to_append);
458
+ }
459
+ (*position)++;
460
+ break;
461
+ }
462
+ case 5:
463
+ {
464
+ int length;
465
+ memcpy(&length, buffer + *position, 4);
466
+ int subtype = (unsigned char)buffer[*position + 4];
467
+ VALUE data;
468
+ if (subtype == 2) {
469
+ data = rb_str_new(buffer + *position + 9, length - 4);
470
+ } else {
471
+ data = rb_str_new(buffer + *position + 5, length);
472
+ }
473
+ VALUE st = INT2FIX(subtype);
474
+ VALUE argv[2] = {data, st};
475
+ value = rb_class_new_instance(2, argv, Binary);
476
+ *position += length + 5;
477
+ break;
478
+ }
479
+ case 6:
480
+ {
481
+ value = rb_class_new_instance(0, NULL, Undefined);
482
+ break;
483
+ }
484
+ case 7:
485
+ {
486
+ VALUE str = rb_str_new(buffer + *position, 12);
487
+ VALUE oid = rb_funcall(str, rb_intern("unpack"), 1, rb_str_new2("C*"));
488
+ value = rb_class_new_instance(1, &oid, ObjectID);
489
+ *position += 12;
490
+ break;
491
+ }
492
+ case 8:
493
+ {
494
+ value = buffer[(*position)++] ? Qtrue : Qfalse;
495
+ break;
496
+ }
497
+ case 9:
498
+ {
499
+ long long millis;
500
+ memcpy(&millis, buffer + *position, 8);
501
+ VALUE seconds = INT2NUM(millis / 1000);
502
+ VALUE microseconds = INT2NUM((millis % 1000) * 1000);
503
+
504
+ value = rb_funcall(Time, rb_intern("at"), 2, seconds, microseconds);
505
+ *position += 8;
506
+ break;
507
+ }
508
+ case 10:
509
+ {
510
+ value = Qnil;
511
+ break;
512
+ }
513
+ case 11:
514
+ {
515
+ int pattern_length = strlen(buffer + *position);
516
+ VALUE pattern = rb_str_new(buffer + *position, pattern_length);
517
+ *position += pattern_length + 1;
518
+
519
+ int flags_length = strlen(buffer + *position);
520
+ int i = 0;
521
+
522
+ int flags = 0;
523
+ char extra[10];
524
+ extra[0] = 0;
525
+ for (i = 0; i < flags_length; i++) {
526
+ char flag = buffer[*position + i];
527
+ if (flag == 'i') {
528
+ flags |= RE_OPTION_IGNORECASE;
529
+ }
530
+ else if (flag == 'm') {
531
+ flags |= RE_OPTION_MULTILINE;
532
+ }
533
+ else if (flag == 'x') {
534
+ flags |= RE_OPTION_EXTENDED;
535
+ }
536
+ else if (strlen(extra) < 9) {
537
+ strncat(extra, &flag, 1);
538
+ }
539
+ }
540
+ VALUE argv[3] = {
541
+ pattern,
542
+ INT2FIX(flags),
543
+ rb_str_new2(extra)
544
+ };
545
+ value = rb_class_new_instance(3, argv, RegexpOfHolding);
546
+ *position += flags_length + 1;
547
+ break;
548
+ }
549
+ case 12:
550
+ {
551
+ *position += 4;
552
+ int collection_length = strlen(buffer + *position);
553
+ VALUE collection = rb_str_new(buffer + *position, collection_length);
554
+ *position += collection_length + 1;
555
+
556
+ VALUE str = rb_str_new(buffer + *position, 12);
557
+ VALUE oid = rb_funcall(str, rb_intern("unpack"), 1, rb_str_new2("C*"));
558
+ VALUE id = rb_class_new_instance(1, &oid, ObjectID);
559
+ *position += 12;
560
+
561
+ VALUE argv[2] = {collection, id};
562
+ value = rb_class_new_instance(2, argv, DBRef);
563
+ break;
564
+ }
565
+ case 14:
566
+ {
567
+ int value_length;
568
+ memcpy(&value_length, buffer + *position, 4);
569
+ value = ID2SYM(rb_intern(buffer + *position + 4));
570
+ *position += value_length + 4;
571
+ break;
572
+ }
573
+ case 15:
574
+ {
575
+ *position += 8;
576
+ int code_length = strlen(buffer + *position);
577
+ VALUE code = rb_str_new(buffer + *position, code_length);
578
+ *position += code_length + 1;
579
+
580
+ int scope_size;
581
+ memcpy(&scope_size, buffer + *position, 4);
582
+ VALUE scope = elements_to_hash(buffer + *position + 4, scope_size - 5);
583
+ *position += scope_size;
584
+
585
+ VALUE argv[2] = {code, scope};
586
+ value = rb_class_new_instance(2, argv, Code);
587
+ break;
588
+ }
589
+ case 16:
590
+ {
591
+ int i;
592
+ memcpy(&i, buffer + *position, 4);
593
+ value = LL2NUM(i);
594
+ *position += 4;
595
+ break;
596
+ }
597
+ default:
598
+ {
599
+ rb_raise(rb_eTypeError, "no c decoder for this type yet (%d)", type);
600
+ break;
601
+ }
602
+ }
603
+ return value;
604
+ }
605
+
606
+ static VALUE elements_to_hash(const char* buffer, int max) {
607
+ VALUE hash = rb_class_new_instance(0, NULL, OrderedHash);
608
+ int position = 0;
609
+ while (position < max) {
610
+ int type = (int)buffer[position++];
611
+ int name_length = strlen(buffer + position);
612
+ VALUE name = rb_str_new(buffer + position, name_length);
613
+ position += name_length + 1;
614
+ VALUE value = get_value(buffer, &position, type);
615
+ rb_funcall(hash, rb_intern("[]="), 2, name, value);
616
+ }
617
+ return hash;
618
+ }
619
+
620
+ static VALUE method_deserialize(VALUE self, VALUE bson) {
621
+ const char* buffer = RSTRING(bson)->ptr;
622
+ int remaining = RSTRING(bson)->len;
623
+
624
+ // NOTE we just swallow the size and end byte here
625
+ buffer += 4;
626
+ remaining -= 5;
627
+
628
+ return elements_to_hash(buffer, remaining);
629
+ }
630
+
631
+ void Init_cbson() {
632
+ Time = rb_const_get(rb_cObject, rb_intern("Time"));
633
+
634
+ VALUE driver = rb_const_get(rb_const_get(rb_const_get(rb_cObject,
635
+ rb_intern("XGen")),
636
+ rb_intern("Mongo")),
637
+ rb_intern("Driver"));
638
+ rb_require("mongo/types/binary");
639
+ Binary = rb_const_get(driver, rb_intern("Binary"));
640
+ rb_require("mongo/types/undefined");
641
+ Undefined = rb_const_get(driver, rb_intern("Undefined"));
642
+ rb_require("mongo/types/objectid");
643
+ ObjectID = rb_const_get(driver, rb_intern("ObjectID"));
644
+ rb_require("mongo/types/dbref");
645
+ DBRef = rb_const_get(driver, rb_intern("DBRef"));
646
+ rb_require("mongo/types/code");
647
+ Code = rb_const_get(driver, rb_intern("Code"));
648
+ rb_require("mongo/types/regexp_of_holding");
649
+ RegexpOfHolding = rb_const_get(driver, rb_intern("RegexpOfHolding"));
650
+ rb_require("mongo/util/ordered_hash");
651
+ OrderedHash = rb_const_get(rb_cObject, rb_intern("OrderedHash"));
652
+
653
+ VALUE CBson = rb_define_module("CBson");
654
+ rb_define_module_function(CBson, "serialize", method_serialize, 1);
655
+ rb_define_module_function(CBson, "deserialize", method_deserialize, 1);
656
+ }
@@ -0,0 +1,4 @@
1
+ require 'mkmf'
2
+
3
+ dir_config('cbson')
4
+ create_makefile('mongo_ext/cbson')
@@ -0,0 +1,26 @@
1
+ # We need to list all of the included files because we aren't allowed to use
2
+ # Dir[...] in the github sandbox.
3
+ PACKAGE_FILES = ['Rakefile', 'mongo-extensions.gemspec',
4
+ 'ext/cbson/cbson.c',
5
+ 'ext/cbson/extconf.rb']
6
+ TEST_FILES = []
7
+
8
+ Gem::Specification.new do |s|
9
+ s.name = 'mongo_ext'
10
+ s.version = '0.1.1'
11
+ s.platform = Gem::Platform::RUBY
12
+ s.summary = 'C extensions for the MongoDB Ruby driver'
13
+ s.description = 'C extensions to accelerate the MondoDB Ruby driver. For more information about Mongo, see http://www.mongodb.org.'
14
+
15
+ s.require_paths = ['ext']
16
+
17
+ s.files = PACKAGE_FILES
18
+ s.test_files = TEST_FILES
19
+
20
+ s.has_rdoc = false
21
+ s.extensions << 'ext/cbson/extconf.rb'
22
+
23
+ s.author = 'Mike Dirolf'
24
+ s.email = 'mongodb-dev@googlegroups.com'
25
+ s.homepage = 'http://www.mongodb.org'
26
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongodb-mongo_ext
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Mike Dirolf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-25 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: C extensions to accelerate the MondoDB Ruby driver. For more information about Mongo, see http://www.mongodb.org.
17
+ email: mongodb-dev@googlegroups.com
18
+ executables: []
19
+
20
+ extensions:
21
+ - ext/cbson/extconf.rb
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - Rakefile
26
+ - mongo-extensions.gemspec
27
+ - ext/cbson/cbson.c
28
+ - ext/cbson/extconf.rb
29
+ has_rdoc: false
30
+ homepage: http://www.mongodb.org
31
+ post_install_message:
32
+ rdoc_options: []
33
+
34
+ require_paths:
35
+ - ext
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: "0"
41
+ version:
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ requirements: []
49
+
50
+ rubyforge_project:
51
+ rubygems_version: 1.2.0
52
+ signing_key:
53
+ specification_version: 2
54
+ summary: C extensions for the MongoDB Ruby driver
55
+ test_files: []
56
+