mongo_ext 0.18 → 0.18.1

Sign up to get free protection for your applications and to get access to all the features.
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