varints.nim 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2018 Nim contributors
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Note this API is still experimental! A variable length integer
  10. ## encoding implementation inspired by SQLite.
  11. const
  12. maxVarIntLen* = 9 ## the maximal number of bytes a varint can take
  13. proc readVu64*(z: openArray[byte]; pResult: var uint64): int =
  14. if z[0] <= 240:
  15. pResult = z[0]
  16. return 1
  17. if z[0] <= 248:
  18. if z.len < 2: return 0
  19. pResult = (uint64 z[0] - 241) * 256 + z[1].uint64 + 240
  20. return 2
  21. if z.len < int(z[0]-246): return 0
  22. if z[0] == 249:
  23. pResult = 2288u64 + 256u64*z[1].uint64 + z[2].uint64
  24. return 3
  25. if z[0] == 250:
  26. pResult = (z[1].uint64 shl 16u64) + (z[2].uint64 shl 8u64) + z[3].uint64
  27. return 4
  28. let x = (z[1].uint64 shl 24) + (z[2].uint64 shl 16) + (z[3].uint64 shl 8) + z[4].uint64
  29. if z[0] == 251:
  30. pResult = x
  31. return 5
  32. if z[0] == 252:
  33. pResult = (((uint64)x) shl 8) + z[5].uint64
  34. return 6
  35. if z[0] == 253:
  36. pResult = (((uint64)x) shl 16) + (z[5].uint64 shl 8) + z[6].uint64
  37. return 7
  38. if z[0] == 254:
  39. pResult = (((uint64)x) shl 24) + (z[5].uint64 shl 16) + (z[6].uint64 shl 8) + z[7].uint64
  40. return 8
  41. pResult = (((uint64)x) shl 32) +
  42. (0xffffffff'u64 and ((z[5].uint64 shl 24) +
  43. (z[6].uint64 shl 16) + (z[7].uint64 shl 8) + z[8].uint64))
  44. return 9
  45. proc varintWrite32(z: var openArray[byte]; y: uint32) =
  46. z[0] = uint8(y shr 24)
  47. z[1] = uint8(y shr 16)
  48. z[2] = uint8(y shr 8)
  49. z[3] = uint8(y)
  50. proc writeVu64*(z: var openArray[byte], x: uint64): int =
  51. ## Write a varint into z. The buffer z must be at least 9 characters
  52. ## long to accommodate the largest possible varint. Returns the number of
  53. ## bytes used.
  54. if x <= 240:
  55. z[0] = uint8 x
  56. return 1
  57. if x <= 2287:
  58. let y = uint32(x - 240)
  59. z[0] = uint8(y shr 8 + 241)
  60. z[1] = uint8(y and 255)
  61. return 2
  62. if x <= 67823:
  63. let y = uint32(x - 2288)
  64. z[0] = 249
  65. z[1] = uint8(y shr 8)
  66. z[2] = uint8(y and 255)
  67. return 3
  68. let y = uint32 x
  69. let w = uint32(x shr 32)
  70. if w == 0:
  71. if y <= 16777215:
  72. z[0] = 250
  73. z[1] = uint8(y shr 16)
  74. z[2] = uint8(y shr 8)
  75. z[3] = uint8(y)
  76. return 4
  77. z[0] = 251
  78. varintWrite32(toOpenArray(z, 1, z.high-1), y)
  79. return 5
  80. if w <= 255:
  81. z[0] = 252
  82. z[1] = uint8 w
  83. varintWrite32(toOpenArray(z, 2, z.high-2), y)
  84. return 6
  85. if w <= 65535:
  86. z[0] = 253
  87. z[1] = uint8(w shr 8)
  88. z[2] = uint8 w
  89. varintWrite32(toOpenArray(z, 3, z.high-3), y)
  90. return 7
  91. if w <= 16777215:
  92. z[0] = 254
  93. z[1] = uint8(w shr 16)
  94. z[2] = uint8(w shr 8)
  95. z[3] = uint8 w
  96. varintWrite32(toOpenArray(z, 4, z.high-4), y)
  97. return 8
  98. z[0] = 255
  99. varintWrite32(toOpenArray(z, 1, z.high-1), w)
  100. varintWrite32(toOpenArray(z, 5, z.high-5), y)
  101. return 9
  102. proc sar(a, b: int64): int64 =
  103. {.emit: [result, " = ", a, " >> ", b, ";"].}
  104. proc sal(a, b: int64): int64 =
  105. {.emit: [result, " = ", a, " << ", b, ";"].}
  106. proc encodeZigzag*(x: int64): uint64 {.inline.} =
  107. uint64(sal(x, 1)) xor uint64(sar(x, 63))
  108. proc decodeZigzag*(x: uint64): int64 {.inline.} =
  109. let casted = cast[int64](x)
  110. result = (`shr`(casted, 1)) xor (-(casted and 1))
  111. when isMainModule:
  112. #import random
  113. var dest: array[50, byte]
  114. var got: uint64
  115. for test in [0xFFFF_FFFF_FFFFF_FFFFu64, 77u64, 0u64, 10_000_000u64, uint64(high(int64)),
  116. uint64(high(int32)),uint64(low(int32)),uint64(low(int64))]:
  117. let wrLen = writeVu64(dest, test)
  118. let rdLen = readVu64(dest, got)
  119. assert wrLen == rdLen
  120. echo(if got == test: "YES" else: "NO")
  121. echo "number is ", got
  122. if encodeZigzag(decodeZigzag(test)) != test:
  123. echo "Failure for ", test, " ", encodeZigzag(decodeZigzag(test)), " ", decodeZigzag(test)
  124. for test in 0u64..300u64:
  125. let wrLen = writeVu64(dest, test)
  126. let rdLen = readVu64(dest, got)
  127. assert wrLen == rdLen
  128. if got != test:
  129. echo "BUG! expected: ", test, " got: ", got, " z0: ", dest[0]
  130. # check this also works for floats:
  131. for test in [0.0, 0.1, 2.0, +Inf, Nan, NegInf]:
  132. let t = cast[uint64](test)
  133. let wrLenB = writeVu64(dest, t)
  134. let rdLenB = readVu64(dest, got)
  135. assert wrLenB == rdLenB
  136. echo rdLenB
  137. echo(if cast[float64](got) == test: "YES" else: "NO")