I'm trying to implement a Base32 decoding for an app as I teach myself Swift, but I cannot seem to figure out how to go below the byte level in this language. It would be convenient if I could truncate a UInt8 into 5 bits and append that to a Data object which I could then work with.
I have this function written in Python:
def base32_decode(secret):
b32alphabet = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")
b32v = [b32alphabet.index(x) for x in secret if x != '=']
t1 = ["{0:0>5}".format(bin(v)[2:]) for v in b32v]
t2 = ''.join(t1)
t3 = textwrap.wrap(t2,8)
t4 = [int(v, 2) for v in t3 if len(v) == 8]
t5 = ''.join(["{0:0>2}".format(hex(v)[2:]) for v in t4])
Which works to output the hex representation of the data in base32. I wanted to replicate this in Swift (although not the converting to hex part). However, I got this far:
func base32decode(string: String) -> Data
{
let b32a: Array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "2", "3", "4", "5", "6", "7"]
let complete: NSMutableData = NSMutableData()
var b32v: Array<UInt8> = []
for c in string.characters
{
let index = b32a.index(of: String(c))!
b32v.append(UInt8(index)) // Need to append only the 5 LSB
}
// Return b32v as base 32 decoded data
...
Is there an easy way to do this? I can't find anything through Google.
Swift has bit manipulation operators (|
, &
, <<
, >>
) which
can be used to extract portions of a byte (and whether that is
"an easy way" or not is certainly opinion-based).
Your Python code apparently creates a string of all binary digits first, which is then partitioned into portions of 8 bits and converted to hexadecimal values.
The following is a possible implementation which does not use an intermediate string. Instead the decoded bits are accumulated in an integer, and as soon as 8 bits are collected, these are appended to the result array.
func base32decode(string: String) -> Data {
let b32a = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".characters)
var b32v: [UInt8] = []
var accum = 0
var bits = 0 // # of valid bits in `accum`
for c in string.characters {
if let index = b32a.index(of: c) {
accum = (accum << 5) | index
bits += 5
if bits >= 8 {
b32v.append(UInt8(truncatingBitPattern: accum >> (bits - 8)))
bits -= 8
}
}
}
return Data(bytes: b32v)
}
Example:
print(base32decode(string: "JBSWY3DPEB3W64TMMQQQ") as NSData)
// <48656c6c 6f20776f 726c6421>
(which is "Hello World!").
The interesting parts in that function are
accum = (accum << 5) | index
which moves all bits in accum
by 5 positions to the left and
sets the lowest 5 bits to index
, and
b32v.append(UInt8(truncatingBitPattern: accum >> (bits - 8)))
which appends the left-most 8 valid bits in accum
to the array.