oj 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +17 -23
  3. data/README.md +74 -425
  4. data/ext/oj/buf.h +103 -0
  5. data/ext/oj/cache8.c +4 -0
  6. data/ext/oj/circarray.c +68 -0
  7. data/ext/oj/circarray.h +23 -0
  8. data/ext/oj/code.c +227 -0
  9. data/ext/oj/code.h +40 -0
  10. data/ext/oj/compat.c +243 -0
  11. data/ext/oj/custom.c +1097 -0
  12. data/ext/oj/dump.c +766 -1534
  13. data/ext/oj/dump.h +92 -0
  14. data/ext/oj/dump_compat.c +937 -0
  15. data/ext/oj/dump_leaf.c +254 -0
  16. data/ext/oj/dump_object.c +810 -0
  17. data/ext/oj/dump_rails.c +329 -0
  18. data/ext/oj/dump_strict.c +416 -0
  19. data/ext/oj/encode.h +51 -0
  20. data/ext/oj/err.c +57 -0
  21. data/ext/oj/err.h +70 -0
  22. data/ext/oj/extconf.rb +17 -7
  23. data/ext/oj/fast.c +213 -180
  24. data/ext/oj/hash.c +163 -0
  25. data/ext/oj/hash.h +46 -0
  26. data/ext/oj/hash_test.c +512 -0
  27. data/ext/oj/mimic_json.c +817 -0
  28. data/ext/oj/mimic_rails.c +806 -0
  29. data/ext/oj/mimic_rails.h +17 -0
  30. data/ext/oj/object.c +752 -0
  31. data/ext/oj/odd.c +230 -0
  32. data/ext/oj/odd.h +44 -0
  33. data/ext/oj/oj.c +1288 -929
  34. data/ext/oj/oj.h +240 -69
  35. data/ext/oj/parse.c +1014 -0
  36. data/ext/oj/parse.h +92 -0
  37. data/ext/oj/reader.c +223 -0
  38. data/ext/oj/reader.h +151 -0
  39. data/ext/oj/resolve.c +127 -0
  40. data/ext/oj/{cache.h → resolve.h} +6 -13
  41. data/ext/oj/rxclass.c +133 -0
  42. data/ext/oj/rxclass.h +27 -0
  43. data/ext/oj/saj.c +77 -175
  44. data/ext/oj/scp.c +224 -0
  45. data/ext/oj/sparse.c +911 -0
  46. data/ext/oj/stream_writer.c +301 -0
  47. data/ext/oj/strict.c +162 -0
  48. data/ext/oj/string_writer.c +480 -0
  49. data/ext/oj/val_stack.c +98 -0
  50. data/ext/oj/val_stack.h +188 -0
  51. data/lib/oj/active_support_helper.rb +41 -0
  52. data/lib/oj/bag.rb +6 -10
  53. data/lib/oj/easy_hash.rb +52 -0
  54. data/lib/oj/json.rb +172 -0
  55. data/lib/oj/mimic.rb +260 -5
  56. data/lib/oj/saj.rb +13 -10
  57. data/lib/oj/schandler.rb +142 -0
  58. data/lib/oj/state.rb +131 -0
  59. data/lib/oj/version.rb +1 -1
  60. data/lib/oj.rb +11 -23
  61. data/pages/Advanced.md +22 -0
  62. data/pages/Compatibility.md +25 -0
  63. data/pages/Custom.md +23 -0
  64. data/pages/Encoding.md +65 -0
  65. data/pages/JsonGem.md +79 -0
  66. data/pages/Modes.md +140 -0
  67. data/pages/Options.md +250 -0
  68. data/pages/Rails.md +60 -0
  69. data/pages/Security.md +20 -0
  70. data/test/_test_active.rb +76 -0
  71. data/test/_test_active_mimic.rb +96 -0
  72. data/test/_test_mimic_rails.rb +126 -0
  73. data/test/activesupport4/decoding_test.rb +105 -0
  74. data/test/activesupport4/encoding_test.rb +531 -0
  75. data/test/activesupport4/test_helper.rb +41 -0
  76. data/test/activesupport5/decoding_test.rb +125 -0
  77. data/test/activesupport5/encoding_test.rb +483 -0
  78. data/test/activesupport5/encoding_test_cases.rb +90 -0
  79. data/test/activesupport5/test_helper.rb +50 -0
  80. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  81. data/test/helper.rb +27 -0
  82. data/test/isolated/shared.rb +310 -0
  83. data/test/isolated/test_mimic_after.rb +13 -0
  84. data/test/isolated/test_mimic_alone.rb +12 -0
  85. data/test/isolated/test_mimic_as_json.rb +45 -0
  86. data/test/isolated/test_mimic_before.rb +13 -0
  87. data/test/isolated/test_mimic_define.rb +28 -0
  88. data/test/isolated/test_mimic_rails_after.rb +22 -0
  89. data/test/isolated/test_mimic_rails_before.rb +21 -0
  90. data/test/isolated/test_mimic_redefine.rb +15 -0
  91. data/test/json_gem/json_addition_test.rb +216 -0
  92. data/test/json_gem/json_common_interface_test.rb +143 -0
  93. data/test/json_gem/json_encoding_test.rb +109 -0
  94. data/test/json_gem/json_ext_parser_test.rb +20 -0
  95. data/test/json_gem/json_fixtures_test.rb +35 -0
  96. data/test/json_gem/json_generator_test.rb +383 -0
  97. data/test/json_gem/json_generic_object_test.rb +90 -0
  98. data/test/json_gem/json_parser_test.rb +470 -0
  99. data/test/json_gem/json_string_matching_test.rb +42 -0
  100. data/test/json_gem/test_helper.rb +18 -0
  101. data/test/perf_compat.rb +130 -0
  102. data/test/perf_fast.rb +9 -9
  103. data/test/perf_file.rb +64 -0
  104. data/test/{perf_obj.rb → perf_object.rb} +24 -10
  105. data/test/perf_scp.rb +151 -0
  106. data/test/perf_strict.rb +32 -113
  107. data/test/sample.rb +2 -3
  108. data/test/test_compat.rb +474 -0
  109. data/test/test_custom.rb +355 -0
  110. data/test/test_debian.rb +53 -0
  111. data/test/test_fast.rb +66 -16
  112. data/test/test_file.rb +237 -0
  113. data/test/test_gc.rb +49 -0
  114. data/test/test_hash.rb +29 -0
  115. data/test/test_null.rb +376 -0
  116. data/test/test_object.rb +1010 -0
  117. data/test/test_saj.rb +16 -16
  118. data/test/test_scp.rb +417 -0
  119. data/test/test_strict.rb +410 -0
  120. data/test/test_various.rb +815 -0
  121. data/test/test_writer.rb +308 -0
  122. data/test/tests.rb +9 -902
  123. data/test/tests_mimic.rb +14 -0
  124. data/test/tests_mimic_addition.rb +7 -0
  125. metadata +253 -38
  126. data/ext/oj/cache.c +0 -148
  127. data/ext/oj/foo.rb +0 -6
  128. data/ext/oj/load.c +0 -1049
  129. data/test/a.rb +0 -38
  130. data/test/perf1.rb +0 -64
  131. data/test/perf2.rb +0 -76
  132. data/test/perf_obj_old.rb +0 -213
  133. data/test/test_mimic.rb +0 -208
@@ -0,0 +1,937 @@
1
+ /* dump_object.c
2
+ * Copyright (c) 2012, 2017, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include "code.h"
7
+ #include "dump.h"
8
+
9
+ // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
10
+ #define OJ_INFINITY (1.0/0.0)
11
+
12
+ bool oj_use_hash_alt = false;
13
+ bool oj_use_array_alt = false;
14
+
15
+ static bool use_struct_alt = false;
16
+ static bool use_exception_alt = false;
17
+ static bool use_bignum_alt = false;
18
+
19
+ static void
20
+ raise_json_err(const char *msg, const char *err_classname) {
21
+ rb_raise(oj_get_json_err_class(err_classname), "%s", msg);
22
+ }
23
+
24
+ static void
25
+ dump_obj_classname(const char *classname, int depth, Out out) {
26
+ int d2 = depth + 1;
27
+ size_t len = strlen(classname);
28
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
29
+ size_t size = d2 * out->indent + 10 + len + out->opts->create_id_len + sep_len;
30
+
31
+ assure_size(out, size);
32
+ *out->cur++ = '{';
33
+ fill_indent(out, d2);
34
+ *out->cur++ = '"';
35
+ memcpy(out->cur, out->opts->create_id, out->opts->create_id_len);
36
+ out->cur += out->opts->create_id_len;
37
+ *out->cur++ = '"';
38
+ if (0 < out->opts->dump_opts.before_size) {
39
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
40
+ out->cur += out->opts->dump_opts.before_size;
41
+ }
42
+ *out->cur++ = ':';
43
+ if (0 < out->opts->dump_opts.after_size) {
44
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
45
+ out->cur += out->opts->dump_opts.after_size;
46
+ }
47
+ *out->cur++ = '"';
48
+ memcpy(out->cur, classname, len);
49
+ out->cur += len;
50
+ *out->cur++ = '"';
51
+ }
52
+
53
+ static void
54
+ dump_values_array(VALUE *values, int depth, Out out) {
55
+ size_t size;
56
+ int d2 = depth + 1;
57
+
58
+ assure_size(out, d2 * out->indent + 3);
59
+ *out->cur++ = '[';
60
+ if (Qundef == *values) {
61
+ *out->cur++ = ']';
62
+ } else {
63
+ if (out->opts->dump_opts.use) {
64
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 2;
65
+ } else {
66
+ size = d2 * out->indent + 3;
67
+ }
68
+ if (out->opts->dump_opts.use) {
69
+ size += out->opts->dump_opts.array_size;
70
+ size += out->opts->dump_opts.indent_size;
71
+ }
72
+ for (; Qundef != *values; values++) {
73
+ assure_size(out, size);
74
+ if (out->opts->dump_opts.use) {
75
+ if (0 < out->opts->dump_opts.array_size) {
76
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
77
+ out->cur += out->opts->dump_opts.array_size;
78
+ }
79
+ if (0 < out->opts->dump_opts.indent_size) {
80
+ int i;
81
+ for (i = d2; 0 < i; i--) {
82
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
83
+ out->cur += out->opts->dump_opts.indent_size;
84
+ }
85
+ }
86
+ } else {
87
+ fill_indent(out, d2);
88
+ }
89
+ oj_dump_compat_val(*values, d2, out, true);
90
+ if (Qundef != *(values + 1)) {
91
+ *out->cur++ = ',';
92
+ }
93
+ }
94
+ assure_size(out, size);
95
+ if (out->opts->dump_opts.use) {
96
+ if (0 < out->opts->dump_opts.array_size) {
97
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
98
+ out->cur += out->opts->dump_opts.array_size;
99
+ }
100
+ if (0 < out->opts->dump_opts.indent_size) {
101
+ int i;
102
+
103
+ for (i = depth; 0 < i; i--) {
104
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
105
+ out->cur += out->opts->dump_opts.indent_size;
106
+ }
107
+ }
108
+ } else {
109
+ fill_indent(out, depth);
110
+ }
111
+ *out->cur++ = ']';
112
+ }
113
+ }
114
+
115
+ static void
116
+ dump_to_json(VALUE obj, Out out) {
117
+ volatile VALUE rs;
118
+ const char *s;
119
+ int len;
120
+
121
+ rs = rb_funcall2(obj, oj_to_json_id, out->argc, out->argv);
122
+ s = rb_string_value_ptr((VALUE*)&rs);
123
+ len = (int)RSTRING_LEN(rs);
124
+
125
+ assure_size(out, len + 1);
126
+ memcpy(out->cur, s, len);
127
+ out->cur += len;
128
+ *out->cur = '\0';
129
+ }
130
+
131
+ static void
132
+ dump_array(VALUE a, int depth, Out out, bool as_ok) {
133
+ size_t size;
134
+ int i, cnt;
135
+ int d2 = depth + 1;
136
+ long id = oj_check_circular(a, out);
137
+
138
+ if (0 > id) {
139
+ raise_json_err("Too deeply nested", "NestingError");
140
+ return;
141
+ }
142
+ if (as_ok && !oj_use_hash_alt && rb_obj_class(a) != rb_cArray && rb_respond_to(a, oj_to_json_id)) {
143
+ dump_to_json(a, out);
144
+ return;
145
+ }
146
+ cnt = (int)RARRAY_LEN(a);
147
+ *out->cur++ = '[';
148
+ assure_size(out, 2);
149
+ if (0 == cnt) {
150
+ *out->cur++ = ']';
151
+ } else {
152
+ if (out->opts->dump_opts.use) {
153
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
154
+ } else {
155
+ size = d2 * out->indent + 2;
156
+ }
157
+ cnt--;
158
+ for (i = 0; i <= cnt; i++) {
159
+ assure_size(out, size);
160
+ if (out->opts->dump_opts.use) {
161
+ if (0 < out->opts->dump_opts.array_size) {
162
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
163
+ out->cur += out->opts->dump_opts.array_size;
164
+ }
165
+ if (0 < out->opts->dump_opts.indent_size) {
166
+ int i;
167
+ for (i = d2; 0 < i; i--) {
168
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
169
+ out->cur += out->opts->dump_opts.indent_size;
170
+ }
171
+ }
172
+ } else {
173
+ fill_indent(out, d2);
174
+ }
175
+ oj_dump_compat_val(rb_ary_entry(a, i), d2, out, true);
176
+ if (i < cnt) {
177
+ *out->cur++ = ',';
178
+ }
179
+ }
180
+ size = depth * out->indent + 1;
181
+ assure_size(out, size);
182
+ if (out->opts->dump_opts.use) {
183
+ if (0 < out->opts->dump_opts.array_size) {
184
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
185
+ out->cur += out->opts->dump_opts.array_size;
186
+ }
187
+ if (0 < out->opts->dump_opts.indent_size) {
188
+ int i;
189
+
190
+ for (i = depth; 0 < i; i--) {
191
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
192
+ out->cur += out->opts->dump_opts.indent_size;
193
+ }
194
+ }
195
+ } else {
196
+ fill_indent(out, depth);
197
+ }
198
+ *out->cur++ = ']';
199
+ }
200
+ *out->cur = '\0';
201
+ }
202
+
203
+ static ID _dump_id = 0;
204
+
205
+ static void
206
+ bigdecimal_alt(VALUE obj, int depth, Out out) {
207
+ struct _Attr attrs[] = {
208
+ { "b", 1, Qnil },
209
+ { NULL, 0, Qnil },
210
+ };
211
+
212
+ if (0 == _dump_id) {
213
+ _dump_id = rb_intern("_dump");
214
+ }
215
+ attrs[0].value = rb_funcall(obj, _dump_id, 0);
216
+
217
+ oj_code_attrs(obj, attrs, depth, out);
218
+ }
219
+
220
+ static ID real_id = 0;
221
+ static ID imag_id = 0;
222
+
223
+ static void
224
+ complex_alt(VALUE obj, int depth, Out out) {
225
+ struct _Attr attrs[] = {
226
+ { "r", 1, Qnil },
227
+ { "i", 1, Qnil },
228
+ { NULL, 0, Qnil },
229
+ };
230
+
231
+ if (0 == real_id) {
232
+ real_id = rb_intern("real");
233
+ imag_id = rb_intern("imag");
234
+ }
235
+ attrs[0].value = rb_funcall(obj, real_id, 0);
236
+ attrs[1].value = rb_funcall(obj, imag_id, 0);
237
+
238
+ oj_code_attrs(obj, attrs, depth, out);
239
+ }
240
+
241
+ static ID year_id = 0;
242
+ static ID month_id = 0;
243
+ static ID day_id = 0;
244
+ static ID start_id = 0;
245
+
246
+ static void
247
+ date_alt(VALUE obj, int depth, Out out) {
248
+ struct _Attr attrs[] = {
249
+ { "y", 1, Qnil },
250
+ { "m", 1, Qnil },
251
+ { "d", 1, Qnil },
252
+ { "sg", 2, Qnil },
253
+ { NULL, 0, Qnil },
254
+ };
255
+ if (0 == year_id) {
256
+ year_id = rb_intern("year");
257
+ month_id = rb_intern("month");
258
+ day_id = rb_intern("day");
259
+ start_id = rb_intern("start");
260
+ }
261
+ attrs[0].value = rb_funcall(obj, year_id, 0);
262
+ attrs[1].value = rb_funcall(obj, month_id, 0);
263
+ attrs[2].value = rb_funcall(obj, day_id, 0);
264
+ attrs[3].value = rb_funcall(obj, start_id, 0);
265
+
266
+ oj_code_attrs(obj, attrs, depth, out);
267
+ }
268
+
269
+ static ID hour_id = 0;
270
+ static ID min_id = 0;
271
+ static ID sec_id = 0;
272
+ static ID offset_id = 0;
273
+
274
+ static void
275
+ datetime_alt(VALUE obj, int depth, Out out) {
276
+ struct _Attr attrs[] = {
277
+ { "y", 1, Qnil },
278
+ { "m", 1, Qnil },
279
+ { "d", 1, Qnil },
280
+ { "H", 1, Qnil },
281
+ { "M", 1, Qnil },
282
+ { "S", 1, Qnil },
283
+ { "of", 2, Qnil },
284
+ { "sg", 2, Qnil },
285
+ { NULL, 0, Qnil },
286
+ };
287
+ if (0 == hour_id) {
288
+ year_id = rb_intern("year");
289
+ month_id = rb_intern("month");
290
+ day_id = rb_intern("day");
291
+ hour_id = rb_intern("hour");
292
+ min_id = rb_intern("min");
293
+ sec_id = rb_intern("sec");
294
+ offset_id = rb_intern("offset");
295
+ start_id = rb_intern("start");
296
+ }
297
+ attrs[0].value = rb_funcall(obj, year_id, 0);
298
+ attrs[1].value = rb_funcall(obj, month_id, 0);
299
+ attrs[2].value = rb_funcall(obj, day_id, 0);
300
+ attrs[3].value = rb_funcall(obj, hour_id, 0);
301
+ attrs[4].value = rb_funcall(obj, min_id, 0);
302
+ attrs[5].value = rb_funcall(obj, sec_id, 0);
303
+ attrs[6].value = rb_funcall(rb_funcall(obj, offset_id, 0), oj_to_s_id, 0);
304
+ attrs[7].value = rb_funcall(obj, start_id, 0);
305
+
306
+ oj_code_attrs(obj, attrs, depth, out);
307
+ }
308
+
309
+ static ID message_id = 0;
310
+ static ID backtrace_id = 0;
311
+
312
+ static void
313
+ exception_alt(VALUE obj, int depth, Out out) {
314
+ int d3 = depth + 2;
315
+ size_t size = d3 * out->indent + 2;
316
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
317
+
318
+ if (0 == message_id) {
319
+ message_id = rb_intern("message");
320
+ backtrace_id = rb_intern("backtrace");
321
+ }
322
+ dump_obj_classname(rb_class2name(rb_obj_class(obj)), depth, out);
323
+
324
+ assure_size(out, size + sep_len + 6);
325
+ *out->cur++ = ',';
326
+ fill_indent(out, d3);
327
+ *out->cur++ = '"';
328
+ *out->cur++ = 'm';
329
+ *out->cur++ = '"';
330
+ if (0 < out->opts->dump_opts.before_size) {
331
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
332
+ out->cur += out->opts->dump_opts.before_size;
333
+ }
334
+ *out->cur++ = ':';
335
+ if (0 < out->opts->dump_opts.after_size) {
336
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
337
+ out->cur += out->opts->dump_opts.after_size;
338
+ }
339
+ oj_dump_str(rb_funcall(obj, message_id, 0), 0, out, false);
340
+ assure_size(out, size + sep_len + 6);
341
+ *out->cur++ = ',';
342
+ fill_indent(out, d3);
343
+ *out->cur++ = '"';
344
+ *out->cur++ = 'b';
345
+ *out->cur++ = '"';
346
+ if (0 < out->opts->dump_opts.before_size) {
347
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
348
+ out->cur += out->opts->dump_opts.before_size;
349
+ }
350
+ *out->cur++ = ':';
351
+ if (0 < out->opts->dump_opts.after_size) {
352
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
353
+ out->cur += out->opts->dump_opts.after_size;
354
+ }
355
+ dump_array(rb_funcall(obj, backtrace_id, 0), depth, out, false);
356
+ fill_indent(out, depth);
357
+ *out->cur++ = '}';
358
+ *out->cur = '\0';
359
+ }
360
+
361
+ static ID table_id = 0;
362
+
363
+ static void
364
+ openstruct_alt(VALUE obj, int depth, Out out) {
365
+ struct _Attr attrs[] = {
366
+ { "t", 1, Qnil },
367
+ { NULL, 0, Qnil },
368
+ };
369
+ if (0 == table_id) {
370
+ table_id = rb_intern("table");
371
+ }
372
+ attrs[0].value = rb_funcall(obj, table_id, 0);
373
+
374
+ oj_code_attrs(obj, attrs, depth, out);
375
+ }
376
+
377
+ static void
378
+ range_alt(VALUE obj, int depth, Out out) {
379
+ int d3 = depth + 2;
380
+ size_t size = d3 * out->indent + 2;
381
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
382
+ VALUE args[] = { Qundef, Qundef, Qundef, Qundef };
383
+
384
+ dump_obj_classname(rb_class2name(rb_obj_class(obj)), depth, out);
385
+
386
+ assure_size(out, size + sep_len + 6);
387
+ *out->cur++ = ',';
388
+ fill_indent(out, d3);
389
+ *out->cur++ = '"';
390
+ *out->cur++ = 'a';
391
+ *out->cur++ = '"';
392
+ if (0 < out->opts->dump_opts.before_size) {
393
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
394
+ out->cur += out->opts->dump_opts.before_size;
395
+ }
396
+ *out->cur++ = ':';
397
+ if (0 < out->opts->dump_opts.after_size) {
398
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
399
+ out->cur += out->opts->dump_opts.after_size;
400
+ }
401
+ args[0] = rb_funcall(obj, oj_begin_id, 0);
402
+ args[1] = rb_funcall(obj, oj_end_id, 0);
403
+ args[2] = rb_funcall(obj, oj_exclude_end_id, 0);
404
+ dump_values_array(args, depth, out);
405
+ fill_indent(out, depth);
406
+ *out->cur++ = '}';
407
+ *out->cur = '\0';
408
+ }
409
+
410
+ static ID numerator_id = 0;
411
+ static ID denominator_id = 0;
412
+
413
+ static void
414
+ rational_alt(VALUE obj, int depth, Out out) {
415
+ struct _Attr attrs[] = {
416
+ { "n", 1, Qnil },
417
+ { "d", 1, Qnil },
418
+ { NULL, 0, Qnil },
419
+ };
420
+ if (0 == numerator_id) {
421
+ numerator_id = rb_intern("numerator");
422
+ denominator_id = rb_intern("denominator");
423
+ }
424
+ attrs[0].value = rb_funcall(obj, numerator_id, 0);
425
+ attrs[1].value = rb_funcall(obj, denominator_id, 0);
426
+
427
+ oj_code_attrs(obj, attrs, depth, out);
428
+ }
429
+
430
+ static ID options_id = 0;
431
+ static ID source_id = 0;
432
+
433
+ static void
434
+ regexp_alt(VALUE obj, int depth, Out out) {
435
+ struct _Attr attrs[] = {
436
+ { "o", 1, Qnil },
437
+ { "s", 1, Qnil },
438
+ { NULL, 0, Qnil },
439
+ };
440
+ if (0 == options_id) {
441
+ options_id = rb_intern("options");
442
+ source_id = rb_intern("source");
443
+ }
444
+ attrs[0].value = rb_funcall(obj, options_id, 0);
445
+ attrs[1].value = rb_funcall(obj, source_id, 0);
446
+
447
+ oj_code_attrs(obj, attrs, depth, out);
448
+ }
449
+
450
+ static void
451
+ time_alt(VALUE obj, int depth, Out out) {
452
+ struct _Attr attrs[] = {
453
+ { "s", 1, Qundef, 0, Qundef },
454
+ { "n", 1, Qundef, 0, Qundef },
455
+ { NULL, 0, Qnil },
456
+ };
457
+ struct timespec ts = rb_time_timespec(obj);
458
+
459
+ attrs[0].num = ts.tv_sec;
460
+ attrs[1].num = ts.tv_nsec;
461
+
462
+ oj_code_attrs(obj, attrs, depth, out);
463
+ }
464
+
465
+ static struct _Code codes[] = {
466
+ { "BigDecimal", Qnil, bigdecimal_alt, NULL, false },
467
+ { "Complex", Qnil, complex_alt, NULL, false },
468
+ { "Date", Qnil, date_alt, false },
469
+ { "DateTime", Qnil, datetime_alt, NULL, false },
470
+ { "OpenStruct", Qnil, openstruct_alt, NULL, false },
471
+ { "Range", Qnil, range_alt, NULL, false },
472
+ { "Rational", Qnil, rational_alt, NULL, false },
473
+ { "Regexp", Qnil, regexp_alt, NULL, false },
474
+ { "Time", Qnil, time_alt, NULL, false },
475
+ // TBD the rest of the library classes
476
+ { NULL, Qundef, NULL, NULL, false },
477
+ };
478
+
479
+ VALUE
480
+ oj_add_to_json(int argc, VALUE *argv, VALUE self) {
481
+ Code a;
482
+
483
+ if (0 == argc) {
484
+ for (a = codes; NULL != a->name; a++) {
485
+ if (Qnil == a->clas || Qundef == a->clas) {
486
+ a->clas = rb_const_get_at(rb_cObject, rb_intern(a->name));
487
+ }
488
+ a->active = true;
489
+ }
490
+ use_struct_alt = true;
491
+ use_exception_alt = true;
492
+ use_bignum_alt = true;
493
+ oj_use_hash_alt = true;
494
+ oj_use_array_alt = true;
495
+ } else {
496
+ for (; 0 < argc; argc--, argv++) {
497
+ if (rb_cStruct == *argv) {
498
+ use_struct_alt = true;
499
+ continue;
500
+ }
501
+ if (rb_eException == *argv) {
502
+ use_exception_alt = true;
503
+ continue;
504
+ }
505
+ if (rb_cInteger == *argv) {
506
+ use_bignum_alt = true;
507
+ continue;
508
+ }
509
+ if (rb_cHash == *argv) {
510
+ oj_use_hash_alt = true;
511
+ continue;
512
+ }
513
+ if (rb_cArray == *argv) {
514
+ oj_use_array_alt = true;
515
+ continue;
516
+ }
517
+ for (a = codes; NULL != a->name; a++) {
518
+ if (Qnil == a->clas || Qundef == a->clas) {
519
+ a->clas = rb_const_get_at(rb_cObject, rb_intern(a->name));
520
+ }
521
+ if (*argv == a->clas) {
522
+ a->active = true;
523
+ break;
524
+ }
525
+ }
526
+ }
527
+ }
528
+ return Qnil;
529
+ }
530
+
531
+ VALUE
532
+ oj_remove_to_json(int argc, VALUE *argv, VALUE self) {
533
+ if (0 == argc) {
534
+ oj_code_set_active(codes, Qnil, false);
535
+ use_struct_alt = false;
536
+ use_exception_alt = false;
537
+ use_bignum_alt = false;
538
+ oj_use_hash_alt = false;
539
+ oj_use_array_alt = false;
540
+ } else {
541
+ for (; 0 < argc; argc--, argv++) {
542
+ if (rb_cStruct == *argv) {
543
+ use_struct_alt = false;
544
+ continue;
545
+ }
546
+ if (rb_eException == *argv) {
547
+ use_exception_alt = false;
548
+ continue;
549
+ }
550
+ if (rb_cInteger == *argv) {
551
+ use_bignum_alt = false;
552
+ continue;
553
+ }
554
+ if (rb_cHash == *argv) {
555
+ oj_use_hash_alt = false;
556
+ continue;
557
+ }
558
+ if (rb_cArray == *argv) {
559
+ oj_use_array_alt = false;
560
+ continue;
561
+ }
562
+ oj_code_set_active(codes, *argv, false);
563
+ }
564
+ }
565
+ return Qnil;
566
+ }
567
+
568
+ // The JSON gem is inconsistent with handling of infinity. Using
569
+ // JSON.dump(0.1/0) returns the string Infinity but (0.1/0).to_json raise and
570
+ // exception. Worse, for BigDecimals a quoted "Infinity" is returned.
571
+ static void
572
+ dump_float(VALUE obj, int depth, Out out, bool as_ok) {
573
+ char buf[64];
574
+ char *b;
575
+ double d = rb_num2dbl(obj);
576
+ int cnt = 0;
577
+
578
+ if (0.0 == d) {
579
+ b = buf;
580
+ *b++ = '0';
581
+ *b++ = '.';
582
+ *b++ = '0';
583
+ *b++ = '\0';
584
+ cnt = 3;
585
+ } else if (OJ_INFINITY == d) {
586
+ if (out->opts->dump_opts.nan_dump) {
587
+ strcpy(buf, "Infinity");
588
+ } else {
589
+ raise_json_err("Infinity not allowed in JSON.", "GeneratorError");
590
+ }
591
+ } else if (-OJ_INFINITY == d) {
592
+ if (out->opts->dump_opts.nan_dump) {
593
+ strcpy(buf, "-Infinity");
594
+ } else {
595
+ raise_json_err("-Infinity not allowed in JSON.", "GeneratorError");
596
+ }
597
+ } else if (isnan(d)) {
598
+ if (out->opts->dump_opts.nan_dump) {
599
+ strcpy(buf, "NaN");
600
+ } else {
601
+ raise_json_err("NaN not allowed in JSON.", "GeneratorError");
602
+ }
603
+ } else if (d == (double)(long long int)d) {
604
+ //cnt = snprintf(buf, sizeof(buf), "%.1Lf", (long double)d);
605
+ cnt = snprintf(buf, sizeof(buf), "%.1f", d);
606
+ } else {
607
+ //cnt = snprintf(buf, sizeof(buf), "%0.16Lg", (long double)d);
608
+ cnt = snprintf(buf, sizeof(buf), "%0.16g", d);
609
+ }
610
+ assure_size(out, cnt);
611
+ for (b = buf; '\0' != *b; b++) {
612
+ *out->cur++ = *b;
613
+ }
614
+ *out->cur = '\0';
615
+ }
616
+
617
+ static int
618
+ hash_cb(VALUE key, VALUE value, Out out) {
619
+ int depth = out->depth;
620
+
621
+ if (out->omit_nil && Qnil == value) {
622
+ return ST_CONTINUE;
623
+ }
624
+ if (!out->opts->dump_opts.use) {
625
+ assure_size(out, depth * out->indent + 1);
626
+ fill_indent(out, depth);
627
+ } else {
628
+ assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
629
+ if (0 < out->opts->dump_opts.hash_size) {
630
+ strcpy(out->cur, out->opts->dump_opts.hash_nl);
631
+ out->cur += out->opts->dump_opts.hash_size;
632
+ }
633
+ if (0 < out->opts->dump_opts.indent_size) {
634
+ int i;
635
+ for (i = depth; 0 < i; i--) {
636
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
637
+ out->cur += out->opts->dump_opts.indent_size;
638
+ }
639
+ }
640
+ }
641
+ switch (rb_type(key)) {
642
+ case T_STRING:
643
+ oj_dump_str(key, 0, out, false);
644
+ break;
645
+ case T_SYMBOL:
646
+ oj_dump_sym(key, 0, out, false);
647
+ break;
648
+ default:
649
+ /*rb_raise(rb_eTypeError, "In :compat mode all Hash keys must be Strings or Symbols, not %s.\n", rb_class2name(rb_obj_class(key)));*/
650
+ oj_dump_str(rb_funcall(key, oj_to_s_id, 0), 0, out, false);
651
+ break;
652
+ }
653
+ if (!out->opts->dump_opts.use) {
654
+ *out->cur++ = ':';
655
+ } else {
656
+ assure_size(out, out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2);
657
+ if (0 < out->opts->dump_opts.before_size) {
658
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
659
+ out->cur += out->opts->dump_opts.before_size;
660
+ }
661
+ *out->cur++ = ':';
662
+ if (0 < out->opts->dump_opts.after_size) {
663
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
664
+ out->cur += out->opts->dump_opts.after_size;
665
+ }
666
+ }
667
+ oj_dump_compat_val(value, depth, out, true);
668
+ out->depth = depth;
669
+ *out->cur++ = ',';
670
+
671
+ return ST_CONTINUE;
672
+ }
673
+
674
+ static void
675
+ dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
676
+ int cnt;
677
+ long id = oj_check_circular(obj, out);
678
+
679
+ if (0 > id) {
680
+ raise_json_err("Too deeply nested", "NestingError");
681
+ return;
682
+ }
683
+ if (as_ok && !oj_use_hash_alt && rb_obj_class(obj) != rb_cHash && rb_respond_to(obj, oj_to_json_id)) {
684
+ dump_to_json(obj, out);
685
+ return;
686
+ }
687
+ cnt = (int)RHASH_SIZE(obj);
688
+ assure_size(out, 2);
689
+ if (0 == cnt) {
690
+ *out->cur++ = '{';
691
+ *out->cur++ = '}';
692
+ } else {
693
+ *out->cur++ = '{';
694
+ out->depth = depth + 1;
695
+ rb_hash_foreach(obj, hash_cb, (VALUE)out);
696
+ if (',' == *(out->cur - 1)) {
697
+ out->cur--; // backup to overwrite last comma
698
+ }
699
+ if (!out->opts->dump_opts.use) {
700
+ assure_size(out, depth * out->indent + 2);
701
+ fill_indent(out, depth);
702
+ } else {
703
+ assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
704
+ if (0 < out->opts->dump_opts.hash_size) {
705
+ strcpy(out->cur, out->opts->dump_opts.hash_nl);
706
+ out->cur += out->opts->dump_opts.hash_size;
707
+ }
708
+ if (0 < out->opts->dump_opts.indent_size) {
709
+ int i;
710
+
711
+ for (i = depth; 0 < i; i--) {
712
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
713
+ out->cur += out->opts->dump_opts.indent_size;
714
+ }
715
+ }
716
+ }
717
+ *out->cur++ = '}';
718
+ }
719
+ *out->cur = '\0';
720
+ }
721
+
722
+ // In compat mode only the first call check for to_json. After that to_s is
723
+ // called.
724
+ static void
725
+ dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
726
+ if (oj_code_dump(codes, obj, depth, out)) {
727
+ return;
728
+ }
729
+ if (use_exception_alt && rb_obj_is_kind_of(obj, rb_eException)) {
730
+ exception_alt(obj, depth, out);
731
+ return;
732
+ }
733
+ if (as_ok && rb_respond_to(obj, oj_to_json_id)) {
734
+ volatile VALUE rs;
735
+ const char *s;
736
+ int len;
737
+
738
+ rs = rb_funcall(obj, oj_to_json_id, 0);
739
+ s = rb_string_value_ptr((VALUE*)&rs);
740
+ len = (int)RSTRING_LEN(rs);
741
+
742
+ assure_size(out, len + 1);
743
+ memcpy(out->cur, s, len);
744
+ out->cur += len;
745
+ *out->cur = '\0';
746
+ return;
747
+ }
748
+ // Nothing else matched so encode as a JSON object with Ruby obj members
749
+ // as JSON object members.
750
+ oj_dump_obj_to_s(obj, out);
751
+ }
752
+
753
+ static void
754
+ dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
755
+ VALUE clas = rb_obj_class(obj);
756
+
757
+ if (oj_code_dump(codes, obj, depth, out)) {
758
+ return;
759
+ }
760
+ if (rb_cRange == clas) {
761
+ *out->cur++ = '"';
762
+ oj_dump_compat_val(rb_funcall(obj, oj_begin_id, 0), 0, out, false);
763
+ assure_size(out, 3);
764
+ *out->cur++ = '.';
765
+ *out->cur++ = '.';
766
+ if (Qtrue == rb_funcall(obj, oj_exclude_end_id, 0)) {
767
+ *out->cur++ = '.';
768
+ }
769
+ oj_dump_compat_val(rb_funcall(obj, oj_end_id, 0), 0, out, false);
770
+ *out->cur++ = '"';
771
+
772
+ return;
773
+ }
774
+ if (as_ok && rb_respond_to(obj, oj_to_json_id)) {
775
+ volatile VALUE rs = rb_funcall(obj, oj_to_json_id, 0);
776
+ const char *s;
777
+ int len;
778
+
779
+ s = rb_string_value_ptr((VALUE*)&rs);
780
+ len = (int)RSTRING_LEN(rs);
781
+ assure_size(out, len);
782
+ memcpy(out->cur, s, len);
783
+ out->cur += len;
784
+
785
+ return;
786
+ }
787
+ if (use_struct_alt) {
788
+ int d3 = depth + 2;
789
+ size_t size = d3 * out->indent + 2;
790
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
791
+ const char *classname = rb_class2name(rb_obj_class(obj));
792
+ VALUE args[100];
793
+ int cnt;
794
+ int i;
795
+
796
+ if (NULL == classname || '#' == *classname) {
797
+ raise_json_err("Only named structs are supported.", "JSONError");
798
+ }
799
+ #ifdef RSTRUCT_LEN
800
+ #if UNIFY_FIXNUM_AND_BIGNUM
801
+ cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
802
+ #else // UNIFY_FIXNUM_AND_INTEGER
803
+ cnt = (int)RSTRUCT_LEN(obj);
804
+ #endif // UNIFY_FIXNUM_AND_INTEGER
805
+ #else
806
+ // This is a bit risky as a struct in C ruby is not the same as a Struct
807
+ // class in interpreted Ruby so length() may not be defined.
808
+ cnt = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
809
+ #endif
810
+ if (sizeof(args) / sizeof(*args) <= (size_t)cnt) {
811
+ // TBD allocate and try again
812
+ cnt = 99;
813
+ }
814
+ dump_obj_classname(rb_class2name(rb_obj_class(obj)), depth, out);
815
+
816
+ assure_size(out, size + sep_len + 6);
817
+ *out->cur++ = ',';
818
+ fill_indent(out, d3);
819
+ *out->cur++ = '"';
820
+ *out->cur++ = 'v';
821
+ *out->cur++ = '"';
822
+ if (0 < out->opts->dump_opts.before_size) {
823
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
824
+ out->cur += out->opts->dump_opts.before_size;
825
+ }
826
+ *out->cur++ = ':';
827
+ if (0 < out->opts->dump_opts.after_size) {
828
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
829
+ out->cur += out->opts->dump_opts.after_size;
830
+ }
831
+ for (i = 0; i < cnt; i++) {
832
+ #ifdef RSTRUCT_LEN
833
+ args[i] = RSTRUCT_GET(obj, i);
834
+ #else
835
+ args[i] = rb_struct_aref(obj, INT2FIX(i));
836
+ #endif
837
+ }
838
+ args[cnt] = Qundef;
839
+ dump_values_array(args, depth, out);
840
+ fill_indent(out, depth);
841
+ *out->cur++ = '}';
842
+ *out->cur = '\0';
843
+ } else {
844
+ oj_dump_obj_to_s(obj, out);
845
+ }
846
+ }
847
+
848
+ static void
849
+ dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
850
+ // The json gem uses to_s explicitly. to_s can be overridden while
851
+ // rb_big2str can not so unless overridden by using add_to_json(Integer)
852
+ // this must use to_s to pass the json gem unit tests.
853
+ volatile VALUE rs;
854
+ int cnt;
855
+
856
+ if (use_bignum_alt) {
857
+ rs = rb_big2str(obj, 10);
858
+ } else {
859
+ rs = rb_funcall(obj, oj_to_s_id, 0);
860
+ }
861
+ rb_check_type(rs, T_STRING);
862
+ cnt = (int)RSTRING_LEN(rs);
863
+ assure_size(out, cnt);
864
+ memcpy(out->cur, rb_string_value_ptr((VALUE*)&rs), cnt);
865
+ out->cur += cnt;
866
+ *out->cur = '\0';
867
+ }
868
+
869
+
870
+ static DumpFunc compat_funcs[] = {
871
+ NULL, // RUBY_T_NONE = 0x00,
872
+ dump_obj, // RUBY_T_OBJECT = 0x01,
873
+ oj_dump_class, // RUBY_T_CLASS = 0x02,
874
+ oj_dump_class, // RUBY_T_MODULE = 0x03,
875
+ dump_float, // RUBY_T_FLOAT = 0x04,
876
+ oj_dump_str, // RUBY_T_STRING = 0x05,
877
+ dump_obj, // RUBY_T_REGEXP = 0x06,
878
+ dump_array, // RUBY_T_ARRAY = 0x07,
879
+ dump_hash, // RUBY_T_HASH = 0x08,
880
+ dump_struct, // RUBY_T_STRUCT = 0x09,
881
+ dump_bignum, // RUBY_T_BIGNUM = 0x0a,
882
+ NULL, // RUBY_T_FILE = 0x0b,
883
+ dump_obj, // RUBY_T_DATA = 0x0c,
884
+ NULL, // RUBY_T_MATCH = 0x0d,
885
+ dump_obj, // RUBY_T_COMPLEX = 0x0e,
886
+ dump_obj, // RUBY_T_RATIONAL = 0x0f,
887
+ NULL, // 0x10
888
+ oj_dump_nil, // RUBY_T_NIL = 0x11,
889
+ oj_dump_true, // RUBY_T_TRUE = 0x12,
890
+ oj_dump_false, // RUBY_T_FALSE = 0x13,
891
+ oj_dump_sym, // RUBY_T_SYMBOL = 0x14,
892
+ oj_dump_fixnum, // RUBY_T_FIXNUM = 0x15,
893
+ };
894
+
895
+ static void
896
+ set_state_depth(VALUE state, int depth) {
897
+ VALUE json_module = rb_const_get_at(rb_cObject, rb_intern("JSON"));
898
+ VALUE ext = rb_const_get(json_module, rb_intern("Ext"));
899
+ VALUE generator = rb_const_get(ext, rb_intern("Generator"));
900
+ VALUE state_class = rb_const_get(generator, rb_intern("State"));
901
+
902
+ if (state_class == rb_obj_class(state)) {
903
+ rb_funcall(state, rb_intern("depth="), 1, INT2NUM(depth));
904
+ }
905
+ }
906
+
907
+ void
908
+ oj_dump_compat_val(VALUE obj, int depth, Out out, bool as_ok) {
909
+ int type = rb_type(obj);
910
+
911
+ if (out->opts->dump_opts.max_depth <= depth) {
912
+ // When JSON.dump is called then an ArgumentError is expected and the
913
+ // limit is the depth inclusive. If JSON.generate is called then a
914
+ // NestingError is expected and the limit is inclusive. Worse than
915
+ // that there are unit tests for both.
916
+ if (CALLER_DUMP == out->caller) {
917
+ if (0 < out->argc) {
918
+ set_state_depth(*out->argv, depth);
919
+ }
920
+ rb_raise(rb_eArgError, "Too deeply nested.");
921
+ } else if (out->opts->dump_opts.max_depth < depth) {
922
+ if (0 < out->argc) {
923
+ set_state_depth(*out->argv, depth - 1);
924
+ }
925
+ raise_json_err("Too deeply nested", "NestingError");
926
+ }
927
+ }
928
+ if (0 < type && type <= RUBY_T_FIXNUM) {
929
+ DumpFunc f = compat_funcs[type];
930
+
931
+ if (NULL != f) {
932
+ f(obj, depth, out, as_ok);
933
+ return;
934
+ }
935
+ }
936
+ oj_dump_nil(Qnil, depth, out, false);
937
+ }