Преобразовать массив байтов в действительную строку JSON base128
Я хочу отправить большой массив байтов, используя JSON (меня вдохновил этот вопрос), чтобы у меня были небольшие накладные расходы, я хочу использовать кодировку base128 (которая на самом деле может создать правильную строку json). Но, к сожалению, я не смог найти некоторые процедуры, которые делают эти преобразования в JS. Я опубликую свои процедуры в качестве ответа на этот вопрос, однако, возможно, кто-то имеет более короткие процедуры или может быть лучшей идеей для эффективной отправки двоичных данных внутри JSON.
1 ответ
ES6:
шифровать
let bytesToBase128 = (bytesArr) => {
// 128 characters to encode as json-string
let c= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz¼½ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"
let fbits=[];
let bits = (n,b=8) => [...Array(b)].map((x,i)=>n>>i&1);
bytesArr.map(x=> fbits.push(...bits(x)));
let fout=[];
for(let i =0; i<fbits.length/7; i++) {
fout.push(parseInt(fbits.slice(i*7, i*7+7).reverse().join(''),2))
};
return (fout.map(x => c[x])).join('');
}
// Example
// bytesToBase128([23, 45, 65, 129, 254, 42, 1, 255]) => "NÚ4AèßÊ0ÿ1"
раскодировать
let base128ToBytes = (base128str) => {
// 128 characters to encode as json-string
let c= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz¼½ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"
dfout = base128str.split('').map(x=>c.indexOf(x));
let dfbits = [];
let bits = (n,b=8) => [...Array(b)].map((x,i)=>n>>i&1);
dfout.map(x=> dfbits.push(...bits(x,7) ));
let dfbytes=[];
let m1 = dfbits.length%8 ? 1 : 0;
for(let i =0; i<dfbits.length/8-m1; i++) {
dfbytes.push(parseInt(dfbits.slice(i*8, i*8+8).reverse().join(''),2))
};
return dfbytes;
}
// Example
// base128ToBytes("NÚ4AèßÊ0ÿ1") => [23, 45, 65, 129, 254, 42, 1, 255]
Я здесь bits
Функция - здесь. Идея покрытия здесь заключается в том, чтобы преобразовать массив байтов в массив битов, а затем взять каждые 7 бит (значение от 0 до 127) в качестве номера символа в списке символов. c
, При декодировании мы меняем каждый символ на 7-битное число и создаем массив, а затем берем каждый 8-битный пакет этого массива и интерпретируем их как байты.
Чтобы просмотреть символы из ASCI и выбрать 128 из них (что является произвольным), я набираю в консоли
[...Array(256)].map((x,i) => String.fromCharCode(i)).join('');
Я стараюсь избегать символов, которые имеют "особое значение" в разных контекстах, таких как ! @ # $ % ' & ...
И вот рабочий пример (который конвертировать Float32Array
в JSON).
Протестировано на Chrome, Firefox и Safari
Заключение
После преобразования байтового массива в строку base128 (что является допустимым json), выходная строка меньше 15%
больше, чем входной массив.
Обновить
Еще немного копаем и раскрываем, что когда мы отправляем символы, коды которых больше 128 (¼½ÀÁÂÃÄ...
) затем chrome фактически отправляет ДВА символа (байта) вместо одного:( - я сделал тест следующим образом: введите в строке URL chrome: // net-internals / # events (и отправьте запрос POST) и в URL_REQUEST> HTTP_STREAM_REQUEST > UPLOAD_DATA_STREAM_INIT > total_size мы видим, что запрос в два раза больше, когда тело содержит символы с кодами ведьм больше 128. Так что на самом деле у нас нет прибыли от отправки этих символов:( . Для строк base64 мы не наблюдаем такого негативного поведения - Однако я оставил это процедуры, потому что они могут быть использованы в других целях, чем отправка (например, лучшая альтернатива хранению двоичных данных в локальном хранилище, чем base64 - однако, вероятно, существуют даже лучшие способы...?).