Самый питонный способ реализовать алгоритм заполнения байтов

Я делаю кое-что по последовательному протоколу и хочу реализовать базовый алгоритм заполнения байтов в Python. Я изо всех сил пытаюсь определить, какой самый питонический способ сделать это.

Вставка байтов в основном просто заменяет любые "зарезервированные" байты парой, состоящей из escape-байта и исходного байта, преобразованного обратимым образом (например, xor'ed).

До сих пор у меня было 5 разных подходов, и у каждого из них есть что-то, что мне не нравится в этом:

1 через генератор

def stuff1(bits):
    for byte in bits:
        if byte in _EscapeCodes:
            yield PacketCode.Escape
            yield byte ^ 0xFF
        else:
            yield byte

Это может быть моим любимым, но, может быть, просто потому, что я в некотором роде очарован генераторами, основанными на доходности. Я беспокоился, что генератор замедлится, но на самом деле он второй по скорости.

2 просто байтов ()

def stuff2(bits):
    result = bytes()
    for byte in bits:
        if byte in _EscapeCodes:
            result += bytes([PacketCode.Escape, byte ^ 0xFF])
        else:
            result += bytes([byte])
    return result

Постоянно приходится создавать одноэлементные массивы, чтобы просто выбросить их, потому что я не знаю ни одной операции "копировать с одним дополнительным элементом". Это связано для самого медленного из группы.

3 Используйте байтовый массив ()

def stuff3(bits):
    result = bytearray()
    for byte in bits:
        if byte in _EscapeCodes:
            result.append(PacketCode.Escape)
            result.append(byte ^ 0xFF)
        else:
            result.append(byte)
    return result

Кажется лучше, чем прямой bytes() подход. На самом деле медленнее, чем генератор доходности, и может делать один байт за раз (вместо необходимости в промежуточных наборах из 1 элемента). Но это звучит грубо. Это середина производительности пакета.

4 байта ()

def stuff4(bits):
    bio = BytesIO()
    for byte in bits:
        if byte in _EscapeCodes:
            bio.write(bytes([PacketCode.Escape, byte ^ 0xFF]))
        else:
            bio.write(bytes([byte]))
    return bio.getbuffer()

Мне нравится потоковый подход здесь. Но досадно, что, похоже, что-то вроде write1 API, который может просто добавить 1 байт, поэтому я должен сделать эти промежуточные bytes снова. Если бы был "записать один байт", я бы хотел этого. Это связано для медленных.

5 Используйте replace()

def stuff5(bits):
    escapeStuffed = bytes(bits).replace(bytes([PacketCode.Escape]), bytes([PacketCode.Escape, PacketCode.Escape ^ 0xFF]))
    stopStuffed = escapeStuffed.replace(bytes([PacketCode.Stop]), bytes([PacketCode.Escape, PacketCode.Stop ^ 0xFF]))
    return stopStuffed.replace(bytes([PacketCode.Start]), bytes([PacketCode.Escape, PacketCode.Start ^ 0xFF]))

Это самый быстрый. Но мне не нравится, как код читает и промежуточные развертки.

Я пытался дополнительно использовать translate(), но AFAICT, он может переводить только 1:1 последовательности.

0 ответов

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