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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 981ed5b9d1d88ec48021ed8ab7757ab595dbb2bb5a16e9b3595dc4aae5c1f193
4
- data.tar.gz: 1fc86464fa02dead5539e47dc37c3dcb3d068707ee8d1e32c574893e1bb702de
3
+ metadata.gz: 2e0f63481c8ba1c4f76f44a86ac9f1814e82fb396125e41e95efddc8e259fe64
4
+ data.tar.gz: a070ae0776f2db0519ec672d3a32be7dee2be2ee10d0bc35c9dde6581ec8c4a8
5
5
  SHA512:
6
- metadata.gz: c06d2e27a811c7b2368ed364f1fd72c0423922094ed29ec0de07a75db7aa53d3f2662aa80220f6d094fe39400603d2919b97308010fcc5c0efab86307089848b
7
- data.tar.gz: c032759c67a721b378518dd3c555e4e1c3f1ca34ff00f1bb21a2f136b626399efdd9c5a701aa018c3c9d5da050d0c110ee98e29853d4949db125f524fad1fdae
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 "ruby.h"
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
- unsigned long initial_length;
61
- unsigned long len;
62
- unsigned long capa;
63
- #ifdef JSON_DEBUG
64
- unsigned long requested;
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, unsigned long len);
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, unsigned long initial_length, char *stack_buffer, long stack_buffer_size)
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
- #ifdef JSON_DEBUG
48
+ #if JSON_DEBUG
95
49
  fb->requested = 0;
96
50
  #endif
97
51
  }
98
52
 
99
- static inline void fbuffer_consumed(FBuffer *fb, unsigned long consumed)
53
+ static inline void fbuffer_consumed(FBuffer *fb, size_t consumed)
100
54
  {
101
- #ifdef JSON_DEBUG
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, unsigned long required)
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, unsigned long requested)
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
- unsigned long required;
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, unsigned long requested)
123
+ static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
170
124
  {
171
- #ifdef JSON_DEBUG
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, unsigned long len)
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, unsigned long len)
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
- #ifdef JSON_DEBUG
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 *newstr = StringValuePtr(str);
211
- unsigned long len = RSTRING_LEN(str);
164
+ const char *ptr;
165
+ size_t len;
166
+ RSTRING_GETMEM(str, ptr, len);
212
167
 
213
- fbuffer_append(fb, newstr, len);
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 *newstr = StringValuePtr(str);
219
- unsigned long len = RSTRING_LEN(str);
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
- #ifdef JSON_DEBUG
179
+ #if JSON_DEBUG
224
180
  fb->requested = len;
225
181
  #endif
226
- fbuffer_append_reserved(fb, newstr, len);
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
- VALUE result = rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
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["JSON_DEBUG"]
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"