바이트 중심 프레이밍 문서의 구현 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
"""프레이밍(바이트 중심) 구현과 검증: BISYNC 보초 방법과 DDCMP 바이트 수 방법. 바이트는 정수 0~255."""
SYN, SOH, STX, ETX, DLE = 0x16, 0x01, 0x02, 0x03, 0x10
def bisync_body(data):
"""끝 ETX 방식: 본문의 ETX·DLE 앞에 DLE를 붙이고, 끝은 맨 ETX 하나."""
out = []
for b in data:
if b in (ETX, DLE):
out.append(DLE)
out.append(b)
return out + [ETX]
def bisync_read(stream):
"""DLE 다음 바이트는 글자 그대로, 앞에 DLE가 없는 ETX에서 끝."""
out, i = [], 0
while True:
b = stream[i]
if b == DLE:
out.append(stream[i + 1]); i += 2
elif b == ETX:
return out, i + 1
else:
out.append(b); i += 1
def transparent_body(data):
"""투명 모드: 본문의 DLE만 두 번 쓰고, 끝은 DLE ETX."""
out = []
for b in data:
out += [DLE, DLE] if b == DLE else [b]
return out + [DLE, ETX]
def transparent_read(stream):
out, i = [], 0
while True:
b = stream[i]
if b == DLE:
if stream[i + 1] == ETX:
return out, i + 2
out.append(DLE); i += 2 # DLE DLE → DLE 하나
else:
out.append(b); i += 1
def ddcmp_frame(body):
return [SYN, SYN, 0x81, len(body)] + list(body)
def ddcmp_read(stream):
n = stream[3]
return stream[4:4 + n]
def main():
data = [0x41, ETX, 0x42, DLE, ETX, 0x43] # 본문에 ETX와 DLE가 우연히 들어 있다
f = bisync_body(data)
assert f == [0x41, DLE, ETX, 0x42, DLE, DLE, DLE, ETX, 0x43, ETX]
back, used = bisync_read(f + [0x99, 0x99]) # 뒤에 CRC 등이 와도 끝을 정확히 찾는다
assert back == data and used == len(f)
# 채우기 없이 ETX에서 끝을 찾으면 본문이 잘린다
naive = data + [ETX]
assert naive[:naive.index(ETX)] == [0x41]
# 카드 C2: 본문 [DLE, ETX] → DLE DLE DLE ETX ETX (5바이트)
assert bisync_body([DLE, ETX]) == [DLE, DLE, DLE, ETX, ETX]
# 투명 모드: 본문 ETX는 그대로, DLE만 두 번. 끝은 DLE ETX
tf = transparent_body(data)
assert tf == [0x41, ETX, 0x42, DLE, DLE, ETX, 0x43, DLE, ETX]
assert transparent_read(tf + [0x99])[0] == data
import random
random.seed(44)
for _ in range(2000):
d = [random.choice([ETX, DLE, 0x41, 0x42]) for _ in range(random.randint(0, 20))]
assert bisync_read(bisync_body(d) + [0x77])[0] == d and transparent_read(transparent_body(d) + [0x77])[0] == d
# 바이트 수 방법: 본문에 무엇이 있든 길이로 자른다
fr = ddcmp_frame(data)
assert ddcmp_read(fr) == data
# 길이 칸이 깨지면 엉뚱한 곳에서 자른다 → CRC가 맞지 않아 오류로 드러난다
bad = list(fr); bad[3] = 4
assert ddcmp_read(bad) != data
# 최악의 오버헤드: 본문이 모두 ETX·DLE이면 BISYNC는 두 배
assert len(bisync_body([DLE] * 10)) == 21
print("ALL CHECKS PASSED")
if __name__ == "__main__":
main()