json 2.9.1 → 2.18.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: 8404bbc1f96ebfb471ebafc0fadef1f11125cd62c6cf1157091793183a8bdc32
4
- data.tar.gz: 4c93c85a2575eda3308cf404f116d8e638605d884fb14cd3ab3ee511b649c211
3
+ metadata.gz: cb2890db4c527125d27bc7c21fc64d3ac532ffbec8080f89a678daf48c36e09e
4
+ data.tar.gz: c4b37d085d05d3c43df97b3c24898dc6be61c76ba64c749b5a8a86bf4fc1198d
5
5
  SHA512:
6
- metadata.gz: 2043b99c8ca617249d1b3c81d899ebaf86dc2436196f08b0f9a32ee3ba7fa174c20124ca0f75157828049dc71f46fcd8937978cf463f952b2f2e6af113dfab42
7
- data.tar.gz: bccae4170d82569a9018ef7497bfb5b228a9371e7c79d84cb4fcc7659be144e3a3ba455db40ecf509a31b576f311d1ad4f85989aea549f59c003210adf87fc77
6
+ metadata.gz: fb55ef5a0aa6961ef0fe3bb30f398834820357045ad27a8fdb7e53eaba3af7c4d356ef26c0e73b7a87d2d9d51e500eae7193d7d1ae3aa1058c7973bcc462674b
7
+ data.tar.gz: bfb499789bbcee7f5f8d67e32ded664dc62c632ae39fe80bf4bff3d6aec16eee3730a7c4883216a393326f2ab33e94e5f9c58da4c5b31627347108c36c2b211c
data/CHANGES.md CHANGED
@@ -1,5 +1,165 @@
1
1
  # Changes
2
2
 
3
+ ### Unreleased
4
+
5
+ ### 2026-02-03 (2.18.1)
6
+
7
+ * Fix a potential crash in very specific circumstance if GC triggers during a call to `to_json`
8
+ without first invoking a user defined `#to_json` method.
9
+
10
+ ### 2025-12-11 (2.18.0)
11
+
12
+ * Add `:allow_control_characters` parser options, to allow JSON strings containing unescaped ASCII control characters (e.g. newlines).
13
+
14
+ ### 2025-12-04 (2.17.1)
15
+
16
+ * Fix a regression in parsing of unicode surogate pairs (`\uXX\uXX`) that could cause an invalid string to be returned.
17
+
18
+ ### 2025-12-03 (2.17.0)
19
+
20
+ * Improve `JSON.load` and `JSON.unsafe_load` to allow passing options as second argument.
21
+ * Fix the parser to no longer ignore invalid escapes in strings.
22
+ Only `\"`, `\\`, `\b`, `\f`, `\n`, `\r`, `\t` and `\u` are valid JSON escapes.
23
+ * Fixed `JSON::Coder` to use the depth it was initialized with.
24
+ * On TruffleRuby, fix the generator to not call `to_json` on the return value of `as_json` for `Float::NAN`.
25
+ * Fixed handling of `state.depth`: when `to_json` changes `state.depth` but does not restore it, it is reset
26
+ automatically to its initial value.
27
+ In particular, when a `NestingError` is raised, `depth` is no longer equal to `max_nesting` after the call to
28
+ generate, and is reset to its initial value. Similarly when `to_json` raises an exception.
29
+
30
+ ### 2025-11-07 (2.16.0)
31
+
32
+ * Deprecate `JSON::State#[]` and `JSON::State#[]=`. Consider using `JSON::Coder` instead.
33
+ * `JSON::Coder` now also yields to the block when encountering strings with invalid encoding.
34
+ * Fix GeneratorError messages to be UTF-8 encoded.
35
+ * Fix memory leak when `Exception` is raised, or `throw` is used during JSON generation.
36
+ * Optimized floating point number parsing by integrating the ryu algorithm (thanks to Josef Šimánek).
37
+ * Optimized numbers parsing using SWAR (thanks to Scott Myron).
38
+ * Optimized parsing of pretty printed documents using SWAR (thanks to Scott Myron).
39
+
40
+ ### 2025-10-25 (2.15.2)
41
+
42
+ * Fix `JSON::Coder` to have one dedicated depth counter per invocation.
43
+ After encountering a circular reference in `JSON::Coder#dump`, any further `#dump` call would raise `JSON::NestingError`.
44
+
45
+ ### 2025-10-07 (2.15.1)
46
+
47
+ * Fix incorrect escaping in the JRuby extension when encoding shared strings.
48
+
49
+ ### 2025-09-22 (2.15.0)
50
+
51
+ * `JSON::Coder` callback now receive a second argument to convey whether the object is a hash key.
52
+ * Tuned the floating point number generator to not use scientific notation as aggressively.
53
+
54
+ ### 2025-09-18 (2.14.1)
55
+
56
+ * Fix `IndexOutOfBoundsException` in the JRuby extension when encoding shared strings.
57
+
58
+ ### 2025-09-18 (2.14.0)
59
+
60
+ * Add new `allow_duplicate_key` generator options. By default a warning is now emitted when a duplicated key is encountered.
61
+ In `json 3.0` an error will be raised.
62
+ ```ruby
63
+ >> Warning[:deprecated] = true
64
+ >> puts JSON.generate({ foo: 1, "foo" => 2 })
65
+ (irb):2: warning: detected duplicate key "foo" in {foo: 1, "foo" => 2}.
66
+ This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`
67
+ {"foo":1,"foo":2}
68
+ >> JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false)
69
+ detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError)
70
+ ```
71
+ * Fix `JSON.generate` `strict: true` mode to also restrict hash keys.
72
+ * Fix `JSON::Coder` to also invoke block for hash keys that aren't strings nor symbols.
73
+ * Fix `JSON.unsafe_load` usage with proc
74
+ * Fix the parser to more consistently reject invalid UTF-16 surogate pairs.
75
+ * Stop defining `String.json_create`, `String#to_json_raw`, `String#to_json_raw_object` when `json/add` isn't loaded.
76
+
77
+ ### 2025-07-28 (2.13.2)
78
+
79
+ * Improve duplicate key warning and errors to include the key name and point to the right caller.
80
+
81
+ ### 2025-07-24 (2.13.1)
82
+
83
+ * Fix support for older compilers without `__builtin_cpu_supports`.
84
+
85
+ ### 2025-07-17 (2.13.0)
86
+
87
+ * Add new `allow_duplicate_key` parsing options. By default a warning is now emitted when a duplicated key is encountered.
88
+ In `json 3.0` an error will be raised.
89
+ * Optimize parsing further using SIMD to scan strings.
90
+
91
+ ### 2025-05-23 (2.12.2)
92
+
93
+ * Fix compiler optimization level.
94
+
95
+ ### 2025-05-23 (2.12.1)
96
+
97
+ * Fix a potential crash in large negative floating point number generation.
98
+ * Fix for JSON.pretty_generate to use passed state object's generate instead of state class as the required parameters aren't available.
99
+
100
+ ### 2025-05-12 (2.12.0)
101
+
102
+ * Improve floating point generation to not use scientific notation as much.
103
+ * Include line and column in parser errors. Both in the message and as exception attributes.
104
+ * Handle non-string hash keys with broken `to_s` implementations.
105
+ * `JSON.generate` now uses SSE2 (x86) or NEON (arm64) instructions when available to escape strings.
106
+
107
+ ### 2025-04-25 (2.11.3)
108
+
109
+ * Fix a regression in `JSON.pretty_generate` that could cause indentation to be off once some `#to_json` has been called.
110
+
111
+ ### 2025-04-24 (2.11.2)
112
+
113
+ * Add back `JSON::PRETTY_STATE_PROTOTYPE`. This constant was private API but is used by popular gems like `multi_json`.
114
+ It now emits a deprecation warning.
115
+
116
+ ### 2025-04-24 (2.11.1)
117
+
118
+ * Add back `JSON.restore`, `JSON.unparse`, `JSON.fast_unparse` and `JSON.pretty_unparse`.
119
+ These were deprecated 16 years ago, but never emitted warnings, only undocumented, so are
120
+ still used by a few gems.
121
+
122
+ ### 2025-04-24 (2.11.0)
123
+
124
+ * Optimize Integer generation to be ~1.8x faster.
125
+ * Optimize Float generation to be ~10x faster.
126
+ * Fix `JSON.load` proc argument to substitute the parsed object with the return value.
127
+ This better match `Marshal.load` behavior.
128
+ * Deprecate `JSON.fast_generate` (it's not any faster, so pointless).
129
+ * Deprecate `JSON.load_default_options`.
130
+ * Deprecate `JSON.unsafe_load_default_options`.
131
+ * Deprecate `JSON.dump_default_options`.
132
+ * Deprecate `Kernel#j`
133
+ * Deprecate `Kernel#jj`
134
+ * Remove outdated `JSON.iconv`.
135
+ * Remove `Class#json_creatable?` monkey patch.
136
+ * Remove deprecated `JSON.restore` method.
137
+ * Remove deprecated `JSON.unparse` method.
138
+ * Remove deprecated `JSON.fast_unparse` method.
139
+ * Remove deprecated `JSON.pretty_unparse` method.
140
+ * Remove deprecated `JSON::UnparserError` constant.
141
+ * Remove outdated `JSON::MissingUnicodeSupport` constant.
142
+
143
+ ### 2025-03-12 (2.10.2)
144
+
145
+ * Fix a potential crash in the C extension parser.
146
+ * Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0` inadvertently changed it.
147
+ * Ensure document snippets that are included in parser errors don't include truncated multibyte characters.
148
+ * Ensure parser error snippets are valid UTF-8.
149
+ * Fix `JSON::GeneratorError#detailed_message` on Ruby < 3.2
150
+
151
+ ### 2025-02-10 (2.10.1)
152
+
153
+ * Fix a compatibility issue with `MultiJson.dump(obj, pretty: true)`: `no implicit conversion of false into Proc (TypeError)`.
154
+
155
+ ### 2025-02-10 (2.10.0)
156
+
157
+ * `strict: true` now accept symbols as values. Previously they'd only be accepted as hash keys.
158
+ * The C extension Parser has been entirely reimplemented from scratch.
159
+ * Introduced `JSON::Coder` as a new API allowing to customize how non native types are serialized in a non-global way.
160
+ * Introduced `JSON::Fragment` to allow assembling cached fragments in a safe way.
161
+ * The Java implementation of the generator received many optimizations.
162
+
3
163
  ### 2024-12-18 (2.9.1)
4
164
 
5
165
  * Fix support for Solaris 10.
@@ -14,7 +174,7 @@
14
174
 
15
175
  ### 2024-11-14 (2.8.2)
16
176
 
17
- * `JSON.load_file` explictly read the file as UTF-8.
177
+ * `JSON.load_file` explicitly read the file as UTF-8.
18
178
 
19
179
  ### 2024-11-06 (2.8.1)
20
180
 
@@ -22,7 +182,7 @@
22
182
 
23
183
  ### 2024-11-06 (2.8.0)
24
184
 
25
- * Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being explictly enabled.
185
+ * Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being explicitly enabled.
26
186
  * Prefer to use `JSON.unsafe_load(string)` or `JSON.load(string, create_additions: true)`.
27
187
  * Emit a deprecation warning when serializing valid UTF-8 strings encoded in `ASCII_8BIT` aka `BINARY`.
28
188
  * Bump required Ruby version to 2.7.
@@ -30,7 +190,7 @@
30
190
  pre-existing support for comments, make it suitable to parse `jsonc` documents.
31
191
  * Many performance improvements to `JSON.parse` and `JSON.load`, up to `1.7x` faster on real world documents.
32
192
  * Some minor performance improvements to `JSON.dump` and `JSON.generate`.
33
- * `JSON.pretty_generate` no longer include newline inside empty object and arrays.
193
+ * `JSON.pretty_generate` no longer includes newlines inside empty object and arrays.
34
194
 
35
195
  ### 2024-11-04 (2.7.6)
36
196
 
@@ -47,13 +207,13 @@
47
207
  * Workaround a bug in 3.4.8 and older https://github.com/rubygems/rubygems/pull/6490.
48
208
  This bug would cause some gems with native extension to fail during compilation.
49
209
  * Workaround different versions of `json` and `json_pure` being loaded (not officially supported).
50
- * Make `json_pure` Ractor compatible.
210
+ * Make `json_pure` Ractor compatible.
51
211
 
52
212
  ### 2024-10-24 (2.7.3)
53
213
 
54
214
  * Numerous performance optimizations in `JSON.generate` and `JSON.dump` (up to 2 times faster).
55
- * Limit the size of ParserError exception messages, only include up to 32 bytes of the unparseable source.
56
- * Fix json-pure's `Object#to_json` to accept non state arguments
215
+ * Limit the size of ParserError exception messages, only include up to 32 bytes of the unparsable source.
216
+ * Fix json-pure's `Object#to_json` to accept non-state arguments.
57
217
  * Fix multiline comment support in `json-pure`.
58
218
  * Fix `JSON.parse` to no longer mutate the argument encoding when passed an ASCII-8BIT string.
59
219
  * Fix `String#to_json` to raise on invalid encoding in `json-pure`.
@@ -198,6 +358,7 @@
198
358
  ## 2015-09-11 (2.0.0)
199
359
  * Now complies to newest JSON RFC 7159.
200
360
  * Implements compatibility to ruby 2.4 integer unification.
361
+ * Removed support for `quirks_mode` option.
201
362
  * Drops support for old rubies whose life has ended, that is rubies < 2.0.
202
363
  Also see https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/
203
364
  * There were still some mentions of dual GPL licensing in the source, but JSON
data/LEGAL CHANGED
@@ -7,54 +7,14 @@ 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
9
 
10
- == MIT License
11
- >>>
12
- Permission is hereby granted, free of charge, to any person obtaining
13
- a copy of this software and associated documentation files (the
14
- "Software"), to deal in the Software without restriction, including
15
- without limitation the rights to use, copy, modify, merge, publish,
16
- distribute, sublicense, and/or sell copies of the Software, and to
17
- permit persons to whom the Software is furnished to do so, subject to
18
- the following conditions:
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.
19
13
 
20
- The above copyright notice and this permission notice shall be
21
- included in all copies or substantial portions of the Software.
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
22
17
 
23
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
-
31
- == Old-style BSD license
32
- >>>
33
- Redistribution and use in source and binary forms, with or without
34
- modification, are permitted provided that the following conditions
35
- are met:
36
- 1. Redistributions of source code must retain the above copyright
37
- notice, this list of conditions and the following disclaimer.
38
- 2. Redistributions in binary form must reproduce the above copyright
39
- notice, this list of conditions and the following disclaimer in the
40
- documentation and/or other materials provided with the distribution.
41
- 3. Neither the name of the University nor the names of its contributors
42
- may be used to endorse or promote products derived from this software
43
- without specific prior written permission.
44
-
45
- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48
- ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55
- SUCH DAMAGE.
56
-
57
- IMPORTANT NOTE::
58
-
59
- From ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
60
- paragraph 3 above is now null and void.
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
@@ -29,7 +29,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
29
29
 
30
30
  $ gem install json
31
31
 
32
- ## Usage
32
+ ## Basic Usage
33
33
 
34
34
  To use JSON you can
35
35
 
@@ -52,7 +52,98 @@ You can also use the `pretty_generate` method (which formats the output more
52
52
  verbosely and nicely) or `fast_generate` (which doesn't do any of the security
53
53
  checks generate performs, e. g. nesting deepness checks).
54
54
 
55
- ## Handling arbitrary types
55
+ ## Casting non native types
56
+
57
+ JSON documents can only support Hashes, Arrays, Strings, Integers and Floats.
58
+
59
+ By default if you attempt to serialize something else, `JSON.generate` will
60
+ search for a `#to_json` method on that object:
61
+
62
+ ```ruby
63
+ Position = Struct.new(:latitude, :longitude) do
64
+ def to_json(state = nil, *)
65
+ JSON::State.from_state(state).generate({
66
+ latitude: latitude,
67
+ longitude: longitude,
68
+ })
69
+ end
70
+ end
71
+
72
+ JSON.generate([
73
+ Position.new(12323.234, 435345.233),
74
+ Position.new(23434.676, 159435.324),
75
+ ]) # => [{"latitude":12323.234,"longitude":435345.233},{"latitude":23434.676,"longitude":159435.324}]
76
+ ```
77
+
78
+ If a `#to_json` method isn't defined on the object, `JSON.generate` will fallback to call `#to_s`:
79
+
80
+ ```ruby
81
+ JSON.generate(Object.new) # => "#<Object:0x000000011e768b98>"
82
+ ```
83
+
84
+ Both of these behavior can be disabled using the `strict: true` option:
85
+
86
+ ```ruby
87
+ 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)
89
+ ```
90
+
91
+ ## JSON::Coder
92
+
93
+ Since `#to_json` methods are global, it can sometimes be problematic if you need a given type to be
94
+ serialized in different ways in different locations.
95
+
96
+ Instead it is recommended to use the newer `JSON::Coder` API:
97
+
98
+ ```ruby
99
+ module MyApp
100
+ API_JSON_CODER = JSON::Coder.new do |object, is_object_key|
101
+ case object
102
+ when Time
103
+ object.iso8601(3)
104
+ else
105
+ object
106
+ end
107
+ end
108
+ end
109
+
110
+ puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
111
+ ```
112
+
113
+ The provided block is called for all objects that don't have a native JSON equivalent, and
114
+ must return a Ruby object that has a native JSON equivalent.
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
+
134
+ ## Combining JSON fragments
135
+
136
+ To combine JSON fragments into a bigger JSON document, you can use `JSON::Fragment`:
137
+
138
+ ```ruby
139
+ posts_json = cache.fetch_multi(post_ids) do |post_id|
140
+ JSON.generate(Post.find(post_id))
141
+ end
142
+ posts_json.map! { |post_json| JSON::Fragment.new(post_json) }
143
+ JSON.generate({ posts: posts_json, count: posts_json.count })
144
+ ```
145
+
146
+ ## Round-tripping arbitrary types
56
147
 
57
148
  > [!CAUTION]
58
149
  > You should never use `JSON.unsafe_load` nor `JSON.parse(str, create_additions: true)` to parse untrusted user input,
@@ -160,6 +251,19 @@ the `pp` library's `pp` methods.
160
251
 
161
252
  ## Development
162
253
 
254
+ ### Prerequisites
255
+
256
+ 1. Clone the repository
257
+ 2. Install dependencies with `bundle install`
258
+
259
+ ### Testing
260
+
261
+ The full test suite can be run with:
262
+
263
+ ```bash
264
+ bundle exec rake test
265
+ ```
266
+
163
267
  ### Release
164
268
 
165
269
  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 "ruby.h"
5
- #include "ruby/encoding.h"
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,
@@ -45,6 +14,9 @@ typedef struct FBufferStruct {
45
14
  unsigned long initial_length;
46
15
  unsigned long len;
47
16
  unsigned long capa;
17
+ #if JSON_DEBUG
18
+ unsigned long requested;
19
+ #endif
48
20
  char *ptr;
49
21
  VALUE io;
50
22
  } FBuffer;
@@ -59,17 +31,11 @@ typedef struct FBufferStruct {
59
31
  #define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb)
60
32
 
61
33
  static void fbuffer_free(FBuffer *fb);
62
- #ifndef JSON_GENERATOR
63
34
  static void fbuffer_clear(FBuffer *fb);
64
- #endif
65
35
  static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len);
66
- #ifdef JSON_GENERATOR
67
36
  static void fbuffer_append_long(FBuffer *fb, long number);
68
- #endif
69
37
  static inline void fbuffer_append_char(FBuffer *fb, char newchr);
70
- #ifdef JSON_GENERATOR
71
38
  static VALUE fbuffer_finalize(FBuffer *fb);
72
- #endif
73
39
 
74
40
  static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size)
75
41
  {
@@ -79,6 +45,20 @@ static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *
79
45
  fb->ptr = stack_buffer;
80
46
  fb->capa = stack_buffer_size;
81
47
  }
48
+ #if JSON_DEBUG
49
+ fb->requested = 0;
50
+ #endif
51
+ }
52
+
53
+ static inline void fbuffer_consumed(FBuffer *fb, unsigned long consumed)
54
+ {
55
+ #if JSON_DEBUG
56
+ if (consumed > fb->requested) {
57
+ rb_bug("fbuffer: Out of bound write");
58
+ }
59
+ fb->requested = 0;
60
+ #endif
61
+ fb->len += consumed;
82
62
  }
83
63
 
84
64
  static void fbuffer_free(FBuffer *fb)
@@ -142,73 +122,128 @@ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
142
122
 
143
123
  static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
144
124
  {
125
+ #if JSON_DEBUG
126
+ fb->requested = requested;
127
+ #endif
128
+
145
129
  if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
146
130
  fbuffer_do_inc_capa(fb, requested);
147
131
  }
148
132
  }
149
133
 
150
- static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
134
+ static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, unsigned long len)
135
+ {
136
+ MEMCPY(fb->ptr + fb->len, newstr, char, len);
137
+ fbuffer_consumed(fb, len);
138
+ }
139
+
140
+ static inline void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
151
141
  {
152
142
  if (len > 0) {
153
143
  fbuffer_inc_capa(fb, len);
154
- MEMCPY(fb->ptr + fb->len, newstr, char, len);
155
- fb->len += len;
144
+ fbuffer_append_reserved(fb, newstr, len);
156
145
  }
157
146
  }
158
147
 
159
- #ifdef JSON_GENERATOR
160
- static void fbuffer_append_str(FBuffer *fb, VALUE str)
148
+ /* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
149
+ static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
161
150
  {
162
- const char *newstr = StringValuePtr(str);
163
- unsigned long len = RSTRING_LEN(str);
151
+ #if JSON_DEBUG
152
+ if (fb->requested < 1) {
153
+ rb_bug("fbuffer: unreserved write");
154
+ }
155
+ fb->requested--;
156
+ #endif
157
+
158
+ fb->ptr[fb->len] = chr;
159
+ fb->len++;
160
+ }
164
161
 
165
- RB_GC_GUARD(str);
162
+ static void fbuffer_append_str(FBuffer *fb, VALUE str)
163
+ {
164
+ const char *ptr;
165
+ unsigned long len;
166
+ RSTRING_GETMEM(str, ptr, len);
166
167
 
167
- fbuffer_append(fb, newstr, len);
168
+ fbuffer_append(fb, ptr, len);
168
169
  }
170
+
171
+ static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
172
+ {
173
+ const char *ptr;
174
+ unsigned long len;
175
+ RSTRING_GETMEM(str, ptr, len);
176
+
177
+ fbuffer_inc_capa(fb, repeat * len);
178
+ while (repeat) {
179
+ #if JSON_DEBUG
180
+ fb->requested = len;
169
181
  #endif
182
+ fbuffer_append_reserved(fb, ptr, len);
183
+ repeat--;
184
+ }
185
+ }
170
186
 
171
187
  static inline void fbuffer_append_char(FBuffer *fb, char newchr)
172
188
  {
173
189
  fbuffer_inc_capa(fb, 1);
174
190
  *(fb->ptr + fb->len) = newchr;
175
- fb->len++;
191
+ fbuffer_consumed(fb, 1);
176
192
  }
177
193
 
178
- #ifdef JSON_GENERATOR
179
- static long fltoa(long number, char *buf)
194
+ static inline char *fbuffer_cursor(FBuffer *fb)
180
195
  {
181
- static const char digits[] = "0123456789";
182
- long sign = number;
183
- char* tmp = buf;
196
+ return fb->ptr + fb->len;
197
+ }
184
198
 
185
- if (sign < 0) number = -number;
186
- do *tmp-- = digits[number % 10]; while (number /= 10);
187
- if (sign < 0) *tmp-- = '-';
188
- return buf - tmp;
199
+ static inline void fbuffer_advance_to(FBuffer *fb, char *end)
200
+ {
201
+ fbuffer_consumed(fb, (end - fb->ptr) - fb->len);
189
202
  }
190
203
 
191
- #define LONG_BUFFER_SIZE 20
204
+ /*
205
+ * Appends the decimal string representation of \a number into the buffer.
206
+ */
192
207
  static void fbuffer_append_long(FBuffer *fb, long number)
193
208
  {
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);
209
+ /*
210
+ * The jeaiii_ultoa() function produces digits left-to-right,
211
+ * allowing us to write directly into the buffer, but we don't know
212
+ * the number of resulting characters.
213
+ *
214
+ * We do know, however, that the `number` argument is always in the
215
+ * range 0xc000000000000000 to 0x3fffffffffffffff, or, in decimal,
216
+ * -4611686018427387904 to 4611686018427387903. The max number of chars
217
+ * generated is therefore 20 (including a potential sign character).
218
+ */
219
+
220
+ static const int MAX_CHARS_FOR_LONG = 20;
221
+
222
+ fbuffer_inc_capa(fb, MAX_CHARS_FOR_LONG);
223
+
224
+ if (number < 0) {
225
+ fbuffer_append_reserved_char(fb, '-');
226
+
227
+ /*
228
+ * Since number is always > LONG_MIN, `-number` will not overflow
229
+ * and is always the positive abs() value.
230
+ */
231
+ number = -number;
232
+ }
233
+
234
+ char *end = jeaiii_ultoa(fbuffer_cursor(fb), number);
235
+ fbuffer_advance_to(fb, end);
198
236
  }
199
237
 
200
238
  static VALUE fbuffer_finalize(FBuffer *fb)
201
239
  {
202
240
  if (fb->io) {
203
241
  fbuffer_flush(fb);
204
- fbuffer_free(fb);
205
242
  rb_io_flush(fb->io);
206
243
  return fb->io;
207
244
  } else {
208
- VALUE result = rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
209
- fbuffer_free(fb);
210
- return result;
245
+ return rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
211
246
  }
212
247
  }
213
- #endif
214
- #endif
248
+
249
+ #endif // _FBUFFER_H_
@@ -6,5 +6,11 @@ if RUBY_ENGINE == 'truffleruby'
6
6
  else
7
7
  append_cflags("-std=c99")
8
8
  $defs << "-DJSON_GENERATOR"
9
+ $defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
10
+
11
+ if enable_config('generator-use-simd', default=!ENV["JSON_DISABLE_SIMD"])
12
+ load __dir__ + "/../simd/conf.rb"
13
+ end
14
+
9
15
  create_makefile 'json/ext/generator'
10
16
  end