Assigning UnsafeRawPointer to UnsafeMutablePointer<UInt8>?

In trying to call zlib functions from Swift, I'm struggling to assign UnsafeRawPointer to UnsafeMutablePointer<UInt8>. zlib has a struct:

public struct z_stream_s {
    public init()
    public init(next_in: UnsafeMutablePointer<Bytef>!, avail_in: uInt, total_in: uLong, next_out: UnsafeMutablePointer<Bytef>!, avail_out: uInt, total_out: uLong, msg: UnsafeMutablePointer<CChar>!, state: OpaquePointer!, zalloc: alloc_func!, zfree: free_func!, opaque: voidpf!, data_type: Int32, adler: uLong, reserved: uLong)
    public var next_in: UnsafeMutablePointer<Bytef>!
    public var avail_in: uInt
    public var total_in: uLong
    public var next_out: UnsafeMutablePointer<Bytef>!
    public var avail_out: uInt
    public var total_out: uLong
    public var msg: UnsafeMutablePointer<CChar>!
    public var state: OpaquePointer!
    public var zalloc: alloc_func!
    public var zfree: free_func!
    public var opaque: voidpf!
    public var data_type: Int32
    public var adler: uLong
    public var reserved: uLong
}

Bytef is UInt8.

I have this code:

let samples = try [T](unsafeUninitializedCapacity: sampleCount)
	{ destBuffer, initializedCount in
		let sourceBuffer = UnsafeMutableRawBufferPointer.allocate(byteCount: Int(tileByteCount), alignment: MemoryLayout<UInt8>.alignment)
		let decompressedSize = try sourceBuffer.withUnsafeBytes
			{ (inCompressedBytes: UnsafeRawBufferPointer) -> Int in
				state.next_in = inCompressedBytes
			}
	}

But Swift doesn't like that. I've tried inCompressedBytes.baseAddress!, inCompressedBytes.assumingMemoryBound(to: UInt8.self), and inCompressedBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)

With other similar C decompression APIs, I haven't had this much trouble, in part because they declare the input buffer pointer to be const. But I also can't seem to assign the output buffer pointer:

state.next_out = destBuffer.baseAddress!

I get "Cannot assign value of type 'UnsafeMutablePointer' to type 'UnsafeMutablePointer' (aka 'UnsafeMutablePointer')".

When I pass that to compression_decode_buffer(), which also takes a UnsafeMutablePointer<UInt8>, everything is fine.

You can convert an UnsafeRawPointer to an UnsafeMutablePointer<UInt8> with

let converted = UnsafeMutablePointer(mutating: unsafeRawPointer.bindMemory(to: UInt8.self, capacity: count))

It should be noted that in some cases, mutating the value referenced by a pointer is undefined behavior, so converting immutable pointers to mutable pointers should be done with care.

assumingMemoryBound should only be used when you know that a raw pointer is bound to a certain type. Memory allocated by UnsafeMutableRawPointerBuffer.allocate(byteCount:alignment:) isn’t bound to any type, so you should use bindMemory instead. Or, just use UnsafeMutablePointerBuffer<UInt8>.allocate(capacity:).

Would this code work?

let samples = [T](unsafeUninitializedCapacity: sampleCount) {
    (destBuffer, initializedCount) in
    let sourceBuffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: Int(tileByteCount))
    state.next_in = sourceBuffer.baseAddress!
    let decompressedSize = ...
    // deallocate sourceBuffer
}

Sometimes, the diagnostics get the problem wrong. Usually, it means that you’ve made a mistake somewhere else in your code. Unfortunately, I can’t tell you where.

Since version 5.6, Swift has some implicit conversions for pointers in declarations from C. See SE-0324.


I hope this helps!

2 Likes