rodutils.nim 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Serialization utilities for the compiler.
  10. import strutils, math
  11. # bcc on windows doesn't have C99 functions
  12. when defined(windows) and defined(bcc):
  13. {.emit: """#if defined(_MSC_VER) && _MSC_VER < 1900
  14. #include <stdarg.h>
  15. static int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) {
  16. int count = -1;
  17. if (size != 0) count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
  18. if (count == -1) count = _vscprintf(format, ap);
  19. return count;
  20. }
  21. int snprintf(char *outBuf, size_t size, const char *format, ...) {
  22. int count;
  23. va_list ap;
  24. va_start(ap, format);
  25. count = c99_vsnprintf(outBuf, size, format, ap);
  26. va_end(ap);
  27. return count;
  28. }
  29. #endif
  30. """.}
  31. proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
  32. proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
  33. case classify(f)
  34. of fcNaN:
  35. result = "NAN"
  36. of fcNegZero:
  37. result = "-0.0" & literalPostfix
  38. of fcZero:
  39. result = "0.0" & literalPostfix
  40. of fcInf:
  41. result = "INF"
  42. of fcNegInf:
  43. result = "-INF"
  44. else:
  45. when defined(nimNoArrayToCstringConversion):
  46. result = newString(81)
  47. let n = c_snprintf(result.cstring, result.len.uint, "%#.16e%s", f, literalPostfix.cstring)
  48. setLen(result, n)
  49. else:
  50. var buf: array[0..80, char]
  51. discard c_snprintf(buf.cstring, buf.len.uint, "%#.16e%s", f, literalPostfix.cstring)
  52. result = $buf.cstring
  53. proc encodeStr*(s: string, result: var string) =
  54. for i in 0 ..< len(s):
  55. case s[i]
  56. of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
  57. else: add(result, '\\' & toHex(ord(s[i]), 2))
  58. proc hexChar(c: char, xi: var int) =
  59. case c
  60. of '0'..'9': xi = (xi shl 4) or (ord(c) - ord('0'))
  61. of 'a'..'f': xi = (xi shl 4) or (ord(c) - ord('a') + 10)
  62. of 'A'..'F': xi = (xi shl 4) or (ord(c) - ord('A') + 10)
  63. else: discard
  64. proc decodeStr*(s: cstring, pos: var int): string =
  65. var i = pos
  66. result = ""
  67. while true:
  68. case s[i]
  69. of '\\':
  70. inc(i, 3)
  71. var xi = 0
  72. hexChar(s[i-2], xi)
  73. hexChar(s[i-1], xi)
  74. add(result, chr(xi))
  75. of 'a'..'z', 'A'..'Z', '0'..'9', '_':
  76. add(result, s[i])
  77. inc(i)
  78. else: break
  79. pos = i
  80. const
  81. chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  82. {.push overflowChecks: off.}
  83. # since negative numbers require a leading '-' they use up 1 byte. Thus we
  84. # subtract/add `vintDelta` here to save space for small negative numbers
  85. # which are common in ROD files:
  86. const
  87. vintDelta = 5
  88. template encodeIntImpl(self) =
  89. var d: char
  90. var v = x
  91. var rem = v mod 190
  92. if rem < 0:
  93. add(result, '-')
  94. v = - (v div 190)
  95. rem = - rem
  96. else:
  97. v = v div 190
  98. var idx = int(rem)
  99. if idx < 62: d = chars[idx]
  100. else: d = chr(idx - 62 + 128)
  101. if v != 0: self(v, result)
  102. add(result, d)
  103. proc encodeVBiggestIntAux(x: BiggestInt, result: var string) =
  104. ## encode a biggest int as a variable length base 190 int.
  105. encodeIntImpl(encodeVBiggestIntAux)
  106. proc encodeVBiggestInt*(x: BiggestInt, result: var string) =
  107. ## encode a biggest int as a variable length base 190 int.
  108. encodeVBiggestIntAux(x +% vintDelta, result)
  109. # encodeIntImpl(encodeVBiggestInt)
  110. proc encodeVIntAux(x: int, result: var string) =
  111. ## encode an int as a variable length base 190 int.
  112. encodeIntImpl(encodeVIntAux)
  113. proc encodeVInt*(x: int, result: var string) =
  114. ## encode an int as a variable length base 190 int.
  115. encodeVIntAux(x +% vintDelta, result)
  116. template decodeIntImpl() =
  117. var i = pos
  118. var sign = - 1
  119. assert(s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'})
  120. if s[i] == '-':
  121. inc(i)
  122. sign = 1
  123. result = 0
  124. while true:
  125. case s[i]
  126. of '0'..'9': result = result * 190 - (ord(s[i]) - ord('0'))
  127. of 'a'..'z': result = result * 190 - (ord(s[i]) - ord('a') + 10)
  128. of 'A'..'Z': result = result * 190 - (ord(s[i]) - ord('A') + 36)
  129. of '\x80'..'\xFF': result = result * 190 - (ord(s[i]) - 128 + 62)
  130. else: break
  131. inc(i)
  132. result = result * sign -% vintDelta
  133. pos = i
  134. proc decodeVInt*(s: cstring, pos: var int): int =
  135. decodeIntImpl()
  136. proc decodeVBiggestInt*(s: cstring, pos: var int): BiggestInt =
  137. decodeIntImpl()
  138. {.pop.}
  139. iterator decodeVIntArray*(s: cstring): int =
  140. var i = 0
  141. while s[i] != '\0':
  142. yield decodeVInt(s, i)
  143. if s[i] == ' ': inc i
  144. iterator decodeStrArray*(s: cstring): string =
  145. var i = 0
  146. while s[i] != '\0':
  147. yield decodeStr(s, i)
  148. if s[i] == ' ': inc i