Skip to content

Commit 10537ec

Browse files
authored
Merge pull request #68240 from CrazyFanFan/feature/crazy_fan/std_u32string
[cxx-interop] Provide similar support for std::u32string as std::string and std::u16string.
2 parents fd1af46 + 02a9259 commit 10537ec

File tree

3 files changed

+203
-0
lines changed

3 files changed

+203
-0
lines changed

stdlib/public/Cxx/cxxshim/libcxxstdlibshim.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ typedef std::hash<std::string> __swift_interopHashOfString;
66

77
/// Used for std::u16string conformance to Swift.Hashable
88
typedef std::hash<std::u16string> __swift_interopHashOfU16String;
9+
10+
/// Used for std::u32string conformance to Swift.Hashable
11+
typedef std::hash<std::u32string> __swift_interopHashOfU32String;

stdlib/public/Cxx/std/String.swift

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@ extension std.u16string {
5555
}
5656
}
5757

58+
extension std.u32string {
59+
/// Creates a C++ UTF-32 string having the same content as the given Swift
60+
/// string.
61+
///
62+
/// - Complexity: O(*n*), where *n* is the number of UTF-32 code units in the
63+
/// Swift string.
64+
public init(_ string: String) {
65+
self.init()
66+
for char in string.unicodeScalars {
67+
self.push_back(char)
68+
}
69+
}
70+
}
71+
5872
// MARK: Initializing C++ string from a Swift String literal
5973

6074
extension std.string: ExpressibleByStringLiteral {
@@ -69,6 +83,12 @@ extension std.u16string: ExpressibleByStringLiteral {
6983
}
7084
}
7185

86+
extension std.u32string: ExpressibleByStringLiteral {
87+
public init(stringLiteral value: String) {
88+
self.init(value)
89+
}
90+
}
91+
7292
// MARK: Concatenating and comparing C++ strings
7393

7494
extension std.string: Equatable {
@@ -113,6 +133,27 @@ extension std.u16string: Equatable {
113133
}
114134
}
115135

136+
extension std.u32string: Equatable {
137+
public static func ==(lhs: std.u32string, rhs: std.u32string) -> Bool {
138+
return lhs.compare(rhs) == 0
139+
}
140+
141+
public static func +=(lhs: inout std.u32string, rhs: std.u32string) {
142+
lhs.append(rhs)
143+
}
144+
145+
@inlinable
146+
public mutating func append(_ other: std.u32string) {
147+
__appendUnsafe(other) // ignore the returned pointer
148+
}
149+
150+
public static func +(lhs: std.u32string, rhs: std.u32string) -> std.u32string {
151+
var copy = lhs
152+
copy += rhs
153+
return copy
154+
}
155+
}
156+
116157
// MARK: Hashing C++ strings
117158

118159
extension std.string: Hashable {
@@ -131,6 +172,14 @@ extension std.u16string: Hashable {
131172
}
132173
}
133174

175+
extension std.u32string: Hashable {
176+
public func hash(into hasher: inout Hasher) {
177+
// Call std::hash<std::u32string>::operator()
178+
let cxxHash = __swift_interopHashOfU32String().callAsFunction(self)
179+
hasher.combine(cxxHash)
180+
}
181+
}
182+
134183
// MARK: Getting a Swift description of a C++ string
135184

136185
extension std.string: CustomDebugStringConvertible {
@@ -145,6 +194,12 @@ extension std.u16string: CustomDebugStringConvertible {
145194
}
146195
}
147196

197+
extension std.u32string: CustomDebugStringConvertible {
198+
public var debugDescription: String {
199+
return "std.u32string(\(String(self)))"
200+
}
201+
}
202+
148203
extension std.string: CustomStringConvertible {
149204
public var description: String {
150205
return String(self)
@@ -157,6 +212,12 @@ extension std.u16string: CustomStringConvertible {
157212
}
158213
}
159214

215+
extension std.u32string: CustomStringConvertible {
216+
public var description: String {
217+
return String(self)
218+
}
219+
}
220+
160221
// MARK: Initializing Swift String from a C++ string
161222

162223
extension String {
@@ -192,4 +253,22 @@ extension String {
192253
self = String(decoding: buffer, as: UTF16.self)
193254
withExtendedLifetime(cxxU16String) {}
194255
}
256+
257+
/// Creates a String having the same content as the given C++ UTF-32 string.
258+
///
259+
/// If `cxxString` contains ill-formed UTF-32 code unit sequences, this
260+
/// initializer replaces them with the Unicode replacement character
261+
/// (`"\u{FFFD}"`).
262+
///
263+
/// - Complexity: O(*n*), where *n* is the number of bytes in the C++ UTF-32
264+
/// string.
265+
public init(_ cxxU32String: std.u32string) {
266+
let buffer = UnsafeBufferPointer<Unicode.Scalar>(
267+
start: cxxU32String.__dataUnsafe(),
268+
count: cxxU32String.size())
269+
self = buffer.withMemoryRebound(to: UInt32.self) {
270+
String(decoding: $0, as: UTF32.self)
271+
}
272+
withExtendedLifetime(cxxU32String) {}
273+
}
195274
}

test/Interop/Cxx/stdlib/overlay/std-string-overlay.swift

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,26 @@ StdStringOverlayTestSuite.test("std::u16string operators") {
8484
expectTrue(s1 == "something123literal")
8585
}
8686

87+
StdStringOverlayTestSuite.test("std::u32string operators") {
88+
var s1 = std.u32string("something")
89+
let s2 = std.u32string("123")
90+
let sum = s1 + s2
91+
expectEqual(sum, std.u32string("something123"))
92+
93+
expectFalse(s1 == s2)
94+
let s3 = std.u32string("something123")
95+
expectFalse(s1 == s3)
96+
expectFalse(s2 == s3)
97+
98+
s1 += s2
99+
expectTrue(s1 == std.u32string("something123"))
100+
expectTrue(s1 == s3)
101+
102+
// Make sure the operators work together with ExpressibleByStringLiteral conformance.
103+
s1 += "literal"
104+
expectTrue(s1 == "something123literal")
105+
}
106+
87107
StdStringOverlayTestSuite.test("std::string::append") {
88108
var s1 = std.string("0123")
89109
let s2 = std.string("abc")
@@ -98,6 +118,13 @@ StdStringOverlayTestSuite.test("std::u16string::append") {
98118
expectEqual(s1, std.u16string("0123abc"))
99119
}
100120

121+
StdStringOverlayTestSuite.test("std::u32string::append") {
122+
var s1 = std.u32string("0123")
123+
let s2 = std.u32string("abc")
124+
s1.append(s2)
125+
expectEqual(s1, std.u32string("0123abc"))
126+
}
127+
101128
StdStringOverlayTestSuite.test("std::string as Hashable") {
102129
let s0 = std.string()
103130
let h0 = s0.hashValue
@@ -140,6 +167,27 @@ StdStringOverlayTestSuite.test("std::u16string as Hashable") {
140167
expectNotEqual(h2, h3)
141168
}
142169

170+
StdStringOverlayTestSuite.test("std::u32string as Hashable") {
171+
let s0 = std.u32string()
172+
let h0 = s0.hashValue
173+
174+
let s1 = std.u32string("something")
175+
let h1 = s1.hashValue
176+
177+
let s2 = std.u32string("something123")
178+
let h2 = s2.hashValue
179+
180+
let s3 = std.u32string("something")
181+
let h3 = s3.hashValue
182+
183+
expectEqual(h1, h3)
184+
expectNotEqual(h0, h1)
185+
expectNotEqual(h0, h2)
186+
expectNotEqual(h0, h3)
187+
expectNotEqual(h1, h2)
188+
expectNotEqual(h2, h3)
189+
}
190+
143191
StdStringOverlayTestSuite.test("std::u16string <=> Swift.String") {
144192
let cxx1 = std.u16string()
145193
let swift1 = String(cxx1)
@@ -170,6 +218,36 @@ StdStringOverlayTestSuite.test("std::u16string <=> Swift.String") {
170218
expectEqual(swift6, "xyz\0abc")
171219
}
172220

221+
StdStringOverlayTestSuite.test("std::u32string <=> Swift.String") {
222+
let cxx1 = std.u32string()
223+
let swift1 = String(cxx1)
224+
expectEqual(swift1, "")
225+
226+
let cxx2 = std.u32string("something123")
227+
expectEqual(cxx2.size(), 12)
228+
let swift2 = String(cxx2)
229+
expectEqual(swift2, "something123")
230+
231+
let cxx3: std.u32string = "literal"
232+
expectEqual(cxx3.size(), 7)
233+
234+
let cxx4: std.u32string = "тест"
235+
expectEqual(cxx4.size(), 4)
236+
let swift4 = String(cxx4)
237+
expectEqual(swift4, "тест")
238+
239+
// Emojis are represented by more than one CWideChar.
240+
let cxx5: std.u32string = "emoji_🤖"
241+
expectEqual(cxx5.size(), 7)
242+
let swift5 = String(cxx5)
243+
expectEqual(swift5, "emoji_🤖")
244+
245+
let cxx6 = std.u32string("xyz\0abc")
246+
expectEqual(cxx6.size(), 7)
247+
let swift6 = String(cxx6)
248+
expectEqual(swift6, "xyz\0abc")
249+
}
250+
173251
StdStringOverlayTestSuite.test("std::string as Swift.CustomDebugStringConvertible") {
174252
let cxx1 = std.string()
175253
expectEqual(cxx1.debugDescription, "std.string()")
@@ -200,6 +278,23 @@ StdStringOverlayTestSuite.test("std::u16string as Swift.CustomDebugStringConvert
200278
expectEqual(cxx3.debugDescription, "std.u16string(a�c)")
201279
}
202280

281+
StdStringOverlayTestSuite.test("std::u32string as Swift.CustomDebugStringConvertible") {
282+
let cxx1 = std.u32string()
283+
expectEqual(cxx1.debugDescription, "std.u32string()")
284+
285+
let cxx2 = std.u32string("something123")
286+
expectEqual(cxx2.debugDescription, "std.u32string(something123)")
287+
288+
// Since std::u32string does not support pushing back UInt32 directly, we utilize UInt16 instead.
289+
let scalars: [UInt16] = [97, 55296, 99]
290+
var cxx3_16 = std.u16string()
291+
for scalar: UInt16 in scalars {
292+
cxx3_16.push_back(scalar)
293+
}
294+
let cxx3 = std.u32string(String(cxx3_16))
295+
expectEqual(cxx3.debugDescription, "std.u32string(a�c)")
296+
}
297+
203298
StdStringOverlayTestSuite.test("std::string as Swift.Sequence") {
204299
let cxx1 = std.string()
205300
var iterated = false
@@ -249,6 +344,32 @@ StdStringOverlayTestSuite.test("std::u16string as Swift.CustomStringConvertible"
249344
expectEqual(cxx3.description, "a�c")
250345
}
251346

347+
StdStringOverlayTestSuite.test("std::u32string as Swift.CustomStringConvertible") {
348+
let cxx1 = std.u32string()
349+
expectEqual(cxx1.description, "")
350+
351+
let cxx2 = std.u32string("something123")
352+
expectEqual(cxx2.description, "something123")
353+
354+
// Since std::u32string does not support pushing back UInt32 directly, we utilize UInt16 instead.
355+
let scalars: [UInt16] = [97, 55296, 99]
356+
var cxx3_16 = std.u16string()
357+
for scalar: UInt16 in scalars {
358+
cxx3_16.push_back(scalar)
359+
}
360+
let cxx3 = std.u32string(String(cxx3_16))
361+
expectEqual(cxx3.description, "a�c")
362+
363+
// For `push_back`
364+
let scalars2: [Unicode.Scalar] = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x4E16, 0x754C]
365+
.compactMap { Unicode.Scalar($0) }
366+
var cxx4 = std.u32string()
367+
for scalar: Unicode.Scalar in scalars2 {
368+
cxx4.push_back(scalar)
369+
}
370+
expectEqual(cxx4.description, "Hello, 世界")
371+
}
372+
252373
StdStringOverlayTestSuite.test("std::string from C string") {
253374
let str = "abc".withCString { ptr in
254375
std.string(ptr)

0 commit comments

Comments
 (0)