mORMot2 DailyDaily read of upstream commits

Tiny decimals are repaired and TimeSpan text becomes bidirectional

The numeric parser work from the previous edition needed two correctness follow-ups: GetExtended and GetNumericVariantFromJson could lose significant digits after a long run of fractional zeros. Their implementations were then simplified, optimized and covered with boundary tests, including Delphi 7 and Delphi Win32 paths. Microsoft TimeSpan support now parses text back to signed 64-bit ticks and accepts safe CSV or JSON delimiters.

TL;DR

  • Land on 2.4.16844; builds 2.4.16828 through 2.4.16839 contain incomplete or still-changing numeric parser work.
  • GetExtended and GetNumericVariantFromJson now preserve significant digits in values such as 0.00000000000000000123 instead of silently losing them.
  • GetNumericVariantFromJson now uses a negative Int64 accumulator, restoring Delphi 7 compatibility while retaining Int64, Currency and double bounds.
  • TextToTimeSpan and TextToTimeSpanBuffer parse Microsoft TimeSpan text into signed 100 ns ticks at millisecond resolution.
  • TSynSystemTime.FromTimeSpan has a pointer-based overload and the final parser accepts NUL, whitespace, comma, semicolon, apostrophe or quote delimiters.
  • Delphi Win32 receives shift-and-add paths for slow 64-bit multiplication; other targets retain ordinary multiplication.

Upgrade advice

Take build 2.4.16844 as a complete unit. Do not stop between 2.4.16828 and 2.4.16839: builds 16836 and 16837 repair tiny fractional values, then builds 16838 and 16839 rewrite the same parser paths before build 16840 completes their regression coverage. Applications that parse small scientific or financial values should rerun boundary tests after upgrading.

Build2.4.16828 → 2.4.16844
Commits14
Lines+518 -338
Read4 min

14 of 14 entries · 14 commits upstream

Stories
# Entry Category Units
1 High JSON variants keep significant digits after long fractional zeros GetNumericVariantFromJson now skips leading zero digits after the decimal point without consuming its significant-digit budget. The fractional exponent is still decremented for every skipped zero. Fixes mormot.core.variants ca07dda2+12-8

core: fixed GetNumericVariantFromJson() long-standing issue

Impact

Backends parsing tiny JSON numbers such as 0.00000000000000000123 no longer silently lose the non-zero digits or return the wrong numeric variant. This is a data-correctness fix for Delphi and FPC.

Public API

  • GetNumericVariantFromJsonbehaviour-changed — Leading fractional zeros no longer consume significant-digit capacity.

Arnaud Bouchez · 16:40 UTC · view the commit on GitHub →

2 High GetExtended preserves tiny non-zero fractional values mormot.core.text changes GetExtended so zero digits immediately after a decimal point adjust the scale without consuming the 19-digit accumulator budget until the first significant digit appears. Fixes mormot.core.text 9e649966+8-2

core: fixed GetExtended() long-standing issue

Impact

Delphi and FPC code parsing small scientific or financial decimal strings no longer gets a silently truncated value after many fractional zeros.

Public API

  • GetExtendedbehaviour-changed — Long runs of fractional zeros no longer discard following significant digits.

Arnaud Bouchez · 16:39 UTC · view the commit on GitHub →

3 Medium TSynSystemTime gains a parser for textual TimeSpan values mormot.core.datetime adds MAX_TIMESPAN and an overloaded TSynSystemTime.FromTimeSpan that parses day, time and optional fractional fields from PUtf8Char. It returns the ending pointer and exposes day count and sign separately. New features mormot.core.datetime 9b3f2566+107-4

core: new TSynSystemTime.FromTimeSpan() overload

Impact

Low-level Delphi and FPC parsers can decode the same compact format emitted by TimeSpanToText. Invalid day or clock ranges return nil and clear the record.

Worth a look

Public API

  • MAX_TIMESPANadded — Maximum whole-day count representable by a signed 64-bit TimeSpan.
  • TSynSystemTime.FromTimeSpan(PUtf8Char, PtrInt, PtrInt)added — Parses compact TimeSpan text and returns its end pointer, days and sign.

Arnaud Bouchez · 20:10 UTC · view the commit on GitHub →

4 Medium Microsoft TimeSpan text can be converted back to ticks mormot.core.datetime adds TextToTimeSpanBuffer and TextToTimeSpan. They combine parsed days, hours, minutes, seconds and milliseconds into a signed Int64 count of Microsoft 100 ns ticks. New features mormot.core.datetime b6bef042+29-2

core: new TextToTimeSpan/TextToTimeSpanBuffer() functions

Impact

Delphi and FPC backends can round-trip directory or .NET duration text without custom parsing. Digits below millisecond precision are accepted but deliberately discarded.

Worth a look

Public API

  • TextToTimeSpanBufferadded — Returns the first byte after a parsed TimeSpan or nil on error.
  • TextToTimeSpanadded — Converts RawUtf8 TimeSpan text into signed 100 ns ticks.

Arnaud Bouchez · 20:11 UTC · view the commit on GitHub →

5 Medium TimeSpan parsing stops cleanly at common data delimiters mormot.core.datetime refines TSynSystemTime.FromTimeSpan(PUtf8Char, days, neg) so a parsed value may end at NUL, whitespace, comma, semicolon, apostrophe or double quote. Fractional seconds still require at least three digits. New features mormot.core.datetime a2908ede+7-6

core: refined TextToTimeSpanBuffer() function

Impact

Delphi and FPC parsers can consume a TimeSpan directly from CSV, JSON-like or tokenized buffers and continue at the returned delimiter. This broadens accepted input for APIs introduced in the same batch.

Worth a look

Public API

  • TSynSystemTime.FromTimeSpan(PUtf8Char, PtrInt, PtrInt)behaviour-changed — Accepts NUL, whitespace and common CSV or JSON delimiters after a valid value.

Arnaud Bouchez · 20:19 UTC · view the commit on GitHub →

6 Medium GetInteger streamlines signed bounds on 32-bit and 64-bit targets mormot.core.base replaces pointer-distance digit counting with a countdown, represents the sign as PtrUInt and separates explicit 32-bit overflow checks from the 64-bit nineteenth-digit bound. Leading spaces, signs and zero trimming remain supported. Performance mormot.core.base b1187b83+27-30

core: refactored/optimized GetInteger() after PR #579

Impact

Delphi and FPC callers keep the same GetInteger contract and signed limits. The change is an implementation optimization following the previous overflow fix.

Arnaud Bouchez · 07:36 UTC · PR #579 · view the commit on GitHub →

7 Medium Slow 64-bit multiplication paths are limited to Delphi Win32 Core integer, YAML, Currency and HTTP range parsers now use shift-and-add multiplication by ten only under HASSLOWMUL64. Other targets use native multiplication, and numeric output assertions are normalized. Performance mormot.core.base+3 33638087+16-27

core: optimize Delphi Win32 HASSLOWMUL64 path

Impact

Delphi Win32 keeps the multiplication workaround while current Delphi and FPC targets avoid unnecessary shift-and-add sequences. Parsed values and public APIs are unchanged.

Arnaud Bouchez · 12:27 UTC · view the commit on GitHub →

8 Medium JSON numeric variants regain Delphi 7 compatibility mormot.core.variants rewrites GetNumericVariantFromJson around a negative Int64 accumulator instead of UInt64. Integer, Currency and double output paths are simplified while preserving grammar and range checks introduced by PR #579. Compiler & platform mormot.core.variants 41f703b6+92-114

core: rewritten GetNumericVariantFromJson()

Impact

Delphi 7 can compile the hardened parser again. Current Delphi and FPC backends keep the same variant selection and boundary behavior with a shorter common path.

Public API

  • GetNumericVariantFromJsonbehaviour-changed — Internal accumulation changes to signed negative Int64, restoring Delphi 7 support.

Arnaud Bouchez · 16:41 UTC · view the commit on GitHub →

9 Medium GetExtended finalizes bounded scaling without changing its API mormot.core.text reorganizes GetExtended scaling into fast -31..31, very small and very large paths. It preserves explicit underflow and overflow checks and applies the sign after the bounded multiplication. Under the hood mormot.core.text 30e95e50+30-33

core: refactored GetExtended() after #579

Impact

Delphi and FPC callers keep the same API. The final implementation reduces common-case work while retaining the corrected tiny-decimal and extreme-value behavior.

Arnaud Bouchez · 16:43 UTC · PR #579 · view the commit on GitHub →

10 Low GetExtended reduces per-digit and sign overhead mormot.core.text caches each decimal digit as an integer for its bounds check and accumulation, then applies a direct unary minus instead of multiplying by a negative power-of-ten constant. Performance mormot.core.text 5bca2b43+6-5

core: optimized GetExtended() after last PR

Impact

No application code changes. GetExtended does slightly less work on Delphi and FPC while preserving the parser behavior from the preceding fix.

Arnaud · 09:24 UTC · view the commit on GitHub →

11 Low GetExtended simplifies limits and special-value handling mormot.core.text consolidates NaN, Infinity and error exits, replaces literal Int64 bounds with a compact comparison, uses HASSLOWMUL64 for accumulation and caps parsed exponents before PtrInt arithmetic can overflow. Under the hood mormot.core.text 19d03c62+26-39

core: another pass on GetExtended() after #579

Impact

No public API change. Delphi and FPC retain the parser bounds added previously with a shorter implementation and an architecture-friendly exponent guard.

Arnaud Bouchez · 12:43 UTC · PR #579 · view the commit on GitHub →

12 Low Int64 bounds are centralized across parsers and storage mormot.core.base publishes MAX_INT64 and MAX_INT64_DIV10. GetExtended, HTTP byte-range parsing, OpenSSL constants and ORM database-ID limits reuse them, with GetNextRange retaining its High(Int64) clamp. Under the hood mormot.core.base+4 a5b75dde+13-11

core: refactored GetNextRange() and other Int64 clamp code

Impact

No application behavior changes are visible in the diff. Delphi and FPC code may reuse the new public constants instead of repeating signed 64-bit limit expressions.

Public API

  • MAX_INT64added — Public alias for High(Int64).
  • MAX_INT64_DIV10added — Public precomputed High(Int64) div 10 limit.

Arnaud Bouchez · 16:37 UTC · view the commit on GitHub →

13 Low TimeSpan round trips and malformed inputs gain boundary tests test.core.base checks TimeSpanToText against TextToTimeSpanBuffer for zero, signed values, day boundaries, millisecond truncation and Int64 extremes. It also rejects incomplete fields, out-of-range hours, minutes and seconds, unsupported plus signs and malformed fractions. Tests 77e2295f+89-25

tests: enhanced TimeSpan validation coverage

Impact

No application code changes. Delphi and FPC builds gain regression coverage for the new parser and its millisecond-resolution contract.

Arnaud Bouchez · 20:11 UTC · view the commit on GitHub →

14 Low Numeric limits and tiny fractions gain cross-backend tests test.core.base expands Int64, Currency, double, malformed-number and tiny-fraction assertions. It checks textual double output across TSynExtended layouts and adjusts overflow tests for supported Delphi and FPC backends. Tests 1cff873b+56-32

tests: fixed and completed numerical conversion coverage

Impact

No application code changes. The stabilized parser behavior is now exercised across compiler-specific floating-point representations.

Arnaud Bouchez · 16:44 UTC · view the commit on GitHub →

Upstream activity commits per day · last 60 days
Commits977
Active days44/60
Busiest2026-09-30
Peak60
2026-08-15: 0 commits2026-08-16: 0 commits2026-08-17: 0 commits2026-08-18: 0 commits2026-08-19: 0 commits2026-08-20: 0 commits2026-08-21: 0 commits2026-08-22: 0 commits2026-08-23: 0 commits2026-08-24: 0 commits2026-08-25: 0 commits2026-08-26: 0 commits2026-08-27: 26 commits2026-08-28: 11 commits2026-08-29: 10 commits2026-08-30: 1 commit2026-08-31: 23 commits2026-09-01: 0 commits2026-09-02: 20 commits2026-09-03: 29 commits2026-09-04: 35 commits2026-09-05: 13 commits2026-09-06: 0 commits2026-09-07: 0 commits2026-09-08: 23 commits2026-09-09: 10 commits2026-09-10: 17 commits2026-09-11: 23 commits2026-09-12: 14 commits2026-09-13: 9 commits2026-09-14: 7 commits2026-09-15: 4 commits2026-09-16: 24 commits2026-09-17: 26 commits2026-09-18: 21 commits2026-09-19: 59 commits2026-09-20: 13 commits2026-09-21: 5 commits2026-09-22: 34 commits2026-09-23: 17 commits2026-09-24: 57 commits2026-09-25: 22 commits2026-09-26: 26 commits2026-09-27: 0 commits2026-09-28: 2 commits2026-09-29: 19 commits2026-09-30: 60 commits2026-10-01: 26 commits2026-10-02: 30 commits2026-10-03: 32 commits2026-10-04: 15 commits2026-10-05: 1 commit2026-10-06: 26 commits2026-10-07: 37 commits2026-10-08: 27 commits2026-10-09: 35 commits2026-10-10: 33 commits2026-10-11: 9 commits2026-10-12: 11 commits2026-10-13: 35 commits
2026-08-152026-10-13