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/dump.c
    CHANGED
    
    | @@ -472,7 +472,7 @@ void oj_dump_time(VALUE obj, Out out, int withZone) { | |
| 472 472 | 
             
            void oj_dump_ruby_time(VALUE obj, Out out) {
         | 
| 473 473 | 
             
                volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
         | 
| 474 474 |  | 
| 475 | 
            -
                oj_dump_cstr( | 
| 475 | 
            +
                oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
         | 
| 476 476 | 
             
            }
         | 
| 477 477 |  | 
| 478 478 | 
             
            void oj_dump_xml_time(VALUE obj, Out out) {
         | 
| @@ -535,59 +535,57 @@ void oj_dump_xml_time(VALUE obj, Out out) { | |
| 535 535 | 
             
                }
         | 
| 536 536 | 
             
                if ((0 == nsec && !out->opts->sec_prec_set) || 0 == out->opts->sec_prec) {
         | 
| 537 537 | 
             
                    if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
         | 
| 538 | 
            -
                        sprintf(buf,
         | 
| 539 | 
            -
             | 
| 540 | 
            -
             | 
| 541 | 
            -
             | 
| 542 | 
            -
             | 
| 543 | 
            -
             | 
| 544 | 
            -
             | 
| 545 | 
            -
             | 
| 546 | 
            -
                        oj_dump_cstr(buf,  | 
| 538 | 
            +
                        int len = sprintf(buf,
         | 
| 539 | 
            +
                                          "%04d-%02d-%02dT%02d:%02d:%02dZ",
         | 
| 540 | 
            +
                                          ti.year,
         | 
| 541 | 
            +
                                          ti.mon,
         | 
| 542 | 
            +
                                          ti.day,
         | 
| 543 | 
            +
                                          ti.hour,
         | 
| 544 | 
            +
                                          ti.min,
         | 
| 545 | 
            +
                                          ti.sec);
         | 
| 546 | 
            +
                        oj_dump_cstr(buf, len, 0, 0, out);
         | 
| 547 547 | 
             
                    } else {
         | 
| 548 | 
            -
                        sprintf(buf,
         | 
| 549 | 
            -
             | 
| 550 | 
            -
             | 
| 551 | 
            -
             | 
| 552 | 
            -
             | 
| 553 | 
            -
             | 
| 554 | 
            -
             | 
| 555 | 
            -
             | 
| 556 | 
            -
             | 
| 557 | 
            -
             | 
| 558 | 
            -
             | 
| 559 | 
            -
                        oj_dump_cstr(buf,  | 
| 548 | 
            +
                        int len = sprintf(buf,
         | 
| 549 | 
            +
                                          "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
         | 
| 550 | 
            +
                                          ti.year,
         | 
| 551 | 
            +
                                          ti.mon,
         | 
| 552 | 
            +
                                          ti.day,
         | 
| 553 | 
            +
                                          ti.hour,
         | 
| 554 | 
            +
                                          ti.min,
         | 
| 555 | 
            +
                                          ti.sec,
         | 
| 556 | 
            +
                                          tzsign,
         | 
| 557 | 
            +
                                          tzhour,
         | 
| 558 | 
            +
                                          tzmin);
         | 
| 559 | 
            +
                        oj_dump_cstr(buf, len, 0, 0, out);
         | 
| 560 560 | 
             
                    }
         | 
| 561 561 | 
             
                } else if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
         | 
| 562 562 | 
             
                    char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
         | 
| 563 | 
            -
                    int  len | 
| 563 | 
            +
                    int  len;
         | 
| 564 564 |  | 
| 565 565 | 
             
                    if (9 > out->opts->sec_prec) {
         | 
| 566 566 | 
             
                        format[32] = '0' + out->opts->sec_prec;
         | 
| 567 | 
            -
                        len -= 9 - out->opts->sec_prec;
         | 
| 568 567 | 
             
                    }
         | 
| 569 | 
            -
                    sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec);
         | 
| 568 | 
            +
                    len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec);
         | 
| 570 569 | 
             
                    oj_dump_cstr(buf, len, 0, 0, out);
         | 
| 571 570 | 
             
                } else {
         | 
| 572 571 | 
             
                    char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
         | 
| 573 | 
            -
                    int  len | 
| 572 | 
            +
                    int  len;
         | 
| 574 573 |  | 
| 575 574 | 
             
                    if (9 > out->opts->sec_prec) {
         | 
| 576 575 | 
             
                        format[32] = '0' + out->opts->sec_prec;
         | 
| 577 | 
            -
                        len -= 9 - out->opts->sec_prec;
         | 
| 578 576 | 
             
                    }
         | 
| 579 | 
            -
                    sprintf(buf,
         | 
| 580 | 
            -
             | 
| 581 | 
            -
             | 
| 582 | 
            -
             | 
| 583 | 
            -
             | 
| 584 | 
            -
             | 
| 585 | 
            -
             | 
| 586 | 
            -
             | 
| 587 | 
            -
             | 
| 588 | 
            -
             | 
| 589 | 
            -
             | 
| 590 | 
            -
             | 
| 577 | 
            +
                    len = sprintf(buf,
         | 
| 578 | 
            +
                                  format,
         | 
| 579 | 
            +
                                  ti.year,
         | 
| 580 | 
            +
                                  ti.mon,
         | 
| 581 | 
            +
                                  ti.day,
         | 
| 582 | 
            +
                                  ti.hour,
         | 
| 583 | 
            +
                                  ti.min,
         | 
| 584 | 
            +
                                  ti.sec,
         | 
| 585 | 
            +
                                  (long)nsec,
         | 
| 586 | 
            +
                                  tzsign,
         | 
| 587 | 
            +
                                  tzhour,
         | 
| 588 | 
            +
                                  tzmin);
         | 
| 591 589 | 
             
                    oj_dump_cstr(buf, len, 0, 0, out);
         | 
| 592 590 | 
             
                }
         | 
| 593 591 | 
             
            }
         | 
| @@ -710,12 +708,12 @@ void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts) { | |
| 710 708 | 
             
            }
         | 
| 711 709 |  | 
| 712 710 | 
             
            void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
         | 
| 713 | 
            -
                rb_encoding *enc =  | 
| 711 | 
            +
                rb_encoding *enc = rb_enc_get(obj);
         | 
| 714 712 |  | 
| 715 | 
            -
                if ( | 
| 716 | 
            -
                    obj = rb_str_conv_enc(obj, enc,  | 
| 713 | 
            +
                if (oj_utf8_encoding != enc) {
         | 
| 714 | 
            +
                    obj = rb_str_conv_enc(obj, enc, oj_utf8_encoding);
         | 
| 717 715 | 
             
                }
         | 
| 718 | 
            -
                oj_dump_cstr( | 
| 716 | 
            +
                oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
         | 
| 719 717 | 
             
            }
         | 
| 720 718 |  | 
| 721 719 | 
             
            void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
         | 
| @@ -724,7 +722,7 @@ void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) { | |
| 724 722 |  | 
| 725 723 | 
             
                volatile VALUE s = rb_sym_to_s(obj);
         | 
| 726 724 |  | 
| 727 | 
            -
                oj_dump_cstr( | 
| 725 | 
            +
                oj_dump_cstr(RSTRING_PTR(s), (int)RSTRING_LEN(s), 0, 0, out);
         | 
| 728 726 | 
             
            }
         | 
| 729 727 |  | 
| 730 728 | 
             
            static void debug_raise(const char *orig, size_t cnt, int line) {
         | 
| @@ -762,7 +760,7 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) { | |
| 762 760 | 
             
                    if (Yes == out->opts->trace) {
         | 
| 763 761 | 
             
                        oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
         | 
| 764 762 | 
             
                    }
         | 
| 765 | 
            -
                    oj_dump_raw( | 
| 763 | 
            +
                    oj_dump_raw(RSTRING_PTR(jv), (size_t)RSTRING_LEN(jv), out);
         | 
| 766 764 | 
             
                }
         | 
| 767 765 | 
             
            }
         | 
| 768 766 |  | 
| @@ -827,9 +825,8 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou | |
| 827 825 | 
             
                    if (is_sym) {
         | 
| 828 826 | 
             
                        *out->cur++ = ':';
         | 
| 829 827 | 
             
                    }
         | 
| 830 | 
            -
                     | 
| 831 | 
            -
             | 
| 832 | 
            -
                    }
         | 
| 828 | 
            +
                    memcpy(out->cur, str, cnt);
         | 
| 829 | 
            +
                    out->cur += cnt;
         | 
| 833 830 | 
             
                    *out->cur++ = '"';
         | 
| 834 831 | 
             
                } else {
         | 
| 835 832 | 
             
                    const char *end         = str + cnt;
         | 
| @@ -961,7 +958,7 @@ void oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) { | |
| 961 958 | 
             
            void oj_dump_obj_to_s(VALUE obj, Out out) {
         | 
| 962 959 | 
             
                volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
         | 
| 963 960 |  | 
| 964 | 
            -
                oj_dump_cstr( | 
| 961 | 
            +
                oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
         | 
| 965 962 | 
             
            }
         | 
| 966 963 |  | 
| 967 964 | 
             
            void oj_dump_raw(const char *str, size_t cnt, Out out) {
         | 
| @@ -1078,7 +1075,7 @@ void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) { | |
| 1078 1075 | 
             
                } else {
         | 
| 1079 1076 | 
             
                    assure_size(out, cnt);
         | 
| 1080 1077 | 
             
                }
         | 
| 1081 | 
            -
                memcpy(out->cur,  | 
| 1078 | 
            +
                memcpy(out->cur, RSTRING_PTR(rs), cnt);
         | 
| 1082 1079 | 
             
                out->cur += cnt;
         | 
| 1083 1080 | 
             
                if (dump_as_string) {
         | 
| 1084 1081 | 
             
                    *out->cur++ = '"';
         | 
| @@ -1206,7 +1203,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) { | |
| 1206 1203 | 
             
                    if ((int)sizeof(buf) <= cnt) {
         | 
| 1207 1204 | 
             
                        cnt = sizeof(buf) - 1;
         | 
| 1208 1205 | 
             
                    }
         | 
| 1209 | 
            -
                     | 
| 1206 | 
            +
                    memcpy(buf, RSTRING_PTR(rstr), cnt);
         | 
| 1210 1207 | 
             
                    buf[cnt] = '\0';
         | 
| 1211 1208 | 
             
                } else {
         | 
| 1212 1209 | 
             
                    cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, out->opts->float_fmt);
         | 
| @@ -1226,7 +1223,7 @@ int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char | |
| 1226 1223 | 
             
                if (17 <= cnt && (0 == strcmp("0001", buf + cnt - 4) || 0 == strcmp("9999", buf + cnt - 4))) {
         | 
| 1227 1224 | 
             
                    volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
         | 
| 1228 1225 |  | 
| 1229 | 
            -
                    strcpy(buf,  | 
| 1226 | 
            +
                    strcpy(buf, RSTRING_PTR(rstr));
         | 
| 1230 1227 | 
             
                    cnt = (int)RSTRING_LEN(rstr);
         | 
| 1231 1228 | 
             
                }
         | 
| 1232 1229 | 
             
                return cnt;
         | 
    
        data/ext/oj/dump_compat.c
    CHANGED
    
    | @@ -129,7 +129,7 @@ dump_to_json(VALUE obj, Out out) { | |
| 129 129 | 
             
            	oj_trace("to_json", obj, __FILE__, __LINE__, 0, TraceRubyOut);
         | 
| 130 130 | 
             
                }
         | 
| 131 131 |  | 
| 132 | 
            -
                s =  | 
| 132 | 
            +
                s = RSTRING_PTR(rs);
         | 
| 133 133 | 
             
                len = (int)RSTRING_LEN(rs);
         | 
| 134 134 |  | 
| 135 135 | 
             
                assure_size(out, len + 1);
         | 
| @@ -635,7 +635,7 @@ dump_float(VALUE obj, int depth, Out out, bool as_ok) { | |
| 635 635 | 
             
                } else {
         | 
| 636 636 | 
             
            	volatile VALUE	rstr = rb_funcall(obj, oj_to_s_id, 0);
         | 
| 637 637 |  | 
| 638 | 
            -
            	strcpy(buf,  | 
| 638 | 
            +
            	strcpy(buf, RSTRING_PTR(rstr));
         | 
| 639 639 | 
             
            	cnt = (int)RSTRING_LEN(rstr);
         | 
| 640 640 | 
             
                }
         | 
| 641 641 | 
             
                assure_size(out, cnt);
         | 
| @@ -886,7 +886,7 @@ dump_bignum(VALUE obj, int depth, Out out, bool as_ok) { | |
| 886 886 | 
             
                } else {
         | 
| 887 887 | 
             
            	assure_size(out, cnt);
         | 
| 888 888 | 
             
                }
         | 
| 889 | 
            -
                memcpy(out->cur,  | 
| 889 | 
            +
                memcpy(out->cur, RSTRING_PTR(rs), cnt);
         | 
| 890 890 | 
             
                out->cur += cnt;
         | 
| 891 891 | 
             
                if (dump_as_string) {
         | 
| 892 892 | 
             
            	*out->cur++ = '"';
         | 
    
        data/ext/oj/dump_object.c
    CHANGED
    
    | @@ -36,7 +36,7 @@ static void dump_data(VALUE obj, int depth, Out out, bool as_ok) { | |
| 36 36 | 
             
                } else {
         | 
| 37 37 | 
             
                    if (oj_bigdecimal_class == clas) {
         | 
| 38 38 | 
             
                        volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
         | 
| 39 | 
            -
                        const char *   str  =  | 
| 39 | 
            +
                        const char *   str  = RSTRING_PTR(rstr);
         | 
| 40 40 | 
             
                        int            len  = (int)RSTRING_LEN(rstr);
         | 
| 41 41 |  | 
| 42 42 | 
             
                        if (No != out->opts->bigdec_as_num) {
         | 
| @@ -65,7 +65,7 @@ static void dump_obj(VALUE obj, int depth, Out out, bool as_ok) { | |
| 65 65 |  | 
| 66 66 | 
             
                if (oj_bigdecimal_class == clas) {
         | 
| 67 67 | 
             
                    volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
         | 
| 68 | 
            -
                    const char *   str  =  | 
| 68 | 
            +
                    const char *   str  = RSTRING_PTR(rstr);
         | 
| 69 69 | 
             
                    int            len  = (int)RSTRING_LEN(rstr);
         | 
| 70 70 |  | 
| 71 71 | 
             
                    if (0 == strcasecmp("Infinity", str)) {
         | 
| @@ -195,7 +195,7 @@ static void dump_str_class(VALUE obj, VALUE clas, int depth, Out out) { | |
| 195 195 | 
             
                if (Qundef != clas && rb_cString != clas) {
         | 
| 196 196 | 
             
                    dump_obj_attrs(obj, clas, 0, depth, out);
         | 
| 197 197 | 
             
                } else {
         | 
| 198 | 
            -
                    const char *s   =  | 
| 198 | 
            +
                    const char *s   = RSTRING_PTR(obj);
         | 
| 199 199 | 
             
                    size_t      len = (int)RSTRING_LEN(obj);
         | 
| 200 200 | 
             
                    char        s1  = s[1];
         | 
| 201 201 |  | 
| @@ -210,7 +210,7 @@ static void dump_str(VALUE obj, int depth, Out out, bool as_ok) { | |
| 210 210 | 
             
            static void dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
         | 
| 211 211 | 
             
                volatile VALUE s = rb_sym_to_s(obj);
         | 
| 212 212 |  | 
| 213 | 
            -
                oj_dump_cstr( | 
| 213 | 
            +
                oj_dump_cstr(RSTRING_PTR(s), (int)RSTRING_LEN(s), 1, 0, out);
         | 
| 214 214 | 
             
            }
         | 
| 215 215 |  | 
| 216 216 | 
             
            static int hash_cb(VALUE key, VALUE value, VALUE ov) {
         | 
| @@ -414,7 +414,7 @@ static void dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) { | |
| 414 414 | 
             
                    if (Qundef == v || T_STRING != rb_type(v)) {
         | 
| 415 415 | 
             
                        rb_raise(rb_eEncodingError, "Invalid type for raw JSON.");
         | 
| 416 416 | 
             
                    } else {
         | 
| 417 | 
            -
                        const char *s    =  | 
| 417 | 
            +
                        const char *s    = RSTRING_PTR(v);
         | 
| 418 418 | 
             
                        int         len  = (int)RSTRING_LEN(v);
         | 
| 419 419 | 
             
                        const char *name = rb_id2name(*odd->attrs);
         | 
| 420 420 | 
             
                        size_t      nlen = strlen(name);
         | 
| @@ -532,7 +532,7 @@ static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) | |
| 532 532 | 
             
                    *out->cur++ = 'f';
         | 
| 533 533 | 
             
                    *out->cur++ = '"';
         | 
| 534 534 | 
             
                    *out->cur++ = ':';
         | 
| 535 | 
            -
                    oj_dump_cstr( | 
| 535 | 
            +
                    oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
         | 
| 536 536 | 
             
                    break;
         | 
| 537 537 | 
             
                case T_ARRAY:
         | 
| 538 538 | 
             
                    assure_size(out, d2 * out->indent + 14);
         | 
| @@ -696,7 +696,7 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) { | |
| 696 696 | 
             
                    for (i = 0; i < cnt; i++) {
         | 
| 697 697 | 
             
                        volatile VALUE s = rb_sym_to_s(rb_ary_entry(ma, i));
         | 
| 698 698 |  | 
| 699 | 
            -
                        name =  | 
| 699 | 
            +
                        name = RSTRING_PTR(s);
         | 
| 700 700 | 
             
                        len  = (int)RSTRING_LEN(s);
         | 
| 701 701 | 
             
                        size = len + 3;
         | 
| 702 702 | 
             
                        assure_size(out, size);
         | 
    
        data/ext/oj/dump_strict.c
    CHANGED
    
    | @@ -98,7 +98,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) { | |
| 98 98 | 
             
                        if ((int)sizeof(buf) <= cnt) {
         | 
| 99 99 | 
             
                            cnt = sizeof(buf) - 1;
         | 
| 100 100 | 
             
                        }
         | 
| 101 | 
            -
                         | 
| 101 | 
            +
                        memcpy(buf, RSTRING_PTR(rstr), cnt);
         | 
| 102 102 | 
             
                        buf[cnt] = '\0';
         | 
| 103 103 | 
             
                    } else {
         | 
| 104 104 | 
             
                        cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, out->opts->float_fmt);
         | 
| @@ -304,7 +304,7 @@ static void dump_data_strict(VALUE obj, int depth, Out out, bool as_ok) { | |
| 304 304 | 
             
                if (oj_bigdecimal_class == clas) {
         | 
| 305 305 | 
             
                    volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
         | 
| 306 306 |  | 
| 307 | 
            -
                    oj_dump_raw( | 
| 307 | 
            +
                    oj_dump_raw(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), out);
         | 
| 308 308 | 
             
                } else {
         | 
| 309 309 | 
             
                    raise_strict(obj);
         | 
| 310 310 | 
             
                }
         | 
| @@ -316,7 +316,7 @@ static void dump_data_null(VALUE obj, int depth, Out out, bool as_ok) { | |
| 316 316 | 
             
                if (oj_bigdecimal_class == clas) {
         | 
| 317 317 | 
             
                    volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
         | 
| 318 318 |  | 
| 319 | 
            -
                    oj_dump_raw( | 
| 319 | 
            +
                    oj_dump_raw(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), out);
         | 
| 320 320 | 
             
                } else {
         | 
| 321 321 | 
             
                    oj_dump_nil(Qnil, depth, out, false);
         | 
| 322 322 | 
             
                }
         | 
    
        data/ext/oj/err.h
    CHANGED
    
    | @@ -4,12 +4,31 @@ | |
| 4 4 | 
             
            #ifndef OJ_ERR_H
         | 
| 5 5 | 
             
            #define OJ_ERR_H
         | 
| 6 6 |  | 
| 7 | 
            +
            #include <errno.h>
         | 
| 7 8 | 
             
            #include "ruby.h"
         | 
| 9 | 
            +
             | 
| 8 10 | 
             
            // Needed to silence 2.4.0 warnings.
         | 
| 9 11 | 
             
            #ifndef NORETURN
         | 
| 10 12 | 
             
            #define NORETURN(x) x
         | 
| 11 13 | 
             
            #endif
         | 
| 12 14 |  | 
| 15 | 
            +
            #define OJ_ERR_START		300
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            typedef enum {
         | 
| 18 | 
            +
                OJ_OK		= 0,
         | 
| 19 | 
            +
                OJ_ERR_MEMORY	= ENOMEM,
         | 
| 20 | 
            +
                OJ_ERR_PARSE	= OJ_ERR_START,
         | 
| 21 | 
            +
                OJ_ERR_READ,
         | 
| 22 | 
            +
                OJ_ERR_WRITE,
         | 
| 23 | 
            +
                OJ_ERR_OVERFLOW,
         | 
| 24 | 
            +
                OJ_ERR_ARG,
         | 
| 25 | 
            +
                OJ_ERR_TOO_MANY,
         | 
| 26 | 
            +
                OJ_ERR_TYPE,
         | 
| 27 | 
            +
                OJ_ERR_KEY,
         | 
| 28 | 
            +
                OJ_ABORT,
         | 
| 29 | 
            +
                OJ_ERR_LAST,
         | 
| 30 | 
            +
            } ojStatus;
         | 
| 31 | 
            +
             | 
| 13 32 | 
             
            #define set_error(err, eclas, msg, json, current) \
         | 
| 14 33 | 
             
                _oj_err_set_with_location(err, eclas, msg, json, current, FILE, LINE)
         | 
| 15 34 |  | 
    
        data/ext/oj/extconf.rb
    CHANGED
    
    | @@ -30,6 +30,10 @@ have_func('rb_ivar_foreach') | |
| 30 30 | 
             
            have_func('rb_gc_mark_movable')
         | 
| 31 31 | 
             
            have_func('stpcpy')
         | 
| 32 32 | 
             
            have_func('pthread_mutex_init')
         | 
| 33 | 
            +
            have_func('rb_enc_associate')
         | 
| 34 | 
            +
            have_func('rb_ext_ractor_safe', 'ruby.h')
         | 
| 35 | 
            +
            # rb_hash_bulk_insert is deep down in a header not included in normal build and that seems to fool have_func.
         | 
| 36 | 
            +
            have_func('rb_hash_bulk_insert', 'ruby.h') unless '2' == version[0] && '6' == version[1]
         | 
| 33 37 |  | 
| 34 38 | 
             
            dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
         | 
| 35 39 |  | 
    
        data/ext/oj/hash_test.c
    CHANGED
    
    | @@ -3,7 +3,7 @@ | |
| 3 3 |  | 
| 4 4 | 
             
            // if windows, comment out the whole file. It's only a performance test.
         | 
| 5 5 | 
             
            #ifndef _WIN32
         | 
| 6 | 
            -
            #include " | 
| 6 | 
            +
            #include "intern.h"
         | 
| 7 7 |  | 
| 8 8 | 
             
            #include <stdint.h>
         | 
| 9 9 | 
             
            #include <sys/time.h>
         | 
| @@ -424,8 +424,6 @@ static uint64_t micro_time() { | |
| 424 424 |  | 
| 425 425 | 
             
            static void perf() {
         | 
| 426 426 | 
             
                StrLen   d;
         | 
| 427 | 
            -
                VALUE    v;
         | 
| 428 | 
            -
                VALUE *  slot = 0;
         | 
| 429 427 | 
             
                uint64_t dt, start;
         | 
| 430 428 | 
             
                int      i, iter = 1000000;
         | 
| 431 429 | 
             
                int      dataCnt = sizeof(data) / sizeof(*data);
         | 
| @@ -434,13 +432,7 @@ static void perf() { | |
| 434 432 | 
             
                start = micro_time();
         | 
| 435 433 | 
             
                for (i = iter; 0 < i; i--) {
         | 
| 436 434 | 
             
                    for (d = data; 0 != d->str; d++) {
         | 
| 437 | 
            -
             | 
| 438 | 
            -
                        if (Qundef == v) {
         | 
| 439 | 
            -
                            if (0 != slot) {
         | 
| 440 | 
            -
                                v     = ID2SYM(rb_intern(d->str));
         | 
| 441 | 
            -
                                *slot = v;
         | 
| 442 | 
            -
                            }
         | 
| 443 | 
            -
                        }
         | 
| 435 | 
            +
            	    oj_class_intern(d->str, d->len, false, NULL, false, Qnil);
         | 
| 444 436 | 
             
                    }
         | 
| 445 437 | 
             
                }
         | 
| 446 438 | 
             
                dt = micro_time() - start;
         | 
| @@ -459,29 +451,10 @@ static void perf() { | |
| 459 451 |  | 
| 460 452 | 
             
            void oj_hash_test() {
         | 
| 461 453 | 
             
                StrLen d;
         | 
| 462 | 
            -
                VALUE  v;
         | 
| 463 | 
            -
                VALUE *slot = 0;
         | 
| 464 | 
            -
                ;
         | 
| 465 454 |  | 
| 466 455 | 
             
                oj_hash_init();
         | 
| 467 456 | 
             
                for (d = data; 0 != d->str; d++) {
         | 
| 468 | 
            -
             | 
| 469 | 
            -
                    v       = oj_class_hash_get(d->str, d->len, &slot);
         | 
| 470 | 
            -
                    if (Qnil == v) {
         | 
| 471 | 
            -
                        if (0 == slot) {
         | 
| 472 | 
            -
                            printf("*** failed to get a slot for %s\n", s);
         | 
| 473 | 
            -
                        } else {
         | 
| 474 | 
            -
                            v     = ID2SYM(rb_intern(d->str));
         | 
| 475 | 
            -
                            *slot = v;
         | 
| 476 | 
            -
                        }
         | 
| 477 | 
            -
                    } else {
         | 
| 478 | 
            -
                        VALUE rs = rb_funcall2(v, rb_intern("to_s"), 0, 0);
         | 
| 479 | 
            -
             | 
| 480 | 
            -
                        printf("*** get on '%s' returned '%s' (%s)\n",
         | 
| 481 | 
            -
                               s,
         | 
| 482 | 
            -
                               StringValuePtr(rs),
         | 
| 483 | 
            -
                               rb_class2name(rb_obj_class(v)));
         | 
| 484 | 
            -
                    }
         | 
| 457 | 
            +
            	oj_class_intern(d->str, d->len, false, NULL, false, Qnil);
         | 
| 485 458 | 
             
                    /*oj_hash_print(c);*/
         | 
| 486 459 | 
             
                }
         | 
| 487 460 | 
             
                printf("*** ---------- hash table ------------\n");
         | 
    
        data/ext/oj/intern.c
    ADDED
    
    | @@ -0,0 +1,398 @@ | |
| 1 | 
            +
            // Copyright (c) 2011, 2021 Peter Ohler. All rights reserved.
         | 
| 2 | 
            +
            // Licensed under the MIT License. See LICENSE file in the project root for license details.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            #include "intern.h"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            #include <stdint.h>
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 9 | 
            +
            #include <pthread.h>
         | 
| 10 | 
            +
            #endif
         | 
| 11 | 
            +
            #include "parse.h"
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            #define HASH_SLOT_CNT ((uint32_t)8192)
         | 
| 14 | 
            +
            #define HASH_MASK (HASH_SLOT_CNT - 1)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            typedef struct _keyVal {
         | 
| 17 | 
            +
                struct _keyVal *next;
         | 
| 18 | 
            +
                const char *    key;
         | 
| 19 | 
            +
                size_t          len;
         | 
| 20 | 
            +
                VALUE           val;
         | 
| 21 | 
            +
            } * KeyVal;
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            typedef struct _hash {
         | 
| 24 | 
            +
                struct _keyVal slots[HASH_SLOT_CNT];
         | 
| 25 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 26 | 
            +
                pthread_mutex_t mutex;
         | 
| 27 | 
            +
            #else
         | 
| 28 | 
            +
                VALUE mutex;
         | 
| 29 | 
            +
            #endif
         | 
| 30 | 
            +
            } * Hash;
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            struct _hash class_hash;
         | 
| 33 | 
            +
            struct _hash str_hash;
         | 
| 34 | 
            +
            struct _hash sym_hash;
         | 
| 35 | 
            +
            struct _hash attr_hash;
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            // almost the Murmur hash algorithm
         | 
| 38 | 
            +
            #define M 0x5bd1e995
         | 
| 39 | 
            +
            #define C1 0xCC9E2D51
         | 
| 40 | 
            +
            #define C2 0x1B873593
         | 
| 41 | 
            +
            #define N 0xE6546B64
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            static uint32_t hash_calc(const uint8_t *key, size_t len) {
         | 
| 44 | 
            +
                const uint8_t *end     = key + len;
         | 
| 45 | 
            +
                const uint8_t *endless = key + (len & 0xFFFFFFFC);
         | 
| 46 | 
            +
                uint32_t       h       = (uint32_t)len;
         | 
| 47 | 
            +
                uint32_t       k;
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                while (key < endless) {
         | 
| 50 | 
            +
                    k = (uint32_t)*key++;
         | 
| 51 | 
            +
                    k |= (uint32_t)*key++ << 8;
         | 
| 52 | 
            +
                    k |= (uint32_t)*key++ << 16;
         | 
| 53 | 
            +
                    k |= (uint32_t)*key++ << 24;
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    k *= M;
         | 
| 56 | 
            +
                    k ^= k >> 24;
         | 
| 57 | 
            +
                    h *= M;
         | 
| 58 | 
            +
                    h ^= k * M;
         | 
| 59 | 
            +
                }
         | 
| 60 | 
            +
                if (1 < end - key) {
         | 
| 61 | 
            +
                    uint16_t k16 = (uint16_t)*key++;
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    k16 |= (uint16_t)*key++ << 8;
         | 
| 64 | 
            +
                    h ^= k16 << 8;
         | 
| 65 | 
            +
                }
         | 
| 66 | 
            +
                if (key < end) {
         | 
| 67 | 
            +
                    h ^= *key;
         | 
| 68 | 
            +
                }
         | 
| 69 | 
            +
                h *= M;
         | 
| 70 | 
            +
                h ^= h >> 13;
         | 
| 71 | 
            +
                h *= M;
         | 
| 72 | 
            +
                h ^= h >> 15;
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                return h;
         | 
| 75 | 
            +
            }
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            void oj_hash_init() {
         | 
| 78 | 
            +
                memset(class_hash.slots, 0, sizeof(class_hash.slots));
         | 
| 79 | 
            +
                memset(str_hash.slots, 0, sizeof(str_hash.slots));
         | 
| 80 | 
            +
                memset(sym_hash.slots, 0, sizeof(sym_hash.slots));
         | 
| 81 | 
            +
                memset(attr_hash.slots, 0, sizeof(attr_hash.slots));
         | 
| 82 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 83 | 
            +
                pthread_mutex_init(&class_hash.mutex, NULL);
         | 
| 84 | 
            +
                pthread_mutex_init(&str_hash.mutex, NULL);
         | 
| 85 | 
            +
                pthread_mutex_init(&sym_hash.mutex, NULL);
         | 
| 86 | 
            +
                pthread_mutex_init(&attr_hash.mutex, NULL);
         | 
| 87 | 
            +
            #else
         | 
| 88 | 
            +
                class_hash.mutex = rb_mutex_new();
         | 
| 89 | 
            +
                rb_gc_register_address(&class_hash.mutex);
         | 
| 90 | 
            +
                str_hash.mutex = rb_mutex_new();
         | 
| 91 | 
            +
                rb_gc_register_address(&str_hash.mutex);
         | 
| 92 | 
            +
                sym_hash.mutex = rb_mutex_new();
         | 
| 93 | 
            +
                rb_gc_register_address(&sym_hash.mutex);
         | 
| 94 | 
            +
                attr_hash.mutex = rb_mutex_new();
         | 
| 95 | 
            +
                rb_gc_register_address(&attr_hash.mutex);
         | 
| 96 | 
            +
            #endif
         | 
| 97 | 
            +
            }
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            void oj_hash_print() {
         | 
| 100 | 
            +
                uint32_t i;
         | 
| 101 | 
            +
                KeyVal   b;
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                for (i = 0; i < HASH_SLOT_CNT; i++) {
         | 
| 104 | 
            +
                    printf("%4d:", i);
         | 
| 105 | 
            +
                    for (b = class_hash.slots + i; 0 != b && 0 != b->key; b = b->next) {
         | 
| 106 | 
            +
                        printf(" %s", b->key);
         | 
| 107 | 
            +
                    }
         | 
| 108 | 
            +
                    printf("\n");
         | 
| 109 | 
            +
                }
         | 
| 110 | 
            +
            }
         | 
| 111 | 
            +
             | 
| 112 | 
            +
            void oj_hash_sizes() {
         | 
| 113 | 
            +
                uint32_t i;
         | 
| 114 | 
            +
                KeyVal   b;
         | 
| 115 | 
            +
                int      max = 0;
         | 
| 116 | 
            +
                int      min = 1000000;
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                for (i = 0; i < HASH_SLOT_CNT; i++) {
         | 
| 119 | 
            +
                    int cnt = 0;
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                    for (b = str_hash.slots + i; 0 != b && 0 != b->key; b = b->next) {
         | 
| 122 | 
            +
                        cnt++;
         | 
| 123 | 
            +
                    }
         | 
| 124 | 
            +
                    // printf(" %4d\n", cnt);
         | 
| 125 | 
            +
                    if (max < cnt) {
         | 
| 126 | 
            +
                        max = cnt;
         | 
| 127 | 
            +
                    }
         | 
| 128 | 
            +
                    if (cnt < min) {
         | 
| 129 | 
            +
                        min = cnt;
         | 
| 130 | 
            +
                    }
         | 
| 131 | 
            +
                }
         | 
| 132 | 
            +
                printf("min: %d  max: %d\n", min, max);
         | 
| 133 | 
            +
            }
         | 
| 134 | 
            +
             | 
| 135 | 
            +
            VALUE
         | 
| 136 | 
            +
            oj_str_intern(const char *key, size_t len) {
         | 
| 137 | 
            +
                uint32_t h      = hash_calc((const uint8_t *)key, len) & HASH_MASK;
         | 
| 138 | 
            +
                KeyVal   bucket = str_hash.slots + h;
         | 
| 139 | 
            +
                KeyVal   b;
         | 
| 140 | 
            +
             | 
| 141 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 142 | 
            +
                pthread_mutex_lock(&str_hash.mutex);
         | 
| 143 | 
            +
            #else
         | 
| 144 | 
            +
                rb_mutex_lock(str_hash.mutex);
         | 
| 145 | 
            +
            #endif
         | 
| 146 | 
            +
                if (NULL != bucket->key) {  // not the top slot
         | 
| 147 | 
            +
                    for (b = bucket; 0 != b; b = b->next) {
         | 
| 148 | 
            +
                        if (len == b->len && 0 == strncmp(b->key, key, len)) {
         | 
| 149 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 150 | 
            +
                            pthread_mutex_unlock(&str_hash.mutex);
         | 
| 151 | 
            +
            #else
         | 
| 152 | 
            +
                            rb_mutex_unlock(str_hash.mutex);
         | 
| 153 | 
            +
            #endif
         | 
| 154 | 
            +
                            return b->val;
         | 
| 155 | 
            +
                        }
         | 
| 156 | 
            +
                        bucket = b;
         | 
| 157 | 
            +
                    }
         | 
| 158 | 
            +
                    b            = ALLOC(struct _keyVal);
         | 
| 159 | 
            +
                    b->next      = NULL;
         | 
| 160 | 
            +
                    bucket->next = b;
         | 
| 161 | 
            +
                    bucket       = b;
         | 
| 162 | 
            +
                }
         | 
| 163 | 
            +
                bucket->key = oj_strndup(key, len);
         | 
| 164 | 
            +
                bucket->len = len;
         | 
| 165 | 
            +
                bucket->val = rb_utf8_str_new(key, len);
         | 
| 166 | 
            +
                bucket->val = rb_str_freeze(bucket->val);
         | 
| 167 | 
            +
                rb_gc_register_address(&bucket->val);
         | 
| 168 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 169 | 
            +
                pthread_mutex_unlock(&str_hash.mutex);
         | 
| 170 | 
            +
            #else
         | 
| 171 | 
            +
                rb_mutex_unlock(str_hash.mutex);
         | 
| 172 | 
            +
            #endif
         | 
| 173 | 
            +
                return bucket->val;
         | 
| 174 | 
            +
            }
         | 
| 175 | 
            +
             | 
| 176 | 
            +
            VALUE
         | 
| 177 | 
            +
            oj_sym_intern(const char *key, size_t len) {
         | 
| 178 | 
            +
                uint32_t h      = hash_calc((const uint8_t *)key, len) & HASH_MASK;
         | 
| 179 | 
            +
                KeyVal   bucket = sym_hash.slots + h;
         | 
| 180 | 
            +
                KeyVal   b;
         | 
| 181 | 
            +
             | 
| 182 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 183 | 
            +
                pthread_mutex_lock(&sym_hash.mutex);
         | 
| 184 | 
            +
            #else
         | 
| 185 | 
            +
                rb_mutex_lock(sym_hash.mutex);
         | 
| 186 | 
            +
            #endif
         | 
| 187 | 
            +
                if (NULL != bucket->key) {  // not the top slot
         | 
| 188 | 
            +
                    for (b = bucket; 0 != b; b = b->next) {
         | 
| 189 | 
            +
                        if (len == b->len && 0 == strncmp(b->key, key, len)) {
         | 
| 190 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 191 | 
            +
                            pthread_mutex_unlock(&sym_hash.mutex);
         | 
| 192 | 
            +
            #else
         | 
| 193 | 
            +
                            rb_mutex_unlock(sym_hash.mutex);
         | 
| 194 | 
            +
            #endif
         | 
| 195 | 
            +
                            return b->val;
         | 
| 196 | 
            +
                        }
         | 
| 197 | 
            +
                        bucket = b;
         | 
| 198 | 
            +
                    }
         | 
| 199 | 
            +
                    b            = ALLOC(struct _keyVal);
         | 
| 200 | 
            +
                    b->next      = NULL;
         | 
| 201 | 
            +
                    bucket->next = b;
         | 
| 202 | 
            +
                    bucket       = b;
         | 
| 203 | 
            +
                }
         | 
| 204 | 
            +
                bucket->key = oj_strndup(key, len);
         | 
| 205 | 
            +
                bucket->len = len;
         | 
| 206 | 
            +
                bucket->val = ID2SYM(rb_intern3(key, len, oj_utf8_encoding));
         | 
| 207 | 
            +
                rb_gc_register_address(&bucket->val);
         | 
| 208 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 209 | 
            +
                pthread_mutex_unlock(&sym_hash.mutex);
         | 
| 210 | 
            +
            #else
         | 
| 211 | 
            +
                rb_mutex_unlock(sym_hash.mutex);
         | 
| 212 | 
            +
            #endif
         | 
| 213 | 
            +
                return bucket->val;
         | 
| 214 | 
            +
            }
         | 
| 215 | 
            +
             | 
| 216 | 
            +
            static ID form_attr(const char *key, size_t klen) {
         | 
| 217 | 
            +
                char attr[256];
         | 
| 218 | 
            +
                ID   var_id;
         | 
| 219 | 
            +
             | 
| 220 | 
            +
                if ((int)sizeof(attr) <= klen + 2) {
         | 
| 221 | 
            +
                    char *buf = ALLOC_N(char, klen + 2);
         | 
| 222 | 
            +
             | 
| 223 | 
            +
                    if ('~' == *key) {
         | 
| 224 | 
            +
                        memcpy(buf, key + 1, klen - 1);
         | 
| 225 | 
            +
                        buf[klen - 1] = '\0';
         | 
| 226 | 
            +
                    } else {
         | 
| 227 | 
            +
                        *buf = '@';
         | 
| 228 | 
            +
                        memcpy(buf + 1, key, klen);
         | 
| 229 | 
            +
                        buf[klen + 1] = '\0';
         | 
| 230 | 
            +
                    }
         | 
| 231 | 
            +
                    var_id = rb_intern(buf);
         | 
| 232 | 
            +
                    xfree(buf);
         | 
| 233 | 
            +
                } else {
         | 
| 234 | 
            +
                    if ('~' == *key) {
         | 
| 235 | 
            +
                        memcpy(attr, key + 1, klen - 1);
         | 
| 236 | 
            +
                        attr[klen - 1] = '\0';
         | 
| 237 | 
            +
                    } else {
         | 
| 238 | 
            +
                        *attr = '@';
         | 
| 239 | 
            +
                        memcpy(attr + 1, key, klen);
         | 
| 240 | 
            +
                        attr[klen + 1] = '\0';
         | 
| 241 | 
            +
                    }
         | 
| 242 | 
            +
                    var_id = rb_intern(attr);
         | 
| 243 | 
            +
                }
         | 
| 244 | 
            +
                return var_id;
         | 
| 245 | 
            +
            }
         | 
| 246 | 
            +
             | 
| 247 | 
            +
            ID oj_attr_intern(const char *key, size_t len) {
         | 
| 248 | 
            +
                uint32_t h      = hash_calc((const uint8_t *)key, len) & HASH_MASK;
         | 
| 249 | 
            +
                KeyVal   bucket = attr_hash.slots + h;
         | 
| 250 | 
            +
                KeyVal   b;
         | 
| 251 | 
            +
             | 
| 252 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 253 | 
            +
                pthread_mutex_lock(&attr_hash.mutex);
         | 
| 254 | 
            +
            #else
         | 
| 255 | 
            +
                rb_mutex_lock(attr_hash.mutex);
         | 
| 256 | 
            +
            #endif
         | 
| 257 | 
            +
                if (NULL != bucket->key) {  // not the top slot
         | 
| 258 | 
            +
                    for (b = bucket; 0 != b; b = b->next) {
         | 
| 259 | 
            +
                        if (len == b->len && 0 == strncmp(b->key, key, len)) {
         | 
| 260 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 261 | 
            +
                            pthread_mutex_unlock(&attr_hash.mutex);
         | 
| 262 | 
            +
            #else
         | 
| 263 | 
            +
                            rb_mutex_unlock(attr_hash.mutex);
         | 
| 264 | 
            +
            #endif
         | 
| 265 | 
            +
                            return (ID)b->val;
         | 
| 266 | 
            +
                        }
         | 
| 267 | 
            +
                        bucket = b;
         | 
| 268 | 
            +
                    }
         | 
| 269 | 
            +
                    b            = ALLOC(struct _keyVal);
         | 
| 270 | 
            +
                    b->next      = NULL;
         | 
| 271 | 
            +
                    bucket->next = b;
         | 
| 272 | 
            +
                    bucket       = b;
         | 
| 273 | 
            +
                }
         | 
| 274 | 
            +
                bucket->key = oj_strndup(key, len);
         | 
| 275 | 
            +
                bucket->len = len;
         | 
| 276 | 
            +
                bucket->val = (VALUE)form_attr(key, len);
         | 
| 277 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 278 | 
            +
                pthread_mutex_unlock(&attr_hash.mutex);
         | 
| 279 | 
            +
            #else
         | 
| 280 | 
            +
                rb_mutex_unlock(attr_hash.mutex);
         | 
| 281 | 
            +
            #endif
         | 
| 282 | 
            +
                return (ID)bucket->val;
         | 
| 283 | 
            +
            }
         | 
| 284 | 
            +
             | 
| 285 | 
            +
            static VALUE resolve_classname(VALUE mod, const char *classname, int auto_define) {
         | 
| 286 | 
            +
                VALUE clas;
         | 
| 287 | 
            +
                ID    ci = rb_intern(classname);
         | 
| 288 | 
            +
             | 
| 289 | 
            +
                if (rb_const_defined_at(mod, ci)) {
         | 
| 290 | 
            +
                    clas = rb_const_get_at(mod, ci);
         | 
| 291 | 
            +
                } else if (auto_define) {
         | 
| 292 | 
            +
                    clas = rb_define_class_under(mod, classname, oj_bag_class);
         | 
| 293 | 
            +
                } else {
         | 
| 294 | 
            +
                    clas = Qundef;
         | 
| 295 | 
            +
                }
         | 
| 296 | 
            +
                return clas;
         | 
| 297 | 
            +
            }
         | 
| 298 | 
            +
             | 
| 299 | 
            +
            static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE error_class) {
         | 
| 300 | 
            +
                char        class_name[1024];
         | 
| 301 | 
            +
                VALUE       clas;
         | 
| 302 | 
            +
                char *      end = class_name + sizeof(class_name) - 1;
         | 
| 303 | 
            +
                char *      s;
         | 
| 304 | 
            +
                const char *n = name;
         | 
| 305 | 
            +
             | 
| 306 | 
            +
                clas = rb_cObject;
         | 
| 307 | 
            +
                for (s = class_name; 0 < len; n++, len--) {
         | 
| 308 | 
            +
                    if (':' == *n) {
         | 
| 309 | 
            +
                        *s = '\0';
         | 
| 310 | 
            +
                        n++;
         | 
| 311 | 
            +
                        len--;
         | 
| 312 | 
            +
                        if (':' != *n) {
         | 
| 313 | 
            +
                            return Qundef;
         | 
| 314 | 
            +
                        }
         | 
| 315 | 
            +
                        if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
         | 
| 316 | 
            +
                            return Qundef;
         | 
| 317 | 
            +
                        }
         | 
| 318 | 
            +
                        s = class_name;
         | 
| 319 | 
            +
                    } else if (end <= s) {
         | 
| 320 | 
            +
                        return Qundef;
         | 
| 321 | 
            +
                    } else {
         | 
| 322 | 
            +
                        *s++ = *n;
         | 
| 323 | 
            +
                    }
         | 
| 324 | 
            +
                }
         | 
| 325 | 
            +
                *s = '\0';
         | 
| 326 | 
            +
                if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
         | 
| 327 | 
            +
                    oj_set_error_at(pi, error_class, __FILE__, __LINE__, "class %s is not defined", name);
         | 
| 328 | 
            +
                    if (Qnil != error_class) {
         | 
| 329 | 
            +
                        pi->err_class = error_class;
         | 
| 330 | 
            +
                    }
         | 
| 331 | 
            +
                }
         | 
| 332 | 
            +
                return clas;
         | 
| 333 | 
            +
            }
         | 
| 334 | 
            +
             | 
| 335 | 
            +
            VALUE oj_class_intern(const char *key, size_t len, bool safe, ParseInfo pi, int auto_define, VALUE error_class) {
         | 
| 336 | 
            +
                uint32_t h      = hash_calc((const uint8_t *)key, len) & HASH_MASK;
         | 
| 337 | 
            +
                KeyVal   bucket = class_hash.slots + h;
         | 
| 338 | 
            +
                KeyVal   b;
         | 
| 339 | 
            +
             | 
| 340 | 
            +
                if (safe) {
         | 
| 341 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 342 | 
            +
                    pthread_mutex_lock(&class_hash.mutex);
         | 
| 343 | 
            +
            #else
         | 
| 344 | 
            +
                    rb_mutex_lock(class_hash.mutex);
         | 
| 345 | 
            +
            #endif
         | 
| 346 | 
            +
                    if (NULL != bucket->key) {  // not the top slot
         | 
| 347 | 
            +
                        for (b = bucket; 0 != b; b = b->next) {
         | 
| 348 | 
            +
                            if (len == b->len && 0 == strncmp(b->key, key, len)) {
         | 
| 349 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 350 | 
            +
                                pthread_mutex_unlock(&class_hash.mutex);
         | 
| 351 | 
            +
            #else
         | 
| 352 | 
            +
                                rb_mutex_unlock(class_hash.mutex);
         | 
| 353 | 
            +
            #endif
         | 
| 354 | 
            +
                                return b->val;
         | 
| 355 | 
            +
                            }
         | 
| 356 | 
            +
                            bucket = b;
         | 
| 357 | 
            +
                        }
         | 
| 358 | 
            +
                        b            = ALLOC(struct _keyVal);
         | 
| 359 | 
            +
                        b->next      = NULL;
         | 
| 360 | 
            +
                        bucket->next = b;
         | 
| 361 | 
            +
                        bucket       = b;
         | 
| 362 | 
            +
                    }
         | 
| 363 | 
            +
                    bucket->key = oj_strndup(key, len);
         | 
| 364 | 
            +
                    bucket->len = len;
         | 
| 365 | 
            +
                    bucket->val = resolve_classpath(pi, key, len, auto_define, error_class);
         | 
| 366 | 
            +
            #if HAVE_PTHREAD_MUTEX_INIT
         | 
| 367 | 
            +
                    pthread_mutex_unlock(&class_hash.mutex);
         | 
| 368 | 
            +
            #else
         | 
| 369 | 
            +
                    rb_mutex_unlock(class_hash.mutex);
         | 
| 370 | 
            +
            #endif
         | 
| 371 | 
            +
                } else {
         | 
| 372 | 
            +
                    if (NULL != bucket->key) {
         | 
| 373 | 
            +
                        for (b = bucket; 0 != b; b = b->next) {
         | 
| 374 | 
            +
                            if (len == b->len && 0 == strncmp(b->key, key, len)) {
         | 
| 375 | 
            +
                                return (ID)b->val;
         | 
| 376 | 
            +
                            }
         | 
| 377 | 
            +
                            bucket = b;
         | 
| 378 | 
            +
                        }
         | 
| 379 | 
            +
                        b            = ALLOC(struct _keyVal);
         | 
| 380 | 
            +
                        b->next      = NULL;
         | 
| 381 | 
            +
                        bucket->next = b;
         | 
| 382 | 
            +
                        bucket       = b;
         | 
| 383 | 
            +
                    }
         | 
| 384 | 
            +
                    bucket->key = oj_strndup(key, len);
         | 
| 385 | 
            +
                    bucket->len = len;
         | 
| 386 | 
            +
                    bucket->val = resolve_classpath(pi, key, len, auto_define, error_class);
         | 
| 387 | 
            +
                }
         | 
| 388 | 
            +
                return bucket->val;
         | 
| 389 | 
            +
            }
         | 
| 390 | 
            +
             | 
| 391 | 
            +
            char *oj_strndup(const char *s, size_t len) {
         | 
| 392 | 
            +
                char *d = ALLOC_N(char, len + 1);
         | 
| 393 | 
            +
             | 
| 394 | 
            +
                memcpy(d, s, len);
         | 
| 395 | 
            +
                d[len] = '\0';
         | 
| 396 | 
            +
             | 
| 397 | 
            +
                return d;
         | 
| 398 | 
            +
            }
         |