swiftutf-8utf-16le

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?


Solution

  • 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]