mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-07 12:24:19 +00:00
added experimental undokumented std/varints module
This commit is contained in:
145
lib/std/varints.nim
Normal file
145
lib/std/varints.nim
Normal file
@@ -0,0 +1,145 @@
|
||||
#
|
||||
#
|
||||
# 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 + uint64 z[1] + 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)
|
||||
|
||||
# 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")
|
||||
Reference in New Issue
Block a user