oj 3.7.4 → 3.11.2

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