Skip to content
Open
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
7 changes: 5 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ list(APPEND CMAKE_MODULE_PATH ${SwiftFoundation_SOURCE_DIR}/cmake/modules)

# Availability Macros (only applies to FoundationEssentials and FoundationInternationalization)
set(_SwiftFoundation_BaseAvailability "macOS 15, iOS 18, tvOS 18, watchOS 11")
set(_SwiftFoundation_InlineArrayAvailability "macOS 26, iOS 26, tvOS 26, watchOS 26, visionOS 26")
set(_SwiftFoundation_FutureAvailability "macOS 10000, iOS 10000, tvOS 10000, watchOS 10000")

# All versions to define for each availability name
Expand All @@ -106,11 +107,13 @@ list(APPEND _SwiftFoundation_versions

# Each availability name to define
list(APPEND _SwiftFoundation_availability_names
"FoundationPreview")
"FoundationPreview"
"FoundationInlineArray")

# The aligned availability for each name (in the same order)
list(APPEND _SwiftFoundation_availability_releases
${_SwiftFoundation_BaseAvailability})
${_SwiftFoundation_BaseAvailability}
${_SwiftFoundation_InlineArrayAvailability})

foreach(version ${_SwiftFoundation_versions})
foreach(name release IN ZIP_LISTS _SwiftFoundation_availability_names _SwiftFoundation_availability_releases)
Expand Down
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import CompilerPluginSupport

let availabilityTags: [_Availability] = [
_Availability("FoundationPreview"), // Default FoundationPreview availability
_Availability("FoundationInlineArray", availability: .macOS26) // Availability of InlineArray
]
let versionNumbers = ["6.0.2", "6.1", "6.2", "6.3"]

Expand Down
28 changes: 27 additions & 1 deletion Sources/FoundationEssentials/Data/ContiguousBytes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
/// Indicates that the conforming type is a contiguous collection of raw bytes
/// whose underlying storage is directly accessible by withUnsafeBytes.
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
public protocol ContiguousBytes {
public protocol ContiguousBytes: ~Escapable, ~Copyable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just confirming, are there any ABI implications on this change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it is not ABI-breaking (or source-breaking) to make this change.

/// Calls the given closure with the contents of underlying storage.
///
/// - note: Calling `withUnsafeBytes` multiple times does not guarantee that
Expand Down Expand Up @@ -109,3 +109,29 @@ extension Slice : ContiguousBytes where Base : ContiguousBytes {
}
}
}

//===--- Span Conformances -----------------------------------------===//

@available(FoundationPreview 6.3, *)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a public conformance, right? We would need an API review.

extension RawSpan: ContiguousBytes {
}

@available(FoundationPreview 6.3, *)
extension MutableRawSpan: ContiguousBytes {
}

@available(FoundationPreview 6.3, *)
extension Span: ContiguousBytes where Element == UInt8 {
}

@available(FoundationPreview 6.3, *)
extension MutableSpan: ContiguousBytes where Element == UInt8 {
}

@available(FoundationInlineArray 6.3, *)
extension InlineArray: ContiguousBytes where Element == UInt8 {
@_alwaysEmitIntoClient
public func withUnsafeBytes<R, E>(_ body: (UnsafeRawBufferPointer) throws(E) -> R) throws(E) -> R {
return try span.withUnsafeBytes(body)
}
}
39 changes: 39 additions & 0 deletions Tests/FoundationEssentialsTests/ContiguousBytesTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

import Testing

#if canImport(FoundationEssentials)
@testable import FoundationEssentials
#else
@testable import Foundation
#endif

func acceptContiguousBytes<T: ContiguousBytes & ~Escapable & ~Copyable>(_ bytes: borrowing T) { }

@Suite("ContiguousBytesTests")
private struct ContiguousBytesTests {
@Test func span() throws {
if #available(FoundationPreview 6.3, *) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the function is already annotated as only available on macOS 26+, I don't think we need this #available check here. In the end what we probably want is for this function to be annotated as @available(FoundationInlineArray 6.3, *)

var bytes: [UInt8] = [1, 2, 3]
bytes.withUnsafeMutableBufferPointer { unsafeBytes in
acceptContiguousBytes(unsafeBytes.span)
acceptContiguousBytes(unsafeBytes.mutableSpan)
acceptContiguousBytes(unsafeBytes.span.bytes)

var ms = unsafeBytes.mutableSpan
acceptContiguousBytes(ms.bytes)
acceptContiguousBytes(ms.mutableBytes)
}
}
}
}