102 lines
2.5 KiB
Nim
102 lines
2.5 KiB
Nim
import hashtable
|
|
|
|
type
|
|
NdString* = ptr object
|
|
len*: uint32
|
|
hash*: uint32
|
|
chars*: UncheckedArray[char]
|
|
|
|
proc free*(ndStr: var NdString) =
|
|
dealloc(ndStr)
|
|
|
|
# hashes
|
|
|
|
proc fnv1a*(ndStr: NdString): int =
|
|
return ndStr.hash.int
|
|
#var hash = 2166136261'u32
|
|
#for i in countup(0, ndStr.len.int - 1):
|
|
# hash = hash xor (ndStr.chars[i]).uint32
|
|
# hash *= 16777619
|
|
#return hash.int
|
|
|
|
proc fnv1a*(str: string): int = # SHOULD RETURN THE SAME HASH AS AN NDSTRING WITH THE SAME CONTENTS
|
|
var hash = 2166136261'u32
|
|
for i in countup(0, str.len - 1):
|
|
hash = hash xor (str[i]).uint32
|
|
hash *= 16777619
|
|
return hash.int
|
|
|
|
proc fnv1a*(str: char): int = # SHOULD RETURN THE SAME AS A 1 LENGTH STRING
|
|
var hash = 2166136261'u32
|
|
hash = hash xor str.uint32
|
|
hash *= 16777619
|
|
return hash.int
|
|
|
|
|
|
# equals
|
|
|
|
proc equal*(left, right: NdString): bool =
|
|
left == right
|
|
|
|
var ndStrings = newTable[NdString, NdString]()
|
|
|
|
proc newString*(str: string): NdString =
|
|
let strlen = str.len()
|
|
let hash = str.fnv1a()
|
|
|
|
if strlen > 0:
|
|
let interned = ndStrings.tableFindString(str[0].unsafeAddr, strlen, hash)
|
|
if interned != nil:
|
|
return interned
|
|
else:
|
|
let interned = ndStrings.tableFindString(nil, 0, hash)
|
|
if interned != nil:
|
|
return interned
|
|
|
|
let len = 8 + strlen
|
|
result = cast[NdString](alloc(len))
|
|
result.len = strlen.uint32
|
|
result.hash = hash.uint32
|
|
if strlen > 0:
|
|
copyMem(result.chars[0].unsafeAddr, str[0].unsafeAddr, strlen)
|
|
|
|
discard ndStrings.tableSet(result, nil)
|
|
|
|
proc newString*(str: char): NdString =
|
|
let hash = str.fnv1a()
|
|
|
|
let interned = ndStrings.tableFindString(str.unsafeAddr, 1, hash)
|
|
if interned != nil:
|
|
return interned
|
|
|
|
let len = 8 + 1
|
|
result = cast[NdString](alloc(len))
|
|
result.len = 1.uint32
|
|
result.hash = hash.uint32
|
|
result.chars[0] = str
|
|
|
|
discard ndStrings.tableSet(result, nil)
|
|
|
|
|
|
proc resetInternedStrings* =
|
|
ndStrings.free()
|
|
ndStrings = newTable[NdString, NdString]()
|
|
|
|
proc `$`*(ndStr: NdString): string =
|
|
result = newString(ndStr.len.int)
|
|
if ndStr.len.int > 0:
|
|
copyMem(result[0].unsafeAddr, ndStr.chars[0].unsafeAddr, ndStr.len.int)
|
|
|
|
proc `&`*(left, right: NdString): NdString =
|
|
# TODO optimize this later when strings will be benchmarked
|
|
let combined: string = $left & $right
|
|
newString(combined)
|
|
|
|
proc getLength*(ndStr: NdString): int =
|
|
ndStr.len.int
|
|
|
|
proc getIndex*(ndStr: NdString, index: int): NdString =
|
|
newString(ndStr.chars[index])
|
|
|
|
proc getIndexAsChar*(ndStr: NdString, index: int): char =
|
|
ndStr.chars[index] |