summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/packages/docutils/rstgen.nim2
-rw-r--r--lib/pure/cgi.nim20
-rw-r--r--lib/pure/parseopt.nim48
-rw-r--r--lib/pure/parseutils.nim107
-rw-r--r--lib/pure/strscans.nim34
-rw-r--r--lib/pure/strutils.nim383
-rw-r--r--lib/pure/uri.nim39
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)