json 2.10.2 → 2.15.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: b909ed90787afa31835c9926529808ca3c6b161755e1f6126b7fe203231fa0c2
4
- data.tar.gz: efd6a873e98f9e4b2b8c078210f141dfde5f3773884bd5851f228c184ff94ff7
3
+ metadata.gz: 981ed5b9d1d88ec48021ed8ab7757ab595dbb2bb5a16e9b3595dc4aae5c1f193
4
+ data.tar.gz: 1fc86464fa02dead5539e47dc37c3dcb3d068707ee8d1e32c574893e1bb702de
5
5
  SHA512:
6
- metadata.gz: bff39363d5d7fa2f209d5bf97a5728738058bc83600b732d95487e263a5a3901b1e18d5c42d7d2be019ac55ef069a50cedc1d9a8d22b11e6061f2eef747dc94d
7
- data.tar.gz: 693e480f8f2489e26c43eef56ad8977ab3f00fada03318a48b88ac55e2b82b15ffae92fd05dd47d86686fae39169b079f59a68fffc6efaf44ee3503a45060e30
6
+ metadata.gz: c06d2e27a811c7b2368ed364f1fd72c0423922094ed29ec0de07a75db7aa53d3f2662aa80220f6d094fe39400603d2919b97308010fcc5c0efab86307089848b
7
+ data.tar.gz: c032759c67a721b378518dd3c555e4e1c3f1ca34ff00f1bb21a2f136b626399efdd9c5a701aa018c3c9d5da050d0c110ee98e29853d4949db125f524fad1fdae
data/CHANGES.md CHANGED
@@ -1,10 +1,112 @@
1
1
  # Changes
2
2
 
3
+ ### Unreleased
4
+
5
+ ### 2025-10-07 (2.15.1)
6
+
7
+ * Fix incorrect escaping in the JRuby extension when encoding shared strings.
8
+
9
+ ### 2025-09-22 (2.15.0)
10
+
11
+ * `JSON::Coder` callback now receive a second argument to convey whether the object is a hash key.
12
+ * Tuned the floating point number generator to not use scientific notation as aggressively.
13
+
14
+ ### 2025-09-18 (2.14.1)
15
+
16
+ * Fix `IndexOutOfBoundsException` in the JRuby extension when encoding shared strings.
17
+
18
+ ### 2025-09-18 (2.14.0)
19
+
20
+ * Add new `allow_duplicate_key` generator options. By default a warning is now emitted when a duplicated key is encountered.
21
+ In `json 3.0` an error will be raised.
22
+ ```ruby
23
+ >> Warning[:deprecated] = true
24
+ >> puts JSON.generate({ foo: 1, "foo" => 2 })
25
+ (irb):2: warning: detected duplicate key "foo" in {foo: 1, "foo" => 2}.
26
+ This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`
27
+ {"foo":1,"foo":2}
28
+ >> JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false)
29
+ detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError)
30
+ ```
31
+ * Fix `JSON.generate` `strict: true` mode to also restrict hash keys.
32
+ * Fix `JSON::Coder` to also invoke block for hash keys that aren't strings nor symbols.
33
+ * Fix `JSON.unsafe_load` usage with proc
34
+ * Fix the parser to more consistently reject invalid UTF-16 surogate pairs.
35
+ * Stop defining `String.json_create`, `String#to_json_raw`, `String#to_json_raw_object` when `json/add` isn't loaded.
36
+
37
+ ### 2025-07-28 (2.13.2)
38
+
39
+ * Improve duplicate key warning and errors to include the key name and point to the right caller.
40
+
41
+ ### 2025-07-24 (2.13.1)
42
+
43
+ * Fix support for older compilers without `__builtin_cpu_supports`.
44
+
45
+ ### 2025-07-17 (2.13.0)
46
+
47
+ * Add new `allow_duplicate_key` parsing options. By default a warning is now emitted when a duplicated key is encountered.
48
+ In `json 3.0` an error will be raised.
49
+ * Optimize parsing further using SIMD to scan strings.
50
+
51
+ ### 2025-05-23 (2.12.2)
52
+
53
+ * Fix compiler optimization level.
54
+
55
+ ### 2025-05-23 (2.12.1)
56
+
57
+ * Fix a potential crash in large negative floating point number generation.
58
+ * Fix for JSON.pretty_generate to use passed state object's generate instead of state class as the required parameters aren't available.
59
+
60
+ ### 2025-05-12 (2.12.0)
61
+
62
+ * Improve floating point generation to not use scientific notation as much.
63
+ * Include line and column in parser errors. Both in the message and as exception attributes.
64
+ * Handle non-string hash keys with broken `to_s` implementations.
65
+ * `JSON.generate` now uses SSE2 (x86) or NEON (arm64) instructions when available to escape strings.
66
+
67
+ ### 2025-04-25 (2.11.3)
68
+
69
+ * Fix a regression in `JSON.pretty_generate` that could cause indentation to be off once some `#to_json` has been called.
70
+
71
+ ### 2025-04-24 (2.11.2)
72
+
73
+ * Add back `JSON::PRETTY_STATE_PROTOTYPE`. This constant was private API but is used by popular gems like `multi_json`.
74
+ It now emits a deprecation warning.
75
+
76
+ ### 2025-04-24 (2.11.1)
77
+
78
+ * Add back `JSON.restore`, `JSON.unparse`, `JSON.fast_unparse` and `JSON.pretty_unparse`.
79
+ These were deprecated 16 years ago, but never emitted warnings, only undocumented, so are
80
+ still used by a few gems.
81
+
82
+ ### 2025-04-24 (2.11.0)
83
+
84
+ * Optimize Integer generation to be ~1.8x faster.
85
+ * Optimize Float generation to be ~10x faster.
86
+ * Fix `JSON.load` proc argument to substitute the parsed object with the return value.
87
+ This better match `Marshal.load` behavior.
88
+ * Deprecate `JSON.fast_generate` (it's not any faster, so pointless).
89
+ * Deprecate `JSON.load_default_options`.
90
+ * Deprecate `JSON.unsafe_load_default_options`.
91
+ * Deprecate `JSON.dump_default_options`.
92
+ * Deprecate `Kernel#j`
93
+ * Deprecate `Kernel#jj`
94
+ * Remove outdated `JSON.iconv`.
95
+ * Remove `Class#json_creatable?` monkey patch.
96
+ * Remove deprecated `JSON.restore` method.
97
+ * Remove deprecated `JSON.unparse` method.
98
+ * Remove deprecated `JSON.fast_unparse` method.
99
+ * Remove deprecated `JSON.pretty_unparse` method.
100
+ * Remove deprecated `JSON::UnparserError` constant.
101
+ * Remove outdated `JSON::MissingUnicodeSupport` constant.
102
+
3
103
  ### 2025-03-12 (2.10.2)
4
104
 
5
105
  * 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` unadvertently changed it.
106
+ * Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0` inadvertently changed it.
7
107
  * Ensure document snippets that are included in parser errors don't include truncated multibyte characters.
108
+ * Ensure parser error snippets are valid UTF-8.
109
+ * Fix `JSON::GeneratorError#detailed_message` on Ruby < 3.2
8
110
 
9
111
  ### 2025-02-10 (2.10.1)
10
112
 
@@ -32,7 +134,7 @@
32
134
 
33
135
  ### 2024-11-14 (2.8.2)
34
136
 
35
- * `JSON.load_file` explictly read the file as UTF-8.
137
+ * `JSON.load_file` explicitly read the file as UTF-8.
36
138
 
37
139
  ### 2024-11-06 (2.8.1)
38
140
 
@@ -40,7 +142,7 @@
40
142
 
41
143
  ### 2024-11-06 (2.8.0)
42
144
 
43
- * Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being explictly enabled.
145
+ * Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being explicitly enabled.
44
146
  * Prefer to use `JSON.unsafe_load(string)` or `JSON.load(string, create_additions: true)`.
45
147
  * Emit a deprecation warning when serializing valid UTF-8 strings encoded in `ASCII_8BIT` aka `BINARY`.
46
148
  * Bump required Ruby version to 2.7.
@@ -48,7 +150,7 @@
48
150
  pre-existing support for comments, make it suitable to parse `jsonc` documents.
49
151
  * Many performance improvements to `JSON.parse` and `JSON.load`, up to `1.7x` faster on real world documents.
50
152
  * Some minor performance improvements to `JSON.dump` and `JSON.generate`.
51
- * `JSON.pretty_generate` no longer include newline inside empty object and arrays.
153
+ * `JSON.pretty_generate` no longer includes newlines inside empty object and arrays.
52
154
 
53
155
  ### 2024-11-04 (2.7.6)
54
156
 
@@ -65,13 +167,13 @@
65
167
  * Workaround a bug in 3.4.8 and older https://github.com/rubygems/rubygems/pull/6490.
66
168
  This bug would cause some gems with native extension to fail during compilation.
67
169
  * Workaround different versions of `json` and `json_pure` being loaded (not officially supported).
68
- * Make `json_pure` Ractor compatible.
170
+ * Make `json_pure` Ractor compatible.
69
171
 
70
172
  ### 2024-10-24 (2.7.3)
71
173
 
72
174
  * 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 unparseable source.
74
- * Fix json-pure's `Object#to_json` to accept non state arguments
175
+ * Limit the size of ParserError exception messages, only include up to 32 bytes of the unparsable source.
176
+ * Fix json-pure's `Object#to_json` to accept non-state arguments.
75
177
  * Fix multiline comment support in `json-pure`.
76
178
  * Fix `JSON.parse` to no longer mutate the argument encoding when passed an ASCII-8BIT string.
77
179
  * Fix `String#to_json` to raise on invalid encoding in `json-pure`.
@@ -216,6 +318,7 @@
216
318
  ## 2015-09-11 (2.0.0)
217
319
  * Now complies to newest JSON RFC 7159.
218
320
  * Implements compatibility to ruby 2.4 integer unification.
321
+ * Removed support for `quirks_mode` option.
219
322
  * Drops support for old rubies whose life has ended, that is rubies < 2.0.
220
323
  Also see https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/
221
324
  * There were still some mentions of dual GPL licensing in the source, but JSON
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,8 @@ 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
+
116
118
  ## Combining JSON fragments
117
119
 
118
120
  To combine JSON fragments into a bigger JSON document, you can use `JSON::Fragment`:
@@ -233,6 +235,19 @@ the `pp` library's `pp` methods.
233
235
 
234
236
  ## Development
235
237
 
238
+ ### Prerequisites
239
+
240
+ 1. Clone the repository
241
+ 2. Install dependencies with `bundle install`
242
+
243
+ ### Testing
244
+
245
+ The full test suite can be run with:
246
+
247
+ ```bash
248
+ bundle exec rake test
249
+ ```
250
+
236
251
  ### Release
237
252
 
238
253
  Update the `lib/json/version.rb` file.
@@ -3,6 +3,7 @@
3
3
 
4
4
  #include "ruby.h"
5
5
  #include "ruby/encoding.h"
6
+ #include "../vendor/jeaiii-ltoa.h"
6
7
 
7
8
  /* shims */
8
9
  /* This is the fallback definition from Ruby 3.4 */
@@ -23,6 +24,14 @@ typedef unsigned char _Bool;
23
24
  #endif
24
25
  #endif
25
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
+
26
35
  #ifndef RB_UNLIKELY
27
36
  #define RB_UNLIKELY(expr) expr
28
37
  #endif
@@ -35,6 +44,12 @@ typedef unsigned char _Bool;
35
44
  # define MAYBE_UNUSED(x) x
36
45
  #endif
37
46
 
47
+ #ifdef RUBY_DEBUG
48
+ #ifndef JSON_DEBUG
49
+ #define JSON_DEBUG RUBY_DEBUG
50
+ #endif
51
+ #endif
52
+
38
53
  enum fbuffer_type {
39
54
  FBUFFER_HEAP_ALLOCATED = 0,
40
55
  FBUFFER_STACK_ALLOCATED = 1,
@@ -45,6 +60,9 @@ typedef struct FBufferStruct {
45
60
  unsigned long initial_length;
46
61
  unsigned long len;
47
62
  unsigned long capa;
63
+ #ifdef JSON_DEBUG
64
+ unsigned long requested;
65
+ #endif
48
66
  char *ptr;
49
67
  VALUE io;
50
68
  } FBuffer;
@@ -73,6 +91,20 @@ static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *
73
91
  fb->ptr = stack_buffer;
74
92
  fb->capa = stack_buffer_size;
75
93
  }
94
+ #ifdef JSON_DEBUG
95
+ fb->requested = 0;
96
+ #endif
97
+ }
98
+
99
+ static inline void fbuffer_consumed(FBuffer *fb, unsigned long consumed)
100
+ {
101
+ #ifdef JSON_DEBUG
102
+ if (consumed > fb->requested) {
103
+ rb_bug("fbuffer: Out of bound write");
104
+ }
105
+ fb->requested = 0;
106
+ #endif
107
+ fb->len += consumed;
76
108
  }
77
109
 
78
110
  static void fbuffer_free(FBuffer *fb)
@@ -136,56 +168,115 @@ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
136
168
 
137
169
  static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
138
170
  {
171
+ #ifdef JSON_DEBUG
172
+ fb->requested = requested;
173
+ #endif
174
+
139
175
  if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
140
176
  fbuffer_do_inc_capa(fb, requested);
141
177
  }
142
178
  }
143
179
 
144
- static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
180
+ static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, unsigned long len)
181
+ {
182
+ MEMCPY(fb->ptr + fb->len, newstr, char, len);
183
+ fbuffer_consumed(fb, len);
184
+ }
185
+
186
+ static inline void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
145
187
  {
146
188
  if (len > 0) {
147
189
  fbuffer_inc_capa(fb, len);
148
- MEMCPY(fb->ptr + fb->len, newstr, char, len);
149
- fb->len += len;
190
+ fbuffer_append_reserved(fb, newstr, len);
150
191
  }
151
192
  }
152
193
 
194
+ /* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
195
+ static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
196
+ {
197
+ #ifdef JSON_DEBUG
198
+ if (fb->requested < 1) {
199
+ rb_bug("fbuffer: unreserved write");
200
+ }
201
+ fb->requested--;
202
+ #endif
203
+
204
+ fb->ptr[fb->len] = chr;
205
+ fb->len++;
206
+ }
207
+
153
208
  static void fbuffer_append_str(FBuffer *fb, VALUE str)
154
209
  {
155
210
  const char *newstr = StringValuePtr(str);
156
211
  unsigned long len = RSTRING_LEN(str);
157
212
 
158
- RB_GC_GUARD(str);
159
-
160
213
  fbuffer_append(fb, newstr, len);
161
214
  }
162
215
 
216
+ static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
217
+ {
218
+ const char *newstr = StringValuePtr(str);
219
+ unsigned long len = RSTRING_LEN(str);
220
+
221
+ fbuffer_inc_capa(fb, repeat * len);
222
+ while (repeat) {
223
+ #ifdef JSON_DEBUG
224
+ fb->requested = len;
225
+ #endif
226
+ fbuffer_append_reserved(fb, newstr, len);
227
+ repeat--;
228
+ }
229
+ }
230
+
163
231
  static inline void fbuffer_append_char(FBuffer *fb, char newchr)
164
232
  {
165
233
  fbuffer_inc_capa(fb, 1);
166
234
  *(fb->ptr + fb->len) = newchr;
167
- fb->len++;
235
+ fbuffer_consumed(fb, 1);
168
236
  }
169
237
 
170
- static long fltoa(long number, char *buf)
238
+ static inline char *fbuffer_cursor(FBuffer *fb)
171
239
  {
172
- static const char digits[] = "0123456789";
173
- long sign = number;
174
- char* tmp = buf;
240
+ return fb->ptr + fb->len;
241
+ }
175
242
 
176
- if (sign < 0) number = -number;
177
- do *tmp-- = digits[number % 10]; while (number /= 10);
178
- if (sign < 0) *tmp-- = '-';
179
- return buf - tmp;
243
+ static inline void fbuffer_advance_to(FBuffer *fb, char *end)
244
+ {
245
+ fbuffer_consumed(fb, (end - fb->ptr) - fb->len);
180
246
  }
181
247
 
182
- #define LONG_BUFFER_SIZE 20
248
+ /*
249
+ * Appends the decimal string representation of \a number into the buffer.
250
+ */
183
251
  static void fbuffer_append_long(FBuffer *fb, long number)
184
252
  {
185
- char buf[LONG_BUFFER_SIZE];
186
- char *buffer_end = buf + LONG_BUFFER_SIZE;
187
- long len = fltoa(number, buffer_end - 1);
188
- fbuffer_append(fb, buffer_end - len, len);
253
+ /*
254
+ * The jeaiii_ultoa() function produces digits left-to-right,
255
+ * allowing us to write directly into the buffer, but we don't know
256
+ * the number of resulting characters.
257
+ *
258
+ * We do know, however, that the `number` argument is always in the
259
+ * range 0xc000000000000000 to 0x3fffffffffffffff, or, in decimal,
260
+ * -4611686018427387904 to 4611686018427387903. The max number of chars
261
+ * generated is therefore 20 (including a potential sign character).
262
+ */
263
+
264
+ static const int MAX_CHARS_FOR_LONG = 20;
265
+
266
+ fbuffer_inc_capa(fb, MAX_CHARS_FOR_LONG);
267
+
268
+ if (number < 0) {
269
+ fbuffer_append_reserved_char(fb, '-');
270
+
271
+ /*
272
+ * Since number is always > LONG_MIN, `-number` will not overflow
273
+ * and is always the positive abs() value.
274
+ */
275
+ number = -number;
276
+ }
277
+
278
+ char *end = jeaiii_ultoa(fbuffer_cursor(fb), number);
279
+ fbuffer_advance_to(fb, end);
189
280
  }
190
281
 
191
282
  static VALUE fbuffer_finalize(FBuffer *fb)
@@ -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["JSON_DEBUG"]
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