mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-17 21:12:42 +00:00
bugfix: strutils.find was broken for strings with uneven number of chars
For some reason, the problem was manifesting only inside the VM, it was detecting an attempt to read past the string end (i.e. the formerly accessible null byte). To catch such errors, strutils now performs static tests too. I've solved the problem by re-implementing the Boyer-Moore algotihm in a cleaner way and I took the opportunity to make some other optimisations to strutils.
This commit is contained in:
committed by
Andreas Rumpf
parent
cf13c5fba4
commit
70ec344bbf
@@ -1262,21 +1262,20 @@ proc initSkipTable*(a: var SkipTable, sub: string)
|
||||
{.noSideEffect, rtl, extern: "nsuInitSkipTable".} =
|
||||
## Preprocess table `a` for `sub`.
|
||||
let m = len(sub)
|
||||
let m1 = m + 1
|
||||
var i = 0
|
||||
while i <= 0xff-7:
|
||||
a[chr(i + 0)] = m1
|
||||
a[chr(i + 1)] = m1
|
||||
a[chr(i + 2)] = m1
|
||||
a[chr(i + 3)] = m1
|
||||
a[chr(i + 4)] = m1
|
||||
a[chr(i + 5)] = m1
|
||||
a[chr(i + 6)] = m1
|
||||
a[chr(i + 7)] = m1
|
||||
a[chr(i + 0)] = m
|
||||
a[chr(i + 1)] = m
|
||||
a[chr(i + 2)] = m
|
||||
a[chr(i + 3)] = m
|
||||
a[chr(i + 4)] = m
|
||||
a[chr(i + 5)] = m
|
||||
a[chr(i + 6)] = m
|
||||
a[chr(i + 7)] = m
|
||||
i += 8
|
||||
|
||||
for i in 0..m-1:
|
||||
a[sub[i]] = m-i
|
||||
for i in 0 ..< m - 1:
|
||||
a[sub[i]] = m - 1 - i
|
||||
|
||||
proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0): int
|
||||
{.noSideEffect, rtl, extern: "nsuFindStrA".} =
|
||||
@@ -1284,18 +1283,29 @@ proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0):
|
||||
## If `last` is unspecified, it defaults to `s.high`.
|
||||
##
|
||||
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||
|
||||
let
|
||||
last = if last==0: s.high else: last
|
||||
m = len(sub)
|
||||
n = last + 1
|
||||
# search:
|
||||
var j = start
|
||||
while j <= n - m:
|
||||
block match:
|
||||
for k in 0..m-1:
|
||||
if sub[k] != s[k+j]: break match
|
||||
return j
|
||||
inc(j, a[s[j+m]])
|
||||
sLen = last - start + 1
|
||||
subLast = sub.len - 1
|
||||
|
||||
if subLast == -1:
|
||||
# this was an empty needle string,
|
||||
# we count this as match in the first possible position:
|
||||
return start
|
||||
|
||||
# This is an implementation of the Boyer-Moore Horspool algorithms
|
||||
# https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm
|
||||
var skip = start
|
||||
|
||||
while last - skip >= subLast:
|
||||
var i = subLast
|
||||
while s[skip + i] == sub[i]:
|
||||
if i == 0:
|
||||
return skip
|
||||
dec i
|
||||
inc skip, a[s[skip + subLast]]
|
||||
|
||||
return -1
|
||||
|
||||
when not (defined(js) or defined(nimdoc) or defined(nimscript)):
|
||||
@@ -1455,23 +1465,41 @@ proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
|
||||
proc replace*(s, sub: string, by = ""): string {.noSideEffect,
|
||||
rtl, extern: "nsuReplaceStr".} =
|
||||
## Replaces `sub` in `s` by the string `by`.
|
||||
var a {.noinit.}: SkipTable
|
||||
result = ""
|
||||
initSkipTable(a, sub)
|
||||
let last = s.high
|
||||
var i = 0
|
||||
while true:
|
||||
let j = find(a, s, sub, i, last)
|
||||
if j < 0: break
|
||||
add result, substr(s, i, j - 1)
|
||||
let subLen = sub.len
|
||||
if subLen == 0:
|
||||
for c in s:
|
||||
add result, by
|
||||
add result, c
|
||||
add result, by
|
||||
if sub.len == 0:
|
||||
if i < s.len: add result, s[i]
|
||||
i = j + 1
|
||||
else:
|
||||
i = j + sub.len
|
||||
# copy the rest:
|
||||
add result, substr(s, i)
|
||||
return
|
||||
elif subLen == 1:
|
||||
# when the pattern is a single char, we use a faster
|
||||
# char-based search that doesn't need a skip table:
|
||||
var c = sub[0]
|
||||
let last = s.high
|
||||
var i = 0
|
||||
while true:
|
||||
let j = find(s, c, i, last)
|
||||
if j < 0: break
|
||||
add result, substr(s, i, j - 1)
|
||||
add result, by
|
||||
i = j + subLen
|
||||
# copy the rest:
|
||||
add result, substr(s, i)
|
||||
else:
|
||||
var a {.noinit.}: SkipTable
|
||||
initSkipTable(a, sub)
|
||||
let last = s.high
|
||||
var i = 0
|
||||
while true:
|
||||
let j = find(a, s, sub, i, last)
|
||||
if j < 0: break
|
||||
add result, substr(s, i, j - 1)
|
||||
add result, by
|
||||
i = j + subLen
|
||||
# copy the rest:
|
||||
add result, substr(s, i)
|
||||
|
||||
proc replace*(s: string, sub, by: char): string {.noSideEffect,
|
||||
rtl, extern: "nsuReplaceChar".} =
|
||||
@@ -1492,6 +1520,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
|
||||
## Each occurrence of `sub` has to be surrounded by word boundaries
|
||||
## (comparable to ``\\w`` in regular expressions), otherwise it is not
|
||||
## replaced.
|
||||
if sub.len == 0: return s
|
||||
const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
|
||||
var a {.noinit.}: SkipTable
|
||||
result = ""
|
||||
@@ -2326,236 +2355,243 @@ proc removePrefix*(s: var string, prefix: string) {.
|
||||
s.delete(0, prefix.len - 1)
|
||||
|
||||
when isMainModule:
|
||||
doAssert align("abc", 4) == " abc"
|
||||
doAssert align("a", 0) == "a"
|
||||
doAssert align("1232", 6) == " 1232"
|
||||
doAssert align("1232", 6, '#') == "##1232"
|
||||
proc nonStaticTests =
|
||||
doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
|
||||
doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235."
|
||||
doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
|
||||
doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
|
||||
doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
|
||||
["1,0e-11", "1,0e-011"]
|
||||
# bug #6589
|
||||
doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
|
||||
|
||||
doAssert alignLeft("abc", 4) == "abc "
|
||||
doAssert alignLeft("a", 0) == "a"
|
||||
doAssert alignLeft("1232", 6) == "1232 "
|
||||
doAssert alignLeft("1232", 6, '#') == "1232##"
|
||||
doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
|
||||
doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
|
||||
|
||||
let
|
||||
inp = """ this is a long text -- muchlongerthan10chars and here
|
||||
it goes"""
|
||||
outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
|
||||
doAssert wordWrap(inp, 10, false) == outp
|
||||
block: # formatSize tests
|
||||
doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
|
||||
doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
|
||||
doAssert formatSize(4096) == "4KiB"
|
||||
doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
|
||||
doAssert formatSize(4096, includeSpace=true) == "4 KiB"
|
||||
doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
|
||||
|
||||
let
|
||||
longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow"""
|
||||
longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow"
|
||||
doAssert wordWrap(longInp, 8, true) == longOutp
|
||||
block: # formatEng tests
|
||||
doAssert formatEng(0, 2, trim=false) == "0.00"
|
||||
doAssert formatEng(0, 2) == "0"
|
||||
doAssert formatEng(53, 2, trim=false) == "53.00"
|
||||
doAssert formatEng(0.053, 2, trim=false) == "53.00e-3"
|
||||
doAssert formatEng(0.053, 4, trim=false) == "53.0000e-3"
|
||||
doAssert formatEng(0.053, 4, trim=true) == "53e-3"
|
||||
doAssert formatEng(0.053, 0) == "53e-3"
|
||||
doAssert formatEng(52731234) == "52.731234e6"
|
||||
doAssert formatEng(-52731234) == "-52.731234e6"
|
||||
doAssert formatEng(52731234, 1) == "52.7e6"
|
||||
doAssert formatEng(-52731234, 1) == "-52.7e6"
|
||||
doAssert formatEng(52731234, 1, decimalSep=',') == "52,7e6"
|
||||
doAssert formatEng(-52731234, 1, decimalSep=',') == "-52,7e6"
|
||||
|
||||
doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
|
||||
doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235."
|
||||
doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
|
||||
doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
|
||||
doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
|
||||
["1,0e-11", "1,0e-011"]
|
||||
# bug #6589
|
||||
doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
|
||||
doAssert formatEng(4100, siPrefix=true, unit="V") == "4.1 kV"
|
||||
doAssert formatEng(4.1, siPrefix=true, unit="V", useUnitSpace=true) == "4.1 V"
|
||||
doAssert formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space
|
||||
doAssert formatEng(4100, siPrefix=true) == "4.1 k"
|
||||
doAssert formatEng(4.1, siPrefix=true, unit="", useUnitSpace=true) == "4.1 " # Includes space
|
||||
doAssert formatEng(4100, siPrefix=true, unit="") == "4.1 k"
|
||||
doAssert formatEng(4100) == "4.1e3"
|
||||
doAssert formatEng(4100, unit="V", useUnitSpace=true) == "4.1e3 V"
|
||||
doAssert formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 "
|
||||
# Don't use SI prefix as number is too big
|
||||
doAssert formatEng(3.1e22, siPrefix=true, unit="a", useUnitSpace=true) == "31e21 a"
|
||||
# Don't use SI prefix as number is too small
|
||||
doAssert formatEng(3.1e-25, siPrefix=true, unit="A", useUnitSpace=true) == "310e-27 A"
|
||||
|
||||
doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
|
||||
doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
|
||||
proc staticTests =
|
||||
doAssert align("abc", 4) == " abc"
|
||||
doAssert align("a", 0) == "a"
|
||||
doAssert align("1232", 6) == " 1232"
|
||||
doAssert align("1232", 6, '#') == "##1232"
|
||||
|
||||
block: # formatSize tests
|
||||
doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
|
||||
doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
|
||||
doAssert formatSize(4096) == "4KiB"
|
||||
doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
|
||||
doAssert formatSize(4096, includeSpace=true) == "4 KiB"
|
||||
doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
|
||||
doAssert alignLeft("abc", 4) == "abc "
|
||||
doAssert alignLeft("a", 0) == "a"
|
||||
doAssert alignLeft("1232", 6) == "1232 "
|
||||
doAssert alignLeft("1232", 6, '#') == "1232##"
|
||||
|
||||
doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
|
||||
"The cat eats fish."
|
||||
let
|
||||
inp = """ this is a long text -- muchlongerthan10chars and here
|
||||
it goes"""
|
||||
outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
|
||||
doAssert wordWrap(inp, 10, false) == outp
|
||||
|
||||
doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
|
||||
doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
|
||||
let
|
||||
longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow"""
|
||||
longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow"
|
||||
doAssert wordWrap(longInp, 8, true) == longOutp
|
||||
|
||||
doAssert "-lda-ldz -ld abc".replaceWord("") == "lda-ldz ld abc"
|
||||
doAssert "oo".replace("", "abc") == "abcoabcoabc"
|
||||
doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
|
||||
"The cat eats fish."
|
||||
|
||||
type MyEnum = enum enA, enB, enC, enuD, enE
|
||||
doAssert parseEnum[MyEnum]("enu_D") == enuD
|
||||
doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
|
||||
doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
|
||||
|
||||
doAssert parseEnum("invalid enum value", enC) == enC
|
||||
doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc"
|
||||
doAssert "oo".replace("", "abc") == "abcoabcoabc"
|
||||
|
||||
doAssert center("foo", 13) == " foo "
|
||||
doAssert center("foo", 0) == "foo"
|
||||
doAssert center("foo", 3, fillChar = 'a') == "foo"
|
||||
doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t"
|
||||
type MyEnum = enum enA, enB, enC, enuD, enE
|
||||
doAssert parseEnum[MyEnum]("enu_D") == enuD
|
||||
|
||||
doAssert count("foofoofoo", "foofoo") == 1
|
||||
doAssert count("foofoofoo", "foofoo", overlapping = true) == 2
|
||||
doAssert count("foofoofoo", 'f') == 3
|
||||
doAssert count("foofoofoobar", {'f','b'}) == 4
|
||||
doAssert parseEnum("invalid enum value", enC) == enC
|
||||
|
||||
doAssert strip(" foofoofoo ") == "foofoofoo"
|
||||
doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo"
|
||||
doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo"
|
||||
doAssert strip("stripme but don't strip this stripme",
|
||||
chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) ==
|
||||
" but don't strip this "
|
||||
doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
|
||||
doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
|
||||
doAssert center("foo", 13) == " foo "
|
||||
doAssert center("foo", 0) == "foo"
|
||||
doAssert center("foo", 3, fillChar = 'a') == "foo"
|
||||
doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t"
|
||||
|
||||
doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar"
|
||||
doAssert count("foofoofoo", "foofoo") == 1
|
||||
doAssert count("foofoofoo", "foofoo", overlapping = true) == 2
|
||||
doAssert count("foofoofoo", 'f') == 3
|
||||
doAssert count("foofoofoobar", {'f','b'}) == 4
|
||||
|
||||
doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab"
|
||||
doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", "PEOPLE!")) == "HELLO PEOPLE!"
|
||||
doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa"
|
||||
doAssert strip(" foofoofoo ") == "foofoofoo"
|
||||
doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo"
|
||||
doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo"
|
||||
doAssert strip("stripme but don't strip this stripme",
|
||||
chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) ==
|
||||
" but don't strip this "
|
||||
doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
|
||||
doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
|
||||
|
||||
doAssert isAlphaAscii('r')
|
||||
doAssert isAlphaAscii('A')
|
||||
doAssert(not isAlphaAscii('$'))
|
||||
doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar"
|
||||
|
||||
doAssert isAlphaAscii("Rasp")
|
||||
doAssert isAlphaAscii("Args")
|
||||
doAssert(not isAlphaAscii("$Tomato"))
|
||||
doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab"
|
||||
doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", "PEOPLE!")) == "HELLO PEOPLE!"
|
||||
doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa"
|
||||
|
||||
doAssert isAlphaNumeric('3')
|
||||
doAssert isAlphaNumeric('R')
|
||||
doAssert(not isAlphaNumeric('!'))
|
||||
doAssert isAlphaAscii('r')
|
||||
doAssert isAlphaAscii('A')
|
||||
doAssert(not isAlphaAscii('$'))
|
||||
|
||||
doAssert isAlphaNumeric("34ABc")
|
||||
doAssert isAlphaNumeric("Rad")
|
||||
doAssert isAlphaNumeric("1234")
|
||||
doAssert(not isAlphaNumeric("@nose"))
|
||||
doAssert isAlphaAscii("Rasp")
|
||||
doAssert isAlphaAscii("Args")
|
||||
doAssert(not isAlphaAscii("$Tomato"))
|
||||
|
||||
doAssert isDigit('3')
|
||||
doAssert(not isDigit('a'))
|
||||
doAssert(not isDigit('%'))
|
||||
doAssert isAlphaNumeric('3')
|
||||
doAssert isAlphaNumeric('R')
|
||||
doAssert(not isAlphaNumeric('!'))
|
||||
|
||||
doAssert isDigit("12533")
|
||||
doAssert(not isDigit("12.33"))
|
||||
doAssert(not isDigit("A45b"))
|
||||
doAssert isAlphaNumeric("34ABc")
|
||||
doAssert isAlphaNumeric("Rad")
|
||||
doAssert isAlphaNumeric("1234")
|
||||
doAssert(not isAlphaNumeric("@nose"))
|
||||
|
||||
doAssert isSpaceAscii('\t')
|
||||
doAssert isSpaceAscii('\l')
|
||||
doAssert(not isSpaceAscii('A'))
|
||||
doAssert isDigit('3')
|
||||
doAssert(not isDigit('a'))
|
||||
doAssert(not isDigit('%'))
|
||||
|
||||
doAssert isSpaceAscii("\t\l \v\r\f")
|
||||
doAssert isSpaceAscii(" ")
|
||||
doAssert(not isSpaceAscii("ABc \td"))
|
||||
doAssert isDigit("12533")
|
||||
doAssert(not isDigit("12.33"))
|
||||
doAssert(not isDigit("A45b"))
|
||||
|
||||
doAssert(isNilOrWhitespace(""))
|
||||
doAssert(isNilOrWhitespace(" "))
|
||||
doAssert(isNilOrWhitespace("\t\l \v\r\f"))
|
||||
doAssert(not isNilOrWhitespace("ABc \td"))
|
||||
doAssert isSpaceAscii('\t')
|
||||
doAssert isSpaceAscii('\l')
|
||||
doAssert(not isSpaceAscii('A'))
|
||||
|
||||
doAssert isLowerAscii('a')
|
||||
doAssert isLowerAscii('z')
|
||||
doAssert(not isLowerAscii('A'))
|
||||
doAssert(not isLowerAscii('5'))
|
||||
doAssert(not isLowerAscii('&'))
|
||||
doAssert isSpaceAscii("\t\l \v\r\f")
|
||||
doAssert isSpaceAscii(" ")
|
||||
doAssert(not isSpaceAscii("ABc \td"))
|
||||
|
||||
doAssert isLowerAscii("abcd")
|
||||
doAssert(not isLowerAscii("abCD"))
|
||||
doAssert(not isLowerAscii("33aa"))
|
||||
doAssert(isNilOrWhitespace(""))
|
||||
doAssert(isNilOrWhitespace(" "))
|
||||
doAssert(isNilOrWhitespace("\t\l \v\r\f"))
|
||||
doAssert(not isNilOrWhitespace("ABc \td"))
|
||||
|
||||
doAssert isUpperAscii('A')
|
||||
doAssert(not isUpperAscii('b'))
|
||||
doAssert(not isUpperAscii('5'))
|
||||
doAssert(not isUpperAscii('%'))
|
||||
doAssert isLowerAscii('a')
|
||||
doAssert isLowerAscii('z')
|
||||
doAssert(not isLowerAscii('A'))
|
||||
doAssert(not isLowerAscii('5'))
|
||||
doAssert(not isLowerAscii('&'))
|
||||
|
||||
doAssert isUpperAscii("ABC")
|
||||
doAssert(not isUpperAscii("AAcc"))
|
||||
doAssert(not isUpperAscii("A#$"))
|
||||
doAssert isLowerAscii("abcd")
|
||||
doAssert(not isLowerAscii("abCD"))
|
||||
doAssert(not isLowerAscii("33aa"))
|
||||
|
||||
doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"]
|
||||
doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"]
|
||||
doAssert rsplit(" foo bar ", seps=Whitespace, maxsplit=1) == @[" foo bar", ""]
|
||||
doAssert rsplit(":foo:bar", sep=':') == @["", "foo", "bar"]
|
||||
doAssert rsplit(":foo:bar", sep=':', maxsplit=2) == @["", "foo", "bar"]
|
||||
doAssert rsplit(":foo:bar", sep=':', maxsplit=3) == @["", "foo", "bar"]
|
||||
doAssert rsplit("foothebar", sep="the") == @["foo", "bar"]
|
||||
doAssert isUpperAscii('A')
|
||||
doAssert(not isUpperAscii('b'))
|
||||
doAssert(not isUpperAscii('5'))
|
||||
doAssert(not isUpperAscii('%'))
|
||||
|
||||
doAssert(unescape(r"\x013", "", "") == "\x013")
|
||||
doAssert isUpperAscii("ABC")
|
||||
doAssert(not isUpperAscii("AAcc"))
|
||||
doAssert(not isUpperAscii("A#$"))
|
||||
|
||||
doAssert join(["foo", "bar", "baz"]) == "foobarbaz"
|
||||
doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz"
|
||||
doAssert join([1, 2, 3]) == "123"
|
||||
doAssert join(@[1, 2, 3], ", ") == "1, 2, 3"
|
||||
doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"]
|
||||
doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"]
|
||||
doAssert rsplit(" foo bar ", seps=Whitespace, maxsplit=1) == @[" foo bar", ""]
|
||||
doAssert rsplit(":foo:bar", sep=':') == @["", "foo", "bar"]
|
||||
doAssert rsplit(":foo:bar", sep=':', maxsplit=2) == @["", "foo", "bar"]
|
||||
doAssert rsplit(":foo:bar", sep=':', maxsplit=3) == @["", "foo", "bar"]
|
||||
doAssert rsplit("foothebar", sep="the") == @["foo", "bar"]
|
||||
|
||||
doAssert """~~!!foo
|
||||
doAssert(unescape(r"\x013", "", "") == "\x013")
|
||||
|
||||
doAssert join(["foo", "bar", "baz"]) == "foobarbaz"
|
||||
doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz"
|
||||
doAssert join([1, 2, 3]) == "123"
|
||||
doAssert join(@[1, 2, 3], ", ") == "1, 2, 3"
|
||||
|
||||
doAssert """~~!!foo
|
||||
~~!!bar
|
||||
~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz"
|
||||
|
||||
doAssert """~~!!foo
|
||||
doAssert """~~!!foo
|
||||
~~!!bar
|
||||
~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz"
|
||||
doAssert """~~foo
|
||||
doAssert """~~foo
|
||||
~~ bar
|
||||
~~ baz""".unindent(4, "~") == "foo\n bar\n baz"
|
||||
doAssert """foo
|
||||
doAssert """foo
|
||||
bar
|
||||
baz
|
||||
""".unindent(4) == "foo\nbar\nbaz\n"
|
||||
doAssert """foo
|
||||
doAssert """foo
|
||||
bar
|
||||
baz
|
||||
""".unindent(2) == "foo\n bar\n baz\n"
|
||||
doAssert """foo
|
||||
doAssert """foo
|
||||
bar
|
||||
baz
|
||||
""".unindent(100) == "foo\nbar\nbaz\n"
|
||||
|
||||
doAssert """foo
|
||||
doAssert """foo
|
||||
foo
|
||||
bar
|
||||
""".unindent() == "foo\nfoo\nbar\n"
|
||||
|
||||
let s = " this is an example "
|
||||
let s2 = ":this;is;an:example;;"
|
||||
let s = " this is an example "
|
||||
let s2 = ":this;is;an:example;;"
|
||||
|
||||
doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
|
||||
doAssert s2.split(seps={':', ';'}) == @["", "this", "is", "an", "example", "", ""]
|
||||
doAssert s.split(maxsplit=4) == @["", "this", "is", "an", "example "]
|
||||
doAssert s.split(' ', maxsplit=1) == @["", "this is an example "]
|
||||
doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example "]
|
||||
doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
|
||||
doAssert s2.split(seps={':', ';'}) == @["", "this", "is", "an", "example", "", ""]
|
||||
doAssert s.split(maxsplit=4) == @["", "this", "is", "an", "example "]
|
||||
doAssert s.split(' ', maxsplit=1) == @["", "this is an example "]
|
||||
doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example "]
|
||||
|
||||
doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
|
||||
doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example "]
|
||||
doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example "]
|
||||
doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example "]
|
||||
doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
|
||||
doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
|
||||
doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example "]
|
||||
doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example "]
|
||||
doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example "]
|
||||
doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
|
||||
|
||||
block: # formatEng tests
|
||||
doAssert formatEng(0, 2, trim=false) == "0.00"
|
||||
doAssert formatEng(0, 2) == "0"
|
||||
doAssert formatEng(53, 2, trim=false) == "53.00"
|
||||
doAssert formatEng(0.053, 2, trim=false) == "53.00e-3"
|
||||
doAssert formatEng(0.053, 4, trim=false) == "53.0000e-3"
|
||||
doAssert formatEng(0.053, 4, trim=true) == "53e-3"
|
||||
doAssert formatEng(0.053, 0) == "53e-3"
|
||||
doAssert formatEng(52731234) == "52.731234e6"
|
||||
doAssert formatEng(-52731234) == "-52.731234e6"
|
||||
doAssert formatEng(52731234, 1) == "52.7e6"
|
||||
doAssert formatEng(-52731234, 1) == "-52.7e6"
|
||||
doAssert formatEng(52731234, 1, decimalSep=',') == "52,7e6"
|
||||
doAssert formatEng(-52731234, 1, decimalSep=',') == "-52,7e6"
|
||||
block: # startsWith / endsWith char tests
|
||||
var s = "abcdef"
|
||||
doAssert s.startsWith('a')
|
||||
doAssert s.startsWith('b') == false
|
||||
doAssert s.endsWith('f')
|
||||
doAssert s.endsWith('a') == false
|
||||
doAssert s.endsWith('\0') == false
|
||||
|
||||
doAssert formatEng(4100, siPrefix=true, unit="V") == "4.1 kV"
|
||||
doAssert formatEng(4.1, siPrefix=true, unit="V", useUnitSpace=true) == "4.1 V"
|
||||
doAssert formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space
|
||||
doAssert formatEng(4100, siPrefix=true) == "4.1 k"
|
||||
doAssert formatEng(4.1, siPrefix=true, unit="", useUnitSpace=true) == "4.1 " # Includes space
|
||||
doAssert formatEng(4100, siPrefix=true, unit="") == "4.1 k"
|
||||
doAssert formatEng(4100) == "4.1e3"
|
||||
doAssert formatEng(4100, unit="V", useUnitSpace=true) == "4.1e3 V"
|
||||
doAssert formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 "
|
||||
# Don't use SI prefix as number is too big
|
||||
doAssert formatEng(3.1e22, siPrefix=true, unit="a", useUnitSpace=true) == "31e21 a"
|
||||
# Don't use SI prefix as number is too small
|
||||
doAssert formatEng(3.1e-25, siPrefix=true, unit="A", useUnitSpace=true) == "310e-27 A"
|
||||
#echo("strutils tests passed")
|
||||
|
||||
block: # startsWith / endsWith char tests
|
||||
var s = "abcdef"
|
||||
doAssert s.startsWith('a')
|
||||
doAssert s.startsWith('b') == false
|
||||
doAssert s.endsWith('f')
|
||||
doAssert s.endsWith('a') == false
|
||||
doAssert s.endsWith('\0') == false
|
||||
nonStaticTests()
|
||||
staticTests()
|
||||
static: staticTests()
|
||||
|
||||
#echo("strutils tests passed")
|
||||
|
||||
Reference in New Issue
Block a user