oj 3.16.3 → 3.16.9
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/CHANGELOG.md +28 -0
- data/ext/oj/dump.c +34 -14
- data/ext/oj/extconf.rb +1 -2
- data/ext/oj/fast.c +3 -3
- data/ext/oj/mimic_json.c +8 -2
- data/ext/oj/oj.c +2 -0
- data/ext/oj/oj.h +1 -0
- data/ext/oj/parse.c +16 -14
- data/ext/oj/parser.c +6 -3
- data/ext/oj/parser.h +2 -2
- data/ext/oj/rails.c +2 -0
- data/ext/oj/reader.c +1 -1
- data/ext/oj/saj.c +2 -2
- data/ext/oj/stream_writer.c +2 -6
- data/ext/oj/string_writer.c +4 -4
- data/ext/oj/usual.c +19 -30
- data/lib/oj/mimic.rb +1 -5
- data/lib/oj/schandler.rb +5 -4
- data/lib/oj/version.rb +1 -1
- data/test/activesupport6/encoding_test.rb +63 -28
- data/test/activesupport7/abstract_unit.rb +4 -1
- data/test/activesupport7/encoding_test.rb +72 -22
- data/test/foo.rb +16 -4
- data/test/test_compat.rb +1 -1
- data/test/test_parser_usual.rb +28 -0
- data/test/test_writer.rb +16 -0
- metadata +17 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: d3e57c02a1fe6782596953f34b8e2a2b729a09b5d8e7128dd4633d430ca7aa0c
         | 
| 4 | 
            +
              data.tar.gz: 7698f8c0203459d62f4421f11a9c3637b04b5b808ecf87ce4ca057e2fd073762
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 417ada5b645a6ba48e81b52bb72cec97bb4a64595a61252989346a975c1026b3cbc039cbf7cef166b5dbfbdd79554d5c9e5786da3299ce1fd3f2ec70d3ef479f
         | 
| 7 | 
            +
              data.tar.gz: ceffb29c6732b107d42091bc8754e8b4174e26e6d8eb9c4162ed8a12a65d37aa2450f9f5398d39242d92fabd46d63be5fc14e0dc003774378e4e6211b02e3f0d
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,5 +1,33 @@ | |
| 1 1 | 
             
            # CHANGELOG
         | 
| 2 2 |  | 
| 3 | 
            +
            ## 3.16.9 - 2024-12-28
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            - Fixed `Oj::Parser` create_id size issue #931.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            - Changed parser to be more optimized (PR from @Watson1978)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            ## 3.16.8 - 2024-12-14
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            - Fixed StreamWriter to write to non-file IO thanks to @jscheid.
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            ## 3.16.7 - 2024-11-01
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            - Changed string_writer_as_json to allow multiple arguments.
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            - Fixed Global variable registration added to mimic_json and rails code thanks to @byroot.
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            ## 3.16.6 - 2024-09-09
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            - Fixed issue with Rails 7.2 that changed the order of calls to to_json and as_json.
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            ## 3.16.5 - 2024-08-07
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            - Fixed Oj::Parser so that block procedures work correctly.
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            ## 3.16.4 - 2024-06-08
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            - Fixed Oj::Parse EOF issue on larger stream input.
         | 
| 30 | 
            +
             | 
| 3 31 | 
             
            ## 3.16.3 - 2023-12-11
         | 
| 4 32 |  | 
| 5 33 | 
             
            - Fixed the gemspec to allow earlier versions of the bigdecimal gem.
         | 
    
        data/ext/oj/dump.c
    CHANGED
    
    | @@ -198,7 +198,18 @@ inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) { | |
| 198 198 | 
             
            }
         | 
| 199 199 |  | 
| 200 200 | 
             
            inline static size_t rails_friendly_size(const uint8_t *str, size_t len) {
         | 
| 201 | 
            -
                 | 
| 201 | 
            +
                long    size = 0;
         | 
| 202 | 
            +
                size_t  i    = len;
         | 
| 203 | 
            +
                uint8_t hi   = 0;
         | 
| 204 | 
            +
             | 
| 205 | 
            +
                for (; 0 < i; str++, i--) {
         | 
| 206 | 
            +
                    size += rails_friendly_chars[*str];
         | 
| 207 | 
            +
                    hi |= *str & 0x80;
         | 
| 208 | 
            +
                }
         | 
| 209 | 
            +
                if (0 == hi) {
         | 
| 210 | 
            +
                    return size - len * (size_t)'0';
         | 
| 211 | 
            +
                }
         | 
| 212 | 
            +
                return -(size - len * (size_t)'0');
         | 
| 202 213 | 
             
            }
         | 
| 203 214 |  | 
| 204 215 | 
             
            const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp) {
         | 
| @@ -750,8 +761,9 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) { | |
| 750 761 | 
             
            void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
         | 
| 751 762 | 
             
                size_t      size;
         | 
| 752 763 | 
             
                char       *cmap;
         | 
| 753 | 
            -
                const char *orig | 
| 754 | 
            -
                bool        has_hi | 
| 764 | 
            +
                const char *orig                  = str;
         | 
| 765 | 
            +
                bool        has_hi                = false;
         | 
| 766 | 
            +
                bool        do_unicode_validation = false;
         | 
| 755 767 |  | 
| 756 768 | 
             
                switch (out->opts->escape_mode) {
         | 
| 757 769 | 
             
                case NLEsc:
         | 
| @@ -772,8 +784,9 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou | |
| 772 784 | 
             
                    size = xss_friendly_size((uint8_t *)str, cnt);
         | 
| 773 785 | 
             
                    break;
         | 
| 774 786 | 
             
                case JXEsc:
         | 
| 775 | 
            -
                    cmap | 
| 776 | 
            -
                    size | 
| 787 | 
            +
                    cmap                  = hixss_friendly_chars;
         | 
| 788 | 
            +
                    size                  = hixss_friendly_size((uint8_t *)str, cnt);
         | 
| 789 | 
            +
                    do_unicode_validation = true;
         | 
| 777 790 | 
             
                    break;
         | 
| 778 791 | 
             
                case RailsXEsc: {
         | 
| 779 792 | 
             
                    long sz;
         | 
| @@ -786,12 +799,22 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou | |
| 786 799 | 
             
                    } else {
         | 
| 787 800 | 
             
                        size = (size_t)sz;
         | 
| 788 801 | 
             
                    }
         | 
| 802 | 
            +
                    do_unicode_validation = true;
         | 
| 789 803 | 
             
                    break;
         | 
| 790 804 | 
             
                }
         | 
| 791 | 
            -
                case RailsEsc:
         | 
| 805 | 
            +
                case RailsEsc: {
         | 
| 806 | 
            +
                    long sz;
         | 
| 792 807 | 
             
                    cmap = rails_friendly_chars;
         | 
| 793 | 
            -
                     | 
| 808 | 
            +
                    sz   = rails_friendly_size((uint8_t *)str, cnt);
         | 
| 809 | 
            +
                    if (sz < 0) {
         | 
| 810 | 
            +
                        has_hi = true;
         | 
| 811 | 
            +
                        size   = (size_t)-sz;
         | 
| 812 | 
            +
                    } else {
         | 
| 813 | 
            +
                        size = (size_t)sz;
         | 
| 814 | 
            +
                    }
         | 
| 815 | 
            +
                    do_unicode_validation = true;
         | 
| 794 816 | 
             
                    break;
         | 
| 817 | 
            +
                }
         | 
| 795 818 | 
             
                case JSONEsc:
         | 
| 796 819 | 
             
                default: cmap = hibit_friendly_chars; size = hibit_friendly_size((uint8_t *)str, cnt);
         | 
| 797 820 | 
             
                }
         | 
| @@ -822,7 +845,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou | |
| 822 845 | 
             
                    for (; str < end; str++) {
         | 
| 823 846 | 
             
                        switch (cmap[(uint8_t)*str]) {
         | 
| 824 847 | 
             
                        case '1':
         | 
| 825 | 
            -
                            if ( | 
| 848 | 
            +
                            if (do_unicode_validation && check_start <= str) {
         | 
| 826 849 | 
             
                                if (0 != (0x80 & (uint8_t)*str)) {
         | 
| 827 850 | 
             
                                    if (0xC0 == (0xC0 & (uint8_t)*str)) {
         | 
| 828 851 | 
             
                                        check_start = check_unicode(str, end, orig);
         | 
| @@ -846,8 +869,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou | |
| 846 869 | 
             
                            }
         | 
| 847 870 | 
             
                            break;
         | 
| 848 871 | 
             
                        case '3':  // Unicode
         | 
| 849 | 
            -
                            if (0xe2 == (uint8_t)*str &&  | 
| 850 | 
            -
                                2 <= end - str) {
         | 
| 872 | 
            +
                            if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
         | 
| 851 873 | 
             
                                if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
         | 
| 852 874 | 
             
                                    str = dump_unicode(str, end, out, orig);
         | 
| 853 875 | 
             
                                } else {
         | 
| @@ -866,8 +888,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou | |
| 866 888 | 
             
                                APPEND_CHARS(out->cur, "\\u00", 4);
         | 
| 867 889 | 
             
                                dump_hex((uint8_t)*str, out);
         | 
| 868 890 | 
             
                            } else {
         | 
| 869 | 
            -
                                if (0xe2 == (uint8_t)*str &&
         | 
| 870 | 
            -
                                    (JXEsc == out->opts->escape_mode || RailsXEsc == out->opts->escape_mode) && 2 <= end - str) {
         | 
| 891 | 
            +
                                if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
         | 
| 871 892 | 
             
                                    if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
         | 
| 872 893 | 
             
                                        str = dump_unicode(str, end, out, orig);
         | 
| 873 894 | 
             
                                    } else {
         | 
| @@ -884,8 +905,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou | |
| 884 905 | 
             
                    }
         | 
| 885 906 | 
             
                    *out->cur++ = '"';
         | 
| 886 907 | 
             
                }
         | 
| 887 | 
            -
                if ( | 
| 888 | 
            -
                    0 != (0x80 & *(str - 1))) {
         | 
| 908 | 
            +
                if (do_unicode_validation && 0 < str - orig && 0 != (0x80 & *(str - 1))) {
         | 
| 889 909 | 
             
                    uint8_t c = (uint8_t) * (str - 1);
         | 
| 890 910 | 
             
                    int     i;
         | 
| 891 911 | 
             
                    int     scnt = (int)(str - orig);
         | 
    
        data/ext/oj/extconf.rb
    CHANGED
    
    | @@ -29,10 +29,9 @@ dflags = { | |
| 29 29 | 
             
            have_func('rb_gc_mark_movable')
         | 
| 30 30 | 
             
            have_func('stpcpy')
         | 
| 31 31 | 
             
            have_func('pthread_mutex_init')
         | 
| 32 | 
            +
            have_func('getrlimit', 'sys/resource.h')
         | 
| 32 33 | 
             
            have_func('rb_enc_interned_str')
         | 
| 33 34 | 
             
            have_func('rb_ext_ractor_safe', 'ruby.h')
         | 
| 34 | 
            -
            # rb_hash_bulk_insert is deep down in a header not included in normal build and that seems to fool have_func.
         | 
| 35 | 
            -
            have_func('rb_hash_bulk_insert', 'ruby.h') unless '2' == version[0] && '6' == version[1]
         | 
| 36 35 |  | 
| 37 36 | 
             
            dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
         | 
| 38 37 |  | 
    
        data/ext/oj/fast.c
    CHANGED
    
    | @@ -40,7 +40,7 @@ typedef struct _doc { | |
| 40 40 | 
             
                Leaf         *where;                  // points to current location
         | 
| 41 41 | 
             
                Leaf          where_path[MAX_STACK];  // points to head of path
         | 
| 42 42 | 
             
                char         *json;
         | 
| 43 | 
            -
                unsigned long size; | 
| 43 | 
            +
                unsigned long size;  // number of leaves/branches in the doc
         | 
| 44 44 | 
             
                VALUE         self;
         | 
| 45 45 | 
             
                Batch         batches;
         | 
| 46 46 | 
             
                struct _batch batch0;
         | 
| @@ -573,7 +573,7 @@ static char *read_quoted_value(ParseInfo pi) { | |
| 573 573 | 
             
                char *h     = pi->s;  // head
         | 
| 574 574 | 
             
                char *t     = h;      // tail
         | 
| 575 575 |  | 
| 576 | 
            -
                h++; | 
| 576 | 
            +
                h++;  // skip quote character
         | 
| 577 577 | 
             
                t++;
         | 
| 578 578 | 
             
                value = h;
         | 
| 579 579 | 
             
                for (; '"' != *h; h++, t++) {
         | 
| @@ -765,7 +765,7 @@ static VALUE parse_json(VALUE clas, char *json, bool given) { | |
| 765 765 | 
             
                pi.s = pi.str;
         | 
| 766 766 | 
             
                doc_init(doc);
         | 
| 767 767 | 
             
                pi.doc = doc;
         | 
| 768 | 
            -
            #if IS_WINDOWS
         | 
| 768 | 
            +
            #if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
         | 
| 769 769 | 
             
                // assume a 1M stack and give half to ruby
         | 
| 770 770 | 
             
                pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
         | 
| 771 771 | 
             
            #else
         | 
    
        data/ext/oj/mimic_json.c
    CHANGED
    
    | @@ -837,11 +837,15 @@ void oj_mimic_json_methods(VALUE json) { | |
| 837 837 | 
             
                } else {
         | 
| 838 838 | 
             
                    json_error = rb_define_class_under(json, "JSONError", rb_eStandardError);
         | 
| 839 839 | 
             
                }
         | 
| 840 | 
            +
             | 
| 841 | 
            +
                rb_global_variable(&oj_json_parser_error_class);
         | 
| 840 842 | 
             
                if (rb_const_defined_at(json, rb_intern("ParserError"))) {
         | 
| 841 843 | 
             
                    oj_json_parser_error_class = rb_const_get(json, rb_intern("ParserError"));
         | 
| 842 844 | 
             
                } else {
         | 
| 843 845 | 
             
                    oj_json_parser_error_class = rb_define_class_under(json, "ParserError", json_error);
         | 
| 844 846 | 
             
                }
         | 
| 847 | 
            +
             | 
| 848 | 
            +
                rb_global_variable(&oj_json_generator_error_class);
         | 
| 845 849 | 
             
                if (rb_const_defined_at(json, rb_intern("GeneratorError"))) {
         | 
| 846 850 | 
             
                    oj_json_generator_error_class = rb_const_get(json, rb_intern("GeneratorError"));
         | 
| 847 851 | 
             
                } else {
         | 
| @@ -867,8 +871,8 @@ void oj_mimic_json_methods(VALUE json) { | |
| 867 871 | 
             
                    rb_require("oj/state");
         | 
| 868 872 | 
             
                }
         | 
| 869 873 | 
             
                // Pull in the JSON::State mimic file.
         | 
| 874 | 
            +
                rb_global_variable(&state_class);
         | 
| 870 875 | 
             
                state_class = rb_const_get_at(generator, rb_intern("State"));
         | 
| 871 | 
            -
                rb_gc_register_mark_object(state_class);
         | 
| 872 876 | 
             
            }
         | 
| 873 877 |  | 
| 874 878 | 
             
            /* Document-module: JSON
         | 
| @@ -905,7 +909,9 @@ oj_define_mimic_json(int argc, VALUE *argv, VALUE self) { | |
| 905 909 | 
             
                }
         | 
| 906 910 | 
             
                oj_mimic_json_methods(json);
         | 
| 907 911 |  | 
| 908 | 
            -
                 | 
| 912 | 
            +
                if (!rb_const_defined(rb_cObject, rb_intern("ActiveSupport"))) {
         | 
| 913 | 
            +
                    rb_define_method(rb_cObject, "to_json", mimic_object_to_json, -1);
         | 
| 914 | 
            +
                }
         | 
| 909 915 |  | 
| 910 916 | 
             
                rb_gv_set("$VERBOSE", verbose);
         | 
| 911 917 |  | 
    
        data/ext/oj/oj.c
    CHANGED
    
    | @@ -36,6 +36,7 @@ ID oj_as_json_id; | |
| 36 36 | 
             
            ID oj_begin_id;
         | 
| 37 37 | 
             
            ID oj_bigdecimal_id;
         | 
| 38 38 | 
             
            ID oj_end_id;
         | 
| 39 | 
            +
            ID oj_eofq_id;
         | 
| 39 40 | 
             
            ID oj_exclude_end_id;
         | 
| 40 41 | 
             
            ID oj_error_id;
         | 
| 41 42 | 
             
            ID oj_file_id;
         | 
| @@ -1849,6 +1850,7 @@ void Init_oj(void) { | |
| 1849 1850 | 
             
                oj_begin_id        = rb_intern("begin");
         | 
| 1850 1851 | 
             
                oj_bigdecimal_id   = rb_intern("BigDecimal");
         | 
| 1851 1852 | 
             
                oj_end_id          = rb_intern("end");
         | 
| 1853 | 
            +
                oj_eofq_id         = rb_intern("eof?");
         | 
| 1852 1854 | 
             
                oj_error_id        = rb_intern("error");
         | 
| 1853 1855 | 
             
                oj_exclude_end_id  = rb_intern("exclude_end?");
         | 
| 1854 1856 | 
             
                oj_file_id         = rb_intern("file?");
         | 
    
        data/ext/oj/oj.h
    CHANGED
    
    
    
        data/ext/oj/parse.c
    CHANGED
    
    | @@ -681,7 +681,7 @@ void oj_parse2(ParseInfo pi) { | |
| 681 681 | 
             
                pi->cur = pi->json;
         | 
| 682 682 | 
             
                err_init(&pi->err);
         | 
| 683 683 | 
             
                while (1) {
         | 
| 684 | 
            -
                    if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
         | 
| 684 | 
            +
                    if (RB_UNLIKELY(0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1)) {
         | 
| 685 685 | 
             
                        VALUE err_clas = oj_get_json_err_class("NestingError");
         | 
| 686 686 |  | 
| 687 687 | 
             
                        oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
         | 
| @@ -689,18 +689,20 @@ void oj_parse2(ParseInfo pi) { | |
| 689 689 | 
             
                        return;
         | 
| 690 690 | 
             
                    }
         | 
| 691 691 | 
             
                    next_non_white(pi);
         | 
| 692 | 
            -
                    if ( | 
| 693 | 
            -
                         | 
| 694 | 
            -
             | 
| 695 | 
            -
             | 
| 696 | 
            -
             | 
| 697 | 
            -
             | 
| 698 | 
            -
                    }
         | 
| 699 | 
            -
             | 
| 700 | 
            -
             | 
| 701 | 
            -
             | 
| 702 | 
            -
             | 
| 703 | 
            -
             | 
| 692 | 
            +
                    if (first) {
         | 
| 693 | 
            +
                        // If no tokens are consumed (i.e. empty string), throw a parse error
         | 
| 694 | 
            +
                        // this is the behavior of JSON.parse in both Ruby and JS.
         | 
| 695 | 
            +
                        if (RB_UNLIKELY('\0' == *pi->cur && No == pi->options.empty_string)) {
         | 
| 696 | 
            +
                            oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
         | 
| 697 | 
            +
                        }
         | 
| 698 | 
            +
                    } else {
         | 
| 699 | 
            +
                        if (RB_UNLIKELY('\0' != *pi->cur)) {
         | 
| 700 | 
            +
                            oj_set_error_at(pi,
         | 
| 701 | 
            +
                                            oj_parse_error_class,
         | 
| 702 | 
            +
                                            __FILE__,
         | 
| 703 | 
            +
                                            __LINE__,
         | 
| 704 | 
            +
                                            "unexpected characters after the JSON document");
         | 
| 705 | 
            +
                        }
         | 
| 704 706 | 
             
                    }
         | 
| 705 707 |  | 
| 706 708 | 
             
                    switch (*pi->cur++) {
         | 
| @@ -761,7 +763,7 @@ void oj_parse2(ParseInfo pi) { | |
| 761 763 | 
             
                    case '\0': pi->cur--; return;
         | 
| 762 764 | 
             
                    default: oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character"); return;
         | 
| 763 765 | 
             
                    }
         | 
| 764 | 
            -
                    if (err_has(&pi->err)) {
         | 
| 766 | 
            +
                    if (RB_UNLIKELY(err_has(&pi->err))) {
         | 
| 765 767 | 
             
                        return;
         | 
| 766 768 | 
             
                    }
         | 
| 767 769 | 
             
                    if (stack_empty(&pi->stack)) {
         | 
    
        data/ext/oj/parser.c
    CHANGED
    
    | @@ -1114,9 +1114,6 @@ static void parse(ojParser p, const byte *json) { | |
| 1114 1114 | 
             
                        p->map = trail_map;
         | 
| 1115 1115 | 
             
                    }
         | 
| 1116 1116 | 
             
                }
         | 
| 1117 | 
            -
                if (0 < p->depth) {
         | 
| 1118 | 
            -
                    parse_error(p, "parse error, not closed");
         | 
| 1119 | 
            -
                }
         | 
| 1120 1117 | 
             
                if (0 == p->depth) {
         | 
| 1121 1118 | 
             
                    switch (p->map[256]) {
         | 
| 1122 1119 | 
             
                    case '0':
         | 
| @@ -1394,6 +1391,12 @@ static VALUE load(VALUE self) { | |
| 1394 1391 | 
             
                    if (0 < RSTRING_LEN(rbuf)) {
         | 
| 1395 1392 | 
             
                        parse(p, (byte *)StringValuePtr(rbuf));
         | 
| 1396 1393 | 
             
                    }
         | 
| 1394 | 
            +
                    if (Qtrue == rb_funcall(p->reader, oj_eofq_id, 0)) {
         | 
| 1395 | 
            +
                        if (0 < p->depth) {
         | 
| 1396 | 
            +
                            parse_error(p, "parse error, not closed");
         | 
| 1397 | 
            +
                        }
         | 
| 1398 | 
            +
                        break;
         | 
| 1399 | 
            +
                    }
         | 
| 1397 1400 | 
             
                }
         | 
| 1398 1401 | 
             
                return Qtrue;
         | 
| 1399 1402 | 
             
            }
         | 
    
        data/ext/oj/parser.h
    CHANGED
    
    | @@ -32,9 +32,9 @@ typedef struct _num { | |
| 32 32 | 
             
                long double dub;
         | 
| 33 33 | 
             
                int64_t     fixnum;  // holds all digits
         | 
| 34 34 | 
             
                uint32_t    len;
         | 
| 35 | 
            -
                int16_t     div; | 
| 35 | 
            +
                int16_t     div;  // 10^div
         | 
| 36 36 | 
             
                int16_t     exp;
         | 
| 37 | 
            -
                uint8_t     shift; | 
| 37 | 
            +
                uint8_t     shift;  // shift of fixnum to get decimal
         | 
| 38 38 | 
             
                bool        neg;
         | 
| 39 39 | 
             
                bool        exp_neg;
         | 
| 40 40 | 
             
                // for numbers as strings, reuse buf
         | 
    
        data/ext/oj/rails.c
    CHANGED
    
    | @@ -1101,6 +1101,8 @@ static VALUE rails_set_decoder(VALUE self) { | |
| 1101 1101 | 
             
                } else {
         | 
| 1102 1102 | 
             
                    json_error = rb_define_class_under(json, "JSONError", rb_eStandardError);
         | 
| 1103 1103 | 
             
                }
         | 
| 1104 | 
            +
             | 
| 1105 | 
            +
                rb_global_variable(&oj_json_parser_error_class);
         | 
| 1104 1106 | 
             
                if (rb_const_defined_at(json, rb_intern("ParserError"))) {
         | 
| 1105 1107 | 
             
                    oj_json_parser_error_class = rb_const_get(json, rb_intern("ParserError"));
         | 
| 1106 1108 | 
             
                } else {
         | 
    
        data/ext/oj/reader.c
    CHANGED
    
    | @@ -101,7 +101,7 @@ int oj_reader_read(Reader reader) { | |
| 101 101 | 
             
                    } else {
         | 
| 102 102 | 
             
                        shift = reader->pro - reader->head - 1;  // leave one character so we can backup one
         | 
| 103 103 | 
             
                    }
         | 
| 104 | 
            -
                    if (0 >= shift) { | 
| 104 | 
            +
                    if (0 >= shift) { /* no space left so allocate more */
         | 
| 105 105 | 
             
                        const char *old  = reader->head;
         | 
| 106 106 | 
             
                        size_t      size = reader->end - reader->head + BUF_PAD;
         | 
| 107 107 |  | 
    
        data/ext/oj/saj.c
    CHANGED
    
    | @@ -578,7 +578,7 @@ static void saj_parse(VALUE handler, char *json) { | |
| 578 578 | 
             
                /* initialize parse info */
         | 
| 579 579 | 
             
                pi.str = json;
         | 
| 580 580 | 
             
                pi.s   = json;
         | 
| 581 | 
            -
            #if IS_WINDOWS
         | 
| 581 | 
            +
            #if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
         | 
| 582 582 | 
             
                pi.stack_min = (void *)((char *)&obj - (512L * 1024L)); /* assume a 1M stack and give half to ruby */
         | 
| 583 583 | 
             
            #else
         | 
| 584 584 | 
             
                {
         | 
| @@ -587,7 +587,7 @@ static void saj_parse(VALUE handler, char *json) { | |
| 587 587 | 
             
                    if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
         | 
| 588 588 | 
             
                        pi.stack_min = (void *)((char *)&obj - (lim.rlim_cur / 4 * 3)); /* let 3/4ths of the stack be used only */
         | 
| 589 589 | 
             
                    } else {
         | 
| 590 | 
            -
                        pi.stack_min = 0; | 
| 590 | 
            +
                        pi.stack_min = 0; /* indicates not to check stack limit */
         | 
| 591 591 | 
             
                    }
         | 
| 592 592 | 
             
                }
         | 
| 593 593 | 
             
            #endif
         | 
    
        data/ext/oj/stream_writer.c
    CHANGED
    
    | @@ -42,7 +42,8 @@ static void stream_writer_write(StreamWriter sw) { | |
| 42 42 |  | 
| 43 43 | 
             
                switch (sw->type) {
         | 
| 44 44 | 
             
                case STRING_IO:
         | 
| 45 | 
            -
                case STREAM_IO: | 
| 45 | 
            +
                case STREAM_IO:
         | 
| 46 | 
            +
                case FILE_IO: {
         | 
| 46 47 | 
             
                    volatile VALUE rs = rb_str_new(sw->sw.out.buf, size);
         | 
| 47 48 |  | 
| 48 49 | 
             
                    // Oddly enough, when pushing ASCII characters with UTF-8 encoding or
         | 
| @@ -53,11 +54,6 @@ static void stream_writer_write(StreamWriter sw) { | |
| 53 54 | 
             
                    rb_funcall(sw->stream, oj_write_id, 1, rs);
         | 
| 54 55 | 
             
                    break;
         | 
| 55 56 | 
             
                }
         | 
| 56 | 
            -
                case FILE_IO:
         | 
| 57 | 
            -
                    if (size != write(sw->fd, sw->sw.out.buf, size)) {
         | 
| 58 | 
            -
                        rb_raise(rb_eIOError, "Write failed. [_%d_:%s]\n", errno, strerror(errno));
         | 
| 59 | 
            -
                    }
         | 
| 60 | 
            -
                    break;
         | 
| 61 57 | 
             
                default: rb_raise(rb_eArgError, "expected an IO Object.");
         | 
| 62 58 | 
             
                }
         | 
| 63 59 | 
             
                stream_writer_reset_buf(sw);
         | 
    
        data/ext/oj/string_writer.c
    CHANGED
    
    | @@ -475,16 +475,16 @@ static VALUE str_writer_to_s(VALUE self) { | |
| 475 475 | 
             
            }
         | 
| 476 476 |  | 
| 477 477 | 
             
            /* Document-method: as_json
         | 
| 478 | 
            -
             * call-seq: as_json()
         | 
| 478 | 
            +
             * call-seq: as_json(*)
         | 
| 479 479 | 
             
             *
         | 
| 480 480 | 
             
             * Returns the contents of the writer as a JSON element. If called from inside
         | 
| 481 481 | 
             
             * an array or hash by Oj the raw buffer will be used othersize a more
         | 
| 482 482 | 
             
             * inefficient parse of the contents and a return of the result is
         | 
| 483 | 
            -
             * completed. The parse uses the strict mode.
         | 
| 483 | 
            +
             * completed. The parse uses the strict mode. Optional arguments are ignored.
         | 
| 484 484 | 
             
             *
         | 
| 485 485 | 
             
             * *return* [_Hash_|_Array_|_String_|_Integer_|_Float_|_True_|_False_|_nil|)
         | 
| 486 486 | 
             
             */
         | 
| 487 | 
            -
            static VALUE str_writer_as_json(VALUE self) {
         | 
| 487 | 
            +
            static VALUE str_writer_as_json(int argc, VALUE *argv, VALUE self) {
         | 
| 488 488 | 
             
                if (string_writer_optimized) {
         | 
| 489 489 | 
             
                    return self;
         | 
| 490 490 | 
             
                }
         | 
| @@ -515,5 +515,5 @@ void oj_string_writer_init(void) { | |
| 515 515 | 
             
                rb_define_method(oj_string_writer_class, "reset", str_writer_reset, 0);
         | 
| 516 516 | 
             
                rb_define_method(oj_string_writer_class, "to_s", str_writer_to_s, 0);
         | 
| 517 517 | 
             
                rb_define_method(oj_string_writer_class, "raw_json", str_writer_to_s, 0);
         | 
| 518 | 
            -
                rb_define_method(oj_string_writer_class, "as_json", str_writer_as_json,  | 
| 518 | 
            +
                rb_define_method(oj_string_writer_class, "as_json", str_writer_as_json, -1);
         | 
| 519 519 | 
             
            }
         | 
    
        data/ext/oj/usual.c
    CHANGED
    
    | @@ -281,7 +281,6 @@ static void close_object(ojParser p) { | |
| 281 281 | 
             
                VALUE         *head = d->vhead + c->vi + 1;
         | 
| 282 282 | 
             
                volatile VALUE obj  = rb_hash_new();
         | 
| 283 283 |  | 
| 284 | 
            -
            #if HAVE_RB_HASH_BULK_INSERT
         | 
| 285 284 | 
             
                for (vp = head; kp < d->ktail; kp++, vp += 2) {
         | 
| 286 285 | 
             
                    *vp = d->get_key(p, kp);
         | 
| 287 286 | 
             
                    if (sizeof(kp->buf) <= (size_t)kp->len) {
         | 
| @@ -289,18 +288,15 @@ static void close_object(ojParser p) { | |
| 289 288 | 
             
                    }
         | 
| 290 289 | 
             
                }
         | 
| 291 290 | 
             
                rb_hash_bulk_insert(d->vtail - head, head, obj);
         | 
| 292 | 
            -
            #else
         | 
| 293 | 
            -
                for (vp = head; kp < d->ktail; kp++, vp += 2) {
         | 
| 294 | 
            -
                    rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
         | 
| 295 | 
            -
                    if (sizeof(kp->buf) <= (size_t)kp->len) {
         | 
| 296 | 
            -
                        OJ_R_FREE(kp->key);
         | 
| 297 | 
            -
                    }
         | 
| 298 | 
            -
                }
         | 
| 299 | 
            -
            #endif
         | 
| 300 291 | 
             
                d->ktail = d->khead + c->ki;
         | 
| 292 | 
            +
             | 
| 301 293 | 
             
                d->vtail = head;
         | 
| 302 294 | 
             
                head--;
         | 
| 303 295 | 
             
                *head = obj;
         | 
| 296 | 
            +
                if (1 == d->vtail - d->vhead && rb_block_given_p()) {
         | 
| 297 | 
            +
                    d->vtail = d->vhead;
         | 
| 298 | 
            +
                    rb_yield(obj);
         | 
| 299 | 
            +
                }
         | 
| 304 300 | 
             
            }
         | 
| 305 301 |  | 
| 306 302 | 
             
            static void close_object_class(ojParser p) {
         | 
| @@ -341,7 +337,6 @@ static void close_object_create(ojParser p) { | |
| 341 337 | 
             
                    head++;
         | 
| 342 338 | 
             
                    if (Qnil == d->hash_class) {
         | 
| 343 339 | 
             
                        obj = rb_hash_new();
         | 
| 344 | 
            -
            #if HAVE_RB_HASH_BULK_INSERT
         | 
| 345 340 | 
             
                        for (vp = head; kp < d->ktail; kp++, vp += 2) {
         | 
| 346 341 | 
             
                            *vp = d->get_key(p, kp);
         | 
| 347 342 | 
             
                            if (sizeof(kp->buf) <= (size_t)kp->len) {
         | 
| @@ -349,14 +344,6 @@ static void close_object_create(ojParser p) { | |
| 349 344 | 
             
                            }
         | 
| 350 345 | 
             
                        }
         | 
| 351 346 | 
             
                        rb_hash_bulk_insert(d->vtail - head, head, obj);
         | 
| 352 | 
            -
            #else
         | 
| 353 | 
            -
                        for (vp = head; kp < d->ktail; kp++, vp += 2) {
         | 
| 354 | 
            -
                            rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
         | 
| 355 | 
            -
                            if (sizeof(kp->buf) <= (size_t)kp->len) {
         | 
| 356 | 
            -
                                OJ_R_FREE(kp->key);
         | 
| 357 | 
            -
                            }
         | 
| 358 | 
            -
                        }
         | 
| 359 | 
            -
            #endif
         | 
| 360 347 | 
             
                    } else {
         | 
| 361 348 | 
             
                        obj = rb_class_new_instance(0, NULL, d->hash_class);
         | 
| 362 349 | 
             
                        for (vp = head; kp < d->ktail; kp++, vp += 2) {
         | 
| @@ -373,7 +360,6 @@ static void close_object_create(ojParser p) { | |
| 373 360 | 
             
                    if (!d->ignore_json_create && rb_respond_to(clas, oj_json_create_id)) {
         | 
| 374 361 | 
             
                        volatile VALUE arg = rb_hash_new();
         | 
| 375 362 |  | 
| 376 | 
            -
            #if HAVE_RB_HASH_BULK_INSERT
         | 
| 377 363 | 
             
                        for (vp = head; kp < d->ktail; kp++, vp += 2) {
         | 
| 378 364 | 
             
                            *vp = d->get_key(p, kp);
         | 
| 379 365 | 
             
                            if (sizeof(kp->buf) <= (size_t)kp->len) {
         | 
| @@ -381,14 +367,6 @@ static void close_object_create(ojParser p) { | |
| 381 367 | 
             
                            }
         | 
| 382 368 | 
             
                        }
         | 
| 383 369 | 
             
                        rb_hash_bulk_insert(d->vtail - head, head, arg);
         | 
| 384 | 
            -
            #else
         | 
| 385 | 
            -
                        for (vp = head; kp < d->ktail; kp++, vp += 2) {
         | 
| 386 | 
            -
                            rb_hash_aset(arg, d->get_key(p, kp), *(vp + 1));
         | 
| 387 | 
            -
                            if (sizeof(kp->buf) <= (size_t)kp->len) {
         | 
| 388 | 
            -
                                OJ_R_FREE(kp->key);
         | 
| 389 | 
            -
                            }
         | 
| 390 | 
            -
                        }
         | 
| 391 | 
            -
            #endif
         | 
| 392 370 | 
             
                        obj = rb_funcall(clas, oj_json_create_id, 1, arg);
         | 
| 393 371 | 
             
                    } else {
         | 
| 394 372 | 
             
                        obj = rb_class_new_instance(0, NULL, clas);
         | 
| @@ -599,7 +577,18 @@ static VALUE result(ojParser p) { | |
| 599 577 | 
             
                Usual d = (Usual)p->ctx;
         | 
| 600 578 |  | 
| 601 579 | 
             
                if (d->vhead < d->vtail) {
         | 
| 602 | 
            -
                     | 
| 580 | 
            +
                    long            cnt = d->vtail - d->vhead;
         | 
| 581 | 
            +
                    volatile VALUE  ary;
         | 
| 582 | 
            +
                    volatile VALUE *vp;
         | 
| 583 | 
            +
             | 
| 584 | 
            +
                    if (1 == cnt) {
         | 
| 585 | 
            +
                        return *d->vhead;
         | 
| 586 | 
            +
                    }
         | 
| 587 | 
            +
                    ary = rb_ary_new();
         | 
| 588 | 
            +
                    for (vp = d->vhead; vp < d->vtail; vp++) {
         | 
| 589 | 
            +
                        rb_ary_push(ary, *vp);
         | 
| 590 | 
            +
                    }
         | 
| 591 | 
            +
                    return ary;
         | 
| 603 592 | 
             
                }
         | 
| 604 593 | 
             
                if (d->raise_on_empty) {
         | 
| 605 594 | 
             
                    rb_raise(oj_parse_error_class, "empty string");
         | 
| @@ -845,8 +834,8 @@ static VALUE opt_create_id_set(ojParser p, VALUE value) { | |
| 845 834 | 
             
                    rb_check_type(value, T_STRING);
         | 
| 846 835 | 
             
                    size_t len = RSTRING_LEN(value);
         | 
| 847 836 |  | 
| 848 | 
            -
                    if (1 << sizeof(d->create_id_len) <= len) {
         | 
| 849 | 
            -
                        rb_raise(rb_eArgError, "The create_id values is limited to %d bytes.", 1 << sizeof(d->create_id_len));
         | 
| 837 | 
            +
                    if (1 << (8 * sizeof(d->create_id_len)) <= len) {
         | 
| 838 | 
            +
                        rb_raise(rb_eArgError, "The create_id values is limited to %d bytes.", 1 << (8 * sizeof(d->create_id_len)));
         | 
| 850 839 | 
             
                    }
         | 
| 851 840 | 
             
                    d->create_id_len                  = (uint8_t)len;
         | 
| 852 841 | 
             
                    d->create_id                      = str_dup(RSTRING_PTR(value), len);
         | 
    
        data/lib/oj/mimic.rb
    CHANGED
    
    
    
        data/lib/oj/schandler.rb
    CHANGED
    
    | @@ -64,13 +64,14 @@ module Oj | |
| 64 64 | 
             
              #
         | 
| 65 65 | 
             
              #    hash_end
         | 
| 66 66 | 
             
              #
         | 
| 67 | 
            -
              #  | 
| 68 | 
            -
              # | 
| 69 | 
            -
              # the key-value pair that follows.
         | 
| 67 | 
            +
              #  At the end of a JSON object element the hash_end() callback is called if
         | 
| 68 | 
            +
              #  public.
         | 
| 70 69 | 
             
              #
         | 
| 71 70 | 
             
              #    hash_key
         | 
| 72 71 | 
             
              #
         | 
| 73 | 
            -
              #  | 
| 72 | 
            +
              # When a hash key is encountered the hash_key() method is called with the
         | 
| 73 | 
            +
              # parsed hash value key. The return value from the call is then used as the
         | 
| 74 | 
            +
              # key in the key-value pair that follows.
         | 
| 74 75 | 
             
              #
         | 
| 75 76 | 
             
              #    hash_set
         | 
| 76 77 | 
             
              #
         | 
    
        data/lib/oj/version.rb
    CHANGED
    
    
| @@ -74,42 +74,77 @@ class TestJSONEncoding < ActiveSupport::TestCase | |
| 74 74 | 
             
                ActiveSupport.escape_html_entities_in_json = false
         | 
| 75 75 | 
             
              end
         | 
| 76 76 |  | 
| 77 | 
            -
              def  | 
| 78 | 
            -
                 | 
| 79 | 
            -
                # ActiveSupport.escape_html_entities_in_json reverts to true even after
         | 
| 80 | 
            -
                # being set to false. I haven't been able to figure that out so the value is
         | 
| 81 | 
            -
                # set to true, the default, before running the test. This might be wrong but
         | 
| 82 | 
            -
                # for now it will have to do.
         | 
| 83 | 
            -
                ActiveSupport.escape_html_entities_in_json = true
         | 
| 84 | 
            -
                result = ActiveSupport::JSON.encode("€2.99")
         | 
| 85 | 
            -
                assert_equal '"€2.99"', result
         | 
| 86 | 
            -
                assert_equal(Encoding::UTF_8, result.encoding)
         | 
| 87 | 
            -
             | 
| 88 | 
            -
                result = ActiveSupport::JSON.encode("✎☺")
         | 
| 89 | 
            -
                assert_equal '"✎☺"', result
         | 
| 90 | 
            -
                assert_equal(Encoding::UTF_8, result.encoding)
         | 
| 77 | 
            +
              def test_hash_keys_encoding_without_escaping
         | 
| 78 | 
            +
                assert_equal "{\"<>\":\"<>\"}", ActiveSupport::JSON.encode("<>" => "<>")
         | 
| 91 79 | 
             
              end
         | 
| 92 80 |  | 
| 93 | 
            -
               | 
| 94 | 
            -
                 | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 81 | 
            +
              module UnicodeTests
         | 
| 82 | 
            +
                def test_utf8_string_encoded_properly
         | 
| 83 | 
            +
                  result = ActiveSupport::JSON.encode("€2.99")
         | 
| 84 | 
            +
                  assert_equal '"€2.99"', result
         | 
| 85 | 
            +
                  assert_equal(Encoding::UTF_8, result.encoding)
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  result = ActiveSupport::JSON.encode("✎☺")
         | 
| 88 | 
            +
                  assert_equal '"✎☺"', result
         | 
| 89 | 
            +
                  assert_equal(Encoding::UTF_8, result.encoding)
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                def test_non_utf8_string_transcodes
         | 
| 93 | 
            +
                  s = "二".encode("Shift_JIS")
         | 
| 94 | 
            +
                  result = ActiveSupport::JSON.encode(s)
         | 
| 95 | 
            +
                  assert_equal '"二"', result
         | 
| 96 | 
            +
                  assert_equal Encoding::UTF_8, result.encoding
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                def test_wide_utf8_chars
         | 
| 100 | 
            +
                  w = "𠜎"
         | 
| 101 | 
            +
                  result = ActiveSupport::JSON.encode(w)
         | 
| 102 | 
            +
                  assert_equal '"𠜎"', result
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                def test_wide_utf8_roundtrip
         | 
| 106 | 
            +
                  hash = { string: "𐒑" }
         | 
| 107 | 
            +
                  json = ActiveSupport::JSON.encode(hash)
         | 
| 108 | 
            +
                  decoded_hash = ActiveSupport::JSON.decode(json)
         | 
| 109 | 
            +
                  assert_equal "𐒑", decoded_hash["string"]
         | 
| 110 | 
            +
                end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                def test_invalid_encoding_raises
         | 
| 113 | 
            +
                  s = "\xAE\xFF\x9F"
         | 
| 114 | 
            +
                  refute s.valid_encoding?
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                  # n.b. this raises EncodingError, because we didn't call Oj.mimic_JSON in the test setup; but,
         | 
| 117 | 
            +
                  # if you do that (even indirectly through Oj.optimize_rails), then this raises a
         | 
| 118 | 
            +
                  # JSON::GeneratorError instead of an EncodingError.
         | 
| 119 | 
            +
                  assert_raises(EncodingError) do
         | 
| 120 | 
            +
                    ActiveSupport::JSON.encode([s])
         | 
| 121 | 
            +
                  end
         | 
| 122 | 
            +
                end
         | 
| 98 123 | 
             
              end
         | 
| 99 124 |  | 
| 100 | 
            -
               | 
| 101 | 
            -
                 | 
| 102 | 
            -
             | 
| 103 | 
            -
                 | 
| 125 | 
            +
              module UnicodeTestsWithEscapingOn
         | 
| 126 | 
            +
                def setup
         | 
| 127 | 
            +
                  ActiveSupport.escape_html_entities_in_json = true
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                def teardown
         | 
| 131 | 
            +
                  ActiveSupport.escape_html_entities_in_json = false
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                include UnicodeTests
         | 
| 104 135 | 
             
              end
         | 
| 105 136 |  | 
| 106 | 
            -
               | 
| 107 | 
            -
                 | 
| 108 | 
            -
             | 
| 109 | 
            -
                 | 
| 110 | 
            -
             | 
| 137 | 
            +
              module UnicodeTestsWithEscapingOff
         | 
| 138 | 
            +
                def setup
         | 
| 139 | 
            +
                  ActiveSupport.escape_html_entities_in_json = false
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                include UnicodeTests
         | 
| 111 143 | 
             
              end
         | 
| 112 144 |  | 
| 145 | 
            +
              include UnicodeTestsWithEscapingOn
         | 
| 146 | 
            +
              include UnicodeTestsWithEscapingOff
         | 
| 147 | 
            +
             | 
| 113 148 | 
             
              def test_hash_key_identifiers_are_always_quoted
         | 
| 114 149 | 
             
                values = { 0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B" }
         | 
| 115 150 | 
             
                assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values))
         | 
| @@ -19,7 +19,10 @@ require "active_support" | |
| 19 19 | 
             
            Thread.abort_on_exception = true
         | 
| 20 20 |  | 
| 21 21 | 
             
            # Show backtraces for deprecated behavior for quicker cleanup.
         | 
| 22 | 
            -
            ActiveSupport::Deprecation.debug | 
| 22 | 
            +
            if ActiveSupport::Deprecation.respond_to?(:debug)
         | 
| 23 | 
            +
              # Rails 7.2 does not have ActiveSupport::Deprecation.debug
         | 
| 24 | 
            +
              ActiveSupport::Deprecation.debug = true
         | 
| 25 | 
            +
            end
         | 
| 23 26 |  | 
| 24 27 | 
             
            # Default to old to_time behavior but allow running tests with new behavior
         | 
| 25 28 | 
             
            ActiveSupport.to_time_preserves_timezone = ENV["PRESERVE_TIMEZONES"] == "1"
         | 
| @@ -8,9 +8,18 @@ require "active_support/time" | |
| 8 8 | 
             
            require_relative "time_zone_test_helpers"
         | 
| 9 9 | 
             
            require_relative "encoding_test_cases"
         | 
| 10 10 |  | 
| 11 | 
            +
            require 'oj'
         | 
| 12 | 
            +
            # Sets the ActiveSupport encoder to be Oj and also wraps the setting of globals.
         | 
| 13 | 
            +
            Oj::Rails.set_encoder()
         | 
| 14 | 
            +
            Oj::Rails.optimize()
         | 
| 15 | 
            +
             | 
| 11 16 | 
             
            class TestJSONEncoding < ActiveSupport::TestCase
         | 
| 12 17 | 
             
              include TimeZoneTestHelpers
         | 
| 13 18 |  | 
| 19 | 
            +
              def test_is_actually_oj
         | 
| 20 | 
            +
                assert_equal Oj::Rails::Encoder, ActiveSupport.json_encoder
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 14 23 | 
             
              def sorted_json(json)
         | 
| 15 24 | 
             
                if json.start_with?("{") && json.end_with?("}")
         | 
| 16 25 | 
             
                  "{" + json[1..-2].split(",").sort.join(",") + "}"
         | 
| @@ -61,36 +70,77 @@ class TestJSONEncoding < ActiveSupport::TestCase | |
| 61 70 | 
             
                ActiveSupport.escape_html_entities_in_json = false
         | 
| 62 71 | 
             
              end
         | 
| 63 72 |  | 
| 64 | 
            -
              def  | 
| 65 | 
            -
                 | 
| 66 | 
            -
                assert_equal '"€2.99"', result
         | 
| 67 | 
            -
                assert_equal(Encoding::UTF_8, result.encoding)
         | 
| 68 | 
            -
             | 
| 69 | 
            -
                result = ActiveSupport::JSON.encode("✎☺")
         | 
| 70 | 
            -
                assert_equal '"✎☺"', result
         | 
| 71 | 
            -
                assert_equal(Encoding::UTF_8, result.encoding)
         | 
| 73 | 
            +
              def test_hash_keys_encoding_without_escaping
         | 
| 74 | 
            +
                assert_equal "{\"<>\":\"<>\"}", ActiveSupport::JSON.encode("<>" => "<>")
         | 
| 72 75 | 
             
              end
         | 
| 73 76 |  | 
| 74 | 
            -
               | 
| 75 | 
            -
                 | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 77 | 
            +
              module UnicodeTests
         | 
| 78 | 
            +
                def test_utf8_string_encoded_properly
         | 
| 79 | 
            +
                  result = ActiveSupport::JSON.encode("€2.99")
         | 
| 80 | 
            +
                  assert_equal '"€2.99"', result
         | 
| 81 | 
            +
                  assert_equal(Encoding::UTF_8, result.encoding)
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  result = ActiveSupport::JSON.encode("✎☺")
         | 
| 84 | 
            +
                  assert_equal '"✎☺"', result
         | 
| 85 | 
            +
                  assert_equal(Encoding::UTF_8, result.encoding)
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                def test_non_utf8_string_transcodes
         | 
| 89 | 
            +
                  s = "二".encode("Shift_JIS")
         | 
| 90 | 
            +
                  result = ActiveSupport::JSON.encode(s)
         | 
| 91 | 
            +
                  assert_equal '"二"', result
         | 
| 92 | 
            +
                  assert_equal Encoding::UTF_8, result.encoding
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                def test_wide_utf8_chars
         | 
| 96 | 
            +
                  w = "𠜎"
         | 
| 97 | 
            +
                  result = ActiveSupport::JSON.encode(w)
         | 
| 98 | 
            +
                  assert_equal '"𠜎"', result
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                def test_wide_utf8_roundtrip
         | 
| 102 | 
            +
                  hash = { string: "𐒑" }
         | 
| 103 | 
            +
                  json = ActiveSupport::JSON.encode(hash)
         | 
| 104 | 
            +
                  decoded_hash = ActiveSupport::JSON.decode(json)
         | 
| 105 | 
            +
                  assert_equal "𐒑", decoded_hash["string"]
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                def test_invalid_encoding_raises
         | 
| 109 | 
            +
                  s = "\xAE\xFF\x9F"
         | 
| 110 | 
            +
                  refute s.valid_encoding?
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                  # n.b. this raises EncodingError, because we didn't call Oj.mimic_JSON in the test setup; but,
         | 
| 113 | 
            +
                  # if you do that (even indirectly through Oj.optimize_rails), then this raises a
         | 
| 114 | 
            +
                  # JSON::GeneratorError instead of an EncodingError.
         | 
| 115 | 
            +
                  assert_raises(EncodingError) do
         | 
| 116 | 
            +
                    ActiveSupport::JSON.encode([s])
         | 
| 117 | 
            +
                  end
         | 
| 118 | 
            +
                end
         | 
| 79 119 | 
             
              end
         | 
| 80 120 |  | 
| 81 | 
            -
               | 
| 82 | 
            -
                 | 
| 83 | 
            -
             | 
| 84 | 
            -
                 | 
| 121 | 
            +
              module UnicodeTestsWithEscapingOn
         | 
| 122 | 
            +
                def setup
         | 
| 123 | 
            +
                  ActiveSupport.escape_html_entities_in_json = true
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                def teardown
         | 
| 127 | 
            +
                  ActiveSupport.escape_html_entities_in_json = false
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                include UnicodeTests
         | 
| 85 131 | 
             
              end
         | 
| 86 132 |  | 
| 87 | 
            -
               | 
| 88 | 
            -
                 | 
| 89 | 
            -
             | 
| 90 | 
            -
                 | 
| 91 | 
            -
             | 
| 133 | 
            +
              module UnicodeTestsWithEscapingOff
         | 
| 134 | 
            +
                def setup
         | 
| 135 | 
            +
                  ActiveSupport.escape_html_entities_in_json = false
         | 
| 136 | 
            +
                end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                include UnicodeTests
         | 
| 92 139 | 
             
              end
         | 
| 93 140 |  | 
| 141 | 
            +
              include UnicodeTestsWithEscapingOn
         | 
| 142 | 
            +
              include UnicodeTestsWithEscapingOff
         | 
| 143 | 
            +
             | 
| 94 144 | 
             
              def test_hash_key_identifiers_are_always_quoted
         | 
| 95 145 | 
             
                values = { 0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B" }
         | 
| 96 146 | 
             
                assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values))
         | 
    
        data/test/foo.rb
    CHANGED
    
    | @@ -5,10 +5,22 @@ $LOAD_PATH << '.' | |
| 5 5 | 
             
            $LOAD_PATH << File.join(__dir__, '../lib')
         | 
| 6 6 | 
             
            $LOAD_PATH << File.join(__dir__, '../ext')
         | 
| 7 7 |  | 
| 8 | 
            -
            require 'json'
         | 
| 9 8 | 
             
            require 'oj'
         | 
| 10 | 
            -
            require 'oj/json'
         | 
| 11 9 |  | 
| 12 | 
            -
             | 
| 10 | 
            +
            reader, writer = IO.pipe
         | 
| 13 11 |  | 
| 14 | 
            -
             | 
| 12 | 
            +
            thread =
         | 
| 13 | 
            +
              Thread.new do
         | 
| 14 | 
            +
                5.times do |id|
         | 
| 15 | 
            +
                  Oj.to_stream(writer, { "id" => id })
         | 
| 16 | 
            +
                  sleep(1)
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                writer.close
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            p = Oj::Parser.new(:usual)
         | 
| 23 | 
            +
            p.load(reader) { |data| puts "#{Time.now} -- ID: #{data["id"]}" }
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            reader.close
         | 
| 26 | 
            +
            thread.join
         | 
    
        data/test/test_compat.rb
    CHANGED
    
    | @@ -468,7 +468,7 @@ class CompatJuice < Minitest::Test | |
| 468 468 |  | 
| 469 469 | 
             
              def test_arg_passing
         | 
| 470 470 | 
             
                json = Oj.to_json(Argy.new(), :max_nesting => 40)
         | 
| 471 | 
            -
                 | 
| 471 | 
            +
                assert_match(/.*max_nesting.*40.*/, json)
         | 
| 472 472 | 
             
              end
         | 
| 473 473 |  | 
| 474 474 | 
             
              def test_max_nesting
         | 
    
        data/test/test_parser_usual.rb
    CHANGED
    
    | @@ -114,6 +114,30 @@ class UsualTest < Minitest::Test | |
| 114 114 | 
             
                assert_equal(Float, doc.class)
         | 
| 115 115 | 
             
              end
         | 
| 116 116 |  | 
| 117 | 
            +
              def test_multi_parse
         | 
| 118 | 
            +
                p = Oj::Parser.new(:usual)
         | 
| 119 | 
            +
                out = []
         | 
| 120 | 
            +
                p.parse('{"a":1}{"b":{"x":2}} {"c":3}') { |j| out.push(j) }
         | 
| 121 | 
            +
                assert_equal([{'a'=>1}, {'b'=>{'x'=>2}},{'c'=>3}], out)
         | 
| 122 | 
            +
              end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
              def test_multi_load
         | 
| 125 | 
            +
                p = Oj::Parser.new(:usual)
         | 
| 126 | 
            +
                out = []
         | 
| 127 | 
            +
                r, w = IO.pipe
         | 
| 128 | 
            +
                thread = Thread.new do
         | 
| 129 | 
            +
                  ['{"a":1}', '{"b":{"x"', ':2}}{"c":', '3}'].each { |seg|
         | 
| 130 | 
            +
                    w.write(seg)
         | 
| 131 | 
            +
                    sleep(0.1)
         | 
| 132 | 
            +
                  }
         | 
| 133 | 
            +
                  w.close
         | 
| 134 | 
            +
                end
         | 
| 135 | 
            +
                p.load(r) { |j| out.push(j) }
         | 
| 136 | 
            +
                r.close
         | 
| 137 | 
            +
                thread.join
         | 
| 138 | 
            +
                assert_equal([{'a'=>1}, {'b'=>{'x'=>2}},{'c'=>3}], out)
         | 
| 139 | 
            +
              end
         | 
| 140 | 
            +
             | 
| 117 141 | 
             
              def test_omit_null
         | 
| 118 142 | 
             
                p = Oj::Parser.new(:usual)
         | 
| 119 143 | 
             
                p.omit_null = true
         | 
| @@ -193,6 +217,10 @@ class UsualTest < Minitest::Test | |
| 193 217 |  | 
| 194 218 | 
             
                doc = p.parse('{"a":true,"^":"UsualTest::MyClass","b":false}')
         | 
| 195 219 | 
             
                assert_equal('UsualTest::MyClass{a: true b: false}', doc.to_s)
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                p.create_id = 'class'
         | 
| 222 | 
            +
                doc = p.parse('{"a":true,"class":"UsualTest::MyClass","b":false}')
         | 
| 223 | 
            +
                assert_equal('UsualTest::MyClass{a: true b: false}', doc.to_s)
         | 
| 196 224 | 
             
              end
         | 
| 197 225 |  | 
| 198 226 | 
             
              def test_missing_class
         | 
    
        data/test/test_writer.rb
    CHANGED
    
    | @@ -4,6 +4,7 @@ | |
| 4 4 | 
             
            $LOAD_PATH << __dir__
         | 
| 5 5 |  | 
| 6 6 | 
             
            require 'helper'
         | 
| 7 | 
            +
            require 'open3'
         | 
| 7 8 |  | 
| 8 9 | 
             
            class OjWriter < Minitest::Test
         | 
| 9 10 |  | 
| @@ -377,4 +378,19 @@ class OjWriter < Minitest::Test | |
| 377 378 | 
             
                w.pop()
         | 
| 378 379 | 
             
                assert_equal(%|{"nothing":null}\n|, output.string())
         | 
| 379 380 | 
             
              end
         | 
| 381 | 
            +
             | 
| 382 | 
            +
              def test_stream_writer_subprocess
         | 
| 383 | 
            +
                skip if RbConfig::CONFIG['host_os'] =~ /(mingw|mswin)/
         | 
| 384 | 
            +
             | 
| 385 | 
            +
                Open3.popen3("/bin/bash", "-c", "cat > /dev/null") do |stdin, _stdout, _stderr, _wait_thr|
         | 
| 386 | 
            +
                  w = Oj::StreamWriter.new(stdin, :indent => 0)
         | 
| 387 | 
            +
                  w.push_array()
         | 
| 388 | 
            +
                  chunk = "{\"foo\":\"#{"bar"*1000}\"}"
         | 
| 389 | 
            +
                  1000.times do |_|
         | 
| 390 | 
            +
                    w.push_json(chunk)
         | 
| 391 | 
            +
                  end
         | 
| 392 | 
            +
                  w.pop()
         | 
| 393 | 
            +
                  stdin.close
         | 
| 394 | 
            +
                end
         | 
| 395 | 
            +
              end
         | 
| 380 396 | 
             
            end # OjWriter
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: oj
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 3.16. | 
| 4 | 
            +
              version: 3.16.9
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Peter Ohler
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2024-12-28 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bigdecimal
         | 
| @@ -24,6 +24,20 @@ dependencies: | |
| 24 24 | 
             
                - - ">="
         | 
| 25 25 | 
             
                  - !ruby/object:Gem::Version
         | 
| 26 26 | 
             
                    version: '3.0'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: ostruct
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - ">="
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '0.2'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - ">="
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '0.2'
         | 
| 27 41 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 42 | 
             
              name: minitest
         | 
| 29 43 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -308,7 +322,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 308 322 | 
             
                - !ruby/object:Gem::Version
         | 
| 309 323 | 
             
                  version: '0'
         | 
| 310 324 | 
             
            requirements: []
         | 
| 311 | 
            -
            rubygems_version: 3. | 
| 325 | 
            +
            rubygems_version: 3.5.11
         | 
| 312 326 | 
             
            signing_key:
         | 
| 313 327 | 
             
            specification_version: 4
         | 
| 314 328 | 
             
            summary: A fast JSON parser and serializer.
         |