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,331 @@
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 <errno.h>
5
+ #include <ruby.h>
6
+
7
+ #include "encode.h"
8
+
9
+ extern VALUE Oj;
10
+
11
+ static void stream_writer_free(void *ptr) {
12
+ StreamWriter sw;
13
+
14
+ if (0 == ptr) {
15
+ return;
16
+ }
17
+ sw = (StreamWriter)ptr;
18
+ xfree(sw->sw.out.buf);
19
+ xfree(sw->sw.types);
20
+ xfree(ptr);
21
+ }
22
+
23
+ static void stream_writer_reset_buf(StreamWriter sw) {
24
+ sw->sw.out.cur = sw->sw.out.buf;
25
+ *sw->sw.out.cur = '\0';
26
+ }
27
+
28
+ static void stream_writer_write(StreamWriter sw) {
29
+ ssize_t size = sw->sw.out.cur - sw->sw.out.buf;
30
+
31
+ switch (sw->type) {
32
+ case STRING_IO:
33
+ case STREAM_IO: {
34
+ volatile VALUE rs = rb_str_new(sw->sw.out.buf, size);
35
+
36
+ // Oddly enough, when pushing ASCII characters with UTF-8 encoding or
37
+ // even ASCII-8BIT does not change the output encoding. Pushing any
38
+ // non-ASCII no matter what the encoding changes the output encoding
39
+ // to ASCII-8BIT if it the string is not forced to UTF-8 here.
40
+ rs = oj_encode(rs);
41
+ rb_funcall(sw->stream, oj_write_id, 1, rs);
42
+ break;
43
+ }
44
+ case FILE_IO:
45
+ if (size != write(sw->fd, sw->sw.out.buf, size)) {
46
+ rb_raise(rb_eIOError, "Write failed. [_%d_:%s]\n", errno, strerror(errno));
47
+ }
48
+ break;
49
+ default: rb_raise(rb_eArgError, "expected an IO Object.");
50
+ }
51
+ stream_writer_reset_buf(sw);
52
+ }
53
+
54
+ static VALUE buffer_size_sym = Qundef;
55
+
56
+ /* Document-method: new
57
+ * call-seq: new(io, options)
58
+ *
59
+ * Creates a new StreamWriter. Options are supported according the specified
60
+ * mode or the mode in the default options. Note that if mimic_JSON or
61
+ * Oj.optimize_rails has not been called then the behavior of the modes may
62
+ * not be the same as if they were.
63
+ *
64
+ * In addition to the regular dump options for the various modes a
65
+ * _:buffer_size_ option is available. It should be set to a positive
66
+ * integer. It is considered a hint of how large the initial internal buffer
67
+ * should be and also a hint on when to flush.
68
+ *
69
+ * - *io* [_IO_] stream to write to
70
+ * - *options* [_Hash_] formatting options
71
+ */
72
+ static VALUE stream_writer_new(int argc, VALUE *argv, VALUE self) {
73
+ StreamWriterType type = STREAM_IO;
74
+ int fd = 0;
75
+ VALUE stream = argv[0];
76
+ VALUE clas = rb_obj_class(stream);
77
+ StreamWriter sw;
78
+ #if !IS_WINDOWS
79
+ VALUE s;
80
+ #endif
81
+
82
+ if (oj_stringio_class == clas) {
83
+ type = STRING_IO;
84
+ #if !IS_WINDOWS
85
+ } else if (rb_respond_to(stream, oj_fileno_id) &&
86
+ Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) && 0 != (fd = FIX2INT(s))) {
87
+ type = FILE_IO;
88
+ #endif
89
+ } else if (rb_respond_to(stream, oj_write_id)) {
90
+ type = STREAM_IO;
91
+ } else {
92
+ rb_raise(rb_eArgError, "expected an IO Object.");
93
+ }
94
+ sw = ALLOC(struct _streamWriter);
95
+ if (2 == argc && T_HASH == rb_type(argv[1])) {
96
+ volatile VALUE v;
97
+ int buf_size = 0;
98
+
99
+ if (Qundef == buffer_size_sym) {
100
+ buffer_size_sym = ID2SYM(rb_intern("buffer_size"));
101
+ rb_gc_register_address(&buffer_size_sym);
102
+ }
103
+ if (Qnil != (v = rb_hash_lookup(argv[1], buffer_size_sym))) {
104
+ #ifdef RUBY_INTEGER_UNIFICATION
105
+ if (rb_cInteger != rb_obj_class(v)) {
106
+ rb_raise(rb_eArgError, ":buffer size must be a Integer.");
107
+ }
108
+ #else
109
+ if (T_FIXNUM != rb_type(v)) {
110
+ rb_raise(rb_eArgError, ":buffer size must be a Integer.");
111
+ }
112
+ #endif
113
+ buf_size = FIX2INT(v);
114
+ }
115
+ oj_str_writer_init(&sw->sw, buf_size);
116
+ oj_parse_options(argv[1], &sw->sw.opts);
117
+ sw->flush_limit = buf_size;
118
+ } else {
119
+ oj_str_writer_init(&sw->sw, 4096);
120
+ sw->flush_limit = 0;
121
+ }
122
+ sw->sw.out.indent = sw->sw.opts.indent;
123
+ sw->stream = stream;
124
+ sw->type = type;
125
+ sw->fd = fd;
126
+
127
+ return Data_Wrap_Struct(oj_stream_writer_class, 0, stream_writer_free, sw);
128
+ }
129
+
130
+ /* Document-method: push_key
131
+ * call-seq: push_key(key)
132
+ *
133
+ * Pushes a key onto the JSON document. The key will be used for the next push
134
+ * if currently in a JSON object and ignored otherwise. If a key is provided on
135
+ * the next push then that new key will be ignored.
136
+ *
137
+ * - *key* [_String_] the key pending for the next push
138
+ */
139
+ static VALUE stream_writer_push_key(VALUE self, VALUE key) {
140
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
141
+
142
+ rb_check_type(key, T_STRING);
143
+ oj_str_writer_push_key(&sw->sw, StringValuePtr(key));
144
+ if (sw->flush_limit < sw->sw.out.cur - sw->sw.out.buf) {
145
+ stream_writer_write(sw);
146
+ }
147
+ return Qnil;
148
+ }
149
+
150
+ /* Document-method: push_object
151
+ * call-seq: push_object(key=nil)
152
+ *
153
+ * Pushes an object onto the JSON document. Future pushes will be to this object
154
+ * until a pop() is called.
155
+ *
156
+ * - *key* [_String_] the key if adding to an object in the JSON document
157
+ */
158
+ static VALUE stream_writer_push_object(int argc, VALUE *argv, VALUE self) {
159
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
160
+
161
+ switch (argc) {
162
+ case 0: oj_str_writer_push_object(&sw->sw, 0); break;
163
+ case 1:
164
+ if (Qnil == argv[0]) {
165
+ oj_str_writer_push_object(&sw->sw, 0);
166
+ } else {
167
+ rb_check_type(argv[0], T_STRING);
168
+ oj_str_writer_push_object(&sw->sw, StringValuePtr(argv[0]));
169
+ }
170
+ break;
171
+ default: rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'."); break;
172
+ }
173
+ if (sw->flush_limit < sw->sw.out.cur - sw->sw.out.buf) {
174
+ stream_writer_write(sw);
175
+ }
176
+ return Qnil;
177
+ }
178
+
179
+ /* Document-method: push_array
180
+ * call-seq: push_array(key=nil)
181
+ *
182
+ * Pushes an array onto the JSON document. Future pushes will be to this object
183
+ * until a pop() is called.
184
+ *
185
+ * - *key* [_String_] the key if adding to an object in the JSON document
186
+ */
187
+ static VALUE stream_writer_push_array(int argc, VALUE *argv, VALUE self) {
188
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
189
+
190
+ switch (argc) {
191
+ case 0: oj_str_writer_push_array(&sw->sw, 0); break;
192
+ case 1:
193
+ if (Qnil == argv[0]) {
194
+ oj_str_writer_push_array(&sw->sw, 0);
195
+ } else {
196
+ rb_check_type(argv[0], T_STRING);
197
+ oj_str_writer_push_array(&sw->sw, StringValuePtr(argv[0]));
198
+ }
199
+ break;
200
+ default: rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'."); break;
201
+ }
202
+ if (sw->flush_limit < sw->sw.out.cur - sw->sw.out.buf) {
203
+ stream_writer_write(sw);
204
+ }
205
+ return Qnil;
206
+ }
207
+
208
+ /* Document-method: push_value
209
+ * call-seq: push_value(value, key=nil)
210
+ *
211
+ * Pushes a value onto the JSON document.
212
+ * - *value* [_Object_] value to add to the JSON document
213
+ * - *key* [_String_] the key if adding to an object in the JSON document
214
+ */
215
+ static VALUE stream_writer_push_value(int argc, VALUE *argv, VALUE self) {
216
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
217
+
218
+ switch (argc) {
219
+ case 1: oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, 0); break;
220
+ case 2:
221
+ if (Qnil == argv[1]) {
222
+ oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, 0);
223
+ } else {
224
+ rb_check_type(argv[1], T_STRING);
225
+ oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, StringValuePtr(argv[1]));
226
+ }
227
+ break;
228
+ default: rb_raise(rb_eArgError, "Wrong number of argument to 'push_value'."); break;
229
+ }
230
+ if (sw->flush_limit < sw->sw.out.cur - sw->sw.out.buf) {
231
+ stream_writer_write(sw);
232
+ }
233
+ return Qnil;
234
+ }
235
+
236
+ /* Document-method: push_json
237
+ * call-seq: push_json(value, key=nil)
238
+ *
239
+ * Pushes a string onto the JSON document. The String must be a valid JSON
240
+ * encoded string. No additional checking is done to verify the validity of the
241
+ * string.
242
+ * - *value* [_Object_] value to add to the JSON document
243
+ * - *key* [_String_] the key if adding to an object in the JSON document
244
+ */
245
+ static VALUE stream_writer_push_json(int argc, VALUE *argv, VALUE self) {
246
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
247
+
248
+ rb_check_type(argv[0], T_STRING);
249
+ switch (argc) {
250
+ case 1: oj_str_writer_push_json((StrWriter)DATA_PTR(self), StringValuePtr(*argv), 0); break;
251
+ case 2:
252
+ if (Qnil == argv[1]) {
253
+ oj_str_writer_push_json((StrWriter)DATA_PTR(self), StringValuePtr(*argv), 0);
254
+ } else {
255
+ rb_check_type(argv[1], T_STRING);
256
+ oj_str_writer_push_json((StrWriter)DATA_PTR(self),
257
+ StringValuePtr(*argv),
258
+ StringValuePtr(argv[1]));
259
+ }
260
+ break;
261
+ default: rb_raise(rb_eArgError, "Wrong number of argument to 'push_json'."); break;
262
+ }
263
+ if (sw->flush_limit < sw->sw.out.cur - sw->sw.out.buf) {
264
+ stream_writer_write(sw);
265
+ }
266
+ return Qnil;
267
+ }
268
+
269
+ /* Document-method: pop
270
+ * call-seq: pop()
271
+ *
272
+ * Pops up a level in the JSON document closing the array or object that is
273
+ * currently open.
274
+ */
275
+ static VALUE stream_writer_pop(VALUE self) {
276
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
277
+
278
+ oj_str_writer_pop(&sw->sw);
279
+ if (sw->flush_limit < sw->sw.out.cur - sw->sw.out.buf) {
280
+ stream_writer_write(sw);
281
+ }
282
+ return Qnil;
283
+ }
284
+
285
+ /* Document-method: pop_all
286
+ * call-seq: pop_all()
287
+ *
288
+ * Pops all level in the JSON document closing all the array or object that is
289
+ * currently open.
290
+ */
291
+ static VALUE stream_writer_pop_all(VALUE self) {
292
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
293
+
294
+ oj_str_writer_pop_all(&sw->sw);
295
+ stream_writer_write(sw);
296
+
297
+ return Qnil;
298
+ }
299
+
300
+ /* Document-method: flush
301
+ * call-seq: flush()
302
+ *
303
+ * Flush any remaining characters in the buffer.
304
+ */
305
+ static VALUE stream_writer_flush(VALUE self) {
306
+ stream_writer_write((StreamWriter)DATA_PTR(self));
307
+
308
+ return Qnil;
309
+ }
310
+
311
+ /* Document-class: Oj::StreamWriter
312
+ *
313
+ * Supports building a JSON document one element at a time. Build the IO stream
314
+ * document by pushing values into the document. Pushing an array or an object
315
+ * will create that element in the JSON document and subsequent pushes will add
316
+ * the elements to that array or object until a pop() is called.
317
+ */
318
+ void oj_stream_writer_init(void) {
319
+ oj_stream_writer_class = rb_define_class_under(Oj, "StreamWriter", rb_cObject);
320
+ rb_gc_register_address(&oj_stream_writer_class);
321
+ rb_undef_alloc_func(oj_stream_writer_class);
322
+ rb_define_module_function(oj_stream_writer_class, "new", stream_writer_new, -1);
323
+ rb_define_method(oj_stream_writer_class, "push_key", stream_writer_push_key, 1);
324
+ rb_define_method(oj_stream_writer_class, "push_object", stream_writer_push_object, -1);
325
+ rb_define_method(oj_stream_writer_class, "push_array", stream_writer_push_array, -1);
326
+ rb_define_method(oj_stream_writer_class, "push_value", stream_writer_push_value, -1);
327
+ rb_define_method(oj_stream_writer_class, "push_json", stream_writer_push_json, -1);
328
+ rb_define_method(oj_stream_writer_class, "pop", stream_writer_pop, 0);
329
+ rb_define_method(oj_stream_writer_class, "pop_all", stream_writer_pop_all, 0);
330
+ rb_define_method(oj_stream_writer_class, "flush", stream_writer_flush, 0);
331
+ }
data/ext/oj/strict.c CHANGED
@@ -1,185 +1,221 @@
1
- /* strict.c
2
- * Copyright (c) 2012, Peter Ohler
3
- * All rights reserved.
4
- *
5
- * Redistribution and use in source and binary forms, with or without
6
- * modification, are permitted provided that the following conditions are met:
7
- *
8
- * - Redistributions of source code must retain the above copyright notice, this
9
- * list of conditions and the following disclaimer.
10
- *
11
- * - Redistributions in binary form must reproduce the above copyright notice,
12
- * this list of conditions and the following disclaimer in the documentation
13
- * and/or other materials provided with the distribution.
14
- *
15
- * - Neither the name of Peter Ohler nor the names of its contributors may be
16
- * used to endorse or promote products derived from this software without
17
- * specific prior written permission.
18
- *
19
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
- */
1
+ // Copyright (c) 2012 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for license details.
30
3
 
31
- #include <stdlib.h>
32
4
  #include <stdio.h>
5
+ #include <stdlib.h>
33
6
  #include <string.h>
34
7
  #include <unistd.h>
35
8
 
36
- #include "oj.h"
9
+ #include "encode.h"
37
10
  #include "err.h"
11
+ #include "intern.h"
12
+ #include "oj.h"
38
13
  #include "parse.h"
39
- #include "encode.h"
14
+ #include "trace.h"
15
+
16
+ VALUE oj_cstr_to_value(const char *str, size_t len, size_t cache_str) {
17
+ volatile VALUE rstr = Qnil;
18
+
19
+ if (len < cache_str) {
20
+ rstr = oj_str_intern(str, len);
21
+ } else {
22
+ rstr = rb_str_new(str, len);
23
+ rstr = oj_encode(rstr);
24
+ }
25
+ return rstr;
26
+ }
27
+
28
+ VALUE oj_calc_hash_key(ParseInfo pi, Val parent) {
29
+ volatile VALUE rkey = parent->key_val;
30
+
31
+ if (Qundef != rkey) {
32
+ return rkey;
33
+ }
34
+ if (Yes != pi->options.cache_keys) {
35
+ if (Yes == pi->options.sym_key) {
36
+ rkey = ID2SYM(rb_intern3(parent->key, parent->klen, oj_utf8_encoding));
37
+ } else {
38
+ rkey = rb_str_new(parent->key, parent->klen);
39
+ rkey = oj_encode(rkey);
40
+ OBJ_FREEZE(rkey); // frozen when used as a Hash key anyway
41
+ }
42
+ return rkey;
43
+ }
44
+ if (Yes == pi->options.sym_key) {
45
+ rkey = oj_sym_intern(parent->key, parent->klen);
46
+ } else {
47
+ rkey = oj_str_intern(parent->key, parent->klen);
48
+ }
49
+ return rkey;
50
+ }
40
51
 
41
- static void
42
- noop_end(struct _ParseInfo *pi) {
52
+ static void hash_end(ParseInfo pi) {
53
+ if (Yes == pi->options.trace) {
54
+ oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
55
+ }
43
56
  }
44
57
 
45
- static VALUE
46
- noop_hash_key(struct _ParseInfo *pi, const char *key, size_t klen) {
58
+ static void array_end(ParseInfo pi) {
59
+ if (Yes == pi->options.trace) {
60
+ oj_trace_parse_array_end(pi, __FILE__, __LINE__);
61
+ }
62
+ }
63
+
64
+ static VALUE noop_hash_key(ParseInfo pi, const char *key, size_t klen) {
47
65
  return Qundef;
48
66
  }
49
67
 
50
- static void
51
- add_value(ParseInfo pi, VALUE val) {
68
+ static void add_value(ParseInfo pi, VALUE val) {
69
+ if (Yes == pi->options.trace) {
70
+ oj_trace_parse_call("add_value", pi, __FILE__, __LINE__, val);
71
+ }
52
72
  pi->stack.head->val = val;
53
73
  }
54
74
 
55
- static void
56
- add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
57
- volatile VALUE rstr = rb_str_new(str, len);
75
+ static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
76
+ volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
58
77
 
59
- rstr = oj_encode(rstr);
60
78
  pi->stack.head->val = rstr;
79
+ if (Yes == pi->options.trace) {
80
+ oj_trace_parse_call("add_string", pi, __FILE__, __LINE__, rstr);
81
+ }
61
82
  }
62
83
 
63
- static void
64
- add_num(ParseInfo pi, NumInfo ni) {
84
+ static void add_num(ParseInfo pi, NumInfo ni) {
65
85
  if (ni->infinity || ni->nan) {
66
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
86
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
67
87
  }
68
88
  pi->stack.head->val = oj_num_as_value(ni);
89
+ if (Yes == pi->options.trace) {
90
+ oj_trace_parse_call("add_number", pi, __FILE__, __LINE__, pi->stack.head->val);
91
+ }
69
92
  }
70
93
 
71
- static VALUE
72
- start_hash(ParseInfo pi) {
94
+ static VALUE start_hash(ParseInfo pi) {
73
95
  if (Qnil != pi->options.hash_class) {
74
- return rb_class_new_instance(0, NULL, pi->options.hash_class);
96
+ return rb_class_new_instance(0, NULL, pi->options.hash_class);
97
+ }
98
+ if (Yes == pi->options.trace) {
99
+ oj_trace_parse_in("start_hash", pi, __FILE__, __LINE__);
75
100
  }
76
101
  return rb_hash_new();
77
102
  }
78
103
 
79
- static VALUE
80
- calc_hash_key(ParseInfo pi, Val parent) {
81
- volatile VALUE rkey = parent->key_val;
104
+ static void hash_set_cstr(ParseInfo pi, Val parent, const char *str, size_t len, const char *orig) {
105
+ volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
82
106
 
83
- if (Qundef == rkey) {
84
- rkey = rb_str_new(parent->key, parent->klen);
85
- }
86
- rkey = oj_encode(rkey);
87
- if (Yes == pi->options.sym_key) {
88
- rkey = rb_str_intern(rkey);
107
+ rb_hash_aset(stack_peek(&pi->stack)->val,
108
+ oj_calc_hash_key(pi, parent),
109
+ rstr);
110
+ if (Yes == pi->options.trace) {
111
+ oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rstr);
89
112
  }
90
- return rkey;
91
113
  }
92
114
 
93
- static void
94
- hash_set_cstr(ParseInfo pi, Val parent, const char *str, size_t len, const char *orig) {
95
- volatile VALUE rstr = rb_str_new(str, len);
96
-
97
- rstr = oj_encode(rstr);
98
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), rstr);
99
- }
115
+ static void hash_set_num(ParseInfo pi, Val parent, NumInfo ni) {
116
+ volatile VALUE v;
100
117
 
101
- static void
102
- hash_set_num(struct _ParseInfo *pi, Val parent, NumInfo ni) {
103
118
  if (ni->infinity || ni->nan) {
104
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
119
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
120
+ }
121
+ v = oj_num_as_value(ni);
122
+ rb_hash_aset(stack_peek(&pi->stack)->val,
123
+ oj_calc_hash_key(pi, parent),
124
+ v);
125
+ if (Yes == pi->options.trace) {
126
+ oj_trace_parse_call("set_number", pi, __FILE__, __LINE__, v);
105
127
  }
106
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), oj_num_as_value(ni));
107
128
  }
108
129
 
109
- static void
110
- hash_set_value(ParseInfo pi, Val parent, VALUE value) {
111
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), value);
130
+ static void hash_set_value(ParseInfo pi, Val parent, VALUE value) {
131
+ rb_hash_aset(stack_peek(&pi->stack)->val,
132
+ oj_calc_hash_key(pi, parent),
133
+ value);
134
+ if (Yes == pi->options.trace) {
135
+ oj_trace_parse_call("set_value", pi, __FILE__, __LINE__, value);
136
+ }
112
137
  }
113
138
 
114
- static VALUE
115
- start_array(ParseInfo pi) {
139
+ static VALUE start_array(ParseInfo pi) {
140
+ if (Yes == pi->options.trace) {
141
+ oj_trace_parse_in("start_array", pi, __FILE__, __LINE__);
142
+ }
116
143
  return rb_ary_new();
117
144
  }
118
145
 
119
- static void
120
- array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
121
- volatile VALUE rstr = rb_str_new(str, len);
146
+ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
147
+ volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
122
148
 
123
- rstr = oj_encode(rstr);
124
149
  rb_ary_push(stack_peek(&pi->stack)->val, rstr);
150
+ if (Yes == pi->options.trace) {
151
+ oj_trace_parse_call("append_string", pi, __FILE__, __LINE__, rstr);
152
+ }
125
153
  }
126
154
 
127
- static void
128
- array_append_num(ParseInfo pi, NumInfo ni) {
155
+ static void array_append_num(ParseInfo pi, NumInfo ni) {
156
+ volatile VALUE v;
157
+
129
158
  if (ni->infinity || ni->nan) {
130
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
159
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
160
+ }
161
+ v = oj_num_as_value(ni);
162
+ rb_ary_push(stack_peek(&pi->stack)->val, v);
163
+ if (Yes == pi->options.trace) {
164
+ oj_trace_parse_call("append_number", pi, __FILE__, __LINE__, v);
131
165
  }
132
- rb_ary_push(stack_peek(&pi->stack)->val, oj_num_as_value(ni));
133
166
  }
134
167
 
135
- static void
136
- array_append_value(ParseInfo pi, VALUE value) {
168
+ static void array_append_value(ParseInfo pi, VALUE value) {
137
169
  rb_ary_push(stack_peek(&pi->stack)->val, value);
170
+ if (Yes == pi->options.trace) {
171
+ oj_trace_parse_call("append_value", pi, __FILE__, __LINE__, value);
172
+ }
138
173
  }
139
174
 
140
- void
141
- oj_set_strict_callbacks(ParseInfo pi) {
142
- pi->start_hash = start_hash;
143
- pi->end_hash = noop_end;
144
- pi->hash_key = noop_hash_key;
145
- pi->hash_set_cstr = hash_set_cstr;
146
- pi->hash_set_num = hash_set_num;
147
- pi->hash_set_value = hash_set_value;
148
- pi->start_array = start_array;
149
- pi->end_array = noop_end;
150
- pi->array_append_cstr = array_append_cstr;
151
- pi->array_append_num = array_append_num;
175
+ void oj_set_strict_callbacks(ParseInfo pi) {
176
+ pi->start_hash = start_hash;
177
+ pi->end_hash = hash_end;
178
+ pi->hash_key = noop_hash_key;
179
+ pi->hash_set_cstr = hash_set_cstr;
180
+ pi->hash_set_num = hash_set_num;
181
+ pi->hash_set_value = hash_set_value;
182
+ pi->start_array = start_array;
183
+ pi->end_array = array_end;
184
+ pi->array_append_cstr = array_append_cstr;
185
+ pi->array_append_num = array_append_num;
152
186
  pi->array_append_value = array_append_value;
153
- pi->add_cstr = add_cstr;
154
- pi->add_num = add_num;
155
- pi->add_value = add_value;
156
- pi->expect_value = 1;
187
+ pi->add_cstr = add_cstr;
188
+ pi->add_num = add_num;
189
+ pi->add_value = add_value;
190
+ pi->expect_value = 1;
157
191
  }
158
192
 
159
193
  VALUE
160
194
  oj_strict_parse(int argc, VALUE *argv, VALUE self) {
161
- struct _ParseInfo pi;
195
+ struct _parseInfo pi;
162
196
 
163
- pi.options = oj_default_options;
164
- pi.handler = Qnil;
197
+ parse_info_init(&pi);
198
+ pi.options = oj_default_options;
199
+ pi.handler = Qnil;
165
200
  pi.err_class = Qnil;
166
201
  oj_set_strict_callbacks(&pi);
167
202
 
168
203
  if (T_STRING == rb_type(*argv)) {
169
- return oj_pi_parse(argc, argv, &pi, 0, 0, 1);
204
+ return oj_pi_parse(argc, argv, &pi, 0, 0, true);
170
205
  } else {
171
- return oj_pi_sparse(argc, argv, &pi, 0);
206
+ return oj_pi_sparse(argc, argv, &pi, 0);
172
207
  }
173
208
  }
174
209
 
175
210
  VALUE
176
211
  oj_strict_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
177
- struct _ParseInfo pi;
212
+ struct _parseInfo pi;
178
213
 
179
- pi.options = oj_default_options;
180
- pi.handler = Qnil;
214
+ parse_info_init(&pi);
215
+ pi.options = oj_default_options;
216
+ pi.handler = Qnil;
181
217
  pi.err_class = Qnil;
182
218
  oj_set_strict_callbacks(&pi);
183
219
 
184
- return oj_pi_parse(argc, argv, &pi, json, len, 1);
220
+ return oj_pi_parse(argc, argv, &pi, json, len, true);
185
221
  }