Binary I/O

바이너리 입출력 검증

바이너리 파일에는 구분자와 사람이 읽을 단서가 없다. magic, version, length, endian 같은 계약을 앞에 두어야 안전하게 읽고 거절할 수 있다.

01

헤더 검증

파일이 기대한 포맷인지 magic과 version으로 먼저 확인한다.

02

크기를 제한한다

파일이 말하는 payload 크기를 그대로 믿지 말고 실제 파일 크기와 상한을 비교한다.

03

타입 폭 고정

int, long 대신 uint32_t처럼 폭이 정해진 타입으로 포맷을 만든다.

read
바이트 수 확인 요청한 크기만큼 읽지 못했으면 부분 파일이나 손상으로 본다.
gcount와 상태를 확인한다.
write
명시적 직렬화 필드별로 쓰면 padding과 정렬 의존을 줄일 수 있다.
구조체 통째 쓰기는 조심한다.
endian
바이트 순서 다른 CPU나 파일 버전에서 값 해석이 달라질 수 있다.
포맷 기준을 하나로 정한다.
version
미래 변경 필드 추가와 의미 변경을 안전하게 거절하거나 변환한다.
처음부터 넣는다.

부분 읽기 · 크기 상한 · 이식성 점검

부분 읽기 파일이 짧을 때 성공처럼 처리하지 않는가.
크기 상한 파일 안 length로 바로 대용량 메모리를 할당하지 않는가.
이식성 포맷이 컴파일러 padding이나 플랫폼 endian에 의존하지 않는가.

헤더 먼저

std::uint32_t magic = 0;
in.read(reinterpret_cast<char*>(&magic), sizeof(magic));
if (!in || magic != ExpectedMagic) throw FormatError{
        overflow-wrap: break-word;
        word-break: keep-all;
      };