Search code examples
swiftsegmentation-faultaccelerate-frameworkvdsp

Segmentation fault using Swift Accelerate vDSP_ctoz


I'm trying to convert an interleaved DSPComplex vector to a DSPSplitComplex vector, using vDSP_ctoz from the Swift Accelerate library. The last line of the code below produces the error Segmentation fault: 11

I don't understand why vDSP_ctoz would try to access out-of-bound memory when I've allocated large vectors and am only trying to process a small number of elements. The vectors are size 2048 and the argument for N (number of elements to process) in vDSP_ctoz is 1.

I've also tried using different stride and N values when calling vDSP_ctoz, to no avail.

// set stride values
let dspComplexStride = MemoryLayout<DSPComplex>.stride
let dspSplitComplexStride = MemoryLayout<DSPSplitComplex>.stride

// make interleaved vector
var interleaved = UnsafeMutablePointer<DSPComplex>.allocate(capacity: 2048)
for index in 0..<16 {
    interleaved[index] = DSPComplex(real: Float(2*index), imag: Float(2*index+1))
}

// make split vector
var splitComplex = UnsafeMutablePointer<DSPSplitComplex>.allocate(capacity: 2048)
vDSP_ctoz(
    interleaved, dspComplexStride, splitComplex, dspSplitComplexStride, 1
)

Solution

  • DSPSplitComplex is a structure containing pointers arrays, so you need a single DSPSplitComplex element and must allocate storage for its realp and imagp properties.

    The "stride" arguments are not measured in bytes but in "element" units. So you pass __IZ == 1 because you want to fill contiguous elements in the destination arrays.

    It may not be obvious that you have to pass __IC == 2 for the source array, i.e. the stride of the source array is given in Float units and not in DSPComplex units. This can be deduced from the vDSP_ctoz documentation which mentions that the function effectively does

    for (n = 0; n < N; ++n)
    {
      Z->realp[n*IZ] = C[n*IC/2].real;
      Z->imagp[n*IZ] = C[n*IC/2].imag;
    }
    

    Finally, the last argument of vDSP_ctoz is the number of elements to process.

    Putting it all together, this is how it should work:

    import Accelerate
    
    let N = 16
    
    var interleaved = UnsafeMutablePointer<DSPComplex>.allocate(capacity: N)
    for index in 0..<N {
        interleaved[index] = DSPComplex(real: Float(2*index), imag: Float(2*index+1))
    }
    
    let realp = UnsafeMutablePointer<Float>.allocate(capacity: N)
    let imagp = UnsafeMutablePointer<Float>.allocate(capacity: N)
    var splitComplex = DSPSplitComplex(realp: realp, imagp: imagp)
    
    vDSP_ctoz(interleaved, 2, &splitComplex, 1, vDSP_Length(N))
    
    for index in 0..<N {
        print(splitComplex.realp[index], splitComplex.imagp[index])
    }
    

    and of course you have to release the memory eventually.