From 3c99826a8566f47406c55253657ec9a6b5a71762 Mon Sep 17 00:00:00 2001 From: Jeremy Schonfeld Date: Fri, 31 Oct 2025 17:11:31 -0700 Subject: [PATCH 1/2] (163828996) Remove expensive availability checks from Data replaceSubrange --- .../Representations/Data+InlineSlice.swift | 40 +++++-------------- .../Representations/Data+LargeSlice.swift | 39 +++++------------- .../Data/Representations/DataStorage.swift | 20 +++++++--- 3 files changed, 35 insertions(+), 64 deletions(-) diff --git a/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift index 409c75b02..0e2abd7e6 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift @@ -172,24 +172,12 @@ extension Data { mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { assert(endIndex + buffer.count < HalfInt.max) ensureUniqueReference() - let upperbound = storage.length + storage._offset -#if FOUNDATION_FRAMEWORK - if #available(macOS 14, iOS 17, watchOS 10, tvOS 17, *) { - storage.replaceBytes( - in: range.upperBound ..< upperbound, - with: buffer.baseAddress, - length: buffer.count) - } else { - storage.replaceBytes( - in: NSRange( - location: range.upperBound, - length: storage.length - (range.upperBound - storage._offset)), - with: buffer.baseAddress, - length: buffer.count) - } -#else - storage.replaceBytes(in: range.upperBound ..< upperbound, with: buffer.baseAddress, length: buffer.count) -#endif + storage.replaceBytes( + in: ( + location: range.upperBound, + length: storage.length - (range.upperBound - storage._offset)), + with: buffer.baseAddress, + length: buffer.count) slice = slice.lowerBound.. + // Using this entrypoint from existing inlinable code required an availability check, and that check has proved to be extremely expensive + // 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 @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) -#endif - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. + @usableFromInline func replaceBytes(in range_: Range, with replacementBytes: UnsafeRawPointer?, length replacementLength: Int) { - let range = range_.lowerBound - _offset ..< range_.upperBound - _offset + // Call through to the main implementation + self.replaceBytes(in: (range_.lowerBound, range_.upperBound &- range_.lowerBound), with: replacementBytes, length: replacementLength) + } + + // This ABI entrypoint was original written using NSRange instead of Range. The ABI contract of this function must continue to accept NSRange values from code inlined into callers + // 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" + @usableFromInline + @_silgen_name("$s10Foundation13__DataStorageC12replaceBytes2in4with6lengthySo8_NSRangeV_SVSgSitF") + internal func replaceBytes(in range_: (location: Int, length: Int), with replacementBytes: UnsafeRawPointer?, length replacementLength: Int) { + let range = range_.location - _offset ..< range_.location + range_.length - _offset let currentLength = _length - let resultingLength = currentLength - (range.upperBound - range.lowerBound) + replacementLength + let resultingLength = currentLength - (range.upperBound &- range.lowerBound) + replacementLength let shift = resultingLength - currentLength let mutableBytes: UnsafeMutableRawPointer if resultingLength > currentLength { From 8b6149e37ce48b74d19bc7e22de500113052b9ee Mon Sep 17 00:00:00 2001 From: Jeremy Schonfeld Date: Mon, 3 Nov 2025 12:22:06 -0800 Subject: [PATCH 2/2] Cleanup range arithmetic --- .../Data/Representations/DataStorage.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/FoundationEssentials/Data/Representations/DataStorage.swift b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift index 6b7ce3a7e..b2740cbbc 100644 --- a/Sources/FoundationEssentials/Data/Representations/DataStorage.swift +++ b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift @@ -345,9 +345,9 @@ internal final class __DataStorage : @unchecked Sendable { @usableFromInline @_silgen_name("$s10Foundation13__DataStorageC12replaceBytes2in4with6lengthySo8_NSRangeV_SVSgSitF") internal func replaceBytes(in range_: (location: Int, length: Int), with replacementBytes: UnsafeRawPointer?, length replacementLength: Int) { - let range = range_.location - _offset ..< range_.location + range_.length - _offset + let range = (location: range_.location - _offset, length: range_.length) let currentLength = _length - let resultingLength = currentLength - (range.upperBound &- range.lowerBound) + replacementLength + let resultingLength = currentLength - range.length + replacementLength let shift = resultingLength - currentLength let mutableBytes: UnsafeMutableRawPointer if resultingLength > currentLength { @@ -358,8 +358,8 @@ internal final class __DataStorage : @unchecked Sendable { } mutableBytes = _bytes! /* shift the trailing bytes */ - let start = range.lowerBound - let length = range.upperBound - range.lowerBound + let start = range.location + let length = range.length if shift != 0 { memmove(mutableBytes + start + replacementLength, mutableBytes + start + length, currentLength - start - length) }