mongo_ext 0.18 → 0.18.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 CHANGED
@@ -10,19 +10,37 @@ rescue LoadError
10
10
  end
11
11
  require 'rbconfig'
12
12
  include Config
13
+ ENV['TEST_MODE'] = 'TRUE'
13
14
 
14
15
  gem_command = "gem"
15
16
  gem_command = "gem1.9" if $0.match(/1\.9$/) # use gem1.9 if we used rake1.9
16
17
 
17
- # NOTE: the functional tests assume MongoDB is running.
18
18
  desc "Test the MongoDB Ruby driver."
19
19
  task :test do
20
- Rake::Task['test:unit'].invoke
21
- Rake::Task['test:functional'].invoke
22
- Rake::Task['test:pooled_threading'].invoke
20
+ puts "\nThis option has changed."
21
+ puts "\nTo test the driver with the c-extensions:\nrake test:c\n"
22
+ puts "To test the pure ruby driver: \nrake test:ruby"
23
23
  end
24
24
 
25
- namespace :test do
25
+ namespace :test do
26
+
27
+ desc "Test the driver with the c extension enabled."
28
+ task :c do
29
+ ENV['C_EXT'] = 'TRUE'
30
+ Rake::Task['test:unit'].invoke
31
+ Rake::Task['test:functional'].invoke
32
+ Rake::Task['test:pooled_threading'].invoke
33
+ ENV['C_EXT'] = nil
34
+ end
35
+
36
+ desc "Test the driver using pure ruby (no c extension)"
37
+ task :ruby do
38
+ ENV['C_EXT'] = nil
39
+ Rake::Task['test:unit'].invoke
40
+ Rake::Task['test:functional'].invoke
41
+ Rake::Task['test:pooled_threading'].invoke
42
+ end
43
+
26
44
  Rake::TestTask.new(:unit) do |t|
27
45
  t.test_files = FileList['test/unit/*_test.rb']
28
46
  t.verbose = true
@@ -79,7 +97,7 @@ namespace :gem do
79
97
  task :install do
80
98
  sh <<EOS
81
99
  #{gem_command} build mongo-ruby-driver.gemspec &&
82
- sudo #{gem_command} install mongo-*.gem &&
100
+ #{gem_command} install mongo-*.gem &&
83
101
  rm mongo-*.gem
84
102
  EOS
85
103
  end
@@ -88,7 +106,7 @@ EOS
88
106
  task :install_extensions do
89
107
  sh <<EOS
90
108
  #{gem_command} build mongo-extensions.gemspec &&
91
- sudo #{gem_command} install mongo_ext-*.gem &&
109
+ #{gem_command} install mongo_ext-*.gem &&
92
110
  rm mongo_ext-*.gem
93
111
  EOS
94
112
  end
@@ -0,0 +1,135 @@
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
+ #include <stdlib.h>
18
+ #include <string.h>
19
+
20
+ #include "buffer.h"
21
+
22
+ #define INITIAL_BUFFER_SIZE 256
23
+
24
+ struct buffer {
25
+ char* buffer;
26
+ int size;
27
+ int position;
28
+ };
29
+
30
+ /* Allocate and return a new buffer.
31
+ * Return NULL on allocation failure. */
32
+ buffer_t buffer_new(void) {
33
+ buffer_t buffer;
34
+ buffer = (buffer_t)malloc(sizeof(struct buffer));
35
+ if (buffer == NULL) {
36
+ return NULL;
37
+ }
38
+
39
+ buffer->size = INITIAL_BUFFER_SIZE;
40
+ buffer->position = 0;
41
+ buffer->buffer = (char*)malloc(sizeof(char) * INITIAL_BUFFER_SIZE);
42
+ if (buffer->buffer == NULL) {
43
+ free(buffer);
44
+ return NULL;
45
+ }
46
+
47
+ return buffer;
48
+ }
49
+
50
+ /* Free the memory allocated for `buffer`.
51
+ * Return non-zero on failure. */
52
+ int buffer_free(buffer_t buffer) {
53
+ if (buffer == NULL) {
54
+ return 1;
55
+ }
56
+ free(buffer->buffer);
57
+ free(buffer);
58
+ return 0;
59
+ }
60
+
61
+ /* Grow `buffer` to at least `min_length`.
62
+ * Return non-zero on allocation failure. */
63
+ static int buffer_grow(buffer_t buffer, int min_length) {
64
+ int size = buffer->size;
65
+ char* old_buffer = buffer->buffer;
66
+ if (size >= min_length) {
67
+ return 0;
68
+ }
69
+ while (size < min_length) {
70
+ size *= 2;
71
+ }
72
+ buffer->buffer = (char*)realloc(buffer->buffer, sizeof(char) * size);
73
+ if (buffer->buffer == NULL) {
74
+ free(old_buffer);
75
+ free(buffer);
76
+ return 1;
77
+ }
78
+ buffer->size = size;
79
+ return 0;
80
+ }
81
+
82
+ /* Assure that `buffer` has at least `size` free bytes (and grow if needed).
83
+ * Return non-zero on allocation failure. */
84
+ static int buffer_assure_space(buffer_t buffer, int size) {
85
+ if (buffer->position + size <= buffer->size) {
86
+ return 0;
87
+ }
88
+ return buffer_grow(buffer, buffer->position + size);
89
+ }
90
+
91
+ /* Save `size` bytes from the current position in `buffer` (and grow if needed).
92
+ * Return offset for writing, or -1 on allocation failure. */
93
+ buffer_position buffer_save_space(buffer_t buffer, int size) {
94
+ int position = buffer->position;
95
+ if (buffer_assure_space(buffer, size) != 0) {
96
+ return -1;
97
+ }
98
+ buffer->position += size;
99
+ return position;
100
+ }
101
+
102
+ /* Write `size` bytes from `data` to `buffer` (and grow if needed).
103
+ * Return non-zero on allocation failure. */
104
+ int buffer_write(buffer_t buffer, const char* data, int size) {
105
+ if (buffer_assure_space(buffer, size) != 0) {
106
+ return 1;
107
+ }
108
+
109
+ memcpy(buffer->buffer + buffer->position, data, size);
110
+ buffer->position += size;
111
+ return 0;
112
+ }
113
+
114
+ /* Write `size` bytes from `data` to `buffer` at position `position`.
115
+ * Does not change the internal position of `buffer`.
116
+ * Return non-zero if buffer isn't large enough for write. */
117
+ int buffer_write_at_position(buffer_t buffer, buffer_position position,
118
+ const char* data, int size) {
119
+ if (position + size > buffer->size) {
120
+ buffer_free(buffer);
121
+ return 1;
122
+ }
123
+
124
+ memcpy(buffer->buffer + position, data, size);
125
+ return 0;
126
+ }
127
+
128
+
129
+ int buffer_get_position(buffer_t buffer) {
130
+ return buffer->position;
131
+ }
132
+
133
+ char* buffer_get_buffer(buffer_t buffer) {
134
+ return buffer->buffer;
135
+ }
@@ -0,0 +1,55 @@
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
+ #ifndef BUFFER_H
18
+ #define BUFFER_H
19
+
20
+ /* Note: if any of these functions return a failure condition then the buffer
21
+ * has already been freed. */
22
+
23
+ /* A buffer */
24
+ typedef struct buffer* buffer_t;
25
+ /* A position in the buffer */
26
+ typedef int buffer_position;
27
+
28
+ /* Allocate and return a new buffer.
29
+ * Return NULL on allocation failure. */
30
+ buffer_t buffer_new(void);
31
+
32
+ /* Free the memory allocated for `buffer`.
33
+ * Return non-zero on failure. */
34
+ int buffer_free(buffer_t buffer);
35
+
36
+ /* Save `size` bytes from the current position in `buffer` (and grow if needed).
37
+ * Return offset for writing, or -1 on allocation failure. */
38
+ buffer_position buffer_save_space(buffer_t buffer, int size);
39
+
40
+ /* Write `size` bytes from `data` to `buffer` (and grow if needed).
41
+ * Return non-zero on allocation failure. */
42
+ int buffer_write(buffer_t buffer, const char* data, int size);
43
+
44
+ /* Write `size` bytes from `data` to `buffer` at position `position`.
45
+ * Does not change the internal position of `buffer`.
46
+ * Return non-zero if buffer isn't large enough for write. */
47
+ int buffer_write_at_position(buffer_t buffer, buffer_position position, const char* data, int size);
48
+
49
+ /* Getters for the internals of a buffer_t.
50
+ * Should try to avoid using these as much as possible
51
+ * since they break the abstraction. */
52
+ buffer_position buffer_get_position(buffer_t buffer);
53
+ char* buffer_get_buffer(buffer_t buffer);
54
+
55
+ #endif
@@ -36,12 +36,23 @@
36
36
  #include "regex.h"
37
37
  #endif
38
38
 
39
- #include <assert.h>
39
+ #include <string.h>
40
40
  #include <math.h>
41
41
  #include <unistd.h>
42
42
  #include <time.h>
43
43
 
44
- #define INITIAL_BUFFER_SIZE 256
44
+ #include "version.h"
45
+ #include "buffer.h"
46
+ #include "encoding_helpers.h"
47
+
48
+ #define SAFE_WRITE(buffer, data, size) \
49
+ if (buffer_write((buffer), (data), (size)) != 0) \
50
+ rb_raise(rb_eNoMemError, "failed to allocate memory in buffer.c")
51
+
52
+ #define SAFE_WRITE_AT_POS(buffer, position, data, size) \
53
+ if (buffer_write_at_position((buffer), (position), (data), (size)) != 0) \
54
+ rb_raise(rb_eNoMemError, "failed to allocate memory in buffer.c")
55
+
45
56
  #define MAX_HOSTNAME_LENGTH 256
46
57
 
47
58
  static VALUE Binary;
@@ -52,13 +63,28 @@ static VALUE Code;
52
63
  static VALUE RegexpOfHolding;
53
64
  static VALUE OrderedHash;
54
65
  static VALUE InvalidName;
66
+ static VALUE InvalidStringEncoding;
55
67
  static VALUE DigestMD5;
56
68
 
57
69
  #if HAVE_RUBY_ENCODING_H
58
70
  #include "ruby/encoding.h"
59
71
  #define STR_NEW(p,n) rb_enc_str_new((p), (n), rb_utf8_encoding())
72
+ /* MUST call TO_UTF8 before calling write_utf8. */
73
+ #define TO_UTF8(string) rb_str_export_to_enc((string), rb_utf8_encoding())
74
+ static void write_utf8(buffer_t buffer, VALUE string) {
75
+ SAFE_WRITE(buffer, RSTRING_PTR(string), RSTRING_LEN(string));
76
+ }
60
77
  #else
61
78
  #define STR_NEW(p,n) rb_str_new((p), (n))
79
+ /* MUST call TO_UTF8 before calling write_utf8. */
80
+ #define TO_UTF8(string) (string)
81
+ static void write_utf8(buffer_t buffer, VALUE string) {
82
+ if (!is_legal_utf8_string(RSTRING_PTR(string), RSTRING_LEN(string))) {
83
+ buffer_free(buffer);
84
+ rb_raise(InvalidStringEncoding, "String not valid UTF-8");
85
+ }
86
+ SAFE_WRITE(buffer, RSTRING_PTR(string), RSTRING_LEN(string));
87
+ }
62
88
  #endif
63
89
 
64
90
  // this sucks. but for some reason these moved around between 1.8 and 1.9
@@ -91,12 +117,6 @@ static VALUE DigestMD5;
91
117
  #define RREGEXP_SRC_LEN(r) RREGEXP(r)->len
92
118
  #endif
93
119
 
94
- typedef struct {
95
- char* buffer;
96
- int size;
97
- int position;
98
- } bson_buffer;
99
-
100
120
  static char zero = 0;
101
121
  static char one = 1;
102
122
 
@@ -104,78 +124,23 @@ static int cmp_char(const void* a, const void* b) {
104
124
  return *(char*)a - *(char*)b;
105
125
  }
106
126
 
107
- static void write_doc(bson_buffer* buffer, VALUE hash, VALUE check_keys);
127
+ static void write_doc(buffer_t buffer, VALUE hash, VALUE check_keys);
108
128
  static int write_element(VALUE key, VALUE value, VALUE extra);
109
129
  static VALUE elements_to_hash(const char* buffer, int max);
110
130
 
111
- static bson_buffer* buffer_new(void) {
112
- bson_buffer* buffer;
113
- buffer = ALLOC(bson_buffer);
114
- assert(buffer);
115
-
116
- buffer->size = INITIAL_BUFFER_SIZE;
117
- buffer->position = 0;
118
- buffer->buffer = ALLOC_N(char, INITIAL_BUFFER_SIZE);
119
- assert(buffer->buffer);
120
-
121
- return buffer;
122
- }
123
-
124
- static void buffer_free(bson_buffer* buffer) {
125
- assert(buffer);
126
- assert(buffer->buffer);
127
-
128
- free(buffer->buffer);
129
- free(buffer);
130
- }
131
-
132
- static void buffer_resize(bson_buffer* buffer, int min_length) {
133
- int size = buffer->size;
134
- if (size >= min_length) {
135
- return;
136
- }
137
- while (size < min_length) {
138
- size *= 2;
139
- }
140
- buffer->buffer = REALLOC_N(buffer->buffer, char, size);
141
- assert(buffer->buffer);
142
- buffer->size = size;
143
- }
144
-
145
- static void buffer_assure_space(bson_buffer* buffer, int size) {
146
- if (buffer->position + size <= buffer->size) {
147
- return;
148
- }
149
- buffer_resize(buffer, buffer->position + size);
150
- }
151
-
152
- /* returns offset for writing */
153
- static int buffer_save_bytes(bson_buffer* buffer, int size) {
154
- int position = buffer->position;
155
- buffer_assure_space(buffer, size);
156
- buffer->position += size;
157
- return position;
158
- }
159
-
160
- static void buffer_write_bytes(bson_buffer* buffer, const char* bytes, int size) {
161
- buffer_assure_space(buffer, size);
162
-
163
- memcpy(buffer->buffer + buffer->position, bytes, size);
164
- buffer->position += size;
165
- }
166
-
167
- static VALUE pack_extra(bson_buffer* buffer, VALUE check_keys) {
131
+ static VALUE pack_extra(buffer_t buffer, VALUE check_keys) {
168
132
  return rb_ary_new3(2, LL2NUM((long long)buffer), check_keys);
169
133
  }
170
134
 
171
- static void write_name_and_type(bson_buffer* buffer, VALUE name, char type) {
172
- buffer_write_bytes(buffer, &type, 1);
173
- buffer_write_bytes(buffer, RSTRING_PTR(name), RSTRING_LEN(name));
174
- buffer_write_bytes(buffer, &zero, 1);
135
+ static void write_name_and_type(buffer_t buffer, VALUE name, char type) {
136
+ SAFE_WRITE(buffer, &type, 1);
137
+ name = TO_UTF8(name);
138
+ write_utf8(buffer, name);
139
+ SAFE_WRITE(buffer, &zero, 1);
175
140
  }
176
141
 
177
142
  static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow_id) {
178
- bson_buffer* buffer = (bson_buffer*)NUM2LL(rb_ary_entry(extra, 0));
143
+ buffer_t buffer = (buffer_t)NUM2LL(rb_ary_entry(extra, 0));
179
144
  VALUE check_keys = rb_ary_entry(extra, 1);
180
145
 
181
146
  if (TYPE(key) == T_SYMBOL) {
@@ -184,6 +149,7 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
184
149
  }
185
150
 
186
151
  if (TYPE(key) != T_STRING) {
152
+ buffer_free(buffer);
187
153
  rb_raise(rb_eTypeError, "keys must be strings or symbols");
188
154
  }
189
155
 
@@ -194,10 +160,12 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
194
160
  if (check_keys == Qtrue) {
195
161
  int i;
196
162
  if (RSTRING_LEN(key) > 0 && RSTRING_PTR(key)[0] == '$') {
163
+ buffer_free(buffer);
197
164
  rb_raise(InvalidName, "key must not start with '$'");
198
165
  }
199
166
  for (i = 0; i < RSTRING_LEN(key); i++) {
200
167
  if (RSTRING_PTR(key)[i] == '.') {
168
+ buffer_free(buffer);
201
169
  rb_raise(InvalidName, "key must not contain '.'");
202
170
  }
203
171
  }
@@ -209,6 +177,7 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
209
177
  {
210
178
  if (rb_funcall(value, rb_intern(">"), 1, LL2NUM(9223372036854775807LL)) == Qtrue ||
211
179
  rb_funcall(value, rb_intern("<"), 1, LL2NUM(-9223372036854775808LL)) == Qtrue) {
180
+ buffer_free(buffer);
212
181
  rb_raise(rb_eRangeError, "MongoDB can only handle 8-byte ints");
213
182
  }
214
183
  if (rb_funcall(value, rb_intern(">"), 1, INT2NUM(2147483647L)) == Qtrue ||
@@ -216,32 +185,32 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
216
185
  long long ll_value;
217
186
  write_name_and_type(buffer, key, 0x12);
218
187
  ll_value = NUM2LL(value);
219
- buffer_write_bytes(buffer, (char*)&ll_value, 8);
188
+ SAFE_WRITE(buffer, (char*)&ll_value, 8);
220
189
  } else {
221
190
  int int_value;
222
191
  write_name_and_type(buffer, key, 0x10);
223
192
  int_value = NUM2LL(value);
224
- buffer_write_bytes(buffer, (char*)&int_value, 4);
193
+ SAFE_WRITE(buffer, (char*)&int_value, 4);
225
194
  }
226
195
  break;
227
196
  }
228
197
  case T_TRUE:
229
198
  {
230
199
  write_name_and_type(buffer, key, 0x08);
231
- buffer_write_bytes(buffer, &one, 1);
200
+ SAFE_WRITE(buffer, &one, 1);
232
201
  break;
233
202
  }
234
203
  case T_FALSE:
235
204
  {
236
205
  write_name_and_type(buffer, key, 0x08);
237
- buffer_write_bytes(buffer, &zero, 1);
206
+ SAFE_WRITE(buffer, &zero, 1);
238
207
  break;
239
208
  }
240
209
  case T_FLOAT:
241
210
  {
242
211
  double d = NUM2DBL(value);
243
212
  write_name_and_type(buffer, key, 0x01);
244
- buffer_write_bytes(buffer, (char*)&d, 8);
213
+ SAFE_WRITE(buffer, (char*)&d, 8);
245
214
  break;
246
215
  }
247
216
  case T_NIL:
@@ -257,14 +226,18 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
257
226
  }
258
227
  case T_ARRAY:
259
228
  {
260
- int start_position, length_location, items, i, obj_length;
229
+ buffer_position length_location, start_position, obj_length;
230
+ int items, i;
261
231
  VALUE* values;
262
232
 
263
233
  write_name_and_type(buffer, key, 0x04);
264
- start_position = buffer->position;
234
+ start_position = buffer_get_position(buffer);
265
235
 
266
236
  // save space for length
267
- length_location = buffer_save_bytes(buffer, 4);
237
+ length_location = buffer_save_space(buffer, 4);
238
+ if (length_location == -1) {
239
+ rb_raise(rb_eNoMemError, "failed to allocate memory in buffer.c");
240
+ }
268
241
 
269
242
  items = RARRAY_LEN(value);
270
243
  values = RARRAY_PTR(value);
@@ -278,37 +251,42 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
278
251
  }
279
252
 
280
253
  // write null byte and fill in length
281
- buffer_write_bytes(buffer, &zero, 1);
282
- obj_length = buffer->position - start_position;
283
- memcpy(buffer->buffer + length_location, &obj_length, 4);
254
+ SAFE_WRITE(buffer, &zero, 1);
255
+ obj_length = buffer_get_position(buffer) - start_position;
256
+ SAFE_WRITE_AT_POS(buffer, length_location, (const char*)&obj_length, 4);
284
257
  break;
285
258
  }
286
259
  case T_STRING:
287
260
  {
288
261
  if (strcmp(rb_class2name(RBASIC(value)->klass),
289
262
  "Mongo::Code") == 0) {
290
- int start_position, length_location, length, total_length;
263
+ buffer_position length_location, start_position, total_length;
264
+ int length;
291
265
  write_name_and_type(buffer, key, 0x0F);
292
266
 
293
- start_position = buffer->position;
294
- length_location = buffer_save_bytes(buffer, 4);
267
+ start_position = buffer_get_position(buffer);
268
+ length_location = buffer_save_space(buffer, 4);
269
+ if (length_location == -1) {
270
+ rb_raise(rb_eNoMemError, "failed to allocate memory in buffer.c");
271
+ }
295
272
 
296
273
  length = RSTRING_LEN(value) + 1;
297
- buffer_write_bytes(buffer, (char*)&length, 4);
298
- buffer_write_bytes(buffer, RSTRING_PTR(value), length - 1);
299
- buffer_write_bytes(buffer, &zero, 1);
274
+ SAFE_WRITE(buffer, (char*)&length, 4);
275
+ SAFE_WRITE(buffer, RSTRING_PTR(value), length - 1);
276
+ SAFE_WRITE(buffer, &zero, 1);
300
277
  write_doc(buffer, rb_funcall(value, rb_intern("scope"), 0), Qfalse);
301
278
 
302
- total_length = buffer->position - start_position;
303
- memcpy(buffer->buffer + length_location, &total_length, 4);
304
-
279
+ total_length = buffer_get_position(buffer) - start_position;
280
+ SAFE_WRITE_AT_POS(buffer, length_location, (const char*)&total_length, 4);
305
281
  break;
306
282
  } else {
307
- int length = RSTRING_LEN(value) + 1;
283
+ int length;
308
284
  write_name_and_type(buffer, key, 0x02);
309
- buffer_write_bytes(buffer, (char*)&length, 4);
310
- buffer_write_bytes(buffer, RSTRING_PTR(value), length - 1);
311
- buffer_write_bytes(buffer, &zero, 1);
285
+ value = TO_UTF8(value);
286
+ length = RSTRING_LEN(value) + 1;
287
+ SAFE_WRITE(buffer, (char*)&length, 4);
288
+ write_utf8(buffer, value);
289
+ SAFE_WRITE(buffer, &zero, 1);
312
290
  break;
313
291
  }
314
292
  }
@@ -317,8 +295,8 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
317
295
  const char* str_value = rb_id2name(SYM2ID(value));
318
296
  int length = strlen(str_value) + 1;
319
297
  write_name_and_type(buffer, key, 0x0E);
320
- buffer_write_bytes(buffer, (char*)&length, 4);
321
- buffer_write_bytes(buffer, str_value, length);
298
+ SAFE_WRITE(buffer, (char*)&length, 4);
299
+ SAFE_WRITE(buffer, str_value, length);
322
300
  break;
323
301
  }
324
302
  case T_OBJECT:
@@ -334,14 +312,14 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
334
312
  write_name_and_type(buffer, key, 0x05);
335
313
  if (subtype == 2) {
336
314
  const int other_length = length + 4;
337
- buffer_write_bytes(buffer, (const char*)&other_length, 4);
338
- buffer_write_bytes(buffer, &subtype, 1);
315
+ SAFE_WRITE(buffer, (const char*)&other_length, 4);
316
+ SAFE_WRITE(buffer, &subtype, 1);
339
317
  }
340
- buffer_write_bytes(buffer, (const char*)&length, 4);
318
+ SAFE_WRITE(buffer, (const char*)&length, 4);
341
319
  if (subtype != 2) {
342
- buffer_write_bytes(buffer, &subtype, 1);
320
+ SAFE_WRITE(buffer, &subtype, 1);
343
321
  }
344
- buffer_write_bytes(buffer, RSTRING_PTR(string_data), length);
322
+ SAFE_WRITE(buffer, RSTRING_PTR(string_data), length);
345
323
  break;
346
324
  }
347
325
  if (strcmp(cls, "Mongo::ObjectID") == 0) {
@@ -350,19 +328,22 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
350
328
  write_name_and_type(buffer, key, 0x07);
351
329
  for (i = 0; i < 12; i++) {
352
330
  char byte = (char)FIX2INT(RARRAY_PTR(as_array)[i]);
353
- buffer_write_bytes(buffer, &byte, 1);
331
+ SAFE_WRITE(buffer, &byte, 1);
354
332
  }
355
333
  break;
356
334
  }
357
335
  if (strcmp(cls, "Mongo::DBRef") == 0) {
358
- int start_position, length_location, obj_length;
336
+ buffer_position length_location, start_position, obj_length;
359
337
  VALUE ns, oid;
360
338
  write_name_and_type(buffer, key, 0x03);
361
339
 
362
- start_position = buffer->position;
340
+ start_position = buffer_get_position(buffer);
363
341
 
364
342
  // save space for length
365
- length_location = buffer_save_bytes(buffer, 4);
343
+ length_location = buffer_save_space(buffer, 4);
344
+ if (length_location == -1) {
345
+ rb_raise(rb_eNoMemError, "failed to allocate memory in buffer.c");
346
+ }
366
347
 
367
348
  ns = rb_funcall(value, rb_intern("namespace"), 0);
368
349
  write_element(rb_str_new2("$ref"), ns, pack_extra(buffer, Qfalse));
@@ -370,9 +351,9 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
370
351
  write_element(rb_str_new2("$id"), oid, pack_extra(buffer, Qfalse));
371
352
 
372
353
  // write null byte and fill in length
373
- buffer_write_bytes(buffer, &zero, 1);
374
- obj_length = buffer->position - start_position;
375
- memcpy(buffer->buffer + length_location, &obj_length, 4);
354
+ SAFE_WRITE(buffer, &zero, 1);
355
+ obj_length = buffer_get_position(buffer) - start_position;
356
+ SAFE_WRITE_AT_POS(buffer, length_location, (const char*)&obj_length, 4);
376
357
  break;
377
358
  }
378
359
  }
@@ -384,7 +365,7 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
384
365
  double t = NUM2DBL(rb_funcall(value, rb_intern("to_f"), 0));
385
366
  long long time_since_epoch = (long long)round(t * 1000);
386
367
  write_name_and_type(buffer, key, 0x09);
387
- buffer_write_bytes(buffer, (const char*)&time_since_epoch, 8);
368
+ SAFE_WRITE(buffer, (const char*)&time_since_epoch, 8);
388
369
  break;
389
370
  }
390
371
  }
@@ -397,35 +378,36 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
397
378
 
398
379
  write_name_and_type(buffer, key, 0x0B);
399
380
 
400
- buffer_write_bytes(buffer, pattern, length);
401
- buffer_write_bytes(buffer, &zero, 1);
381
+ SAFE_WRITE(buffer, pattern, length);
382
+ SAFE_WRITE(buffer, &zero, 1);
402
383
 
403
384
  if (flags & IGNORECASE) {
404
385
  char ignorecase = 'i';
405
- buffer_write_bytes(buffer, &ignorecase, 1);
386
+ SAFE_WRITE(buffer, &ignorecase, 1);
406
387
  }
407
388
  if (flags & MULTILINE) {
408
389
  char multiline = 'm';
409
- buffer_write_bytes(buffer, &multiline, 1);
390
+ SAFE_WRITE(buffer, &multiline, 1);
410
391
  }
411
392
  if (flags & EXTENDED) {
412
393
  char extended = 'x';
413
- buffer_write_bytes(buffer, &extended, 1);
394
+ SAFE_WRITE(buffer, &extended, 1);
414
395
  }
415
396
 
416
397
  has_extra = rb_funcall(value, rb_intern("respond_to?"), 1, rb_str_new2("extra_options_str"));
417
398
  if (TYPE(has_extra) == T_TRUE) {
418
399
  VALUE extra = rb_funcall(value, rb_intern("extra_options_str"), 0);
419
- int old_position = buffer->position;
420
- buffer_write_bytes(buffer, RSTRING_PTR(extra), RSTRING_LEN(extra));
421
- qsort(buffer->buffer + old_position, RSTRING_LEN(extra), sizeof(char), cmp_char);
400
+ buffer_position old_position = buffer_get_position(buffer);
401
+ SAFE_WRITE(buffer, RSTRING_PTR(extra), RSTRING_LEN(extra));
402
+ qsort(buffer_get_buffer(buffer) + old_position, RSTRING_LEN(extra), sizeof(char), cmp_char);
422
403
  }
423
- buffer_write_bytes(buffer, &zero, 1);
404
+ SAFE_WRITE(buffer, &zero, 1);
424
405
 
425
406
  break;
426
407
  }
427
408
  default:
428
409
  {
410
+ buffer_free(buffer);
429
411
  rb_raise(rb_eTypeError, "no c encoder for this type yet (%d)", TYPE(value));
430
412
  break;
431
413
  }
@@ -437,13 +419,17 @@ static int write_element(VALUE key, VALUE value, VALUE extra) {
437
419
  return write_element_allow_id(key, value, extra, 0);
438
420
  }
439
421
 
440
- static void write_doc(bson_buffer* buffer, VALUE hash, VALUE check_keys) {
441
- int start_position = buffer->position;
442
- int length_location = buffer_save_bytes(buffer, 4);
443
- int length;
444
-
422
+ static void write_doc(buffer_t buffer, VALUE hash, VALUE check_keys) {
423
+ buffer_position start_position = buffer_get_position(buffer);
424
+ buffer_position length_location = buffer_save_space(buffer, 4);
425
+ buffer_position length;
445
426
  VALUE id_str = rb_str_new2("_id");
446
427
  VALUE id_sym = ID2SYM(rb_intern("_id"));
428
+
429
+ if (length_location == -1) {
430
+ rb_raise(rb_eNoMemError, "failed to allocate memory in buffer.c");
431
+ }
432
+
447
433
  if (rb_funcall(hash, rb_intern("has_key?"), 1, id_str) == Qtrue) {
448
434
  VALUE id = rb_hash_aref(hash, id_str);
449
435
  write_element_allow_id(id_str, id, pack_extra(buffer, check_keys), 1);
@@ -467,20 +453,24 @@ static void write_doc(bson_buffer* buffer, VALUE hash, VALUE check_keys) {
467
453
  }
468
454
 
469
455
  // write null byte and fill in length
470
- buffer_write_bytes(buffer, &zero, 1);
471
- length = buffer->position - start_position;
472
- memcpy(buffer->buffer + length_location, &length, 4);
456
+ SAFE_WRITE(buffer, &zero, 1);
457
+ length = buffer_get_position(buffer) - start_position;
458
+ SAFE_WRITE_AT_POS(buffer, length_location, &length, 4);
473
459
  }
474
460
 
475
461
  static VALUE method_serialize(VALUE self, VALUE doc, VALUE check_keys) {
476
462
  VALUE result;
477
- bson_buffer* buffer = buffer_new();
478
- assert(buffer);
463
+ buffer_t buffer = buffer_new();
464
+ if (buffer == NULL) {
465
+ rb_raise(rb_eNoMemError, "failed to allocate memory in buffer.c");
466
+ }
479
467
 
480
468
  write_doc(buffer, doc, check_keys);
481
469
 
482
- result = rb_str_new(buffer->buffer, buffer->position);
483
- buffer_free(buffer);
470
+ result = rb_str_new(buffer_get_buffer(buffer), buffer_get_position(buffer));
471
+ if (buffer_free(buffer) != 0) {
472
+ rb_raise(rb_eRuntimeError, "failed to free buffer");
473
+ }
484
474
  return result;
485
475
  }
486
476
 
@@ -589,7 +579,7 @@ static VALUE get_value(const char* buffer, int* position, int type) {
589
579
  long long millis;
590
580
  VALUE seconds, microseconds;
591
581
  memcpy(&millis, buffer + *position, 8);
592
- seconds = INT2NUM(millis / 1000);
582
+ seconds = LL2NUM(millis / 1000);
593
583
  microseconds = INT2NUM((millis % 1000) * 1000);
594
584
 
595
585
  value = rb_funcall(Time, rb_intern("at"), 2, seconds, microseconds);
@@ -771,7 +761,7 @@ static VALUE objectid_generate(VALUE self)
771
761
  t = htonl(time(NULL));
772
762
  MEMCPY(&oid_bytes, &t, unsigned char, 4);
773
763
 
774
- if (gethostname(&hostname, MAX_HOSTNAME_LENGTH) != 0) {
764
+ if (gethostname(hostname, MAX_HOSTNAME_LENGTH) != 0) {
775
765
  rb_raise(rb_eRuntimeError, "failed to get hostname");
776
766
  }
777
767
  digest = rb_funcall(DigestMD5, rb_intern("digest"), 1, rb_str_new2(hostname));
@@ -792,7 +782,7 @@ static VALUE objectid_generate(VALUE self)
792
782
 
793
783
 
794
784
  void Init_cbson() {
795
- VALUE mongo, CBson, Digest;
785
+ VALUE mongo, CBson, Digest, ext_version;
796
786
  Time = rb_const_get(rb_cObject, rb_intern("Time"));
797
787
 
798
788
  mongo = rb_const_get(rb_cObject, rb_intern("Mongo"));
@@ -808,10 +798,13 @@ void Init_cbson() {
808
798
  RegexpOfHolding = rb_const_get(mongo, rb_intern("RegexpOfHolding"));
809
799
  rb_require("mongo/errors");
810
800
  InvalidName = rb_const_get(mongo, rb_intern("InvalidName"));
801
+ InvalidStringEncoding = rb_const_get(mongo, rb_intern("InvalidStringEncoding"));
811
802
  rb_require("mongo/util/ordered_hash");
812
803
  OrderedHash = rb_const_get(rb_cObject, rb_intern("OrderedHash"));
813
804
 
814
805
  CBson = rb_define_module("CBson");
806
+ ext_version = rb_str_new2(VERSION);
807
+ rb_define_const(CBson, "VERSION", ext_version);
815
808
  rb_define_module_function(CBson, "serialize", method_serialize, 2);
816
809
  rb_define_module_function(CBson, "deserialize", method_deserialize, 1);
817
810
 
@@ -0,0 +1,107 @@
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
+ * Copyright 2001 Unicode, Inc.
19
+ *
20
+ * Disclaimer
21
+ *
22
+ * This source code is provided as is by Unicode, Inc. No claims are
23
+ * made as to fitness for any particular purpose. No warranties of any
24
+ * kind are expressed or implied. The recipient agrees to determine
25
+ * applicability of information provided. If this file has been
26
+ * purchased on magnetic or optical media from Unicode, Inc., the
27
+ * sole remedy for any claim will be exchange of defective media
28
+ * within 90 days of receipt.
29
+ *
30
+ * Limitations on Rights to Redistribute This Code
31
+ *
32
+ * Unicode, Inc. hereby grants the right to freely use the information
33
+ * supplied in this file in the creation of products supporting the
34
+ * Unicode Standard, and to make copies of this file in any form
35
+ * for internal or external distribution as long as this notice
36
+ * remains attached.
37
+ */
38
+
39
+ /*
40
+ * Index into the table below with the first byte of a UTF-8 sequence to
41
+ * get the number of trailing bytes that are supposed to follow it.
42
+ */
43
+ static const char trailingBytesForUTF8[256] = {
44
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
45
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
46
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
47
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
48
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
49
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
50
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
51
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
52
+ };
53
+
54
+ /* --------------------------------------------------------------------- */
55
+
56
+ /*
57
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
58
+ * This must be called with the length pre-determined by the first byte.
59
+ * The length can be set by:
60
+ * length = trailingBytesForUTF8[*source]+1;
61
+ * and the sequence is illegal right away if there aren't that many bytes
62
+ * available.
63
+ * If presented with a length > 4, this returns 0. The Unicode
64
+ * definition of UTF-8 goes up to 4-byte sequences.
65
+ */
66
+ static unsigned char isLegalUTF8(const unsigned char* source, int length) {
67
+ unsigned char a;
68
+ const unsigned char* srcptr = source + length;
69
+ switch (length) {
70
+ default: return 0;
71
+ /* Everything else falls through when "true"... */
72
+ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
73
+ case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
74
+ case 2: if ((a = (*--srcptr)) > 0xBF) return 0;
75
+ switch (*source) {
76
+ /* no fall-through in this inner switch */
77
+ case 0xE0: if (a < 0xA0) return 0; break;
78
+ case 0xF0: if (a < 0x90) return 0; break;
79
+ case 0xF4: if (a > 0x8F) return 0; break;
80
+ default: if (a < 0x80) return 0;
81
+ }
82
+ case 1: if (*source >= 0x80 && *source < 0xC2) return 0;
83
+ if (*source > 0xF4) return 0;
84
+ }
85
+ return 1;
86
+ }
87
+
88
+ /* --------------------------------------------------------------------- */
89
+
90
+ /*
91
+ * Return whether a string containing UTF-8 is legal.
92
+ */
93
+ unsigned char is_legal_utf8_string(const unsigned char* string, const int length) {
94
+ int position = 0;
95
+
96
+ while (position < length) {
97
+ int sequence_length = trailingBytesForUTF8[*(string + position)] + 1;
98
+ if ((position + sequence_length) > length) {
99
+ return 0;
100
+ }
101
+ if (!isLegalUTF8(string + position, sequence_length)) {
102
+ return 0;
103
+ }
104
+ position += sequence_length;
105
+ }
106
+ return 1;
107
+ }
@@ -0,0 +1,22 @@
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
+ #ifndef ENCODING_HELPERS_H
18
+ #define ENCODING_HELPERS_H
19
+
20
+ unsigned char is_legal_utf8_string(const unsigned char* string, const int length);
21
+
22
+ #endif
@@ -0,0 +1,17 @@
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
+ #define VERSION "0.18.1"
@@ -1,16 +1,17 @@
1
1
  require 'lib/mongo'
2
-
2
+ VERSION_HEADER = File.open(File.join(File.dirname(__FILE__), 'ext', 'cbson', 'version.h'), "r")
3
+ VERSION = VERSION_HEADER.read.scan(/VERSION\s+"(\d+\.\d+(\.\d+)?)\"/)[0][0]
3
4
  Gem::Specification.new do |s|
4
5
  s.name = 'mongo_ext'
5
6
 
6
- s.version = Mongo::VERSION
7
+ s.version = VERSION
7
8
  s.platform = Gem::Platform::RUBY
8
- s.summary = 'C extensions for the MongoDB Ruby driver'
9
+ s.summary = 'C extensions for the MongoDB Ruby driver'
9
10
  s.description = 'C extensions to accelerate the MongoDB Ruby driver. For more information about Mongo, see http://www.mongodb.org.'
10
11
 
11
12
  s.require_paths = ['ext']
12
13
  s.files = ['Rakefile', 'mongo-extensions.gemspec']
13
- s.files += Dir['ext/**/*.rb'] + Dir['ext/**/*.c']
14
+ s.files += Dir['ext/**/*.rb'] + Dir['ext/**/*.c'] + Dir['ext/**/*.h']
14
15
  s.test_files = []
15
16
 
16
17
  s.has_rdoc = false
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongo_ext
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.18"
4
+ version: 0.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Dirolf
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-25 00:00:00 -05:00
12
+ date: 2009-12-05 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -25,11 +25,14 @@ files:
25
25
  - Rakefile
26
26
  - mongo-extensions.gemspec
27
27
  - ext/cbson/extconf.rb
28
+ - ext/cbson/buffer.c
28
29
  - ext/cbson/cbson.c
29
- has_rdoc: true
30
+ - ext/cbson/encoding_helpers.c
31
+ - ext/cbson/buffer.h
32
+ - ext/cbson/encoding_helpers.h
33
+ - ext/cbson/version.h
34
+ has_rdoc: false
30
35
  homepage: http://www.mongodb.org
31
- licenses: []
32
-
33
36
  post_install_message:
34
37
  rdoc_options: []
35
38
 
@@ -50,9 +53,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
53
  requirements: []
51
54
 
52
55
  rubyforge_project:
53
- rubygems_version: 1.3.5
56
+ rubygems_version: 1.3.1
54
57
  signing_key:
55
- specification_version: 3
58
+ specification_version: 2
56
59
  summary: C extensions for the MongoDB Ruby driver
57
60
  test_files: []
58
61