Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/jsony.nim
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,12 @@ proc parseHook*(s: string, i: var int, v: var SomeUnsignedInt) =
v2: uint64 = 0
startI = i
while i < s.len and s[i] in {'0'..'9'}:
v2 = v2 * 10 + (s[i].ord - '0'.ord).uint64
let digit = (s[i].ord - '0'.ord).uint64
# Reject numbers that do not fit the target type instead of silently
# wrapping modulo (unsigned conversions are unchecked). See issue #109.
if v2 > (uint64(high(type(v))) - digit) div 10:
error("Number type to small to contain the number.", i)
v2 = v2 * 10 + digit
inc i
if startI == i:
error("Number expected.", i)
Expand All @@ -123,14 +128,20 @@ proc parseHook*(s: string, i: var int, v: var SomeSignedInt) =
var v2: uint64
inc i
parseHook(s, i, v2)
v = -type(v)(v2)
# The negative range extends one past high(T) (e.g. int8 reaches -128).
# Range-check explicitly; release builds do not raise. See issue #109.
if v2 > uint64(high(type(v))) + 1:
error("Number type to small to contain the number.", i)
elif v2 == uint64(high(type(v))) + 1:
v = low(type(v))
else:
v = -type(v)(v2)
else:
var v2: uint64
parseHook(s, i, v2)
try:
v = type(v)(v2)
except:
if v2 > uint64(high(type(v))):
error("Number type to small to contain the number.", i)
v = type(v)(v2)

proc parseHook*(s: string, i: var int, v: var SomeFloat) =
## Will parse float32 and float64.
Expand Down
15 changes: 15 additions & 0 deletions tests/test_numbers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,18 @@ block:
@["hi", "bye", "maybe"]
doAssert """[["hi", "bye"], ["maybe"], []]""".fromJson(seq[seq[string]]) ==
@[@["hi", "bye"], @["maybe"], @[]]

block:
# Out-of-range integers must raise instead of silently wrapping/truncating.
# See https://github.com/treeform/jsony/issues/109
doAssertRaises(JsonError): discard "256".fromJson(uint8)
doAssertRaises(JsonError): discard "99999999999999999999999999".fromJson(uint64)
doAssertRaises(JsonError): discard "128".fromJson(int8)
doAssertRaises(JsonError): discard "-129".fromJson(int8)
doAssertRaises(JsonError): discard "300".fromJson(uint8)
# Boundary values still parse correctly.
doAssert "255".fromJson(uint8) == 255'u8
doAssert "127".fromJson(int8) == 127'i8
doAssert "-128".fromJson(int8) == -128'i8
doAssert "65535".fromJson(uint16) == 65535'u16
doAssert "18446744073709551615".fromJson(uint64) == 18446744073709551615'u64