Best way to copy Data() into UnsafeMutableRawPointer?

Sometimes I think I know what I’m doing, and then I find myself having to use the Unsafe… API, and I feel dumb. Every time.

Right now I have to fill a provided UnsafeMutableRawPointer with data I get in chunks as a Data instance. I want to copy the entire Data into the UnsafeMutableRawPointer at a specific offset. (The memory is supplied by Core Image and I'm filling it with pixel data; the source pixel data is not contiguous (and in fact is being read from a file).

I see methods like storeBytes<T>(of value: T, toByteOffset offset: Int = 0, as type: T.Type), but that can only take a trivial type, which would mean I have to iterate over the bytes in my Data. This seems inefficient.

I see methods like copyBytes<C>(from source: C) where C : Collection, C.Element == UInt8, but it copies to the start of the destination buffer with no offset parameter.

Should I make a new UnsafeMutableRawBufferPointer using init(rebasing:) with a slice of the original destination representing the row I want to copy to, and then use copyBytes(from:)? I’m going to try that but I’d sure appreciate confirmation that this is the best (or only) way.

EDIT: I just realized I have to byte-swap for endianness sometimes, so I’ll have to iterate each pixel’s data in some cases (the pixel format I’m most interested in is 16-bit grayscale). However, in those situations where I don’t have to byte-swap, I’d like to make the copies as fast as possible.


*For all the revision this stuff has undergone over the years, it still seems extraordinarily difficult to use. And while examples abound online, I can't quite find one that fits this use case (which honestly seems like a very common use case).

1 Like

I see methods like copyBytes<C>(from source: C) where C : Collection, C.Element == UInt8 , but it copies to the start of the destination buffer with no offset parameter.

If you have a UnsafeMutableRawPointer, you can offset it by simply adding any integer to it and you get a new UnsafeMutableRawPointer back:

let offset = 2
(mutableRawPointer + offset).copyMemory(from: source, byteCount: count)

But I think you mean UnsafeMutableRawBufferPointer as UnsafeMutableRawPointer does not define a copyBytes method, only a copyMemory method.

Could you slice the Data and call pointer.copyBytes(from: data[start..<end])? Data slicing reuses the same buffer and should be cheap.

1 Like

I think @harlanhaskins has given the right advice for the wrong object: the concern is that copyBytes(from:) copies into the start of the buffer pointer, not from the start of the Data. The solution is the same as the one provided by @dnadoba, albeit with a much more annoying spelling:

let offset = 2
UnsafeMutableRawPointer(rebasing: mutableRawPointer.dropFirst(2)).copyMemory(from: source, byteCount: count)
2 Likes

D'oh, I totally missed that part of the problem. Thanks!

1 Like

I ended up writing the code like this:

func
provideImageData(_ outData: UnsafeMutableRawPointer, …)
{
    let destinationByteCount = <computed from input parameters>
    let destBuffer = UnsafeMutableRawBufferPointer(start: ioData, count: destinationByteCount)
    
    let data = <get the data the caller wants>
    
    let start = <compute start and end locations in destination from input parameters>
    let end = ...
    let destSlice = UnsafeMutableRawBufferPointer(rebasing: dest[start ..< end])
    destSlice.copyBytes(from: data)
}

This worked, although the overall method is very slow (I'm reading from a mem-mapped Data and yeah, too much overhead). I'm re-writing it to read from FileDescriptor.