json 2.18.1 → 3.0.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 +127 -0
- data/LEGAL +3 -3
- data/README.md +19 -90
- data/ext/json/ext/fbuffer/fbuffer.h +46 -36
- data/ext/json/ext/generator/extconf.rb +3 -0
- data/ext/json/ext/generator/generator.c +200 -364
- data/ext/json/ext/json.h +95 -0
- data/ext/json/ext/parser/extconf.rb +27 -0
- data/ext/json/ext/parser/parser.c +1654 -406
- data/ext/json/ext/simd/simd.h +0 -10
- data/ext/json/ext/vendor/fast_float_parser.h +814 -0
- data/lib/json/common.rb +184 -411
- data/lib/json/ext/generator/state.rb +2 -28
- data/lib/json/ext.rb +25 -1
- data/lib/json/truffle_ruby/generator.rb +89 -81
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +53 -264
- metadata +3 -19
- data/ext/json/ext/vendor/ryu.h +0 -819
- data/lib/json/add/bigdecimal.rb +0 -58
- data/lib/json/add/complex.rb +0 -51
- data/lib/json/add/core.rb +0 -13
- data/lib/json/add/date.rb +0 -54
- data/lib/json/add/date_time.rb +0 -67
- data/lib/json/add/exception.rb +0 -49
- data/lib/json/add/ostruct.rb +0 -54
- data/lib/json/add/range.rb +0 -54
- data/lib/json/add/rational.rb +0 -49
- data/lib/json/add/regexp.rb +0 -48
- data/lib/json/add/set.rb +0 -48
- data/lib/json/add/string.rb +0 -35
- data/lib/json/add/struct.rb +0 -52
- data/lib/json/add/symbol.rb +0 -52
- data/lib/json/add/time.rb +0 -52
- data/lib/json/generic_object.rb +0 -67
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b4a457db7bd0eba155736808c796e81741e7ea72438768e183a7b7a795e8de4e
|
|
4
|
+
data.tar.gz: 61bb013e78224c5c91f620d3097986735411befd5139f33eaecb2c3c510ababe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6e0b4b4a8160235dcef28c7924ecda754a7ae792a714e6b5fbc5baa810fd358129c4340dd1056d31df823bf139f2b42bccb9c2d400c7282079d3dcf3b2f8c2c8
|
|
7
|
+
data.tar.gz: e67782447e1934dcada56b848982e6bb15708997b9eca220f7b631470734e0b14b2b227ac82612b03b8eb6b3d54b7686d4627a6d551d05b8252da40203f07087
|
data/CHANGES.md
CHANGED
|
@@ -2,6 +2,125 @@
|
|
|
2
2
|
|
|
3
3
|
### Unreleased
|
|
4
4
|
|
|
5
|
+
### 2026-09-07 (3.0.0)
|
|
6
|
+
|
|
7
|
+
* Add `JSON::ParserError#json_path` to locate parse errors in the document as a JSONPath-style string (e.g. `$.foo[0].bar`). For duplicate key errors it points at the duplicated key itself.
|
|
8
|
+
* Fix the parser to also reject lone trailing UTF-16 surrogates (`\uDCxx` with no leading partner), symmetric to the leading-surrogate case. The Java parser already rejected these; this closes the CRuby/JRuby parity gap.
|
|
9
|
+
|
|
10
|
+
### 2026-08-11 (3.0.0.rc1)
|
|
11
|
+
|
|
12
|
+
With the removal of the insecure `create_additions` option, `JSON.load` and `JSON.dump` are
|
|
13
|
+
now safe to use. Them being unsafe by default caused multiple security vulnerabilites in the past.
|
|
14
|
+
|
|
15
|
+
If you did depend on `create_additions`, the recommended migration is to [implement a custom serializer using
|
|
16
|
+
`JSON::Coder`](https://byroot.github.io/ruby/json/2025/08/02/whats-wrong-with-the-json-gem-api.html#the-create_additions-option).
|
|
17
|
+
|
|
18
|
+
All the mutable default options, such as `JSON.load_default_options` have been removed.
|
|
19
|
+
They were preventing Ractor compatiblity, and causing bug in libraries using JSON expecting the default behavior.
|
|
20
|
+
`JSON` methods now always behave the same unless monkey patched.
|
|
21
|
+
|
|
22
|
+
All methods options are now either keyword arguments or checked like keyword arguments, meaning
|
|
23
|
+
unknown options such as typos raise `ArgumentError`.
|
|
24
|
+
|
|
25
|
+
Duplicated keys are now rejected by default.
|
|
26
|
+
|
|
27
|
+
JavaScript comments in documents are no longer supported by default.
|
|
28
|
+
|
|
29
|
+
Numerous rarely used aliases have been removed.
|
|
30
|
+
|
|
31
|
+
* `JSON.load` defaults are now safe to use.
|
|
32
|
+
* All unknown options will now cause an `ArgumentError` rather than to be ignored.
|
|
33
|
+
* The `allow_comments` parsing option now defaults to `false`.
|
|
34
|
+
* The `allow_duplicate_key` option now defaults to `false`, for both parsing and generating JSON.
|
|
35
|
+
* Removed the `limit` positional argument of `JSON.dump`.
|
|
36
|
+
* Removed the `escape_slash` alias of `script_safe`.
|
|
37
|
+
* Removed `Kernel#j` and `Kernel#jj`.
|
|
38
|
+
* Removed `JSON.load_default_options`.
|
|
39
|
+
* Removed `JSON.unsafe_load_default_options`.
|
|
40
|
+
* Removed `JSON.dump_default_options`.
|
|
41
|
+
* Removed `JSON::State#[]` and `JSON::State#[]=`.
|
|
42
|
+
* Removed `JSON.unparse`.
|
|
43
|
+
* Removed `JSON.fast_generate`.
|
|
44
|
+
* Removed `JSON.fast_unparse`.
|
|
45
|
+
* Removed `JSON.pretty_unparse`.
|
|
46
|
+
* Removed `JSON.restore`.
|
|
47
|
+
* Removed `JSON::PRETTY_STATE_PROTOTYPE`.
|
|
48
|
+
* Removed the insecure `create_additions` option.
|
|
49
|
+
* Removed `JSON::GenericObject`.
|
|
50
|
+
|
|
51
|
+
### 2026-07-31 (2.21.2)
|
|
52
|
+
|
|
53
|
+
* Fix a use-after-free bug in `JSON::ResumableParser`. [GHSA-9hj4-r449-hfvc][CVE-2026-71847].
|
|
54
|
+
|
|
55
|
+
### 2026-07-13 (2.21.1)
|
|
56
|
+
|
|
57
|
+
* Fix a compilation issue on Window and Microsoft Visual C++.
|
|
58
|
+
|
|
59
|
+
### 2026-07-12 (2.21.0)
|
|
60
|
+
|
|
61
|
+
* `JSON.generate` now accept a `sort_keys` option, which takes either a boolean or a block.
|
|
62
|
+
* Added `#empty?` and `#partial_value?` methods on `JSON::ResumableParser`.
|
|
63
|
+
* Numerous correctness and performance fixes for `JSON::ResumableParser`.
|
|
64
|
+
* Avoid triggering Ruby's `float out of range` warning when parsing out of range numbers.
|
|
65
|
+
* Declare C types with Ruby 4.1 `RUBY_TYPED_THREAD_SAFE_FREE`.
|
|
66
|
+
|
|
67
|
+
### 2026-06-23 (2.20.0)
|
|
68
|
+
|
|
69
|
+
* Both C and Java parsers are no longer recursive, so parsing very deep documents with `max_nesting: false` will no longer
|
|
70
|
+
result in `SystemStackError stack level too deep` errors.
|
|
71
|
+
* The `:max_nesting` option still defaults to `100`.
|
|
72
|
+
* Optimized floating point number parsing further by replacing the ryu algorithm by a port of Eisel-Lemire Fast Float.
|
|
73
|
+
* Added `JSON::ResumableParser` to parse streams of JSON documents. Not yet available on JRuby.
|
|
74
|
+
* Deprecate default support of JavaScript comments in the parser and add `allow_comments: true` parsing option.
|
|
75
|
+
* Integrate with Ruby 4.1 `ruby_sized_xfree`.
|
|
76
|
+
|
|
77
|
+
### 2026-06-11 (2.19.9)
|
|
78
|
+
|
|
79
|
+
* Fix buffer overflow that could lead to a crash when writing JSON directly into an IO
|
|
80
|
+
with `JSON.generate(object, io)`. [CVE-2026-54696].
|
|
81
|
+
|
|
82
|
+
### 2026-06-03 (2.19.8)
|
|
83
|
+
|
|
84
|
+
* Fix 1-byte buffer overread on EOS errors.
|
|
85
|
+
* Handle invalid types passed as `max_nesting` option.
|
|
86
|
+
|
|
87
|
+
### 2026-05-28 (2.19.7)
|
|
88
|
+
|
|
89
|
+
* Fix some more edge cases with out of range floats.
|
|
90
|
+
* Ensure the string provided to `JSON.parse` can't be mutated during parsing.
|
|
91
|
+
* Add missing write barriers in `State#dup`.
|
|
92
|
+
* Further validate generator `depth` config.
|
|
93
|
+
|
|
94
|
+
### 2026-05-28 (2.19.6)
|
|
95
|
+
|
|
96
|
+
* Cleanly handle overly large `depth` generator argument.
|
|
97
|
+
* Add missing write barrier in `ParserConfig`.
|
|
98
|
+
|
|
99
|
+
### 2026-05-04 (2.19.5)
|
|
100
|
+
|
|
101
|
+
* Cap the parser to emit a maximum of 5 deprecation warnings per document. Emitting more is not helpful.
|
|
102
|
+
|
|
103
|
+
### 2026-04-19 (2.19.4)
|
|
104
|
+
|
|
105
|
+
* Fix parsing of out of range floats (very large exponents that lead to either `0.0` or `Inf`).
|
|
106
|
+
|
|
107
|
+
### 2026-03-25 (2.19.3)
|
|
108
|
+
|
|
109
|
+
* Fix handling of unescaped control characters preceeded by a backslash.
|
|
110
|
+
|
|
111
|
+
### 2026-03-18 (2.19.2)
|
|
112
|
+
|
|
113
|
+
* Fix a format string injection vulnerability in `JSON.parse(doc, allow_duplicate_key: false)`. `CVE-2026-33210`.
|
|
114
|
+
|
|
115
|
+
### 2026-03-08 (2.19.1)
|
|
116
|
+
|
|
117
|
+
* Fix a compiler dependent GC bug introduced in `2.18.0`.
|
|
118
|
+
|
|
119
|
+
### 2026-03-06 (2.19.0)
|
|
120
|
+
|
|
121
|
+
* Fix `allow_blank` parsing option to no longer allow invalid types (e.g. `load([], allow_blank: true)` now raise a type error).
|
|
122
|
+
* Add `allow_invalid_escape` parsing option to ignore backslashes that aren't followed by one of the valid escape characters.
|
|
123
|
+
|
|
5
124
|
### 2026-02-03 (2.18.1)
|
|
6
125
|
|
|
7
126
|
* Fix a potential crash in very specific circumstance if GC triggers during a call to `to_json`
|
|
@@ -11,6 +130,10 @@
|
|
|
11
130
|
|
|
12
131
|
* Add `:allow_control_characters` parser options, to allow JSON strings containing unescaped ASCII control characters (e.g. newlines).
|
|
13
132
|
|
|
133
|
+
### 2026-03-18 (2.17.1.2) - Security Backport
|
|
134
|
+
|
|
135
|
+
* Fix a format string injection vulnerability in `JSON.parse(doc, allow_duplicate_key: false)`. `CVE-2026-33210`.
|
|
136
|
+
|
|
14
137
|
### 2025-12-04 (2.17.1)
|
|
15
138
|
|
|
16
139
|
* Fix a regression in parsing of unicode surogate pairs (`\uXX\uXX`) that could cause an invalid string to be returned.
|
|
@@ -37,6 +160,10 @@
|
|
|
37
160
|
* Optimized numbers parsing using SWAR (thanks to Scott Myron).
|
|
38
161
|
* Optimized parsing of pretty printed documents using SWAR (thanks to Scott Myron).
|
|
39
162
|
|
|
163
|
+
### 2026-03-18 (2.15.2.1) - Security Backport
|
|
164
|
+
|
|
165
|
+
* Fix a format string injection vulnerability in `JSON.parse(doc, allow_duplicate_key: false)`. `CVE-2026-33210`.
|
|
166
|
+
|
|
40
167
|
### 2025-10-25 (2.15.2)
|
|
41
168
|
|
|
42
169
|
* Fix `JSON::Coder` to have one dedicated depth counter per invocation.
|
data/LEGAL
CHANGED
|
@@ -15,6 +15,6 @@ ext/json/ext/vendor/jeaiii-ltoa.h::
|
|
|
15
15
|
This file is adapted from https://github.com/jeaiii/itoa
|
|
16
16
|
It is licensed under the MIT License
|
|
17
17
|
|
|
18
|
-
ext/json/ext/vendor/
|
|
19
|
-
This file is adapted from the
|
|
20
|
-
It is
|
|
18
|
+
ext/json/ext/vendor/fast_float_parser.h::
|
|
19
|
+
This file is adapted from the Fast Float C++ library by The fast_float authors https://github.com/fastfloat/fast_float
|
|
20
|
+
It is licensed under the MIT License
|
data/README.md
CHANGED
|
@@ -14,10 +14,7 @@ UTF-16 surrogate pairs in order to be able to generate the whole range of
|
|
|
14
14
|
unicode code points.
|
|
15
15
|
|
|
16
16
|
All strings, that are to be encoded as JSON strings, should be UTF-8 byte
|
|
17
|
-
sequences on the Ruby side.
|
|
18
|
-
encoded, please use the to\_json\_raw\_object method of String (which produces
|
|
19
|
-
an object, that contains a byte array) and decode the result on the receiving
|
|
20
|
-
endpoint.
|
|
17
|
+
sequences on the Ruby side.
|
|
21
18
|
|
|
22
19
|
## Installation
|
|
23
20
|
|
|
@@ -49,8 +46,7 @@ JSON.generate(data)
|
|
|
49
46
|
```
|
|
50
47
|
|
|
51
48
|
You can also use the `pretty_generate` method (which formats the output more
|
|
52
|
-
verbosely and nicely)
|
|
53
|
-
checks generate performs, e. g. nesting deepness checks).
|
|
49
|
+
verbosely and nicely).
|
|
54
50
|
|
|
55
51
|
## Casting non native types
|
|
56
52
|
|
|
@@ -85,7 +81,7 @@ Both of these behavior can be disabled using the `strict: true` option:
|
|
|
85
81
|
|
|
86
82
|
```ruby
|
|
87
83
|
JSON.generate(Object.new, strict: true) # => Object not allowed in JSON (JSON::GeneratorError)
|
|
88
|
-
JSON.generate(Position.new(1, 2)) # => Position not allowed in JSON (JSON::GeneratorError)
|
|
84
|
+
JSON.generate(Position.new(1, 2), strict: true) # => Position not allowed in JSON (JSON::GeneratorError)
|
|
89
85
|
```
|
|
90
86
|
|
|
91
87
|
## JSON::Coder
|
|
@@ -117,13 +113,13 @@ It is also called for objects that do have a JSON equivalent, but are used as Ha
|
|
|
117
113
|
as well as for strings that aren't valid UTF-8:
|
|
118
114
|
|
|
119
115
|
```ruby
|
|
120
|
-
coder = JSON::
|
|
116
|
+
coder = JSON::Coder.new do |object, is_object_key|
|
|
121
117
|
case object
|
|
122
118
|
when String
|
|
123
|
-
if !
|
|
124
|
-
Base64.encode64(
|
|
119
|
+
if !object.valid_encoding? || object.encoding != Encoding::UTF_8
|
|
120
|
+
Base64.encode64(object)
|
|
125
121
|
else
|
|
126
|
-
|
|
122
|
+
object
|
|
127
123
|
end
|
|
128
124
|
else
|
|
129
125
|
object
|
|
@@ -143,84 +139,12 @@ posts_json.map! { |post_json| JSON::Fragment.new(post_json) }
|
|
|
143
139
|
JSON.generate({ posts: posts_json, count: posts_json.count })
|
|
144
140
|
```
|
|
145
141
|
|
|
146
|
-
##
|
|
147
|
-
|
|
148
|
-
> [!CAUTION]
|
|
149
|
-
> You should never use `JSON.unsafe_load` nor `JSON.parse(str, create_additions: true)` to parse untrusted user input,
|
|
150
|
-
> as it can lead to remote code execution vulnerabilities.
|
|
151
|
-
|
|
152
|
-
To create a JSON document from a ruby data structure, you can call
|
|
153
|
-
`JSON.generate` like that:
|
|
154
|
-
|
|
155
|
-
```ruby
|
|
156
|
-
json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
|
157
|
-
# => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
To get back a ruby data structure from a JSON document, you have to call
|
|
161
|
-
JSON.parse on it:
|
|
162
|
-
|
|
163
|
-
```ruby
|
|
164
|
-
JSON.parse json
|
|
165
|
-
# => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"]
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
Note, that the range from the original data structure is a simple
|
|
169
|
-
string now. The reason for this is, that JSON doesn't support ranges
|
|
170
|
-
or arbitrary classes. In this case the json library falls back to call
|
|
171
|
-
`Object#to_json`, which is the same as `#to_s.to_json`.
|
|
172
|
-
|
|
173
|
-
It's possible to add JSON support serialization to arbitrary classes by
|
|
174
|
-
simply implementing a more specialized version of the `#to_json method`, that
|
|
175
|
-
should return a JSON object (a hash converted to JSON with `#to_json`) like
|
|
176
|
-
this (don't forget the `*a` for all the arguments):
|
|
177
|
-
|
|
178
|
-
```ruby
|
|
179
|
-
class Range
|
|
180
|
-
def to_json(*a)
|
|
181
|
-
{
|
|
182
|
-
'json_class' => self.class.name, # = 'Range'
|
|
183
|
-
'data' => [ first, last, exclude_end? ]
|
|
184
|
-
}.to_json(*a)
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
The hash key `json_class` is the class, that will be asked to deserialise the
|
|
190
|
-
JSON representation later. In this case it's `Range`, but any namespace of
|
|
191
|
-
the form `A::B` or `::A::B` will do. All other keys are arbitrary and can be
|
|
192
|
-
used to store the necessary data to configure the object to be deserialised.
|
|
193
|
-
|
|
194
|
-
If the key `json_class` is found in a JSON object, the JSON parser checks
|
|
195
|
-
if the given class responds to the `json_create` class method. If so, it is
|
|
196
|
-
called with the JSON object converted to a Ruby hash. So a range can
|
|
197
|
-
be deserialised by implementing `Range.json_create` like this:
|
|
198
|
-
|
|
199
|
-
```ruby
|
|
200
|
-
class Range
|
|
201
|
-
def self.json_create(o)
|
|
202
|
-
new(*o['data'])
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
Now it possible to serialise/deserialise ranges as well:
|
|
208
|
-
|
|
209
|
-
```ruby
|
|
210
|
-
json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
|
211
|
-
# => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
|
|
212
|
-
JSON.parse json
|
|
213
|
-
# => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
|
214
|
-
json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
|
215
|
-
# => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
|
|
216
|
-
JSON.unsafe_load json
|
|
217
|
-
# => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
|
|
218
|
-
```
|
|
142
|
+
## Pretty Printing
|
|
219
143
|
|
|
220
144
|
`JSON.generate` always creates the shortest possible string representation of a
|
|
221
145
|
ruby data structure in one line. This is good for data storage or network
|
|
222
146
|
protocols, but not so good for humans to read. Fortunately there's also
|
|
223
|
-
`JSON.pretty_generate`
|
|
147
|
+
`JSON.pretty_generate` that creates a more readable
|
|
224
148
|
output:
|
|
225
149
|
|
|
226
150
|
```ruby
|
|
@@ -245,9 +169,16 @@ output:
|
|
|
245
169
|
]
|
|
246
170
|
```
|
|
247
171
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
172
|
+
## Security
|
|
173
|
+
|
|
174
|
+
When parsing or serializing untrusted input, parser and generator options should never be user controlled.
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
# Dangerous, DO NOT DO THIS.
|
|
178
|
+
JSON.generate(params[:data], params[:options])
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Security vulnerability reports relying on attacker controlled parsing or generator options will be handled as regular bug fixes.
|
|
251
182
|
|
|
252
183
|
## Development
|
|
253
184
|
|
|
@@ -295,5 +226,3 @@ The latest version of this library can be downloaded at
|
|
|
295
226
|
Online Documentation should be located at
|
|
296
227
|
|
|
297
228
|
* https://www.rubydoc.info/gems/json
|
|
298
|
-
|
|
299
|
-
[Ragel]: http://www.colm.net/open-source/ragel/
|
|
@@ -11,11 +11,11 @@ enum fbuffer_type {
|
|
|
11
11
|
|
|
12
12
|
typedef struct FBufferStruct {
|
|
13
13
|
enum fbuffer_type type;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
size_t initial_length;
|
|
15
|
+
size_t len;
|
|
16
|
+
size_t capa;
|
|
17
17
|
#if JSON_DEBUG
|
|
18
|
-
|
|
18
|
+
size_t requested;
|
|
19
19
|
#endif
|
|
20
20
|
char *ptr;
|
|
21
21
|
VALUE io;
|
|
@@ -32,25 +32,29 @@ typedef struct FBufferStruct {
|
|
|
32
32
|
|
|
33
33
|
static void fbuffer_free(FBuffer *fb);
|
|
34
34
|
static void fbuffer_clear(FBuffer *fb);
|
|
35
|
-
static void fbuffer_append(FBuffer *fb, const char *newstr,
|
|
35
|
+
static void fbuffer_append(FBuffer *fb, const char *newstr, size_t len);
|
|
36
36
|
static void fbuffer_append_long(FBuffer *fb, long number);
|
|
37
37
|
static inline void fbuffer_append_char(FBuffer *fb, char newchr);
|
|
38
38
|
static VALUE fbuffer_finalize(FBuffer *fb);
|
|
39
39
|
|
|
40
|
-
static void
|
|
40
|
+
static void fbuffer_init(FBuffer *fb, size_t initial_length, VALUE io, char *stack_buffer, size_t stack_buffer_size)
|
|
41
41
|
{
|
|
42
|
-
|
|
43
|
-
|
|
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 {
|
|
44
47
|
fb->type = FBUFFER_STACK_ALLOCATED;
|
|
45
48
|
fb->ptr = stack_buffer;
|
|
46
49
|
fb->capa = stack_buffer_size;
|
|
50
|
+
fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
|
|
47
51
|
}
|
|
48
52
|
#if JSON_DEBUG
|
|
49
53
|
fb->requested = 0;
|
|
50
54
|
#endif
|
|
51
55
|
}
|
|
52
56
|
|
|
53
|
-
static inline void fbuffer_consumed(FBuffer *fb,
|
|
57
|
+
static inline void fbuffer_consumed(FBuffer *fb, size_t consumed)
|
|
54
58
|
{
|
|
55
59
|
#if JSON_DEBUG
|
|
56
60
|
if (consumed > fb->requested) {
|
|
@@ -64,7 +68,7 @@ static inline void fbuffer_consumed(FBuffer *fb, unsigned long consumed)
|
|
|
64
68
|
static void fbuffer_free(FBuffer *fb)
|
|
65
69
|
{
|
|
66
70
|
if (fb->ptr && fb->type == FBUFFER_HEAP_ALLOCATED) {
|
|
67
|
-
|
|
71
|
+
JSON_SIZED_FREE_N(fb->ptr, fb->capa);
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
|
|
@@ -79,48 +83,43 @@ static void fbuffer_flush(FBuffer *fb)
|
|
|
79
83
|
fbuffer_clear(fb);
|
|
80
84
|
}
|
|
81
85
|
|
|
82
|
-
static void fbuffer_realloc(FBuffer *fb,
|
|
86
|
+
static void fbuffer_realloc(FBuffer *fb, size_t new_capa)
|
|
83
87
|
{
|
|
84
|
-
if (
|
|
88
|
+
if (new_capa > fb->capa) {
|
|
85
89
|
if (fb->type == FBUFFER_STACK_ALLOCATED) {
|
|
86
90
|
const char *old_buffer = fb->ptr;
|
|
87
|
-
fb->ptr = ALLOC_N(char,
|
|
91
|
+
fb->ptr = ALLOC_N(char, new_capa);
|
|
88
92
|
fb->type = FBUFFER_HEAP_ALLOCATED;
|
|
89
93
|
MEMCPY(fb->ptr, old_buffer, char, fb->len);
|
|
90
94
|
} else {
|
|
91
|
-
|
|
95
|
+
JSON_SIZED_REALLOC_N(fb->ptr, char, new_capa, fb->capa);
|
|
92
96
|
}
|
|
93
|
-
fb->capa =
|
|
97
|
+
fb->capa = new_capa;
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
100
|
|
|
97
|
-
static void fbuffer_do_inc_capa(FBuffer *fb,
|
|
101
|
+
static void fbuffer_do_inc_capa(FBuffer *fb, size_t requested)
|
|
98
102
|
{
|
|
99
103
|
if (RB_UNLIKELY(fb->io)) {
|
|
100
|
-
if (fb->capa
|
|
101
|
-
fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE);
|
|
102
|
-
} else {
|
|
104
|
+
if (fb->capa != 0) {
|
|
103
105
|
fbuffer_flush(fb);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return;
|
|
106
|
+
if (RB_LIKELY(requested < fb->capa)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
|
|
112
|
+
size_t new_capa = fb->capa ? fb->capa : fb->initial_length;
|
|
113
|
+
size_t needed_capa = requested + fb->len;
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
fb->capa = fb->initial_length;
|
|
115
|
+
while (new_capa < needed_capa) {
|
|
116
|
+
new_capa *= 2;
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
fbuffer_realloc(fb, required);
|
|
119
|
+
fbuffer_realloc(fb, new_capa);
|
|
121
120
|
}
|
|
122
121
|
|
|
123
|
-
static inline void fbuffer_inc_capa(FBuffer *fb,
|
|
122
|
+
static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
|
|
124
123
|
{
|
|
125
124
|
#if JSON_DEBUG
|
|
126
125
|
fb->requested = requested;
|
|
@@ -131,13 +130,22 @@ static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
|
|
|
131
130
|
}
|
|
132
131
|
}
|
|
133
132
|
|
|
134
|
-
static inline
|
|
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)
|
|
135
143
|
{
|
|
136
144
|
MEMCPY(fb->ptr + fb->len, newstr, char, len);
|
|
137
145
|
fbuffer_consumed(fb, len);
|
|
138
146
|
}
|
|
139
147
|
|
|
140
|
-
static inline void fbuffer_append(FBuffer *fb, const char *newstr,
|
|
148
|
+
static inline void fbuffer_append(FBuffer *fb, const char *newstr, size_t len)
|
|
141
149
|
{
|
|
142
150
|
if (len > 0) {
|
|
143
151
|
fbuffer_inc_capa(fb, len);
|
|
@@ -162,19 +170,20 @@ static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
|
|
|
162
170
|
static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
|
163
171
|
{
|
|
164
172
|
const char *ptr;
|
|
165
|
-
|
|
173
|
+
size_t len;
|
|
166
174
|
RSTRING_GETMEM(str, ptr, len);
|
|
167
175
|
|
|
168
176
|
fbuffer_append(fb, ptr, len);
|
|
177
|
+
RB_GC_GUARD(str);
|
|
169
178
|
}
|
|
170
179
|
|
|
171
180
|
static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
|
|
172
181
|
{
|
|
173
182
|
const char *ptr;
|
|
174
|
-
|
|
183
|
+
size_t len;
|
|
175
184
|
RSTRING_GETMEM(str, ptr, len);
|
|
176
185
|
|
|
177
|
-
fbuffer_inc_capa(fb, repeat
|
|
186
|
+
fbuffer_inc_capa(fb, fbuffer_size_mul_or_raise(repeat, len));
|
|
178
187
|
while (repeat) {
|
|
179
188
|
#if JSON_DEBUG
|
|
180
189
|
fb->requested = len;
|
|
@@ -182,6 +191,7 @@ static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
|
|
|
182
191
|
fbuffer_append_reserved(fb, ptr, len);
|
|
183
192
|
repeat--;
|
|
184
193
|
}
|
|
194
|
+
RB_GC_GUARD(str);
|
|
185
195
|
}
|
|
186
196
|
|
|
187
197
|
static inline void fbuffer_append_char(FBuffer *fb, char newchr)
|
|
@@ -5,6 +5,9 @@ if RUBY_ENGINE == 'truffleruby'
|
|
|
5
5
|
File.write('Makefile', dummy_makefile("").join)
|
|
6
6
|
else
|
|
7
7
|
append_cflags("-std=c99")
|
|
8
|
+
have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3
|
|
9
|
+
have_func("ruby_xfree_sized", "ruby.h") # RUBY_VERSION >= 4.1
|
|
10
|
+
|
|
8
11
|
$defs << "-DJSON_GENERATOR"
|
|
9
12
|
$defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
|
|
10
13
|
|