Skip to content

Commit 7e26972

Browse files
authored
Merge pull request #82851 from glessard/rdar154331399-makeReallyContiguous
[stdlib] Make String.makeContiguousUTF8() strictly true
2 parents 37bfabd + 9341973 commit 7e26972

File tree

4 files changed

+104
-14
lines changed

4 files changed

+104
-14
lines changed

stdlib/public/core/SmallString.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ internal struct _SmallString {
7979
extension _SmallString {
8080
@inlinable @inline(__always)
8181
internal static var capacity: Int {
82-
#if _pointerBitWidth(_32) && os(watchOS)
82+
#if os(watchOS) && _pointerBitWidth(_32)
8383
return 10
8484
#elseif _pointerBitWidth(_32) || _pointerBitWidth(_16)
8585
// Note: changed from 10 for contiguous storage.
@@ -95,7 +95,7 @@ extension _SmallString {
9595

9696
@_alwaysEmitIntoClient @inline(__always)
9797
internal static func contiguousCapacity() -> Int {
98-
#if _pointerBitWidth(_32) && os(watchOS)
98+
#if os(watchOS) && _pointerBitWidth(_32)
9999
return capacity &- 2
100100
#else
101101
return capacity

stdlib/public/core/StringCreate.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,19 @@ extension String {
312312
@inline(never) // slow-path
313313
internal static func _copying(_ str: Substring) -> String {
314314
if _fastPath(str._wholeGuts.isFastUTF8) {
315-
return unsafe str._wholeGuts.withFastUTF8(range: str._offsetRange) {
315+
var new = unsafe str._wholeGuts.withFastUTF8(range: str._offsetRange) {
316316
unsafe String._uncheckedFromUTF8($0)
317317
}
318+
#if os(watchOS) && _pointerBitWidth(_32)
319+
// Required for compatibility with some small strings that
320+
// may be encoded in the 32-bit slice of watchOS binaries.
321+
if str._wholeGuts.isSmall,
322+
str._wholeGuts.count > _SmallString.contiguousCapacity() {
323+
new.reserveCapacity(_SmallString.capacity + 1)
324+
return new
325+
}
326+
#endif
327+
return new
318328
}
319329
return unsafe Array(str.utf8).withUnsafeBufferPointer {
320330
unsafe String._uncheckedFromUTF8($0)

stdlib/public/core/StringProtocol.swift

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,27 @@ extension StringProtocol {
177177

178178
// Contiguous UTF-8 strings
179179
extension String {
180-
/// Returns whether this string is capable of providing access to
181-
/// validly-encoded UTF-8 contents in contiguous memory in O(1) time.
180+
/// Returns whether this string's storage contains
181+
/// validly-encoded UTF-8 contents in contiguous memory.
182182
///
183-
/// Contiguous strings always operate in O(1) time for withUTF8 and always
184-
/// give a result for String.UTF8View.withContiguousStorageIfAvailable.
183+
/// Contiguous strings always operate in O(1) time for withUTF8, always give
184+
/// a result for String.UTF8View.withContiguousStorageIfAvailable, and always
185+
/// return a non-nil value from `String._utf8Span` and `String.UTF8View._span`.
185186
/// Contiguous strings also benefit from fast-paths and better optimizations.
186-
///
187187
@_alwaysEmitIntoClient
188-
public var isContiguousUTF8: Bool { return _guts.isFastUTF8 }
188+
public var isContiguousUTF8: Bool {
189+
if _guts.isFastUTF8 {
190+
#if os(watchOS) && _pointerBitWidth(_32)
191+
// Required for compatibility with some small strings that
192+
// may be encoded in the 32-bit slice of watchOS binaries.
193+
if _guts.isSmall && _guts.count > _SmallString.contiguousCapacity() {
194+
return false
195+
}
196+
#endif
197+
return true
198+
}
199+
return false
200+
}
189201

190202
/// If this string is not contiguous, make it so. If this mutates the string,
191203
/// it will invalidate any pre-existing indices.
@@ -223,13 +235,14 @@ extension String {
223235

224236
// Contiguous UTF-8 strings
225237
extension Substring {
226-
/// Returns whether this string is capable of providing access to
227-
/// validly-encoded UTF-8 contents in contiguous memory in O(1) time.
238+
/// Returns whether this string's storage contains
239+
/// validly-encoded UTF-8 contents in contiguous memory.
228240
///
229-
/// Contiguous strings always operate in O(1) time for withUTF8 and always
230-
/// give a result for String.UTF8View.withContiguousStorageIfAvailable.
241+
/// Contiguous strings always operate in O(1) time for withUTF8, always give
242+
/// a result for Substring.UTF8View.withContiguousStorageIfAvailable, and
243+
/// always return a non-nil value from `Substring._utf8Span` and
244+
/// `Substring.UTF8View._span`.
231245
/// Contiguous strings also benefit from fast-paths and better optimizations.
232-
///
233246
@_alwaysEmitIntoClient
234247
public var isContiguousUTF8: Bool { return self.base.isContiguousUTF8 }
235248

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===--- SmallStringCompatibility.swift -----------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
// RUN: %target-run-stdlib-swift
14+
15+
// REQUIRES: executable_test
16+
// REQUIRES: CPU=wasm32 || CPU=arm64_32
17+
18+
import StdlibUnittest
19+
20+
var suite = TestSuite("SmallStringCompatibility")
21+
defer { runAllTests() }
22+
23+
var strings = [
24+
("Small", true),
25+
("Less small", true),
26+
("Positively large.", true),
27+
]
28+
29+
#if os(watchOS)
30+
strings[1].1 = false
31+
#endif
32+
33+
strings.forEach { (string, contiguous) in
34+
suite.test("Contiguous: \(string)")
35+
.require(.stdlib_6_2).code {
36+
37+
expectEqual(string.isContiguousUTF8, contiguous)
38+
}
39+
}
40+
41+
strings.forEach { (string, contiguous) in
42+
suite.test("Contiguous Substring: \(string)")
43+
.require(.stdlib_6_2).code {
44+
let substring = string[...]
45+
expectEqual(substring.isContiguousUTF8, contiguous)
46+
}
47+
}
48+
49+
strings.forEach { (string, contiguous) in
50+
suite.test("String.makeContiguousUTF8: \(string)")
51+
.require(.stdlib_6_2).code {
52+
var s = string
53+
s.makeContiguousUTF8()
54+
expectTrue(s.isContiguousUTF8)
55+
expectEqual(s, string)
56+
}
57+
}
58+
59+
strings.forEach { (string, contiguous) in
60+
suite.test("Substring.makeContiguousUTF8: \(string)")
61+
.require(.stdlib_6_2).code {
62+
var s: Substring = string.dropFirst().dropLast()
63+
s.makeContiguousUTF8()
64+
expectTrue(s.isContiguousUTF8)
65+
expectEqual(s, string.dropFirst().dropLast())
66+
}
67+
}

0 commit comments

Comments
 (0)