Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to byte reverse NSData output in Swift the littleEndian way?

I have this output from NSData: <00000100 84000c00 071490fe 4dfbd7e9>

So how could I byte reverse it in Swift and have this output: <00000001 0084000c 1407fe90 fb4de9d7>?

like image 237
Marco Almeida Avatar asked May 12 '15 15:05

Marco Almeida


2 Answers

This should work to swap each pair of adjacent bytes in the data. The idea is to interpret the bytes as an array of UInt16 integers and use the built-in byteSwapped property.

func swapUInt16Data(data : NSData) -> NSData {

    // Copy data into UInt16 array:
    let count = data.length / sizeof(UInt16)
    var array = [UInt16](count: count, repeatedValue: 0)
    data.getBytes(&array, length: count * sizeof(UInt16))

    // Swap each integer:
    for i in 0 ..< count {
        array[i] = array[i].byteSwapped // *** (see below)
    }

    // Create NSData from array:
    return NSData(bytes: &array, length: count * sizeof(UInt16))
}

If your actual intention is to convert data from an (external) big-endian representation to the host (native) byte order (which happens to be little-endian on all current iOS and OS X devices) then you should replace *** by

array[i] = UInt16(bigEndian: array[i])

Example:

var bytes : [UInt8] = [1, 2, 3, 4, 5, 6, 7, 8]
let data = NSData(bytes: &bytes, length: bytes.count)
print(data)
// <01020304 05060708>
print(swapUInt16Data(data))
// <02010403 06050807>

Update for Swift 3: The generic withUnsafeMutableBytes() methods allows to obtain a UnsafeMutablePointer<UInt16> to the bytes and modify them directly:

func swapUInt16Data(data : Data) -> Data {
    var mdata = data // make a mutable copy
    let count = data.count / MemoryLayout<UInt16>.size
    mdata.withUnsafeMutableBytes { (i16ptr: UnsafeMutablePointer<UInt16>) in
        for i in 0..<count {
            i16ptr[i] =  i16ptr[i].byteSwapped
        }
    }
    return mdata
}

Example:

let data = Data(bytes: [1, 2, 3, 4, 5, 6, 7, 8])
print(data as NSData) // <01020304 05060708>

let swapped = swapUInt16Data(data: data)
print(swapped as NSData) // <02010403 06050807>
like image 71
Martin R Avatar answered Oct 24 '22 01:10

Martin R


CoreFoundation has CFSwapInt32BigToHost and CFSwapInt32LittleToHost and other swap functions.

In swift3 it looks like this

struct FileHeader {
    var magicNumber: UInt32 = 0
    var count: UInt32 = 0
    var width: UInt32 = 0
    var height: UInt32 = 0

    static func create(data: Data) -> FileHeader {
        let structSize = MemoryLayout<FileHeader>.size
        assert(data.count >= structSize)
        var result = FileHeader()
        let nsdata = data as NSData
        nsdata.getBytes(&result, range: NSRange(location: 0, length: structSize))
        result.magicNumber = CFSwapInt32BigToHost(result.magicNumber)
        result.count = CFSwapInt32BigToHost(result.count)
        result.width = CFSwapInt32BigToHost(result.width)
        result.height = CFSwapInt32BigToHost(result.height)
        return result
    }
}
like image 44
neoneye Avatar answered Oct 24 '22 03:10

neoneye