oj 2.18.3 → 3.13.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1324 -0
  3. data/README.md +51 -204
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +49 -72
  6. data/ext/oj/cache.c +326 -0
  7. data/ext/oj/cache.h +21 -0
  8. data/ext/oj/cache8.c +61 -64
  9. data/ext/oj/cache8.h +12 -39
  10. data/ext/oj/circarray.c +37 -68
  11. data/ext/oj/circarray.h +16 -42
  12. data/ext/oj/code.c +221 -0
  13. data/ext/oj/code.h +40 -0
  14. data/ext/oj/compat.c +231 -107
  15. data/ext/oj/custom.c +1125 -0
  16. data/ext/oj/debug.c +132 -0
  17. data/ext/oj/dump.c +935 -2513
  18. data/ext/oj/dump.h +108 -0
  19. data/ext/oj/dump_compat.c +936 -0
  20. data/ext/oj/dump_leaf.c +164 -0
  21. data/ext/oj/dump_object.c +761 -0
  22. data/ext/oj/dump_strict.c +410 -0
  23. data/ext/oj/encode.h +7 -42
  24. data/ext/oj/encoder.c +43 -0
  25. data/ext/oj/err.c +40 -54
  26. data/ext/oj/err.h +52 -46
  27. data/ext/oj/extconf.rb +21 -30
  28. data/ext/oj/fast.c +1097 -1080
  29. data/ext/oj/intern.c +301 -0
  30. data/ext/oj/intern.h +26 -0
  31. data/ext/oj/mimic_json.c +893 -0
  32. data/ext/oj/object.c +549 -620
  33. data/ext/oj/odd.c +155 -167
  34. data/ext/oj/odd.h +37 -63
  35. data/ext/oj/oj.c +1661 -2063
  36. data/ext/oj/oj.h +341 -270
  37. data/ext/oj/parse.c +974 -737
  38. data/ext/oj/parse.h +105 -97
  39. data/ext/oj/parser.c +1526 -0
  40. data/ext/oj/parser.h +90 -0
  41. data/ext/oj/rails.c +1504 -0
  42. data/ext/oj/rails.h +18 -0
  43. data/ext/oj/reader.c +141 -163
  44. data/ext/oj/reader.h +75 -113
  45. data/ext/oj/resolve.c +45 -93
  46. data/ext/oj/resolve.h +7 -34
  47. data/ext/oj/rxclass.c +143 -0
  48. data/ext/oj/rxclass.h +26 -0
  49. data/ext/oj/saj.c +447 -511
  50. data/ext/oj/saj2.c +348 -0
  51. data/ext/oj/scp.c +91 -138
  52. data/ext/oj/sparse.c +793 -644
  53. data/ext/oj/stream_writer.c +331 -0
  54. data/ext/oj/strict.c +145 -109
  55. data/ext/oj/string_writer.c +493 -0
  56. data/ext/oj/trace.c +72 -0
  57. data/ext/oj/trace.h +28 -0
  58. data/ext/oj/usual.c +1254 -0
  59. data/ext/oj/util.c +136 -0
  60. data/ext/oj/util.h +20 -0
  61. data/ext/oj/val_stack.c +62 -70
  62. data/ext/oj/val_stack.h +95 -129
  63. data/ext/oj/validate.c +51 -0
  64. data/ext/oj/wab.c +622 -0
  65. data/lib/oj/bag.rb +1 -0
  66. data/lib/oj/easy_hash.rb +17 -8
  67. data/lib/oj/error.rb +10 -11
  68. data/lib/oj/json.rb +176 -0
  69. data/lib/oj/mimic.rb +158 -19
  70. data/lib/oj/state.rb +132 -0
  71. data/lib/oj/version.rb +2 -2
  72. data/lib/oj.rb +1 -31
  73. data/pages/Advanced.md +22 -0
  74. data/pages/Compatibility.md +25 -0
  75. data/pages/Custom.md +23 -0
  76. data/pages/Encoding.md +65 -0
  77. data/pages/JsonGem.md +94 -0
  78. data/pages/Modes.md +161 -0
  79. data/pages/Options.md +327 -0
  80. data/pages/Parser.md +309 -0
  81. data/pages/Rails.md +167 -0
  82. data/pages/Security.md +20 -0
  83. data/pages/WAB.md +13 -0
  84. data/test/activerecord/result_test.rb +32 -0
  85. data/test/activesupport4/decoding_test.rb +108 -0
  86. data/test/activesupport4/encoding_test.rb +531 -0
  87. data/test/activesupport4/test_helper.rb +41 -0
  88. data/test/activesupport5/abstract_unit.rb +45 -0
  89. data/test/activesupport5/decoding_test.rb +133 -0
  90. data/test/activesupport5/encoding_test.rb +500 -0
  91. data/test/activesupport5/encoding_test_cases.rb +98 -0
  92. data/test/activesupport5/test_helper.rb +72 -0
  93. data/test/activesupport5/time_zone_test_helpers.rb +39 -0
  94. data/test/activesupport6/abstract_unit.rb +44 -0
  95. data/test/activesupport6/decoding_test.rb +133 -0
  96. data/test/activesupport6/encoding_test.rb +507 -0
  97. data/test/activesupport6/encoding_test_cases.rb +98 -0
  98. data/test/activesupport6/test_common.rb +17 -0
  99. data/test/activesupport6/test_helper.rb +163 -0
  100. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  101. data/test/activesupport7/abstract_unit.rb +49 -0
  102. data/test/activesupport7/decoding_test.rb +125 -0
  103. data/test/activesupport7/encoding_test.rb +486 -0
  104. data/test/activesupport7/encoding_test_cases.rb +104 -0
  105. data/test/activesupport7/time_zone_test_helpers.rb +47 -0
  106. data/test/bar.rb +9 -0
  107. data/test/baz.rb +16 -0
  108. data/test/bug.rb +11 -46
  109. data/test/foo.rb +69 -16
  110. data/test/helper.rb +10 -1
  111. data/test/isolated/shared.rb +12 -8
  112. data/test/isolated/test_mimic_rails_after.rb +3 -3
  113. data/test/isolated/test_mimic_rails_before.rb +3 -3
  114. data/test/json_gem/json_addition_test.rb +216 -0
  115. data/test/json_gem/json_common_interface_test.rb +153 -0
  116. data/test/json_gem/json_encoding_test.rb +107 -0
  117. data/test/json_gem/json_ext_parser_test.rb +20 -0
  118. data/test/json_gem/json_fixtures_test.rb +35 -0
  119. data/test/json_gem/json_generator_test.rb +397 -0
  120. data/test/json_gem/json_generic_object_test.rb +90 -0
  121. data/test/json_gem/json_parser_test.rb +470 -0
  122. data/test/json_gem/json_string_matching_test.rb +42 -0
  123. data/test/json_gem/test_helper.rb +26 -0
  124. data/test/mem.rb +33 -0
  125. data/test/perf.rb +1 -1
  126. data/test/perf_compat.rb +30 -28
  127. data/test/perf_dump.rb +50 -0
  128. data/test/perf_object.rb +1 -1
  129. data/test/perf_once.rb +58 -0
  130. data/test/perf_parser.rb +189 -0
  131. data/test/perf_scp.rb +11 -10
  132. data/test/perf_strict.rb +30 -19
  133. data/test/perf_wab.rb +131 -0
  134. data/test/prec.rb +23 -0
  135. data/test/sample.rb +0 -1
  136. data/test/sample_json.rb +1 -1
  137. data/test/test_compat.rb +219 -102
  138. data/test/test_custom.rb +533 -0
  139. data/test/test_fast.rb +107 -35
  140. data/test/test_file.rb +19 -25
  141. data/test/test_generate.rb +21 -0
  142. data/test/test_hash.rb +11 -1
  143. data/test/test_integer_range.rb +72 -0
  144. data/test/test_null.rb +376 -0
  145. data/test/test_object.rb +357 -70
  146. data/test/test_parser.rb +27 -0
  147. data/test/test_parser_saj.rb +245 -0
  148. data/test/test_parser_usual.rb +217 -0
  149. data/test/test_rails.rb +35 -0
  150. data/test/test_saj.rb +1 -1
  151. data/test/test_scp.rb +39 -2
  152. data/test/test_strict.rb +186 -7
  153. data/test/test_various.rb +160 -774
  154. data/test/test_wab.rb +307 -0
  155. data/test/test_writer.rb +90 -2
  156. data/test/tests.rb +24 -0
  157. data/test/tests_mimic.rb +14 -0
  158. data/test/tests_mimic_addition.rb +7 -0
  159. data/test/zoo.rb +13 -0
  160. metadata +194 -56
  161. data/ext/oj/hash.c +0 -163
  162. data/ext/oj/hash.h +0 -46
  163. data/ext/oj/hash_test.c +0 -512
  164. data/test/activesupport_datetime_test.rb +0 -23
  165. data/test/bug2.rb +0 -10
  166. data/test/bug3.rb +0 -46
  167. data/test/bug_fast.rb +0 -32
  168. data/test/bug_load.rb +0 -24
  169. data/test/crash.rb +0 -111
  170. data/test/curl/curl_oj.rb +0 -46
  171. data/test/curl/get_oj.rb +0 -24
  172. data/test/curl/just_curl.rb +0 -31
  173. data/test/curl/just_oj.rb +0 -51
  174. data/test/example.rb +0 -11
  175. data/test/io.rb +0 -48
  176. data/test/isolated/test_mimic_rails_datetime.rb +0 -27
  177. data/test/mod.rb +0 -16
  178. data/test/rails.rb +0 -50
  179. data/test/russian.rb +0 -18
  180. data/test/struct.rb +0 -29
  181. data/test/test_serializer.rb +0 -59
  182. data/test/write_timebars.rb +0 -31
@@ -0,0 +1,893 @@
1
+ // Copyright (c) 2012, 2017 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for license details.
3
+
4
+ #include "dump.h"
5
+ #include "encode.h"
6
+ #include "oj.h"
7
+ #include "parse.h"
8
+
9
+ extern const char oj_json_class[];
10
+
11
+ VALUE oj_array_nl_sym;
12
+ VALUE oj_ascii_only_sym;
13
+ VALUE oj_json_generator_error_class;
14
+ VALUE oj_json_parser_error_class;
15
+ VALUE oj_max_nesting_sym;
16
+ VALUE oj_object_nl_sym;
17
+ VALUE oj_space_before_sym;
18
+ VALUE oj_space_sym;
19
+
20
+ static VALUE state_class = Qundef;
21
+
22
+ // mimic JSON documentation
23
+
24
+ /* Document-module: JSON::Ext
25
+ *
26
+ * The Ext module is a placeholder in the mimic JSON module used for
27
+ * compatibility only.
28
+ */
29
+ /* Document-class: JSON::Ext::Parser
30
+ *
31
+ * The JSON::Ext::Parser is a placeholder in the mimic JSON module used for
32
+ * compatibility only.
33
+ */
34
+ /* Document-class: JSON::Ext::Generator
35
+ *
36
+ * The JSON::Ext::Generator is a placeholder in the mimic JSON module used for
37
+ * compatibility only.
38
+ */
39
+
40
+ /* Document-method: parser=
41
+ * call-seq: parser=(parser)
42
+ *
43
+ * Does nothing other than provide compatibility.
44
+ * - *parser* [_Object_] ignored
45
+ */
46
+ /* Document-method: generator=
47
+ * call-seq: generator=(generator)
48
+ *
49
+ * Does nothing other than provide compatibility.
50
+ * - *generator* [_Object_] ignored
51
+ */
52
+
53
+ VALUE
54
+ oj_get_json_err_class(const char *err_classname) {
55
+ volatile VALUE json_module;
56
+ volatile VALUE clas;
57
+ volatile VALUE json_error_class;
58
+
59
+ if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
60
+ json_module = rb_const_get_at(rb_cObject, rb_intern("JSON"));
61
+ } else {
62
+ json_module = rb_define_module("JSON");
63
+ }
64
+ if (rb_const_defined_at(json_module, rb_intern("JSONError"))) {
65
+ json_error_class = rb_const_get(json_module, rb_intern("JSONError"));
66
+ } else {
67
+ json_error_class = rb_define_class_under(json_module, "JSONError", rb_eStandardError);
68
+ }
69
+ if (0 == strcmp(err_classname, "JSONError")) {
70
+ clas = json_error_class;
71
+ } else {
72
+ if (rb_const_defined_at(json_module, rb_intern(err_classname))) {
73
+ clas = rb_const_get(json_module, rb_intern(err_classname));
74
+ } else {
75
+ clas = rb_define_class_under(json_module, err_classname, json_error_class);
76
+ }
77
+ }
78
+ return clas;
79
+ }
80
+
81
+ void oj_parse_mimic_dump_options(VALUE ropts, Options copts) {
82
+ VALUE v;
83
+ size_t len;
84
+
85
+ if (T_HASH != rb_type(ropts)) {
86
+ if (rb_respond_to(ropts, oj_to_hash_id)) {
87
+ ropts = rb_funcall(ropts, oj_to_hash_id, 0);
88
+ } else if (rb_respond_to(ropts, oj_to_h_id)) {
89
+ ropts = rb_funcall(ropts, oj_to_h_id, 0);
90
+ } else if (Qnil == ropts) {
91
+ return;
92
+ } else {
93
+ rb_raise(rb_eArgError, "options must be a hash.");
94
+ }
95
+ }
96
+ v = rb_hash_lookup(ropts, oj_max_nesting_sym);
97
+ if (Qtrue == v) {
98
+ copts->dump_opts.max_depth = 100;
99
+ } else if (Qfalse == v || Qnil == v) {
100
+ copts->dump_opts.max_depth = MAX_DEPTH;
101
+ } else if (T_FIXNUM == rb_type(v)) {
102
+ copts->dump_opts.max_depth = NUM2INT(v);
103
+ if (0 >= copts->dump_opts.max_depth) {
104
+ copts->dump_opts.max_depth = MAX_DEPTH;
105
+ }
106
+ }
107
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_allow_nan_sym))) {
108
+ if (Qtrue == v) {
109
+ copts->dump_opts.nan_dump = WordNan;
110
+ } else {
111
+ copts->dump_opts.nan_dump = RaiseNan;
112
+ }
113
+ }
114
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_indent_sym))) {
115
+ rb_check_type(v, T_STRING);
116
+ if (sizeof(copts->dump_opts.indent_str) <= (len = RSTRING_LEN(v))) {
117
+ rb_raise(rb_eArgError,
118
+ "indent string is limited to %lu characters.",
119
+ (unsigned long)sizeof(copts->dump_opts.indent_str));
120
+ }
121
+ strcpy(copts->dump_opts.indent_str, StringValuePtr(v));
122
+ copts->dump_opts.indent_size = (uint8_t)len;
123
+ copts->dump_opts.use = true;
124
+ }
125
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_space_sym))) {
126
+ rb_check_type(v, T_STRING);
127
+ if (sizeof(copts->dump_opts.after_sep) <= (len = RSTRING_LEN(v))) {
128
+ rb_raise(rb_eArgError,
129
+ "space string is limited to %lu characters.",
130
+ (unsigned long)sizeof(copts->dump_opts.after_sep));
131
+ }
132
+ strcpy(copts->dump_opts.after_sep, StringValuePtr(v));
133
+ copts->dump_opts.after_size = (uint8_t)len;
134
+ copts->dump_opts.use = true;
135
+ }
136
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_space_before_sym))) {
137
+ rb_check_type(v, T_STRING);
138
+ if (sizeof(copts->dump_opts.before_sep) <= (len = RSTRING_LEN(v))) {
139
+ rb_raise(rb_eArgError,
140
+ "space_before string is limited to %lu characters.",
141
+ (unsigned long)sizeof(copts->dump_opts.before_sep));
142
+ }
143
+ strcpy(copts->dump_opts.before_sep, StringValuePtr(v));
144
+ copts->dump_opts.before_size = (uint8_t)len;
145
+ copts->dump_opts.use = true;
146
+ }
147
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_object_nl_sym))) {
148
+ rb_check_type(v, T_STRING);
149
+ if (sizeof(copts->dump_opts.hash_nl) <= (len = RSTRING_LEN(v))) {
150
+ rb_raise(rb_eArgError,
151
+ "object_nl string is limited to %lu characters.",
152
+ (unsigned long)sizeof(copts->dump_opts.hash_nl));
153
+ }
154
+ strcpy(copts->dump_opts.hash_nl, StringValuePtr(v));
155
+ copts->dump_opts.hash_size = (uint8_t)len;
156
+ copts->dump_opts.use = true;
157
+ }
158
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_array_nl_sym))) {
159
+ rb_check_type(v, T_STRING);
160
+ if (sizeof(copts->dump_opts.array_nl) <= (len = RSTRING_LEN(v))) {
161
+ rb_raise(rb_eArgError,
162
+ "array_nl string is limited to %lu characters.",
163
+ (unsigned long)sizeof(copts->dump_opts.array_nl));
164
+ }
165
+ strcpy(copts->dump_opts.array_nl, StringValuePtr(v));
166
+ copts->dump_opts.array_size = (uint8_t)len;
167
+ copts->dump_opts.use = true;
168
+ }
169
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_quirks_mode_sym))) {
170
+ copts->quirks_mode = (Qtrue == v) ? Yes : No;
171
+ }
172
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_ascii_only_sym))) {
173
+ // generate seems to assume anything except nil and false are true.
174
+ if (Qfalse == v) {
175
+ copts->escape_mode = JXEsc;
176
+ } else {
177
+ copts->escape_mode = ASCIIEsc;
178
+ }
179
+ }
180
+ }
181
+
182
+ static int mimic_limit_arg(VALUE a) {
183
+ if (Qnil == a || T_FIXNUM != rb_type(a)) {
184
+ return -1;
185
+ }
186
+ return NUM2INT(a);
187
+ }
188
+
189
+ /* Document-method: dump
190
+ * call-seq: dump(obj, anIO=nil, limit=nil)
191
+ *
192
+ * Encodes an object as a JSON String.
193
+ *
194
+ * - *obj* [_Object_] object to convert to encode as JSON
195
+ * - *anIO* [_IO_] an IO that allows writing
196
+ * - *limit* [_Fixnum_] ignored
197
+ *
198
+ * Returns [_String_] a JSON string.
199
+ */
200
+ static VALUE mimic_dump(int argc, VALUE *argv, VALUE self) {
201
+ struct _out out;
202
+ struct _options copts = oj_default_options;
203
+ VALUE rstr;
204
+ VALUE active_hack[1];
205
+
206
+ copts.str_rx.head = NULL;
207
+ copts.str_rx.tail = NULL;
208
+
209
+ oj_out_init(&out);
210
+
211
+ out.caller = CALLER_DUMP;
212
+ copts.escape_mode = JXEsc;
213
+ copts.mode = CompatMode;
214
+
215
+ /* seems like this is not correct
216
+ if (No == copts.nilnil && Qnil == *argv) {
217
+ rb_raise(rb_eTypeError, "nil not allowed.");
218
+ }
219
+ */
220
+ copts.dump_opts.max_depth = MAX_DEPTH; // when using dump there is no limit
221
+ out.omit_nil = copts.dump_opts.omit_nil;
222
+
223
+ if (2 <= argc) {
224
+ int limit;
225
+
226
+ // The json gem take a more liberal approach to optional
227
+ // arguments. Expected are (obj, anIO=nil, limit=nil) yet the io
228
+ // argument can be left off completely and the 2nd argument is then
229
+ // the limit.
230
+ if (0 <= (limit = mimic_limit_arg(argv[1]))) {
231
+ copts.dump_opts.max_depth = limit;
232
+ }
233
+ if (3 <= argc && 0 <= (limit = mimic_limit_arg(argv[2]))) {
234
+ copts.dump_opts.max_depth = limit;
235
+ }
236
+ }
237
+ // ActiveSupport in active_support/core_ext/object/json.rb check the
238
+ // optional argument type to to_json and it the argument is a
239
+ // ::JSON::State it calls the JSON gem code otherwise it calls the active
240
+ // support encoder code. To make sure the desired branch is called a
241
+ // default ::JSON::State argument is passed in. Basically a hack to get
242
+ // around the active support hack so two wrongs make a right this time.
243
+ active_hack[0] = rb_funcall(state_class, oj_new_id, 0);
244
+ oj_dump_obj_to_json_using_params(*argv, &copts, &out, 1, active_hack);
245
+
246
+ if (0 == out.buf) {
247
+ rb_raise(rb_eNoMemError, "Not enough memory.");
248
+ }
249
+ rstr = rb_str_new2(out.buf);
250
+ rstr = oj_encode(rstr);
251
+ if (2 <= argc && Qnil != argv[1] && rb_respond_to(argv[1], oj_write_id)) {
252
+ VALUE io = argv[1];
253
+ VALUE args[1];
254
+
255
+ *args = rstr;
256
+ rb_funcall2(io, oj_write_id, 1, args);
257
+ rstr = io;
258
+ }
259
+
260
+ oj_out_free(&out);
261
+
262
+ return rstr;
263
+ }
264
+
265
+ // This is the signature for the hash_foreach callback also.
266
+ static int mimic_walk(VALUE key, VALUE obj, VALUE proc) {
267
+ switch (rb_type(obj)) {
268
+ case T_HASH: rb_hash_foreach(obj, mimic_walk, proc); break;
269
+ case T_ARRAY: {
270
+ size_t cnt = RARRAY_LEN(obj);
271
+ size_t i;
272
+
273
+ for (i = 0; i < cnt; i++) {
274
+ mimic_walk(Qnil, RARRAY_AREF(obj, i), proc);
275
+ }
276
+ break;
277
+ }
278
+ default: break;
279
+ }
280
+ if (Qnil == proc) {
281
+ if (rb_block_given_p()) {
282
+ rb_yield(obj);
283
+ }
284
+ } else {
285
+ VALUE args[1];
286
+
287
+ *args = obj;
288
+ rb_proc_call_with_block(proc, 1, args, Qnil);
289
+ }
290
+ return ST_CONTINUE;
291
+ }
292
+
293
+ /* Document-method: restore
294
+ * call-seq: restore(source, proc=nil)
295
+ *
296
+ * Loads a Ruby Object from a JSON source that can be either a String or an
297
+ * IO. If Proc is given or a block is provided it is called with each nested
298
+ * element of the loaded Object.
299
+ *
300
+ * - *source* [_String_|IO] JSON source
301
+ * - *proc* [_Proc_] to yield to on each element or nil
302
+ *
303
+ * Returns [_Object_] the decoded Object.
304
+ */
305
+
306
+ /* Document-method: load
307
+ * call-seq: load(source, proc=nil)
308
+ *
309
+ * Loads a Ruby Object from a JSON source that can be either a String or an
310
+ * IO. If Proc is given or a block is provided it is called with each nested
311
+ * element of the loaded Object.
312
+ *
313
+ * - *source* [_String_|IO] JSON source
314
+ * - *proc* [_Proc_] to yield to on each element or nil
315
+ *
316
+ * Returns [_Object_] the decode Object.
317
+ */
318
+ static VALUE mimic_load(int argc, VALUE *argv, VALUE self) {
319
+ VALUE obj;
320
+ VALUE p = Qnil;
321
+
322
+ obj = oj_compat_load(argc, argv, self);
323
+ if (2 <= argc) {
324
+ if (rb_cProc == rb_obj_class(argv[1])) {
325
+ p = argv[1];
326
+ } else if (3 <= argc) {
327
+ if (rb_cProc == rb_obj_class(argv[2])) {
328
+ p = argv[2];
329
+ }
330
+ }
331
+ }
332
+ mimic_walk(Qnil, obj, p);
333
+
334
+ return obj;
335
+ }
336
+
337
+ /* Document-method: []
338
+ * call-seq: [](obj, opts={})
339
+ *
340
+ * If the obj argument is a String then it is assumed to be a JSON String and
341
+ * parsed otherwise the obj is encoded as a JSON String.
342
+ *
343
+ * - *obj* [_String_|Hash|Array] object to convert
344
+ * - *opts* [_Hash_] same options as either generate or parse
345
+ *
346
+ * Returns [_Object_]
347
+ */
348
+ static VALUE mimic_dump_load(int argc, VALUE *argv, VALUE self) {
349
+ if (1 > argc) {
350
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
351
+ } else if (T_STRING == rb_type(*argv)) {
352
+ return mimic_load(argc, argv, self);
353
+ } else {
354
+ return mimic_dump(argc, argv, self);
355
+ }
356
+ return Qnil;
357
+ }
358
+
359
+ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
360
+ struct _out out;
361
+ VALUE rstr;
362
+
363
+ if (0 == argc) {
364
+ rb_raise(rb_eArgError, "wrong number of arguments (0))");
365
+ }
366
+ memset(out.stack_buffer, 0, sizeof(out.stack_buffer));
367
+
368
+ oj_out_init(&out);
369
+
370
+ out.omit_nil = copts->dump_opts.omit_nil;
371
+ out.caller = CALLER_GENERATE;
372
+ // For obj.to_json or generate nan is not allowed but if called from dump
373
+ // it is.
374
+ copts->dump_opts.nan_dump = RaiseNan;
375
+ copts->mode = CompatMode;
376
+ copts->to_json = Yes;
377
+ if (2 == argc && Qnil != argv[1]) {
378
+ oj_parse_mimic_dump_options(argv[1], copts);
379
+ }
380
+ /* seems like this is not correct
381
+ if (No == copts->nilnil && Qnil == *argv) {
382
+ rb_raise(rb_eTypeError, "nil not allowed.");
383
+ }
384
+ */
385
+ if (1 < argc) {
386
+ oj_dump_obj_to_json_using_params(*argv, copts, &out, argc - 1, argv + 1);
387
+ } else {
388
+ VALUE active_hack[1];
389
+
390
+ if (Qundef == state_class) {
391
+ oj_define_mimic_json(0, NULL, Qnil);
392
+ }
393
+ active_hack[0] = rb_funcall(state_class, oj_new_id, 0);
394
+ oj_dump_obj_to_json_using_params(*argv, copts, &out, 1, active_hack);
395
+ }
396
+ if (0 == out.buf) {
397
+ rb_raise(rb_eNoMemError, "Not enough memory.");
398
+ }
399
+ rstr = rb_str_new2(out.buf);
400
+ rstr = oj_encode(rstr);
401
+
402
+ oj_out_free(&out);
403
+
404
+ return rstr;
405
+ }
406
+
407
+ /* Document-method: fast_generate
408
+ * call-seq: fast_generate(obj, opts=nil)
409
+ * Same as generate().
410
+ * @see generate
411
+ */
412
+
413
+ /* Document-method: generate
414
+ * call-seq: generate(obj, opts=nil)
415
+ *
416
+ * Encode obj as a JSON String. The obj argument must be a Hash, Array, or
417
+ * respond to to_h or to_json. Options other than those listed such as
418
+ * +:allow_nan+ or +:max_nesting+ are ignored.
419
+ *
420
+ * - *obj* [_Object_|Hash|Array] object to convert to a JSON String
421
+ * - *opts* [_Hash_] options
422
+ * - - *:indent* [_String_] String to use for indentation.
423
+ * - *:space* [_String_] String placed after a , or : delimiter
424
+ * - *:space_before* [_String_] String placed before a : delimiter
425
+ * - *:object_nl* [_String_] String placed after a JSON object
426
+ * - *:array_nl* [_String_] String placed after a JSON array
427
+ * - *:ascii_only* [_Boolean_] if not nil or false then use only ascii characters in the output.
428
+ * Note JSON.generate does support this even if it is not documented.
429
+ *
430
+ * Returns [_String_] generated JSON.
431
+ */
432
+ VALUE
433
+ oj_mimic_generate(int argc, VALUE *argv, VALUE self) {
434
+ struct _options copts = oj_default_options;
435
+
436
+ copts.str_rx.head = NULL;
437
+ copts.str_rx.tail = NULL;
438
+
439
+ return mimic_generate_core(argc, argv, &copts);
440
+ }
441
+
442
+ /* Document-method: pretty_generate
443
+ * call-seq: pretty_generate(obj, opts=nil)
444
+ *
445
+ * Same as generate() but with different defaults for the spacing options.
446
+ * @see generate
447
+ *
448
+ * Return [_String_] the generated JSON.
449
+ */
450
+ VALUE
451
+ oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
452
+ struct _options copts = oj_default_options;
453
+ VALUE rargs[2];
454
+ volatile VALUE h;
455
+
456
+ // Some (all?) json gem to_json methods need a State instance and not just
457
+ // a Hash. I haven't dug deep enough to find out why but using a State
458
+ // instance and not a Hash gives the desired behavior.
459
+ *rargs = *argv;
460
+ if (0 == argc) {
461
+ rb_raise(rb_eArgError, "wrong number of arguments (0))");
462
+ }
463
+ if (1 == argc || Qnil == argv[1]) {
464
+ h = rb_hash_new();
465
+ } else {
466
+ h = argv[1];
467
+ }
468
+ if (!oj_hash_has_key(h, oj_indent_sym)) {
469
+ rb_hash_aset(h, oj_indent_sym, rb_str_new2(" "));
470
+ }
471
+ if (!oj_hash_has_key(h, oj_space_before_sym)) {
472
+ rb_hash_aset(h, oj_space_before_sym, rb_str_new2(""));
473
+ }
474
+ if (!oj_hash_has_key(h, oj_space_sym)) {
475
+ rb_hash_aset(h, oj_space_sym, rb_str_new2(" "));
476
+ }
477
+ if (!oj_hash_has_key(h, oj_object_nl_sym)) {
478
+ rb_hash_aset(h, oj_object_nl_sym, rb_str_new2("\n"));
479
+ }
480
+ if (!oj_hash_has_key(h, oj_array_nl_sym)) {
481
+ rb_hash_aset(h, oj_array_nl_sym, rb_str_new2("\n"));
482
+ }
483
+ if (Qundef == state_class) {
484
+ oj_define_mimic_json(0, NULL, Qnil);
485
+ }
486
+ rargs[1] = rb_funcall(state_class, oj_new_id, 1, h);
487
+
488
+ copts.str_rx.head = NULL;
489
+ copts.str_rx.tail = NULL;
490
+ strcpy(copts.dump_opts.indent_str, " ");
491
+ copts.dump_opts.indent_size = (uint8_t)strlen(copts.dump_opts.indent_str);
492
+ strcpy(copts.dump_opts.before_sep, "");
493
+ copts.dump_opts.before_size = (uint8_t)strlen(copts.dump_opts.before_sep);
494
+ strcpy(copts.dump_opts.after_sep, " ");
495
+ copts.dump_opts.after_size = (uint8_t)strlen(copts.dump_opts.after_sep);
496
+ strcpy(copts.dump_opts.hash_nl, "\n");
497
+ copts.dump_opts.hash_size = (uint8_t)strlen(copts.dump_opts.hash_nl);
498
+ strcpy(copts.dump_opts.array_nl, "\n");
499
+ copts.dump_opts.array_size = (uint8_t)strlen(copts.dump_opts.array_nl);
500
+ copts.dump_opts.use = true;
501
+
502
+ return mimic_generate_core(2, rargs, &copts);
503
+ }
504
+
505
+ static int parse_options_cb(VALUE k, VALUE v, VALUE info) {
506
+ struct _parseInfo *pi = (struct _parseInfo *)info;
507
+
508
+ if (oj_symbolize_names_sym == k) {
509
+ pi->options.sym_key = (Qtrue == v) ? Yes : No;
510
+ } else if (oj_quirks_mode_sym == k) {
511
+ pi->options.quirks_mode = (Qtrue == v) ? Yes : No;
512
+ } else if (oj_create_additions_sym == k) {
513
+ pi->options.create_ok = (Qtrue == v) ? Yes : No;
514
+ } else if (oj_allow_nan_sym == k) {
515
+ pi->options.allow_nan = (Qtrue == v) ? Yes : No;
516
+ } else if (oj_hash_class_sym == k) {
517
+ if (Qnil == v) {
518
+ pi->options.hash_class = Qnil;
519
+ } else {
520
+ rb_check_type(v, T_CLASS);
521
+ pi->options.hash_class = v;
522
+ }
523
+ } else if (oj_object_class_sym == k) {
524
+ if (Qnil == v) {
525
+ pi->options.hash_class = Qnil;
526
+ } else {
527
+ rb_check_type(v, T_CLASS);
528
+ pi->options.hash_class = v;
529
+ }
530
+ } else if (oj_array_class_sym == k) {
531
+ if (Qnil == v) {
532
+ pi->options.array_class = Qnil;
533
+ } else {
534
+ rb_check_type(v, T_CLASS);
535
+ pi->options.array_class = v;
536
+ }
537
+ } else if (oj_decimal_class_sym == k) {
538
+ pi->options.compat_bigdec = (oj_bigdecimal_class == v);
539
+ } else if (oj_max_nesting_sym == k) {
540
+ if (Qtrue == v) {
541
+ pi->max_depth = 100;
542
+ } else if (Qfalse == v || Qnil == v) {
543
+ pi->max_depth = 0;
544
+ } else if (T_FIXNUM == rb_type(v)) {
545
+ pi->max_depth = NUM2INT(v);
546
+ }
547
+ }
548
+ return ST_CONTINUE;
549
+ }
550
+
551
+ static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
552
+ struct _parseInfo pi;
553
+ VALUE ropts;
554
+ VALUE args[1];
555
+
556
+ rb_scan_args(argc, argv, "11", NULL, &ropts);
557
+ parse_info_init(&pi);
558
+ oj_set_compat_callbacks(&pi);
559
+
560
+ pi.err_class = oj_json_parser_error_class;
561
+ // pi.err_class = Qnil;
562
+
563
+ pi.options = oj_default_options;
564
+ pi.options.auto_define = No;
565
+ pi.options.quirks_mode = Yes;
566
+ pi.options.allow_invalid = Yes;
567
+ pi.options.empty_string = No;
568
+ pi.options.create_ok = No;
569
+ pi.options.allow_nan = (bang ? Yes : No);
570
+ pi.options.nilnil = No;
571
+ pi.options.bigdec_load = RubyDec;
572
+ pi.options.mode = CompatMode;
573
+ pi.max_depth = 100;
574
+
575
+ if (Qnil != ropts) {
576
+ if (T_HASH != rb_type(ropts)) {
577
+ rb_raise(rb_eArgError, "options must be a hash.");
578
+ }
579
+
580
+ rb_hash_foreach(ropts, parse_options_cb, (VALUE)&pi);
581
+ oj_parse_opt_match_string(&pi.options.str_rx, ropts);
582
+ if (Yes == pi.options.create_ok && Yes == pi.options.sym_key) {
583
+ rb_raise(rb_eArgError, ":symbolize_names and :create_additions can not both be true.");
584
+ }
585
+ }
586
+ *args = *argv;
587
+
588
+ if (T_STRING == rb_type(*args)) {
589
+ return oj_pi_parse(1, args, &pi, 0, 0, false);
590
+ } else {
591
+ return oj_pi_sparse(1, args, &pi, 0);
592
+ }
593
+ }
594
+
595
+ /* Document-method: parse
596
+ * call-seq: parse(source, opts=nil)
597
+ *
598
+ * Parses a JSON String or IO into a Ruby Object. Options other than those
599
+ * listed such as +:allow_nan+ or +:max_nesting+ are ignored. +:object_class+ and
600
+ * +:array_object+ are not supported.
601
+ *
602
+ * - *source* [_String_|IO] source to parse
603
+ * - *opts* [_Hash_] options
604
+ * - *:symbolize* [Boolean] _names flag indicating JSON object keys should be Symbols instead of
605
+ * Strings
606
+ * - *:create_additions* [Boolean] flag indicating a key matching +create_id+ in a JSON object
607
+ * should trigger the creation of Ruby Object
608
+ *
609
+ * Returns [Object]
610
+ * @see create_id=
611
+ */
612
+ VALUE
613
+ oj_mimic_parse(int argc, VALUE *argv, VALUE self) {
614
+ return mimic_parse_core(argc, argv, self, false);
615
+ }
616
+
617
+ /* Document-method: parse!
618
+ * call-seq: parse!(source, opts=nil)
619
+ *
620
+ * Same as parse().
621
+ * @see parse
622
+ */
623
+ static VALUE mimic_parse_bang(int argc, VALUE *argv, VALUE self) {
624
+ return mimic_parse_core(argc, argv, self, true);
625
+ }
626
+
627
+ /* Document-method: recurse_proc
628
+ * call-seq: recurse_proc(obj, &proc)
629
+ *
630
+ * Yields to the proc for every element in the obj recursively.
631
+ *
632
+ * - *obj* [_Hash_|Array] object to walk
633
+ * - *proc* [_Proc_] to yield to on each element
634
+ */
635
+ static VALUE mimic_recurse_proc(VALUE self, VALUE obj) {
636
+ rb_need_block();
637
+ mimic_walk(Qnil, obj, Qnil);
638
+
639
+ return Qnil;
640
+ }
641
+
642
+ /* Document-method: create_id=
643
+ * call-seq: create_id=(id)
644
+ *
645
+ * Sets the create_id tag to look for in JSON document. That key triggers the
646
+ * creation of a class with the same name.
647
+ *
648
+ * - *id* [_nil_|String] new create_id
649
+ *
650
+ * Returns [_String_] the id.
651
+ */
652
+ static VALUE mimic_set_create_id(VALUE self, VALUE id) {
653
+ Check_Type(id, T_STRING);
654
+
655
+ if (NULL != oj_default_options.create_id) {
656
+ if (oj_json_class != oj_default_options.create_id) {
657
+ xfree((char *)oj_default_options.create_id);
658
+ }
659
+ oj_default_options.create_id = NULL;
660
+ oj_default_options.create_id_len = 0;
661
+ }
662
+ if (Qnil != id) {
663
+ size_t len = RSTRING_LEN(id) + 1;
664
+
665
+ oj_default_options.create_id = ALLOC_N(char, len);
666
+ strcpy((char *)oj_default_options.create_id, StringValuePtr(id));
667
+ oj_default_options.create_id_len = len - 1;
668
+ }
669
+ return id;
670
+ }
671
+
672
+ /* Document-method: create_id
673
+ * call-seq: create_id()
674
+ *
675
+ * Returns [_String_] the create_id.
676
+ */
677
+ static VALUE mimic_create_id(VALUE self) {
678
+ if (NULL != oj_default_options.create_id) {
679
+ return rb_utf8_str_new(oj_default_options.create_id, oj_default_options.create_id_len);
680
+ }
681
+ return rb_str_new_cstr(oj_json_class);
682
+ }
683
+
684
+ static struct _options mimic_object_to_json_options = {0, // indent
685
+ No, // circular
686
+ No, // auto_define
687
+ No, // sym_key
688
+ JXEsc, // escape_mode
689
+ CompatMode, // mode
690
+ No, // class_cache
691
+ RubyTime, // time_format
692
+ No, // bigdec_as_num
693
+ RubyDec, // bigdec_load
694
+ false, // compat_bigdec
695
+ No, // to_hash
696
+ No, // to_json
697
+ No, // as_json
698
+ No, // raw_json
699
+ No, // nilnil
700
+ No, // empty_string
701
+ Yes, // allow_gc
702
+ Yes, // quirks_mode
703
+ Yes, // allow_invalid
704
+ No, // create_ok
705
+ No, // allow_nan
706
+ No, // trace
707
+ No, // safe
708
+ false, // sec_prec_set
709
+ No, // ignore_under
710
+ Yes, // cache_keys
711
+ 0, // cache_str
712
+ 0, // int_range_min
713
+ 0, // int_range_max
714
+ oj_json_class, // create_id
715
+ 10, // create_id_len
716
+ 3, // sec_prec
717
+ 0, // float_prec
718
+ "%0.16g", // float_fmt
719
+ Qnil, // hash_class
720
+ Qnil, // array_class
721
+ {
722
+ // dump_opts
723
+ false, // use
724
+ "", // indent
725
+ "", // before_sep
726
+ "", // after_sep
727
+ "", // hash_nl
728
+ "", // array_nl
729
+ 0, // indent_size
730
+ 0, // before_size
731
+ 0, // after_size
732
+ 0, // hash_size
733
+ 0, // array_size
734
+ RaiseNan, // nan_dump
735
+ false, // omit_nil
736
+ 100, // max_depth
737
+ },
738
+ {
739
+ // str_rx
740
+ NULL, // head
741
+ NULL, // tail
742
+ {'\0'}, // err
743
+ }};
744
+
745
+ static VALUE mimic_object_to_json(int argc, VALUE *argv, VALUE self) {
746
+ struct _out out;
747
+ VALUE rstr;
748
+ struct _options copts = oj_default_options;
749
+
750
+ copts.str_rx.head = NULL;
751
+ copts.str_rx.tail = NULL;
752
+
753
+ oj_out_init(&out);
754
+
755
+ out.omit_nil = copts.dump_opts.omit_nil;
756
+ copts.mode = CompatMode;
757
+ copts.to_json = No;
758
+ if (1 <= argc && Qnil != argv[0]) {
759
+ oj_parse_mimic_dump_options(argv[0], &copts);
760
+ }
761
+ // To be strict the mimic_object_to_json_options should be used but people
762
+ // seem to prefer the option of changing that.
763
+ // oj_dump_obj_to_json(self, &mimic_object_to_json_options, &out);
764
+ oj_dump_obj_to_json_using_params(self, &copts, &out, argc, argv);
765
+ if (NULL == out.buf) {
766
+ rb_raise(rb_eNoMemError, "Not enough memory.");
767
+ }
768
+ rstr = rb_str_new2(out.buf);
769
+ rstr = oj_encode(rstr);
770
+
771
+ oj_out_free(&out);
772
+
773
+ return rstr;
774
+ }
775
+
776
+ /* Document-method: state
777
+ * call-seq: state()
778
+ *
779
+ * Returns [_JSON::State_] the JSON::State class.
780
+ */
781
+ static VALUE mimic_state(VALUE self) {
782
+ return state_class;
783
+ }
784
+
785
+ void oj_mimic_json_methods(VALUE json) {
786
+ VALUE json_error;
787
+ VALUE generator;
788
+ VALUE ext;
789
+
790
+ rb_define_module_function(json, "create_id=", mimic_set_create_id, 1);
791
+ rb_define_module_function(json, "create_id", mimic_create_id, 0);
792
+
793
+ rb_define_module_function(json, "dump", mimic_dump, -1);
794
+ rb_define_module_function(json, "load", mimic_load, -1);
795
+ rb_define_module_function(json, "restore", mimic_load, -1);
796
+ rb_define_module_function(json, "recurse_proc", mimic_recurse_proc, 1);
797
+ rb_define_module_function(json, "[]", mimic_dump_load, -1);
798
+
799
+ rb_define_module_function(json, "generate", oj_mimic_generate, -1);
800
+ rb_define_module_function(json, "fast_generate", oj_mimic_generate, -1);
801
+ rb_define_module_function(json, "pretty_generate", oj_mimic_pretty_generate, -1);
802
+ // For older versions of JSON, the deprecated unparse methods.
803
+ rb_define_module_function(json, "unparse", oj_mimic_generate, -1);
804
+ rb_define_module_function(json, "fast_unparse", oj_mimic_generate, -1);
805
+ rb_define_module_function(json, "pretty_unparse", oj_mimic_pretty_generate, -1);
806
+
807
+ rb_define_module_function(json, "parse", oj_mimic_parse, -1);
808
+ rb_define_module_function(json, "parse!", mimic_parse_bang, -1);
809
+
810
+ rb_define_module_function(json, "state", mimic_state, 0);
811
+
812
+ if (rb_const_defined_at(json, rb_intern("JSONError"))) {
813
+ json_error = rb_const_get(json, rb_intern("JSONError"));
814
+ } else {
815
+ json_error = rb_define_class_under(json, "JSONError", rb_eStandardError);
816
+ }
817
+ if (rb_const_defined_at(json, rb_intern("ParserError"))) {
818
+ oj_json_parser_error_class = rb_const_get(json, rb_intern("ParserError"));
819
+ } else {
820
+ oj_json_parser_error_class = rb_define_class_under(json, "ParserError", json_error);
821
+ }
822
+ if (rb_const_defined_at(json, rb_intern("GeneratorError"))) {
823
+ oj_json_generator_error_class = rb_const_get(json, rb_intern("GeneratorError"));
824
+ } else {
825
+ oj_json_generator_error_class = rb_define_class_under(json, "GeneratorError", json_error);
826
+ }
827
+ if (rb_const_defined_at(json, rb_intern("NestingError"))) {
828
+ rb_const_get(json, rb_intern("NestingError"));
829
+ } else {
830
+ rb_define_class_under(json, "NestingError", json_error);
831
+ }
832
+
833
+ if (rb_const_defined_at(json, rb_intern("Ext"))) {
834
+ ext = rb_const_get_at(json, rb_intern("Ext"));
835
+ } else {
836
+ ext = rb_define_module_under(json, "Ext");
837
+ }
838
+ if (rb_const_defined_at(ext, rb_intern("Generator"))) {
839
+ generator = rb_const_get_at(ext, rb_intern("Generator"));
840
+ } else {
841
+ generator = rb_define_module_under(ext, "Generator");
842
+ }
843
+ if (!rb_const_defined_at(generator, rb_intern("State"))) {
844
+ rb_require("oj/state");
845
+ }
846
+ // Pull in the JSON::State mimic file.
847
+ state_class = rb_const_get_at(generator, rb_intern("State"));
848
+ rb_gc_register_mark_object(state_class);
849
+ }
850
+
851
+ /* Document-module: JSON
852
+ *
853
+ * A mimic of the json gem module.
854
+ */
855
+ VALUE
856
+ oj_define_mimic_json(int argc, VALUE *argv, VALUE self) {
857
+ VALUE dummy;
858
+ VALUE verbose;
859
+ VALUE json;
860
+
861
+ // Either set the paths to indicate JSON has been loaded or replaces the
862
+ // methods if it has been loaded.
863
+ if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
864
+ json = rb_const_get_at(rb_cObject, rb_intern("JSON"));
865
+ } else {
866
+ json = rb_define_module("JSON");
867
+ }
868
+ verbose = rb_gv_get("$VERBOSE");
869
+ rb_gv_set("$VERBOSE", Qfalse);
870
+ rb_define_module_function(rb_cObject, "JSON", mimic_dump_load, -1);
871
+ dummy = rb_gv_get("$LOADED_FEATURES");
872
+ if (rb_type(dummy) == T_ARRAY) {
873
+ rb_ary_push(dummy, rb_str_new2("json"));
874
+ if (0 < argc) {
875
+ VALUE mimic_args[1];
876
+
877
+ *mimic_args = *argv;
878
+ rb_funcall2(Oj, rb_intern("mimic_loaded"), 1, mimic_args);
879
+ } else {
880
+ rb_funcall2(Oj, rb_intern("mimic_loaded"), 0, 0);
881
+ }
882
+ }
883
+ oj_mimic_json_methods(json);
884
+
885
+ rb_define_method(rb_cObject, "to_json", mimic_object_to_json, -1);
886
+
887
+ rb_gv_set("$VERBOSE", verbose);
888
+
889
+ oj_default_options = mimic_object_to_json_options;
890
+ oj_default_options.to_json = Yes;
891
+
892
+ return json;
893
+ }