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)