123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- #
- #
- # Nim's Runtime Library
- # (c) Copyright 2018 Nim contributors
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- ## Note this API is still experimental! A variable length integer
- ## encoding implementation inspired by SQLite.
- const
- maxVarIntLen* = 9 ## the maximal number of bytes a varint can take
- proc readVu64*(z: openArray[byte]; pResult: var uint64): int =
- if z[0] <= 240:
- pResult = z[0]
- return 1
- if z[0] <= 248:
- if z.len < 2: return 0
- pResult = (uint64 z[0] - 241) * 256 + z[1].uint64 + 240
- return 2
- if z.len < int(z[0]-246): return 0
- if z[0] == 249:
- pResult = 2288u64 + 256u64*z[1].uint64 + z[2].uint64
- return 3
- if z[0] == 250:
- pResult = (z[1].uint64 shl 16u64) + (z[2].uint64 shl 8u64) + z[3].uint64
- return 4
- let x = (z[1].uint64 shl 24) + (z[2].uint64 shl 16) + (z[3].uint64 shl 8) + z[4].uint64
- if z[0] == 251:
- pResult = x
- return 5
- if z[0] == 252:
- pResult = (((uint64)x) shl 8) + z[5].uint64
- return 6
- if z[0] == 253:
- pResult = (((uint64)x) shl 16) + (z[5].uint64 shl 8) + z[6].uint64
- return 7
- if z[0] == 254:
- pResult = (((uint64)x) shl 24) + (z[5].uint64 shl 16) + (z[6].uint64 shl 8) + z[7].uint64
- return 8
- pResult = (((uint64)x) shl 32) +
- (0xffffffff'u64 and ((z[5].uint64 shl 24) +
- (z[6].uint64 shl 16) + (z[7].uint64 shl 8) + z[8].uint64))
- return 9
- proc varintWrite32(z: var openArray[byte]; y: uint32) =
- z[0] = uint8(y shr 24)
- z[1] = uint8(y shr 16)
- z[2] = uint8(y shr 8)
- z[3] = uint8(y)
- proc writeVu64*(z: var openArray[byte], x: uint64): int =
- ## Write a varint into z. The buffer z must be at least 9 characters
- ## long to accommodate the largest possible varint. Returns the number of
- ## bytes used.
- if x <= 240:
- z[0] = uint8 x
- return 1
- if x <= 2287:
- let y = uint32(x - 240)
- z[0] = uint8(y shr 8 + 241)
- z[1] = uint8(y and 255)
- return 2
- if x <= 67823:
- let y = uint32(x - 2288)
- z[0] = 249
- z[1] = uint8(y shr 8)
- z[2] = uint8(y and 255)
- return 3
- let y = uint32 x
- let w = uint32(x shr 32)
- if w == 0:
- if y <= 16777215:
- z[0] = 250
- z[1] = uint8(y shr 16)
- z[2] = uint8(y shr 8)
- z[3] = uint8(y)
- return 4
- z[0] = 251
- varintWrite32(toOpenArray(z, 1, z.high-1), y)
- return 5
- if w <= 255:
- z[0] = 252
- z[1] = uint8 w
- varintWrite32(toOpenArray(z, 2, z.high-2), y)
- return 6
- if w <= 65535:
- z[0] = 253
- z[1] = uint8(w shr 8)
- z[2] = uint8 w
- varintWrite32(toOpenArray(z, 3, z.high-3), y)
- return 7
- if w <= 16777215:
- z[0] = 254
- z[1] = uint8(w shr 16)
- z[2] = uint8(w shr 8)
- z[3] = uint8 w
- varintWrite32(toOpenArray(z, 4, z.high-4), y)
- return 8
- z[0] = 255
- varintWrite32(toOpenArray(z, 1, z.high-1), w)
- varintWrite32(toOpenArray(z, 5, z.high-5), y)
- return 9
- proc sar(a, b: int64): int64 =
- {.emit: [result, " = ", a, " >> ", b, ";"].}
- proc sal(a, b: int64): int64 =
- {.emit: [result, " = ", a, " << ", b, ";"].}
- proc encodeZigzag*(x: int64): uint64 {.inline.} =
- uint64(sal(x, 1)) xor uint64(sar(x, 63))
- proc decodeZigzag*(x: uint64): int64 {.inline.} =
- let casted = cast[int64](x)
- result = (`shr`(casted, 1)) xor (-(casted and 1))
- when isMainModule:
- #import random
- var dest: array[50, byte]
- var got: uint64
- for test in [0xFFFF_FFFF_FFFFF_FFFFu64, 77u64, 0u64, 10_000_000u64, uint64(high(int64)),
- uint64(high(int32)),uint64(low(int32)),uint64(low(int64))]:
- let wrLen = writeVu64(dest, test)
- let rdLen = readVu64(dest, got)
- assert wrLen == rdLen
- echo(if got == test: "YES" else: "NO")
- echo "number is ", got
- if encodeZigzag(decodeZigzag(test)) != test:
- echo "Failure for ", test, " ", encodeZigzag(decodeZigzag(test)), " ", decodeZigzag(test)
- for test in 0u64..300u64:
- let wrLen = writeVu64(dest, test)
- let rdLen = readVu64(dest, got)
- assert wrLen == rdLen
- if got != test:
- echo "BUG! expected: ", test, " got: ", got, " z0: ", dest[0]
- # check this also works for floats:
- for test in [0.0, 0.1, 2.0, +Inf, Nan, NegInf]:
- let t = cast[uint64](test)
- let wrLenB = writeVu64(dest, t)
- let rdLenB = readVu64(dest, got)
- assert wrLenB == rdLenB
- echo rdLenB
- echo(if cast[float64](got) == test: "YES" else: "NO")
|