[PR] Bound a decimal by the digits it denotes, not just its characters [poi-xmlbeans]
pjfanning (via GitHub) <[email protected]>
| Newsgroups | gmane.comp.jakarta.poi.devel |
|---|---|
| Message-ID | <[email protected]> |
pjfanning opened a new pull request, #98:
URL: https://github.com/apache/poi-xmlbeans/pull/98
Follow-up to #97. That PR hardened the paths that *consume* an oversized decimal; this one stops one being created from a document in the first place.
### The gap
`maxNumberOfCharsForNumbers` is documented as bounding the size of numbers read out of a document, but it only checked the length of the lexical value. An exponent is a handful of characters denoting arbitrarily many digits:
```
parse <xml-fragment>1E+2000000000</xml-fragment> as XmlDecimal, default options
-> stored, scale=-2000000000 precision=1
```
13 characters, under any limit, producing a `BigDecimal` that is trivial to hold and catastrophic to expand. No option is needed to get there: `Factory.parse` doesn't validate on set, so `validateLexical` — the check that rejects an exponent — never runs. `setLoadAllowDecimalExponent(true)` reaches it by the documented route.
### Why the check is safe
Exponent notation isn't part of the `xsd:decimal` lexical space at all; it belongs to `float`/`double`. XSD leaves the value space unbounded and only requires that a minimally conforming processor support at least 18 digits (Part 2 §3.2.3), so an implementation limit well above that is conformant either way.
The consequence that matters here: for conformant input the digit count is bounded by the length of the lexical value, so a bound on digits can never reject something the existing length check accepts. It fires only for exponent forms that were never valid `xsd:decimal` — which is why `testParseAsBigDecimalAcceptsPlainValues` can assert the plain forms are untouched.
### The change
Applied in `MathUtil.parseAsBigDecimal`, the single point that both `JavaDecimalHolder.set_text` and `JavaDecimalHolderEx.set_text` go through, along with `validateLexical`'s exponent branch and `Validator`. The bound is the length of the value written out without an exponent, which is the form `xsd:decimal` allows and therefore the form the limit is meant to bound:
```java
return scale <= 0
? (long) precision - scale // integer digits
: Math.max(precision, (long) scale + 1) + 1; // digits either side of the point
```
Both directions are covered — `1E-2000000000` would otherwise make `printDecimal` allocate a two-billion-character `StringBuilder`.
A raised limit still raises the bound: `1E+2000` is rejected at the default 1024 and accepted at `maxChars(4096)`.
### Still open
`setBigDecimalValue` remains unbounded, so a value like this can still be constructed programmatically. That's why #97's guards in `value_hash_code` and `to_BigInteger` stay: they now defend a path that a document can no longer reach, rather than the one it could.
### Tests
Two in `TestMathUtil` (the bound applies to denoted digits, in both exponent directions and under raised and lowered limits; plain lexical forms are unaffected) and two in `MaxNumberOfCharsTest` (a document is rejected with and without the exponent option, and an exponent within the limit still parses).
Full suite: 3086 tests, 0 failures.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]