1

In Swift 4 I'm creating a DSPSplitComplex to use in vDSP_fft_zip(), but it's being immediately overwritten the next time I create another DSPSplitComplex.

( DSPSplitComplex structure has 2 UnsafeMutablePointer<Float> )

//--Create the DSPSplitComplex    
var A = [Float](repeating:0, count:32); 
var B = [Float](repeating:0, count:32)
var splitComplex1 = DSPSplitComplex(realp: &A, imagp: &B)

//--Perform fft
log2Size = vDSP_Length(log2f(Float(32)))
setupFFT = vDSP_create_fftsetup(log2Size, FFTRadix(FFT_RADIX2))!;
vDSP_fft_zip(setupFFT, & splitComplex1, 1, log2Size, FFTDirection(FFT_FORWARD));

//--Create another DSPSplitComplex    
var C = [Float](repeating:0, count:32); 
var D = [Float](repeating:0, count:32)
var splitComplex2 = DSPSplitComplex(realp: &C, imagp: &D)

I can now see in the debugger that the UnsafeMutablePointer of splitComplex2.realp is the same address as splitComplex1.realp, and consequently anything I do with splitComplex2 overwrites splitComplex1

I guess the clue might be in the title 'unsafe', but if it's actually unusable, then what is the correct strategy for storing the contents of the returned DSPSplitComplex?

I guess creating new [Float] arrays from them is a way to make a permanent copy

let arrary = Array(UnsafeBufferPointer(start: splitComplex1.realp, count: 32))

...but it seems, despite reading the Swift docs, I'm still misunderstanding the point of UnsafeMutablePointer, as why would vDSP_fft_zip() return something that is unusable from the get-go?

Hamish
  • 78,605
  • 19
  • 187
  • 280
Joey FourSheds
  • 479
  • 1
  • 7
  • 18
  • 1
    From what it looks like, `DSPSplitComplex` doesn't take its own copy of the buffers you pass it, so its storing two dangling pointers (dangling because inout-to-pointer argument conversions produce pointers only valid for the duration of the call). Depending on your needs, you could use `Array`'s `withUnsafeMutableBufferPointer` method to get scoped access to the underlying memory. But if you need more flexibility, I would consider creating a class wrapper type that allocates the necessary memory and deallocs it in `deinit` (and you could use `ManagedBuffer` in order improve memory efficiency) – Hamish Oct 10 '18 at 10:42
  • Thanks, I see the problem, using withUnsafeMutableBufferPointer solves it. – Joey FourSheds Oct 10 '18 at 12:35

1 Answers1

2

Your way of creating DSPSplitComplex is wrong.

When you pass Array<Float> to UnsafeMutablePointer<Float> using &, the address passed to the function is only valid within the function.

So, in your code, two addresses passed DSPSplitComplex(realp: &A, imagp: &B) are not valid when the initializer of DSPSplitComplex finished.

To avoid this sort of behavior, you can try something like this:

var A = [Float](repeating:0, count:32)
var B = [Float](repeating:0, count:32)
A.withUnsafeMutableBufferPointer {aumbp in
    B.withUnsafeMutableBufferPointer {bumbp in
        var splitComplex1 = DSPSplitComplex(realp: aumbp.baseAddress!, imagp: bumbp.baseAddress!)

        //--Perform fft
        let log2Size = vDSP_Length(log2f(Float(32)))
        let setupFFT = vDSP_create_fftsetup(log2Size, FFTRadix(FFT_RADIX2))!
        vDSP_fft_zip(setupFFT, &splitComplex1, 1, log2Size, FFTDirection(FFT_FORWARD))
    }
}

Or else, you can allocate UnsafeMutableBufferPointer<Float>s and use them instead of Array<Float>.

OOPer
  • 47,149
  • 6
  • 107
  • 142