在网页中嵌入二进制数据?

pat*_*rit 10 javascript performance steganography bit-manipulation page-size

我有一个6000个元素的数据结构,每个元素我需要存储7位信息.如果我天真地将它存储为6000个元素填充数字的数组,它需要大约22 KB.我试图减少页面的大小 - 什么是存储6000*7位信息的最佳方式(应该是大约5 KB).我想要像数据结构一样的"比特流".我想过把它编码成一个字符串甚至一个图像,但不完全确定.我没有编码为字符串的原因是因为我无法保证所有字符都不会是不可打印的ASCII字符之一(例如ASCII 1-25)

小智 8

让我们考虑两种解决方案.

基地32

为了好玩,让我们考虑使用base-32数字.是的,你可以用JavaScript做到这一点.

首先将四个7位值打包成一个整数:

function pack(a1,a2,a3,a4){
    return ((a1 << 8 | a2) << 8 | a3) << 8 | a4;
}
Run Code Online (Sandbox Code Playgroud)

现在,转换为基数32.

function encode(n){
    var str = "000000" + n.toString(32);
    str = str.slice(0,6);
    return str;
}
Run Code Online (Sandbox Code Playgroud)

那不应该超过六位数.我们确保它正好是六个.

走向另一个方向:

function decode(s){
    return parseInt(s, 32);
}

function unpack(x){
    var a1 = x & 0xff0000>>24, a2 = x & 0x00ff0000>>16, a3 = x & 0x0000ff00>>8, a4 = x & 0x000000ff;
    return [a1, a2, a3, a4];
}
Run Code Online (Sandbox Code Playgroud)

剩下的就是围绕它来处理逻辑以处理6000个元素.要压缩:

function compress(elts){
    var str = '';
    for(var i = 0; i < elts.length; i+=4){
        str += encode(pack(elts[i], elts[i+1], elts[i+2], elts[i+3])
    }
    return str;
}
Run Code Online (Sandbox Code Playgroud)

并解压缩:

function uncompress(str){
    var elts = [];
    for(var i = 0; i < str.length; i+=6){
        elts = elts.concat(unpack(decode(str.slice(i, i+6)));
    }
    return elts;
}
Run Code Online (Sandbox Code Playgroud)

如果你连接所有6,000个元素的结果,你将拥有1,500个打包数字,每个包含6个字符,将变成大约9K.每个7位值约为1.5个字节.它绝不是信息理论上的最大压缩,但并不是那么糟糕.解码只需反转过程:

统一

首先,我们将两个7位值打包成一个整数:

function pack(a1,a2){
    return (a1 << 8 | a2) << 8;
}
Run Code Online (Sandbox Code Playgroud)

我们将对所有6,000个输入执行此操作,然后使用我们的朋友String.fromCharCode将所有3,000个值转换为3,000个字符的Unicode字符串:

function compress(elts){
    var packeds = [];
    for (var i = 0; i < elts.length; i+=2) {
        packeds.push(pack(elts[i], elts[i+1]);
    }
    return String.fromCharCode.apply(0, packeds);
}
Run Code Online (Sandbox Code Playgroud)

回到另一个方向,这很容易:

function uncompress(str) {
    var elts = [], code;
    for (var i = 0; i < str.length; i++) {
        code=str.charCodeAt(i);
        elts.push(code>>8, code & 0xff);
    }
    return elts;
}
Run Code Online (Sandbox Code Playgroud)

这将占用每两个7位值两个字节,因此比基本32方法的效率高约33%.

如果上述字符串将作为Javascript赋值写入脚本标记,例如var data="HUGE UNICODE STRING";,则字符串中的引号将需要转义:

javascript_assignment = 'var data = "' + compress(elts).replace(/"/g,'\\"') + '";';
Run Code Online (Sandbox Code Playgroud)

上述代码并不意味着生产,特别是不处理输入数量不是四个或两个的倍数的边缘情况.