From bba61766f945d275f34f33d76ab58a48e0d47a15 Mon Sep 17 00:00:00 2001 From: Jeremy Schonfeld Date: Wed, 15 Oct 2025 17:06:58 -0700 Subject: [PATCH 1/2] Refactor Data implementation into separate chunks --- Sources/FoundationEssentials/CMakeLists.txt | 1 + .../FoundationEssentials/Data/CMakeLists.txt | 15 +- .../Data/ContiguousBytes.swift | 5 +- .../Data/Data+Base64.swift | 52 +- .../Data/Data+Deprecated.swift | 59 + .../Data/Data+Iterator.swift | 79 + .../Data/Data+Reading.swift | 48 +- .../Data/Data+Searching.swift | 55 + .../Data/Data+Writing.swift | 63 +- Sources/FoundationEssentials/Data/Data.swift | 2095 +---------------- .../Data/DataProtocol.swift | 12 +- .../Data/{Data+Stub.swift => PathOrURL.swift} | 25 +- .../Data/Representations/Data+Inline.swift | 240 ++ .../Representations/Data+InlineSlice.swift | 267 +++ .../Representations/Data+LargeSlice.swift | 250 ++ .../Representations/Data+Representation.swift | 561 +++++ .../Data/Representations/DataStorage.swift | 479 ++++ .../FoundationEssentials/Progress+Stub.swift | 34 + 18 files changed, 2237 insertions(+), 2103 deletions(-) create mode 100644 Sources/FoundationEssentials/Data/Data+Deprecated.swift create mode 100644 Sources/FoundationEssentials/Data/Data+Iterator.swift create mode 100644 Sources/FoundationEssentials/Data/Data+Searching.swift rename Sources/FoundationEssentials/Data/{Data+Stub.swift => PathOrURL.swift} (64%) create mode 100644 Sources/FoundationEssentials/Data/Representations/Data+Inline.swift create mode 100644 Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift create mode 100644 Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift create mode 100644 Sources/FoundationEssentials/Data/Representations/Data+Representation.swift create mode 100644 Sources/FoundationEssentials/Data/Representations/DataStorage.swift create mode 100644 Sources/FoundationEssentials/Progress+Stub.swift diff --git a/Sources/FoundationEssentials/CMakeLists.txt b/Sources/FoundationEssentials/CMakeLists.txt index c896c8c66..fb162b8c7 100644 --- a/Sources/FoundationEssentials/CMakeLists.txt +++ b/Sources/FoundationEssentials/CMakeLists.txt @@ -28,6 +28,7 @@ add_library(FoundationEssentials Logging.swift OutputBuffer.swift Platform.swift + Progress+Stub.swift SortComparator.swift UUID_Wrappers.swift UUID.swift diff --git a/Sources/FoundationEssentials/Data/CMakeLists.txt b/Sources/FoundationEssentials/Data/CMakeLists.txt index 6667ad4d9..16b892e72 100644 --- a/Sources/FoundationEssentials/Data/CMakeLists.txt +++ b/Sources/FoundationEssentials/Data/CMakeLists.txt @@ -2,7 +2,7 @@ ## ## This source file is part of the Swift open source project ## -## Copyright (c) 2024 Apple Inc. and the Swift project authors +## Copyright (c) 2024-2025 Apple Inc. and the Swift project authors ## Licensed under Apache License v2.0 ## ## See LICENSE.txt for license information @@ -13,13 +13,22 @@ ##===----------------------------------------------------------------------===## target_sources(FoundationEssentials PRIVATE + Representations/Data+Inline.swift + Representations/Data+InlineSlice.swift + Representations/Data+LargeSlice.swift + Representations/Data+Representation.swift + Representations/DataStorage.swift + Collections+DataProtocol.swift ContiguousBytes.swift + Data.swift Data+Base64.swift + Data+Deprecated.swift Data+Error.swift + Data+Iterator.swift Data+Reading.swift - Data+Stub.swift + Data+Searching.swift Data+Writing.swift - Data.swift DataProtocol.swift + PathOrURL.swift Pointers+DataProtocol.swift) diff --git a/Sources/FoundationEssentials/Data/ContiguousBytes.swift b/Sources/FoundationEssentials/Data/ContiguousBytes.swift index c7569c6d7..c81c8d00f 100644 --- a/Sources/FoundationEssentials/Data/ContiguousBytes.swift +++ b/Sources/FoundationEssentials/Data/ContiguousBytes.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2018-2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -39,6 +39,9 @@ extension ArraySlice : ContiguousBytes where Element == UInt8 { } @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension ContiguousArray : ContiguousBytes where Element == UInt8 { } +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) +extension Data : ContiguousBytes { } + //===--- Pointer Conformances ---------------------------------------------===// @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) diff --git a/Sources/FoundationEssentials/Data/Data+Base64.swift b/Sources/FoundationEssentials/Data/Data+Base64.swift index bb1d64f60..bdc8985f1 100644 --- a/Sources/FoundationEssentials/Data/Data+Base64.swift +++ b/Sources/FoundationEssentials/Data/Data+Base64.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2023-2024 Apple Inc. and the Swift project authors +// Copyright (c) 2023-2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -25,6 +25,56 @@ import WinSDK import WASILibc #endif +#if !FOUNDATION_FRAMEWORK +extension Data { + + @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) + public struct Base64EncodingOptions : OptionSet, Sendable { + public let rawValue: UInt + + public init(rawValue: UInt) { + self.rawValue = rawValue + } + /// Set the maximum line length to 64 characters, after which a line ending is inserted. + public static let lineLength64Characters = Base64EncodingOptions(rawValue: 1 << 0) + /// Set the maximum line length to 76 characters, after which a line ending is inserted. + public static let lineLength76Characters = Base64EncodingOptions(rawValue: 1 << 1) + /// When a maximum line length is set, specify that the line ending to insert should include a carriage return. + public static let endLineWithCarriageReturn = Base64EncodingOptions(rawValue: 1 << 4) + /// When a maximum line length is set, specify that the line ending to insert should include a line feed. + public static let endLineWithLineFeed = Base64EncodingOptions(rawValue: 1 << 5) + } + + @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) + public struct Base64DecodingOptions : OptionSet, Sendable { + public let rawValue: UInt + + public init(rawValue: UInt) { + self.rawValue = rawValue + } + /// Modify the decoding algorithm so that it ignores unknown non-Base-64 bytes, including line ending characters. + public static let ignoreUnknownCharacters = Base64DecodingOptions(rawValue: 1 << 0) + } +} +#else +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) +extension Data { + // These types are typealiased to the `NSData` options for framework builds only. + public typealias Base64EncodingOptions = NSData.Base64EncodingOptions + public typealias Base64DecodingOptions = NSData.Base64DecodingOptions +} +#endif //!FOUNDATION_FRAMEWORK + +extension Data.Base64EncodingOptions { + /// Use the base64url alphabet to encode the data + @available(FoundationPreview 6.3, *) + public static let base64URLAlphabet = Self(rawValue: 1 << 6) + + /// Omit the `=` padding characters in the end of the base64 encoded result + @available(FoundationPreview 6.3, *) + public static let omitPaddingCharacter = Self(rawValue: 1 << 7) +} + @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Data { diff --git a/Sources/FoundationEssentials/Data/Data+Deprecated.swift b/Sources/FoundationEssentials/Data/Data+Deprecated.swift new file mode 100644 index 000000000..e9c470501 --- /dev/null +++ b/Sources/FoundationEssentials/Data/Data+Deprecated.swift @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +extension Data { + @available(swift, introduced: 4.2) + @available(swift, deprecated: 5, message: "use `init(_:)` instead") + public init(bytes elements: S) where S.Iterator.Element == UInt8 { + self.init(elements) + } + + @available(swift, obsoleted: 4.2) + public init(bytes: Array) { + self.init(bytes) + } + + @available(swift, obsoleted: 4.2) + public init(bytes: ArraySlice) { + self.init(bytes) + } + + /// Access the bytes in the data. + /// + /// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure. + @available(swift, deprecated: 5, message: "use `withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R` instead") + public func withUnsafeBytes(_ body: (UnsafePointer) throws -> ResultType) rethrows -> ResultType { + return try _representation.withUnsafeBytes { + return try body($0.baseAddress?.assumingMemoryBound(to: ContentType.self) ?? UnsafePointer(bitPattern: 0xBAD0)!) + } + } + + /// Mutate the bytes in the data. + /// + /// This function assumes that you are mutating the contents. + /// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure. + @available(swift, deprecated: 5, message: "use `withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R` instead") + public mutating func withUnsafeMutableBytes(_ body: (UnsafeMutablePointer) throws -> ResultType) rethrows -> ResultType { + return try _representation.withUnsafeMutableBytes { + return try body($0.baseAddress?.assumingMemoryBound(to: ContentType.self) ?? UnsafeMutablePointer(bitPattern: 0xBAD0)!) + } + } + + /// Enumerate the contents of the data. + /// + /// In some cases, (for example, a `Data` backed by a `dispatch_data_t`, the bytes may be stored discontinuously. In those cases, this function invokes the closure for each contiguous region of bytes. + /// - parameter block: The closure to invoke for each region of data. You may stop the enumeration by setting the `stop` parameter to `true`. + @available(swift, deprecated: 5, message: "use `regions` or `for-in` instead") + public func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Index, _ stop: inout Bool) -> Void) { + _representation.enumerateBytes(block) + } +} diff --git a/Sources/FoundationEssentials/Data/Data+Iterator.swift b/Sources/FoundationEssentials/Data/Data+Iterator.swift new file mode 100644 index 000000000..e5be6bca7 --- /dev/null +++ b/Sources/FoundationEssentials/Data/Data+Iterator.swift @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +extension Data { + /// An iterator over the contents of the data. + /// + /// The iterator will increment byte-by-byte. + @inlinable // This is @inlinable as trivially computable. + public func makeIterator() -> Data.Iterator { + return Iterator(self, at: startIndex) + } + + public struct Iterator : IteratorProtocol, Sendable { + @usableFromInline + internal typealias Buffer = ( + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) + + @usableFromInline internal let _data: Data + @usableFromInline internal var _buffer: Buffer + @usableFromInline internal var _idx: Data.Index + @usableFromInline internal let _endIdx: Data.Index + + @usableFromInline // This is @usableFromInline as a non-trivial initializer. + internal init(_ data: Data, at loc: Data.Index) { + // The let vars prevent this from being marked as @inlinable + _data = data + _buffer = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) + _idx = loc + _endIdx = data.endIndex + + let bufferSize = MemoryLayout.size + Swift.withUnsafeMutableBytes(of: &_buffer) { + $0.withMemoryRebound(to: UInt8.self) { [endIndex = data.endIndex] buf in + let bufferIdx = (loc - data.startIndex) % bufferSize + let end = (endIndex - (loc - bufferIdx) > bufferSize) ? (loc - bufferIdx + bufferSize) : endIndex + data.copyBytes(to: buf, from: (loc - bufferIdx).. UInt8? { + let idx = _idx + let bufferSize = MemoryLayout.size + + guard idx < _endIdx else { return nil } + _idx += 1 + + let bufferIdx = (idx - _data.startIndex) % bufferSize + + + if bufferIdx == 0 { + var buffer = _buffer + Swift.withUnsafeMutableBytes(of: &buffer) { + $0.withMemoryRebound(to: UInt8.self) { + // populate the buffer + _data.copyBytes(to: $0, from: idx..<(_endIdx - idx > bufferSize ? idx + bufferSize : _endIdx)) + } + } + _buffer = buffer + } + + return Swift.withUnsafeMutableBytes(of: &_buffer) { + $0.load(fromByteOffset: bufferIdx, as: UInt8.self) + } + } + } +} diff --git a/Sources/FoundationEssentials/Data/Data+Reading.swift b/Sources/FoundationEssentials/Data/Data+Reading.swift index 7b0d444dd..aaccd9dd6 100644 --- a/Sources/FoundationEssentials/Data/Data+Reading.swift +++ b/Sources/FoundationEssentials/Data/Data+Reading.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -513,3 +513,49 @@ private func readBytesFromFileDescriptor(_ fd: Int32, path: PathOrURL, buffer in return length - numBytesRemaining } + +extension Data { +#if FOUNDATION_FRAMEWORK + public typealias ReadingOptions = NSData.ReadingOptions +#else + public struct ReadingOptions : OptionSet, Sendable { + public let rawValue: UInt + public init(rawValue: UInt) { self.rawValue = rawValue } + + public static let mappedIfSafe = ReadingOptions(rawValue: 1 << 0) + public static let uncached = ReadingOptions(rawValue: 1 << 1) + public static let alwaysMapped = ReadingOptions(rawValue: 1 << 3) + } +#endif + +#if !FOUNDATION_FRAMEWORK + @_spi(SwiftCorelibsFoundation) + public dynamic init(_contentsOfRemote url: URL, options: ReadingOptions = []) throws { + assert(!url.isFileURL) + throw CocoaError(.fileReadUnsupportedScheme) + } +#endif + + /// Initialize a `Data` with the contents of a `URL`. + /// + /// - parameter url: The `URL` to read. + /// - parameter options: Options for the read operation. Default value is `[]`. + /// - throws: An error in the Cocoa domain, if `url` cannot be read. + public init(contentsOf url: __shared URL, options: ReadingOptions = []) throws { + if url.isFileURL { + self = try readDataFromFile(path: .url(url), reportProgress: true, options: options) + } else { +#if FOUNDATION_FRAMEWORK + // Fallback to NSData, to read via NSURLSession + let d = try NSData(contentsOf: url, options: NSData.ReadingOptions(rawValue: options.rawValue)) + self.init(referencing: d) +#else + try self.init(_contentsOfRemote: url, options: options) +#endif + } + } + + internal init(contentsOfFile path: String, options: ReadingOptions = []) throws { + self = try readDataFromFile(path: .path(path), reportProgress: true, options: options) + } +} diff --git a/Sources/FoundationEssentials/Data/Data+Searching.swift b/Sources/FoundationEssentials/Data/Data+Searching.swift new file mode 100644 index 000000000..375563fe8 --- /dev/null +++ b/Sources/FoundationEssentials/Data/Data+Searching.swift @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +extension Data { +#if FOUNDATION_FRAMEWORK + public typealias SearchOptions = NSData.SearchOptions + + /// Find the given `Data` in the content of this `Data`. + /// + /// - parameter dataToFind: The data to be searched for. + /// - parameter options: Options for the search. Default value is `[]`. + /// - parameter range: The range of this data in which to perform the search. Default value is `nil`, which means the entire content of this data. + /// - returns: A `Range` specifying the location of the found data, or nil if a match could not be found. + /// - precondition: `range` must be in the bounds of the Data. + public func range(of dataToFind: Data, options: Data.SearchOptions = [], in range: Range? = nil) -> Range? { + let nsRange : NSRange + if let r = range { + nsRange = NSRange(location: r.lowerBound - startIndex, length: r.upperBound - r.lowerBound) + } else { + nsRange = NSRange(location: 0, length: count) + } + let result = _representation.withInteriorPointerReference { + let opts = NSData.SearchOptions(rawValue: options.rawValue) + return $0.range(of: dataToFind, options: opts, in: nsRange) + } + if result.location == NSNotFound { + return nil + } + return (result.location + startIndex)..<((result.location + startIndex) + result.length) + } +#else + // TODO: Implement range(of:options:in:) for Foundation package. + + public struct SearchOptions : OptionSet, Sendable { + public let rawValue: UInt + + public init(rawValue: UInt) { + self.rawValue = rawValue + } + /// Search from the end of the data object. + public static let backwards = SearchOptions(rawValue: 1 << 0) + /// Search is limited to start (or end, if searching backwards) of the data object. + public static let anchored = SearchOptions(rawValue: 1 << 1) + } +#endif +} diff --git a/Sources/FoundationEssentials/Data/Data+Writing.swift b/Sources/FoundationEssentials/Data/Data+Writing.swift index 6f51c3d16..4cef7a370 100644 --- a/Sources/FoundationEssentials/Data/Data+Writing.swift +++ b/Sources/FoundationEssentials/Data/Data+Writing.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -720,5 +720,64 @@ private func writeExtendedAttributes(fd: Int32, attributes: [String : Data]) { } } } - #endif // !NO_FILESYSTEM + +extension Data { +#if FOUNDATION_FRAMEWORK + public typealias WritingOptions = NSData.WritingOptions +#else + + // This is imported from the ObjC 'option set', which is actually a combination of an option and an enumeration (file protection). + public struct WritingOptions : OptionSet, Sendable { + public let rawValue: UInt + public init(rawValue: UInt) { self.rawValue = rawValue } + + /// An option to write data to an auxiliary file first and then replace the original file with the auxiliary file when the write completes. +#if os(WASI) + @available(*, unavailable, message: "atomic writing is unavailable in WASI because temporary files are not supported") +#endif + public static let atomic = WritingOptions(rawValue: 1 << 0) + + /// An option that attempts to write data to a file and fails with an error if the destination file already exists. + public static let withoutOverwriting = WritingOptions(rawValue: 1 << 1) + + /// An option to not encrypt the file when writing it out. + public static let noFileProtection = WritingOptions(rawValue: 0x10000000) + + /// An option to make the file accessible only while the device is unlocked. + public static let completeFileProtection = WritingOptions(rawValue: 0x20000000) + + /// An option to allow the file to be accessible while the device is unlocked or the file is already open. + public static let completeFileProtectionUnlessOpen = WritingOptions(rawValue: 0x30000000) + + /// An option to allow the file to be accessible after a user first unlocks the device. + public static let completeFileProtectionUntilFirstUserAuthentication = WritingOptions(rawValue: 0x40000000) + + /// An option the system uses when determining the file protection options that the system assigns to the data. + public static let fileProtectionMask = WritingOptions(rawValue: 0xf0000000) + } +#endif + + /// Write the contents of the `Data` to a location. + /// + /// - parameter url: The location to write the data into. + /// - parameter options: Options for writing the data. Default value is `[]`. + /// - throws: An error in the Cocoa domain, if there is an error writing to the `URL`. + public func write(to url: URL, options: Data.WritingOptions = []) throws { +#if !os(WASI) // `.atomic` is unavailable on WASI + if options.contains(.withoutOverwriting) && options.contains(.atomic) { + fatalError("withoutOverwriting is not supported with atomic") + } +#endif + + guard url.isFileURL else { + throw CocoaError(.fileWriteUnsupportedScheme) + } + +#if !NO_FILESYSTEM + try writeToFile(path: .url(url), data: self, options: options, reportProgress: true) +#else + throw CocoaError(.featureUnsupported) +#endif + } +} diff --git a/Sources/FoundationEssentials/Data/Data.swift b/Sources/FoundationEssentials/Data/Data.swift index 7ec56dca8..2cd254dca 100644 --- a/Sources/FoundationEssentials/Data/Data.swift +++ b/Sources/FoundationEssentials/Data/Data.swift @@ -134,1750 +134,14 @@ internal func _withStackOrHeapBuffer(capacity: Int, _ body: (UnsafeMutableBuffer body(buffer) } -// Underlying storage representation for medium and large data. -// Inlinability strategy: methods from here should not inline into InlineSlice or LargeSlice unless trivial. -// NOTE: older overlays called this class _DataStorage. The two must -// coexist without a conflicting ObjC class name, so it was renamed. -// The old name must not be used in the new runtime. -@usableFromInline -@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) -internal final class __DataStorage : @unchecked Sendable { - @usableFromInline static let maxSize = Int.max >> 1 - @usableFromInline static let vmOpsThreshold = Platform.pageSize * 4 - -#if !FOUNDATION_FRAMEWORK - static func allocate(_ size: Int, _ clear: Bool) -> UnsafeMutableRawPointer? { - if clear { - return calloc(1, size) - } else { - return malloc(size) - } - } - - static func reallocate(_ ptr: UnsafeMutableRawPointer, _ newSize: Int) -> UnsafeMutableRawPointer? { - return realloc(ptr, newSize); - } -#endif // !FOUNDATION_FRAMEWORK - - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - static func move(_ dest_: UnsafeMutableRawPointer, _ source_: UnsafeRawPointer?, _ num_: Int) { - var dest = dest_ - var source = source_ - var num = num_ - if __DataStorage.vmOpsThreshold <= num && ((unsafeBitCast(source, to: Int.self) | Int(bitPattern: dest)) & (Platform.pageSize - 1)) == 0 { - let pages = Platform.roundDownToMultipleOfPageSize(num) - Platform.copyMemoryPages(source!, dest, pages) - source = source!.advanced(by: pages) - dest = dest.advanced(by: pages) - num -= pages - } - if num > 0 { - memmove(dest, source!, num) - } - } - - @inlinable // This is @inlinable as trivially forwarding, and does not escape the _DataStorage boundary layer. - static func shouldAllocateCleared(_ size: Int) -> Bool { - return (size > (128 * 1024)) - } - - @usableFromInline var _bytes: UnsafeMutableRawPointer? - @usableFromInline var _length: Int - @usableFromInline var _capacity: Int - @usableFromInline var _offset: Int - @usableFromInline var _deallocator: ((UnsafeMutableRawPointer, Int) -> Void)? - @usableFromInline var _needToZero: Bool - - @inlinable // This is @inlinable as trivially computable. - var bytes: UnsafeRawPointer? { - return UnsafeRawPointer(_bytes)?.advanced(by: -_offset) - } - - @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is generic and trivially forwarding. - @discardableResult - func withUnsafeBytes(in range: Range, apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - return try apply(UnsafeRawBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length))) - } - - @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is generic and trivially forwarding. - @discardableResult - func withUnsafeMutableBytes(in range: Range, apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { - return try apply(UnsafeMutableRawBufferPointer(start: _bytes!.advanced(by:range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length))) - } - - @inlinable // This is @inlinable as trivially computable. - var mutableBytes: UnsafeMutableRawPointer? { - return _bytes?.advanced(by: _offset &* -1) // _offset is guaranteed to be non-negative, so it can never overflow when negating - } - - @inlinable - static var copyWillRetainMask: Int { -#if _pointerBitWidth(_64) - return Int(bitPattern: 0x8000000000000000) -#elseif _pointerBitWidth(_32) - return Int(bitPattern: 0x80000000) -#endif - } - - @inlinable - static var capacityMask: Int { -#if _pointerBitWidth(_64) - return Int(bitPattern: 0x7FFFFFFFFFFFFFFF) -#elseif _pointerBitWidth(_32) - return Int(bitPattern: 0x7FFFFFFF) -#endif - } - - @inlinable // This is @inlinable as trivially computable. - var capacity: Int { - return _capacity & __DataStorage.capacityMask - } - - @inlinable - var _copyWillRetain: Bool { - get { - return _capacity & __DataStorage.copyWillRetainMask == 0 - } - set { - if !newValue { - _capacity |= __DataStorage.copyWillRetainMask - } else { - _capacity &= __DataStorage.capacityMask - } - } - } - - @inlinable // This is @inlinable as trivially computable. - var length: Int { - get { - return _length - } - set { - setLength(newValue) - } - } - - @inlinable // This is inlinable as trivially computable. - var isExternallyOwned: Bool { - // all __DataStorages will have some sort of capacity, because empty cases hit the .empty enum _Representation - // anything with 0 capacity means that we have not allocated this pointer and consequently mutation is not ours to make. - return _capacity == 0 - } - - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - func ensureUniqueBufferReference(growingTo newLength: Int = 0, clear: Bool = false) { - guard isExternallyOwned || newLength > _capacity else { return } - - if newLength == 0 { - if isExternallyOwned { - let newCapacity = malloc_good_size(_length) - let newBytes = __DataStorage.allocate(newCapacity, false) - __DataStorage.move(newBytes!, _bytes!, _length) - _freeBytes() - _bytes = newBytes - _capacity = newCapacity - _needToZero = false - } - } else if isExternallyOwned { - let newCapacity = malloc_good_size(newLength) - let newBytes = __DataStorage.allocate(newCapacity, clear) - if let bytes = _bytes { - __DataStorage.move(newBytes!, bytes, _length) - } - _freeBytes() - _bytes = newBytes - _capacity = newCapacity - _length = newLength - _needToZero = true - } else { - let cap = _capacity - var additionalCapacity = (newLength >> (__DataStorage.vmOpsThreshold <= newLength ? 2 : 1)) - if Int.max - additionalCapacity < newLength { - additionalCapacity = 0 - } - var newCapacity = malloc_good_size(Swift.max(cap, newLength + additionalCapacity)) - let origLength = _length - var allocateCleared = clear && __DataStorage.shouldAllocateCleared(newCapacity) - var newBytes: UnsafeMutableRawPointer? = nil - if _bytes == nil { - newBytes = __DataStorage.allocate(newCapacity, allocateCleared) - if newBytes == nil { - /* Try again with minimum length */ - allocateCleared = clear && __DataStorage.shouldAllocateCleared(newLength) - newBytes = __DataStorage.allocate(newLength, allocateCleared) - } - } else { - let tryCalloc = (origLength == 0 || (newLength / origLength) >= 4) - if allocateCleared && tryCalloc { - newBytes = __DataStorage.allocate(newCapacity, true) - if let newBytes = newBytes { - __DataStorage.move(newBytes, _bytes!, origLength) - _freeBytes() - } - } - /* Where calloc/memmove/free fails, realloc might succeed */ - if newBytes == nil { - allocateCleared = false - if _deallocator != nil { - newBytes = __DataStorage.allocate(newCapacity, true) - if let newBytes = newBytes { - __DataStorage.move(newBytes, _bytes!, origLength) - _freeBytes() - } - } else { - newBytes = __DataStorage.reallocate(_bytes!, newCapacity) - } - } - /* Try again with minimum length */ - if newBytes == nil { - newCapacity = malloc_good_size(newLength) - allocateCleared = clear && __DataStorage.shouldAllocateCleared(newCapacity) - if allocateCleared && tryCalloc { - newBytes = __DataStorage.allocate(newCapacity, true) - if let newBytes = newBytes { - __DataStorage.move(newBytes, _bytes!, origLength) - _freeBytes() - } - } - if newBytes == nil { - allocateCleared = false - newBytes = __DataStorage.reallocate(_bytes!, newCapacity) - } - } - } - - if newBytes == nil { - /* Could not allocate bytes */ - // At this point if the allocation cannot occur the process is likely out of memory - // and Bad-Things™ are going to happen anyhow - fatalError("unable to allocate memory for length (\(newLength))") - } - - if origLength < newLength && clear && !allocateCleared { - _ = memset(newBytes!.advanced(by: origLength), 0, newLength - origLength) - } - - /* _length set by caller */ - _bytes = newBytes - _capacity = newCapacity - /* Realloc/memset doesn't zero out the entire capacity, so we must be safe and clear next time we grow the length */ - _needToZero = !allocateCleared - } - } - - @inlinable // This is @inlinable as it does not escape the _DataStorage boundary layer. - func _freeBytes() { - if let bytes = _bytes { - if let dealloc = _deallocator { - dealloc(bytes, length) - } else { - free(bytes) - } - } - _deallocator = nil - } - - @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is trivially computed. - func enumerateBytes(in range: Range, _ block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Data.Index, _ stop: inout Bool) -> Void) { - var stopv: Bool = false - let buffer = UnsafeRawBufferPointer(start: _bytes, count: Swift.min(range.upperBound - range.lowerBound, _length)) - buffer.withMemoryRebound(to: UInt8.self) { block($0, 0, &stopv) } - } - - @inlinable // This is @inlinable as it does not escape the _DataStorage boundary layer. - func setLength(_ length: Int) { - let origLength = _length - let newLength = length - if capacity < newLength || _bytes == nil { - ensureUniqueBufferReference(growingTo: newLength, clear: true) - } else if origLength < newLength && _needToZero { - _ = memset(_bytes! + origLength, 0, newLength - origLength) - } else if newLength < origLength { - _needToZero = true - } - _length = newLength - } - - @inlinable // This is @inlinable as it does not escape the _DataStorage boundary layer. - func append(_ bytes: UnsafeRawPointer, length: Int) { - precondition(length >= 0, "Length of appending bytes must not be negative") - let origLength = _length - let newLength = origLength + length - if capacity < newLength || _bytes == nil { - ensureUniqueBufferReference(growingTo: newLength, clear: false) - } - _length = newLength - __DataStorage.move(_bytes!.advanced(by: origLength), bytes, length) - } - - @inlinable // This is @inlinable despite escaping the __DataStorage boundary layer because it is trivially computed. - func get(_ index: Int) -> UInt8 { - // index must have already been validated by the caller - return _bytes!.load(fromByteOffset: index - _offset, as: UInt8.self) - } - - @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is trivially computed. - func set(_ index: Int, to value: UInt8) { - // index must have already been validated by the caller - ensureUniqueBufferReference() - _bytes!.storeBytes(of: value, toByteOffset: index - _offset, as: UInt8.self) - } - - @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is trivially computed. - func copyBytes(to pointer: UnsafeMutableRawPointer, from range: Range) { - let offsetPointer = UnsafeRawBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length)) - UnsafeMutableRawBufferPointer(start: pointer, count: range.upperBound - range.lowerBound).copyMemory(from: offsetPointer) - } - - #if FOUNDATION_FRAMEWORK - @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) - #endif - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - func replaceBytes(in range_: Range, with replacementBytes: UnsafeRawPointer?, length replacementLength: Int) { - let range = range_.lowerBound - _offset ..< range_.upperBound - _offset - let currentLength = _length - let resultingLength = currentLength - (range.upperBound - range.lowerBound) + replacementLength - let shift = resultingLength - currentLength - let mutableBytes: UnsafeMutableRawPointer - if resultingLength > currentLength { - ensureUniqueBufferReference(growingTo: resultingLength) - _length = resultingLength - } else { - ensureUniqueBufferReference() - } - mutableBytes = _bytes! - /* shift the trailing bytes */ - let start = range.lowerBound - let length = range.upperBound - range.lowerBound - if shift != 0 { - memmove(mutableBytes + start + replacementLength, mutableBytes + start + length, currentLength - start - length) - } - if replacementLength != 0 { - if let replacementBytes = replacementBytes { - memmove(mutableBytes + start, replacementBytes, replacementLength) - } else { - _ = memset(mutableBytes + start, 0, replacementLength) - } - } - - if resultingLength < currentLength { - setLength(resultingLength) - } - } - - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - func resetBytes(in range_: Range) { - let range = range_.lowerBound - _offset ..< range_.upperBound - _offset - if range.upperBound - range.lowerBound == 0 { return } - if _length < range.upperBound { - if capacity <= range.upperBound { - ensureUniqueBufferReference(growingTo: range.upperBound, clear: false) - } - _length = range.upperBound - } else { - ensureUniqueBufferReference() - } - _ = memset(_bytes!.advanced(by: range.lowerBound), 0, range.upperBound - range.lowerBound) - } - - @usableFromInline // This is not @inlinable as a non-trivial, non-convenience initializer. - init(length: Int) { - precondition(length < __DataStorage.maxSize) - var capacity = (length < 1024 * 1024 * 1024) ? length + (length >> 2) : length - if __DataStorage.vmOpsThreshold <= capacity { - capacity = Platform.roundUpToMultipleOfPageSize(capacity) - } - - let clear = __DataStorage.shouldAllocateCleared(length) - _bytes = __DataStorage.allocate(capacity, clear)! - _capacity = capacity - _needToZero = !clear - _length = 0 - _offset = 0 - setLength(length) - } - - @usableFromInline // This is not @inlinable as a non-convenience initializer. - init(capacity capacity_: Int = 0) { - var capacity = capacity_ - precondition(capacity < __DataStorage.maxSize) - if __DataStorage.vmOpsThreshold <= capacity { - capacity = Platform.roundUpToMultipleOfPageSize(capacity) - } - _length = 0 - _bytes = __DataStorage.allocate(capacity, false)! - _capacity = capacity - _needToZero = true - _offset = 0 - } - - @usableFromInline // This is not @inlinable as a non-convenience initializer. - init(bytes: UnsafeRawPointer?, length: Int) { - precondition(length < __DataStorage.maxSize) - _offset = 0 - if length == 0 { - _capacity = 0 - _length = 0 - _needToZero = false - _bytes = nil - } else if __DataStorage.vmOpsThreshold <= length { - _capacity = length - _length = length - _needToZero = true - _bytes = __DataStorage.allocate(length, false)! - __DataStorage.move(_bytes!, bytes, length) - } else { - var capacity = length - if __DataStorage.vmOpsThreshold <= capacity { - capacity = Platform.roundUpToMultipleOfPageSize(capacity) - } - _length = length - _bytes = __DataStorage.allocate(capacity, false)! - _capacity = capacity - _needToZero = true - __DataStorage.move(_bytes!, bytes, length) - } - } - - @usableFromInline // This is not @inlinable as a non-convenience initializer. - init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?, offset: Int) { - precondition(length < __DataStorage.maxSize) - _offset = offset - if length == 0 { - _capacity = 0 - _length = 0 - _needToZero = false - _bytes = nil - if let dealloc = deallocator, - let bytes_ = bytes { - dealloc(bytes_, length) - } - } else if !copy { - _capacity = length - _length = length - _needToZero = false - _bytes = bytes - _deallocator = deallocator - } else if __DataStorage.vmOpsThreshold <= length { - _capacity = length - _length = length - _needToZero = true - _bytes = __DataStorage.allocate(length, false)! - __DataStorage.move(_bytes!, bytes, length) - if let dealloc = deallocator { - dealloc(bytes!, length) - } - } else { - var capacity = length - if __DataStorage.vmOpsThreshold <= capacity { - capacity = Platform.roundUpToMultipleOfPageSize(capacity) - } - _length = length - _bytes = __DataStorage.allocate(capacity, false)! - _capacity = capacity - _needToZero = true - __DataStorage.move(_bytes!, bytes, length) - if let dealloc = deallocator { - dealloc(bytes!, length) - } - } - } - - @usableFromInline - init(offset: Int, bytes: UnsafeMutableRawPointer, capacity: Int, needToZero: Bool, length: Int, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?) { - _offset = offset - _bytes = bytes - _capacity = capacity - _needToZero = needToZero - _length = length - _deallocator = deallocator - } - - deinit { - _freeBytes() - } - - @inlinable // This is @inlinable despite escaping the __DataStorage boundary layer because it is trivially computed. - func mutableCopy(_ range: Range) -> __DataStorage { - return __DataStorage(bytes: _bytes?.advanced(by: range.lowerBound - _offset), length: range.upperBound - range.lowerBound, copy: true, deallocator: nil, offset: range.lowerBound) - } -} - -@frozen -@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) -#if compiler(>=6.2) -@_addressableForDependencies -#endif -public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollection, RangeReplaceableCollection, MutableDataProtocol, ContiguousBytes, Sendable { - - public typealias Index = Int - public typealias Indices = Range - - // A small inline buffer of bytes suitable for stack-allocation of small data. - // Inlinability strategy: everything here should be inlined for direct operation on the stack wherever possible. - @usableFromInline - @frozen - internal struct InlineData : Sendable { -#if _pointerBitWidth(_64) - @usableFromInline typealias Buffer = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) //len //enum - @usableFromInline var bytes: Buffer -#elseif _pointerBitWidth(_32) - @usableFromInline typealias Buffer = (UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8) //len //enum - @usableFromInline var bytes: Buffer -#else - #error ("Unsupported architecture: a definition of Buffer needs to be made with N = (MemoryLayout<(Int, Int)>.size - 2) UInt8 members to a tuple") -#endif - @usableFromInline var length: UInt8 - - @inlinable // This is @inlinable as trivially computable. - static func canStore(count: Int) -> Bool { - return count <= MemoryLayout.size - } - - static var maximumCapacity: Int { - return MemoryLayout.size - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ srcBuffer: UnsafeRawBufferPointer) { - self.init(count: srcBuffer.count) - if !srcBuffer.isEmpty { - Swift.withUnsafeMutableBytes(of: &bytes) { dstBuffer in - dstBuffer.baseAddress?.copyMemory(from: srcBuffer.baseAddress!, byteCount: srcBuffer.count) - } - } - } - - @inlinable // This is @inlinable as a trivial initializer. - init(count: Int = 0) { - assert(count <= MemoryLayout.size) -#if _pointerBitWidth(_64) - bytes = (UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0)) -#elseif _pointerBitWidth(_32) - bytes = (UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0)) -#else - #error ("Unsupported architecture: initialization for Buffer is required for this architecture") -#endif - length = UInt8(count) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ slice: InlineSlice, count: Int) { - self.init(count: count) - Swift.withUnsafeMutableBytes(of: &bytes) { dstBuffer in - slice.withUnsafeBytes { srcBuffer in - dstBuffer.copyMemory(from: UnsafeRawBufferPointer(start: srcBuffer.baseAddress, count: count)) - } - } - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ slice: LargeSlice, count: Int) { - self.init(count: count) - Swift.withUnsafeMutableBytes(of: &bytes) { dstBuffer in - slice.withUnsafeBytes { srcBuffer in - dstBuffer.copyMemory(from: UnsafeRawBufferPointer(start: srcBuffer.baseAddress, count: count)) - } - } - } - - @inlinable // This is @inlinable as trivially computable. - var capacity: Int { - return MemoryLayout.size - } - - @inlinable // This is @inlinable as trivially computable. - var count: Int { - get { - return Int(length) - } - set(newValue) { - assert(newValue <= MemoryLayout.size) - if newValue > length { - resetBytes(in: Int(length) ..< newValue) // Also extends length - } else { - length = UInt8(newValue) - } - } - } - - @inlinable // This is @inlinable as trivially computable. - var startIndex: Int { - return 0 - } - - @inlinable // This is @inlinable as trivially computable. - var endIndex: Int { - return count - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - let count = Int(length) - return try Swift.withUnsafeBytes(of: bytes) { (rawBuffer) throws -> Result in - return try apply(UnsafeRawBufferPointer(start: rawBuffer.baseAddress, count: count)) - } - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { - let count = Int(length) - return try Swift.withUnsafeMutableBytes(of: &bytes) { (rawBuffer) throws -> Result in - return try apply(UnsafeMutableRawBufferPointer(start: rawBuffer.baseAddress, count: count)) - } - } - - @inlinable // This is @inlinable as trivially computable. - mutating func append(byte: UInt8) { - let count = self.count - assert(count + 1 <= MemoryLayout.size) - Swift.withUnsafeMutableBytes(of: &bytes) { $0[count] = byte } - self.length += 1 - } - - @inlinable // This is @inlinable as trivially computable. - mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { - guard !buffer.isEmpty else { return } - assert(count + buffer.count <= MemoryLayout.size) - let cnt = count - _ = Swift.withUnsafeMutableBytes(of: &bytes) { rawBuffer in - rawBuffer.baseAddress?.advanced(by: cnt).copyMemory(from: buffer.baseAddress!, byteCount: buffer.count) - } - - length += UInt8(buffer.count) - } - - @inlinable // This is @inlinable as trivially computable. - subscript(index: Index) -> UInt8 { - get { - assert(index <= MemoryLayout.size) - precondition(index < length, "index \(index) is out of bounds of 0..<\(length)") - return Swift.withUnsafeBytes(of: bytes) { rawBuffer -> UInt8 in - return rawBuffer[index] - } - } - set(newValue) { - assert(index <= MemoryLayout.size) - precondition(index < length, "index \(index) is out of bounds of 0..<\(length)") - Swift.withUnsafeMutableBytes(of: &bytes) { rawBuffer in - rawBuffer[index] = newValue - } - } - } - - @inlinable // This is @inlinable as trivially computable. - mutating func resetBytes(in range: Range) { - assert(range.lowerBound <= MemoryLayout.size) - assert(range.upperBound <= MemoryLayout.size) - precondition(range.lowerBound <= length, "index \(range.lowerBound) is out of bounds of 0..<\(length)") - if length < range.upperBound { - length = UInt8(range.upperBound) - } - - let _ = Swift.withUnsafeMutableBytes(of: &bytes) { rawBuffer in - memset(rawBuffer.baseAddress!.advanced(by: range.lowerBound), 0, range.upperBound - range.lowerBound) - } - } - - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - mutating func replaceSubrange(_ subrange: Range, with replacementBytes: UnsafeRawPointer?, count replacementLength: Int) { - assert(subrange.lowerBound <= MemoryLayout.size) - assert(subrange.upperBound <= MemoryLayout.size) - assert(count - (subrange.upperBound - subrange.lowerBound) + replacementLength <= MemoryLayout.size) - precondition(subrange.lowerBound <= length, "index \(subrange.lowerBound) is out of bounds of 0..<\(length)") - precondition(subrange.upperBound <= length, "index \(subrange.upperBound) is out of bounds of 0..<\(length)") - let currentLength = count - let resultingLength = currentLength - (subrange.upperBound - subrange.lowerBound) + replacementLength - let shift = resultingLength - currentLength - Swift.withUnsafeMutableBytes(of: &bytes) { mutableBytes in - /* shift the trailing bytes */ - let start = subrange.lowerBound - let length = subrange.upperBound - subrange.lowerBound - if shift != 0 { - memmove(mutableBytes.baseAddress!.advanced(by: start + replacementLength), mutableBytes.baseAddress!.advanced(by: start + length), currentLength - start - length) - } - if replacementLength != 0 { - memmove(mutableBytes.baseAddress!.advanced(by: start), replacementBytes!, replacementLength) - } - } - length = UInt8(resultingLength) - } - - @inlinable // This is @inlinable as trivially computable. - func copyBytes(to pointer: UnsafeMutableRawPointer, from range: Range) { - precondition(startIndex <= range.lowerBound, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(startIndex <= range.upperBound, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(range.upperBound <= endIndex, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - - Swift.withUnsafeBytes(of: bytes) { - let cnt = Swift.min($0.count, range.upperBound - range.lowerBound) - guard cnt > 0 else { return } - pointer.copyMemory(from: $0.baseAddress!.advanced(by: range.lowerBound), byteCount: cnt) - } - } - - @inline(__always) // This should always be inlined into _Representation.hash(into:). - func hash(into hasher: inout Hasher) { - // **NOTE**: this uses `count` (an Int) and NOT `length` (a UInt8) - // Despite having the same value, they hash differently. InlineSlice and LargeSlice both use `count` (an Int); if you combine the same bytes but with `length` over `count`, you can get a different hash. - // - // This affects slices, which are InlineSlice and not InlineData: - // - // let d = Data([0xFF, 0xFF]) // InlineData - // let s = Data([0, 0xFF, 0xFF]).dropFirst() // InlineSlice - // assert(s == d) - // assert(s.hashValue == d.hashValue) - hasher.combine(count) - - Swift.withUnsafeBytes(of: bytes) { - // We have access to the full byte buffer here, but not all of it is meaningfully used (bytes past self.length may be garbage). - let bytes = UnsafeRawBufferPointer(start: $0.baseAddress, count: self.count) - hasher.combine(bytes: bytes) - } - } - } - -#if _pointerBitWidth(_64) - @usableFromInline internal typealias HalfInt = Int32 -#elseif _pointerBitWidth(_32) - @usableFromInline internal typealias HalfInt = Int16 -#else - #error ("Unsupported architecture: a definition of half of the pointer sized Int needs to be defined for this architecture") -#endif - - // A buffer of bytes too large to fit in an InlineData, but still small enough to fit a storage pointer + range in two words. - // Inlinability strategy: everything here should be easily inlinable as large _DataStorage methods should not inline into here. - @usableFromInline - @frozen - internal struct InlineSlice : Sendable { - // ***WARNING*** - // These ivars are specifically laid out so that they cause the enum _Representation to be 16 bytes on 64 bit platforms. This means we _MUST_ have the class type thing last - @usableFromInline var slice: Range - @usableFromInline var storage: __DataStorage - - @inlinable // This is @inlinable as trivially computable. - static func canStore(count: Int) -> Bool { - return count < HalfInt.max - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ buffer: UnsafeRawBufferPointer) { - assert(buffer.count < HalfInt.max) - self.init(__DataStorage(bytes: buffer.baseAddress, length: buffer.count), count: buffer.count) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(capacity: Int) { - assert(capacity < HalfInt.max) - self.init(__DataStorage(capacity: capacity), count: 0) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(count: Int) { - assert(count < HalfInt.max) - self.init(__DataStorage(length: count), count: count) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ inline: InlineData) { - assert(inline.count < HalfInt.max) - self.init(inline.withUnsafeBytes { return __DataStorage(bytes: $0.baseAddress, length: $0.count) }, count: inline.count) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ inline: InlineData, range: Range) { - assert(range.lowerBound < HalfInt.max) - assert(range.upperBound < HalfInt.max) - self.init(inline.withUnsafeBytes { return __DataStorage(bytes: $0.baseAddress, length: $0.count) }, range: range) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ large: LargeSlice) { - assert(large.range.lowerBound < HalfInt.max) - assert(large.range.upperBound < HalfInt.max) - self.init(large.storage, range: large.range) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ large: LargeSlice, range: Range) { - assert(range.lowerBound < HalfInt.max) - assert(range.upperBound < HalfInt.max) - self.init(large.storage, range: range) - } - - @inlinable // This is @inlinable as a trivial initializer. - init(_ storage: __DataStorage, count: Int) { - assert(count < HalfInt.max) - self.storage = storage - slice = 0..) { - assert(range.lowerBound < HalfInt.max) - assert(range.upperBound < HalfInt.max) - self.storage = storage - slice = HalfInt(range.lowerBound).. 0 { - let additionalRange = Int(slice.upperBound) ..< Int(slice.upperBound) + difference - storage.resetBytes(in: additionalRange) // Also extends storage length - } else { - storage.length += difference - } - slice = slice.lowerBound..<(slice.lowerBound + HalfInt(newValue)) - } - } - - @inlinable // This is @inlinable as trivially computable. - var range: Range { - get { - return Int(slice.lowerBound)..(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - return try storage.withUnsafeBytes(in: range, apply: apply) - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { - ensureUniqueReference() - return try storage.withUnsafeMutableBytes(in: range, apply: apply) - } - - @inlinable // This is @inlinable as reasonably small. - 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 - slice = slice.lowerBound.. UInt8 { - get { - assert(index < HalfInt.max) - precondition(startIndex <= index, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(index < endIndex, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - return storage.get(index) - } - set(newValue) { - assert(index < HalfInt.max) - precondition(startIndex <= index, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(index < endIndex, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - ensureUniqueReference() - storage.set(index, to: newValue) - } - } - - @inlinable // This is @inlinable as reasonably small. - mutating func resetBytes(in range: Range) { - assert(range.lowerBound < HalfInt.max) - assert(range.upperBound < HalfInt.max) - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - ensureUniqueReference() - storage.resetBytes(in: range) - if slice.upperBound < range.upperBound { - slice = slice.lowerBound.., with bytes: UnsafeRawPointer?, count cnt: Int) { - precondition(startIndex <= subrange.lowerBound, "index \(subrange.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(subrange.lowerBound <= endIndex, "index \(subrange.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(startIndex <= subrange.upperBound, "index \(subrange.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(subrange.upperBound <= endIndex, "index \(subrange.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - - ensureUniqueReference() - let upper = range.upperBound - #if FOUNDATION_FRAMEWORK - if #available(macOS 14, iOS 17, watchOS 10, tvOS 17, *) { - storage.replaceBytes(in: subrange, with: bytes, length: cnt) - } else { - let nsRange = NSRange( - location: subrange.lowerBound, - length: subrange.upperBound - subrange.lowerBound) - storage.replaceBytes(in: nsRange, with: bytes, length: cnt) - } - #else - storage.replaceBytes(in: subrange, with: bytes, length: cnt) - #endif - let resultingUpper = upper - (subrange.upperBound - subrange.lowerBound) + cnt - slice = slice.lowerBound..) { - precondition(startIndex <= range.lowerBound, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(startIndex <= range.upperBound, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(range.upperBound <= endIndex, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - storage.copyBytes(to: pointer, from: range) - } - - @inline(__always) // This should always be inlined into _Representation.hash(into:). - func hash(into hasher: inout Hasher) { - hasher.combine(count) - - self.withUnsafeBytes { bytes in - hasher.combine(bytes: bytes) - } - } - } - - // A reference wrapper around a Range for when the range of a data buffer is too large to whole in a single word. - // Inlinability strategy: everything should be inlinable as trivial. - @usableFromInline - @_fixed_layout - internal final class RangeReference : @unchecked Sendable { - @usableFromInline var range: Range - - @inlinable @inline(__always) // This is @inlinable as trivially forwarding. - var lowerBound: Int { - return range.lowerBound - } - - @inlinable @inline(__always) // This is @inlinable as trivially forwarding. - var upperBound: Int { - return range.upperBound - } - - @inlinable @inline(__always) // This is @inlinable as trivially computable. - var count: Int { - // The upper bound is guaranteed to be greater than or equal to the lower bound, and the lower bound must be non-negative so subtraction can never overflow - return range.upperBound &- range.lowerBound - } - - @inlinable @inline(__always) // This is @inlinable as a trivial initializer. - init(_ range: Range) { - self.range = range - } - } - - // A buffer of bytes whose range is too large to fit in a single word. Used alongside a RangeReference to make it fit into _Representation's two-word size. - // Inlinability strategy: everything here should be easily inlinable as large _DataStorage methods should not inline into here. - @usableFromInline - @frozen - internal struct LargeSlice : Sendable { - // ***WARNING*** - // These ivars are specifically laid out so that they cause the enum _Representation to be 16 bytes on 64 bit platforms. This means we _MUST_ have the class type thing last - @usableFromInline var slice: RangeReference - @usableFromInline var storage: __DataStorage - - @inlinable // This is @inlinable as a convenience initializer. - init(_ buffer: UnsafeRawBufferPointer) { - self.init(__DataStorage(bytes: buffer.baseAddress, length: buffer.count), count: buffer.count) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(capacity: Int) { - self.init(__DataStorage(capacity: capacity), count: 0) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(count: Int) { - self.init(__DataStorage(length: count), count: count) - } - - @inlinable // This is @inlinable as a convenience initializer. - init(_ inline: InlineData) { - let storage = inline.withUnsafeBytes { return __DataStorage(bytes: $0.baseAddress, length: $0.count) } - self.init(storage, count: inline.count) - } - - @inlinable // This is @inlinable as a trivial initializer. - init(_ slice: InlineSlice) { - self.storage = slice.storage - self.slice = RangeReference(slice.range) - } - - @inlinable // This is @inlinable as a trivial initializer. - init(_ storage: __DataStorage, count: Int) { - self.storage = storage - self.slice = RangeReference(0..) { - self.storage = storage - self.slice = RangeReference(range) - } - - @inlinable // This is @inlinable as trivially computable (and inlining may help avoid retain-release traffic). - mutating func ensureUniqueReference() { - if !isKnownUniquelyReferenced(&storage) { - storage = storage.mutableCopy(range) - } - if !isKnownUniquelyReferenced(&slice) { - slice = RangeReference(range) - } - } - - @inlinable // This is @inlinable as trivially forwarding. - var startIndex: Int { - return slice.range.lowerBound - } - - @inlinable // This is @inlinable as trivially forwarding. - var endIndex: Int { - return slice.range.upperBound - } - - @inlinable // This is @inlinable as trivially forwarding. - var capacity: Int { - return storage.capacity - } - - @inlinable // This is @inlinable as trivially computable. - mutating func reserveCapacity(_ minimumCapacity: Int) { - ensureUniqueReference() - // the current capacity can be zero (representing externally owned buffer), and count can be greater than the capacity - storage.ensureUniqueBufferReference(growingTo: Swift.max(minimumCapacity, count)) - } - - @inlinable // This is @inlinable as trivially computable. - var count: Int { - get { - return slice.count - } - set(newValue) { - ensureUniqueReference() - let difference = newValue - count - if difference > 0 { - let additionalRange = Int(slice.upperBound) ..< Int(slice.upperBound) + difference - storage.resetBytes(in: additionalRange) // Already sets the length - } else { - storage.length += difference - } - slice.range = slice.range.lowerBound..<(slice.range.lowerBound + newValue) - } - } - - @inlinable // This is @inlinable as it is trivially forwarding. - var range: Range { - return slice.range - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - return try storage.withUnsafeBytes(in: range, apply: apply) - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { - ensureUniqueReference() - return try storage.withUnsafeMutableBytes(in: range, apply: apply) - } - - @inlinable // This is @inlinable as reasonably small. - mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { - 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 - slice.range = slice.range.lowerBound.. UInt8 { - get { - precondition(startIndex <= index, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(index < endIndex, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - return storage.get(index) - } - set(newValue) { - precondition(startIndex <= index, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(index < endIndex, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") - ensureUniqueReference() - storage.set(index, to: newValue) - } - } - - @inlinable // This is @inlinable as reasonably small. - mutating func resetBytes(in range: Range) { - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - ensureUniqueReference() - storage.resetBytes(in: range) - if slice.range.upperBound < range.upperBound { - slice.range = slice.range.lowerBound.., with bytes: UnsafeRawPointer?, count cnt: Int) { - precondition(startIndex <= subrange.lowerBound, "index \(subrange.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(subrange.lowerBound <= endIndex, "index \(subrange.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(startIndex <= subrange.upperBound, "index \(subrange.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(subrange.upperBound <= endIndex, "index \(subrange.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - - ensureUniqueReference() - let upper = range.upperBound - #if FOUNDATION_FRAMEWORK - if #available(macOS 14, iOS 17, watchOS 10, tvOS 17, *) { - storage.replaceBytes(in: subrange, with: bytes, length: cnt) - } else { - let nsRange = NSRange( - location: subrange.lowerBound, - length: subrange.upperBound - subrange.lowerBound) - storage.replaceBytes(in: nsRange, with: bytes, length: cnt) - } - #else - storage.replaceBytes(in: subrange, with: bytes, length: cnt) - #endif - let resultingUpper = upper - (subrange.upperBound - subrange.lowerBound) + cnt - slice.range = slice.range.lowerBound..) { - precondition(startIndex <= range.lowerBound, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(startIndex <= range.upperBound, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - precondition(range.upperBound <= endIndex, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") - storage.copyBytes(to: pointer, from: range) - } - - @inline(__always) // This should always be inlined into _Representation.hash(into:). - func hash(into hasher: inout Hasher) { - hasher.combine(count) - - self.withUnsafeBytes { bytes in - hasher.combine(bytes: bytes) - } - } - } - - // The actual storage for Data's various representations. - // Inlinability strategy: almost everything should be inlinable as forwarding the underlying implementations. (Inlining can also help avoid retain-release traffic around pulling values out of enums.) - @usableFromInline - @frozen - internal enum _Representation : Sendable { - case empty - case inline(InlineData) - case slice(InlineSlice) - case large(LargeSlice) - - @inlinable // This is @inlinable as a trivial initializer. - init(_ buffer: UnsafeRawBufferPointer) { - if buffer.isEmpty { - self = .empty - } else if InlineData.canStore(count: buffer.count) { - self = .inline(InlineData(buffer)) - } else if InlineSlice.canStore(count: buffer.count) { - self = .slice(InlineSlice(buffer)) - } else { - self = .large(LargeSlice(buffer)) - } - } - - @inlinable // This is @inlinable as a trivial initializer. - init(_ buffer: UnsafeRawBufferPointer, owner: AnyObject) { - if buffer.isEmpty { - self = .empty - } else if InlineData.canStore(count: buffer.count) { - self = .inline(InlineData(buffer)) - } else { - let count = buffer.count - let storage = __DataStorage(bytes: UnsafeMutableRawPointer(mutating: buffer.baseAddress), length: count, copy: false, deallocator: { _, _ in - _fixLifetime(owner) - }, offset: 0) - if InlineSlice.canStore(count: count) { - self = .slice(InlineSlice(storage, count: count)) - } else { - self = .large(LargeSlice(storage, count: count)) - } - } - } - - @inlinable // This is @inlinable as a trivial initializer. - init(capacity: Int) { - if capacity == 0 { - self = .empty - } else if InlineData.canStore(count: capacity) { - self = .inline(InlineData()) - } else if InlineSlice.canStore(count: capacity) { - self = .slice(InlineSlice(capacity: capacity)) - } else { - self = .large(LargeSlice(capacity: capacity)) - } - } - - @inlinable // This is @inlinable as a trivial initializer. - init(count: Int) { - if count == 0 { - self = .empty - } else if InlineData.canStore(count: count) { - self = .inline(InlineData(count: count)) - } else if InlineSlice.canStore(count: count) { - self = .slice(InlineSlice(count: count)) - } else { - self = .large(LargeSlice(count: count)) - } - } - - @inlinable // This is @inlinable as a trivial initializer. - init(_ storage: __DataStorage, count: Int) { - if count == 0 { - self = .empty - } else if InlineData.canStore(count: count) { - self = .inline(storage.withUnsafeBytes(in: 0.. 0 else { return } - switch self { - case .empty: - if InlineData.canStore(count: minimumCapacity) { - self = .inline(InlineData()) - } else if InlineSlice.canStore(count: minimumCapacity) { - self = .slice(InlineSlice(capacity: minimumCapacity)) - } else { - self = .large(LargeSlice(capacity: minimumCapacity)) - } - case .inline(let inline): - guard minimumCapacity > inline.capacity else { return } - // we know we are going to be heap promoted - if InlineSlice.canStore(count: minimumCapacity) { - var slice = InlineSlice(inline) - slice.reserveCapacity(minimumCapacity) - self = .slice(slice) - } else { - var slice = LargeSlice(inline) - slice.reserveCapacity(minimumCapacity) - self = .large(slice) - } - case .slice(var slice): - guard minimumCapacity > slice.capacity else { return } - if InlineSlice.canStore(count: minimumCapacity) { - self = .empty - slice.reserveCapacity(minimumCapacity) - self = .slice(slice) - } else { - var large = LargeSlice(slice) - large.reserveCapacity(minimumCapacity) - self = .large(large) - } - case .large(var slice): - guard minimumCapacity > slice.capacity else { return } - self = .empty - slice.reserveCapacity(minimumCapacity) - self = .large(slice) - } - } - - @inlinable // This is @inlinable as reasonably small. - var count: Int { - get { - switch self { - case .empty: return 0 - case .inline(let inline): return inline.count - case .slice(let slice): return slice.count - case .large(let slice): return slice.count - } - } - set(newValue) { - // HACK: The definition of this inline function takes an inout reference to self, giving the optimizer a unique referencing guarantee. - // This allows us to avoid excessive retain-release traffic around modifying enum values, and inlining the function then avoids the additional frame. - @inline(__always) - func apply(_ representation: inout _Representation, _ newValue: Int) -> _Representation? { - switch representation { - case .empty: - if newValue == 0 { - return nil - } else if InlineData.canStore(count: newValue) { - return .inline(InlineData(count: newValue)) - } else if InlineSlice.canStore(count: newValue) { - return .slice(InlineSlice(count: newValue)) - } else { - return .large(LargeSlice(count: newValue)) - } - case .inline(var inline): - if newValue == 0 { - return .empty - } else if InlineData.canStore(count: newValue) { - guard inline.count != newValue else { return nil } - inline.count = newValue - return .inline(inline) - } else if InlineSlice.canStore(count: newValue) { - var slice = InlineSlice(inline) - slice.count = newValue - return .slice(slice) - } else { - var slice = LargeSlice(inline) - slice.count = newValue - return .large(slice) - } - case .slice(var slice): - if newValue == 0 && slice.startIndex == 0 { - return .empty - } else if slice.startIndex == 0 && InlineData.canStore(count: newValue) { - return .inline(InlineData(slice, count: newValue)) - } else if InlineSlice.canStore(count: newValue + slice.startIndex) { - guard slice.count != newValue else { return nil } - representation = .empty // TODO: remove this when mgottesman lands optimizations - slice.count = newValue - return .slice(slice) - } else { - var newSlice = LargeSlice(slice) - newSlice.count = newValue - return .large(newSlice) - } - case .large(var slice): - if newValue == 0 && slice.startIndex == 0 { - return .empty - } else if slice.startIndex == 0 && InlineData.canStore(count: newValue) { - return .inline(InlineData(slice, count: newValue)) - } else { - guard slice.count != newValue else { return nil} - representation = .empty // TODO: remove this when mgottesman lands optimizations - slice.count = newValue - return .large(slice) - } - } - } - - if let rep = apply(&self, newValue) { - self = rep - } - } - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { - switch self { - case .empty: - let empty = InlineData() - return try empty.withUnsafeBytes(apply) - case .inline(let inline): - return try inline.withUnsafeBytes(apply) - case .slice(let slice): - return try slice.withUnsafeBytes(apply) - case .large(let slice): - return try slice.withUnsafeBytes(apply) - } - } - - @inlinable // This is @inlinable as a generic, trivially forwarding function. - mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { - switch self { - case .empty: - var empty = InlineData() - return try empty.withUnsafeMutableBytes(apply) - case .inline(var inline): - defer { self = .inline(inline) } - return try inline.withUnsafeMutableBytes(apply) - case .slice(var slice): - self = .empty - defer { self = .slice(slice) } - return try slice.withUnsafeMutableBytes(apply) - case .large(var slice): - self = .empty - defer { self = .large(slice) } - return try slice.withUnsafeMutableBytes(apply) - } - } - - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Index, _ stop: inout Bool) -> Void) { - switch self { - case .empty: - var stop = false - block(UnsafeBufferPointer(start: nil, count: 0), 0, &stop) - case .inline(let inline): - inline.withUnsafeBytes { - var stop = false - $0.withMemoryRebound(to: UInt8.self) { block($0, 0, &stop) } - } - case .slice(let slice): - slice.storage.enumerateBytes(in: slice.range, block) - case .large(let slice): - slice.storage.enumerateBytes(in: slice.range, block) - } - } - - @inlinable // This is @inlinable as reasonably small. - mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { - switch self { - case .empty: - self = _Representation(buffer) - case .inline(var inline): - if InlineData.canStore(count: inline.count + buffer.count) { - inline.append(contentsOf: buffer) - self = .inline(inline) - } else if InlineSlice.canStore(count: inline.count + buffer.count) { - var newSlice = InlineSlice(inline) - newSlice.append(contentsOf: buffer) - self = .slice(newSlice) - } else { - var newSlice = LargeSlice(inline) - newSlice.append(contentsOf: buffer) - self = .large(newSlice) - } - case .slice(var slice): - if InlineSlice.canStore(count: slice.range.upperBound + buffer.count) { - self = .empty - defer { self = .slice(slice) } - slice.append(contentsOf: buffer) - } else { - self = .empty - var newSlice = LargeSlice(slice) - newSlice.append(contentsOf: buffer) - self = .large(newSlice) - } - case .large(var slice): - self = .empty - defer { self = .large(slice) } - slice.append(contentsOf: buffer) - } - } - - @inlinable // This is @inlinable as reasonably small. - mutating func resetBytes(in range: Range) { - switch self { - case .empty: - if range.upperBound == 0 { - self = .empty - } else if InlineData.canStore(count: range.upperBound) { - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - self = .inline(InlineData(count: range.upperBound)) - } else if InlineSlice.canStore(count: range.upperBound) { - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - self = .slice(InlineSlice(count: range.upperBound)) - } else { - precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") - self = .large(LargeSlice(count: range.upperBound)) - } - case .inline(var inline): - if inline.count < range.upperBound { - if InlineSlice.canStore(count: range.upperBound) { - var slice = InlineSlice(inline) - slice.resetBytes(in: range) - self = .slice(slice) - } else { - var slice = LargeSlice(inline) - slice.resetBytes(in: range) - self = .large(slice) - } - } else { - inline.resetBytes(in: range) - self = .inline(inline) - } - case .slice(var slice): - if InlineSlice.canStore(count: range.upperBound) { - self = .empty - slice.resetBytes(in: range) - self = .slice(slice) - } else { - self = .empty - var newSlice = LargeSlice(slice) - newSlice.resetBytes(in: range) - self = .large(newSlice) - } - case .large(var slice): - self = .empty - slice.resetBytes(in: range) - self = .large(slice) - } - } - - @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. - mutating func replaceSubrange(_ subrange: Range, with bytes: UnsafeRawPointer?, count cnt: Int) { - switch self { - case .empty: - precondition(subrange.lowerBound == 0 && subrange.upperBound == 0, "range \(subrange) out of bounds of 0..<0") - if cnt == 0 { - return - } else if InlineData.canStore(count: cnt) { - self = .inline(InlineData(UnsafeRawBufferPointer(start: bytes, count: cnt))) - } else if InlineSlice.canStore(count: cnt) { - self = .slice(InlineSlice(UnsafeRawBufferPointer(start: bytes, count: cnt))) - } else { - self = .large(LargeSlice(UnsafeRawBufferPointer(start: bytes, count: cnt))) - } - case .inline(var inline): - let resultingCount = inline.count + cnt - (subrange.upperBound - subrange.lowerBound) - if resultingCount == 0 { - self = .empty - } else if InlineData.canStore(count: resultingCount) { - inline.replaceSubrange(subrange, with: bytes, count: cnt) - self = .inline(inline) - } else if InlineSlice.canStore(count: resultingCount) { - var slice = InlineSlice(inline) - slice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .slice(slice) - } else { - var slice = LargeSlice(inline) - slice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .large(slice) - } - case .slice(var slice): - let resultingUpper = slice.endIndex + cnt - (subrange.upperBound - subrange.lowerBound) - if slice.startIndex == 0 && resultingUpper == 0 { - self = .empty - } else if slice.startIndex == 0 && InlineData.canStore(count: resultingUpper) { - self = .empty - slice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .inline(InlineData(slice, count: slice.count)) - } else if InlineSlice.canStore(count: resultingUpper) { - self = .empty - slice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .slice(slice) - } else { - self = .empty - var newSlice = LargeSlice(slice) - newSlice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .large(newSlice) - } - case .large(var slice): - let resultingUpper = slice.endIndex + cnt - (subrange.upperBound - subrange.lowerBound) - if slice.startIndex == 0 && resultingUpper == 0 { - self = .empty - } else if slice.startIndex == 0 && InlineData.canStore(count: resultingUpper) { - var inline = InlineData(count: resultingUpper) - inline.withUnsafeMutableBytes { inlineBuffer in - if cnt > 0 { - inlineBuffer.baseAddress?.advanced(by: subrange.lowerBound).copyMemory(from: bytes!, byteCount: cnt) - } - slice.withUnsafeBytes { buffer in - if subrange.lowerBound > 0 { - inlineBuffer.baseAddress?.copyMemory(from: buffer.baseAddress!, byteCount: subrange.lowerBound) - } - if subrange.upperBound < resultingUpper { - inlineBuffer.baseAddress?.advanced(by: subrange.upperBound).copyMemory(from: buffer.baseAddress!.advanced(by: subrange.upperBound), byteCount: resultingUpper - subrange.upperBound) - } - } - } - self = .inline(inline) - } else if InlineSlice.canStore(count: slice.startIndex) && InlineSlice.canStore(count: resultingUpper) { - self = .empty - var newSlice = InlineSlice(slice) - newSlice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .slice(newSlice) - } else { - self = .empty - slice.replaceSubrange(subrange, with: bytes, count: cnt) - self = .large(slice) - } - } - } - - @inlinable // This is @inlinable as trivially forwarding. - subscript(index: Index) -> UInt8 { - get { - switch self { - case .empty: preconditionFailure("index \(index) out of range of 0") - case .inline(let inline): return inline[index] - case .slice(let slice): return slice[index] - case .large(let slice): return slice[index] - } - } - set(newValue) { - switch self { - case .empty: preconditionFailure("index \(index) out of range of 0") - case .inline(var inline): - inline[index] = newValue - self = .inline(inline) - case .slice(var slice): - self = .empty - slice[index] = newValue - self = .slice(slice) - case .large(var slice): - self = .empty - slice[index] = newValue - self = .large(slice) - } - } - } - - @inlinable // This is @inlinable as reasonably small. - subscript(bounds: Range) -> Data { - get { - switch self { - case .empty: - precondition(bounds.lowerBound == 0 && (bounds.upperBound - bounds.lowerBound) == 0, "Range \(bounds) out of bounds 0..<0") - return Data() - case .inline(let inline): - precondition(bounds.upperBound <= inline.count, "Range \(bounds) out of bounds 0..<\(inline.count)") - if bounds.lowerBound == 0 { - var newInline = inline - newInline.count = bounds.upperBound - return Data(representation: .inline(newInline)) - } else { - return Data(representation: .slice(InlineSlice(inline, range: bounds))) - } - case .slice(let slice): - precondition(slice.startIndex <= bounds.lowerBound, "Range \(bounds) out of bounds \(slice.range)") - precondition(bounds.lowerBound <= slice.endIndex, "Range \(bounds) out of bounds \(slice.range)") - precondition(slice.startIndex <= bounds.upperBound, "Range \(bounds) out of bounds \(slice.range)") - precondition(bounds.upperBound <= slice.endIndex, "Range \(bounds) out of bounds \(slice.range)") - if bounds.lowerBound == 0 && bounds.upperBound == 0 { - return Data() - } else if bounds.lowerBound == 0 && InlineData.canStore(count: bounds.count) { - return Data(representation: .inline(InlineData(slice, count: bounds.count))) - } else { - var newSlice = slice - newSlice.range = bounds - return Data(representation: .slice(newSlice)) - } - case .large(let slice): - precondition(slice.startIndex <= bounds.lowerBound, "Range \(bounds) out of bounds \(slice.range)") - precondition(bounds.lowerBound <= slice.endIndex, "Range \(bounds) out of bounds \(slice.range)") - precondition(slice.startIndex <= bounds.upperBound, "Range \(bounds) out of bounds \(slice.range)") - precondition(bounds.upperBound <= slice.endIndex, "Range \(bounds) out of bounds \(slice.range)") - if bounds.lowerBound == 0 && bounds.upperBound == 0 { - return Data() - } else if bounds.lowerBound == 0 && InlineData.canStore(count: bounds.upperBound) { - return Data(representation: .inline(InlineData(slice, count: bounds.upperBound))) - } else if InlineSlice.canStore(count: bounds.lowerBound) && InlineSlice.canStore(count: bounds.upperBound) { - return Data(representation: .slice(InlineSlice(slice, range: bounds))) - } else { - var newSlice = slice - newSlice.slice = RangeReference(bounds) - return Data(representation: .large(newSlice)) - } - } - } - } - - @inlinable // This is @inlinable as trivially forwarding. - var startIndex: Int { - switch self { - case .empty: return 0 - case .inline: return 0 - case .slice(let slice): return slice.startIndex - case .large(let slice): return slice.startIndex - } - } - - @inlinable // This is @inlinable as trivially forwarding. - var endIndex: Int { - switch self { - case .empty: return 0 - case .inline(let inline): return inline.count - case .slice(let slice): return slice.endIndex - case .large(let slice): return slice.endIndex - } - } - - @inlinable // This is @inlinable as trivially forwarding. - func copyBytes(to pointer: UnsafeMutableRawPointer, from range: Range) { - switch self { - case .empty: - precondition(range.lowerBound == 0 && range.upperBound == 0, "Range \(range) out of bounds 0..<0") - return - case .inline(let inline): - inline.copyBytes(to: pointer, from: range) - case .slice(let slice): - slice.copyBytes(to: pointer, from: range) - case .large(let slice): - slice.copyBytes(to: pointer, from: range) - } - } - - @inline(__always) // This should always be inlined into Data.hash(into:). - func hash(into hasher: inout Hasher) { - switch self { - case .empty: - hasher.combine(0) - case .inline(let inline): - inline.hash(into: &hasher) - case .slice(let slice): - slice.hash(into: &hasher) - case .large(let large): - large.hash(into: &hasher) - } - } - } +@frozen +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) +#if compiler(>=6.2) +@_addressableForDependencies +#endif +public struct Data : RandomAccessCollection, MutableCollection, RangeReplaceableCollection, Sendable { + public typealias Index = Int + public typealias Indices = Range @usableFromInline internal var _representation: _Representation @@ -2067,101 +331,10 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect } } - @available(swift, introduced: 4.2) - @available(swift, deprecated: 5, message: "use `init(_:)` instead") - public init(bytes elements: S) where S.Iterator.Element == UInt8 { - self.init(elements) - } - - @available(swift, obsoleted: 4.2) - public init(bytes: Array) { - self.init(bytes) - } - - @available(swift, obsoleted: 4.2) - public init(bytes: ArraySlice) { - self.init(bytes) - } - @inlinable // This is @inlinable as a trivial initializer. internal init(representation: _Representation) { _representation = representation } - -#if FOUNDATION_FRAMEWORK - public typealias ReadingOptions = NSData.ReadingOptions - public typealias WritingOptions = NSData.WritingOptions -#else - public struct ReadingOptions : OptionSet, Sendable { - public let rawValue: UInt - public init(rawValue: UInt) { self.rawValue = rawValue } - - public static let mappedIfSafe = ReadingOptions(rawValue: 1 << 0) - public static let uncached = ReadingOptions(rawValue: 1 << 1) - public static let alwaysMapped = ReadingOptions(rawValue: 1 << 3) - } - - // This is imported from the ObjC 'option set', which is actually a combination of an option and an enumeration (file protection). - public struct WritingOptions : OptionSet, Sendable { - public let rawValue: UInt - public init(rawValue: UInt) { self.rawValue = rawValue } - - /// An option to write data to an auxiliary file first and then replace the original file with the auxiliary file when the write completes. -#if os(WASI) - @available(*, unavailable, message: "atomic writing is unavailable in WASI because temporary files are not supported") -#endif - public static let atomic = WritingOptions(rawValue: 1 << 0) - - /// An option that attempts to write data to a file and fails with an error if the destination file already exists. - public static let withoutOverwriting = WritingOptions(rawValue: 1 << 1) - - /// An option to not encrypt the file when writing it out. - public static let noFileProtection = WritingOptions(rawValue: 0x10000000) - - /// An option to make the file accessible only while the device is unlocked. - public static let completeFileProtection = WritingOptions(rawValue: 0x20000000) - - /// An option to allow the file to be accessible while the device is unlocked or the file is already open. - public static let completeFileProtectionUnlessOpen = WritingOptions(rawValue: 0x30000000) - - /// An option to allow the file to be accessible after a user first unlocks the device. - public static let completeFileProtectionUntilFirstUserAuthentication = WritingOptions(rawValue: 0x40000000) - - /// An option the system uses when determining the file protection options that the system assigns to the data. - public static let fileProtectionMask = WritingOptions(rawValue: 0xf0000000) - } -#endif - - #if !FOUNDATION_FRAMEWORK - @_spi(SwiftCorelibsFoundation) - public dynamic init(_contentsOfRemote url: URL, options: ReadingOptions = []) throws { - assert(!url.isFileURL) - throw CocoaError(.fileReadUnsupportedScheme) - } - #endif - - /// Initialize a `Data` with the contents of a `URL`. - /// - /// - parameter url: The `URL` to read. - /// - parameter options: Options for the read operation. Default value is `[]`. - /// - throws: An error in the Cocoa domain, if `url` cannot be read. - public init(contentsOf url: __shared URL, options: ReadingOptions = []) throws { - if url.isFileURL { - self = try readDataFromFile(path: .url(url), reportProgress: true, options: options) - } else { - #if FOUNDATION_FRAMEWORK - // Fallback to NSData, to read via NSURLSession - let d = try NSData(contentsOf: url, options: NSData.ReadingOptions(rawValue: options.rawValue)) - self.init(referencing: d) - #else - try self.init(_contentsOfRemote: url, options: options) - #endif - } - } - - internal init(contentsOfFile path: String, options: ReadingOptions = []) throws { - self = try readDataFromFile(path: .path(path), reportProgress: true, options: options) - } // ----------------------------------- // MARK: - Properties and Functions @@ -2187,21 +360,6 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect } } - @inlinable // This is @inlinable as trivially computable. - public var regions: CollectionOfOne { - return CollectionOfOne(self) - } - - /// Access the bytes in the data. - /// - /// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure. - @available(swift, deprecated: 5, message: "use `withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R` instead") - public func withUnsafeBytes(_ body: (UnsafePointer) throws -> ResultType) rethrows -> ResultType { - return try _representation.withUnsafeBytes { - return try body($0.baseAddress?.assumingMemoryBound(to: ContentType.self) ?? UnsafePointer(bitPattern: 0xBAD0)!) - } - } - @inlinable // This is @inlinable as a generic, trivially forwarding function. public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { return try _representation.withUnsafeBytes(body) @@ -2330,17 +488,6 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect } } - /// Mutate the bytes in the data. - /// - /// This function assumes that you are mutating the contents. - /// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure. - @available(swift, deprecated: 5, message: "use `withUnsafeMutableBytes(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R` instead") - public mutating func withUnsafeMutableBytes(_ body: (UnsafeMutablePointer) throws -> ResultType) rethrows -> ResultType { - return try _representation.withUnsafeMutableBytes { - return try body($0.baseAddress?.assumingMemoryBound(to: ContentType.self) ?? UnsafeMutablePointer(bitPattern: 0xBAD0)!) - } - } - @inlinable // This is @inlinable as a generic, trivially forwarding function. public mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { return try _representation.withUnsafeMutableBytes(body) @@ -2405,15 +552,6 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect // MARK: - - /// Enumerate the contents of the data. - /// - /// In some cases, (for example, a `Data` backed by a `dispatch_data_t`, the bytes may be stored discontinuously. In those cases, this function invokes the closure for each contiguous region of bytes. - /// - parameter block: The closure to invoke for each region of data. You may stop the enumeration by setting the `stop` parameter to `true`. - @available(swift, deprecated: 5, message: "use `regions` or `for-in` instead") - public func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Index, _ stop: inout Bool) -> Void) { - _representation.enumerateBytes(block) - } - @inlinable // This is @inlinable as a generic, trivially forwarding function. internal mutating func _append(_ buffer : UnsafeBufferPointer) { if buffer.isEmpty { return } @@ -2593,49 +731,16 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect } } - // MARK: - - - /// Write the contents of the `Data` to a location. - /// - /// - parameter url: The location to write the data into. - /// - parameter options: Options for writing the data. Default value is `[]`. - /// - throws: An error in the Cocoa domain, if there is an error writing to the `URL`. - public func write(to url: URL, options: Data.WritingOptions = []) throws { -#if !os(WASI) // `.atomic` is unavailable on WASI - if options.contains(.withoutOverwriting) && options.contains(.atomic) { - fatalError("withoutOverwriting is not supported with atomic") - } -#endif - - guard url.isFileURL else { - throw CocoaError(.fileWriteUnsupportedScheme) - } - -#if !NO_FILESYSTEM - try writeToFile(path: .url(url), data: self, options: options, reportProgress: true) -#else - throw CocoaError(.featureUnsupported) -#endif - } - // MARK: - // - /// The hash value for the data. - @inline(never) // This is not inlinable as emission into clients could cause cross-module inconsistencies if they are not all recompiled together. - public func hash(into hasher: inout Hasher) { - _representation.hash(into: &hasher) - } - public func advanced(by amount: Int) -> Data { precondition(amount >= 0) let start = self.index(self.startIndex, offsetBy: amount) precondition(start <= self.endIndex) return Data(self[start...]) } - - // MARK: - - + // MARK: - // MARK: Index and Subscript @@ -2730,106 +835,26 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect return (Iterator(self, at: startIndex + cnt), buffer.index(buffer.startIndex, offsetBy: cnt)) } +} - /// An iterator over the contents of the data. - /// - /// The iterator will increment byte-by-byte. - @inlinable // This is @inlinable as trivially computable. - public func makeIterator() -> Data.Iterator { - return Iterator(self, at: startIndex) - } - - public struct Iterator : IteratorProtocol, Sendable { - @usableFromInline - internal typealias Buffer = ( - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) - - @usableFromInline internal let _data: Data - @usableFromInline internal var _buffer: Buffer - @usableFromInline internal var _idx: Data.Index - @usableFromInline internal let _endIdx: Data.Index - - @usableFromInline // This is @usableFromInline as a non-trivial initializer. - internal init(_ data: Data, at loc: Data.Index) { - // The let vars prevent this from being marked as @inlinable - _data = data - _buffer = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) - _idx = loc - _endIdx = data.endIndex - - let bufferSize = MemoryLayout.size - Swift.withUnsafeMutableBytes(of: &_buffer) { - $0.withMemoryRebound(to: UInt8.self) { [endIndex = data.endIndex] buf in - let bufferIdx = (loc - data.startIndex) % bufferSize - let end = (endIndex - (loc - bufferIdx) > bufferSize) ? (loc - bufferIdx + bufferSize) : endIndex - data.copyBytes(to: buf, from: (loc - bufferIdx).. UInt8? { - let idx = _idx - let bufferSize = MemoryLayout.size - - guard idx < _endIdx else { return nil } - _idx += 1 - - let bufferIdx = (idx - _data.startIndex) % bufferSize - - - if bufferIdx == 0 { - var buffer = _buffer - Swift.withUnsafeMutableBytes(of: &buffer) { - $0.withMemoryRebound(to: UInt8.self) { - // populate the buffer - _data.copyBytes(to: $0, from: idx..<(_endIdx - idx > bufferSize ? idx + bufferSize : _endIdx)) - } - } - _buffer = buffer - } - - return Swift.withUnsafeMutableBytes(of: &_buffer) { - $0.load(fromByteOffset: bufferIdx, as: UInt8.self) - } - } - } +@available(macOS, unavailable, introduced: 10.10) +@available(iOS, unavailable, introduced: 8.0) +@available(tvOS, unavailable, introduced: 9.0) +@available(watchOS, unavailable, introduced: 2.0) +@available(*, unavailable) +extension Data.Deallocator : Sendable {} - // MARK: - Range - -#if FOUNDATION_FRAMEWORK - /// Find the given `Data` in the content of this `Data`. - /// - /// - parameter dataToFind: The data to be searched for. - /// - parameter options: Options for the search. Default value is `[]`. - /// - parameter range: The range of this data in which to perform the search. Default value is `nil`, which means the entire content of this data. - /// - returns: A `Range` specifying the location of the found data, or nil if a match could not be found. - /// - precondition: `range` must be in the bounds of the Data. - public func range(of dataToFind: Data, options: Data.SearchOptions = [], in range: Range? = nil) -> Range? { - let nsRange : NSRange - if let r = range { - nsRange = NSRange(location: r.lowerBound - startIndex, length: r.upperBound - r.lowerBound) - } else { - nsRange = NSRange(location: 0, length: count) - } - let result = _representation.withInteriorPointerReference { - let opts = NSData.SearchOptions(rawValue: options.rawValue) - return $0.range(of: dataToFind, options: opts, in: nsRange) - } - if result.location == NSNotFound { - return nil - } - return (result.location + startIndex)..<((result.location + startIndex) + result.length) +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) +extension Data : Hashable { + /// The hash value for the data. + @inline(never) // This is not inlinable as emission into clients could cause cross-module inconsistencies if they are not all recompiled together. + public func hash(into hasher: inout Hasher) { + _representation.hash(into: &hasher) } -#else - // TODO: Implement range(of:options:in:) for Foundation package. -#endif - - // MARK: - - // +} +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) +extension Data : Equatable { /// Returns `true` if the two `Data` arguments are equal. @inlinable // This is @inlinable as emission into clients is safe -- the concept of equality on Data will not change. public static func ==(d1 : Data, d2 : Data) -> Bool { @@ -2870,76 +895,6 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect } } -@available(macOS, unavailable, introduced: 10.10) -@available(iOS, unavailable, introduced: 8.0) -@available(tvOS, unavailable, introduced: 9.0) -@available(watchOS, unavailable, introduced: 2.0) -@available(*, unavailable) -extension Data.Deallocator : Sendable {} - -#if !FOUNDATION_FRAMEWORK -// MARK: Exported Types -extension Data { - public struct SearchOptions : OptionSet, Sendable { - public let rawValue: UInt - - public init(rawValue: UInt) { - self.rawValue = rawValue - } - /// Search from the end of the data object. - public static let backwards = SearchOptions(rawValue: 1 << 0) - /// Search is limited to start (or end, if searching backwards) of the data object. - public static let anchored = SearchOptions(rawValue: 1 << 1) - } - - @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) - public struct Base64EncodingOptions : OptionSet, Sendable { - public let rawValue: UInt - - public init(rawValue: UInt) { - self.rawValue = rawValue - } - /// Set the maximum line length to 64 characters, after which a line ending is inserted. - public static let lineLength64Characters = Base64EncodingOptions(rawValue: 1 << 0) - /// Set the maximum line length to 76 characters, after which a line ending is inserted. - public static let lineLength76Characters = Base64EncodingOptions(rawValue: 1 << 1) - /// When a maximum line length is set, specify that the line ending to insert should include a carriage return. - public static let endLineWithCarriageReturn = Base64EncodingOptions(rawValue: 1 << 4) - /// When a maximum line length is set, specify that the line ending to insert should include a line feed. - public static let endLineWithLineFeed = Base64EncodingOptions(rawValue: 1 << 5) - } - - @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) - public struct Base64DecodingOptions : OptionSet, Sendable { - public let rawValue: UInt - - public init(rawValue: UInt) { - self.rawValue = rawValue - } - /// Modify the decoding algorithm so that it ignores unknown non-Base-64 bytes, including line ending characters. - public static let ignoreUnknownCharacters = Base64DecodingOptions(rawValue: 1 << 0) - } -} -#else -@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) -extension Data { - // These types are typealiased to the `NSData` options for framework builds only. - public typealias SearchOptions = NSData.SearchOptions - public typealias Base64EncodingOptions = NSData.Base64EncodingOptions - public typealias Base64DecodingOptions = NSData.Base64DecodingOptions -} -#endif //!FOUNDATION_FRAMEWORK - -extension Data.Base64EncodingOptions { - /// Use the base64url alphabet to encode the data - @available(FoundationPreview 6.3, *) - public static let base64URLAlphabet = Self(rawValue: 1 << 6) - - /// Omit the `=` padding characters in the end of the base64 encoded result - @available(FoundationPreview 6.3, *) - public static let omitPaddingCharacter = Self(rawValue: 1 << 7) -} - @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Data : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable { /// A human-readable description for the data. diff --git a/Sources/FoundationEssentials/Data/DataProtocol.swift b/Sources/FoundationEssentials/Data/DataProtocol.swift index 2f3667af8..d842f135f 100644 --- a/Sources/FoundationEssentials/Data/DataProtocol.swift +++ b/Sources/FoundationEssentials/Data/DataProtocol.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2018-2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -231,6 +231,16 @@ extension MutableDataProtocol { } } +//===--- DataProtocol Conformances ----------------------------------------===// + +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) +extension Data : MutableDataProtocol { + @inlinable // This is @inlinable as trivially computable. + public var regions: CollectionOfOne { + return CollectionOfOne(self) + } +} + //===--- DataProtocol Conditional Conformances ----------------------------===// @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) diff --git a/Sources/FoundationEssentials/Data/Data+Stub.swift b/Sources/FoundationEssentials/Data/PathOrURL.swift similarity index 64% rename from Sources/FoundationEssentials/Data/Data+Stub.swift rename to Sources/FoundationEssentials/Data/PathOrURL.swift index b449511de..0777a4465 100644 --- a/Sources/FoundationEssentials/Data/Data+Stub.swift +++ b/Sources/FoundationEssentials/Data/PathOrURL.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2018 - 2023 Apple Inc. and the Swift project authors +// Copyright (c) 2018 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -10,29 +10,6 @@ // //===----------------------------------------------------------------------===// -#if !FOUNDATION_FRAMEWORK - -// Placeholder for Progress -internal final class Progress { - var completedUnitCount: Int64 - var totalUnitCount: Int64 - - init(totalUnitCount: Int64) { - self.completedUnitCount = 0 - self.totalUnitCount = totalUnitCount - } - - func becomeCurrent(withPendingUnitCount: Int64) { } - func resignCurrent() { } - var isCancelled: Bool { false } - static func current() -> Progress? { nil } - var fractionCompleted: Double { - 0.0 - } -} - -#endif // !FOUNDATION_FRAMEWORK - internal enum PathOrURL { case path(String) case url(URL) diff --git a/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift b/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift new file mode 100644 index 000000000..b0be12d2a --- /dev/null +++ b/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift @@ -0,0 +1,240 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +extension Data { + // A small inline buffer of bytes suitable for stack-allocation of small data. + // Inlinability strategy: everything here should be inlined for direct operation on the stack wherever possible. + @usableFromInline + @frozen + internal struct InlineData : Sendable { +#if _pointerBitWidth(_64) + @usableFromInline typealias Buffer = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) //len //enum + @usableFromInline var bytes: Buffer +#elseif _pointerBitWidth(_32) + @usableFromInline typealias Buffer = (UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8) //len //enum + @usableFromInline var bytes: Buffer +#else +#error ("Unsupported architecture: a definition of Buffer needs to be made with N = (MemoryLayout<(Int, Int)>.size - 2) UInt8 members to a tuple") +#endif + @usableFromInline var length: UInt8 + + @inlinable // This is @inlinable as trivially computable. + static func canStore(count: Int) -> Bool { + return count <= MemoryLayout.size + } + + static var maximumCapacity: Int { + return MemoryLayout.size + } + + @inlinable // This is @inlinable as a convenience initializer. + init(_ srcBuffer: UnsafeRawBufferPointer) { + self.init(count: srcBuffer.count) + if !srcBuffer.isEmpty { + Swift.withUnsafeMutableBytes(of: &bytes) { dstBuffer in + dstBuffer.baseAddress?.copyMemory(from: srcBuffer.baseAddress!, byteCount: srcBuffer.count) + } + } + } + + @inlinable // This is @inlinable as a trivial initializer. + init(count: Int = 0) { + assert(count <= MemoryLayout.size) +#if _pointerBitWidth(_64) + bytes = (UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0)) +#elseif _pointerBitWidth(_32) + bytes = (UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0)) +#else +#error ("Unsupported architecture: initialization for Buffer is required for this architecture") +#endif + length = UInt8(count) + } + + @inlinable // This is @inlinable as a convenience initializer. + init(_ slice: InlineSlice, count: Int) { + self.init(count: count) + Swift.withUnsafeMutableBytes(of: &bytes) { dstBuffer in + slice.withUnsafeBytes { srcBuffer in + dstBuffer.copyMemory(from: UnsafeRawBufferPointer(start: srcBuffer.baseAddress, count: count)) + } + } + } + + @inlinable // This is @inlinable as a convenience initializer. + init(_ slice: LargeSlice, count: Int) { + self.init(count: count) + Swift.withUnsafeMutableBytes(of: &bytes) { dstBuffer in + slice.withUnsafeBytes { srcBuffer in + dstBuffer.copyMemory(from: UnsafeRawBufferPointer(start: srcBuffer.baseAddress, count: count)) + } + } + } + + @inlinable // This is @inlinable as trivially computable. + var capacity: Int { + return MemoryLayout.size + } + + @inlinable // This is @inlinable as trivially computable. + var count: Int { + get { + return Int(length) + } + set(newValue) { + assert(newValue <= MemoryLayout.size) + if newValue > length { + resetBytes(in: Int(length) ..< newValue) // Also extends length + } else { + length = UInt8(newValue) + } + } + } + + @inlinable // This is @inlinable as trivially computable. + var startIndex: Int { + return 0 + } + + @inlinable // This is @inlinable as trivially computable. + var endIndex: Int { + return count + } + + @inlinable // This is @inlinable as a generic, trivially forwarding function. + func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { + let count = Int(length) + return try Swift.withUnsafeBytes(of: bytes) { (rawBuffer) throws -> Result in + return try apply(UnsafeRawBufferPointer(start: rawBuffer.baseAddress, count: count)) + } + } + + @inlinable // This is @inlinable as a generic, trivially forwarding function. + mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { + let count = Int(length) + return try Swift.withUnsafeMutableBytes(of: &bytes) { (rawBuffer) throws -> Result in + return try apply(UnsafeMutableRawBufferPointer(start: rawBuffer.baseAddress, count: count)) + } + } + + @inlinable // This is @inlinable as trivially computable. + mutating func append(byte: UInt8) { + let count = self.count + assert(count + 1 <= MemoryLayout.size) + Swift.withUnsafeMutableBytes(of: &bytes) { $0[count] = byte } + self.length += 1 + } + + @inlinable // This is @inlinable as trivially computable. + mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { + guard !buffer.isEmpty else { return } + assert(count + buffer.count <= MemoryLayout.size) + let cnt = count + _ = Swift.withUnsafeMutableBytes(of: &bytes) { rawBuffer in + rawBuffer.baseAddress?.advanced(by: cnt).copyMemory(from: buffer.baseAddress!, byteCount: buffer.count) + } + + length += UInt8(buffer.count) + } + + @inlinable // This is @inlinable as trivially computable. + subscript(index: Index) -> UInt8 { + get { + assert(index <= MemoryLayout.size) + precondition(index < length, "index \(index) is out of bounds of 0..<\(length)") + return Swift.withUnsafeBytes(of: bytes) { rawBuffer -> UInt8 in + return rawBuffer[index] + } + } + set(newValue) { + assert(index <= MemoryLayout.size) + precondition(index < length, "index \(index) is out of bounds of 0..<\(length)") + Swift.withUnsafeMutableBytes(of: &bytes) { rawBuffer in + rawBuffer[index] = newValue + } + } + } + + @inlinable // This is @inlinable as trivially computable. + mutating func resetBytes(in range: Range) { + assert(range.lowerBound <= MemoryLayout.size) + assert(range.upperBound <= MemoryLayout.size) + precondition(range.lowerBound <= length, "index \(range.lowerBound) is out of bounds of 0..<\(length)") + if length < range.upperBound { + length = UInt8(range.upperBound) + } + + let _ = Swift.withUnsafeMutableBytes(of: &bytes) { rawBuffer in + memset(rawBuffer.baseAddress!.advanced(by: range.lowerBound), 0, range.upperBound - range.lowerBound) + } + } + + @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. + mutating func replaceSubrange(_ subrange: Range, with replacementBytes: UnsafeRawPointer?, count replacementLength: Int) { + assert(subrange.lowerBound <= MemoryLayout.size) + assert(subrange.upperBound <= MemoryLayout.size) + assert(count - (subrange.upperBound - subrange.lowerBound) + replacementLength <= MemoryLayout.size) + precondition(subrange.lowerBound <= length, "index \(subrange.lowerBound) is out of bounds of 0..<\(length)") + precondition(subrange.upperBound <= length, "index \(subrange.upperBound) is out of bounds of 0..<\(length)") + let currentLength = count + let resultingLength = currentLength - (subrange.upperBound - subrange.lowerBound) + replacementLength + let shift = resultingLength - currentLength + Swift.withUnsafeMutableBytes(of: &bytes) { mutableBytes in + /* shift the trailing bytes */ + let start = subrange.lowerBound + let length = subrange.upperBound - subrange.lowerBound + if shift != 0 { + memmove(mutableBytes.baseAddress!.advanced(by: start + replacementLength), mutableBytes.baseAddress!.advanced(by: start + length), currentLength - start - length) + } + if replacementLength != 0 { + memmove(mutableBytes.baseAddress!.advanced(by: start), replacementBytes!, replacementLength) + } + } + length = UInt8(resultingLength) + } + + @inlinable // This is @inlinable as trivially computable. + func copyBytes(to pointer: UnsafeMutableRawPointer, from range: Range) { + precondition(startIndex <= range.lowerBound, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(startIndex <= range.upperBound, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(range.upperBound <= endIndex, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") + + Swift.withUnsafeBytes(of: bytes) { + let cnt = Swift.min($0.count, range.upperBound - range.lowerBound) + guard cnt > 0 else { return } + pointer.copyMemory(from: $0.baseAddress!.advanced(by: range.lowerBound), byteCount: cnt) + } + } + + @inline(__always) // This should always be inlined into _Representation.hash(into:). + func hash(into hasher: inout Hasher) { + // **NOTE**: this uses `count` (an Int) and NOT `length` (a UInt8) + // Despite having the same value, they hash differently. InlineSlice and LargeSlice both use `count` (an Int); if you combine the same bytes but with `length` over `count`, you can get a different hash. + // + // This affects slices, which are InlineSlice and not InlineData: + // + // let d = Data([0xFF, 0xFF]) // InlineData + // let s = Data([0, 0xFF, 0xFF]).dropFirst() // InlineSlice + // assert(s == d) + // assert(s.hashValue == d.hashValue) + hasher.combine(count) + + Swift.withUnsafeBytes(of: bytes) { + // We have access to the full byte buffer here, but not all of it is meaningfully used (bytes past self.length may be garbage). + let bytes = UnsafeRawBufferPointer(start: $0.baseAddress, count: self.count) + hasher.combine(bytes: bytes) + } + } + } +} diff --git a/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift new file mode 100644 index 000000000..dd27a71c4 --- /dev/null +++ b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift @@ -0,0 +1,267 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +extension Data { +#if _pointerBitWidth(_64) + @usableFromInline internal typealias HalfInt = Int32 +#elseif _pointerBitWidth(_32) + @usableFromInline internal typealias HalfInt = Int16 +#else +#error ("Unsupported architecture: a definition of half of the pointer sized Int needs to be defined for this architecture") +#endif + + // A buffer of bytes too large to fit in an InlineData, but still small enough to fit a storage pointer + range in two words. + // Inlinability strategy: everything here should be easily inlinable as large _DataStorage methods should not inline into here. + @usableFromInline + @frozen + internal struct InlineSlice : Sendable { + // ***WARNING*** + // These ivars are specifically laid out so that they cause the enum _Representation to be 16 bytes on 64 bit platforms. This means we _MUST_ have the class type thing last + @usableFromInline var slice: Range + @usableFromInline var storage: __DataStorage + + @inlinable // This is @inlinable as trivially computable. + static func canStore(count: Int) -> Bool { + return count < HalfInt.max + } + + @inlinable // This is @inlinable as a convenience initializer. + init(_ buffer: UnsafeRawBufferPointer) { + assert(buffer.count < HalfInt.max) + self.init(__DataStorage(bytes: buffer.baseAddress, length: buffer.count), count: buffer.count) + } + + @inlinable // This is @inlinable as a convenience initializer. + init(capacity: Int) { + assert(capacity < HalfInt.max) + self.init(__DataStorage(capacity: capacity), count: 0) + } + + @inlinable // This is @inlinable as a convenience initializer. + init(count: Int) { + assert(count < HalfInt.max) + self.init(__DataStorage(length: count), count: count) + } + + @inlinable // This is @inlinable as a convenience initializer. + init(_ inline: InlineData) { + assert(inline.count < HalfInt.max) + self.init(inline.withUnsafeBytes { return __DataStorage(bytes: $0.baseAddress, length: $0.count) }, count: inline.count) + } + + @inlinable // This is @inlinable as a convenience initializer. + init(_ inline: InlineData, range: Range) { + assert(range.lowerBound < HalfInt.max) + assert(range.upperBound < HalfInt.max) + self.init(inline.withUnsafeBytes { return __DataStorage(bytes: $0.baseAddress, length: $0.count) }, range: range) + } + + @inlinable // This is @inlinable as a convenience initializer. + init(_ large: LargeSlice) { + assert(large.range.lowerBound < HalfInt.max) + assert(large.range.upperBound < HalfInt.max) + self.init(large.storage, range: large.range) + } + + @inlinable // This is @inlinable as a convenience initializer. + init(_ large: LargeSlice, range: Range) { + assert(range.lowerBound < HalfInt.max) + assert(range.upperBound < HalfInt.max) + self.init(large.storage, range: range) + } + + @inlinable // This is @inlinable as a trivial initializer. + init(_ storage: __DataStorage, count: Int) { + assert(count < HalfInt.max) + self.storage = storage + slice = 0..) { + assert(range.lowerBound < HalfInt.max) + assert(range.upperBound < HalfInt.max) + self.storage = storage + slice = HalfInt(range.lowerBound).. 0 { + let additionalRange = Int(slice.upperBound) ..< Int(slice.upperBound) + difference + storage.resetBytes(in: additionalRange) // Also extends storage length + } else { + storage.length += difference + } + slice = slice.lowerBound..<(slice.lowerBound + HalfInt(newValue)) + } + } + + @inlinable // This is @inlinable as trivially computable. + var range: Range { + get { + return Int(slice.lowerBound)..(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { + return try storage.withUnsafeBytes(in: range, apply: apply) + } + + @inlinable // This is @inlinable as a generic, trivially forwarding function. + mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { + ensureUniqueReference() + return try storage.withUnsafeMutableBytes(in: range, apply: apply) + } + + @inlinable // This is @inlinable as reasonably small. + 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 + slice = slice.lowerBound.. UInt8 { + get { + assert(index < HalfInt.max) + precondition(startIndex <= index, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(index < endIndex, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") + return storage.get(index) + } + set(newValue) { + assert(index < HalfInt.max) + precondition(startIndex <= index, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(index < endIndex, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") + ensureUniqueReference() + storage.set(index, to: newValue) + } + } + + @inlinable // This is @inlinable as reasonably small. + mutating func resetBytes(in range: Range) { + assert(range.lowerBound < HalfInt.max) + assert(range.upperBound < HalfInt.max) + precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + ensureUniqueReference() + storage.resetBytes(in: range) + if slice.upperBound < range.upperBound { + slice = slice.lowerBound.., with bytes: UnsafeRawPointer?, count cnt: Int) { + precondition(startIndex <= subrange.lowerBound, "index \(subrange.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(subrange.lowerBound <= endIndex, "index \(subrange.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(startIndex <= subrange.upperBound, "index \(subrange.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(subrange.upperBound <= endIndex, "index \(subrange.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") + + ensureUniqueReference() + let upper = range.upperBound +#if FOUNDATION_FRAMEWORK + if #available(macOS 14, iOS 17, watchOS 10, tvOS 17, *) { + storage.replaceBytes(in: subrange, with: bytes, length: cnt) + } else { + let nsRange = NSRange( + location: subrange.lowerBound, + length: subrange.upperBound - subrange.lowerBound) + storage.replaceBytes(in: nsRange, with: bytes, length: cnt) + } +#else + storage.replaceBytes(in: subrange, with: bytes, length: cnt) +#endif + let resultingUpper = upper - (subrange.upperBound - subrange.lowerBound) + cnt + slice = slice.lowerBound..) { + precondition(startIndex <= range.lowerBound, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(startIndex <= range.upperBound, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(range.upperBound <= endIndex, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") + storage.copyBytes(to: pointer, from: range) + } + + @inline(__always) // This should always be inlined into _Representation.hash(into:). + func hash(into hasher: inout Hasher) { + hasher.combine(count) + + self.withUnsafeBytes { bytes in + hasher.combine(bytes: bytes) + } + } + } +} diff --git a/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift b/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift new file mode 100644 index 000000000..7e26cdb73 --- /dev/null +++ b/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift @@ -0,0 +1,250 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +extension Data { + // A reference wrapper around a Range for when the range of a data buffer is too large to whole in a single word. + // Inlinability strategy: everything should be inlinable as trivial. + @usableFromInline + @_fixed_layout + internal final class RangeReference : @unchecked Sendable { + @usableFromInline var range: Range + + @inlinable @inline(__always) // This is @inlinable as trivially forwarding. + var lowerBound: Int { + return range.lowerBound + } + + @inlinable @inline(__always) // This is @inlinable as trivially forwarding. + var upperBound: Int { + return range.upperBound + } + + @inlinable @inline(__always) // This is @inlinable as trivially computable. + var count: Int { + // The upper bound is guaranteed to be greater than or equal to the lower bound, and the lower bound must be non-negative so subtraction can never overflow + return range.upperBound &- range.lowerBound + } + + @inlinable @inline(__always) // This is @inlinable as a trivial initializer. + init(_ range: Range) { + self.range = range + } + } + + // A buffer of bytes whose range is too large to fit in a single word. Used alongside a RangeReference to make it fit into _Representation's two-word size. + // Inlinability strategy: everything here should be easily inlinable as large _DataStorage methods should not inline into here. + @usableFromInline + @frozen + internal struct LargeSlice : Sendable { + // ***WARNING*** + // These ivars are specifically laid out so that they cause the enum _Representation to be 16 bytes on 64 bit platforms. This means we _MUST_ have the class type thing last + @usableFromInline var slice: RangeReference + @usableFromInline var storage: __DataStorage + + @inlinable // This is @inlinable as a convenience initializer. + init(_ buffer: UnsafeRawBufferPointer) { + self.init(__DataStorage(bytes: buffer.baseAddress, length: buffer.count), count: buffer.count) + } + + @inlinable // This is @inlinable as a convenience initializer. + init(capacity: Int) { + self.init(__DataStorage(capacity: capacity), count: 0) + } + + @inlinable // This is @inlinable as a convenience initializer. + init(count: Int) { + self.init(__DataStorage(length: count), count: count) + } + + @inlinable // This is @inlinable as a convenience initializer. + init(_ inline: InlineData) { + let storage = inline.withUnsafeBytes { return __DataStorage(bytes: $0.baseAddress, length: $0.count) } + self.init(storage, count: inline.count) + } + + @inlinable // This is @inlinable as a trivial initializer. + init(_ slice: InlineSlice) { + self.storage = slice.storage + self.slice = RangeReference(slice.range) + } + + @inlinable // This is @inlinable as a trivial initializer. + init(_ storage: __DataStorage, count: Int) { + self.storage = storage + self.slice = RangeReference(0..) { + self.storage = storage + self.slice = RangeReference(range) + } + + @inlinable // This is @inlinable as trivially computable (and inlining may help avoid retain-release traffic). + mutating func ensureUniqueReference() { + if !isKnownUniquelyReferenced(&storage) { + storage = storage.mutableCopy(range) + } + if !isKnownUniquelyReferenced(&slice) { + slice = RangeReference(range) + } + } + + @inlinable // This is @inlinable as trivially forwarding. + var startIndex: Int { + return slice.range.lowerBound + } + + @inlinable // This is @inlinable as trivially forwarding. + var endIndex: Int { + return slice.range.upperBound + } + + @inlinable // This is @inlinable as trivially forwarding. + var capacity: Int { + return storage.capacity + } + + @inlinable // This is @inlinable as trivially computable. + mutating func reserveCapacity(_ minimumCapacity: Int) { + ensureUniqueReference() + // the current capacity can be zero (representing externally owned buffer), and count can be greater than the capacity + storage.ensureUniqueBufferReference(growingTo: Swift.max(minimumCapacity, count)) + } + + @inlinable // This is @inlinable as trivially computable. + var count: Int { + get { + return slice.count + } + set(newValue) { + ensureUniqueReference() + let difference = newValue - count + if difference > 0 { + let additionalRange = Int(slice.upperBound) ..< Int(slice.upperBound) + difference + storage.resetBytes(in: additionalRange) // Already sets the length + } else { + storage.length += difference + } + slice.range = slice.range.lowerBound..<(slice.range.lowerBound + newValue) + } + } + + @inlinable // This is @inlinable as it is trivially forwarding. + var range: Range { + return slice.range + } + + @inlinable // This is @inlinable as a generic, trivially forwarding function. + func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { + return try storage.withUnsafeBytes(in: range, apply: apply) + } + + @inlinable // This is @inlinable as a generic, trivially forwarding function. + mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { + ensureUniqueReference() + return try storage.withUnsafeMutableBytes(in: range, apply: apply) + } + + @inlinable // This is @inlinable as reasonably small. + mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { + 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 + slice.range = slice.range.lowerBound.. UInt8 { + get { + precondition(startIndex <= index, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(index < endIndex, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") + return storage.get(index) + } + set(newValue) { + precondition(startIndex <= index, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(index < endIndex, "index \(index) is out of bounds of \(startIndex)..<\(endIndex)") + ensureUniqueReference() + storage.set(index, to: newValue) + } + } + + @inlinable // This is @inlinable as reasonably small. + mutating func resetBytes(in range: Range) { + precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + ensureUniqueReference() + storage.resetBytes(in: range) + if slice.range.upperBound < range.upperBound { + slice.range = slice.range.lowerBound.., with bytes: UnsafeRawPointer?, count cnt: Int) { + precondition(startIndex <= subrange.lowerBound, "index \(subrange.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(subrange.lowerBound <= endIndex, "index \(subrange.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(startIndex <= subrange.upperBound, "index \(subrange.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(subrange.upperBound <= endIndex, "index \(subrange.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") + + ensureUniqueReference() + let upper = range.upperBound +#if FOUNDATION_FRAMEWORK + if #available(macOS 14, iOS 17, watchOS 10, tvOS 17, *) { + storage.replaceBytes(in: subrange, with: bytes, length: cnt) + } else { + let nsRange = NSRange( + location: subrange.lowerBound, + length: subrange.upperBound - subrange.lowerBound) + storage.replaceBytes(in: nsRange, with: bytes, length: cnt) + } +#else + storage.replaceBytes(in: subrange, with: bytes, length: cnt) +#endif + let resultingUpper = upper - (subrange.upperBound - subrange.lowerBound) + cnt + slice.range = slice.range.lowerBound..) { + precondition(startIndex <= range.lowerBound, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(startIndex <= range.upperBound, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") + precondition(range.upperBound <= endIndex, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") + storage.copyBytes(to: pointer, from: range) + } + + @inline(__always) // This should always be inlined into _Representation.hash(into:). + func hash(into hasher: inout Hasher) { + hasher.combine(count) + + self.withUnsafeBytes { bytes in + hasher.combine(bytes: bytes) + } + } + } +} diff --git a/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift b/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift new file mode 100644 index 000000000..dfa76e595 --- /dev/null +++ b/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift @@ -0,0 +1,561 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +extension Data { + // The actual storage for Data's various representations. + // Inlinability strategy: almost everything should be inlinable as forwarding the underlying implementations. (Inlining can also help avoid retain-release traffic around pulling values out of enums.) + @usableFromInline + @frozen + internal enum _Representation : Sendable { + case empty + case inline(InlineData) + case slice(InlineSlice) + case large(LargeSlice) + + @inlinable // This is @inlinable as a trivial initializer. + init(_ buffer: UnsafeRawBufferPointer) { + if buffer.isEmpty { + self = .empty + } else if InlineData.canStore(count: buffer.count) { + self = .inline(InlineData(buffer)) + } else if InlineSlice.canStore(count: buffer.count) { + self = .slice(InlineSlice(buffer)) + } else { + self = .large(LargeSlice(buffer)) + } + } + + @inlinable // This is @inlinable as a trivial initializer. + init(_ buffer: UnsafeRawBufferPointer, owner: AnyObject) { + if buffer.isEmpty { + self = .empty + } else if InlineData.canStore(count: buffer.count) { + self = .inline(InlineData(buffer)) + } else { + let count = buffer.count + let storage = __DataStorage(bytes: UnsafeMutableRawPointer(mutating: buffer.baseAddress), length: count, copy: false, deallocator: { _, _ in + _fixLifetime(owner) + }, offset: 0) + if InlineSlice.canStore(count: count) { + self = .slice(InlineSlice(storage, count: count)) + } else { + self = .large(LargeSlice(storage, count: count)) + } + } + } + + @inlinable // This is @inlinable as a trivial initializer. + init(capacity: Int) { + if capacity == 0 { + self = .empty + } else if InlineData.canStore(count: capacity) { + self = .inline(InlineData()) + } else if InlineSlice.canStore(count: capacity) { + self = .slice(InlineSlice(capacity: capacity)) + } else { + self = .large(LargeSlice(capacity: capacity)) + } + } + + @inlinable // This is @inlinable as a trivial initializer. + init(count: Int) { + if count == 0 { + self = .empty + } else if InlineData.canStore(count: count) { + self = .inline(InlineData(count: count)) + } else if InlineSlice.canStore(count: count) { + self = .slice(InlineSlice(count: count)) + } else { + self = .large(LargeSlice(count: count)) + } + } + + @inlinable // This is @inlinable as a trivial initializer. + init(_ storage: __DataStorage, count: Int) { + if count == 0 { + self = .empty + } else if InlineData.canStore(count: count) { + self = .inline(storage.withUnsafeBytes(in: 0.. 0 else { return } + switch self { + case .empty: + if InlineData.canStore(count: minimumCapacity) { + self = .inline(InlineData()) + } else if InlineSlice.canStore(count: minimumCapacity) { + self = .slice(InlineSlice(capacity: minimumCapacity)) + } else { + self = .large(LargeSlice(capacity: minimumCapacity)) + } + case .inline(let inline): + guard minimumCapacity > inline.capacity else { return } + // we know we are going to be heap promoted + if InlineSlice.canStore(count: minimumCapacity) { + var slice = InlineSlice(inline) + slice.reserveCapacity(minimumCapacity) + self = .slice(slice) + } else { + var slice = LargeSlice(inline) + slice.reserveCapacity(minimumCapacity) + self = .large(slice) + } + case .slice(var slice): + guard minimumCapacity > slice.capacity else { return } + if InlineSlice.canStore(count: minimumCapacity) { + self = .empty + slice.reserveCapacity(minimumCapacity) + self = .slice(slice) + } else { + var large = LargeSlice(slice) + large.reserveCapacity(minimumCapacity) + self = .large(large) + } + case .large(var slice): + guard minimumCapacity > slice.capacity else { return } + self = .empty + slice.reserveCapacity(minimumCapacity) + self = .large(slice) + } + } + + @inlinable // This is @inlinable as reasonably small. + var count: Int { + get { + switch self { + case .empty: return 0 + case .inline(let inline): return inline.count + case .slice(let slice): return slice.count + case .large(let slice): return slice.count + } + } + set(newValue) { + // HACK: The definition of this inline function takes an inout reference to self, giving the optimizer a unique referencing guarantee. + // This allows us to avoid excessive retain-release traffic around modifying enum values, and inlining the function then avoids the additional frame. + @inline(__always) + func apply(_ representation: inout _Representation, _ newValue: Int) -> _Representation? { + switch representation { + case .empty: + if newValue == 0 { + return nil + } else if InlineData.canStore(count: newValue) { + return .inline(InlineData(count: newValue)) + } else if InlineSlice.canStore(count: newValue) { + return .slice(InlineSlice(count: newValue)) + } else { + return .large(LargeSlice(count: newValue)) + } + case .inline(var inline): + if newValue == 0 { + return .empty + } else if InlineData.canStore(count: newValue) { + guard inline.count != newValue else { return nil } + inline.count = newValue + return .inline(inline) + } else if InlineSlice.canStore(count: newValue) { + var slice = InlineSlice(inline) + slice.count = newValue + return .slice(slice) + } else { + var slice = LargeSlice(inline) + slice.count = newValue + return .large(slice) + } + case .slice(var slice): + if newValue == 0 && slice.startIndex == 0 { + return .empty + } else if slice.startIndex == 0 && InlineData.canStore(count: newValue) { + return .inline(InlineData(slice, count: newValue)) + } else if InlineSlice.canStore(count: newValue + slice.startIndex) { + guard slice.count != newValue else { return nil } + representation = .empty // TODO: remove this when mgottesman lands optimizations + slice.count = newValue + return .slice(slice) + } else { + var newSlice = LargeSlice(slice) + newSlice.count = newValue + return .large(newSlice) + } + case .large(var slice): + if newValue == 0 && slice.startIndex == 0 { + return .empty + } else if slice.startIndex == 0 && InlineData.canStore(count: newValue) { + return .inline(InlineData(slice, count: newValue)) + } else { + guard slice.count != newValue else { return nil} + representation = .empty // TODO: remove this when mgottesman lands optimizations + slice.count = newValue + return .large(slice) + } + } + } + + if let rep = apply(&self, newValue) { + self = rep + } + } + } + + @inlinable // This is @inlinable as a generic, trivially forwarding function. + func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { + switch self { + case .empty: + let empty = InlineData() + return try empty.withUnsafeBytes(apply) + case .inline(let inline): + return try inline.withUnsafeBytes(apply) + case .slice(let slice): + return try slice.withUnsafeBytes(apply) + case .large(let slice): + return try slice.withUnsafeBytes(apply) + } + } + + @inlinable // This is @inlinable as a generic, trivially forwarding function. + mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { + switch self { + case .empty: + var empty = InlineData() + return try empty.withUnsafeMutableBytes(apply) + case .inline(var inline): + defer { self = .inline(inline) } + return try inline.withUnsafeMutableBytes(apply) + case .slice(var slice): + self = .empty + defer { self = .slice(slice) } + return try slice.withUnsafeMutableBytes(apply) + case .large(var slice): + self = .empty + defer { self = .large(slice) } + return try slice.withUnsafeMutableBytes(apply) + } + } + + @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. + func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Index, _ stop: inout Bool) -> Void) { + switch self { + case .empty: + var stop = false + block(UnsafeBufferPointer(start: nil, count: 0), 0, &stop) + case .inline(let inline): + inline.withUnsafeBytes { + var stop = false + $0.withMemoryRebound(to: UInt8.self) { block($0, 0, &stop) } + } + case .slice(let slice): + slice.storage.enumerateBytes(in: slice.range, block) + case .large(let slice): + slice.storage.enumerateBytes(in: slice.range, block) + } + } + + @inlinable // This is @inlinable as reasonably small. + mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { + switch self { + case .empty: + self = _Representation(buffer) + case .inline(var inline): + if InlineData.canStore(count: inline.count + buffer.count) { + inline.append(contentsOf: buffer) + self = .inline(inline) + } else if InlineSlice.canStore(count: inline.count + buffer.count) { + var newSlice = InlineSlice(inline) + newSlice.append(contentsOf: buffer) + self = .slice(newSlice) + } else { + var newSlice = LargeSlice(inline) + newSlice.append(contentsOf: buffer) + self = .large(newSlice) + } + case .slice(var slice): + if InlineSlice.canStore(count: slice.range.upperBound + buffer.count) { + self = .empty + defer { self = .slice(slice) } + slice.append(contentsOf: buffer) + } else { + self = .empty + var newSlice = LargeSlice(slice) + newSlice.append(contentsOf: buffer) + self = .large(newSlice) + } + case .large(var slice): + self = .empty + defer { self = .large(slice) } + slice.append(contentsOf: buffer) + } + } + + @inlinable // This is @inlinable as reasonably small. + mutating func resetBytes(in range: Range) { + switch self { + case .empty: + if range.upperBound == 0 { + self = .empty + } else if InlineData.canStore(count: range.upperBound) { + precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + self = .inline(InlineData(count: range.upperBound)) + } else if InlineSlice.canStore(count: range.upperBound) { + precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + self = .slice(InlineSlice(count: range.upperBound)) + } else { + precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") + self = .large(LargeSlice(count: range.upperBound)) + } + case .inline(var inline): + if inline.count < range.upperBound { + if InlineSlice.canStore(count: range.upperBound) { + var slice = InlineSlice(inline) + slice.resetBytes(in: range) + self = .slice(slice) + } else { + var slice = LargeSlice(inline) + slice.resetBytes(in: range) + self = .large(slice) + } + } else { + inline.resetBytes(in: range) + self = .inline(inline) + } + case .slice(var slice): + if InlineSlice.canStore(count: range.upperBound) { + self = .empty + slice.resetBytes(in: range) + self = .slice(slice) + } else { + self = .empty + var newSlice = LargeSlice(slice) + newSlice.resetBytes(in: range) + self = .large(newSlice) + } + case .large(var slice): + self = .empty + slice.resetBytes(in: range) + self = .large(slice) + } + } + + @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. + mutating func replaceSubrange(_ subrange: Range, with bytes: UnsafeRawPointer?, count cnt: Int) { + switch self { + case .empty: + precondition(subrange.lowerBound == 0 && subrange.upperBound == 0, "range \(subrange) out of bounds of 0..<0") + if cnt == 0 { + return + } else if InlineData.canStore(count: cnt) { + self = .inline(InlineData(UnsafeRawBufferPointer(start: bytes, count: cnt))) + } else if InlineSlice.canStore(count: cnt) { + self = .slice(InlineSlice(UnsafeRawBufferPointer(start: bytes, count: cnt))) + } else { + self = .large(LargeSlice(UnsafeRawBufferPointer(start: bytes, count: cnt))) + } + case .inline(var inline): + let resultingCount = inline.count + cnt - (subrange.upperBound - subrange.lowerBound) + if resultingCount == 0 { + self = .empty + } else if InlineData.canStore(count: resultingCount) { + inline.replaceSubrange(subrange, with: bytes, count: cnt) + self = .inline(inline) + } else if InlineSlice.canStore(count: resultingCount) { + var slice = InlineSlice(inline) + slice.replaceSubrange(subrange, with: bytes, count: cnt) + self = .slice(slice) + } else { + var slice = LargeSlice(inline) + slice.replaceSubrange(subrange, with: bytes, count: cnt) + self = .large(slice) + } + case .slice(var slice): + let resultingUpper = slice.endIndex + cnt - (subrange.upperBound - subrange.lowerBound) + if slice.startIndex == 0 && resultingUpper == 0 { + self = .empty + } else if slice.startIndex == 0 && InlineData.canStore(count: resultingUpper) { + self = .empty + slice.replaceSubrange(subrange, with: bytes, count: cnt) + self = .inline(InlineData(slice, count: slice.count)) + } else if InlineSlice.canStore(count: resultingUpper) { + self = .empty + slice.replaceSubrange(subrange, with: bytes, count: cnt) + self = .slice(slice) + } else { + self = .empty + var newSlice = LargeSlice(slice) + newSlice.replaceSubrange(subrange, with: bytes, count: cnt) + self = .large(newSlice) + } + case .large(var slice): + let resultingUpper = slice.endIndex + cnt - (subrange.upperBound - subrange.lowerBound) + if slice.startIndex == 0 && resultingUpper == 0 { + self = .empty + } else if slice.startIndex == 0 && InlineData.canStore(count: resultingUpper) { + var inline = InlineData(count: resultingUpper) + inline.withUnsafeMutableBytes { inlineBuffer in + if cnt > 0 { + inlineBuffer.baseAddress?.advanced(by: subrange.lowerBound).copyMemory(from: bytes!, byteCount: cnt) + } + slice.withUnsafeBytes { buffer in + if subrange.lowerBound > 0 { + inlineBuffer.baseAddress?.copyMemory(from: buffer.baseAddress!, byteCount: subrange.lowerBound) + } + if subrange.upperBound < resultingUpper { + inlineBuffer.baseAddress?.advanced(by: subrange.upperBound).copyMemory(from: buffer.baseAddress!.advanced(by: subrange.upperBound), byteCount: resultingUpper - subrange.upperBound) + } + } + } + self = .inline(inline) + } else if InlineSlice.canStore(count: slice.startIndex) && InlineSlice.canStore(count: resultingUpper) { + self = .empty + var newSlice = InlineSlice(slice) + newSlice.replaceSubrange(subrange, with: bytes, count: cnt) + self = .slice(newSlice) + } else { + self = .empty + slice.replaceSubrange(subrange, with: bytes, count: cnt) + self = .large(slice) + } + } + } + + @inlinable // This is @inlinable as trivially forwarding. + subscript(index: Index) -> UInt8 { + get { + switch self { + case .empty: preconditionFailure("index \(index) out of range of 0") + case .inline(let inline): return inline[index] + case .slice(let slice): return slice[index] + case .large(let slice): return slice[index] + } + } + set(newValue) { + switch self { + case .empty: preconditionFailure("index \(index) out of range of 0") + case .inline(var inline): + inline[index] = newValue + self = .inline(inline) + case .slice(var slice): + self = .empty + slice[index] = newValue + self = .slice(slice) + case .large(var slice): + self = .empty + slice[index] = newValue + self = .large(slice) + } + } + } + + @inlinable // This is @inlinable as reasonably small. + subscript(bounds: Range) -> Data { + get { + switch self { + case .empty: + precondition(bounds.lowerBound == 0 && (bounds.upperBound - bounds.lowerBound) == 0, "Range \(bounds) out of bounds 0..<0") + return Data() + case .inline(let inline): + precondition(bounds.upperBound <= inline.count, "Range \(bounds) out of bounds 0..<\(inline.count)") + if bounds.lowerBound == 0 { + var newInline = inline + newInline.count = bounds.upperBound + return Data(representation: .inline(newInline)) + } else { + return Data(representation: .slice(InlineSlice(inline, range: bounds))) + } + case .slice(let slice): + precondition(slice.startIndex <= bounds.lowerBound, "Range \(bounds) out of bounds \(slice.range)") + precondition(bounds.lowerBound <= slice.endIndex, "Range \(bounds) out of bounds \(slice.range)") + precondition(slice.startIndex <= bounds.upperBound, "Range \(bounds) out of bounds \(slice.range)") + precondition(bounds.upperBound <= slice.endIndex, "Range \(bounds) out of bounds \(slice.range)") + if bounds.lowerBound == 0 && bounds.upperBound == 0 { + return Data() + } else if bounds.lowerBound == 0 && InlineData.canStore(count: bounds.count) { + return Data(representation: .inline(InlineData(slice, count: bounds.count))) + } else { + var newSlice = slice + newSlice.range = bounds + return Data(representation: .slice(newSlice)) + } + case .large(let slice): + precondition(slice.startIndex <= bounds.lowerBound, "Range \(bounds) out of bounds \(slice.range)") + precondition(bounds.lowerBound <= slice.endIndex, "Range \(bounds) out of bounds \(slice.range)") + precondition(slice.startIndex <= bounds.upperBound, "Range \(bounds) out of bounds \(slice.range)") + precondition(bounds.upperBound <= slice.endIndex, "Range \(bounds) out of bounds \(slice.range)") + if bounds.lowerBound == 0 && bounds.upperBound == 0 { + return Data() + } else if bounds.lowerBound == 0 && InlineData.canStore(count: bounds.upperBound) { + return Data(representation: .inline(InlineData(slice, count: bounds.upperBound))) + } else if InlineSlice.canStore(count: bounds.lowerBound) && InlineSlice.canStore(count: bounds.upperBound) { + return Data(representation: .slice(InlineSlice(slice, range: bounds))) + } else { + var newSlice = slice + newSlice.slice = RangeReference(bounds) + return Data(representation: .large(newSlice)) + } + } + } + } + + @inlinable // This is @inlinable as trivially forwarding. + var startIndex: Int { + switch self { + case .empty: return 0 + case .inline: return 0 + case .slice(let slice): return slice.startIndex + case .large(let slice): return slice.startIndex + } + } + + @inlinable // This is @inlinable as trivially forwarding. + var endIndex: Int { + switch self { + case .empty: return 0 + case .inline(let inline): return inline.count + case .slice(let slice): return slice.endIndex + case .large(let slice): return slice.endIndex + } + } + + @inlinable // This is @inlinable as trivially forwarding. + func copyBytes(to pointer: UnsafeMutableRawPointer, from range: Range) { + switch self { + case .empty: + precondition(range.lowerBound == 0 && range.upperBound == 0, "Range \(range) out of bounds 0..<0") + return + case .inline(let inline): + inline.copyBytes(to: pointer, from: range) + case .slice(let slice): + slice.copyBytes(to: pointer, from: range) + case .large(let slice): + slice.copyBytes(to: pointer, from: range) + } + } + + @inline(__always) // This should always be inlined into Data.hash(into:). + func hash(into hasher: inout Hasher) { + switch self { + case .empty: + hasher.combine(0) + case .inline(let inline): + inline.hash(into: &hasher) + case .slice(let slice): + slice.hash(into: &hasher) + case .large(let large): + large.hash(into: &hasher) + } + } + } +} diff --git a/Sources/FoundationEssentials/Data/Representations/DataStorage.swift b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift new file mode 100644 index 000000000..81dc109a7 --- /dev/null +++ b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift @@ -0,0 +1,479 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// Underlying storage representation for medium and large data. +// Inlinability strategy: methods from here should not inline into InlineSlice or LargeSlice unless trivial. +// NOTE: older overlays called this class _DataStorage. The two must +// coexist without a conflicting ObjC class name, so it was renamed. +// The old name must not be used in the new runtime. +@usableFromInline +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) +internal final class __DataStorage : @unchecked Sendable { + @usableFromInline static let maxSize = Int.max >> 1 + @usableFromInline static let vmOpsThreshold = Platform.pageSize * 4 + +#if !FOUNDATION_FRAMEWORK + static func allocate(_ size: Int, _ clear: Bool) -> UnsafeMutableRawPointer? { + if clear { + return calloc(1, size) + } else { + return malloc(size) + } + } + + static func reallocate(_ ptr: UnsafeMutableRawPointer, _ newSize: Int) -> UnsafeMutableRawPointer? { + return realloc(ptr, newSize); + } +#endif // !FOUNDATION_FRAMEWORK + + @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. + static func move(_ dest_: UnsafeMutableRawPointer, _ source_: UnsafeRawPointer?, _ num_: Int) { + var dest = dest_ + var source = source_ + var num = num_ + if __DataStorage.vmOpsThreshold <= num && ((unsafeBitCast(source, to: Int.self) | Int(bitPattern: dest)) & (Platform.pageSize - 1)) == 0 { + let pages = Platform.roundDownToMultipleOfPageSize(num) + Platform.copyMemoryPages(source!, dest, pages) + source = source!.advanced(by: pages) + dest = dest.advanced(by: pages) + num -= pages + } + if num > 0 { + memmove(dest, source!, num) + } + } + + @inlinable // This is @inlinable as trivially forwarding, and does not escape the _DataStorage boundary layer. + static func shouldAllocateCleared(_ size: Int) -> Bool { + return (size > (128 * 1024)) + } + + @usableFromInline var _bytes: UnsafeMutableRawPointer? + @usableFromInline var _length: Int + @usableFromInline var _capacity: Int + @usableFromInline var _offset: Int + @usableFromInline var _deallocator: ((UnsafeMutableRawPointer, Int) -> Void)? + @usableFromInline var _needToZero: Bool + + @inlinable // This is @inlinable as trivially computable. + var bytes: UnsafeRawPointer? { + return UnsafeRawPointer(_bytes)?.advanced(by: -_offset) + } + + @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is generic and trivially forwarding. + @discardableResult + func withUnsafeBytes(in range: Range, apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { + return try apply(UnsafeRawBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length))) + } + + @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is generic and trivially forwarding. + @discardableResult + func withUnsafeMutableBytes(in range: Range, apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { + return try apply(UnsafeMutableRawBufferPointer(start: _bytes!.advanced(by:range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length))) + } + + @inlinable // This is @inlinable as trivially computable. + var mutableBytes: UnsafeMutableRawPointer? { + return _bytes?.advanced(by: _offset &* -1) // _offset is guaranteed to be non-negative, so it can never overflow when negating + } + + @inlinable + static var copyWillRetainMask: Int { +#if _pointerBitWidth(_64) + return Int(bitPattern: 0x8000000000000000) +#elseif _pointerBitWidth(_32) + return Int(bitPattern: 0x80000000) +#endif + } + + @inlinable + static var capacityMask: Int { +#if _pointerBitWidth(_64) + return Int(bitPattern: 0x7FFFFFFFFFFFFFFF) +#elseif _pointerBitWidth(_32) + return Int(bitPattern: 0x7FFFFFFF) +#endif + } + + @inlinable // This is @inlinable as trivially computable. + var capacity: Int { + return _capacity & __DataStorage.capacityMask + } + + @inlinable + var _copyWillRetain: Bool { + get { + return _capacity & __DataStorage.copyWillRetainMask == 0 + } + set { + if !newValue { + _capacity |= __DataStorage.copyWillRetainMask + } else { + _capacity &= __DataStorage.capacityMask + } + } + } + + @inlinable // This is @inlinable as trivially computable. + var length: Int { + get { + return _length + } + set { + setLength(newValue) + } + } + + @inlinable // This is inlinable as trivially computable. + var isExternallyOwned: Bool { + // all __DataStorages will have some sort of capacity, because empty cases hit the .empty enum _Representation + // anything with 0 capacity means that we have not allocated this pointer and consequently mutation is not ours to make. + return _capacity == 0 + } + + @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. + func ensureUniqueBufferReference(growingTo newLength: Int = 0, clear: Bool = false) { + guard isExternallyOwned || newLength > _capacity else { return } + + if newLength == 0 { + if isExternallyOwned { + let newCapacity = malloc_good_size(_length) + let newBytes = __DataStorage.allocate(newCapacity, false) + __DataStorage.move(newBytes!, _bytes!, _length) + _freeBytes() + _bytes = newBytes + _capacity = newCapacity + _needToZero = false + } + } else if isExternallyOwned { + let newCapacity = malloc_good_size(newLength) + let newBytes = __DataStorage.allocate(newCapacity, clear) + if let bytes = _bytes { + __DataStorage.move(newBytes!, bytes, _length) + } + _freeBytes() + _bytes = newBytes + _capacity = newCapacity + _length = newLength + _needToZero = true + } else { + let cap = _capacity + var additionalCapacity = (newLength >> (__DataStorage.vmOpsThreshold <= newLength ? 2 : 1)) + if Int.max - additionalCapacity < newLength { + additionalCapacity = 0 + } + var newCapacity = malloc_good_size(Swift.max(cap, newLength + additionalCapacity)) + let origLength = _length + var allocateCleared = clear && __DataStorage.shouldAllocateCleared(newCapacity) + var newBytes: UnsafeMutableRawPointer? = nil + if _bytes == nil { + newBytes = __DataStorage.allocate(newCapacity, allocateCleared) + if newBytes == nil { + /* Try again with minimum length */ + allocateCleared = clear && __DataStorage.shouldAllocateCleared(newLength) + newBytes = __DataStorage.allocate(newLength, allocateCleared) + } + } else { + let tryCalloc = (origLength == 0 || (newLength / origLength) >= 4) + if allocateCleared && tryCalloc { + newBytes = __DataStorage.allocate(newCapacity, true) + if let newBytes = newBytes { + __DataStorage.move(newBytes, _bytes!, origLength) + _freeBytes() + } + } + /* Where calloc/memmove/free fails, realloc might succeed */ + if newBytes == nil { + allocateCleared = false + if _deallocator != nil { + newBytes = __DataStorage.allocate(newCapacity, true) + if let newBytes = newBytes { + __DataStorage.move(newBytes, _bytes!, origLength) + _freeBytes() + } + } else { + newBytes = __DataStorage.reallocate(_bytes!, newCapacity) + } + } + /* Try again with minimum length */ + if newBytes == nil { + newCapacity = malloc_good_size(newLength) + allocateCleared = clear && __DataStorage.shouldAllocateCleared(newCapacity) + if allocateCleared && tryCalloc { + newBytes = __DataStorage.allocate(newCapacity, true) + if let newBytes = newBytes { + __DataStorage.move(newBytes, _bytes!, origLength) + _freeBytes() + } + } + if newBytes == nil { + allocateCleared = false + newBytes = __DataStorage.reallocate(_bytes!, newCapacity) + } + } + } + + if newBytes == nil { + /* Could not allocate bytes */ + // At this point if the allocation cannot occur the process is likely out of memory + // and Bad-Things™ are going to happen anyhow + fatalError("unable to allocate memory for length (\(newLength))") + } + + if origLength < newLength && clear && !allocateCleared { + _ = memset(newBytes!.advanced(by: origLength), 0, newLength - origLength) + } + + /* _length set by caller */ + _bytes = newBytes + _capacity = newCapacity + /* Realloc/memset doesn't zero out the entire capacity, so we must be safe and clear next time we grow the length */ + _needToZero = !allocateCleared + } + } + + func _freeBytes() { + if let bytes = _bytes { + if let dealloc = _deallocator { + dealloc(bytes, length) + } else { + free(bytes) + } + } + _deallocator = nil + } + + @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is trivially computed. + func enumerateBytes(in range: Range, _ block: (_ buffer: UnsafeBufferPointer, _ byteIndex: Data.Index, _ stop: inout Bool) -> Void) { + var stopv: Bool = false + let buffer = UnsafeRawBufferPointer(start: _bytes, count: Swift.min(range.upperBound - range.lowerBound, _length)) + buffer.withMemoryRebound(to: UInt8.self) { block($0, 0, &stopv) } + } + + @inlinable // This is @inlinable as it does not escape the _DataStorage boundary layer. + func setLength(_ length: Int) { + let origLength = _length + let newLength = length + if capacity < newLength || _bytes == nil { + ensureUniqueBufferReference(growingTo: newLength, clear: true) + } else if origLength < newLength && _needToZero { + _ = memset(_bytes! + origLength, 0, newLength - origLength) + } else if newLength < origLength { + _needToZero = true + } + _length = newLength + } + + @inlinable // This is @inlinable as it does not escape the _DataStorage boundary layer. + func append(_ bytes: UnsafeRawPointer, length: Int) { + precondition(length >= 0, "Length of appending bytes must not be negative") + let origLength = _length + let newLength = origLength + length + if capacity < newLength || _bytes == nil { + ensureUniqueBufferReference(growingTo: newLength, clear: false) + } + _length = newLength + __DataStorage.move(_bytes!.advanced(by: origLength), bytes, length) + } + + @inlinable // This is @inlinable despite escaping the __DataStorage boundary layer because it is trivially computed. + func get(_ index: Int) -> UInt8 { + // index must have already been validated by the caller + return _bytes!.load(fromByteOffset: index - _offset, as: UInt8.self) + } + + @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is trivially computed. + func set(_ index: Int, to value: UInt8) { + // index must have already been validated by the caller + ensureUniqueBufferReference() + _bytes!.storeBytes(of: value, toByteOffset: index - _offset, as: UInt8.self) + } + + @inlinable // This is @inlinable despite escaping the _DataStorage boundary layer because it is trivially computed. + func copyBytes(to pointer: UnsafeMutableRawPointer, from range: Range) { + let offsetPointer = UnsafeRawBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset), count: Swift.min(range.upperBound - range.lowerBound, _length)) + UnsafeMutableRawBufferPointer(start: pointer, count: range.upperBound - range.lowerBound).copyMemory(from: offsetPointer) + } + +#if FOUNDATION_FRAMEWORK + @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) +#endif + @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. + func replaceBytes(in range_: Range, with replacementBytes: UnsafeRawPointer?, length replacementLength: Int) { + let range = range_.lowerBound - _offset ..< range_.upperBound - _offset + let currentLength = _length + let resultingLength = currentLength - (range.upperBound - range.lowerBound) + replacementLength + let shift = resultingLength - currentLength + let mutableBytes: UnsafeMutableRawPointer + if resultingLength > currentLength { + ensureUniqueBufferReference(growingTo: resultingLength) + _length = resultingLength + } else { + ensureUniqueBufferReference() + } + mutableBytes = _bytes! + /* shift the trailing bytes */ + let start = range.lowerBound + let length = range.upperBound - range.lowerBound + if shift != 0 { + memmove(mutableBytes + start + replacementLength, mutableBytes + start + length, currentLength - start - length) + } + if replacementLength != 0 { + if let replacementBytes = replacementBytes { + memmove(mutableBytes + start, replacementBytes, replacementLength) + } else { + _ = memset(mutableBytes + start, 0, replacementLength) + } + } + + if resultingLength < currentLength { + setLength(resultingLength) + } + } + + @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. + func resetBytes(in range_: Range) { + let range = range_.lowerBound - _offset ..< range_.upperBound - _offset + if range.upperBound - range.lowerBound == 0 { return } + if _length < range.upperBound { + if capacity <= range.upperBound { + ensureUniqueBufferReference(growingTo: range.upperBound, clear: false) + } + _length = range.upperBound + } else { + ensureUniqueBufferReference() + } + _ = memset(_bytes!.advanced(by: range.lowerBound), 0, range.upperBound - range.lowerBound) + } + + @usableFromInline // This is not @inlinable as a non-trivial, non-convenience initializer. + init(length: Int) { + precondition(length < __DataStorage.maxSize) + var capacity = (length < 1024 * 1024 * 1024) ? length + (length >> 2) : length + if __DataStorage.vmOpsThreshold <= capacity { + capacity = Platform.roundUpToMultipleOfPageSize(capacity) + } + + let clear = __DataStorage.shouldAllocateCleared(length) + _bytes = __DataStorage.allocate(capacity, clear)! + _capacity = capacity + _needToZero = !clear + _length = 0 + _offset = 0 + setLength(length) + } + + @usableFromInline // This is not @inlinable as a non-convenience initializer. + init(capacity capacity_: Int = 0) { + var capacity = capacity_ + precondition(capacity < __DataStorage.maxSize) + if __DataStorage.vmOpsThreshold <= capacity { + capacity = Platform.roundUpToMultipleOfPageSize(capacity) + } + _length = 0 + _bytes = __DataStorage.allocate(capacity, false)! + _capacity = capacity + _needToZero = true + _offset = 0 + } + + @usableFromInline // This is not @inlinable as a non-convenience initializer. + init(bytes: UnsafeRawPointer?, length: Int) { + precondition(length < __DataStorage.maxSize) + _offset = 0 + if length == 0 { + _capacity = 0 + _length = 0 + _needToZero = false + _bytes = nil + } else if __DataStorage.vmOpsThreshold <= length { + _capacity = length + _length = length + _needToZero = true + _bytes = __DataStorage.allocate(length, false)! + __DataStorage.move(_bytes!, bytes, length) + } else { + var capacity = length + if __DataStorage.vmOpsThreshold <= capacity { + capacity = Platform.roundUpToMultipleOfPageSize(capacity) + } + _length = length + _bytes = __DataStorage.allocate(capacity, false)! + _capacity = capacity + _needToZero = true + __DataStorage.move(_bytes!, bytes, length) + } + } + + @usableFromInline // This is not @inlinable as a non-convenience initializer. + init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?, offset: Int) { + precondition(length < __DataStorage.maxSize) + _offset = offset + if length == 0 { + _capacity = 0 + _length = 0 + _needToZero = false + _bytes = nil + if let dealloc = deallocator, + let bytes_ = bytes { + dealloc(bytes_, length) + } + } else if !copy { + _capacity = length + _length = length + _needToZero = false + _bytes = bytes + _deallocator = deallocator + } else if __DataStorage.vmOpsThreshold <= length { + _capacity = length + _length = length + _needToZero = true + _bytes = __DataStorage.allocate(length, false)! + __DataStorage.move(_bytes!, bytes, length) + if let dealloc = deallocator { + dealloc(bytes!, length) + } + } else { + var capacity = length + if __DataStorage.vmOpsThreshold <= capacity { + capacity = Platform.roundUpToMultipleOfPageSize(capacity) + } + _length = length + _bytes = __DataStorage.allocate(capacity, false)! + _capacity = capacity + _needToZero = true + __DataStorage.move(_bytes!, bytes, length) + if let dealloc = deallocator { + dealloc(bytes!, length) + } + } + } + + @usableFromInline + init(offset: Int, bytes: UnsafeMutableRawPointer, capacity: Int, needToZero: Bool, length: Int, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?) { + _offset = offset + _bytes = bytes + _capacity = capacity + _needToZero = needToZero + _length = length + _deallocator = deallocator + } + + deinit { + _freeBytes() + } + + @inlinable // This is @inlinable despite escaping the __DataStorage boundary layer because it is trivially computed. + func mutableCopy(_ range: Range) -> __DataStorage { + return __DataStorage(bytes: _bytes?.advanced(by: range.lowerBound - _offset), length: range.upperBound - range.lowerBound, copy: true, deallocator: nil, offset: range.lowerBound) + } +} diff --git a/Sources/FoundationEssentials/Progress+Stub.swift b/Sources/FoundationEssentials/Progress+Stub.swift new file mode 100644 index 000000000..c6d2280ac --- /dev/null +++ b/Sources/FoundationEssentials/Progress+Stub.swift @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if !FOUNDATION_FRAMEWORK + +// Placeholder for Progress +internal final class Progress { + var completedUnitCount: Int64 + var totalUnitCount: Int64 + + init(totalUnitCount: Int64) { + self.completedUnitCount = 0 + self.totalUnitCount = totalUnitCount + } + + func becomeCurrent(withPendingUnitCount: Int64) { } + func resignCurrent() { } + var isCancelled: Bool { false } + static func current() -> Progress? { nil } + var fractionCompleted: Double { + 0.0 + } +} + +#endif // !FOUNDATION_FRAMEWORK From 3eaf5835ee5d42ddfe3b4c8a2320220f767a1390 Mon Sep 17 00:00:00 2001 From: Jeremy Schonfeld Date: Tue, 28 Oct 2025 16:01:24 -0700 Subject: [PATCH 2/2] Resolve availability warnings / fix non-Darwin build --- .../FoundationEssentials/Data/Data+Deprecated.swift | 1 + Sources/FoundationEssentials/Data/Data+Iterator.swift | 1 + Sources/FoundationEssentials/Data/Data+Reading.swift | 1 + .../FoundationEssentials/Data/Data+Searching.swift | 1 + Sources/FoundationEssentials/Data/Data+Writing.swift | 1 + .../Data/Representations/Data+Inline.swift | 11 +++++++++++ .../Data/Representations/Data+InlineSlice.swift | 1 + .../Data/Representations/Data+LargeSlice.swift | 1 + .../Data/Representations/Data+Representation.swift | 1 + .../Data/Representations/DataStorage.swift | 10 ++++++++++ 10 files changed, 29 insertions(+) diff --git a/Sources/FoundationEssentials/Data/Data+Deprecated.swift b/Sources/FoundationEssentials/Data/Data+Deprecated.swift index e9c470501..9df11f2a8 100644 --- a/Sources/FoundationEssentials/Data/Data+Deprecated.swift +++ b/Sources/FoundationEssentials/Data/Data+Deprecated.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Data { @available(swift, introduced: 4.2) @available(swift, deprecated: 5, message: "use `init(_:)` instead") diff --git a/Sources/FoundationEssentials/Data/Data+Iterator.swift b/Sources/FoundationEssentials/Data/Data+Iterator.swift index e5be6bca7..a19662ae8 100644 --- a/Sources/FoundationEssentials/Data/Data+Iterator.swift +++ b/Sources/FoundationEssentials/Data/Data+Iterator.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Data { /// An iterator over the contents of the data. /// diff --git a/Sources/FoundationEssentials/Data/Data+Reading.swift b/Sources/FoundationEssentials/Data/Data+Reading.swift index aaccd9dd6..5132bfaf2 100644 --- a/Sources/FoundationEssentials/Data/Data+Reading.swift +++ b/Sources/FoundationEssentials/Data/Data+Reading.swift @@ -514,6 +514,7 @@ private func readBytesFromFileDescriptor(_ fd: Int32, path: PathOrURL, buffer in return length - numBytesRemaining } +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Data { #if FOUNDATION_FRAMEWORK public typealias ReadingOptions = NSData.ReadingOptions diff --git a/Sources/FoundationEssentials/Data/Data+Searching.swift b/Sources/FoundationEssentials/Data/Data+Searching.swift index 375563fe8..92e9a3f16 100644 --- a/Sources/FoundationEssentials/Data/Data+Searching.swift +++ b/Sources/FoundationEssentials/Data/Data+Searching.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Data { #if FOUNDATION_FRAMEWORK public typealias SearchOptions = NSData.SearchOptions diff --git a/Sources/FoundationEssentials/Data/Data+Writing.swift b/Sources/FoundationEssentials/Data/Data+Writing.swift index 4cef7a370..03d3211d5 100644 --- a/Sources/FoundationEssentials/Data/Data+Writing.swift +++ b/Sources/FoundationEssentials/Data/Data+Writing.swift @@ -722,6 +722,7 @@ private func writeExtendedAttributes(fd: Int32, attributes: [String : Data]) { } #endif // !NO_FILESYSTEM +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Data { #if FOUNDATION_FRAMEWORK public typealias WritingOptions = NSData.WritingOptions diff --git a/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift b/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift index b0be12d2a..5981f0a6d 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+Inline.swift @@ -10,6 +10,17 @@ // //===----------------------------------------------------------------------===// +#if canImport(Glibc) +@preconcurrency import Glibc +#elseif canImport(Musl) +@preconcurrency import Musl +#elseif canImport(ucrt) +import ucrt +#elseif canImport(WASILibc) +@preconcurrency import WASILibc +#endif + +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Data { // A small inline buffer of bytes suitable for stack-allocation of small data. // Inlinability strategy: everything here should be inlined for direct operation on the stack wherever possible. diff --git a/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift index dd27a71c4..409c75b02 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+InlineSlice.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Data { #if _pointerBitWidth(_64) @usableFromInline internal typealias HalfInt = Int32 diff --git a/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift b/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift index 7e26cdb73..553a73965 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+LargeSlice.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Data { // A reference wrapper around a Range for when the range of a data buffer is too large to whole in a single word. // Inlinability strategy: everything should be inlinable as trivial. diff --git a/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift b/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift index dfa76e595..a6de6d806 100644 --- a/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift +++ b/Sources/FoundationEssentials/Data/Representations/Data+Representation.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Data { // The actual storage for Data's various representations. // Inlinability strategy: almost everything should be inlinable as forwarding the underlying implementations. (Inlining can also help avoid retain-release traffic around pulling values out of enums.) diff --git a/Sources/FoundationEssentials/Data/Representations/DataStorage.swift b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift index 81dc109a7..0ef9506cc 100644 --- a/Sources/FoundationEssentials/Data/Representations/DataStorage.swift +++ b/Sources/FoundationEssentials/Data/Representations/DataStorage.swift @@ -10,6 +10,16 @@ // //===----------------------------------------------------------------------===// +#if canImport(Glibc) +@preconcurrency import Glibc +#elseif canImport(Musl) +@preconcurrency import Musl +#elseif canImport(ucrt) +import ucrt +#elseif canImport(WASILibc) +@preconcurrency import WASILibc +#endif + // Underlying storage representation for medium and large data. // Inlinability strategy: methods from here should not inline into InlineSlice or LargeSlice unless trivial. // NOTE: older overlays called this class _DataStorage. The two must