[swift-evolution] [Pitch] Normalize Slice Types for Unsafe Buffers

Karl razielim at gmail.com
Wed Nov 30 11:51:09 CST 2016


+1. Sensible change.

> On 30 Nov 2016, at 18:15, Nate Cook via swift-evolution <swift-evolution at swift.org> wrote:
> 
> Hello all—
> 
> This is a proposal for a fairly minor change in slicing behavior for unsafe buffers.
> 
> Nate
> 
> 
> ------
> 
> 
> This proposal changes Swift's typed UnsafeBufferPointers to be their own slice type, like the UnsafeRawBufferPointer types. This is a minor change in the subscript API of UnsafeBufferPointer and UnsafeMutableBufferPointer, but constitutes a change to the standard library's ABI, as it can't be solved through type aliasing.
>  <https://gist.github.com/natecook1000/a239f4db2704db9c5d54d540c0a09499#motivation>Motivation
> 
> The standard library has parallel pointer and buffer types for working with raw and typed memory. These types have broadly similar APIs that streamline working with pointers, as some kinds of memory manipulation involve moving back and forth between the two. One significant difference between the two groups of buffer types, however, is that while UnsafeRawBufferPointers are their own slice type, UnsafeBufferPointers use the default Slice type as a wrapper.
> 
> Using a Slice wrapper is a needless addition when working with buffers—the wrapper is most useful when used to prevent copying of a collection's stored data, but since UnsafeBufferPointers aren't owners of the memory they reference, there is no copying performed when simply creating a new buffer over a subrange of the memory. Moreover, the overhead of a Slice wrapper around an UnsafeBufferPointer is almost certainly higher than another UnsafeBufferPointer. instance.
> 
> The Slice wrapper makes using buffer pointers as parameters more cumbersome than necessary. To pass a slice of a buffer to a function taking a buffer, you need to create a new buffer manually:
> 
> func _operateOnBuffer<T>(_ buffer: UnsafeMutableBufferPointer<T>) {
>     // ...
> }
> 
> let buffer: UnsafeMutableBufferPointer<Int> = ...
> _operateOnBuffer(buffer)            // okay
> _operateOnBuffer(buffer[0..<16])    // error: type mismatch
> let subBuffer = UnsafeMutableBufferPointer(start: buffer, count: 16)
> _operateOnBuffer(subBuffer)         // okay
> The wrapper complicates subscript assignment, as well. Instead of using simple assignment to copy all the elements of one buffer into a memory range of another, you must either manually create a slice or subscript the source buffer with its full range:
> 
> let biggerBuffer: UnsafeMutableBufferPointer<Int> = ...
> let smallerBuffer: UnsafeMutableBufferPointer<Int> = ...
> 
> biggerBuffer[0..<smallerBuffer.count] = 
>     smallerBuffer[0..<smallerBuffer.count]
>  <https://gist.github.com/natecook1000/a239f4db2704db9c5d54d540c0a09499#proposed-solution>Proposed solution
> 
> The proposed solution is to switch the UnsafeBufferPointers to be their own slice type. This uses less overhead than the Slice type, which needs to store both the original buffer and a bounding range.
> 
> The operations above are simpler with this change:
> 
> _operateOnBuffer(buffer[0..<16])        // subscripting okay
> 
> // no need to subscript 'smallerBuffer'
> biggerBuffer[0..<smallerBuffer.count] = smallerBuffer 
>  <https://gist.github.com/natecook1000/a239f4db2704db9c5d54d540c0a09499#detailed-design>Detailed design
> 
> The change follows the example of the raw buffer pointer types:
> 
> struct UnsafeBufferPointer<Element> : Collection, ... {
>     // other declarations
>     subscript(bounds: Range<Int>) -> UnsafeBufferPointer {
>         get {
>             // check bounds
>             return UnsafeMutableBufferPointer(
>                 start: self + bounds.lowerBound,
>                 count: bounds.count)
>         }
>     }
> }
> 
> struct UnsafeMutableBufferPointer<Element> : Collection, ... {
>     // other declarations
>     subscript(bounds: Range<Int>) -> UnsafeMutableBufferPointer {
>         get {
>             // check bounds
>             return UnsafeMutableBufferPointer(
>                 start: self + bounds.lowerBound,
>                 count: bounds.count)
>         }
>         set {
>             // check bounds
>             _writeBackMutableSlice(&self, bounds: bounds, slice: newValue)
>         }
>     }
> }
>  <https://gist.github.com/natecook1000/a239f4db2704db9c5d54d540c0a09499#impact-on-existing-code>Impact on existing code
> 
> Any existing code that works with slices of UnsafeMutableBufferPointer and specifies the Slice type explicitly will need to change that specification. This isn't a terribly common thing to do (I can't find any in the standard library or test suite), so the impact of the change should be minor.
> 
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161130/1a93d1ed/attachment.html>


More information about the swift-evolution mailing list