This solution is very fragile, and will break the moment any changes are made to Hasher's memory layout. I'm not recommending it unless you're in a situation where you truly need reproducible hashes across executions and don't want an external library. Additionally, for the reasons mentioned above, you probably truly do not need this. The hashing algorithm makes no promises to be stable across Swift versions.
This is 50 shades of wrong but I needed runtime seeding of Hashable and there was no public API, so here's my solution:
public extension Hasher {
init(seed: [UInt8]) {
guard seed.count == 32 else {
preconditionFailure("Seed must be exactly 32 bytes")
}
let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: MemoryLayout<Hasher>.size, alignment: MemoryLayout<Hasher>.alignment)
for index in 0..<8 {
buffer[index] = 0
}
for index in 8..<40 {
buffer[index] = seed[index - 8]
}
for index in 40..<72 {
buffer[index] = 0
}
defer {
buffer.deallocate()
}
self = buffer.bindMemory(to: Hasher.self).baseAddress!.move()
}
}
and an example which will yield the same finalized hash across executions:
// Returns an array of random UInt8 that you can pass to Hasher(seed:), if you need to bootstrap a seed for your project
func randomSeed() -> [UInt8] {
Array(unsafeUninitializedCapacity: 32) { pointer, initializedCount in
for i in 0..<32 {
pointer[i] = UInt8(arc4random())
}
initializedCount = 32
}
}
let PROJECT_SEED: [UInt8] = [235, 45, 216, 214, 16, 112, 184, 247, 156, 181, 159, 38, 245, 165, 35, 224, 255, 58, 206, 200, 16, 122, 174, 232, 130, 191, 143, 49, 246, 179, 41, 240]
var hasher = Hasher(seed: PROJECT_SEED)
"asdf".hash(into: &hasher)
print(hasher.finalize())