|
12 | 12 |
|
13 | 13 | import SwiftShims |
14 | 14 |
|
15 | | -func _emptyASCIIHashBuffer() -> _UIntBuffer<UInt64, UInt8> { |
16 | | - var buffer = _UIntBuffer<UInt64, UInt8>() |
17 | | - // We don't want the unused bits of a partially filled buffer to collide |
18 | | - // with trailing nuls when hashing |
19 | | - buffer._storage = UInt64.max |
20 | | - return buffer |
21 | | -} |
22 | | - |
23 | | -internal struct ASCIIHasher { |
24 | | - private var buffer = _emptyASCIIHashBuffer() |
25 | | - |
26 | | - internal mutating func consume() -> UInt64? { |
27 | | - if !buffer.isEmpty { |
28 | | - defer { resetBuffer() } |
29 | | - return buffer._storage |
30 | | - } |
31 | | - return nil |
32 | | - } |
33 | | - |
34 | | - private mutating func resetBuffer() { |
35 | | - buffer = _emptyASCIIHashBuffer() |
36 | | - } |
37 | | - |
38 | | - internal mutating func append(_ c: UInt8) -> UInt64? { |
39 | | - if buffer.count < buffer.capacity { |
40 | | - buffer.append(c) |
41 | | - } |
42 | | - |
43 | | - if buffer.count == buffer.capacity { |
44 | | - defer { resetBuffer() } |
45 | | - return buffer._storage |
46 | | - } |
47 | | - return nil |
48 | | - } |
49 | | -} |
50 | | - |
51 | 15 | extension _UnmanagedString where CodeUnit == UInt8 { |
52 | | - // NOT @usableFromInline |
53 | | - @effects(releasenone) |
54 | | - internal func hashASCII(into hasher: inout _Hasher) { |
55 | | - var asciiHasher = ASCIIHasher() |
56 | | - for c in self { |
57 | | - if let chunk = asciiHasher.append(UInt8(truncatingIfNeeded: c)) { |
58 | | - hasher.combine(chunk) |
59 | | - } |
60 | | - } |
61 | | - |
62 | | - if let chunk = asciiHasher.consume() { |
63 | | - hasher.combine(chunk) |
64 | | - } |
| 16 | + internal func hashASCII(into core: inout _Hasher.Core) { |
| 17 | + core.combine(bytes: rawBuffer) |
65 | 18 | } |
66 | 19 | } |
67 | 20 |
|
68 | 21 | extension BidirectionalCollection where Element == UInt16, SubSequence == Self { |
69 | | - // NOT @usableFromInline |
70 | | - internal func hashUTF16(into hasher: inout _Hasher) { |
71 | | - var asciiHasher = ASCIIHasher() |
72 | | - |
| 22 | + internal func hashUTF16(into core: inout _Hasher.Core) { |
73 | 23 | for i in self.indices { |
74 | 24 | let cu = self[i] |
75 | 25 | let cuIsASCII = cu <= 0x7F |
76 | 26 | let isSingleSegmentScalar = self.hasNormalizationBoundary(after: i) |
77 | 27 |
|
78 | | - guard cuIsASCII && isSingleSegmentScalar else { |
79 | | - if let chunk = asciiHasher.consume() { |
80 | | - hasher.combine(chunk) |
81 | | - } |
82 | | - |
83 | | - let codeUnitSequence = IteratorSequence( |
84 | | - _NormalizedCodeUnitIterator(self[i..<endIndex]) |
85 | | - ) |
86 | | - for element in codeUnitSequence { |
87 | | - hasher.combine(UInt(element)) |
| 28 | + if cuIsASCII && isSingleSegmentScalar { |
| 29 | + core.combine(UInt8(truncatingIfNeeded: cu)) |
| 30 | + } else { |
| 31 | + for encodedScalar in Unicode._ParsingIterator( |
| 32 | + codeUnits: _NormalizedCodeUnitIterator(self[i..<endIndex]), |
| 33 | + parser: Unicode.UTF16.ForwardParser() |
| 34 | + ) { |
| 35 | + let transcoded = Unicode.UTF8.transcode( |
| 36 | + encodedScalar, from: Unicode.UTF16.self |
| 37 | + ).unsafelyUnwrapped // never fails |
| 38 | + let (bytes, count) = transcoded._bytes |
| 39 | + core.combine(bytes: bytes, count: count) |
88 | 40 | } |
89 | 41 | return |
90 | 42 | } |
91 | | - |
92 | | - if let chunk = asciiHasher.append(UInt8(truncatingIfNeeded: cu)) { |
93 | | - hasher.combine(chunk) |
94 | | - } |
95 | | - } |
96 | | - |
97 | | - if let chunk = asciiHasher.consume() { |
98 | | - hasher.combine(chunk) |
99 | 43 | } |
100 | 44 | } |
101 | 45 | } |
102 | 46 |
|
103 | 47 | extension _UnmanagedString where CodeUnit == UInt8 { |
104 | | - @effects(releasenone) |
105 | | - @usableFromInline |
106 | | - internal func computeHashValue(into hasher: inout _Hasher) { |
107 | | - self.hashASCII(into: &hasher) |
| 48 | + internal func hash(into hasher: inout _Hasher) { |
| 49 | + self.hashASCII(into: &hasher._core) |
| 50 | + hasher._core.combine(0xFF as UInt8) // terminator |
108 | 51 | } |
109 | 52 | } |
110 | 53 |
|
111 | 54 | extension _UnmanagedString where CodeUnit == UInt16 { |
112 | | - @effects(releasenone) |
113 | | - @usableFromInline |
114 | | - internal func computeHashValue(into hasher: inout _Hasher) { |
115 | | - self.hashUTF16(into: &hasher) |
| 55 | + internal func hash(into hasher: inout _Hasher) { |
| 56 | + self.hashUTF16(into: &hasher._core) |
| 57 | + hasher._core.combine(0xFF as UInt8) // terminator |
116 | 58 | } |
117 | 59 | } |
118 | 60 |
|
119 | 61 | extension _UnmanagedOpaqueString { |
120 | | - @usableFromInline |
121 | | - internal func computeHashValue(into hasher: inout _Hasher) { |
122 | | - self.hashUTF16(into: &hasher) |
| 62 | + internal func hash(into hasher: inout _Hasher) { |
| 63 | + self.hashUTF16(into: &hasher._core) |
| 64 | + hasher._core.combine(0xFF as UInt8) // terminator |
123 | 65 | } |
124 | 66 | } |
125 | 67 |
|
126 | 68 | extension _SmallUTF8String { |
127 | | - @inlinable |
128 | | - internal func computeHashValue(into hasher: inout _Hasher) { |
| 69 | + internal func hash(into hasher: inout _Hasher) { |
129 | 70 | #if arch(i386) || arch(arm) |
130 | 71 | unsupportedOn32bit() |
131 | 72 | #else |
132 | 73 | if isASCII { |
133 | | - return self.withUnmanagedASCII { $0.computeHashValue(into: &hasher) } |
| 74 | + self.withUnmanagedASCII { $0.hash(into: &hasher) } |
| 75 | + return |
134 | 76 | } |
135 | | - return self.withUnmanagedUTF16 { $0.computeHashValue(into: &hasher) } |
| 77 | + self.withUnmanagedUTF16 { $0.hash(into: &hasher) } |
136 | 78 | #endif // 64-bit |
137 | 79 | } |
138 | 80 | } |
139 | 81 |
|
140 | 82 | extension _StringGuts { |
| 83 | + @effects(releasenone) // FIXME: Is this valid in the opaque case? |
141 | 84 | @usableFromInline |
142 | | - @effects(releasenone) // FIXME: Is this guaranteed in the opaque case? |
143 | 85 | internal func _hash(into hasher: inout _Hasher) { |
144 | 86 | if _isSmall { |
145 | | - return _smallUTF8String.computeHashValue(into: &hasher) |
| 87 | + _smallUTF8String.hash(into: &hasher) |
| 88 | + return |
146 | 89 | } |
147 | 90 |
|
148 | 91 | defer { _fixLifetime(self) } |
149 | 92 | if _slowPath(_isOpaque) { |
150 | | - _asOpaque().computeHashValue(into: &hasher) |
| 93 | + _asOpaque().hash(into: &hasher) |
151 | 94 | return |
152 | 95 | } |
153 | 96 | if isASCII { |
154 | | - _unmanagedASCIIView.computeHashValue(into: &hasher) |
| 97 | + _unmanagedASCIIView.hash(into: &hasher) |
155 | 98 | return |
156 | 99 | } |
157 | | - _unmanagedUTF16View.computeHashValue(into: &hasher) |
| 100 | + _unmanagedUTF16View.hash(into: &hasher) |
158 | 101 | } |
159 | 102 |
|
| 103 | + @effects(releasenone) // FIXME: Is this valid in the opaque case? |
160 | 104 | @usableFromInline |
161 | | - @effects(releasenone) // FIXME: Is this guaranteed in the opaque case? |
162 | 105 | internal func _hash(_ range: Range<Int>, into hasher: inout _Hasher) { |
163 | 106 | if _isSmall { |
164 | | - return _smallUTF8String[range].computeHashValue(into: &hasher) |
| 107 | + _smallUTF8String[range].hash(into: &hasher) |
| 108 | + return |
165 | 109 | } |
166 | 110 |
|
167 | 111 | defer { _fixLifetime(self) } |
168 | 112 | if _slowPath(_isOpaque) { |
169 | | - _asOpaque()[range].computeHashValue(into: &hasher) |
| 113 | + _asOpaque()[range].hash(into: &hasher) |
170 | 114 | return |
171 | 115 | } |
172 | 116 | if isASCII { |
173 | | - _unmanagedASCIIView[range].computeHashValue(into: &hasher) |
| 117 | + _unmanagedASCIIView[range].hash(into: &hasher) |
174 | 118 | return |
175 | 119 | } |
176 | | - _unmanagedUTF16View[range].computeHashValue(into: &hasher) |
| 120 | + _unmanagedUTF16View[range].hash(into: &hasher) |
177 | 121 | } |
178 | 122 | } |
179 | 123 |
|
|
0 commit comments