Преобразовать массив байтов в действительную строку 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 - однако, вероятно, существуют даже лучшие способы...?).

Другие вопросы по тегам