Преобразование массива байтов в допустимую строку JSON base128

Я хочу отправить большой массив байтов с помощью JSON (я был вдохновлен этим вопрос), чтобы иметь небольшие накладные расходы, я хочу использовать кодировку base128 (которая на самом деле может создавать действительную строку json). Но, к сожалению, мне не удалось найти некоторые процедуры, которые делают эти преобразования в JS. Я опубликую свои процедуры в качестве ответа на этот вопрос, однако, возможно, у кого-то есть более короткие процедуры или может быть лучшая идея для эффективной отправки двоичных данных внутри JSON.


person Kamil Kiełczewski    schedule 02.11.2018    source источник
comment
Разве сжатие gzip не должно сделать это за вас?   -  person bigless    schedule 02.11.2018
comment
@bigless слово «сжатие» было в двойных кавычках - вопрос в том, чтобы эффективно отправлять двоичные данные (возможно, сжатые) внутри JSON (конечно, вы можете сжать эти данные с помощью zip перед отправкой их в виде json).   -  person Kamil Kiełczewski    schedule 02.11.2018
comment
Является ли это случаем преобразования числа с основанием 2 в основание 128?   -  person Trunk    schedule 02.11.2018
comment
Байт @Trunk = 8 бит = 256 значений   -  person Kamil Kiełczewski    schedule 02.11.2018
comment
Я понимаю. 7-битная кодировка. Похоже, вы сами ответили на свой вопрос!   -  person Trunk    schedule 02.11.2018
comment
Я знаю, что OP может не интересоваться этим вопросом, но для любого реального случая, пожалуйста, подумайте, почему вам нужно отправлять двоичные данные в виде JSON. Существуют средства отправки двоичных данных в виде двоичных данных, и это всегда будет лучшим и предпочтительным способом.   -  person Kaiido    schedule 02.11.2018
comment
@Kaiido - извините, я отправил неправильную ссылку в предыдущем комментарии (я удалил ее), вот, вероятно, пример из реальной жизни: stackoverflow.com/q/ 37330746/860099   -  person Kamil Kiełczewski    schedule 05.11.2018
comment
Да и там они, наверное, просто не нужны. Я не знаю, какую библиотеку они используют, но, похоже, у нее есть правильный метод для прямой отправки двоичных данных: demo.easyrtc.com/demos/demo_data_channel_filesharing.html и send() принимает двоичные данные напрямую, поэтому на самом деле нет необходимости преобразовывать их в строку.   -  person Kaiido    schedule 05.11.2018


Ответы (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 ">здесь.

person Kamil Kiełczewski    schedule 02.11.2018