Skip to content

Commit dffc6c6

Browse files
committed
rdar://179770000 (CharacterSet Performance: Optimize contains method) (#4206)
1 parent 5a990a9 commit dffc6c6

4 files changed

Lines changed: 134 additions & 21 deletions

File tree

Sources/FoundationEssentials/String/BuiltInUnicodeScalarSet.swift

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ internal struct BuiltInUnicodeScalarSet {
6060
case decomposable
6161
}
6262

63-
var charset: SetType
63+
let charset: SetType
6464
init(type: SetType) {
6565
charset = type
6666
}
@@ -120,7 +120,7 @@ internal struct BuiltInUnicodeScalarSet {
120120
return nil
121121
}
122122
}
123-
123+
124124
enum BitmapResult {
125125
case bitmapFilled // kCFUniCharBitmapFilled
126126
case bitmapEmpty // kCFUniCharBitmapEmpty
@@ -295,7 +295,7 @@ internal struct BuiltInUnicodeScalarSet {
295295
// Full version used internally; the `shouldInvert` flag indicates that the stored bitmap
296296
// is the LEGAL set (for illegal charset) and callers must invert membership results.
297297
@_lifetime(immortal)
298-
private func _bitmapPtrForPlane(_ plane: Int) -> (span: Span<UInt8>, shouldInvert: Bool)? {
298+
internal func _bitmapPtrForPlane(_ plane: Int) -> (span: Span<UInt8>, shouldInvert: Bool)? {
299299
switch charset {
300300
case .whitespace, .whitespaceAndNewline, .newline:
301301
return nil
@@ -307,12 +307,8 @@ internal struct BuiltInUnicodeScalarSet {
307307
guard tableIndex < __CFUniCharNumberOfBitmaps else {
308308
return nil
309309
}
310-
311-
let data = withUnsafePointer(to: __CFUniCharBitmapDataArray) { ptr in
312-
ptr.withMemoryRebound(to: __CFUniCharBitmapData.self, capacity: Int(__CFUniCharNumberOfBitmaps)) { bitmapDataPtr in
313-
bitmapDataPtr.advanced(by: tableIndex).pointee
314-
}
315-
}
310+
311+
let data = getCFUniCharBitmapDataArray().advanced(by: tableIndex).pointee
316312

317313
guard plane < data._numPlanes, let planePtr = data._planes[plane] else {
318314
return nil
@@ -435,22 +431,17 @@ internal struct BuiltInUnicodeScalarSet {
435431
return 17
436432
default:
437433
precondition(_bitmapTableIndex != nil)
438-
let data = withUnsafePointer(to: __CFUniCharBitmapDataArray) { ptr in
439-
ptr.withMemoryRebound(to: __CFUniCharBitmapData.self, capacity: Int(__CFUniCharNumberOfBitmaps)) { bitmapDataPtr in
440-
bitmapDataPtr.advanced(by: _bitmapTableIndex!).pointee
441-
}
442-
}
443-
434+
let data = getCFUniCharBitmapDataArray().advanced(by: _bitmapTableIndex!).pointee
444435
return Int(data._numPlanes)
445436
}
446437
}
447438

448439
// MARK: Helper methods
449-
private func isWhitespace(_ scalar: Unicode.Scalar) -> Bool {
440+
internal func isWhitespace(_ scalar: Unicode.Scalar) -> Bool {
450441
return (scalar.value == 0x0020) || (scalar.value == 0x0009) || (scalar.value == 0x00A0) || (scalar.value == 0x1680) || (scalar.value >= 0x2000 && scalar.value <= 0x200B) || (scalar.value == 0x202F) || (scalar.value == 0x205F) || (scalar.value == 0x3000)
451442
}
452443

453-
private func isNewline(_ scalar: Unicode.Scalar) -> Bool {
444+
internal func isNewline(_ scalar: Unicode.Scalar) -> Bool {
454445
return ((scalar.value >= 0x000A && scalar.value <= 0x000D) || (scalar.value == 0x0085) || (scalar.value == 0x2028) || (scalar.value == 0x2029))
455446
}
456447

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
CFUniCharBitmapDataAccess.h
3+
Copyright (c) 2026, Apple Inc. and the Swift project authors. All rights reserved.
4+
5+
This header relies on CFUniCharBitmapData.inc.h and CFUniCharBitmapData.h
6+
being included before it.
7+
*/
8+
9+
#ifndef _cfunichar_bitmap_data_access_h
10+
#define _cfunichar_bitmap_data_access_h
11+
12+
static inline __CFUniCharBitmapData const *getCFUniCharBitmapDataArray(void) {
13+
return __CFUniCharBitmapDataArray;
14+
}
15+
16+
#endif /* _cfunichar_bitmap_data_access_h */

Sources/_FoundationCShims/include/_FoundationCShims.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "_CStdlib.h"
1818
#include "CFUniCharBitmapData.inc.h"
1919
#include "CFUniCharBitmapData.h"
20+
#include "CFUniCharBitmapDataAccess.h"
2021
#include "string_shims.h"
2122
#include "bplist_shims.h"
2223
#include "io_shims.h"

Tests/FoundationEssentialsTests/BuiltInUnicodeScalarSetTests.swift

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ import Testing
2121
@Suite("BuiltInUnicodeScalarSet")
2222
private struct BuiltInUnicodeScalarSetTests {
2323

24-
@Test func membership() {
25-
func setContainsScalar(_ set: BuiltInUnicodeScalarSet, _ scalar: Unicode.Scalar, _ expect: Bool, sourceLocation: SourceLocation = #_sourceLocation) {
26-
#expect(set.contains(scalar) == expect, sourceLocation: sourceLocation)
27-
}
24+
func setContainsScalar(_ set: BuiltInUnicodeScalarSet, _ scalar: Unicode.Scalar, _ expect: Bool, sourceLocation: SourceLocation = #_sourceLocation) {
25+
#expect(set.contains(scalar) == expect, sourceLocation: sourceLocation)
26+
}
2827

28+
@Test func membership() {
2929
setContainsScalar(.lowercaseLetters, "a", true)
3030
setContainsScalar(.lowercaseLetters, "ô", true)
3131
setContainsScalar(.lowercaseLetters, "\u{01FB}", true)
@@ -56,4 +56,109 @@ private struct BuiltInUnicodeScalarSetTests {
5656
setContainsScalar(.graphemeExtends, "~", false)
5757
}
5858

59+
@Test func bmpMembership() {
60+
setContainsScalar(.uppercaseLetters, "\u{FFFF}", false)
61+
setContainsScalar(.lowercaseLetters, "\u{FFFF}", false)
62+
setContainsScalar(BuiltInUnicodeScalarSet(type: .letter), "\u{FFFF}", false)
63+
setContainsScalar(BuiltInUnicodeScalarSet(type: .alphanumeric), "\u{FFFF}", false)
64+
setContainsScalar(BuiltInUnicodeScalarSet(type: .decimalDigit), "\u{FFFF}", false)
65+
}
66+
67+
@Test func nonBMPMembership() {
68+
setContainsScalar(BuiltInUnicodeScalarSet(type: .letter), "\u{10000}", true) // LINEAR B SYLLABLE B008 A
69+
setContainsScalar(BuiltInUnicodeScalarSet(type: .alphanumeric), "\u{10000}", true) // LINEAR B SYLLABLE B008 A
70+
setContainsScalar(.uppercaseLetters, "\u{10000}", false)
71+
setContainsScalar(.lowercaseLetters, "\u{10000}", false)
72+
setContainsScalar(BuiltInUnicodeScalarSet(type: .decimalDigit), "\u{10000}", false)
73+
}
74+
75+
@Test func illegalSetMembership() {
76+
let illegal = BuiltInUnicodeScalarSet(type: .illegal)
77+
setContainsScalar(illegal, "A", false)
78+
setContainsScalar(illegal, "0", false)
79+
setContainsScalar(illegal, "a", false)
80+
setContainsScalar(illegal, "\u{E0001}", false) // LANGUAGE TAG
81+
setContainsScalar(illegal, "\u{E0020}", false) // TAG SPACE
82+
setContainsScalar(illegal, "\u{E007F}", false) // CANCEL TAG
83+
setContainsScalar(illegal, "\u{E0000}", true)
84+
}
85+
86+
@Test func controlAndFormatterSetMembership() {
87+
let controlAndFormatter = BuiltInUnicodeScalarSet(type: .controlAndFormatter)
88+
setContainsScalar(controlAndFormatter, "\u{0001}", true) // START OF HEADING
89+
setContainsScalar(controlAndFormatter, "\u{0007}", true) // BELL
90+
setContainsScalar(controlAndFormatter, "\u{200B}", true) // ZERO WIDTH SPACE
91+
setContainsScalar(controlAndFormatter, "a", false)
92+
setContainsScalar(controlAndFormatter, "0", false)
93+
setContainsScalar(controlAndFormatter, "\u{E0001}", true) // LANGUAGE TAG
94+
setContainsScalar(controlAndFormatter, "\u{E0020}", true) // TAG SPACE
95+
setContainsScalar(controlAndFormatter, "\u{E007F}", true) // CANCEL TAG
96+
setContainsScalar(controlAndFormatter, "\u{E0000}", false)
97+
}
98+
99+
@Test func whitespaceSetMembership() {
100+
let whitespace = BuiltInUnicodeScalarSet(type: .whitespace)
101+
setContainsScalar(whitespace, " ", true)
102+
setContainsScalar(whitespace, "\u{0009}", true) // CHARACTER TABULATION
103+
setContainsScalar(whitespace, "\u{00A0}", true) // NO-BREAK SPACE
104+
setContainsScalar(whitespace, "\u{1680}", true) // OGHAM SPACE MARK
105+
setContainsScalar(whitespace, "\u{2000}", true) // EN QUAD
106+
setContainsScalar(whitespace, "\u{200B}", true) // ZERO WIDTH SPACE
107+
setContainsScalar(whitespace, "\u{202F}", true) // NARROW NO-BREAK SPACE
108+
setContainsScalar(whitespace, "\u{205F}", true) // MEDIUM MATHEMATICAL SPACE
109+
setContainsScalar(whitespace, "\u{3000}", true) // IDEOGRAPHIC SPACE
110+
setContainsScalar(whitespace, "\u{000A}", false) // LINE FEED
111+
setContainsScalar(whitespace, "a", false)
112+
}
113+
114+
@Test func newlineSetMembership() {
115+
let newline = BuiltInUnicodeScalarSet(type: .newline)
116+
setContainsScalar(newline, "\u{000A}", true) // LINE FEED
117+
setContainsScalar(newline, "\u{000B}", true) // LINE TABULATION
118+
setContainsScalar(newline, "\u{000C}", true) // FORM FEED
119+
setContainsScalar(newline, "\u{000D}", true) // CARRIAGE RETURN
120+
setContainsScalar(newline, "\u{0085}", true) // NEXT LINE
121+
setContainsScalar(newline, "\u{2028}", true) // LINE SEPARATOR
122+
setContainsScalar(newline, "\u{2029}", true) // PARAGRAPH SEPARATOR
123+
setContainsScalar(newline, " ", false)
124+
setContainsScalar(newline, "a", false)
125+
}
126+
127+
@Test func whitespaceAndNewlineSetMembership() {
128+
let whitespaceAndNewline = BuiltInUnicodeScalarSet(type: .whitespaceAndNewline)
129+
setContainsScalar(whitespaceAndNewline, " ", true)
130+
setContainsScalar(whitespaceAndNewline, "\u{000A}", true) // LINE FEED
131+
setContainsScalar(whitespaceAndNewline, "\u{2028}", true) // LINE SEPARATOR
132+
setContainsScalar(whitespaceAndNewline, "\u{3000}", true) // IDEOGRAPHIC SPACE
133+
setContainsScalar(whitespaceAndNewline, "a", false)
134+
}
135+
136+
@Test func unsupportedNonBMPPlaneMembership() {
137+
setContainsScalar(.uppercaseLetters, "\u{D0000}", false)
138+
setContainsScalar(.lowercaseLetters, "\u{D0000}", false)
139+
setContainsScalar(BuiltInUnicodeScalarSet(type: .decimalDigit), "\u{D0000}", false)
140+
}
141+
142+
@Test func illegalSetUnsupportedNonBMPNonSpecialPlaneMembership() {
143+
let illegal = BuiltInUnicodeScalarSet(type: .illegal)
144+
setContainsScalar(illegal, "\u{10000}", false) // LINEAR B SYLLABLE B008 A
145+
setContainsScalar(illegal, "\u{1D7CF}", false) // MATHEMATICAL BOLD DIGIT ZERO
146+
setContainsScalar(illegal, "\u{D0000}", false)
147+
}
148+
149+
@Test func illegalSetUnsupportedPlane15And16Membership() {
150+
let illegal = BuiltInUnicodeScalarSet(type: .illegal)
151+
setContainsScalar(illegal, "\u{F0000}", false)
152+
setContainsScalar(illegal, "\u{FFFFE}", false)
153+
setContainsScalar(illegal, "\u{100000}", false)
154+
setContainsScalar(illegal, "\u{10FFFD}", false)
155+
}
156+
157+
@Test func controlAndFormatterSetUnsupportedNonBMPNonPlane14() {
158+
let controlAndFormatter = BuiltInUnicodeScalarSet(type: .controlAndFormatter)
159+
setContainsScalar(controlAndFormatter, "\u{10000}", false) // LINEAR B SYLLABLE B008 A
160+
setContainsScalar(controlAndFormatter, "\u{1D7CF}", false) // MATHEMATICAL BOLD DIGIT ZERO
161+
setContainsScalar(controlAndFormatter, "\u{D0000}", false)
162+
}
163+
59164
}

0 commit comments

Comments
 (0)