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 = 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 = var hash = 2166136261'u32 for i in countup(0, str.len - 1): hash = hash xor (str[i]).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() let interned = ndStrings.tableFindString(str[0].unsafeAddr, strlen, hash) if interned != nil: return interned let len = 8 + strlen result = cast[NdString](alloc(len)) result.len = strlen.uint32 result.hash = hash.uint32 copyMem(result.chars[0].unsafeAddr, str[0].unsafeAddr, strlen) discard ndStrings.tableSet(result, nil) proc resetInternedStrings* = ndStrings.free() ndStrings = newTable[NdString, NdString]() proc `$`*(ndStr: NdString): string = result = newString(ndStr.len.int) 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 = # TODO optimize this later newString($($ndStr)[index])