oj 3.16.11 → 3.17.6

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.
data/ext/oj/parser.c CHANGED
@@ -5,6 +5,7 @@
5
5
  #include <fcntl.h>
6
6
 
7
7
  #include "oj.h"
8
+ #include "simd.h"
8
9
 
9
10
  #define DEBUG 0
10
11
 
@@ -75,7 +76,7 @@ enum {
75
76
 
76
77
  /*
77
78
  0123456789abcdef0123456789abcdef */
78
- static const char value_map[257] = "\
79
+ static const char value_map[258] = "\
79
80
  X........ab..a..................\
80
81
  a.i..........f..ghhhhhhhhh......\
81
82
  ...........................k.m..\
@@ -85,7 +86,7 @@ a.i..........f..ghhhhhhhhh......\
85
86
  ................................\
86
87
  ................................v";
87
88
 
88
- static const char null_map[257] = "\
89
+ static const char null_map[258] = "\
89
90
  ................................\
90
91
  ............o...................\
91
92
  ................................\
@@ -95,7 +96,7 @@ static const char null_map[257] = "\
95
96
  ................................\
96
97
  ................................N";
97
98
 
98
- static const char true_map[257] = "\
99
+ static const char true_map[258] = "\
99
100
  ................................\
100
101
  ............o...................\
101
102
  ................................\
@@ -105,7 +106,7 @@ static const char true_map[257] = "\
105
106
  ................................\
106
107
  ................................T";
107
108
 
108
- static const char false_map[257] = "\
109
+ static const char false_map[258] = "\
109
110
  ................................\
110
111
  ............o...................\
111
112
  ................................\
@@ -115,7 +116,7 @@ static const char false_map[257] = "\
115
116
  ................................\
116
117
  ................................F";
117
118
 
118
- static const char comma_map[257] = "\
119
+ static const char comma_map[258] = "\
119
120
  .........ab..a..................\
120
121
  a.i..........f..ghhhhhhhhh......\
121
122
  ...........................k....\
@@ -125,7 +126,7 @@ a.i..........f..ghhhhhhhhh......\
125
126
  ................................\
126
127
  ................................,";
127
128
 
128
- static const char after_map[257] = "\
129
+ static const char after_map[258] = "\
129
130
  X........ab..a..................\
130
131
  a...........o...................\
131
132
  .............................m..\
@@ -135,7 +136,7 @@ a...........o...................\
135
136
  ................................\
136
137
  ................................a";
137
138
 
138
- static const char key1_map[257] = "\
139
+ static const char key1_map[258] = "\
139
140
  .........ab..a..................\
140
141
  a.p.............................\
141
142
  ................................\
@@ -145,7 +146,7 @@ a.p.............................\
145
146
  ................................\
146
147
  ................................K";
147
148
 
148
- static const char key_map[257] = "\
149
+ static const char key_map[258] = "\
149
150
  .........ab..a..................\
150
151
  a.p.............................\
151
152
  ................................\
@@ -155,7 +156,7 @@ a.p.............................\
155
156
  ................................\
156
157
  ................................k";
157
158
 
158
- static const char colon_map[257] = "\
159
+ static const char colon_map[258] = "\
159
160
  .........ab..a..................\
160
161
  a.........................q.....\
161
162
  ................................\
@@ -165,7 +166,7 @@ a.........................q.....\
165
166
  ................................\
166
167
  ................................:";
167
168
 
168
- static const char neg_map[257] = "\
169
+ static const char neg_map[258] = "\
169
170
  ................................\
170
171
  ................O---------......\
171
172
  ................................\
@@ -175,7 +176,7 @@ static const char neg_map[257] = "\
175
176
  ................................\
176
177
  ................................-";
177
178
 
178
- static const char zero_map[257] = "\
179
+ static const char zero_map[258] = "\
179
180
  .........rs..r..................\
180
181
  r...........u.t.................\
181
182
  .............................H..\
@@ -185,7 +186,7 @@ r...........u.t.................\
185
186
  ................................\
186
187
  ................................0";
187
188
 
188
- static const char digit_map[257] = "\
189
+ static const char digit_map[258] = "\
189
190
  .........rs..r..................\
190
191
  r...........u.t.NNNNNNNNNN......\
191
192
  .....w.......................H..\
@@ -195,7 +196,7 @@ r...........u.t.NNNNNNNNNN......\
195
196
  ................................\
196
197
  ................................d";
197
198
 
198
- static const char dot_map[257] = "\
199
+ static const char dot_map[258] = "\
199
200
  ................................\
200
201
  ................vvvvvvvvvv......\
201
202
  ................................\
@@ -205,7 +206,7 @@ static const char dot_map[257] = "\
205
206
  ................................\
206
207
  .................................";
207
208
 
208
- static const char frac_map[257] = "\
209
+ static const char frac_map[258] = "\
209
210
  .........rs..r..................\
210
211
  r...........u...vvvvvvvvvv......\
211
212
  .....w.......................H..\
@@ -215,7 +216,7 @@ r...........u...vvvvvvvvvv......\
215
216
  ................................\
216
217
  ................................f";
217
218
 
218
- static const char exp_sign_map[257] = "\
219
+ static const char exp_sign_map[258] = "\
219
220
  ................................\
220
221
  ...........x.x..yyyyyyyyyy......\
221
222
  ................................\
@@ -225,7 +226,7 @@ static const char exp_sign_map[257] = "\
225
226
  ................................\
226
227
  ................................x";
227
228
 
228
- static const char exp_zero_map[257] = "\
229
+ static const char exp_zero_map[258] = "\
229
230
  ................................\
230
231
  ................yyyyyyyyyy......\
231
232
  ................................\
@@ -235,7 +236,7 @@ static const char exp_zero_map[257] = "\
235
236
  ................................\
236
237
  ................................z";
237
238
 
238
- static const char exp_map[257] = "\
239
+ static const char exp_map[258] = "\
239
240
  .........rs..r..................\
240
241
  r...........u...yyyyyyyyyy......\
241
242
  .............................H..\
@@ -245,7 +246,7 @@ r...........u...yyyyyyyyyy......\
245
246
  ................................\
246
247
  ................................X";
247
248
 
248
- static const char big_digit_map[257] = "\
249
+ static const char big_digit_map[258] = "\
249
250
  .........rs..r..................\
250
251
  r...........u.D.CCCCCCCCCC......\
251
252
  .....J.......................H..\
@@ -255,7 +256,7 @@ r...........u.D.CCCCCCCCCC......\
255
256
  ................................\
256
257
  ................................D";
257
258
 
258
- static const char big_dot_map[257] = "\
259
+ static const char big_dot_map[258] = "\
259
260
  ................................\
260
261
  ................IIIIIIIIII......\
261
262
  ................................\
@@ -265,7 +266,7 @@ static const char big_dot_map[257] = "\
265
266
  ................................\
266
267
  ................................o";
267
268
 
268
- static const char big_frac_map[257] = "\
269
+ static const char big_frac_map[258] = "\
269
270
  .........rs..r..................\
270
271
  r...........u...IIIIIIIIII......\
271
272
  .....J.......................H..\
@@ -275,7 +276,7 @@ r...........u...IIIIIIIIII......\
275
276
  ................................\
276
277
  ................................g";
277
278
 
278
- static const char big_exp_sign_map[257] = "\
279
+ static const char big_exp_sign_map[258] = "\
279
280
  ................................\
280
281
  ...........K.K..LLLLLLLLLL......\
281
282
  ................................\
@@ -285,7 +286,7 @@ static const char big_exp_sign_map[257] = "\
285
286
  ................................\
286
287
  ................................B";
287
288
 
288
- static const char big_exp_zero_map[257] = "\
289
+ static const char big_exp_zero_map[258] = "\
289
290
  ................................\
290
291
  ................LLLLLLLLLL......\
291
292
  ................................\
@@ -295,7 +296,7 @@ static const char big_exp_zero_map[257] = "\
295
296
  ................................\
296
297
  ................................Z";
297
298
 
298
- static const char big_exp_map[257] = "\
299
+ static const char big_exp_map[258] = "\
299
300
  .........rs..r..................\
300
301
  r...........u...LLLLLLLLLL......\
301
302
  .............................H..\
@@ -305,7 +306,7 @@ r...........u...LLLLLLLLLL......\
305
306
  ................................\
306
307
  ................................Y";
307
308
 
308
- static const char string_map[257] = "\
309
+ static const char string_map[258] = "\
309
310
  ................................\
310
311
  RRzRRRRRRRRRRRRRRRRRRRRRRRRRRRRR\
311
312
  RRRRRRRRRRRRRRRRRRRRRRRRRRRRARRR\
@@ -315,7 +316,7 @@ RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR\
315
316
  MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\
316
317
  PPPPPPPPPPPPPPPPQQQQQQQQ........s";
317
318
 
318
- static const char esc_map[257] = "\
319
+ static const char esc_map[258] = "\
319
320
  ................................\
320
321
  ..B............B................\
321
322
  ............................B...\
@@ -325,7 +326,7 @@ static const char esc_map[257] = "\
325
326
  ................................\
326
327
  ................................~";
327
328
 
328
- static const char esc_byte_map[257] = "\
329
+ static const char esc_byte_map[258] = "\
329
330
  ................................\
330
331
  ..\"............/................\
331
332
  ............................\\...\
@@ -335,7 +336,7 @@ static const char esc_byte_map[257] = "\
335
336
  ................................\
336
337
  ................................b";
337
338
 
338
- static const char u_map[257] = "\
339
+ static const char u_map[258] = "\
339
340
  ................................\
340
341
  ................EEEEEEEEEE......\
341
342
  .EEEEEE.........................\
@@ -345,7 +346,7 @@ static const char u_map[257] = "\
345
346
  ................................\
346
347
  ................................u";
347
348
 
348
- static const char utf_map[257] = "\
349
+ static const char utf_map[258] = "\
349
350
  ................................\
350
351
  ................................\
351
352
  ................................\
@@ -355,7 +356,7 @@ SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS\
355
356
  ................................\
356
357
  ................................8";
357
358
 
358
- static const char space_map[257] = "\
359
+ static const char space_map[258] = "\
359
360
  .........ab..a..................\
360
361
  a...............................\
361
362
  ................................\
@@ -365,7 +366,7 @@ a...............................\
365
366
  ................................\
366
367
  ................................S";
367
368
 
368
- static const char trail_map[257] = "\
369
+ static const char trail_map[258] = "\
369
370
  .........ab..a..................\
370
371
  a...............................\
371
372
  ................................\
@@ -375,7 +376,7 @@ a...............................\
375
376
  ................................\
376
377
  ................................R";
377
378
 
378
- static const byte hex_map[256] = "\
379
+ static const byte hex_map[257] = "\
379
380
  ................................\
380
381
  ................\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09......\
381
382
  .\x0a\x0b\x0c\x0d\x0e\x0f.........................\
@@ -594,9 +595,64 @@ static void big_change(ojParser p) {
594
595
  }
595
596
  }
596
597
 
597
- static void parse(ojParser p, const byte *json) {
598
+ // Scan forward over string content, returning the first byte that is not
599
+ // STR_OK in string_map. This is a pure drop-in for the scalar loop
600
+ //
601
+ // for (; STR_OK == string_map[*b]; b++) {}
602
+ //
603
+ // and must return the exact same stop position. The stop set (bytes whose
604
+ // string_map class is not 'R'/STR_OK) is:
605
+ //
606
+ // * control bytes 0x00..0x1F (class '.', also stops at the NUL terminator)
607
+ // * the quote 0x22 '"' (class 'z')
608
+ // * the backslash 0x5C '\' (class 'A')
609
+ // * every high byte 0x80..0xFF (UTF-8 lead/continuation, classes M/P/Q/'.')
610
+ //
611
+ // Note this differs from parse.c's string_scan_neon, which stops only on
612
+ // \0 \\ " -- parser.c hands multi-byte UTF-8 to its state machine, so the
613
+ // scanner must stop on the high bytes too. The predictor below is derived from
614
+ // string_map, not copied from parse.c.
615
+ //
616
+ // The NEON path only loads a full 16-byte vector when [b, b+16) stays within
617
+ // [., end), so it never reads past the string's allocation; the sub-16-byte
618
+ // tail (and the whole scan on non-NEON builds) uses the scalar loop, which
619
+ // stops naturally at the guaranteed NUL terminator.
620
+ static inline const byte *oj_scan_str_simd(const byte *b, const byte *end) {
621
+ #ifdef HAVE_SIMD_NEON
622
+ if (SIMD_NEON == SIMD_Impl) {
623
+ const uint8x16_t quote = vdupq_n_u8('"');
624
+ const uint8x16_t bslash = vdupq_n_u8('\\');
625
+ const uint8x16_t space = vdupq_n_u8(0x20);
626
+ const uint8x16_t high = vdupq_n_u8(0x80);
627
+
628
+ while (b + sizeof(uint8x16_t) <= end) {
629
+ const uint8x16_t chunk = vld1q_u8((const uint8_t *)b);
630
+ // special lane == 0xFF for any byte that stops the scan.
631
+ const uint8x16_t special = vorrq_u8(vorrq_u8(vceqq_u8(chunk, quote), vceqq_u8(chunk, bslash)),
632
+ vorrq_u8(vcltq_u8(chunk, space), vcgeq_u8(chunk, high)));
633
+ // Reduce to a 64-bit mask with 4 bits per lane (same idiom as
634
+ // parse.c's string_scan_neon) and locate the first set lane.
635
+ const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(special), 4);
636
+ uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
637
+ if (0 != mask) {
638
+ mask &= 0x8888888888888888ull;
639
+ return b + (OJ_CTZ64(mask) >> 2);
640
+ }
641
+ b += sizeof(uint8x16_t);
642
+ }
643
+ }
644
+ #else
645
+ (void)end;
646
+ #endif
647
+ for (; STR_OK == string_map[*b]; b++) {
648
+ }
649
+ return b;
650
+ }
651
+
652
+ static void parse(ojParser p, const byte *json, size_t len, bool more) {
598
653
  const byte *start;
599
- const byte *b = json;
654
+ const byte *b = json;
655
+ const byte *end = json + len;
600
656
  int i;
601
657
 
602
658
  p->line = 1;
@@ -629,12 +685,14 @@ static void parse(ojParser p, const byte *json) {
629
685
  b++;
630
686
  p->key.tail = p->key.head;
631
687
  start = b;
632
- for (; STR_OK == string_map[*b]; b++) {
633
- }
688
+ b = oj_scan_str_simd(b, end);
634
689
  buf_append_string(&p->key, (const char *)start, b - start);
635
690
  if ('"' == *b) {
636
691
  p->map = colon_map;
637
692
  break;
693
+ } else if ('\0' == *b && !more) {
694
+ parse_error(p, "quoted string not terminated");
695
+ break;
638
696
  }
639
697
  b--;
640
698
  p->map = string_map;
@@ -651,14 +709,16 @@ static void parse(ojParser p, const byte *json) {
651
709
  b++;
652
710
  start = b;
653
711
  p->buf.tail = p->buf.head;
654
- for (; STR_OK == string_map[*b]; b++) {
655
- }
712
+ b = oj_scan_str_simd(b, end);
656
713
  buf_append_string(&p->buf, (const char *)start, b - start);
657
714
  if ('"' == *b) {
658
715
  p->cur = b - json;
659
716
  p->funcs[p->stack[p->depth]].add_str(p);
660
717
  p->map = (0 == p->depth) ? value_map : after_map;
661
718
  break;
719
+ } else if ('\0' == *b && !more) {
720
+ parse_error(p, "quoted string not terminated");
721
+ break;
662
722
  }
663
723
  b--;
664
724
  p->map = string_map;
@@ -668,6 +728,10 @@ static void parse(ojParser p, const byte *json) {
668
728
  p->cur = b - json;
669
729
  p->funcs[p->stack[p->depth]].open_object(p);
670
730
  p->depth++;
731
+ if ((int)sizeof(p->stack) <= p->depth) {
732
+ parse_error(p, "too deeply nested");
733
+ break;
734
+ }
671
735
  p->stack[p->depth] = OBJECT_FUN;
672
736
  p->map = key1_map;
673
737
  break;
@@ -690,6 +754,10 @@ static void parse(ojParser p, const byte *json) {
690
754
  p->cur = b - json;
691
755
  p->funcs[p->stack[p->depth]].open_array(p);
692
756
  p->depth++;
757
+ if ((int)sizeof(p->stack) <= p->depth) {
758
+ parse_error(p, "too deeply nested");
759
+ break;
760
+ }
693
761
  p->stack[p->depth] = ARRAY_FUN;
694
762
  p->map = value_map;
695
763
  break;
@@ -824,10 +892,10 @@ static void parse(ojParser p, const byte *json) {
824
892
  case EXP_DIGIT:
825
893
  p->map = exp_map;
826
894
  for (; NUM_DIGIT == digit_map[*b]; b++) {
827
- int16_t x = p->num.exp * 10 + (int16_t)(*b - '0');
895
+ int x = p->num.exp * 10 + (*b - '0');
828
896
 
829
897
  if (x <= MAX_EXP) {
830
- p->num.exp = x;
898
+ p->num.exp = (int16_t)x;
831
899
  } else {
832
900
  big_change(p);
833
901
  p->map = big_exp_map;
@@ -891,8 +959,7 @@ static void parse(ojParser p, const byte *json) {
891
959
  break;
892
960
  case STR_OK:
893
961
  start = b;
894
- for (; STR_OK == string_map[*b]; b++) {
895
- }
962
+ b = oj_scan_str_simd(b, end);
896
963
  if (':' == p->next_map[256]) {
897
964
  buf_append_string(&p->key, (const char *)start, b - start);
898
965
  } else {
@@ -1176,6 +1243,8 @@ extern void oj_set_parser_validator(ojParser p);
1176
1243
  extern void oj_set_parser_saj(ojParser p);
1177
1244
  extern void oj_set_parser_usual(ojParser p);
1178
1245
  extern void oj_set_parser_debug(ojParser p);
1246
+ extern void oj_set_parser_safe(ojParser p, VALUE options);
1247
+ extern void oj_safe_init(VALUE parser_class);
1179
1248
 
1180
1249
  static int opt_cb(VALUE rkey, VALUE value, VALUE ptr) {
1181
1250
  ojParser p = (ojParser)ptr;
@@ -1213,7 +1282,8 @@ static int opt_cb(VALUE rkey, VALUE value, VALUE ptr) {
1213
1282
  * Oj::Parser.new(:usual, cache_keys: true).
1214
1283
  */
1215
1284
  static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
1216
- ojParser p = OJ_R_ALLOC(struct _ojParser);
1285
+ ojParser p = OJ_R_ALLOC(struct _ojParser);
1286
+ volatile VALUE pv;
1217
1287
 
1218
1288
  #if HAVE_RB_EXT_RACTOR_SAFE
1219
1289
  // This doesn't seem to do anything.
@@ -1223,6 +1293,10 @@ static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
1223
1293
  buf_init(&p->key);
1224
1294
  buf_init(&p->buf);
1225
1295
  p->map = value_map;
1296
+ // Until the parser is wrapped the GC can not free it, so the mode and the
1297
+ // options, both of which raise on a value they do not take, are handled
1298
+ // after the wrap.
1299
+ pv = TypedData_Wrap_Struct(parser_class, &oj_parser_type, p);
1226
1300
 
1227
1301
  if (argc < 1) {
1228
1302
  oj_set_parser_validator(p);
@@ -1263,7 +1337,7 @@ static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
1263
1337
  rb_hash_foreach(ropts, opt_cb, (VALUE)p);
1264
1338
  }
1265
1339
  }
1266
- return TypedData_Wrap_Struct(parser_class, &oj_parser_type, p);
1340
+ return pv;
1267
1341
  }
1268
1342
 
1269
1343
  // Create a new parser without setting the delegate. The parser is
@@ -1354,6 +1428,34 @@ static VALUE parser_missing(int argc, VALUE *argv, VALUE self) {
1354
1428
  return p->option(p, key, rv);
1355
1429
  }
1356
1430
 
1431
+ static void validate_primitives_are_complete(ojParser p) {
1432
+ if (0 >= p->ri) {
1433
+ return;
1434
+ }
1435
+
1436
+ switch (p->map[256]) {
1437
+ case 'N': parse_error(p, "expected null"); break;
1438
+ case 'F': parse_error(p, "expected false"); break;
1439
+ case 'T': parse_error(p, "expected true"); break;
1440
+ }
1441
+ }
1442
+
1443
+ static void validate_non_primitives_are_complete(ojParser p) {
1444
+ if (0 >= p->depth) {
1445
+ return;
1446
+ }
1447
+ if (OBJECT_FUN == p->stack[p->depth]) {
1448
+ parse_error(p, "Object is not closed");
1449
+ } else {
1450
+ parse_error(p, "Array is not closed");
1451
+ }
1452
+ }
1453
+
1454
+ static void validate_document_end(ojParser p) {
1455
+ validate_primitives_are_complete(p);
1456
+ validate_non_primitives_are_complete(p);
1457
+ }
1458
+
1357
1459
  /* Document-method: parse(json)
1358
1460
  * call-seq: parse(json)
1359
1461
  *
@@ -1363,13 +1465,20 @@ static VALUE parser_missing(int argc, VALUE *argv, VALUE self) {
1363
1465
  */
1364
1466
  static VALUE parser_parse(VALUE self, VALUE json) {
1365
1467
  ojParser p;
1366
- const byte *ptr = (const byte *)StringValuePtr(json);
1468
+ int frozen = OBJ_FROZEN(json);
1469
+ const byte *ptr;
1470
+
1471
+ if (!frozen) {
1472
+ rb_str_freeze(json);
1473
+ }
1474
+ ptr = (const byte *)StringValuePtr(json);
1367
1475
 
1368
1476
  TypedData_Get_Struct(self, struct _ojParser, &oj_parser_type, p);
1369
1477
 
1370
1478
  parser_reset(p);
1371
1479
  p->start(p);
1372
- parse(p, ptr);
1480
+ parse(p, ptr, (size_t)RSTRING_LEN(json), false);
1481
+ validate_document_end(p);
1373
1482
 
1374
1483
  return p->result(p);
1375
1484
  }
@@ -1389,7 +1498,7 @@ static VALUE load(VALUE self) {
1389
1498
  while (true) {
1390
1499
  rb_funcall(p->reader, oj_readpartial_id, 2, INT2NUM(16385), rbuf);
1391
1500
  if (0 < RSTRING_LEN(rbuf)) {
1392
- parse(p, (byte *)StringValuePtr(rbuf));
1501
+ parse(p, (byte *)StringValuePtr(rbuf), (size_t)RSTRING_LEN(rbuf), true);
1393
1502
  }
1394
1503
  if (Qtrue == rb_funcall(p->reader, oj_eofq_id, 0)) {
1395
1504
  if (0 < p->depth) {
@@ -1420,6 +1529,39 @@ static VALUE parser_load(VALUE self, VALUE reader) {
1420
1529
  return p->result(p);
1421
1530
  }
1422
1531
 
1532
+ struct _fileParse {
1533
+ ojParser p;
1534
+ const char *path;
1535
+ int fd;
1536
+ };
1537
+
1538
+ static VALUE file_parse(VALUE x) {
1539
+ struct _fileParse *fp = (struct _fileParse *)x;
1540
+ byte buf[16385];
1541
+ size_t size = sizeof(buf) - 1;
1542
+ ssize_t rsize;
1543
+
1544
+ while (true) {
1545
+ if (0 > (rsize = read(fp->fd, buf, size))) {
1546
+ rb_raise(rb_eIOError, "error reading from %s", fp->path);
1547
+ }
1548
+ if (0 == rsize) {
1549
+ break;
1550
+ }
1551
+ buf[rsize] = '\0';
1552
+ parse(fp->p, buf, rsize, true);
1553
+ }
1554
+ validate_document_end(fp->p);
1555
+
1556
+ return fp->p->result(fp->p);
1557
+ }
1558
+
1559
+ static VALUE file_close(VALUE x) {
1560
+ close(((struct _fileParse *)x)->fd);
1561
+
1562
+ return Qnil;
1563
+ }
1564
+
1423
1565
  /* Document-method: file(filename)
1424
1566
  * call-seq: file(filename)
1425
1567
  *
@@ -1428,9 +1570,10 @@ static VALUE parser_load(VALUE self, VALUE reader) {
1428
1570
  * Returns the result according to the delegate of the parser.
1429
1571
  */
1430
1572
  static VALUE parser_file(VALUE self, VALUE filename) {
1431
- ojParser p;
1432
- const char *path;
1433
- int fd;
1573
+ struct _fileParse fp;
1574
+ ojParser p;
1575
+ const char *path;
1576
+ int fd;
1434
1577
 
1435
1578
  TypedData_Get_Struct(self, struct _ojParser, &oj_parser_type, p);
1436
1579
 
@@ -1449,26 +1592,15 @@ static VALUE parser_file(VALUE self, VALUE filename) {
1449
1592
  // Use threaded version.
1450
1593
  // TBD only if has pthreads
1451
1594
  // TBD parse_large(p, fd);
1595
+ close(fd);
1452
1596
  return p->result(p);
1453
1597
  }
1454
1598
  #endif
1455
- byte buf[16385];
1456
- size_t size = sizeof(buf) - 1;
1457
- size_t rsize;
1599
+ fp.p = p;
1600
+ fp.path = path;
1601
+ fp.fd = fd;
1458
1602
 
1459
- while (true) {
1460
- if (0 < (rsize = read(fd, buf, size))) {
1461
- buf[rsize] = '\0';
1462
- parse(p, buf);
1463
- }
1464
- if (rsize <= 0) {
1465
- if (0 != rsize) {
1466
- rb_raise(rb_eIOError, "error reading from %s", path);
1467
- }
1468
- break;
1469
- }
1470
- }
1471
- return p->result(p);
1603
+ return rb_ensure(file_parse, (VALUE)&fp, file_close, (VALUE)&fp);
1472
1604
  }
1473
1605
 
1474
1606
  /* Document-method: just_one
@@ -1570,6 +1702,32 @@ static VALUE parser_validate(VALUE self) {
1570
1702
  return validate_parser;
1571
1703
  }
1572
1704
 
1705
+ static VALUE parser_safe(int argc, VALUE *argv, VALUE self) {
1706
+ VALUE options;
1707
+
1708
+ if (1 == argc) {
1709
+ options = argv[0];
1710
+
1711
+ Check_Type(options, T_HASH);
1712
+ } else {
1713
+ options = rb_hash_new();
1714
+ }
1715
+
1716
+ ojParser p = OJ_R_ALLOC(struct _ojParser);
1717
+ volatile VALUE pv;
1718
+
1719
+ memset(p, 0, sizeof(struct _ojParser));
1720
+ buf_init(&p->key);
1721
+ buf_init(&p->buf);
1722
+ p->map = value_map;
1723
+ // The limits raise on a value that is not an Integer, so the parser has to
1724
+ // be wrapped before they are read.
1725
+ pv = TypedData_Wrap_Struct(parser_class, &oj_parser_type, p);
1726
+ oj_set_parser_safe(p, options);
1727
+
1728
+ return pv;
1729
+ }
1730
+
1573
1731
  /* Document-class: Oj::Parser
1574
1732
  *
1575
1733
  * A reusable parser that makes use of named delegates to determine the
@@ -1597,4 +1755,7 @@ void oj_parser_init(void) {
1597
1755
  rb_define_module_function(parser_class, "usual", parser_usual, 0);
1598
1756
  rb_define_module_function(parser_class, "saj", parser_saj, 0);
1599
1757
  rb_define_module_function(parser_class, "validate", parser_validate, 0);
1758
+ rb_define_module_function(parser_class, "safe", parser_safe, -1);
1759
+
1760
+ oj_safe_init(parser_class);
1600
1761
  }