Search code examples
swiftpointersmemory-managementunsafe-pointersunsafemutablepointer

How to convert UnsafePointer<[Float]> or fit Float Array data to Unsafe[Mutable]Pointer<Float> in Swift 3?


I have an array of float value (floatArray) coming from HDF5 file, and I'd like to use this data as an input for the function that accepts Unsafe[Mutable]Pointer (inputForFunction). I'm new to UnsafePointer, so I need someone's help.

One way I have in mind is, to store the array to a file, then to open it and to map the file descriptor to the memory of the variable for the input of the function:

  // read the data from HDF5 file
  var floatArray: [Float] = try originalHDF5DataSet.read()

  // store the data
  let dataToStore = NSData(bytes: &floatArray, length: sizeOfArray * MemoryLayout<Float>.size)
  let tmpPath = URL(fileURLWithPath: NSTemporaryDirectory())
  let filePath = tmpPath.appendingPathComponent("floatArrayData").appendingPathExtension("dat")
  do{
    try data.write(to: filePath, options: .atomic)
    let fileManager : FileManager   = FileManager.default
    if fileManager.fileExists(atPath:filePath.path){
      print("Success")
    }else{
      print("Fail")
    }

    // open the data 
    let dataPath = filePath.path
    let fd = open(dataPath, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
    assert(fd != -1, "Error: failed to open output file at \""+dataPath+"\"  errno = \(errno)\n")
    // memory map the parameters
    let hdr = mmap(nil, Int(sizeOfArray), PROT_READ, MAP_FILE | MAP_SHARED, fd, 0)
    // cast Void pointers to Float
    let inputForFunction = UnsafePointer<Float>(hdr!.assumingMemoryBound(to: Float.self))
  } catch{
    print("failed")
  }

However, it may not be the best implementation because the process to store the data seems time-consuming.

That's why I'm considering to use UnsafePointer to pass the pointer from floatArray to inputForFunction, but I'm not sure how to implement it. Just for your information, one way I'm thinking of is to use withUnsafePointermethod directly:

var floatArray: [Float] = try originalHDF5DataSet.read()
var inputForFunction = UnsafeMutablePointer<Float>.allocate(capacity: Int(sizeOfArray))
withUnsafePointer(to: &floatArray[0]) { (ptr: UnsafePointer<Float>) in
  inputForFunction = UnsafeMutablePointer(mutating: ptr)
}

or the other way is to use withMemoryRebound (but the following didn't work):

var floatArray: [Float] = try originalHDF5DataSet.read()
var inputForFunction = UnsafeMutablePointer<Float>.allocate(capacity: Int(sizeOfArray))
withUnsafePointer(to: &floatArray) { (ptrDataArray: UnsafePointer<[Float]>) in
  inputForFunction = ptrDataArray.withMemoryRebound(to: inputForFunction.self, capacity: Int(sizeOfArray), { (ptr: UnsafePointer<[Float]>) -> UnsafeMutablePointer<Float> in
    return ptr[0]
  })
}

My question is

  • How to pass the address of floatArray's data to inputForFunction?
  • If I need to convert from UnsafePointer<[Float]> to Unsafe[Mutable]Pointer, how to do it?
  • If needed, how to use withMemoryRebound for this purpose? Can I assign the address/value to inputForFunction inside the closure?
  • When I checked the addresses of floatArray and floatArray[0], they were different (This seems different from C/C++). Which address should be passed to inputForFunction?

Solution

  • You can find many articles explaining how you can get Unsafe(Mutable)Pointer<Float> from [Float]. Some of them answer some of your questions. Try find them later.

    • How to pass the address of floatArray's data to inputForFunction?

      You usually use the Array's methods withUnsafeBufferPointer or withUnsafeMutableBufferPointer. You can get an Unsafe(Mutable)BufferPointer<Element> inside the closure parameter, which contains an Unsafe(Mutable)Pointer<Element> as its baseAddress property.

      var floatArray: [Float] = try originalHDF5DataSet.read()
      floatArray.withUnsafeBufferPointer {unsafeBufferPointer in
          let inputForFunction = unsafeBufferPointer.baseAddress
          //`unsafeBufferPointer` (including its `baseAddress`) is valid only in this closure.
          //So, do not pass `inputForFunction` outside of the closure, use it inside.
          //...
      }
      
    • If I need to convert from UnsafePointer<[Float]> to Unsafe[Mutable]Pointer, how to do it?

      When you find something like UnsafePointer<[Float]>, then you are going the wrong way. In Swift, Array is a hidden struct which may contain (multi-level) indirect reference to its elements. You usually have no need to use UnsafePointer<Array<Float>>.

    • If needed, how to use withMemoryRebound for this purpose? Can I assign the address/value to inputForFunction inside the closure?

      When you have an Array of AType and want to pass it as an Unsafe(Mutable)Pointer<AnotherType>, you may utilize withMemoryRebound. But in your case, you only need to work with Float, so withMemoryRebound may not be needed for your purpose.

    • When I checked the addresses of floatArray and floatArray[0], they were different (This seems different from C/C++). Which address should be passed to inputForFunction?

      None. As I wrote above, address of floatArray is just pointing somewhere which contains a hidden struct, so it's completely useless for your purpose. And address of floatArray[0] is neither guaranteed to be the address of the first element of the whole content. Swift may create a temporal region which contains only one element, and passing the address of the temporal region.


    By the way, do you really need to convert your [Float] to Unsafe(Mutable)Pointer<Float>?

    If your functions' signature is something like this:

    func takingUnsafePointer(_ pointer: UnsafePointer<Float>)
    

    You can call it like:

    takingUnsafePointer(floatArray)
    //In this case `floatArray` can be a `let` constant.
    

    Or else, if the function signature is:

    func takingUnsafeMutablePointer(_ pointer: UnsafeMutablePointer<Float>)
    

    Call it as:

    takingUnsafeMutablePointer(&floatArray)
    //In this case `floatArray` needs to be a `var`, not `let`.
    

    Remember, in both cases, the passed pointer is valid only while the function call. You should not export the pointer somewhere else.