smarter_json 1.2.4 → 1.2.6
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/CHANGELOG.md +16 -0
- data/ext/smarter_json/smarter_json.c +17 -9
- data/lib/smarter_json/parser.rb +19 -12
- data/lib/smarter_json/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d7ded6a0b9872aaa577e5a8381ed69c5eeb89056a05eaa82a253228b1456c2b9
|
|
4
|
+
data.tar.gz: 5fb399f6c8ae40528d521182b02b4e156734c436b70e3cbd662b832ad339f245
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0c87d064c656210698c3fd0307bd2673861105e1be998e0770a6833cc7c7bd9658c329b19c5866d87bb23db11fc2dec13a25b8007417fa634ed2fe6ab4d71624
|
|
7
|
+
data.tar.gz: 959b5022a439414a101285ee4d3480e8ca011c1d9293eac911024f9748b574bda1a108b280d529118b5e572fcd1dd633a9ef4ca89d0bea5e07823135868a5e2c
|
data/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,22 @@
|
|
|
13
13
|
> ⚠️ We discourage the use of `process(input).first` / `process(input)[0]` because it silently drops potential additional documents
|
|
14
14
|
> Please use `process_one` if you are expecting only one JSON doc, e.g. in API payloads, because it emits on_warning if it finds multiple docs.
|
|
15
15
|
|
|
16
|
+
## 1.2.6 (2026-07-02)
|
|
17
|
+
|
|
18
|
+
RSpec tests: 1,308 → 1,325
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
- **The `:number_overflow` warning now reports the sign of the collapsed value.** A negative number literal beyond Float range (e.g. `-1e400`) collapses to `-Infinity`, but the warning said "collapsed to Infinity" — always on the C path, and for top-level numbers on the pure-Ruby path. Both paths now report the actual collapsed value (`Infinity` or `-Infinity`) in every position, and the warning-parity suite locks them together.
|
|
23
|
+
|
|
24
|
+
## 1.2.5 (2026-07-01)
|
|
25
|
+
|
|
26
|
+
RSpec tests: 1,282 → 1,308
|
|
27
|
+
|
|
28
|
+
### New Features
|
|
29
|
+
|
|
30
|
+
- **RFC 7464 JSON Text Sequences are now read natively.** The record separator (`0x1E`) that frames each record is a first-class document separator, so an RS-framed stream parses into its documents without a warning, and bare-scalar records (a number / keyword / string on its own) are read correctly.
|
|
31
|
+
|
|
16
32
|
## 1.2.4 (2026-07-01)
|
|
17
33
|
|
|
18
34
|
RSpec tests: 1,268 → 1,282
|
|
@@ -649,7 +649,11 @@ static FJ_ALWAYS_INLINE VALUE fj_float_from_parts(fj_state *st, uint64_t m10, in
|
|
|
649
649
|
still returned). The Infinity/NaN keywords take separate paths and never get here.
|
|
650
650
|
Gate isinf on a listening handler (matches the Ruby float_or_warn): no handler ->
|
|
651
651
|
no point detecting, and it keeps the test off the hot number path. */
|
|
652
|
-
if (st->on_warning != Qnil && isinf(d))
|
|
652
|
+
if (st->on_warning != Qnil && isinf(d)) {
|
|
653
|
+
fj_warn(st, fj_sym_number_overflow,
|
|
654
|
+
d > 0 ? "number literal out of Float range — collapsed to Infinity"
|
|
655
|
+
: "number literal out of Float range — collapsed to -Infinity");
|
|
656
|
+
}
|
|
653
657
|
return rb_float_new(d);
|
|
654
658
|
}
|
|
655
659
|
|
|
@@ -1624,14 +1628,17 @@ static int fj_implicit_root_ahead(fj_state *st) {
|
|
|
1624
1628
|
return result;
|
|
1625
1629
|
}
|
|
1626
1630
|
|
|
1627
|
-
/* Between top-level documents, whitespace, comments, AND
|
|
1628
|
-
* (commas collapse like the in-container
|
|
1629
|
-
*
|
|
1630
|
-
* the
|
|
1631
|
+
/* Between top-level documents, whitespace, comments, commas, AND the RFC 7464
|
|
1632
|
+
* record separator (0x1E) all separate (commas collapse like the in-container
|
|
1633
|
+
* lenient-comma rule; 0x1E frames each JSON Text Sequence record). A space alone
|
|
1634
|
+
* never separates — that is handled inside the document by the quoteless run.
|
|
1635
|
+
* Mirrors the Ruby Parser#skip_document_separators. */
|
|
1631
1636
|
static void fj_skip_document_separators(fj_state *st) {
|
|
1632
1637
|
for (;;) {
|
|
1638
|
+
int b;
|
|
1633
1639
|
fj_skip_ws_comments(st);
|
|
1634
|
-
|
|
1640
|
+
b = fj_byte(st);
|
|
1641
|
+
if (b != ',' && b != 0x1E) break;
|
|
1635
1642
|
fj_advance(st, 1);
|
|
1636
1643
|
}
|
|
1637
1644
|
}
|
|
@@ -1640,8 +1647,9 @@ static int fj_is_hws(int b) { return b == ' ' || b == '\t' || b == 0x0B || b ==
|
|
|
1640
1647
|
|
|
1641
1648
|
/* After a top-level value: a self-delimiting value (object / array / string) may be
|
|
1642
1649
|
* followed by anything, but a bare scalar (number / keyword) must be followed by a
|
|
1643
|
-
* real separator — a newline, ',',
|
|
1644
|
-
* `1 2 3` and `42 "x" true` raise. Mirrors the
|
|
1650
|
+
* real separator — a newline, ',', the RFC 7464 record separator (0x1E), a comment,
|
|
1651
|
+
* or EOF. A space is NOT a separator, so `1 2 3` and `42 "x" true` raise. Mirrors the
|
|
1652
|
+
* Ruby Parser#enforce_scalar_boundary. */
|
|
1645
1653
|
static void fj_enforce_scalar_boundary(fj_state *st, VALUE value) {
|
|
1646
1654
|
int b, nx;
|
|
1647
1655
|
if (RB_TYPE_P(value, T_STRING) || RB_TYPE_P(value, T_HASH) || RB_TYPE_P(value, T_ARRAY)) return;
|
|
@@ -1655,7 +1663,7 @@ static void fj_enforce_scalar_boundary(fj_state *st, VALUE value) {
|
|
|
1655
1663
|
break;
|
|
1656
1664
|
}
|
|
1657
1665
|
b = fj_byte(st);
|
|
1658
|
-
if (b == -1 || b == 0x0A || b == 0x0D || b == ',') return;
|
|
1666
|
+
if (b == -1 || b == 0x0A || b == 0x0D || b == ',' || b == 0x1E) return;
|
|
1659
1667
|
if (b == '#') return;
|
|
1660
1668
|
if (b == '/') { nx = fj_byte_at(st, 1); if (nx == '/' || nx == '*') return; }
|
|
1661
1669
|
fj_error(st, "a top-level number or keyword must be followed by a newline, ',', or end of input");
|
data/lib/smarter_json/parser.rb
CHANGED
|
@@ -384,6 +384,7 @@ module SmarterJSON
|
|
|
384
384
|
TAB = 0x09
|
|
385
385
|
LF = 0x0A
|
|
386
386
|
CR = 0x0D
|
|
387
|
+
RS = 0x1E # RFC 7464 record separator — frames each JSON Text Sequence record
|
|
387
388
|
end
|
|
388
389
|
|
|
389
390
|
module Framer
|
|
@@ -1052,13 +1053,14 @@ module SmarterJSON
|
|
|
1052
1053
|
parse_iter(implicit_root_object_ahead?)
|
|
1053
1054
|
end
|
|
1054
1055
|
|
|
1055
|
-
# Between top-level documents, whitespace, comments, AND
|
|
1056
|
-
# (commas collapse like the in-container
|
|
1057
|
-
#
|
|
1056
|
+
# Between top-level documents, whitespace, comments, commas, AND the RFC 7464
|
|
1057
|
+
# record separator (0x1E) all separate (commas collapse like the in-container
|
|
1058
|
+
# lenient-comma rule; 0x1E frames each JSON Text Sequence record). A space alone
|
|
1059
|
+
# never separates — that is handled inside the document by the quoteless run, so
|
|
1058
1060
|
# `1 2 3` is one document (the string "1 2 3") while `1, 2, 3` is three.
|
|
1059
1061
|
def skip_document_separators
|
|
1060
1062
|
skip_whitespace_and_comments
|
|
1061
|
-
while byte == COMMA
|
|
1063
|
+
while (b = byte) == COMMA || b == RS
|
|
1062
1064
|
advance(1)
|
|
1063
1065
|
skip_whitespace_and_comments
|
|
1064
1066
|
end
|
|
@@ -1066,15 +1068,16 @@ module SmarterJSON
|
|
|
1066
1068
|
|
|
1067
1069
|
# After a top-level value: a self-delimiting value (object / array / quoted string)
|
|
1068
1070
|
# may be followed by anything (the next document self-delimits), but a bare scalar
|
|
1069
|
-
# (number / keyword) must be followed by a real separator — a newline, ',',
|
|
1070
|
-
# comment, or EOF. A space is NOT a separator, so
|
|
1071
|
-
# rather than silently splitting; bare top-level
|
|
1071
|
+
# (number / keyword) must be followed by a real separator — a newline, ',', the RFC
|
|
1072
|
+
# 7464 record separator (0x1E), a comment, or EOF. A space is NOT a separator, so
|
|
1073
|
+
# `1 2 3` and `42 "x" true` raise rather than silently splitting; bare top-level
|
|
1074
|
+
# words raise in parse_value itself.
|
|
1072
1075
|
def enforce_scalar_boundary(value)
|
|
1073
1076
|
return if value.is_a?(String) || value.is_a?(Hash) || value.is_a?(Array)
|
|
1074
1077
|
|
|
1075
1078
|
skip_horizontal_whitespace
|
|
1076
1079
|
b = byte
|
|
1077
|
-
return if b.nil? || b == LF || b == CR || b == COMMA
|
|
1080
|
+
return if b.nil? || b == LF || b == CR || b == COMMA || b == RS
|
|
1078
1081
|
return if b == HASH || (b == SLASH && ((c = byte_at(1)) == SLASH || c == STAR))
|
|
1079
1082
|
|
|
1080
1083
|
raise error("a top-level number or keyword must be followed by a newline, ',', or end of input")
|
|
@@ -1922,6 +1925,7 @@ module SmarterJSON
|
|
|
1922
1925
|
# --- numbers (top-level / strict positions) ---
|
|
1923
1926
|
|
|
1924
1927
|
def parse_number
|
|
1928
|
+
token_start = @pos
|
|
1925
1929
|
negative = false
|
|
1926
1930
|
signed = false
|
|
1927
1931
|
if byte == MINUS
|
|
@@ -1942,7 +1946,6 @@ module SmarterJSON
|
|
|
1942
1946
|
return Float::NAN
|
|
1943
1947
|
end
|
|
1944
1948
|
|
|
1945
|
-
int_start = @pos
|
|
1946
1949
|
had_leading_zero = false
|
|
1947
1950
|
|
|
1948
1951
|
if byte == ZERO
|
|
@@ -1998,9 +2001,13 @@ module SmarterJSON
|
|
|
1998
2001
|
raise error("invalid number with a leading zero")
|
|
1999
2002
|
end
|
|
2000
2003
|
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
+
# token_start sits BEFORE the sign, so the slice keeps it ("-1e400", not "1e400"):
|
|
2005
|
+
# decimal_value -> float_or_warn sees the signed token, and a negative overflow
|
|
2006
|
+
# warns "collapsed to -Infinity" — the same warning numeric_value produces for a
|
|
2007
|
+
# member-position number. String#to_i, String#to_f, and BigDecimal() all parse
|
|
2008
|
+
# the leading sign, so there is no negation step here.
|
|
2009
|
+
slice = @input.byteslice(token_start, @pos - token_start).delete("_")
|
|
2010
|
+
is_float ? decimal_value(slice) : slice.to_i
|
|
2004
2011
|
end
|
|
2005
2012
|
|
|
2006
2013
|
def hex_digit?(b)
|
data/lib/smarter_json/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: smarter_json
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tilo Sloboda
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-07-
|
|
10
|
+
date: 2026-07-03 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: bigdecimal
|