#include "../fbuffer/fbuffer.h" #include "parser.h" /* unicode */ static const signed char digit_values[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; static uint32_t unescape_unicode(const unsigned char *p) { const uint32_t replacement_char = 0xFFFD; signed char b; uint32_t result = 0; b = digit_values[p[0]]; if (b < 0) return replacement_char; result = (result << 4) | (unsigned char)b; b = digit_values[p[1]]; if (b < 0) return replacement_char; result = (result << 4) | (unsigned char)b; b = digit_values[p[2]]; if (b < 0) return replacement_char; result = (result << 4) | (unsigned char)b; b = digit_values[p[3]]; if (b < 0) return replacement_char; result = (result << 4) | (unsigned char)b; return result; } static int convert_UTF32_to_UTF8(char *buf, uint32_t ch) { int len = 1; if (ch <= 0x7F) { buf[0] = (char) ch; } else if (ch <= 0x07FF) { buf[0] = (char) ((ch >> 6) | 0xC0); buf[1] = (char) ((ch & 0x3F) | 0x80); len++; } else if (ch <= 0xFFFF) { buf[0] = (char) ((ch >> 12) | 0xE0); buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80); buf[2] = (char) ((ch & 0x3F) | 0x80); len += 2; } else if (ch <= 0x1fffff) { buf[0] =(char) ((ch >> 18) | 0xF0); buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80); buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80); buf[3] =(char) ((ch & 0x3F) | 0x80); len += 3; } else { buf[0] = '?'; } return len; } #define PARSE_ERROR_FRAGMENT_LEN 32 #ifdef RBIMPL_ATTR_NORETURN RBIMPL_ATTR_NORETURN() #endif static void raise_parse_error(const char *format, const char *start) { char buffer[PARSE_ERROR_FRAGMENT_LEN + 1]; size_t len = strnlen(start, PARSE_ERROR_FRAGMENT_LEN); const char *ptr = start; if (len == PARSE_ERROR_FRAGMENT_LEN) { MEMCPY(buffer, start, char, PARSE_ERROR_FRAGMENT_LEN); buffer[PARSE_ERROR_FRAGMENT_LEN] = '\0'; ptr = buffer; } rb_enc_raise(rb_utf8_encoding(), rb_path2class("JSON::ParserError"), format, ptr); } static VALUE mJSON, mExt, cParser, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_object_class, i_array_class, i_decimal_class, i_deep_const_get, i_match, i_match_string, i_aset, i_aref, i_leftshift, i_new, i_try_convert, i_freeze, i_uminus; static int binary_encindex; static int utf8_encindex; %%{ machine JSON_common; cr = '\n'; cr_neg = [^\n]; ws = [ \t\r\n]; c_comment = '/*' ( any* - (any* '*/' any* ) ) '*/'; cpp_comment = '//' cr_neg* cr; comment = c_comment | cpp_comment; ignore = ws | comment; name_separator = ':'; value_separator = ','; Vnull = 'null'; Vfalse = 'false'; Vtrue = 'true'; VNaN = 'NaN'; VInfinity = 'Infinity'; VMinusInfinity = '-Infinity'; begin_value = [nft\"\-\[\{NI] | digit; begin_object = '{'; end_object = '}'; begin_array = '['; end_array = ']'; begin_string = '"'; begin_name = begin_string; begin_number = digit | '-'; }%% %%{ machine JSON_object; include JSON_common; write data; action parse_value { VALUE v = Qnil; char *np = JSON_parse_value(json, fpc, pe, &v, current_nesting); if (np == NULL) { fhold; fbreak; } else { if (NIL_P(json->object_class)) { OBJ_FREEZE(last_name); rb_hash_aset(*result, last_name, v); } else { rb_funcall(*result, i_aset, 2, last_name, v); } fexec np; } } action parse_name { char *np; json->parsing_name = 1; np = JSON_parse_string(json, fpc, pe, &last_name); json->parsing_name = 0; if (np == NULL) { fhold; fbreak; } else fexec np; } action exit { fhold; fbreak; } pair = ignore* begin_name >parse_name ignore* name_separator ignore* begin_value >parse_value; next_pair = ignore* value_separator pair; main := ( begin_object (pair (next_pair)*)? ignore* end_object ) @exit; }%% static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { int cs = EVIL; VALUE last_name = Qnil; VALUE object_class = json->object_class; if (json->max_nesting && current_nesting > json->max_nesting) { rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); } *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class); %% write init; %% write exec; if (cs >= JSON_object_first_final) { if (json->create_additions) { VALUE klassname; if (NIL_P(json->object_class)) { klassname = rb_hash_aref(*result, json->create_id); } else { klassname = rb_funcall(*result, i_aref, 1, json->create_id); } if (!NIL_P(klassname)) { VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname); if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) { *result = rb_funcall(klass, i_json_create, 1, *result); } } } return p + 1; } else { return NULL; } } %%{ machine JSON_value; include JSON_common; write data; action parse_null { *result = Qnil; } action parse_false { *result = Qfalse; } action parse_true { *result = Qtrue; } action parse_nan { if (json->allow_nan) { *result = CNaN; } else { raise_parse_error("unexpected token at '%s'", p - 2); } } action parse_infinity { if (json->allow_nan) { *result = CInfinity; } else { raise_parse_error("unexpected token at '%s'", p - 7); } } action parse_string { char *np = JSON_parse_string(json, fpc, pe, result); if (np == NULL) { fhold; fbreak; } else fexec np; } action parse_number { char *np; if(pe > fpc + 8 && !strncmp(MinusInfinity, fpc, 9)) { if (json->allow_nan) { *result = CMinusInfinity; fexec p + 10; fhold; fbreak; } else { raise_parse_error("unexpected token at '%s'", p); } } np = JSON_parse_float(json, fpc, pe, result); if (np != NULL) fexec np; np = JSON_parse_integer(json, fpc, pe, result); if (np != NULL) fexec np; fhold; fbreak; } action parse_array { char *np; np = JSON_parse_array(json, fpc, pe, result, current_nesting + 1); if (np == NULL) { fhold; fbreak; } else fexec np; } action parse_object { char *np; np = JSON_parse_object(json, fpc, pe, result, current_nesting + 1); if (np == NULL) { fhold; fbreak; } else fexec np; } action exit { fhold; fbreak; } main := ignore* ( Vnull @parse_null | Vfalse @parse_false | Vtrue @parse_true | VNaN @parse_nan | VInfinity @parse_infinity | begin_number >parse_number | begin_string >parse_string | begin_array >parse_array | begin_object >parse_object ) ignore* %*exit; }%% static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { int cs = EVIL; %% write init; %% write exec; if (json->freeze) { OBJ_FREEZE(*result); } if (cs >= JSON_value_first_final) { return p; } else { return NULL; } } %%{ machine JSON_integer; write data; action exit { fhold; fbreak; } main := '-'? ('0' | [1-9][0-9]*) (^[0-9]? @exit); }%% static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result) { int cs = EVIL; %% write init; json->memo = p; %% write exec; if (cs >= JSON_integer_first_final) { long len = p - json->memo; fbuffer_clear(json->fbuffer); fbuffer_append(json->fbuffer, json->memo, len); fbuffer_append_char(json->fbuffer, '\0'); *result = rb_cstr2inum(FBUFFER_PTR(json->fbuffer), 10); return p + 1; } else { return NULL; } } %%{ machine JSON_float; include JSON_common; write data; action exit { fhold; fbreak; } main := '-'? ( (('0' | [1-9][0-9]*) '.' [0-9]+ ([Ee] [+\-]?[0-9]+)?) | (('0' | [1-9][0-9]*) ([Ee] [+\-]?[0-9]+)) ) (^[0-9Ee.\-]? @exit ); }%% static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result) { int cs = EVIL; %% write init; json->memo = p; %% write exec; if (cs >= JSON_float_first_final) { VALUE mod = Qnil; ID method_id = 0; if (!NIL_P(json->decimal_class)) { if (rb_respond_to(json->decimal_class, i_try_convert)) { mod = json->decimal_class; method_id = i_try_convert; } else if (rb_respond_to(json->decimal_class, i_new)) { mod = json->decimal_class; method_id = i_new; } else if (RB_TYPE_P(json->decimal_class, T_CLASS)) { VALUE name = rb_class_name(json->decimal_class); const char *name_cstr = RSTRING_PTR(name); const char *last_colon = strrchr(name_cstr, ':'); if (last_colon) { const char *mod_path_end = last_colon - 1; VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr); mod = rb_path_to_class(mod_path); const char *method_name_beg = last_colon + 1; long before_len = method_name_beg - name_cstr; long len = RSTRING_LEN(name) - before_len; VALUE method_name = rb_str_substr(name, before_len, len); method_id = SYM2ID(rb_str_intern(method_name)); } else { mod = rb_mKernel; method_id = SYM2ID(rb_str_intern(name)); } } } long len = p - json->memo; fbuffer_clear(json->fbuffer); fbuffer_append(json->fbuffer, json->memo, len); fbuffer_append_char(json->fbuffer, '\0'); if (method_id) { VALUE text = rb_str_new2(FBUFFER_PTR(json->fbuffer)); *result = rb_funcallv(mod, method_id, 1, &text); } else { *result = DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(json->fbuffer), 1)); } return p + 1; } else { return NULL; } } %%{ machine JSON_array; include JSON_common; write data; action parse_value { VALUE v = Qnil; char *np = JSON_parse_value(json, fpc, pe, &v, current_nesting); if (np == NULL) { fhold; fbreak; } else { if (NIL_P(json->array_class)) { rb_ary_push(*result, v); } else { rb_funcall(*result, i_leftshift, 1, v); } fexec np; } } action exit { fhold; fbreak; } next_element = value_separator ignore* begin_value >parse_value; main := begin_array ignore* ((begin_value >parse_value ignore*) (ignore* next_element ignore*)*)? end_array @exit; }%% static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting) { int cs = EVIL; VALUE array_class = json->array_class; if (json->max_nesting && current_nesting > json->max_nesting) { rb_raise(eNestingError, "nesting of %d is too deep", current_nesting); } *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); %% write init; %% write exec; if(cs >= JSON_array_first_final) { return p + 1; } else { raise_parse_error("unexpected token at '%s'", p); return NULL; } } static const size_t MAX_STACK_BUFFER_SIZE = 128; static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int symbolize) { VALUE result = Qnil; size_t bufferSize = stringEnd - string; char *p = string, *pe = string, *unescape, *bufferStart, *buffer; int unescape_len; char buf[4]; if (bufferSize > MAX_STACK_BUFFER_SIZE) { # ifdef HAVE_RB_ENC_INTERNED_STR bufferStart = buffer = ALLOC_N(char, bufferSize ? bufferSize : 1); # else bufferStart = buffer = ALLOC_N(char, bufferSize); # endif } else { # ifdef HAVE_RB_ENC_INTERNED_STR bufferStart = buffer = ALLOCA_N(char, bufferSize ? bufferSize : 1); # else bufferStart = buffer = ALLOCA_N(char, bufferSize); # endif } while (pe < stringEnd) { if (*pe == '\\') { unescape = (char *) "?"; unescape_len = 1; if (pe > p) { MEMCPY(buffer, p, char, pe - p); buffer += pe - p; } switch (*++pe) { case 'n': unescape = (char *) "\n"; break; case 'r': unescape = (char *) "\r"; break; case 't': unescape = (char *) "\t"; break; case '"': unescape = (char *) "\""; break; case '\\': unescape = (char *) "\\"; break; case 'b': unescape = (char *) "\b"; break; case 'f': unescape = (char *) "\f"; break; case 'u': if (pe > stringEnd - 4) { if (bufferSize > MAX_STACK_BUFFER_SIZE) { ruby_xfree(bufferStart); } raise_parse_error("incomplete unicode character escape sequence at '%s'", p); } else { uint32_t ch = unescape_unicode((unsigned char *) ++pe); pe += 3; /* To handle values above U+FFFF, we take a sequence of * \uXXXX escapes in the U+D800..U+DBFF then * U+DC00..U+DFFF ranges, take the low 10 bits from each * to make a 20-bit number, then add 0x10000 to get the * final codepoint. * * See Unicode 15: 3.8 "Surrogates", 5.3 "Handling * Surrogate Pairs in UTF-16", and 23.6 "Surrogates * Area". */ if ((ch & 0xFC00) == 0xD800) { pe++; if (pe > stringEnd - 6) { if (bufferSize > MAX_STACK_BUFFER_SIZE) { ruby_xfree(bufferStart); } raise_parse_error("incomplete surrogate pair at '%s'", p); } if (pe[0] == '\\' && pe[1] == 'u') { uint32_t sur = unescape_unicode((unsigned char *) pe + 2); ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) | (sur & 0x3FF)); pe += 5; } else { unescape = (char *) "?"; break; } } unescape_len = convert_UTF32_to_UTF8(buf, ch); unescape = buf; } break; default: p = pe; continue; } MEMCPY(buffer, unescape, char, unescape_len); buffer += unescape_len; p = ++pe; } else { pe++; } } if (pe > p) { MEMCPY(buffer, p, char, pe - p); buffer += pe - p; } # ifdef HAVE_RB_ENC_INTERNED_STR if (intern) { result = rb_enc_interned_str(bufferStart, (long)(buffer - bufferStart), rb_utf8_encoding()); } else { result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); } if (bufferSize > MAX_STACK_BUFFER_SIZE) { ruby_xfree(bufferStart); } # else result = rb_utf8_str_new(bufferStart, (long)(buffer - bufferStart)); if (bufferSize > MAX_STACK_BUFFER_SIZE) { ruby_xfree(bufferStart); } if (intern) { # if STR_UMINUS_DEDUPE_FROZEN // Starting from MRI 2.8 it is preferable to freeze the string // before deduplication so that it can be interned directly // otherwise it would be duplicated first which is wasteful. result = rb_funcall(rb_str_freeze(result), i_uminus, 0); # elif STR_UMINUS_DEDUPE // MRI 2.5 and older do not deduplicate strings that are already // frozen. result = rb_funcall(result, i_uminus, 0); # else result = rb_str_freeze(result); # endif } # endif if (symbolize) { result = rb_str_intern(result); } return result; } %%{ machine JSON_string; include JSON_common; write data; action parse_string { *result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names); if (NIL_P(*result)) { fhold; fbreak; } else { fexec p + 1; } } action exit { fhold; fbreak; } main := '"' ((^([\"\\] | 0..0x1f) | '\\'[\"\\/bfnrt] | '\\u'[0-9a-fA-F]{4} | '\\'^([\"\\/bfnrtu]|0..0x1f))* %parse_string) '"' @exit; }%% static int match_i(VALUE regexp, VALUE klass, VALUE memo) { if (regexp == Qundef) return ST_STOP; if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) && RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) { rb_ary_push(memo, klass); return ST_STOP; } return ST_CONTINUE; } static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *result) { int cs = EVIL; VALUE match_string; %% write init; json->memo = p; %% write exec; if (json->create_additions && RTEST(match_string = json->match_string)) { VALUE klass; VALUE memo = rb_ary_new2(2); rb_ary_push(memo, *result); rb_hash_foreach(match_string, match_i, memo); klass = rb_ary_entry(memo, 1); if (RTEST(klass)) { *result = rb_funcall(klass, i_json_create, 1, *result); } } if (cs >= JSON_string_first_final) { return p + 1; } else { return NULL; } } /* * Document-class: JSON::Ext::Parser * * This is the JSON parser implemented as a C extension. It can be configured * to be used by setting * * JSON.parser = JSON::Ext::Parser * * with the method parser= in JSON. * */ static VALUE convert_encoding(VALUE source) { int encindex = RB_ENCODING_GET(source); if (encindex == utf8_encindex) { return source; } if (encindex == binary_encindex) { // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work. // TODO: Deprecate in 2.8.0 // TODO: Remove in 3.0.0 return rb_enc_associate_index(rb_str_dup(source), utf8_encindex); } return rb_str_conv_enc(source, rb_enc_from_index(encindex), rb_utf8_encoding()); } /* * call-seq: new(source, opts => {}) * * Creates a new JSON::Ext::Parser instance for the string _source_. * * It will be configured by the _opts_ hash. _opts_ can have the following * keys: * * _opts_ can have the following keys: * * *max_nesting*: The maximum depth of nesting allowed in the parsed data * structures. Disable depth checking with :max_nesting => false|nil|0, it * defaults to 100. * * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in * defiance of RFC 4627 to be parsed by the Parser. This option defaults to * false. * * *symbolize_names*: If set to true, returns symbols for the names * (keys) in a JSON object. Otherwise strings are returned, which is * also the default. It's not possible to use this option in * conjunction with the *create_additions* option. * * *create_additions*: If set to false, the Parser doesn't create * additions even if a matching class and create_id was found. This option * defaults to false. * * *object_class*: Defaults to Hash. If another type is provided, it will be used * instead of Hash to represent JSON objects. The type must respond to * +new+ without arguments, and return an object that respond to +[]=+. * * *array_class*: Defaults to Array If another type is provided, it will be used * instead of Hash to represent JSON arrays. The type must respond to * +new+ without arguments, and return an object that respond to +<<+. * * *decimal_class*: Specifies which class to use instead of the default * (Float) when parsing decimal numbers. This class must accept a single * string argument in its constructor. */ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) { VALUE source, opts; GET_PARSER_INIT; if (json->Vsource) { rb_raise(rb_eTypeError, "already initialized instance"); } rb_check_arity(argc, 1, 2); source = argv[0]; opts = Qnil; if (argc == 2) { opts = argv[1]; Check_Type(argv[1], T_HASH); if (RHASH_SIZE(argv[1]) > 0) { opts = argv[1]; } } if (!NIL_P(opts)) { VALUE tmp = ID2SYM(i_max_nesting); if (option_given_p(opts, tmp)) { VALUE max_nesting = rb_hash_aref(opts, tmp); if (RTEST(max_nesting)) { Check_Type(max_nesting, T_FIXNUM); json->max_nesting = FIX2INT(max_nesting); } else { json->max_nesting = 0; } } else { json->max_nesting = 100; } tmp = ID2SYM(i_allow_nan); if (option_given_p(opts, tmp)) { json->allow_nan = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; } else { json->allow_nan = 0; } tmp = ID2SYM(i_symbolize_names); if (option_given_p(opts, tmp)) { json->symbolize_names = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; } else { json->symbolize_names = 0; } tmp = ID2SYM(i_freeze); if (option_given_p(opts, tmp)) { json->freeze = RTEST(rb_hash_aref(opts, tmp)) ? 1 : 0; } else { json->freeze = 0; } tmp = ID2SYM(i_create_additions); if (option_given_p(opts, tmp)) { json->create_additions = RTEST(rb_hash_aref(opts, tmp)); } else { json->create_additions = 0; } if (json->symbolize_names && json->create_additions) { rb_raise(rb_eArgError, "options :symbolize_names and :create_additions cannot be " " used in conjunction"); } tmp = ID2SYM(i_create_id); if (option_given_p(opts, tmp)) { json->create_id = rb_hash_aref(opts, tmp); } else { json->create_id = rb_funcall(mJSON, i_create_id, 0); } tmp = ID2SYM(i_object_class); if (option_given_p(opts, tmp)) { json->object_class = rb_hash_aref(opts, tmp); } else { json->object_class = Qnil; } tmp = ID2SYM(i_array_class); if (option_given_p(opts, tmp)) { json->array_class = rb_hash_aref(opts, tmp); } else { json->array_class = Qnil; } tmp = ID2SYM(i_decimal_class); if (option_given_p(opts, tmp)) { json->decimal_class = rb_hash_aref(opts, tmp); } else { json->decimal_class = Qnil; } tmp = ID2SYM(i_match_string); if (option_given_p(opts, tmp)) { VALUE match_string = rb_hash_aref(opts, tmp); json->match_string = RTEST(match_string) ? match_string : Qnil; } else { json->match_string = Qnil; } } else { json->max_nesting = 100; json->allow_nan = 0; json->create_additions = 0; json->create_id = Qnil; json->object_class = Qnil; json->array_class = Qnil; json->decimal_class = Qnil; } source = convert_encoding(StringValue(source)); StringValue(source); json->len = RSTRING_LEN(source); json->source = RSTRING_PTR(source);; json->Vsource = source; return self; } %%{ machine JSON; write data; include JSON_common; action parse_value { char *np = JSON_parse_value(json, fpc, pe, &result, 0); if (np == NULL) { fhold; fbreak; } else fexec np; } main := ignore* ( begin_value >parse_value ) ignore*; }%% /* * call-seq: parse() * * Parses the current JSON text _source_ and returns the complete data * structure as a result. * It raises JSON::ParserError if fail to parse. */ static VALUE cParser_parse(VALUE self) { char *p, *pe; int cs = EVIL; VALUE result = Qnil; GET_PARSER; %% write init; p = json->source; pe = p + json->len; %% write exec; if (cs >= JSON_first_final && p == pe) { return result; } else { raise_parse_error("unexpected token at '%s'", p); return Qnil; } } static void JSON_mark(void *ptr) { JSON_Parser *json = ptr; rb_gc_mark(json->Vsource); rb_gc_mark(json->create_id); rb_gc_mark(json->object_class); rb_gc_mark(json->array_class); rb_gc_mark(json->decimal_class); rb_gc_mark(json->match_string); } static void JSON_free(void *ptr) { JSON_Parser *json = ptr; fbuffer_free(json->fbuffer); ruby_xfree(json); } static size_t JSON_memsize(const void *ptr) { const JSON_Parser *json = ptr; return sizeof(*json) + FBUFFER_CAPA(json->fbuffer); } static const rb_data_type_t JSON_Parser_type = { "JSON/Parser", {JSON_mark, JSON_free, JSON_memsize,}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static VALUE cJSON_parser_s_allocate(VALUE klass) { JSON_Parser *json; VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json); json->fbuffer = fbuffer_alloc(0); return obj; } /* * call-seq: source() * * Returns a copy of the current _source_ string, that was used to construct * this Parser. */ static VALUE cParser_source(VALUE self) { GET_PARSER; return rb_str_dup(json->Vsource); } void Init_parser(void) { #ifdef HAVE_RB_EXT_RACTOR_SAFE rb_ext_ractor_safe(true); #endif #undef rb_intern rb_require("json/common"); mJSON = rb_define_module("JSON"); mExt = rb_define_module_under(mJSON, "Ext"); cParser = rb_define_class_under(mExt, "Parser", rb_cObject); eNestingError = rb_path2class("JSON::NestingError"); rb_gc_register_mark_object(eNestingError); rb_define_alloc_func(cParser, cJSON_parser_s_allocate); rb_define_method(cParser, "initialize", cParser_initialize, -1); rb_define_method(cParser, "parse", cParser_parse, 0); rb_define_method(cParser, "source", cParser_source, 0); CNaN = rb_const_get(mJSON, rb_intern("NaN")); rb_gc_register_mark_object(CNaN); CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); rb_gc_register_mark_object(CInfinity); CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity")); rb_gc_register_mark_object(CMinusInfinity); i_json_creatable_p = rb_intern("json_creatable?"); i_json_create = rb_intern("json_create"); i_create_id = rb_intern("create_id"); i_create_additions = rb_intern("create_additions"); i_chr = rb_intern("chr"); i_max_nesting = rb_intern("max_nesting"); i_allow_nan = rb_intern("allow_nan"); i_symbolize_names = rb_intern("symbolize_names"); i_object_class = rb_intern("object_class"); i_array_class = rb_intern("array_class"); i_decimal_class = rb_intern("decimal_class"); i_match = rb_intern("match"); i_match_string = rb_intern("match_string"); i_deep_const_get = rb_intern("deep_const_get"); i_aset = rb_intern("[]="); i_aref = rb_intern("[]"); i_leftshift = rb_intern("<<"); i_new = rb_intern("new"); i_try_convert = rb_intern("try_convert"); i_freeze = rb_intern("freeze"); i_uminus = rb_intern("-@"); binary_encindex = rb_ascii8bit_encindex(); utf8_encindex = rb_utf8_encindex(); } /* * Local variables: * mode: c * c-file-style: ruby * indent-tabs-mode: nil * End: */