mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
185 lines
4.8 KiB
Swift
185 lines
4.8 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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 @_versioned
|
|
@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 @_versioned
|
|
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)
|
|
@_versioned
|
|
internal func computeHashValue(into hasher: inout _Hasher) {
|
|
self.hashASCII(into: &hasher)
|
|
}
|
|
}
|
|
|
|
extension _UnmanagedString where CodeUnit == UInt16 {
|
|
@effects(releasenone)
|
|
@_versioned
|
|
internal func computeHashValue(into hasher: inout _Hasher) {
|
|
self.hashUTF16(into: &hasher)
|
|
}
|
|
}
|
|
|
|
extension _UnmanagedOpaqueString {
|
|
@_versioned
|
|
internal func computeHashValue(into hasher: inout _Hasher) {
|
|
self.hashUTF16(into: &hasher)
|
|
}
|
|
}
|
|
|
|
extension _StringGuts {
|
|
@_versioned
|
|
@effects(releasenone) // FIXME: Is this guaranteed in the opaque case?
|
|
internal func _hash(into hasher: inout _Hasher) {
|
|
defer { _fixLifetime(self) }
|
|
if _slowPath(_isOpaque) {
|
|
_asOpaque().computeHashValue(into: &hasher)
|
|
return
|
|
}
|
|
if isASCII {
|
|
_unmanagedASCIIView.computeHashValue(into: &hasher)
|
|
return
|
|
}
|
|
_unmanagedUTF16View.computeHashValue(into: &hasher)
|
|
}
|
|
|
|
@_versioned
|
|
@effects(releasenone) // FIXME: Is this guaranteed in the opaque case?
|
|
internal func _hash(_ range: Range<Int>, into hasher: inout _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.
|
|
@_inlineable
|
|
public var hashValue: Int {
|
|
return _hashValue(for: self)
|
|
}
|
|
|
|
@_inlineable
|
|
public func _hash(into hasher: inout _Hasher) {
|
|
_guts._hash(into: &hasher)
|
|
}
|
|
}
|
|
|
|
extension StringProtocol {
|
|
@_inlineable
|
|
public var hashValue : Int {
|
|
return _hashValue(for: self)
|
|
}
|
|
|
|
@_inlineable
|
|
public func _hash(into hasher: inout _Hasher) {
|
|
_wholeString._guts._hash(_encodedOffsetRange, into: &hasher)
|
|
}
|
|
}
|