Skip to content

Add Int128, decimal and ISO 8601 options, and number conversion - #62

Open
letko-dmitry wants to merge 12 commits into
almazrafi:mainfrom
letko-dmitry:feature/value-formats
Open

letko-dmitry wants to merge 12 commits into
almazrafi:mainfrom
letko-dmitry:feature/value-formats

Conversation

@letko-dmitry

@letko-dmitry letko-dmitry commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Builds on #61: the first 7 commits belong to it. One commit per change here, so any of them can be dropped:

  • Decode numbers of other types as NSNumber ones are. A dictionary built in Swift holds Int and Double values, while one from JSONSerialization holds NSNumber, so ["price": 10] decoded into a Double only in the second case. Conversions stay exact (1.5 into an Int still fails), and booleans aren't numbers, as in JSONDecoder. The conversion runs only where decoding used to fail, so values of the expected type decode as fast as before.
  • Compare the types of values instead of casting them. Every value that isn't a date, data or URL went through three dynamic casts to find that out. Comparing type identifiers makes encoding 9% faster, 5% for small values.
  • Add decimal strategies. Decimal encodes itself as {exponent, isCompact, isNegative, length, mantissa}, which nothing outside Swift reads, while JSONEncoder writes a number. DictionaryEncoder(decimalEncodingStrategy: .number) keeps the Decimal as is. decimalDecodingStrategy takes the set of forms to accept, so data in both forms can be read during a migration. The defaults keep today's behavior. Telling decimals apart costs about 0.5% when encoding.
  • Support Int128 and UInt128. Encoding them threw EncodingError.invalidValue from the standard library's default implementation, and decoding failed. The containers keep them as is, like the other integers, without checking any other value for them.
  • Format ISO 8601 dates with a shared format style. Every date created a new ISO8601DateFormatter: about 85 µs to format and 124 µs to parse one date on an M4 Mac. A shared Date.ISO8601FormatStyle takes 0.15 and 0.4 µs. The output is byte for byte the same (checked on 1.3 million dates) and so are the accepted strings (checked on 600,000). .iso8601(style: .formatStyle) adds the behavior of JSONEncoder and JSONDecoder: fractions of a second are dropped when encoding and kept when decoding. .iso8601 keeps working as before.

The first change affects observable behavior: values that failed to decode now succeed, and an infinite Double decoded into a Float reports dataCorrupted instead of typeMismatch. Happy to split it out or drop it.

Measured as retired instructions in release builds on an M4, alternating A/B runs; differences within ±0.3% are noise.

@letko-dmitry
letko-dmitry marked this pull request as ready for review October 5, 2026 15:34
@letko-dmitry
letko-dmitry marked this pull request as draft October 5, 2026 15:49
@letko-dmitry
letko-dmitry force-pushed the feature/value-formats branch from 7825c0d to 653b284 Compare October 5, 2026 18:26
@letko-dmitry
letko-dmitry marked this pull request as ready for review October 5, 2026 18:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant