Skip to content

Commit aa8b06f

Browse files
committed
[string] Skip allocation in reserveCapacity if smol
If the requested capacity is small enough to fit in our small string representation, don't allocate a UTF-16 buffer, instead just return early.
1 parent 674b466 commit aa8b06f

File tree

3 files changed

+18
-16
lines changed

3 files changed

+18
-16
lines changed

stdlib/public/core/StringGuts.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -966,18 +966,22 @@ extension _StringGuts {
966966
}
967967
}
968968

969-
// TODO (TODO: JIRA): check if we're small and still within capacity
969+
// Small strings can accomodate small capacities
970+
if capacity <= _SmallUTF8String.capacity {
971+
return
972+
}
970973

974+
let selfCount = self.count
971975
if isASCII {
972976
let storage = _copyToNativeStorage(
973977
of: UInt8.self,
974-
from: 0..<self.count,
978+
from: 0..<selfCount,
975979
unusedCapacity: Swift.max(capacity - count, 0))
976980
self = _StringGuts(_large: storage)
977981
} else {
978982
let storage = _copyToNativeStorage(
979983
of: UTF16.CodeUnit.self,
980-
from: 0..<self.count,
984+
from: 0..<selfCount,
981985
unusedCapacity: Swift.max(capacity - count, 0))
982986
self = _StringGuts(_large: storage)
983987
}

test/stdlib/NewStringAppending.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,27 +69,25 @@ var s = "⓪" // start non-empty
6969

7070
// To make this test independent of the memory allocator implementation,
7171
// explicitly request initial capacity.
72-
s.reserveCapacity(8)
72+
s.reserveCapacity(16)
7373

74-
// CHECK-NEXT: String(Native(owner: @[[storage0:[x0-9a-f]+]], count: 1, capacity: 8)) = "⓪"
74+
// CHECK-NEXT: String(Native(owner: @[[storage0:[x0-9a-f]+]], count: 1, capacity: 16)) = "⓪"
7575
print("\(repr(s))")
7676

77-
// CHECK-NEXT: String(Native(owner: @[[storage0]], count: 2, capacity: 8)) = "⓪1"
77+
// CHECK-NEXT: String(Native(owner: @[[storage0]], count: 2, capacity: 16)) = "⓪1"
7878
s += "1"
7979
print("\(repr(s))")
8080

81-
// CHECK-NEXT: String(Native(owner: @[[storage0]], count: 8, capacity: 8)) = "⓪1234567"
81+
// CHECK-NEXT: String(Native(owner: @[[storage0]], count: 8, capacity: 16)) = "⓪1234567"
8282
s += "234567"
8383
print("\(repr(s))")
8484

85-
// -- expect a reallocation here
86-
87-
// CHECK-NEXT: String(Native(owner: @[[storage1:[x0-9a-f]+]], count: 9, capacity: 16)) = "⓪12345678"
85+
// CHECK-NEXT: String(Native(owner: @[[storage0:[x0-9a-f]+]], count: 9, capacity: 16)) = "⓪12345678"
8886
// CHECK-NOT: @[[storage0]],
8987
s += "8"
9088
print("\(repr(s))")
9189

92-
// CHECK-NEXT: String(Native(owner: @[[storage1]], count: 16, capacity: 16)) = "⓪123456789012345"
90+
// CHECK-NEXT: String(Native(owner: @[[storage0]], count: 16, capacity: 16)) = "⓪123456789012345"
9391
s += "9012345"
9492
print("\(repr(s))")
9593

validation-test/stdlib/String.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@ StringTests.test("stringGutsReserve")
948948
base._guts._objectIdentifier != nil &&
949949
isUnique
950950

951-
base.reserveCapacity(0)
951+
base.reserveCapacity(16)
952952
// Now it's unique
953953

954954
// If it was already native and unique, no reallocation
@@ -1065,11 +1065,11 @@ StringTests.test("reserveCapacity") {
10651065
expectNotEqual(id0, s.bufferID)
10661066
s = ""
10671067
print("empty capacity \(s.capacity)")
1068-
s.reserveCapacity(oldCap + 2)
1069-
print("reserving \(oldCap + 2) -> \(s.capacity), width = \(s._guts.byteWidth)")
1068+
s.reserveCapacity(oldCap + 18)
1069+
print("reserving \(oldCap + 18) -> \(s.capacity), width = \(s._guts.byteWidth)")
10701070
let id1 = s.bufferID
1071-
s.insert(contentsOf: repeatElement(x, count: oldCap + 2), at: s.endIndex)
1072-
print("extending by \(oldCap + 2) -> \(s.capacity), width = \(s._guts.byteWidth)")
1071+
s.insert(contentsOf: repeatElement(x, count: oldCap + 18), at: s.endIndex)
1072+
print("extending by \(oldCap + 18) -> \(s.capacity), width = \(s._guts.byteWidth)")
10731073
expectEqual(id1, s.bufferID)
10741074
s.insert(contentsOf: repeatElement(x, count: s.capacity + 100), at: s.endIndex)
10751075
expectNotEqual(id1, s.bufferID)

0 commit comments

Comments
 (0)