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

Dave Abrahams dabrahams at apple.com
Wed Nov 30 18:15:45 CST 2016


on Wed Nov 30 2016, Kevin Ballard <swift-evolution at swift.org> wrote:

> This sounds like a sensible idea. But there is one behavioral change you
> haven't addressed, which is that this changes how indexes work on the
> slice. With all other slice types that come to mind, the slice shares
> the same indexes as the base, e.g.
>
>   let ary = Array(0..<10)
>
>   print(ary[3]) // prints 3
>
>   print(ary[2..<5][3]) // still prints 3

This is an important invariant that we need to maintain.

> UnsafeBufferPointer is indexed using 0-based integers, so with your
> proposal, slicing an UnsafeBufferPointer produces a value that uses
> different indexes. We could solve this by adding a new field, but that
> would break the expectation that startIndex is always zero. 

I'm not sure that's an expectation we're obligated to honor.  Of course,
once you get into “unsafe” territory like this, breaking even the
expectations that aren't based on documented guarantees can be really
dangerous.

We probably ought to have wrapped those integers in some Index type
specific to UnsafeBufferPointer, so zero wasn't even a value.

> But we can't just ignore this problem, because algorithms that are
> designed around collections may assume that slices preserve indexes.
>
> In addition, since you point out that UnsafeRawBufferPointer is already
> its own subsequence, and that type also guarantees that startIndex is
> always zero, it sounds like we already have an instance of this problem
> in the stdlib, and so this needs to be addressed with
> UnsafeRawBufferPointer as well.

Sounds like it!

> -Kevin Ballard
>
> On Wed, Nov 30, 2016, at 09:15 AM, Nate Cook via swift-evolution 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.
>> 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[..<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[..<smallerBuffer.count] =
>> smallerBuffer[..<smallerBuffer.count]
>> 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[..<16])        // subscripting okay
>>
>> // no need to subscript 'smallerBuffer'
>> biggerBuffer[..<smallerBuffer.count] = smallerBuffer
>> 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) } } }
>> 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
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>

-- 
-Dave



More information about the swift-evolution mailing list