oj 3.12.0 → 3.13.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.
- checksums.yaml +4 -4
- data/README.md +1 -2
- data/ext/oj/buf.h +9 -0
- data/ext/oj/cache.c +187 -0
- data/ext/oj/cache.h +20 -0
- data/ext/oj/compat.c +10 -16
- data/ext/oj/custom.c +13 -12
- data/ext/oj/debug.c +131 -0
- data/ext/oj/dump.c +49 -52
- data/ext/oj/dump_compat.c +3 -3
- data/ext/oj/dump_object.c +7 -7
- data/ext/oj/dump_strict.c +3 -3
- data/ext/oj/err.h +19 -0
- data/ext/oj/extconf.rb +4 -0
- data/ext/oj/hash_test.c +3 -30
- data/ext/oj/intern.c +398 -0
- data/ext/oj/intern.h +27 -0
- data/ext/oj/mimic_json.c +9 -9
- data/ext/oj/object.c +10 -58
- data/ext/oj/odd.c +1 -1
- data/ext/oj/oj.c +172 -107
- data/ext/oj/oj.h +2 -2
- data/ext/oj/parse.c +4 -4
- data/ext/oj/parser.c +1527 -0
- data/ext/oj/parser.h +90 -0
- data/ext/oj/rails.c +4 -4
- data/ext/oj/resolve.c +2 -20
- data/ext/oj/saj2.c +346 -0
- data/ext/oj/scp.c +1 -1
- data/ext/oj/sparse.c +1 -1
- data/ext/oj/stream_writer.c +3 -3
- data/ext/oj/strict.c +10 -27
- data/ext/oj/usual.c +1222 -0
- data/ext/oj/validate.c +50 -0
- data/ext/oj/wab.c +15 -16
- data/lib/oj/mimic.rb +2 -0
- data/lib/oj/version.rb +1 -1
- data/pages/Modes.md +2 -0
- data/pages/Options.md +23 -5
- data/pages/Parser.md +309 -0
- data/test/foo.rb +2 -9
- data/test/json_gem/json_common_interface_test.rb +1 -1
- data/test/perf_parser.rb +184 -0
- data/test/test_parser.rb +27 -0
- data/test/test_parser_saj.rb +245 -0
- data/test/test_parser_usual.rb +213 -0
- metadata +23 -5
- data/ext/oj/hash.c +0 -146
- data/ext/oj/hash.h +0 -21
    
        data/ext/oj/parser.h
    ADDED
    
    | @@ -0,0 +1,90 @@ | |
| 1 | 
            +
            // Copyright (c) 2021 Peter Ohler, All rights reserved.
         | 
| 2 | 
            +
            // Licensed under the MIT License. See LICENSE file in the project root for license details.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            #ifndef OJ_PARSER_H
         | 
| 5 | 
            +
            #define OJ_PARSER_H
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            #include <stdbool.h>
         | 
| 8 | 
            +
            #include <ruby.h>
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            #include "buf.h"
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            #define TOP_FUN 0
         | 
| 13 | 
            +
            #define ARRAY_FUN 1
         | 
| 14 | 
            +
            #define OBJECT_FUN 2
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            typedef uint8_t byte;
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            typedef enum {
         | 
| 19 | 
            +
                OJ_NONE    = '\0',
         | 
| 20 | 
            +
                OJ_NULL    = 'n',
         | 
| 21 | 
            +
                OJ_TRUE    = 't',
         | 
| 22 | 
            +
                OJ_FALSE   = 'f',
         | 
| 23 | 
            +
                OJ_INT     = 'i',
         | 
| 24 | 
            +
                OJ_DECIMAL = 'd',
         | 
| 25 | 
            +
                OJ_BIG     = 'b',  // indicates parser buf is used
         | 
| 26 | 
            +
                OJ_STRING  = 's',
         | 
| 27 | 
            +
                OJ_OBJECT  = 'o',
         | 
| 28 | 
            +
                OJ_ARRAY   = 'a',
         | 
| 29 | 
            +
            } ojType;
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            typedef struct _num {
         | 
| 32 | 
            +
                long double dub;
         | 
| 33 | 
            +
                int64_t     fixnum;  // holds all digits
         | 
| 34 | 
            +
                uint32_t    len;
         | 
| 35 | 
            +
                int16_t     div;  // 10^div
         | 
| 36 | 
            +
                int16_t     exp;
         | 
| 37 | 
            +
                uint8_t     shift;  // shift of fixnum to get decimal
         | 
| 38 | 
            +
                bool        neg;
         | 
| 39 | 
            +
                bool        exp_neg;
         | 
| 40 | 
            +
                // for numbers as strings, reuse buf
         | 
| 41 | 
            +
            } * Num;
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            struct _ojParser;
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            typedef struct _funcs {
         | 
| 46 | 
            +
                void (*add_null)(struct _ojParser *p);
         | 
| 47 | 
            +
                void (*add_true)(struct _ojParser *p);
         | 
| 48 | 
            +
                void (*add_false)(struct _ojParser *p);
         | 
| 49 | 
            +
                void (*add_int)(struct _ojParser *p);
         | 
| 50 | 
            +
                void (*add_float)(struct _ojParser *p);
         | 
| 51 | 
            +
                void (*add_big)(struct _ojParser *p);
         | 
| 52 | 
            +
                void (*add_str)(struct _ojParser *p);
         | 
| 53 | 
            +
                void (*open_array)(struct _ojParser *p);
         | 
| 54 | 
            +
                void (*close_array)(struct _ojParser *p);
         | 
| 55 | 
            +
                void (*open_object)(struct _ojParser *p);
         | 
| 56 | 
            +
                void (*close_object)(struct _ojParser *p);
         | 
| 57 | 
            +
            } * Funcs;
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            typedef struct _ojParser {
         | 
| 60 | 
            +
                const char *  map;
         | 
| 61 | 
            +
                const char *  next_map;
         | 
| 62 | 
            +
                int           depth;
         | 
| 63 | 
            +
                unsigned char stack[1024];
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                // value data
         | 
| 66 | 
            +
                struct _num num;
         | 
| 67 | 
            +
                struct _buf key;
         | 
| 68 | 
            +
                struct _buf buf;
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                struct _funcs funcs[3]; // indexed by XXX_FUN defines
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                void (*start)(struct _ojParser *p);
         | 
| 73 | 
            +
                VALUE (*option)(struct _ojParser *p, const char *key, VALUE value);
         | 
| 74 | 
            +
                VALUE (*result)(struct _ojParser *p);
         | 
| 75 | 
            +
                void (*free)(struct _ojParser *p);
         | 
| 76 | 
            +
                void (*mark)(struct _ojParser *p);
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                void *ctx;
         | 
| 79 | 
            +
                VALUE reader;
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                char     token[8];
         | 
| 82 | 
            +
                long     line;
         | 
| 83 | 
            +
                long     col;
         | 
| 84 | 
            +
                int      ri;
         | 
| 85 | 
            +
                uint32_t ucode;
         | 
| 86 | 
            +
                ojType   type;  // valType
         | 
| 87 | 
            +
                bool     just_one;
         | 
| 88 | 
            +
            } * ojParser;
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            #endif /* OJ_PARSER_H */
         | 
    
        data/ext/oj/rails.c
    CHANGED
    
    | @@ -159,7 +159,7 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) { | |
| 159 159 | 
             
                for (i = 0; i < cnt; i++) {
         | 
| 160 160 | 
             
                    volatile VALUE s = rb_sym_to_s(rb_ary_entry(ma, i));
         | 
| 161 161 |  | 
| 162 | 
            -
                    name =  | 
| 162 | 
            +
                    name = RSTRING_PTR(s);
         | 
| 163 163 | 
             
                    len  = (int)RSTRING_LEN(s);
         | 
| 164 164 | 
             
                    assure_size(out, size + sep_len + 6);
         | 
| 165 165 | 
             
                    if (0 < i) {
         | 
| @@ -202,7 +202,7 @@ static void dump_enumerable(VALUE obj, int depth, Out out, bool as_ok) { | |
| 202 202 |  | 
| 203 203 | 
             
            static void dump_bigdecimal(VALUE obj, int depth, Out out, bool as_ok) {
         | 
| 204 204 | 
             
                volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
         | 
| 205 | 
            -
                const char *   str  =  | 
| 205 | 
            +
                const char *   str  = RSTRING_PTR(rstr);
         | 
| 206 206 |  | 
| 207 207 | 
             
                if ('I' == *str || 'N' == *str || ('-' == *str && 'I' == str[1])) {
         | 
| 208 208 | 
             
                    oj_dump_nil(Qnil, depth, out, false);
         | 
| @@ -355,7 +355,7 @@ static void dump_timewithzone(VALUE obj, int depth, Out out, bool as_ok) { | |
| 355 355 | 
             
            static void dump_to_s(VALUE obj, int depth, Out out, bool as_ok) {
         | 
| 356 356 | 
             
                volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
         | 
| 357 357 |  | 
| 358 | 
            -
                oj_dump_cstr( | 
| 358 | 
            +
                oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
         | 
| 359 359 | 
             
            }
         | 
| 360 360 |  | 
| 361 361 | 
             
            static ID parameters_id = 0;
         | 
| @@ -1224,7 +1224,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) { | |
| 1224 1224 | 
             
                    } else {
         | 
| 1225 1225 | 
             
                        volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
         | 
| 1226 1226 |  | 
| 1227 | 
            -
                        strcpy(buf,  | 
| 1227 | 
            +
                        strcpy(buf, RSTRING_PTR(rstr));
         | 
| 1228 1228 | 
             
                        cnt = (int)RSTRING_LEN(rstr);
         | 
| 1229 1229 | 
             
                    }
         | 
| 1230 1230 | 
             
                }
         | 
    
        data/ext/oj/resolve.c
    CHANGED
    
    | @@ -9,7 +9,7 @@ | |
| 9 9 | 
             
            #endif
         | 
| 10 10 |  | 
| 11 11 | 
             
            #include "err.h"
         | 
| 12 | 
            -
            #include " | 
| 12 | 
            +
            #include "intern.h"
         | 
| 13 13 | 
             
            #include "oj.h"
         | 
| 14 14 | 
             
            #include "parse.h"
         | 
| 15 15 |  | 
| @@ -66,28 +66,10 @@ resolve_classpath(ParseInfo pi, const char *name, size_t len, int auto_define, V | |
| 66 66 |  | 
| 67 67 | 
             
            VALUE
         | 
| 68 68 | 
             
            oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE error_class) {
         | 
| 69 | 
            -
                VALUE  clas;
         | 
| 70 | 
            -
                VALUE *slot;
         | 
| 71 | 
            -
             | 
| 72 69 | 
             
                if (No == pi->options.class_cache) {
         | 
| 73 70 | 
             
                    return resolve_classpath(pi, name, len, auto_define, error_class);
         | 
| 74 71 | 
             
                }
         | 
| 75 | 
            -
             | 
| 76 | 
            -
                pthread_mutex_lock(&oj_cache_mutex);
         | 
| 77 | 
            -
            #else
         | 
| 78 | 
            -
                rb_mutex_lock(oj_cache_mutex);
         | 
| 79 | 
            -
            #endif
         | 
| 80 | 
            -
                if (Qnil == (clas = oj_class_hash_get(name, len, &slot))) {
         | 
| 81 | 
            -
                    if (Qundef != (clas = resolve_classpath(pi, name, len, auto_define, error_class))) {
         | 
| 82 | 
            -
                        *slot = clas;
         | 
| 83 | 
            -
                    }
         | 
| 84 | 
            -
                }
         | 
| 85 | 
            -
            #ifdef HAVE_PTHREAD_MUTEX_INIT
         | 
| 86 | 
            -
                pthread_mutex_unlock(&oj_cache_mutex);
         | 
| 87 | 
            -
            #else
         | 
| 88 | 
            -
                rb_mutex_unlock(oj_cache_mutex);
         | 
| 89 | 
            -
            #endif
         | 
| 90 | 
            -
                return clas;
         | 
| 72 | 
            +
                return oj_class_intern(name, len, true, pi, auto_define, error_class);
         | 
| 91 73 | 
             
            }
         | 
| 92 74 |  | 
| 93 75 | 
             
            VALUE
         | 
    
        data/ext/oj/saj2.c
    ADDED
    
    | @@ -0,0 +1,346 @@ | |
| 1 | 
            +
            // Copyright (c) 2021, Peter Ohler, All rights reserved.
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            #include "cache.h"
         | 
| 4 | 
            +
            #include "oj.h"
         | 
| 5 | 
            +
            #include "parser.h"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            typedef struct _delegate {
         | 
| 8 | 
            +
                VALUE          handler;
         | 
| 9 | 
            +
                VALUE *        keys;
         | 
| 10 | 
            +
                VALUE *        tail;
         | 
| 11 | 
            +
                size_t         klen;
         | 
| 12 | 
            +
                struct _cache *str_cache;
         | 
| 13 | 
            +
                uint8_t        cache_str;
         | 
| 14 | 
            +
                bool           cache_keys;
         | 
| 15 | 
            +
                bool           thread_safe;
         | 
| 16 | 
            +
            } * Delegate;
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            static VALUE get_key(ojParser p) {
         | 
| 19 | 
            +
                Delegate       d   = (Delegate)p->ctx;
         | 
| 20 | 
            +
                const char *   key = buf_str(&p->key);
         | 
| 21 | 
            +
                size_t         len = buf_len(&p->key);
         | 
| 22 | 
            +
                volatile VALUE rkey;
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                if (d->cache_keys) {
         | 
| 25 | 
            +
                    rkey = cache_intern(d->str_cache, key, len);
         | 
| 26 | 
            +
                } else {
         | 
| 27 | 
            +
                    rkey = rb_utf8_str_new(key, len);
         | 
| 28 | 
            +
                }
         | 
| 29 | 
            +
                return rkey;
         | 
| 30 | 
            +
            }
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            static void push_key(Delegate d, VALUE key) {
         | 
| 33 | 
            +
                if (d->klen <= (size_t)(d->tail - d->keys)) {
         | 
| 34 | 
            +
                    size_t off = d->tail - d->keys;
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    d->klen += d->klen / 2;
         | 
| 37 | 
            +
                    REALLOC_N(d->keys, VALUE, d->klen);
         | 
| 38 | 
            +
                    d->tail = d->keys + off;
         | 
| 39 | 
            +
                }
         | 
| 40 | 
            +
                *d->tail = key;
         | 
| 41 | 
            +
                d->tail++;
         | 
| 42 | 
            +
            }
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            static void noop(ojParser p) {
         | 
| 45 | 
            +
            }
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            static void open_object(ojParser p) {
         | 
| 48 | 
            +
                rb_funcall(((Delegate)p->ctx)->handler, oj_hash_start_id, 1, Qnil);
         | 
| 49 | 
            +
            }
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            static void open_object_key(ojParser p) {
         | 
| 52 | 
            +
                Delegate       d   = (Delegate)p->ctx;
         | 
| 53 | 
            +
                volatile VALUE key = get_key(p);
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                push_key(d, key);
         | 
| 56 | 
            +
                rb_funcall(d->handler, oj_hash_start_id, 1, key);
         | 
| 57 | 
            +
            }
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            static void open_array(ojParser p) {
         | 
| 60 | 
            +
                rb_funcall(((Delegate)p->ctx)->handler, oj_array_start_id, 1, Qnil);
         | 
| 61 | 
            +
            }
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            static void open_array_key(ojParser p) {
         | 
| 64 | 
            +
                Delegate       d   = (Delegate)p->ctx;
         | 
| 65 | 
            +
                volatile VALUE key = get_key(p);
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                push_key(d, key);
         | 
| 68 | 
            +
                rb_funcall(d->handler, oj_array_start_id, 1, key);
         | 
| 69 | 
            +
            }
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            static void close_object(ojParser p) {
         | 
| 72 | 
            +
                Delegate d   = (Delegate)p->ctx;
         | 
| 73 | 
            +
                VALUE    key = Qnil;
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                if (OBJECT_FUN == p->stack[p->depth]) {
         | 
| 76 | 
            +
                    d->tail--;
         | 
| 77 | 
            +
                    if (d->tail < d->keys) {
         | 
| 78 | 
            +
                        rb_raise(rb_eIndexError, "accessing key stack");
         | 
| 79 | 
            +
                    }
         | 
| 80 | 
            +
                    key = *d->tail;
         | 
| 81 | 
            +
                }
         | 
| 82 | 
            +
                rb_funcall(d->handler, oj_hash_end_id, 1, key);
         | 
| 83 | 
            +
            }
         | 
| 84 | 
            +
             | 
| 85 | 
            +
            static void close_array(ojParser p) {
         | 
| 86 | 
            +
                Delegate d   = (Delegate)p->ctx;
         | 
| 87 | 
            +
                VALUE    key = Qnil;
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                if (OBJECT_FUN == p->stack[p->depth]) {
         | 
| 90 | 
            +
                    d->tail--;
         | 
| 91 | 
            +
                    if (d->tail < d->keys) {
         | 
| 92 | 
            +
                        rb_raise(rb_eIndexError, "accessing key stack");
         | 
| 93 | 
            +
                    }
         | 
| 94 | 
            +
                    key = *d->tail;
         | 
| 95 | 
            +
                }
         | 
| 96 | 
            +
                rb_funcall(d->handler, oj_array_end_id, 1, key);
         | 
| 97 | 
            +
            }
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            static void add_null(ojParser p) {
         | 
| 100 | 
            +
                rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, Qnil, Qnil);
         | 
| 101 | 
            +
            }
         | 
| 102 | 
            +
             | 
| 103 | 
            +
            static void add_null_key(ojParser p) {
         | 
| 104 | 
            +
                rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, Qnil, get_key(p));
         | 
| 105 | 
            +
            }
         | 
| 106 | 
            +
             | 
| 107 | 
            +
            static void add_true(ojParser p) {
         | 
| 108 | 
            +
                rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, Qtrue, Qnil);
         | 
| 109 | 
            +
            }
         | 
| 110 | 
            +
             | 
| 111 | 
            +
            static void add_true_key(ojParser p) {
         | 
| 112 | 
            +
                rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, Qtrue, get_key(p));
         | 
| 113 | 
            +
            }
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            static void add_false(ojParser p) {
         | 
| 116 | 
            +
                rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, Qfalse, Qnil);
         | 
| 117 | 
            +
            }
         | 
| 118 | 
            +
             | 
| 119 | 
            +
            static void add_false_key(ojParser p) {
         | 
| 120 | 
            +
                rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, Qfalse, get_key(p));
         | 
| 121 | 
            +
            }
         | 
| 122 | 
            +
             | 
| 123 | 
            +
            static void add_int(ojParser p) {
         | 
| 124 | 
            +
                rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, LONG2NUM(p->num.fixnum), Qnil);
         | 
| 125 | 
            +
            }
         | 
| 126 | 
            +
             | 
| 127 | 
            +
            static void add_int_key(ojParser p) {
         | 
| 128 | 
            +
                rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, LONG2NUM(p->num.fixnum), get_key(p));
         | 
| 129 | 
            +
            }
         | 
| 130 | 
            +
             | 
| 131 | 
            +
            static void add_float(ojParser p) {
         | 
| 132 | 
            +
                rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, rb_float_new(p->num.dub), Qnil);
         | 
| 133 | 
            +
            }
         | 
| 134 | 
            +
             | 
| 135 | 
            +
            static void add_float_key(ojParser p) {
         | 
| 136 | 
            +
                rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, rb_float_new(p->num.dub), get_key(p));
         | 
| 137 | 
            +
            }
         | 
| 138 | 
            +
             | 
| 139 | 
            +
            static void add_big(ojParser p) {
         | 
| 140 | 
            +
                rb_funcall((VALUE)p->ctx,
         | 
| 141 | 
            +
                           oj_add_value_id,
         | 
| 142 | 
            +
                           2,
         | 
| 143 | 
            +
                           rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(buf_str(&p->buf), buf_len(&p->buf))),
         | 
| 144 | 
            +
                           Qnil);
         | 
| 145 | 
            +
            }
         | 
| 146 | 
            +
             | 
| 147 | 
            +
            static void add_big_key(ojParser p) {
         | 
| 148 | 
            +
                rb_funcall((VALUE)p->ctx,
         | 
| 149 | 
            +
                           oj_add_value_id,
         | 
| 150 | 
            +
                           2,
         | 
| 151 | 
            +
                           rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(buf_str(&p->buf), buf_len(&p->buf))),
         | 
| 152 | 
            +
                           get_key(p));
         | 
| 153 | 
            +
            }
         | 
| 154 | 
            +
             | 
| 155 | 
            +
            static void add_str(ojParser p) {
         | 
| 156 | 
            +
                Delegate       d = (Delegate)p->ctx;
         | 
| 157 | 
            +
                volatile VALUE rstr;
         | 
| 158 | 
            +
                const char *   str = buf_str(&p->buf);
         | 
| 159 | 
            +
                size_t         len = buf_len(&p->buf);
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                if (d->cache_str <= len) {
         | 
| 162 | 
            +
                    rstr = cache_intern(d->str_cache, str, len);
         | 
| 163 | 
            +
                } else {
         | 
| 164 | 
            +
                    rstr = rb_utf8_str_new(str, len);
         | 
| 165 | 
            +
                }
         | 
| 166 | 
            +
                rb_funcall(d->handler, oj_add_value_id, 2, rstr, Qnil);
         | 
| 167 | 
            +
            }
         | 
| 168 | 
            +
             | 
| 169 | 
            +
            static void add_str_key(ojParser p) {
         | 
| 170 | 
            +
                Delegate       d = (Delegate)p->ctx;
         | 
| 171 | 
            +
                volatile VALUE rstr;
         | 
| 172 | 
            +
                const char *   str = buf_str(&p->buf);
         | 
| 173 | 
            +
                size_t         len = buf_len(&p->buf);
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                if (d->cache_str <= len) {
         | 
| 176 | 
            +
                    rstr = cache_intern(d->str_cache, str, len);
         | 
| 177 | 
            +
                } else {
         | 
| 178 | 
            +
                    rstr = rb_utf8_str_new(str, len);
         | 
| 179 | 
            +
                }
         | 
| 180 | 
            +
                rb_funcall(d->handler, oj_add_value_id, 2, rstr, get_key(p));
         | 
| 181 | 
            +
            }
         | 
| 182 | 
            +
             | 
| 183 | 
            +
            static void reset(ojParser p) {
         | 
| 184 | 
            +
                Funcs end = p->funcs + 3;
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                for (Funcs f = p->funcs; f < end; f++) {
         | 
| 187 | 
            +
                    f->add_null     = noop;
         | 
| 188 | 
            +
                    f->add_true     = noop;
         | 
| 189 | 
            +
                    f->add_false    = noop;
         | 
| 190 | 
            +
                    f->add_int      = noop;
         | 
| 191 | 
            +
                    f->add_float    = noop;
         | 
| 192 | 
            +
                    f->add_big      = noop;
         | 
| 193 | 
            +
                    f->add_str      = noop;
         | 
| 194 | 
            +
                    f->open_array   = noop;
         | 
| 195 | 
            +
                    f->close_array  = noop;
         | 
| 196 | 
            +
                    f->open_object  = noop;
         | 
| 197 | 
            +
                    f->close_object = noop;
         | 
| 198 | 
            +
                }
         | 
| 199 | 
            +
            }
         | 
| 200 | 
            +
             | 
| 201 | 
            +
            static VALUE option(ojParser p, const char *key, VALUE value) {
         | 
| 202 | 
            +
                Delegate d = (Delegate)p->ctx;
         | 
| 203 | 
            +
             | 
| 204 | 
            +
                if (0 == strcmp(key, "handler")) {
         | 
| 205 | 
            +
                    return d->handler;
         | 
| 206 | 
            +
                }
         | 
| 207 | 
            +
                if (0 == strcmp(key, "handler=")) {
         | 
| 208 | 
            +
                    d->tail    = d->keys;
         | 
| 209 | 
            +
                    d->handler = value;
         | 
| 210 | 
            +
                    reset(p);
         | 
| 211 | 
            +
                    if (rb_respond_to(value, oj_hash_start_id)) {
         | 
| 212 | 
            +
                        p->funcs[TOP_FUN].open_object    = open_object;
         | 
| 213 | 
            +
                        p->funcs[ARRAY_FUN].open_object  = open_object;
         | 
| 214 | 
            +
                        p->funcs[OBJECT_FUN].open_object = open_object_key;
         | 
| 215 | 
            +
                    }
         | 
| 216 | 
            +
                    if (rb_respond_to(value, oj_array_start_id)) {
         | 
| 217 | 
            +
                        p->funcs[TOP_FUN].open_array    = open_array;
         | 
| 218 | 
            +
                        p->funcs[ARRAY_FUN].open_array  = open_array;
         | 
| 219 | 
            +
                        p->funcs[OBJECT_FUN].open_array = open_array_key;
         | 
| 220 | 
            +
                    }
         | 
| 221 | 
            +
                    if (rb_respond_to(value, oj_hash_end_id)) {
         | 
| 222 | 
            +
                        p->funcs[TOP_FUN].close_object    = close_object;
         | 
| 223 | 
            +
                        p->funcs[ARRAY_FUN].close_object  = close_object;
         | 
| 224 | 
            +
                        p->funcs[OBJECT_FUN].close_object = close_object;
         | 
| 225 | 
            +
                    }
         | 
| 226 | 
            +
                    if (rb_respond_to(value, oj_array_end_id)) {
         | 
| 227 | 
            +
                        p->funcs[TOP_FUN].close_array    = close_array;
         | 
| 228 | 
            +
                        p->funcs[ARRAY_FUN].close_array  = close_array;
         | 
| 229 | 
            +
                        p->funcs[OBJECT_FUN].close_array = close_array;
         | 
| 230 | 
            +
                    }
         | 
| 231 | 
            +
                    if (rb_respond_to(value, oj_add_value_id)) {
         | 
| 232 | 
            +
                        p->funcs[TOP_FUN].add_null    = add_null;
         | 
| 233 | 
            +
                        p->funcs[ARRAY_FUN].add_null  = add_null;
         | 
| 234 | 
            +
                        p->funcs[OBJECT_FUN].add_null = add_null_key;
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                        p->funcs[TOP_FUN].add_true    = add_true;
         | 
| 237 | 
            +
                        p->funcs[ARRAY_FUN].add_true  = add_true;
         | 
| 238 | 
            +
                        p->funcs[OBJECT_FUN].add_true = add_true_key;
         | 
| 239 | 
            +
             | 
| 240 | 
            +
                        p->funcs[TOP_FUN].add_false    = add_false;
         | 
| 241 | 
            +
                        p->funcs[ARRAY_FUN].add_false  = add_false;
         | 
| 242 | 
            +
                        p->funcs[OBJECT_FUN].add_false = add_false_key;
         | 
| 243 | 
            +
             | 
| 244 | 
            +
                        p->funcs[TOP_FUN].add_int    = add_int;
         | 
| 245 | 
            +
                        p->funcs[ARRAY_FUN].add_int  = add_int;
         | 
| 246 | 
            +
                        p->funcs[OBJECT_FUN].add_int = add_int_key;
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                        p->funcs[TOP_FUN].add_float    = add_float;
         | 
| 249 | 
            +
                        p->funcs[ARRAY_FUN].add_float  = add_float;
         | 
| 250 | 
            +
                        p->funcs[OBJECT_FUN].add_float = add_float_key;
         | 
| 251 | 
            +
             | 
| 252 | 
            +
                        p->funcs[TOP_FUN].add_big    = add_big;
         | 
| 253 | 
            +
                        p->funcs[ARRAY_FUN].add_big  = add_big;
         | 
| 254 | 
            +
                        p->funcs[OBJECT_FUN].add_big = add_big_key;
         | 
| 255 | 
            +
             | 
| 256 | 
            +
                        p->funcs[TOP_FUN].add_str    = add_str;
         | 
| 257 | 
            +
                        p->funcs[ARRAY_FUN].add_str  = add_str;
         | 
| 258 | 
            +
                        p->funcs[OBJECT_FUN].add_str = add_str_key;
         | 
| 259 | 
            +
                    }
         | 
| 260 | 
            +
                    return Qnil;
         | 
| 261 | 
            +
                }
         | 
| 262 | 
            +
                if (0 == strcmp(key, "cache_keys")) {
         | 
| 263 | 
            +
                    return d->cache_keys ? Qtrue : Qfalse;
         | 
| 264 | 
            +
                }
         | 
| 265 | 
            +
                if (0 == strcmp(key, "cache_keys=")) {
         | 
| 266 | 
            +
                    d->cache_keys = (Qtrue == value);
         | 
| 267 | 
            +
             | 
| 268 | 
            +
                    return d->cache_keys ? Qtrue : Qfalse;
         | 
| 269 | 
            +
                }
         | 
| 270 | 
            +
                if (0 == strcmp(key, "cache_strings")) {
         | 
| 271 | 
            +
                    return INT2NUM((int)d->cache_str);
         | 
| 272 | 
            +
                }
         | 
| 273 | 
            +
                if (0 == strcmp(key, "cache_strings=")) {
         | 
| 274 | 
            +
                    int limit = NUM2INT(value);
         | 
| 275 | 
            +
             | 
| 276 | 
            +
                    if (CACHE_MAX_KEY < limit) {
         | 
| 277 | 
            +
                        limit = CACHE_MAX_KEY;
         | 
| 278 | 
            +
                    } else if (limit < 0) {
         | 
| 279 | 
            +
                        limit = 0;
         | 
| 280 | 
            +
                    }
         | 
| 281 | 
            +
                    d->cache_str = limit;
         | 
| 282 | 
            +
             | 
| 283 | 
            +
                    return INT2NUM((int)d->cache_str);
         | 
| 284 | 
            +
                }
         | 
| 285 | 
            +
                rb_raise(rb_eArgError, "%s is not an option for the SAJ (Simple API for JSON) delegate", key);
         | 
| 286 | 
            +
             | 
| 287 | 
            +
                return Qnil;  // Never reached due to the raise but required by the compiler.
         | 
| 288 | 
            +
            }
         | 
| 289 | 
            +
             | 
| 290 | 
            +
            static VALUE result(ojParser p) {
         | 
| 291 | 
            +
                return Qnil;
         | 
| 292 | 
            +
            }
         | 
| 293 | 
            +
             | 
| 294 | 
            +
            static void start(ojParser p) {
         | 
| 295 | 
            +
                Delegate d = (Delegate)p->ctx;
         | 
| 296 | 
            +
             | 
| 297 | 
            +
                d->tail = d->keys;
         | 
| 298 | 
            +
            }
         | 
| 299 | 
            +
             | 
| 300 | 
            +
            static void dfree(ojParser p) {
         | 
| 301 | 
            +
                Delegate d = (Delegate)p->ctx;
         | 
| 302 | 
            +
             | 
| 303 | 
            +
                if (NULL != d->keys) {
         | 
| 304 | 
            +
                    xfree(d->keys);
         | 
| 305 | 
            +
                }
         | 
| 306 | 
            +
                cache_free(d->str_cache);
         | 
| 307 | 
            +
                xfree(p->ctx);
         | 
| 308 | 
            +
            }
         | 
| 309 | 
            +
             | 
| 310 | 
            +
            static void mark(ojParser p) {
         | 
| 311 | 
            +
                if (NULL == p->ctx) {
         | 
| 312 | 
            +
                    return;
         | 
| 313 | 
            +
                }
         | 
| 314 | 
            +
                Delegate d = (Delegate)p->ctx;
         | 
| 315 | 
            +
             | 
| 316 | 
            +
                cache_mark(d->str_cache);
         | 
| 317 | 
            +
                if (Qnil != d->handler) {
         | 
| 318 | 
            +
                    rb_gc_mark(d->handler);
         | 
| 319 | 
            +
                }
         | 
| 320 | 
            +
                if (!d->cache_keys) {
         | 
| 321 | 
            +
                    for (VALUE *kp = d->keys; kp < d->tail; kp++) {
         | 
| 322 | 
            +
                        rb_gc_mark(*kp);
         | 
| 323 | 
            +
                    }
         | 
| 324 | 
            +
                }
         | 
| 325 | 
            +
            }
         | 
| 326 | 
            +
             | 
| 327 | 
            +
            static VALUE form_str(const char *str, size_t len) {
         | 
| 328 | 
            +
                return rb_str_freeze(rb_utf8_str_new(str, len));
         | 
| 329 | 
            +
            }
         | 
| 330 | 
            +
             | 
| 331 | 
            +
            void oj_set_parser_saj(ojParser p) {
         | 
| 332 | 
            +
                Delegate d = ALLOC(struct _delegate);
         | 
| 333 | 
            +
             | 
| 334 | 
            +
                d->klen      = 256;
         | 
| 335 | 
            +
                d->keys      = ALLOC_N(VALUE, d->klen);
         | 
| 336 | 
            +
                d->tail      = d->keys;
         | 
| 337 | 
            +
                d->str_cache = cache_create(0, form_str, true);
         | 
| 338 | 
            +
             | 
| 339 | 
            +
                p->ctx = (void *)d;
         | 
| 340 | 
            +
                reset(p);
         | 
| 341 | 
            +
                p->option = option;
         | 
| 342 | 
            +
                p->result = result;
         | 
| 343 | 
            +
                p->free   = dfree;
         | 
| 344 | 
            +
                p->mark   = mark;
         | 
| 345 | 
            +
                p->start  = start;
         | 
| 346 | 
            +
            }
         |