oj 2.18.5 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -226
  3. data/ext/oj/circarray.c +0 -25
  4. data/ext/oj/circarray.h +0 -25
  5. data/ext/oj/code.c +227 -0
  6. data/ext/oj/code.h +40 -0
  7. data/ext/oj/compat.c +126 -38
  8. data/ext/oj/custom.c +1097 -0
  9. data/ext/oj/dump.c +658 -2376
  10. data/ext/oj/dump.h +92 -0
  11. data/ext/oj/dump_compat.c +937 -0
  12. data/ext/oj/dump_leaf.c +254 -0
  13. data/ext/oj/dump_object.c +810 -0
  14. data/ext/oj/dump_rails.c +329 -0
  15. data/ext/oj/dump_strict.c +416 -0
  16. data/ext/oj/err.c +0 -25
  17. data/ext/oj/err.h +8 -2
  18. data/ext/oj/fast.c +24 -24
  19. data/ext/oj/mimic_json.c +817 -0
  20. data/ext/oj/mimic_rails.c +806 -0
  21. data/ext/oj/mimic_rails.h +17 -0
  22. data/ext/oj/object.c +18 -72
  23. data/ext/oj/odd.c +0 -25
  24. data/ext/oj/odd.h +2 -27
  25. data/ext/oj/oj.c +655 -1503
  26. data/ext/oj/oj.h +93 -40
  27. data/ext/oj/parse.c +99 -46
  28. data/ext/oj/parse.h +12 -26
  29. data/ext/oj/reader.c +1 -25
  30. data/ext/oj/reader.h +3 -25
  31. data/ext/oj/resolve.c +9 -11
  32. data/ext/oj/resolve.h +2 -2
  33. data/ext/oj/rxclass.c +133 -0
  34. data/ext/oj/rxclass.h +27 -0
  35. data/ext/oj/saj.c +4 -25
  36. data/ext/oj/scp.c +3 -25
  37. data/ext/oj/sparse.c +89 -13
  38. data/ext/oj/stream_writer.c +301 -0
  39. data/ext/oj/strict.c +4 -27
  40. data/ext/oj/string_writer.c +480 -0
  41. data/ext/oj/val_stack.h +6 -2
  42. data/lib/oj.rb +1 -23
  43. data/lib/oj/easy_hash.rb +12 -4
  44. data/lib/oj/json.rb +172 -0
  45. data/lib/oj/mimic.rb +123 -18
  46. data/lib/oj/state.rb +131 -0
  47. data/lib/oj/version.rb +1 -1
  48. data/pages/Advanced.md +22 -0
  49. data/pages/Compatibility.md +25 -0
  50. data/pages/Custom.md +23 -0
  51. data/pages/Encoding.md +65 -0
  52. data/pages/JsonGem.md +79 -0
  53. data/pages/Modes.md +140 -0
  54. data/pages/Options.md +250 -0
  55. data/pages/Rails.md +60 -0
  56. data/pages/Security.md +20 -0
  57. data/test/activesupport4/decoding_test.rb +105 -0
  58. data/test/activesupport4/encoding_test.rb +531 -0
  59. data/test/activesupport4/test_helper.rb +41 -0
  60. data/test/activesupport5/decoding_test.rb +125 -0
  61. data/test/activesupport5/encoding_test.rb +483 -0
  62. data/test/activesupport5/encoding_test_cases.rb +90 -0
  63. data/test/activesupport5/test_helper.rb +50 -0
  64. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  65. data/test/json_gem/json_addition_test.rb +216 -0
  66. data/test/json_gem/json_common_interface_test.rb +143 -0
  67. data/test/json_gem/json_encoding_test.rb +109 -0
  68. data/test/json_gem/json_ext_parser_test.rb +20 -0
  69. data/test/json_gem/json_fixtures_test.rb +35 -0
  70. data/test/json_gem/json_generator_test.rb +383 -0
  71. data/test/json_gem/json_generic_object_test.rb +90 -0
  72. data/test/json_gem/json_parser_test.rb +470 -0
  73. data/test/json_gem/json_string_matching_test.rb +42 -0
  74. data/test/json_gem/test_helper.rb +18 -0
  75. data/test/perf_compat.rb +30 -28
  76. data/test/perf_object.rb +1 -1
  77. data/test/perf_strict.rb +18 -1
  78. data/test/sample.rb +0 -1
  79. data/test/test_compat.rb +169 -93
  80. data/test/test_custom.rb +355 -0
  81. data/test/test_file.rb +0 -8
  82. data/test/test_null.rb +376 -0
  83. data/test/test_object.rb +268 -3
  84. data/test/test_scp.rb +22 -1
  85. data/test/test_strict.rb +160 -4
  86. data/test/test_various.rb +52 -620
  87. data/test/tests.rb +14 -0
  88. data/test/tests_mimic.rb +14 -0
  89. data/test/tests_mimic_addition.rb +7 -0
  90. metadata +89 -47
  91. data/test/activesupport_datetime_test.rb +0 -23
  92. data/test/bug.rb +0 -51
  93. data/test/bug2.rb +0 -10
  94. data/test/bug3.rb +0 -46
  95. data/test/bug_fast.rb +0 -32
  96. data/test/bug_load.rb +0 -24
  97. data/test/crash.rb +0 -111
  98. data/test/curl/curl_oj.rb +0 -46
  99. data/test/curl/get_oj.rb +0 -24
  100. data/test/curl/just_curl.rb +0 -31
  101. data/test/curl/just_oj.rb +0 -51
  102. data/test/example.rb +0 -11
  103. data/test/foo.rb +0 -24
  104. data/test/io.rb +0 -48
  105. data/test/isolated/test_mimic_rails_datetime.rb +0 -27
  106. data/test/mod.rb +0 -16
  107. data/test/rails.rb +0 -50
  108. data/test/russian.rb +0 -18
  109. data/test/struct.rb +0 -29
  110. data/test/test_serializer.rb +0 -59
  111. data/test/write_timebars.rb +0 -31
@@ -1,31 +1,6 @@
1
1
  /* oj.h
2
2
  * Copyright (c) 2011, Peter Ohler
3
3
  * All rights reserved.
4
- *
5
- * Redistribution and use in source and binary forms, with or without
6
- * modification, are permitted provided that the following conditions are met:
7
- *
8
- * - Redistributions of source code must retain the above copyright notice, this
9
- * list of conditions and the following disclaimer.
10
- *
11
- * - Redistributions in binary form must reproduce the above copyright notice,
12
- * this list of conditions and the following disclaimer in the documentation
13
- * and/or other materials provided with the distribution.
14
- *
15
- * - Neither the name of Peter Ohler nor the names of its contributors may be
16
- * used to endorse or promote products derived from this software without
17
- * specific prior written permission.
18
- *
19
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
4
  */
30
5
 
31
6
  #ifndef __OJ_H__
@@ -45,8 +20,8 @@ extern "C" {
45
20
  #include "ruby/encoding.h"
46
21
  #endif
47
22
 
48
- #include "stdint.h"
49
- #include "stdbool.h"
23
+ #include <stdint.h>
24
+ #include <stdbool.h>
50
25
 
51
26
  #if USE_PTHREAD_MUTEX
52
27
  #include <pthread.h>
@@ -66,6 +41,7 @@ enum st_retval {ST_CONTINUE = 0, ST_STOP = 1, ST_DELETE = 2, ST_CHECK};
66
41
  #endif
67
42
  #endif
68
43
 
44
+ #include "rxclass.h"
69
45
  #include "err.h"
70
46
 
71
47
  #define INF_VAL "3.0e14159265358979323846"
@@ -82,7 +58,9 @@ typedef enum {
82
58
  StrictMode = 's',
83
59
  ObjectMode = 'o',
84
60
  NullMode = 'n',
85
- CompatMode = 'c'
61
+ CompatMode = 'c',
62
+ RailsMode = 'r',
63
+ CustomMode = 'C',
86
64
  } Mode;
87
65
 
88
66
  typedef enum {
@@ -96,7 +74,9 @@ typedef enum {
96
74
  NLEsc = 'n',
97
75
  JSONEsc = 'j',
98
76
  XSSEsc = 'x',
99
- ASCIIEsc = 'a'
77
+ ASCIIEsc = 'a',
78
+ JXEsc = 'r', // json
79
+ RailsEsc = 'R', // rails non escape
100
80
  } Encoding;
101
81
 
102
82
  typedef enum {
@@ -126,6 +106,13 @@ typedef enum {
126
106
  FILE_IO = 'f',
127
107
  } StreamWriterType;
128
108
 
109
+ typedef enum {
110
+ CALLER_DUMP = 'd',
111
+ CALLER_TO_JSON = 't',
112
+ CALLER_GENERATE = 'g',
113
+ // Add the fast versions if necessary. Maybe unparse as well if needed.
114
+ } DumpCaller;
115
+
129
116
  typedef struct _DumpOpts {
130
117
  bool use;
131
118
  char indent_str[16];
@@ -140,6 +127,7 @@ typedef struct _DumpOpts {
140
127
  uint8_t array_size;
141
128
  char nan_dump; // NanDump
142
129
  bool omit_nil;
130
+ int max_depth;
143
131
  } *DumpOpts;
144
132
 
145
133
  typedef struct _Options {
@@ -153,6 +141,7 @@ typedef struct _Options {
153
141
  char time_format; // TimeFormat
154
142
  char bigdec_as_num; // YesNo
155
143
  char bigdec_load; // BigLoad
144
+ char to_hash; // YesNo
156
145
  char to_json; // YesNo
157
146
  char as_json; // YesNo
158
147
  char nilnil; // YesNo
@@ -160,27 +149,51 @@ typedef struct _Options {
160
149
  char allow_gc; // allow GC during parse
161
150
  char quirks_mode; // allow single JSON values instead of documents
162
151
  char allow_invalid; // YesNo - allow invalid unicode
152
+ char create_ok; // YesNo allow create_id
153
+ char allow_nan; // YEsyNo for parsing only
163
154
  const char *create_id; // 0 or string
164
155
  size_t create_id_len; // length of create_id
165
156
  int sec_prec; // second precision when dumping time
166
157
  char float_prec; // float precision, linked to float_fmt
167
158
  char float_fmt[7]; // float format for dumping, if empty use Ruby
168
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
169
161
  struct _DumpOpts dump_opts;
162
+ struct _RxClass str_rx;
170
163
  } *Options;
171
164
 
165
+ struct _Out;
166
+ typedef void (*DumpFunc)(VALUE obj, int depth, struct _Out *out, bool as_ok);
167
+
168
+ // rails optimize
169
+ typedef struct _ROpt {
170
+ VALUE clas;
171
+ bool on;
172
+ DumpFunc dump;
173
+ } *ROpt;
174
+
175
+ typedef struct _ROptTable {
176
+ int len;
177
+ int alen;
178
+ ROpt table;
179
+ } *ROptTable;
180
+
172
181
  typedef struct _Out {
173
- char *buf;
174
- char *end;
175
- char *cur;
176
- Cache8 circ_cache;
177
- slot_t circ_cnt;
178
- int indent;
179
- int depth; // used by dump_hash
180
- Options opts;
181
- uint32_t hash_cnt;
182
- int allocated;
183
- bool omit_nil;
182
+ char *buf;
183
+ char *end;
184
+ char *cur;
185
+ Cache8 circ_cache;
186
+ slot_t circ_cnt;
187
+ int indent;
188
+ int depth; // used by dump_hash
189
+ Options opts;
190
+ uint32_t hash_cnt;
191
+ int allocated;
192
+ bool omit_nil;
193
+ int argc;
194
+ VALUE *argv;
195
+ DumpCaller caller; // used for the mimic json only
196
+ ROptTable ropts;
184
197
  } *Out;
185
198
 
186
199
  typedef struct _StrWriter {
@@ -229,10 +242,12 @@ extern VALUE oj_strict_parse(int argc, VALUE *argv, VALUE self);
229
242
  extern VALUE oj_strict_sparse(int argc, VALUE *argv, VALUE self);
230
243
  extern VALUE oj_compat_parse(int argc, VALUE *argv, VALUE self);
231
244
  extern VALUE oj_object_parse(int argc, VALUE *argv, VALUE self);
245
+ extern VALUE oj_custom_parse(int argc, VALUE *argv, VALUE self);
232
246
 
233
247
  extern VALUE oj_strict_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
234
248
  extern VALUE oj_compat_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
235
249
  extern VALUE oj_object_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
250
+ extern VALUE oj_custom_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
236
251
 
237
252
  extern void oj_parse_options(VALUE ropts, Options copts);
238
253
 
@@ -252,6 +267,19 @@ extern void oj_str_writer_pop(StrWriter sw);
252
267
  extern void oj_str_writer_pop_all(StrWriter sw);
253
268
 
254
269
  extern void oj_init_doc(void);
270
+ extern void oj_string_writer_init();
271
+ extern void oj_stream_writer_init();
272
+ extern void oj_str_writer_init(StrWriter sw);
273
+ extern VALUE oj_define_mimic_json(int argc, VALUE *argv, VALUE self);
274
+ extern VALUE oj_mimic_generate(int argc, VALUE *argv, VALUE self);
275
+ extern VALUE oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self);
276
+ extern void oj_parse_mimic_dump_options(VALUE ropts, Options copts);
277
+
278
+ extern VALUE oj_mimic_parse(int argc, VALUE *argv, VALUE self);
279
+ extern VALUE oj_get_json_err_class(const char *err_classname);
280
+ extern void oj_parse_opt_match_string(RxClass rc, VALUE ropts);
281
+
282
+ extern VALUE oj_rails_encode(int argc, VALUE *argv, VALUE self);
255
283
 
256
284
  extern VALUE Oj;
257
285
  extern struct _Options oj_default_options;
@@ -267,11 +295,28 @@ extern VALUE oj_cstack_class;
267
295
  extern VALUE oj_date_class;
268
296
  extern VALUE oj_datetime_class;
269
297
  extern VALUE oj_doc_class;
298
+ extern VALUE oj_enumerable_class;
299
+ extern VALUE oj_json_generator_error_class;
300
+ extern VALUE oj_json_parser_error_class;
270
301
  extern VALUE oj_stream_writer_class;
271
302
  extern VALUE oj_string_writer_class;
272
303
  extern VALUE oj_stringio_class;
273
304
  extern VALUE oj_struct_class;
274
305
 
306
+ extern VALUE oj_allow_nan_sym;
307
+ extern VALUE oj_array_class_sym;
308
+ extern VALUE oj_array_nl_sym;
309
+ extern VALUE oj_ascii_only_sym;
310
+ extern VALUE oj_create_additions_sym;
311
+ extern VALUE oj_hash_class_sym;
312
+ extern VALUE oj_indent_sym;
313
+ extern VALUE oj_max_nesting_sym;
314
+ extern VALUE oj_object_class_sym;
315
+ extern VALUE oj_object_nl_sym;
316
+ extern VALUE oj_quirks_mode_sym;
317
+ extern VALUE oj_space_before_sym;
318
+ extern VALUE oj_space_sym;
319
+
275
320
  extern VALUE oj_slash_string;
276
321
 
277
322
  extern ID oj_add_value_id;
@@ -279,10 +324,14 @@ extern ID oj_array_append_id;
279
324
  extern ID oj_array_end_id;
280
325
  extern ID oj_array_start_id;
281
326
  extern ID oj_as_json_id;
327
+ extern ID oj_begin_id;
328
+ extern ID oj_end_id;
282
329
  extern ID oj_error_id;
330
+ extern ID oj_exclude_end_id;
283
331
  extern ID oj_file_id;
284
332
  extern ID oj_fileno_id;
285
333
  extern ID oj_ftype_id;
334
+ extern ID oj_has_key_id;
286
335
  extern ID oj_hash_end_id;
287
336
  extern ID oj_hash_key_id;
288
337
  extern ID oj_hash_set_id;
@@ -299,6 +348,7 @@ extern ID oj_readpartial_id;
299
348
  extern ID oj_replace_id;
300
349
  extern ID oj_stat_id;
301
350
  extern ID oj_string_id;
351
+ extern ID oj_to_h_id;
302
352
  extern ID oj_to_hash_id;
303
353
  extern ID oj_to_json_id;
304
354
  extern ID oj_to_s_id;
@@ -312,6 +362,9 @@ extern ID oj_utc_offset_id;
312
362
  extern ID oj_utcq_id;
313
363
  extern ID oj_write_id;
314
364
 
365
+ extern bool oj_use_hash_alt;
366
+ extern bool oj_use_array_alt;
367
+
315
368
  #if USE_PTHREAD_MUTEX
316
369
  extern pthread_mutex_t oj_cache_mutex;
317
370
  #elif USE_RB_MUTEX
@@ -1,31 +1,6 @@
1
1
  /* parse.c
2
2
  * Copyright (c) 2013, Peter Ohler
3
3
  * All rights reserved.
4
- *
5
- * Redistribution and use in source and binary forms, with or without
6
- * modification, are permitted provided that the following conditions are met:
7
- *
8
- * - Redistributions of source code must retain the above copyright notice, this
9
- * list of conditions and the following disclaimer.
10
- *
11
- * - Redistributions in binary form must reproduce the above copyright notice,
12
- * this list of conditions and the following disclaimer in the documentation
13
- * and/or other materials provided with the distribution.
14
- *
15
- * - Neither the name of Peter Ohler nor the names of its contributors may be
16
- * used to endorse or promote products derived from this software without
17
- * specific prior written permission.
18
- *
19
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
4
  */
30
5
 
31
6
  #include <stdlib.h>
@@ -35,9 +10,11 @@
35
10
  #include <math.h>
36
11
 
37
12
  #include "oj.h"
13
+ #include "encode.h"
38
14
  #include "parse.h"
39
15
  #include "buf.h"
40
16
  #include "val_stack.h"
17
+ #include "rxclass.h"
41
18
 
42
19
  // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
43
20
  #define OJ_INFINITY (1.0/0.0)
@@ -237,6 +214,18 @@ read_escaped_str(ParseInfo pi, const char *start) {
237
214
  case '"': buf_append(&buf, '"'); break;
238
215
  case '/': buf_append(&buf, '/'); break;
239
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;
240
229
  case 'u':
241
230
  s++;
242
231
  if (0 == (code = read_hex(pi, s)) && err_has(&pi->err)) {
@@ -411,7 +400,7 @@ read_num(ParseInfo pi) {
411
400
  pi->cur++;
412
401
  }
413
402
  if ('I' == *pi->cur) {
414
- if (0 != strncmp("Infinity", pi->cur, 8)) {
403
+ if (No == pi->options.allow_nan || 0 != strncmp("Infinity", pi->cur, 8)) {
415
404
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
416
405
  return;
417
406
  }
@@ -426,14 +415,25 @@ read_num(ParseInfo pi) {
426
415
  ni.nan = 1;
427
416
  } else {
428
417
  int dec_cnt = 0;
429
-
418
+ bool zero1 = false;
419
+
430
420
  for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
421
+ if (0 == ni.i && '0' == *pi->cur) {
422
+ zero1 = true;
423
+ }
431
424
  if (0 < ni.i) {
432
425
  dec_cnt++;
433
426
  }
434
427
  if (!ni.big) {
435
428
  int d = (*pi->cur - '0');
436
429
 
430
+ if (0 < d) {
431
+ if (zero1 && CompatMode == pi->options.mode) {
432
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
433
+ return;
434
+ }
435
+ zero1 = false;
436
+ }
437
437
  ni.i = ni.i * 10 + d;
438
438
  if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
439
439
  ni.big = 1;
@@ -442,6 +442,10 @@ read_num(ParseInfo pi) {
442
442
  }
443
443
  if ('.' == *pi->cur) {
444
444
  pi->cur++;
445
+ if (*pi->cur < '0' || '9' < *pi->cur) {
446
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
447
+ return;
448
+ }
445
449
  for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
446
450
  int d = (*pi->cur - '0');
447
451
 
@@ -589,18 +593,26 @@ colon(ParseInfo pi) {
589
593
 
590
594
  void
591
595
  oj_parse2(ParseInfo pi) {
592
- int first = 1;
596
+ int first = 1;
597
+ long start = 0;
593
598
 
594
599
  pi->cur = pi->json;
595
600
  err_init(&pi->err);
596
601
  while (1) {
602
+ if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
603
+ VALUE err_clas = oj_get_json_err_class("NestingError");
604
+
605
+ oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
606
+ pi->err_class = err_clas;
607
+ return;
608
+ }
597
609
  next_non_white(pi);
598
610
  if (!first && '\0' != *pi->cur) {
599
611
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected characters after the JSON document");
600
612
  }
601
613
 
602
- // if no tokens are consumed (i.e. empty string), throw a parse error
603
- // this is the behavior of JSON.parse in both Ruby and JS
614
+ // If no tokens are consumed (i.e. empty string), throw a parse error
615
+ // this is the behavior of JSON.parse in both Ruby and JS.
604
616
  if (No == pi->options.empty_string && 1 == first && '\0' == *pi->cur) {
605
617
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
606
618
  }
@@ -627,7 +639,7 @@ oj_parse2(ParseInfo pi) {
627
639
  case '"':
628
640
  read_str(pi);
629
641
  break;
630
- case '+':
642
+ //case '+':
631
643
  case '-':
632
644
  case '0':
633
645
  case '1':
@@ -639,11 +651,18 @@ oj_parse2(ParseInfo pi) {
639
651
  case '7':
640
652
  case '8':
641
653
  case '9':
642
- case 'I':
643
- case 'N':
644
654
  pi->cur--;
645
655
  read_num(pi);
646
656
  break;
657
+ case 'I':
658
+ case 'N':
659
+ if (Yes == pi->options.allow_nan) {
660
+ pi->cur--;
661
+ read_num(pi);
662
+ } else {
663
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
664
+ }
665
+ break;
647
666
  case 't':
648
667
  read_true(pi);
649
668
  break;
@@ -676,22 +695,27 @@ oj_parse2(ParseInfo pi) {
676
695
  }
677
696
  if (stack_empty(&pi->stack)) {
678
697
  if (Qundef != pi->proc) {
698
+ VALUE args[3];
699
+ long len = (pi->cur - pi->json) - start;
700
+
701
+ *args = stack_head_val(&pi->stack);
702
+ args[1] = LONG2NUM(start);
703
+ args[2] = LONG2NUM(len);
704
+
679
705
  if (Qnil == pi->proc) {
680
- rb_yield(stack_head_val(&pi->stack));
706
+ rb_yield_values2(3, args);
681
707
  } else {
682
708
  #if HAS_PROC_WITH_BLOCK
683
- VALUE args[1];
684
-
685
- *args = stack_head_val(&pi->stack);
686
- rb_proc_call_with_block(pi->proc, 1, args, Qnil);
709
+ rb_proc_call_with_block(pi->proc, 3, args, Qnil);
687
710
  #else
688
711
  rb_raise(rb_eNotImpError,
689
712
  "Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead.");
690
713
  #endif
691
714
  }
692
- } else {
715
+ } else if (!pi->has_callbacks) {
693
716
  first = 0;
694
717
  }
718
+ start = pi->cur - pi->json;
695
719
  }
696
720
  }
697
721
  }
@@ -818,8 +842,12 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
818
842
  rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
819
843
  }
820
844
  input = argv[0];
821
- if (2 == argc) {
822
- oj_parse_options(argv[1], &pi->options);
845
+ if (2 <= argc) {
846
+ if (T_HASH == rb_type(argv[1])) {
847
+ oj_parse_options(argv[1], &pi->options);
848
+ } else if (3 <= argc && T_HASH == rb_type(argv[2])) {
849
+ oj_parse_options(argv[2], &pi->options);
850
+ }
823
851
  }
824
852
  if (yieldOk && rb_block_given_p()) {
825
853
  pi->proc = Qnil;
@@ -831,9 +859,16 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
831
859
  pi->end = json + len;
832
860
  free_json = 1;
833
861
  } else if (T_STRING == rb_type(input)) {
862
+ if (No == pi->options.nilnil && 0 == RSTRING_LEN(input)) {
863
+ rb_raise(oj_json_parser_error_class, "An empty string is not a valid JSON string.");
864
+ }
834
865
  oj_pi_set_input_str(pi, &input);
835
- } else if (Qnil == input && Yes == pi->options.nilnil) {
836
- return Qnil;
866
+ } else if (Qnil == input) {
867
+ if (Yes == pi->options.nilnil) {
868
+ return Qnil;
869
+ } else {
870
+ rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
871
+ }
837
872
  } else {
838
873
  VALUE clas = rb_obj_class(input);
839
874
  volatile VALUE s;
@@ -860,14 +895,14 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
860
895
  ((char*)pi->json)[len] = '\0';
861
896
  /* skip UTF-8 BOM if present */
862
897
  if (0xEF == (uint8_t)*pi->json && 0xBB == (uint8_t)pi->json[1] && 0xBF == (uint8_t)pi->json[2]) {
863
- pi->json += 3;
898
+ pi->cur += 3;
864
899
  }
865
900
  #endif
866
901
  } else if (rb_respond_to(input, oj_read_id)) {
867
902
  // use stream parser instead
868
903
  return oj_pi_sparse(argc, argv, pi, 0);
869
904
  } else {
870
- rb_raise(rb_eArgError, "strict_parse() expected a String or IO Object.");
905
+ rb_raise(rb_eArgError, "parse() expected a String or IO Object.");
871
906
  }
872
907
  }
873
908
  if (Yes == pi->options.circular) {
@@ -922,6 +957,9 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
922
957
  xfree(json);
923
958
  }
924
959
  stack_cleanup(&pi->stack);
960
+ if (pi->str_rx.head != oj_default_options.str_rx.head) {
961
+ oj_rxclass_cleanup(&pi->str_rx);
962
+ }
925
963
  if (0 != line) {
926
964
  rb_jump_tag(line);
927
965
  }
@@ -929,7 +967,22 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
929
967
  if (Qnil != pi->err_class) {
930
968
  pi->err.clas = pi->err_class;
931
969
  }
932
- oj_err_raise(&pi->err);
970
+ if (CompatMode == pi->options.mode) {
971
+ // The json gem requires the error message be UTF-8 encoded. In
972
+ // additional the complete JSON source must be returned. There
973
+ // does not seem to be a size limit.
974
+ VALUE msg = oj_encode(rb_str_new2(pi->err.msg));
975
+ VALUE args[1];
976
+
977
+ if (NULL != pi->json) {
978
+ msg = rb_str_append(msg, oj_encode(rb_str_new2(" in '")));
979
+ msg = rb_str_append(msg, oj_encode(rb_str_new2(pi->json)));
980
+ }
981
+ args[0] = msg;
982
+ rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
983
+ } else {
984
+ oj_err_raise(&pi->err);
985
+ }
933
986
  }
934
987
  if (pi->options.quirks_mode == No) {
935
988
  switch (rb_type(result)) {