oj 3.17.6 → 3.17.8

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/ext/oj/intern.c CHANGED
@@ -17,9 +17,6 @@
17
17
  #define HASH_SLOT_CNT ((uint64_t)256)
18
18
  #define HASH_MASK (HASH_SLOT_CNT - 1)
19
19
 
20
- // almost the Murmur hash algorithm
21
- #define M 0x5bd1e995
22
-
23
20
  typedef struct _keyVal {
24
21
  struct _keyVal *next;
25
22
  const char *key;
@@ -50,7 +47,12 @@ static VALUE form_str(const char *str, size_t len) {
50
47
  }
51
48
 
52
49
  static VALUE form_sym(const char *str, size_t len) {
53
- return rb_to_symbol(rb_str_intern(rb_utf8_str_new(str, len)));
50
+ VALUE sym = rb_check_symbol_cstr(str, (long)len, oj_utf8_encoding);
51
+
52
+ if (Qnil == sym) {
53
+ sym = rb_str_intern(rb_utf8_str_new(str, len));
54
+ }
55
+ return sym;
54
56
  }
55
57
 
56
58
  static VALUE form_attr(const char *str, size_t len) {
@@ -150,40 +152,6 @@ ID oj_attr_intern(const char *key, size_t len) {
150
152
  return cache_intern(c, key, len);
151
153
  }
152
154
 
153
- static uint64_t hash_calc(const uint8_t *key, size_t len) {
154
- const uint8_t *end = key + len;
155
- const uint8_t *endless = key + (len & 0xFFFFFFFC);
156
- uint64_t h = (uint64_t)len ^ oj_hash_seed;
157
- uint64_t k;
158
-
159
- while (key < endless) {
160
- k = (uint64_t)*key++;
161
- k |= (uint64_t)*key++ << 8;
162
- k |= (uint64_t)*key++ << 16;
163
- k |= (uint64_t)*key++ << 24;
164
-
165
- k *= M;
166
- k ^= k >> 24;
167
- h *= M;
168
- h ^= k * M;
169
- }
170
- if (1 < end - key) {
171
- uint16_t k16 = (uint16_t)*key++;
172
-
173
- k16 |= (uint16_t)*key++ << 8;
174
- h ^= k16 << 8;
175
- }
176
- if (key < end) {
177
- h ^= *key;
178
- }
179
- h *= M;
180
- h ^= h >> 13;
181
- h *= M;
182
- h ^= h >> 15;
183
-
184
- return h;
185
- }
186
-
187
155
  static VALUE resolve_classname(VALUE mod, const char *classname, int auto_define) {
188
156
  VALUE clas;
189
157
  ID ci = rb_intern(classname);
@@ -243,61 +211,91 @@ static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int a
243
211
  return clas;
244
212
  }
245
213
 
214
+ typedef struct _internArgs {
215
+ KeyVal bucket; // last bucket in the chain for the key's slot
216
+ const char *key;
217
+ size_t len;
218
+ ParseInfo pi;
219
+ int auto_define;
220
+ VALUE error_class;
221
+ } *InternArgs;
222
+
223
+ // Resolves the class and only then adds it to the cache so a raise while
224
+ // resolving does not leave a key cached with no value.
225
+ static VALUE intern_new_class(VALUE a) {
226
+ InternArgs args = (InternArgs)a;
227
+ KeyVal bucket = args->bucket;
228
+ VALUE clas = resolve_classpath(args->pi, args->key, args->len, args->auto_define, args->error_class);
229
+
230
+ if (Qundef == clas) {
231
+ return clas;
232
+ }
233
+ if (NULL != bucket->key) { // not the top slot
234
+ KeyVal b = (KeyVal)OJ_R_ALLOC_N(char, sizeof(struct _keyVal) + args->len + 1);
235
+ char *key = (char *)(b + 1);
236
+
237
+ memcpy(key, args->key, args->len);
238
+ key[args->len] = '\0';
239
+ b->next = NULL;
240
+ b->key = key;
241
+ bucket->next = b;
242
+ bucket = b;
243
+ } else {
244
+ bucket->key = oj_strndup(args->key, args->len);
245
+ }
246
+ bucket->len = args->len;
247
+ bucket->val = clas;
248
+
249
+ return clas;
250
+ }
251
+
252
+ static KeyVal find_class(KeyVal bucket, const char *key, size_t len, VALUE *clas) {
253
+ if (NULL != bucket->key) { // not the top slot
254
+ for (KeyVal b = bucket; 0 != b; b = b->next) {
255
+ if (len == b->len && 0 == memcmp(b->key, key, len)) {
256
+ *clas = b->val;
257
+ return NULL;
258
+ }
259
+ bucket = b;
260
+ }
261
+ }
262
+ return bucket;
263
+ }
264
+
246
265
  VALUE oj_class_intern(const char *key, size_t len, bool safe, ParseInfo pi, int auto_define, VALUE error_class) {
247
- uint64_t h = hash_calc((const uint8_t *)key, len) & HASH_MASK;
248
- KeyVal bucket = class_hash.slots + h;
249
- KeyVal b;
266
+ uint64_t h = oj_hash_calc((const uint8_t *)key, len) & HASH_MASK;
267
+ struct _internArgs args = {NULL, key, len, pi, auto_define, error_class};
268
+ VALUE clas = Qundef;
250
269
 
251
270
  if (safe) {
271
+ int state = 0;
272
+
252
273
  #if HAVE_PTHREAD_MUTEX_INIT
253
274
  pthread_mutex_lock(&class_hash.mutex);
254
275
  #else
255
276
  rb_mutex_lock(class_hash.mutex);
256
277
  #endif
257
- if (NULL != bucket->key) { // not the top slot
258
- for (b = bucket; 0 != b; b = b->next) {
259
- if (len == b->len && 0 == strncmp(b->key, key, len)) {
260
- #if HAVE_PTHREAD_MUTEX_INIT
261
- pthread_mutex_unlock(&class_hash.mutex);
262
- #else
263
- rb_mutex_unlock(class_hash.mutex);
264
- #endif
265
- return b->val;
266
- }
267
- bucket = b;
268
- }
269
- b = OJ_R_ALLOC(struct _keyVal);
270
- b->next = NULL;
271
- bucket->next = b;
272
- bucket = b;
278
+ // Resolving calls into Ruby and allocating, either of which can
279
+ // raise and unwind past the unlock, so the work is protected and any
280
+ // exception rethrown after the mutex is released.
281
+ if (NULL != (args.bucket = find_class(class_hash.slots + h, key, len, &clas))) {
282
+ clas = rb_protect(intern_new_class, (VALUE)&args, &state);
273
283
  }
274
- bucket->key = oj_strndup(key, len);
275
- bucket->len = len;
276
- bucket->val = resolve_classpath(pi, key, len, auto_define, error_class);
277
284
  #if HAVE_PTHREAD_MUTEX_INIT
278
285
  pthread_mutex_unlock(&class_hash.mutex);
279
286
  #else
280
287
  rb_mutex_unlock(class_hash.mutex);
281
288
  #endif
282
- } else {
283
- if (NULL != bucket->key) {
284
- for (b = bucket; 0 != b; b = b->next) {
285
- if (len == b->len && 0 == strncmp(b->key, key, len)) {
286
- return (ID)b->val;
287
- }
288
- bucket = b;
289
- }
290
- b = OJ_R_ALLOC(struct _keyVal);
291
- b->next = NULL;
292
- bucket->next = b;
293
- bucket = b;
289
+ if (0 != state) {
290
+ rb_jump_tag(state);
294
291
  }
295
- bucket->key = oj_strndup(key, len);
296
- bucket->len = len;
297
- bucket->val = resolve_classpath(pi, key, len, auto_define, error_class);
292
+ } else if (NULL != (args.bucket = find_class(class_hash.slots + h, key, len, &clas))) {
293
+ clas = intern_new_class((VALUE)&args);
294
+ }
295
+ if (NULL != args.bucket && Qundef != clas) { // newly added to the cache
296
+ rb_gc_register_mark_object(clas);
298
297
  }
299
- rb_gc_register_mark_object(bucket->val);
300
- return bucket->val;
298
+ return clas;
301
299
  }
302
300
 
303
301
  char *oj_strndup(const char *s, size_t len) {
data/ext/oj/mimic_json.c CHANGED
@@ -241,12 +241,7 @@ static VALUE mimic_dump(int argc, VALUE *argv, VALUE self) {
241
241
  // default ::JSON::State argument is passed in. Basically a hack to get
242
242
  // around the active support hack so two wrongs make a right this time.
243
243
  active_hack[0] = rb_funcall(state_class, oj_new_id, 0);
244
- oj_dump_obj_to_json_using_params(*argv, &copts, &out, 1, active_hack);
245
-
246
- if (0 == out.buf) {
247
- rb_raise(rb_eNoMemError, "Not enough memory.");
248
- }
249
- rstr = rb_utf8_str_new_cstr(out.buf);
244
+ rstr = oj_dump_obj_to_str(*argv, &copts, &out, 1, active_hack);
250
245
  if (2 <= argc && Qnil != argv[1] && rb_respond_to(argv[1], oj_write_id)) {
251
246
  VALUE io = argv[1];
252
247
  VALUE args[1];
@@ -255,9 +250,6 @@ static VALUE mimic_dump(int argc, VALUE *argv, VALUE self) {
255
250
  rb_funcall2(io, oj_write_id, 1, args);
256
251
  rstr = io;
257
252
  }
258
-
259
- oj_out_free(&out);
260
-
261
253
  return rstr;
262
254
  }
263
255
 
@@ -380,7 +372,7 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
380
372
  }
381
373
  */
382
374
  if (1 < argc) {
383
- oj_dump_obj_to_json_using_params(*argv, copts, &out, argc - 1, argv + 1);
375
+ rstr = oj_dump_obj_to_str(*argv, copts, &out, argc - 1, argv + 1);
384
376
  } else {
385
377
  VALUE active_hack[1];
386
378
 
@@ -390,15 +382,8 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
390
382
  oj_define_mimic_json(0, NULL, Qnil);
391
383
  }
392
384
  active_hack[0] = rb_funcall(state_class, oj_new_id, 0);
393
- oj_dump_obj_to_json_using_params(*argv, copts, &out, 1, active_hack);
394
- }
395
- if (0 == out.buf) {
396
- rb_raise(rb_eNoMemError, "Not enough memory.");
385
+ rstr = oj_dump_obj_to_str(*argv, copts, &out, 1, active_hack);
397
386
  }
398
- rstr = rb_utf8_str_new_cstr(out.buf);
399
-
400
- oj_out_free(&out);
401
-
402
387
  return rstr;
403
388
  }
404
389
 
@@ -461,7 +446,7 @@ oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
461
446
  if (1 == argc || Qnil == argv[1]) {
462
447
  h = rb_hash_new();
463
448
  } else {
464
- h = argv[1];
449
+ h = rb_hash_dup(argv[1]);
465
450
  }
466
451
  if (!oj_hash_has_key(h, oj_indent_sym)) {
467
452
  rb_hash_aset(h, oj_indent_sym, rb_str_new2(" "));
@@ -748,7 +733,6 @@ static struct _options mimic_object_to_json_options = {0, // indent
748
733
 
749
734
  static VALUE mimic_object_to_json(int argc, VALUE *argv, VALUE self) {
750
735
  struct _out out;
751
- VALUE rstr;
752
736
  struct _options copts = oj_default_options;
753
737
 
754
738
  copts.str_rx.head = NULL;
@@ -765,15 +749,7 @@ static VALUE mimic_object_to_json(int argc, VALUE *argv, VALUE self) {
765
749
  // To be strict the mimic_object_to_json_options should be used but people
766
750
  // seem to prefer the option of changing that.
767
751
  // oj_dump_obj_to_json(self, &mimic_object_to_json_options, &out);
768
- oj_dump_obj_to_json_using_params(self, &copts, &out, argc, argv);
769
- if (NULL == out.buf) {
770
- rb_raise(rb_eNoMemError, "Not enough memory.");
771
- }
772
- rstr = rb_utf8_str_new_cstr(out.buf);
773
-
774
- oj_out_free(&out);
775
-
776
- return rstr;
752
+ return oj_dump_obj_to_str(self, &copts, &out, argc, argv);
777
753
  }
778
754
 
779
755
  /* Document-method: state
data/ext/oj/object.c CHANGED
@@ -213,8 +213,12 @@ static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t
213
213
  switch (key[1]) {
214
214
  case 'o': // object
215
215
  { // name2class sets an error if the class is not found or created
216
- VALUE clas = oj_name2class(pi, str, len, Yes == pi->options.auto_define, rb_eArgError);
216
+ VALUE clas;
217
217
 
218
+ if (oj_is_backtrace_name(str, len)) {
219
+ break;
220
+ }
221
+ clas = oj_name2class(pi, str, len, Yes == pi->options.auto_define, rb_eArgError);
218
222
  if (Qundef != clas) {
219
223
  parent->val = rb_obj_alloc(clas);
220
224
  }
@@ -346,9 +350,23 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
346
350
  }
347
351
  if (sc == rb_cRange) {
348
352
  parent->val = rb_class_new_instance((int)(len - 1), RARRAY_CONST_PTR(value) + 1, rb_cRange);
353
+ } else if (Qtrue == rb_class_inherited_p(sc, rb_cRange)) {
354
+ volatile VALUE args = rb_ary_new_from_values((long)len, RARRAY_CONST_PTR(value));
355
+ volatile VALUE init = rb_funcall(rb_cRange,
356
+ rb_intern("instance_method"),
357
+ 1,
358
+ ID2SYM(rb_intern("initialize")));
359
+
360
+ parent->val = rb_obj_alloc(sc);
361
+ rb_ary_store(args, 0, parent->val);
362
+ rb_apply(init, rb_intern("bind_call"), args);
349
363
  } else {
350
364
  // Create a properly initialized struct instance without calling the initialize method.
351
365
  parent->val = rb_obj_alloc(sc);
366
+ if (T_STRUCT != rb_type(parent->val)) {
367
+ oj_set_error_at(pi, rb_eArgError, __FILE__, __LINE__, "%s is not a Struct", rb_class2name(sc));
368
+ return 1;
369
+ }
352
370
  // If the JSON array has more entries than the struct class allows, we record an error.
353
371
  #ifdef RSTRUCT_LEN
354
372
  #if RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
@@ -393,7 +411,8 @@ void oj_set_obj_ivar(Val parent, Val kval, VALUE value) {
393
411
  parent->val = rb_funcall(parent->val, rb_intern("exception"), 1, value);
394
412
  } else if (kval->klen == 3 && strncmp("~bt", kval->key, 3) == 0 && rb_obj_is_kind_of(parent->val, rb_eException)) {
395
413
  rb_funcall(parent->val, rb_intern("set_backtrace"), 1, value);
396
- } else {
414
+ } else if (kval->klen != 13 || strncmp("~bt_locations", kval->key, 13) != 0 ||
415
+ !rb_obj_is_kind_of(parent->val, rb_eException)) {
397
416
  rb_ivar_set(parent->val, oj_attr_intern(kval->key, kval->klen), value);
398
417
  }
399
418
  }
data/ext/oj/oj.c CHANGED
@@ -1241,45 +1241,84 @@ static bool parse_one_option(VALUE k, VALUE v, Options copts) {
1241
1241
  return true;
1242
1242
  }
1243
1243
 
1244
- // Collects the return of parse_one_option() over every key in a hash.
1244
+ // Collects the results of parse_one_option() over every key in a hash.
1245
1245
  struct _optsParse {
1246
1246
  Options copts;
1247
- bool consumed;
1247
+ bool consumed; // some key was an Oj option
1248
+ VALUE orig; // the caller's hash, never modified
1249
+ VALUE rest; // orig, or a copy of it without the Oj options
1248
1250
  };
1249
1251
 
1252
+ static void drop_from_rest(struct _optsParse *op, VALUE k) {
1253
+ if (op->rest == op->orig) {
1254
+ op->rest = rb_hash_dup(op->orig);
1255
+ }
1256
+ rb_hash_delete(op->rest, k);
1257
+ }
1258
+
1250
1259
  static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1251
1260
  struct _optsParse *op = (struct _optsParse *)opts;
1252
1261
 
1253
1262
  if (parse_one_option(k, v, op->copts)) {
1254
1263
  op->consumed = true;
1264
+ // :only and :except are also read by ActiveSupport's as_json, which
1265
+ // compat mode reaches through to_json, so they are left in.
1266
+ if (only_sym != k && except_sym != k) {
1267
+ drop_from_rest(op, k);
1268
+ }
1255
1269
  }
1256
1270
  return ST_CONTINUE;
1257
1271
  }
1258
1272
 
1259
- // Apply the options in ropts to copts and report whether any key was
1260
- // recognized. A hash of nothing but unknown keys leaves copts untouched and
1261
- // returns false.
1262
- bool oj_parse_options_consumed(VALUE ropts, Options copts) {
1263
- struct _optsParse op = {copts, false};
1273
+ static void parse_options(VALUE ropts, Options copts, struct _optsParse *op) {
1274
+ op->copts = copts;
1275
+ op->consumed = false;
1276
+ op->orig = ropts;
1277
+ op->rest = ropts;
1264
1278
 
1265
1279
  if (T_HASH != rb_type(ropts)) {
1266
- return false;
1280
+ return;
1267
1281
  }
1268
- rb_hash_foreach(ropts, parse_options_cb, (VALUE)&op);
1282
+ rb_hash_foreach(ropts, parse_options_cb, (VALUE)op);
1269
1283
  if (Qnil != rb_hash_lookup(ropts, match_string_sym)) {
1270
- op.consumed = true;
1284
+ op->consumed = true;
1285
+ drop_from_rest(op, match_string_sym);
1271
1286
  }
1272
1287
  oj_parse_opt_match_string(&copts->str_rx, ropts);
1273
1288
 
1274
1289
  copts->dump_opts.use = (0 < copts->dump_opts.indent_size || 0 < copts->dump_opts.after_size ||
1275
1290
  0 < copts->dump_opts.before_size || 0 < copts->dump_opts.hash_size ||
1276
1291
  0 < copts->dump_opts.array_size);
1292
+ }
1293
+
1294
+ // Apply the options in ropts to copts and report whether any key was
1295
+ // recognized. A hash of nothing but unknown keys leaves copts untouched and
1296
+ // returns false.
1297
+ bool oj_parse_options_consumed(VALUE ropts, Options copts) {
1298
+ struct _optsParse op;
1299
+
1300
+ parse_options(ropts, copts, &op);
1277
1301
 
1278
1302
  return op.consumed;
1279
1303
  }
1280
1304
 
1305
+ // Apply the options in ropts to copts and return what is left for the
1306
+ // to_json and as_json methods that Oj.dump() calls: ropts itself when it
1307
+ // holds no Oj option, otherwise a copy without them. The json gem's
1308
+ // generator raises on options it does not know as of json 3.0, so Oj's own
1309
+ // must not reach it. ropts is never modified.
1310
+ VALUE oj_parse_options_rest(VALUE ropts, Options copts) {
1311
+ struct _optsParse op;
1312
+
1313
+ parse_options(ropts, copts, &op);
1314
+
1315
+ return op.rest;
1316
+ }
1317
+
1281
1318
  void oj_parse_options(VALUE ropts, Options copts) {
1282
- oj_parse_options_consumed(ropts, copts);
1319
+ struct _optsParse op;
1320
+
1321
+ parse_options(ropts, copts, &op);
1283
1322
  }
1284
1323
 
1285
1324
  // Free the option buffers that oj_parse_options() allocated for a single call.
@@ -1550,7 +1589,7 @@ static VALUE load_file(int argc, VALUE *argv, VALUE self) {
1550
1589
  if (1 > argc) {
1551
1590
  rb_raise(rb_eArgError, "Wrong number of arguments to load().");
1552
1591
  }
1553
- path = StringValuePtr(*argv);
1592
+ path = StringValueCStr(*argv);
1554
1593
  parse_info_init(&pi);
1555
1594
  pi.options = oj_default_options;
1556
1595
  pi.handler = Qnil;
@@ -1703,6 +1742,7 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
1703
1742
  struct dump_arg arg;
1704
1743
  struct _out out;
1705
1744
  struct _options copts = oj_default_options;
1745
+ VALUE to_json_argv[2];
1706
1746
 
1707
1747
  if (1 > argc) {
1708
1748
  rb_raise(rb_eArgError, "wrong number of arguments (0 for 1).");
@@ -1711,7 +1751,11 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
1711
1751
  copts.dump_opts.nan_dump = WordNan;
1712
1752
  }
1713
1753
  if (2 == argc) {
1714
- oj_parse_options(argv[1], &copts);
1754
+ // The options hash is also what to_json and as_json receive, so
1755
+ // hand them the hash without the options Oj itself acted on.
1756
+ to_json_argv[0] = argv[0];
1757
+ to_json_argv[1] = oj_parse_options_rest(argv[1], &copts);
1758
+ argv = to_json_argv;
1715
1759
  }
1716
1760
  if (CompatMode == copts.mode && copts.escape_mode != ASCIIEsc) {
1717
1761
  copts.escape_mode = JSONEsc;
@@ -1755,7 +1799,6 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
1755
1799
  static VALUE to_json(int argc, VALUE *argv, VALUE self) {
1756
1800
  struct _out out;
1757
1801
  struct _options copts = oj_default_options;
1758
- VALUE rstr;
1759
1802
 
1760
1803
  if (1 > argc) {
1761
1804
  rb_raise(rb_eArgError, "wrong number of arguments (0 for 1).");
@@ -1774,16 +1817,71 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
1774
1817
  out.omit_null_byte = copts.dump_opts.omit_null_byte;
1775
1818
  // For obj.to_json or generate nan is not allowed but if called from dump
1776
1819
  // it is.
1777
- oj_dump_obj_to_json_using_params(*argv, &copts, &out, argc - 1, argv + 1);
1820
+ return oj_dump_obj_to_str(*argv, &copts, &out, argc - 1, argv + 1);
1821
+ }
1778
1822
 
1779
- if (0 == out.buf) {
1780
- rb_raise(rb_eNoMemError, "Not enough memory.");
1823
+ struct option_buffers {
1824
+ const char *create_id;
1825
+ VALUE *ignore;
1826
+ const char *only;
1827
+ const char *except;
1828
+ };
1829
+
1830
+ struct write_call_arg {
1831
+ Options copts;
1832
+ int argc;
1833
+ VALUE *argv;
1834
+ struct option_buffers base;
1835
+ struct option_buffers own;
1836
+ bool owned;
1837
+ };
1838
+
1839
+ static void option_buffers_get(Options copts, struct option_buffers *bufs) {
1840
+ bufs->create_id = copts->create_id;
1841
+ bufs->ignore = copts->ignore;
1842
+ bufs->only = copts->dump_opts.only;
1843
+ bufs->except = copts->dump_opts.except;
1844
+ }
1845
+
1846
+ static void write_call_options(struct write_call_arg *arg) {
1847
+ oj_options_take_ownership(arg->copts);
1848
+ option_buffers_get(arg->copts, &arg->own);
1849
+ arg->owned = true;
1850
+ if (3 == arg->argc) {
1851
+ oj_parse_options(arg->argv[2], arg->copts);
1781
1852
  }
1782
- rstr = rb_utf8_str_new_cstr(out.buf);
1853
+ }
1783
1854
 
1784
- oj_out_free(&out);
1855
+ static void free_call_buffer(const void *cur, const void *own, const void *base) {
1856
+ if (NULL != cur && base != cur) {
1857
+ OJ_R_FREE((void *)cur);
1858
+ }
1859
+ if (NULL != own && base != own && cur != own) {
1860
+ OJ_R_FREE((void *)own);
1861
+ }
1862
+ }
1785
1863
 
1786
- return rstr;
1864
+ static VALUE write_call_ensure(VALUE a) {
1865
+ struct write_call_arg *arg = (struct write_call_arg *)a;
1866
+ Options copts = arg->copts;
1867
+ struct option_buffers none = {NULL, NULL, NULL, NULL};
1868
+ struct option_buffers *base = arg->owned ? &none : &arg->base;
1869
+
1870
+ free_call_buffer(copts->create_id, arg->own.create_id, base->create_id);
1871
+ free_call_buffer(copts->ignore, arg->own.ignore, base->ignore);
1872
+ free_call_buffer(copts->dump_opts.only, arg->own.only, base->only);
1873
+ free_call_buffer(copts->dump_opts.except, arg->own.except, base->except);
1874
+
1875
+ return Qnil;
1876
+ }
1877
+
1878
+ static VALUE to_file_body(VALUE a) {
1879
+ struct write_call_arg *arg = (struct write_call_arg *)a;
1880
+
1881
+ write_call_options(arg);
1882
+ oj_write_obj_to_file(arg->argv[1], StringValueCStr(*arg->argv), arg->copts);
1883
+
1884
+ return Qnil;
1787
1885
  }
1788
1886
 
1789
1887
  /* Document-method: to_file
@@ -1797,13 +1895,21 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
1797
1895
  * - *:circular* [_Boolean_] allow circular references, default: false
1798
1896
  */
1799
1897
  static VALUE to_file(int argc, VALUE *argv, VALUE self) {
1800
- struct _options copts = oj_default_options;
1898
+ struct _options copts;
1899
+ struct write_call_arg arg = {&copts, argc, argv};
1801
1900
 
1802
- if (3 == argc) {
1803
- oj_parse_options(argv[2], &copts);
1804
- }
1805
- oj_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
1806
- oj_free_call_options(&copts);
1901
+ StringValueCStr(*argv);
1902
+ copts = oj_default_options;
1903
+ option_buffers_get(&copts, &arg.base);
1904
+
1905
+ return rb_ensure(to_file_body, (VALUE)&arg, write_call_ensure, (VALUE)&arg);
1906
+ }
1907
+
1908
+ static VALUE to_stream_body(VALUE a) {
1909
+ struct write_call_arg *arg = (struct write_call_arg *)a;
1910
+
1911
+ write_call_options(arg);
1912
+ oj_write_obj_to_stream(arg->argv[1], *arg->argv, arg->copts);
1807
1913
 
1808
1914
  return Qnil;
1809
1915
  }
@@ -1819,15 +1925,12 @@ static VALUE to_file(int argc, VALUE *argv, VALUE self) {
1819
1925
  * - *:circular* [_Boolean_] allow circular references, default: false
1820
1926
  */
1821
1927
  static VALUE to_stream(int argc, VALUE *argv, VALUE self) {
1822
- struct _options copts = oj_default_options;
1928
+ struct _options copts = oj_default_options;
1929
+ struct write_call_arg arg = {&copts, argc, argv};
1823
1930
 
1824
- if (3 == argc) {
1825
- oj_parse_options(argv[2], &copts);
1826
- }
1827
- oj_write_obj_to_stream(argv[1], *argv, &copts);
1828
- oj_free_call_options(&copts);
1931
+ option_buffers_get(&copts, &arg.base);
1829
1932
 
1830
- return Qnil;
1933
+ return rb_ensure(to_stream_body, (VALUE)&arg, write_call_ensure, (VALUE)&arg);
1831
1934
  }
1832
1935
 
1833
1936
  /* Document-method: register_odd
data/ext/oj/oj.h CHANGED
@@ -255,16 +255,18 @@ extern VALUE oj_compat_parse_cstr(int argc, VALUE *argv, char *json, size_t len)
255
255
  extern VALUE oj_object_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
256
256
  extern VALUE oj_custom_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
257
257
 
258
- extern bool oj_hash_has_key(VALUE hash, VALUE key);
259
- extern void oj_parse_options(VALUE ropts, Options copts);
260
- extern bool oj_parse_options_consumed(VALUE ropts, Options copts);
261
- extern void oj_free_call_options(Options copts);
262
- extern void oj_options_take_ownership(Options copts);
263
- extern void oj_options_release(Options copts);
264
- extern void oj_options_mark(Options copts);
258
+ extern bool oj_hash_has_key(VALUE hash, VALUE key);
259
+ extern void oj_parse_options(VALUE ropts, Options copts);
260
+ extern bool oj_parse_options_consumed(VALUE ropts, Options copts);
261
+ extern VALUE oj_parse_options_rest(VALUE ropts, Options copts);
262
+ extern void oj_free_call_options(Options copts);
263
+ extern void oj_options_take_ownership(Options copts);
264
+ extern void oj_options_release(Options copts);
265
+ extern void oj_options_mark(Options copts);
265
266
 
266
267
  extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
267
268
  extern void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
269
+ extern VALUE oj_dump_obj_to_str(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
268
270
  extern void oj_write_obj_to_file(VALUE obj, const char *path, Options copts);
269
271
  extern void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts);
270
272
  extern void oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out);