D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
opt
/
td-agent
/
embedded
/
lib
/
ruby
/
gems
/
2.1.0
/
gems
/
oj-2.18.1
/
ext
/
oj
/
Filename :
sparse.c
back
Copy
/* parse.c * Copyright (c) 2013, Peter Ohler * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * - Neither the name of Peter Ohler nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <math.h> #include "oj.h" #include "parse.h" #include "buf.h" #include "hash.h" // for oj_strndup() #include "val_stack.h" // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS #define OJ_INFINITY (1.0/0.0) #ifdef RUBINIUS_RUBY #define NUM_MAX 0x07FFFFFF #else #define NUM_MAX (FIXNUM_MAX >> 8) #endif #define EXP_MAX 100000 #define DEC_MAX 15 static void skip_comment(ParseInfo pi) { char c = reader_get(&pi->rd); if ('*' == c) { while ('\0' != (c = reader_get(&pi->rd))) { if ('*' == c) { c = reader_get(&pi->rd); if ('/' == c) { return; } } } } else if ('/' == c) { while ('\0' != (c = reader_get(&pi->rd))) { switch (c) { case '\n': case '\r': case '\f': case '\0': return; default: break; } } } else { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid comment format"); } if ('\0' == c) { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "comment not terminated"); return; } } static void add_value(ParseInfo pi, VALUE rval) { Val parent = stack_peek(&pi->stack); if (0 == parent) { // simple add pi->add_value(pi, rval); } else { switch (parent->next) { case NEXT_ARRAY_NEW: case NEXT_ARRAY_ELEMENT: pi->array_append_value(pi, rval); parent->next = NEXT_ARRAY_COMMA; break; case NEXT_HASH_VALUE: pi->hash_set_value(pi, parent, rval); if (parent->kalloc) { xfree((char*)parent->key); } parent->key = 0; parent->kalloc = 0; parent->next = NEXT_HASH_COMMA; break; case NEXT_HASH_NEW: case NEXT_HASH_KEY: case NEXT_HASH_COMMA: case NEXT_NONE: case NEXT_ARRAY_COMMA: case NEXT_HASH_COLON: default: oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next)); break; } } } static void add_num_value(ParseInfo pi, NumInfo ni) { Val parent = stack_peek(&pi->stack); if (0 == parent) { pi->add_num(pi, ni); } else { switch (parent->next) { case NEXT_ARRAY_NEW: case NEXT_ARRAY_ELEMENT: pi->array_append_num(pi, ni); parent->next = NEXT_ARRAY_COMMA; break; case NEXT_HASH_VALUE: pi->hash_set_num(pi, parent, ni); if (parent->kalloc) { xfree((char*)parent->key); } parent->key = 0; parent->kalloc = 0; parent->next = NEXT_HASH_COMMA; break; default: oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next)); break; } } } static void read_true(ParseInfo pi) { if (0 == reader_expect(&pi->rd, "rue")) { add_value(pi, Qtrue); } else { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected true"); } } static void read_false(ParseInfo pi) { if (0 == reader_expect(&pi->rd, "alse")) { add_value(pi, Qfalse); } else { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected false"); } } static uint32_t read_hex(ParseInfo pi) { uint32_t b = 0; int i; char c; for (i = 0; i < 4; i++) { c = reader_get(&pi->rd); b = b << 4; if ('0' <= c && c <= '9') { b += c - '0'; } else if ('A' <= c && c <= 'F') { b += c - 'A' + 10; } else if ('a' <= c && c <= 'f') { b += c - 'a' + 10; } else { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hex character"); return 0; } } return b; } static void unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) { if (0x0000007F >= code) { buf_append(buf, (char)code); } else if (0x000007FF >= code) { buf_append(buf, 0xC0 | (code >> 6)); buf_append(buf, 0x80 | (0x3F & code)); } else if (0x0000FFFF >= code) { buf_append(buf, 0xE0 | (code >> 12)); buf_append(buf, 0x80 | ((code >> 6) & 0x3F)); buf_append(buf, 0x80 | (0x3F & code)); } else if (0x001FFFFF >= code) { buf_append(buf, 0xF0 | (code >> 18)); buf_append(buf, 0x80 | ((code >> 12) & 0x3F)); buf_append(buf, 0x80 | ((code >> 6) & 0x3F)); buf_append(buf, 0x80 | (0x3F & code)); } else if (0x03FFFFFF >= code) { buf_append(buf, 0xF8 | (code >> 24)); buf_append(buf, 0x80 | ((code >> 18) & 0x3F)); buf_append(buf, 0x80 | ((code >> 12) & 0x3F)); buf_append(buf, 0x80 | ((code >> 6) & 0x3F)); buf_append(buf, 0x80 | (0x3F & code)); } else if (0x7FFFFFFF >= code) { buf_append(buf, 0xFC | (code >> 30)); buf_append(buf, 0x80 | ((code >> 24) & 0x3F)); buf_append(buf, 0x80 | ((code >> 18) & 0x3F)); buf_append(buf, 0x80 | ((code >> 12) & 0x3F)); buf_append(buf, 0x80 | ((code >> 6) & 0x3F)); buf_append(buf, 0x80 | (0x3F & code)); } else { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid Unicode character"); } } // entered at backslash static void read_escaped_str(ParseInfo pi) { struct _Buf buf; char c; uint32_t code; Val parent = stack_peek(&pi->stack); buf_init(&buf); if (pi->rd.str < pi->rd.tail) { buf_append_string(&buf, pi->rd.str, pi->rd.tail - pi->rd.str); } while ('\"' != (c = reader_get(&pi->rd))) { if ('\0' == c) { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated"); buf_cleanup(&buf); return; } else if ('\\' == c) { c = reader_get(&pi->rd); switch (c) { case 'n': buf_append(&buf, '\n'); break; case 'r': buf_append(&buf, '\r'); break; case 't': buf_append(&buf, '\t'); break; case 'f': buf_append(&buf, '\f'); break; case 'b': buf_append(&buf, '\b'); break; case '"': buf_append(&buf, '"'); break; case '/': buf_append(&buf, '/'); break; case '\\': buf_append(&buf, '\\'); break; case 'u': if (0 == (code = read_hex(pi)) && err_has(&pi->err)) { buf_cleanup(&buf); return; } if (0x0000D800 <= code && code <= 0x0000DFFF) { uint32_t c1 = (code - 0x0000D800) & 0x000003FF; uint32_t c2; char ch2; c = reader_get(&pi->rd); ch2 = reader_get(&pi->rd); if ('\\' != c || 'u' != ch2) { if (Yes == pi->options.allow_invalid) { unicode_to_chars(pi, &buf, code); reader_backup(&pi->rd); reader_backup(&pi->rd); break; } oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character"); buf_cleanup(&buf); return; } if (0 == (c2 = read_hex(pi)) && err_has(&pi->err)) { buf_cleanup(&buf); return; } c2 = (c2 - 0x0000DC00) & 0x000003FF; code = ((c1 << 10) | c2) + 0x00010000; } unicode_to_chars(pi, &buf, code); if (err_has(&pi->err)) { buf_cleanup(&buf); return; } break; default: oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character"); buf_cleanup(&buf); return; } } else { buf_append(&buf, c); } } if (0 == parent) { pi->add_cstr(pi, buf.head, buf_len(&buf), pi->rd.str); } else { switch (parent->next) { case NEXT_ARRAY_NEW: case NEXT_ARRAY_ELEMENT: pi->array_append_cstr(pi, buf.head, buf_len(&buf), pi->rd.str); parent->next = NEXT_ARRAY_COMMA; break; case NEXT_HASH_NEW: case NEXT_HASH_KEY: if (Qundef == (parent->key_val = pi->hash_key(pi, buf.head, buf_len(&buf)))) { parent->key = strdup(buf.head); parent->klen = buf_len(&buf); } else { parent->key = ""; parent->klen = 0; } parent->k1 = *pi->rd.str; parent->next = NEXT_HASH_COLON; break; case NEXT_HASH_VALUE: pi->hash_set_cstr(pi, parent, buf.head, buf_len(&buf), pi->rd.str); if (parent->kalloc) { xfree((char*)parent->key); } parent->key = 0; parent->kalloc = 0; parent->next = NEXT_HASH_COMMA; break; case NEXT_HASH_COMMA: case NEXT_NONE: case NEXT_ARRAY_COMMA: case NEXT_HASH_COLON: default: oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next)); break; } } buf_cleanup(&buf); } static void read_str(ParseInfo pi) { Val parent = stack_peek(&pi->stack); char c; reader_protect(&pi->rd); while ('\"' != (c = reader_get(&pi->rd))) { if ('\0' == c) { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated"); return; } else if ('\\' == c) { reader_backup(&pi->rd); read_escaped_str(pi); reader_release(&pi->rd); return; } } if (0 == parent) { // simple add pi->add_cstr(pi, pi->rd.str, pi->rd.tail - pi->rd.str - 1, pi->rd.str); } else { switch (parent->next) { case NEXT_ARRAY_NEW: case NEXT_ARRAY_ELEMENT: pi->array_append_cstr(pi, pi->rd.str, pi->rd.tail - pi->rd.str - 1, pi->rd.str); parent->next = NEXT_ARRAY_COMMA; break; case NEXT_HASH_NEW: case NEXT_HASH_KEY: parent->klen = pi->rd.tail - pi->rd.str - 1; if (sizeof(parent->karray) <= parent->klen) { parent->key = oj_strndup(pi->rd.str, parent->klen); parent->kalloc = 1; } else { memcpy(parent->karray, pi->rd.str, parent->klen); parent->karray[parent->klen] = '\0'; parent->key = parent->karray; parent->kalloc = 0; } parent->key_val = pi->hash_key(pi, parent->key, parent->klen); parent->k1 = *pi->rd.str; parent->next = NEXT_HASH_COLON; break; case NEXT_HASH_VALUE: pi->hash_set_cstr(pi, parent, pi->rd.str, pi->rd.tail - pi->rd.str - 1, pi->rd.str); if (parent->kalloc) { xfree((char*)parent->key); } parent->key = 0; parent->kalloc = 0; parent->next = NEXT_HASH_COMMA; break; case NEXT_HASH_COMMA: case NEXT_NONE: case NEXT_ARRAY_COMMA: case NEXT_HASH_COLON: default: oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next)); break; } } reader_release(&pi->rd); } static void read_num(ParseInfo pi) { struct _NumInfo ni; char c; reader_protect(&pi->rd); ni.i = 0; ni.num = 0; ni.div = 1; ni.di = 0; ni.len = 0; ni.exp = 0; ni.big = 0; ni.infinity = 0; ni.nan = 0; ni.neg = 0; ni.hasExp = 0; ni.no_big = (FloatDec == pi->options.bigdec_load); c = reader_get(&pi->rd); if ('-' == c) { c = reader_get(&pi->rd); ni.neg = 1; } else if ('+' == c) { c = reader_get(&pi->rd); } if ('I' == c) { if (0 != reader_expect(&pi->rd, "nfinity")) { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value"); return; } ni.infinity = 1; } else { int dec_cnt = 0; for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) { if (0 < ni.i) { dec_cnt++; } if (ni.big) { ni.big++; } else { int d = (c - '0'); ni.i = ni.i * 10 + d; if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) { ni.big = 1; } } } if ('.' == c) { c = reader_get(&pi->rd); for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) { int d = (c - '0'); if (0 < ni.num || 0 < ni.i) { dec_cnt++; } ni.num = ni.num * 10 + d; ni.div *= 10; ni.di++; if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) { ni.big = 1; } } } if ('e' == c || 'E' == c) { int eneg = 0; ni.hasExp = 1; c = reader_get(&pi->rd); if ('-' == c) { c = reader_get(&pi->rd); eneg = 1; } else if ('+' == c) { c = reader_get(&pi->rd); } for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) { ni.exp = ni.exp * 10 + (c - '0'); if (EXP_MAX <= ni.exp) { ni.big = 1; } } if (eneg) { ni.exp = -ni.exp; } } ni.len = pi->rd.tail - pi->rd.str; if (0 != c) { reader_backup(&pi->rd); } } ni.str = pi->rd.str; ni.len = pi->rd.tail - pi->rd.str; // Check for special reserved values for Infinity and NaN. if (ni.big) { if (0 == strcasecmp(INF_VAL, ni.str)) { ni.infinity = 1; } else if (0 == strcasecmp(NINF_VAL, ni.str)) { ni.infinity = 1; ni.neg = 1; } else if (0 == strcasecmp(NAN_VAL, ni.str)) { ni.nan = 1; } } if (BigDec == pi->options.bigdec_load) { ni.big = 1; } add_num_value(pi, &ni); reader_release(&pi->rd); } static void read_nan(ParseInfo pi) { struct _NumInfo ni; char c; ni.str = pi->rd.str; ni.i = 0; ni.num = 0; ni.div = 1; ni.di = 0; ni.len = 0; ni.exp = 0; ni.big = 0; ni.infinity = 0; ni.nan = 1; ni.neg = 0; ni.no_big = (FloatDec == pi->options.bigdec_load); if ('a' != reader_get(&pi->rd) || ('N' != (c = reader_get(&pi->rd)) && 'n' != c)) { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value"); return; } if (BigDec == pi->options.bigdec_load) { ni.big = 1; } add_num_value(pi, &ni); } static void array_start(ParseInfo pi) { VALUE v = pi->start_array(pi); stack_push(&pi->stack, v, NEXT_ARRAY_NEW); } static void array_end(ParseInfo pi) { Val array = stack_pop(&pi->stack); if (0 == array) { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected array close"); } else if (NEXT_ARRAY_COMMA != array->next && NEXT_ARRAY_NEW != array->next) { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not an array close", oj_stack_next_string(array->next)); } else { pi->end_array(pi); add_value(pi, array->val); } } static void hash_start(ParseInfo pi) { volatile VALUE v = pi->start_hash(pi); stack_push(&pi->stack, v, NEXT_HASH_NEW); } static void hash_end(ParseInfo pi) { volatile Val hash = stack_peek(&pi->stack); // leave hash on stack until just before if (0 == hash) { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected hash close"); } else if (NEXT_HASH_COMMA != hash->next && NEXT_HASH_NEW != hash->next) { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a hash close", oj_stack_next_string(hash->next)); } else { pi->end_hash(pi); stack_pop(&pi->stack); add_value(pi, hash->val); } } static void comma(ParseInfo pi) { Val parent = stack_peek(&pi->stack); if (0 == parent) { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma"); } else if (NEXT_ARRAY_COMMA == parent->next) { parent->next = NEXT_ARRAY_ELEMENT; } else if (NEXT_HASH_COMMA == parent->next) { parent->next = NEXT_HASH_KEY; } else { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma"); } } static void colon(ParseInfo pi) { Val parent = stack_peek(&pi->stack); if (0 != parent && NEXT_HASH_COLON == parent->next) { parent->next = NEXT_HASH_VALUE; } else { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected colon"); } } void oj_sparse2(ParseInfo pi) { int first = 1; char c; err_init(&pi->err); while (1) { c = reader_next_non_white(&pi->rd); if (!first && '\0' != c) { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected characters after the JSON document"); } switch (c) { case '{': hash_start(pi); break; case '}': hash_end(pi); break; case ':': colon(pi); break; case '[': array_start(pi); break; case ']': array_end(pi); break; case ',': comma(pi); break; case '"': read_str(pi); break; case '+': case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'I': reader_backup(&pi->rd); read_num(pi); break; case 'N': read_nan(pi); break; case 't': read_true(pi); break; case 'f': read_false(pi); break; case 'n': c = reader_get(&pi->rd); if ('u' == c) { if (0 == reader_expect(&pi->rd, "ll")) { add_value(pi, Qnil); } else { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected null"); return; } } else if ('a' == c) { struct _NumInfo ni; c = reader_get(&pi->rd); if ('N' != c && 'n' != c) { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected NaN"); return; } ni.str = pi->rd.str; ni.i = 0; ni.num = 0; ni.div = 1; ni.di = 0; ni.len = 0; ni.exp = 0; ni.big = 0; ni.infinity = 0; ni.nan = 1; ni.neg = 0; ni.no_big = (FloatDec == pi->options.bigdec_load); add_num_value(pi, &ni); } else { oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid token"); return; } break; case '/': skip_comment(pi); break; case '\0': return; default: oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character"); return; } if (err_has(&pi->err)) { return; } if (stack_empty(&pi->stack)) { if (Qundef != pi->proc) { if (Qnil == pi->proc) { rb_yield(stack_head_val(&pi->stack)); } else { #if HAS_PROC_WITH_BLOCK VALUE args[1]; *args = stack_head_val(&pi->stack); rb_proc_call_with_block(pi->proc, 1, args, Qnil); #else oj_set_error_at(pi, rb_eNotImpError, __FILE__, __LINE__, "Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead."); return; #endif } } else { first = 0; } } } } static VALUE protect_parse(VALUE pip) { oj_sparse2((ParseInfo)pip); return Qnil; } VALUE oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd) { volatile VALUE input; volatile VALUE wrapped_stack; VALUE result = Qnil; int line = 0; if (argc < 1) { rb_raise(rb_eArgError, "Wrong number of arguments to parse."); } input = argv[0]; if (2 == argc) { oj_parse_options(argv[1], &pi->options); } if (Qnil == input && Yes == pi->options.nilnil) { return Qnil; } if (rb_block_given_p()) { pi->proc = Qnil; } else { pi->proc = Qundef; } oj_reader_init(&pi->rd, input, fd); pi->json = 0; // indicates reader is in use if (Yes == pi->options.circular) { pi->circ_array = oj_circ_array_new(); } else { pi->circ_array = 0; } if (No == pi->options.allow_gc) { rb_gc_disable(); } // GC can run at any time. When it runs any Object created by C will be // freed. We protect against this by wrapping the value stack in a ruby // data object and poviding a mark function for ruby objects on the // value stack (while it is in scope). wrapped_stack = oj_stack_init(&pi->stack); rb_protect(protect_parse, (VALUE)pi, &line); result = stack_head_val(&pi->stack); DATA_PTR(wrapped_stack) = 0; if (No == pi->options.allow_gc) { rb_gc_enable(); } if (!err_has(&pi->err)) { // If the stack is not empty then the JSON terminated early. Val v; if (0 != (v = stack_peek(&pi->stack))) { switch (v->next) { case NEXT_ARRAY_NEW: case NEXT_ARRAY_ELEMENT: case NEXT_ARRAY_COMMA: oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Array not terminated"); break; case NEXT_HASH_NEW: case NEXT_HASH_KEY: case NEXT_HASH_COLON: case NEXT_HASH_VALUE: case NEXT_HASH_COMMA: oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Hash/Object not terminated"); break; default: oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not terminated"); } } } // proceed with cleanup if (0 != pi->circ_array) { oj_circ_array_free(pi->circ_array); } stack_cleanup(&pi->stack); if (0 != fd) { close(fd); } if (0 != line) { rb_jump_tag(line); } if (err_has(&pi->err)) { if (Qnil != pi->err_class) { pi->err.clas = pi->err_class; } oj_err_raise(&pi->err); } return result; }