Add parse bin int, fixes #8018 (#8020)

* clarify `parseHexInt`, `parseOctInt` docstring and exception msgs

* add `parseBinInt` based on `parseutil.parseBin` implementation

Adds a `parseBinInt`, which parses a binary integer string and returns
it as an integer. This is based on the implementation of
`parseutil.parseBin`, removing the unnecessary parts.

* add tests for all `parse(Hex|Oct|Bin)Int` procs

* replace `parse*Int` proc impls by call to parseutil procs

Replaces the `parse(Hex|Oct|Bin)Int` procedure implementation by calls
to the `parseutil` procs, which receive a mutable argument.

Has the main advantage that the empty string as well as a "prefix
only" string, e.g. "0x" counts as an invalid integer.

Also moves the `parseOctInt` proc further up in the file so that all
`parse` procs are below one another.

* replace `var L` by `let L` in `parse` procs

There's no reason for the usage of `var` here.

* add `maxLen` optional arg for `parseutil.parse(Oct|Bin)`

Plus small change to test cases.

* update changelog about `parse*Int` procs

* fix `rejectParse` template in `tstrutils`

* make sure only `s.len` chars are parsed, if `maxLen+start` > s.len

Fixes a previous bug in `parseHex` (and now affected `parseOct` and
`parseBin`), which allowed to set `start + maxLen` to be larger than
the strings length. This resulted in an out of bounds access.

* move `parse*Int` proc change to breaking changes, add double `
This commit is contained in:
Vindaar 2018-06-13 19:32:12 +02:00 • committed by Varriount
commit e80be6173d
4 changed files with 111 additions and 53 deletions

View file

@ -47,12 +47,14 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
## discard parseHex("0x38", value)
## assert value == -200
##
## If 'maxLen==0' the length of the hexadecimal number has no
## upper bound. Not more than ```maxLen`` characters are parsed.
## If ``maxLen == 0`` the length of the hexadecimal number has no upper bound.
## Else no more than ``start + maxLen`` characters are parsed, up to the
## length of the string.
var i = start
var foundDigit = false
let last = if maxLen == 0: s.len else: i+maxLen
if i+1 < last and s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
# get last index based on minimum `start + maxLen` or `s.len`
let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
if i+1 < last and s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
elif i < last and s[i] == '#': inc(i)
while i < last:
case s[i]
@ -70,14 +72,20 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
inc(i)
if foundDigit: result = i-start
proc parseOct*(s: string, number: var int, start = 0): int {.
proc parseOct*(s: string, number: var int, start = 0, maxLen = 0): int {.
rtl, extern: "npuParseOct", noSideEffect.} =
## parses an octal number and stores its value in ``number``. Returns
## Parses an octal number and stores its value in ``number``. Returns
## the number of the parsed characters or 0 in case of an error.
##
## If ``maxLen == 0`` the length of the octal number has no upper bound.
## Else no more than ``start + maxLen`` characters are parsed, up to the
## length of the string.
var i = start
var foundDigit = false
if i+1 < s.len and s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
while i < s.len:
# get last index based on minimum `start + maxLen` or `s.len`
let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
if i+1 < last and s[i] == '0' and (s[i+1] in {'o', 'O'}): inc(i, 2)
while i < last:
case s[i]
of '_': discard
of '0'..'7':
@ -87,14 +95,20 @@ proc parseOct*(s: string, number: var int, start = 0): int {.
inc(i)
if foundDigit: result = i-start
proc parseBin*(s: string, number: var int, start = 0): int {.
proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int {.
rtl, extern: "npuParseBin", noSideEffect.} =
## parses an binary number and stores its value in ``number``. Returns
## Parses an binary number and stores its value in ``number``. Returns
## the number of the parsed characters or 0 in case of an error.
##
## If ``maxLen == 0`` the length of the binary number has no upper bound.
## Else no more than ``start + maxLen`` characters are parsed, up to the
## length of the string.
var i = start
var foundDigit = false
if i+1 < s.len and s[i] == '0' and (s[i+1] == 'b' or s[i+1] == 'B'): inc(i, 2)
while i < s.len:
# get last index based on minimum `start + maxLen` or `s.len`
let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
if i+1 < last and s[i] == '0' and (s[i+1] in {'b', 'B'}): inc(i, 2)
while i < last:
case s[i]
of '_': discard
of '0'..'1':