Skip to content

Commit 1ac5330

Browse files
authored
Remove expensive availability checks from Data replaceSubrange (#1571)
* (163828996) Remove expensive availability checks from Data replaceSubrange * Cleanup range arithmetic
1 parent bfdb63b commit 1ac5330

File tree

3 files changed

+37
-66
lines changed

3 files changed

+37
-66
lines changed

Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -172,24 +172,12 @@ extension Data {
172172
mutating func append(contentsOf buffer: UnsafeRawBufferPointer) {
173173
assert(endIndex + buffer.count < HalfInt.max)
174174
ensureUniqueReference()
175-
let upperbound = storage.length + storage._offset
176-
#if FOUNDATION_FRAMEWORK
177-
if #available(macOS 14, iOS 17, watchOS 10, tvOS 17, *) {
178-
storage.replaceBytes(
179-
in: range.upperBound ..< upperbound,
180-
with: buffer.baseAddress,
181-
length: buffer.count)
182-
} else {
183-
storage.replaceBytes(
184-
in: NSRange(
185-
location: range.upperBound,
186-
length: storage.length - (range.upperBound - storage._offset)),
187-
with: buffer.baseAddress,
188-
length: buffer.count)
189-
}
190-
#else
191-
storage.replaceBytes(in: range.upperBound ..< upperbound, with: buffer.baseAddress, length: buffer.count)
192-
#endif
175+
storage.replaceBytes(
176+
in: (
177+
location: range.upperBound,
178+
length: storage.length - (range.upperBound - storage._offset)),
179+
with: buffer.baseAddress,
180+
length: buffer.count)
193181
slice = slice.lowerBound..<HalfInt(Int(slice.upperBound) + buffer.count)
194182
}
195183

@@ -231,18 +219,10 @@ extension Data {
231219

232220
ensureUniqueReference()
233221
let upper = range.upperBound
234-
#if FOUNDATION_FRAMEWORK
235-
if #available(macOS 14, iOS 17, watchOS 10, tvOS 17, *) {
236-
storage.replaceBytes(in: subrange, with: bytes, length: cnt)
237-
} else {
238-
let nsRange = NSRange(
239-
location: subrange.lowerBound,
240-
length: subrange.upperBound - subrange.lowerBound)
241-
storage.replaceBytes(in: nsRange, with: bytes, length: cnt)
242-
}
243-
#else
244-
storage.replaceBytes(in: subrange, with: bytes, length: cnt)
245-
#endif
222+
let nsRange = (
223+
location: subrange.lowerBound,
224+
length: subrange.upperBound - subrange.lowerBound)
225+
storage.replaceBytes(in: nsRange, with: bytes, length: cnt)
246226
let resultingUpper = upper - (subrange.upperBound - subrange.lowerBound) + cnt
247227
slice = slice.lowerBound..<HalfInt(resultingUpper)
248228
}

Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -160,23 +160,12 @@ extension Data {
160160
mutating func append(contentsOf buffer: UnsafeRawBufferPointer) {
161161
ensureUniqueReference()
162162
let upperbound = storage.length + storage._offset
163-
#if FOUNDATION_FRAMEWORK
164-
if #available(macOS 14, iOS 17, watchOS 10, tvOS 17, *) {
165-
storage.replaceBytes(
166-
in: range.upperBound ..< upperbound,
167-
with: buffer.baseAddress,
168-
length: buffer.count)
169-
} else {
170-
storage.replaceBytes(
171-
in: NSRange(
172-
location: range.upperBound,
173-
length: storage.length - (range.upperBound - storage._offset)),
174-
with: buffer.baseAddress,
175-
length: buffer.count)
176-
}
177-
#else
178-
storage.replaceBytes(in: range.upperBound ..< upperbound, with: buffer.baseAddress, length: buffer.count)
179-
#endif
163+
storage.replaceBytes(
164+
in: (
165+
location: range.upperBound,
166+
length: storage.length - (range.upperBound - storage._offset)),
167+
with: buffer.baseAddress,
168+
length: buffer.count)
180169
slice.range = slice.range.lowerBound..<slice.range.upperBound + buffer.count
181170
}
182171

@@ -214,18 +203,10 @@ extension Data {
214203

215204
ensureUniqueReference()
216205
let upper = range.upperBound
217-
#if FOUNDATION_FRAMEWORK
218-
if #available(macOS 14, iOS 17, watchOS 10, tvOS 17, *) {
219-
storage.replaceBytes(in: subrange, with: bytes, length: cnt)
220-
} else {
221-
let nsRange = NSRange(
222-
location: subrange.lowerBound,
223-
length: subrange.upperBound - subrange.lowerBound)
224-
storage.replaceBytes(in: nsRange, with: bytes, length: cnt)
225-
}
226-
#else
227-
storage.replaceBytes(in: subrange, with: bytes, length: cnt)
228-
#endif
206+
let nsRange = (
207+
location: subrange.lowerBound,
208+
length: subrange.upperBound - subrange.lowerBound)
209+
storage.replaceBytes(in: nsRange, with: bytes, length: cnt)
229210
let resultingUpper = upper - (subrange.upperBound - subrange.lowerBound) + cnt
230211
slice.range = slice.range.lowerBound..<resultingUpper
231212
}

Sources/FoundationEssentials/Data/Representations/DataStorage.swift

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -330,14 +330,24 @@ internal final class __DataStorage : @unchecked Sendable {
330330
UnsafeMutableRawBufferPointer(start: pointer, count: range.upperBound - range.lowerBound).copyMemory(from: offsetPointer)
331331
}
332332

333-
#if FOUNDATION_FRAMEWORK
333+
// This was an ABI entrypoint added in macOS 14-aligned releases in an attempt to work around the original declaration using NSRange instead of Range<Int>
334+
// Using this entrypoint from existing inlinable code required an availability check, and that check has proved to be extremely expensive
335+
// This entrypoint is left to preserve ABI compatibility, but inlinable code has since switched back to calling the original entrypoint using a tuple that is layout-compatible with NSRange
334336
@available(macOS 14, iOS 17, tvOS 17, watchOS 10, *)
335-
#endif
336-
@usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function.
337+
@usableFromInline
337338
func replaceBytes(in range_: Range<Int>, with replacementBytes: UnsafeRawPointer?, length replacementLength: Int) {
338-
let range = range_.lowerBound - _offset ..< range_.upperBound - _offset
339+
// Call through to the main implementation
340+
self.replaceBytes(in: (range_.lowerBound, range_.upperBound &- range_.lowerBound), with: replacementBytes, length: replacementLength)
341+
}
342+
343+
// This ABI entrypoint was original written using NSRange instead of Range<Int>. The ABI contract of this function must continue to accept NSRange values from code inlined into callers
344+
// To avoid using the real NSRange type at the source level, we use a tuple that is layout-compatible with NSRange instead and use @_silgen_name to preserve the original symbol name that includes "NSRange"
345+
@usableFromInline
346+
@_silgen_name("$s10Foundation13__DataStorageC12replaceBytes2in4with6lengthySo8_NSRangeV_SVSgSitF")
347+
internal func replaceBytes(in range_: (location: Int, length: Int), with replacementBytes: UnsafeRawPointer?, length replacementLength: Int) {
348+
let range = (location: range_.location - _offset, length: range_.length)
339349
let currentLength = _length
340-
let resultingLength = currentLength - (range.upperBound - range.lowerBound) + replacementLength
350+
let resultingLength = currentLength - range.length + replacementLength
341351
let shift = resultingLength - currentLength
342352
let mutableBytes: UnsafeMutableRawPointer
343353
if resultingLength > currentLength {
@@ -348,8 +358,8 @@ internal final class __DataStorage : @unchecked Sendable {
348358
}
349359
mutableBytes = _bytes!
350360
/* shift the trailing bytes */
351-
let start = range.lowerBound
352-
let length = range.upperBound - range.lowerBound
361+
let start = range.location
362+
let length = range.length
353363
if shift != 0 {
354364
memmove(mutableBytes + start + replacementLength, mutableBytes + start + length, currentLength - start - length)
355365
}

0 commit comments

Comments
 (0)