Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Release UnsafeMutableBufferPointer<UInt8> values

I am implementing a pure Swift NSData alternative. Below is part of my Swift 2 code. As far as I know, Data instance deinitialization does not destroy() and dealloc() bytes the block buffer points to. So, are there any ways of calling destroy and dealloc() on buffer pointers to prevent memory leaks before the Data instance deinitializes?

public struct Data: DataContainer {
    public typealias Block = UInt8
    public var blocks: UnsafeMutableBufferPointer<Block>

    public init(bytes: UnsafeMutablePointer<Block>, length: Int) {
        // copy bytes
        let bytesCopy = UnsafeMutablePointer<Block>.alloc(length)
        bytesCopy.initializeFrom(bytes, count: length)
        // init byte blocks
        self.blocks = UnsafeMutableBufferPointer<Block>(start: bytesCopy, count: length)
    }
}
like image 585
tie Avatar asked Mar 08 '26 01:03

tie


2 Answers

You can solve this by using a class for the actual implementation. An example how this can be done is given in Friday Q&A 2015-04-17: Let's Build Swift.Array:

Destruction can be solved by using a class, which provides deinit. The pointer can be destroyed there. class doesn't have value semantics, but we can solve this by using class for the implementation of the struct, and exposing the struct as the external interface to the array. This looks something like:

class ArrayImpl<T> {
    var ptr: UnsafeMutablePointer<T>

    deinit {
        ptr.destroy(...)
        ptr.dealloc(...)
    }
}

struct Array<T> {
    var impl: ArrayImpl<T>
}

You then write methods on Array that forward to implementations on ArrayImpl, where the real work is done.

Applying this approach to your problem could roughly look like this:

private class DataImpl {
    typealias Block = UInt8
    var blocks: UnsafeMutableBufferPointer<Block>

    init(bytes: UnsafeMutablePointer<Block>, length: Int) {
        // copy bytes
        let bytesCopy = UnsafeMutablePointer<Block>.alloc(length)
        bytesCopy.initializeFrom(bytes, count: length)
        // init byte blocks
        self.blocks = UnsafeMutableBufferPointer<Block>(start: bytesCopy, count: length)
    }

    deinit {
        print("deinit")
        blocks.baseAddress.destroy(blocks.count)
        blocks.baseAddress.dealloc(blocks.count)
    }
}

struct Data {
    typealias Block = UInt8
    private var impl : DataImpl

    init(bytes: UnsafeMutablePointer<Block>, length: Int) {
        impl = DataImpl(bytes: bytes, length: length)
    }
}

A simple test shows that this works as expected, the data is released when the variable goes out of scope:

var bytes : [UInt8] = [1, 2, 3, 4]
do {
    let data = Data(bytes: &bytes, length: bytes.count)
}
print("finished")

Output:

deinit
finished
like image 90
Martin R Avatar answered Mar 09 '26 21:03

Martin R


Thanks to @Martin R for his answer. I've added class AutoreleasingMutableBufferPointer<T> as container and deallocator of struct UnsafeMutableBufferPointer<T>. Here is the working code:

public class AutoreleasingMutableBufferPointer<T> {
    public var buffer: UnsafeMutableBufferPointer<T>

    public init(start pointer: UnsafeMutablePointer<T>, count length: Int) {
        self.buffer = UnsafeMutableBufferPointer<T>(start: pointer, count: length)
    }

    public init(buffer: UnsafeMutableBufferPointer<T>) {
        self.buffer = buffer
    }

    deinit {
        self.buffer.baseAddress.destroy(buffer.count)
        self.buffer.baseAddress.dealloc(buffer.count)
    }
}

public struct Data: DataContainer {
    public typealias Block = UInt8
    public var blocks: AutoreleasingMutableBufferPointer<Block>
}

extension Data {
    public init(bytes: UnsafeMutablePointer<Block>, length: Int) {
        let bytesCopy = UnsafeMutablePointer<Block>.alloc(length)
        bytesCopy.initializeFrom(bytes, count: length)
        self.blocks = AutoreleasingMutableBufferPointer<Block>(start: bytesCopy, count: length)
    }
}
like image 27
tie Avatar answered Mar 09 '26 22:03

tie



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!