summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorFederico Ceratto <federico.ceratto@gmail.com>2017-10-15 22:02:52 +0100
committerFederico Ceratto <federico.ceratto@gmail.com>2017-11-27 16:43:41 +0000
commit4100932a4b9abc184aa59e7e78dc975331eff920 (patch)
tree3064b29e1f131b5750ee77228c69ad88e2fd6dbd /lib
parent5e93eb9d7a839984c99789a4b9409b7a70f691f7 (diff)
downloadNim-4100932a4b9abc184aa59e7e78dc975331eff920.tar.gz
Add string-to-hex and back conversions
Add parseHexStr and toHex and tests.
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/strutils.nim50
1 files changed, 50 insertions, 0 deletions
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 4ac40d8b4..1d356dff4 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -930,6 +930,19 @@ proc toHex*[T](x: T): string =
   ## Shortcut for ``toHex(x, T.sizeOf * 2)``
   toHex(BiggestInt(x), T.sizeOf * 2)
 
+proc toHex*(s: string): string {.noSideEffect, rtl.} =
+  ## Converts a bytes string to its hexadecimal representation.
+  ##
+  ## The output is twice the input long. No prefix like
+  ## ``0x`` is generated.
+  const HexChars = "0123456789ABCDEF"
+  result = newString(s.len * 2)
+  for pos, c in s:
+    var n = ord(c)
+    result[pos * 2 + 1] = HexChars[n and 0xF]
+    n = n shr 4
+    result[pos * 2] = HexChars[n]
+
 proc intToStr*(x: int, minchars: Positive = 1): string {.noSideEffect,
   rtl, extern: "nsuIntToStr".} =
   ## Converts `x` to its decimal representation.
@@ -1012,6 +1025,43 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar,
     of '\0': break
     else: raise newException(ValueError, "invalid integer: " & s)
 
+proc generateHexCharToValueMap(): string =
+  ## Generate a string to map a hex digit to uint value
+  result = ""
+  for inp in 0..255:
+    let ch = chr(inp)
+    let o =
+      case ch:
+        of '0'..'9': inp - ord('0')
+        of 'a'..'f': inp - ord('a') + 10
+        of 'A'..'F': inp - ord('A') + 10
+        else: 17  # indicates an invalid hex char
+    result.add chr(o)
+
+const hexCharToValueMap = generateHexCharToValueMap()
+
+proc parseHexStr*(s: string): string {.noSideEffect, procvar,
+  rtl, extern: "nsuParseHexStr".} =
+  ## Convert hex-encoded string to byte string, e.g.:
+  ##
+  ## .. code-block:: nim
+  ##    hexToStr("00ff") == "\0\255"
+  ##
+  ## Raises ``ValueError`` for an invalid hex values. The comparison is
+  ## case-insensitive.
+  if s.len mod 2 != 0:
+    raise newException(ValueError, "Incorrect hex string len")
+  result = newString(s.len div 2)
+  var buf = 0
+  for pos, c in s:
+    let val = hexCharToValueMap[ord(c)].ord
+    if val == 17:
+      raise newException(ValueError, "Invalid hex char " & repr(c))
+    if pos mod 2 == 0:
+      buf = val
+    else:
+      result[pos div 2] = chr(val + buf shl 4)
+
 proc parseBool*(s: string): bool =
   ## Parses a value into a `bool`.
   ##