|
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