We’re using TensorFlow in our iOS app, which requires the weights file be 64-byte aligned. We also encrypt our weights file, so to decrypt it we have this mess (I didn’t write this, and I believe the outPtr
is wrong, although it seems to work in practice).
private func crypt(input: Data, operation: CCOperation) throws -> DecryptionResults?
{
var outLength = Int(0)
let outData = Data(count: input.count + kCCBlockSizeAES128 + 64) // Allocate an extra 64 bytes to allow for 64-byte alignment of the resulting data.
var outPtr: UnsafeMutableRawPointer? = nil
outData.withUnsafeBytes
{ (u8Ptr: UnsafePointer<UInt8>) in
outPtr = UnsafeMutableRawPointer(mutating: u8Ptr)
}
// We want to pass 64 byte aligned data to Tensorflow, so we align the output buffer to that
var offset = unsafeBitCast(outPtr, to: Int.self)
offset = offset % 64 > 0 ? (64 - offset % 64) : 0
var status: CCCryptorStatus = CCCryptorStatus(kCCSuccess)
input.withUnsafeBytes { (encryptedBytes: UnsafePointer<UInt8>!) -> () in
self.iv.withUnsafeBytes { (ivBytes: UnsafePointer<UInt8>!) in
self.key.withUnsafeBytes { (keyBytes: UnsafePointer<UInt8>!) -> () in
status = CCCrypt(operation,
CCAlgorithm(kCCAlgorithmAES128), // algorithm
CCOptions(kCCOptionPKCS7Padding), // options
keyBytes, // key
self.key.count, // keylength
ivBytes, // iv
encryptedBytes, // dataIn
input.count, // dataInLength
outPtr! + offset, // dataOut
input.count + kCCBlockSizeAES128, // dataOutAvailable
&outLength) // dataOutMoved
}
}
}
...
return DecryptionResults(decryptionData: outData, offset: offset, outLength: outLength)
}
I got here because while profiling memory use in Instruments, it seems the input Data isn't being deallocated in the scope in which it’s being allocated, and I'm wondering if something about this code here is hanging on to it.
It seems I also need to nest this yet one more scope to properly use outData.withUnsafeBytes
.
So, a few questions:
- Is there a way to get a
Data
allocated to a 64-byte boundary that's better than this? This current solution makes us pass an offset and “real” length around along with the decrypted data. - Does
withUnsafeBytes
increase the reference count onData
in some unexpected way? - As I’ve lamented several times in the past, working with C and “unsafe” data is very cumbersome in Swift. Is there a more elegant way to do this in Swift 5.1?
UPDATE: After a bit more investigating, it seems I should be able to do this:
let outputRaw = UnsafeMutableRawPointer.allocate(byteCount: input.count + kCCBlockSizeAES128, alignment: 64)
let outputData = Data(bytesNoCopy: outputRaw, count: input.count + kCCBlockSizeAES128, deallocator: .custom({ inPtr, inSize in inPtr.deallocate() }))
Does this seem reasonable? I haven't actually tried it yet.
As always, thank you.