oj 2.18.3 → 3.13.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1324 -0
  3. data/README.md +51 -204
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +49 -72
  6. data/ext/oj/cache.c +326 -0
  7. data/ext/oj/cache.h +21 -0
  8. data/ext/oj/cache8.c +61 -64
  9. data/ext/oj/cache8.h +12 -39
  10. data/ext/oj/circarray.c +37 -68
  11. data/ext/oj/circarray.h +16 -42
  12. data/ext/oj/code.c +221 -0
  13. data/ext/oj/code.h +40 -0
  14. data/ext/oj/compat.c +231 -107
  15. data/ext/oj/custom.c +1125 -0
  16. data/ext/oj/debug.c +132 -0
  17. data/ext/oj/dump.c +935 -2513
  18. data/ext/oj/dump.h +108 -0
  19. data/ext/oj/dump_compat.c +936 -0
  20. data/ext/oj/dump_leaf.c +164 -0
  21. data/ext/oj/dump_object.c +761 -0
  22. data/ext/oj/dump_strict.c +410 -0
  23. data/ext/oj/encode.h +7 -42
  24. data/ext/oj/encoder.c +43 -0
  25. data/ext/oj/err.c +40 -54
  26. data/ext/oj/err.h +52 -46
  27. data/ext/oj/extconf.rb +21 -30
  28. data/ext/oj/fast.c +1097 -1080
  29. data/ext/oj/intern.c +301 -0
  30. data/ext/oj/intern.h +26 -0
  31. data/ext/oj/mimic_json.c +893 -0
  32. data/ext/oj/object.c +549 -620
  33. data/ext/oj/odd.c +155 -167
  34. data/ext/oj/odd.h +37 -63
  35. data/ext/oj/oj.c +1661 -2063
  36. data/ext/oj/oj.h +341 -270
  37. data/ext/oj/parse.c +974 -737
  38. data/ext/oj/parse.h +105 -97
  39. data/ext/oj/parser.c +1526 -0
  40. data/ext/oj/parser.h +90 -0
  41. data/ext/oj/rails.c +1504 -0
  42. data/ext/oj/rails.h +18 -0
  43. data/ext/oj/reader.c +141 -163
  44. data/ext/oj/reader.h +75 -113
  45. data/ext/oj/resolve.c +45 -93
  46. data/ext/oj/resolve.h +7 -34
  47. data/ext/oj/rxclass.c +143 -0
  48. data/ext/oj/rxclass.h +26 -0
  49. data/ext/oj/saj.c +447 -511
  50. data/ext/oj/saj2.c +348 -0
  51. data/ext/oj/scp.c +91 -138
  52. data/ext/oj/sparse.c +793 -644
  53. data/ext/oj/stream_writer.c +331 -0
  54. data/ext/oj/strict.c +145 -109
  55. data/ext/oj/string_writer.c +493 -0
  56. data/ext/oj/trace.c +72 -0
  57. data/ext/oj/trace.h +28 -0
  58. data/ext/oj/usual.c +1254 -0
  59. data/ext/oj/util.c +136 -0
  60. data/ext/oj/util.h +20 -0
  61. data/ext/oj/val_stack.c +62 -70
  62. data/ext/oj/val_stack.h +95 -129
  63. data/ext/oj/validate.c +51 -0
  64. data/ext/oj/wab.c +622 -0
  65. data/lib/oj/bag.rb +1 -0
  66. data/lib/oj/easy_hash.rb +17 -8
  67. data/lib/oj/error.rb +10 -11
  68. data/lib/oj/json.rb +176 -0
  69. data/lib/oj/mimic.rb +158 -19
  70. data/lib/oj/state.rb +132 -0
  71. data/lib/oj/version.rb +2 -2
  72. data/lib/oj.rb +1 -31
  73. data/pages/Advanced.md +22 -0
  74. data/pages/Compatibility.md +25 -0
  75. data/pages/Custom.md +23 -0
  76. data/pages/Encoding.md +65 -0
  77. data/pages/JsonGem.md +94 -0
  78. data/pages/Modes.md +161 -0
  79. data/pages/Options.md +327 -0
  80. data/pages/Parser.md +309 -0
  81. data/pages/Rails.md +167 -0
  82. data/pages/Security.md +20 -0
  83. data/pages/WAB.md +13 -0
  84. data/test/activerecord/result_test.rb +32 -0
  85. data/test/activesupport4/decoding_test.rb +108 -0
  86. data/test/activesupport4/encoding_test.rb +531 -0
  87. data/test/activesupport4/test_helper.rb +41 -0
  88. data/test/activesupport5/abstract_unit.rb +45 -0
  89. data/test/activesupport5/decoding_test.rb +133 -0
  90. data/test/activesupport5/encoding_test.rb +500 -0
  91. data/test/activesupport5/encoding_test_cases.rb +98 -0
  92. data/test/activesupport5/test_helper.rb +72 -0
  93. data/test/activesupport5/time_zone_test_helpers.rb +39 -0
  94. data/test/activesupport6/abstract_unit.rb +44 -0
  95. data/test/activesupport6/decoding_test.rb +133 -0
  96. data/test/activesupport6/encoding_test.rb +507 -0
  97. data/test/activesupport6/encoding_test_cases.rb +98 -0
  98. data/test/activesupport6/test_common.rb +17 -0
  99. data/test/activesupport6/test_helper.rb +163 -0
  100. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  101. data/test/activesupport7/abstract_unit.rb +49 -0
  102. data/test/activesupport7/decoding_test.rb +125 -0
  103. data/test/activesupport7/encoding_test.rb +486 -0
  104. data/test/activesupport7/encoding_test_cases.rb +104 -0
  105. data/test/activesupport7/time_zone_test_helpers.rb +47 -0
  106. data/test/bar.rb +9 -0
  107. data/test/baz.rb +16 -0
  108. data/test/bug.rb +11 -46
  109. data/test/foo.rb +69 -16
  110. data/test/helper.rb +10 -1
  111. data/test/isolated/shared.rb +12 -8
  112. data/test/isolated/test_mimic_rails_after.rb +3 -3
  113. data/test/isolated/test_mimic_rails_before.rb +3 -3
  114. data/test/json_gem/json_addition_test.rb +216 -0
  115. data/test/json_gem/json_common_interface_test.rb +153 -0
  116. data/test/json_gem/json_encoding_test.rb +107 -0
  117. data/test/json_gem/json_ext_parser_test.rb +20 -0
  118. data/test/json_gem/json_fixtures_test.rb +35 -0
  119. data/test/json_gem/json_generator_test.rb +397 -0
  120. data/test/json_gem/json_generic_object_test.rb +90 -0
  121. data/test/json_gem/json_parser_test.rb +470 -0
  122. data/test/json_gem/json_string_matching_test.rb +42 -0
  123. data/test/json_gem/test_helper.rb +26 -0
  124. data/test/mem.rb +33 -0
  125. data/test/perf.rb +1 -1
  126. data/test/perf_compat.rb +30 -28
  127. data/test/perf_dump.rb +50 -0
  128. data/test/perf_object.rb +1 -1
  129. data/test/perf_once.rb +58 -0
  130. data/test/perf_parser.rb +189 -0
  131. data/test/perf_scp.rb +11 -10
  132. data/test/perf_strict.rb +30 -19
  133. data/test/perf_wab.rb +131 -0
  134. data/test/prec.rb +23 -0
  135. data/test/sample.rb +0 -1
  136. data/test/sample_json.rb +1 -1
  137. data/test/test_compat.rb +219 -102
  138. data/test/test_custom.rb +533 -0
  139. data/test/test_fast.rb +107 -35
  140. data/test/test_file.rb +19 -25
  141. data/test/test_generate.rb +21 -0
  142. data/test/test_hash.rb +11 -1
  143. data/test/test_integer_range.rb +72 -0
  144. data/test/test_null.rb +376 -0
  145. data/test/test_object.rb +357 -70
  146. data/test/test_parser.rb +27 -0
  147. data/test/test_parser_saj.rb +245 -0
  148. data/test/test_parser_usual.rb +217 -0
  149. data/test/test_rails.rb +35 -0
  150. data/test/test_saj.rb +1 -1
  151. data/test/test_scp.rb +39 -2
  152. data/test/test_strict.rb +186 -7
  153. data/test/test_various.rb +160 -774
  154. data/test/test_wab.rb +307 -0
  155. data/test/test_writer.rb +90 -2
  156. data/test/tests.rb +24 -0
  157. data/test/tests_mimic.rb +14 -0
  158. data/test/tests_mimic_addition.rb +7 -0
  159. data/test/zoo.rb +13 -0
  160. metadata +194 -56
  161. data/ext/oj/hash.c +0 -163
  162. data/ext/oj/hash.h +0 -46
  163. data/ext/oj/hash_test.c +0 -512
  164. data/test/activesupport_datetime_test.rb +0 -23
  165. data/test/bug2.rb +0 -10
  166. data/test/bug3.rb +0 -46
  167. data/test/bug_fast.rb +0 -32
  168. data/test/bug_load.rb +0 -24
  169. data/test/crash.rb +0 -111
  170. data/test/curl/curl_oj.rb +0 -46
  171. data/test/curl/get_oj.rb +0 -24
  172. data/test/curl/just_curl.rb +0 -31
  173. data/test/curl/just_oj.rb +0 -51
  174. data/test/example.rb +0 -11
  175. data/test/io.rb +0 -48
  176. data/test/isolated/test_mimic_rails_datetime.rb +0 -27
  177. data/test/mod.rb +0 -16
  178. data/test/rails.rb +0 -50
  179. data/test/russian.rb +0 -18
  180. data/test/struct.rb +0 -29
  181. data/test/test_serializer.rb +0 -59
  182. data/test/write_timebars.rb +0 -31
@@ -0,0 +1,493 @@
1
+ // Copyright (c) 2012, 2017 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for license details.
3
+
4
+ #include "dump.h"
5
+ #include "encode.h"
6
+
7
+ extern VALUE Oj;
8
+
9
+ bool string_writer_optimized = false;
10
+
11
+ static void key_check(StrWriter sw, const char *key) {
12
+ DumpType type = sw->types[sw->depth];
13
+
14
+ if (0 == key && (ObjectNew == type || ObjectType == type)) {
15
+ rb_raise(rb_eStandardError, "Can not push onto an Object without a key.");
16
+ }
17
+ }
18
+
19
+ static void push_type(StrWriter sw, DumpType type) {
20
+ if (sw->types_end <= sw->types + sw->depth + 1) {
21
+ size_t size = (sw->types_end - sw->types) * 2;
22
+
23
+ REALLOC_N(sw->types, char, size);
24
+ sw->types_end = sw->types + size;
25
+ }
26
+ sw->depth++;
27
+ sw->types[sw->depth] = type;
28
+ }
29
+
30
+ static void maybe_comma(StrWriter sw) {
31
+ switch (sw->types[sw->depth]) {
32
+ case ObjectNew: sw->types[sw->depth] = ObjectType; break;
33
+ case ArrayNew: sw->types[sw->depth] = ArrayType; break;
34
+ case ObjectType:
35
+ case ArrayType:
36
+ // Always have a few characters available in the out.buf.
37
+ *sw->out.cur++ = ',';
38
+ break;
39
+ }
40
+ }
41
+
42
+ // Used by stream writer also.
43
+ void oj_str_writer_init(StrWriter sw, int buf_size) {
44
+ sw->opts = oj_default_options;
45
+ sw->depth = 0;
46
+ sw->types = ALLOC_N(char, 256);
47
+ sw->types_end = sw->types + 256;
48
+ *sw->types = '\0';
49
+ sw->keyWritten = 0;
50
+
51
+ if (0 == buf_size) {
52
+ buf_size = 4096;
53
+ } else if (buf_size < 1024) {
54
+ buf_size = 1024;
55
+ }
56
+ // Must be allocated. Using the out.stack_buffer results in double frees
57
+ // and I haven't figured out why yet.
58
+ sw->out.buf = ALLOC_N(char, buf_size);
59
+ sw->out.cur = sw->out.buf;
60
+ sw->out.end = sw->out.buf + buf_size - BUFFER_EXTRA;
61
+ sw->out.allocated = true;
62
+
63
+ *sw->out.cur = '\0';
64
+ sw->out.circ_cache = NULL;
65
+ sw->out.circ_cnt = 0;
66
+ sw->out.hash_cnt = 0;
67
+ sw->out.opts = &sw->opts;
68
+ sw->out.indent = sw->opts.indent;
69
+ sw->out.depth = 0;
70
+ sw->out.argc = 0;
71
+ sw->out.argv = NULL;
72
+ sw->out.caller = 0;
73
+ sw->out.ropts = NULL;
74
+ sw->out.omit_nil = oj_default_options.dump_opts.omit_nil;
75
+ }
76
+
77
+ void oj_str_writer_push_key(StrWriter sw, const char *key) {
78
+ DumpType type = sw->types[sw->depth];
79
+ long size;
80
+
81
+ if (sw->keyWritten) {
82
+ rb_raise(rb_eStandardError, "Can not push more than one key before pushing a non-key.");
83
+ }
84
+ if (ObjectNew != type && ObjectType != type) {
85
+ rb_raise(rb_eStandardError, "Can only push a key onto an Object.");
86
+ }
87
+ size = sw->depth * sw->out.indent + 3;
88
+ assure_size(&sw->out, size);
89
+ maybe_comma(sw);
90
+ if (0 < sw->depth) {
91
+ fill_indent(&sw->out, sw->depth);
92
+ }
93
+ oj_dump_cstr(key, strlen(key), 0, 0, &sw->out);
94
+ *sw->out.cur++ = ':';
95
+ sw->keyWritten = 1;
96
+ }
97
+
98
+ void oj_str_writer_push_object(StrWriter sw, const char *key) {
99
+ if (sw->keyWritten) {
100
+ sw->keyWritten = 0;
101
+ assure_size(&sw->out, 1);
102
+ } else {
103
+ long size;
104
+
105
+ key_check(sw, key);
106
+ size = sw->depth * sw->out.indent + 3;
107
+ assure_size(&sw->out, size);
108
+ maybe_comma(sw);
109
+ if (0 < sw->depth) {
110
+ fill_indent(&sw->out, sw->depth);
111
+ }
112
+ if (0 != key) {
113
+ oj_dump_cstr(key, strlen(key), 0, 0, &sw->out);
114
+ *sw->out.cur++ = ':';
115
+ }
116
+ }
117
+ *sw->out.cur++ = '{';
118
+ push_type(sw, ObjectNew);
119
+ }
120
+
121
+ void oj_str_writer_push_array(StrWriter sw, const char *key) {
122
+ if (sw->keyWritten) {
123
+ sw->keyWritten = 0;
124
+ assure_size(&sw->out, 1);
125
+ } else {
126
+ long size;
127
+
128
+ key_check(sw, key);
129
+ size = sw->depth * sw->out.indent + 3;
130
+ assure_size(&sw->out, size);
131
+ maybe_comma(sw);
132
+ if (0 < sw->depth) {
133
+ fill_indent(&sw->out, sw->depth);
134
+ }
135
+ if (0 != key) {
136
+ oj_dump_cstr(key, strlen(key), 0, 0, &sw->out);
137
+ *sw->out.cur++ = ':';
138
+ }
139
+ }
140
+ *sw->out.cur++ = '[';
141
+ push_type(sw, ArrayNew);
142
+ }
143
+
144
+ void oj_str_writer_push_value(StrWriter sw, VALUE val, const char *key) {
145
+ Out out = &sw->out;
146
+
147
+ if (sw->keyWritten) {
148
+ sw->keyWritten = 0;
149
+ } else {
150
+ long size;
151
+
152
+ key_check(sw, key);
153
+ size = sw->depth * out->indent + 3;
154
+ assure_size(out, size);
155
+ maybe_comma(sw);
156
+ if (0 < sw->depth) {
157
+ fill_indent(&sw->out, sw->depth);
158
+ }
159
+ if (0 != key) {
160
+ oj_dump_cstr(key, strlen(key), 0, 0, out);
161
+ *out->cur++ = ':';
162
+ }
163
+ }
164
+ switch (out->opts->mode) {
165
+ case StrictMode: oj_dump_strict_val(val, sw->depth, out); break;
166
+ case NullMode: oj_dump_null_val(val, sw->depth, out); break;
167
+ case ObjectMode: oj_dump_obj_val(val, sw->depth, out); break;
168
+ case CompatMode: oj_dump_compat_val(val, sw->depth, out, Yes == out->opts->to_json); break;
169
+ case RailsMode: oj_dump_rails_val(val, sw->depth, out); break;
170
+ case CustomMode: oj_dump_custom_val(val, sw->depth, out, true); break;
171
+ default: oj_dump_custom_val(val, sw->depth, out, true); break;
172
+ }
173
+ }
174
+
175
+ void oj_str_writer_push_json(StrWriter sw, const char *json, const char *key) {
176
+ if (sw->keyWritten) {
177
+ sw->keyWritten = 0;
178
+ } else {
179
+ long size;
180
+
181
+ key_check(sw, key);
182
+ size = sw->depth * sw->out.indent + 3;
183
+ assure_size(&sw->out, size);
184
+ maybe_comma(sw);
185
+ if (0 < sw->depth) {
186
+ fill_indent(&sw->out, sw->depth);
187
+ }
188
+ if (0 != key) {
189
+ oj_dump_cstr(key, strlen(key), 0, 0, &sw->out);
190
+ *sw->out.cur++ = ':';
191
+ }
192
+ }
193
+ oj_dump_raw(json, strlen(json), &sw->out);
194
+ }
195
+
196
+ void oj_str_writer_pop(StrWriter sw) {
197
+ long size;
198
+ DumpType type = sw->types[sw->depth];
199
+
200
+ if (sw->keyWritten) {
201
+ sw->keyWritten = 0;
202
+ rb_raise(rb_eStandardError, "Can not pop after writing a key but no value.");
203
+ }
204
+ sw->depth--;
205
+ if (0 > sw->depth) {
206
+ rb_raise(rb_eStandardError, "Can not pop with no open array or object.");
207
+ }
208
+ size = sw->depth * sw->out.indent + 2;
209
+ assure_size(&sw->out, size);
210
+ fill_indent(&sw->out, sw->depth);
211
+ switch (type) {
212
+ case ObjectNew:
213
+ case ObjectType: *sw->out.cur++ = '}'; break;
214
+ case ArrayNew:
215
+ case ArrayType: *sw->out.cur++ = ']'; break;
216
+ }
217
+ if (0 == sw->depth && 0 <= sw->out.indent) {
218
+ *sw->out.cur++ = '\n';
219
+ }
220
+ }
221
+
222
+ void oj_str_writer_pop_all(StrWriter sw) {
223
+ while (0 < sw->depth) {
224
+ oj_str_writer_pop(sw);
225
+ }
226
+ }
227
+
228
+ static void str_writer_free(void *ptr) {
229
+ StrWriter sw;
230
+
231
+ if (0 == ptr) {
232
+ return;
233
+ }
234
+ sw = (StrWriter)ptr;
235
+
236
+ oj_out_free(&sw->out);
237
+
238
+ xfree(sw->types);
239
+ xfree(ptr);
240
+ }
241
+
242
+ /* Document-method: new
243
+ * call-seq: new(io, options)
244
+ *
245
+ * Creates a new StringWriter. Options are supported according the the
246
+ * specified mode or the mode in the default options. Note that if mimic_JSON
247
+ * or Oj.optimize_rails has not been called then the behavior of the modes may
248
+ * not be the same as if they were.
249
+ *
250
+ * In addition to the regular dump options for the various modes a
251
+ * _:buffer_size_ option is available. It should be set to a positive
252
+ * integer. It is considered a hint of how large the initial internal buffer
253
+ * should be.
254
+ *
255
+ * - *io* [_IO_] stream to write to
256
+ * - *options* [_Hash_] formatting options
257
+ */
258
+ static VALUE str_writer_new(int argc, VALUE *argv, VALUE self) {
259
+ StrWriter sw = ALLOC(struct _strWriter);
260
+
261
+ oj_str_writer_init(sw, 0);
262
+ if (1 == argc) {
263
+ oj_parse_options(argv[0], &sw->opts);
264
+ }
265
+ sw->out.argc = argc - 1;
266
+ sw->out.argv = argv + 1;
267
+ sw->out.indent = sw->opts.indent;
268
+
269
+ return Data_Wrap_Struct(oj_string_writer_class, 0, str_writer_free, sw);
270
+ }
271
+
272
+ /* Document-method: push_key
273
+ * call-seq: push_key(key)
274
+ *
275
+ * Pushes a key onto the JSON document. The key will be used for the next push
276
+ * if currently in a JSON object and ignored otherwise. If a key is provided on
277
+ * the next push then that new key will be ignored.
278
+ * - *key* [_String_] the key pending for the next push
279
+ */
280
+ static VALUE str_writer_push_key(VALUE self, VALUE key) {
281
+ StrWriter sw = (StrWriter)DATA_PTR(self);
282
+
283
+ rb_check_type(key, T_STRING);
284
+ oj_str_writer_push_key(sw, StringValuePtr(key));
285
+
286
+ return Qnil;
287
+ }
288
+
289
+ /* Document-method: push_object
290
+ * call-seq: push_object(key=nil)
291
+ *
292
+ * Pushes an object onto the JSON document. Future pushes will be to this object
293
+ * until a pop() is called.
294
+ * - *key* [_String_] the key if adding to an object in the JSON document
295
+ */
296
+ static VALUE str_writer_push_object(int argc, VALUE *argv, VALUE self) {
297
+ StrWriter sw = (StrWriter)DATA_PTR(self);
298
+
299
+ switch (argc) {
300
+ case 0: oj_str_writer_push_object(sw, 0); break;
301
+ case 1:
302
+ if (Qnil == argv[0]) {
303
+ oj_str_writer_push_object(sw, 0);
304
+ } else {
305
+ rb_check_type(argv[0], T_STRING);
306
+ oj_str_writer_push_object(sw, StringValuePtr(argv[0]));
307
+ }
308
+ break;
309
+ default: rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'."); break;
310
+ }
311
+ if (rb_block_given_p()) {
312
+ rb_yield(Qnil);
313
+ oj_str_writer_pop(sw);
314
+ }
315
+ return Qnil;
316
+ }
317
+
318
+ /* Document-method: push_array
319
+ * call-seq: push_array(key=nil)
320
+ *
321
+ * Pushes an array onto the JSON document. Future pushes will be to this object
322
+ * until a pop() is called.
323
+ * - *key* [_String_] the key if adding to an object in the JSON document
324
+ */
325
+ static VALUE str_writer_push_array(int argc, VALUE *argv, VALUE self) {
326
+ StrWriter sw = (StrWriter)DATA_PTR(self);
327
+
328
+ switch (argc) {
329
+ case 0: oj_str_writer_push_array(sw, 0); break;
330
+ case 1:
331
+ if (Qnil == argv[0]) {
332
+ oj_str_writer_push_array(sw, 0);
333
+ } else {
334
+ rb_check_type(argv[0], T_STRING);
335
+ oj_str_writer_push_array(sw, StringValuePtr(argv[0]));
336
+ }
337
+ break;
338
+ default: rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'."); break;
339
+ }
340
+ if (rb_block_given_p()) {
341
+ rb_yield(Qnil);
342
+ oj_str_writer_pop(sw);
343
+ }
344
+ return Qnil;
345
+ }
346
+
347
+ /* Document-method: push_value
348
+ * call-seq: push_value(value, key=nil)
349
+ *
350
+ * Pushes a value onto the JSON document.
351
+ * - *value* [_Object_] value to add to the JSON document
352
+ * - *key* [_String_] the key if adding to an object in the JSON document
353
+ */
354
+ static VALUE str_writer_push_value(int argc, VALUE *argv, VALUE self) {
355
+ switch (argc) {
356
+ case 1: oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, 0); break;
357
+ case 2:
358
+ if (Qnil == argv[1]) {
359
+ oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, 0);
360
+ } else {
361
+ rb_check_type(argv[1], T_STRING);
362
+ oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, StringValuePtr(argv[1]));
363
+ }
364
+ break;
365
+ default: rb_raise(rb_eArgError, "Wrong number of argument to 'push_value'."); break;
366
+ }
367
+ return Qnil;
368
+ }
369
+
370
+ /* Document-method: push_json
371
+ * call-seq: push_json(value, key=nil)
372
+ *
373
+ * Pushes a string onto the JSON document. The String must be a valid JSON
374
+ * encoded string. No additional checking is done to verify the validity of the
375
+ * string.
376
+ * - *value* [_Object_] value to add to the JSON document
377
+ * - *key* [_String_] the key if adding to an object in the JSON document
378
+ */
379
+ static VALUE str_writer_push_json(int argc, VALUE *argv, VALUE self) {
380
+ rb_check_type(argv[0], T_STRING);
381
+ switch (argc) {
382
+ case 1: oj_str_writer_push_json((StrWriter)DATA_PTR(self), StringValuePtr(*argv), 0); break;
383
+ case 2:
384
+ if (Qnil == argv[1]) {
385
+ oj_str_writer_push_json((StrWriter)DATA_PTR(self), StringValuePtr(*argv), 0);
386
+ } else {
387
+ rb_check_type(argv[1], T_STRING);
388
+ oj_str_writer_push_json((StrWriter)DATA_PTR(self),
389
+ StringValuePtr(*argv),
390
+ StringValuePtr(argv[1]));
391
+ }
392
+ break;
393
+ default: rb_raise(rb_eArgError, "Wrong number of argument to 'push_json'."); break;
394
+ }
395
+ return Qnil;
396
+ }
397
+ /* Document-method: pop
398
+ * call-seq: pop()
399
+ *
400
+ * Pops up a level in the JSON document closing the array or object that is
401
+ * currently open.
402
+ */
403
+ static VALUE str_writer_pop(VALUE self) {
404
+ oj_str_writer_pop((StrWriter)DATA_PTR(self));
405
+ return Qnil;
406
+ }
407
+
408
+ /* Document-method: pop_all
409
+ * call-seq: pop_all()
410
+ *
411
+ * Pops all level in the JSON document closing all the array or object that is
412
+ * currently open.
413
+ */
414
+ static VALUE str_writer_pop_all(VALUE self) {
415
+ oj_str_writer_pop_all((StrWriter)DATA_PTR(self));
416
+
417
+ return Qnil;
418
+ }
419
+
420
+ /* Document-method: reset
421
+ * call-seq: reset()
422
+ *
423
+ * Reset the writer back to the empty state.
424
+ */
425
+ static VALUE str_writer_reset(VALUE self) {
426
+ StrWriter sw = (StrWriter)DATA_PTR(self);
427
+
428
+ sw->depth = 0;
429
+ *sw->types = '\0';
430
+ sw->keyWritten = 0;
431
+ sw->out.cur = sw->out.buf;
432
+ *sw->out.cur = '\0';
433
+
434
+ return Qnil;
435
+ }
436
+
437
+ /* Document-method: to_s
438
+ * call-seq: to_s()
439
+ *
440
+ * Returns the JSON document string in what ever state the construction is at.
441
+ *
442
+ * *return* [_String_]
443
+ */
444
+ static VALUE str_writer_to_s(VALUE self) {
445
+ StrWriter sw = (StrWriter)DATA_PTR(self);
446
+ VALUE rstr = rb_str_new(sw->out.buf, sw->out.cur - sw->out.buf);
447
+
448
+ return oj_encode(rstr);
449
+ }
450
+
451
+ /* Document-method: as_json
452
+ * call-seq: as_json()
453
+ *
454
+ * Returns the contents of the writer as a JSON element. If called from inside
455
+ * an array or hash by Oj the raw buffer will be used othersize a more
456
+ * inefficient parse of the contents and a return of the result is
457
+ * completed. The parse uses the strict mode.
458
+ *
459
+ * *return* [_Hash_|_Array_|_String_|_Integer_|_Float_|_True_|_False_|_nil|)
460
+ */
461
+ static VALUE str_writer_as_json(VALUE self) {
462
+ if (string_writer_optimized) {
463
+ return self;
464
+ }
465
+ return rb_hash_new();
466
+ }
467
+
468
+ /* Document-class: Oj::StringWriter
469
+ *
470
+ * Supports building a JSON document one element at a time. Build the document
471
+ * by pushing values into the document. Pushing an array or an object will
472
+ * create that element in the JSON document and subsequent pushes will add the
473
+ * elements to that array or object until a pop() is called. When complete
474
+ * calling to_s() will return the JSON document. Note that calling to_s() before
475
+ * construction is complete will return the document in it's current state.
476
+ */
477
+ void oj_string_writer_init(void) {
478
+ oj_string_writer_class = rb_define_class_under(Oj, "StringWriter", rb_cObject);
479
+ rb_gc_register_address(&oj_string_writer_class);
480
+ rb_undef_alloc_func(oj_string_writer_class);
481
+ rb_define_module_function(oj_string_writer_class, "new", str_writer_new, -1);
482
+ rb_define_method(oj_string_writer_class, "push_key", str_writer_push_key, 1);
483
+ rb_define_method(oj_string_writer_class, "push_object", str_writer_push_object, -1);
484
+ rb_define_method(oj_string_writer_class, "push_array", str_writer_push_array, -1);
485
+ rb_define_method(oj_string_writer_class, "push_value", str_writer_push_value, -1);
486
+ rb_define_method(oj_string_writer_class, "push_json", str_writer_push_json, -1);
487
+ rb_define_method(oj_string_writer_class, "pop", str_writer_pop, 0);
488
+ rb_define_method(oj_string_writer_class, "pop_all", str_writer_pop_all, 0);
489
+ rb_define_method(oj_string_writer_class, "reset", str_writer_reset, 0);
490
+ rb_define_method(oj_string_writer_class, "to_s", str_writer_to_s, 0);
491
+ rb_define_method(oj_string_writer_class, "raw_json", str_writer_to_s, 0);
492
+ rb_define_method(oj_string_writer_class, "as_json", str_writer_as_json, 0);
493
+ }
data/ext/oj/trace.c ADDED
@@ -0,0 +1,72 @@
1
+ // Copyright (c) 2018 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for license details.
3
+
4
+ #include "trace.h"
5
+
6
+ #include "parse.h"
7
+
8
+ #define MAX_INDENT 256
9
+
10
+ static void fill_indent(char *indent, int depth) {
11
+ if (MAX_INDENT <= depth) {
12
+ depth = MAX_INDENT - 1;
13
+ } else if (depth < 0) {
14
+ depth = 0;
15
+ }
16
+ if (0 < depth) {
17
+ memset(indent, ' ', depth);
18
+ }
19
+ indent[depth] = '\0';
20
+ }
21
+
22
+ void oj_trace(const char *func, VALUE obj, const char *file, int line, int depth, TraceWhere where) {
23
+ char fmt[64];
24
+ char indent[MAX_INDENT];
25
+
26
+ depth *= 2;
27
+ fill_indent(indent, depth);
28
+ sprintf(fmt, "#0:%%13s:%%3d:Oj:%c:%%%ds %%s %%s\n", where, depth);
29
+ printf(fmt, file, line, indent, func, rb_obj_classname(obj));
30
+ }
31
+
32
+ void oj_trace_parse_call(const char *func, ParseInfo pi, const char *file, int line, VALUE obj) {
33
+ char fmt[64];
34
+ char indent[MAX_INDENT];
35
+ int depth = (int)(stack_size(&pi->stack) * 2);
36
+
37
+ fill_indent(indent, depth);
38
+ sprintf(fmt, "#0:%%13s:%%3d:Oj:-:%%%ds %%s %%s\n", depth);
39
+ printf(fmt, file, line, indent, func, rb_obj_classname(obj));
40
+ }
41
+
42
+ void oj_trace_parse_in(const char *func, ParseInfo pi, const char *file, int line) {
43
+ char fmt[64];
44
+ char indent[MAX_INDENT];
45
+ int depth = (int)(stack_size(&pi->stack) * 2);
46
+
47
+ fill_indent(indent, depth);
48
+ sprintf(fmt, "#0:%%13s:%%3d:Oj:}:%%%ds %%s\n", depth);
49
+ printf(fmt, file, line, indent, func);
50
+ }
51
+
52
+ void oj_trace_parse_hash_end(ParseInfo pi, const char *file, int line) {
53
+ char fmt[64];
54
+ char indent[MAX_INDENT];
55
+ int depth = (int)(stack_size(&pi->stack) * 2 - 2);
56
+ Val v = stack_peek(&pi->stack);
57
+ VALUE obj = v->val;
58
+
59
+ fill_indent(indent, depth);
60
+ sprintf(fmt, "#0:%%13s:%%3d:Oj:{:%%%ds hash_end %%s\n", depth);
61
+ printf(fmt, file, line, indent, rb_obj_classname(obj));
62
+ }
63
+
64
+ void oj_trace_parse_array_end(ParseInfo pi, const char *file, int line) {
65
+ char fmt[64];
66
+ char indent[MAX_INDENT];
67
+ int depth = (int)(stack_size(&pi->stack) * 2);
68
+
69
+ fill_indent(indent, depth);
70
+ sprintf(fmt, "#0:%%13s:%%3d:Oj:{:%%%ds array_ned\n", depth);
71
+ printf(fmt, file, line, indent);
72
+ }
data/ext/oj/trace.h ADDED
@@ -0,0 +1,28 @@
1
+ // Copyright (c) 2018 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for license details.
3
+
4
+ #ifndef OJ_TRACE_H
5
+ #define OJ_TRACE_H
6
+
7
+ #include <ruby.h>
8
+ #include <stdbool.h>
9
+
10
+ typedef enum {
11
+ TraceIn = '}',
12
+ TraceOut = '{',
13
+ TraceCall = '-',
14
+ TraceRubyIn = '>',
15
+ TraceRubyOut = '<',
16
+ } TraceWhere;
17
+
18
+ struct _parseInfo;
19
+
20
+ extern void
21
+ oj_trace(const char *func, VALUE obj, const char *file, int line, int depth, TraceWhere where);
22
+ extern void oj_trace_parse_in(const char *func, struct _parseInfo *pi, const char *file, int line);
23
+ extern void
24
+ oj_trace_parse_call(const char *func, struct _parseInfo *pi, const char *file, int line, VALUE obj);
25
+ extern void oj_trace_parse_hash_end(struct _parseInfo *pi, const char *file, int line);
26
+ extern void oj_trace_parse_array_end(struct _parseInfo *pi, const char *file, int line);
27
+
28
+ #endif /* OJ_TRACE_H */