hdoria hdoria - 1 month ago 32
Swift Question

Swift 3: Encode String to UTF-16LE

I need to encode a string to UTF-16LE (and convert to sha1 later), but I'm having some problems. This is what I had tried:

let utf16array = Array("password".utf16)
print(utf16array)
// [112, 97, 115, 115, 119, 111, 114, 100]


But this is what I was expecting:

// [112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111, 0, 114, 0, 100, 0]


Same thing using utf8array:

let utf8array = "password".utf8.map({ $0 as UInt8 })
// [112, 97, 115, 115, 119, 111, 114, 100]


So, this is what I did to "fix" it:

var bytesArray:[UInt16] = []
for byte in utf16array {
bytesArray.append(byte)
bytesArray.append(0)
}
print(bytesArray)
// [112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111, 0, 114, 0, 100, 0]


But I'm sure this is not the right way. Any suggestions?

Answer

You can get a representation as UTF-16LE data with

let password = "password€"
let data = password.data(using: .utf16LittleEndian)!
print(data as NSData)
// <70006100 73007300 77006f00 72006400 ac20>

That would already be sufficient to compute the SHA1 digest (code from How to crypt string to sha1 with Swift?):

var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes { 
    _ = CC_SHA1($0, CC_LONG(data.count), &digest)
}
let hexEncodedDigest = digest.map { String(format: "%02hhx", $0) }.joined()
print(hexEncodedDigest)
// 177f0d080dfe533e102dd67d6321204813cf1b0c

But if you need it as a byte array then

let bytesArray = data.map { $0 }
print(bytesArray)
// [112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111, 0, 114, 0, 100, 0, 172, 32]

would work.

(I have appended a non-ASCII characters for demonstration, € = U+20AC becomes 172, 32.)


If you are curious how to convert the [UInt16] array to an [UInt8] array, this is how you could do it with some pointer juggling (and just a single copy):

let utf16array = Array("password€".utf16)
print(utf16array)
// [112, 97, 115, 115, 119, 111, 114, 100, 8364]

let bytes = Array(utf16array.withUnsafeBufferPointer {
    $0.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: 2 * utf16array.count) {
        UnsafeBufferPointer(start: $0, count: 2 * utf16array.count)
    }
})
print(bytes)
// [112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111, 0, 114, 0, 100, 0, 172, 32]