Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Sources/FoundationEssentials/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ add_library(FoundationEssentials
Logging.swift
OutputBuffer.swift
Platform.swift
Progress+Stub.swift
SortComparator.swift
UUID_Wrappers.swift
UUID.swift
Expand Down
15 changes: 12 additions & 3 deletions Sources/FoundationEssentials/Data/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
5 changes: 4 additions & 1 deletion Sources/FoundationEssentials/Data/ContiguousBytes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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, *)
Expand Down
52 changes: 51 additions & 1 deletion Sources/FoundationEssentials/Data/Data+Base64.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {

Expand Down
60 changes: 60 additions & 0 deletions Sources/FoundationEssentials/Data/Data+Deprecated.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

@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")
public init<S: Sequence>(bytes elements: S) where S.Iterator.Element == UInt8 {
self.init(elements)
}

@available(swift, obsoleted: 4.2)
public init(bytes: Array<UInt8>) {
self.init(bytes)
}

@available(swift, obsoleted: 4.2)
public init(bytes: ArraySlice<UInt8>) {
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<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R` instead")
public func withUnsafeBytes<ResultType, ContentType>(_ body: (UnsafePointer<ContentType>) throws -> ResultType) rethrows -> ResultType {
return try _representation.withUnsafeBytes {
return try body($0.baseAddress?.assumingMemoryBound(to: ContentType.self) ?? UnsafePointer<ContentType>(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<R>(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R` instead")
public mutating func withUnsafeMutableBytes<ResultType, ContentType>(_ body: (UnsafeMutablePointer<ContentType>) throws -> ResultType) rethrows -> ResultType {
return try _representation.withUnsafeMutableBytes {
return try body($0.baseAddress?.assumingMemoryBound(to: ContentType.self) ?? UnsafeMutablePointer<ContentType>(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<UInt8>, _ byteIndex: Index, _ stop: inout Bool) -> Void) {
_representation.enumerateBytes(block)
}
}
80 changes: 80 additions & 0 deletions Sources/FoundationEssentials/Data/Data+Iterator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
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<Buffer>.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)..<end)
}
}
}

public mutating func next() -> UInt8? {
let idx = _idx
let bufferSize = MemoryLayout<Buffer>.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)
}
}
}
}
49 changes: 48 additions & 1 deletion Sources/FoundationEssentials/Data/Data+Reading.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -513,3 +513,50 @@ 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
#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)
}
}
56 changes: 56 additions & 0 deletions Sources/FoundationEssentials/Data/Data+Searching.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
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<Index>? = nil) -> Range<Index>? {
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
}
Loading