oj 3.7.12

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 +96 -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 +1188 -0
  13. data/ext/oj/dump.c +1232 -0
  14. data/ext/oj/dump.h +94 -0
  15. data/ext/oj/dump_compat.c +973 -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 +873 -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 +1694 -0
  32. data/ext/oj/oj.h +381 -0
  33. data/ext/oj/parse.c +1085 -0
  34. data/ext/oj/parse.h +111 -0
  35. data/ext/oj/rails.c +1485 -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 +512 -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 +154 -0
  73. data/pages/Options.md +266 -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/big.rb +15 -0
  90. data/test/files.rb +29 -0
  91. data/test/foo.rb +33 -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/mem.rb +35 -0
  113. data/test/perf.rb +107 -0
  114. data/test/perf_compat.rb +130 -0
  115. data/test/perf_fast.rb +164 -0
  116. data/test/perf_file.rb +64 -0
  117. data/test/perf_object.rb +138 -0
  118. data/test/perf_saj.rb +109 -0
  119. data/test/perf_scp.rb +151 -0
  120. data/test/perf_simple.rb +287 -0
  121. data/test/perf_strict.rb +145 -0
  122. data/test/perf_wab.rb +131 -0
  123. data/test/sample.rb +54 -0
  124. data/test/sample/change.rb +14 -0
  125. data/test/sample/dir.rb +19 -0
  126. data/test/sample/doc.rb +36 -0
  127. data/test/sample/file.rb +48 -0
  128. data/test/sample/group.rb +16 -0
  129. data/test/sample/hasprops.rb +16 -0
  130. data/test/sample/layer.rb +12 -0
  131. data/test/sample/line.rb +20 -0
  132. data/test/sample/oval.rb +10 -0
  133. data/test/sample/rect.rb +10 -0
  134. data/test/sample/shape.rb +35 -0
  135. data/test/sample/text.rb +20 -0
  136. data/test/sample_json.rb +37 -0
  137. data/test/test_compat.rb +509 -0
  138. data/test/test_custom.rb +406 -0
  139. data/test/test_debian.rb +53 -0
  140. data/test/test_fast.rb +470 -0
  141. data/test/test_file.rb +239 -0
  142. data/test/test_gc.rb +49 -0
  143. data/test/test_hash.rb +29 -0
  144. data/test/test_integer_range.rb +73 -0
  145. data/test/test_null.rb +376 -0
  146. data/test/test_object.rb +1018 -0
  147. data/test/test_saj.rb +186 -0
  148. data/test/test_scp.rb +433 -0
  149. data/test/test_strict.rb +410 -0
  150. data/test/test_various.rb +739 -0
  151. data/test/test_wab.rb +307 -0
  152. data/test/test_writer.rb +380 -0
  153. data/test/tests.rb +24 -0
  154. data/test/tests_mimic.rb +14 -0
  155. data/test/tests_mimic_addition.rb +7 -0
  156. metadata +359 -0
@@ -0,0 +1,873 @@
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 providedit 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 providedit 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
+ out.buf = buf;
361
+ out.end = buf + sizeof(buf) - 10;
362
+ out.allocated = false;
363
+ out.omit_nil = copts->dump_opts.omit_nil;
364
+ out.caller = CALLER_GENERATE;
365
+ // For obj.to_json or generate nan is not allowed but if called from dump
366
+ // it is.
367
+ copts->dump_opts.nan_dump = RaiseNan;
368
+ copts->mode = CompatMode;
369
+ copts->to_json = Yes;
370
+ if (2 == argc && Qnil != argv[1]) {
371
+ oj_parse_mimic_dump_options(argv[1], copts);
372
+ }
373
+ /* seems like this is not correct
374
+ if (No == copts->nilnil && Qnil == *argv) {
375
+ rb_raise(rb_eTypeError, "nil not allowed.");
376
+ }
377
+ */
378
+ oj_dump_obj_to_json_using_params(*argv, copts, &out, argc - 1, argv + 1);
379
+
380
+ if (0 == out.buf) {
381
+ rb_raise(rb_eNoMemError, "Not enough memory.");
382
+ }
383
+ rstr = rb_str_new2(out.buf);
384
+ rstr = oj_encode(rstr);
385
+ if (out.allocated) {
386
+ xfree(out.buf);
387
+ }
388
+ return rstr;
389
+ }
390
+
391
+ /* Document-method: fast_generate
392
+ * call-seq: fast_generate(obj, opts=nil)
393
+ * Same as generate().
394
+ * @see generate
395
+ */
396
+
397
+ /* Document-method: generate
398
+ * call-seq: generate(obj, opts=nil)
399
+ *
400
+ * Encode obj as a JSON String. The obj argument must be a Hash, Array, or
401
+ * respond to to_h or to_json. Options other than those listed such as
402
+ * +:allow_nan+ or +:max_nesting+ are ignored.
403
+ *
404
+ * - *obj* [_Object_|Hash|Array] object to convert to a JSON String
405
+ * - *opts* [_Hash_] options
406
+ * - - *:indent* [_String_] String to use for indentation.
407
+ * - *:space* [_String_] String placed after a , or : delimiter
408
+ * - *:space_before* [_String_] String placed before a : delimiter
409
+ * - *:object_nl* [_String_] String placed after a JSON object
410
+ * - *:array_nl* [_String_] String placed after a JSON array
411
+ * - *: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.
412
+ *
413
+ * Returns [_String_] generated JSON.
414
+ */
415
+ VALUE
416
+ oj_mimic_generate(int argc, VALUE *argv, VALUE self) {
417
+ struct _options copts = oj_default_options;
418
+
419
+ copts.str_rx.head = NULL;
420
+ copts.str_rx.tail = NULL;
421
+
422
+ return mimic_generate_core(argc, argv, &copts);
423
+ }
424
+
425
+ /* Document-method: pretty_generate
426
+ * call-seq: pretty_generate(obj, opts=nil)
427
+ *
428
+ * Same as generate() but with different defaults for the spacing options.
429
+ * @see generate
430
+ *
431
+ * Return [_String_] the generated JSON.
432
+ */
433
+ VALUE
434
+ oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
435
+ struct _options copts = oj_default_options;
436
+ VALUE rargs[2];
437
+ volatile VALUE h;
438
+
439
+ // Some (all?) json gem to_json methods need a State instance and not just
440
+ // a Hash. I haven't dug deep enough to find out why but using a State
441
+ // instance and not a Hash gives the desired behavior.
442
+ *rargs = *argv;
443
+ if (1 == argc) {
444
+ h = rb_hash_new();
445
+ } else {
446
+ h = argv[1];
447
+ }
448
+ if (Qfalse == rb_funcall(h, oj_has_key_id, 1, oj_indent_sym)) {
449
+ rb_hash_aset(h, oj_indent_sym, rb_str_new2(" "));
450
+ }
451
+ if (Qfalse == rb_funcall(h, oj_has_key_id, 1, oj_space_before_sym)) {
452
+ rb_hash_aset(h, oj_space_before_sym, rb_str_new2(""));
453
+ }
454
+ if (Qfalse == rb_funcall(h, oj_has_key_id, 1, oj_space_sym)) {
455
+ rb_hash_aset(h, oj_space_sym, rb_str_new2(" "));
456
+ }
457
+ if (Qfalse == rb_funcall(h, oj_has_key_id, 1, oj_object_nl_sym)) {
458
+ rb_hash_aset(h, oj_object_nl_sym, rb_str_new2("\n"));
459
+ }
460
+ if (Qfalse == rb_funcall(h, oj_has_key_id, 1, oj_array_nl_sym)) {
461
+ rb_hash_aset(h, oj_array_nl_sym, rb_str_new2("\n"));
462
+ }
463
+ rargs[1] = rb_funcall(state_class, oj_new_id, 1, h);
464
+
465
+ copts.str_rx.head = NULL;
466
+ copts.str_rx.tail = NULL;
467
+ strcpy(copts.dump_opts.indent_str, " ");
468
+ copts.dump_opts.indent_size = (uint8_t)strlen(copts.dump_opts.indent_str);
469
+ strcpy(copts.dump_opts.before_sep, "");
470
+ copts.dump_opts.before_size = (uint8_t)strlen(copts.dump_opts.before_sep);
471
+ strcpy(copts.dump_opts.after_sep, " ");
472
+ copts.dump_opts.after_size = (uint8_t)strlen(copts.dump_opts.after_sep);
473
+ strcpy(copts.dump_opts.hash_nl, "\n");
474
+ copts.dump_opts.hash_size = (uint8_t)strlen(copts.dump_opts.hash_nl);
475
+ strcpy(copts.dump_opts.array_nl, "\n");
476
+ copts.dump_opts.array_size = (uint8_t)strlen(copts.dump_opts.array_nl);
477
+ copts.dump_opts.use = true;
478
+
479
+ return mimic_generate_core(2, rargs, &copts);
480
+ }
481
+
482
+ static VALUE
483
+ mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
484
+ struct _parseInfo pi;
485
+ VALUE ropts;
486
+ VALUE args[1];
487
+
488
+ rb_scan_args(argc, argv, "11", NULL, &ropts);
489
+ parse_info_init(&pi);
490
+ oj_set_compat_callbacks(&pi);
491
+
492
+ pi.err_class = oj_json_parser_error_class;
493
+ //pi.err_class = Qnil;
494
+
495
+ pi.options = oj_default_options;
496
+ pi.options.auto_define = No;
497
+ pi.options.quirks_mode = Yes;
498
+ pi.options.allow_invalid = No;
499
+ pi.options.empty_string = No;
500
+ pi.options.create_ok = No;
501
+ pi.options.allow_nan = (bang ? Yes : No);
502
+ pi.options.nilnil = No;
503
+ pi.options.bigdec_load = FloatDec;
504
+ pi.options.mode = CompatMode;
505
+ pi.max_depth = 100;
506
+
507
+ if (Qnil != ropts) {
508
+ VALUE v;
509
+
510
+ if (T_HASH != rb_type(ropts)) {
511
+ rb_raise(rb_eArgError, "options must be a hash.");
512
+ }
513
+ if (Qundef == symbolize_names_sym) {
514
+ symbolize_names_sym = ID2SYM(rb_intern("symbolize_names")); rb_gc_register_address(&symbolize_names_sym);
515
+ }
516
+ if (Qnil != (v = rb_hash_lookup(ropts, symbolize_names_sym))) {
517
+ pi.options.sym_key = (Qtrue == v) ? Yes : No;
518
+ }
519
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_quirks_mode_sym))) {
520
+ pi.options.quirks_mode = (Qtrue == v) ? Yes : No;
521
+ }
522
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_create_additions_sym))) {
523
+ pi.options.create_ok = (Qtrue == v) ? Yes : No;
524
+ }
525
+ if (Qnil != (v = rb_hash_lookup(ropts, oj_allow_nan_sym))) {
526
+ pi.options.allow_nan = (Qtrue == v) ? Yes : No;
527
+ }
528
+
529
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_hash_class_sym)) {
530
+ if (Qnil == (v = rb_hash_lookup(ropts, oj_hash_class_sym))) {
531
+ pi.options.hash_class = Qnil;
532
+ } else {
533
+ rb_check_type(v, T_CLASS);
534
+ pi.options.hash_class = v;
535
+ }
536
+ }
537
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_object_class_sym)) {
538
+ if (Qnil == (v = rb_hash_lookup(ropts, oj_object_class_sym))) {
539
+ pi.options.hash_class = Qnil;
540
+ } else {
541
+ rb_check_type(v, T_CLASS);
542
+ pi.options.hash_class = v;
543
+ }
544
+ }
545
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_array_class_sym)) {
546
+ if (Qnil == (v = rb_hash_lookup(ropts, oj_array_class_sym))) {
547
+ pi.options.array_class = Qnil;
548
+ } else {
549
+ rb_check_type(v, T_CLASS);
550
+ pi.options.array_class = v;
551
+ }
552
+ }
553
+ v = rb_hash_lookup(ropts, oj_max_nesting_sym);
554
+ if (Qtrue == v) {
555
+ pi.max_depth = 100;
556
+ } else if (Qfalse == v || Qnil == v) {
557
+ pi.max_depth = 0;
558
+ } else if (T_FIXNUM == rb_type(v)) {
559
+ pi.max_depth = NUM2INT(v);
560
+ }
561
+ oj_parse_opt_match_string(&pi.options.str_rx, ropts);
562
+ if (Yes == pi.options.create_ok && Yes == pi.options.sym_key) {
563
+ rb_raise(rb_eArgError, ":symbolize_names and :create_additions can not both be true.");
564
+ }
565
+ }
566
+ *args = *argv;
567
+
568
+ if (T_STRING == rb_type(*args)) {
569
+ return oj_pi_parse(1, args, &pi, 0, 0, false);
570
+ } else {
571
+ return oj_pi_sparse(1, args, &pi, 0);
572
+ }
573
+ }
574
+
575
+ /* Document-method: parse
576
+ * call-seq: parse(source, opts=nil)
577
+ *
578
+ * Parses a JSON String or IO into a Ruby Object. Options other than those
579
+ * listed such as +:allow_nan+ or +:max_nesting+ are ignored. +:object_class+ and
580
+ * +:array_object+ are not supported.
581
+ *
582
+ * - *source* [_String_|IO] source to parse
583
+ * - *opts* [_Hash_] options
584
+ * - *:symbolize* [Boolean] _names flag indicating JSON object keys should be Symbols instead of Strings
585
+ * - *:create_additions* [Boolean] flag indicating a key matching +create_id+ in a JSON object should trigger the creation of Ruby Object
586
+ *
587
+ * Returns [Object]
588
+ * @see create_id=
589
+ */
590
+ VALUE
591
+ oj_mimic_parse(int argc, VALUE *argv, VALUE self) {
592
+ return mimic_parse_core(argc, argv, self, false);
593
+ }
594
+
595
+ /* Document-method: parse!
596
+ * call-seq: parse!(source, opts=nil)
597
+ *
598
+ * Same as parse().
599
+ * @see parse
600
+ */
601
+ static VALUE
602
+ mimic_parse_bang(int argc, VALUE *argv, VALUE self) {
603
+ return mimic_parse_core(argc, argv, self, true);
604
+ }
605
+
606
+ /* Document-method: recurse_proc
607
+ * call-seq: recurse_proc(obj, &proc)
608
+ *
609
+ * Yields to the proc for every element in the obj recursively.
610
+ *
611
+ * - *obj* [_Hash_|Array] object to walk
612
+ * - *proc* [_Proc_] to yield to on each element
613
+ */
614
+ static VALUE
615
+ mimic_recurse_proc(VALUE self, VALUE obj) {
616
+ rb_need_block();
617
+ mimic_walk(Qnil, obj, Qnil);
618
+
619
+ return Qnil;
620
+ }
621
+
622
+ /* Document-method: create_id=
623
+ * call-seq: create_id=(id)
624
+ *
625
+ * Sets the create_id tag to look for in JSON document. That key triggers the
626
+ * creation of a class with the same name.
627
+ *
628
+ * - *id* [_nil_|String] new create_id
629
+ *
630
+ * Returns [_String_] the id.
631
+ */
632
+ static VALUE
633
+ mimic_set_create_id(VALUE self, VALUE id) {
634
+ Check_Type(id, T_STRING);
635
+
636
+ if (NULL != oj_default_options.create_id) {
637
+ if (oj_json_class != oj_default_options.create_id) {
638
+ xfree((char*)oj_default_options.create_id);
639
+ }
640
+ oj_default_options.create_id = NULL;
641
+ oj_default_options.create_id_len = 0;
642
+ }
643
+ if (Qnil != id) {
644
+ size_t len = RSTRING_LEN(id) + 1;
645
+
646
+ oj_default_options.create_id = ALLOC_N(char, len);
647
+ strcpy((char*)oj_default_options.create_id, StringValuePtr(id));
648
+ oj_default_options.create_id_len = len - 1;
649
+ }
650
+ return id;
651
+ }
652
+
653
+ /* Document-method: create_id
654
+ * call-seq: create_id()
655
+ *
656
+ * Returns [_String_] the create_id.
657
+ */
658
+ static VALUE
659
+ mimic_create_id(VALUE self) {
660
+ if (NULL != oj_default_options.create_id) {
661
+ return oj_encode(rb_str_new_cstr(oj_default_options.create_id));
662
+ }
663
+ return rb_str_new_cstr(oj_json_class);
664
+ }
665
+
666
+ static struct _options mimic_object_to_json_options = {
667
+ 0, // indent
668
+ No, // circular
669
+ No, // auto_define
670
+ No, // sym_key
671
+ JXEsc, // escape_mode
672
+ CompatMode, // mode
673
+ No, // class_cache
674
+ RubyTime, // time_format
675
+ No, // bigdec_as_num
676
+ FloatDec, // bigdec_load
677
+ No, // to_hash
678
+ No, // to_json
679
+ No, // as_json
680
+ No, // nilnil
681
+ No, // empty_string
682
+ Yes, // allow_gc
683
+ Yes, // quirks_mode
684
+ No, // allow_invalid
685
+ No, // create_ok
686
+ No, // allow_nan
687
+ No, // trace
688
+ 0, // integer_range_min
689
+ 0, // integer_range_max
690
+ oj_json_class,// create_id
691
+ 10, // create_id_len
692
+ 3, // sec_prec
693
+ 16, // float_prec
694
+ "%0.16g", // float_fmt
695
+ Qnil, // hash_class
696
+ Qnil, // array_class
697
+ { // dump_opts
698
+ false, //use
699
+ "", // indent
700
+ "", // before_sep
701
+ "", // after_sep
702
+ "", // hash_nl
703
+ "", // array_nl
704
+ 0, // indent_size
705
+ 0, // before_size
706
+ 0, // after_size
707
+ 0, // hash_size
708
+ 0, // array_size
709
+ RaiseNan,// nan_dump
710
+ false, // omit_nil
711
+ 100, // max_depth
712
+ },
713
+ { // str_rx
714
+ NULL, // head
715
+ NULL, // tail
716
+ { '\0' }, // err
717
+ }
718
+ };
719
+
720
+ static VALUE
721
+ mimic_object_to_json(int argc, VALUE *argv, VALUE self) {
722
+ char buf[4096];
723
+ struct _out out;
724
+ VALUE rstr;
725
+ struct _options copts = oj_default_options;
726
+
727
+ copts.str_rx.head = NULL;
728
+ copts.str_rx.tail = NULL;
729
+ out.buf = buf;
730
+ out.end = buf + sizeof(buf) - 10;
731
+ out.allocated = false;
732
+ out.omit_nil = copts.dump_opts.omit_nil;
733
+ copts.mode = CompatMode;
734
+ copts.to_json = No;
735
+ if (1 <= argc && Qnil != argv[0]) {
736
+ oj_parse_mimic_dump_options(argv[0], &copts);
737
+ }
738
+ // To be strict the mimic_object_to_json_options should be used but people
739
+ // seem to prefer the option of changing that.
740
+ //oj_dump_obj_to_json(self, &mimic_object_to_json_options, &out);
741
+ oj_dump_obj_to_json_using_params(self, &copts, &out, argc, argv);
742
+ if (0 == out.buf) {
743
+ rb_raise(rb_eNoMemError, "Not enough memory.");
744
+ }
745
+ rstr = rb_str_new2(out.buf);
746
+ rstr = oj_encode(rstr);
747
+ if (out.allocated) {
748
+ xfree(out.buf);
749
+ }
750
+ return rstr;
751
+ }
752
+
753
+ /* Document-method: state
754
+ * call-seq: state()
755
+ *
756
+ * Returns [_JSON::State_] the JSON::State class.
757
+ */
758
+ static VALUE
759
+ mimic_state(VALUE self) {
760
+ return state_class;
761
+ }
762
+
763
+ void
764
+ oj_mimic_json_methods(VALUE json) {
765
+ VALUE json_error;
766
+ VALUE generator;
767
+ VALUE ext;
768
+
769
+ rb_define_module_function(json, "create_id=", mimic_set_create_id, 1);
770
+ rb_define_module_function(json, "create_id", mimic_create_id, 0);
771
+
772
+ rb_define_module_function(json, "dump", mimic_dump, -1);
773
+ rb_define_module_function(json, "load", mimic_load, -1);
774
+ rb_define_module_function(json, "restore", mimic_load, -1);
775
+ rb_define_module_function(json, "recurse_proc", mimic_recurse_proc, 1);
776
+ rb_define_module_function(json, "[]", mimic_dump_load, -1);
777
+
778
+ rb_define_module_function(json, "generate", oj_mimic_generate, -1);
779
+ rb_define_module_function(json, "fast_generate", oj_mimic_generate, -1);
780
+ rb_define_module_function(json, "pretty_generate", oj_mimic_pretty_generate, -1);
781
+ // For older versions of JSON, the deprecated unparse methods.
782
+ rb_define_module_function(json, "unparse", oj_mimic_generate, -1);
783
+ rb_define_module_function(json, "fast_unparse", oj_mimic_generate, -1);
784
+ rb_define_module_function(json, "pretty_unparse", oj_mimic_pretty_generate, -1);
785
+
786
+ rb_define_module_function(json, "parse", oj_mimic_parse, -1);
787
+ rb_define_module_function(json, "parse!", mimic_parse_bang, -1);
788
+
789
+ rb_define_module_function(json, "state", mimic_state, 0);
790
+
791
+ if (rb_const_defined_at(json, rb_intern("JSONError"))) {
792
+ json_error = rb_const_get(json, rb_intern("JSONError"));
793
+ } else {
794
+ json_error = rb_define_class_under(json, "JSONError", rb_eStandardError);
795
+ }
796
+ if (rb_const_defined_at(json, rb_intern("ParserError"))) {
797
+ oj_json_parser_error_class = rb_const_get(json, rb_intern("ParserError"));
798
+ } else {
799
+ oj_json_parser_error_class = rb_define_class_under(json, "ParserError", json_error);
800
+ }
801
+ if (rb_const_defined_at(json, rb_intern("GeneratorError"))) {
802
+ oj_json_generator_error_class = rb_const_get(json, rb_intern("GeneratorError"));
803
+ } else {
804
+ oj_json_generator_error_class = rb_define_class_under(json, "GeneratorError", json_error);
805
+ }
806
+ if (rb_const_defined_at(json, rb_intern("NestingError"))) {
807
+ rb_const_get(json, rb_intern("NestingError"));
808
+ } else {
809
+ rb_define_class_under(json, "NestingError", json_error);
810
+ }
811
+
812
+ if (rb_const_defined_at(json, rb_intern("Ext"))) {
813
+ ext = rb_const_get_at(json, rb_intern("Ext"));
814
+ } else {
815
+ ext = rb_define_module_under(json, "Ext");
816
+ }
817
+ if (rb_const_defined_at(ext, rb_intern("Generator"))) {
818
+ generator = rb_const_get_at(ext, rb_intern("Generator"));
819
+ } else {
820
+ generator = rb_define_module_under(ext, "Generator");
821
+ }
822
+ if (!rb_const_defined_at(generator, rb_intern("State"))) {
823
+ rb_require("oj/state");
824
+ }
825
+ // Pull in the JSON::State mimic file.
826
+ state_class = rb_const_get_at(generator, rb_intern("State"));
827
+
828
+ symbolize_names_sym = ID2SYM(rb_intern("symbolize_names")); rb_gc_register_address(&symbolize_names_sym);
829
+ }
830
+
831
+ /* Document-module: JSON
832
+ *
833
+ * A mimic of the json gem module.
834
+ */
835
+ VALUE
836
+ oj_define_mimic_json(int argc, VALUE *argv, VALUE self) {
837
+ VALUE dummy;
838
+ VALUE verbose;
839
+ VALUE json;
840
+
841
+ // Either set the paths to indicate JSON has been loaded or replaces the
842
+ // methods if it has been loaded.
843
+ if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
844
+ json = rb_const_get_at(rb_cObject, rb_intern("JSON"));
845
+ } else {
846
+ json = rb_define_module("JSON");
847
+ }
848
+ verbose = rb_gv_get("$VERBOSE");
849
+ rb_gv_set("$VERBOSE", Qfalse);
850
+ rb_define_module_function(rb_cObject, "JSON", mimic_dump_load, -1);
851
+ dummy = rb_gv_get("$LOADED_FEATURES");
852
+ if (rb_type(dummy) == T_ARRAY) {
853
+ rb_ary_push(dummy, rb_str_new2("json"));
854
+ if (0 < argc) {
855
+ VALUE mimic_args[1];
856
+
857
+ *mimic_args = *argv;
858
+ rb_funcall2(Oj, rb_intern("mimic_loaded"), 1, mimic_args);
859
+ } else {
860
+ rb_funcall2(Oj, rb_intern("mimic_loaded"), 0, 0);
861
+ }
862
+ }
863
+ oj_mimic_json_methods(json);
864
+
865
+ rb_define_method(rb_cObject, "to_json", mimic_object_to_json, -1);
866
+
867
+ rb_gv_set("$VERBOSE", verbose);
868
+
869
+ oj_default_options = mimic_object_to_json_options;
870
+ oj_default_options.to_json = Yes;
871
+
872
+ return json;
873
+ }