bson 4.7.1 → 4.8.0

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.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -2
  3. data.tar.gz.sig +0 -0
  4. data/README.md +5 -1
  5. data/ext/bson/bson-native.h +14 -4
  6. data/ext/bson/init.c +25 -2
  7. data/ext/bson/read.c +61 -15
  8. data/ext/bson/util.c +40 -0
  9. data/ext/bson/write.c +17 -13
  10. data/lib/bson.rb +3 -0
  11. data/lib/bson/array.rb +21 -3
  12. data/lib/bson/binary.rb +41 -3
  13. data/lib/bson/boolean.rb +3 -1
  14. data/lib/bson/code.rb +15 -1
  15. data/lib/bson/code_with_scope.rb +17 -3
  16. data/lib/bson/db_pointer.rb +104 -0
  17. data/lib/bson/decimal128.rb +15 -1
  18. data/lib/bson/error.rb +17 -0
  19. data/lib/bson/ext_json.rb +374 -0
  20. data/lib/bson/float.rb +47 -1
  21. data/lib/bson/hash.rb +23 -3
  22. data/lib/bson/int32.rb +21 -1
  23. data/lib/bson/int64.rb +28 -3
  24. data/lib/bson/integer.rb +34 -0
  25. data/lib/bson/max_key.rb +12 -0
  26. data/lib/bson/min_key.rb +12 -0
  27. data/lib/bson/nil_class.rb +3 -1
  28. data/lib/bson/object.rb +27 -0
  29. data/lib/bson/object_id.rb +15 -1
  30. data/lib/bson/regexp.rb +19 -2
  31. data/lib/bson/specialized.rb +3 -1
  32. data/lib/bson/string.rb +3 -1
  33. data/lib/bson/symbol.rb +92 -3
  34. data/lib/bson/time.rb +28 -3
  35. data/lib/bson/timestamp.rb +15 -1
  36. data/lib/bson/undefined.rb +11 -0
  37. data/lib/bson/version.rb +1 -1
  38. data/spec/bson/binary_spec.rb +33 -3
  39. data/spec/bson/ext_json_parse_spec.rb +276 -0
  40. data/spec/bson/float_spec.rb +36 -0
  41. data/spec/bson/hash_spec.rb +70 -0
  42. data/spec/bson/int32_spec.rb +20 -0
  43. data/spec/bson/int64_spec.rb +38 -0
  44. data/spec/bson/integer_spec.rb +26 -0
  45. data/spec/bson/raw_spec.rb +22 -1
  46. data/spec/bson/symbol_raw_spec.rb +45 -0
  47. data/spec/bson/symbol_spec.rb +60 -0
  48. data/spec/{support → runners}/common_driver.rb +0 -0
  49. data/spec/runners/corpus.rb +182 -0
  50. data/spec/{support/corpus.rb → runners/corpus_legacy.rb} +40 -58
  51. data/spec/spec_helper.rb +9 -2
  52. data/spec/{bson/driver_bson_spec.rb → spec_tests/common_driver_spec.rb} +1 -0
  53. data/spec/{bson/corpus_spec.rb → spec_tests/corpus_legacy_spec.rb} +4 -4
  54. data/spec/spec_tests/corpus_spec.rb +124 -0
  55. data/spec/spec_tests/data/corpus/README.md +15 -0
  56. data/spec/spec_tests/data/corpus/array.json +49 -0
  57. data/spec/spec_tests/data/corpus/binary.json +85 -0
  58. data/spec/spec_tests/data/corpus/boolean.json +27 -0
  59. data/spec/spec_tests/data/corpus/code.json +67 -0
  60. data/spec/spec_tests/data/corpus/code_w_scope.json +78 -0
  61. data/spec/spec_tests/data/corpus/datetime.json +42 -0
  62. data/spec/spec_tests/data/corpus/dbpointer.json +56 -0
  63. data/spec/spec_tests/data/corpus/dbref.json +31 -0
  64. data/spec/spec_tests/data/corpus/decimal128-1.json +317 -0
  65. data/spec/spec_tests/data/corpus/decimal128-2.json +793 -0
  66. data/spec/spec_tests/data/corpus/decimal128-3.json +1771 -0
  67. data/spec/spec_tests/data/corpus/decimal128-4.json +117 -0
  68. data/spec/spec_tests/data/corpus/decimal128-5.json +402 -0
  69. data/spec/spec_tests/data/corpus/decimal128-6.json +119 -0
  70. data/spec/spec_tests/data/corpus/decimal128-7.json +323 -0
  71. data/spec/spec_tests/data/corpus/document.json +36 -0
  72. data/spec/spec_tests/data/corpus/double.json +87 -0
  73. data/spec/spec_tests/data/corpus/int32.json +43 -0
  74. data/spec/spec_tests/data/corpus/int64.json +43 -0
  75. data/spec/spec_tests/data/corpus/maxkey.json +12 -0
  76. data/spec/spec_tests/data/corpus/minkey.json +12 -0
  77. data/spec/spec_tests/data/corpus/multi-type-deprecated.json +15 -0
  78. data/spec/spec_tests/data/corpus/multi-type.json +11 -0
  79. data/spec/spec_tests/data/corpus/null.json +12 -0
  80. data/spec/spec_tests/data/corpus/oid.json +28 -0
  81. data/spec/spec_tests/data/corpus/regex.json +65 -0
  82. data/spec/spec_tests/data/corpus/string.json +72 -0
  83. data/spec/spec_tests/data/corpus/symbol.json +80 -0
  84. data/spec/spec_tests/data/corpus/timestamp.json +24 -0
  85. data/spec/spec_tests/data/corpus/top.json +240 -0
  86. data/spec/spec_tests/data/corpus/undefined.json +15 -0
  87. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/array.json +0 -0
  88. data/spec/{support/corpus-tests/failures → spec_tests/data/corpus_legacy}/binary.json +0 -0
  89. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/boolean.json +0 -0
  90. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/code.json +1 -1
  91. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/code_w_scope.json +1 -1
  92. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/document.json +1 -1
  93. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/double.json +1 -1
  94. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/datetime.json +0 -0
  95. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/dbpointer.json +0 -0
  96. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/int64.json +0 -0
  97. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/symbol.json +0 -0
  98. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/int32.json +1 -1
  99. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/maxkey.json +1 -1
  100. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/minkey.json +1 -1
  101. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/null.json +1 -1
  102. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/oid.json +0 -0
  103. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/regex.json +1 -1
  104. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/string.json +0 -0
  105. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/timestamp.json +1 -1
  106. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/top.json +0 -0
  107. data/spec/{support/corpus-tests/failures → spec_tests/data/corpus_legacy}/undefined.json +0 -0
  108. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-1.json +0 -0
  109. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-2.json +0 -0
  110. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-3.json +0 -0
  111. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-4.json +0 -0
  112. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-5.json +0 -0
  113. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-6.json +0 -0
  114. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-7.json +0 -0
  115. metadata +170 -95
  116. metadata.gz.sig +2 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8a3b9746ff3d1a260ea84b199e2d0f5633dd64a2f35cd0b25f5c6e04fe7087d
4
- data.tar.gz: cfa985f7f17d689484240c54756b3a0248f0a8c97573cbfc1af0fbc037835e3d
3
+ metadata.gz: c37081fa823c2700f07a23ca6ecbec6fc67cffbf7915b4a21e59940b332b678d
4
+ data.tar.gz: 16d483a311c4252f4ef1463be2f86240450b82294a972d8bb5bb2bb332fd7b17
5
5
  SHA512:
6
- metadata.gz: bde5c1c92fd57f6e9ed5ad0cec9957521949022141a98a7fd47c551cdb313380c56607dd6946575cbfe20413170ff009cb6d8d165f674ed3f82e7ce63576c8e9
7
- data.tar.gz: c857d6e12472340748ebb2c31da5ddc637510fbf07248d4a8ce9277967acfd74bca0bb065cbc1369c75e14d1994fa099ce8a40c4e468003376b92304a7883029
6
+ metadata.gz: 1618b74348d155c904b2f24739d7b9403bc0b1c23e67cc6ce89a92ac19070dd5965dc1fccc6353c2eefb82993bf5fec30702bccaaf6bb8171777421253832cce
7
+ data.tar.gz: 1362710c9b36348437bf266a52c18347c1228ddd92231b520e6b068b1742f42895c2108a64cdff2487c5b874c12e3af5ce4d4435c00cebad942b83f1ea9e17ab
@@ -1,2 +1,2 @@
1
- �}( v�+�͎z�5�s�n�:���@�;�vL��Y��<T���BE8$�n�O;�@��y�y��ۆ�\#�;+9�S#r���A���\~JdJt��X �&H�R/j`U��.����Ռ=���W&W��������$l������C�b>gCՍ]P���Ԓ&�M�K& [��,�6�+V��+��Z֦�iE62~���S��hQ�Sd���v���}�f������/"�'
2
- ��)�Ɏ I��
1
+ �}
2
+ O��t���9�a���2)����=%Zf{ms 3VeB�L)¡o�crp �e��r��@�K�~�
data.tar.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -1,4 +1,8 @@
1
- BSON [![Build Status](https://secure.travis-ci.org/mongodb/bson-ruby.png?branch=master&.png)](http://travis-ci.org/mongodb/bson-ruby) [![Code Climate](https://codeclimate.com/github/mongodb/bson-ruby.png)](https://codeclimate.com/github/mongodb/bson-ruby) [![Coverage Status](https://coveralls.io/repos/mongodb/bson-ruby/badge.png?branch=master)](https://coveralls.io/r/mongodb/bson-ruby?branch=master) [![Inline docs](http://inch-ci.org/github/mongodb/bson-ruby.svg?branch=master)](http://inch-ci.org/github/mongodb/bson-ruby)
1
+ BSON [![Build Status](https://secure.travis-ci.org/mongodb/bson-ruby.svg?branch=master)](http://travis-ci.org/mongodb/bson-ruby)
2
+ [![Build status Windows](https://ci.appveyor.com/api/projects/status/p5aqko7umsx351nm?svg=true)](https://ci.appveyor.com/project/p-mongo/bson-ruby/branch/master)
3
+ [![Code Climate](https://codeclimate.com/github/mongodb/bson-ruby.svg)](https://codeclimate.com/github/mongodb/bson-ruby)
4
+ [![Coverage Status](https://coveralls.io/repos/mongodb/bson-ruby/badge.svg?branch=master)](https://coveralls.io/r/mongodb/bson-ruby?branch=master)
5
+ [![Inline docs](http://inch-ci.org/github/mongodb/bson-ruby.svg?branch=master)](http://inch-ci.org/github/mongodb/bson-ruby)
2
6
  ====
3
7
 
4
8
  An implementation of the BSON specification in Ruby.
@@ -33,13 +33,15 @@ rb_bson_utf8_validate (const char *utf8, /* IN */
33
33
  #define HOST_NAME_HASH_MAX 256
34
34
  #endif
35
35
 
36
+ /* See the type list in http://bsonspec.org/spec.html. */
36
37
  #define BSON_TYPE_DOUBLE 1
37
38
  #define BSON_TYPE_STRING 2
38
39
  #define BSON_TYPE_DOCUMENT 3
39
40
  #define BSON_TYPE_ARRAY 4
40
41
  #define BSON_TYPE_BOOLEAN 8
41
- #define BSON_TYPE_INT32 16
42
- #define BSON_TYPE_INT64 18
42
+ #define BSON_TYPE_SYMBOL 0x0E
43
+ #define BSON_TYPE_INT32 0x10
44
+ #define BSON_TYPE_INT64 0x12
43
45
 
44
46
  typedef struct {
45
47
  size_t size;
@@ -76,8 +78,8 @@ VALUE rb_bson_byte_buffer_get_double(VALUE self);
76
78
  VALUE rb_bson_byte_buffer_get_int32(VALUE self);
77
79
  VALUE rb_bson_byte_buffer_get_int64(VALUE self);
78
80
  VALUE rb_bson_byte_buffer_get_string(VALUE self);
79
- VALUE rb_bson_byte_buffer_get_hash(VALUE self);
80
- VALUE rb_bson_byte_buffer_get_array(VALUE self);
81
+ VALUE rb_bson_byte_buffer_get_hash(int argc, VALUE *argv, VALUE self);
82
+ VALUE rb_bson_byte_buffer_get_array(int argc, VALUE *argv, VALUE self);
81
83
  VALUE rb_bson_byte_buffer_put_byte(VALUE self, VALUE byte);
82
84
  VALUE rb_bson_byte_buffer_put_bytes(VALUE self, VALUE bytes);
83
85
  VALUE rb_bson_byte_buffer_put_cstring(VALUE self, VALUE string);
@@ -101,6 +103,14 @@ void rb_bson_byte_buffer_free(void *ptr);
101
103
  void rb_bson_expand_buffer(byte_buffer_t* buffer_ptr, size_t length);
102
104
  void rb_bson_generate_machine_id(VALUE rb_md5_class, char *rb_bson_machine_id);
103
105
 
106
+ VALUE pvt_const_get_2(const char *c1, const char *c2);
107
+ VALUE pvt_const_get_3(const char *c1, const char *c2, const char *c3);
108
+
109
+ #define BSON_MODE_DEFAULT 0
110
+ #define BSON_MODE_BSON 1
111
+
112
+ int pvt_get_mode_option(int argc, VALUE *argv);
113
+
104
114
  /**
105
115
  * The counter for incrementing object ids.
106
116
  */
@@ -70,8 +70,31 @@ void Init_bson_native()
70
70
  rb_define_method(rb_byte_buffer_class, "get_cstring", rb_bson_byte_buffer_get_cstring, 0);
71
71
  rb_define_method(rb_byte_buffer_class, "get_decimal128_bytes", rb_bson_byte_buffer_get_decimal128_bytes, 0);
72
72
  rb_define_method(rb_byte_buffer_class, "get_double", rb_bson_byte_buffer_get_double, 0);
73
- rb_define_method(rb_byte_buffer_class, "get_hash", rb_bson_byte_buffer_get_hash, 0);
74
- rb_define_method(rb_byte_buffer_class, "get_array", rb_bson_byte_buffer_get_array, 0);
73
+
74
+ /*
75
+ * call-seq:
76
+ * buffer.get_hash(**options) -> Hash
77
+ *
78
+ * Reads a document from the byte buffer and returns it as a BSON::Document.
79
+ *
80
+ * @option options [ nil | :bson ] :mode Decoding mode to use.
81
+ *
82
+ * @return [ BSON::Document ] The decoded document.
83
+ */
84
+ rb_define_method(rb_byte_buffer_class, "get_hash", rb_bson_byte_buffer_get_hash, -1);
85
+
86
+ /*
87
+ * call-seq:
88
+ * buffer.get_array(**options) -> Array
89
+ *
90
+ * Reads an array from the byte buffer..
91
+ *
92
+ * @option options [ nil | :bson ] :mode Decoding mode to use.
93
+ *
94
+ * @return [ Array ] The decoded array.
95
+ */
96
+ rb_define_method(rb_byte_buffer_class, "get_array", rb_bson_byte_buffer_get_array, -1);
97
+
75
98
  rb_define_method(rb_byte_buffer_class, "get_int32", rb_bson_byte_buffer_get_int32, 0);
76
99
  rb_define_method(rb_byte_buffer_class, "get_int64", rb_bson_byte_buffer_get_int64, 0);
77
100
  rb_define_method(rb_byte_buffer_class, "get_string", rb_bson_byte_buffer_get_string, 0);
@@ -20,11 +20,12 @@
20
20
  static void pvt_validate_length(byte_buffer_t *b);
21
21
  static uint8_t pvt_get_type_byte(byte_buffer_t *b);
22
22
  static VALUE pvt_get_int32(byte_buffer_t *b);
23
- static VALUE pvt_get_int64(byte_buffer_t *b);
23
+ static VALUE pvt_get_int64(byte_buffer_t *b, int argc, VALUE *argv);
24
24
  static VALUE pvt_get_double(byte_buffer_t *b);
25
25
  static VALUE pvt_get_string(byte_buffer_t *b);
26
+ static VALUE pvt_get_symbol(byte_buffer_t *b, VALUE rb_buffer, int argc, VALUE *argv);
26
27
  static VALUE pvt_get_boolean(byte_buffer_t *b);
27
- static VALUE pvt_read_field(byte_buffer_t *b, VALUE rb_buffer, uint8_t type);
28
+ static VALUE pvt_read_field(byte_buffer_t *b, VALUE rb_buffer, uint8_t type, int argc, VALUE *argv);
28
29
  static void pvt_skip_cstring(byte_buffer_t *b);
29
30
 
30
31
  /**
@@ -57,14 +58,16 @@ void pvt_validate_length(byte_buffer_t *b)
57
58
  /**
58
59
  * Read a single field from a hash or array
59
60
  */
60
- VALUE pvt_read_field(byte_buffer_t *b, VALUE rb_buffer, uint8_t type){
61
+ VALUE pvt_read_field(byte_buffer_t *b, VALUE rb_buffer, uint8_t type, int argc, VALUE *argv)
62
+ {
61
63
  switch(type) {
62
64
  case BSON_TYPE_INT32: return pvt_get_int32(b);
63
- case BSON_TYPE_INT64: return pvt_get_int64(b);
65
+ case BSON_TYPE_INT64: return pvt_get_int64(b, argc, argv);
64
66
  case BSON_TYPE_DOUBLE: return pvt_get_double(b);
65
67
  case BSON_TYPE_STRING: return pvt_get_string(b);
66
- case BSON_TYPE_ARRAY: return rb_bson_byte_buffer_get_array(rb_buffer);
67
- case BSON_TYPE_DOCUMENT: return rb_bson_byte_buffer_get_hash(rb_buffer);
68
+ case BSON_TYPE_SYMBOL: return pvt_get_symbol(b, rb_buffer, argc, argv);
69
+ case BSON_TYPE_ARRAY: return rb_bson_byte_buffer_get_array(argc, argv, rb_buffer);
70
+ case BSON_TYPE_DOCUMENT: return rb_bson_byte_buffer_get_hash(argc, argv, rb_buffer);
68
71
  case BSON_TYPE_BOOLEAN: return pvt_get_boolean(b);
69
72
  default:
70
73
  {
@@ -150,6 +153,31 @@ VALUE pvt_get_string(byte_buffer_t *b)
150
153
  return string;
151
154
  }
152
155
 
156
+ /**
157
+ * Reads a UTF-8 string out of the byte buffer. If the argc/argv arguments
158
+ * have a :mode option with the value of :bson, wraps the string in a
159
+ * BSON::Symbol::Raw. Otherwise consults the BSON registry to determine
160
+ * which class to instantiate (String in bson-ruby, overridden to Symbol by
161
+ * the Ruby driver). Returns either a BSON::Symbol::Raw, Symbol or String
162
+ * value.
163
+ */
164
+ VALUE pvt_get_symbol(byte_buffer_t *b, VALUE rb_buffer, int argc, VALUE *argv)
165
+ {
166
+ VALUE value, klass;
167
+
168
+ if (pvt_get_mode_option(argc, argv) == BSON_MODE_BSON) {
169
+ value = pvt_get_string(b);
170
+ klass = pvt_const_get_3("BSON", "Symbol", "Raw");
171
+ value = rb_funcall(klass, rb_intern("new"), 1, value);
172
+ } else {
173
+ klass = rb_funcall(rb_bson_registry, rb_intern("get"), 1, INT2FIX(BSON_TYPE_SYMBOL));
174
+ value = rb_funcall(klass, rb_intern("from_bson"), 1, rb_buffer);
175
+ }
176
+
177
+ RB_GC_GUARD(klass);
178
+ return value;
179
+ }
180
+
153
181
  /**
154
182
  * Get a cstring from the buffer.
155
183
  */
@@ -206,17 +234,35 @@ VALUE rb_bson_byte_buffer_get_int64(VALUE self)
206
234
  {
207
235
  byte_buffer_t *b;
208
236
  TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b);
209
- return pvt_get_int64(b);
237
+ return pvt_get_int64(b, 0, NULL);
210
238
  }
211
239
 
212
- VALUE pvt_get_int64(byte_buffer_t *b)
240
+ /**
241
+ * Reads a 64-bit integer out of the byte buffer into a Ruby Integer instance.
242
+ * If the argc/argv arguments have a :mode option with the value of :bson,
243
+ * wraps the integer in a BSON::Int64. Returns either the Integer or the
244
+ * BSON::Int64 instance.
245
+ */
246
+ VALUE pvt_get_int64(byte_buffer_t *b, int argc, VALUE *argv)
213
247
  {
214
248
  int64_t i64;
249
+ VALUE num;
215
250
 
216
251
  ENSURE_BSON_READ(b, 8);
217
252
  memcpy(&i64, READ_PTR(b), 8);
218
253
  b->read_position += 8;
219
- return LL2NUM(BSON_UINT64_FROM_LE(i64));
254
+ num = LL2NUM(BSON_UINT64_FROM_LE(i64));
255
+
256
+ if (pvt_get_mode_option(argc, argv) == BSON_MODE_BSON) {
257
+ VALUE klass = rb_funcall(rb_bson_registry,rb_intern("get"),1, INT2FIX(BSON_TYPE_INT64));
258
+ VALUE value = rb_funcall(klass, rb_intern("new"), 1, num);
259
+ RB_GC_GUARD(klass);
260
+ return value;
261
+ } else {
262
+ return num;
263
+ }
264
+
265
+ RB_GC_GUARD(num);
220
266
  }
221
267
 
222
268
  /**
@@ -255,27 +301,27 @@ VALUE rb_bson_byte_buffer_get_decimal128_bytes(VALUE self)
255
301
  return bytes;
256
302
  }
257
303
 
258
- VALUE rb_bson_byte_buffer_get_hash(VALUE self){
304
+ VALUE rb_bson_byte_buffer_get_hash(int argc, VALUE *argv, VALUE self){
259
305
  VALUE doc = Qnil;
260
306
  byte_buffer_t *b = NULL;
261
307
  uint8_t type;
262
- VALUE cDocument = rb_const_get(rb_const_get(rb_cObject, rb_intern("BSON")), rb_intern("Document"));
308
+ VALUE cDocument = pvt_const_get_2("BSON", "Document");
263
309
 
264
310
  TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b);
265
311
 
266
312
  pvt_validate_length(b);
267
313
 
268
- doc = rb_funcall(cDocument, rb_intern("allocate"),0);
314
+ doc = rb_funcall(cDocument, rb_intern("allocate"), 0);
269
315
 
270
316
  while((type = pvt_get_type_byte(b)) != 0){
271
317
  VALUE field = rb_bson_byte_buffer_get_cstring(self);
318
+ rb_hash_aset(doc, field, pvt_read_field(b, self, type, argc, argv));
272
319
  RB_GC_GUARD(field);
273
- rb_hash_aset(doc, field, pvt_read_field(b, self, type));
274
320
  }
275
321
  return doc;
276
322
  }
277
323
 
278
- VALUE rb_bson_byte_buffer_get_array(VALUE self){
324
+ VALUE rb_bson_byte_buffer_get_array(int argc, VALUE *argv, VALUE self){
279
325
  byte_buffer_t *b;
280
326
  VALUE array = Qnil;
281
327
  uint8_t type;
@@ -287,7 +333,7 @@ VALUE rb_bson_byte_buffer_get_array(VALUE self){
287
333
  array = rb_ary_new();
288
334
  while((type = pvt_get_type_byte(b)) != 0){
289
335
  pvt_skip_cstring(b);
290
- rb_ary_push(array, pvt_read_field(b, self, type));
336
+ rb_ary_push(array, pvt_read_field(b, self, type, argc, argv));
291
337
  }
292
338
  RB_GC_GUARD(array);
293
339
  return array;
@@ -53,3 +53,43 @@ VALUE rb_bson_object_id_generator_next(int argc, VALUE* args, VALUE self)
53
53
  rb_bson_object_id_counter++;
54
54
  return rb_str_new(bytes, 12);
55
55
  }
56
+
57
+ /**
58
+ * Returns a Ruby constant nested one level, e.g. BSON::Document.
59
+ */
60
+ VALUE pvt_const_get_2(const char *c1, const char *c2) {
61
+ return rb_const_get(rb_const_get(rb_cObject, rb_intern(c1)), rb_intern(c2));
62
+ }
63
+
64
+ /**
65
+ * Returns a Ruby constant nested two levels, e.g. BSON::Regexp::Raw.
66
+ */
67
+ VALUE pvt_const_get_3(const char *c1, const char *c2, const char *c3) {
68
+ return rb_const_get(pvt_const_get_2(c1, c2), rb_intern(c3));
69
+ }
70
+
71
+ /**
72
+ * Returns the value of the :mode option, or the default if the option is not
73
+ * specified. Raises ArgumentError if the value is not one of nil or :bson.
74
+ * A future version of bson-ruby is expected to also support :ruby and :ruby!
75
+ * values. Returns one of the BSON_MODE_* values.
76
+ */
77
+ int pvt_get_mode_option(int argc, VALUE *argv) {
78
+ VALUE opts;
79
+ VALUE mode;
80
+
81
+ rb_scan_args(argc, argv, ":", &opts);
82
+ if (NIL_P(opts)) {
83
+ return BSON_MODE_DEFAULT;
84
+ } else {
85
+ mode = rb_hash_lookup(opts, ID2SYM(rb_intern("mode")));
86
+ if (mode == Qnil) {
87
+ return BSON_MODE_DEFAULT;
88
+ } else if (mode == ID2SYM(rb_intern("bson"))) {
89
+ return BSON_MODE_BSON;
90
+ } else {
91
+ rb_raise(rb_eArgError, "Invalid value for :mode option: %s",
92
+ RSTRING_PTR(rb_funcall(mode, rb_intern("inspect"), 0)));
93
+ }
94
+ }
95
+ }
@@ -125,40 +125,44 @@ VALUE rb_bson_byte_buffer_put_bytes(VALUE self, VALUE bytes)
125
125
  return self;
126
126
  }
127
127
 
128
- /* write the byte denoting the BSON type for the passed object*/
128
+ /* write the byte denoting the BSON type for the passed object */
129
129
  void pvt_put_type_byte(byte_buffer_t *b, VALUE val){
130
- switch(TYPE(val)){
130
+ char type_byte;
131
+
132
+ switch (TYPE(val)){
131
133
  case T_BIGNUM:
132
134
  case T_FIXNUM:
133
- if(fits_int32(NUM2LL(val))){
134
- pvt_put_byte(b, BSON_TYPE_INT32);
135
- }else{
136
- pvt_put_byte(b, BSON_TYPE_INT64);
135
+ if (fits_int32(NUM2LL(val))) {
136
+ type_byte = BSON_TYPE_INT32;
137
+ } else {
138
+ type_byte = BSON_TYPE_INT64;
137
139
  }
138
140
  break;
139
141
  case T_STRING:
140
- pvt_put_byte(b, BSON_TYPE_STRING);
142
+ type_byte = BSON_TYPE_STRING;
141
143
  break;
142
144
  case T_ARRAY:
143
- pvt_put_byte(b, BSON_TYPE_ARRAY);
145
+ type_byte = BSON_TYPE_ARRAY;
144
146
  break;
145
147
  case T_TRUE:
146
148
  case T_FALSE:
147
- pvt_put_byte(b, BSON_TYPE_BOOLEAN);
149
+ type_byte = BSON_TYPE_BOOLEAN;
148
150
  break;
149
151
  case T_HASH:
150
- pvt_put_byte(b, BSON_TYPE_DOCUMENT);
152
+ type_byte = BSON_TYPE_DOCUMENT;
151
153
  break;
152
154
  case T_FLOAT:
153
- pvt_put_byte(b, BSON_TYPE_DOUBLE);
155
+ type_byte = BSON_TYPE_DOUBLE;
154
156
  break;
155
157
  default:{
156
- VALUE type = rb_funcall(val, rb_intern("bson_type"),0);
158
+ VALUE type = rb_funcall(val, rb_intern("bson_type"), 0);
159
+ type_byte = *RSTRING_PTR(type);
157
160
  RB_GC_GUARD(type);
158
- pvt_put_byte(b, *RSTRING_PTR(type));
159
161
  break;
160
162
  }
161
163
  }
164
+
165
+ pvt_put_byte(b, type_byte);
162
166
  }
163
167
 
164
168
  /**
@@ -57,6 +57,7 @@ module BSON
57
57
  end
58
58
 
59
59
  require "bson/config"
60
+ require "bson/error"
60
61
  require "bson/registry"
61
62
  require "bson/specialized"
62
63
  require "bson/json"
@@ -70,8 +71,10 @@ require "bson/code"
70
71
  require "bson/code_with_scope"
71
72
  require "bson/date"
72
73
  require "bson/date_time"
74
+ require "bson/db_pointer"
73
75
  require "bson/decimal128"
74
76
  require "bson/document"
77
+ require "bson/ext_json"
75
78
  require "bson/false_class"
76
79
  require "bson/float"
77
80
  require "bson/hash"
@@ -85,26 +85,44 @@ module BSON
85
85
  map { |value| value.to_bson_normalized_value }
86
86
  end
87
87
 
88
+ # Converts this object to a representation directly serializable to
89
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json.rst).
90
+ #
91
+ # This method recursively invokes +as_extended_json+ with the provided
92
+ # options on each array element.
93
+ #
94
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
95
+ # (default is canonical extended JSON)
96
+ #
97
+ # @return [ Array ] This array converted to extended json representation.
98
+ def as_extended_json(**options)
99
+ map do |item|
100
+ item.as_extended_json(**options)
101
+ end
102
+ end
103
+
88
104
  module ClassMethods
89
105
 
90
106
  # Deserialize the array from BSON.
91
107
  #
92
108
  # @param [ ByteBuffer ] buffer The byte buffer.
93
109
  #
110
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
111
+ #
94
112
  # @return [ Array ] The decoded array.
95
113
  #
96
114
  # @see http://bsonspec.org/#/specification
97
115
  #
98
116
  # @since 2.0.0
99
- def from_bson(buffer)
117
+ def from_bson(buffer, **options)
100
118
  if buffer.respond_to?(:get_array)
101
- buffer.get_array
119
+ buffer.get_array(**options)
102
120
  else
103
121
  array = new
104
122
  buffer.get_int32 # throw away the length
105
123
  while (type = buffer.get_byte) != NULL_BYTE
106
124
  buffer.get_cstring
107
- array << BSON::Registry.get(type).from_bson(buffer)
125
+ array << BSON::Registry.get(type).from_bson(buffer, **options)
108
126
  end
109
127
  array
110
128
  end
@@ -31,6 +31,12 @@ module BSON
31
31
 
32
32
  # The mappings of subtypes to their single byte identifiers.
33
33
  #
34
+ # @note subtype 6 (ciphertext) is used for the Client-Side Encryption
35
+ # feature. Data represented by this subtype is often encrypted, but
36
+ # may also be plaintext. All instances of this subtype necessary for
37
+ # Client-Side Encryption will be created internally by the Ruby driver.
38
+ # An application should not create new BSON::Binary objects of this subtype.
39
+ #
34
40
  # @since 2.0.0
35
41
  SUBTYPES = {
36
42
  :generic => 0.chr,
@@ -39,6 +45,7 @@ module BSON
39
45
  :uuid_old => 3.chr,
40
46
  :uuid => 4.chr,
41
47
  :md5 => 5.chr,
48
+ :ciphertext => 6.chr,
42
49
  :user => 128.chr
43
50
  }.freeze
44
51
 
@@ -94,8 +101,31 @@ module BSON
94
101
  # @return [ Hash ] The binary as a JSON hash.
95
102
  #
96
103
  # @since 2.0.0
104
+ # @deprecated Use as_extended_json instead.
97
105
  def as_json(*args)
98
- { "$binary" => Base64.encode64(data), "$type" => type }
106
+ as_extended_json
107
+ end
108
+
109
+ # Converts this object to a representation directly serializable to
110
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json.rst).
111
+ #
112
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
113
+ # (default is canonical extended JSON)
114
+ #
115
+ # @return [ Hash ] The extended json representation.
116
+ def as_extended_json(**options)
117
+ subtype = SUBTYPES[type].each_byte.map { |c| c.to_s(16) }.join
118
+ if subtype.length == 1
119
+ subtype = "0#{subtype}"
120
+ end
121
+
122
+ value = Base64.encode64(data).strip
123
+
124
+ if options[:mode] == :legacy
125
+ { "$binary" => value, "$type" => subtype }
126
+ else
127
+ { "$binary" => {'base64' => value, "subType" => subtype }}
128
+ end
99
129
  end
100
130
 
101
131
  # Instantiate the new binary object.
@@ -228,14 +258,22 @@ module BSON
228
258
  #
229
259
  # @param [ ByteBuffer ] buffer The byte buffer.
230
260
  #
261
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
262
+ #
231
263
  # @return [ Binary ] The decoded binary data.
232
264
  #
233
265
  # @see http://bsonspec.org/#/specification
234
266
  #
235
267
  # @since 2.0.0
236
- def self.from_bson(buffer)
268
+ def self.from_bson(buffer, **options)
237
269
  length = buffer.get_int32
238
- type = TYPES[buffer.get_byte]
270
+ type_byte = buffer.get_byte
271
+ type = TYPES[type_byte]
272
+ if type.nil?
273
+ raise Error::UnsupportedBinarySubtype,
274
+ "BSON data contains unsupported binary subtype #{'0x%02x' % type_byte.ord}"
275
+ end
276
+
239
277
  length = buffer.get_int32 if type == :old
240
278
  data = buffer.get_bytes(length)
241
279
  new(data, type)