209 lines
5.9 KiB
Nim
209 lines
5.9 KiB
Nim
|
import std/strformat
|
||
|
import std/hashes
|
||
|
|
||
|
const header_size = when not defined(release):
|
||
|
sizeof(pointer)*2 + sizeof(Natural)
|
||
|
else:
|
||
|
sizeof(pointer)
|
||
|
|
||
|
type ArrRef*[T] = ref object
|
||
|
endp: pointer
|
||
|
when not defined(release):
|
||
|
# to see in the debugger
|
||
|
size_bytes: Natural
|
||
|
arr_ptr: ptr T
|
||
|
arr: UncheckedArray[T]
|
||
|
|
||
|
proc newArrRef*[T](size: Natural): ArrRef[T] =
|
||
|
when defined(TCC):
|
||
|
var r: ref pointer # workaround for tcc bug
|
||
|
unsafeNew(r, size * sizeof(T) + header_size)
|
||
|
result = cast[ArrRef[T]](r)
|
||
|
else:
|
||
|
unsafeNew(result, size * sizeof(T) + header_size)
|
||
|
result.endp = addr(result.arr[size])
|
||
|
when not defined(release):
|
||
|
result.size_bytes = size * sizeof(T)
|
||
|
result.arr_ptr = result.arr[0].addr
|
||
|
|
||
|
template len*[T](a: ArrRef[T]): Natural =
|
||
|
((cast[int](a.endp) -% cast[int](a)) -% header_size) div sizeof(T)
|
||
|
|
||
|
template byteLen*[T](a: ArrRef[T]): Natural =
|
||
|
((cast[int](a.endp) -% cast[int](a)) -% header_size)
|
||
|
|
||
|
template low*[T](a: ArrRef[T]): Natural = 0
|
||
|
|
||
|
template high*[T](a: ArrRef[T]): int = a.len - 1
|
||
|
|
||
|
template rangeError[T](a: ArrRef[T], i: Natural) =
|
||
|
raise newException(RangeDefect, &"index out of range: {i} >= {a.len}")
|
||
|
|
||
|
proc `[]`*[T](a: ArrRef[T], i: Natural): var T =
|
||
|
let p = cast[int](a) +% header_size +% sizeof(T) * i
|
||
|
when compileOption("rangechecks"):
|
||
|
if p +% sizeof(T) > cast[int](a.endp): rangeError(a, i)
|
||
|
cast[ptr T](p)[]
|
||
|
|
||
|
proc `[]=`*[T](a: ArrRef[T], i: Natural, v: T) =
|
||
|
let p = cast[int](a) +% header_size +% sizeof(T) * i
|
||
|
when compileOption("rangechecks"):
|
||
|
if p +% sizeof(T) > cast[int](a.endp): rangeError(a, i)
|
||
|
cast[ptr T](p)[] = v
|
||
|
|
||
|
template toPointer*[T](a: ArrRef[T]): pointer = a.arr[0].addr
|
||
|
|
||
|
iterator items*[T](a: ArrRef[T]): T =
|
||
|
for i in 0 ..< a.len:
|
||
|
yield a[i]
|
||
|
|
||
|
iterator pairs*[T](a: ArrRef[T]): tuple[key: int, val: T] =
|
||
|
for i in 0 ..< a.len:
|
||
|
yield (i, a[i])
|
||
|
|
||
|
iterator mitems*[T](a: ArrRef[T]): var T =
|
||
|
for i in 0 ..< a.len:
|
||
|
yield a[i]
|
||
|
|
||
|
iterator mpairs*[T](a: ArrRef[T]): tuple[key: int, val: var T] =
|
||
|
for i in 0 ..< a.len:
|
||
|
yield (i, a[i])
|
||
|
|
||
|
proc `$`*[T](a: ArrRef[T]): string =
|
||
|
result = "["
|
||
|
let hi = a.high
|
||
|
for i in 0 ..< hi:
|
||
|
result &= $a[i] & ", "
|
||
|
result &= $a[hi] & "]"
|
||
|
|
||
|
template to*[T](a: ArrRef[T], U: untyped): untyped =
|
||
|
cast[ArrRef[U]](a)
|
||
|
|
||
|
# proc setCap*[T](a: var ArrRef[T], size: Natural) =
|
||
|
# let n = newArrRef[T](size)
|
||
|
# copyMem(n[0].addr, a[0].addr, min(size, a.len) * sizeof(T))
|
||
|
# a = n
|
||
|
|
||
|
# proc setGrow*[T](a: var ArrRef[T], i: Natural, v: T) =
|
||
|
# let p = cast[int](a) +% header_size +% sizeof(T) * i
|
||
|
# if p +% sizeof(T) > cast[int](a.endp):
|
||
|
# var cap = max(1, a.len)
|
||
|
# while i >= cap:
|
||
|
# if cap < 65535: cap *= 2
|
||
|
# else: cap = (cap * 3) div 2
|
||
|
# a.setCap cap
|
||
|
# cast[ptr T](p)[] = v
|
||
|
|
||
|
proc fill*[T](a: ArrRef[T], v: T) =
|
||
|
for i in a.low .. a.high: a[i] = v
|
||
|
|
||
|
proc newArrRefWith*[T](size: Natural, v: T): ArrRef[T] =
|
||
|
when defined(TCC):
|
||
|
var r: ref pointer # workaround for tcc bug
|
||
|
unsafeNew(r, size * sizeof(T) + header_size)
|
||
|
result = cast[ArrRef[T]](r)
|
||
|
else:
|
||
|
unsafeNew(result, size * sizeof(T) + header_size)
|
||
|
result.endp = addr(result.arr[size])
|
||
|
when not defined(release):
|
||
|
result.size_bytes = size * sizeof(T)
|
||
|
result.arr_ptr = result.arr[0].addr
|
||
|
for i in result.low .. result.high: result[i] = v
|
||
|
|
||
|
proc newArrRef*[T](s: seq[T] | ArrRef[T]): ArrRef[T] =
|
||
|
result = newArrRef[T](s.len)
|
||
|
if s.len != 0:
|
||
|
copyMem(result.arr.addr, s[0].addr, s.len * sizeof(T))
|
||
|
|
||
|
proc newArrRef*[T; U: not T](s: seq[U]): ArrRef[T] =
|
||
|
result = newArrRef[T](s.len)
|
||
|
for i,v in s: result[i] = v.T
|
||
|
|
||
|
when defined(isNimSkull):
|
||
|
# mainline nim is WAY too buggy to handle this
|
||
|
proc newSeq*[T: not ref](a: ArrRef[T]): var seq[T] =
|
||
|
when defined(isNimSkull):
|
||
|
result = newSeqUninitialized[T](a.len)
|
||
|
# elif compiles(newSeqUninit[T](a.len)):
|
||
|
# result = newSeqUninit[T](a.len)
|
||
|
# else:
|
||
|
# result = newSeqOfCap[T](a.len)
|
||
|
if a.len != 0:
|
||
|
copyMem(result[0].addr, a.arr.addr, result.len * sizeof(T))
|
||
|
|
||
|
# The following is just copied straight from hashes.nim, just replacing openArray by ArrRef...
|
||
|
|
||
|
when defined(js):
|
||
|
proc imul(a, b: uint32): uint32 =
|
||
|
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul
|
||
|
let mask = 0xffff'u32
|
||
|
var
|
||
|
aHi = (a shr 16) and mask
|
||
|
aLo = a and mask
|
||
|
bHi = (b shr 16) and mask
|
||
|
bLo = b and mask
|
||
|
result = (aLo * bLo) + (aHi * bLo + aLo * bHi) shl 16
|
||
|
else:
|
||
|
template imul(a, b: uint32): untyped = a * b
|
||
|
|
||
|
proc rotl32(x: uint32, r: int): uint32 {.inline.} =
|
||
|
(x shl r) or (x shr (32 - r))
|
||
|
|
||
|
proc hash*[T](arr: ArrRef[T]): Hash =
|
||
|
# https://github.com/PeterScott/murmur3/blob/master/murmur3.c
|
||
|
const
|
||
|
c1 = 0xcc9e2d51'u32
|
||
|
c2 = 0x1b873593'u32
|
||
|
n1 = 0xe6546b64'u32
|
||
|
m1 = 0x85ebca6b'u32
|
||
|
m2 = 0xc2b2ae35'u32
|
||
|
let
|
||
|
x = arr.to byte
|
||
|
size = len(x)
|
||
|
stepSize = 4 # 32-bit
|
||
|
n = size div stepSize
|
||
|
var
|
||
|
h1: uint32
|
||
|
i = 0
|
||
|
|
||
|
# body
|
||
|
while i < n * stepSize:
|
||
|
var k1: uint32
|
||
|
when defined(js) or defined(sparc) or defined(sparc64):
|
||
|
var j = stepSize
|
||
|
while j > 0:
|
||
|
dec j
|
||
|
k1 = (k1 shl 8) or (ord(x[i+j])).uint32
|
||
|
else:
|
||
|
k1 = cast[ptr uint32](unsafeAddr x[i])[]
|
||
|
inc i, stepSize
|
||
|
|
||
|
k1 = imul(k1, c1)
|
||
|
k1 = rotl32(k1, 15)
|
||
|
k1 = imul(k1, c2)
|
||
|
|
||
|
h1 = h1 xor k1
|
||
|
h1 = rotl32(h1, 13)
|
||
|
h1 = h1*5 + n1
|
||
|
|
||
|
# tail
|
||
|
var k1: uint32
|
||
|
var rem = size mod stepSize
|
||
|
while rem > 0:
|
||
|
dec rem
|
||
|
k1 = (k1 shl 8) or (ord(x[i+rem])).uint32
|
||
|
k1 = imul(k1, c1)
|
||
|
k1 = rotl32(k1, 15)
|
||
|
k1 = imul(k1, c2)
|
||
|
h1 = h1 xor k1
|
||
|
|
||
|
# finalization
|
||
|
h1 = h1 xor size.uint32
|
||
|
h1 = h1 xor (h1 shr 16)
|
||
|
h1 = imul(h1, m1)
|
||
|
h1 = h1 xor (h1 shr 13)
|
||
|
h1 = imul(h1, m2)
|
||
|
h1 = h1 xor (h1 shr 16)
|
||
|
return cast[Hash](h1)
|
||
|
|