oj 3.10.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +104 -0
  4. data/ext/oj/buf.h +103 -0
  5. data/ext/oj/cache8.c +107 -0
  6. data/ext/oj/cache8.h +48 -0
  7. data/ext/oj/circarray.c +68 -0
  8. data/ext/oj/circarray.h +23 -0
  9. data/ext/oj/code.c +235 -0
  10. data/ext/oj/code.h +42 -0
  11. data/ext/oj/compat.c +299 -0
  12. data/ext/oj/custom.c +1218 -0
  13. data/ext/oj/dump.c +1249 -0
  14. data/ext/oj/dump.h +96 -0
  15. data/ext/oj/dump_compat.c +975 -0
  16. data/ext/oj/dump_leaf.c +252 -0
  17. data/ext/oj/dump_object.c +844 -0
  18. data/ext/oj/dump_strict.c +434 -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 +53 -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 +890 -0
  28. data/ext/oj/object.c +775 -0
  29. data/ext/oj/odd.c +231 -0
  30. data/ext/oj/odd.h +44 -0
  31. data/ext/oj/oj.c +1723 -0
  32. data/ext/oj/oj.h +387 -0
  33. data/ext/oj/parse.c +1134 -0
  34. data/ext/oj/parse.h +112 -0
  35. data/ext/oj/rails.c +1528 -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 +924 -0
  46. data/ext/oj/stream_writer.c +363 -0
  47. data/ext/oj/strict.c +212 -0
  48. data/ext/oj/string_writer.c +534 -0
  49. data/ext/oj/trace.c +79 -0
  50. data/ext/oj/trace.h +28 -0
  51. data/ext/oj/util.c +136 -0
  52. data/ext/oj/util.h +19 -0
  53. data/ext/oj/val_stack.c +118 -0
  54. data/ext/oj/val_stack.h +185 -0
  55. data/ext/oj/wab.c +631 -0
  56. data/lib/oj.rb +21 -0
  57. data/lib/oj/active_support_helper.rb +41 -0
  58. data/lib/oj/bag.rb +88 -0
  59. data/lib/oj/easy_hash.rb +52 -0
  60. data/lib/oj/error.rb +22 -0
  61. data/lib/oj/json.rb +176 -0
  62. data/lib/oj/mimic.rb +267 -0
  63. data/lib/oj/saj.rb +66 -0
  64. data/lib/oj/schandler.rb +142 -0
  65. data/lib/oj/state.rb +131 -0
  66. data/lib/oj/version.rb +5 -0
  67. data/pages/Advanced.md +22 -0
  68. data/pages/Compatibility.md +25 -0
  69. data/pages/Custom.md +23 -0
  70. data/pages/Encoding.md +65 -0
  71. data/pages/JsonGem.md +79 -0
  72. data/pages/Modes.md +155 -0
  73. data/pages/Options.md +287 -0
  74. data/pages/Rails.md +155 -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/abstract_unit.rb +45 -0
  85. data/test/activesupport5/decoding_test.rb +133 -0
  86. data/test/activesupport5/encoding_test.rb +500 -0
  87. data/test/activesupport5/encoding_test_cases.rb +98 -0
  88. data/test/activesupport5/test_helper.rb +72 -0
  89. data/test/activesupport5/time_zone_test_helpers.rb +39 -0
  90. data/test/activesupport6/abstract_unit.rb +44 -0
  91. data/test/activesupport6/decoding_test.rb +133 -0
  92. data/test/activesupport6/encoding_test.rb +507 -0
  93. data/test/activesupport6/encoding_test_cases.rb +98 -0
  94. data/test/activesupport6/test_common.rb +17 -0
  95. data/test/activesupport6/test_helper.rb +163 -0
  96. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  97. data/test/bar.rb +35 -0
  98. data/test/baz.rb +16 -0
  99. data/test/files.rb +29 -0
  100. data/test/foo.rb +52 -0
  101. data/test/helper.rb +26 -0
  102. data/test/isolated/shared.rb +308 -0
  103. data/test/isolated/test_mimic_after.rb +13 -0
  104. data/test/isolated/test_mimic_alone.rb +12 -0
  105. data/test/isolated/test_mimic_as_json.rb +45 -0
  106. data/test/isolated/test_mimic_before.rb +13 -0
  107. data/test/isolated/test_mimic_define.rb +28 -0
  108. data/test/isolated/test_mimic_rails_after.rb +22 -0
  109. data/test/isolated/test_mimic_rails_before.rb +21 -0
  110. data/test/isolated/test_mimic_redefine.rb +15 -0
  111. data/test/json_gem/json_addition_test.rb +216 -0
  112. data/test/json_gem/json_common_interface_test.rb +148 -0
  113. data/test/json_gem/json_encoding_test.rb +107 -0
  114. data/test/json_gem/json_ext_parser_test.rb +20 -0
  115. data/test/json_gem/json_fixtures_test.rb +35 -0
  116. data/test/json_gem/json_generator_test.rb +383 -0
  117. data/test/json_gem/json_generic_object_test.rb +90 -0
  118. data/test/json_gem/json_parser_test.rb +470 -0
  119. data/test/json_gem/json_string_matching_test.rb +42 -0
  120. data/test/json_gem/test_helper.rb +18 -0
  121. data/test/perf.rb +107 -0
  122. data/test/perf_compat.rb +130 -0
  123. data/test/perf_fast.rb +164 -0
  124. data/test/perf_file.rb +64 -0
  125. data/test/perf_object.rb +138 -0
  126. data/test/perf_saj.rb +109 -0
  127. data/test/perf_scp.rb +151 -0
  128. data/test/perf_simple.rb +287 -0
  129. data/test/perf_strict.rb +145 -0
  130. data/test/perf_wab.rb +131 -0
  131. data/test/prec.rb +23 -0
  132. data/test/sample.rb +54 -0
  133. data/test/sample/change.rb +14 -0
  134. data/test/sample/dir.rb +19 -0
  135. data/test/sample/doc.rb +36 -0
  136. data/test/sample/file.rb +48 -0
  137. data/test/sample/group.rb +16 -0
  138. data/test/sample/hasprops.rb +16 -0
  139. data/test/sample/layer.rb +12 -0
  140. data/test/sample/line.rb +20 -0
  141. data/test/sample/oval.rb +10 -0
  142. data/test/sample/rect.rb +10 -0
  143. data/test/sample/shape.rb +35 -0
  144. data/test/sample/text.rb +20 -0
  145. data/test/sample_json.rb +37 -0
  146. data/test/test_compat.rb +502 -0
  147. data/test/test_custom.rb +527 -0
  148. data/test/test_debian.rb +53 -0
  149. data/test/test_fast.rb +470 -0
  150. data/test/test_file.rb +239 -0
  151. data/test/test_gc.rb +49 -0
  152. data/test/test_hash.rb +29 -0
  153. data/test/test_integer_range.rb +72 -0
  154. data/test/test_null.rb +376 -0
  155. data/test/test_object.rb +1027 -0
  156. data/test/test_rails.rb +26 -0
  157. data/test/test_saj.rb +186 -0
  158. data/test/test_scp.rb +433 -0
  159. data/test/test_strict.rb +433 -0
  160. data/test/test_various.rb +719 -0
  161. data/test/test_wab.rb +307 -0
  162. data/test/test_writer.rb +380 -0
  163. data/test/tests.rb +25 -0
  164. data/test/tests_mimic.rb +14 -0
  165. data/test/tests_mimic_addition.rb +7 -0
  166. data/test/zoo.rb +13 -0
  167. metadata +381 -0
@@ -0,0 +1,387 @@
1
+ /* oj.h
2
+ * Copyright (c) 2011, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #ifndef OJ_H
7
+ #define OJ_H
8
+
9
+ #if defined(cplusplus)
10
+ extern "C" {
11
+ #if 0
12
+ } /* satisfy cc-mode */
13
+ #endif
14
+ #endif
15
+
16
+ #define RSTRING_NOT_MODIFIED
17
+
18
+ #include "ruby.h"
19
+ #include "ruby/encoding.h"
20
+
21
+ #include <stdint.h>
22
+ #include <stdbool.h>
23
+
24
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
25
+ #include <pthread.h>
26
+ #endif
27
+ #include "cache8.h"
28
+
29
+ #ifdef RUBINIUS_RUBY
30
+ #undef T_RATIONAL
31
+ #undef T_COMPLEX
32
+ enum st_retval {ST_CONTINUE = 0, ST_STOP = 1, ST_DELETE = 2, ST_CHECK};
33
+ #else
34
+ #include "ruby/st.h"
35
+ #endif
36
+
37
+ #include "rxclass.h"
38
+ #include "err.h"
39
+
40
+ #define INF_VAL "3.0e14159265358979323846"
41
+ #define NINF_VAL "-3.0e14159265358979323846"
42
+ #define NAN_VAL "3.3e14159265358979323846"
43
+
44
+ typedef enum {
45
+ Yes = 'y',
46
+ No = 'n',
47
+ NotSet = 0
48
+ } YesNo;
49
+
50
+ typedef enum {
51
+ StrictMode = 's',
52
+ ObjectMode = 'o',
53
+ NullMode = 'n',
54
+ CompatMode = 'c',
55
+ RailsMode = 'r',
56
+ CustomMode = 'C',
57
+ WabMode = 'w',
58
+ } Mode;
59
+
60
+ typedef enum {
61
+ UnixTime = 'u',
62
+ UnixZTime = 'z',
63
+ XmlTime = 'x',
64
+ RubyTime = 'r'
65
+ } TimeFormat;
66
+
67
+ typedef enum {
68
+ NLEsc = 'n',
69
+ JSONEsc = 'j',
70
+ XSSEsc = 'x',
71
+ ASCIIEsc = 'a',
72
+ JXEsc = 'g', // json gem
73
+ RailsXEsc = 'r', // rails xss mode
74
+ RailsEsc = 'R', // rails non escape
75
+ } Encoding;
76
+
77
+ typedef enum {
78
+ BigDec = 'b',
79
+ FloatDec = 'f',
80
+ AutoDec = 'a'
81
+ } BigLoad;
82
+
83
+ typedef enum {
84
+ ArrayNew = 'A',
85
+ ArrayType = 'a',
86
+ ObjectNew = 'O',
87
+ ObjectType = 'o',
88
+ } DumpType;
89
+
90
+ typedef enum {
91
+ AutoNan = 'a',
92
+ NullNan = 'n',
93
+ HugeNan = 'h',
94
+ WordNan = 'w',
95
+ RaiseNan = 'r',
96
+ } NanDump;
97
+
98
+ typedef enum {
99
+ STRING_IO = 'c',
100
+ STREAM_IO = 's',
101
+ FILE_IO = 'f',
102
+ } StreamWriterType;
103
+
104
+ typedef enum {
105
+ CALLER_DUMP = 'd',
106
+ CALLER_TO_JSON = 't',
107
+ CALLER_GENERATE = 'g',
108
+ // Add the fast versions if necessary. Maybe unparse as well if needed.
109
+ } DumpCaller;
110
+
111
+ typedef struct _dumpOpts {
112
+ bool use;
113
+ char indent_str[16];
114
+ char before_sep[16];
115
+ char after_sep[16];
116
+ char hash_nl[16];
117
+ char array_nl[16];
118
+ uint8_t indent_size;
119
+ uint8_t before_size;
120
+ uint8_t after_size;
121
+ uint8_t hash_size;
122
+ uint8_t array_size;
123
+ char nan_dump; // NanDump
124
+ bool omit_nil;
125
+ int max_depth;
126
+ } *DumpOpts;
127
+
128
+ typedef struct _options {
129
+ int indent; // indention for dump, default 2
130
+ char circular; // YesNo
131
+ char auto_define; // YesNo
132
+ char sym_key; // YesNo
133
+ char escape_mode; // Escape_Mode
134
+ char mode; // Mode
135
+ char class_cache; // YesNo
136
+ char time_format; // TimeFormat
137
+ char bigdec_as_num; // YesNo
138
+ char bigdec_load; // BigLoad
139
+ char to_hash; // YesNo
140
+ char to_json; // YesNo
141
+ char as_json; // YesNo
142
+ char raw_json; // YesNo
143
+ char nilnil; // YesNo
144
+ char empty_string; // YesNo
145
+ char allow_gc; // allow GC during parse
146
+ char quirks_mode; // allow single JSON values instead of documents
147
+ char allow_invalid; // YesNo - allow invalid unicode
148
+ char create_ok; // YesNo allow create_id
149
+ char allow_nan; // YEsyNo for parsing only
150
+ char trace; // YesNo
151
+ char safe; // YesNo
152
+ char sec_prec_set; // boolean (0 or 1)
153
+ char ignore_under; // YesNo - ignore attrs starting with _ if true in object and custom modes
154
+ int64_t int_range_min; // dump numbers below as string
155
+ int64_t int_range_max; // dump numbers above as string
156
+ const char *create_id; // 0 or string
157
+ size_t create_id_len; // length of create_id
158
+ int sec_prec; // second precision when dumping time
159
+ char float_prec; // float precision, linked to float_fmt
160
+ char float_fmt[7]; // float format for dumping, if empty use Ruby
161
+ VALUE hash_class; // class to use in place of Hash on load
162
+ VALUE array_class; // class to use in place of Array on load
163
+ struct _dumpOpts dump_opts;
164
+ struct _rxClass str_rx;
165
+ VALUE *ignore; // Qnil terminated array of classes or NULL
166
+ } *Options;
167
+
168
+ struct _out;
169
+ typedef void (*DumpFunc)(VALUE obj, int depth, struct _out *out, bool as_ok);
170
+
171
+ // rails optimize
172
+ typedef struct _rOpt {
173
+ VALUE clas;
174
+ bool on;
175
+ DumpFunc dump;
176
+ } *ROpt;
177
+
178
+ typedef struct _rOptTable {
179
+ int len;
180
+ int alen;
181
+ ROpt table;
182
+ } *ROptTable;
183
+
184
+ typedef struct _out {
185
+ char *buf;
186
+ char *end;
187
+ char *cur;
188
+ Cache8 circ_cache;
189
+ slot_t circ_cnt;
190
+ int indent;
191
+ int depth; // used by dump_hash
192
+ Options opts;
193
+ uint32_t hash_cnt;
194
+ bool allocated;
195
+ bool omit_nil;
196
+ int argc;
197
+ VALUE *argv;
198
+ DumpCaller caller; // used for the mimic json only
199
+ ROptTable ropts;
200
+ } *Out;
201
+
202
+ typedef struct _strWriter {
203
+ struct _out out;
204
+ struct _options opts;
205
+ int depth;
206
+ char *types; // DumpType
207
+ char *types_end;
208
+ int keyWritten;
209
+
210
+ } *StrWriter;
211
+
212
+ typedef struct _streamWriter {
213
+ struct _strWriter sw;
214
+ StreamWriterType type;
215
+ VALUE stream;
216
+ int fd;
217
+ int flush_limit; // indicator of when to flush
218
+ } *StreamWriter;
219
+
220
+ enum {
221
+ NO_VAL = 0x00,
222
+ STR_VAL = 0x01,
223
+ COL_VAL = 0x02,
224
+ RUBY_VAL = 0x03
225
+ };
226
+
227
+ typedef struct _leaf {
228
+ struct _leaf *next;
229
+ union {
230
+ const char *key; // hash key
231
+ size_t index; // array index, 0 is not set
232
+ };
233
+ union {
234
+ char *str; // pointer to location in json string or allocated
235
+ struct _leaf *elements; // array and hash elements
236
+ VALUE value;
237
+ };
238
+ uint8_t rtype;
239
+ uint8_t parent_type;
240
+ uint8_t value_type;
241
+ } *Leaf;
242
+
243
+ extern VALUE oj_saj_parse(int argc, VALUE *argv, VALUE self);
244
+ extern VALUE oj_sc_parse(int argc, VALUE *argv, VALUE self);
245
+
246
+ extern VALUE oj_strict_parse(int argc, VALUE *argv, VALUE self);
247
+ extern VALUE oj_strict_sparse(int argc, VALUE *argv, VALUE self);
248
+ extern VALUE oj_compat_parse(int argc, VALUE *argv, VALUE self);
249
+ extern VALUE oj_compat_load(int argc, VALUE *argv, VALUE self);
250
+ extern VALUE oj_object_parse(int argc, VALUE *argv, VALUE self);
251
+ extern VALUE oj_custom_parse(int argc, VALUE *argv, VALUE self);
252
+ extern VALUE oj_wab_parse(int argc, VALUE *argv, VALUE self);
253
+
254
+ extern VALUE oj_strict_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
255
+ extern VALUE oj_compat_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
256
+ extern VALUE oj_object_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
257
+ extern VALUE oj_custom_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
258
+
259
+ extern void oj_parse_options(VALUE ropts, Options copts);
260
+
261
+ extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
262
+ extern void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
263
+ extern void oj_write_obj_to_file(VALUE obj, const char *path, Options copts);
264
+ extern void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts);
265
+ extern void oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out);
266
+ extern void oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts);
267
+
268
+ extern void oj_str_writer_push_key(StrWriter sw, const char *key);
269
+ extern void oj_str_writer_push_object(StrWriter sw, const char *key);
270
+ extern void oj_str_writer_push_array(StrWriter sw, const char *key);
271
+ extern void oj_str_writer_push_value(StrWriter sw, VALUE val, const char *key);
272
+ extern void oj_str_writer_push_json(StrWriter sw, const char *json, const char *key);
273
+ extern void oj_str_writer_pop(StrWriter sw);
274
+ extern void oj_str_writer_pop_all(StrWriter sw);
275
+
276
+ extern void oj_init_doc(void);
277
+ extern void oj_string_writer_init();
278
+ extern void oj_stream_writer_init();
279
+ extern void oj_str_writer_init(StrWriter sw, int buf_size);
280
+ extern VALUE oj_define_mimic_json(int argc, VALUE *argv, VALUE self);
281
+ extern VALUE oj_mimic_generate(int argc, VALUE *argv, VALUE self);
282
+ extern VALUE oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self);
283
+ extern void oj_parse_mimic_dump_options(VALUE ropts, Options copts);
284
+
285
+ extern VALUE oj_mimic_parse(int argc, VALUE *argv, VALUE self);
286
+ extern VALUE oj_get_json_err_class(const char *err_classname);
287
+ extern void oj_parse_opt_match_string(RxClass rc, VALUE ropts);
288
+
289
+ extern VALUE oj_rails_encode(int argc, VALUE *argv, VALUE self);
290
+
291
+ extern VALUE Oj;
292
+ extern struct _options oj_default_options;
293
+ extern rb_encoding *oj_utf8_encoding;
294
+
295
+ extern VALUE oj_bag_class;
296
+ extern VALUE oj_bigdecimal_class;
297
+ extern VALUE oj_cstack_class;
298
+ extern VALUE oj_date_class;
299
+ extern VALUE oj_datetime_class;
300
+ extern VALUE oj_doc_class;
301
+ extern VALUE oj_enumerable_class;
302
+ extern VALUE oj_json_generator_error_class;
303
+ extern VALUE oj_json_parser_error_class;
304
+ extern VALUE oj_stream_writer_class;
305
+ extern VALUE oj_string_writer_class;
306
+ extern VALUE oj_stringio_class;
307
+ extern VALUE oj_struct_class;
308
+
309
+ extern VALUE oj_allow_nan_sym;
310
+ extern VALUE oj_array_class_sym;
311
+ extern VALUE oj_array_nl_sym;
312
+ extern VALUE oj_ascii_only_sym;
313
+ extern VALUE oj_create_additions_sym;
314
+ extern VALUE oj_hash_class_sym;
315
+ extern VALUE oj_indent_sym;
316
+ extern VALUE oj_max_nesting_sym;
317
+ extern VALUE oj_object_class_sym;
318
+ extern VALUE oj_object_nl_sym;
319
+ extern VALUE oj_quirks_mode_sym;
320
+ extern VALUE oj_space_before_sym;
321
+ extern VALUE oj_space_sym;
322
+ extern VALUE oj_trace_sym;
323
+
324
+ extern VALUE oj_slash_string;
325
+
326
+ extern ID oj_add_value_id;
327
+ extern ID oj_array_append_id;
328
+ extern ID oj_array_end_id;
329
+ extern ID oj_array_start_id;
330
+ extern ID oj_as_json_id;
331
+ extern ID oj_begin_id;
332
+ extern ID oj_bigdecimal_id;
333
+ extern ID oj_end_id;
334
+ extern ID oj_error_id;
335
+ extern ID oj_exclude_end_id;
336
+ extern ID oj_file_id;
337
+ extern ID oj_fileno_id;
338
+ extern ID oj_ftype_id;
339
+ extern ID oj_has_key_id;
340
+ extern ID oj_hash_end_id;
341
+ extern ID oj_hash_key_id;
342
+ extern ID oj_hash_set_id;
343
+ extern ID oj_hash_start_id;
344
+ extern ID oj_iconv_id;
345
+ extern ID oj_instance_variables_id;
346
+ extern ID oj_json_create_id;
347
+ extern ID oj_length_id;
348
+ extern ID oj_new_id;
349
+ extern ID oj_parse_id;
350
+ extern ID oj_pos_id;
351
+ extern ID oj_read_id;
352
+ extern ID oj_readpartial_id;
353
+ extern ID oj_replace_id;
354
+ extern ID oj_stat_id;
355
+ extern ID oj_string_id;
356
+ extern ID oj_raw_json_id;
357
+ extern ID oj_to_h_id;
358
+ extern ID oj_to_hash_id;
359
+ extern ID oj_to_json_id;
360
+ extern ID oj_to_s_id;
361
+ extern ID oj_to_sym_id;
362
+ extern ID oj_to_time_id;
363
+ extern ID oj_tv_nsec_id;
364
+ extern ID oj_tv_sec_id;
365
+ extern ID oj_tv_usec_id;
366
+ extern ID oj_utc_id;
367
+ extern ID oj_utc_offset_id;
368
+ extern ID oj_utcq_id;
369
+ extern ID oj_write_id;
370
+
371
+ extern bool oj_use_hash_alt;
372
+ extern bool oj_use_array_alt;
373
+ extern bool string_writer_optimized;
374
+
375
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
376
+ extern pthread_mutex_t oj_cache_mutex;
377
+ #else
378
+ extern VALUE oj_cache_mutex;
379
+ #endif
380
+
381
+ #if defined(cplusplus)
382
+ #if 0
383
+ { /* satisfy cc-mode */
384
+ #endif
385
+ } /* extern "C" { */
386
+ #endif
387
+ #endif /* OJ_H */
@@ -0,0 +1,1134 @@
1
+ /* parse.c
2
+ * Copyright (c) 2013, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include <stdlib.h>
7
+ #include <stdio.h>
8
+ #include <string.h>
9
+ #include <unistd.h>
10
+ #include <math.h>
11
+ #include <ruby/util.h>
12
+
13
+ #include "oj.h"
14
+ #include "encode.h"
15
+ #include "parse.h"
16
+ #include "buf.h"
17
+ #include "val_stack.h"
18
+ #include "rxclass.h"
19
+
20
+ // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
21
+ #define OJ_INFINITY (1.0/0.0)
22
+
23
+ //#define EXP_MAX 1023
24
+ #define EXP_MAX 100000
25
+ #define DEC_MAX 15
26
+
27
+ static void
28
+ next_non_white(ParseInfo pi) {
29
+ for (; 1; pi->cur++) {
30
+ switch(*pi->cur) {
31
+ case ' ':
32
+ case '\t':
33
+ case '\f':
34
+ case '\n':
35
+ case '\r':
36
+ break;
37
+ default:
38
+ return;
39
+ }
40
+ }
41
+ }
42
+
43
+ static void
44
+ skip_comment(ParseInfo pi) {
45
+ if ('*' == *pi->cur) {
46
+ pi->cur++;
47
+ for (; pi->cur < pi->end; pi->cur++) {
48
+ if ('*' == *pi->cur && '/' == *(pi->cur + 1)) {
49
+ pi->cur += 2;
50
+ return;
51
+ } else if (pi->end <= pi->cur) {
52
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "comment not terminated");
53
+ return;
54
+ }
55
+ }
56
+ } else if ('/' == *pi->cur) {
57
+ for (; 1; pi->cur++) {
58
+ switch (*pi->cur) {
59
+ case '\n':
60
+ case '\r':
61
+ case '\f':
62
+ case '\0':
63
+ return;
64
+ default:
65
+ break;
66
+ }
67
+ }
68
+ } else {
69
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid comment format");
70
+ }
71
+ }
72
+
73
+ static void
74
+ add_value(ParseInfo pi, VALUE rval) {
75
+ Val parent = stack_peek(&pi->stack);
76
+
77
+ if (0 == parent) { // simple add
78
+ pi->add_value(pi, rval);
79
+ } else {
80
+ switch (parent->next) {
81
+ case NEXT_ARRAY_NEW:
82
+ case NEXT_ARRAY_ELEMENT:
83
+ pi->array_append_value(pi, rval);
84
+ parent->next = NEXT_ARRAY_COMMA;
85
+ break;
86
+ case NEXT_HASH_VALUE:
87
+ pi->hash_set_value(pi, parent, rval);
88
+ if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
89
+ xfree((char*)parent->key);
90
+ parent->key = 0;
91
+ }
92
+ parent->next = NEXT_HASH_COMMA;
93
+ break;
94
+ case NEXT_HASH_NEW:
95
+ case NEXT_HASH_KEY:
96
+ case NEXT_HASH_COMMA:
97
+ case NEXT_NONE:
98
+ case NEXT_ARRAY_COMMA:
99
+ case NEXT_HASH_COLON:
100
+ default:
101
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next));
102
+ break;
103
+ }
104
+ }
105
+ }
106
+
107
+ static void
108
+ read_null(ParseInfo pi) {
109
+ if ('u' == *pi->cur++ && 'l' == *pi->cur++ && 'l' == *pi->cur++) {
110
+ add_value(pi, Qnil);
111
+ } else {
112
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected null");
113
+ }
114
+ }
115
+
116
+ static void
117
+ read_true(ParseInfo pi) {
118
+ if ('r' == *pi->cur++ && 'u' == *pi->cur++ && 'e' == *pi->cur++) {
119
+ add_value(pi, Qtrue);
120
+ } else {
121
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected true");
122
+ }
123
+ }
124
+
125
+ static void
126
+ read_false(ParseInfo pi) {
127
+ if ('a' == *pi->cur++ && 'l' == *pi->cur++ && 's' == *pi->cur++ && 'e' == *pi->cur++) {
128
+ add_value(pi, Qfalse);
129
+ } else {
130
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected false");
131
+ }
132
+ }
133
+
134
+ static uint32_t
135
+ read_hex(ParseInfo pi, const char *h) {
136
+ uint32_t b = 0;
137
+ int i;
138
+
139
+ for (i = 0; i < 4; i++, h++) {
140
+ b = b << 4;
141
+ if ('0' <= *h && *h <= '9') {
142
+ b += *h - '0';
143
+ } else if ('A' <= *h && *h <= 'F') {
144
+ b += *h - 'A' + 10;
145
+ } else if ('a' <= *h && *h <= 'f') {
146
+ b += *h - 'a' + 10;
147
+ } else {
148
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hex character");
149
+ return 0;
150
+ }
151
+ }
152
+ return b;
153
+ }
154
+
155
+ static void
156
+ unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
157
+ if (0x0000007F >= code) {
158
+ buf_append(buf, (char)code);
159
+ } else if (0x000007FF >= code) {
160
+ buf_append(buf, 0xC0 | (code >> 6));
161
+ buf_append(buf, 0x80 | (0x3F & code));
162
+ } else if (0x0000FFFF >= code) {
163
+ buf_append(buf, 0xE0 | (code >> 12));
164
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
165
+ buf_append(buf, 0x80 | (0x3F & code));
166
+ } else if (0x001FFFFF >= code) {
167
+ buf_append(buf, 0xF0 | (code >> 18));
168
+ buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
169
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
170
+ buf_append(buf, 0x80 | (0x3F & code));
171
+ } else if (0x03FFFFFF >= code) {
172
+ buf_append(buf, 0xF8 | (code >> 24));
173
+ buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
174
+ buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
175
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
176
+ buf_append(buf, 0x80 | (0x3F & code));
177
+ } else if (0x7FFFFFFF >= code) {
178
+ buf_append(buf, 0xFC | (code >> 30));
179
+ buf_append(buf, 0x80 | ((code >> 24) & 0x3F));
180
+ buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
181
+ buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
182
+ buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
183
+ buf_append(buf, 0x80 | (0x3F & code));
184
+ } else {
185
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid Unicode character");
186
+ }
187
+ }
188
+
189
+ // entered at /
190
+ static void
191
+ read_escaped_str(ParseInfo pi, const char *start) {
192
+ struct _buf buf;
193
+ const char *s;
194
+ int cnt = (int)(pi->cur - start);
195
+ uint32_t code;
196
+ Val parent = stack_peek(&pi->stack);
197
+
198
+ buf_init(&buf);
199
+ if (0 < cnt) {
200
+ buf_append_string(&buf, start, cnt);
201
+ }
202
+ for (s = pi->cur; '"' != *s; s++) {
203
+ if (s >= pi->end) {
204
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
205
+ buf_cleanup(&buf);
206
+ return;
207
+ } else if ('\\' == *s) {
208
+ s++;
209
+ switch (*s) {
210
+ case 'n': buf_append(&buf, '\n'); break;
211
+ case 'r': buf_append(&buf, '\r'); break;
212
+ case 't': buf_append(&buf, '\t'); break;
213
+ case 'f': buf_append(&buf, '\f'); break;
214
+ case 'b': buf_append(&buf, '\b'); break;
215
+ case '"': buf_append(&buf, '"'); break;
216
+ case '/': buf_append(&buf, '/'); break;
217
+ case '\\': buf_append(&buf, '\\'); break;
218
+ case '\'':
219
+ // The json gem claims this is not an error despite the
220
+ // ECMA-404 indicating it is not valid.
221
+ if (CompatMode == pi->options.mode) {
222
+ buf_append(&buf, '\'');
223
+ } else {
224
+ pi->cur = s;
225
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
226
+ buf_cleanup(&buf);
227
+ return;
228
+ }
229
+ break;
230
+ case 'u':
231
+ s++;
232
+ if (0 == (code = read_hex(pi, s)) && err_has(&pi->err)) {
233
+ buf_cleanup(&buf);
234
+ return;
235
+ }
236
+ s += 3;
237
+ if (0x0000D800 <= code && code <= 0x0000DFFF) {
238
+ uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
239
+ uint32_t c2;
240
+
241
+ s++;
242
+ if ('\\' != *s || 'u' != *(s + 1)) {
243
+ if (Yes == pi->options.allow_invalid) {
244
+ s--;
245
+ unicode_to_chars(pi, &buf, code);
246
+ break;
247
+ }
248
+ pi->cur = s;
249
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
250
+ buf_cleanup(&buf);
251
+ return;
252
+ }
253
+ s += 2;
254
+ if (0 == (c2 = read_hex(pi, s)) && err_has(&pi->err)) {
255
+ buf_cleanup(&buf);
256
+ return;
257
+ }
258
+ s += 3;
259
+ c2 = (c2 - 0x0000DC00) & 0x000003FF;
260
+ code = ((c1 << 10) | c2) + 0x00010000;
261
+ }
262
+ unicode_to_chars(pi, &buf, code);
263
+ if (err_has(&pi->err)) {
264
+ buf_cleanup(&buf);
265
+ return;
266
+ }
267
+ break;
268
+ default:
269
+ pi->cur = s;
270
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
271
+ buf_cleanup(&buf);
272
+ return;
273
+ }
274
+ } else {
275
+ buf_append(&buf, *s);
276
+ }
277
+ }
278
+ if (0 == parent) {
279
+ pi->add_cstr(pi, buf.head, buf_len(&buf), start);
280
+ } else {
281
+ switch (parent->next) {
282
+ case NEXT_ARRAY_NEW:
283
+ case NEXT_ARRAY_ELEMENT:
284
+ pi->array_append_cstr(pi, buf.head, buf_len(&buf), start);
285
+ parent->next = NEXT_ARRAY_COMMA;
286
+ break;
287
+ case NEXT_HASH_NEW:
288
+ case NEXT_HASH_KEY:
289
+ if (Qundef == (parent->key_val = pi->hash_key(pi, buf.head, buf_len(&buf)))) {
290
+ parent->klen = buf_len(&buf);
291
+ parent->key = malloc(parent->klen + 1);
292
+ memcpy((char*)parent->key, buf.head, parent->klen);
293
+ *(char*)(parent->key + parent->klen) = '\0';
294
+ } else {
295
+ parent->key = "";
296
+ parent->klen = 0;
297
+ }
298
+ parent->k1 = *start;
299
+ parent->next = NEXT_HASH_COLON;
300
+ break;
301
+ case NEXT_HASH_VALUE:
302
+ pi->hash_set_cstr(pi, parent, buf.head, buf_len(&buf), start);
303
+ if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
304
+ xfree((char*)parent->key);
305
+ parent->key = 0;
306
+ }
307
+ parent->next = NEXT_HASH_COMMA;
308
+ break;
309
+ case NEXT_HASH_COMMA:
310
+ case NEXT_NONE:
311
+ case NEXT_ARRAY_COMMA:
312
+ case NEXT_HASH_COLON:
313
+ default:
314
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next));
315
+ break;
316
+ }
317
+ }
318
+ pi->cur = s + 1;
319
+ buf_cleanup(&buf);
320
+ }
321
+
322
+ static void
323
+ read_str(ParseInfo pi) {
324
+ const char *str = pi->cur;
325
+ Val parent = stack_peek(&pi->stack);
326
+
327
+ for (; '"' != *pi->cur; pi->cur++) {
328
+ if (pi->end <= pi->cur) {
329
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
330
+ return;
331
+ } else if ('\0' == *pi->cur) {
332
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "NULL byte in string");
333
+ return;
334
+ } else if ('\\' == *pi->cur) {
335
+ read_escaped_str(pi, str);
336
+ return;
337
+ }
338
+ }
339
+ if (0 == parent) { // simple add
340
+ pi->add_cstr(pi, str, pi->cur - str, str);
341
+ } else {
342
+ switch (parent->next) {
343
+ case NEXT_ARRAY_NEW:
344
+ case NEXT_ARRAY_ELEMENT:
345
+ pi->array_append_cstr(pi, str, pi->cur - str, str);
346
+ parent->next = NEXT_ARRAY_COMMA;
347
+ break;
348
+ case NEXT_HASH_NEW:
349
+ case NEXT_HASH_KEY:
350
+ if (Qundef == (parent->key_val = pi->hash_key(pi, str, pi->cur - str))) {
351
+ parent->key = str;
352
+ parent->klen = pi->cur - str;
353
+ } else {
354
+ parent->key = "";
355
+ parent->klen = 0;
356
+ }
357
+ parent->k1 = *str;
358
+ parent->next = NEXT_HASH_COLON;
359
+ break;
360
+ case NEXT_HASH_VALUE:
361
+ pi->hash_set_cstr(pi, parent, str, pi->cur - str, str);
362
+ if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
363
+ xfree((char*)parent->key);
364
+ parent->key = 0;
365
+ }
366
+ parent->next = NEXT_HASH_COMMA;
367
+ break;
368
+ case NEXT_HASH_COMMA:
369
+ case NEXT_NONE:
370
+ case NEXT_ARRAY_COMMA:
371
+ case NEXT_HASH_COLON:
372
+ default:
373
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next));
374
+ break;
375
+ }
376
+ }
377
+ pi->cur++; // move past "
378
+ }
379
+
380
+ static void
381
+ read_num(ParseInfo pi) {
382
+ struct _numInfo ni;
383
+ Val parent = stack_peek(&pi->stack);
384
+
385
+ ni.str = pi->cur;
386
+ ni.i = 0;
387
+ ni.num = 0;
388
+ ni.div = 1;
389
+ ni.di = 0;
390
+ ni.len = 0;
391
+ ni.exp = 0;
392
+ ni.big = 0;
393
+ ni.infinity = 0;
394
+ ni.nan = 0;
395
+ ni.neg = 0;
396
+ ni.hasExp = 0;
397
+ ni.no_big = (FloatDec == pi->options.bigdec_load);
398
+
399
+ if ('-' == *pi->cur) {
400
+ pi->cur++;
401
+ ni.neg = 1;
402
+ } else if ('+' == *pi->cur) {
403
+ if (StrictMode == pi->options.mode) {
404
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
405
+ return;
406
+ }
407
+ pi->cur++;
408
+ }
409
+ if ('I' == *pi->cur) {
410
+ if (No == pi->options.allow_nan || 0 != strncmp("Infinity", pi->cur, 8)) {
411
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
412
+ return;
413
+ }
414
+ pi->cur += 8;
415
+ ni.infinity = 1;
416
+ } else if ('N' == *pi->cur || 'n' == *pi->cur) {
417
+ if ('a' != pi->cur[1] || ('N' != pi->cur[2] && 'n' != pi->cur[2])) {
418
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
419
+ return;
420
+ }
421
+ pi->cur += 3;
422
+ ni.nan = 1;
423
+ } else {
424
+ int dec_cnt = 0;
425
+ bool zero1 = false;
426
+
427
+ for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
428
+ if (0 == ni.i && '0' == *pi->cur) {
429
+ zero1 = true;
430
+ }
431
+ if (0 < ni.i) {
432
+ dec_cnt++;
433
+ }
434
+ if (!ni.big) {
435
+ int d = (*pi->cur - '0');
436
+
437
+ if (0 < d) {
438
+ if (zero1 && CompatMode == pi->options.mode) {
439
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
440
+ return;
441
+ }
442
+ zero1 = false;
443
+ }
444
+ ni.i = ni.i * 10 + d;
445
+ if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
446
+ ni.big = 1;
447
+ }
448
+ }
449
+ }
450
+ if ('.' == *pi->cur) {
451
+ pi->cur++;
452
+ // A trailing . is not a valid decimal but if encountered allow it
453
+ // except when mimicing the JSON gem or in strict mode.
454
+ if (StrictMode == pi->options.mode || CompatMode == pi->options.mode) {
455
+ int pos = (int)(pi->cur - ni.str);
456
+
457
+ if (1 == pos || (2 == pos && ni.neg)) {
458
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
459
+ return;
460
+ }
461
+ if (*pi->cur < '0' || '9' < *pi->cur) {
462
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
463
+ return;
464
+ }
465
+ }
466
+ for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
467
+ int d = (*pi->cur - '0');
468
+
469
+ if (0 < ni.num || 0 < ni.i) {
470
+ dec_cnt++;
471
+ }
472
+ if (INT64_MAX <= ni.div) {
473
+ if (!ni.no_big) {
474
+ ni.big = true;
475
+ }
476
+ } else {
477
+ ni.num = ni.num * 10 + d;
478
+ ni.div *= 10;
479
+ ni.di++;
480
+ if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
481
+ if (!ni.no_big) {
482
+ ni.big = true;
483
+ }
484
+ }
485
+ }
486
+ }
487
+ }
488
+ if ('e' == *pi->cur || 'E' == *pi->cur) {
489
+ int eneg = 0;
490
+
491
+ ni.hasExp = 1;
492
+ pi->cur++;
493
+ if ('-' == *pi->cur) {
494
+ pi->cur++;
495
+ eneg = 1;
496
+ } else if ('+' == *pi->cur) {
497
+ pi->cur++;
498
+ }
499
+ for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
500
+ ni.exp = ni.exp * 10 + (*pi->cur - '0');
501
+ if (EXP_MAX <= ni.exp) {
502
+ ni.big = true;
503
+ }
504
+ }
505
+ if (eneg) {
506
+ ni.exp = -ni.exp;
507
+ }
508
+ }
509
+ ni.len = pi->cur - ni.str;
510
+ }
511
+ // Check for special reserved values for Infinity and NaN.
512
+ if (ni.big) {
513
+ if (0 == strcasecmp(INF_VAL, ni.str)) {
514
+ ni.infinity = 1;
515
+ } else if (0 == strcasecmp(NINF_VAL, ni.str)) {
516
+ ni.infinity = 1;
517
+ ni.neg = 1;
518
+ } else if (0 == strcasecmp(NAN_VAL, ni.str)) {
519
+ ni.nan = 1;
520
+ }
521
+ }
522
+ if (BigDec == pi->options.bigdec_load) {
523
+ ni.big = 1;
524
+ }
525
+ if (0 == parent) {
526
+ pi->add_num(pi, &ni);
527
+ } else {
528
+ switch (parent->next) {
529
+ case NEXT_ARRAY_NEW:
530
+ case NEXT_ARRAY_ELEMENT:
531
+ pi->array_append_num(pi, &ni);
532
+ parent->next = NEXT_ARRAY_COMMA;
533
+ break;
534
+ case NEXT_HASH_VALUE:
535
+ pi->hash_set_num(pi, parent, &ni);
536
+ if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
537
+ xfree((char*)parent->key);
538
+ parent->key = 0;
539
+ }
540
+ parent->next = NEXT_HASH_COMMA;
541
+ break;
542
+ default:
543
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next));
544
+ break;
545
+ }
546
+ }
547
+ }
548
+
549
+ static void
550
+ array_start(ParseInfo pi) {
551
+ volatile VALUE v = pi->start_array(pi);
552
+
553
+ stack_push(&pi->stack, v, NEXT_ARRAY_NEW);
554
+ }
555
+
556
+ static void
557
+ array_end(ParseInfo pi) {
558
+ Val array = stack_pop(&pi->stack);
559
+
560
+ if (0 == array) {
561
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected array close");
562
+ } else if (NEXT_ARRAY_COMMA != array->next && NEXT_ARRAY_NEW != array->next) {
563
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not an array close", oj_stack_next_string(array->next));
564
+ } else {
565
+ pi->end_array(pi);
566
+ add_value(pi, array->val);
567
+ }
568
+ }
569
+
570
+ static void
571
+ hash_start(ParseInfo pi) {
572
+ volatile VALUE v = pi->start_hash(pi);
573
+
574
+ stack_push(&pi->stack, v, NEXT_HASH_NEW);
575
+ }
576
+
577
+ static void
578
+ hash_end(ParseInfo pi) {
579
+ volatile Val hash = stack_peek(&pi->stack);
580
+
581
+ // leave hash on stack until just before
582
+ if (0 == hash) {
583
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected hash close");
584
+ } else if (NEXT_HASH_COMMA != hash->next && NEXT_HASH_NEW != hash->next) {
585
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a hash close", oj_stack_next_string(hash->next));
586
+ } else {
587
+ pi->end_hash(pi);
588
+ stack_pop(&pi->stack);
589
+ add_value(pi, hash->val);
590
+ }
591
+ }
592
+
593
+ static void
594
+ comma(ParseInfo pi) {
595
+ Val parent = stack_peek(&pi->stack);
596
+
597
+ if (0 == parent) {
598
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
599
+ } else if (NEXT_ARRAY_COMMA == parent->next) {
600
+ parent->next = NEXT_ARRAY_ELEMENT;
601
+ } else if (NEXT_HASH_COMMA == parent->next) {
602
+ parent->next = NEXT_HASH_KEY;
603
+ } else {
604
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
605
+ }
606
+ }
607
+
608
+ static void
609
+ colon(ParseInfo pi) {
610
+ Val parent = stack_peek(&pi->stack);
611
+
612
+ if (0 != parent && NEXT_HASH_COLON == parent->next) {
613
+ parent->next = NEXT_HASH_VALUE;
614
+ } else {
615
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected colon");
616
+ }
617
+ }
618
+
619
+ void
620
+ oj_parse2(ParseInfo pi) {
621
+ int first = 1;
622
+ long start = 0;
623
+
624
+ pi->cur = pi->json;
625
+ err_init(&pi->err);
626
+ while (1) {
627
+ if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
628
+ VALUE err_clas = oj_get_json_err_class("NestingError");
629
+
630
+ oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
631
+ pi->err_class = err_clas;
632
+ return;
633
+ }
634
+ next_non_white(pi);
635
+ if (!first && '\0' != *pi->cur) {
636
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected characters after the JSON document");
637
+ }
638
+
639
+ // If no tokens are consumed (i.e. empty string), throw a parse error
640
+ // this is the behavior of JSON.parse in both Ruby and JS.
641
+ if (No == pi->options.empty_string && 1 == first && '\0' == *pi->cur) {
642
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
643
+ }
644
+
645
+ switch (*pi->cur++) {
646
+ case '{':
647
+ hash_start(pi);
648
+ break;
649
+ case '}':
650
+ hash_end(pi);
651
+ break;
652
+ case ':':
653
+ colon(pi);
654
+ break;
655
+ case '[':
656
+ array_start(pi);
657
+ break;
658
+ case ']':
659
+ array_end(pi);
660
+ break;
661
+ case ',':
662
+ comma(pi);
663
+ break;
664
+ case '"':
665
+ read_str(pi);
666
+ break;
667
+ //case '+':
668
+ case '+':
669
+ if (CompatMode == pi->options.mode) {
670
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
671
+ return;
672
+ }
673
+ pi->cur--;
674
+ read_num(pi);
675
+ break;
676
+ case '-':
677
+ case '0':
678
+ case '1':
679
+ case '2':
680
+ case '3':
681
+ case '4':
682
+ case '5':
683
+ case '6':
684
+ case '7':
685
+ case '8':
686
+ case '9':
687
+ pi->cur--;
688
+ read_num(pi);
689
+ break;
690
+ case 'I':
691
+ case 'N':
692
+ if (Yes == pi->options.allow_nan) {
693
+ pi->cur--;
694
+ read_num(pi);
695
+ } else {
696
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
697
+ }
698
+ break;
699
+ case 't':
700
+ read_true(pi);
701
+ break;
702
+ case 'f':
703
+ read_false(pi);
704
+ break;
705
+ case 'n':
706
+ if ('u' == *pi->cur) {
707
+ read_null(pi);
708
+ } else {
709
+ pi->cur--;
710
+ read_num(pi);
711
+ }
712
+ break;
713
+ case '/':
714
+ skip_comment(pi);
715
+ if (first) {
716
+ continue;
717
+ }
718
+ break;
719
+ case '\0':
720
+ pi->cur--;
721
+ return;
722
+ default:
723
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
724
+ return;
725
+ }
726
+ if (err_has(&pi->err)) {
727
+ return;
728
+ }
729
+ if (stack_empty(&pi->stack)) {
730
+ if (Qundef != pi->proc) {
731
+ VALUE args[3];
732
+ long len = (pi->cur - pi->json) - start;
733
+
734
+ *args = stack_head_val(&pi->stack);
735
+ args[1] = LONG2NUM(start);
736
+ args[2] = LONG2NUM(len);
737
+
738
+ if (Qnil == pi->proc) {
739
+ rb_yield_values2(3, args);
740
+ } else {
741
+ rb_proc_call_with_block(pi->proc, 3, args, Qnil);
742
+ }
743
+ } else if (!pi->has_callbacks) {
744
+ first = 0;
745
+ }
746
+ start = pi->cur - pi->json;
747
+ }
748
+ }
749
+ }
750
+
751
+ static VALUE
752
+ rescue_big_decimal(VALUE str, VALUE ignore) {
753
+ rb_raise(oj_parse_error_class, "Invalid value for BigDecimal()");
754
+ return Qnil;
755
+ }
756
+
757
+ static VALUE
758
+ parse_big_decimal(VALUE str) {
759
+ return rb_funcall(rb_cObject, oj_bigdecimal_id, 1, str);
760
+ }
761
+
762
+ VALUE
763
+ oj_num_as_value(NumInfo ni) {
764
+ volatile VALUE rnum = Qnil;
765
+
766
+ if (ni->infinity) {
767
+ if (ni->neg) {
768
+ rnum = rb_float_new(-OJ_INFINITY);
769
+ } else {
770
+ rnum = rb_float_new(OJ_INFINITY);
771
+ }
772
+ } else if (ni->nan) {
773
+ rnum = rb_float_new(0.0/0.0);
774
+ } else if (1 == ni->div && 0 == ni->exp) { // fixnum
775
+ if (ni->big) {
776
+ if (256 > ni->len) {
777
+ char buf[256];
778
+
779
+ memcpy(buf, ni->str, ni->len);
780
+ buf[ni->len] = '\0';
781
+ rnum = rb_cstr_to_inum(buf, 10, 0);
782
+ } else {
783
+ char *buf = ALLOC_N(char, ni->len + 1);
784
+
785
+ memcpy(buf, ni->str, ni->len);
786
+ buf[ni->len] = '\0';
787
+ rnum = rb_cstr_to_inum(buf, 10, 0);
788
+ xfree(buf);
789
+ }
790
+ } else {
791
+ if (ni->neg) {
792
+ rnum = rb_ll2inum(-ni->i);
793
+ } else {
794
+ rnum = rb_ll2inum(ni->i);
795
+ }
796
+ }
797
+ } else { // decimal
798
+ if (ni->big) {
799
+ volatile VALUE bd = rb_str_new(ni->str, ni->len);
800
+
801
+ rnum = rb_rescue2(parse_big_decimal, bd, rescue_big_decimal, bd, rb_eException, 0);
802
+ if (ni->no_big) {
803
+ rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
804
+ }
805
+ } else {
806
+ // All these machinations are to get rounding to work better.
807
+ long double ld = (long double)ni->i * (long double)ni->div + (long double)ni->num;
808
+ int x = (int)((int64_t)ni->exp - ni->di);
809
+
810
+ // Rounding sometimes cuts off the last digit even if there are only
811
+ // 15 digits. This attempts to fix those few cases where this
812
+ // occurs.
813
+ if ((long double)INT64_MAX > ld && (int64_t)ld != (ni->i * ni->div + ni->num)) {
814
+ volatile VALUE bd = rb_str_new(ni->str, ni->len);
815
+
816
+ rnum = rb_rescue2(parse_big_decimal, bd, rescue_big_decimal, bd, rb_eException, 0);
817
+ if (ni->no_big) {
818
+ rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
819
+ }
820
+ } else {
821
+ double d;
822
+ double d2;
823
+
824
+ ld = roundl(ld);
825
+ // You would expect that staying with a long double would be
826
+ // more accurate but it fails to match what Ruby generates so
827
+ // drop down to a double.
828
+ if (0 < x) {
829
+ d = (double)(ld * powl(10.0, x));
830
+ d2 = (double)ld * pow(10.0, x);
831
+ } else if (0 > x) {
832
+ d = (double)(ld / powl(10.0, -x));
833
+ d2 = (double)ld / pow(10.0, -x);
834
+ } else {
835
+ d = (double)ld;
836
+ d2 = d;
837
+ }
838
+ if (d != d2) {
839
+ volatile VALUE bd = rb_str_new(ni->str, ni->len);
840
+
841
+ rnum = rb_rescue2(parse_big_decimal, bd, rescue_big_decimal, bd, rb_eException, 0);
842
+ if (ni->no_big) {
843
+ rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
844
+ }
845
+ } else {
846
+ if (ni->neg) {
847
+ d = -d;
848
+ }
849
+ rnum = rb_float_new(d);
850
+ }
851
+ }
852
+ }
853
+ }
854
+ return rnum;
855
+ }
856
+
857
+ void
858
+ oj_set_error_at(ParseInfo pi, VALUE err_clas, const char* file, int line, const char *format, ...) {
859
+ va_list ap;
860
+ char msg[256];
861
+ char *p = msg;
862
+ char *end = p + sizeof(msg) - 2;
863
+ char *start;
864
+ Val vp;
865
+
866
+ va_start(ap, format);
867
+ p += vsnprintf(msg, sizeof(msg) - 1, format, ap);
868
+ va_end(ap);
869
+ pi->err.clas = err_clas;
870
+ if (p + 3 < end) {
871
+ *p++ = ' ';
872
+ *p++ = '(';
873
+ *p++ = 'a';
874
+ *p++ = 'f';
875
+ *p++ = 't';
876
+ *p++ = 'e';
877
+ *p++ = 'r';
878
+ *p++ = ' ';
879
+ start = p;
880
+ for (vp = pi->stack.head; vp < pi->stack.tail; vp++) {
881
+ if (end <= p + 1 + vp->klen) {
882
+ break;
883
+ }
884
+ if (NULL != vp->key) {
885
+ if (start < p) {
886
+ *p++ = '.';
887
+ }
888
+ memcpy(p, vp->key, vp->klen);
889
+ p += vp->klen;
890
+ } else {
891
+ if (RUBY_T_ARRAY == rb_type(vp->val)) {
892
+ if (end <= p + 12) {
893
+ break;
894
+ }
895
+ p += snprintf(p, end - p, "[%ld]", RARRAY_LEN(vp->val));
896
+ }
897
+ }
898
+ }
899
+ *p++ = ')';
900
+ }
901
+ *p = '\0';
902
+ if (0 == pi->json) {
903
+ oj_err_set(&pi->err, err_clas, "%s at line %d, column %d [%s:%d]", msg, pi->rd.line, pi->rd.col, file, line);
904
+ } else {
905
+ _oj_err_set_with_location(&pi->err, err_clas, msg, pi->json, pi->cur - 1, file, line);
906
+ }
907
+ }
908
+
909
+ static VALUE
910
+ protect_parse(VALUE pip) {
911
+ oj_parse2((ParseInfo)pip);
912
+
913
+ return Qnil;
914
+ }
915
+
916
+ extern int oj_utf8_index;
917
+
918
+ static void
919
+ oj_pi_set_input_str(ParseInfo pi, volatile VALUE *inputp) {
920
+ rb_encoding *enc = rb_to_encoding(rb_obj_encoding(*inputp));
921
+
922
+ if (rb_utf8_encoding() != enc) {
923
+ *inputp = rb_str_conv_enc(*inputp, enc, rb_utf8_encoding());
924
+ }
925
+ pi->json = rb_string_value_ptr((VALUE*)inputp);
926
+ pi->end = pi->json + RSTRING_LEN(*inputp);
927
+ }
928
+
929
+ VALUE
930
+ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yieldOk) {
931
+ char *buf = 0;
932
+ volatile VALUE input;
933
+ volatile VALUE wrapped_stack;
934
+ volatile VALUE result = Qnil;
935
+ int line = 0;
936
+ int free_json = 0;
937
+
938
+ if (argc < 1) {
939
+ rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
940
+ }
941
+ input = argv[0];
942
+ if (2 <= argc) {
943
+ if (T_HASH == rb_type(argv[1])) {
944
+ oj_parse_options(argv[1], &pi->options);
945
+ } else if (3 <= argc && T_HASH == rb_type(argv[2])) {
946
+ oj_parse_options(argv[2], &pi->options);
947
+ }
948
+ }
949
+ if (yieldOk && rb_block_given_p()) {
950
+ pi->proc = Qnil;
951
+ } else {
952
+ pi->proc = Qundef;
953
+ }
954
+ if (0 != json) {
955
+ pi->json = json;
956
+ pi->end = json + len;
957
+ free_json = 1;
958
+ } else if (T_STRING == rb_type(input)) {
959
+ if (CompatMode == pi->options.mode) {
960
+ if (No == pi->options.nilnil && 0 == RSTRING_LEN(input)) {
961
+ rb_raise(oj_json_parser_error_class, "An empty string is not a valid JSON string.");
962
+ }
963
+ }
964
+ oj_pi_set_input_str(pi, &input);
965
+ } else if (Qnil == input) {
966
+ if (Yes == pi->options.nilnil) {
967
+ return Qnil;
968
+ } else {
969
+ rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
970
+ }
971
+ } else {
972
+ VALUE clas = rb_obj_class(input);
973
+ volatile VALUE s;
974
+
975
+ if (oj_stringio_class == clas) {
976
+ s = rb_funcall2(input, oj_string_id, 0, 0);
977
+ oj_pi_set_input_str(pi, &s);
978
+ #if !IS_WINDOWS
979
+ } else if (rb_cFile == clas && 0 == FIX2INT(rb_funcall(input, oj_pos_id, 0))) {
980
+ int fd = FIX2INT(rb_funcall(input, oj_fileno_id, 0));
981
+ ssize_t cnt;
982
+ size_t len = lseek(fd, 0, SEEK_END);
983
+
984
+ lseek(fd, 0, SEEK_SET);
985
+ buf = ALLOC_N(char, len + 1);
986
+ pi->json = buf;
987
+ pi->end = buf + len;
988
+ if (0 >= (cnt = read(fd, (char*)pi->json, len)) || cnt != (ssize_t)len) {
989
+ if (0 != buf) {
990
+ xfree(buf);
991
+ }
992
+ rb_raise(rb_eIOError, "failed to read from IO Object.");
993
+ }
994
+ ((char*)pi->json)[len] = '\0';
995
+ /* skip UTF-8 BOM if present */
996
+ if (0xEF == (uint8_t)*pi->json && 0xBB == (uint8_t)pi->json[1] && 0xBF == (uint8_t)pi->json[2]) {
997
+ pi->cur += 3;
998
+ }
999
+ #endif
1000
+ } else if (rb_respond_to(input, oj_read_id)) {
1001
+ // use stream parser instead
1002
+ return oj_pi_sparse(argc, argv, pi, 0);
1003
+ } else {
1004
+ rb_raise(rb_eArgError, "parse() expected a String or IO Object.");
1005
+ }
1006
+ }
1007
+ if (Yes == pi->options.circular) {
1008
+ pi->circ_array = oj_circ_array_new();
1009
+ } else {
1010
+ pi->circ_array = 0;
1011
+ }
1012
+ if (No == pi->options.allow_gc) {
1013
+ rb_gc_disable();
1014
+ }
1015
+ // GC can run at any time. When it runs any Object created by C will be
1016
+ // freed. We protect against this by wrapping the value stack in a ruby
1017
+ // data object and poviding a mark function for ruby objects on the
1018
+ // value stack (while it is in scope).
1019
+ wrapped_stack = oj_stack_init(&pi->stack);
1020
+ rb_protect(protect_parse, (VALUE)pi, &line);
1021
+ if (Qundef == pi->stack.head->val && !empty_ok(&pi->options)) {
1022
+ if (No == pi->options.nilnil || (CompatMode == pi->options.mode && 0 < pi->cur - pi->json)) {
1023
+ oj_set_error_at(pi, oj_json_parser_error_class, __FILE__, __LINE__, "Empty input");
1024
+ }
1025
+ }
1026
+ result = stack_head_val(&pi->stack);
1027
+ DATA_PTR(wrapped_stack) = 0;
1028
+ if (No == pi->options.allow_gc) {
1029
+ rb_gc_enable();
1030
+ }
1031
+ if (!err_has(&pi->err)) {
1032
+ // If the stack is not empty then the JSON terminated early.
1033
+ Val v;
1034
+ VALUE err_class = oj_parse_error_class;
1035
+
1036
+ if (0 != line) {
1037
+ VALUE ec = rb_obj_class(rb_errinfo());
1038
+
1039
+ if (rb_eArgError != ec && 0 != ec) {
1040
+ err_class = ec;
1041
+ }
1042
+ if (rb_eIOError != ec) {
1043
+ goto CLEANUP;
1044
+ }
1045
+ }
1046
+ if (NULL != (v = stack_peek(&pi->stack))) {
1047
+ switch (v->next) {
1048
+ case NEXT_ARRAY_NEW:
1049
+ case NEXT_ARRAY_ELEMENT:
1050
+ case NEXT_ARRAY_COMMA:
1051
+ oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Array not terminated");
1052
+ break;
1053
+ case NEXT_HASH_NEW:
1054
+ case NEXT_HASH_KEY:
1055
+ case NEXT_HASH_COLON:
1056
+ case NEXT_HASH_VALUE:
1057
+ case NEXT_HASH_COMMA:
1058
+ oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Hash/Object not terminated");
1059
+ break;
1060
+ default:
1061
+ oj_set_error_at(pi, err_class, __FILE__, __LINE__, "not terminated");
1062
+ }
1063
+ }
1064
+ }
1065
+ CLEANUP:
1066
+ // proceed with cleanup
1067
+ if (0 != pi->circ_array) {
1068
+ oj_circ_array_free(pi->circ_array);
1069
+ }
1070
+ if (0 != buf) {
1071
+ xfree(buf);
1072
+ } else if (free_json) {
1073
+ xfree(json);
1074
+ }
1075
+ stack_cleanup(&pi->stack);
1076
+ if (pi->str_rx.head != oj_default_options.str_rx.head) {
1077
+ oj_rxclass_cleanup(&pi->str_rx);
1078
+ }
1079
+ if (err_has(&pi->err)) {
1080
+ rb_set_errinfo(Qnil);
1081
+ if (Qnil != pi->err_class) {
1082
+ pi->err.clas = pi->err_class;
1083
+ }
1084
+ if (CompatMode == pi->options.mode && Yes != pi->options.safe) {
1085
+ // The json gem requires the error message be UTF-8 encoded. In
1086
+ // additional the complete JSON source must be returned. There
1087
+ // does not seem to be a size limit.
1088
+ VALUE msg = oj_encode(rb_str_new2(pi->err.msg));
1089
+ VALUE args[1];
1090
+
1091
+ if (NULL != pi->json) {
1092
+ msg = rb_str_append(msg, oj_encode(rb_str_new2(" in '")));
1093
+ msg = rb_str_append(msg, oj_encode(rb_str_new2(pi->json)));
1094
+ }
1095
+ args[0] = msg;
1096
+ if (pi->err.clas == oj_parse_error_class) {
1097
+ // The error was an Oj::ParseError so change to a JSON::ParserError.
1098
+ pi->err.clas = oj_json_parser_error_class;
1099
+ }
1100
+ rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
1101
+ } else {
1102
+ oj_err_raise(&pi->err);
1103
+ }
1104
+ } else if (0 != line) {
1105
+ rb_jump_tag(line);
1106
+ }
1107
+ if (pi->options.quirks_mode == No) {
1108
+ switch (rb_type(result)) {
1109
+ case T_NIL:
1110
+ case T_TRUE:
1111
+ case T_FALSE:
1112
+ case T_FIXNUM:
1113
+ case T_FLOAT:
1114
+ case T_CLASS:
1115
+ case T_STRING:
1116
+ case T_SYMBOL: {
1117
+ struct _err err;
1118
+
1119
+ if (Qnil == pi->err_class) {
1120
+ err.clas = oj_parse_error_class;
1121
+ } else {
1122
+ err.clas = pi->err_class;
1123
+ }
1124
+ snprintf(err.msg, sizeof(err.msg), "unexpected non-document value");
1125
+ oj_err_raise(&err);
1126
+ break;
1127
+ }
1128
+ default:
1129
+ // okay
1130
+ break;
1131
+ }
1132
+ }
1133
+ return result;
1134
+ }