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 {.inline.} = 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 {.inline.} = # 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 {.inline.} = # 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 newString($left & $right) 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]