Skip to content

Commit bba6176

Browse files
committed
Refactor Data implementation into separate chunks
1 parent 1aac1b5 commit bba6176

18 files changed

+2237
-2103
lines changed

Sources/FoundationEssentials/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ add_library(FoundationEssentials
2828
Logging.swift
2929
OutputBuffer.swift
3030
Platform.swift
31+
Progress+Stub.swift
3132
SortComparator.swift
3233
UUID_Wrappers.swift
3334
UUID.swift

Sources/FoundationEssentials/Data/CMakeLists.txt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
##
33
## This source file is part of the Swift open source project
44
##
5-
## Copyright (c) 2024 Apple Inc. and the Swift project authors
5+
## Copyright (c) 2024-2025 Apple Inc. and the Swift project authors
66
## Licensed under Apache License v2.0
77
##
88
## See LICENSE.txt for license information
@@ -13,13 +13,22 @@
1313
##===----------------------------------------------------------------------===##
1414

1515
target_sources(FoundationEssentials PRIVATE
16+
Representations/Data+Inline.swift
17+
Representations/Data+InlineSlice.swift
18+
Representations/Data+LargeSlice.swift
19+
Representations/Data+Representation.swift
20+
Representations/DataStorage.swift
21+
1622
Collections+DataProtocol.swift
1723
ContiguousBytes.swift
24+
Data.swift
1825
Data+Base64.swift
26+
Data+Deprecated.swift
1927
Data+Error.swift
28+
Data+Iterator.swift
2029
Data+Reading.swift
21-
Data+Stub.swift
30+
Data+Searching.swift
2231
Data+Writing.swift
23-
Data.swift
2432
DataProtocol.swift
33+
PathOrURL.swift
2534
Pointers+DataProtocol.swift)

Sources/FoundationEssentials/Data/ContiguousBytes.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2018 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2018-2025 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -39,6 +39,9 @@ extension ArraySlice : ContiguousBytes where Element == UInt8 { }
3939
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
4040
extension ContiguousArray : ContiguousBytes where Element == UInt8 { }
4141

42+
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
43+
extension Data : ContiguousBytes { }
44+
4245
//===--- Pointer Conformances ---------------------------------------------===//
4346

4447
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)

Sources/FoundationEssentials/Data/Data+Base64.swift

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2023-2024 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2023-2025 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -25,6 +25,56 @@ import WinSDK
2525
import WASILibc
2626
#endif
2727

28+
#if !FOUNDATION_FRAMEWORK
29+
extension Data {
30+
31+
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
32+
public struct Base64EncodingOptions : OptionSet, Sendable {
33+
public let rawValue: UInt
34+
35+
public init(rawValue: UInt) {
36+
self.rawValue = rawValue
37+
}
38+
/// Set the maximum line length to 64 characters, after which a line ending is inserted.
39+
public static let lineLength64Characters = Base64EncodingOptions(rawValue: 1 << 0)
40+
/// Set the maximum line length to 76 characters, after which a line ending is inserted.
41+
public static let lineLength76Characters = Base64EncodingOptions(rawValue: 1 << 1)
42+
/// When a maximum line length is set, specify that the line ending to insert should include a carriage return.
43+
public static let endLineWithCarriageReturn = Base64EncodingOptions(rawValue: 1 << 4)
44+
/// When a maximum line length is set, specify that the line ending to insert should include a line feed.
45+
public static let endLineWithLineFeed = Base64EncodingOptions(rawValue: 1 << 5)
46+
}
47+
48+
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
49+
public struct Base64DecodingOptions : OptionSet, Sendable {
50+
public let rawValue: UInt
51+
52+
public init(rawValue: UInt) {
53+
self.rawValue = rawValue
54+
}
55+
/// Modify the decoding algorithm so that it ignores unknown non-Base-64 bytes, including line ending characters.
56+
public static let ignoreUnknownCharacters = Base64DecodingOptions(rawValue: 1 << 0)
57+
}
58+
}
59+
#else
60+
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
61+
extension Data {
62+
// These types are typealiased to the `NSData` options for framework builds only.
63+
public typealias Base64EncodingOptions = NSData.Base64EncodingOptions
64+
public typealias Base64DecodingOptions = NSData.Base64DecodingOptions
65+
}
66+
#endif //!FOUNDATION_FRAMEWORK
67+
68+
extension Data.Base64EncodingOptions {
69+
/// Use the base64url alphabet to encode the data
70+
@available(FoundationPreview 6.3, *)
71+
public static let base64URLAlphabet = Self(rawValue: 1 << 6)
72+
73+
/// Omit the `=` padding characters in the end of the base64 encoded result
74+
@available(FoundationPreview 6.3, *)
75+
public static let omitPaddingCharacter = Self(rawValue: 1 << 7)
76+
}
77+
2878
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
2979
extension Data {
3080

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
extension Data {
14+
@available(swift, introduced: 4.2)
15+
@available(swift, deprecated: 5, message: "use `init(_:)` instead")
16+
public init<S: Sequence>(bytes elements: S) where S.Iterator.Element == UInt8 {
17+
self.init(elements)
18+
}
19+
20+
@available(swift, obsoleted: 4.2)
21+
public init(bytes: Array<UInt8>) {
22+
self.init(bytes)
23+
}
24+
25+
@available(swift, obsoleted: 4.2)
26+
public init(bytes: ArraySlice<UInt8>) {
27+
self.init(bytes)
28+
}
29+
30+
/// Access the bytes in the data.
31+
///
32+
/// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure.
33+
@available(swift, deprecated: 5, message: "use `withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R` instead")
34+
public func withUnsafeBytes<ResultType, ContentType>(_ body: (UnsafePointer<ContentType>) throws -> ResultType) rethrows -> ResultType {
35+
return try _representation.withUnsafeBytes {
36+
return try body($0.baseAddress?.assumingMemoryBound(to: ContentType.self) ?? UnsafePointer<ContentType>(bitPattern: 0xBAD0)!)
37+
}
38+
}
39+
40+
/// Mutate the bytes in the data.
41+
///
42+
/// This function assumes that you are mutating the contents.
43+
/// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure.
44+
@available(swift, deprecated: 5, message: "use `withUnsafeMutableBytes<R>(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R` instead")
45+
public mutating func withUnsafeMutableBytes<ResultType, ContentType>(_ body: (UnsafeMutablePointer<ContentType>) throws -> ResultType) rethrows -> ResultType {
46+
return try _representation.withUnsafeMutableBytes {
47+
return try body($0.baseAddress?.assumingMemoryBound(to: ContentType.self) ?? UnsafeMutablePointer<ContentType>(bitPattern: 0xBAD0)!)
48+
}
49+
}
50+
51+
/// Enumerate the contents of the data.
52+
///
53+
/// 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.
54+
/// - parameter block: The closure to invoke for each region of data. You may stop the enumeration by setting the `stop` parameter to `true`.
55+
@available(swift, deprecated: 5, message: "use `regions` or `for-in` instead")
56+
public func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer<UInt8>, _ byteIndex: Index, _ stop: inout Bool) -> Void) {
57+
_representation.enumerateBytes(block)
58+
}
59+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
extension Data {
14+
/// An iterator over the contents of the data.
15+
///
16+
/// The iterator will increment byte-by-byte.
17+
@inlinable // This is @inlinable as trivially computable.
18+
public func makeIterator() -> Data.Iterator {
19+
return Iterator(self, at: startIndex)
20+
}
21+
22+
public struct Iterator : IteratorProtocol, Sendable {
23+
@usableFromInline
24+
internal typealias Buffer = (
25+
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
26+
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
27+
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
28+
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)
29+
30+
@usableFromInline internal let _data: Data
31+
@usableFromInline internal var _buffer: Buffer
32+
@usableFromInline internal var _idx: Data.Index
33+
@usableFromInline internal let _endIdx: Data.Index
34+
35+
@usableFromInline // This is @usableFromInline as a non-trivial initializer.
36+
internal init(_ data: Data, at loc: Data.Index) {
37+
// The let vars prevent this from being marked as @inlinable
38+
_data = data
39+
_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)
40+
_idx = loc
41+
_endIdx = data.endIndex
42+
43+
let bufferSize = MemoryLayout<Buffer>.size
44+
Swift.withUnsafeMutableBytes(of: &_buffer) {
45+
$0.withMemoryRebound(to: UInt8.self) { [endIndex = data.endIndex] buf in
46+
let bufferIdx = (loc - data.startIndex) % bufferSize
47+
let end = (endIndex - (loc - bufferIdx) > bufferSize) ? (loc - bufferIdx + bufferSize) : endIndex
48+
data.copyBytes(to: buf, from: (loc - bufferIdx)..<end)
49+
}
50+
}
51+
}
52+
53+
public mutating func next() -> UInt8? {
54+
let idx = _idx
55+
let bufferSize = MemoryLayout<Buffer>.size
56+
57+
guard idx < _endIdx else { return nil }
58+
_idx += 1
59+
60+
let bufferIdx = (idx - _data.startIndex) % bufferSize
61+
62+
63+
if bufferIdx == 0 {
64+
var buffer = _buffer
65+
Swift.withUnsafeMutableBytes(of: &buffer) {
66+
$0.withMemoryRebound(to: UInt8.self) {
67+
// populate the buffer
68+
_data.copyBytes(to: $0, from: idx..<(_endIdx - idx > bufferSize ? idx + bufferSize : _endIdx))
69+
}
70+
}
71+
_buffer = buffer
72+
}
73+
74+
return Swift.withUnsafeMutableBytes(of: &_buffer) {
75+
$0.load(fromByteOffset: bufferIdx, as: UInt8.self)
76+
}
77+
}
78+
}
79+
}

Sources/FoundationEssentials/Data/Data+Reading.swift

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -513,3 +513,49 @@ private func readBytesFromFileDescriptor(_ fd: Int32, path: PathOrURL, buffer in
513513

514514
return length - numBytesRemaining
515515
}
516+
517+
extension Data {
518+
#if FOUNDATION_FRAMEWORK
519+
public typealias ReadingOptions = NSData.ReadingOptions
520+
#else
521+
public struct ReadingOptions : OptionSet, Sendable {
522+
public let rawValue: UInt
523+
public init(rawValue: UInt) { self.rawValue = rawValue }
524+
525+
public static let mappedIfSafe = ReadingOptions(rawValue: 1 << 0)
526+
public static let uncached = ReadingOptions(rawValue: 1 << 1)
527+
public static let alwaysMapped = ReadingOptions(rawValue: 1 << 3)
528+
}
529+
#endif
530+
531+
#if !FOUNDATION_FRAMEWORK
532+
@_spi(SwiftCorelibsFoundation)
533+
public dynamic init(_contentsOfRemote url: URL, options: ReadingOptions = []) throws {
534+
assert(!url.isFileURL)
535+
throw CocoaError(.fileReadUnsupportedScheme)
536+
}
537+
#endif
538+
539+
/// Initialize a `Data` with the contents of a `URL`.
540+
///
541+
/// - parameter url: The `URL` to read.
542+
/// - parameter options: Options for the read operation. Default value is `[]`.
543+
/// - throws: An error in the Cocoa domain, if `url` cannot be read.
544+
public init(contentsOf url: __shared URL, options: ReadingOptions = []) throws {
545+
if url.isFileURL {
546+
self = try readDataFromFile(path: .url(url), reportProgress: true, options: options)
547+
} else {
548+
#if FOUNDATION_FRAMEWORK
549+
// Fallback to NSData, to read via NSURLSession
550+
let d = try NSData(contentsOf: url, options: NSData.ReadingOptions(rawValue: options.rawValue))
551+
self.init(referencing: d)
552+
#else
553+
try self.init(_contentsOfRemote: url, options: options)
554+
#endif
555+
}
556+
}
557+
558+
internal init(contentsOfFile path: String, options: ReadingOptions = []) throws {
559+
self = try readDataFromFile(path: .path(path), reportProgress: true, options: options)
560+
}
561+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
extension Data {
14+
#if FOUNDATION_FRAMEWORK
15+
public typealias SearchOptions = NSData.SearchOptions
16+
17+
/// Find the given `Data` in the content of this `Data`.
18+
///
19+
/// - parameter dataToFind: The data to be searched for.
20+
/// - parameter options: Options for the search. Default value is `[]`.
21+
/// - 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.
22+
/// - returns: A `Range` specifying the location of the found data, or nil if a match could not be found.
23+
/// - precondition: `range` must be in the bounds of the Data.
24+
public func range(of dataToFind: Data, options: Data.SearchOptions = [], in range: Range<Index>? = nil) -> Range<Index>? {
25+
let nsRange : NSRange
26+
if let r = range {
27+
nsRange = NSRange(location: r.lowerBound - startIndex, length: r.upperBound - r.lowerBound)
28+
} else {
29+
nsRange = NSRange(location: 0, length: count)
30+
}
31+
let result = _representation.withInteriorPointerReference {
32+
let opts = NSData.SearchOptions(rawValue: options.rawValue)
33+
return $0.range(of: dataToFind, options: opts, in: nsRange)
34+
}
35+
if result.location == NSNotFound {
36+
return nil
37+
}
38+
return (result.location + startIndex)..<((result.location + startIndex) + result.length)
39+
}
40+
#else
41+
// TODO: Implement range(of:options:in:) for Foundation package.
42+
43+
public struct SearchOptions : OptionSet, Sendable {
44+
public let rawValue: UInt
45+
46+
public init(rawValue: UInt) {
47+
self.rawValue = rawValue
48+
}
49+
/// Search from the end of the data object.
50+
public static let backwards = SearchOptions(rawValue: 1 << 0)
51+
/// Search is limited to start (or end, if searching backwards) of the data object.
52+
public static let anchored = SearchOptions(rawValue: 1 << 1)
53+
}
54+
#endif
55+
}

0 commit comments

Comments
 (0)