summary refs log tree commit diff stats
path: root/compiler/rodutils.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rodutils.nim')
-rw-r--r--compiler/rodutils.nim177
1 files changed, 177 insertions, 0 deletions
diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim
new file mode 100644
index 000000000..5355829c1
--- /dev/null
+++ b/compiler/rodutils.nim
@@ -0,0 +1,177 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Serialization utilities for the compiler.
+import std/[strutils, math]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+# bcc on windows doesn't have C99 functions
+when defined(windows) and defined(bcc):
+  {.emit: """#if defined(_MSC_VER) && _MSC_VER < 1900
+  #include <stdarg.h>
+  static int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) {
+    int count = -1;
+    if (size != 0) count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
+    if (count == -1) count = _vscprintf(format, ap);
+    return count;
+  }
+  int snprintf(char *outBuf, size_t size, const char *format, ...) {
+    int count;
+    va_list ap;
+    va_start(ap, format);
+    count = c99_vsnprintf(outBuf, size, format, ap);
+    va_end(ap);
+    return count;
+  }
+  #endif
+  """.}
+
+proc c_snprintf(s: cstring; n: uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
+
+
+when not declared(signbit):
+  proc c_signbit(x: SomeFloat): cint {.importc: "signbit", header: "<math.h>".}
+  proc signbit*(x: SomeFloat): bool {.inline.} =
+    result = c_signbit(x) != 0
+
+import std/formatfloat
+
+proc toStrMaxPrecision*(f: BiggestFloat | float32): string =
+  const literalPostfix = when f is float32: "f" else: ""
+  case classify(f)
+  of fcNan:
+    if signbit(f):
+      result = "-NAN"
+    else:
+      result = "NAN"
+  of fcNegZero:
+    result = "-0.0" & literalPostfix
+  of fcZero:
+    result = "0.0" & literalPostfix
+  of fcInf:
+    result = "INF"
+  of fcNegInf:
+    result = "-INF"
+  else:
+    result = ""
+    result.addFloatRoundtrip(f)
+    result.add literalPostfix
+
+proc encodeStr*(s: string, result: var string) =
+  for i in 0..<s.len:
+    case s[i]
+    of 'a'..'z', 'A'..'Z', '0'..'9', '_': result.add(s[i])
+    else: result.add('\\' & toHex(ord(s[i]), 2))
+
+proc hexChar(c: char, xi: var int) =
+  case c
+  of '0'..'9': xi = (xi shl 4) or (ord(c) - ord('0'))
+  of 'a'..'f': xi = (xi shl 4) or (ord(c) - ord('a') + 10)
+  of 'A'..'F': xi = (xi shl 4) or (ord(c) - ord('A') + 10)
+  else: discard
+
+proc decodeStr*(s: cstring, pos: var int): string =
+  var i = pos
+  result = ""
+  while true:
+    case s[i]
+    of '\\':
+      inc(i, 3)
+      var xi = 0
+      hexChar(s[i-2], xi)
+      hexChar(s[i-1], xi)
+      result.add(chr(xi))
+    of 'a'..'z', 'A'..'Z', '0'..'9', '_':
+      result.add(s[i])
+      inc(i)
+    else: break
+  pos = i
+
+const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+{.push overflowChecks: off.}
+
+# since negative numbers require a leading '-' they use up 1 byte. Thus we
+# subtract/add `vintDelta` here to save space for small negative numbers
+# which are common in ROD files:
+const vintDelta = 5
+
+template encodeIntImpl(self) =
+  var d: char
+  var v = x
+  var rem = v mod 190
+  if rem < 0:
+    result.add('-')
+    v = - (v div 190)
+    rem = - rem
+  else:
+    v = v div 190
+  var idx = int(rem)
+  if idx < 62: d = chars[idx]
+  else: d = chr(idx - 62 + 128)
+  if v != 0: self(v, result)
+  result.add(d)
+
+proc encodeVBiggestIntAux(x: BiggestInt, result: var string) =
+  ## encode a biggest int as a variable length base 190 int.
+  encodeIntImpl(encodeVBiggestIntAux)
+
+proc encodeVBiggestInt*(x: BiggestInt, result: var string) =
+  ## encode a biggest int as a variable length base 190 int.
+  encodeVBiggestIntAux(x +% vintDelta, result)
+  #  encodeIntImpl(encodeVBiggestInt)
+
+proc encodeVIntAux(x: int, result: var string) =
+  ## encode an int as a variable length base 190 int.
+  encodeIntImpl(encodeVIntAux)
+
+proc encodeVInt*(x: int, result: var string) =
+  ## encode an int as a variable length base 190 int.
+  encodeVIntAux(x +% vintDelta, result)
+
+template decodeIntImpl() =
+  var i = pos
+  var sign = - 1
+  assert(s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'})
+  if s[i] == '-':
+    inc(i)
+    sign = 1
+  result = 0
+  while true:
+    case s[i]
+    of '0'..'9': result = result * 190 - (ord(s[i]) - ord('0'))
+    of 'a'..'z': result = result * 190 - (ord(s[i]) - ord('a') + 10)
+    of 'A'..'Z': result = result * 190 - (ord(s[i]) - ord('A') + 36)
+    of '\x80'..'\xFF': result = result * 190 - (ord(s[i]) - 128 + 62)
+    else: break
+    inc(i)
+  result = result * sign -% vintDelta
+  pos = i
+
+proc decodeVInt*(s: cstring, pos: var int): int =
+  decodeIntImpl()
+
+proc decodeVBiggestInt*(s: cstring, pos: var int): BiggestInt =
+  decodeIntImpl()
+
+{.pop.}
+
+iterator decodeVIntArray*(s: cstring): int =
+  var i = 0
+  while s[i] != '\0':
+    yield decodeVInt(s, i)
+    if s[i] == ' ': inc i
+
+iterator decodeStrArray*(s: cstring): string =
+  var i = 0
+  while s[i] != '\0':
+    yield decodeStr(s, i)
+    if s[i] == ' ': inc i