blob: cc4ab2792da6033607cdcb4db77a72775bc063b8 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SwiftShims
func _emptyASCIIHashBuffer() -> _UIntBuffer<UInt64, UInt8> {
var buffer = _UIntBuffer<UInt64, UInt8>()
// We don't want the unused bits of a partially filled buffer to collide
// with trailing nuls when hashing
buffer._storage = UInt64.max
return buffer
}
internal struct ASCIIHasher {
private var buffer = _emptyASCIIHashBuffer()
internal mutating func consume() -> UInt64? {
if !buffer.isEmpty {
defer { resetBuffer() }
return buffer._storage
}
return nil
}
private mutating func resetBuffer() {
buffer = _emptyASCIIHashBuffer()
}
internal mutating func append(_ c: UInt8) -> UInt64? {
if buffer.count < buffer.capacity {
buffer.append(c)
}
if buffer.count == buffer.capacity {
defer { resetBuffer() }
return buffer._storage
}
return nil
}
}
extension _UnmanagedString where CodeUnit == UInt8 {
// NOT @usableFromInline
@effects(releasenone)
internal func hashASCII(into hasher: inout _Hasher) {
var asciiHasher = ASCIIHasher()
for c in self {
if let combined = asciiHasher.append(UInt8(truncatingIfNeeded: c)) {
hasher.append(combined)
}
}
if let combined = asciiHasher.consume() {
hasher.append(combined)
}
}
}
extension BidirectionalCollection where Element == UInt16, SubSequence == Self {
// NOT @usableFromInline
internal func hashUTF16(into hasher: inout _Hasher) {
var asciiHasher = ASCIIHasher()
for i in self.indices {
let cu = self[i]
let cuIsASCII = cu <= 0x7F
let isSingleSegmentScalar = self.hasNormalizationBoundary(after: i)
guard cuIsASCII && isSingleSegmentScalar else {
if let combined = asciiHasher.consume() {
hasher.append(combined)
}
let codeUnitSequence = IteratorSequence(
_NormalizedCodeUnitIterator(self[i..<endIndex])
)
for element in codeUnitSequence {
hasher.append(UInt(element))
}
return
}
if let combined = asciiHasher.append(UInt8(truncatingIfNeeded: cu)) {
hasher.append(combined)
}
}
if let combined = asciiHasher.consume() {
hasher.append(combined)
}
}
}
extension _UnmanagedString where CodeUnit == UInt8 {
@effects(releasenone)
@usableFromInline
internal func computeHashValue(into hasher: inout _Hasher) {
self.hashASCII(into: &hasher)
}
}
extension _UnmanagedString where CodeUnit == UInt16 {
@effects(releasenone)
@usableFromInline
internal func computeHashValue(into hasher: inout _Hasher) {
self.hashUTF16(into: &hasher)
}
}
extension _UnmanagedOpaqueString {
@usableFromInline
internal func computeHashValue(into hasher: inout _Hasher) {
self.hashUTF16(into: &hasher)
}
}
extension _SmallUTF8String {
@usableFromInline
@inlinable
internal func computeHashValue(into hasher: inout _Hasher) {
#if arch(i386) || arch(arm)
unsupportedOn32bit()
#else
if isASCII {
return self.withUnmanagedASCII { $0.computeHashValue(into: &hasher) }
}
return self.withUnmanagedUTF16 { $0.computeHashValue(into: &hasher) }
#endif // 64-bit
}
}
extension _StringGuts {
@usableFromInline
@effects(releasenone) // FIXME: Is this guaranteed in the opaque case?
internal func _hash(into hasher: inout _Hasher) {
if _isSmall {
return _smallUTF8String.computeHashValue(into: &hasher)
}
defer { _fixLifetime(self) }
if _slowPath(_isOpaque) {
_asOpaque().computeHashValue(into: &hasher)
return
}
if isASCII {
_unmanagedASCIIView.computeHashValue(into: &hasher)
return
}
_unmanagedUTF16View.computeHashValue(into: &hasher)
}
@usableFromInline
@effects(releasenone) // FIXME: Is this guaranteed in the opaque case?
internal func _hash(_ range: Range<Int>, into hasher: inout _Hasher) {
if _isSmall {
return _smallUTF8String[range].computeHashValue(into: &hasher)
}
defer { _fixLifetime(self) }
if _slowPath(_isOpaque) {
_asOpaque()[range].computeHashValue(into: &hasher)
return
}
if isASCII {
_unmanagedASCIIView[range].computeHashValue(into: &hasher)
return
}
_unmanagedUTF16View[range].computeHashValue(into: &hasher)
}
}
extension String : Hashable {
/// The string's hash value.
///
/// Hash values are not guaranteed to be equal across different executions of
/// your program. Do not save hash values to use during a future execution.
@inlinable
public var hashValue: Int {
return _hashValue(for: self)
}
@inlinable
public func _hash(into hasher: inout _Hasher) {
_guts._hash(into: &hasher)
}
}
extension StringProtocol {
@inlinable
public var hashValue : Int {
return _hashValue(for: self)
}
@inlinable
public func _hash(into hasher: inout _Hasher) {
_wholeString._guts._hash(_encodedOffsetRange, into: &hasher)
}
}