oj 3.7.12

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