Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
59 changes: 59 additions & 0 deletions Sources/FoundationEssentials/Data/Data+Deprecated.swift
Original file line number Diff line number Diff line change
@@ -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<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)
}
}
79 changes: 79 additions & 0 deletions Sources/FoundationEssentials/Data/Data+Iterator.swift
Original file line number Diff line number Diff line change
@@ -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<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)
}
}
}
}
48 changes: 47 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,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)
}
}
55 changes: 55 additions & 0 deletions Sources/FoundationEssentials/Data/Data+Searching.swift
Original file line number Diff line number Diff line change
@@ -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<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
Loading