Merge pull request #83000 from glessard/rdar147780495-OutputSpan-updates-62

[SE-0485, 6.2] OutputSpan and OutputRawSpan
This commit is contained in:
Guillaume Lessard
2025-07-17 19:12:58 -07:00
committed by GitHub
19 changed files with 2065 additions and 769 deletions

View File

@@ -154,6 +154,8 @@ add_library(swiftCore
Span/RawSpan.swift
Span/MutableSpan.swift
Span/MutableRawSpan.swift
Span/OutputSpan.swift
Span/OutputRawSpan.swift
StaticString.swift
StaticPrint.swift
Stride.swift

View File

@@ -158,6 +158,8 @@ split_embedded_sources(
EMBEDDED Sort.swift
EMBEDDED Span/MutableRawSpan.swift
EMBEDDED Span/MutableSpan.swift
EMBEDDED Span/OutputRawSpan.swift
EMBEDDED Span/OutputSpan.swift
EMBEDDED Span/RawSpan.swift
EMBEDDED Span/Span.swift
EMBEDDED StaticString.swift

View File

@@ -202,6 +202,8 @@
"Span": [
"MutableRawSpan.swift",
"MutableSpan.swift",
"OutputRawSpan.swift",
"OutputSpan.swift",
"RawSpan.swift",
"Span.swift"
],

View File

@@ -296,6 +296,25 @@ extension InlineArray where Element: ~Copyable {
}
#else
fatalError()
#endif
}
@available(SwiftStdlib 6.2, *)
@_alwaysEmitIntoClient
public init<E: Error>(
initializingWith initializer: (inout OutputSpan<Element>) throws(E) -> Void
) throws(E) {
#if $BuiltinEmplaceTypedThrows
_storage = try Builtin.emplace { (rawPtr) throws(E) -> () in
let buffer = unsafe Self._initializationBuffer(start: rawPtr)
_internalInvariant(Self.count == buffer.count)
var output = unsafe OutputSpan(buffer: buffer, initializedCount: 0)
try initializer(&output)
let initialized = unsafe output.finalize(for: buffer)
_precondition(count == initialized, "InlineArray initialization underflow")
}
#else
fatalError()
#endif
}
}

View File

@@ -360,108 +360,6 @@ extension MutableRawSpan {
}
}
// FIXME: The functions in this extension crash the SIL optimizer when built inside
// the stub. But these declarations don't generate a public symbol anyway.
#if !SPAN_COMPATIBILITY_STUB
//MARK: copyMemory
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension MutableRawSpan {
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update<S: Sequence>(
from source: S
) -> (unwritten: S.Iterator, byteOffset: Int) where S.Element: BitwiseCopyable {
var iterator = source.makeIterator()
let offset = update(from: &iterator)
return (iterator, offset)
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update<Element: BitwiseCopyable>(
from elements: inout some IteratorProtocol<Element>
) -> Int {
var offset = 0
while offset + MemoryLayout<Element>.stride <= _count {
guard let element = elements.next() else { break }
unsafe storeBytes(
of: element, toUncheckedByteOffset: offset, as: Element.self
)
offset &+= MemoryLayout<Element>.stride
}
return offset
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update<C: Collection>(
fromContentsOf source: C
) -> Int where C.Element: BitwiseCopyable {
let newOffset = source.withContiguousStorageIfAvailable {
self.update(fromContentsOf: unsafe RawSpan(_unsafeElements: $0))
}
if let newOffset { return newOffset }
var elements = source.makeIterator()
let lastOffset = update(from: &elements)
_precondition(
elements.next() == nil,
"destination span cannot contain every element from source."
)
return lastOffset
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update<Element: BitwiseCopyable>(
fromContentsOf source: Span<Element>
) -> Int {
// update(from: source.bytes)
unsafe source.withUnsafeBytes {
unsafe update(fromContentsOf: $0)
}
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update<Element: BitwiseCopyable>(
fromContentsOf source: borrowing MutableSpan<Element>
) -> Int {
// update(from: source.span.bytes)
unsafe source.withUnsafeBytes {
unsafe update(fromContentsOf: $0)
}
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update(
fromContentsOf source: RawSpan
) -> Int {
_precondition(
source.byteCount <= self.byteCount,
"destination span cannot contain every byte from source."
)
if source.byteCount == 0 { return 0 }
unsafe source.withUnsafeBytes {
unsafe _start().copyMemory(from: $0.baseAddress!, byteCount: $0.count)
}
return source.byteCount
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update(
fromContentsOf source: borrowing MutableRawSpan
) -> Int {
update(fromContentsOf: source.bytes)
}
}
#endif
// MARK: sub-spans
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
@@ -482,13 +380,31 @@ extension MutableRawSpan {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(_ bounds: Range<Int>) -> Self {
mutating public func _mutatingExtracting(_ bounds: Range<Int>) -> Self {
_precondition(
UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) &&
UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count),
"Index range out of bounds"
)
return unsafe extracting(unchecked: bounds)
return unsafe _mutatingExtracting(unchecked: bounds)
}
@available(*, deprecated, renamed: "_mutatingExtracting(_:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(_ bounds: Range<Int>) -> Self {
_mutatingExtracting(bounds)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(_ bounds: Range<Int>) -> Self {
_precondition(
UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) &&
UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count),
"Index range out of bounds"
)
return unsafe _consumingExtracting(unchecked: bounds)
}
/// Constructs a new span over the items within the supplied range of
@@ -509,12 +425,29 @@ extension MutableRawSpan {
@unsafe
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(unchecked bounds: Range<Int>) -> Self {
mutating public func _mutatingExtracting(unchecked bounds: Range<Int>) -> Self {
let newStart = unsafe _pointer?.advanced(by: bounds.lowerBound)
let newSpan = unsafe Self(_unchecked: newStart, byteCount: bounds.count)
return unsafe _overrideLifetime(newSpan, mutating: &self)
}
@unsafe
@available(*, deprecated, renamed: "_mutatingExtracting(unchecked:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(unchecked bounds: Range<Int>) -> Self {
unsafe _mutatingExtracting(unchecked: bounds)
}
@unsafe
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(unchecked bounds: Range<Int>) -> Self {
let newStart = unsafe _pointer?.advanced(by: bounds.lowerBound)
let newSpan = unsafe Self(_unchecked: newStart, byteCount: bounds.count)
return unsafe _overrideLifetime(newSpan, copying: self)
}
/// Constructs a new span over the items within the supplied range of
/// positions within this span.
///
@@ -530,10 +463,27 @@ extension MutableRawSpan {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func _mutatingExtracting(
_ bounds: some RangeExpression<Int>
) -> Self {
_mutatingExtracting(bounds.relative(to: byteOffsets))
}
@available(*, deprecated, renamed: "_mutatingExtracting(_:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(
_ bounds: some RangeExpression<Int>
) -> Self {
extracting(bounds.relative(to: byteOffsets))
_mutatingExtracting(bounds)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(
_ bounds: some RangeExpression<Int>
) -> Self {
_consumingExtracting(bounds.relative(to: byteOffsets))
}
/// Constructs a new span over the items within the supplied range of
@@ -554,11 +504,35 @@ extension MutableRawSpan {
@unsafe
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(unchecked bounds: ClosedRange<Int>) -> Self {
mutating public func _mutatingExtracting(
unchecked bounds: ClosedRange<Int>
) -> Self {
let range = unsafe Range(
_uncheckedBounds: (bounds.lowerBound, bounds.upperBound+1)
_uncheckedBounds: (bounds.lowerBound, bounds.upperBound + 1)
)
return unsafe extracting(unchecked: range)
return unsafe _mutatingExtracting(unchecked: range)
}
@unsafe
@available(*, deprecated, renamed: "_mutatingExtracting(unchecked:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(
unchecked bounds: ClosedRange<Int>
) -> Self {
unsafe _mutatingExtracting(unchecked: bounds)
}
@unsafe
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(
unchecked bounds: ClosedRange<Int>
) -> Self {
let range = unsafe Range(
_uncheckedBounds: (bounds.lowerBound, bounds.upperBound + 1)
)
return unsafe _consumingExtracting(unchecked: range)
}
/// Constructs a new span over all the items of this span.
@@ -572,10 +546,23 @@ extension MutableRawSpan {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(_: UnboundedRange) -> Self {
mutating public func _mutatingExtracting(_: UnboundedRange) -> Self {
let newSpan = unsafe Self(_unchecked: _pointer, byteCount: _count)
return unsafe _overrideLifetime(newSpan, mutating: &self)
}
@available(*, deprecated, renamed: "_mutatingExtracting(_:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(_: UnboundedRange) -> Self {
_mutatingExtracting(...)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(_: UnboundedRange) -> Self {
self
}
}
// MARK: prefixes and suffixes
@@ -600,7 +587,7 @@ extension MutableRawSpan {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(first maxLength: Int) -> Self {
mutating public func _mutatingExtracting(first maxLength: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(maxLength >= 0, "Can't have a prefix of negative length")
let newCount = min(maxLength, byteCount)
@@ -611,6 +598,26 @@ extension MutableRawSpan {
#endif
}
@available(*, deprecated, renamed: "_mutatingExtracting(first:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(first maxLength: Int) -> Self {
_mutatingExtracting(first: maxLength)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(first maxLength: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(maxLength >= 0, "Can't have a prefix of negative length")
let newCount = min(maxLength, byteCount)
let newSpan = unsafe Self(_unchecked: _pointer, byteCount: newCount)
return unsafe _overrideLifetime(newSpan, copying: self)
#else
fatalError("Unsupported compiler")
#endif
}
/// Returns a span over all but the given number of trailing elements.
///
/// If the number of elements to drop exceeds the number of elements in
@@ -627,7 +634,7 @@ extension MutableRawSpan {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(droppingLast k: Int) -> Self {
mutating public func _mutatingExtracting(droppingLast k: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(k >= 0, "Can't drop a negative number of elements")
let droppedCount = min(k, byteCount)
@@ -639,6 +646,27 @@ extension MutableRawSpan {
#endif
}
@available(*, deprecated, renamed: "_mutatingExtracting(droppingLast:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(droppingLast k: Int) -> Self {
_mutatingExtracting(droppingLast: k)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(droppingLast k: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(k >= 0, "Can't drop a negative number of elements")
let droppedCount = min(k, byteCount)
let newCount = byteCount &- droppedCount
let newSpan = unsafe Self(_unchecked: _pointer, byteCount: newCount)
return unsafe _overrideLifetime(newSpan, copying: self)
#else
fatalError("Unsupported compiler")
#endif
}
/// Returns a span containing the final elements of the span,
/// up to the given maximum length.
///
@@ -656,12 +684,37 @@ extension MutableRawSpan {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func _mutatingExtracting(last maxLength: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(maxLength >= 0, "Can't have a suffix of negative length")
let newCount = min(maxLength, byteCount)
let newStart = unsafe _pointer?.advanced(by: byteCount &- newCount)
let newSpan = unsafe Self(_unchecked: newStart, byteCount: newCount)
return unsafe _overrideLifetime(newSpan, mutating: &self)
#else
fatalError("Unsupported compiler")
#endif
}
@available(*, deprecated, renamed: "_mutatingExtracting(last:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(last maxLength: Int) -> Self {
_mutatingExtracting(last: maxLength)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(last maxLength: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(maxLength >= 0, "Can't have a suffix of negative length")
let newCount = min(maxLength, byteCount)
let newStart = unsafe _pointer?.advanced(by: byteCount &- newCount)
let newSpan = unsafe Self(_unchecked: newStart, byteCount: newCount)
return unsafe _overrideLifetime(newSpan, copying: self)
#else
fatalError("Unsupported compiler")
#endif
}
/// Returns a span over all but the given number of initial elements.
@@ -680,7 +733,7 @@ extension MutableRawSpan {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(droppingFirst k: Int) -> Self {
mutating public func _mutatingExtracting(droppingFirst k: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(k >= 0, "Can't drop a negative number of bytes")
let droppedCount = min(k, byteCount)
@@ -690,6 +743,28 @@ extension MutableRawSpan {
return unsafe _overrideLifetime(newSpan, mutating: &self)
#else
fatalError("Unsupported compiler")
#endif
}
@available(*, deprecated, renamed: "_mutatingExtracting(droppingFirst:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(droppingFirst k: Int) -> Self {
_mutatingExtracting(droppingFirst: k)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(droppingFirst k: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(k >= 0, "Can't drop a negative number of bytes")
let droppedCount = min(k, byteCount)
let newStart = unsafe _pointer?.advanced(by: droppedCount)
let newCount = byteCount &- droppedCount
let newSpan = unsafe Self(_unchecked: newStart, byteCount: newCount)
return unsafe _overrideLifetime(newSpan, copying: self)
#else
fatalError("Unsupported compiler")
#endif
}
}

View File

@@ -355,56 +355,6 @@ extension MutableSpan where Element: ~Copyable {
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension MutableSpan where Element: BitwiseCopyable {
/// Accesses the element at the specified position in the `Span`.
///
/// - Parameter position: The offset of the element to access. `position`
/// must be greater or equal to zero, and less than `count`.
///
/// - Complexity: O(1)
@_alwaysEmitIntoClient
public subscript(_ position: Index) -> Element {
get {
_precondition(indices.contains(position), "index out of bounds")
return unsafe self[unchecked: position]
}
@lifetime(self: copy self)
set {
_precondition(indices.contains(position), "index out of bounds")
unsafe self[unchecked: position] = newValue
}
}
/// Accesses the element at the specified position in the `Span`.
///
/// This subscript does not validate `position`; this is an unsafe operation.
///
/// - Parameter position: The offset of the element to access. `position`
/// must be greater or equal to zero, and less than `count`.
///
/// - Complexity: O(1)
@unsafe
@_alwaysEmitIntoClient
public subscript(unchecked position: Index) -> Element {
get {
let offset = position&*MemoryLayout<Element>.stride
return unsafe _start().loadUnaligned(
fromByteOffset: offset, as: Element.self
)
}
@lifetime(self: copy self)
set {
let offset = position&*MemoryLayout<Element>.stride
unsafe _start().storeBytes(
of: newValue, toByteOffset: offset, as: Element.self
)
}
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension MutableSpan where Element: ~Copyable {
@@ -476,220 +426,6 @@ extension MutableSpan {
unsafe $0.update(repeating: repeatedValue, count: count)
}
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update<S: Sequence>(
from source: S
) -> (unwritten: S.Iterator, index: Index) where S.Element == Element {
var iterator = source.makeIterator()
let index = update(from: &iterator)
return (iterator, index)
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update(
from elements: inout some IteratorProtocol<Element>
) -> Index {
var index = 0
while index < _count {
guard let element = elements.next() else { break }
unsafe self[unchecked: index] = element
index &+= 1
}
return index
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update(
fromContentsOf source: some Collection<Element>
) -> Index {
let updated = source.withContiguousStorageIfAvailable {
self.update(fromContentsOf: unsafe Span(_unsafeElements: $0))
}
if let updated {
return updated
}
//TODO: use _copyContents here
var iterator = source.makeIterator()
let index = update(from: &iterator)
_precondition(
iterator.next() == nil,
"destination buffer view cannot contain every element from source."
)
return index
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update(fromContentsOf source: Span<Element>) -> Index {
guard !source.isEmpty else { return 0 }
_precondition(
source.count <= self.count,
"destination span cannot contain every element from source."
)
unsafe _start().withMemoryRebound(
to: Element.self, capacity: source.count
) { dest in
unsafe source.withUnsafeBufferPointer {
unsafe dest.update(from: $0.baseAddress!, count: $0.count)
}
}
return source.count
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update(
fromContentsOf source: borrowing MutableSpan<Element>
) -> Index {
update(fromContentsOf: source.span)
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension MutableSpan where Element: ~Copyable {
// @_alwaysEmitIntoClient
// public mutating func moveUpdate(
// fromContentsOf source: consuming OutputSpan<Element>
// ) -> Index {
// guard !source.isEmpty else { return 0 }
// _precondition(
// source.count <= self.count,
// "destination span cannot contain every element from source."
// )
// let buffer = unsafe source.relinquishBorrowedMemory()
// // we must now deinitialize the returned UMBP
// unsafe _start().moveInitializeMemory(
// as: Element.self, from: buffer.baseAddress!, count: buffer.count
// )
// return buffer.count
// }
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func moveUpdate(
fromContentsOf source: UnsafeMutableBufferPointer<Element>
) -> Index {
// let source = OutputSpan(_initializing: source, initialized: source.count)
// return self.moveUpdate(fromContentsOf: source)
unsafe withUnsafeMutableBufferPointer {
unsafe $0.moveUpdate(fromContentsOf: source)
}
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension MutableSpan {
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func moveUpdate(
fromContentsOf source: Slice<UnsafeMutableBufferPointer<Element>>
) -> Index {
unsafe moveUpdate(fromContentsOf: .init(rebasing: source))
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension MutableSpan where Element: BitwiseCopyable {
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update(
repeating repeatedValue: Element
) where Element: BitwiseCopyable {
guard count > 0 else { return }
// rebind _start manually in order to avoid assumptions about alignment.
let rp = unsafe _start()._rawValue
let binding = Builtin.bindMemory(rp, count._builtinWordValue, Element.self)
let rebound = unsafe UnsafeMutablePointer<Element>(rp)
unsafe rebound.update(repeating: repeatedValue, count: count)
Builtin.rebindMemory(rp, binding)
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update<S: Sequence>(
from source: S
) -> (unwritten: S.Iterator, index: Index)
where S.Element == Element, Element: BitwiseCopyable {
var iterator = source.makeIterator()
let index = update(from: &iterator)
return (iterator, index)
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update(
from elements: inout some IteratorProtocol<Element>
) -> Index {
var index = 0
while index < _count {
guard let element = elements.next() else { break }
unsafe self[unchecked: index] = element
index &+= 1
}
return index
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update(
fromContentsOf source: some Collection<Element>
) -> Index where Element: BitwiseCopyable {
let updated = source.withContiguousStorageIfAvailable {
self.update(fromContentsOf: unsafe Span(_unsafeElements: $0))
}
if let updated {
return updated
}
//TODO: use _copyContents here
var iterator = source.makeIterator()
let index = update(from: &iterator)
_precondition(
iterator.next() == nil,
"destination buffer view cannot contain every element from source."
)
return index
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update(
fromContentsOf source: Span<Element>
) -> Index where Element: BitwiseCopyable {
guard !source.isEmpty else { return 0 }
_precondition(
source.count <= self.count,
"destination span cannot contain every element from source."
)
unsafe source.withUnsafeBufferPointer {
unsafe _start().copyMemory(
from: $0.baseAddress!,
byteCount: $0.count &* MemoryLayout<Element>.stride
)
}
return source.count
}
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func update(
fromContentsOf source: borrowing MutableSpan<Element>
) -> Index where Element: BitwiseCopyable {
update(fromContentsOf: source.span)
}
}
// MARK: sub-spans
@@ -712,13 +448,31 @@ extension MutableSpan where Element: ~Copyable {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(_ bounds: Range<Index>) -> Self {
mutating public func _mutatingExtracting(_ bounds: Range<Index>) -> Self {
_precondition(
UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) &&
UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count),
"Index range out of bounds"
)
return unsafe extracting(unchecked: bounds)
return unsafe _mutatingExtracting(unchecked: bounds)
}
@available(*, deprecated, renamed: "_mutatingExtracting(_:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(_ bounds: Range<Index>) -> Self {
_mutatingExtracting(bounds)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(_ bounds: Range<Index>) -> Self {
_precondition(
UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) &&
UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count),
"Index range out of bounds"
)
return unsafe _consumingExtracting(unchecked: bounds)
}
/// Constructs a new span over the items within the supplied range of
@@ -739,13 +493,31 @@ extension MutableSpan where Element: ~Copyable {
@unsafe
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(unchecked bounds: Range<Index>) -> Self {
mutating public func _mutatingExtracting(unchecked bounds: Range<Index>) -> Self {
let delta = bounds.lowerBound &* MemoryLayout<Element>.stride
let newStart = unsafe _pointer?.advanced(by: delta)
let newSpan = unsafe Self(_unchecked: newStart, count: bounds.count)
return unsafe _overrideLifetime(newSpan, mutating: &self)
}
@unsafe
@available(*, deprecated, renamed: "_mutatingExtracting(unchecked:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(unchecked bounds: Range<Index>) -> Self {
unsafe _mutatingExtracting(unchecked: bounds)
}
@unsafe
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(unchecked bounds: Range<Index>) -> Self {
let delta = bounds.lowerBound &* MemoryLayout<Element>.stride
let newStart = unsafe _pointer?.advanced(by: delta)
let newSpan = unsafe Self(_unchecked: newStart, count: bounds.count)
return unsafe _overrideLifetime(newSpan, copying: self)
}
/// Constructs a new span over the items within the supplied range of
/// positions within this span.
///
@@ -761,10 +533,27 @@ extension MutableSpan where Element: ~Copyable {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func _mutatingExtracting(
_ bounds: some RangeExpression<Index>
) -> Self {
_mutatingExtracting(bounds.relative(to: indices))
}
@available(*, deprecated, renamed: "_mutatingExtracting(_:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(
_ bounds: some RangeExpression<Index>
) -> Self {
extracting(bounds.relative(to: indices))
_mutatingExtracting(bounds)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(
_ bounds: some RangeExpression<Index>
) -> Self {
_consumingExtracting(bounds.relative(to: indices))
}
/// Constructs a new span over the items within the supplied range of
@@ -785,13 +574,35 @@ extension MutableSpan where Element: ~Copyable {
@unsafe
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(
mutating public func _mutatingExtracting(
unchecked bounds: ClosedRange<Index>
) -> Self {
let range = unsafe Range(
_uncheckedBounds: (bounds.lowerBound, bounds.upperBound&+1)
_uncheckedBounds: (bounds.lowerBound, bounds.upperBound + 1)
)
return unsafe extracting(unchecked: range)
return unsafe _mutatingExtracting(unchecked: range)
}
@unsafe
@available(*, deprecated, renamed: "_mutatingExtracting(unchecked:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(
unchecked bounds: ClosedRange<Index>
) -> Self {
unsafe _mutatingExtracting(unchecked: bounds)
}
@unsafe
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(
unchecked bounds: ClosedRange<Index>
) -> Self {
let range = unsafe Range(
_uncheckedBounds: (bounds.lowerBound, bounds.upperBound + 1)
)
return unsafe _consumingExtracting(unchecked: range)
}
/// Constructs a new span over all the items of this span.
@@ -805,10 +616,23 @@ extension MutableSpan where Element: ~Copyable {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(_: UnboundedRange) -> Self {
mutating public func _mutatingExtracting(_: UnboundedRange) -> Self {
let newSpan = unsafe Self(_unchecked: _pointer, count: _count)
return unsafe _overrideLifetime(newSpan, mutating: &self)
}
@available(*, deprecated, renamed: "_mutatingExtracting(_:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(_: UnboundedRange) -> Self {
_mutatingExtracting(...)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(_: UnboundedRange) -> Self {
self
}
}
// MARK: prefixes and suffixes
@@ -833,7 +657,7 @@ extension MutableSpan where Element: ~Copyable {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(first maxLength: Int) -> Self {
mutating public func _mutatingExtracting(first maxLength: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(maxLength >= 0, "Can't have a prefix of negative length")
let newCount = min(maxLength, count)
@@ -844,6 +668,26 @@ extension MutableSpan where Element: ~Copyable {
#endif
}
@available(*, deprecated, renamed: "_mutatingExtracting(first:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(first maxLength: Int) -> Self {
_mutatingExtracting(first: maxLength)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(first maxLength: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(maxLength >= 0, "Can't have a prefix of negative length")
let newCount = min(maxLength, count)
let newSpan = unsafe Self(_unchecked: _pointer, count: newCount)
return unsafe _overrideLifetime(newSpan, copying: self)
#else
fatalError("Unsupported compiler")
#endif
}
/// Returns a span over all but the given number of trailing elements.
///
/// If the number of elements to drop exceeds the number of elements in
@@ -860,7 +704,7 @@ extension MutableSpan where Element: ~Copyable {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(droppingLast k: Int) -> Self {
mutating public func _mutatingExtracting(droppingLast k: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(k >= 0, "Can't drop a negative number of elements")
let droppedCount = min(k, count)
@@ -872,6 +716,27 @@ extension MutableSpan where Element: ~Copyable {
#endif
}
@available(*, deprecated, renamed: "_mutatingExtracting(droppingLast:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(droppingLast k: Int) -> Self {
_mutatingExtracting(droppingLast: k)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(droppingLast k: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(k >= 0, "Can't drop a negative number of elements")
let droppedCount = min(k, count)
let newCount = count &- droppedCount
let newSpan = unsafe Self(_unchecked: _pointer, count: newCount)
return unsafe _overrideLifetime(newSpan, copying: self)
#else
fatalError("Unsupported compiler")
#endif
}
/// Returns a span containing the final elements of the span,
/// up to the given maximum length.
///
@@ -889,7 +754,7 @@ extension MutableSpan where Element: ~Copyable {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(last maxLength: Int) -> Self {
mutating public func _mutatingExtracting(last maxLength: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(maxLength >= 0, "Can't have a suffix of negative length")
let newCount = min(maxLength, count)
@@ -902,6 +767,28 @@ extension MutableSpan where Element: ~Copyable {
#endif
}
@available(*, deprecated, renamed: "_mutatingExtracting(last:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(last maxLength: Int) -> Self {
_mutatingExtracting(last: maxLength)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(last maxLength: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(maxLength >= 0, "Can't have a suffix of negative length")
let newCount = min(maxLength, count)
let offset = (count &- newCount) * MemoryLayout<Element>.stride
let newStart = unsafe _pointer?.advanced(by: offset)
let newSpan = unsafe Self(_unchecked: newStart, count: newCount)
return unsafe _overrideLifetime(newSpan, copying: self)
#else
fatalError("Unsupported compiler")
#endif
}
/// Returns a span over all but the given number of initial elements.
///
/// If the number of elements to drop exceeds the number of elements in
@@ -918,7 +805,7 @@ extension MutableSpan where Element: ~Copyable {
/// - Complexity: O(1)
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(droppingFirst k: Int) -> Self {
mutating public func _mutatingExtracting(droppingFirst k: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(k >= 0, "Can't drop a negative number of elements")
let droppedCount = min(k, count)
@@ -929,6 +816,29 @@ extension MutableSpan where Element: ~Copyable {
return unsafe _overrideLifetime(newSpan, mutating: &self)
#else
fatalError("Unsupported compiler")
#endif
}
@available(*, deprecated, renamed: "_mutatingExtracting(droppingFirst:)")
@_alwaysEmitIntoClient
@lifetime(&self)
mutating public func extracting(droppingFirst k: Int) -> Self {
_mutatingExtracting(droppingFirst: k)
}
@_alwaysEmitIntoClient
@lifetime(copy self)
consuming public func _consumingExtracting(droppingFirst k: Int) -> Self {
#if compiler(>=5.3) && hasFeature(SendableCompletionHandlers)
_precondition(k >= 0, "Can't drop a negative number of elements")
let droppedCount = min(k, count)
let offset = droppedCount * MemoryLayout<Element>.stride
let newStart = unsafe _pointer?.advanced(by: offset)
let newCount = count &- droppedCount
let newSpan = unsafe Self(_unchecked: newStart, count: newCount)
return unsafe _overrideLifetime(newSpan, copying: self)
#else
fatalError("Unsupported compiler")
#endif
}
}

View File

@@ -0,0 +1,380 @@
//===--- OutputRawSpan.swift ----------------------------------------------===//
//
// 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 SPAN_COMPATIBILITY_STUB
import Swift
#endif
// OutputRawSpan is a reference to a contiguous region of memory which starts
// some number of initialized bytes, followed by uninitialized memory.
@safe
@frozen
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
public struct OutputRawSpan: ~Copyable, ~Escapable {
@usableFromInline
internal let _pointer: UnsafeMutableRawPointer?
public let capacity: Int
@usableFromInline
internal var _count: Int
/// Create an OutputRawSpan with zero capacity
@_alwaysEmitIntoClient
@lifetime(immortal)
public init() {
unsafe _pointer = nil
capacity = 0
_count = 0
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputRawSpan: @unchecked Sendable {}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputRawSpan {
@_alwaysEmitIntoClient
@_transparent
@unsafe
internal func _start() -> UnsafeMutableRawPointer {
unsafe _pointer._unsafelyUnwrappedUnchecked
}
@_alwaysEmitIntoClient
@_transparent
@unsafe
internal func _tail() -> UnsafeMutableRawPointer {
// NOTE: `_pointer` must be known to be not-nil.
unsafe _start().advanced(by: _count)
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputRawSpan {
/// The number of initialized bytes in this span.
@_alwaysEmitIntoClient
public var byteCount: Int { _count }
/// The number of additional bytes that can be appended to this span.
@_alwaysEmitIntoClient
public var freeCapacity: Int { capacity &- _count }
/// A Boolean value indicating whether the span is empty.
@_alwaysEmitIntoClient
public var isEmpty: Bool { _count == 0 }
/// A Boolean value indicating whether the span is full.
@_alwaysEmitIntoClient
public var isFull: Bool { _count == capacity }
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputRawSpan {
@unsafe
@_alwaysEmitIntoClient
@lifetime(borrow buffer)
internal init(
_uncheckedBuffer buffer: UnsafeMutableRawBufferPointer,
initializedCount: Int
) {
unsafe _pointer = .init(buffer.baseAddress)
capacity = buffer.count
_count = initializedCount
}
/// Unsafely create an OutputRawSpan over partly-initialized memory.
///
/// The memory in `buffer` must remain valid throughout the lifetime
/// of the newly-created `OutputRawSpan`. Its prefix must contain
/// `initializedCount` initialized bytes, followed by uninitialized
/// memory.
///
/// - Parameters:
/// - buffer: an `UnsafeMutableBufferPointer` to be initialized
/// - initializedCount: the number of initialized bytes
/// at the beginning of `buffer`.
@unsafe
@_alwaysEmitIntoClient
@lifetime(borrow buffer)
public init(
buffer: UnsafeMutableRawBufferPointer,
initializedCount: Int
) {
if let baseAddress = buffer.baseAddress {
_precondition(
unsafe baseAddress.advanced(by: buffer.count) >= baseAddress,
"Buffer must not wrap around the address space"
)
}
_precondition(
0 <= initializedCount && initializedCount <= buffer.count,
"OutputSpan count is not within capacity"
)
unsafe self.init(
_uncheckedBuffer: buffer, initializedCount: initializedCount
)
}
/// Unsafely create an OutputRawSpan over partly-initialized memory.
///
/// The memory in `buffer` must remain valid throughout the lifetime
/// of the newly-created `OutputRawSpan`. Its prefix must contain
/// `initializedCount` initialized bytes, followed by uninitialized
/// memory.
///
/// - Parameters:
/// - buffer: an `UnsafeMutableBufferPointer` to be initialized
/// - initializedCount: the number of initialized bytes
/// at the beginning of `buffer`.
@unsafe
@_alwaysEmitIntoClient
@lifetime(borrow buffer)
public init(
buffer: borrowing Slice<UnsafeMutableRawBufferPointer>,
initializedCount: Int
) {
let rebased = unsafe UnsafeMutableRawBufferPointer(rebasing: buffer)
let os = unsafe OutputRawSpan(
buffer: rebased, initializedCount: initializedCount
)
self = unsafe _overrideLifetime(os, borrowing: buffer)
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputRawSpan {
/// Append a single byte to this span.
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func append(_ value: UInt8) {
_precondition(_count < capacity, "OutputRawSpan capacity overflow")
unsafe _tail().storeBytes(of: value, as: UInt8.self)
_count &+= 1
}
/// Remove the last byte from this span.
@_alwaysEmitIntoClient
@discardableResult
@lifetime(self: copy self)
public mutating func removeLast() -> UInt8 {
_precondition(!isEmpty, "OutputRawSpan underflow")
_count &-= 1
return unsafe _tail().load(as: UInt8.self)
}
/// Remove the last N elements, returning the memory they occupy
/// to the uninitialized state.
///
/// `n` must not be greater than `count`
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func removeLast(_ k: Int) {
_precondition(k >= 0, "Can't remove a negative number of bytes")
_precondition(k <= _count, "OutputRawSpan underflow")
_count &-= k
}
/// Remove all this span's elements and return its memory
/// to the uninitialized state.
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func removeAll() {
// TODO: Consider an option to zero the `_count` bytes being removed.
_count = 0
}
}
//MARK: bulk-append functions
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputRawSpan {
/// Appends the given value's bytes to this span's bytes.
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func append<T: BitwiseCopyable>(_ value: T, as type: T.Type) {
_precondition(
MemoryLayout<T>.size <= freeCapacity, "OutputRawSpan capacity overflow"
)
unsafe _tail().initializeMemory(as: T.self, to: value)
_count &+= MemoryLayout<T>.size
}
/// Appends the given value's bytes repeatedly to this span's bytes.
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func append<T: BitwiseCopyable>(
repeating repeatedValue: T, count: Int, as type: T.Type
) {
let total = count * MemoryLayout<T>.stride
_precondition(total <= freeCapacity, "OutputRawSpan capacity overflow")
unsafe _tail().initializeMemory(
as: T.self, repeating: repeatedValue, count: count
)
_count &+= total
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputRawSpan {
/// Borrow the underlying initialized memory for read-only access.
@_alwaysEmitIntoClient
public var bytes: RawSpan {
@lifetime(borrow self)
borrowing get {
let buffer = unsafe UnsafeRawBufferPointer(start: _pointer, count: _count)
let span = unsafe RawSpan(_unsafeBytes: buffer)
return unsafe _overrideLifetime(span, borrowing: self)
}
}
/// Exclusively borrow the underlying initialized memory for mutation.
@_alwaysEmitIntoClient
public var mutableBytes: MutableRawSpan {
@lifetime(&self)
mutating get {
let buffer = unsafe UnsafeMutableRawBufferPointer(
start: _pointer, count: _count
)
let span = unsafe MutableRawSpan(_unsafeBytes: buffer)
return unsafe _overrideLifetime(span, mutating: &self)
}
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputRawSpan {
/// Call the given closure with the unsafe buffer pointer addressed by this
/// OutputRawSpan and a mutable reference to its count of initialized bytes.
///
/// This method provides a way to process or populate an `OutputRawSpan` using
/// unsafe operations, such as dispatching to code written in legacy
/// (memory-unsafe) languages.
///
/// The supplied closure may process the buffer in any way it wants; however,
/// when it finishes (whether by returning or throwing), it must leave the
/// buffer in a state that satisfies the invariants of the output span:
///
/// 1. The inout integer passed in as the second argument must be the exact
/// number of initialized bytes in the buffer passed in as the first
/// argument.
/// 2. These initialized elements must be located in a single contiguous
/// region starting at the beginning of the buffer. The rest of the buffer
/// must hold uninitialized memory.
///
/// This function cannot verify these two invariants, and therefore
/// this is an unsafe operation. Violating the invariants of `OutputRawSpan`
/// may result in undefined behavior.
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func withUnsafeMutableBytes<E: Error, R: ~Copyable>(
_ body: (
UnsafeMutableRawBufferPointer,
_ initializedCount: inout Int
) throws(E) -> R
) throws(E) -> R {
guard let start = unsafe _pointer, capacity > 0 else {
let buffer = UnsafeMutableRawBufferPointer(_empty: ())
var initializedCount = 0
defer {
_precondition(initializedCount == 0, "OutputRawSpan capacity overflow")
}
return unsafe try body(buffer, &initializedCount)
}
#if SPAN_COMPATIBILITY_STUB
let buffer = unsafe UnsafeMutableRawBufferPointer(
start: start, count: capacity
)
#else
let buffer = unsafe UnsafeMutableRawBufferPointer(
_uncheckedStart: start, count: capacity
)
#endif
var initializedCount = _count
defer {
_precondition(
0 <= initializedCount && initializedCount <= capacity,
"OutputRawSpan capacity overflow"
)
_count = initializedCount
}
return unsafe try body(buffer, &initializedCount)
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputRawSpan {
/// Consume the output span (relinquishing its control over the buffer it is
/// addressing), and return the number of initialized bytes in it.
///
/// This method should be invoked in the scope where the `OutputRawSpan` was
/// created, when it is time to commit the contents of the updated buffer
/// back into the construct being initialized.
///
/// The context that created the output span is expected to remember what
/// memory region the span is addressing. This consuming method expects to
/// receive a copy of the same buffer pointer as a (loose) proof of ownership.
///
/// - Parameter buffer: The buffer we expect the `OutputRawSpan` to reference.
/// This must be the same region of memory passed to
/// the `OutputRawSpan` initializer.
/// - Returns: The number of initialized bytes in the same buffer, as
/// tracked by the consumed `OutputRawSpan` instance.
@unsafe
@_alwaysEmitIntoClient
public consuming func finalize(
for buffer: UnsafeMutableRawBufferPointer
) -> Int {
_precondition(
unsafe buffer.baseAddress == self._pointer
&& buffer.count == self.capacity,
"OutputRawSpan identity mismatch")
return _count
}
/// Consume the output span (relinquishing its control over the buffer it is
/// addressing), and return the number of initialized bytes in it.
///
/// This method should be invoked in the scope where the `OutputRawSpan` was
/// created, when it is time to commit the contents of the updated buffer
/// back into the construct being initialized.
///
/// The context that created the output span is expected to remember what
/// memory region the span is addressing. This consuming method expects to
/// receive a copy of the same buffer pointer as a (loose) proof of ownership.
///
/// - Parameter buffer: The buffer we expect the `OutputRawSpan` to reference.
/// This must be the same region of memory passed to
/// the `OutputRawSpan` initializer.
/// - Returns: The number of initialized bytes in the same buffer, as
/// tracked by the consumed `OutputRawSpan` instance.
@unsafe
@_alwaysEmitIntoClient
public consuming func finalize(
for buffer: Slice<UnsafeMutableRawBufferPointer>
) -> Int {
let rebased = unsafe UnsafeMutableRawBufferPointer(rebasing: buffer)
return unsafe self.finalize(for: rebased)
}
}

View File

@@ -0,0 +1,497 @@
//===--- OutputSpan.swift -------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 - 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 SPAN_COMPATIBILITY_STUB
import Swift
#endif
// `OutputSpan` is a reference to a contiguous region of memory that starts with
// some number of initialized `Element` instances followed by uninitialized
// memory. It provides operations to access the items it stores, as well as to
// add new elements and to remove existing ones.
@safe
@frozen
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
public struct OutputSpan<Element: ~Copyable>: ~Copyable, ~Escapable {
@usableFromInline
internal let _pointer: UnsafeMutableRawPointer?
public let capacity: Int
@usableFromInline
internal var _count: Int
@_alwaysEmitIntoClient
@inlinable
deinit {
if _count > 0 {
unsafe _start().withMemoryRebound(
to: Element.self, capacity: _count
) {
[ workaround = _count ] in
_ = unsafe $0.deinitialize(count: workaround)
}
}
}
/// Create an OutputSpan with zero capacity
@_alwaysEmitIntoClient
@lifetime(immortal)
public init() {
unsafe _pointer = nil
capacity = 0
_count = 0
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputSpan: @unchecked Sendable where Element: Sendable & ~Copyable {}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputSpan where Element: ~Copyable {
@_alwaysEmitIntoClient
@_transparent
@unsafe
internal func _start() -> UnsafeMutableRawPointer {
unsafe _pointer._unsafelyUnwrappedUnchecked
}
@_alwaysEmitIntoClient
@_transparent
@unsafe
internal func _tail() -> UnsafeMutableRawPointer {
// NOTE: `_pointer` must be known to be not-nil.
unsafe _start().advanced(by: _count &* MemoryLayout<Element>.stride)
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputSpan where Element: ~Copyable {
/// The number of initialized elements in this span.
@_alwaysEmitIntoClient
public var count: Int { _count }
/// The number of additional elements that can be added to this span.
@_alwaysEmitIntoClient
public var freeCapacity: Int { capacity &- _count }
/// A Boolean value indicating whether the span is empty.
@_alwaysEmitIntoClient
public var isEmpty: Bool { _count == 0 }
/// A Boolean value indicating whether the span is full.
@_alwaysEmitIntoClient
public var isFull: Bool { _count == capacity }
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputSpan where Element: ~Copyable {
@unsafe
@_alwaysEmitIntoClient
@lifetime(borrow buffer)
@usableFromInline
internal init(
_uncheckedBuffer buffer: UnsafeMutableBufferPointer<Element>,
initializedCount: Int
) {
unsafe _pointer = .init(buffer.baseAddress)
capacity = buffer.count
_count = initializedCount
}
/// Unsafely create an OutputSpan over partly-initialized memory.
///
/// The memory in `buffer` must remain valid throughout the lifetime
/// of the newly-created `OutputSpan`. Its prefix must contain
/// `initializedCount` initialized instances, followed by uninitialized
/// memory. The default value of `initializedCount` is 0, representing
/// the common case of a completely uninitialized `buffer`.
///
/// - Parameters:
/// - buffer: an `UnsafeMutableBufferPointer` to be initialized
/// - initializedCount: the number of initialized elements
/// at the beginning of `buffer`.
@unsafe
@_alwaysEmitIntoClient
@lifetime(borrow buffer)
public init(
buffer: UnsafeMutableBufferPointer<Element>,
initializedCount: Int
) {
_precondition(buffer._isWellAligned(), "Misaligned OutputSpan")
if let baseAddress = buffer.baseAddress {
_precondition(
unsafe baseAddress.advanced(by: buffer.count) >= baseAddress,
"Buffer must not wrap around the address space"
)
}
_precondition(
0 <= initializedCount && initializedCount <= buffer.count,
"OutputSpan count is not within capacity"
)
unsafe self.init(
_uncheckedBuffer: buffer, initializedCount: initializedCount
)
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputSpan {
/// Unsafely create an OutputSpan over partly-initialized memory.
///
/// The memory in `buffer` must remain valid throughout the lifetime
/// of the newly-created `OutputSpan`. Its prefix must contain
/// `initializedCount` initialized instances, followed by uninitialized
/// memory. The default value of `initializedCount` is 0, representing
/// the common case of a completely uninitialized `buffer`.
///
/// - Parameters:
/// - buffer: an `UnsafeMutableBufferPointer` to be initialized
/// - initializedCount: the number of initialized elements
/// at the beginning of `buffer`.
@unsafe
@_alwaysEmitIntoClient
@lifetime(borrow buffer)
public init(
buffer: borrowing Slice<UnsafeMutableBufferPointer<Element>>,
initializedCount: Int
) {
let rebased = unsafe UnsafeMutableBufferPointer(rebasing: buffer)
let os = unsafe OutputSpan(
buffer: rebased, initializedCount: initializedCount
)
self = unsafe _overrideLifetime(os, borrowing: buffer)
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputSpan where Element: ~Copyable {
/// The type that represents an initialized position in an `OutputSpan`.
public typealias Index = Int
/// The range of initialized positions for this `OutputSpan`.
@_alwaysEmitIntoClient
public var indices: Range<Index> {
unsafe Range(_uncheckedBounds: (0, _count))
}
/// Accesses the element at the specified position.
///
/// - Parameter index: A valid index into this span.
///
/// - Complexity: O(1)
@_alwaysEmitIntoClient
public subscript(_ index: Index) -> Element {
unsafeAddress {
_precondition(indices.contains(index), "index out of bounds")
return unsafe UnsafePointer(_unsafeAddressOfElement(unchecked: index))
}
@lifetime(self: copy self)
unsafeMutableAddress {
_precondition(indices.contains(index), "index out of bounds")
return unsafe _unsafeAddressOfElement(unchecked: index)
}
}
/// Accesses the element at the specified position.
///
/// This subscript does not validate `position`; this is an unsafe operation.
///
/// - Parameter index: A valid index into this span.
///
/// - Complexity: O(1)
@unsafe
@_alwaysEmitIntoClient
public subscript(unchecked index: Index) -> Element {
unsafeAddress {
unsafe UnsafePointer(_unsafeAddressOfElement(unchecked: index))
}
@lifetime(self: copy self)
unsafeMutableAddress {
unsafe _unsafeAddressOfElement(unchecked: index)
}
}
@unsafe
@_alwaysEmitIntoClient
internal func _unsafeAddressOfElement(
unchecked index: Index
) -> UnsafeMutablePointer<Element> {
let elementOffset = index &* MemoryLayout<Element>.stride
let address = unsafe _start().advanced(by: elementOffset)
return unsafe address.assumingMemoryBound(to: Element.self)
}
/// Exchange the elements at the two given offsets
///
/// - Parameter i: A valid index into this span.
/// - Parameter j: A valid index into this span.
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func swapAt(_ i: Index, _ j: Index) {
_precondition(indices.contains(Index(i)))
_precondition(indices.contains(Index(j)))
unsafe swapAt(unchecked: i, unchecked: j)
}
/// Exchange the elements at the two given offsets
///
/// This subscript does not validate `i` or `j`; this is an unsafe operation.
///
/// - Parameter i: A valid index into this span.
/// - Parameter j: A valid index into this span.
@unsafe
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func swapAt(unchecked i: Index, unchecked j: Index) {
let pi = unsafe _unsafeAddressOfElement(unchecked: i)
let pj = unsafe _unsafeAddressOfElement(unchecked: j)
let temporary = unsafe pi.move()
unsafe pi.initialize(to: pj.move())
unsafe pj.initialize(to: consume temporary)
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputSpan where Element: ~Copyable {
/// Append a single element to this span.
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func append(_ value: consuming Element) {
_precondition(_count < capacity, "OutputSpan capacity overflow")
unsafe _tail().initializeMemory(as: Element.self, to: value)
_count &+= 1
}
/// Remove the last initialized element from this span.
///
/// Returns the last element. The `OutputSpan` must not be empty.
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func removeLast() -> Element {
_precondition(!isEmpty, "OutputSpan underflow")
_count &-= 1
return unsafe _tail().withMemoryRebound(to: Element.self, capacity: 1) {
unsafe $0.move()
}
}
/// Remove the last N elements of this span, returning the memory they occupy
/// to the uninitialized state.
///
/// `n` must not be greater than `count`
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func removeLast(_ k: Int) {
_precondition(k >= 0, "Can't remove a negative number of elements")
_precondition(k <= _count, "OutputSpan underflow")
_count &-= k
unsafe _tail().withMemoryRebound(to: Element.self, capacity: k) {
_ = unsafe $0.deinitialize(count: k)
}
}
/// Remove all this span's elements and return its memory
/// to the uninitialized state.
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func removeAll() {
_ = unsafe _start().withMemoryRebound(to: Element.self, capacity: _count) {
unsafe $0.deinitialize(count: _count)
}
_count = 0
}
}
//MARK: bulk-append functions
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputSpan {
/// Repeatedly append an element to this span.
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func append(repeating repeatedValue: Element, count: Int) {
_precondition(count <= freeCapacity, "OutputSpan capacity overflow")
unsafe _tail().initializeMemory(
as: Element.self, repeating: repeatedValue, count: count
)
_count &+= count
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputSpan where Element: ~Copyable {
/// Borrow the underlying initialized memory for read-only access.
@_alwaysEmitIntoClient
public var span: Span<Element> {
@lifetime(borrow self)
borrowing get {
let pointer = unsafe _pointer?.assumingMemoryBound(to: Element.self)
let buffer = unsafe UnsafeBufferPointer(start: pointer, count: _count)
let span = unsafe Span(_unsafeElements: buffer)
return unsafe _overrideLifetime(span, borrowing: self)
}
}
/// Exclusively borrow the underlying initialized memory for mutation.
@_alwaysEmitIntoClient
public var mutableSpan: MutableSpan<Element> {
@lifetime(&self)
mutating get {
let pointer = unsafe _pointer?.assumingMemoryBound(to: Element.self)
let buffer = unsafe UnsafeMutableBufferPointer(
start: pointer, count: _count
)
let span = unsafe MutableSpan(_unsafeElements: buffer)
return unsafe _overrideLifetime(span, mutating: &self)
}
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputSpan where Element: ~Copyable {
/// Call the given closure with the unsafe buffer pointer addressed by this
/// OutputSpan and a mutable reference to its count of initialized elements.
///
/// This method provides a way to process or populate an `OutputSpan` using
/// unsafe operations, such as dispatching to code written in legacy
/// (memory-unsafe) languages.
///
/// The supplied closure may process the buffer in any way it wants; however,
/// when it finishes (whether by returning or throwing), it must leave the
/// buffer in a state that satisfies the invariants of the output span:
///
/// 1. The inout integer passed in as the second argument must be the exact
/// number of initialized items in the buffer passed in as the first
/// argument.
/// 2. These initialized elements must be located in a single contiguous
/// region starting at the beginning of the buffer. The rest of the buffer
/// must hold uninitialized memory.
///
/// This function cannot verify these two invariants, and therefore
/// this is an unsafe operation. Violating the invariants of `OutputSpan`
/// may result in undefined behavior.
@_alwaysEmitIntoClient
@lifetime(self: copy self)
public mutating func withUnsafeMutableBufferPointer<E: Error, R: ~Copyable>(
_ body: (
UnsafeMutableBufferPointer<Element>,
_ initializedCount: inout Int
) throws(E) -> R
) throws(E) -> R {
guard let start = unsafe _pointer, capacity > 0 else {
let buffer = UnsafeMutableBufferPointer<Element>(_empty: ())
var initializedCount = 0
defer {
_precondition(initializedCount == 0, "OutputSpan capacity overflow")
}
return unsafe try body(buffer, &initializedCount)
}
// bind memory by hand to sidestep alignment concerns
let binding = Builtin.bindMemory(
start._rawValue, capacity._builtinWordValue, Element.self
)
defer { Builtin.rebindMemory(start._rawValue, binding) }
#if SPAN_COMPATIBILITY_STUB
let buffer = unsafe UnsafeMutableBufferPointer<Element>(
start: .init(start._rawValue), count: capacity
)
#else
let buffer = unsafe UnsafeMutableBufferPointer<Element>(
_uncheckedStart: .init(start._rawValue), count: capacity
)
#endif
var initializedCount = self._count
defer {
_precondition(
0 <= initializedCount && initializedCount <= capacity,
"OutputSpan capacity overflow"
)
self._count = initializedCount
}
return unsafe try body(buffer, &initializedCount)
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputSpan where Element: ~Copyable {
/// Consume the output span and return the number of initialized elements.
///
/// This method should be invoked in the scope where the `OutputSpan` was
/// created, when it is time to commit the contents of the updated buffer
/// back into the construct being initialized.
///
/// The context that created the output span is expected to remember what
/// memory region the span is addressing. This consuming method expects to
/// receive a copy of the same buffer pointer as a (loose) proof of ownership.
///
/// - Parameter buffer: The buffer we expect the `OutputSpan` to reference.
/// This must be the same region of memory passed to
/// the `OutputSpan` initializer.
/// - Returns: The number of initialized elements in the same buffer, as
/// tracked by the consumed `OutputSpan` instance.
@unsafe
@_alwaysEmitIntoClient
public consuming func finalize(
for buffer: UnsafeMutableBufferPointer<Element>
) -> Int {
_precondition(
unsafe UnsafeMutableRawPointer(buffer.baseAddress) == self._pointer
&& buffer.count == self.capacity,
"OutputSpan identity mismatch"
)
let count = self._count
discard self
return count
}
}
@available(SwiftCompatibilitySpan 5.0, *)
@_originallyDefinedIn(module: "Swift;CompatibilitySpan", SwiftCompatibilitySpan 6.2)
extension OutputSpan {
/// Consume the output span and return the number of initialized elements.
///
/// This method should be invoked in the scope where the `OutputSpan` was
/// created, when it is time to commit the contents of the updated buffer
/// back into the construct being initialized.
///
/// The context that created the output span is expected to remember what
/// memory region the span is addressing. This consuming method expects to
/// receive a copy of the same buffer pointer as a (loose) proof of ownership.
///
/// - Parameter buffer: The buffer we expect the `OutputSpan` to reference.
/// This must be the same region of memory passed to
/// the `OutputSpan` initializer.
/// - Returns: The number of initialized elements in the same buffer, as
/// tracked by the consumed `OutputSpan` instance.
@unsafe
@_alwaysEmitIntoClient
public consuming func finalize(
for buffer: Slice<UnsafeMutableBufferPointer<Element>>
) -> Int {
unsafe finalize(for: UnsafeMutableBufferPointer(rebasing: buffer))
}
}

View File

@@ -795,6 +795,15 @@ extension Unsafe${Mutable}BufferPointer:
% end
}
extension Unsafe${Mutable}BufferPointer where Element: ~Copyable {
@safe
@_alwaysEmitIntoClient
public func _isWellAligned() -> Bool {
guard let p = baseAddress else { return true }
return p._isWellAligned()
}
}
extension Unsafe${Mutable}BufferPointer {
% if not Mutable:
/// Creates a buffer over the same memory as the given buffer slice.

View File

@@ -469,6 +469,13 @@ extension UnsafePointer where Pointee: ~Copyable {
}
}
extension UnsafePointer where Pointee: ~Copyable {
@safe
@_alwaysEmitIntoClient
public func _isWellAligned() -> Bool {
(Int(bitPattern: self) & (MemoryLayout<Pointee>.alignment &- 1)) == 0
}
}
/// A pointer for accessing and manipulating data of a
/// specific type.
@@ -1382,3 +1389,11 @@ extension UnsafeMutablePointer where Pointee: ~Copyable {
)._unsafelyUnwrappedUnchecked
}
}
extension UnsafeMutablePointer where Pointee: ~Copyable {
@safe
@_alwaysEmitIntoClient
public func _isWellAligned() -> Bool {
(Int(bitPattern: self) & (MemoryLayout<Pointee>.alignment &- 1)) == 0
}
}

View File

@@ -100,6 +100,17 @@ public struct Unsafe${Mutable}RawBufferPointer {
@usableFromInline
internal let _position, _end: Unsafe${Mutable}RawPointer?
// This works around _debugPrecondition() impacting the performance of
// optimized code. (rdar://72246338)
@_alwaysEmitIntoClient
internal init(
@_nonEphemeral _uncheckedStart start: Unsafe${Mutable}RawPointer?,
count: Int
) {
unsafe _position = start
unsafe _end = start.map { unsafe $0 + _assumeNonNegative(count) }
}
/// Creates a buffer over the specified number of contiguous bytes starting
/// at the given pointer.
///
@@ -116,9 +127,14 @@ public struct Unsafe${Mutable}RawBufferPointer {
_debugPrecondition(count >= 0, "${Self} with negative count")
_debugPrecondition(unsafe count == 0 || start != nil,
"${Self} has a nil start and nonzero count")
unsafe self.init(_uncheckedStart: start, count: count)
}
unsafe _position = start
unsafe _end = start.map { unsafe $0 + _assumeNonNegative(count) }
@safe
@_alwaysEmitIntoClient
public init(_empty: ()) {
unsafe _position = nil
unsafe _end = nil
}
}

View File

@@ -6,6 +6,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND DEFINED SWIFT_STDLIB_LIBRARY_BUILD_TY
FakeStdlib.swift
../../public/core/Span/MutableRawSpan.swift
../../public/core/Span/MutableSpan.swift
../../public/core/Span/OutputRawSpan.swift
../../public/core/Span/OutputSpan.swift
../../public/core/Span/RawSpan.swift
../../public/core/Span/Span.swift

View File

@@ -927,6 +927,23 @@ Added: _$ss14MutableRawSpanVN
Added: _$sSS8UTF8ViewV4spans4SpanVys5UInt8VGvg
Added: _$sSs8UTF8ViewV4spans4SpanVys5UInt8VGvg
// OutputSpan
Added: _$ss10OutputSpanVMa
Added: _$ss10OutputSpanVMn
Added: _$ss10OutputSpanVsRi_zrlE6_countSivM
Added: _$ss10OutputSpanVsRi_zrlE6_countSivg
Added: _$ss10OutputSpanVsRi_zrlE6_countSivs
Added: _$ss10OutputSpanVsRi_zrlE8_pointerSvSgvg
Added: _$ss10OutputSpanVsRi_zrlE8capacitySivg
Added: _$ss13OutputRawSpanV6_countSivM
Added: _$ss13OutputRawSpanV6_countSivg
Added: _$ss13OutputRawSpanV6_countSivs
Added: _$ss13OutputRawSpanV8_pointerSvSgvg
Added: _$ss13OutputRawSpanV8capacitySivg
Added: _$ss13OutputRawSpanVMa
Added: _$ss13OutputRawSpanVMn
Added: _$ss13OutputRawSpanVN
// _SwiftifyInfo enum for _SwiftifyImports macro
Added: _$ss13_SwiftifyExprO5paramyABSicABmFWC
Added: _$ss13_SwiftifyExprO6returnyA2BmFWC
@@ -1048,6 +1065,21 @@ Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss14M
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss14MutableRawSpanVMa$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss14MutableRawSpanVMn$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss14MutableRawSpanVN$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVMa$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVMn$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVsRi_zrlE6_countSivM$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVsRi_zrlE6_countSivg$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVsRi_zrlE6_countSivs$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVsRi_zrlE8_pointerSvSgvg$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVsRi_zrlE8capacitySivg$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanV6_countSivM$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanV6_countSivg$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanV6_countSivs$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanV8_pointerSvSgvg$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanV8capacitySivg$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanVMa$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanVMn$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanVN$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss4SpanVMa$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss4SpanVMn$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss4SpanVsRi_zrlE6_countSivg$

View File

@@ -928,6 +928,23 @@ Added: _$ss14MutableRawSpanVN
Added: _$sSS8UTF8ViewV4spans4SpanVys5UInt8VGvg
Added: _$sSs8UTF8ViewV4spans4SpanVys5UInt8VGvg
// OutputSpan
Added: _$ss10OutputSpanVMa
Added: _$ss10OutputSpanVMn
Added: _$ss10OutputSpanVsRi_zrlE6_countSivM
Added: _$ss10OutputSpanVsRi_zrlE6_countSivg
Added: _$ss10OutputSpanVsRi_zrlE6_countSivs
Added: _$ss10OutputSpanVsRi_zrlE8_pointerSvSgvg
Added: _$ss10OutputSpanVsRi_zrlE8capacitySivg
Added: _$ss13OutputRawSpanV6_countSivM
Added: _$ss13OutputRawSpanV6_countSivg
Added: _$ss13OutputRawSpanV6_countSivs
Added: _$ss13OutputRawSpanV8_pointerSvSgvg
Added: _$ss13OutputRawSpanV8capacitySivg
Added: _$ss13OutputRawSpanVMa
Added: _$ss13OutputRawSpanVMn
Added: _$ss13OutputRawSpanVN
// _SwiftifyInfo enum for _SwiftifyImports macro
Added: _$ss13_SwiftifyExprO5paramyABSicABmFWC
Added: _$ss13_SwiftifyExprO6returnyA2BmFWC
@@ -1048,6 +1065,21 @@ Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss14M
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss14MutableRawSpanVMa$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss14MutableRawSpanVMn$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss14MutableRawSpanVN$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVMa$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVMn$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVsRi_zrlE6_countSivM$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVsRi_zrlE6_countSivg$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVsRi_zrlE6_countSivs$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVsRi_zrlE8_pointerSvSgvg$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss10OutputSpanVsRi_zrlE8capacitySivg$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanV6_countSivM$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanV6_countSivg$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanV6_countSivs$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanV8_pointerSvSgvg$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanV8capacitySivg$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanVMa$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanVMn$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss13OutputRawSpanVN$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss4SpanVMa$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss4SpanVMn$
Added: $ld$previous$@rpath/libswiftCompatibilitySpan.dylib$$1$10.14$26.0$_$ss4SpanVsRi_zrlE6_countSivg$

View File

@@ -168,14 +168,14 @@ enum InlineArrayTests {
let error = CancellationError()
do {
expectDoesNotThrow {
let a = try InlineArray<0, String> { _ in throw error }
let a = try InlineArray<0, String>({ _ in throw error })
_checkInlineArray(a, oracle: [])
}
_expectThrows {
let _ = try InlineArray<1, String> { _ in throw error }
let _ = try InlineArray<1, String>({ _ in throw error })
}
_expectThrows {
let _ = try InlineArray<1, any P> { _ in throw error }
let _ = try InlineArray<1, any P>({ _ in throw error })
}
_expectThrows {
let _ = try InlineArray<2, String> { index in

View File

@@ -249,7 +249,7 @@ suite.test("unsafeLoadUnaligned(as:)")
a.withUnsafeMutableBytes {
var span = MutableRawSpan(_unsafeBytes: $0)
let suffix = span.extracting(droppingFirst: 2)
let suffix = span._mutatingExtracting(droppingFirst: 2)
let u0 = suffix.unsafeLoadUnaligned(as: UInt64.self)
expectEqual(u0 & 0xff, 2)
expectEqual(u0.byteSwapped & 0xff, 9)
@@ -281,111 +281,7 @@ suite.test("storeBytes(of:as:)")
expectEqual(a[0].bigEndian & 0xffff, 0xffff)
}
suite.test("update(from: some Sequence<some BitwiseCopyable>)")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
))
.code {
guard #available(SwiftStdlib 6.2, *) else { return }
let capacity = 8
var a = Array(repeating: Int.max, count: capacity)
expectEqual(a.allSatisfy({ $0 == .max }), true)
a.withUnsafeMutableBufferPointer {
let empty = UnsafeMutableBufferPointer<Int>(start: nil, count: 0)
var span = MutableRawSpan(_unsafeElements: empty)
var (iterator, updated) = span.update(from: 0..<0)
expectNil(iterator.next())
expectEqual(updated, 0)
span = MutableRawSpan(_unsafeElements: $0)
(iterator, updated) = span.update(from: 0..<0)
expectNil(iterator.next())
expectEqual(updated, 0)
(iterator, updated) = span.update(from: 0..<10000)
expectNotNil(iterator.next())
expectEqual(updated, capacity*MemoryLayout<Int>.stride)
}
expectEqual(a.elementsEqual(0..<capacity), true)
}
suite.test("update(from: some Collection<some BitwiseCopyable>)")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
))
.code {
guard #available(SwiftStdlib 6.2, *) else { return }
let capacity = 8
var a = Array(repeating: Int.max, count: capacity)
let e = Array(EmptyCollection<UInt>())
expectEqual(a.allSatisfy({ $0 == .max }), true)
a.withUnsafeMutableBytes {
let emptyPrefix = $0.prefix(0)
var span = MutableRawSpan(_unsafeBytes: emptyPrefix)
var updated = span.update(fromContentsOf: e)
expectEqual(updated, 0)
updated = span.update(fromContentsOf: AnyCollection(e))
expectEqual(updated, 0)
span = MutableRawSpan(_unsafeBytes: $0)
updated = span.update(fromContentsOf: 0..<capacity)
expectEqual(updated, capacity*MemoryLayout<Int>.stride)
}
expectEqual(a.elementsEqual(0..<capacity), true)
}
suite.test("update(fromContentsOf:) (contiguous memory)")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
))
.code {
guard #available(SwiftStdlib 6.2, *) else { return }
let capacity = 8
var a = Array(repeating: Int.max, count: capacity)
expectEqual(a.allSatisfy({ $0 == .max }), true)
a.withUnsafeMutableBytes {
var span = MutableRawSpan(_unsafeBytes: $0)
let array = Array(0..<capacity)
var updated = span.update(fromContentsOf: array.prefix(0))
expectEqual(updated, 0)
updated = span.update(fromContentsOf: array)
expectEqual(updated, capacity*MemoryLayout<Int>.stride)
}
expectEqual(a.elementsEqual(0..<capacity), true)
a.withUnsafeMutableBytes {
var span = MutableRawSpan(_unsafeBytes: $0)
var array = Array(repeating: Int.min, count: capacity)
array.withUnsafeMutableBytes {
let source = MutableRawSpan(_unsafeBytes: $0)
let updated = span.update(fromContentsOf: source)
expectEqual(updated, capacity*MemoryLayout<Int>.stride)
}
}
expectEqual(a.allSatisfy({ $0 == Int.min }), true)
a.withUnsafeMutableBytes {
var span = MutableRawSpan(_unsafeBytes: $0)
let array = Array(0..<capacity)
array.withUnsafeBufferPointer {
let source = Span(_unsafeElements: $0)
let updated = span.update(fromContentsOf: source)
expectEqual(updated, capacity*MemoryLayout<Int>.stride)
}
}
expectEqual(a.elementsEqual(0..<capacity), true)
}
suite.test("extracting()")
suite.test("_mutatingExtracting()")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
@@ -398,25 +294,54 @@ suite.test("extracting()")
b.withUnsafeMutableBytes {
var span = MutableRawSpan(_unsafeBytes: $0)
var sub = span.extracting(0..<2)
var sub = span._mutatingExtracting(0..<2)
expectEqual(sub.byteCount, 2)
expectEqual(sub.unsafeLoad(as: UInt8.self), 0)
sub = span.extracting(..<2)
sub = span._mutatingExtracting(..<2)
expectEqual(sub.byteCount, 2)
expectEqual(sub.unsafeLoad(as: UInt8.self), 0)
sub = span.extracting(...)
sub = span._mutatingExtracting(...)
expectEqual(sub.byteCount, 4)
expectEqual(sub.unsafeLoad(as: UInt8.self), 0)
sub = span.extracting(2...)
sub = span._mutatingExtracting(2...)
expectEqual(sub.byteCount, 2)
expectEqual(sub.unsafeLoad(as: UInt8.self), 2)
}
}
suite.test("extracting(unchecked:)")
suite.test("_consumingExtracting()")
.require(.stdlib_6_2).code {
let c = 16
let b = UnsafeMutableRawBufferPointer.allocate(byteCount: c, alignment: c)
defer { b.deallocate() }
_ = b.initializeMemory(as: UInt8.self, from: 0..<UInt8(c))
var span = b.mutableBytes
span = span._consumingExtracting(0..<2)
expectEqual(span.byteCount, 2)
expectEqual(span.unsafeLoad(as: UInt8.self), 0)
span = b.mutableBytes
span = span._consumingExtracting(..<2)
expectEqual(span.byteCount, 2)
expectEqual(span.unsafeLoad(as: UInt8.self), 0)
span = b.mutableBytes
span = span._consumingExtracting(...)
expectEqual(span.byteCount, c)
expectEqual(span.unsafeLoad(as: UInt8.self), 0)
span = b.mutableBytes
span = span._consumingExtracting(2...)
expectEqual(span.byteCount, c-2)
expectEqual(span.unsafeLoad(as: UInt8.self), 2)
}
suite.test("_mutatingExtracting(unchecked:)")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
@@ -428,14 +353,28 @@ suite.test("extracting(unchecked:)")
var b = (0..<capacity).map(UInt8.init)
b.withUnsafeMutableBytes {
var span = MutableRawSpan(_unsafeBytes: $0.prefix(8))
let beyond = span.extracting(unchecked: 16...23)
let beyond = span._mutatingExtracting(unchecked: 16...23)
expectEqual(beyond.byteCount, 8)
let fromBeyond = beyond.unsafeLoad(as: UInt8.self)
expectEqual(fromBeyond, 16)
}
}
suite.test("extracting prefixes")
suite.test("_consumingExtracting(unchecked:)")
.require(.stdlib_6_2).code {
let capacity = 32
var b = (0..<capacity).map(UInt8.init)
b.withUnsafeMutableBytes {
let span = MutableRawSpan(_unsafeBytes: $0.prefix(8))
let beyond = span._consumingExtracting(unchecked: 16...23)
expectEqual(beyond.byteCount, 8)
let fromBeyond = beyond.unsafeLoad(as: UInt8.self)
expectEqual(fromBeyond, 16)
}
}
suite.test("_mutatingExtracting prefixes")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
@@ -450,19 +389,19 @@ suite.test("extracting prefixes")
var span = MutableRawSpan(_unsafeBytes: $0)
expectEqual(span.byteCount, capacity)
prefix = span.extracting(first: 1)
prefix = span._mutatingExtracting(first: 1)
expectEqual(prefix.unsafeLoad(as: UInt8.self), 0)
prefix = span.extracting(first: capacity)
prefix = span._mutatingExtracting(first: capacity)
expectEqual(
prefix.unsafeLoad(fromByteOffset: capacity-1, as: UInt8.self),
UInt8(capacity-1)
)
prefix = span.extracting(droppingLast: capacity)
prefix = span._mutatingExtracting(droppingLast: capacity)
expectEqual(prefix.isEmpty, true)
prefix = span.extracting(droppingLast: 1)
prefix = span._mutatingExtracting(droppingLast: 1)
expectEqual(
prefix.unsafeLoad(fromByteOffset: capacity-2, as: UInt8.self),
UInt8(capacity-2)
@@ -473,12 +412,51 @@ suite.test("extracting prefixes")
let b = UnsafeMutableRawBufferPointer(start: nil, count: 0)
var span = MutableRawSpan(_unsafeBytes: b)
expectEqual(span.byteCount, b.count)
expectEqual(span.extracting(first: 1).byteCount, b.count)
expectEqual(span.extracting(droppingLast: 1).byteCount, b.count)
expectEqual(span._mutatingExtracting(first: 1).byteCount, b.count)
expectEqual(span._mutatingExtracting(droppingLast: 1).byteCount, b.count)
}
}
suite.test("extracting suffixes")
suite.test("_consumingExtracting prefixes")
.require(.stdlib_6_2).code {
let capacity = 4
var a = Array(0..<UInt8(capacity))
a.withUnsafeMutableBytes {
var span = $0.mutableBytes
expectEqual(span.byteCount, capacity)
span = span._consumingExtracting(first: 1)
expectEqual(span.unsafeLoad(as: UInt8.self), 0)
span = $0.mutableBytes._consumingExtracting(first: capacity)
expectEqual(
span.unsafeLoad(fromByteOffset: capacity-1, as: UInt8.self),
UInt8(capacity-1)
)
span = $0.mutableBytes._consumingExtracting(droppingLast: capacity)
expectEqual(span.isEmpty, true)
span = $0.mutableBytes._consumingExtracting(droppingLast: 1)
expectEqual(
span.unsafeLoad(fromByteOffset: capacity-2, as: UInt8.self),
UInt8(capacity-2)
)
}
do {
let b = UnsafeMutableRawBufferPointer(start: nil, count: 0)
var span = MutableRawSpan(_unsafeBytes: b)
expectEqual(span.byteCount, b.count)
span = b.mutableBytes._consumingExtracting(first: 1)
expectEqual(span.byteCount, b.count)
span = b.mutableBytes._consumingExtracting(droppingLast: 1)
expectEqual(span.byteCount, b.count)
}
}
suite.test("_mutatingExtracting suffixes")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
@@ -493,19 +471,19 @@ suite.test("extracting suffixes")
var span = MutableRawSpan(_unsafeBytes: $0)
expectEqual(span.byteCount, capacity)
prefix = span.extracting(last: capacity)
prefix = span._mutatingExtracting(last: capacity)
expectEqual(prefix.unsafeLoad(as: UInt8.self), 0)
prefix = span.extracting(last: capacity-1)
prefix = span._mutatingExtracting(last: capacity-1)
expectEqual(prefix.unsafeLoad(as: UInt8.self), 1)
prefix = span.extracting(last: 1)
prefix = span._mutatingExtracting(last: 1)
expectEqual(prefix.unsafeLoad(as: UInt8.self), UInt8(capacity-1))
prefix = span.extracting(droppingFirst: capacity)
prefix = span._mutatingExtracting(droppingFirst: capacity)
expectTrue(prefix.isEmpty)
prefix = span.extracting(droppingFirst: 1)
prefix = span._mutatingExtracting(droppingFirst: 1)
expectEqual(prefix.unsafeLoad(as: UInt8.self), 1)
}
@@ -513,8 +491,44 @@ suite.test("extracting suffixes")
let b = UnsafeMutableRawBufferPointer(start: nil, count: 0)
var span = MutableRawSpan(_unsafeBytes: b)
expectEqual(span.byteCount, b.count)
expectEqual(span.extracting(last: 1).byteCount, b.count)
expectEqual(span.extracting(droppingFirst: 1).byteCount, b.count)
expectEqual(span._mutatingExtracting(last: 1).byteCount, b.count)
expectEqual(span._mutatingExtracting(droppingFirst: 1).byteCount, b.count)
}
}
suite.test("_consumingExtracting suffixes")
.require(.stdlib_6_2).code {
let capacity = 4
var a = Array(0..<UInt8(capacity))
a.withUnsafeMutableBytes {
var span = $0.mutableBytes
expectEqual(span.byteCount, capacity)
span = span._consumingExtracting(last: capacity)
expectEqual(span.unsafeLoad(as: UInt8.self), 0)
span = $0.mutableBytes._consumingExtracting(last: capacity-1)
expectEqual(span.unsafeLoad(as: UInt8.self), 1)
span = $0.mutableBytes._consumingExtracting(last: 1)
expectEqual(span.unsafeLoad(as: UInt8.self), UInt8(capacity-1))
span = $0.mutableBytes._consumingExtracting(droppingFirst: capacity)
expectEqual(span.isEmpty, true)
span = $0.mutableBytes._consumingExtracting(droppingFirst: 1)
expectEqual(span.unsafeLoad(as: UInt8.self), 1)
}
do {
let b = UnsafeMutableRawBufferPointer(start: nil, count: 0)
var span = MutableRawSpan(_unsafeBytes: b)
expectEqual(span.byteCount, b.count)
span = b.mutableBytes._consumingExtracting(last: 1)
expectEqual(span.byteCount, b.count)
span = b.mutableBytes._consumingExtracting(droppingFirst: 1)
expectEqual(span.byteCount, b.count)
}
}

View File

@@ -315,232 +315,6 @@ suite.test("update(repeating:)")
expectEqual(a.allSatisfy({ $0.id == .max }), true)
}
suite.test("update(repeating:) - BitwiseCopyable")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
))
.code {
guard #available(SwiftStdlib 6.2, *) else { return }
var a = Array(0..<8)
expectEqual(a.contains(.max), false)
a.withUnsafeMutableBufferPointer {
var span = MutableSpan(_unsafeElements: $0)
span.update(repeating: .max)
}
expectEqual(a.allSatisfy({ $0 == .max }), true)
}
suite.test("update(from: some Sequence)")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
))
.code {
guard #available(SwiftStdlib 6.2, *) else { return }
let capacity = 8
var a = Array(repeating: ID(id: .max), count: capacity)
expectEqual(a.allSatisfy({ $0.id == .max }), true)
a.withUnsafeMutableBufferPointer {
let emptyPrefix = $0.prefix(0)
var span = MutableSpan(_unsafeElements: emptyPrefix)
var (iterator, updated) = span.update(from: [])
expectNil(iterator.next())
expectEqual(updated, 0)
span = MutableSpan(_unsafeElements: $0)
(iterator, updated) = span.update(from: [])
expectNil(iterator.next())
expectEqual(updated, 0)
(iterator, updated) = span.update(from: (0..<12).map(ID.init(id:)))
expectNotNil(iterator.next())
expectEqual(updated, capacity)
}
expectEqual(a.map(\.id).elementsEqual(0..<capacity), true)
}
suite.test("update(from: some Sequence) - BitwiseCopyable")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
))
.code {
guard #available(SwiftStdlib 6.2, *) else { return }
let capacity = 8
var a = Array(repeating: Int.max, count: capacity)
expectEqual(a.allSatisfy({ $0 == .max }), true)
a.withUnsafeMutableBufferPointer {
let empty = UnsafeMutableBufferPointer<Int>(start: nil, count: 0)
var span = MutableSpan(_unsafeElements: empty)
var (iterator, updated) = span.update(from: 0..<0)
expectNil(iterator.next())
expectEqual(updated, 0)
span = MutableSpan(_unsafeElements: $0)
(iterator, updated) = span.update(from: 0..<0)
expectNil(iterator.next())
expectEqual(updated, 0)
(iterator, updated) = span.update(from: 0..<10000)
expectNotNil(iterator.next())
expectEqual(updated, capacity)
}
expectEqual(a.elementsEqual(0..<capacity), true)
}
suite.test("update(fromContentsOf: some Collection)")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
))
.code {
guard #available(SwiftStdlib 6.2, *) else { return }
let capacity = 8
var a = Array(repeating: ID(id: .max), count: capacity)
expectEqual(a.allSatisfy({ $0.id == .max }), true)
a.withUnsafeMutableBufferPointer {
let emptyPrefix = $0.prefix(0)
var span = MutableSpan(_unsafeElements: emptyPrefix)
var updated = span.update(fromContentsOf: [])
expectEqual(updated, 0)
updated = span.update(fromContentsOf: AnyCollection([]))
expectEqual(updated, 0)
span = MutableSpan(_unsafeElements: $0)
let elements = (0..<capacity).map(ID.init(id:))
updated = span.update(fromContentsOf: AnyCollection(elements))
expectEqual(updated, capacity)
}
expectEqual(a.map(\.id).elementsEqual(0..<capacity), true)
}
suite.test("update(fromContentsOf: some Collection) - BitwiseCopyable")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
))
.code {
guard #available(SwiftStdlib 6.2, *) else { return }
let capacity = 8
var a = Array(repeating: Int.max, count: capacity)
expectEqual(a.allSatisfy({ $0 == .max }), true)
a.withUnsafeMutableBufferPointer {
let emptyPrefix = $0.prefix(0)
var span = MutableSpan(_unsafeElements: emptyPrefix)
var updated = span.update(fromContentsOf: [])
expectEqual(updated, 0)
updated = span.update(fromContentsOf: AnyCollection([]))
expectEqual(updated, 0)
span = MutableSpan(_unsafeElements: $0)
updated = span.update(fromContentsOf: 0..<capacity)
expectEqual(updated, capacity)
}
expectEqual(a.elementsEqual(0..<capacity), true)
}
suite.test("update(fromContentsOf: Span)")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
))
.code {
guard #available(SwiftStdlib 6.2, *) else { return }
let capacity = 8
var a = Array(repeating: ID(id: .max), count: capacity)
expectEqual(a.allSatisfy({ $0.id == .max }), true)
a.withUnsafeMutableBufferPointer {
let emptyPrefix = $0.prefix(0)
var span = MutableSpan(_unsafeElements: emptyPrefix)
let updated = span.update(
fromContentsOf: UnsafeBufferPointer(start: nil, count: 0)
)
expectEqual(updated, 0)
span = MutableSpan(_unsafeElements: $0)
var elements = (0..<capacity).map(ID.init(id:))
elements.withUnsafeMutableBufferPointer {
let source = MutableSpan(_unsafeElements: $0)
let updated = span.update(fromContentsOf: source)
expectEqual(updated, capacity)
}
}
expectEqual(a.map(\.id).elementsEqual(0..<capacity), true)
}
suite.test("update(fromContentsOf: Span) - BitwiseCopyable")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
))
.code {
guard #available(SwiftStdlib 6.2, *) else { return }
let capacity = 8
var a = Array(repeating: Int.max, count: capacity)
expectEqual(a.allSatisfy({ $0 == .max }), true)
a.withUnsafeMutableBufferPointer {
let emptyPrefix = $0.prefix(0)
var span = MutableSpan(_unsafeElements: emptyPrefix)
let update = span.update(fromContentsOf: [])
expectEqual(update, 0)
span = MutableSpan(_unsafeElements: $0)
var array = Array(0..<capacity)
array.withUnsafeMutableBufferPointer {
let source = MutableSpan(_unsafeElements: $0)
let update = span.update(fromContentsOf: source)
expectEqual(update, capacity)
}
}
expectEqual(a.elementsEqual(0..<capacity), true)
}
suite.test("moveUpdate()")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
))
.code {
guard #available(SwiftStdlib 6.2, *) else { return }
let capacity = 8
var a = Array(repeating: ID(id: .max), count: capacity)
a.withUnsafeMutableBufferPointer {
var span = MutableSpan(_unsafeElements: $0)
let empty = UnsafeMutableBufferPointer(start: $0.baseAddress, count: 0)
let updated = span.moveUpdate(fromContentsOf: empty)
expectEqual(updated, 0)
}
expectEqual(a.allSatisfy({ $0.id == .max }), true)
let b = UnsafeMutableBufferPointer<ID>.allocate(capacity: 2*capacity)
let i = b.initialize(fromContentsOf: (0..<2*capacity).map(ID.init(id:)))
expectEqual(i, 2*capacity)
a.withUnsafeMutableBufferPointer {
var span = MutableSpan(_unsafeElements: $0)
let updated = span.moveUpdate(fromContentsOf: b.suffix(capacity))
expectEqual(updated, capacity)
}
expectEqual(a.map(\.id).elementsEqual(capacity..<2*capacity), true)
a = []
b.prefix(capacity).deinitialize()
b.deallocate()
}
suite.test("span property")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
@@ -596,7 +370,7 @@ suite.test("swapAt")
expectEqual(array, (0..<count).reversed())
}
suite.test("extracting()")
suite.test("_mutatingExtracting()")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
@@ -609,25 +383,54 @@ suite.test("extracting()")
b.withUnsafeMutableBufferPointer {
var span = MutableSpan(_unsafeElements: $0)
var sub = span.extracting(0..<2)
var sub = span._mutatingExtracting(0..<2)
expectEqual(sub.count, 2)
expectEqual(sub[0], 0)
sub = span.extracting(..<2)
sub = span._mutatingExtracting(..<2)
expectEqual(sub.count, 2)
expectEqual(sub[0], 0)
sub = span.extracting(...)
sub = span._mutatingExtracting(...)
expectEqual(sub.count, 4)
expectEqual(sub[0], 0)
sub = span.extracting(2...)
sub = span._mutatingExtracting(2...)
expectEqual(sub.count, 2)
expectEqual(sub[0], 2)
}
}
suite.test("extracting(unchecked:)")
suite.test("_consumingExtracting()")
.require(.stdlib_6_2).code {
let c = 16
let b = UnsafeMutableBufferPointer<Int8>.allocate(capacity: c)
defer { b.deallocate() }
_ = b.initialize(fromContentsOf: 0..<Int8(c))
var span = b.mutableSpan
span = span._consumingExtracting(0..<2)
expectEqual(span.count, 2)
expectEqual(span[0], 0)
span = b.mutableSpan
span = span._consumingExtracting(..<2)
expectEqual(span.count, 2)
expectEqual(span[0], 0)
span = b.mutableSpan
span = span._consumingExtracting(...)
expectEqual(span.count, c)
expectEqual(span[0], 0)
span = b.mutableSpan
span = span._consumingExtracting(2...)
expectEqual(span.count, c-2)
expectEqual(span[0], 2)
}
suite.test("_mutatingExtracting(unchecked:)")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
@@ -639,14 +442,27 @@ suite.test("extracting(unchecked:)")
var b = (0..<capacity).map(UInt8.init)
b.withUnsafeMutableBufferPointer {
var span = MutableSpan(_unsafeElements: $0.prefix(8))
let beyond = span.extracting(unchecked: 16...23)
let beyond = span._mutatingExtracting(unchecked: 16...23)
expectEqual(beyond.count, 8)
let fromBeyond = beyond[0]
expectEqual(fromBeyond, 16)
}
}
suite.test("extracting prefixes")
suite.test("_consumingExtracting(unchecked:)")
.require(.stdlib_6_2).code {
let capacity = 32
var b = (0..<capacity).map(UInt8.init)
b.withUnsafeMutableBufferPointer {
let span = MutableSpan(_unsafeElements: $0.prefix(8))
let beyond = span._consumingExtracting(unchecked: 16...23)
expectEqual(beyond.count, 8)
expectEqual(beyond[0], 16)
}
}
suite.test("_mutatingExtracting prefixes")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
@@ -661,16 +477,16 @@ suite.test("extracting prefixes")
var span = MutableSpan(_unsafeElements: $0)
expectEqual(span.count, capacity)
prefix = span.extracting(first: 1)
prefix = span._mutatingExtracting(first: 1)
expectEqual(prefix[0], 0)
prefix = span.extracting(first: capacity)
prefix = span._mutatingExtracting(first: capacity)
expectEqual(prefix[capacity-1], UInt8(capacity-1))
prefix = span.extracting(droppingLast: capacity)
prefix = span._mutatingExtracting(droppingLast: capacity)
expectEqual(prefix.isEmpty, true)
prefix = span.extracting(droppingLast: 1)
prefix = span._mutatingExtracting(droppingLast: 1)
expectEqual(prefix[capacity-2], UInt8(capacity-2))
}
@@ -678,12 +494,46 @@ suite.test("extracting prefixes")
let b = UnsafeMutableBufferPointer<Int>(start: nil, count: 0)
var span = MutableSpan(_unsafeElements: b)
expectEqual(span.count, b.count)
expectEqual(span.extracting(first: 1).count, b.count)
expectEqual(span.extracting(droppingLast: 1).count, b.count)
expectEqual(span._mutatingExtracting(first: 1).count, b.count)
expectEqual(span._mutatingExtracting(droppingLast: 1).count, b.count)
}
}
suite.test("extracting suffixes")
suite.test("_consumingExtracting prefixes")
.require(.stdlib_6_2).code {
let capacity = 4
var a = Array(0..<capacity)
a.withUnsafeMutableBufferPointer {
var span = $0.mutableSpan
expectEqual(span.count, capacity)
span = span._consumingExtracting(first: 1)
expectEqual(span.count, 1)
expectEqual(span[0], 0)
span = $0.mutableSpan._consumingExtracting(first: capacity)
expectEqual(span[capacity-1], capacity-1)
span = $0.mutableSpan._consumingExtracting(droppingLast: capacity)
expectEqual(span.isEmpty, true)
span = $0.mutableSpan._consumingExtracting(droppingLast: 1)
expectEqual(span[capacity-2], capacity-2)
}
do {
let b = UnsafeMutableBufferPointer<Int>(start: nil, count: 0)
var span = b.mutableSpan
expectEqual(span.count, b.count)
span = b.mutableSpan._consumingExtracting(first: 1)
expectEqual(span.count, b.count)
span = b.mutableSpan._consumingExtracting(droppingLast: 1)
expectEqual(span.count, b.count)
}
}
suite.test("_mutatingExtracting suffixes")
.skip(.custom(
{ if #available(SwiftStdlib 6.2, *) { false } else { true } },
reason: "Requires Swift 6.2's standard library"
@@ -698,19 +548,19 @@ suite.test("extracting suffixes")
var span = MutableSpan(_unsafeElements: $0)
expectEqual(span.count, capacity)
suffix = span.extracting(last: capacity)
suffix = span._mutatingExtracting(last: capacity)
expectEqual(suffix[0], 0)
suffix = span.extracting(last: capacity-1)
suffix = span._mutatingExtracting(last: capacity-1)
expectEqual(suffix[0], 1)
suffix = span.extracting(last: 1)
suffix = span._mutatingExtracting(last: 1)
expectEqual(suffix[0], UInt8(capacity-1))
suffix = span.extracting(droppingFirst: capacity)
suffix = span._mutatingExtracting(droppingFirst: capacity)
expectTrue(suffix.isEmpty)
suffix = span.extracting(droppingFirst: 1)
suffix = span._mutatingExtracting(droppingFirst: 1)
expectEqual(suffix[0], 1)
}
@@ -718,8 +568,44 @@ suite.test("extracting suffixes")
let b = UnsafeMutableBufferPointer<ObjectIdentifier>(start: nil, count: 0)
var span = MutableSpan(_unsafeElements: b)
expectEqual(span.count, b.count)
expectEqual(span.extracting(last: 1).count, b.count)
expectEqual(span.extracting(droppingFirst: 1).count, b.count)
expectEqual(span._mutatingExtracting(last: 1).count, b.count)
expectEqual(span._mutatingExtracting(droppingFirst: 1).count, b.count)
}
}
suite.test("_consumingExtracting suffixes")
.require(.stdlib_6_2).code {
let capacity = 4
var a = Array(0..<capacity)
a.withUnsafeMutableBufferPointer {
var span = $0.mutableSpan
expectEqual(span.count, capacity)
span = span._consumingExtracting(last: capacity)
expectEqual(span[0], 0)
span = $0.mutableSpan._consumingExtracting(last: capacity-1)
expectEqual(span[0], 1)
span = $0.mutableSpan._consumingExtracting(last: 1)
expectEqual(span[0], capacity-1)
span = $0.mutableSpan._consumingExtracting(droppingFirst: capacity)
expectEqual(span.isEmpty, true)
span = $0.mutableSpan._consumingExtracting(droppingFirst: 1)
expectEqual(span[0], 1)
}
do {
let b = UnsafeMutableBufferPointer<AnyObject>(start: nil, count: 0)
var span = b.mutableSpan
expectEqual(span.count, b.count)
span = b.mutableSpan._consumingExtracting(last: 1)
expectEqual(span.count, b.count)
span = b.mutableSpan._consumingExtracting(droppingFirst: 1)
expectEqual(span.count, b.count)
}
}

View File

@@ -0,0 +1,155 @@
//===--- OutputRawSpanTests.swift -----------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// RUN: %target-run-stdlib-swift
// REQUIRES: executable_test
import StdlibUnittest
var suite = TestSuite("OutputRawSpan Tests")
defer { runAllTests() }
@available(SwiftStdlib 6.2, *)
struct Allocation: ~Copyable {
let allocation: UnsafeMutableRawBufferPointer
var byteCount: Int? = nil
init(byteCount: Int = 1) {
precondition(byteCount >= 0)
allocation = .allocate(byteCount: byteCount, alignment: 16)
}
var isEmpty: Bool { (byteCount ?? 0) == 0 }
mutating func initialize<E>(
_ body: (inout OutputRawSpan) throws(E) -> Void
) throws(E) {
if byteCount != nil { fatalError() }
var outputBuffer = OutputRawSpan(buffer: allocation, initializedCount: 0)
do {
try body(&outputBuffer)
let initialized = outputBuffer.finalize(for: allocation)
byteCount = initialized
}
catch {
outputBuffer.removeAll()
let initialized = outputBuffer.finalize(for: allocation)
assert(initialized == 0)
throw error
}
}
borrowing func withSpan<E, R: ~Copyable>(
_ body: (borrowing RawSpan) throws(E) -> R
) throws(E) -> R {
try body(RawSpan(_unsafeBytes: allocation[0..<(byteCount ?? 0)]))
}
deinit {
allocation.deallocate()
}
}
enum MyTestError: Error { case error }
suite.test("Create OutputRawSpan")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
let c = 48
let allocation = UnsafeMutableRawBufferPointer.allocate(byteCount: c, alignment: 16)
defer { allocation.deallocate() }
let ob = unsafe OutputRawSpan(buffer: allocation, initializedCount: 0)
let initialized = ob.finalize(for: allocation)
expectEqual(initialized, 0)
}
suite.test("deinit without relinquishing memory")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
let c = 48
let allocation = UnsafeMutableRawBufferPointer.allocate(byteCount: c, alignment: 16)
defer { allocation.deallocate() }
var ob = unsafe OutputRawSpan(buffer: allocation, initializedCount: 0)
// OutputRawSpan(buffer: Slice(base: allocation, bounds: 0..<c))
ob.append(repeating: 65, count: 12, as: UInt8.self)
expectEqual(ob.byteCount, 12)
_ = ob
}
suite.test("append single elements")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
var a = Allocation(byteCount: 48)
let c = 10
a.initialize {
for i in 0...c {
$0.append(UInt8(i))
}
let oops = $0.removeLast()
expectEqual(Int(oops), c)
}
expectNotNil(a.byteCount)
expectEqual(a.byteCount, c)
a.withSpan {
expectEqual($0.byteCount, c)
for o in $0.byteOffsets {
expectEqual(Int($0.unsafeLoad(fromByteOffset: o, as: UInt8.self)), o)
}
}
}
suite.test("initialize buffer with repeated elements")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
var a = Allocation(byteCount: 48)
let c = UInt8(10)
a.initialize {
$0.append(repeating: c, count: Int(c), as: UInt8.self)
let oops = $0.removeLast()
expectEqual(oops, c)
expectEqual($0.byteCount, Int(c-1))
}
a.withSpan {
expectEqual($0.byteCount, Int(c-1))
for o in $0.byteOffsets {
expectEqual($0.unsafeLoad(fromByteOffset: o, as: UInt8.self), c)
}
}
}
suite.test("deinitialize buffer")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
var a = Allocation(byteCount: 48)
do {
try a.initialize {
$0.append(0)
$0.append(1)
expectTrue($0.byteCount > 0)
throw MyTestError.error
}
}
catch MyTestError.error {
expectEqual(a.isEmpty, true)
}
catch {
expectTrue(false)
}
}

View File

@@ -0,0 +1,248 @@
//===--- OutputSpanTests.swift --------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// RUN: %target-run-stdlib-swift
// REQUIRES: executable_test
import StdlibUnittest
var suite = TestSuite("OutputSpan Tests")
defer { runAllTests() }
@available(SwiftStdlib 6.2, *)
struct Allocation<T>: ~Copyable {
let allocation: UnsafeMutableBufferPointer<T>
var count: Int? = nil
init(of count: Int = 1, _ t: T.Type) {
precondition(count >= 0)
allocation = .allocate(capacity: count)
}
var isEmpty: Bool { (count ?? 0) == 0 }
mutating func initialize<E>(
_ body: (inout OutputSpan<T>) throws(E) -> Void
) throws(E) {
if count != nil { fatalError() }
var outputBuffer = OutputSpan<T>(buffer: allocation, initializedCount: 0)
do {
try body(&outputBuffer)
let initialized = outputBuffer.finalize(for: allocation)
count = initialized
}
catch {
outputBuffer.removeAll()
let initialized = outputBuffer.finalize(for: allocation)
assert(initialized == 0)
throw error
}
}
borrowing func withSpan<E, R: ~Copyable>(
_ body: (borrowing Span<T>) throws(E) -> R
) throws(E) -> R {
try body(Span(_unsafeElements: allocation[0..<count!]))
}
deinit {
if let count {
allocation.prefix(count).deinitialize()
}
allocation.deallocate()
}
}
enum MyTestError: Error { case error }
suite.test("Create OutputSpan")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
let c = 48
let allocation = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: c)
defer { allocation.deallocate() }
let ob = unsafe OutputSpan(buffer: allocation, initializedCount: 0)
let initialized = ob.finalize(for: allocation)
expectEqual(initialized, 0)
}
suite.test("deinit without relinquishing memory")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
let c = 48
let allocation = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: c)
defer { allocation.deallocate() }
var ob = unsafe OutputSpan(buffer: allocation, initializedCount: 0)
// OutputSpan(buffer: Slice(base: allocation, bounds: 0..<c))
ob.append(repeating: 65, count: 12)
expectEqual(ob.count, 12)
_ = ob
}
suite.test("append single elements")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
var a = Allocation(of: 48, Int.self)
let c = 10
a.initialize {
for i in 0...c {
$0.append(i)
}
let oops = $0.removeLast()
expectEqual(oops, c)
}
expectNotNil(a.count)
expectEqual(a.count, c)
a.withSpan {
expectEqual($0.count, c)
for i in $0.indices {
expectEqual($0[i], i)
}
}
}
suite.test("initialize buffer with repeated elements")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
var a = Allocation(of: 48, Int.self)
let c = 10
a.initialize {
$0.append(repeating: c, count: c)
let oops = $0.removeLast()
expectEqual(oops, c)
expectEqual($0.count, c-1)
}
a.withSpan {
expectEqual($0.count, c-1)
for i in $0.indices {
expectEqual($0[i], c)
}
}
}
suite.test("indices property")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
let capacity = 4
let b = UnsafeMutableBufferPointer<Int>.allocate(capacity: capacity)
defer { b.deallocate() }
_ = b.initialize(fromContentsOf: 0..<capacity)
defer { b.deinitialize() }
let span = unsafe OutputSpan(buffer: b, initializedCount: capacity)
expectEqual(span.indices.count, capacity)
let equal = span.indices.elementsEqual(0..<capacity)
expectTrue(equal)
}
suite.test("IndexingSubscript")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
let capacity = 4
let b = UnsafeMutableBufferPointer<Int>.allocate(capacity: capacity)
defer { b.deallocate() }
_ = b.initialize(fromContentsOf: 0..<capacity)
defer { b.deinitialize() }
var span = unsafe OutputSpan(buffer: b, initializedCount: capacity)
expectEqual(span[0], b.first)
span[0] += 1
expectEqual(span[0], b.first)
}
suite.test("deinitialize buffer")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
var a = Allocation(of: 48, Int.self)
do {
try a.initialize {
$0.append(0)
$0.append(1)
expectTrue($0.count > 0)
throw MyTestError.error
}
}
catch MyTestError.error {
expectEqual(a.isEmpty, true)
}
catch {
expectTrue(false)
}
}
suite.test("InlineArray initialization")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
let i = InlineArray<10, Int> {
(o: inout OutputSpan<Int>) in
expectEqual(o.count, 0)
for i in 0..<o.capacity {
o.append(i)
}
expectEqual(o.freeCapacity, 0)
}
for j in i.indices {
expectEqual(j, i[j])
}
}
suite.test("InlineArray initialization underflow")
.skip(.wasiAny(reason: "Trap tests aren't supported on WASI."))
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
expectCrashLater()
_ = InlineArray<4, Int> {
$0.append(1)
}
}
suite.test("InlineArray initialization throws")
.require(.stdlib_6_2).code {
guard #available(SwiftStdlib 6.2, *) else { return }
enum LocalError: Error { case error }
class I {
static var count = 0
init() { Self.count += 1 }
deinit { Self.count -= 1 }
}
let a: InlineArray<4, I>
do throws(LocalError) {
a = try InlineArray {
o throws(LocalError) in
o.append(I())
o.append(I())
o.append(I())
o.append(I())
expectEqual(I.count, 4)
throw LocalError.error
}
_ = a
} catch {
expectEqual(I.count, 0)
}
}