oj 2.0.0 → 3.0.0

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