UTF-8000: Unlimited UTF-8
UTF-8000: 무제한 UTF-8
UTF-8000은 UTF-8의 자기 동기화·자기 구두점 구조를 유지하면서 코드 유닛 길이를 임의로 확장하는 인코딩 설계입니다. 길이에 따라 시작 비트를 여러 바이트에 분산해 임의로 큰 코드포인트를 표현하며, 기존 UTF-8의 정렬·오버롱 인코딩 규칙도 계승합니다.
- 주제
AI 요약
UTF-8000은 현재 UTF-8이 사용하는 최대 4바이트라는 실용적 제한을 넘어, 임의로 큰 정수형 코드포인트를 인코딩하기 위한 확장 설계입니다. 핵심은 UTF-8의 바이트 단위 자기 동기화(self-synchronization)와 코드 유닛 길이를 스스로 나타내는 자기 구두점(self-punctuation)을 그대로 유지하면서, 코드 유닛의 시작을 표시하는 비트를 여러 바이트로 분산하는 것입니다. 문서에서는 이 구조가 UTF-8에 새 특수 규칙을 덧붙이는 방식이 아니라, 기존 설계가 이미 갖고 있던 확장성을 끝까지 밀어붙인 결과라고 설명합니다.
■ 바이트 구조와 임의 길이 확장
ASCII는 기존 UTF-8과 동일하게 `0xxxxxxx` 한 바이트로 표현합니다. 다중 바이트 코드 유닛에서는 모든 바이트의 상위 비트가 역할을 나눕니다. 첫 바이트의 자기 동기화 접두부는 `11`이고, 연속 바이트(continuation byte)의 접두부는 `10`입니다. 따라서 임의 위치에서 바이트를 읽더라도 해당 바이트가 코드 유닛의 첫 바이트인지, 뒤따르는 연속 바이트인지 즉시 구별할 수 있습니다. ASCII의 접두부는 `0`이므로 ASCII와 다중 바이트 코드 유닛도 서로 혼동되지 않습니다.
UTF-8에서는 코드 유닛의 길이를 첫 바이트에 있는 연속된 1 비트와 마지막 0 비트로 나타냅니다. 예를 들어 2바이트는 `110xxxxx`, 3바이트는 `1110xxxx`, 4바이트는 `11110xxx`입니다. UTF-8000은 이 시작 비트(start bits)를 여러 바이트에 걸쳐 이어 붙입니다. n바이트 코드 유닛에는 n-1개의 시작 비트가 있으며, 그 형태는 `111...10`입니다. n이 8보다 작으면 시작 비트가 첫 바이트 안에 모두 들어가지만, n이 8 이상이면 첫 몇 바이트에 시작 비트를 채워 넣습니다. 그래서 5바이트는 `111110xx`, 6바이트는 `1111110x`, 7바이트는 `11111110`으로 시작하고, 8바이트부터는 첫 바이트가 `11111111`인 뒤 다음 바이트에도 길이 정보가 이어집니다. 문서의 22바이트 예시에서는 첫 바이트 이후의 일부 연속 바이트가 동시에 시작 바이트 역할도 합니다.
이 방식에서는 시작 바이트가 코드 유닛의 맨 앞에 연속해서 배치됩니다. 첫 바이트는 항상 시작 바이트이지만, UTF-8000에서는 그 뒤의 연속 바이트 일부도 시작 비트를 포함할 수 있으므로 ‘첫 바이트’와 ‘시작 바이트’가 같은 말이 아닙니다. 문서가 제시하는 22바이트 예시의 네 번째 바이트는 연속 바이트이면서 시작 바이트이고, 마지막 시작 바이트이기도 하며, 콘텐츠 비트까지 포함합니다. 또한 마지막 시작 바이트와 첫 번째 비시작 바이트 사이에 의무 콘텐츠 비트가 걸쳐 있을 수 있습니다.
■ 콘텐츠 비트와 오버롱 인코딩
ASCII에는 7개의 콘텐츠 비트가 있습니다. 2바이트 이상의 UTF-8000 코드 유닛은 일반적으로 `5n+1`개의 콘텐츠 비트를 가지며, 여기서 n은 코드 유닛의 바이트 수입니다. 3바이트 UTF-8은 16비트, 5바이트 UTF-8000은 26비트, 22바이트 코드 유닛은 111비트를 저장할 수 있습니다. 이 식은 n개의 8비트 바이트에서 자기 동기화 접두부 2n개와 시작 비트 n-1개를 제외한 결과인 `8n - 2n - (n-1) = 5n+1`에서 나옵니다. 코드 유닛이 길어질 때마다 연속 바이트 하나가 추가되어 6비트를 얻지만, 시작 비트가 하나 늘어나므로 실제 콘텐츠 비트 증가는 5비트입니다. 정보 밀도는 ASCII에서 7/8, 즉 87.5%이며, n바이트 다중 코드 유닛에서는 `(5n+1)/(8n)`입니다. n이 커지면 이 값은 62.5%에 가까워집니다.
오버롱 인코딩(overlong encoding)을 막기 위한 의무 콘텐츠 비트(mandatory content bits)도 기존 UTF-8의 규칙을 따릅니다. ASCII에는 의무 콘텐츠 비트가 없고, 2바이트 코드 유닛에는 4개, 3바이트 이상에는 5개가 있습니다. 가장 짧은 표현이 가능한 값을 더 긴 코드 유닛으로 표현하면 안 되기 때문입니다. 예를 들어 ASCII 대문자 A인 `0x41`을 2바이트 UTF-8의 `1100000110000001`로 표현하면 첫 부분의 의무 콘텐츠 비트가 모두 0이므로 잘못된 오버롱 인코딩입니다. 이 규칙은 표현의 유일성을 보장하고, 서로 다른 바이트열이 같은 코드포인트로 해석되는 문제를 막습니다. 그 결과 `0xC0`과 `0xC1`은 어떤 유효한 UTF-8000 코드 유닛에도 나타날 수 없는 바이트가 됩니다.
■ 디코딩, 무작위 접근과 정렬
자기 동기화 특성 덕분에 디코더는 파일이나 스트림의 임의 바이트 위치에서 이전 상태를 모두 처리하지 않고도 복구할 수 있습니다. 현재 바이트가 ASCII 또는 다중 바이트의 첫 바이트라면 즉시 새 코드 유닛을 시작할 수 있고, 연속 바이트라면 다음 첫 바이트를 찾을 때까지 이동하면 됩니다. 문서는 오류가 발생한 스트림에서 잘못된 `0xC0` 같은 바이트를 만났을 때 `U+FFFD`로 대체한 뒤 다음 첫 바이트에서 다시 디코딩을 시작하는 오류 복구 사례를 설명합니다. 시작 비트의 `111...10` 패턴은 코드 유닛이 몇 바이트인지도 스스로 나타내므로, 현재 유닛이 끝났는지 확인하기 위해 다음 유닛의 첫 바이트까지 미리 읽을 필요가 없습니다.
UTF-8000은 UTF-8의 바이트 정렬 특성도 유지한다고 설명합니다. 첫 바이트의 자기 동기화 접두부와 시작 비트가 코드 유닛 길이에 따라 단조 증가하고, 콘텐츠 비트 역시 코드포인트 값에 대응하도록 배치되기 때문에 C 표준 라이브러리의 `strcmp(3)`로 인코딩된 문자열을 비교하면 실제 디코딩 없이도 코드포인트 순서에 해당하는 비교가 가능합니다. 또한 최소 단위가 1바이트이므로 UTF-16처럼 바이트 순서를 정하는 엔디언(endianness)이 필요하지 않습니다.
■ 길이 제한과 계산 예시
UTF-8000 자체는 코드 유닛 길이에 이론적 상한을 두지 않습니다. 다만 실제 디코더는 처리 비용과 구현 복잡성을 고려해 합리적인 최대 길이를 정할 수 있습니다. 문서는 콘텐츠 비트 수 `5n+1`이 2의 거듭제곱이 되는 길이를 예로 듭니다. `2^N = 1 mod 5`가 되려면 N이 4의 배수여야 하므로, N=4K일 때 `5n+1 = 2^(4K) = 16^K`가 됩니다. 이에 따라 16비트를 담는 3바이트, 256비트를 담는 51바이트, 4096비트를 담는 819바이트, 65536비트를 담는 13107바이트 코드 유닛을 만들 수 있습니다. 다만 작은 정수를 이런 긴 코드 유닛에 넣을 때는 오버롱 인코딩이 되므로 허용해서는 안 되며, 가변 길이 구조를 이용해 값에 맞는 가장 짧은 표현을 사용해야 합니다.
BOM(Byte Order Mark)은 아직 확정된 규칙이라기보다 검토 대상으로 제시됩니다. UTF-8과 구분하기 위한 별도 BOM을 두거나, 최대 코드 유닛 길이를 표시하기 위해 4 이상인 각 N에 대해 여러 BOM을 둘 수 있다는 아이디어가 언급됩니다. 문서는 또한 현재 UTF-8의 4바이트 제한이 인코딩 구조 자체의 필연적 한계가 아니라 Unicode와 UTF-16 호환성을 위해 정해진 범위라고 설명합니다. UTF-8000은 Unicode Consortium의 표준이나 승인된 인코딩이 아니라, UTF-8의 구조를 임의 정밀도의 정수 인코딩까지 확장해 본 설계입니다.
■ Hacker News 반응
• @sph — “UTF-8000은 Unicode Consortium이 승인하거나 대표하는 방식이 전혀 아닙니다. 그들이 이모지 범위를 확장하고, 과거와 미래의 모든 가상 언어와 새소리·개 짖는 소리를 위한 공간을 배정하기 전까지는 말입니다. 컨소시엄의 누군가는 새로 생긴 공간을 보고 손을 비비며 기뻐하고 있을 것입니다. 하지만 솔직히 멋진 해킹입니다! 큰 수를 바이트로 인코딩하는 방법을 발명했다면, 왜 자신을 24비트 수로 제한합니까?”
• @flohofwoe — “...24비트 수라고요? 기술적으로 현재 UTF-8은 현재 UNICODE 범위 때문에 21비트까지만 갑니다. 하지만 인코딩 자체에 그런 제한이 있는 것은 아닙니다. 전통적인 UTF-8의 ‘단일 선행 바이트(single lead byte)’ 방식을 사용하면 페이로드는 36비트까지 갈 수 있습니다.”
• @throw0101a — “24비트로 제한한 이유는 UTF-16과의 호환성 때문입니다. 문자는 `0000-10FFFF` 범위, 즉 UTF-16이 접근할 수 있는 범위로 제한되었습니다. 원래 사양은 UTF-32/UCS-4 범위인 31비트를 사용했습니다.”
• @sharktheone — “그렇게 해도 크게 달라지지는 않을 것이라고 생각합니다. 사람들은 더 많은 그래핌 클러스터(grapheme cluster)를 사용하게 될 뿐입니다. 이모지는 이미 Zero-Width-Joiner를 적극적으로 사용하고 있습니다. 예를 들어 여성 소방관은 여성 이모지와 ZWJ, 소방차를 조합합니다. 물론 UTF-8000 방식이 인코딩 크기 측면에서는 훨씬 낫습니다.”
• @mitxela — “미래에는 여성 소방차를 어떻게 인코딩할지 궁금합니다.”
원문: UTF-8000 / 번역·요약: Trawling