json 2.10.2 → 2.19.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/CHANGES.md +205 -7
- data/LEGAL +12 -0
- data/README.md +43 -1
- data/ext/json/ext/fbuffer/fbuffer.h +140 -86
- data/ext/json/ext/generator/extconf.rb +8 -0
- data/ext/json/ext/generator/generator.c +787 -616
- data/ext/json/ext/json.h +116 -0
- data/ext/json/ext/parser/extconf.rb +11 -3
- data/ext/json/ext/parser/parser.c +974 -703
- data/ext/json/ext/simd/conf.rb +24 -0
- data/ext/json/ext/simd/simd.h +208 -0
- data/ext/json/ext/vendor/fpconv.c +480 -0
- data/ext/json/ext/vendor/jeaiii-ltoa.h +267 -0
- data/ext/json/ext/vendor/ryu.h +819 -0
- data/json.gemspec +2 -3
- data/lib/json/add/core.rb +1 -0
- data/lib/json/add/string.rb +35 -0
- data/lib/json/common.rb +374 -188
- data/lib/json/ext/generator/state.rb +11 -14
- data/lib/json/ext.rb +2 -2
- data/lib/json/generic_object.rb +0 -8
- data/lib/json/truffle_ruby/generator.rb +137 -72
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +90 -2
- metadata +66 -57
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5e5fed66aaa650ac7aaf223c4d8e505a2d6ce3fe72599af4931356ae619f020b
|
|
4
|
+
data.tar.gz: 347004780c7a7568685502ade79f145371a49a213d348a65c99ea8fbffc6eee7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0bffdb4cd21e4656a1402a00d95ae890dc75befff379aeef4aad575a7f715a0dbeb87cfef6fefa3cd5235c3302a29fabb510d5db67e527678023cd8a88c34b49
|
|
7
|
+
data.tar.gz: 7e08cfae0c5d404687644528ecfb9438c9ea254f43fd285d116ba1542a4ef78045463b18850324e91ba4d0c735e7d9ed4b583499aadafe7d0bab51dd54a8d086
|
data/CHANGES.md
CHANGED
|
@@ -1,10 +1,207 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
### Unreleased
|
|
4
|
+
|
|
5
|
+
### 2026-06-11 (2.19.9)
|
|
6
|
+
|
|
7
|
+
* Fix buffer overflow that could lead to a crash when writing JSON directly into an IO
|
|
8
|
+
with `JSON.generate(object, io)`. [CVE-PENDING].
|
|
9
|
+
|
|
10
|
+
### 2026-06-03 (2.19.8)
|
|
11
|
+
|
|
12
|
+
* Fix 1-byte buffer overread on EOS errors.
|
|
13
|
+
* Handle invalid types passed as `max_nesting` option.
|
|
14
|
+
|
|
15
|
+
### 2026-05-28 (2.19.7)
|
|
16
|
+
|
|
17
|
+
* Fix some more edge cases with out of range floats.
|
|
18
|
+
* Ensure the string provided to `JSON.parse` can't be mutated during parsing.
|
|
19
|
+
* Add missing write barriers in `State#dup`.
|
|
20
|
+
* Further validate generator `depth` config.
|
|
21
|
+
|
|
22
|
+
### 2026-05-28 (2.19.6)
|
|
23
|
+
|
|
24
|
+
* Cleanly handle overly large `depth` generator argument.
|
|
25
|
+
* Add missing write barrier in `ParserConfig`.
|
|
26
|
+
|
|
27
|
+
### 2026-05-04 (2.19.5)
|
|
28
|
+
|
|
29
|
+
* Cap the parser to emit a maximum of 5 deprecation warnings per document. Emitting more is not helpful.
|
|
30
|
+
|
|
31
|
+
### 2026-04-19 (2.19.4)
|
|
32
|
+
|
|
33
|
+
* Fix parsing of out of range floats (very large exponents that lead to either `0.0` or `Inf`).
|
|
34
|
+
|
|
35
|
+
### 2026-03-25 (2.19.3)
|
|
36
|
+
|
|
37
|
+
* Fix handling of unescaped control characters preceeded by a backslash.
|
|
38
|
+
|
|
39
|
+
### 2026-03-18 (2.19.2)
|
|
40
|
+
|
|
41
|
+
* Fix a format string injection vulnerability in `JSON.parse(doc, allow_duplicate_key: false)`. `CVE-2026-33210`.
|
|
42
|
+
|
|
43
|
+
### 2026-03-08 (2.19.1)
|
|
44
|
+
|
|
45
|
+
* Fix a compiler dependent GC bug introduced in `2.18.0`.
|
|
46
|
+
|
|
47
|
+
### 2026-03-06 (2.19.0)
|
|
48
|
+
|
|
49
|
+
* Fix `allow_blank` parsing option to no longer allow invalid types (e.g. `load([], allow_blank: true)` now raise a type error).
|
|
50
|
+
* Add `allow_invalid_escape` parsing option to ignore backslashes that aren't followed by one of the valid escape characters.
|
|
51
|
+
|
|
52
|
+
### 2026-02-03 (2.18.1)
|
|
53
|
+
|
|
54
|
+
* Fix a potential crash in very specific circumstance if GC triggers during a call to `to_json`
|
|
55
|
+
without first invoking a user defined `#to_json` method.
|
|
56
|
+
|
|
57
|
+
### 2025-12-11 (2.18.0)
|
|
58
|
+
|
|
59
|
+
* Add `:allow_control_characters` parser options, to allow JSON strings containing unescaped ASCII control characters (e.g. newlines).
|
|
60
|
+
|
|
61
|
+
### 2026-03-18 (2.17.1.2) - Security Backport
|
|
62
|
+
|
|
63
|
+
* Fix a format string injection vulnerability in `JSON.parse(doc, allow_duplicate_key: false)`. `CVE-2026-33210`.
|
|
64
|
+
|
|
65
|
+
### 2025-12-04 (2.17.1)
|
|
66
|
+
|
|
67
|
+
* Fix a regression in parsing of unicode surogate pairs (`\uXX\uXX`) that could cause an invalid string to be returned.
|
|
68
|
+
|
|
69
|
+
### 2025-12-03 (2.17.0)
|
|
70
|
+
|
|
71
|
+
* Improve `JSON.load` and `JSON.unsafe_load` to allow passing options as second argument.
|
|
72
|
+
* Fix the parser to no longer ignore invalid escapes in strings.
|
|
73
|
+
Only `\"`, `\\`, `\b`, `\f`, `\n`, `\r`, `\t` and `\u` are valid JSON escapes.
|
|
74
|
+
* Fixed `JSON::Coder` to use the depth it was initialized with.
|
|
75
|
+
* On TruffleRuby, fix the generator to not call `to_json` on the return value of `as_json` for `Float::NAN`.
|
|
76
|
+
* Fixed handling of `state.depth`: when `to_json` changes `state.depth` but does not restore it, it is reset
|
|
77
|
+
automatically to its initial value.
|
|
78
|
+
In particular, when a `NestingError` is raised, `depth` is no longer equal to `max_nesting` after the call to
|
|
79
|
+
generate, and is reset to its initial value. Similarly when `to_json` raises an exception.
|
|
80
|
+
|
|
81
|
+
### 2025-11-07 (2.16.0)
|
|
82
|
+
|
|
83
|
+
* Deprecate `JSON::State#[]` and `JSON::State#[]=`. Consider using `JSON::Coder` instead.
|
|
84
|
+
* `JSON::Coder` now also yields to the block when encountering strings with invalid encoding.
|
|
85
|
+
* Fix GeneratorError messages to be UTF-8 encoded.
|
|
86
|
+
* Fix memory leak when `Exception` is raised, or `throw` is used during JSON generation.
|
|
87
|
+
* Optimized floating point number parsing by integrating the ryu algorithm (thanks to Josef Šimánek).
|
|
88
|
+
* Optimized numbers parsing using SWAR (thanks to Scott Myron).
|
|
89
|
+
* Optimized parsing of pretty printed documents using SWAR (thanks to Scott Myron).
|
|
90
|
+
|
|
91
|
+
### 2026-03-18 (2.15.2.1) - Security Backport
|
|
92
|
+
|
|
93
|
+
* Fix a format string injection vulnerability in `JSON.parse(doc, allow_duplicate_key: false)`. `CVE-2026-33210`.
|
|
94
|
+
|
|
95
|
+
### 2025-10-25 (2.15.2)
|
|
96
|
+
|
|
97
|
+
* Fix `JSON::Coder` to have one dedicated depth counter per invocation.
|
|
98
|
+
After encountering a circular reference in `JSON::Coder#dump`, any further `#dump` call would raise `JSON::NestingError`.
|
|
99
|
+
|
|
100
|
+
### 2025-10-07 (2.15.1)
|
|
101
|
+
|
|
102
|
+
* Fix incorrect escaping in the JRuby extension when encoding shared strings.
|
|
103
|
+
|
|
104
|
+
### 2025-09-22 (2.15.0)
|
|
105
|
+
|
|
106
|
+
* `JSON::Coder` callback now receive a second argument to convey whether the object is a hash key.
|
|
107
|
+
* Tuned the floating point number generator to not use scientific notation as aggressively.
|
|
108
|
+
|
|
109
|
+
### 2025-09-18 (2.14.1)
|
|
110
|
+
|
|
111
|
+
* Fix `IndexOutOfBoundsException` in the JRuby extension when encoding shared strings.
|
|
112
|
+
|
|
113
|
+
### 2025-09-18 (2.14.0)
|
|
114
|
+
|
|
115
|
+
* Add new `allow_duplicate_key` generator options. By default a warning is now emitted when a duplicated key is encountered.
|
|
116
|
+
In `json 3.0` an error will be raised.
|
|
117
|
+
```ruby
|
|
118
|
+
>> Warning[:deprecated] = true
|
|
119
|
+
>> puts JSON.generate({ foo: 1, "foo" => 2 })
|
|
120
|
+
(irb):2: warning: detected duplicate key "foo" in {foo: 1, "foo" => 2}.
|
|
121
|
+
This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`
|
|
122
|
+
{"foo":1,"foo":2}
|
|
123
|
+
>> JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false)
|
|
124
|
+
detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError)
|
|
125
|
+
```
|
|
126
|
+
* Fix `JSON.generate` `strict: true` mode to also restrict hash keys.
|
|
127
|
+
* Fix `JSON::Coder` to also invoke block for hash keys that aren't strings nor symbols.
|
|
128
|
+
* Fix `JSON.unsafe_load` usage with proc
|
|
129
|
+
* Fix the parser to more consistently reject invalid UTF-16 surogate pairs.
|
|
130
|
+
* Stop defining `String.json_create`, `String#to_json_raw`, `String#to_json_raw_object` when `json/add` isn't loaded.
|
|
131
|
+
|
|
132
|
+
### 2025-07-28 (2.13.2)
|
|
133
|
+
|
|
134
|
+
* Improve duplicate key warning and errors to include the key name and point to the right caller.
|
|
135
|
+
|
|
136
|
+
### 2025-07-24 (2.13.1)
|
|
137
|
+
|
|
138
|
+
* Fix support for older compilers without `__builtin_cpu_supports`.
|
|
139
|
+
|
|
140
|
+
### 2025-07-17 (2.13.0)
|
|
141
|
+
|
|
142
|
+
* Add new `allow_duplicate_key` parsing options. By default a warning is now emitted when a duplicated key is encountered.
|
|
143
|
+
In `json 3.0` an error will be raised.
|
|
144
|
+
* Optimize parsing further using SIMD to scan strings.
|
|
145
|
+
|
|
146
|
+
### 2025-05-23 (2.12.2)
|
|
147
|
+
|
|
148
|
+
* Fix compiler optimization level.
|
|
149
|
+
|
|
150
|
+
### 2025-05-23 (2.12.1)
|
|
151
|
+
|
|
152
|
+
* Fix a potential crash in large negative floating point number generation.
|
|
153
|
+
* Fix for JSON.pretty_generate to use passed state object's generate instead of state class as the required parameters aren't available.
|
|
154
|
+
|
|
155
|
+
### 2025-05-12 (2.12.0)
|
|
156
|
+
|
|
157
|
+
* Improve floating point generation to not use scientific notation as much.
|
|
158
|
+
* Include line and column in parser errors. Both in the message and as exception attributes.
|
|
159
|
+
* Handle non-string hash keys with broken `to_s` implementations.
|
|
160
|
+
* `JSON.generate` now uses SSE2 (x86) or NEON (arm64) instructions when available to escape strings.
|
|
161
|
+
|
|
162
|
+
### 2025-04-25 (2.11.3)
|
|
163
|
+
|
|
164
|
+
* Fix a regression in `JSON.pretty_generate` that could cause indentation to be off once some `#to_json` has been called.
|
|
165
|
+
|
|
166
|
+
### 2025-04-24 (2.11.2)
|
|
167
|
+
|
|
168
|
+
* Add back `JSON::PRETTY_STATE_PROTOTYPE`. This constant was private API but is used by popular gems like `multi_json`.
|
|
169
|
+
It now emits a deprecation warning.
|
|
170
|
+
|
|
171
|
+
### 2025-04-24 (2.11.1)
|
|
172
|
+
|
|
173
|
+
* Add back `JSON.restore`, `JSON.unparse`, `JSON.fast_unparse` and `JSON.pretty_unparse`.
|
|
174
|
+
These were deprecated 16 years ago, but never emitted warnings, only undocumented, so are
|
|
175
|
+
still used by a few gems.
|
|
176
|
+
|
|
177
|
+
### 2025-04-24 (2.11.0)
|
|
178
|
+
|
|
179
|
+
* Optimize Integer generation to be ~1.8x faster.
|
|
180
|
+
* Optimize Float generation to be ~10x faster.
|
|
181
|
+
* Fix `JSON.load` proc argument to substitute the parsed object with the return value.
|
|
182
|
+
This better match `Marshal.load` behavior.
|
|
183
|
+
* Deprecate `JSON.fast_generate` (it's not any faster, so pointless).
|
|
184
|
+
* Deprecate `JSON.load_default_options`.
|
|
185
|
+
* Deprecate `JSON.unsafe_load_default_options`.
|
|
186
|
+
* Deprecate `JSON.dump_default_options`.
|
|
187
|
+
* Deprecate `Kernel#j`
|
|
188
|
+
* Deprecate `Kernel#jj`
|
|
189
|
+
* Remove outdated `JSON.iconv`.
|
|
190
|
+
* Remove `Class#json_creatable?` monkey patch.
|
|
191
|
+
* Remove deprecated `JSON.restore` method.
|
|
192
|
+
* Remove deprecated `JSON.unparse` method.
|
|
193
|
+
* Remove deprecated `JSON.fast_unparse` method.
|
|
194
|
+
* Remove deprecated `JSON.pretty_unparse` method.
|
|
195
|
+
* Remove deprecated `JSON::UnparserError` constant.
|
|
196
|
+
* Remove outdated `JSON::MissingUnicodeSupport` constant.
|
|
197
|
+
|
|
3
198
|
### 2025-03-12 (2.10.2)
|
|
4
199
|
|
|
5
200
|
* Fix a potential crash in the C extension parser.
|
|
6
|
-
* Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0`
|
|
201
|
+
* Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0` inadvertently changed it.
|
|
7
202
|
* Ensure document snippets that are included in parser errors don't include truncated multibyte characters.
|
|
203
|
+
* Ensure parser error snippets are valid UTF-8.
|
|
204
|
+
* Fix `JSON::GeneratorError#detailed_message` on Ruby < 3.2
|
|
8
205
|
|
|
9
206
|
### 2025-02-10 (2.10.1)
|
|
10
207
|
|
|
@@ -32,7 +229,7 @@
|
|
|
32
229
|
|
|
33
230
|
### 2024-11-14 (2.8.2)
|
|
34
231
|
|
|
35
|
-
* `JSON.load_file`
|
|
232
|
+
* `JSON.load_file` explicitly read the file as UTF-8.
|
|
36
233
|
|
|
37
234
|
### 2024-11-06 (2.8.1)
|
|
38
235
|
|
|
@@ -40,7 +237,7 @@
|
|
|
40
237
|
|
|
41
238
|
### 2024-11-06 (2.8.0)
|
|
42
239
|
|
|
43
|
-
* Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being
|
|
240
|
+
* Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being explicitly enabled.
|
|
44
241
|
* Prefer to use `JSON.unsafe_load(string)` or `JSON.load(string, create_additions: true)`.
|
|
45
242
|
* Emit a deprecation warning when serializing valid UTF-8 strings encoded in `ASCII_8BIT` aka `BINARY`.
|
|
46
243
|
* Bump required Ruby version to 2.7.
|
|
@@ -48,7 +245,7 @@
|
|
|
48
245
|
pre-existing support for comments, make it suitable to parse `jsonc` documents.
|
|
49
246
|
* Many performance improvements to `JSON.parse` and `JSON.load`, up to `1.7x` faster on real world documents.
|
|
50
247
|
* Some minor performance improvements to `JSON.dump` and `JSON.generate`.
|
|
51
|
-
* `JSON.pretty_generate` no longer
|
|
248
|
+
* `JSON.pretty_generate` no longer includes newlines inside empty object and arrays.
|
|
52
249
|
|
|
53
250
|
### 2024-11-04 (2.7.6)
|
|
54
251
|
|
|
@@ -65,13 +262,13 @@
|
|
|
65
262
|
* Workaround a bug in 3.4.8 and older https://github.com/rubygems/rubygems/pull/6490.
|
|
66
263
|
This bug would cause some gems with native extension to fail during compilation.
|
|
67
264
|
* Workaround different versions of `json` and `json_pure` being loaded (not officially supported).
|
|
68
|
-
* Make `json_pure` Ractor compatible.
|
|
265
|
+
* Make `json_pure` Ractor compatible.
|
|
69
266
|
|
|
70
267
|
### 2024-10-24 (2.7.3)
|
|
71
268
|
|
|
72
269
|
* Numerous performance optimizations in `JSON.generate` and `JSON.dump` (up to 2 times faster).
|
|
73
|
-
* Limit the size of ParserError exception messages, only include up to 32 bytes of the
|
|
74
|
-
* Fix json-pure's `Object#to_json` to accept non
|
|
270
|
+
* Limit the size of ParserError exception messages, only include up to 32 bytes of the unparsable source.
|
|
271
|
+
* Fix json-pure's `Object#to_json` to accept non-state arguments.
|
|
75
272
|
* Fix multiline comment support in `json-pure`.
|
|
76
273
|
* Fix `JSON.parse` to no longer mutate the argument encoding when passed an ASCII-8BIT string.
|
|
77
274
|
* Fix `String#to_json` to raise on invalid encoding in `json-pure`.
|
|
@@ -216,6 +413,7 @@
|
|
|
216
413
|
## 2015-09-11 (2.0.0)
|
|
217
414
|
* Now complies to newest JSON RFC 7159.
|
|
218
415
|
* Implements compatibility to ruby 2.4 integer unification.
|
|
416
|
+
* Removed support for `quirks_mode` option.
|
|
219
417
|
* Drops support for old rubies whose life has ended, that is rubies < 2.0.
|
|
220
418
|
Also see https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/
|
|
221
419
|
* There were still some mentions of dual GPL licensing in the source, but JSON
|
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
|
@@ -97,7 +97,7 @@ Instead it is recommended to use the newer `JSON::Coder` API:
|
|
|
97
97
|
|
|
98
98
|
```ruby
|
|
99
99
|
module MyApp
|
|
100
|
-
API_JSON_CODER = JSON::Coder.new do |object|
|
|
100
|
+
API_JSON_CODER = JSON::Coder.new do |object, is_object_key|
|
|
101
101
|
case object
|
|
102
102
|
when Time
|
|
103
103
|
object.iso8601(3)
|
|
@@ -113,6 +113,24 @@ 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}`,
|
|
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
|
+
```
|
|
133
|
+
|
|
116
134
|
## Combining JSON fragments
|
|
117
135
|
|
|
118
136
|
To combine JSON fragments into a bigger JSON document, you can use `JSON::Fragment`:
|
|
@@ -231,8 +249,32 @@ There are also the methods `Kernel#j` for generate, and `Kernel#jj` for
|
|
|
231
249
|
`pretty_generate` output to the console, that work analogous to Core Ruby's `p` and
|
|
232
250
|
the `pp` library's `pp` methods.
|
|
233
251
|
|
|
252
|
+
## Security
|
|
253
|
+
|
|
254
|
+
When parsing or serializing untrusted input, parser and generator options should never be user controlled.
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
# Dangerous, DO NOT DO THIS.
|
|
258
|
+
JSON.generate(params[:data], params[:options])
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Security vulnerability reports relying on attacker controlled parsing or generator options will be handled as regular bug fixes.
|
|
262
|
+
|
|
234
263
|
## Development
|
|
235
264
|
|
|
265
|
+
### Prerequisites
|
|
266
|
+
|
|
267
|
+
1. Clone the repository
|
|
268
|
+
2. Install dependencies with `bundle install`
|
|
269
|
+
|
|
270
|
+
### Testing
|
|
271
|
+
|
|
272
|
+
The full test suite can be run with:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
bundle exec rake test
|
|
276
|
+
```
|
|
277
|
+
|
|
236
278
|
### Release
|
|
237
279
|
|
|
238
280
|
Update the `lib/json/version.rb` file.
|
|
@@ -1,39 +1,8 @@
|
|
|
1
1
|
#ifndef _FBUFFER_H_
|
|
2
2
|
#define _FBUFFER_H_
|
|
3
3
|
|
|
4
|
-
#include "
|
|
5
|
-
#include "
|
|
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
|
|
4
|
+
#include "../json.h"
|
|
5
|
+
#include "../vendor/jeaiii-ltoa.h"
|
|
37
6
|
|
|
38
7
|
enum fbuffer_type {
|
|
39
8
|
FBUFFER_HEAP_ALLOCATED = 0,
|
|
@@ -42,9 +11,12 @@ enum fbuffer_type {
|
|
|
42
11
|
|
|
43
12
|
typedef struct FBufferStruct {
|
|
44
13
|
enum fbuffer_type type;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
14
|
+
size_t initial_length;
|
|
15
|
+
size_t len;
|
|
16
|
+
size_t capa;
|
|
17
|
+
#if JSON_DEBUG
|
|
18
|
+
size_t requested;
|
|
19
|
+
#endif
|
|
48
20
|
char *ptr;
|
|
49
21
|
VALUE io;
|
|
50
22
|
} FBuffer;
|
|
@@ -60,19 +32,37 @@ typedef struct FBufferStruct {
|
|
|
60
32
|
|
|
61
33
|
static void fbuffer_free(FBuffer *fb);
|
|
62
34
|
static void fbuffer_clear(FBuffer *fb);
|
|
63
|
-
static void fbuffer_append(FBuffer *fb, const char *newstr,
|
|
35
|
+
static void fbuffer_append(FBuffer *fb, const char *newstr, size_t len);
|
|
64
36
|
static void fbuffer_append_long(FBuffer *fb, long number);
|
|
65
37
|
static inline void fbuffer_append_char(FBuffer *fb, char newchr);
|
|
66
38
|
static VALUE fbuffer_finalize(FBuffer *fb);
|
|
67
39
|
|
|
68
|
-
static void
|
|
40
|
+
static void fbuffer_init(FBuffer *fb, size_t initial_length, VALUE io, char *stack_buffer, size_t stack_buffer_size)
|
|
69
41
|
{
|
|
70
|
-
|
|
71
|
-
|
|
42
|
+
if (RTEST(io)) {
|
|
43
|
+
JSON_ASSERT(fb->type == FBUFFER_HEAP_ALLOCATED);
|
|
44
|
+
fb->io = io;
|
|
45
|
+
fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_IO_BUFFER_SIZE;
|
|
46
|
+
} else {
|
|
72
47
|
fb->type = FBUFFER_STACK_ALLOCATED;
|
|
73
48
|
fb->ptr = stack_buffer;
|
|
74
49
|
fb->capa = stack_buffer_size;
|
|
50
|
+
fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
|
|
51
|
+
}
|
|
52
|
+
#if JSON_DEBUG
|
|
53
|
+
fb->requested = 0;
|
|
54
|
+
#endif
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static inline void fbuffer_consumed(FBuffer *fb, size_t consumed)
|
|
58
|
+
{
|
|
59
|
+
#if JSON_DEBUG
|
|
60
|
+
if (consumed > fb->requested) {
|
|
61
|
+
rb_bug("fbuffer: Out of bound write");
|
|
75
62
|
}
|
|
63
|
+
fb->requested = 0;
|
|
64
|
+
#endif
|
|
65
|
+
fb->len += consumed;
|
|
76
66
|
}
|
|
77
67
|
|
|
78
68
|
static void fbuffer_free(FBuffer *fb)
|
|
@@ -93,113 +83,177 @@ static void fbuffer_flush(FBuffer *fb)
|
|
|
93
83
|
fbuffer_clear(fb);
|
|
94
84
|
}
|
|
95
85
|
|
|
96
|
-
static void fbuffer_realloc(FBuffer *fb,
|
|
86
|
+
static void fbuffer_realloc(FBuffer *fb, size_t new_capa)
|
|
97
87
|
{
|
|
98
|
-
if (
|
|
88
|
+
if (new_capa > fb->capa) {
|
|
99
89
|
if (fb->type == FBUFFER_STACK_ALLOCATED) {
|
|
100
90
|
const char *old_buffer = fb->ptr;
|
|
101
|
-
fb->ptr = ALLOC_N(char,
|
|
91
|
+
fb->ptr = ALLOC_N(char, new_capa);
|
|
102
92
|
fb->type = FBUFFER_HEAP_ALLOCATED;
|
|
103
93
|
MEMCPY(fb->ptr, old_buffer, char, fb->len);
|
|
104
94
|
} else {
|
|
105
|
-
REALLOC_N(fb->ptr, char,
|
|
95
|
+
REALLOC_N(fb->ptr, char, new_capa);
|
|
106
96
|
}
|
|
107
|
-
fb->capa =
|
|
97
|
+
fb->capa = new_capa;
|
|
108
98
|
}
|
|
109
99
|
}
|
|
110
100
|
|
|
111
|
-
static void fbuffer_do_inc_capa(FBuffer *fb,
|
|
101
|
+
static void fbuffer_do_inc_capa(FBuffer *fb, size_t requested)
|
|
112
102
|
{
|
|
113
103
|
if (RB_UNLIKELY(fb->io)) {
|
|
114
|
-
if (fb->capa
|
|
115
|
-
fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE);
|
|
116
|
-
} else {
|
|
104
|
+
if (fb->capa != 0) {
|
|
117
105
|
fbuffer_flush(fb);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return;
|
|
106
|
+
if (RB_LIKELY(requested < fb->capa)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
122
109
|
}
|
|
123
110
|
}
|
|
124
111
|
|
|
125
|
-
|
|
112
|
+
size_t new_capa = fb->capa ? fb->capa : fb->initial_length;
|
|
113
|
+
size_t needed_capa = requested + fb->len;
|
|
126
114
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
fb->capa = fb->initial_length;
|
|
115
|
+
while (new_capa < needed_capa) {
|
|
116
|
+
new_capa *= 2;
|
|
130
117
|
}
|
|
131
118
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
fbuffer_realloc(fb, required);
|
|
119
|
+
fbuffer_realloc(fb, new_capa);
|
|
135
120
|
}
|
|
136
121
|
|
|
137
|
-
static inline void fbuffer_inc_capa(FBuffer *fb,
|
|
122
|
+
static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
|
|
138
123
|
{
|
|
124
|
+
#if JSON_DEBUG
|
|
125
|
+
fb->requested = requested;
|
|
126
|
+
#endif
|
|
127
|
+
|
|
139
128
|
if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
|
|
140
129
|
fbuffer_do_inc_capa(fb, requested);
|
|
141
130
|
}
|
|
142
131
|
}
|
|
143
132
|
|
|
144
|
-
static
|
|
133
|
+
static inline size_t fbuffer_size_mul_or_raise(size_t a, size_t b)
|
|
134
|
+
{
|
|
135
|
+
size_t result = a * b;
|
|
136
|
+
if (RB_UNLIKELY(a != 0 && (result / a) != b)) {
|
|
137
|
+
rb_raise(rb_eArgError, "Buffer overflow, the resulting document is too large to be generated");
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, size_t len)
|
|
143
|
+
{
|
|
144
|
+
MEMCPY(fb->ptr + fb->len, newstr, char, len);
|
|
145
|
+
fbuffer_consumed(fb, len);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static inline void fbuffer_append(FBuffer *fb, const char *newstr, size_t len)
|
|
145
149
|
{
|
|
146
150
|
if (len > 0) {
|
|
147
151
|
fbuffer_inc_capa(fb, len);
|
|
148
|
-
|
|
149
|
-
|
|
152
|
+
fbuffer_append_reserved(fb, newstr, len);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
|
|
157
|
+
static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
|
|
158
|
+
{
|
|
159
|
+
#if JSON_DEBUG
|
|
160
|
+
if (fb->requested < 1) {
|
|
161
|
+
rb_bug("fbuffer: unreserved write");
|
|
150
162
|
}
|
|
163
|
+
fb->requested--;
|
|
164
|
+
#endif
|
|
165
|
+
|
|
166
|
+
fb->ptr[fb->len] = chr;
|
|
167
|
+
fb->len++;
|
|
151
168
|
}
|
|
152
169
|
|
|
153
170
|
static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
|
154
171
|
{
|
|
155
|
-
const char *
|
|
156
|
-
|
|
172
|
+
const char *ptr;
|
|
173
|
+
size_t len;
|
|
174
|
+
RSTRING_GETMEM(str, ptr, len);
|
|
157
175
|
|
|
176
|
+
fbuffer_append(fb, ptr, len);
|
|
158
177
|
RB_GC_GUARD(str);
|
|
178
|
+
}
|
|
159
179
|
|
|
160
|
-
|
|
180
|
+
static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
|
|
181
|
+
{
|
|
182
|
+
const char *ptr;
|
|
183
|
+
size_t len;
|
|
184
|
+
RSTRING_GETMEM(str, ptr, len);
|
|
185
|
+
|
|
186
|
+
fbuffer_inc_capa(fb, fbuffer_size_mul_or_raise(repeat, len));
|
|
187
|
+
while (repeat) {
|
|
188
|
+
#if JSON_DEBUG
|
|
189
|
+
fb->requested = len;
|
|
190
|
+
#endif
|
|
191
|
+
fbuffer_append_reserved(fb, ptr, len);
|
|
192
|
+
repeat--;
|
|
193
|
+
}
|
|
194
|
+
RB_GC_GUARD(str);
|
|
161
195
|
}
|
|
162
196
|
|
|
163
197
|
static inline void fbuffer_append_char(FBuffer *fb, char newchr)
|
|
164
198
|
{
|
|
165
199
|
fbuffer_inc_capa(fb, 1);
|
|
166
200
|
*(fb->ptr + fb->len) = newchr;
|
|
167
|
-
fb
|
|
201
|
+
fbuffer_consumed(fb, 1);
|
|
168
202
|
}
|
|
169
203
|
|
|
170
|
-
static
|
|
204
|
+
static inline char *fbuffer_cursor(FBuffer *fb)
|
|
171
205
|
{
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
char* tmp = buf;
|
|
206
|
+
return fb->ptr + fb->len;
|
|
207
|
+
}
|
|
175
208
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
return buf - tmp;
|
|
209
|
+
static inline void fbuffer_advance_to(FBuffer *fb, char *end)
|
|
210
|
+
{
|
|
211
|
+
fbuffer_consumed(fb, (end - fb->ptr) - fb->len);
|
|
180
212
|
}
|
|
181
213
|
|
|
182
|
-
|
|
214
|
+
/*
|
|
215
|
+
* Appends the decimal string representation of \a number into the buffer.
|
|
216
|
+
*/
|
|
183
217
|
static void fbuffer_append_long(FBuffer *fb, long number)
|
|
184
218
|
{
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
219
|
+
/*
|
|
220
|
+
* The jeaiii_ultoa() function produces digits left-to-right,
|
|
221
|
+
* allowing us to write directly into the buffer, but we don't know
|
|
222
|
+
* the number of resulting characters.
|
|
223
|
+
*
|
|
224
|
+
* We do know, however, that the `number` argument is always in the
|
|
225
|
+
* range 0xc000000000000000 to 0x3fffffffffffffff, or, in decimal,
|
|
226
|
+
* -4611686018427387904 to 4611686018427387903. The max number of chars
|
|
227
|
+
* generated is therefore 20 (including a potential sign character).
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
static const int MAX_CHARS_FOR_LONG = 20;
|
|
231
|
+
|
|
232
|
+
fbuffer_inc_capa(fb, MAX_CHARS_FOR_LONG);
|
|
233
|
+
|
|
234
|
+
if (number < 0) {
|
|
235
|
+
fbuffer_append_reserved_char(fb, '-');
|
|
236
|
+
|
|
237
|
+
/*
|
|
238
|
+
* Since number is always > LONG_MIN, `-number` will not overflow
|
|
239
|
+
* and is always the positive abs() value.
|
|
240
|
+
*/
|
|
241
|
+
number = -number;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
char *end = jeaiii_ultoa(fbuffer_cursor(fb), number);
|
|
245
|
+
fbuffer_advance_to(fb, end);
|
|
189
246
|
}
|
|
190
247
|
|
|
191
248
|
static VALUE fbuffer_finalize(FBuffer *fb)
|
|
192
249
|
{
|
|
193
250
|
if (fb->io) {
|
|
194
251
|
fbuffer_flush(fb);
|
|
195
|
-
fbuffer_free(fb);
|
|
196
252
|
rb_io_flush(fb->io);
|
|
197
253
|
return fb->io;
|
|
198
254
|
} else {
|
|
199
|
-
|
|
200
|
-
fbuffer_free(fb);
|
|
201
|
-
return result;
|
|
255
|
+
return rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
|
|
202
256
|
}
|
|
203
257
|
}
|
|
204
258
|
|
|
205
|
-
#endif
|
|
259
|
+
#endif // _FBUFFER_H_
|