Skip to content

Commit 83e1137

Browse files
committed
[String] Naturalize Character
Characters should always be native, and never shared.
1 parent 8672455 commit 83e1137

File tree

3 files changed

+44
-2
lines changed

3 files changed

+44
-2
lines changed

stdlib/public/core/Character.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ extension Character {
8181
internal func _invariantCheck() {
8282
_internalInvariant(_str.count == 1)
8383
_internalInvariant(_str._guts.isFastUTF8)
84+
85+
// TODO(@eject): Switch to a helper property on StringObject/StringGuts.
86+
_internalInvariant(
87+
_str._guts.isSmall || _str._guts._object._countAndFlags.isTailAllocated)
8488
}
8589
#endif // INTERNAL_CHECKS_ENABLED
8690
}
@@ -173,7 +177,17 @@ extension Character :
173177
"Can't form a Character from an empty String")
174178
_debugPrecondition(s.index(after: s.startIndex) == s.endIndex,
175179
"Can't form a Character from a String containing more than one extended grapheme cluster")
176-
self.init(unchecked: s)
180+
181+
// TODO(@eject): Switch to a helper property on StringObject/StringGuts.
182+
if _fastPath(
183+
s._guts.isSmall || s._guts._object._countAndFlags.isTailAllocated
184+
) {
185+
self.init(unchecked: s)
186+
return
187+
}
188+
189+
// TODO(@eject): Outline this
190+
self.init(unchecked: s._withUTF8 { String._uncheckedFromUTF8($0) })
177191
}
178192
}
179193

stdlib/public/core/StringObject.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,9 +617,10 @@ extension _StringObject {
617617
isNativelyStored: set for native stored strings
618618
- `largeAddressBits` holds an instance of `_StringStorage`.
619619
- I.e. the start of the code units is at the stored address + `nativeBias`
620-
isTailAllocated: start of the code units is at the stored address + `nativeBias`
620+
isTailAllocated: contiguous UTF-8 code units starts at address + `nativeBias`
621621
- `isNativelyStored` always implies `isTailAllocated`, but not vice versa
622622
(e.g. literals)
623+
- `isTailAllocated` always implies `isFastUTF8`
623624
TBD: Reserved for future usage
624625
- Setting a TBD bit to 1 must be semantically equivalent to 0
625626
- I.e. it can only be used to "cache" fast-path information in the future
@@ -1073,6 +1074,9 @@ extension _StringObject {
10731074
} else {
10741075
_internalInvariant(isLarge)
10751076
_internalInvariant(largeCount == count)
1077+
if _countAndFlags.isTailAllocated {
1078+
_internalInvariant(providesFastUTF8)
1079+
}
10761080
if providesFastUTF8 && largeFastIsTailAllocated {
10771081
_internalInvariant(!isSmall)
10781082
_internalInvariant(!largeIsCocoa)

test/stdlib/StringBridge.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,5 +75,29 @@ StringBridgeTests.test("Tagged NSString") {
7575
#endif // not 32bit
7676
}
7777

78+
func returnOne<T>(_ t: T) -> Int { return 1 }
79+
StringBridgeTests.test("Character from NSString") {
80+
// NOTE: Using hard-coded literals to directly construct NSStrings
81+
let ns1 = "A" as NSString
82+
let ns2 = "A\u{301}" as NSString
83+
let ns3 = "𓁹͇͈͉͍͎͊͋͌ͧͨͩͪͫͬͭͮ͏̛͓͔͕͖͙͚̗̘̙̜̹̺̻̼͐͑͒͗͛ͣͤͥͦ̽̾̿̀́͂̓̈́͆ͧͨͩͪͫͬͭͮ͘̚͜͟͢͝͞͠͡ͅ" as NSString
84+
85+
let c1 = Character(ns1 as String)
86+
let c2 = Character(ns2 as String)
87+
let c3 = Character(ns3 as String)
88+
89+
expectEqual("A", String(c1))
90+
expectNotNil(String(c1).utf8.withContiguousStorageIfAvailable(returnOne))
91+
92+
expectEqual("A\u{301}", String(c2))
93+
expectNotNil(String(c2).utf8.withContiguousStorageIfAvailable(returnOne))
94+
expectNil((ns2 as String).utf8.withContiguousStorageIfAvailable(returnOne))
95+
96+
expectEqual("𓁹͇͈͉͍͎͊͋͌ͧͨͩͪͫͬͭͮ͏̛͓͔͕͖͙͚̗̘̙̜̹̺̻̼͐͑͒͗͛ͣͤͥͦ̽̾̿̀́͂̓̈́͆ͧͨͩͪͫͬͭͮ͘̚͜͟͢͝͞͠͡ͅ", String(c3))
97+
expectNotNil(String(c3).utf8.withContiguousStorageIfAvailable(returnOne))
98+
expectNil((ns3 as String).utf8.withContiguousStorageIfAvailable(returnOne))
99+
}
100+
101+
78102
runAllTests()
79103

0 commit comments

Comments
 (0)