json 2.15.1 → 2.19.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/CHANGES.md +46 -1
- data/LEGAL +12 -0
- data/README.md +17 -1
- data/ext/json/ext/fbuffer/fbuffer.h +30 -77
- data/ext/json/ext/generator/extconf.rb +1 -1
- data/ext/json/ext/generator/generator.c +288 -472
- data/ext/json/ext/json.h +105 -0
- data/ext/json/ext/parser/extconf.rb +2 -1
- data/ext/json/ext/parser/parser.c +597 -474
- data/ext/json/ext/simd/simd.h +42 -22
- data/ext/json/ext/vendor/fpconv.c +3 -3
- data/ext/json/ext/vendor/ryu.h +819 -0
- data/lib/json/common.rb +69 -26
- data/lib/json/ext/generator/state.rb +5 -1
- data/lib/json/truffle_ruby/generator.rb +66 -22
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +33 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2e0f63481c8ba1c4f76f44a86ac9f1814e82fb396125e41e95efddc8e259fe64
|
|
4
|
+
data.tar.gz: a070ae0776f2db0519ec672d3a32be7dee2be2ee10d0bc35c9dde6581ec8c4a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fc49905c26e2173018856dba7c4ae10ef74f015233b607f977b42ceea5f1d9e511f9ad4a7c22d4aed66e4ef8c60ad23a506d8bb0e053c0155ea2f7eb40a82d06
|
|
7
|
+
data.tar.gz: 61c1367d7b91621a34c0fc9fc29802534f14d27c1b9c50ecfa09d68d100fcda840847393063ec4c477437261398c559b93f292d2594bdf1e597c4b5e33959322
|
data/CHANGES.md
CHANGED
|
@@ -2,6 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
### Unreleased
|
|
4
4
|
|
|
5
|
+
### 2026-03-06 (2.19.0)
|
|
6
|
+
|
|
7
|
+
* Fix `allow_blank` parsing option to no longer allow invalid types (e.g. `load([], allow_blank: true)` now raise a type error).
|
|
8
|
+
* Add `allow_invalid_escape` parsing option to ignore backslashes that aren't followed by one of the valid escape characters.
|
|
9
|
+
|
|
10
|
+
### 2026-02-03 (2.18.1)
|
|
11
|
+
|
|
12
|
+
* Fix a potential crash in very specific circumstance if GC triggers during a call to `to_json`
|
|
13
|
+
without first invoking a user defined `#to_json` method.
|
|
14
|
+
|
|
15
|
+
### 2025-12-11 (2.18.0)
|
|
16
|
+
|
|
17
|
+
* Add `:allow_control_characters` parser options, to allow JSON strings containing unescaped ASCII control characters (e.g. newlines).
|
|
18
|
+
|
|
19
|
+
### 2025-12-04 (2.17.1)
|
|
20
|
+
|
|
21
|
+
* Fix a regression in parsing of unicode surogate pairs (`\uXX\uXX`) that could cause an invalid string to be returned.
|
|
22
|
+
|
|
23
|
+
### 2025-12-03 (2.17.0)
|
|
24
|
+
|
|
25
|
+
* Improve `JSON.load` and `JSON.unsafe_load` to allow passing options as second argument.
|
|
26
|
+
* Fix the parser to no longer ignore invalid escapes in strings.
|
|
27
|
+
Only `\"`, `\\`, `\b`, `\f`, `\n`, `\r`, `\t` and `\u` are valid JSON escapes.
|
|
28
|
+
* Fixed `JSON::Coder` to use the depth it was initialized with.
|
|
29
|
+
* On TruffleRuby, fix the generator to not call `to_json` on the return value of `as_json` for `Float::NAN`.
|
|
30
|
+
* Fixed handling of `state.depth`: when `to_json` changes `state.depth` but does not restore it, it is reset
|
|
31
|
+
automatically to its initial value.
|
|
32
|
+
In particular, when a `NestingError` is raised, `depth` is no longer equal to `max_nesting` after the call to
|
|
33
|
+
generate, and is reset to its initial value. Similarly when `to_json` raises an exception.
|
|
34
|
+
|
|
35
|
+
### 2025-11-07 (2.16.0)
|
|
36
|
+
|
|
37
|
+
* Deprecate `JSON::State#[]` and `JSON::State#[]=`. Consider using `JSON::Coder` instead.
|
|
38
|
+
* `JSON::Coder` now also yields to the block when encountering strings with invalid encoding.
|
|
39
|
+
* Fix GeneratorError messages to be UTF-8 encoded.
|
|
40
|
+
* Fix memory leak when `Exception` is raised, or `throw` is used during JSON generation.
|
|
41
|
+
* Optimized floating point number parsing by integrating the ryu algorithm (thanks to Josef Šimánek).
|
|
42
|
+
* Optimized numbers parsing using SWAR (thanks to Scott Myron).
|
|
43
|
+
* Optimized parsing of pretty printed documents using SWAR (thanks to Scott Myron).
|
|
44
|
+
|
|
45
|
+
### 2025-10-25 (2.15.2)
|
|
46
|
+
|
|
47
|
+
* Fix `JSON::Coder` to have one dedicated depth counter per invocation.
|
|
48
|
+
After encountering a circular reference in `JSON::Coder#dump`, any further `#dump` call would raise `JSON::NestingError`.
|
|
49
|
+
|
|
5
50
|
### 2025-10-07 (2.15.1)
|
|
6
51
|
|
|
7
52
|
* Fix incorrect escaping in the JRuby extension when encoding shared strings.
|
|
@@ -31,7 +76,7 @@
|
|
|
31
76
|
* Fix `JSON.generate` `strict: true` mode to also restrict hash keys.
|
|
32
77
|
* Fix `JSON::Coder` to also invoke block for hash keys that aren't strings nor symbols.
|
|
33
78
|
* Fix `JSON.unsafe_load` usage with proc
|
|
34
|
-
* Fix the parser to more consistently reject invalid UTF-16 surogate pairs.
|
|
79
|
+
* Fix the parser to more consistently reject invalid UTF-16 surogate pairs.
|
|
35
80
|
* Stop defining `String.json_create`, `String#to_json_raw`, `String#to_json_raw_object` when `json/add` isn't loaded.
|
|
36
81
|
|
|
37
82
|
### 2025-07-28 (2.13.2)
|
data/LEGAL
CHANGED
|
@@ -6,3 +6,15 @@
|
|
|
6
6
|
All the files in this distribution are covered under either the Ruby's
|
|
7
7
|
license (see the file COPYING) or public-domain except some files
|
|
8
8
|
mentioned below.
|
|
9
|
+
|
|
10
|
+
ext/json/ext/vendor/fpconv.h::
|
|
11
|
+
This file is adapted from https://github.com/night-shift/fpconv
|
|
12
|
+
It is licensed under Boost Software License 1.0.
|
|
13
|
+
|
|
14
|
+
ext/json/ext/vendor/jeaiii-ltoa.h::
|
|
15
|
+
This file is adapted from https://github.com/jeaiii/itoa
|
|
16
|
+
It is licensed under the MIT License
|
|
17
|
+
|
|
18
|
+
ext/json/ext/vendor/ryu.h::
|
|
19
|
+
This file is adapted from the Ryu algorithm by Ulf Adams https://github.com/ulfjack/ryu.
|
|
20
|
+
It is dual-licensed under Apache License 2.0 OR Boost Software License 1.0.
|
data/README.md
CHANGED
|
@@ -113,7 +113,23 @@ puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
|
|
|
113
113
|
The provided block is called for all objects that don't have a native JSON equivalent, and
|
|
114
114
|
must return a Ruby object that has a native JSON equivalent.
|
|
115
115
|
|
|
116
|
-
It is also called for objects that do have a JSON equivalent, but are used as Hash keys, for instance `{ 1 => 2}
|
|
116
|
+
It is also called for objects that do have a JSON equivalent, but are used as Hash keys, for instance `{ 1 => 2}`,
|
|
117
|
+
as well as for strings that aren't valid UTF-8:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
coder = JSON::Combining.new do |object, is_object_key|
|
|
121
|
+
case object
|
|
122
|
+
when String
|
|
123
|
+
if !string.valid_encoding? || string.encoding != Encoding::UTF_8
|
|
124
|
+
Base64.encode64(string)
|
|
125
|
+
else
|
|
126
|
+
string
|
|
127
|
+
end
|
|
128
|
+
else
|
|
129
|
+
object
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
```
|
|
117
133
|
|
|
118
134
|
## Combining JSON fragments
|
|
119
135
|
|
|
@@ -1,55 +1,9 @@
|
|
|
1
1
|
#ifndef _FBUFFER_H_
|
|
2
2
|
#define _FBUFFER_H_
|
|
3
3
|
|
|
4
|
-
#include "
|
|
5
|
-
#include "ruby/encoding.h"
|
|
4
|
+
#include "../json.h"
|
|
6
5
|
#include "../vendor/jeaiii-ltoa.h"
|
|
7
6
|
|
|
8
|
-
/* shims */
|
|
9
|
-
/* This is the fallback definition from Ruby 3.4 */
|
|
10
|
-
|
|
11
|
-
#ifndef RBIMPL_STDBOOL_H
|
|
12
|
-
#if defined(__cplusplus)
|
|
13
|
-
# if defined(HAVE_STDBOOL_H) && (__cplusplus >= 201103L)
|
|
14
|
-
# include <cstdbool>
|
|
15
|
-
# endif
|
|
16
|
-
#elif defined(HAVE_STDBOOL_H)
|
|
17
|
-
# include <stdbool.h>
|
|
18
|
-
#elif !defined(HAVE__BOOL)
|
|
19
|
-
typedef unsigned char _Bool;
|
|
20
|
-
# define bool _Bool
|
|
21
|
-
# define true ((_Bool)+1)
|
|
22
|
-
# define false ((_Bool)+0)
|
|
23
|
-
# define __bool_true_false_are_defined
|
|
24
|
-
#endif
|
|
25
|
-
#endif
|
|
26
|
-
|
|
27
|
-
#ifndef NOINLINE
|
|
28
|
-
#if defined(__has_attribute) && __has_attribute(noinline)
|
|
29
|
-
#define NOINLINE() __attribute__((noinline))
|
|
30
|
-
#else
|
|
31
|
-
#define NOINLINE()
|
|
32
|
-
#endif
|
|
33
|
-
#endif
|
|
34
|
-
|
|
35
|
-
#ifndef RB_UNLIKELY
|
|
36
|
-
#define RB_UNLIKELY(expr) expr
|
|
37
|
-
#endif
|
|
38
|
-
|
|
39
|
-
#ifndef RB_LIKELY
|
|
40
|
-
#define RB_LIKELY(expr) expr
|
|
41
|
-
#endif
|
|
42
|
-
|
|
43
|
-
#ifndef MAYBE_UNUSED
|
|
44
|
-
# define MAYBE_UNUSED(x) x
|
|
45
|
-
#endif
|
|
46
|
-
|
|
47
|
-
#ifdef RUBY_DEBUG
|
|
48
|
-
#ifndef JSON_DEBUG
|
|
49
|
-
#define JSON_DEBUG RUBY_DEBUG
|
|
50
|
-
#endif
|
|
51
|
-
#endif
|
|
52
|
-
|
|
53
7
|
enum fbuffer_type {
|
|
54
8
|
FBUFFER_HEAP_ALLOCATED = 0,
|
|
55
9
|
FBUFFER_STACK_ALLOCATED = 1,
|
|
@@ -57,11 +11,11 @@ enum fbuffer_type {
|
|
|
57
11
|
|
|
58
12
|
typedef struct FBufferStruct {
|
|
59
13
|
enum fbuffer_type type;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
#
|
|
64
|
-
|
|
14
|
+
size_t initial_length;
|
|
15
|
+
size_t len;
|
|
16
|
+
size_t capa;
|
|
17
|
+
#if JSON_DEBUG
|
|
18
|
+
size_t requested;
|
|
65
19
|
#endif
|
|
66
20
|
char *ptr;
|
|
67
21
|
VALUE io;
|
|
@@ -78,12 +32,12 @@ typedef struct FBufferStruct {
|
|
|
78
32
|
|
|
79
33
|
static void fbuffer_free(FBuffer *fb);
|
|
80
34
|
static void fbuffer_clear(FBuffer *fb);
|
|
81
|
-
static void fbuffer_append(FBuffer *fb, const char *newstr,
|
|
35
|
+
static void fbuffer_append(FBuffer *fb, const char *newstr, size_t len);
|
|
82
36
|
static void fbuffer_append_long(FBuffer *fb, long number);
|
|
83
37
|
static inline void fbuffer_append_char(FBuffer *fb, char newchr);
|
|
84
38
|
static VALUE fbuffer_finalize(FBuffer *fb);
|
|
85
39
|
|
|
86
|
-
static void fbuffer_stack_init(FBuffer *fb,
|
|
40
|
+
static void fbuffer_stack_init(FBuffer *fb, size_t initial_length, char *stack_buffer, size_t stack_buffer_size)
|
|
87
41
|
{
|
|
88
42
|
fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
|
|
89
43
|
if (stack_buffer) {
|
|
@@ -91,14 +45,14 @@ static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *
|
|
|
91
45
|
fb->ptr = stack_buffer;
|
|
92
46
|
fb->capa = stack_buffer_size;
|
|
93
47
|
}
|
|
94
|
-
#
|
|
48
|
+
#if JSON_DEBUG
|
|
95
49
|
fb->requested = 0;
|
|
96
50
|
#endif
|
|
97
51
|
}
|
|
98
52
|
|
|
99
|
-
static inline void fbuffer_consumed(FBuffer *fb,
|
|
53
|
+
static inline void fbuffer_consumed(FBuffer *fb, size_t consumed)
|
|
100
54
|
{
|
|
101
|
-
#
|
|
55
|
+
#if JSON_DEBUG
|
|
102
56
|
if (consumed > fb->requested) {
|
|
103
57
|
rb_bug("fbuffer: Out of bound write");
|
|
104
58
|
}
|
|
@@ -125,7 +79,7 @@ static void fbuffer_flush(FBuffer *fb)
|
|
|
125
79
|
fbuffer_clear(fb);
|
|
126
80
|
}
|
|
127
81
|
|
|
128
|
-
static void fbuffer_realloc(FBuffer *fb,
|
|
82
|
+
static void fbuffer_realloc(FBuffer *fb, size_t required)
|
|
129
83
|
{
|
|
130
84
|
if (required > fb->capa) {
|
|
131
85
|
if (fb->type == FBUFFER_STACK_ALLOCATED) {
|
|
@@ -140,7 +94,7 @@ static void fbuffer_realloc(FBuffer *fb, unsigned long required)
|
|
|
140
94
|
}
|
|
141
95
|
}
|
|
142
96
|
|
|
143
|
-
static void fbuffer_do_inc_capa(FBuffer *fb,
|
|
97
|
+
static void fbuffer_do_inc_capa(FBuffer *fb, size_t requested)
|
|
144
98
|
{
|
|
145
99
|
if (RB_UNLIKELY(fb->io)) {
|
|
146
100
|
if (fb->capa < FBUFFER_IO_BUFFER_SIZE) {
|
|
@@ -154,7 +108,7 @@ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
|
|
|
154
108
|
}
|
|
155
109
|
}
|
|
156
110
|
|
|
157
|
-
|
|
111
|
+
size_t required;
|
|
158
112
|
|
|
159
113
|
if (RB_UNLIKELY(!fb->ptr)) {
|
|
160
114
|
fb->ptr = ALLOC_N(char, fb->initial_length);
|
|
@@ -166,9 +120,9 @@ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
|
|
|
166
120
|
fbuffer_realloc(fb, required);
|
|
167
121
|
}
|
|
168
122
|
|
|
169
|
-
static inline void fbuffer_inc_capa(FBuffer *fb,
|
|
123
|
+
static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
|
|
170
124
|
{
|
|
171
|
-
#
|
|
125
|
+
#if JSON_DEBUG
|
|
172
126
|
fb->requested = requested;
|
|
173
127
|
#endif
|
|
174
128
|
|
|
@@ -177,13 +131,13 @@ static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
|
|
|
177
131
|
}
|
|
178
132
|
}
|
|
179
133
|
|
|
180
|
-
static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr,
|
|
134
|
+
static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, size_t len)
|
|
181
135
|
{
|
|
182
136
|
MEMCPY(fb->ptr + fb->len, newstr, char, len);
|
|
183
137
|
fbuffer_consumed(fb, len);
|
|
184
138
|
}
|
|
185
139
|
|
|
186
|
-
static inline void fbuffer_append(FBuffer *fb, const char *newstr,
|
|
140
|
+
static inline void fbuffer_append(FBuffer *fb, const char *newstr, size_t len)
|
|
187
141
|
{
|
|
188
142
|
if (len > 0) {
|
|
189
143
|
fbuffer_inc_capa(fb, len);
|
|
@@ -194,7 +148,7 @@ static inline void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long
|
|
|
194
148
|
/* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
|
|
195
149
|
static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
|
|
196
150
|
{
|
|
197
|
-
#
|
|
151
|
+
#if JSON_DEBUG
|
|
198
152
|
if (fb->requested < 1) {
|
|
199
153
|
rb_bug("fbuffer: unreserved write");
|
|
200
154
|
}
|
|
@@ -207,23 +161,25 @@ static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
|
|
|
207
161
|
|
|
208
162
|
static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
|
209
163
|
{
|
|
210
|
-
const char *
|
|
211
|
-
|
|
164
|
+
const char *ptr;
|
|
165
|
+
size_t len;
|
|
166
|
+
RSTRING_GETMEM(str, ptr, len);
|
|
212
167
|
|
|
213
|
-
fbuffer_append(fb,
|
|
168
|
+
fbuffer_append(fb, ptr, len);
|
|
214
169
|
}
|
|
215
170
|
|
|
216
171
|
static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
|
|
217
172
|
{
|
|
218
|
-
const char *
|
|
219
|
-
|
|
173
|
+
const char *ptr;
|
|
174
|
+
size_t len;
|
|
175
|
+
RSTRING_GETMEM(str, ptr, len);
|
|
220
176
|
|
|
221
177
|
fbuffer_inc_capa(fb, repeat * len);
|
|
222
178
|
while (repeat) {
|
|
223
|
-
#
|
|
179
|
+
#if JSON_DEBUG
|
|
224
180
|
fb->requested = len;
|
|
225
181
|
#endif
|
|
226
|
-
fbuffer_append_reserved(fb,
|
|
182
|
+
fbuffer_append_reserved(fb, ptr, len);
|
|
227
183
|
repeat--;
|
|
228
184
|
}
|
|
229
185
|
}
|
|
@@ -283,14 +239,11 @@ static VALUE fbuffer_finalize(FBuffer *fb)
|
|
|
283
239
|
{
|
|
284
240
|
if (fb->io) {
|
|
285
241
|
fbuffer_flush(fb);
|
|
286
|
-
fbuffer_free(fb);
|
|
287
242
|
rb_io_flush(fb->io);
|
|
288
243
|
return fb->io;
|
|
289
244
|
} else {
|
|
290
|
-
|
|
291
|
-
fbuffer_free(fb);
|
|
292
|
-
return result;
|
|
245
|
+
return rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
|
|
293
246
|
}
|
|
294
247
|
}
|
|
295
248
|
|
|
296
|
-
#endif
|
|
249
|
+
#endif // _FBUFFER_H_
|
|
@@ -6,7 +6,7 @@ if RUBY_ENGINE == 'truffleruby'
|
|
|
6
6
|
else
|
|
7
7
|
append_cflags("-std=c99")
|
|
8
8
|
$defs << "-DJSON_GENERATOR"
|
|
9
|
-
$defs << "-DJSON_DEBUG" if ENV
|
|
9
|
+
$defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
|
|
10
10
|
|
|
11
11
|
if enable_config('generator-use-simd', default=!ENV["JSON_DISABLE_SIMD"])
|
|
12
12
|
load __dir__ + "/../simd/conf.rb"
|