Проблема с UTF-8 заключается в том, что это не самая экономичная кодировка. Кроме того, некоторые случайные двоичные байтовые последовательности недопустимы в кодировке UTF-8. Таким образом, вы не можете просто интерпретировать случайную двоичную последовательность байтов как некоторые данные UTF-8, потому что это будет недопустимой кодировкой UTF-8. Преимущество этого ограничения на кодировку UTF-8 состоит в том, что он делает надежным и возможным обнаружение многобайтовых символов начала и конца любого байта, на который мы начинаем смотреть.
Как следствие, если для кодирования значения байта в диапазоне [0..127] потребуется только один байт в кодировке UTF-8, для кодирования значения байта в диапазоне [128..255] потребуется 2 байта!
Хуже этого. В JSON управляющие символы "и \ не могут появляться в строке. Поэтому для правильного кодирования двоичных данных потребуется некоторое преобразование.
Давай посмотрим. Если мы примем равномерно распределенные случайные байтовые значения в наших двоичных данных, то в среднем половина байтов будет закодирована в один байт, а другая половина - в два байта. У двоичных данных в кодировке UTF-8 будет 150% от исходного размера.
Кодировка Base64 увеличивается только до 133% от исходного размера. Таким образом, кодировка Base64 более эффективна.
А как насчет использования другой базовой кодировки? В UTF-8 кодирование 128 значений ASCII является наиболее эффективным с точки зрения места. В 8 битах вы можете хранить 7 бит. Поэтому, если мы разрежем двоичные данные на 7-битные порции, чтобы сохранить их в каждом байте строки в кодировке UTF-8, кодированные данные вырастут только до 114% от исходного размера. Лучше, чем Base64. К сожалению, мы не можем использовать этот простой трюк, потому что JSON не допускает некоторые символы ASCII. 33 управляющих символа ASCII ([0..31] и 127) и символы "и \" должны быть исключены. Это оставляет нам только 128-35 = 93 символа.
Таким образом, теоретически мы могли бы определить кодировку Base93, которая бы увеличивала кодированный размер до 8 / log2 (93) = 8 * log10 (2) / log10 (93) = 122%. Но кодировка Base93 будет не такой удобной, как кодировка Base64. Base64 требует разрезать последовательность входных байтов на 6-битные порции, для которых хорошо работает простая побитовая операция. При этом 133% - это не намного больше, чем 122%.
Вот почему я независимо пришел к общему выводу, что Base64 действительно лучший выбор для кодирования двоичных данных в JSON. Мой ответ оправдывает это. Я согласен, что это не очень привлекательно с точки зрения производительности, но учту также преимущество использования JSON с его удобочитаемым представлением строк, которым легко манипулировать на всех языках программирования.
Если производительность критична, то чистая двоичная кодировка должна рассматриваться как замена JSON. Но с JSON я пришел к выводу, что Base64 - лучший.