Самый питонный способ реализовать алгоритм заполнения байтов
Я делаю кое-что по последовательному протоколу и хочу реализовать базовый алгоритм заполнения байтов в 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 последовательности.