Как взять N бит из переменной UInt8 в Swift?

Я пытаюсь реализовать декодирование Base32 для приложения, поскольку я учу себя Swift, но я не могу понять, как пойти ниже уровня байтов в этом языке. Было бы удобно, если бы я мог урезать UInt8 до 5 битов и добавить его к объекту данных, с которым я мог бы затем работать.

У меня есть эта функция написана на 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])

Который работает, чтобы вывести шестнадцатеричное представление данных в base32. Я хотел повторить это в Swift (хотя и не конвертировать в шестнадцатеричную часть). Тем не менее, я получил это далеко:

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
...

Есть простой способ сделать это? Я не могу найти что-либо через Google.

1 ответ

Решение

Swift имеет операторы битовых манипуляций (|, &, <<, >>) который может использоваться для извлечения частей байта (и то, является ли это "легким способом" или нет, безусловно, основано на мнении).

Ваш код Python сначала создает строку из всех двоичных цифр, которая затем разбивается на части по 8 бит и преобразуется в шестнадцатеричные значения.

Ниже приведена возможная реализация, в которой не используется промежуточная строка. Вместо этого декодированные биты накапливаются в целое число, и, как только 8 битов собраны, они добавляются в массив результатов.

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)
}

Пример:

print(base32decode(string: "JBSWY3DPEB3W64TMMQQQ") as NSData)
// <48656c6c 6f20776f 726c6421>

(что является "Hello World!").

Интересные части в этой функции

  accum = (accum << 5) | index

который перемещает все биты в accum на 5 позиций слева и устанавливает младшие 5 битов в index, а также

  b32v.append(UInt8(truncatingBitPattern: accum >> (bits - 8)))

который добавляет самые левые 8 действительных битов в accum в массив.

Другие вопросы по тегам