@@ -35,7 +35,7 @@ static ID i_chr, i_aset, i_aref,
35
35
i_leftshift , i_new , i_try_convert , i_uminus , i_encode ;
36
36
37
37
static VALUE sym_max_nesting , sym_allow_nan , sym_allow_trailing_comma , sym_symbolize_names , sym_freeze ,
38
- sym_decimal_class , sym_on_load ;
38
+ sym_decimal_class , sym_on_load , sym_allow_duplicate_key ;
39
39
40
40
static int binary_encindex ;
41
41
static int utf8_encindex ;
@@ -363,10 +363,17 @@ static int convert_UTF32_to_UTF8(char *buf, uint32_t ch)
363
363
return len ;
364
364
}
365
365
366
+ enum duplicate_key_action {
367
+ JSON_DEPRECATED = 0 ,
368
+ JSON_IGNORE ,
369
+ JSON_RAISE ,
370
+ };
371
+
366
372
typedef struct JSON_ParserStruct {
367
373
VALUE on_load_proc ;
368
374
VALUE decimal_class ;
369
375
ID decimal_method_id ;
376
+ enum duplicate_key_action on_duplicate_key ;
370
377
int max_nesting ;
371
378
bool allow_nan ;
372
379
bool allow_trailing_comma ;
@@ -386,15 +393,8 @@ typedef struct JSON_ParserStateStruct {
386
393
int current_nesting ;
387
394
} JSON_ParserState ;
388
395
389
-
390
- #define PARSE_ERROR_FRAGMENT_LEN 32
391
- #ifdef RBIMPL_ATTR_NORETURN
392
- RBIMPL_ATTR_NORETURN ()
393
- #endif
394
- static void raise_parse_error (const char * format , JSON_ParserState * state )
396
+ static void cursor_position (JSON_ParserState * state , long * line_out , long * column_out )
395
397
{
396
- unsigned char buffer [PARSE_ERROR_FRAGMENT_LEN + 3 ];
397
-
398
398
const char * cursor = state -> cursor ;
399
399
long column = 0 ;
400
400
long line = 1 ;
@@ -411,6 +411,27 @@ static void raise_parse_error(const char *format, JSON_ParserState *state)
411
411
line ++ ;
412
412
}
413
413
}
414
+ * line_out = line ;
415
+ * column_out = column ;
416
+ }
417
+
418
+ static void emit_parse_warning (const char * message , JSON_ParserState * state )
419
+ {
420
+ long line , column ;
421
+ cursor_position (state , & line , & column );
422
+
423
+ rb_warn ("%s at line %ld column %ld" , message , line , column );
424
+ }
425
+
426
+ #define PARSE_ERROR_FRAGMENT_LEN 32
427
+ #ifdef RBIMPL_ATTR_NORETURN
428
+ RBIMPL_ATTR_NORETURN ()
429
+ #endif
430
+ static void raise_parse_error (const char * format , JSON_ParserState * state )
431
+ {
432
+ unsigned char buffer [PARSE_ERROR_FRAGMENT_LEN + 3 ];
433
+ long line , column ;
434
+ cursor_position (state , & line , & column );
414
435
415
436
const char * ptr = "EOF" ;
416
437
if (state -> cursor && state -> cursor < state -> end ) {
@@ -807,11 +828,25 @@ static inline VALUE json_decode_array(JSON_ParserState *state, JSON_ParserConfig
807
828
return array ;
808
829
}
809
830
810
- static inline VALUE json_decode_object (JSON_ParserState * state , JSON_ParserConfig * config , long count )
831
+ static inline VALUE json_decode_object (JSON_ParserState * state , JSON_ParserConfig * config , size_t count )
811
832
{
812
- VALUE object = rb_hash_new_capa (count );
833
+ size_t entries_count = count / 2 ;
834
+ VALUE object = rb_hash_new_capa (entries_count );
813
835
rb_hash_bulk_insert (count , rvalue_stack_peek (state -> stack , count ), object );
814
836
837
+ if (RB_UNLIKELY (RHASH_SIZE (object ) < entries_count )) {
838
+ switch (config -> on_duplicate_key ) {
839
+ case JSON_IGNORE :
840
+ break ;
841
+ case JSON_DEPRECATED :
842
+ emit_parse_warning ("detected duplicate keys in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`" , state );
843
+ break ;
844
+ case JSON_RAISE :
845
+ raise_parse_error ("duplicate key" , state );
846
+ break ;
847
+ }
848
+ }
849
+
815
850
rvalue_stack_pop (state -> stack , count );
816
851
817
852
if (config -> freeze ) {
@@ -1060,6 +1095,8 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
1060
1095
break ;
1061
1096
}
1062
1097
case '{' : {
1098
+ const char * object_start_cursor = state -> cursor ;
1099
+
1063
1100
state -> cursor ++ ;
1064
1101
json_eat_whitespace (state );
1065
1102
long stack_head = state -> stack -> head ;
@@ -1094,8 +1131,15 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
1094
1131
if (* state -> cursor == '}' ) {
1095
1132
state -> cursor ++ ;
1096
1133
state -> current_nesting -- ;
1097
- long count = state -> stack -> head - stack_head ;
1098
- return json_push_value (state , config , json_decode_object (state , config , count ));
1134
+ size_t count = state -> stack -> head - stack_head ;
1135
+
1136
+ // Temporary rewind cursor in case an error is raised
1137
+ const char * final_cursor = state -> cursor ;
1138
+ state -> cursor = object_start_cursor ;
1139
+ VALUE object = json_decode_object (state , config , count );
1140
+ state -> cursor = final_cursor ;
1141
+
1142
+ return json_push_value (state , config , object );
1099
1143
}
1100
1144
1101
1145
if (* state -> cursor == ',' ) {
@@ -1184,6 +1228,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
1184
1228
else if (key == sym_symbolize_names ) { config -> symbolize_names = RTEST (val ); }
1185
1229
else if (key == sym_freeze ) { config -> freeze = RTEST (val ); }
1186
1230
else if (key == sym_on_load ) { config -> on_load_proc = RTEST (val ) ? val : Qfalse ; }
1231
+ else if (key == sym_allow_duplicate_key ) { config -> on_duplicate_key = RTEST (val ) ? JSON_IGNORE : JSON_RAISE ; }
1187
1232
else if (key == sym_decimal_class ) {
1188
1233
if (RTEST (val )) {
1189
1234
if (rb_respond_to (val , i_try_convert )) {
@@ -1400,6 +1445,7 @@ void Init_parser(void)
1400
1445
sym_freeze = ID2SYM (rb_intern ("freeze" ));
1401
1446
sym_on_load = ID2SYM (rb_intern ("on_load" ));
1402
1447
sym_decimal_class = ID2SYM (rb_intern ("decimal_class" ));
1448
+ sym_allow_duplicate_key = ID2SYM (rb_intern ("allow_duplicate_key" ));
1403
1449
1404
1450
i_chr = rb_intern ("chr" );
1405
1451
i_aset = rb_intern ("[]=" );
0 commit comments