클러스터와 FAT 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
# 48.클러스터와 FAT 문서의 계산 확인. 실행: python3 48_fat-file-system_verify.py
# 16비트 MS-DOS 디스크를 실제로 읽지 않는다. 문서 그림과 표의 숫자만 파이썬으로 다시 센다.
# 클러스터 번호는 문서의 표대로 1부터 센다.
import math
EOF_MARK = "eof"
def clusters_needed(file_bytes, cluster_bytes):
"""파일이 차지하는 클러스터 수. 0바이트여도 문서의 규칙대로 최소 1개."""
return max(1, math.ceil(file_bytes / cluster_bytes))
def wasted(file_bytes, cluster_bytes):
return clusters_needed(file_bytes, cluster_bytes) * cluster_bytes - file_bytes
def follow_chain(fat, start):
"""FAT 칸에 적힌 '다음 클러스터 번호'를 eof가 나올 때까지 따라간다."""
chain, cur, seen = [], start, set()
while True:
assert cur not in seen, "사슬이 돈다"
seen.add(cur)
chain.append(cur)
nxt = fat[cur]
if nxt == EOF_MARK:
return chain
cur = nxt
if __name__ == "__main__":
# 8,200바이트 파일, 4K 클러스터
assert clusters_needed(8200, 4096) == 3
assert 8200 - 2 * 4096 == 8
assert wasted(8200, 4096) == 4088
# 경계: 딱 맞는 크기, 1바이트
assert clusters_needed(8192, 4096) == 2 and wasted(8192, 4096) == 0
assert clusters_needed(1, 4096) == 1 and wasted(1, 4096) == 4095
# 섹터 4개가 클러스터 하나. 섹터 512바이트라면 2,048바이트
assert 4 * 512 == 2048
# 16칸 표: File1 (시작 1, 7개), File2 (시작 5, 5개)
fat1 = {1: 2, 2: 3, 3: 4, 4: 8, 8: 9, 9: 10, 10: EOF_MARK}
fat2 = {5: 6, 6: 7, 7: 11, 11: 12, 12: EOF_MARK}
c1 = follow_chain(fat1, 1)
c2 = follow_chain(fat2, 5)
assert c1 == [1, 2, 3, 4, 8, 9, 10] and len(c1) == 7
assert c2 == [5, 6, 7, 11, 12] and len(c2) == 5
# 두 파일은 한 표를 같이 쓴다. 칸이 겹치지 않는다.
assert not set(c1) & set(c2)
fat = {**fat1, **fat2}
assert follow_chain(fat, 1) == c1 and follow_chain(fat, 5) == c2
# 16칸 가운데 빈 칸
free = [n for n in range(1, 17) if n not in fat]
assert free == [13, 14, 15, 16]
# File1의 다섯 번째 클러스터는 사슬을 4번 따라가야 나온다
assert c1[4] == 8
# FAT-12/16/32: 칸 하나가 n비트면 적을 수 있는 번호는 2^n가지
assert (2 ** 12, 2 ** 16, 2 ** 32) == (4096, 65536, 4294967296)
# 카드 C2: 10,000바이트 파일, 4K 클러스터
assert clusters_needed(10000, 4096) == 3 and wasted(10000, 4096) == 2288
# 카드 C3: File2의 네 번째 클러스터
assert c2[3] == 11
print("ALL CHECKS PASSED")