Write to slice of UnsafeMutableBufferPointer

What is the correct way of writing a sequence of values into an UnsafeMutableBufferPointer?
E.g. say we have:

let buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: 16)
buffer.initialize(repeating: 0)

// buffer values: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

let newValues: [UInt8] = [1, 2, 3]

I would like to do something like:

buffer[3...5] = newValues

// buffer values: 0 0 0 1 2 3 0 0 0 0 0 0 0 0 0 0 0 
3 Likes

It seems that the UnsafeMutableBufferPointer subscript supports both get and set operations, but it uses the Slice<UnsafeMutableBufferPointer> type. So, you can make this work as follows:

let buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: 16)
buffer.initialize(repeating: 0)
var newValues: [UInt8] = [1, 2, 3]
newValues.withUnsafeMutableBufferPointer { newValuesBuf in
    buffer[3..<6] = newValuesBuf[0..<3]
}
print(buffer.map { "\($0)" }.joined(separator: ", "))
// prints: 0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0

Is the UInt8 in the above example just an example? Or are you working with blocks of untyped memory? If it’s the latter, I recommend a raw buffer pointer (UnsafeMutableRawBufferPointer) which has a copyBytes(from:) method.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

1 Like

You can write a subscript that enables the nice syntax for assigning values from any Sequence into a slice of any MutableCollection:

Generic slice assignment subscript
extension MutableCollection {
  subscript<S: Sequence>(_ range: Range<Index>) -> S
    where S.Element == Element
  {
    set {
      var i = range.lowerBound
      for x in newValue {
        if i >= range.upperBound { break }
        self[i] = x
        formIndex(after: &i)
      }
    }
    
    @available(*, unavailable)
    get { fatalError() }
  }
  
  subscript<R: RangeExpression, S: Sequence>(_ range: R) -> S
    where R.Bound == Index, S.Element == Element
  {
    set { self[range.relative(to: self)] = newValue }
    
    @available(*, unavailable)
    get { fatalError() }
  }
}

That really “should be” a set-only subscript, which we approximate here by marking the getter as unavailable.

This example implementation does not enforce that the sequence and index range have the same number of elements, but you could do so if desired. It does guarantee that nothing outside the slice will be modified, and the length of the collection will not change.

1 Like

It seems like that it could be nice if just UnsafeMutableBufferPointer had implemented conformance to RangeReplaceableCollection, that way this operation would be just a buffer.replaceSubrange(3...5, with: [1, 2, 3]). It would've make sense to me have such conformance, but maybe I'm missing something and there is a good rational for it to not conform...

Unsafe[Raw]MutableBufferPointer cannot conform to RangeReplaceableCollection because it cannot resize itself. Remember that these memory allocations are unmanaged.

4 Likes

Makes sense, thanks!