Skip to content

Commit dc40552

Browse files
authored
Optimize checking for all-ASCII bytes (swiftlang#72312)
1 parent 3c8af3e commit dc40552

File tree

1 file changed

+42
-21
lines changed

1 file changed

+42
-21
lines changed

stdlib/public/core/StringCreate.swift

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,33 +19,54 @@ internal func _allASCII(_ input: UnsafeBufferPointer<UInt8>) -> Bool {
1919
//
2020
// TODO(String performance): SIMD-ize
2121
//
22-
let ptr = input.baseAddress._unsafelyUnwrappedUnchecked
23-
var i = 0
24-
2522
let count = input.count
26-
let stride = MemoryLayout<UInt>.stride
27-
let address = Int(bitPattern: ptr)
28-
29-
let wordASCIIMask = UInt(truncatingIfNeeded: 0x8080_8080_8080_8080 as UInt64)
30-
let byteASCIIMask = UInt8(truncatingIfNeeded: wordASCIIMask)
23+
var ptr = UnsafeRawPointer(input.baseAddress._unsafelyUnwrappedUnchecked)
24+
var i = 0
3125

32-
while (address &+ i) % stride != 0 && i < count {
33-
guard ptr[i] & byteASCIIMask == 0 else { return false }
34-
i &+= 1
26+
let asciiMask64 = 0x8080_8080_8080_8080 as UInt64
27+
let asciiMask32 = UInt32(truncatingIfNeeded: asciiMask64)
28+
let asciiMask16 = UInt16(truncatingIfNeeded: asciiMask64)
29+
let asciiMask8 = UInt8(truncatingIfNeeded: asciiMask64)
30+
31+
let end128 = ptr + count & ~(MemoryLayout<(UInt64, UInt64)>.stride &- 1)
32+
let end64 = ptr + count & ~(MemoryLayout<UInt64>.stride &- 1)
33+
let end32 = ptr + count & ~(MemoryLayout<UInt32>.stride &- 1)
34+
let end16 = ptr + count & ~(MemoryLayout<UInt16>.stride &- 1)
35+
let end = ptr + count
36+
37+
38+
while ptr < end128 {
39+
let pair = ptr.loadUnaligned(as: (UInt64, UInt64).self)
40+
let result = (pair.0 | pair.1) & asciiMask64
41+
guard result == 0 else { return false }
42+
ptr = ptr + MemoryLayout<(UInt64, UInt64)>.stride
3543
}
36-
37-
while (i &+ stride) <= count {
38-
let word: UInt = UnsafePointer(
39-
bitPattern: address &+ i
40-
)._unsafelyUnwrappedUnchecked.pointee
41-
guard word & wordASCIIMask == 0 else { return false }
42-
i &+= stride
44+
45+
// If we had enough bytes for two iterations of this, we would have hit
46+
// the loop above, so we only need to do this once
47+
if ptr < end64 {
48+
let value = ptr.loadUnaligned(as: UInt64.self)
49+
guard value & asciiMask64 == 0 else { return false }
50+
ptr = ptr + MemoryLayout<UInt64>.stride
51+
}
52+
53+
if ptr < end32 {
54+
let value = ptr.loadUnaligned(as: UInt32.self)
55+
guard value & asciiMask32 == 0 else { return false }
56+
ptr = ptr + MemoryLayout<UInt32>.stride
57+
}
58+
59+
if ptr < end16 {
60+
let value = ptr.loadUnaligned(as: UInt16.self)
61+
guard value & asciiMask16 == 0 else { return false }
62+
ptr = ptr + MemoryLayout<UInt16>.stride
4363
}
4464

45-
while i < count {
46-
guard ptr[i] & byteASCIIMask == 0 else { return false }
47-
i &+= 1
65+
if ptr < end {
66+
let value = ptr.loadUnaligned(fromByteOffset: i, as: UInt8.self)
67+
guard value & asciiMask8 == 0 else { return false }
4868
}
69+
_internalInvariant(ptr == end || ptr + 1 == end)
4970
return true
5071
}
5172

0 commit comments

Comments
 (0)