Сжатие и двоичные данные
UTF-8, массивы байтов, ZLIB, GZIP и Protobuf с обратимыми примерами и точными границами каждого представления.
Здесь решаются три разные задачи: перевод текста в байты, сжатие текста и создание двоичного сообщения Protobuf. Не смешивайте их: массив чисел, Base64 и Protobuf могут описывать одни и те же байты, но принимаются разными функциями. Все вызовы используют .
Base64 не сжимает и не защищает данные
Base64 только записывает двоичные данные печатными знаками. Размер такой записи обычно больше исходного массива байтов; восстановить её можно без ключа.
Что выбрать
Текст ↔ массив байтов
STRTOBYTES возвращает байты UTF-8 как массив десятичных чисел JSON, а BYTESTOSTR выполняет обратное преобразование.
Сжать текст для своей стороны
Выберите согласованную пару ZLIB или GZIP. Обе пары возвращают Base64, но их двоичные обёртки несовместимы друг с другом.
Обмен по известной схеме
PROTOBUF создаёт и разбирает сообщение по описанию полей. Описание должно точно повторять номера и типы полей другой стороны.
Только сменить запись байтов
Для перехода между Base64 и шестнадцатеричной строкой используйте функции из раздела .
Текст, байты и их запись
Функции сначала получают строку программы, а затем сами выбирают, как представить её байты. На примере слова Привет цепочка выглядит так:
Привет — шесть знаков Unicode. Сам по себе текст ещё не определяет последовательность байтов без выбранной кодировки.
D0 9F D1 80 D0 B8 D0 B2 D0 B5 D1 82 — двенадцать байтов того же текста в шестнадцатеричной записи.
[208,159,209,128,208,184,208,178,208,181,209,130] — те же байты как десятичные числа, которые возвращает STRTOBYTES.
ZLIB и GZIP также начинают с байтов UTF-8, но после сжатия записывают двоичный результат в Base64. PROTOBUF строит байты по номерам и типам полей, поэтому одного исходного текста для него недостаточно.
STRTOBYTES и BYTESTOSTR
Эти функции образуют обратимую пару, если массив не менялся и содержит допустимый текст UTF-8.
После выполнения:
STRTOBYTES
Принимает обычный текст, выполняет вложенные значения, преобразует результат в UTF-8 и возвращает однострочный массив JSON.
Результат:
Числа записаны в десятичном виде и всегда обозначают байты от 0 до 255.
BYTESTOSTR не предназначена для произвольного файла
Случайный набор байтов может не быть допустимым UTF-8 и при чтении потерять исходное значение. Для надёжной обратимости передавайте в BYTESTOSTR результат STRTOBYTES либо заранее известный массив UTF-8; двоичный файл храните в Base64 или шестнадцатеричной форме.
Некорректный JSON, значение вместо массива или нечисловой элемент вызывают ошибку выполнения. Не передавайте дроби и числа вне диапазона байта: это не входит в договор функции.
ZLIB и GZIP
Обе пары выполняют одну цепочку: текст → UTF-8 → сжатые байты → Base64. При восстановлении шаги идут в обратном порядке. Входом функций распаковки служит Base64, а результатом — текст UTF-8.
ZLIBCOMPRESS ↔ ZLIBDECOMPRESS
Использует поток ZLIB: заголовок ZLIB, данные DEFLATE и контрольную сумму Adler-32. Подходит только стороне, которая ожидает именно такую обёртку.
GZIPCOMPRESS ↔ GZIPDECOMPRESS
Использует поток GZIP: заголовок GZIP, данные DEFLATE и собственную завершающую часть. Это не архив с несколькими файлами и именами, а один сжатый поток.
Обратимая пара ZLIB
|DV|[ZlibRestored] будет равно {"event":"heartbeat"}. При текущей реализации |DV|[ZlibPacked] имеет вид eJyrVkotS80rUbJSykhNLCpJSk0sUaoFAFHbB40=, но проверяйте восстановленный текст, а не точное совпадение сжатой строки: допустимый поток может отличаться после смены средства сжатия или его настроек.
Устройство потока определено в .
Обратимая пара GZIP
|DV|[GzipRestored] будет равно {"event":"sync"}. Один из допустимых результатов сжатия — H4sIAAAAAAAAAKtWSi1LzStRslIqrsxLVqoFADsT6ZcQAAAA; заголовок GZIP допускает служебные поля, поэтому точная строка также не является устойчивой проверкой.
Устройство потока определено в .
Пары нельзя перекрещивать
Результат ZLIBCOMPRESS передавайте только в ZLIBDECOMPRESS, а результат GZIPCOMPRESS — только в GZIPDECOMPRESS. Внутри обеих обёрток применяется DEFLATE, но заголовки и контрольные суммы различаются.
Пустой ввод и ошибки
- Пустая строка на входе любой из четырёх функций сразу даёт пустую строку. Это особое поведение, а не Base64 сжатого пустого потока.
- Функции сжатия принимают текст, а не произвольный массив байтов. Для файла или уже готового двоичного тела отдельного входа здесь нет.
- Неверный Base64, чужая обёртка или повреждённый поток не останавливают исполнение исключением: функция возвращает текст вида
Error: <описание>. Такой результат нельзя передавать дальше как успешно восстановленные данные.
PROTOBUF
PROTOBUF строит или разбирает одно сообщение по описанию полей в JSON. Функция не читает .proto, не получает схему из сообщения и не угадывает типы: номер и тип каждого нужного поля задаются при каждом вызове.
action: encode
Создаёт новое сообщение из field_values и возвращает его байты в Base64.
action: decode
Разбирает protobuf_base64 по описанию полей и возвращает объект JSON одной строкой.
action: update
Разбирает имеющееся сообщение, добавляет поля из field_values и возвращает новый Base64. Это добавление записей, а не надёжная замена прежних значений.
Договор входного объекта
Обязательная строка encode, decode или update. Регистр значения не важен, потому что исполнитель приводит его к нижнему регистру.
Обязательное имя сообщения. Оно выбирает одноимённый объект внутри field_mapping, но само не записывается в двоичный результат.
Обязательный объект для всех действий. Сначала содержит имя сообщения, затем имена полей; у каждого поля обязательны number и type.
Обязательный объект для encode и update. Ключи должны существовать в выбранном описании полей; отсутствующие ключи просто не записываются.
Обязательная непустая строка для decode и update. Это Base64 ровно одного двоичного сообщения Protobuf.
Поле типа message получает вложенный объект fields; поле типа enum — объект values, который сопоставляет названия перечисления с целыми числами.
Свойство name внутри описания поля не используется: именем служит ключ login, password и так далее. Лишнее name не заменяет правильный ключ и не исправляет неверный number.
Поддерживаемые типы
Знаковые целые числа. В field_values передаются числом JSON; при разборе возвращаются числом JSON.
Беззнаковые целые числа. Вход — число JSON; разобранный uint64 возвращается строкой, чтобы не переполнить знаковое целое программы.
Принимает true, false, 1 или 0. Любое числовое значение, кроме 1, будет прочитано как false, поэтому используйте только эти четыре явных значения.
Числа с плавающей точкой одинарной и двойной точности. Вход и результат представлены числами JSON.
Обычная строка, которая записывается и читается как UTF-8.
Двоичное поле. Значение в field_values и результат decode представлены строкой Base64, а не массивом чисел.
Перечисление с объектом values. При создании принимает известное название или число; при разборе возвращает название, если число найдено, иначе само число.
Вложенное сообщение с собственным объектом fields. Можно передать один объект или массив объектов для повторяющегося вложенного поля.
Полная пара: создать и разобрать
Результаты:
Имена login и password в двоичном сообщении не хранятся. При разборе они восстанавливаются только из field_mapping; если поменять номера местами, значения будут подписаны неверными именами.
Что в действительности делает update
Следующий вызов получает созданный выше пакет и добавляет ещё одну запись поля login:
Результат — CgZBcnRoYXMSB1Njb3VyZ2UKBUphaW5h. При последующем decode эта реализация покажет два вхождения login как массив ["Arthas","Jaina"]; порядок свойств самого объекта JSON не определён.
update не сохраняет сообщение целиком
При разборе неизвестные описанию поля пропускаются, а при повторной записи уже не возвращаются. Известное поле update добавляет повторно вместо удаления прежнего значения. Не применяйте это действие как обычное «заменить поле», если необходимо сохранить неизвестные данные или получить единственное значение.
Повторяющиеся известные поля при decode собираются в массив. При encode массив поддержан только для вложенных полей типа message; массив простых строк, чисел или bytes не принимается. Упакованные повторяющиеся числа также не входят в поддерживаемый договор.
Границы и ошибки
- Номер поля должен совпадать со схемой другой стороны. Допустимы номера от
1до536870911, кроме зарезервированного промежутка19000–19999; исполнитель сам полностью это правило не проверяет. - Публичный договор ограничен одиннадцатью типами выше. Частично присутствующие во внутреннем исполнителе названия
sint32,sint64,fixed32,fixed64,sfixed32иsfixed64записываются несовместимо либо разбираются неполно, поэтому использовать их нельзя. - Неизвестное поле при
decodeпропускается. Отсутствующее известное поле не дополняется значением по умолчанию. - Некорректный JSON, отсутствующее обязательное свойство, неизвестное действие, неверный тип значения или повреждённый Base64 вызывают ошибку выполнения;
PROTOBUFне возвращает строкуError: ..., как функции сжатия.
Почему номер и вид записи поля определяют смысл двоичных данных, показано в .
Сначала получите точную схему
Для внешнего запроса возьмите исходный .proto или точное описание службы и вручную перенесите только нужные поля. Пример из этой страницы объясняет форму вызова, но его AuthRequest не является общей схемой для чужого узла.