json 2.7.5 → 2.9.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8138305febf8acb1cc4533b5e5b071928167b88f17567cf18d99fc1dcbeef234
4
- data.tar.gz: 9648ecf580a9f686cd1fb879efbeb85509c800f13c1372e282298eb713dce740
3
+ metadata.gz: 8404bbc1f96ebfb471ebafc0fadef1f11125cd62c6cf1157091793183a8bdc32
4
+ data.tar.gz: 4c93c85a2575eda3308cf404f116d8e638605d884fb14cd3ab3ee511b649c211
5
5
  SHA512:
6
- metadata.gz: 6a34f95616a3ac364d06004e8b2049c36b760206553e2596993af96cf295755019c083f2e1a51b931829dc199c2ebc919dfbac2079be9c7198b8ed03ae999df6
7
- data.tar.gz: fbd73f6e5fa24dd5406556a0dba0ad8cb7d3c704700d29322eded2a2bc6405a16dd12d7cadd6792153970b7dfbbf013dddf70d626ae05609c57a76c4401de950
6
+ metadata.gz: 2043b99c8ca617249d1b3c81d899ebaf86dc2436196f08b0f9a32ee3ba7fa174c20124ca0f75157828049dc71f46fcd8937978cf463f952b2f2e6af113dfab42
7
+ data.tar.gz: bccae4170d82569a9018ef7497bfb5b228a9371e7c79d84cb4fcc7659be144e3a3ba455db40ecf509a31b576f311d1ad4f85989aea549f59c003210adf87fc77
data/CHANGES.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # Changes
2
2
 
3
+ ### 2024-12-18 (2.9.1)
4
+
5
+ * Fix support for Solaris 10.
6
+
7
+ ### 2024-12-03 (2.9.0)
8
+
9
+ * Fix C implementation of `script_safe` escaping to not confuse some other 3 wide characters with `\u2028` and `\u2029`.
10
+ e.g. `JSON.generate(["倩", "瀨"], script_safe: true)` would generate the wrong JSON.
11
+ * `JSON.dump(object, some_io)` now write into the IO in chunks while previously it would buffer the entire JSON before writing.
12
+ * `JSON::GeneratorError` now has a `#invalid_object` attribute, making it easier to understand why an object tree cannot be serialized.
13
+ * Numerous improvements to the JRuby extension.
14
+
15
+ ### 2024-11-14 (2.8.2)
16
+
17
+ * `JSON.load_file` explictly read the file as UTF-8.
18
+
19
+ ### 2024-11-06 (2.8.1)
20
+
21
+ * Fix the java packages to include the extension.
22
+
23
+ ### 2024-11-06 (2.8.0)
24
+
25
+ * Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being explictly enabled.
26
+ * Prefer to use `JSON.unsafe_load(string)` or `JSON.load(string, create_additions: true)`.
27
+ * Emit a deprecation warning when serializing valid UTF-8 strings encoded in `ASCII_8BIT` aka `BINARY`.
28
+ * Bump required Ruby version to 2.7.
29
+ * Add support for optionally parsing trailing commas, via `allow_trailing_comma: true`, which in cunjunction with the
30
+ pre-existing support for comments, make it suitable to parse `jsonc` documents.
31
+ * Many performance improvements to `JSON.parse` and `JSON.load`, up to `1.7x` faster on real world documents.
32
+ * Some minor performance improvements to `JSON.dump` and `JSON.generate`.
33
+ * `JSON.pretty_generate` no longer include newline inside empty object and arrays.
34
+
35
+ ### 2024-11-04 (2.7.6)
36
+
37
+ * Fix a regression in JSON.generate when dealing with Hash keys that are string subclasses, call `to_json` on them.
38
+
3
39
  ### 2024-10-25 (2.7.5)
4
40
 
5
41
  * Fix a memory leak when `#to_json` methods raise an exception.
data/README.md CHANGED
@@ -5,16 +5,10 @@
5
5
  ## Description
6
6
 
7
7
  This is an implementation of the JSON specification according to RFC 7159
8
- http://www.ietf.org/rfc/rfc7159.txt . There is two variants available:
8
+ http://www.ietf.org/rfc/rfc7159.txt .
9
9
 
10
- * A pure ruby variant, that relies on the `strscan` extensions, which is
11
- part of the ruby standard library.
12
- * The quite a bit faster native extension variant, which is in parts
13
- implemented in C or Java and comes with a parser generated by the [Ragel]
14
- state machine compiler.
15
-
16
- Both variants of the JSON generator generate UTF-8 character sequences by
17
- default. If an :ascii\_only option with a true value is given, they escape all
10
+ The JSON generator generate UTF-8 character sequences by default.
11
+ If an :ascii\_only option with a true value is given, they escape all
18
12
  non-ASCII and control characters with \uXXXX escape sequences, and support
19
13
  UTF-16 surrogate pairs in order to be able to generate the whole range of
20
14
  unicode code points.
@@ -27,10 +21,6 @@ endpoint.
27
21
 
28
22
  ## Installation
29
23
 
30
- It's recommended to use the extension variant of JSON, because it's faster than
31
- the pure ruby variant. If you cannot build it on your system, you can settle
32
- for the latter.
33
-
34
24
  Install the gem and add to the application's Gemfile by executing:
35
25
 
36
26
  $ bundle add json
@@ -39,12 +29,6 @@ If bundler is not being used to manage dependencies, install the gem by executin
39
29
 
40
30
  $ gem install json
41
31
 
42
-
43
- There is also a pure ruby json only variant of the gem, that can be installed
44
- with:
45
-
46
- $ gem install json_pure
47
-
48
32
  ## Usage
49
33
 
50
34
  To use JSON you can
@@ -53,20 +37,6 @@ To use JSON you can
53
37
  require 'json'
54
38
  ```
55
39
 
56
- to load the installed variant (either the extension `'json'` or the pure
57
- variant `'json_pure'`). If you have installed the extension variant, you can
58
- pick either the extension variant or the pure variant by typing
59
-
60
- ```ruby
61
- require 'json/ext'
62
- ```
63
-
64
- or
65
-
66
- ```ruby
67
- require 'json/pure'
68
- ```
69
-
70
40
  Now you can parse a JSON document into a ruby data structure by calling
71
41
 
72
42
  ```ruby
@@ -82,50 +52,11 @@ You can also use the `pretty_generate` method (which formats the output more
82
52
  verbosely and nicely) or `fast_generate` (which doesn't do any of the security
83
53
  checks generate performs, e. g. nesting deepness checks).
84
54
 
85
- There are also the JSON and JSON[] methods which use parse on a String or
86
- generate a JSON document from an array or hash:
87
-
88
- ```ruby
89
- document = JSON 'test' => 23 # => "{\"test\":23}"
90
- document = JSON['test' => 23] # => "{\"test\":23}"
91
- ```
92
-
93
- and
94
-
95
- ```ruby
96
- data = JSON '{"test":23}' # => {"test"=>23}
97
- data = JSON['{"test":23}'] # => {"test"=>23}
98
- ```
99
-
100
- You can choose to load a set of common additions to ruby core's objects if
101
- you
102
-
103
- ```ruby
104
- require 'json/add/core'
105
- ```
106
-
107
- After requiring this you can, e. g., serialise/deserialise Ruby ranges:
108
-
109
- ```ruby
110
- JSON JSON(1..10) # => 1..10
111
- ```
112
-
113
- To find out how to add JSON support to other or your own classes, read the
114
- section "More Examples" below.
115
-
116
- ## Serializing exceptions
117
-
118
- The JSON module doesn't extend `Exception` by default. If you convert an `Exception`
119
- object to JSON, it will by default only include the exception message.
120
-
121
- To include the full details, you must either load the `json/add/core` mentioned
122
- above, or specifically load the exception addition:
123
-
124
- ```ruby
125
- require 'json/add/exception'
126
- ```
55
+ ## Handling arbitrary types
127
56
 
128
- ## More Examples
57
+ > [!CAUTION]
58
+ > You should never use `JSON.unsafe_load` nor `JSON.parse(str, create_additions: true)` to parse untrusted user input,
59
+ > as it can lead to remote code execution vulnerabilities.
129
60
 
130
61
  To create a JSON document from a ruby data structure, you can call
131
62
  `JSON.generate` like that:
@@ -191,7 +122,7 @@ JSON.parse json
191
122
  # => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
192
123
  json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
193
124
  # => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
194
- JSON.parse json, :create_additions => true
125
+ JSON.unsafe_load json
195
126
  # => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
196
127
  ```
197
128
 
@@ -4,21 +4,60 @@
4
4
  #include "ruby.h"
5
5
  #include "ruby/encoding.h"
6
6
 
7
+ /* shims */
8
+ /* This is the fallback definition from Ruby 3.4 */
9
+
10
+ #ifndef RBIMPL_STDBOOL_H
11
+ #if defined(__cplusplus)
12
+ # if defined(HAVE_STDBOOL_H) && (__cplusplus >= 201103L)
13
+ # include <cstdbool>
14
+ # endif
15
+ #elif defined(HAVE_STDBOOL_H)
16
+ # include <stdbool.h>
17
+ #elif !defined(HAVE__BOOL)
18
+ typedef unsigned char _Bool;
19
+ # define bool _Bool
20
+ # define true ((_Bool)+1)
21
+ # define false ((_Bool)+0)
22
+ # define __bool_true_false_are_defined
23
+ #endif
24
+ #endif
25
+
26
+ #ifndef RB_UNLIKELY
27
+ #define RB_UNLIKELY(expr) expr
28
+ #endif
29
+
30
+ #ifndef RB_LIKELY
31
+ #define RB_LIKELY(expr) expr
32
+ #endif
33
+
34
+ #ifndef MAYBE_UNUSED
35
+ # define MAYBE_UNUSED(x) x
36
+ #endif
37
+
38
+ enum fbuffer_type {
39
+ FBUFFER_HEAP_ALLOCATED = 0,
40
+ FBUFFER_STACK_ALLOCATED = 1,
41
+ };
42
+
7
43
  typedef struct FBufferStruct {
44
+ enum fbuffer_type type;
8
45
  unsigned long initial_length;
9
- char *ptr;
10
46
  unsigned long len;
11
47
  unsigned long capa;
48
+ char *ptr;
49
+ VALUE io;
12
50
  } FBuffer;
13
51
 
52
+ #define FBUFFER_STACK_SIZE 512
53
+ #define FBUFFER_IO_BUFFER_SIZE (16384 - 1)
14
54
  #define FBUFFER_INITIAL_LENGTH_DEFAULT 1024
15
55
 
16
- #define FBUFFER_PTR(fb) (fb->ptr)
17
- #define FBUFFER_LEN(fb) (fb->len)
18
- #define FBUFFER_CAPA(fb) (fb->capa)
56
+ #define FBUFFER_PTR(fb) ((fb)->ptr)
57
+ #define FBUFFER_LEN(fb) ((fb)->len)
58
+ #define FBUFFER_CAPA(fb) ((fb)->capa)
19
59
  #define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb)
20
60
 
21
- static FBuffer *fbuffer_alloc(unsigned long initial_length);
22
61
  static void fbuffer_free(FBuffer *fb);
23
62
  #ifndef JSON_GENERATOR
24
63
  static void fbuffer_clear(FBuffer *fb);
@@ -27,55 +66,85 @@ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len);
27
66
  #ifdef JSON_GENERATOR
28
67
  static void fbuffer_append_long(FBuffer *fb, long number);
29
68
  #endif
30
- static void fbuffer_append_char(FBuffer *fb, char newchr);
69
+ static inline void fbuffer_append_char(FBuffer *fb, char newchr);
31
70
  #ifdef JSON_GENERATOR
32
- static VALUE fbuffer_to_s(FBuffer *fb);
33
- #endif
34
-
35
- #ifndef RB_UNLIKELY
36
- #define RB_UNLIKELY(expr) expr
71
+ static VALUE fbuffer_finalize(FBuffer *fb);
37
72
  #endif
38
73
 
39
- static FBuffer *fbuffer_alloc(unsigned long initial_length)
74
+ static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size)
40
75
  {
41
- FBuffer *fb;
42
- if (initial_length <= 0) initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT;
43
- fb = ALLOC(FBuffer);
44
- memset((void *) fb, 0, sizeof(FBuffer));
45
- fb->initial_length = initial_length;
46
- return fb;
76
+ fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
77
+ if (stack_buffer) {
78
+ fb->type = FBUFFER_STACK_ALLOCATED;
79
+ fb->ptr = stack_buffer;
80
+ fb->capa = stack_buffer_size;
81
+ }
47
82
  }
48
83
 
49
84
  static void fbuffer_free(FBuffer *fb)
50
85
  {
51
- if (fb->ptr) ruby_xfree(fb->ptr);
52
- ruby_xfree(fb);
86
+ if (fb->ptr && fb->type == FBUFFER_HEAP_ALLOCATED) {
87
+ ruby_xfree(fb->ptr);
88
+ }
53
89
  }
54
90
 
55
- #ifndef JSON_GENERATOR
56
91
  static void fbuffer_clear(FBuffer *fb)
57
92
  {
58
93
  fb->len = 0;
59
94
  }
60
- #endif
61
95
 
62
- static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
96
+ static void fbuffer_flush(FBuffer *fb)
63
97
  {
64
- if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
65
- unsigned long required;
98
+ rb_io_write(fb->io, rb_utf8_str_new(fb->ptr, fb->len));
99
+ fbuffer_clear(fb);
100
+ }
66
101
 
67
- if (RB_UNLIKELY(!fb->ptr)) {
68
- fb->ptr = ALLOC_N(char, fb->initial_length);
69
- fb->capa = fb->initial_length;
102
+ static void fbuffer_realloc(FBuffer *fb, unsigned long required)
103
+ {
104
+ if (required > fb->capa) {
105
+ if (fb->type == FBUFFER_STACK_ALLOCATED) {
106
+ const char *old_buffer = fb->ptr;
107
+ fb->ptr = ALLOC_N(char, required);
108
+ fb->type = FBUFFER_HEAP_ALLOCATED;
109
+ MEMCPY(fb->ptr, old_buffer, char, fb->len);
110
+ } else {
111
+ REALLOC_N(fb->ptr, char, required);
70
112
  }
113
+ fb->capa = required;
114
+ }
115
+ }
71
116
 
72
- for (required = fb->capa; requested > required - fb->len; required <<= 1);
117
+ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
118
+ {
119
+ if (RB_UNLIKELY(fb->io)) {
120
+ if (fb->capa < FBUFFER_IO_BUFFER_SIZE) {
121
+ fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE);
122
+ } else {
123
+ fbuffer_flush(fb);
124
+ }
73
125
 
74
- if (required > fb->capa) {
75
- REALLOC_N(fb->ptr, char, required);
76
- fb->capa = required;
126
+ if (RB_LIKELY(requested < fb->capa)) {
127
+ return;
77
128
  }
78
129
  }
130
+
131
+ unsigned long required;
132
+
133
+ if (RB_UNLIKELY(!fb->ptr)) {
134
+ fb->ptr = ALLOC_N(char, fb->initial_length);
135
+ fb->capa = fb->initial_length;
136
+ }
137
+
138
+ for (required = fb->capa; requested > required - fb->len; required <<= 1);
139
+
140
+ fbuffer_realloc(fb, required);
141
+ }
142
+
143
+ static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
144
+ {
145
+ if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
146
+ fbuffer_do_inc_capa(fb, requested);
147
+ }
79
148
  }
80
149
 
81
150
  static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
@@ -99,7 +168,7 @@ static void fbuffer_append_str(FBuffer *fb, VALUE str)
99
168
  }
100
169
  #endif
101
170
 
102
- static void fbuffer_append_char(FBuffer *fb, char newchr)
171
+ static inline void fbuffer_append_char(FBuffer *fb, char newchr)
103
172
  {
104
173
  fbuffer_inc_capa(fb, 1);
105
174
  *(fb->ptr + fb->len) = newchr;
@@ -107,40 +176,39 @@ static void fbuffer_append_char(FBuffer *fb, char newchr)
107
176
  }
108
177
 
109
178
  #ifdef JSON_GENERATOR
110
- static void freverse(char *start, char *end)
111
- {
112
- char c;
113
-
114
- while (end > start) {
115
- c = *end, *end-- = *start, *start++ = c;
116
- }
117
- }
118
-
119
179
  static long fltoa(long number, char *buf)
120
180
  {
121
- static char digits[] = "0123456789";
181
+ static const char digits[] = "0123456789";
122
182
  long sign = number;
123
183
  char* tmp = buf;
124
184
 
125
185
  if (sign < 0) number = -number;
126
- do *tmp++ = digits[number % 10]; while (number /= 10);
127
- if (sign < 0) *tmp++ = '-';
128
- freverse(buf, tmp - 1);
129
- return tmp - buf;
186
+ do *tmp-- = digits[number % 10]; while (number /= 10);
187
+ if (sign < 0) *tmp-- = '-';
188
+ return buf - tmp;
130
189
  }
131
190
 
191
+ #define LONG_BUFFER_SIZE 20
132
192
  static void fbuffer_append_long(FBuffer *fb, long number)
133
193
  {
134
- char buf[20];
135
- unsigned long len = fltoa(number, buf);
136
- fbuffer_append(fb, buf, len);
194
+ char buf[LONG_BUFFER_SIZE];
195
+ char *buffer_end = buf + LONG_BUFFER_SIZE;
196
+ long len = fltoa(number, buffer_end - 1);
197
+ fbuffer_append(fb, buffer_end - len, len);
137
198
  }
138
199
 
139
- static VALUE fbuffer_to_s(FBuffer *fb)
200
+ static VALUE fbuffer_finalize(FBuffer *fb)
140
201
  {
141
- VALUE result = rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
142
- fbuffer_free(fb);
143
- return result;
202
+ if (fb->io) {
203
+ fbuffer_flush(fb);
204
+ fbuffer_free(fb);
205
+ rb_io_flush(fb->io);
206
+ return fb->io;
207
+ } else {
208
+ VALUE result = rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
209
+ fbuffer_free(fb);
210
+ return result;
211
+ }
144
212
  }
145
213
  #endif
146
214
  #endif