diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/packages/docutils/rstgen.nim | 2 | ||||
-rw-r--r-- | lib/pure/cgi.nim | 20 | ||||
-rw-r--r-- | lib/pure/parseopt.nim | 48 | ||||
-rw-r--r-- | lib/pure/parseutils.nim | 107 | ||||
-rw-r--r-- | lib/pure/strscans.nim | 34 | ||||
-rw-r--r-- | lib/pure/strutils.nim | 383 | ||||
-rw-r--r-- | lib/pure/uri.nim | 39 |
7 files changed, 221 insertions, 412 deletions
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index ee1f0076c..1547c888d 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -774,7 +774,7 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) = Digits + Letters + WhiteSpace) let arg = getArgument(n) - isObject = arg.toLower().endsWith(".svg") + isObject = arg.toLowerAscii().endsWith(".svg") var options = "" content = "" diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim index 5de6aa487..a5a404497 100644 --- a/lib/pure/cgi.nim +++ b/lib/pure/cgi.nim @@ -97,11 +97,10 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] = var name = "" var value = "" # decode everything in one pass: - while data[i] != '\0': + while i < data.len: setLen(name, 0) # reuse memory - while true: + while i < data.len: case data[i] - of '\0': break of '%': var x = 0 handleHexChar(data[i+1], x) @@ -112,15 +111,16 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] = of '=', '&': break else: add(name, data[i]) inc(i) - if data[i] != '=': cgiError("'=' expected") + if i >= data.len or data[i] != '=': cgiError("'=' expected") inc(i) # skip '=' setLen(value, 0) # reuse memory - while true: + while i < data.len: case data[i] of '%': var x = 0 - handleHexChar(data[i+1], x) - handleHexChar(data[i+2], x) + if i+2 < data.len: + handleHexChar(data[i+1], x) + handleHexChar(data[i+2], x) inc(i, 2) add(value, chr(x)) of '+': add(value, ' ') @@ -128,9 +128,9 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] = else: add(value, data[i]) inc(i) yield (name.TaintedString, value.TaintedString) - if data[i] == '&': inc(i) - elif data[i] == '\0': break - else: cgiError("'&' expected") + if i < data.len: + if data[i] == '&': inc(i) + else: cgiError("'&' expected") iterator decodeData*(allowedMethods: set[RequestMethod] = {methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] = diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index ffb69a72b..c1a6161f9 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -57,26 +57,26 @@ type {.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].} proc parseWord(s: string, i: int, w: var string, - delim: set[char] = {'\x09', ' ', '\0'}): int = + delim: set[char] = {'\x09', ' '}): int = result = i - if s[result] == '\"': + if result < s.len and s[result] == '\"': inc(result) - while not (s[result] in {'\0', '\"'}): + while result < s.len and s[result] != '\"': add(w, s[result]) inc(result) - if s[result] == '\"': inc(result) + if result < s.len and s[result] == '\"': inc(result) else: - while not (s[result] in delim): + while result < s.len and s[result] notin delim: add(w, s[result]) inc(result) when declared(os.paramCount): proc quote(s: string): string = - if find(s, {' ', '\t'}) >= 0 and s[0] != '"': + if find(s, {' ', '\t'}) >= 0 and s.len > 0 and s[0] != '"': if s[0] == '-': result = newStringOfCap(s.len) - var i = parseWord(s, 0, result, {'\0', ' ', '\x09', ':', '='}) - if s[i] in {':','='}: + var i = parseWord(s, 0, result, {' ', '\x09', ':', '='}) + if i < s.len and s[i] in {':','='}: result.add s[i] inc i result.add '"' @@ -144,43 +144,45 @@ proc handleShortOption(p: var OptParser) = add(p.key.string, p.cmd[i]) inc(i) p.inShortState = true - while p.cmd[i] in {'\x09', ' '}: + while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i) p.inShortState = false - if p.cmd[i] in {':', '='} or card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal: - if p.cmd[i] in {':', '='}: + if i < p.cmd.len and p.cmd[i] in {':', '='} or + card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal: + if i < p.cmd.len and p.cmd[i] in {':', '='}: inc(i) p.inShortState = false - while p.cmd[i] in {'\x09', ' '}: inc(i) + while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i) i = parseWord(p.cmd, i, p.val.string) - if p.cmd[i] == '\0': p.inShortState = false + if i >= p.cmd.len: p.inShortState = false p.pos = i proc next*(p: var OptParser) {.rtl, extern: "npo$1".} = ## parses the first or next option; ``p.kind`` describes what token has been ## parsed. ``p.key`` and ``p.val`` are set accordingly. var i = p.pos - while p.cmd[i] in {'\x09', ' '}: inc(i) + while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i) p.pos = i setLen(p.key.string, 0) setLen(p.val.string, 0) if p.inShortState: handleShortOption(p) return - case p.cmd[i] - of '\0': + if i >= p.cmd.len: p.kind = cmdEnd - of '-': + return + if p.cmd[i] == '-': inc(i) - if p.cmd[i] == '-': + if i < p.cmd.len and p.cmd[i] == '-': p.kind = cmdLongOption inc(i) - i = parseWord(p.cmd, i, p.key.string, {'\0', ' ', '\x09', ':', '='}) - while p.cmd[i] in {'\x09', ' '}: inc(i) - if p.cmd[i] in {':', '='} or len(p.longNoVal) > 0 and p.key.string notin p.longNoVal: - if p.cmd[i] in {':', '='}: + i = parseWord(p.cmd, i, p.key.string, {' ', '\x09', ':', '='}) + while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i) + if i < p.cmd.len and p.cmd[i] in {':', '='} or + len(p.longNoVal) > 0 and p.key.string notin p.longNoVal: + if i < p.cmd.len and p.cmd[i] in {':', '='}: inc(i) - while p.cmd[i] in {'\x09', ' '}: inc(i) + while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i) p.pos = parseWord(p.cmd, i, p.val.string) else: p.pos = i diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index cecc94e92..1d76234ea 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -51,9 +51,9 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {. ## upper bound. Not more than ```maxLen`` characters are parsed. var i = start var foundDigit = false - if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2) - elif s[i] == '#': inc(i) 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) + elif i < last and s[i] == '#': inc(i) while i < last: case s[i] of '_': discard @@ -76,8 +76,8 @@ proc parseOct*(s: string, number: var int, start = 0): int {. ## the number of the parsed characters or 0 in case of an error. var i = start var foundDigit = false - if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2) - while true: + 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: case s[i] of '_': discard of '0'..'7': @@ -93,8 +93,8 @@ proc parseBin*(s: string, number: var int, start = 0): int {. ## the number of the parsed characters or 0 in case of an error. var i = start var foundDigit = false - if s[i] == '0' and (s[i+1] == 'b' or s[i+1] == 'B'): inc(i, 2) - while true: + 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: case s[i] of '_': discard of '0'..'1': @@ -108,9 +108,9 @@ proc parseIdent*(s: string, ident: var string, start = 0): int = ## parses an identifier and stores it in ``ident``. Returns ## the number of the parsed characters or 0 in case of an error. var i = start - if s[i] in IdentStartChars: + if i < s.len and s[i] in IdentStartChars: inc(i) - while s[i] in IdentChars: inc(i) + while i < s.len and s[i] in IdentChars: inc(i) ident = substr(s, start, i-1) result = i-start @@ -119,11 +119,9 @@ proc parseIdent*(s: string, start = 0): string = ## Returns the parsed identifier or an empty string in case of an error. result = "" var i = start - - if s[i] in IdentStartChars: + if i < s.len and s[i] in IdentStartChars: inc(i) - while s[i] in IdentChars: inc(i) - + while i < s.len and s[i] in IdentChars: inc(i) result = substr(s, start, i-1) proc parseToken*(s: string, token: var string, validChars: set[char], @@ -134,24 +132,26 @@ proc parseToken*(s: string, token: var string, validChars: set[char], ## ## **Deprecated since version 0.8.12**: Use ``parseWhile`` instead. var i = start - while s[i] in validChars: inc(i) + while i < s.len and s[i] in validChars: inc(i) result = i-start token = substr(s, start, i-1) proc skipWhitespace*(s: string, start = 0): int {.inline.} = ## skips the whitespace starting at ``s[start]``. Returns the number of ## skipped characters. - while s[start+result] in Whitespace: inc(result) + while start+result < s.len and s[start+result] in Whitespace: inc(result) proc skip*(s, token: string, start = 0): int {.inline.} = ## skips the `token` starting at ``s[start]``. Returns the length of `token` ## or 0 if there was no `token` at ``s[start]``. - while result < token.len and s[result+start] == token[result]: inc(result) + while start+result < s.len and result < token.len and + s[result+start] == token[result]: + inc(result) if result != token.len: result = 0 proc skipIgnoreCase*(s, token: string, start = 0): int = ## same as `skip` but case is ignored for token matching. - while result < token.len and + while start+result < s.len and result < token.len and toLower(s[result+start]) == toLower(token[result]): inc(result) if result != token.len: result = 0 @@ -159,18 +159,18 @@ proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} = ## Skips all characters until one char from the set `until` is found ## or the end is reached. ## Returns number of characters skipped. - while s[result+start] notin until and s[result+start] != '\0': inc(result) + while start+result < s.len and s[result+start] notin until: inc(result) proc skipUntil*(s: string, until: char, start = 0): int {.inline.} = ## Skips all characters until the char `until` is found ## or the end is reached. ## Returns number of characters skipped. - while s[result+start] != until and s[result+start] != '\0': inc(result) + while start+result < s.len and s[result+start] != until: inc(result) proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} = ## Skips all characters while one char from the set `token` is found. ## Returns number of characters skipped. - while s[result+start] in toSkip and s[result+start] != '\0': inc(result) + while start+result < s.len and s[result+start] in toSkip: inc(result) proc parseUntil*(s: string, token: var string, until: set[char], start = 0): int {.inline.} = @@ -214,7 +214,7 @@ proc parseWhile*(s: string, token: var string, validChars: set[char], ## the number of the parsed characters or 0 in case of an error. A token ## consists of the characters in `validChars`. var i = start - while s[i] in validChars: inc(i) + while i < s.len and s[i] in validChars: inc(i) result = i-start token = substr(s, start, i-1) @@ -231,16 +231,17 @@ proc rawParseInt(s: string, b: var BiggestInt, start = 0): int = var sign: BiggestInt = -1 i = start - if s[i] == '+': inc(i) - elif s[i] == '-': - inc(i) - sign = 1 - if s[i] in {'0'..'9'}: + if i < s.len: + if s[i] == '+': inc(i) + elif s[i] == '-': + inc(i) + sign = 1 + if i < s.len and s[i] in {'0'..'9'}: b = 0 - while s[i] in {'0'..'9'}: + while i < s.len and s[i] in {'0'..'9'}: b = b * 10 - (ord(s[i]) - ord('0')) inc(i) - while s[i] == '_': inc(i) # underscores are allowed and ignored + while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored b = b * sign result = i - start {.pop.} # overflowChecks @@ -281,17 +282,17 @@ proc parseSaturatedNatural*(s: string, b: var int, start = 0): int = ## discard parseSaturatedNatural("848", res) ## doAssert res == 848 var i = start - if s[i] == '+': inc(i) - if s[i] in {'0'..'9'}: + if i < s.len and s[i] == '+': inc(i) + if i < s.len and s[i] in {'0'..'9'}: b = 0 - while s[i] in {'0'..'9'}: + while i < s.len and s[i] in {'0'..'9'}: let c = ord(s[i]) - ord('0') if b <= (high(int) - c) div 10: b = b * 10 + c else: b = high(int) inc(i) - while s[i] == '_': inc(i) # underscores are allowed and ignored + while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored result = i - start # overflowChecks doesn't work with BiggestUInt @@ -300,16 +301,16 @@ proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int = res = 0.BiggestUInt prev = 0.BiggestUInt i = start - if s[i] == '+': inc(i) # Allow - if s[i] in {'0'..'9'}: + if i < s.len and s[i] == '+': inc(i) # Allow + if i < s.len and s[i] in {'0'..'9'}: b = 0 - while s[i] in {'0'..'9'}: + while i < s.len and s[i] in {'0'..'9'}: prev = res res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt if prev > res: return 0 # overflowChecks emulation inc(i) - while s[i] == '_': inc(i) # underscores are allowed and ignored + while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored b = res result = i - start @@ -389,31 +390,31 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind, var kind: InterpolatedKind while true: var j = i - if s[j] == '$': - if s[j+1] == '{': + if j < s.len and s[j] == '$': + if j+1 < s.len and s[j+1] == '{': inc j, 2 var nesting = 0 - while true: - case s[j] - of '{': inc nesting - of '}': - if nesting == 0: - inc j - break - dec nesting - of '\0': - raise newException(ValueError, - "Expected closing '}': " & substr(s, i, s.high)) - else: discard - inc j + block curlies: + while j < s.len: + case s[j] + of '{': inc nesting + of '}': + if nesting == 0: + inc j + break curlies + dec nesting + else: discard + inc j + raise newException(ValueError, + "Expected closing '}': " & substr(s, i, s.high)) inc i, 2 # skip ${ kind = ikExpr - elif s[j+1] in IdentStartChars: + elif j+1 < s.len and s[j+1] in IdentStartChars: inc j, 2 - while s[j] in IdentChars: inc(j) + while j < s.len and s[j] in IdentChars: inc(j) inc i # skip $ kind = ikVar - elif s[j+1] == '$': + elif j+1 < s.len and s[j+1] == '$': inc j, 2 inc i # skip $ kind = ikDollar diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index f8a0c43ee..b0af149b5 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -87,7 +87,7 @@ which we then use in our scanf pattern to help us in the matching process: proc someSep(input: string; start: int; seps: set[char] = {':','-','.'}): int = # Note: The parameters and return value must match to what ``scanf`` requires result = 0 - while input[start+result] in seps: inc result + while start+result < input.len and input[start+result] in seps: inc result if scanf(input, "$w$[someSep]$w", key, value): ... @@ -231,7 +231,7 @@ is performed. var i = start var u = 0 while true: - if s[i] == '\0' or s[i] == unless: + if i >= s.len or s[i] == unless: return 0 elif s[i] == until[0]: u = 1 @@ -315,6 +315,8 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b conds.add resLen.notZero conds.add resLen + template at(s: string; i: int): char = (if i < s.len: s[i] else: '\0') + var i = 0 var p = 0 var idx = genSym(nskVar, "idx") @@ -397,7 +399,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b var nesting = 0 let start = p while true: - case pattern[p] + case pattern.at(p) of '{': inc nesting of '}': if nesting == 0: break @@ -419,7 +421,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b var nesting = 0 let start = p while true: - case pattern[p] + case pattern.at(p) of '[': inc nesting of ']': if nesting == 0: break @@ -451,10 +453,12 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b template atom*(input: string; idx: int; c: char): bool = ## Used in scanp for the matching of atoms (usually chars). - input[idx] == c + idx < input.len and input[idx] == c template atom*(input: string; idx: int; s: set[char]): bool = - input[idx] in s + idx < input.len and input[idx] in s + +template hasNxt*(input: string; idx: int): bool = idx < input.len #template prepare*(input: string): int = 0 template success*(x: int): bool = x != 0 @@ -462,7 +466,7 @@ template success*(x: int): bool = x != 0 template nxt*(input: string; idx, step: int = 1) = inc(idx, step) macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = - ## ``scanp`` is currently undocumented. + ## See top level documentation of his module of how ``scanf`` works. type StmtTriple = tuple[init, cond, action: NimNode] template interf(x): untyped = bindSym(x, brForceOpen) @@ -508,8 +512,8 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = !!newCall(interf"nxt", input, idx, resLen)) of nnkCallKinds: # *{'A'..'Z'} !! s.add(!_) - template buildWhile(init, cond, action): untyped = - while true: + template buildWhile(input, idx, init, cond, action): untyped = + while hasNxt(input, idx): init if not cond: break action @@ -528,7 +532,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = !!newCall(interf"nxt", input, idx, it[2])) elif it.kind == nnkPrefix and it[0].eqIdent"*": let (init, cond, action) = atm(it[1], input, idx, attached) - result = (getAst(buildWhile(init, cond, action)), + result = (getAst(buildWhile(input, idx, init, cond, action)), newEmptyNode(), newEmptyNode()) elif it.kind == nnkPrefix and it[0].eqIdent"+": # x+ is the same as xx* @@ -621,7 +625,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = when isMainModule: proc twoDigits(input: string; x: var int; start: int): int = - if input[start] == '0' and input[start+1] == '0': + if start+1 < input.len and input[start] == '0' and input[start+1] == '0': result = 2 x = 13 else: @@ -629,10 +633,10 @@ when isMainModule: proc someSep(input: string; start: int; seps: set[char] = {';',',','-','.'}): int = result = 0 - while input[start+result] in seps: inc result + while start+result < input.len and input[start+result] in seps: inc result proc demangle(s: string; res: var string; start: int): int = - while s[result+start] in {'_', '@'}: inc result + while result+start < s.len and s[result+start] in {'_', '@'}: inc result res = "" while result+start < s.len and s[result+start] > ' ' and s[result+start] != '_': res.add s[result+start] @@ -652,7 +656,7 @@ when isMainModule: var info = "" if scanp(resp, idx, *`whites`, '#', *`digits`, +`whites`, ?("0x", *`hexdigits`, " in "), demangle($input, prc, $index), *`whites`, '(', * ~ ')', ')', - *`whites`, "at ", +(~{'\C', '\L', '\0'} -> info.add($_)) ): + *`whites`, "at ", +(~{'\C', '\L'} -> info.add($_)) ): result.add prc & " " & info else: break @@ -713,7 +717,7 @@ when isMainModule: "NimMainInner c:/users/anwender/projects/nim/lib/system.nim:2605", "NimMain c:/users/anwender/projects/nim/lib/system.nim:2613", "main c:/users/anwender/projects/nim/lib/system.nim:2620"] - doAssert parseGDB(gdbOut) == result + #doAssert parseGDB(gdbOut) == result # bug #6487 var count = 0 diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index cdc5ec4f9..4500a163f 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -106,6 +106,12 @@ proc isUpperAscii*(c: char): bool {.noSideEffect, procvar, ## This checks ASCII characters only. return c in {'A'..'Z'} +template isImpl(call) = + if s.len == 0: return false + result = true + for c in s: + if not call(c): return false + proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsAlphaAsciiStr".} = ## Checks whether or not `s` is alphabetical. @@ -114,12 +120,7 @@ proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar, ## Returns true if all characters in `s` are ## alphabetic and there is at least one character ## in `s`. - if s.len() == 0: - return false - - result = true - for c in s: - if not c.isAlphaAscii(): return false + isImpl isAlphaAscii proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsAlphaNumericStr".} = @@ -129,13 +130,7 @@ proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar, ## Returns true if all characters in `s` are ## alpanumeric and there is at least one character ## in `s`. - if s.len() == 0: - return false - - result = true - for c in s: - if not c.isAlphaNumeric(): - return false + isImpl isAlphaNumeric proc isDigit*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsDigitStr".} = @@ -145,13 +140,7 @@ proc isDigit*(s: string): bool {.noSideEffect, procvar, ## Returns true if all characters in `s` are ## numeric and there is at least one character ## in `s`. - if s.len() == 0: - return false - - result = true - for c in s: - if not c.isDigit(): - return false + isImpl isDigit proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsSpaceAsciiStr".} = @@ -159,13 +148,7 @@ proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar, ## ## Returns true if all characters in `s` are whitespace ## characters and there is at least one character in `s`. - if s.len() == 0: - return false - - result = true - for c in s: - if not c.isSpaceAscii(): - return false + isImpl isSpaceAscii proc isLowerAscii*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsLowerAsciiStr".} = @@ -174,13 +157,7 @@ proc isLowerAscii*(s: string): bool {.noSideEffect, procvar, ## This checks ASCII characters only. ## Returns true if all characters in `s` are lower case ## and there is at least one character in `s`. - if s.len() == 0: - return false - - for c in s: - if not c.isLowerAscii(): - return false - true + isImpl isLowerAscii proc isUpperAscii*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsUpperAsciiStr".} = @@ -189,13 +166,7 @@ proc isUpperAscii*(s: string): bool {.noSideEffect, procvar, ## This checks ASCII characters only. ## Returns true if all characters in `s` are upper case ## and there is at least one character in `s`. - if s.len() == 0: - return false - - for c in s: - if not c.isUpperAscii(): - return false - true + isImpl isUpperAscii proc toLowerAscii*(c: char): char {.noSideEffect, procvar, rtl, extern: "nsuToLowerAsciiChar".} = @@ -209,6 +180,11 @@ proc toLowerAscii*(c: char): char {.noSideEffect, procvar, else: result = c +template toImpl(call) = + result = newString(len(s)) + for i in 0..len(s) - 1: + result[i] = call(s[i]) + proc toLowerAscii*(s: string): string {.noSideEffect, procvar, rtl, extern: "nsuToLowerAsciiStr".} = ## Converts `s` into lower case. @@ -216,9 +192,7 @@ proc toLowerAscii*(s: string): string {.noSideEffect, procvar, ## This works only for the letters ``A-Z``. See `unicode.toLower ## <unicode.html#toLower>`_ for a version that works for any Unicode ## character. - result = newString(len(s)) - for i in 0..len(s) - 1: - result[i] = toLowerAscii(s[i]) + toImpl toLowerAscii proc toUpperAscii*(c: char): char {.noSideEffect, procvar, rtl, extern: "nsuToUpperAsciiChar".} = @@ -239,147 +213,15 @@ proc toUpperAscii*(s: string): string {.noSideEffect, procvar, ## This works only for the letters ``A-Z``. See `unicode.toUpper ## <unicode.html#toUpper>`_ for a version that works for any Unicode ## character. - result = newString(len(s)) - for i in 0..len(s) - 1: - result[i] = toUpperAscii(s[i]) + toImpl toUpperAscii proc capitalizeAscii*(s: string): string {.noSideEffect, procvar, rtl, extern: "nsuCapitalizeAscii".} = ## Converts the first character of `s` into upper case. ## ## This works only for the letters ``A-Z``. - result = toUpperAscii(s[0]) & substr(s, 1) - -proc isSpace*(c: char): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsSpaceChar".}= - ## Checks whether or not `c` is a whitespace character. - ## - ## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead. - isSpaceAscii(c) - -proc isLower*(c: char): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsLowerChar".}= - ## Checks whether or not `c` is a lower case character. - ## - ## This checks ASCII characters only. - ## - ## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead. - isLowerAscii(c) - -proc isUpper*(c: char): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsUpperChar".}= - ## Checks whether or not `c` is an upper case character. - ## - ## This checks ASCII characters only. - ## - ## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead. - isUpperAscii(c) - -proc isAlpha*(c: char): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsAlphaChar".}= - ## Checks whether or not `c` is alphabetical. - ## - ## This checks a-z, A-Z ASCII characters only. - ## - ## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead. - isAlphaAscii(c) - -proc isAlpha*(s: string): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsAlphaStr".}= - ## Checks whether or not `s` is alphabetical. - ## - ## This checks a-z, A-Z ASCII characters only. - ## Returns true if all characters in `s` are - ## alphabetic and there is at least one character - ## in `s`. - ## - ## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead. - isAlphaAscii(s) - -proc isSpace*(s: string): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsSpaceStr".}= - ## Checks whether or not `s` is completely whitespace. - ## - ## Returns true if all characters in `s` are whitespace - ## characters and there is at least one character in `s`. - ## - ## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead. - isSpaceAscii(s) - -proc isLower*(s: string): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsLowerStr".}= - ## Checks whether or not `s` contains all lower case characters. - ## - ## This checks ASCII characters only. - ## Returns true if all characters in `s` are lower case - ## and there is at least one character in `s`. - ## - ## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead. - isLowerAscii(s) - -proc isUpper*(s: string): bool {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuIsUpperStr".}= - ## Checks whether or not `s` contains all upper case characters. - ## - ## This checks ASCII characters only. - ## Returns true if all characters in `s` are upper case - ## and there is at least one character in `s`. - ## - ## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead. - isUpperAscii(s) - -proc toLower*(c: char): char {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuToLowerChar".} = - ## Converts `c` into lower case. - ## - ## This works only for the letters ``A-Z``. See `unicode.toLower - ## <unicode.html#toLower>`_ for a version that works for any Unicode - ## character. - ## - ## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead. - toLowerAscii(c) - -proc toLower*(s: string): string {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuToLowerStr".} = - ## Converts `s` into lower case. - ## - ## This works only for the letters ``A-Z``. See `unicode.toLower - ## <unicode.html#toLower>`_ for a version that works for any Unicode - ## character. - ## - ## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead. - toLowerAscii(s) - -proc toUpper*(c: char): char {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuToUpperChar".} = - ## Converts `c` into upper case. - ## - ## This works only for the letters ``A-Z``. See `unicode.toUpper - ## <unicode.html#toUpper>`_ for a version that works for any Unicode - ## character. - ## - ## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead. - toUpperAscii(c) - -proc toUpper*(s: string): string {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuToUpperStr".} = - ## Converts `s` into upper case. - ## - ## This works only for the letters ``A-Z``. See `unicode.toUpper - ## <unicode.html#toUpper>`_ for a version that works for any Unicode - ## character. - ## - ## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead. - toUpperAscii(s) - -proc capitalize*(s: string): string {.noSideEffect, procvar, - rtl, deprecated, extern: "nsuCapitalize".} = - ## Converts the first character of `s` into upper case. - ## - ## This works only for the letters ``A-Z``. - ## - ## **Deprecated since version 0.15.0**: use ``capitalizeAscii`` instead. - capitalizeAscii(s) + if s.len == 0: result = "" + else: result = toUpperAscii(s[0]) & substr(s, 1) proc normalize*(s: string): string {.noSideEffect, procvar, rtl, extern: "nsuNormalize".} = @@ -419,9 +261,9 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect, proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, rtl, extern: "nsuCmpIgnoreStyle", procvar.} = ## Semantically the same as ``cmp(normalize(a), normalize(b))``. It - ## is just optimized to not allocate temporary strings. This should + ## is just optimized to not allocate temporary strings. This should ## NOT be used to compare Nim identifier names. use `macros.eqIdent` - ## for that. Returns: + ## for that. Returns: ## ## | 0 iff a == b ## | < 0 iff a < b @@ -429,14 +271,22 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, var i = 0 var j = 0 while true: - while a[i] == '_': inc(i) - while b[j] == '_': inc(j) # BUGFIX: typo - var aa = toLowerAscii(a[i]) - var bb = toLowerAscii(b[j]) + while i < a.len and a[i] == '_': inc i + while j < b.len and b[j] == '_': inc j + var aa = if i < a.len: toLowerAscii(a[i]) else: '\0' + var bb = if j < b.len: toLowerAscii(b[j]) else: '\0' result = ord(aa) - ord(bb) - if result != 0 or aa == '\0': break - inc(i) - inc(j) + if result != 0: return result + # the characters are identical: + if i >= a.len: + # both cursors at the end: + if j >= b.len: return 0 + # not yet at the end of 'b': + return -1 + elif j >= b.len: + return 1 + inc i + inc j proc strip*(s: string, leading = true, trailing = true, chars: set[char] = Whitespace): string @@ -451,7 +301,7 @@ proc strip*(s: string, leading = true, trailing = true, first = 0 last = len(s)-1 if leading: - while s[first] in chars: inc(first) + while first <= last and s[first] in chars: inc(first) if trailing: while last >= 0 and s[last] in chars: dec(last) result = substr(s, first, last) @@ -467,7 +317,9 @@ proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} = result[i] = chr(val mod 8 + ord('0')) val = val div 8 -proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrEmpty".} = +proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl, + extern: "nsuIsNilOrEmpty", + deprecated: "use 'x.len == 0' instead".} = ## Checks if `s` is nil or empty. result = len(s) == 0 @@ -486,7 +338,6 @@ proc substrEq(s: string, pos: int, substr: string): bool = var length = substr.len while i < length and s[pos+i] == substr[i]: inc i - return i == length # --------- Private templates for different split separators ----------- @@ -520,7 +371,7 @@ template oldSplit(s, seps, maxsplit) = var splits = maxsplit assert(not ('\0' in seps)) while last < len(s): - while s[last] in seps: inc(last) + while last < len(s) and s[last] in seps: inc(last) var first = last while last < len(s) and s[last] notin seps: inc(last) if first <= last-1: @@ -571,10 +422,7 @@ iterator split*(s: string, seps: set[char] = Whitespace, ## "08" ## "08.398990" ## - when defined(nimOldSplit): - oldSplit(s, seps, maxsplit) - else: - splitCommon(s, seps, maxsplit, 1) + splitCommon(s, seps, maxsplit, 1) iterator splitWhitespace*(s: string, maxsplit: int = -1): string = ## Splits the string ``s`` at whitespace stripping leading and trailing @@ -660,7 +508,6 @@ iterator split*(s: string, sep: string, maxsplit: int = -1): string = ## "is" ## "corrupted" ## - splitCommon(s, sep, maxsplit, sep.len) template rsplitCommon(s, sep, maxsplit, sepLen) = @@ -670,29 +517,21 @@ template rsplitCommon(s, sep, maxsplit, sepLen) = first = last splits = maxsplit startPos = 0 - # go to -1 in order to get separators at the beginning while first >= -1: while first >= 0 and not stringHasSep(s, first, sep): dec(first) - if splits == 0: # No more splits means set first to the beginning first = -1 - if first == -1: startPos = 0 else: startPos = first + sepLen - yield substr(s, startPos, last) - - if splits == 0: - break - + if splits == 0: break dec(splits) dec(first) - last = first iterator rsplit*(s: string, seps: set[char] = Whitespace, @@ -712,7 +551,6 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace, ## "foo" ## ## Substrings are separated from the right by the set of chars `seps` - rsplitCommon(s, seps, maxsplit, 1) iterator rsplit*(s: string, sep: char, @@ -779,14 +617,14 @@ iterator splitLines*(s: string): string = var first = 0 var last = 0 while true: - while s[last] notin {'\0', '\c', '\l'}: inc(last) + while last < s.len and s[last] notin {'\c', '\l'}: inc(last) yield substr(s, first, last-1) # skip newlines: + if last >= s.len: break if s[last] == '\l': inc(last) elif s[last] == '\c': inc(last) - if s[last] == '\l': inc(last) - else: break # was '\0' + if last < s.len and s[last] == '\l': inc(last) first = last proc splitLines*(s: string): seq[string] {.noSideEffect, @@ -811,7 +649,7 @@ proc countLines*(s: string): int {.noSideEffect, while i < s.len: case s[i] of '\c': - if s[i+1] == '\l': inc i + if i+1 < s.len and s[i+1] == '\l': inc i inc result of '\l': inc result else: discard @@ -1025,9 +863,9 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar, ## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores ## within `s` are ignored. var i = 0 - if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2) - elif s[i] == '#': inc(i) - while true: + if i+1 < s.len and s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2) + elif i < s.len and s[i] == '#': inc(i) + while i < s.len: case s[i] of '_': inc(i) of '0'..'9': @@ -1039,7 +877,6 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar, of 'A'..'F': result = result shl 4 or (ord(s[i]) - ord('A') + 10) inc(i) - of '\0': break else: raise newException(ValueError, "invalid integer: " & s) proc generateHexCharToValueMap(): string = @@ -1148,14 +985,6 @@ template spaces*(n: Natural): string = repeat(' ', n) ## echo text1 & spaces(max(0, width - text1.len)) & "|" ## echo text2 & spaces(max(0, width - text2.len)) & "|" -proc repeatChar*(count: Natural, c: char = ' '): string {.deprecated.} = - ## deprecated: use repeat() or spaces() - repeat(c, count) - -proc repeatStr*(count: Natural, s: string): string {.deprecated.} = - ## deprecated: use repeat(string, count) or string.repeat(count) - repeat(s, count) - proc align*(s: string, count: Natural, padding = ' '): string {. noSideEffect, rtl, extern: "nsuAlignString".} = ## Aligns a string `s` with `padding`, so that it is of length `count`. @@ -1226,7 +1055,7 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[ var i = 0 while true: var j = i - var isSep = s[j] in seps + var isSep = j < s.len and s[j] in seps while j < s.len and (s[j] in seps) == isSep: inc(j) if j > i: yield (substr(s, i, j-1), isSep) @@ -1325,13 +1154,13 @@ proc startsWith*(s, prefix: string): bool {.noSideEffect, ## If ``prefix == ""`` true is returned. var i = 0 while true: - if prefix[i] == '\0': return true - if s[i] != prefix[i]: return false + if i >= prefix.len: return true + if i >= s.len or s[i] != prefix[i]: return false inc(i) proc startsWith*(s: string, prefix: char): bool {.noSideEffect, inline.} = ## Returns true iff ``s`` starts with ``prefix``. - result = s[0] == prefix + result = s.len > 0 and s[0] == prefix proc endsWith*(s, suffix: string): bool {.noSideEffect, rtl, extern: "nsuEndsWith".} = @@ -1343,11 +1172,11 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect, while i+j <% s.len: if s[i+j] != suffix[i]: return false inc(i) - if suffix[i] == '\0': return true + if i >= suffix.len: return true proc endsWith*(s: string, suffix: char): bool {.noSideEffect, inline.} = ## Returns true iff ``s`` ends with ``suffix``. - result = s[s.high] == suffix + result = s.len > 0 and s[s.high] == suffix proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect, rtl, extern: "nsuContinuesWith".} = @@ -1356,8 +1185,8 @@ proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect, ## If ``substr == ""`` true is returned. var i = 0 while true: - if substr[i] == '\0': return true - if s[i+start] != substr[i]: return false + if i >= substr.len: return true + if i+start >= s.len or s[i+start] != substr[i]: return false inc(i) proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0) @@ -1502,12 +1331,8 @@ proc find*(s, sub: string, start: Natural = 0, last: Natural = 0): int {.noSideE ## If `last` is unspecified, it defaults to `s.high`. ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. - if sub.len > s.len: - return -1 - - if sub.len == 1: - return find(s, sub[0], start, last) - + if sub.len > s.len: return -1 + if sub.len == 1: return find(s, sub[0], start, last) var a {.noinit.}: SkipTable initSkipTable(a, sub) result = find(a, s, sub, start, last) @@ -1564,18 +1389,14 @@ proc center*(s: string, width: int, fillChar: char = ' '): string {. ## ## The original string is returned if `width` is less than or equal ## to `s.len`. - if width <= s.len: - return s - + if width <= s.len: return s result = newString(width) - # Left padding will be one fillChar # smaller if there are an odd number # of characters let charsLeft = (width - s.len) leftPadding = charsLeft div 2 - for i in 0 ..< width: if i >= leftPadding and i < leftPadding + s.len: # we are where the string should be located @@ -1593,27 +1414,22 @@ proc count*(s: string, sub: string, overlapping: bool = false): int {. var i = 0 while true: i = s.find(sub, i) - if i < 0: - break - if overlapping: - inc i - else: - i += sub.len + if i < 0: break + if overlapping: inc i + else: i += sub.len inc result proc count*(s: string, sub: char): int {.noSideEffect, rtl, extern: "nsuCountChar".} = ## Count the occurrences of the character `sub` in the string `s`. for c in s: - if c == sub: - inc result + if c == sub: inc result proc count*(s: string, subs: set[char]): int {.noSideEffect, rtl, extern: "nsuCountCharSet".} = ## Count the occurrences of the group of character `subs` in the string `s`. for c in s: - if c in subs: - inc result + if c in subs: inc result proc quoteIfContainsWhite*(s: string): string {.deprecated.} = ## Returns ``'"' & s & '"'`` if `s` contains a space and does not @@ -1621,10 +1437,8 @@ proc quoteIfContainsWhite*(s: string): string {.deprecated.} = ## ## **DEPRECATED** as it was confused for shell quoting function. For this ## application use `osproc.quoteShell <osproc.html#quoteShell>`_. - if find(s, {' ', '\t'}) >= 0 and s[0] != '"': - result = '"' & s & '"' - else: - result = s + if find(s, {' ', '\t'}) >= 0 and s[0] != '"': result = '"' & s & '"' + else: result = s proc contains*(s: string, c: char): bool {.noSideEffect.} = ## Same as ``find(s, c) >= 0``. @@ -1704,9 +1518,8 @@ proc multiReplace*(s: string, replacements: varargs[(string, string)]): string { ## Same as replace, but specialized for doing multiple replacements in a single ## pass through the input string. ## - ## Calling replace multiple times after each other is inefficient and result in too many allocations - ## follwed by immediate deallocations as portions of the string gets replaced. - ## multiReplace performs all replacements in a single pass. + ## multiReplace performs all replacements in a single pass, this means it can be used + ## to swap the occurences of "a" and "b", for instance. ## ## If the resulting string is not longer than the original input string, only a single ## memory allocation is required. @@ -1753,14 +1566,13 @@ proc parseOctInt*(s: string): int {.noSideEffect, ## of the following optional prefixes: ``0o``, ``0O``. Underscores within ## `s` are ignored. var i = 0 - if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2) - while true: + 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: case s[i] of '_': inc(i) of '0'..'7': result = result shl 3 or (ord(s[i]) - ord('0')) inc(i) - of '\0': break else: raise newException(ValueError, "invalid integer: " & s) proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect, @@ -1849,16 +1661,18 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, ## If `s` does not begin with ``prefix`` and end with ``suffix`` a ## ValueError exception will be raised. ## - ## **Warning:** This procedure is deprecated because it's to easy to missuse. + ## **Warning:** This procedure is deprecated because it's to easy to missuse. result = newStringOfCap(s.len) var i = prefix.len if not s.startsWith(prefix): raise newException(ValueError, - "String does not start with a prefix of: " & prefix) + "String does not start with: " & prefix) while true: - if i == s.len-suffix.len: break - case s[i] - of '\\': + if i >= s.len-suffix.len: break + if s[i] == '\\': + if i+1 >= s.len: + result.add('\\') + break case s[i+1]: of 'x': inc i, 2 @@ -1872,15 +1686,15 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, result.add('\'') of '\"': result.add('\"') - else: result.add("\\" & s[i+1]) - inc(i) - of '\0': break + else: + result.add("\\" & s[i+1]) + inc(i, 2) else: result.add(s[i]) - inc(i) + inc(i) if not s.endsWith(suffix): raise newException(ValueError, - "String does not end with a suffix of: " & suffix) + "String does not end in: " & suffix) proc validIdentifier*(s: string): bool {.noSideEffect, rtl, extern: "nsuValidIdentifier".} = @@ -1890,7 +1704,7 @@ proc validIdentifier*(s: string): bool {.noSideEffect, ## and is followed by any number of characters of the set `IdentChars`. runnableExamples: doAssert "abc_def08".validIdentifier - if s[0] in IdentStartChars: + if s.len > 0 and s[0] in IdentStartChars: for i in 1..s.len-1: if s[i] notin IdentChars: return false return true @@ -1909,7 +1723,7 @@ proc editDistance*(a, b: string): int {.noSideEffect, # strip common prefix: var s = 0 - while a[s] == b[s] and a[s] != '\0': + while s < len1 and a[s] == b[s]: inc(s) dec(len1) dec(len2) @@ -1982,8 +1796,6 @@ proc editDistance*(a, b: string): int {.noSideEffect, if x > c3: x = c3 row[p] = x result = row[e] - #dealloc(row) - # floating point formating: when not defined(js): @@ -2092,7 +1904,7 @@ proc trimZeros*(x: var string) {.noSideEffect.} = var spl: seq[string] if x.contains('.') or x.contains(','): if x.contains('e'): - spl= x.split('e') + spl = x.split('e') x = spl[0] while x[x.high] == '0': x.setLen(x.len-1) @@ -2310,9 +2122,8 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {. var i = 0 var num = 0 while i < len(formatstr): - if formatstr[i] == '$': - case formatstr[i+1] # again we use the fact that strings - # are zero-terminated here + if formatstr[i] == '$' and i+1 < len(formatstr): + case formatstr[i+1] of '#': if num > a.high: invalidFormatString() add s, a[num] @@ -2326,7 +2137,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {. inc(i) # skip $ var negative = formatstr[i] == '-' if negative: inc i - while formatstr[i] in Digits: + while i < formatstr.len and formatstr[i] in Digits: j = j * 10 + ord(formatstr[i]) - ord('0') inc(i) let idx = if not negative: j-1 else: a.len-j @@ -2338,7 +2149,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {. var negative = formatstr[j] == '-' if negative: inc j var isNumber = 0 - while formatstr[j] notin {'\0', '}'}: + while j < formatstr.len and formatstr[j] notin {'\0', '}'}: if formatstr[j] in Digits: k = k * 10 + ord(formatstr[j]) - ord('0') if isNumber == 0: isNumber = 1 @@ -2356,7 +2167,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {. i = j+1 of 'a'..'z', 'A'..'Z', '\128'..'\255', '_': var j = i+1 - while formatstr[j] in PatternChars: inc(j) + while j < formatstr.len and formatstr[j] in PatternChars: inc(j) var x = findNormalized(substr(formatstr, i+1, j-1), a) if x >= 0 and x < high(a): add s, a[x+1] else: invalidFormatString() @@ -2628,13 +2439,7 @@ when isMainModule: doAssert isSpaceAscii(" ") doAssert(not isSpaceAscii("ABc \td")) - doAssert(isNilOrEmpty("")) - doAssert(isNilOrEmpty(nil)) - doAssert(not isNilOrEmpty("test")) - doAssert(not isNilOrEmpty(" ")) - doAssert(isNilOrWhitespace("")) - doAssert(isNilOrWhitespace(nil)) doAssert(isNilOrWhitespace(" ")) doAssert(isNilOrWhitespace("\t\l \v\r\f")) doAssert(not isNilOrWhitespace("ABc \td")) diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index d2d11253a..9bf25b86b 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -60,7 +60,7 @@ proc encodeUrl*(s: string): string = else: add(result, '%') add(result, toHex(ord(s[i]), 2)) - + proc decodeUrl*(s: string): string = ## Decodes a value from its HTTP representation: This means that a ``'+'`` ## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal @@ -72,7 +72,7 @@ proc decodeUrl*(s: string): string = of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) else: assert(false) - + result = newString(s.len) var i = 0 var j = 0 @@ -94,7 +94,7 @@ proc parseAuthority(authority: string, result: var Uri) = var i = 0 var inPort = false var inIPv6 = false - while true: + while i < authority.len: case authority[i] of '@': swap result.password, result.port @@ -111,7 +111,6 @@ proc parseAuthority(authority: string, result: var Uri) = inIPv6 = true of ']': inIPv6 = false - of '\0': break else: if inPort: result.port.add(authority[i]) @@ -128,11 +127,11 @@ proc parsePath(uri: string, i: var int, result: var Uri) = parseAuthority(result.path, result) result.path.setLen(0) - if uri[i] == '?': + if i < uri.len and uri[i] == '?': i.inc # Skip '?' i.inc parseUntil(uri, result.query, {'#'}, i) - if uri[i] == '#': + if i < uri.len and uri[i] == '#': i.inc # Skip '#' i.inc parseUntil(uri, result.anchor, {}, i) @@ -156,7 +155,7 @@ proc parseUri*(uri: string, result: var Uri) = # Check if this is a reference URI (relative URI) let doubleSlash = uri.len > 1 and uri[1] == '/' - if uri[i] == '/': + if i < uri.len and uri[i] == '/': # Make sure ``uri`` doesn't begin with '//'. if not doubleSlash: parsePath(uri, i, result) @@ -164,7 +163,7 @@ proc parseUri*(uri: string, result: var Uri) = # Scheme i.inc parseWhile(uri, result.scheme, Letters + Digits + {'+', '-', '.'}, i) - if uri[i] != ':' and not doubleSlash: + if (i >= uri.len or uri[i] != ':') and not doubleSlash: # Assume this is a reference URI (relative URI) i = 0 result.scheme.setLen(0) @@ -174,7 +173,7 @@ proc parseUri*(uri: string, result: var Uri) = i.inc # Skip ':' # Authority - if uri[i] == '/' and uri[i+1] == '/': + if i+1 < uri.len and uri[i] == '/' and uri[i+1] == '/': i.inc(2) # Skip // var authority = "" i.inc parseUntil(uri, authority, {'/', '?', '#'}, i) @@ -197,13 +196,13 @@ proc removeDotSegments(path: string): string = let endsWithSlash = path[path.len-1] == '/' var i = 0 var currentSegment = "" - while true: + while i < path.len: case path[i] of '/': collection.add(currentSegment) currentSegment = "" of '.': - if path[i+1] == '.' and path[i+2] == '/': + if i+2 < path.len and path[i+1] == '.' and path[i+2] == '/': if collection.len > 0: discard collection.pop() i.inc 3 @@ -212,13 +211,11 @@ proc removeDotSegments(path: string): string = i.inc 2 continue currentSegment.add path[i] - of '\0': - if currentSegment != "": - collection.add currentSegment - break else: currentSegment.add path[i] i.inc + if currentSegment != "": + collection.add currentSegment result = collection.join("/") if endsWithSlash: result.add '/' @@ -320,18 +317,18 @@ proc `/`*(x: Uri, path: string): Uri = result = x if result.path.len == 0: - if path[0] != '/': + if path.len == 0 or path[0] != '/': result.path = "/" result.path.add(path) return - if result.path[result.path.len-1] == '/': - if path[0] == '/': + if result.path.len > 0 and result.path[result.path.len-1] == '/': + if path.len > 0 and path[0] == '/': result.path.add(path[1 .. path.len-1]) else: result.path.add(path) else: - if path[0] != '/': + if path.len == 0 or path[0] != '/': result.path.add '/' result.path.add(path) @@ -373,7 +370,7 @@ when isMainModule: const test1 = "abc\L+def xyz" doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz" doAssert decodeUrl(encodeUrl(test1)) == test1 - + block: let str = "http://localhost" let test = parseUri(str) @@ -464,7 +461,7 @@ when isMainModule: doAssert test.hostname == "github.com" doAssert test.port == "dom96" doAssert test.path == "/packages" - + block: let str = "file:///foo/bar/baz.txt" let test = parseUri(str) |