mongodb-mongo_ext 0.1.1
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/Rakefile +57 -0
- data/ext/cbson/cbson.c +656 -0
- data/ext/cbson/extconf.rb +4 -0
- data/mongo-extensions.gemspec +26 -0
- metadata +56 -0
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,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
|
+
|