Кодирование и декодирование Base91

Конвертируйте текст или двоичные байты в basE91: 13 бит входа на 2 печатных символа, что делает его самым плотным из повседневных печатных кодирований — накладные расходы около 23% против 33% у Base64. Текст обрабатывается как байты UTF-8; режим шестнадцатеричных байтов предназначен для двоичных данных. Также: Base64 | Base32 / Base58 | Ascii85.
Вся обработка выполняется локально в вашем браузере — ваши данные никогда не загружаются ни на один сервер.

Никогда не слышали о Base91? Что такое Base91 — простым языком (с разобранным примером, алфавитом и подводными камнями).
Как это работает: Вход превращается в байты, затем упаковывается в группы по 13 бит, каждая из которых становится двумя символами, а там, где короткая форма была бы неоднозначной, используется группа из 14 бит.
Вход — это:
Как это работает: Пробельные символы игнорируются, и каждый символ ищется в алфавите, напечатанном ниже на этой странице. Неизвестный символ сообщается вместе с его позицией.
Результат в виде:
Ctrl+Enter во входном поле запускает активный режим.
Выберите «Кодировать» или «Декодировать» и нажмите кнопку действия. Самопроверка выполняет 130 циклов кодирования и декодирования и выводит количество проверок здесь.
Алфавит, приведённый полностью: у Base91 нет органа стандартизации, поэтому алфавит не гарантирован в разных реализациях. Эта страница использует алфавит эталонной реализации, и каждое значение ниже получено ровно из 91 символа, перечисленного в таблице.
Зелёные ячейки — символы, которые безопасно вставлять в текст HTML или в запрос URL; оранжевые необходимо экранировать или кодировать в процентах (см. примечания под таблицей).
Зелёные ячейки — символы, которые безопасно помещать в текст HTML или в строку запроса URL; оранжевые необходимо экранировать или кодировать процентами (см. примечания под таблицей).
Измеренные размеры для одного и того же входа из 92 байт:
Количество символов, измеренное здесь для строки. Base91 выигрывает, потому что 912 = 8281 покрывает 8192 значения 13 бит, тогда как Base64 тратит 4 символа всего на 24 бита.
Что такое Base91?

Base91 — способ записи произвольных байтов, используя только печатные символы ASCII; та же задача, что и у Base64, но он помещает больше бит в каждый символ. Вместо того чтобы нарезать вход на группы по 6 бит и печатать по одному символу на группу, он собирает биты, пока их не станет не меньше 13, а затем печатает два символа, которые вместе задают значение этих 13 (или 14) бит.

Два символа из алфавита в 91 символ могут задать 91 × 91 = 8281 различных значений, а 213 = 8192, поэтому два символа всегда имеют место для 13 бит — с 89 значениями в запасе. В этом весь приём: 13 бит на 2 символа — это 6,5 бита на символ против 6 у Base64, и накладные расходы примерно 23% вместо 33%.

Версия на 30 секунд: test превращается в fPNKd, а Hello, World! — в >OwJh>}AQ;r@@Y?F. Декодирование — точный обратный ход, и ему нужен тот же алфавит из 91 символа: другой алфавит даёт из тех же байтов другой результат.
Откуда берутся 13 и 14 бит

Пара символов может задать 91 × 91 = 8281 значение, что больше 213 = 8192, но меньше 214 = 16384. Поэтому пара символов всегда может нести 13 бит, а иногда и 14. Кодировщик сначала пробует 13 бит: если значение из 13 бит больше 88, он печатает это значение двумя символами; иначе он берёт 14-й бит и печатает полученное 14-битное значение.

Граница 88 не случайна. Наибольшее значение, которое могут задать два символа, — это 90 + 91 × 90 = 8280, а 8192 + 89 = 8281 уже выходит за него. Поэтому никакая группа из 14 бит никогда не может иметь младшее 13-битное окно 89 или больше, а значит, декодер различает два случая, глядя только на само значение:

Что видит декодерСъеденные битыПочему
значение & 8191 равно от 89 до 819113 битЗначения от 89 и выше могут появиться только из ветки 13 бит, потому что 14-битная группа с младшим 13-битным окном такой величины должна была бы быть не меньше 8281 — а такое число не может задать никакая пара символов.
значение & 8191 равно от 0 до 8814 битКодировщик переходит к 14 битам, только если чтение в 13 бит дало 88 или меньше, поэтому эта ветка является запасной и ничем иным не бывает.

Всё, что остаётся в конце — от 1 до 13 бит, — печатается одним символом, а вторым символом, когда остаток действительно требовал больше 7 бит. Поэтому длина закодированной строки растёт шагами по два символа и иногда заканчивается нечётным количеством.

Алфавит и три символа, которые он опускает

Печатный ASCII содержит 94 символа, от ! (0x21) до ~ (0x7E). Base91 использует 91 из них, а три оставшихся — дефис -, апостроф ' и обратная косая черта \. Буквы, цифры и почти вся пунктуация входят в алфавит ровно один раз, в порядке, показанном в таблице выше; с 92-го по 94-й печатные символы просто не используются.

Поскольку алфавит — фиксированный порядок, символ для значения находится поиском, а не по формуле. Две реализации, которые упорядочивают алфавит по-разному, обе будут называться «Base91» и не смогут прочитать вывод друг друга — это самый частый источник запутанных результатов. Эта страница печатает свой алфавит, чтобы результат всегда можно было проследить до него.

Base91 рядом с другими печатными кодировками
КодировкаБит на символРазмер относительно входаПримечания
Base16 (hex)4200%Два символа на байт, тривиально читается, алфавита искать не нужно.
Base325160%Без учёта регистра и безопасна для продиктовывания, но самая расточительная в группе.
Base58~5,86~137%Выбран для адресов, а не для плотности: он избегает похожих символов.
Base646133%По умолчанию везде: 4 символа на 3 байта, при этом 6 неиспользуемых бит последнего символа пропадают.
Ascii85 / Base856,4125%5 символов на 4 байта, таблица поиска не нужна, но он делит на 85 — см. конвертер Ascii85.
Base91~6,5~123%Самый плотный из них: 13 бит на 2 символа, ценой группы из 14 бит время от времени.

Проценты — асимптотическая стоимость каждого кодирования, округлённая; измеренные количества символов для одного реального входа из 92 байт приведены в таблице выше на этой странице. Base91 близок к лучшему, что возможно в печатном ASCII: 7 бит на символ означали бы использование 128 различных символов, а печатный диапазон этого не даёт.

Чем Base91 не является
  • Не стандарт. RFC нет. Эталонная реализация — basE91 0.6.0 Joachim Henke, распространяемая по лицензии BSD 3-clause; название «Base91» также используется для алфавитов, которые не совпадают с этим.
  • Не шифрование. Каждый символ — прямое переупаковывание битов входа, и любой, у кого есть алфавит, обращает его за один проход.
  • Не безопасен для URL. Алфавит содержит #, %, &, /, ? и ", и все они меняют смысл в URL, поэтому вывод Base91 нужно кодировать процентами, прежде чем помещать его в строку запроса.
  • Не безопасен для HTML. Он также содержит <, >, & и ", поэтому строку Base91, которую помещают в разметку, нужно экранировать с помощью конвертера HTML-сущностей.
  • Не фиксированной длины. Длина вывода зависит от битового шаблона, а не только от количества байтов, поэтому два входа одного размера могут отличаться на один символ.
Ловушки, которые действительно кусают
  • Алфавит на этой странице — эталонный. Если другой инструмент выдаёт другой результат для того же входа, сравните его алфавит, прежде чем считать сломанной какую-либо из сторон. 91 символ должен идти в том же порядке.
  • Один неверный символ может изменить весь хвост. Границы групп определяются предыдущими значениями, поэтому правка в начале строки сдвигает всё, что после неё, а хвост декодируется только остатком бит последней группы.
  • Нечётная длина вывода — нормальна. Это не значит, что строка обрезана; последняя группа печатает один символ, если её остаток помещается в 7 бит или меньше.
  • Опубликованный пример может содержать перевод строки, который вы не набирали. Командная оболочка, которая добавляет окончание строки, — обычная причина того, что известное значение Base91 на один символ длиннее того, что даёт тот же текст здесь: echo "Hello, World!" передаёт кодировщику 14 байт и печатает >OwJh>}AQ;r@@Y?FF, а 13 байт одного текста печатают >OwJh>}AQ;r@@Y?F. Оба результата верны для разного количества байтов, и счётчик под полем вывода показывает, какой из них у вас.
  • Декодирование двоичных данных как текста не сработает. Если декодированные байты не являются корректным UTF-8, разумного текста показать нельзя, для этого и предназначен режим вывода в шестнадцатеричном виде.
Краткие ответы

«Почему эта страница показывает значение, отличающееся на один символ от другого инструмента Base91?»

Почти всегда из-за другого алфавита. Вторая по частоте причина — инструмент, который использует только ветку 13 бит и никогда не использует запасной вариант 14 бит; такой вариант короче в коде, но даёт иной результат, чем эталонный алгоритм, используемый здесь.

Есть и один точный случай, который можно решить подсчётом байтов, и он объясняет самый часто цитируемый пример: завершающий перевод строки. Большинство опубликованных векторов получено командной оболочкой, а она добавляет окончание строки. 13 байт Hello, World! печатаются здесь 16 символами, а 14 байт Hello, World! с переводом строки печатаются 17-символьной строкой >OwJh>}AQ;r@@Y?FF. Вставьте любое из этих значений в декодирование: эта страница показывает количество байт, поэтому два прочтения можно различить, а не спорить о них. Кнопка Пример загружает то из двух значений, которое относится к выбранному вами читателю, и следует переключателю «Кодировать / Декодировать» и селектору Вход в виде / Результат в виде: текстовый читатель получает 16-символьное значение, читатель шестнадцатеричного вида получает 17-символьное с завершающим байтом 0A на виду, а кодировщик получает текст для кодирования, а не строку Base91.

«Base91 короче Base64?»

Да, примерно на 8% от размера закодированного: 13 бит на 2 символа против 6 бит на символ. На 92 байтах текста Base64 печатает 124 символа, а Base91 — 114.

«Можно ли декодировать это декодером Base64 или наоборот?»

Нет. Алфавиты пересекаются в буквах и цифрах, поэтому декодер Base64 примет строку Base91 как «допустимые» символы и выдаст молчаливый мусор. Они согласуются лишь в самой схеме построения.

«Заканчивается ли вывод заполнением, как у Base64?»

Нет. У Base91 вообще нет символа заполнения: оставшиеся в конце биты печатаются как последняя группа, поэтому ничего не нужно добавлять или удалять.

«Откуда мне знать, что результат на этой странице верен?»

Нажмите Самопроверка от 0 до 64 байт. Она кодирует и декодирует 130 байтовых шаблонов, покрывающих каждую длину, при которой меняется разбиение на 13 и 14 бит, и сравнивает байты, которые возвращаются, с байтами, которые были на входе. Значения кнопки «Пример» дополнительно сверены со второй реализацией, написанной для инструментов тестирования этого сайта.

Стандарты и источники

Эта страница реализует алгоритм эталонной реализации basE91 (версия 0.6.0) Joachim Henke, распространяемой по лицензии BSD 3-clause, с алфавитом, приведённым выше. Декодирование принимает и игнорирует пробельные символы, как это делает эталонный инструмент.

Текст страницы написан для этого сайта; алгоритм нигде не копировался. Значения кнопки «Пример» — это единственный пример, поставляемый с эталонным пакетом (помечен «эталонный пример»), плюс значения, полученные второй независимой реализацией, хранящейся в инструментах тестирования этого сайта (помечены «перекрёстная проверка»).