Write array of Float to binary file and read it in swift - ios

How can I write array of Floatto binary file and then read it?
var array: [Float]: [0.1, 0.2, 0.3]
func writeArrayToBinary(array: [Float]) {
//...
}
func readArrayFromBinary() -> [Float] {
//...
}

As you stated in a comment, speed is priority. Then, I suggest you write your array to a binary file (as originally requested), using the Data class, provided with Cocoa.
let url = URL(fileURLWithPath: "myTestFile.myBinExt")
// Writing
var wArray: [Float] = [1.1, 3.7, 2.5, 6.4, 7.8]
let wData = Data(bytes: &wArray, count: wArray.count * MemoryLayout<Float>.stride)
try! wData.write(to: url)
// Reading file
let rData = try! Data(contentsOf: url)
// Converting data, version 1
var rArray: [Float]?
rData.withUnsafeBytes { (bytes: UnsafePointer<Float>) in
rArray = Array(UnsafeBufferPointer(start: bytes, count: rData.count / MemoryLayout<Float>.size))
}
print(rArray!)
// Converting data, version 2
let tPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: rData.count)
rData.copyBytes(to: tPointer, count: rData.count)
defer {
tPointer.deinitialize(count: rData.count)
tPointer.deallocate(capacity: rData.count)
}
var pointer = UnsafeRawPointer(tPointer) // Performs no allocation or copying; no deallocation shall be done.
// MemoryLayout<Float>.size = 4
print(pointer.load(fromByteOffset: 00, as: Float.self))
print(pointer.load(fromByteOffset: 04, as: Float.self))
print(pointer.load(fromByteOffset: 08, as: Float.self))
print(pointer.load(fromByteOffset: 12, as: Float.self))
print(pointer.load(fromByteOffset: 16, as: Float.self))
Output:
[1.10000002, 3.70000005, 2.5, 6.4000001, 7.80000019]
1.1
3.7
2.5
6.4
7.8

Please try this...
var array: [Float] = [0.1, 0.2, 0.3]
func writeArrayToPlist(array: [Float]) {
if let arrayPath: String = createArrayPath() {
(array as NSArray).writeToFile(arrayPath, atomically: false)
}
}
func readArrayFromPlist() -> [Float]? {
if let arrayPath: String = createArrayPath() {
if let arrayFromFile: [Float] = NSArray(contentsOfFile: arrayPath) as? [Float] {
return arrayFromFile
}
}
return nil
}
func createArrayPath () -> String? {
if let docsPath: String = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true).last {
return ((docsPath as NSString).stringByAppendingPathComponent("myArrayFileName") as NSString).stringByAppendingPathExtension("plist")
}
return nil
}

Related

iOS and Android generating different hash values in SHA512

We need to verify hash value coming from the server. Android part is working. On iOS side we use CryptoKit to generate hash value.
Passing as paramater "jsonString" from which the hash key needs to be generated and "hasValue" which needs to be matched. While both functions use the same algorythm the outcome is always different.
let digest = SHA512.hash(data: jsonStringWithSaltBytes)
let hasBytes = digest.bytes
In computeHas function when we try to get the let hasBytes = digest.bytes it always give different HashBytes after SHA512.hash bytes conversion.
import CryptoKit
extension Digest {
var bytes: [UInt8] { Array(makeIterator()) }
var data: Data { Data(bytes) }
}
func generateHasKey(jsonString: String, hasValue: String) -> Bool {
let data = Data(base64Encoded: hasValue)
let hashWithSaltByte: [UInt8] = Array(data!)
let hashSizeByts: Int = 512 / 8
if hashWithSaltByte.count < hashSizeByts {
return false
}
var saltBytesArr: [UInt8] = Array(repeating: 0, count: (hasWithSaltByte.count - hashSizeInByts))
for index in 0..<saltBytes.count {
saltBytesArr[index] = hasWithSaltByte[hashSizeInByts + index]
}
let expectedHasString = computeHas(jsonString: jsonString, saltBytes: saltBytesArr)
if expectedHashString == hasValue {
print("Hash value match..")
return true
}else {
print("Hash value did not match..")
return false
}
}
func computeHash(jsonString: String, saltBytes: [UInt8]) -> String {
let saltBytes = saltBytes
let jsonStringBytes: [UInt8] = Array(jsonString.utf8)
var jsonStringWithSaltBytes: [UInt8] = Array(repeating: 0, count: (jsonStringBytes.count + saltBytes.count))
for index in 0..<jsonStringBytes.count {
jsonStringWithSaltBytes[index] = jsonStringBytes[index]
}
for index in 0..<saltBytes.count {
jsonStringWithSaltBytes[jsonStringBytes.count + index] = saltBytes[index]
}
let digest = SHA512.hash(data: jsonStringWithSaltBytes)
let hashBytes = digest.bytes
var hashWithSaltBytes: [UInt8] = Array(repeating: 0, count: (hashBytes.count + saltBytes.count))
for index in 0..<hasBytes.count {
hashWithSaltBytes[index] = hashBytes[index]
}
for index in 0..<saltBytes.count {
hashWithSaltBytes[hashBytes.count + index] = saltBytes[index]
}
let FinalsaltData = Data(hasWithSaltBytes)
let finalHashString = FinalsaltData.base64EncodedString()
return finalHashString ?? ""
}

How can I encode an array of simd_float4x4 elements in Swift (convert simd_float4x4 to Data)?

I am building an app that captures facetracking data from the iPhone TrueDepth camera.
I need to write this data to files so I can use it as the basis for another app.
Within the app, the data is saved into four separate arrays, one containing ARFaceGeometry objects, and the other three with transform coordinates as simd_float4x4 matrices.
I am converting the arrays into Data objects using archivedData(withRootObject: requiringSecureCoding:) then calling write(to:) on them to create the files.
The file containing the ARFaceGeometry data is written and read back in correctly. But the three simd_float4x4 arrays aren't being written, even though the code for doing so is identical. Along with my print logs, the error being given is 'unrecognized selector sent to instance'.
Properties:
var faceGeometryCapture = [ARFaceGeometry]()
var faceTransformCapture = [simd_float4x4]()
var leftEyeTransformCapture = [simd_float4x4]()
var rightEyeTransformCapture = [simd_float4x4]()
var faceGeometryCaptureFilePath: URL!
var faceTransformCaptureFilePath: URL!
var leftEyeTransformCaptureFilePath: URL!
var rightEyeTransformCaptureFilePath: URL!
Code for establishing file URLs:
let fileManager = FileManager.default
let dirPaths = fileManager.urls(for: .documentDirectory,
in: .userDomainMask)
faceGeometryCaptureFilePath = dirPaths[0].appendingPathComponent("face-geometries.txt")
faceTransformCaptureFilePath = dirPaths[0].appendingPathComponent("face-transforms.txt")
leftEyeTransformCaptureFilePath = dirPaths[0].appendingPathComponent("left-eye-transforms.txt")
rightEyeTransformCaptureFilePath = dirPaths[0].appendingPathComponent("right-eye-transforms.txt")
Code for writing the data to files:
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: faceGeometryCapture, requiringSecureCoding: false)
try data.write(to: faceGeometryCaptureFilePath)
} catch { print("Error writing face geometries to file") }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: faceTransformCapture, requiringSecureCoding: false)
try data.write(to: faceTransformCaptureFilePath)
} catch { print("Error writing face transforms to file") }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: leftEyeTransformCapture, requiringSecureCoding: false)
try data.write(to: leftEyeTransformCaptureFilePath)
} catch { print("Error writing left eye transforms to file") }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: rightEyeTransformCapture, requiringSecureCoding: false)
try data.write(to: rightEyeTransformCaptureFilePath)
} catch { print("Error writing right eye transforms to file") }
I'm guessing it's the simd_float4x4 struct that is causing the issue, as this is the only difference between working and not working. Can anyone confirm and suggest a solution?
Thanks in advance.
As already mentioned in comments structures can't conform to NSCoding but you can make simd_float4x4 conform to Codable and persist its data:
extension simd_float4x4: Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
try self.init(container.decode([SIMD4<Float>].self))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode([columns.0,columns.1, columns.2, columns.3])
}
}
Playground testing:
do {
let vector = simd_float4x4(2.7) // simd_float4x4([[2.7, 0.0, 0.0, 0.0], [0.0, 2.7, 0.0, 0.0], [0.0, 0.0, 2.7, 0.0], [0.0, 0.0, 0.0, 2.7]])
let data = try JSONEncoder().encode(vector) // 111 bytes
let json = String(data: data, encoding: .utf8)
print(json ?? "") // [[[2.7000000476837158,0,0,0],[0,2.7000000476837158,0,0],[0,0,2.7000000476837158,0],[0,0,0,2.7000000476837158]]]\n"
let decoded = try JSONDecoder().decode(simd_float4x4.self, from: data)
print(decoded) // "simd_float4x4([[2.7, 0.0, 0.0, 0.0], [0.0, 2.7, 0.0, 0.0], [0.0, 0.0, 2.7, 0.0], [0.0, 0.0, 0.0, 2.7]])\n"
decoded == vector // true
} catch {
print(error)
}
edit/update:
Another option is to save its raw bytes. It will use only 64 bytes:
extension simd_float4x4: ContiguousBytes {
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
try Swift.withUnsafeBytes(of: self) { try body($0) }
}
}
extension ContiguousBytes {
init<T: ContiguousBytes>(_ bytes: T) {
self = bytes.withUnsafeBytes { $0.load(as: Self.self) }
}
var bytes: [UInt8] { withUnsafeBytes { .init($0) } }
var data: Data { withUnsafeBytes { .init($0) } }
func object<T>() -> T { withUnsafeBytes { $0.load(as: T.self) } }
func objects<T>() -> [T] { withUnsafeBytes { .init($0.bindMemory(to: T.self)) } }
var simdFloat4x4: simd_float4x4 { object() }
var simdFloat4x4Collection: [simd_float4x4] { objects() }
}
extension Array where Element: ContiguousBytes {
var bytes: [UInt8] { withUnsafeBytes { .init($0) } }
var data: Data { withUnsafeBytes { .init($0) } }
}
let vector1 = simd_float4x4(.init(2, 1, 1, 1), .init(1, 2, 1, 1), .init(1, 1, 2, 1), .init(1, 1, 1, 2))
let vector2 = simd_float4x4(.init(3, 1, 1, 1), .init(1, 3, 1, 1), .init(1, 1, 3, 1), .init(1, 1, 1, 3))
let data = [vector1,vector2].data // 128 bytes
let loaded = data.simdFloat4x4Collection
print(loaded) // "[simd_float4x4([[2.0, 1.0, 1.0, 1.0], [1.0, 2.0, 1.0, 1.0], [1.0, 1.0, 2.0, 1.0], [1.0, 1.0, 1.0, 2.0]]), simd_float4x4([[3.0, 1.0, 1.0, 1.0], [1.0, 3.0, 1.0, 1.0], [1.0, 1.0, 3.0, 1.0], [1.0, 1.0, 1.0, 3.0]])]\n"
loaded[0] == vector1 // true
loaded[1] == vector2 // true

How to convert data into little endian format?

var val = 1240;
convert into little endian formate swift 3
Ex: 1500 (0x5DC) to 0xDC050000
let value = UInt16(bigEndian: 1500)
print(String(format:"%04X", value.bigEndian)) //05DC
print(String(format:"%04X", value.littleEndian)) //DC05
Make sure you are actually using the bigEndian initializer.
With 32-bit integers:
let value = UInt32(bigEndian: 1500)
print(String(format:"%08X", value.bigEndian)) //000005DC
print(String(format:"%08X", value.littleEndian)) //DC050000
If you want 1500 as an array of bytes in little-endian order:
var value = UInt32(littleEndian: 1500)
let array = withUnsafeBytes(of: &value) { Array($0) }
If you want that as a Data:
let data = Data(array)
Or, if you really wanted that as a hex string:
let string = array.map { String(format: "%02x", $0) }.joined()
let timeDevide = self.setmiliSecond/100
var newTime = UInt32(littleEndian: timeDevide)
let arrayTime = withUnsafeBytes(of: &newTime)
{Array($0)}
let timeDelayValue = [0x0B] + arrayTime
You can do something like
//: Playground - noun: a place where people can play
import UIKit
extension String {
func hexadecimal() -> Data? {
var data = Data(capacity: count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, range: NSRange(location: 0, length: utf16.count)) { match, _, _ in
let byteString = (self as NSString).substring(with: match!.range)
var num = UInt8(byteString, radix: 16)!
data.append(&num, count: 1)
}
guard !data.isEmpty else { return nil }
return data
}
}
func convertInputValue<T: FixedWidthInteger>(_ inputValue: Data) -> T where T: CVarArg {
let stride = MemoryLayout<T>.stride
assert(inputValue.count % (stride / 2) == 0, "invalid pack size")
let fwInt = T.init(littleEndian: inputValue.withUnsafeBytes { $0.pointee })
let valuefwInt = String(format: "%0\(stride)x", fwInt).capitalized
print(valuefwInt)
return fwInt
}
var inputString = "479F"
var inputValue: Data! = inputString.hexadecimal()
let val: UInt16 = convertInputValue(inputValue) //9F47
inputString = "479F8253"
inputValue = inputString.hexadecimal()
let val2: UInt32 = convertInputValue(inputValue) //53829F47

Swift - Convert values in array to doubles or floats

I have an array with values which are strings (but all the strings are values like 1.0, 2.0, etc). I'm trying to convert those strings into doubles or floats so I can add them all together. How do I do this in swift?
let x = ["1.0", "1.5", "2.0"]
print(x.map {Double($0)!})
update: Xcode 8.3.2 • Swift 3.1
extension Collection where Iterator.Element == String {
var doubleArray: [Double] {
return flatMap{ Double($0) }
}
var floatArray: [Float] {
return flatMap{ Float($0) }
}
}
usage:
let strNumbersArray = ["1.5","2.3","3.7","4.5"] // ["1.5", "2.3", "3.7", "4.5"]
let doublesArray = strNumbersArray.doubleArray // [1.5, 2.3, 3.7, 4.5]
let floatsArray = strNumbersArray.floatArray // [1.5, 2.3, 3.7, 4.5]
let total = doublesArray.reduce(0, +) // 12
let average = total / Double(doublesArray.count) // 3
If you have an Array of Any? where you need to convert all strings from Optional Any to Double:
extension Collection where Iterator.Element == Any? {
var doubleArrayFromStrings: [Double] {
return flatMap{ Double($0 as? String ?? "") }
}
var floatArrayFromStrings: [Float] {
return flatMap{ Float($0 as? String ?? "") }
}
}
usage:
let strNumbersArray:[Any?] = ["1.5","2.3","3.7","4.5", nil] // [{Some "1.5"}, {Some "2.3"}, {Some "3.7"}, {Some "4.5"}, nil]
let doublesArray = strNumbersArray.doubleArrayFromStrings // [1.5, 2.3, 3.7, 4.5]
let floatsArray = strNumbersArray.floatArrayFromStrings // [1.5, 2.3, 3.7, 4.5]
let total = doublesArray.reduce(0, +) // 12
let average = total / Double(doublesArray.count) // 3
Swift 5.1
extension Collection where Iterator.Element == String {
var convertToDouble: [Double] {
return compactMap{ Double($0) }
}
var convertToFloat: [Float] {
return compactMap{ Float($0) }
}
}
Example How to Use
let String_Array = ["1.5","2.3","3.7","4.5"] // ["1.5", "2.3", "3.7", "4.5"]
let Double_Array = String_Array.convertToDouble // [1.5, 2.3, 3.7, 4.5]
let Float_Array = String_Array.convertToFloat. // [1.5, 2.3, 3.7, 4.5]
OR You can do this
let numArray = ["1.0","2.0","3.0"]
let stringFromArray = numberArray[0]
let floatFromString = Float(stringFromArray)
let doubleFromString = Double(stringFromArray)
if you are sure that all of your array is string, you can directly cast your string into float or double.
let numberArray:Array = ["1.0","2.0","3.0"]
let stringFromArray = numberArray[0]
let floatFromString = Float(stringFromArray)
let doubleFromString = Double(stringFromArray)

Epub Font Mangling is not working

I am creating an EPUB 3 reader for iOS using Swift 2.
The problem I'm currently facing is with font obfuscation / font mangling. I've read a tutorial that goes over how to do that in Swift, and integrated it into my project with some adaptations.
When I load an obfuscated epub into my app, the fonts are not loaded correctly and fall back to other system fonts. When I load an epub with the same fonts but not obfuscated, everything looks fine. Obviously, that means there's something wrong with my obfuscation code, but I can't for the life of me find the error.
Here's my code:
public struct Crypto {
public func obfuscateFontIDPF(data:NSData, key:String) -> NSData {
let source = data
var destination = [UInt8]()
let shaKey = key.sha1()
let keyData = shaKey.utf8Array
var arr = [UInt8](count: source.length, repeatedValue: 0)
source.getBytes(&arr, length:source.length)
var outer = 0
while outer < 52 && arr.isEmpty == false {
var inner = 0
while inner < 20 && arr.isEmpty == false {
let byte = arr.removeAtIndex(0) //Assumes read advances file position
let sourceByte = byte
let keyByte = keyData[inner]
let obfuscatedByte = sourceByte ^ keyByte
destination.append(obfuscatedByte)
inner++
}
outer++
}
if arr.isEmpty == false {
while arr.isEmpty == false {
let byte = arr.removeAtIndex(0)
destination.append(byte)
}
}
let newData = NSData(bytes: &destination, length: destination.count*sizeof(UInt8))
return newData
}
}
extension String {
func sha1() -> String {
var selfAsSha1 = ""
if let data = self.dataUsingEncoding(NSUTF8StringEncoding)
{
var digest = [UInt8](count: Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
for index in 0..<CC_SHA1_DIGEST_LENGTH
{
selfAsSha1 += String(format: "%02x", digest[Int(index)])
}
}
return selfAsSha1
}
var utf8Array: [UInt8] {
return Array(utf8)
}
}
And here I call the obfuscation method:
func parserDidEndDocument(parser: NSXMLParser) {
if encryptedFilePaths!.count != 0 {
for file in encryptedFilePaths! {
let epubMainDirectoryPath = NSString(string: epubBook!.epubMainFolderPath!).stringByDeletingLastPathComponent
let fullFilePath = epubMainDirectoryPath.stringByAppendingString("/" + file)
let url = NSURL(fileURLWithPath: fullFilePath)
if let source = NSData(contentsOfURL: url) {
let decryptedFont = Crypto().obfuscateFontIDPF(source, key: self.epubBook!.encryptionKey!)
do {
try decryptedFont.writeToFile(fullFilePath, options: .DataWritingAtomic)
} catch {
print(error)
}
}
}
}
}
If you see where the error might be, please let me know.
I figured it out, here is the working code:
private func obfuscateData(data: NSData, key: String) -> NSData {
var destinationBytes = [UInt8]()
// Key needs to be SHA1 hash with length of exactly 20 chars
let hashedKeyBytes = generateHashedBytesFromString(key)
var sourceBytes = [UInt8](count: data.length, repeatedValue: 0)
data.getBytes(&sourceBytes, length: data.length)
var outerCount = 0
while outerCount < 52 && sourceBytes.isEmpty == false {
var innerCount = 0
while innerCount < 20 && sourceBytes.isEmpty == false {
let sourceByte = sourceBytes.removeAtIndex(0)
let keyByte = hashedKeyBytes[innerCount]
let obfuscatedByte = (sourceByte ^ keyByte)
destinationBytes.append(obfuscatedByte)
innerCount += 1
}
outerCount += 1
}
destinationBytes.appendContentsOf(sourceBytes)
let destinationData = NSData(bytes: &destinationBytes, length: destinationBytes.count*sizeof(UInt8))
sourceBytes.removeAll(keepCapacity: false)
destinationBytes.removeAll(keepCapacity: false)
return destinationData
}
/// Convert the key string to a SHA1 hashed Byte Array
private func generateHashedBytesFromString(string: String) -> [UInt8] {
var resultBytes = [UInt8]()
var hashedString = string.sha1()
for _ in 0.stride(to: hashedString.characters.count, by: 2) {
let character = "0x\(hashedString.returnTwoCharacters())"
resultBytes.append(UInt8(strtod(character, nil)))
}
return resultBytes
}
extension String {
func sha1() -> String {
var selfAsSha1 = ""
if let data = self.dataUsingEncoding(NSUTF8StringEncoding) {
var digest = [UInt8](count: Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
for index in 0..<CC_SHA1_DIGEST_LENGTH {
selfAsSha1 += String(format: "%02x", digest[Int(index)])
}
}
return selfAsSha1
}
mutating func returnTwoCharacters() -> String {
var characters: String = ""
characters.append(self.removeAtIndex(startIndex))
characters.append(self.removeAtIndex(startIndex))
return characters
}
}

Resources