Skip to content

[stdlib] Finalize one-shot hashing interface #19495

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
Sep 24, 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
6 changes: 3 additions & 3 deletions stdlib/private/StdlibUnittest/StdlibUnittest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2378,7 +2378,7 @@ public func checkHashable<Instances: Collection>(
""",
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
expectEqual(
x._rawHashValue(seed: (0, 0)), y._rawHashValue(seed: (0, 0)),
x._rawHashValue(seed: 0), y._rawHashValue(seed: 0),
"""
_rawHashValue expected to match, found to differ
lhs (at index \(i)): \(x)
Expand All @@ -2399,8 +2399,8 @@ public func checkHashable<Instances: Collection>(
""",
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
expectTrue(
(0..<10 as Range<UInt64>).contains { i in
x._rawHashValue(seed: (0, i)) != y._rawHashValue(seed: (0, i))
(0..<10).contains { i in
x._rawHashValue(seed: i) != y._rawHashValue(seed: i)
},
"""
_rawHashValue(seed:) expected to differ, found to match
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/SDK/ObjectiveC/ObjectiveC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ extension NSObject : Equatable, Hashable {
hasher.combine(hashValue)
}

public func _rawHashValue(seed: (UInt64, UInt64)) -> Int {
public func _rawHashValue(seed: Int) -> Int {
// FIXME: We should use self.hash here, but hashValue is currently
// overridable.
return self.hashValue._rawHashValue(seed: seed)
Expand Down
6 changes: 2 additions & 4 deletions stdlib/public/SwiftShims/GlobalObjects.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ struct _SwiftDictionaryBodyStorage {
__swift_intptr_t count;
__swift_intptr_t capacity;
__swift_intptr_t scale;
__swift_uint64_t seed0;
__swift_uint64_t seed1;
__swift_intptr_t seed;
void *rawKeys;
void *rawValues;
};
Expand All @@ -54,8 +53,7 @@ struct _SwiftSetBodyStorage {
__swift_intptr_t count;
__swift_intptr_t capacity;
__swift_intptr_t scale;
__swift_uint64_t seed0;
__swift_uint64_t seed1;
__swift_intptr_t seed;
void *rawElements;
};

Expand Down
6 changes: 3 additions & 3 deletions stdlib/public/core/AnyHashable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ internal protocol _AnyHashableBox {
func _isEqual(to box: _AnyHashableBox) -> Bool?
var _hashValue: Int { get }
func _hash(into hasher: inout Hasher)
func _rawHashValue(_seed: (UInt64, UInt64)) -> Int
func _rawHashValue(_seed: Int) -> Int

var _base: Any { get }
func _unbox<T: Hashable>() -> T?
Expand Down Expand Up @@ -97,7 +97,7 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
}

@inlinable // FIXME(sil-serialize-all)
func _rawHashValue(_seed: (UInt64, UInt64)) -> Int {
func _rawHashValue(_seed: Int) -> Int {
return _baseHashable._rawHashValue(seed: _seed)
}

Expand Down Expand Up @@ -269,7 +269,7 @@ extension AnyHashable : Hashable {
}

@inlinable // FIXME(sil-serialize-all)
public func _rawHashValue(seed: (UInt64, UInt64)) -> Int {
public func _rawHashValue(seed: Int) -> Int {
return _box._canonicalBox._rawHashValue(_seed: seed)
}
}
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/Array.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1836,7 +1836,7 @@ internal struct _ArrayAnyHashableBox<Element: Hashable>
}
}

func _rawHashValue(_seed: (UInt64, UInt64)) -> Int {
func _rawHashValue(_seed: Int) -> Int {
var hasher = Hasher(_seed: _seed)
self._hash(into: &hasher)
return hasher._finalize()
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/Dictionary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1584,7 +1584,7 @@ internal struct _DictionaryAnyHashableBox<Key: Hashable, Value: Hashable>
_canonical.hash(into: &hasher)
}

internal func _rawHashValue(_seed: Hasher._Seed) -> Int {
internal func _rawHashValue(_seed: Int) -> Int {
return _canonical._rawHashValue(seed: _seed)
}

Expand Down
7 changes: 3 additions & 4 deletions stdlib/public/core/DictionaryStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ internal class _RawDictionaryStorage: _SwiftNativeNSDictionary {
internal final var _scale: Int

@usableFromInline
internal final var _seed: Hasher._Seed
internal final var _seed: Int

@usableFromInline
@nonobjc
Expand Down Expand Up @@ -287,9 +287,8 @@ final internal class _DictionaryStorage<Key: Hashable, Value>
// FIXME: Use true per-instance seeding instead. Per-capacity seeding still
// leaves hash values the same in same-sized tables, which may affect
// operations on two tables at once. (E.g., union.)
storage._seed = (
Hasher._seed.0 ^ UInt64(truncatingIfNeeded: scale),
Hasher._seed.1)
storage._seed = scale

// Initialize hash table metadata.
storage._hashTable.clear()
return storage
Expand Down
6 changes: 3 additions & 3 deletions stdlib/public/core/FloatingPointTypes.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -1519,7 +1519,7 @@ extension ${Self} : Hashable {
}

@inlinable
public func _rawHashValue(seed: (UInt64, UInt64)) -> Int {
public func _rawHashValue(seed: Int) -> Int {
// To satisfy the axiom that equality implies hash equality, we need to
// finesse the hash value of -0.0 to match +0.0.
let v = isZero ? 0 : self
Expand Down Expand Up @@ -1839,7 +1839,7 @@ internal struct _${Self}AnyHashableBox: _AnyHashableBox {
}

internal var _hashValue: Int {
return _rawHashValue(_seed: Hasher._seed)
return _rawHashValue(_seed: 0)
}

internal func _hash(into hasher: inout Hasher) {
Expand All @@ -1848,7 +1848,7 @@ internal struct _${Self}AnyHashableBox: _AnyHashableBox {
hasher.combine(_value)
}

internal func _rawHashValue(_seed: (UInt64, UInt64)) -> Int {
internal func _rawHashValue(_seed: Int) -> Int {
var hasher = Hasher(_seed: _seed)
_hash(into: &hasher)
return hasher.finalize()
Expand Down
10 changes: 3 additions & 7 deletions stdlib/public/core/Hashable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,13 @@ public protocol Hashable : Equatable {
// Raw top-level hashing interface. Some standard library types (mostly
// primitives) specialize this to eliminate small resiliency overheads. (This
// only matters for tiny keys.)
//
// FIXME(hasher): Change to take a Hasher instead. To achieve the same
// performance, this requires Set and Dictionary to store their fully
// initialized local hashers, not just their seeds.
func _rawHashValue(seed: (UInt64, UInt64)) -> Int
func _rawHashValue(seed: Int) -> Int
}

extension Hashable {
@inlinable
@inline(__always)
public func _rawHashValue(seed: (UInt64, UInt64)) -> Int {
public func _rawHashValue(seed: Int) -> Int {
var hasher = Hasher(_seed: seed)
hasher.combine(self)
return hasher._finalize()
Expand All @@ -147,7 +143,7 @@ extension Hashable {
@inlinable
@inline(__always)
public func _hashValue<H: Hashable>(for value: H) -> Int {
return value._rawHashValue(seed: Hasher._seed)
return value._rawHashValue(seed: 0)
}

// Called by the SwiftValue implementation.
Expand Down
91 changes: 49 additions & 42 deletions stdlib/public/core/Hasher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,27 @@ import SwiftShims
// rdar://problem/38549901
@usableFromInline
internal protocol _HasherCore {
init(seed: Hasher._Seed)
init(rawSeed: (UInt64, UInt64))
mutating func compress(_ value: UInt64)
mutating func finalize(tailAndByteCount: UInt64) -> UInt64
}

// FIXME(hasher): Remove once one-shot hashing has a hasher parameter.
/// Generate a seed value from the current state of this hasher.
///
/// Note that the returned value is not same as the seed that was used to
/// initialize the hasher.
///
/// This comes handy when type's _hash(into:) implementation needs to perform
/// one-shot hashing for some of its components. (E.g., for commutative
/// hashing.)
func _generateSeed() -> Hasher._Seed
extension _HasherCore {
@inline(__always)
internal init() {
self.init(rawSeed: Hasher._executionSeed)
}

@inline(__always)
internal init(seed: Int) {
let executionSeed = Hasher._executionSeed
// Prevent sign-extending the supplied seed; this makes testing slightly
// easier.
let seed = UInt(bitPattern: seed)
self.init(rawSeed: (
executionSeed.0 ^ UInt64(truncatingIfNeeded: seed),
executionSeed.1))
}
}

@inline(__always)
Expand Down Expand Up @@ -155,14 +162,24 @@ internal struct _HasherTailBuffer {
// FIXME: Remove @usableFromInline and @_fixed_layout once Hasher is resilient.
// rdar://problem/38549901
@usableFromInline @_fixed_layout
internal struct _BufferingHasher<Core: _HasherCore> {
internal struct _BufferingHasher<RawCore: _HasherCore> {
private var _buffer: _HasherTailBuffer
private var _core: Core
private var _core: RawCore

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

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

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

@inline(__always)
Expand Down Expand Up @@ -249,13 +266,6 @@ internal struct _BufferingHasher<Core: _HasherCore> {
}
}

// Generate a seed value from the current state of this hasher.
// FIXME(hasher): Remove
@inline(__always)
internal func _generateSeed() -> Hasher._Seed {
return _core._generateSeed()
}

@inline(__always)
internal mutating func finalize() -> UInt64 {
return _core.finalize(tailAndByteCount: _buffer.value)
Expand Down Expand Up @@ -296,9 +306,6 @@ public struct Hasher {
@usableFromInline
internal typealias Core = _BufferingHasher<RawCore>

@usableFromInline
internal typealias _Seed = (UInt64, UInt64)

internal var _core: Core

/// Creates a new hasher.
Expand All @@ -307,14 +314,22 @@ public struct Hasher {
/// startup, usually from a high-quality random source.
@_effects(releasenone)
public init() {
self._core = Core(seed: Hasher._seed)
self._core = Core()
}

/// 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 seed: _Seed) {
self._core = Core(seed: seed)
internal init(_seed: Int) {
self._core = Core(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))
}

/// Indicates whether we're running in an environment where hashing needs to
Expand All @@ -333,8 +348,8 @@ public struct Hasher {

/// The 128-bit hash seed used to initialize the hasher state. Initialized
/// once during process startup.
@inlinable
internal static var _seed: _Seed {
@inlinable // @testable
internal static var _executionSeed: (UInt64, UInt64) {
@inline(__always)
get {
// The seed itself is defined in C++ code so that it is initialized during
Expand Down Expand Up @@ -427,17 +442,9 @@ public struct Hasher {
return Int(truncatingIfNeeded: core.finalize())
}

// Generate a seed value from the current state of this hasher.
// FIXME(hasher): Remove
@_effects(readnone)
@usableFromInline
internal func _generateSeed() -> Hasher._Seed {
return _core._generateSeed()
}

@_effects(readnone)
@usableFromInline
internal static func _hash(seed: _Seed, _ value: UInt64) -> Int {
internal static func _hash(seed: Int, _ value: UInt64) -> Int {
var core = RawCore(seed: seed)
core.compress(value)
let tbc = _HasherTailBuffer(tail: 0, byteCount: 8)
Expand All @@ -446,7 +453,7 @@ public struct Hasher {

@_effects(readnone)
@usableFromInline
internal static func _hash(seed: _Seed, _ value: UInt) -> Int {
internal static func _hash(seed: Int, _ value: UInt) -> Int {
var core = RawCore(seed: seed)
#if arch(i386) || arch(arm)
_sanityCheck(UInt.bitWidth < UInt64.bitWidth)
Expand All @@ -464,7 +471,7 @@ public struct Hasher {
@_effects(readnone)
@usableFromInline
internal static func _hash(
seed: _Seed,
seed: Int,
bytes value: UInt64,
count: Int) -> Int {
_sanityCheck(count >= 0 && count < 8)
Expand All @@ -476,7 +483,7 @@ public struct Hasher {
@_effects(readnone)
@usableFromInline
internal static func _hash(
seed: _Seed,
seed: Int,
bytes: UnsafeRawBufferPointer) -> Int {
var core = Core(seed: seed)
core.combine(bytes: bytes)
Expand Down
4 changes: 2 additions & 2 deletions stdlib/public/core/IntegerTypes.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -1673,7 +1673,7 @@ extension ${Self} : Hashable {
}

@inlinable
public func _rawHashValue(seed: (UInt64, UInt64)) -> Int {
public func _rawHashValue(seed: Int) -> Int {
% if bits == 64:
return Hasher._hash(seed: seed, UInt64(_value))
% elif bits == word_bits:
Expand Down Expand Up @@ -1905,7 +1905,7 @@ internal struct _IntegerAnyHashableBox<
_value.hash(into: &hasher)
}

internal func _rawHashValue(_seed: (UInt64, UInt64)) -> Int {
internal func _rawHashValue(_seed: Int) -> Int {
_sanityCheck(Base.self == UInt64.self || Base.self == Int64.self,
"self isn't canonical")
return _value._rawHashValue(seed: _seed)
Expand Down
4 changes: 2 additions & 2 deletions stdlib/public/core/NewtypeWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ extension _SwiftNewtypeWrapper where Self: Hashable, Self.RawValue: Hashable {
}

@inlinable // FIXME(sil-serialize-all)
public func _rawHashValue(seed: (UInt64, UInt64)) -> Int {
public func _rawHashValue(seed: Int) -> Int {
return rawValue._rawHashValue(seed: seed)
}
}
Expand Down Expand Up @@ -75,7 +75,7 @@ where Base: _SwiftNewtypeWrapper & Hashable, Base.RawValue: Hashable {
_preconditionFailure("_hash(into:) called on non-canonical AnyHashable box")
}

func _rawHashValue(_seed: (UInt64, UInt64)) -> Int {
func _rawHashValue(_seed: Int) -> Int {
_preconditionFailure("_rawHashValue(_seed:) called on non-canonical AnyHashable box")
}

Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/Pointer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ extension _Pointer /*: Hashable */ {
}

@inlinable
public func _rawHashValue(seed: (UInt64, UInt64)) -> Int {
public func _rawHashValue(seed: Int) -> Int {
return Hasher._hash(seed: seed, UInt(bitPattern: self))
}
}
Expand Down
Loading