oj 3.7.4 → 3.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -4
  3. data/ext/oj/buf.h +6 -34
  4. data/ext/oj/cache8.c +3 -3
  5. data/ext/oj/cache8.h +5 -33
  6. data/ext/oj/circarray.c +5 -9
  7. data/ext/oj/circarray.h +5 -8
  8. data/ext/oj/code.c +3 -6
  9. data/ext/oj/code.h +7 -10
  10. data/ext/oj/compat.c +11 -14
  11. data/ext/oj/custom.c +108 -75
  12. data/ext/oj/dump.c +132 -92
  13. data/ext/oj/dump.h +6 -7
  14. data/ext/oj/dump_compat.c +37 -34
  15. data/ext/oj/dump_leaf.c +3 -6
  16. data/ext/oj/dump_object.c +23 -17
  17. data/ext/oj/dump_strict.c +7 -9
  18. data/ext/oj/encode.h +6 -32
  19. data/ext/oj/err.c +2 -5
  20. data/ext/oj/err.h +6 -34
  21. data/ext/oj/extconf.rb +6 -0
  22. data/ext/oj/fast.c +39 -56
  23. data/ext/oj/hash.c +11 -39
  24. data/ext/oj/hash.h +5 -33
  25. data/ext/oj/hash_test.c +3 -31
  26. data/ext/oj/mimic_json.c +65 -44
  27. data/ext/oj/object.c +38 -69
  28. data/ext/oj/odd.c +18 -17
  29. data/ext/oj/odd.h +6 -9
  30. data/ext/oj/oj.c +139 -93
  31. data/ext/oj/oj.h +43 -35
  32. data/ext/oj/parse.c +164 -60
  33. data/ext/oj/parse.h +30 -31
  34. data/ext/oj/rails.c +119 -83
  35. data/ext/oj/rails.h +4 -7
  36. data/ext/oj/reader.c +5 -8
  37. data/ext/oj/reader.h +7 -10
  38. data/ext/oj/resolve.c +4 -7
  39. data/ext/oj/resolve.h +4 -7
  40. data/ext/oj/rxclass.c +8 -11
  41. data/ext/oj/rxclass.h +8 -11
  42. data/ext/oj/saj.c +9 -12
  43. data/ext/oj/scp.c +4 -7
  44. data/ext/oj/sparse.c +67 -33
  45. data/ext/oj/stream_writer.c +16 -15
  46. data/ext/oj/strict.c +9 -12
  47. data/ext/oj/string_writer.c +27 -8
  48. data/ext/oj/trace.c +5 -8
  49. data/ext/oj/trace.h +9 -12
  50. data/ext/oj/util.c +136 -0
  51. data/ext/oj/util.h +19 -0
  52. data/ext/oj/val_stack.c +28 -36
  53. data/ext/oj/val_stack.h +19 -50
  54. data/ext/oj/wab.c +29 -29
  55. data/lib/oj.rb +0 -8
  56. data/lib/oj/json.rb +1 -1
  57. data/lib/oj/mimic.rb +46 -2
  58. data/lib/oj/version.rb +2 -2
  59. data/pages/Modes.md +47 -45
  60. data/pages/Options.md +43 -10
  61. data/pages/Rails.md +60 -21
  62. data/pages/Security.md +1 -1
  63. data/test/activesupport5/abstract_unit.rb +45 -0
  64. data/test/activesupport5/decoding_test.rb +68 -60
  65. data/test/activesupport5/encoding_test.rb +111 -96
  66. data/test/activesupport5/encoding_test_cases.rb +33 -25
  67. data/test/activesupport5/test_helper.rb +43 -21
  68. data/test/activesupport5/time_zone_test_helpers.rb +18 -3
  69. data/test/activesupport6/abstract_unit.rb +44 -0
  70. data/test/activesupport6/decoding_test.rb +133 -0
  71. data/test/activesupport6/encoding_test.rb +507 -0
  72. data/test/activesupport6/encoding_test_cases.rb +98 -0
  73. data/test/activesupport6/test_common.rb +17 -0
  74. data/test/activesupport6/test_helper.rb +163 -0
  75. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  76. data/test/bar.rb +24 -6
  77. data/test/baz.rb +16 -0
  78. data/test/foo.rb +26 -57
  79. data/test/helper.rb +10 -0
  80. data/test/json_gem/json_common_interface_test.rb +8 -3
  81. data/test/json_gem/json_generator_test.rb +15 -3
  82. data/test/json_gem/test_helper.rb +8 -0
  83. data/test/prec.rb +23 -0
  84. data/test/sample_json.rb +1 -1
  85. data/test/test_compat.rb +21 -10
  86. data/test/test_custom.rb +135 -8
  87. data/test/test_integer_range.rb +1 -2
  88. data/test/test_object.rb +35 -2
  89. data/test/test_rails.rb +35 -0
  90. data/test/test_strict.rb +24 -1
  91. data/test/test_various.rb +52 -63
  92. data/test/test_writer.rb +19 -2
  93. data/test/tests.rb +1 -0
  94. data/test/zoo.rb +13 -0
  95. metadata +100 -75
data/ext/oj/parse.h CHANGED
@@ -1,10 +1,7 @@
1
- /* parse.h
2
- * Copyright (c) 2011, Peter Ohler
3
- * All rights reserved.
4
- */
1
+ // Copyright (c) 2011 Peter Ohler. All rights reserved.
5
2
 
6
- #ifndef __OJ_PARSE_H__
7
- #define __OJ_PARSE_H__
3
+ #ifndef OJ_PARSE_H
4
+ #define OJ_PARSE_H
8
5
 
9
6
  #include <stdarg.h>
10
7
  #include <stdio.h>
@@ -17,9 +14,9 @@
17
14
  #include "reader.h"
18
15
  #include "rxclass.h"
19
16
 
20
- struct _RxClass;
17
+ struct _rxClass;
21
18
 
22
- typedef struct _NumInfo {
19
+ typedef struct _numInfo {
23
20
  int64_t i;
24
21
  int64_t num;
25
22
  int64_t div;
@@ -31,43 +28,44 @@ typedef struct _NumInfo {
31
28
  int infinity;
32
29
  int nan;
33
30
  int neg;
34
- int hasExp;
31
+ int has_exp;
35
32
  int no_big;
33
+ int bigdec_load;
36
34
  } *NumInfo;
37
35
 
38
- typedef struct _ParseInfo {
36
+ typedef struct _parseInfo {
39
37
  // used for the string parser
40
38
  const char *json;
41
39
  const char *cur;
42
40
  const char *end;
43
41
  // used for the stream parser
44
- struct _Reader rd;
42
+ struct _reader rd;
45
43
 
46
- struct _Err err;
47
- struct _Options options;
44
+ struct _err err;
45
+ struct _options options;
48
46
  VALUE handler;
49
- struct _ValStack stack;
47
+ struct _valStack stack;
50
48
  CircArray circ_array;
51
- struct _RxClass str_rx;
49
+ struct _rxClass str_rx;
52
50
  int expect_value;
53
51
  int max_depth; // just for the json gem
54
52
  VALUE proc;
55
- VALUE (*start_hash)(struct _ParseInfo *pi);
56
- void (*end_hash)(struct _ParseInfo *pi);
57
- VALUE (*hash_key)(struct _ParseInfo *pi, const char *key, size_t klen);
58
- void (*hash_set_cstr)(struct _ParseInfo *pi, Val kval, const char *str, size_t len, const char *orig);
59
- void (*hash_set_num)(struct _ParseInfo *pi, Val kval, NumInfo ni);
60
- void (*hash_set_value)(struct _ParseInfo *pi, Val kval, VALUE value);
53
+ VALUE (*start_hash)(struct _parseInfo *pi);
54
+ void (*end_hash)(struct _parseInfo *pi);
55
+ VALUE (*hash_key)(struct _parseInfo *pi, const char *key, size_t klen);
56
+ void (*hash_set_cstr)(struct _parseInfo *pi, Val kval, const char *str, size_t len, const char *orig);
57
+ void (*hash_set_num)(struct _parseInfo *pi, Val kval, NumInfo ni);
58
+ void (*hash_set_value)(struct _parseInfo *pi, Val kval, VALUE value);
61
59
 
62
- VALUE (*start_array)(struct _ParseInfo *pi);
63
- void (*end_array)(struct _ParseInfo *pi);
64
- void (*array_append_cstr)(struct _ParseInfo *pi, const char *str, size_t len, const char *orig);
65
- void (*array_append_num)(struct _ParseInfo *pi, NumInfo ni);
66
- void (*array_append_value)(struct _ParseInfo *pi, VALUE value);
60
+ VALUE (*start_array)(struct _parseInfo *pi);
61
+ void (*end_array)(struct _parseInfo *pi);
62
+ void (*array_append_cstr)(struct _parseInfo *pi, const char *str, size_t len, const char *orig);
63
+ void (*array_append_num)(struct _parseInfo *pi, NumInfo ni);
64
+ void (*array_append_value)(struct _parseInfo *pi, VALUE value);
67
65
 
68
- void (*add_cstr)(struct _ParseInfo *pi, const char *str, size_t len, const char *orig);
69
- void (*add_num)(struct _ParseInfo *pi, NumInfo ni);
70
- void (*add_value)(struct _ParseInfo *pi, VALUE val);
66
+ void (*add_cstr)(struct _parseInfo *pi, const char *str, size_t len, const char *orig);
67
+ void (*add_num)(struct _parseInfo *pi, NumInfo ni);
68
+ void (*add_value)(struct _parseInfo *pi, VALUE val);
71
69
  VALUE err_class;
72
70
  bool has_callbacks;
73
71
  } *ParseInfo;
@@ -80,6 +78,7 @@ extern VALUE oj_num_as_value(NumInfo ni);
80
78
  extern void oj_set_strict_callbacks(ParseInfo pi);
81
79
  extern void oj_set_object_callbacks(ParseInfo pi);
82
80
  extern void oj_set_compat_callbacks(ParseInfo pi);
81
+ extern void oj_set_custom_callbacks(ParseInfo pi);
83
82
  extern void oj_set_wab_callbacks(ParseInfo pi);
84
83
 
85
84
  extern void oj_sparse2(ParseInfo pi);
@@ -87,7 +86,7 @@ extern VALUE oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd);
87
86
 
88
87
  static inline void
89
88
  parse_info_init(ParseInfo pi) {
90
- memset(pi, 0, sizeof(struct _ParseInfo));
89
+ memset(pi, 0, sizeof(struct _parseInfo));
91
90
  }
92
91
 
93
92
  static inline bool
@@ -108,4 +107,4 @@ empty_ok(Options options) {
108
107
  return Yes == options->empty_string;
109
108
  }
110
109
 
111
- #endif /* __OJ_PARSE_H__ */
110
+ #endif /* OJ_PARSE_H */
data/ext/oj/rails.c CHANGED
@@ -1,20 +1,18 @@
1
- /* rails.c
2
- * Copyright (c) 2017, Peter Ohler
3
- * All rights reserved.
4
- */
1
+ // Copyright (c) 2017 Peter Ohler. All rights reserved.
5
2
 
6
3
  #include "rails.h"
7
4
  #include "encode.h"
8
5
  #include "code.h"
9
6
  #include "encode.h"
10
7
  #include "trace.h"
8
+ #include "util.h"
11
9
 
12
10
  #define OJ_INFINITY (1.0/0.0)
13
11
 
14
12
  // TBD keep static array of strings and functions to help with rails optimization
15
- typedef struct _Encoder {
16
- struct _ROptTable ropts;
17
- struct _Options opts;
13
+ typedef struct _encoder {
14
+ struct _rOptTable ropts;
15
+ struct _options opts;
18
16
  VALUE arg;
19
17
  } *Encoder;
20
18
 
@@ -28,7 +26,7 @@ static void dump_rails_val(VALUE obj, int depth, Out out, bool as_ok);
28
26
 
29
27
  extern VALUE Oj;
30
28
 
31
- static struct _ROptTable ropts = { 0, 0, NULL };
29
+ static struct _rOptTable ropts = { 0, 0, NULL };
32
30
 
33
31
  static VALUE encoder_class = Qnil;
34
32
  static bool escape_html = true;
@@ -79,14 +77,15 @@ copy_opts(ROptTable src, ROptTable dest) {
79
77
  if (NULL == src->table) {
80
78
  dest->table = NULL;
81
79
  } else {
82
- dest->table = ALLOC_N(struct _ROpt, dest->alen);
83
- memcpy(dest->table, src->table, sizeof(struct _ROpt) * dest->alen);
80
+ dest->table = ALLOC_N(struct _rOpt, dest->alen);
81
+ memcpy(dest->table, src->table, sizeof(struct _rOpt) * dest->alen);
84
82
  }
85
83
  return NULL;
86
84
  }
87
85
 
88
86
  static int
89
- dump_attr_cb(ID key, VALUE value, Out out) {
87
+ dump_attr_cb(ID key, VALUE value, VALUE ov) {
88
+ Out out = (Out)ov;
90
89
  int depth = out->depth;
91
90
  size_t size = depth * out->indent + 1;
92
91
  const char *attr = rb_id2name(key);
@@ -116,7 +115,7 @@ dump_attr_cb(ID key, VALUE value, Out out) {
116
115
  dump_rails_val(value, depth, out, true);
117
116
  out->depth = depth;
118
117
  *out->cur++ = ',';
119
-
118
+
120
119
  return ST_CONTINUE;
121
120
  }
122
121
 
@@ -213,6 +212,8 @@ dump_bigdecimal(VALUE obj, int depth, Out out, bool as_ok) {
213
212
 
214
213
  if ('I' == *str || 'N' == *str || ('-' == *str && 'I' == str[1])) {
215
214
  oj_dump_nil(Qnil, depth, out, false);
215
+ } else if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) {
216
+ oj_dump_cstr(str, (int)RSTRING_LEN(rstr), 0, 0, out);
216
217
  } else if (Yes == out->opts->bigdec_as_num) {
217
218
  oj_dump_raw(str, (int)RSTRING_LEN(rstr), out);
218
219
  } else {
@@ -221,15 +222,15 @@ dump_bigdecimal(VALUE obj, int depth, Out out, bool as_ok) {
221
222
  }
222
223
 
223
224
  static void
224
- dump_sec_nano(VALUE obj, time_t sec, long nsec, Out out) {
225
+ dump_sec_nano(VALUE obj, int64_t sec, long nsec, Out out) {
225
226
  char buf[64];
226
- struct tm *tm;
227
+ struct _timeInfo ti;
227
228
  long one = 1000000000;
228
229
  long tzsecs = NUM2LONG(rb_funcall2(obj, oj_utc_offset_id, 0, 0));
229
230
  int tzhour, tzmin;
230
231
  char tzsign = '+';
231
232
  int len;
232
-
233
+
233
234
  if (out->end - out->cur <= 36) {
234
235
  assure_size(out, 36);
235
236
  }
@@ -248,7 +249,7 @@ dump_sec_nano(VALUE obj, time_t sec, long nsec, Out out) {
248
249
  }
249
250
  // 2012-01-05T23:58:07.123456000+09:00 or 2012/01/05 23:58:07 +0900
250
251
  sec += tzsecs;
251
- tm = gmtime(&sec);
252
+ sec_as_time(sec, &ti);
252
253
  if (0 > tzsecs) {
253
254
  tzsign = '-';
254
255
  tzhour = (int)(tzsecs / -3600);
@@ -258,19 +259,12 @@ dump_sec_nano(VALUE obj, time_t sec, long nsec, Out out) {
258
259
  tzmin = (int)(tzsecs / 60) - (tzhour * 60);
259
260
  }
260
261
  if (!xml_time) {
261
- len = sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d %c%02d%02d",
262
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
263
- tm->tm_hour, tm->tm_min, tm->tm_sec, tzsign, tzhour, tzmin);
262
+ len = sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d %c%02d%02d", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, tzsign, tzhour, tzmin);
264
263
  } else if (0 == out->opts->sec_prec) {
265
264
  if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
266
- len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ",
267
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
268
- tm->tm_hour, tm->tm_min, tm->tm_sec);
265
+ len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec);
269
266
  } else {
270
- len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
271
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
272
- tm->tm_hour, tm->tm_min, tm->tm_sec,
273
- tzsign, tzhour, tzmin);
267
+ len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, tzsign, tzhour, tzmin);
274
268
  }
275
269
  } else if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
276
270
  char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
@@ -280,9 +274,7 @@ dump_sec_nano(VALUE obj, time_t sec, long nsec, Out out) {
280
274
  format[32] = '0' + out->opts->sec_prec;
281
275
  len -= 9 - out->opts->sec_prec;
282
276
  }
283
- len = sprintf(buf, format,
284
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
285
- tm->tm_hour, tm->tm_min, tm->tm_sec, nsec);
277
+ len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, nsec);
286
278
  } else {
287
279
  char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
288
280
 
@@ -291,24 +283,25 @@ dump_sec_nano(VALUE obj, time_t sec, long nsec, Out out) {
291
283
  format[32] = '0' + out->opts->sec_prec;
292
284
  len -= 9 - out->opts->sec_prec;
293
285
  }
294
- len = sprintf(buf, format,
295
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
296
- tm->tm_hour, tm->tm_min, tm->tm_sec, nsec,
297
- tzsign, tzhour, tzmin);
286
+ len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, nsec, tzsign, tzhour, tzmin);
298
287
  }
299
288
  oj_dump_cstr(buf, len, 0, 0, out);
300
289
  }
301
290
 
302
291
  static void
303
292
  dump_time(VALUE obj, int depth, Out out, bool as_ok) {
304
- time_t sec;
293
+ long long sec;
305
294
  long long nsec;
306
295
 
307
296
  #ifdef HAVE_RB_TIME_TIMESPEC
308
- {
297
+ if (16 <= sizeof(struct timespec)) {
309
298
  struct timespec ts = rb_time_timespec(obj);
310
- sec = ts.tv_sec;
299
+
300
+ sec = (long long)ts.tv_sec;
311
301
  nsec = ts.tv_nsec;
302
+ } else {
303
+ sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
304
+ nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
312
305
  }
313
306
  #else
314
307
  sec = rb_num2ll(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
@@ -319,7 +312,7 @@ dump_time(VALUE obj, int depth, Out out, bool as_ok) {
319
312
 
320
313
  static void
321
314
  dump_timewithzone(VALUE obj, int depth, Out out, bool as_ok) {
322
- time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
315
+ int64_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
323
316
  long long nsec = 0;
324
317
 
325
318
  if (rb_respond_to(obj, oj_tv_nsec_id)) {
@@ -339,7 +332,7 @@ dump_to_s(VALUE obj, int depth, Out out, bool as_ok) {
339
332
 
340
333
  static ID parameters_id = 0;
341
334
 
342
- typedef struct _StrLen {
335
+ typedef struct _strLen {
343
336
  const char *str;
344
337
  int len;
345
338
  } *StrLen;
@@ -360,9 +353,9 @@ columns_array(VALUE rcols, int *ccnt) {
360
353
  StrLen cols;
361
354
  int i;
362
355
  int cnt = (int)RARRAY_LEN(rcols);
363
-
356
+
364
357
  *ccnt = cnt;
365
- cols = ALLOC_N(struct _StrLen, cnt);
358
+ cols = ALLOC_N(struct _strLen, cnt);
366
359
  for (i = 0, cp = cols; i < cnt; i++, cp++) {
367
360
  v = rb_ary_entry(rcols, i);
368
361
  if (T_STRING != rb_type(v)) {
@@ -379,7 +372,7 @@ dump_row(VALUE row, StrLen cols, int ccnt, int depth, Out out) {
379
372
  size_t size;
380
373
  int d2 = depth + 1;
381
374
  int i;
382
-
375
+
383
376
  assure_size(out, 2);
384
377
  *out->cur++ = '{';
385
378
  size = depth * out->indent + 3;
@@ -439,7 +432,7 @@ dump_activerecord_result(VALUE obj, int depth, Out out, bool as_ok) {
439
432
  int i, rcnt;
440
433
  size_t size;
441
434
  int d2 = depth + 1;
442
-
435
+
443
436
  if (0 == rows_id) {
444
437
  rows_id = rb_intern("@rows");
445
438
  columns_id = rb_intern("@columns");
@@ -500,7 +493,7 @@ dump_activerecord_result(VALUE obj, int depth, Out out, bool as_ok) {
500
493
  *out->cur++ = ']';
501
494
  }
502
495
 
503
- typedef struct _NamedFunc {
496
+ typedef struct _namedFunc {
504
497
  const char *name;
505
498
  DumpFunc func;
506
499
  } *NamedFunc;
@@ -557,7 +550,7 @@ dump_regexp(VALUE obj, int depth, Out out, bool as_ok) {
557
550
  dump_as_string(obj, depth, out, as_ok);
558
551
  }
559
552
 
560
- static struct _NamedFunc dump_map[] = {
553
+ static struct _namedFunc dump_map[] = {
561
554
  { "ActionController::Parameters", dump_actioncontroller_parameters },
562
555
  { "ActiveRecord::Result", dump_activerecord_result },
563
556
  { "ActiveSupport::TimeWithZone", dump_timewithzone },
@@ -591,12 +584,12 @@ create_opt(ROptTable rot, VALUE clas) {
591
584
  rot->len++;
592
585
  if (NULL == rot->table) {
593
586
  rot->alen = 256;
594
- rot->table = ALLOC_N(struct _ROpt, rot->alen);
595
- memset(rot->table, 0, sizeof(struct _ROpt) * rot->alen);
587
+ rot->table = ALLOC_N(struct _rOpt, rot->alen);
588
+ memset(rot->table, 0, sizeof(struct _rOpt) * rot->alen);
596
589
  } else if (rot->alen <= rot->len) {
597
590
  rot->alen *= 2;
598
- REALLOC_N(rot->table, struct _ROpt, rot->alen);
599
- memset(rot->table + olen, 0, sizeof(struct _ROpt) * olen);
591
+ REALLOC_N(rot->table, struct _rOpt, rot->alen);
592
+ memset(rot->table + olen, 0, sizeof(struct _rOpt) * olen);
600
593
  }
601
594
  if (0 == olen) {
602
595
  ro = rot->table;
@@ -604,10 +597,10 @@ create_opt(ROptTable rot, VALUE clas) {
604
597
  ro = &rot->table[olen];
605
598
  } else {
606
599
  int i;
607
-
600
+
608
601
  for (i = 0, ro = rot->table; i < olen; i++, ro++) {
609
602
  if (clas < ro->clas) {
610
- memmove(ro + 1, ro, sizeof(struct _ROpt) * (olen - i));
603
+ memmove(ro + 1, ro, sizeof(struct _rOpt) * (olen - i));
611
604
  break;
612
605
  }
613
606
  }
@@ -640,7 +633,7 @@ create_opt(ROptTable rot, VALUE clas) {
640
633
  ro->dump = dump_to_s;
641
634
  }
642
635
  }
643
- return NULL;
636
+ return ro;
644
637
  }
645
638
 
646
639
  static void
@@ -674,12 +667,12 @@ encoder_mark(void *ptr) {
674
667
  */
675
668
  static VALUE
676
669
  encoder_new(int argc, VALUE *argv, VALUE self) {
677
- Encoder e = ALLOC(struct _Encoder);
670
+ Encoder e = ALLOC(struct _encoder);
678
671
 
679
672
  e->opts = oj_default_options;
680
673
  e->arg = Qnil;
681
674
  copy_opts(&ropts, &e->ropts);
682
-
675
+
683
676
  if (1 <= argc && Qnil != *argv) {
684
677
  oj_parse_options(*argv, &e->opts);
685
678
  e->arg = *argv;
@@ -734,7 +727,7 @@ optimize(int argc, VALUE *argv, ROptTable rot, bool on) {
734
727
  int i;
735
728
  NamedFunc nf;
736
729
  VALUE clas;
737
-
730
+
738
731
  oj_rails_hash_opt = on;
739
732
  oj_rails_array_opt = on;
740
733
  oj_rails_float_opt = on;
@@ -757,6 +750,8 @@ optimize(int argc, VALUE *argv, ROptTable rot, bool on) {
757
750
  oj_rails_array_opt = on;
758
751
  } else if (rb_cFloat == *argv) {
759
752
  oj_rails_float_opt = on;
753
+ } else if (oj_string_writer_class == *argv) {
754
+ string_writer_optimized = on;
760
755
  } else if (NULL != (ro = oj_rails_get_opt(rot, *argv)) ||
761
756
  NULL != (ro = create_opt(rot, *argv))) {
762
757
  ro->on = on;
@@ -766,14 +761,14 @@ optimize(int argc, VALUE *argv, ROptTable rot, bool on) {
766
761
 
767
762
  /* Document-method optimize
768
763
  * call-seq: optimize(*classes)
769
- *
764
+ *
770
765
  * Use Oj rails optimized routines to encode the specified classes. This
771
766
  * ignores the as_json() method on the class and uses an internal encoding
772
767
  * instead. Passing in no classes indicates all should use the optimized
773
768
  * version of encoding for all previously optimized classes. Passing in the
774
769
  * Object class set a global switch that will then use the optimized behavior
775
770
  * for all classes.
776
- *
771
+ *
777
772
  * - *classes* [_Class_] a list of classes to optimize
778
773
  */
779
774
  static VALUE
@@ -787,19 +782,20 @@ encoder_optimize(int argc, VALUE *argv, VALUE self) {
787
782
 
788
783
  /* Document-method: optimize
789
784
  * call-seq: optimize(*classes)
790
- *
785
+ *
791
786
  * Use Oj rails optimized routines to encode the specified classes. This
792
787
  * ignores the as_json() method on the class and uses an internal encoding
793
788
  * instead. Passing in no classes indicates all should use the optimized
794
789
  * version of encoding for all previously optimized classes. Passing in the
795
790
  * Object class set a global switch that will then use the optimized behavior
796
791
  * for all classes.
797
- *
792
+ *
798
793
  * - *classes* [_Class_] a list of classes to optimize
799
794
  */
800
795
  static VALUE
801
796
  rails_optimize(int argc, VALUE *argv, VALUE self) {
802
797
  optimize(argc, argv, &ropts, true);
798
+ string_writer_optimized = true;
803
799
 
804
800
  return Qnil;
805
801
  }
@@ -813,7 +809,7 @@ rails_optimize(int argc, VALUE *argv, VALUE self) {
813
809
  VALUE
814
810
  rails_mimic_json(VALUE self) {
815
811
  VALUE json;
816
-
812
+
817
813
  if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
818
814
  json = rb_const_get_at(rb_cObject, rb_intern("JSON"));
819
815
  } else {
@@ -827,7 +823,7 @@ rails_mimic_json(VALUE self) {
827
823
 
828
824
  /* Document-method: deoptimize
829
825
  * call-seq: deoptimize(*classes)
830
- *
826
+ *
831
827
  * Turn off Oj rails optimization on the specified classes.
832
828
  *
833
829
  * - *classes* [_Class_] a list of classes to deoptimize
@@ -843,7 +839,7 @@ encoder_deoptimize(int argc, VALUE *argv, VALUE self) {
843
839
 
844
840
  /* Document-method: deoptimize
845
841
  * call-seq: deoptimize(*classes)
846
- *
842
+ *
847
843
  * Turn off Oj rails optimization on the specified classes.
848
844
  *
849
845
  * - *classes* [_Class_] a list of classes to deoptimize
@@ -851,13 +847,14 @@ encoder_deoptimize(int argc, VALUE *argv, VALUE self) {
851
847
  static VALUE
852
848
  rails_deoptimize(int argc, VALUE *argv, VALUE self) {
853
849
  optimize(argc, argv, &ropts, false);
850
+ string_writer_optimized = false;
854
851
 
855
852
  return Qnil;
856
853
  }
857
854
 
858
855
  /* Document-method:optimized?
859
856
  * call-seq: optimized?(clas)
860
- *
857
+ *
861
858
  * - *clas* [_Class_] Class to check
862
859
  *
863
860
  * @return true if the class is being optimized for rails and false otherwise
@@ -875,7 +872,7 @@ encoder_optimized(VALUE self, VALUE clas) {
875
872
 
876
873
  /* Document-method: optimized?
877
874
  * call-seq: optimized?(clas)
878
- *
875
+ *
879
876
  * Returns true if the specified Class is being optimized.
880
877
  */
881
878
  static VALUE
@@ -888,7 +885,7 @@ rails_optimized(VALUE self, VALUE clas) {
888
885
  return (ro->on) ? Qtrue : Qfalse;
889
886
  }
890
887
 
891
- typedef struct _OO {
888
+ typedef struct _oo {
892
889
  Out out;
893
890
  VALUE obj;
894
891
  } *OO;
@@ -905,10 +902,10 @@ protect_dump(VALUE ov) {
905
902
  static VALUE
906
903
  encode(VALUE obj, ROptTable ropts, Options opts, int argc, VALUE *argv) {
907
904
  char buf[4096];
908
- struct _Out out;
909
- struct _Options copts = *opts;
905
+ struct _out out;
906
+ struct _options copts = *opts;
910
907
  volatile VALUE rstr = Qnil;
911
- struct _OO oo;
908
+ struct _oo oo;
912
909
  int line = 0;
913
910
 
914
911
  oo.out = &out;
@@ -917,7 +914,7 @@ encode(VALUE obj, ROptTable ropts, Options opts, int argc, VALUE *argv) {
917
914
  copts.str_rx.tail = NULL;
918
915
  copts.mode = RailsMode;
919
916
  if (escape_html) {
920
- copts.escape_mode = JXEsc;
917
+ copts.escape_mode = RailsXEsc;
921
918
  } else {
922
919
  copts.escape_mode = RailsEsc;
923
920
  }
@@ -973,7 +970,7 @@ encode(VALUE obj, ROptTable ropts, Options opts, int argc, VALUE *argv) {
973
970
 
974
971
  /* Document-method: encode
975
972
  * call-seq: encode(obj)
976
- *
973
+ *
977
974
  * - *obj* [_Object_] object to encode
978
975
  *
979
976
  * Returns encoded object as a JSON string.
@@ -984,7 +981,7 @@ encoder_encode(VALUE self, VALUE obj) {
984
981
 
985
982
  if (Qnil != e->arg) {
986
983
  VALUE argv[1] = { e->arg };
987
-
984
+
988
985
  return encode(obj, &e->ropts, &e->opts, 1, argv);
989
986
  }
990
987
  return encode(obj, &e->ropts, &e->opts, 0, NULL);
@@ -992,9 +989,9 @@ encoder_encode(VALUE self, VALUE obj) {
992
989
 
993
990
  /* Document-method: encode
994
991
  * call-seq: encode(obj, opts=nil)
995
- *
992
+ *
996
993
  * Encode obj as a JSON String.
997
- *
994
+ *
998
995
  * - *obj* [_Object_|Hash|Array] object to convert to a JSON String
999
996
  * - *opts* [_Hash_] options
1000
997
  *
@@ -1015,6 +1012,7 @@ rails_encode(int argc, VALUE *argv, VALUE self) {
1015
1012
  static VALUE
1016
1013
  rails_use_standard_json_time_format(VALUE self, VALUE state) {
1017
1014
  if (Qtrue == state || Qfalse == state) {
1015
+ // no change needed
1018
1016
  } else if (Qnil == state) {
1019
1017
  state = Qfalse;
1020
1018
  } else {
@@ -1026,6 +1024,11 @@ rails_use_standard_json_time_format(VALUE self, VALUE state) {
1026
1024
  return state;
1027
1025
  }
1028
1026
 
1027
+ static VALUE
1028
+ rails_use_standard_json_time_format_get(VALUE self) {
1029
+ return xml_time ? Qtrue : Qfalse;
1030
+ }
1031
+
1029
1032
  static VALUE
1030
1033
  rails_escape_html_entities_in_json(VALUE self, VALUE state) {
1031
1034
  rb_iv_set(self, "@escape_html_entities_in_json", state);
@@ -1034,17 +1037,23 @@ rails_escape_html_entities_in_json(VALUE self, VALUE state) {
1034
1037
  return state;
1035
1038
  }
1036
1039
 
1040
+ static VALUE
1041
+ rails_escape_html_entities_in_json_get(VALUE self) {
1042
+ return escape_html ? Qtrue : Qfalse;
1043
+ }
1044
+
1037
1045
  static VALUE
1038
1046
  rails_time_precision(VALUE self, VALUE prec) {
1039
1047
  rb_iv_set(self, "@time_precision", prec);
1040
1048
  oj_default_options.sec_prec = NUM2INT(prec);
1049
+ oj_default_options.sec_prec_set = true;
1041
1050
 
1042
1051
  return prec;
1043
1052
  }
1044
1053
 
1045
1054
  /* Document-method: set_encoder
1046
1055
  * call-seq: set_encoder()
1047
- *
1056
+ *
1048
1057
  * Sets the ActiveSupport.encoder to Oj::Rails::Encoder and wraps some of the
1049
1058
  * formatting globals used by ActiveSupport to allow the use of those globals
1050
1059
  * in the Oj::Rails optimizations.
@@ -1056,7 +1065,12 @@ rails_set_encoder(VALUE self) {
1056
1065
  VALUE encoding;
1057
1066
  VALUE pv;
1058
1067
  VALUE verbose;
1059
-
1068
+ VALUE enc = resolve_classpath("ActiveSupport::JSON::Encoding");
1069
+
1070
+ if (Qnil != enc) {
1071
+ escape_html = Qtrue == rb_iv_get(self, "@escape_html_entities_in_json");
1072
+ xml_time = Qtrue == rb_iv_get(enc, "@use_standard_json_time_format");
1073
+ }
1060
1074
  if (rb_const_defined_at(rb_cObject, rb_intern("ActiveSupport"))) {
1061
1075
  active = rb_const_get_at(rb_cObject, rb_intern("ActiveSupport"));
1062
1076
  } else {
@@ -1073,12 +1087,19 @@ rails_set_encoder(VALUE self) {
1073
1087
  rb_gv_set("$VERBOSE", Qfalse);
1074
1088
  rb_undef_method(encoding, "use_standard_json_time_format=");
1075
1089
  rb_define_module_function(encoding, "use_standard_json_time_format=", rails_use_standard_json_time_format, 1);
1090
+ rb_undef_method(encoding, "use_standard_json_time_format");
1091
+ rb_define_module_function(encoding, "use_standard_json_time_format", rails_use_standard_json_time_format_get, 0);
1076
1092
 
1093
+ pv = rb_iv_get(encoding, "@escape_html_entities_in_json");
1094
+ escape_html = Qtrue == pv;
1077
1095
  rb_undef_method(encoding, "escape_html_entities_in_json=");
1078
1096
  rb_define_module_function(encoding, "escape_html_entities_in_json=", rails_escape_html_entities_in_json, 1);
1097
+ rb_undef_method(encoding, "escape_html_entities_in_json");
1098
+ rb_define_module_function(encoding, "escape_html_entities_in_json", rails_escape_html_entities_in_json_get, 0);
1079
1099
 
1080
1100
  pv = rb_iv_get(encoding, "@time_precision");
1081
1101
  oj_default_options.sec_prec = NUM2INT(pv);
1102
+ oj_default_options.sec_prec_set = true;
1082
1103
  rb_undef_method(encoding, "time_precision=");
1083
1104
  rb_define_module_function(encoding, "time_precision=", rails_time_precision, 1);
1084
1105
  rb_gv_set("$VERBOSE", verbose);
@@ -1097,7 +1118,7 @@ rails_set_decoder(VALUE self) {
1097
1118
  VALUE json;
1098
1119
  VALUE json_error;
1099
1120
  VALUE verbose;
1100
-
1121
+
1101
1122
  if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
1102
1123
  json = rb_const_get_at(rb_cObject, rb_intern("JSON"));
1103
1124
  } else {
@@ -1120,7 +1141,7 @@ rails_set_decoder(VALUE self) {
1120
1141
  rb_undef_method(json, "parse");
1121
1142
  rb_define_module_function(json, "parse", oj_mimic_parse, -1);
1122
1143
  rb_gv_set("$VERBOSE", verbose);
1123
-
1144
+
1124
1145
  return Qnil;
1125
1146
  }
1126
1147
 
@@ -1140,7 +1161,7 @@ oj_optimize_rails(VALUE self) {
1140
1161
  }
1141
1162
 
1142
1163
  /* Document-module: Oj::Rails
1143
- *
1164
+ *
1144
1165
  * Module that provides rails and active support compatibility.
1145
1166
  */
1146
1167
  /* Document-class: Oj::Rails::Encoder
@@ -1150,7 +1171,7 @@ oj_optimize_rails(VALUE self) {
1150
1171
  void
1151
1172
  oj_mimic_rails_init() {
1152
1173
  VALUE rails = rb_define_module_under(Oj, "Rails");
1153
-
1174
+
1154
1175
  rb_define_module_function(rails, "encode", rails_encode, -1);
1155
1176
 
1156
1177
  encoder_class = rb_define_class_under(rails, "Encoder", rb_cObject);
@@ -1286,11 +1307,15 @@ dump_array(VALUE a, int depth, Out out, bool as_ok) {
1286
1307
  }
1287
1308
 
1288
1309
  static int
1289
- hash_cb(VALUE key, VALUE value, Out out) {
1310
+ hash_cb(VALUE key, VALUE value, VALUE ov) {
1311
+ Out out = (Out)ov;
1290
1312
  int depth = out->depth;
1291
1313
  long size;
1292
1314
  int rtype = rb_type(key);
1293
-
1315
+
1316
+ if (out->omit_nil && Qnil == value) {
1317
+ return ST_CONTINUE;
1318
+ }
1294
1319
  if (rtype != T_STRING && rtype != T_SYMBOL) {
1295
1320
  key = rb_funcall(key, oj_to_s_id, 0);
1296
1321
  rtype = rb_type(key);
@@ -1396,25 +1421,36 @@ dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
1396
1421
 
1397
1422
  static void
1398
1423
  dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
1424
+ VALUE clas;
1425
+
1399
1426
  if (oj_code_dump(oj_compat_codes, obj, depth, out)) {
1400
1427
  out->argc = 0;
1401
1428
  return;
1402
1429
  }
1430
+ clas = rb_obj_class(obj);
1403
1431
  if (as_ok) {
1404
1432
  ROpt ro;
1405
1433
 
1406
- if (NULL != (ro = oj_rails_get_opt(out->ropts, rb_obj_class(obj))) && ro->on) {
1434
+ if (NULL != (ro = oj_rails_get_opt(out->ropts, clas)) && ro->on) {
1407
1435
  ro->dump(obj, depth, out, as_ok);
1436
+ } else if (Yes == out->opts->raw_json && rb_respond_to(obj, oj_raw_json_id)) {
1437
+ oj_dump_raw_json(obj, depth, out);
1408
1438
  } else if (rb_respond_to(obj, oj_as_json_id)) {
1409
1439
  dump_as_json(obj, depth, out, true);
1410
1440
  } else if (rb_respond_to(obj, oj_to_hash_id)) {
1411
1441
  dump_to_hash(obj, depth, out);
1442
+ } else if (oj_bigdecimal_class == clas) {
1443
+ dump_bigdecimal(obj, depth, out, false);
1412
1444
  } else {
1413
1445
  oj_dump_obj_to_s(obj, out);
1414
1446
  }
1447
+ } else if (Yes == out->opts->raw_json && rb_respond_to(obj, oj_raw_json_id)) {
1448
+ oj_dump_raw_json(obj, depth, out);
1415
1449
  } else if (rb_respond_to(obj, oj_to_hash_id)) {
1416
1450
  // Always attempt to_hash.
1417
1451
  dump_to_hash(obj, depth, out);
1452
+ } else if (oj_bigdecimal_class == clas) {
1453
+ dump_bigdecimal(obj, depth, out, false);
1418
1454
  } else {
1419
1455
  oj_dump_obj_to_s(obj, out);
1420
1456
  }
@@ -1433,7 +1469,7 @@ static DumpFunc rails_funcs[] = {
1433
1469
  dump_hash, // RUBY_T_HASH = 0x08,
1434
1470
  dump_obj, // RUBY_T_STRUCT = 0x09,
1435
1471
  oj_dump_bignum, // RUBY_T_BIGNUM = 0x0a,
1436
- NULL, // RUBY_T_FILE = 0x0b,
1472
+ dump_as_string, // RUBY_T_FILE = 0x0b,
1437
1473
  dump_obj, // RUBY_T_DATA = 0x0c,
1438
1474
  NULL, // RUBY_T_MATCH = 0x0d,
1439
1475
  // Rails raises a stack error on Complex and Rational. It also corrupts
@@ -1481,7 +1517,7 @@ oj_dump_rails_val(VALUE obj, int depth, Out out) {
1481
1517
  out->opts->str_rx.head = NULL;
1482
1518
  out->opts->str_rx.tail = NULL;
1483
1519
  if (escape_html) {
1484
- out->opts->escape_mode = JXEsc;
1520
+ out->opts->escape_mode = RailsXEsc;
1485
1521
  } else {
1486
1522
  out->opts->escape_mode = RailsEsc;
1487
1523
  }