We are proposing a new abstraction for safe, direct access to memory: StorageView
. This is the proposal formerly known as BufferView
, and is a companion to Non-Escapable Types and to Lifetime Dependency Annotations for Non-escapable Types, which were jointly pitched yesterday by @tbkka.
Introduction
We introduce StorageView<T>
, an abstraction for container-agnostic access to contiguous memory. It will expand the expressivity of performant Swift code without giving up on the memory safety properties we rely on: temporal safety, spatial safety, definite initialization and type safety.
In the C family of programming languages, memory can be shared with any function by using a pointer and (ideally) a length. This allows contiguous memory to be shared with a function that doesn't know the layout of a struct being used by the caller. A heap-allocated array, contiguously-stored named fields or even a single stack-allocated instance can all be accessed through a C pointer. We aim to create a similar idiom in Swift, with no compromise to memory safety.
Motivation
Consider for example a program using multiple libraries, including base64 decoding. The program would obtain encoded data from one or more of its dependencies, which could supply it in the form of [UInt8]
, Foundation.Data
or even String
, among others. None of these types is necessarily more correct than another, but the base64 decoding library must pick an input format. It could declare its input parameter type to be some Sequence<UInt8>
, but such a generic function significantly limits performance. This may force the library author to either declare its entry point as inlinable, or to implement an internal fast path using withContiguousStorageIfAvailable()
and use an unsafe type. The ideal interface would have a combination of the properties of both some Sequence<UInt8>
and UnsafeBufferPointer<UInt8>
.
Proposed solution
StorageView
will allow sharing the contiguous internal representation of a type, by providing access to a borrowed view of a span of contiguous memory. A view does not copy the underlying data: it instead relies on a guarantee that the original container cannot be modified or destroyed during the lifetime of the view. StorageView
's lifetime is statically enforced as a lifetime dependency to a binding of the type vending it, preventing its escape from the scope where it is valid for use. This guarantee preserves temporal safety. StorageView
also performs bounds-checking on every access to preserve spatial safety. Additionally StorageView
always represents initialized memory, preserving the definite initialization guarantee.
By relying on borrowing, StorageView
can provide simultaneous access to a non-copyable container, and can help avoid unwanted copies of copyable containers.
The full proposal can be found here:
Safe Access to Contiguous Storage
It depends on the capabilities pitched in Non-Escapable Types and Lifetime Dependency, and the related discussions: