진짜 형식

gzip 과 일치하는 압축기와 해제기는 닫힌 루프지만, gzip 은 그릇이지 용도가 아니다. PNG 는 용도다 —— DEFLATE 를 진짜 이미지 형식 안에 감싸고, 거기 이르려면 gzip probe 가 건너뛰게 해 준 압축 주변의 모든 것을 구현해야 했다. gzip 이 little-endian 인 데 대비되는 big-endian 의 chunk 길이와 CRC. gzip 의 CRC-32 가 아니라 Adler-32 체크섬을 갖는 zlib 컨테이너. 그리고 scanline filter —— 왼쪽 픽셀과 위 행을 참조하여 이미지 데이터가 압축되게 만드는 이차원의 바이트 예측. decode 도 encode 도 처음 동작한 빌드에서 참조 라이브러리와 round-trip 했다. 언어에 대한 변경은 0. 압축의 핵심은 그대로 넘어갔고, 새로운 것은 모두 그 주변의 형식이었다.

merepngcompressionfile-formatsdogfood

gzip 을 round-trip 하면 압축이 동작함은 증명되지만, gzip 은 배관이다 —— 압축 스트림을 담는 것만이 일인 그릇. 더 흥미로운 물음은 같은 핵심이 사람들이 실제로 쓰는 형식에 —— DEFLATE 가 요점이 아니라 부품인 형식에 —— 봉사할 수 있는가다. PNG 가 그 형식이다. 그 이미지 데이터는 zlib 로 감싼 DEFLATE 라 지난 두 편의 압축기와 해제기가 그대로 들어맞는다. PNG probe 가 재는 것은 그 밖의 모든 것, 압축 주위에 짜인 형식이며, 언어의 손이 그것을 쥐기에 이미 충분히 갖춰져 있었는가다.

처음으로 big-endian

gzip 은 길이도 체크섬도 little-endian, 하위 바이트부터 저장하고, 지금까지의 모든 바이트 조립 루틴은 그렇게 쓰여 있었다. PNG 는 big-endian 이다. chunk 의 길이, 헤더의 치수, 각 chunk 끝의 CRC 가 모두 최상위 바이트부터 온다. 이것은 작고, 그리고 진짜인 일이다 —— 코드가 여러 바이트 정수를 역순으로 읽고 써야 했던 첫 지점 —— 그리고 떠오른 것은 바이트를 반대 순서로 쓸 필요뿐이었다. chunk 구조 자체, 길이에 이어 4 글자의 형, 이어 데이터, 이어 형과 데이터에 걸리는 CRC-32 는 명세에서 바이트 vector 위의 중첩 루프로 곧장 옮겨진다.

다른 컨테이너, 다른 체크섬

이미지 chunk 안에서 픽셀 데이터는 날것의 DEFLATE 가 아니라 zlib 로 감싼 DEFLATE 다. 2 바이트 헤더, 압축 스트림, 그리고 Adler-32 체크섬 —— gzip 도 PNG 의 chunk 틀도 쓰는 CRC-32 와는 다른 알고리즘이다. 복호는 DEFLATE 페이로드를 읽기만 하면 되므로 decoder 는 그 검증을 건너뛸 수 있지만, encoder 는 못 건너뛴다. 올바른 Adler-32 가 없는 PNG 는 거부된다. Adler-32 의 구현 —— 소수를 법으로 하는 두 개의 달리는 합, 하나는 바이트의, 하나는 첫 합의 —— 은 몇 줄이었지만, 이 한 형식이 몇 개의 서로 다른 체크섬을 요구했는지는 적을 가치가 있다. 컨테이너에서는 chunk 마다의 CRC-32, zlib 층에서는 비압축 픽셀에 걸리는 Adler-32. 진짜 형식은 작은 합의의 쌓임이고, 각각이 고유의 산술을 가진다.

scanline filter 는 이차원의 바이트 산술

gzip 에 대응물이 없는 부분이 filter 다. 압축 전에 각 픽셀 행은 예측기로 변환된다 —— 이미 본 이웃, 왼쪽 픽셀, 위 행의 픽셀, 왼쪽 위 픽셀에서 만든 추정을 빼서, 날것의 색값보다 훨씬 잘 압축되는 작은 잔차를 남긴다. 그런 filter 는 다섯 개이고, 진짜 encoder 는 행마다 고른다. decoder 는 골라진 어느 것이든 그것을 뒤집어, 각 scanline 첫머리의 filter 바이트를 읽고 예측을 픽셀마다 되돌려야 한다. 이것은 아크 전체에서 처음의 진정으로 이차원인 바이트 계산이다 —— 각 출력 바이트는 그 위와 왼쪽의 바이트에 의존한다 —— 그리고 바로, 미묘하게 틀리기 쉬운 index 가 많은 루프다. 검증은 참조 라이브러리가 쓴 PNG 를, 그 적응적인 행마다의 filter 선택째 복호하고 픽셀을 알려진 체크섬과 대조하는 것이었다. 일치했다.

양방향이 닫혔다. decoder 는 filter 를 다섯 종 모두 순환시킨 자작 RGB 이미지, 자작 RGBA 이미지, 참조 라이브러리가 적응 filter 로 부호화한 이미지를 읽어 —— 모두 일치하는 픽셀 체크섬으로, interp 에서도 C backend 에서도. encoder 는 자작 decoder · 참조 라이브러리 · 원본 데이터가 모두 일치하고 OS 가 유효한 PNG 로 인식하는 그라데이션을 썼다. 이 어느 것도 언어에 대한 변경을 요구하지 않았다. 압축의 핵심은 변하지 않고 넘어갔고, big-endian 바이트, 두 번째 체크섬, 이차원 예측이 모두 이미 있는 것 안에 들어맞았다. gzip 아크는 진짜 형식에 이르렀다. encode 도 decode 도 순수 Mere 로, 모두가 쓰는 도구와 바이트 호환으로.

← Back to Mere: 언어를 만들다