mirror of
https://github.com/apple/swift-foundation.git
synced 2025-05-23 14:00:14 +08:00
* [base64] initial Base-64 re-implementation * Add BufferView._assertBounds() functions - they act the same as the _checkBounds() functions - but only in debug mode * Improve BufferView slicing - when all the necessary checking has been done, the BufferView initializer doesn’t need to validate the buffer length another time. * remove premature reference to typealias * add the `unsafe` particle to an initializer argument label * [base64] modify Base64 with `OutputBuffer` and `BufferView` * remove unused bits in OutputBuffer * express a cascaded if statement as a switch * make internal decoding initializer failable * Update Sources/FoundationEssentials/Data/Data+Base64.swift Co-authored-by: Elliot Knight <63256761+Harry-KNIGHT@users.noreply.github.com> --------- Co-authored-by: Elliot Knight <63256761+Harry-KNIGHT@users.noreply.github.com>
230 lines
7.4 KiB
Swift
230 lines
7.4 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2023 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct OutputBuffer<T>: ~Copyable // ~Escapable
|
|
{
|
|
let start: UnsafeMutablePointer<T>
|
|
let capacity: Int
|
|
var initialized: Int = 0
|
|
|
|
deinit {
|
|
// `self` always borrows memory, and it shouldn't have gotten here.
|
|
// Failing to use `relinquishBorrowedMemory()` is an error.
|
|
if initialized > 0 {
|
|
fatalError()
|
|
}
|
|
}
|
|
|
|
// precondition: pointer points to uninitialized memory for count elements
|
|
init(initializing: UnsafeMutablePointer<T>, capacity: Int) {
|
|
start = initializing
|
|
self.capacity = capacity
|
|
}
|
|
}
|
|
|
|
extension OutputBuffer {
|
|
mutating func appendElement(_ value: T) {
|
|
precondition(initialized < capacity, "Output buffer overflow")
|
|
start.advanced(by: initialized).initialize(to: value)
|
|
initialized &+= 1
|
|
}
|
|
|
|
mutating func deinitializeLastElement() -> T? {
|
|
guard initialized > 0 else { return nil }
|
|
initialized &-= 1
|
|
return start.advanced(by: initialized).move()
|
|
}
|
|
}
|
|
|
|
extension OutputBuffer {
|
|
mutating func deinitialize() {
|
|
let b = UnsafeMutableBufferPointer(start: start, count: initialized)
|
|
b.deinitialize()
|
|
initialized = 0
|
|
}
|
|
}
|
|
|
|
extension OutputBuffer {
|
|
mutating func append<S>(
|
|
from elements: S
|
|
) -> S.Iterator where S: Sequence, S.Element == T {
|
|
var iterator = elements.makeIterator()
|
|
append(from: &iterator)
|
|
return iterator
|
|
}
|
|
|
|
mutating func append(
|
|
from elements: inout some IteratorProtocol<T>
|
|
) {
|
|
while initialized < capacity {
|
|
guard let element = elements.next() else { break }
|
|
start.advanced(by: initialized).initialize(to: element)
|
|
initialized &+= 1
|
|
}
|
|
}
|
|
|
|
mutating func append(
|
|
fromContentsOf source: some Collection<T>
|
|
) {
|
|
let count = source.withContiguousStorageIfAvailable {
|
|
guard let sourceAddress = $0.baseAddress, !$0.isEmpty else {
|
|
return 0
|
|
}
|
|
let available = capacity &- initialized
|
|
precondition(
|
|
$0.count <= available,
|
|
"buffer cannot contain every element from source."
|
|
)
|
|
let tail = start.advanced(by: initialized)
|
|
tail.initialize(from: sourceAddress, count: $0.count)
|
|
return $0.count
|
|
}
|
|
if let count {
|
|
initialized &+= count
|
|
return
|
|
}
|
|
|
|
let available = capacity &- initialized
|
|
let tail = start.advanced(by: initialized)
|
|
let suffix = UnsafeMutableBufferPointer(start: tail, count: available)
|
|
var (iterator, copied) = source._copyContents(initializing: suffix)
|
|
precondition(
|
|
iterator.next() == nil,
|
|
"buffer cannot contain every element from source."
|
|
)
|
|
assert(initialized + copied <= capacity)
|
|
initialized &+= copied
|
|
}
|
|
|
|
mutating func moveAppend(
|
|
fromContentsOf source: UnsafeMutableBufferPointer<T>
|
|
) {
|
|
guard let sourceAddress = source.baseAddress, !source.isEmpty else {
|
|
return
|
|
}
|
|
let available = capacity &- initialized
|
|
precondition(
|
|
source.count <= available,
|
|
"buffer cannot contain every element from source."
|
|
)
|
|
let tail = start.advanced(by: initialized)
|
|
tail.moveInitialize(from: sourceAddress, count: source.count)
|
|
initialized &+= source.count
|
|
}
|
|
|
|
mutating func moveAppend(
|
|
fromContentsOf source: Slice<UnsafeMutableBufferPointer<T>>
|
|
) {
|
|
moveAppend(fromContentsOf: UnsafeMutableBufferPointer(rebasing: source))
|
|
}
|
|
}
|
|
|
|
extension OutputBuffer<UInt8> /* where T: BitwiseCopyable */ {
|
|
|
|
mutating func appendBytes<Value /*: BitwiseCopyable */>(
|
|
of value: borrowing Value, as: Value.Type
|
|
) {
|
|
precondition(_isPOD(Value.self))
|
|
let (q,r) = MemoryLayout<Value>.stride.quotientAndRemainder(
|
|
dividingBy: MemoryLayout<T>.stride
|
|
)
|
|
precondition(
|
|
r == 0, "Stride of Value must be divisible by stride of Element"
|
|
)
|
|
precondition(
|
|
(capacity &- initialized) >= q,
|
|
"buffer cannot contain every byte of value."
|
|
)
|
|
let p = UnsafeMutableRawPointer(start.advanced(by: initialized))
|
|
p.storeBytes(of: value, as: Value.self)
|
|
initialized &+= q
|
|
}
|
|
}
|
|
|
|
extension OutputBuffer {
|
|
var initializedPrefix: /*borrowed*/ BufferView<T> {
|
|
/* _read */ get /* borrowing(self) */ {
|
|
/* yield */ return BufferView(
|
|
unsafeBufferPointer: .init(start: start, count: initialized)
|
|
).unsafelyUnwrapped
|
|
}
|
|
}
|
|
|
|
func withBufferView<R>(_ body: (borrowing BufferView<T>) throws -> R) rethrows -> R {
|
|
let view = BufferView<T>(
|
|
unsafeBufferPointer: .init(start: start, count: initialized)
|
|
).unsafelyUnwrapped
|
|
return try body(view)
|
|
}
|
|
}
|
|
|
|
extension OutputBuffer {
|
|
|
|
consuming func relinquishBorrowedMemory() -> UnsafeMutableBufferPointer<T> {
|
|
let start = self.start
|
|
let initialized = self.initialized
|
|
discard self
|
|
return .init(start: start, count: initialized)
|
|
}
|
|
}
|
|
|
|
extension String {
|
|
|
|
// also see https://github.com/apple/swift/pull/23050
|
|
// and `final class __SharedStringStorage`
|
|
|
|
init(
|
|
utf8Capacity capacity: Int,
|
|
initializingWith initializer: (inout OutputBuffer<UInt8>) throws -> Void
|
|
) rethrows {
|
|
try self.init(
|
|
unsafeUninitializedCapacity: capacity,
|
|
initializingUTF8With: { buffer in
|
|
var output = OutputBuffer(
|
|
initializing: buffer.baseAddress.unsafelyUnwrapped,
|
|
capacity: capacity
|
|
)
|
|
try initializer(&output)
|
|
let initialized = output.relinquishBorrowedMemory()
|
|
assert(initialized.baseAddress == buffer.baseAddress)
|
|
return initialized.count
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Data {
|
|
|
|
init(
|
|
capacity: Int,
|
|
initializingWith initializer: (inout OutputBuffer<UInt8>) throws -> Void
|
|
) rethrows {
|
|
self = Data(count: capacity) // initialized with zeroed buffer
|
|
let count = try self.withUnsafeMutableBytes { rawBuffer in
|
|
try rawBuffer.withMemoryRebound(to: UInt8.self) { buffer in
|
|
buffer.deinitialize()
|
|
var output = OutputBuffer(
|
|
initializing: buffer.baseAddress.unsafelyUnwrapped,
|
|
capacity: capacity
|
|
)
|
|
try initializer(&output)
|
|
let initialized = output.relinquishBorrowedMemory()
|
|
assert(initialized.baseAddress == buffer.baseAddress)
|
|
buffer[initialized.count..<buffer.count].initialize(repeating: 0)
|
|
return initialized.count
|
|
}
|
|
}
|
|
assert(count <= self.count)
|
|
self.replaceSubrange(count..<self.count, with: EmptyCollection())
|
|
}
|
|
}
|