oj 2.13.1 → 2.14.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.
@@ -90,6 +90,8 @@ ID oj_utc_offset_id;
90
90
  ID oj_utcq_id;
91
91
  ID oj_write_id;
92
92
 
93
+ static ID has_key_id;
94
+
93
95
  VALUE oj_bag_class;
94
96
  VALUE oj_bigdecimal_class;
95
97
  VALUE oj_cstack_class;
@@ -160,26 +162,38 @@ VALUE oj_cache_mutex = Qnil;
160
162
  static const char json_class[] = "json_class";
161
163
 
162
164
  struct _Options oj_default_options = {
163
- 0, // indent
164
- No, // circular
165
- No, // auto_define
166
- No, // sym_key
167
- JSONEsc, // escape_mode
168
- ObjectMode, // mode
169
- Yes, // class_cache
170
- UnixZTime, // time_format
171
- Yes, // bigdec_as_num
172
- AutoDec, // bigdec_load
173
- Yes, // to_json
174
- No, // nilnil
175
- Yes, // allow_gc
176
- Yes, // quirks_mode
177
- json_class, // create_id
178
- 10, // create_id_len
179
- 9, // sec_prec
180
- 0, // dump_opts
181
- 15, // float_prec
182
- "%0.15g", // float_fmt
165
+ 0, // indent
166
+ No, // circular
167
+ No, // auto_define
168
+ No, // sym_key
169
+ JSONEsc, // escape_mode
170
+ ObjectMode, // mode
171
+ Yes, // class_cache
172
+ UnixZTime, // time_format
173
+ Yes, // bigdec_as_num
174
+ AutoDec, // bigdec_load
175
+ Yes, // to_json
176
+ No, // nilnil
177
+ Yes, // allow_gc
178
+ Yes, // quirks_mode
179
+ json_class, // create_id
180
+ 10, // create_id_len
181
+ 9, // sec_prec
182
+ 15, // float_prec
183
+ "%0.15g", // float_fmt
184
+ { // dump_opts
185
+ false, //use
186
+ "", // indent
187
+ "", // before_sep
188
+ "", // after_sep
189
+ "", // hash_nl
190
+ "", // array_nl
191
+ 0, // indent_size
192
+ 0, // before_size
193
+ 0, // after_size
194
+ 0, // hash_size
195
+ 0, // array_size
196
+ }
183
197
  };
184
198
 
185
199
  static VALUE define_mimic_json(int argc, VALUE *argv, VALUE self);
@@ -187,7 +201,7 @@ static VALUE define_mimic_json(int argc, VALUE *argv, VALUE self);
187
201
  /* call-seq: default_options() => Hash
188
202
  *
189
203
  * Returns the default load and dump options as a Hash. The options are
190
- * - indent: [Fixnum] number of spaces to indent each element in an JSON document, zero is no newline between JSON elements, negative indicates no newline between top level JSON elements in a stream
204
+ * - indent: [Fixnum|String|nil] number of spaces to indent each element in an JSON document, zero or nil is no newline between JSON elements, negative indicates no newline between top level JSON elements in a stream, a String indicates the string should be used for indentation
191
205
  * - circular: [true|false|nil] support circular references while dumping
192
206
  * - auto_define: [true|false|nil] automatically define classes if they do not exist
193
207
  * - symbol_keys: [true|false|nil] use symbols instead of strings for hash keys
@@ -204,13 +218,22 @@ static VALUE define_mimic_json(int argc, VALUE *argv, VALUE self);
204
218
  * - nilnil: [true|false|nil] if true a nil input to load will return nil and not raise an Exception
205
219
  * - allow_gc: [true|false|nil] allow or prohibit GC during parsing, default is true (allow)
206
220
  * - quirks_mode: [true,|false|nil] Allow single JSON values instead of documents, default is true (allow)
221
+ * - indent_str: [String|nil] String to use for indentation, overriding the indent option is not nil
222
+ * - space: [String|nil] String to use for the space after the colon in JSON object fields
223
+ * - space_before: [String|nil] String to use before the colon separator in JSON object fields
224
+ * - object_nl: [String|nil] String to use after a JSON object field value
225
+ * - array_nl: [String|nil] String to use after a JSON array value
207
226
  * @return [Hash] all current option settings.
208
227
  */
209
228
  static VALUE
210
229
  get_def_opts(VALUE self) {
211
230
  VALUE opts = rb_hash_new();
212
-
213
- rb_hash_aset(opts, indent_sym, INT2FIX(oj_default_options.indent));
231
+
232
+ if (0 == oj_default_options.dump_opts.indent_size) {
233
+ rb_hash_aset(opts, indent_sym, INT2FIX(oj_default_options.indent));
234
+ } else {
235
+ rb_hash_aset(opts, indent_sym, rb_str_new2(oj_default_options.dump_opts.indent_str));
236
+ }
214
237
  rb_hash_aset(opts, sec_prec_sym, INT2FIX(oj_default_options.sec_prec));
215
238
  rb_hash_aset(opts, circular_sym, (Yes == oj_default_options.circular) ? Qtrue : ((No == oj_default_options.circular) ? Qfalse : Qnil));
216
239
  rb_hash_aset(opts, class_cache_sym, (Yes == oj_default_options.class_cache) ? Qtrue : ((No == oj_default_options.class_cache) ? Qfalse : Qnil));
@@ -250,6 +273,10 @@ get_def_opts(VALUE self) {
250
273
  default: rb_hash_aset(opts, bigdecimal_load_sym, auto_sym); break;
251
274
  }
252
275
  rb_hash_aset(opts, create_id_sym, (0 == oj_default_options.create_id) ? Qnil : rb_str_new2(oj_default_options.create_id));
276
+ rb_hash_aset(opts, space_sym, (0 == oj_default_options.dump_opts.after_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.after_sep));
277
+ rb_hash_aset(opts, space_before_sym, (0 == oj_default_options.dump_opts.before_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.before_sep));
278
+ rb_hash_aset(opts, object_nl_sym, (0 == oj_default_options.dump_opts.hash_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.hash_nl));
279
+ rb_hash_aset(opts, array_nl_sym, (0 == oj_default_options.dump_opts.array_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.array_nl));
253
280
 
254
281
  return opts;
255
282
  }
@@ -258,7 +285,7 @@ get_def_opts(VALUE self) {
258
285
  *
259
286
  * Sets the default options for load and dump.
260
287
  * @param [Hash] opts options to change
261
- * @param [Fixnum] :indent number of spaces to indent each element in an JSON document
288
+ * @param [Fixnum|String|nil] :indent number of spaces to indent each element in a JSON document or the String to use for identation.
262
289
  * @param [true|false|nil] :circular support circular references while dumping
263
290
  * @param [true|false|nil] :auto_define automatically define classes if they do not exist
264
291
  * @param [true|false|nil] :symbol_keys convert hash keys to symbols
@@ -289,314 +316,247 @@ get_def_opts(VALUE self) {
289
316
  * @param [true|false|nil] :nilnil if true a nil input to load will return nil and not raise an Exception
290
317
  * @param [true|false|nil] :allow_gc allow or prohibit GC during parsing, default is true (allow)
291
318
  * @param [true|false|nil] :quirks_mode allow single JSON values instead of documents, default is true (allow)
319
+ * @param [String|nil] :space String to use for the space after the colon in JSON object fields
320
+ * @param [String|nil] :space_before String to use before the colon separator in JSON object fields
321
+ * @param [String|nil] :object_nl String to use after a JSON object field value
322
+ * @param [String|nil] :array_nl String to use after a JSON array value
292
323
  * @return [nil]
293
324
  */
294
325
  static VALUE
295
326
  set_def_opts(VALUE self, VALUE opts) {
327
+ Check_Type(opts, T_HASH);
328
+ oj_parse_options(opts, &oj_default_options);
329
+
330
+ return Qnil;
331
+ }
332
+
333
+ void
334
+ oj_parse_options(VALUE ropts, Options copts) {
296
335
  struct _YesNoOpt ynos[] = {
297
- { circular_sym, &oj_default_options.circular },
298
- { auto_define_sym, &oj_default_options.auto_define },
299
- { symbol_keys_sym, &oj_default_options.sym_key },
300
- { class_cache_sym, &oj_default_options.class_cache },
301
- { bigdecimal_as_decimal_sym, &oj_default_options.bigdec_as_num },
302
- { use_to_json_sym, &oj_default_options.to_json },
303
- { nilnil_sym, &oj_default_options.nilnil },
304
- { allow_gc_sym, &oj_default_options.allow_gc },
305
- { quirks_mode_sym, &oj_default_options.quirks_mode },
336
+ { circular_sym, &copts->circular },
337
+ { auto_define_sym, &copts->auto_define },
338
+ { symbol_keys_sym, &copts->sym_key },
339
+ { class_cache_sym, &copts->class_cache },
340
+ { bigdecimal_as_decimal_sym, &copts->bigdec_as_num },
341
+ { use_to_json_sym, &copts->to_json },
342
+ { nilnil_sym, &copts->nilnil },
343
+ { allow_gc_sym, &copts->allow_gc },
344
+ { quirks_mode_sym, &copts->quirks_mode },
306
345
  { Qnil, 0 }
307
346
  };
308
- YesNoOpt o;
309
- VALUE v;
347
+ YesNoOpt o;
348
+ volatile VALUE v;
349
+ size_t len;
310
350
 
311
- Check_Type(opts, T_HASH);
312
- v = rb_hash_aref(opts, indent_sym);
313
- if (Qnil != v) {
314
- Check_Type(v, T_FIXNUM);
315
- oj_default_options.indent = FIX2INT(v);
351
+ if (T_HASH != rb_type(ropts)) {
352
+ return;
316
353
  }
317
- v = rb_hash_aref(opts, float_prec_sym);
318
- if (Qnil != v) {
354
+ if (Qtrue == rb_funcall(ropts, has_key_id, 1, indent_sym)) {
355
+ v = rb_hash_lookup(ropts, indent_sym);
356
+ switch (rb_type(v)) {
357
+ case T_NIL:
358
+ copts->dump_opts.indent_size = 0;
359
+ *copts->dump_opts.indent_str = '\0';
360
+ copts->indent = 0;
361
+ break;
362
+ case T_FIXNUM:
363
+ copts->dump_opts.indent_size = 0;
364
+ *copts->dump_opts.indent_str = '\0';
365
+ copts->indent = FIX2INT(v);
366
+ break;
367
+ case T_STRING:
368
+ if (sizeof(copts->dump_opts.indent_str) <= (len = RSTRING_LEN(v))) {
369
+ rb_raise(rb_eArgError, "indent string is limited to %lu characters.", sizeof(copts->dump_opts.indent_str));
370
+ }
371
+ strcpy(copts->dump_opts.indent_str, StringValuePtr(v));
372
+ copts->dump_opts.indent_size = (uint8_t)len;
373
+ copts->indent = 0;
374
+ break;
375
+ default:
376
+ rb_raise(rb_eTypeError, "indent must be a Fixnum, String, or nil.");
377
+ break;
378
+ }
379
+ }
380
+ if (Qnil != (v = rb_hash_lookup(ropts, float_prec_sym))) {
319
381
  int n;
320
382
 
383
+ if (rb_cFixnum != rb_obj_class(v)) {
384
+ rb_raise(rb_eArgError, ":float_precision must be a Fixnum.");
385
+ }
321
386
  Check_Type(v, T_FIXNUM);
322
387
  n = FIX2INT(v);
323
388
  if (0 >= n) {
324
- *oj_default_options.float_fmt = '\0';
325
- oj_default_options.float_prec = 0;
389
+ *copts->float_fmt = '\0';
390
+ copts->float_prec = 0;
326
391
  } else {
327
392
  if (20 < n) {
328
393
  n = 20;
329
394
  }
330
- sprintf(oj_default_options.float_fmt, "%%0.%dg", n);
331
- oj_default_options.float_prec = n;
395
+ sprintf(copts->float_fmt, "%%0.%dg", n);
396
+ copts->float_prec = n;
332
397
  }
333
398
  }
334
- v = rb_hash_aref(opts, sec_prec_sym);
335
- if (Qnil != v) {
399
+ if (Qnil != (v = rb_hash_lookup(ropts, sec_prec_sym))) {
336
400
  int n;
337
401
 
338
- Check_Type(v, T_FIXNUM);
339
- n = FIX2INT(v);
402
+ if (rb_cFixnum != rb_obj_class(v)) {
403
+ rb_raise(rb_eArgError, ":second_precision must be a Fixnum.");
404
+ }
405
+ n = NUM2INT(v);
340
406
  if (0 > n) {
341
407
  n = 0;
342
408
  } else if (9 < n) {
343
409
  n = 9;
344
410
  }
345
- oj_default_options.sec_prec = n;
346
- }
347
-
348
- v = rb_hash_lookup(opts, mode_sym);
349
- if (Qnil == v) {
350
- // ignore
351
- } else if (object_sym == v) {
352
- oj_default_options.mode = ObjectMode;
353
- } else if (strict_sym == v) {
354
- oj_default_options.mode = StrictMode;
355
- } else if (compat_sym == v) {
356
- oj_default_options.mode = CompatMode;
357
- } else if (null_sym == v) {
358
- oj_default_options.mode = NullMode;
359
- } else {
360
- rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.");
361
- }
362
-
363
- v = rb_hash_lookup(opts, time_format_sym);
364
- if (Qnil == v) {
365
- // ignore
366
- } else if (unix_sym == v) {
367
- oj_default_options.time_format = UnixTime;
368
- } else if (unix_zone_sym == v) {
369
- oj_default_options.time_format = UnixZTime;
370
- } else if (xmlschema_sym == v) {
371
- oj_default_options.time_format = XmlTime;
372
- } else if (ruby_sym == v) {
373
- oj_default_options.time_format = RubyTime;
374
- } else {
375
- rb_raise(rb_eArgError, ":time_format must be :unix, :unix_zone, :xmlschema, or :ruby.");
376
- }
377
-
378
- v = rb_hash_lookup(opts, escape_mode_sym);
379
- if (Qnil == v) {
380
- // ignore
381
- } else if (newline_sym == v) {
382
- oj_default_options.escape_mode = NLEsc;
383
- } else if (json_sym == v) {
384
- oj_default_options.escape_mode = JSONEsc;
385
- } else if (xss_safe_sym == v) {
386
- oj_default_options.escape_mode = XSSEsc;
387
- } else if (ascii_sym == v) {
388
- oj_default_options.escape_mode = ASCIIEsc;
389
- } else {
390
- rb_raise(rb_eArgError, ":escape_mode must be :newline, :json, :xss_safe, or :ascii.");
391
- }
392
-
393
- v = rb_hash_lookup(opts, bigdecimal_load_sym);
394
- if (Qnil == v) {
395
- // ignore
396
- } else if (bigdecimal_sym == v || Qtrue == v) {
397
- oj_default_options.bigdec_load = BigDec;
398
- } else if (float_sym == v) {
399
- oj_default_options.bigdec_load = FloatDec;
400
- } else if (auto_sym == v || Qfalse == v) {
401
- oj_default_options.bigdec_load = AutoDec;
402
- } else {
403
- rb_raise(rb_eArgError, ":bigdecimal_load must be :bigdecimal, :float, or :auto.");
411
+ copts->sec_prec = n;
412
+ }
413
+ if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
414
+ if (object_sym == v) {
415
+ copts->mode = ObjectMode;
416
+ } else if (strict_sym == v) {
417
+ copts->mode = StrictMode;
418
+ } else if (compat_sym == v) {
419
+ copts->mode = CompatMode;
420
+ } else if (null_sym == v) {
421
+ copts->mode = NullMode;
422
+ } else {
423
+ rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.");
424
+ }
404
425
  }
405
-
406
- if (Qtrue == rb_funcall(opts, rb_intern("has_key?"), 1, create_id_sym)) {
407
- if (0 != oj_default_options.create_id) {
426
+ if (Qnil != (v = rb_hash_lookup(ropts, time_format_sym))) {
427
+ if (unix_sym == v) {
428
+ copts->time_format = UnixTime;
429
+ } else if (unix_zone_sym == v) {
430
+ copts->time_format = UnixZTime;
431
+ } else if (xmlschema_sym == v) {
432
+ copts->time_format = XmlTime;
433
+ } else if (ruby_sym == v) {
434
+ copts->time_format = RubyTime;
435
+ } else {
436
+ rb_raise(rb_eArgError, ":time_format must be :unix, :unix_zone, :xmlschema, or :ruby.");
437
+ }
438
+ }
439
+ if (Qnil != (v = rb_hash_lookup(ropts, escape_mode_sym))) {
440
+ if (newline_sym == v) {
441
+ copts->escape_mode = NLEsc;
442
+ } else if (json_sym == v) {
443
+ copts->escape_mode = JSONEsc;
444
+ } else if (xss_safe_sym == v) {
445
+ copts->escape_mode = XSSEsc;
446
+ } else if (ascii_sym == v) {
447
+ copts->escape_mode = ASCIIEsc;
448
+ } else {
449
+ rb_raise(rb_eArgError, ":encoding must be :newline, :json, :xss_safe, or :ascii.");
450
+ }
451
+ }
452
+ if (Qnil != (v = rb_hash_lookup(ropts, bigdecimal_load_sym))) {
453
+ if (bigdecimal_sym == v || Qtrue == v) {
454
+ copts->bigdec_load = BigDec;
455
+ } else if (float_sym == v) {
456
+ copts->bigdec_load = FloatDec;
457
+ } else if (auto_sym == v || Qfalse == v) {
458
+ copts->bigdec_load = AutoDec;
459
+ } else {
460
+ rb_raise(rb_eArgError, ":bigdecimal_load must be :bigdecimal, :float, or :auto.");
461
+ }
462
+ }
463
+ if (Qtrue == rb_funcall(ropts, has_key_id, 1, create_id_sym)) {
464
+ v = rb_hash_lookup(ropts, create_id_sym);
465
+ if (Qnil == v) {
408
466
  if (json_class != oj_default_options.create_id) {
409
467
  xfree((char*)oj_default_options.create_id);
410
468
  }
411
- oj_default_options.create_id = 0;
412
- oj_default_options.create_id_len = 0;
413
- }
414
- v = rb_hash_lookup(opts, create_id_sym);
415
- if (Qnil != v) {
416
- size_t len = RSTRING_LEN(v) + 1;
417
-
418
- oj_default_options.create_id = ALLOC_N(char, len);
419
- strcpy((char*)oj_default_options.create_id, StringValuePtr(v));
420
- oj_default_options.create_id_len = len - 1;
469
+ copts->create_id = NULL;
470
+ copts->create_id_len = 0;
471
+ } else if (T_STRING == rb_type(v)) {
472
+ const char *str = StringValuePtr(v);
473
+
474
+ len = RSTRING_LEN(v);
475
+ if (len != copts->create_id_len ||
476
+ 0 != strcmp(copts->create_id, str)) {
477
+ copts->create_id = ALLOC_N(char, len + 1);
478
+ strcpy((char*)copts->create_id, str);
479
+ copts->create_id_len = len;
480
+ }
481
+ } else {
482
+ rb_raise(rb_eArgError, ":create_id must be string.");
421
483
  }
422
484
  }
423
-
424
485
  for (o = ynos; 0 != o->attr; o++) {
425
- if (Qtrue != rb_funcall(opts, rb_intern("has_key?"), 1, o->sym)) {
426
- continue;
427
- }
428
- if (Qnil != (v = rb_hash_lookup(opts, o->sym))) {
486
+ if (Qnil != (v = rb_hash_lookup(ropts, o->sym))) {
429
487
  if (Qtrue == v) {
430
488
  *o->attr = Yes;
431
489
  } else if (Qfalse == v) {
432
490
  *o->attr = No;
433
491
  } else {
434
- rb_raise(rb_eArgError, "%s must be true, false, or nil.", rb_id2name(SYM2ID(o->sym)));
492
+ rb_raise(rb_eArgError, "%s must be true or false.", rb_id2name(SYM2ID(o->sym)));
435
493
  }
436
494
  }
437
495
  }
438
- // This is here only for backwards compatibility with the original Oj.
439
- v = rb_hash_lookup(opts, ascii_only_sym);
440
- if (Qtrue == v) {
441
- oj_default_options.escape_mode = ASCIIEsc;
442
- } else if (Qfalse == v) {
443
- oj_default_options.escape_mode = JSONEsc;
444
- }
445
- return Qnil;
446
- }
447
-
448
- void
449
- oj_parse_options(VALUE ropts, Options copts) {
450
- struct _YesNoOpt ynos[] = {
451
- { circular_sym, &copts->circular },
452
- { auto_define_sym, &copts->auto_define },
453
- { symbol_keys_sym, &copts->sym_key },
454
- { class_cache_sym, &copts->class_cache },
455
- { bigdecimal_as_decimal_sym, &copts->bigdec_as_num },
456
- { use_to_json_sym, &copts->to_json },
457
- { nilnil_sym, &copts->nilnil },
458
- { allow_gc_sym, &copts->allow_gc },
459
- { quirks_mode_sym, &copts->quirks_mode },
460
- { Qnil, 0 }
461
- };
462
- YesNoOpt o;
463
-
464
- if (rb_cHash == rb_obj_class(ropts)) {
465
- VALUE v;
466
-
467
- if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) {
468
- if (rb_cFixnum != rb_obj_class(v)) {
469
- rb_raise(rb_eArgError, ":indent must be a Fixnum.");
470
- }
471
- copts->indent = NUM2INT(v);
472
- }
473
- if (Qnil != (v = rb_hash_lookup(ropts, float_prec_sym))) {
474
- int n;
475
-
476
- if (rb_cFixnum != rb_obj_class(v)) {
477
- rb_raise(rb_eArgError, ":float_precision must be a Fixnum.");
478
- }
479
- Check_Type(v, T_FIXNUM);
480
- n = FIX2INT(v);
481
- if (0 >= n) {
482
- *copts->float_fmt = '\0';
483
- copts->float_prec = 0;
484
- } else {
485
- if (20 < n) {
486
- n = 20;
487
- }
488
- sprintf(copts->float_fmt, "%%0.%dg", n);
489
- copts->float_prec = n;
490
- }
491
- }
492
- if (Qnil != (v = rb_hash_lookup(ropts, sec_prec_sym))) {
493
- int n;
494
-
495
- if (rb_cFixnum != rb_obj_class(v)) {
496
- rb_raise(rb_eArgError, ":second_precision must be a Fixnum.");
497
- }
498
- n = NUM2INT(v);
499
- if (0 > n) {
500
- n = 0;
501
- } else if (9 < n) {
502
- n = 9;
503
- }
504
- copts->sec_prec = n;
505
- }
506
- if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
507
- if (object_sym == v) {
508
- copts->mode = ObjectMode;
509
- } else if (strict_sym == v) {
510
- copts->mode = StrictMode;
511
- } else if (compat_sym == v) {
512
- copts->mode = CompatMode;
513
- } else if (null_sym == v) {
514
- copts->mode = NullMode;
515
- } else {
516
- rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.");
517
- }
518
- }
519
- if (Qnil != (v = rb_hash_lookup(ropts, time_format_sym))) {
520
- if (unix_sym == v) {
521
- copts->time_format = UnixTime;
522
- } else if (unix_zone_sym == v) {
523
- copts->time_format = UnixZTime;
524
- } else if (xmlschema_sym == v) {
525
- copts->time_format = XmlTime;
526
- } else if (ruby_sym == v) {
527
- copts->time_format = RubyTime;
528
- } else {
529
- rb_raise(rb_eArgError, ":time_format must be :unix, :unix_zone, :xmlschema, or :ruby.");
530
- }
531
- }
532
-
533
- if (Qnil != (v = rb_hash_lookup(ropts, escape_mode_sym))) {
534
- if (newline_sym == v) {
535
- copts->escape_mode = NLEsc;
536
- } else if (json_sym == v) {
537
- copts->escape_mode = JSONEsc;
538
- } else if (xss_safe_sym == v) {
539
- copts->escape_mode = XSSEsc;
540
- } else if (ascii_sym == v) {
541
- copts->escape_mode = ASCIIEsc;
542
- } else {
543
- rb_raise(rb_eArgError, ":encoding must be :newline, :json, :xss_safe, or :ascii.");
496
+ if (Qtrue == rb_funcall(ropts, has_key_id, 1, space_sym)) {
497
+ if (Qnil == (v = rb_hash_lookup(ropts, space_sym))) {
498
+ copts->dump_opts.after_size = 0;
499
+ *copts->dump_opts.after_sep = '\0';
500
+ } else {
501
+ rb_check_type(v, T_STRING);
502
+ if (sizeof(copts->dump_opts.after_sep) <= (len = RSTRING_LEN(v))) {
503
+ rb_raise(rb_eArgError, "space string is limited to %lu characters.", sizeof(copts->dump_opts.after_sep));
544
504
  }
505
+ strcpy(copts->dump_opts.after_sep, StringValuePtr(v));
506
+ copts->dump_opts.after_size = (uint8_t)len;
545
507
  }
546
-
547
- if (Qnil != (v = rb_hash_lookup(ropts, bigdecimal_load_sym))) {
548
- if (bigdecimal_sym == v || Qtrue == v) {
549
- copts->bigdec_load = BigDec;
550
- } else if (float_sym == v) {
551
- copts->bigdec_load = FloatDec;
552
- } else if (auto_sym == v || Qfalse == v) {
553
- copts->bigdec_load = AutoDec;
554
- } else {
555
- rb_raise(rb_eArgError, ":bigdecimal_load must be :bigdecimal, :float, or :auto.");
508
+ }
509
+ if (Qtrue == rb_funcall(ropts, has_key_id, 1, space_before_sym)) {
510
+ if (Qnil == (v = rb_hash_lookup(ropts, space_before_sym))) {
511
+ copts->dump_opts.before_size = 0;
512
+ *copts->dump_opts.before_sep = '\0';
513
+ } else {
514
+ rb_check_type(v, T_STRING);
515
+ if (sizeof(copts->dump_opts.before_sep) <= (len = RSTRING_LEN(v))) {
516
+ rb_raise(rb_eArgError, "sapce_before string is limited to %lu characters.", sizeof(copts->dump_opts.before_sep));
556
517
  }
518
+ strcpy(copts->dump_opts.before_sep, StringValuePtr(v));
519
+ copts->dump_opts.before_size = (uint8_t)len;
557
520
  }
558
-
559
- if (Qtrue == rb_funcall(ropts, rb_intern("has_key?"), 1, create_id_sym)) {
560
- v = rb_hash_lookup(ropts, create_id_sym);
561
- if (Qnil == v) {
562
- if (json_class != oj_default_options.create_id) {
563
- xfree((char*)oj_default_options.create_id);
564
- }
565
- copts->create_id = 0;
566
- copts->create_id_len = 0;
567
- } else if (T_STRING == rb_type(v)) {
568
- size_t len = RSTRING_LEN(v);
569
- const char *str = StringValuePtr(v);
570
-
571
- if (len != copts->create_id_len ||
572
- 0 != strcmp(copts->create_id, str)) {
573
- copts->create_id = ALLOC_N(char, len + 1);
574
- strcpy((char*)copts->create_id, str);
575
- copts->create_id_len = len;
576
- }
577
- } else {
578
- rb_raise(rb_eArgError, ":create_id must be string.");
521
+ }
522
+ if (Qtrue == rb_funcall(ropts, has_key_id, 1, object_nl_sym)) {
523
+ if (Qnil == (v = rb_hash_lookup(ropts, object_nl_sym))) {
524
+ copts->dump_opts.hash_size = 0;
525
+ *copts->dump_opts.hash_nl = '\0';
526
+ } else {
527
+ rb_check_type(v, T_STRING);
528
+ if (sizeof(copts->dump_opts.hash_nl) <= (len = RSTRING_LEN(v))) {
529
+ rb_raise(rb_eArgError, "object_nl string is limited to %lu characters.", sizeof(copts->dump_opts.hash_nl));
579
530
  }
531
+ strcpy(copts->dump_opts.hash_nl, StringValuePtr(v));
532
+ copts->dump_opts.hash_size = (uint8_t)len;
580
533
  }
581
- for (o = ynos; 0 != o->attr; o++) {
582
- if (Qnil != (v = rb_hash_lookup(ropts, o->sym))) {
583
- if (Qtrue == v) {
584
- *o->attr = Yes;
585
- } else if (Qfalse == v) {
586
- *o->attr = No;
587
- } else {
588
- rb_raise(rb_eArgError, "%s must be true or false.", rb_id2name(SYM2ID(o->sym)));
589
- }
534
+ }
535
+ if (Qtrue == rb_funcall(ropts, has_key_id, 1, array_nl_sym)) {
536
+ if (Qnil == (v = rb_hash_lookup(ropts, array_nl_sym))) {
537
+ copts->dump_opts.array_size = 0;
538
+ *copts->dump_opts.array_nl = '\0';
539
+ } else {
540
+ rb_check_type(v, T_STRING);
541
+ if (sizeof(copts->dump_opts.array_nl) <= (len = RSTRING_LEN(v))) {
542
+ rb_raise(rb_eArgError, "array_nl string is limited to %lu characters.", sizeof(copts->dump_opts.array_nl));
590
543
  }
591
- }
592
- // This is here only for backwards compatibility with the original Oj.
593
- v = rb_hash_lookup(ropts, ascii_only_sym);
594
- if (Qtrue == v) {
595
- copts->escape_mode = ASCIIEsc;
596
- } else if (Qfalse == v) {
597
- copts->escape_mode = JSONEsc;
544
+ strcpy(copts->dump_opts.array_nl, StringValuePtr(v));
545
+ copts->dump_opts.array_size = (uint8_t)len;
598
546
  }
599
547
  }
548
+ copts->dump_opts.use = (0 < copts->dump_opts.indent_size ||
549
+ 0 < copts->dump_opts.after_size ||
550
+ 0 < copts->dump_opts.before_size ||
551
+ 0 < copts->dump_opts.hash_size ||
552
+ 0 < copts->dump_opts.array_size);
553
+ // This is here only for backwards compatibility with the original Oj.
554
+ v = rb_hash_lookup(ropts, ascii_only_sym);
555
+ if (Qtrue == v) {
556
+ copts->escape_mode = ASCIIEsc;
557
+ } else if (Qfalse == v) {
558
+ copts->escape_mode = JSONEsc;
559
+ }
600
560
  }
601
561
 
602
562
  /* Document-method: strict_load
@@ -1665,53 +1625,57 @@ mimic_generate_core(int argc, VALUE *argv, Options copts) {
1665
1625
  out.end = buf + sizeof(buf) - 10;
1666
1626
  out.allocated = 0;
1667
1627
  if (2 == argc && Qnil != argv[1]) {
1668
- struct _DumpOpts dump_opts;
1669
- VALUE ropts = argv[1];
1670
- VALUE v;
1628
+ VALUE ropts = argv[1];
1629
+ VALUE v;
1630
+ size_t len;
1671
1631
 
1672
- memset(&dump_opts, 0, sizeof(dump_opts)); // may not be needed
1673
1632
  if (T_HASH != rb_type(ropts)) {
1674
1633
  rb_raise(rb_eArgError, "options must be a hash.");
1675
1634
  }
1676
- if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) {
1635
+ if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) { // TBD fixnum also ok
1677
1636
  rb_check_type(v, T_STRING);
1678
- if (0 == copts->dump_opts) {
1679
- copts->dump_opts = &dump_opts;
1637
+ if (sizeof(copts->dump_opts.indent_str) <= (len = RSTRING_LEN(v))) {
1638
+ rb_raise(rb_eArgError, "indent string is limited to %lu characters.", sizeof(copts->dump_opts.indent_str));
1680
1639
  }
1681
- copts->dump_opts->indent = StringValuePtr(v);
1682
- copts->dump_opts->indent_size = (uint8_t)strlen(copts->dump_opts->indent);
1640
+ strcpy(copts->dump_opts.indent_str, StringValuePtr(v));
1641
+ copts->dump_opts.indent_size = (uint8_t)len;
1642
+ copts->dump_opts.use = true;
1683
1643
  }
1684
1644
  if (Qnil != (v = rb_hash_lookup(ropts, space_sym))) {
1685
1645
  rb_check_type(v, T_STRING);
1686
- if (0 == copts->dump_opts) {
1687
- copts->dump_opts = &dump_opts;
1646
+ if (sizeof(copts->dump_opts.after_sep) <= (len = RSTRING_LEN(v))) {
1647
+ rb_raise(rb_eArgError, "space string is limited to %lu characters.", sizeof(copts->dump_opts.after_sep));
1688
1648
  }
1689
- copts->dump_opts->after_sep = StringValuePtr(v);
1690
- copts->dump_opts->after_size = (uint8_t)strlen(copts->dump_opts->after_sep);
1649
+ strcpy(copts->dump_opts.after_sep, StringValuePtr(v));
1650
+ copts->dump_opts.after_size = (uint8_t)len;
1651
+ copts->dump_opts.use = true;
1691
1652
  }
1692
1653
  if (Qnil != (v = rb_hash_lookup(ropts, space_before_sym))) {
1693
1654
  rb_check_type(v, T_STRING);
1694
- if (0 == copts->dump_opts) {
1695
- copts->dump_opts = &dump_opts;
1655
+ if (sizeof(copts->dump_opts.before_sep) <= (len = RSTRING_LEN(v))) {
1656
+ rb_raise(rb_eArgError, "space_before string is limited to %lu characters.", sizeof(copts->dump_opts.before_sep));
1696
1657
  }
1697
- copts->dump_opts->before_sep = StringValuePtr(v);
1698
- copts->dump_opts->before_size = (uint8_t)strlen(copts->dump_opts->before_sep);
1658
+ strcpy(copts->dump_opts.before_sep, StringValuePtr(v));
1659
+ copts->dump_opts.before_size = (uint8_t)len;
1660
+ copts->dump_opts.use = true;
1699
1661
  }
1700
1662
  if (Qnil != (v = rb_hash_lookup(ropts, object_nl_sym))) {
1701
1663
  rb_check_type(v, T_STRING);
1702
- if (0 == copts->dump_opts) {
1703
- copts->dump_opts = &dump_opts;
1664
+ if (sizeof(copts->dump_opts.hash_nl) <= (len = RSTRING_LEN(v))) {
1665
+ rb_raise(rb_eArgError, "object_nl string is limited to %lu characters.", sizeof(copts->dump_opts.hash_nl));
1704
1666
  }
1705
- copts->dump_opts->hash_nl = StringValuePtr(v);
1706
- copts->dump_opts->hash_size = (uint8_t)strlen(copts->dump_opts->hash_nl);
1667
+ strcpy(copts->dump_opts.hash_nl, StringValuePtr(v));
1668
+ copts->dump_opts.hash_size = (uint8_t)len;
1669
+ copts->dump_opts.use = true;
1707
1670
  }
1708
1671
  if (Qnil != (v = rb_hash_lookup(ropts, array_nl_sym))) {
1709
1672
  rb_check_type(v, T_STRING);
1710
- if (0 == copts->dump_opts) {
1711
- copts->dump_opts = &dump_opts;
1673
+ if (sizeof(copts->dump_opts.array_nl) <= (len = RSTRING_LEN(v))) {
1674
+ rb_raise(rb_eArgError, "array_nl string is limited to %lu characters.", sizeof(copts->dump_opts.array_nl));
1712
1675
  }
1713
- copts->dump_opts->array_nl = StringValuePtr(v);
1714
- copts->dump_opts->array_size = (uint8_t)strlen(copts->dump_opts->array_nl);
1676
+ strcpy(copts->dump_opts.array_nl, StringValuePtr(v));
1677
+ copts->dump_opts.array_size = (uint8_t)len;
1678
+ copts->dump_opts.use = true;
1715
1679
  }
1716
1680
  // :allow_nan is not supported as Oj always allows_nan
1717
1681
  // :max_nesting is always set to 100
@@ -1738,19 +1702,18 @@ mimic_generate(int argc, VALUE *argv, VALUE self) {
1738
1702
  static VALUE
1739
1703
  mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
1740
1704
  struct _Options copts = oj_default_options;
1741
- struct _DumpOpts dump_opts;
1742
-
1743
- dump_opts.indent = " ";
1744
- dump_opts.indent_size = (uint8_t)strlen(dump_opts.indent);
1745
- dump_opts.before_sep = " ";
1746
- dump_opts.before_size = (uint8_t)strlen(dump_opts.before_sep);
1747
- dump_opts.after_sep = " ";
1748
- dump_opts.after_size = (uint8_t)strlen(dump_opts.after_sep);
1749
- dump_opts.hash_nl = "\n";
1750
- dump_opts.hash_size = (uint8_t)strlen(dump_opts.hash_nl);
1751
- dump_opts.array_nl = "\n";
1752
- dump_opts.array_size = (uint8_t)strlen(dump_opts.array_nl);
1753
- copts.dump_opts = &dump_opts;
1705
+
1706
+ strcpy(copts.dump_opts.indent_str, " ");
1707
+ copts.dump_opts.indent_size = (uint8_t)strlen(copts.dump_opts.indent_str);
1708
+ strcpy(copts.dump_opts.before_sep, " ");
1709
+ copts.dump_opts.before_size = (uint8_t)strlen(copts.dump_opts.before_sep);
1710
+ strcpy(copts.dump_opts.after_sep, " ");
1711
+ copts.dump_opts.after_size = (uint8_t)strlen(copts.dump_opts.after_sep);
1712
+ strcpy(copts.dump_opts.hash_nl, "\n");
1713
+ copts.dump_opts.hash_size = (uint8_t)strlen(copts.dump_opts.hash_nl);
1714
+ strcpy(copts.dump_opts.array_nl, "\n");
1715
+ copts.dump_opts.array_size = (uint8_t)strlen(copts.dump_opts.array_nl);
1716
+ copts.dump_opts.use = true;
1754
1717
 
1755
1718
  return mimic_generate_core(argc, argv, &copts);
1756
1719
  }
@@ -1833,26 +1796,38 @@ mimic_create_id(VALUE self, VALUE id) {
1833
1796
  }
1834
1797
 
1835
1798
  static struct _Options mimic_object_to_json_options = {
1836
- 0, // indent
1837
- No, // circular
1838
- No, // auto_define
1839
- No, // sym_key
1840
- JSONEsc, // escape_mode
1841
- CompatMode, // mode
1842
- No, // class_cache
1843
- RubyTime, // time_format
1844
- No, // bigdec_as_num
1845
- FloatDec, // bigdec_load
1846
- No, // to_json
1847
- Yes, // nilnil
1848
- Yes, // allow_gc
1849
- Yes, // quirks_mode
1850
- json_class, // create_id
1851
- 10, // create_id_len
1852
- 9, // sec_prec
1853
- 0, // dump_opts
1854
- 15, // float_prec
1855
- "%0.15g", // float_fmt
1799
+ 0, // indent
1800
+ No, // circular
1801
+ No, // auto_define
1802
+ No, // sym_key
1803
+ JSONEsc, // escape_mode
1804
+ CompatMode, // mode
1805
+ No, // class_cache
1806
+ RubyTime, // time_format
1807
+ No, // bigdec_as_num
1808
+ FloatDec, // bigdec_load
1809
+ No, // to_json
1810
+ Yes, // nilnil
1811
+ Yes, // allow_gc
1812
+ Yes, // quirks_mode
1813
+ json_class, // create_id
1814
+ 10, // create_id_len
1815
+ 9, // sec_prec
1816
+ 15, // float_prec
1817
+ "%0.15g", // float_fmt
1818
+ { // dump_opts
1819
+ false, //use
1820
+ "", // indent
1821
+ "", // before_sep
1822
+ "", // after_sep
1823
+ "", // hash_nl
1824
+ "", // array_nl
1825
+ 0, // indent_size
1826
+ 0, // before_size
1827
+ 0, // after_size
1828
+ 0, // hash_size
1829
+ 0, // array_size
1830
+ }
1856
1831
  };
1857
1832
 
1858
1833
  static VALUE
@@ -1961,11 +1936,7 @@ define_mimic_json(int argc, VALUE *argv, VALUE self) {
1961
1936
 
1962
1937
  rb_gv_set("$VERBOSE", dummy);
1963
1938
 
1964
- array_nl_sym = ID2SYM(rb_intern("array_nl")); rb_gc_register_address(&array_nl_sym);
1965
1939
  create_additions_sym = ID2SYM(rb_intern("create_additions")); rb_gc_register_address(&create_additions_sym);
1966
- object_nl_sym = ID2SYM(rb_intern("object_nl")); rb_gc_register_address(&object_nl_sym);
1967
- space_before_sym = ID2SYM(rb_intern("space_before")); rb_gc_register_address(&space_before_sym);
1968
- space_sym = ID2SYM(rb_intern("space")); rb_gc_register_address(&space_sym);
1969
1940
  symbolize_names_sym = ID2SYM(rb_intern("symbolize_names")); rb_gc_register_address(&symbolize_names_sym);
1970
1941
 
1971
1942
  if (rb_const_defined_at(mimic, rb_intern("ParserError"))) {
@@ -2121,6 +2092,7 @@ void Init_oj() {
2121
2092
  oj_utc_offset_id = rb_intern("utc_offset");
2122
2093
  oj_utcq_id = rb_intern("utc?");
2123
2094
  oj_write_id = rb_intern("write");
2095
+ has_key_id = rb_intern("has_key?");
2124
2096
 
2125
2097
  rb_require("oj/bag");
2126
2098
  rb_require("oj/error");
@@ -2141,6 +2113,7 @@ void Init_oj() {
2141
2113
  ascii_sym = ID2SYM(rb_intern("ascii")); rb_gc_register_address(&ascii_sym);
2142
2114
  auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_gc_register_address(&auto_define_sym);
2143
2115
  auto_sym = ID2SYM(rb_intern("auto")); rb_gc_register_address(&auto_sym);
2116
+ array_nl_sym = ID2SYM(rb_intern("array_nl")); rb_gc_register_address(&array_nl_sym);
2144
2117
  bigdecimal_as_decimal_sym = ID2SYM(rb_intern("bigdecimal_as_decimal"));rb_gc_register_address(&bigdecimal_as_decimal_sym);
2145
2118
  bigdecimal_load_sym = ID2SYM(rb_intern("bigdecimal_load"));rb_gc_register_address(&bigdecimal_load_sym);
2146
2119
  bigdecimal_sym = ID2SYM(rb_intern("bigdecimal")); rb_gc_register_address(&bigdecimal_sym);
@@ -2157,10 +2130,13 @@ void Init_oj() {
2157
2130
  newline_sym = ID2SYM(rb_intern("newline")); rb_gc_register_address(&newline_sym);
2158
2131
  nilnil_sym = ID2SYM(rb_intern("nilnil")); rb_gc_register_address(&nilnil_sym);
2159
2132
  null_sym = ID2SYM(rb_intern("null")); rb_gc_register_address(&null_sym);
2133
+ object_nl_sym = ID2SYM(rb_intern("object_nl")); rb_gc_register_address(&object_nl_sym);
2160
2134
  object_sym = ID2SYM(rb_intern("object")); rb_gc_register_address(&object_sym);
2161
2135
  quirks_mode_sym = ID2SYM(rb_intern("quirks_mode")); rb_gc_register_address(&quirks_mode_sym);
2162
2136
  ruby_sym = ID2SYM(rb_intern("ruby")); rb_gc_register_address(&ruby_sym);
2163
2137
  sec_prec_sym = ID2SYM(rb_intern("second_precision"));rb_gc_register_address(&sec_prec_sym);
2138
+ space_before_sym = ID2SYM(rb_intern("space_before"));rb_gc_register_address(&space_before_sym);
2139
+ space_sym = ID2SYM(rb_intern("space")); rb_gc_register_address(&space_sym);
2164
2140
  strict_sym = ID2SYM(rb_intern("strict")); rb_gc_register_address(&strict_sym);
2165
2141
  symbol_keys_sym = ID2SYM(rb_intern("symbol_keys")); rb_gc_register_address(&symbol_keys_sym);
2166
2142
  time_format_sym = ID2SYM(rb_intern("time_format")); rb_gc_register_address(&time_format_sym);