rubrapack 매뉴얼←↑→

25 압축

MSI 의 파일은 캐비닛에, MSIX 의 파일은 ZIP 압축 파일에 담겨 옮겨지고, 둘 다 보통 압축되어 있다: 더 적은 바이트로 쓰이고, 거기서 원래 것이 정확히 되살아난다. 이 장은 둘이 함께 쓰는 방법인 deflate 가 그것을 어떻게 하는지를 실제 예의 비트까지 내려가 보인다.

25.1 데이터를 줄일 수 있는 까닭#

손실 없는 압축이 되는 것은 실제 데이터가 무작위가 아니기 때문이다. 두 가지가 되풀이된다:

  1. 나열. 프로그램의 기계어는 명령 무늬를 되풀이하고, 글은 낱말을 되풀이하고, 파란 하늘 그림은 같은 색을 되풀이한다.
  2. 기호, 고르지 않게. 영어 글에서 e 는 z 보다 훨씬 흔하고, 프로그램에서는 바이트 00 이 아주 흔하다.

deflate 는 둘을 차례로 쓴다: LZ77 이 되풀이된 나열을 짧은 참조로 바꾸고, 이어서 허프만 부호가 흔한 기호를 더 적은 비트로 쓴다.

어느 쪽도 없는 데이터 - 무작위 수, 또는 이미 압축된 데이터(PNG, JPEG 그림, ZIP 파일, 동영상) - 는 줄일 수 없다. 해 보면 시간이 들고 몇 바이트가 늘어난다.

25.2 LZ77: "앞에서 베껴 와라"#

LZ77(Lempel 과 Ziv, 1977년)은 데이터를 처음부터 읽다가, 다음 바이트들이 조금 전에 나온 적이 있으면 대신 뒤 참조를 쓴다: "거리만큼 뒤로 가서 길이만큼 베껴라". 나머지는 글자 그대로(literal) 한 바이트씩 쓴다.

12 바이트 abcabcabcabc 를 보자. deflate 는 이것을 글자 넷과 참조 하나로 쓴다:

  literal a
  literal b
  literal c
  literal a
  copy: distance 3, length 8

베끼는 것은 자기가 만드는 것과 겹쳐도 된다: 3 바이트 뒤로 가면 bca 에 닿고, 한 바이트씩 베끼면 방금 쓴 바이트를 계속 읽게 된다:

  written so far:  a b c a
                     ^ start 3 back
  copy 8:          a b c a b c a b c a b c
                           \_______________/ 8 copied bytes

그래서 아무리 긴 반복도 짧은 참조 하나다. deflate 는 거리 32,768 바이트(창)까지, 길이 3 에서 258 까지를 쓴다.

25.3 허프만 부호: 흔한 기호에 짧은 부호#

LZ77 을 지나면 데이터는 기호의 줄이 된다: 글자 바이트, 길이, 거리. 보통의 바이트는 무엇이든 8비트를 쓴다. 허프만 부호는 기호마다 비트 수를 따로 준다 - 잦은 것에는 적게 - 그리고 어떤 부호도 다른 부호의 앞부분이 되지 않게 해서, 읽는 쪽이 언제나 한 부호가 어디서 끝나는지 안다.

기호 넷으로 된 작은 예:

기호빈도고정 2비트 부호허프만 부호
A50%000
B25%0110
C12.5%10110
D12.5%11111

평균하면 기호 하나에 2비트 대신 0.5 x 1 + 0.25 x 2 + 0.125 x 3 + 0.125 x 3 = 1.75 비트다. ABAD 는 0 10 0 111 이 되고, 0100111 을 왼쪽부터 읽으면 A, B, A, D 로만 읽힌다.

deflate 에는 부호 표가 두 가지다: 표준에 한 번 정해 둔 고정 표(작은 데이터에 좋다)와, 블록마다 그 블록의 기호 수를 세어 만들어 블록 앞에 저장하는 동적 표(큰 데이터에 좋다).

25.4 deflate, 비트 하나하나#

deflate(RFC 1951)는 블록을 쓴다. 블록은 저마다 세 비트짜리 머리로 시작한다:

비트뜻
1비트: BFINAL1 = 마지막 블록
2비트: BTYPE0 = 저장(바이트 그대로), 1 = 고정 허프만 부호, 2 = 동적 허프만 부호

고정 표의 기호:

기호뜻부호 길이부호
0-143글자 바이트 0-1438비트00110000 + 기호
144-255글자 바이트 144-2559비트110010000 + (기호 - 144)
256블록 끝7비트0000000
257-279길이(와 추가 비트)7비트0000001 ...
280-287길이(와 추가 비트)8비트11000000 ...

길이 뒤에는 5비트 거리 부호가 오고, 어떤 부호는 범위 안의 정확한 값을 고르려고 추가 비트를 몇 개 더 붙인다.

abcabcabcabc 를 deflate 로 압축하면 이렇다 - 열두 바이트 대신 일곱 바이트:

4b 4c 4a 4e 84 21 00

deflate 는 바이트마다 가장 낮은 비트(비트 0, 2장)부터 채운다. 그 순서로 풀어 쓰면 비트의 줄은:

byte 4b = 01001011  -> bits in order: 1 1 0 1 0 0 1 0
byte 4c = 01001100  -> bits in order: 0 0 1 1 0 0 1 0
...

이고, 이렇게 읽힌다:

비트(순서대로)뜻
1BFINAL = 1: 마지막 블록
1 0BTYPE = 1(두 비트를 낮은 비트부터 수로 읽는다): 고정 부호
10010001글자 a (0x61 = 97. 00110000 + 97 = 10010001)
10010010글자 b
10010011글자 c
10010001글자 a
0000110기호 262: 길이 8
00010거리 부호 2: 거리 3
0000000기호 256: 블록 끝

3 + 4 x 8 + 7 + 5 + 7 = 54 비트이고, 일곱째 바이트의 마지막 두 비트는 쓰지 않는다. (허프만 부호만은 deflate 가 부호의 가장 높은 비트부터 쓴다. BTYPE 이나 추가 비트 같은 수는 가장 낮은 비트부터다. 제4부가 모든 표를 적어 두었다.)

25.5 MSZIP: 캐비닛 속의 deflate#

캐비닛(.cab)은 파일 데이터를 32 KiB 이하의 블록으로 나눈다. MSZIP 에서는 블록마다 두 글자 CK(43 4b) 뒤에 deflate 스트림 하나가 오고, 블록은 앞 블록 안을 참조할 수 있다. 원본의 compress = "mszip:6" 은 9 단계 중 6 의 노력으로 하는 deflate 다: 수가 클수록 일치를 더 오래 찾는다 - 결과는 작아지고 시간은 더 든다 - 풀리는 방식은 같다.

25.6 MSIX 속의 deflate#

MSIX 는 ZIP 압축 파일이고, ZIP 은 파일마다 그대로("저장", 방법 0) 또는 deflate 로 압축해(방법 8) 담는다. 튜토리얼의 hello.msix(17장):

파일크기패키지 안
hello.exe17,9206,706deflate: 37%
Registry.dat8,192430deflate: 대부분 0 바이트라 5%
AppxManifest.xml2,9601,010deflate: 글이라 34%
Assets\Square150x150.png301301저장: PNG 는 이미 압축되어 있다
guide.txt614deflate: 너무 작아 얻는 것이 없다

마지막 줄이 아주 작은 데이터를 압축하는 비용을 보인다: deflate 블록에는 머리 몇 비트와 끝 부호가 있고, 여섯 바이트에는 되풀이할 것이 없다.

25.7 쓰이는 곳#