oj 3.8.0

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