oj 3.10.7

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