Binary I/O

바이너리 저장 계약

바이너리 파일은 빠르고 작지만 사람이 읽어 주지 않는다. 포맷 헤더, 크기, 정렬, 엔디언을 코드가 직접 책임져야 한다.

01

바이너리 헤더 해석

magic number와 version으로 이 파일을 현재 코드가 해석할 수 있는지 확인한다.

파일명 확장자는 신뢰 경계가 아니다.
02

길이 검증

payload byte 수가 실제 파일 크기와 맞는지 비교하고, 과도한 길이는 할당 전에 거절한다.

03

자료형 명시

uint32_t처럼 폭이 고정된 타입으로 저장하고, 문자열과 vector는 길이 뒤에 원시 바이트를 둔다.

텍스트와 차이
구분자가 없다 줄바꿈이나 공백 파싱 대신 정해진 byte 수만큼 읽는다.
read 결과 바이트 수를 반드시 확인한다.
이식성
엔디언과 폭 int, long의 크기와 byte order는 환경마다 달라질 수 있다.
파일 포맷에는 고정 폭 타입을 쓴다.
성능
큰 덩어리 단위 작은 read/write 반복보다 버퍼 단위 처리가 시스템 호출을 줄인다.
포맷 검증을 빼면 빠른 실패가 늦어진다.
보안
입력은 적대적 파일 안의 길이 값으로 바로 메모리를 잡으면 OOM이나 overflow가 생긴다.
상한을 먼저 둔다.

파일 헤더 · 부분 읽기 · 구조체 저장 점검

파일 헤더 magic과 version이 없으면 미래의 포맷 변경을 안전하게 거절할 수 없다.
부분 읽기 read 실패와 EOF를 구분하고, gcount 또는 스트림 상태를 확인한다.
구조체 저장 reinterpret_cast로 구조체 전체를 쓰는 코드는 padding 의존성을 남기는지 확인한다.

작은 포맷 헤더

struct Header {
    std::uint32_t magic;   // 'NHC0' 같은 식별자
    std::uint16_t version;
    std::uint32_t payloadSize;
        overflow-wrap: break-word;
        word-break: keep-all;
      };

// 헤더 검증 뒤 payloadSize 상한을 검사하고 본문을 읽는다.