oj 3.8.1

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