Я хочу отправить большой массив байтов с помощью JSON (я был вдохновлен этим вопрос), чтобы иметь небольшие накладные расходы, я хочу использовать кодировку base128 (которая на самом деле может создавать действительную строку json). Но, к сожалению, мне не удалось найти некоторые процедуры, которые делают эти преобразования в JS. Я опубликую свои процедуры в качестве ответа на этот вопрос, однако, возможно, у кого-то есть более короткие процедуры или может быть лучшая идея для эффективной отправки двоичных данных внутри JSON.
Преобразование массива байтов в допустимую строку JSON base128
Ответы (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 (¼½ÀÁÂÃÄ...
), тогда хром фактически отправляет ДВА символа (байта) вместо одного :( - Я сделал тест таким образом: введите строку URL < strong>chrome://net-internals/#events (и отправить запрос POST) и в URL_REQUEST› HTTP_STREAM_REQUEST › UPLOAD_DATA_STREAM_INIT › total_size мы видим, что запрос в два раза больше, когда тело содержит символы коды ведьм больше 128. Так что на самом деле у нас нет прибыли от отправки этих символов :( альтернатива хранению двоичных данных в локальном хранилище, чем base64 - однако, возможно, существуют еще лучшие способы...?). ОБНОВЛЕНИЕ 2019 ">здесь.