Skip to content

[stdlib] Add 256 bits of extra state to Hasher #20202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 14 additions & 18 deletions stdlib/public/core/Hasher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,24 +162,24 @@ internal struct _HasherTailBuffer {
// FIXME: Remove @usableFromInline and @_fixed_layout once Hasher is resilient.
// rdar://problem/38549901
@usableFromInline @_fixed_layout
internal struct _BufferingHasher<RawCore: _HasherCore> {
internal struct _BufferingHasher<Core: _HasherCore> {
private var _buffer: _HasherTailBuffer
private var _core: RawCore
private var _core: Core

@inline(__always)
internal init(core: RawCore) {
internal init(core: Core) {
self._buffer = _HasherTailBuffer()
self._core = core
}

@inline(__always)
internal init() {
self.init(core: RawCore())
self.init(core: Core())
}

@inline(__always)
internal init(seed: Int) {
self.init(core: RawCore(seed: seed))
self.init(core: Core(seed: seed))
}

@inline(__always)
Expand Down Expand Up @@ -300,36 +300,32 @@ public struct Hasher {
// FIXME: Remove @usableFromInline once Hasher is resilient.
// rdar://problem/38549901
@usableFromInline
internal typealias RawCore = _SipHash13Core
// FIXME: Remove @usableFromInline once Hasher is resilient.
// rdar://problem/38549901
@usableFromInline
internal typealias Core = _BufferingHasher<RawCore>
internal typealias _BufferingCore = _BufferingHasher<_Core>

internal var _core: Core
internal var _core: _BufferingCore

/// Creates a new hasher.
///
/// The hasher uses a per-execution seed value that is set during process
/// startup, usually from a high-quality random source.
@_effects(releasenone)
public init() {
self._core = Core()
self._core = _BufferingCore()
}

/// Initialize a new hasher using the specified seed value.
/// The provided seed is mixed in with the global execution seed.
@usableFromInline
@_effects(releasenone)
internal init(_seed: Int) {
self._core = Core(seed: _seed)
self._core = _BufferingCore(seed: _seed)
}

/// Initialize a new hasher using the specified seed value.
@usableFromInline // @testable
@_effects(releasenone)
internal init(_rawSeed: (UInt64, UInt64)) {
self._core = Core(core: RawCore(rawSeed: _rawSeed))
self._core = _BufferingCore(core: _Core(rawSeed: _rawSeed))
}

/// Indicates whether we're running in an environment where hashing needs to
Expand Down Expand Up @@ -445,7 +441,7 @@ public struct Hasher {
@_effects(readnone)
@usableFromInline
internal static func _hash(seed: Int, _ value: UInt64) -> Int {
var core = RawCore(seed: seed)
var core = _Core(seed: seed)
core.compress(value)
let tbc = _HasherTailBuffer(tail: 0, byteCount: 8)
return Int(truncatingIfNeeded: core.finalize(tailAndByteCount: tbc.value))
Expand All @@ -454,7 +450,7 @@ public struct Hasher {
@_effects(readnone)
@usableFromInline
internal static func _hash(seed: Int, _ value: UInt) -> Int {
var core = RawCore(seed: seed)
var core = _Core(seed: seed)
#if arch(i386) || arch(arm)
_sanityCheck(UInt.bitWidth < UInt64.bitWidth)
let tbc = _HasherTailBuffer(
Expand All @@ -475,7 +471,7 @@ public struct Hasher {
bytes value: UInt64,
count: Int) -> Int {
_sanityCheck(count >= 0 && count < 8)
var core = RawCore(seed: seed)
var core = _Core(seed: seed)
let tbc = _HasherTailBuffer(tail: value, byteCount: count)
return Int(truncatingIfNeeded: core.finalize(tailAndByteCount: tbc.value))
}
Expand All @@ -485,7 +481,7 @@ public struct Hasher {
internal static func _hash(
seed: Int,
bytes: UnsafeRawBufferPointer) -> Int {
var core = Core(seed: seed)
var core = _BufferingCore(seed: seed)
core.combine(bytes: bytes)
return Int(truncatingIfNeeded: core.finalize())
}
Expand Down
235 changes: 71 additions & 164 deletions stdlib/public/core/SipHash.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,181 +19,88 @@
/// * Daniel J. Bernstein <[email protected]>
//===----------------------------------------------------------------------===//

// FIXME: Remove @usableFromInline and @_fixed_layout once Hasher is resilient.
// rdar://problem/38549901
@usableFromInline @_fixed_layout
internal struct _SipHashState {
// "somepseudorandomlygeneratedbytes"
fileprivate var v0: UInt64 = 0x736f6d6570736575
fileprivate var v1: UInt64 = 0x646f72616e646f6d
fileprivate var v2: UInt64 = 0x6c7967656e657261
fileprivate var v3: UInt64 = 0x7465646279746573

@inline(__always)
fileprivate init(rawSeed: (UInt64, UInt64)) {
v3 ^= rawSeed.1
v2 ^= rawSeed.0
v1 ^= rawSeed.1
v0 ^= rawSeed.0
}

@inline(__always)
fileprivate
static func _rotateLeft(_ x: UInt64, by amount: UInt64) -> UInt64 {
return (x &<< amount) | (x &>> (64 - amount))
}

@inline(__always)
fileprivate mutating func _round() {
v0 = v0 &+ v1
v1 = _SipHashState._rotateLeft(v1, by: 13)
v1 ^= v0
v0 = _SipHashState._rotateLeft(v0, by: 32)
v2 = v2 &+ v3
v3 = _SipHashState._rotateLeft(v3, by: 16)
v3 ^= v2
v0 = v0 &+ v3
v3 = _SipHashState._rotateLeft(v3, by: 21)
v3 ^= v0
v2 = v2 &+ v1
v1 = _SipHashState._rotateLeft(v1, by: 17)
v1 ^= v2
v2 = _SipHashState._rotateLeft(v2, by: 32)
}

@inline(__always)
fileprivate func _extract() -> UInt64 {
return v0 ^ v1 ^ v2 ^ v3
}
}

// FIXME: Remove @usableFromInline and @_fixed_layout once Hasher is resilient.
// rdar://problem/38549901
@usableFromInline @_fixed_layout
internal struct _SipHash13Core: _HasherCore {
private var _state: _SipHashState
extension Hasher {
// FIXME: Remove @usableFromInline and @_fixed_layout once Hasher is resilient.
// rdar://problem/38549901
@usableFromInline @_fixed_layout
internal struct _State {
// "somepseudorandomlygeneratedbytes"
fileprivate var v0: UInt64 = 0x736f6d6570736575
fileprivate var v1: UInt64 = 0x646f72616e646f6d
fileprivate var v2: UInt64 = 0x6c7967656e657261
fileprivate var v3: UInt64 = 0x7465646279746573
// The fields below are reserved for future use. They aren't currently used.
fileprivate var v4: UInt64 = 0
fileprivate var v5: UInt64 = 0
fileprivate var v6: UInt64 = 0
fileprivate var v7: UInt64 = 0

@inline(__always)
fileprivate init(rawSeed: (UInt64, UInt64)) {
v3 ^= rawSeed.1
v2 ^= rawSeed.0
v1 ^= rawSeed.1
v0 ^= rawSeed.0
}

@inline(__always)
internal init(rawSeed: (UInt64, UInt64)) {
_state = _SipHashState(rawSeed: rawSeed)
}
@inline(__always)
fileprivate
static func _rotateLeft(_ x: UInt64, by amount: UInt64) -> UInt64 {
return (x &<< amount) | (x &>> (64 - amount))
}

@inline(__always)
internal mutating func compress(_ m: UInt64) {
_state.v3 ^= m
_state._round()
_state.v0 ^= m
}
@inline(__always)
fileprivate mutating func _round() {
v0 = v0 &+ v1
v1 = Hasher._State._rotateLeft(v1, by: 13)
v1 ^= v0
v0 = Hasher._State._rotateLeft(v0, by: 32)
v2 = v2 &+ v3
v3 = Hasher._State._rotateLeft(v3, by: 16)
v3 ^= v2
v0 = v0 &+ v3
v3 = Hasher._State._rotateLeft(v3, by: 21)
v3 ^= v0
v2 = v2 &+ v1
v1 = Hasher._State._rotateLeft(v1, by: 17)
v1 ^= v2
v2 = Hasher._State._rotateLeft(v2, by: 32)
}

@inline(__always)
internal mutating func finalize(tailAndByteCount: UInt64) -> UInt64 {
compress(tailAndByteCount)
_state.v2 ^= 0xff
for _ in 0..<3 {
_state._round()
@inline(__always)
fileprivate func _extract() -> UInt64 {
return v0 ^ v1 ^ v2 ^ v3
}
return _state._extract()
}
}

internal struct _SipHash24Core: _HasherCore {
private var _state: _SipHashState

@inline(__always)
internal init(rawSeed: (UInt64, UInt64)) {
_state = _SipHashState(rawSeed: rawSeed)
}
extension Hasher {
// FIXME: Remove @usableFromInline and @_fixed_layout once Hasher is resilient.
// rdar://problem/38549901
@usableFromInline @_fixed_layout
internal struct _Core: _HasherCore {
private var _state: Hasher._State

@inline(__always)
internal mutating func compress(_ m: UInt64) {
_state.v3 ^= m
_state._round()
_state._round()
_state.v0 ^= m
}

@inline(__always)
internal mutating func finalize(tailAndByteCount: UInt64) -> UInt64 {
compress(tailAndByteCount)
@inline(__always)
internal init(rawSeed: (UInt64, UInt64)) {
_state = Hasher._State(rawSeed: rawSeed)
}

_state.v2 ^= 0xff
for _ in 0..<4 {
@inline(__always)
internal mutating func compress(_ m: UInt64) {
_state.v3 ^= m
_state._round()
_state.v0 ^= m
}
return _state._extract()
}
}

// FIXME: This type only exists to facilitate testing, and should not exist in
// production builds.
@usableFromInline // @testable
internal struct _SipHash13 {
internal typealias Core = _SipHash13Core

internal var _core: _BufferingHasher<Core>

@usableFromInline // @testable
internal init(_rawSeed: (UInt64, UInt64)) {
_core = _BufferingHasher(core: Core(rawSeed: _rawSeed))
}
@usableFromInline // @testable
internal mutating func _combine(_ v: UInt) { _core.combine(v) }
@usableFromInline // @testable
internal mutating func _combine(_ v: UInt64) { _core.combine(v) }
@usableFromInline // @testable
internal mutating func _combine(_ v: UInt32) { _core.combine(v) }
@usableFromInline // @testable
internal mutating func _combine(_ v: UInt16) { _core.combine(v) }
@usableFromInline // @testable
internal mutating func _combine(_ v: UInt8) { _core.combine(v) }
@usableFromInline // @testable
internal mutating func _combine(bytes v: UInt64, count: Int) {
_core.combine(bytes: v, count: count)
}
@usableFromInline // @testable
internal mutating func combine(bytes: UnsafeRawBufferPointer) {
_core.combine(bytes: bytes)
}
@usableFromInline // @testable
internal __consuming func finalize() -> UInt64 {
var core = _core
return core.finalize()
}
}

// FIXME: This type only exists to facilitate testing, and should not exist in
// production builds.
@usableFromInline // @testable
internal struct _SipHash24 {
internal typealias Core = _SipHash24Core

internal var _core: _BufferingHasher<Core>

@usableFromInline // @testable
internal init(_rawSeed: (UInt64, UInt64)) {
_core = _BufferingHasher(core: Core(rawSeed: _rawSeed))
}
@usableFromInline // @testable
internal mutating func _combine(_ v: UInt) { _core.combine(v) }
@usableFromInline // @testable
internal mutating func _combine(_ v: UInt64) { _core.combine(v) }
@usableFromInline // @testable
internal mutating func _combine(_ v: UInt32) { _core.combine(v) }
@usableFromInline // @testable
internal mutating func _combine(_ v: UInt16) { _core.combine(v) }
@usableFromInline // @testable
internal mutating func _combine(_ v: UInt8) { _core.combine(v) }
@usableFromInline // @testable
internal mutating func _combine(bytes v: UInt64, count: Int) {
_core.combine(bytes: v, count: count)
}
@usableFromInline // @testable
internal mutating func combine(bytes: UnsafeRawBufferPointer) {
_core.combine(bytes: bytes)
}
@usableFromInline // @testable
internal __consuming func finalize() -> UInt64 {
var core = _core
return core.finalize()
@inline(__always)
internal mutating func finalize(tailAndByteCount: UInt64) -> UInt64 {
compress(tailAndByteCount)
_state.v2 ^= 0xff
for _ in 0..<3 {
_state._round()
}
return _state._extract()
}
}
}
8 changes: 4 additions & 4 deletions stdlib/public/core/StringHashable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
import SwiftShims

extension _UnmanagedString where CodeUnit == UInt8 {
internal func hashASCII(into core: inout Hasher.Core) {
internal func hashASCII(into core: inout Hasher._BufferingCore) {
core.combine(bytes: rawBuffer)
}
}

extension BidirectionalCollection where Element == UInt16, SubSequence == Self {
internal func hashUTF16(into core: inout Hasher.Core) {
internal func hashUTF16(into core: inout Hasher._BufferingCore) {
for i in self.indices {
let cu = self[i]
let cuIsASCII = cu <= 0x7F
Expand Down Expand Up @@ -62,7 +62,7 @@ extension _UnmanagedString where CodeUnit == UInt16 {
}

internal func _rawHashValue(seed: Int) -> Int {
var core = Hasher.Core(seed: seed)
var core = Hasher._BufferingCore(seed: seed)
self.hashUTF16(into: &core)
return Int(truncatingIfNeeded: core.finalize())
}
Expand All @@ -75,7 +75,7 @@ extension _UnmanagedOpaqueString {
}

internal func _rawHashValue(seed: Int) -> Int {
var core = Hasher.Core(seed: seed)
var core = Hasher._BufferingCore(seed: seed)
self.hashUTF16(into: &core)
return Int(truncatingIfNeeded: core.finalize())
}
Expand Down
Loading