oj 2.13.1 → 2.14.0

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