开关状态信息的保存

本文介绍了一种利用位操作优化状态存储的方法,特别是在JavaScript中如何高效地存储大量布尔型状态信息,通过压缩多位到一个整数中实现空间节省。

系统中常常会存在大量的状态信息,特别是0-1值信息,某个条件是否达成,某个功能是否存在,某个操作是否成功等等,通常的做法是将各种状态条件编号,利用boolean数组来表示。

 

//0表示a功能,1表示b功能
//a功能生效
status[0]=true;
//b功能生效
status[1]=false;

 

由于大多数语言实际上是将boolean类型等同于整数类型,javascript也不例外,而其他语言和javascript不同的是:javascript目前必须运行在浏览器中,在特定条件下对于存储要求更加严格(例如cookie的4k限制),这时就要使用不常用的位操作来挖掘每一位存储的潜力。


将开关状态使用 0-1 二进制位表示,即将多个状态压缩到一个整数里面存储,一般一个整数有32位(javascript存在差异,尚不确定),则理论上节省了8倍的空间,代价则是增加了存取的运算。

 

1.存储数组


使用整数数组来拼成位集合

 

var store = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];

 

2.设置状态


根据开关状态序号,定位到整数数组中的某个整数,设置特定的某个位为1

 

var index = Math.floor(num / 32),
                index2 = num % 32,
                mask = Math.pow(2, index2);
        store[index] |= mask;

 

3.清除状态


根据开关状态序号,定位到整数数组中的某个整数,设置特定的某个位为0

 

var index = Math.floor(num / 32),
all_one = (Math.pow(2, 32) - 1),
                index2 = num % 32,
                mask = all_one ^ Math.pow(2, index2);
        store[index] &= mask;

 

4.存储

 

最后存储到cookie时都要转换成字符串的格式,这里我们有三个选项

1.将 store 的每个 int,直接 toString() 转换成 10 进制字符串

2.将 store 的每个 int,toString(16) 转换成 16 进制字符串


但是这两种都没有充分利用一个字符字节的完全存储空间,即使16进制下4位就已经占据了一个字符字节的空间。


3.更优的解即是:对每8位,通过 String.fromCharCode 转为一个字节,最终存储到cookie。

 

5.解码

 

根据cookie中的字节,通过string.charCodeAt 即可获得该字节代表的数字,进一步通过位运算即可获取各个开关状态

 

var v = S.Cookie.get("test");
for (var i = 0; i < v.length; i++) {
  //解码
  var n = v[i].charCodeAt(0);
}

 

简单的可以通过对状态组数字的每一位进行检测是否0,1来得知状态设置为开的序号 :复杂度为 O(n) ,一般 n=32

 

//slower    
    function getOnesSlow(n,base,re) {    	
    	base=base||0;
    	var mask=1;
    	for(var i=0;i<32;i++){
    		if(n&mask) re.push(i);
    		mask=mask<<1;	
    	}
    }
 

其实存在更精巧的算法,主要是利用了负数的二进制其实就是由绝对值二进制取反后加一形成的,当一个数字和其负数进行按位与时,返回结果就是除了原数的最低位1保留外,其他都是0,例如

 

1010 对应负数为 111...0101,加1为 1111...0110

 

1111...0110 & 1010 = 10

 

结果 1 的位顺序数即为原数字 1010 的最低位 1的位顺序数

 

为了能根据 10..0 快速得知1所在的位顺序数,可以预先建立索引而不用循环计数:

 

    var bton={};
    var base=1;
    for(var i=0;i<32;i++){
    	bton[base]=i;
    	base=base<<1;
    }
 

那么现在就可以快速得到数字包含的1所在的位数数组:

 

//fastest one :
    function getOnes(n,base,re){
    	base=base||0;
    	while(n){
    		var mask=n&-n;
    		re.push(bton[mask]+base);
    		n &= ~mask;
    	}
    }
 

最快0次循环,即 n=0,最慢32次循环,即 n=1111...1(javascript位操作对象为32位整数),平均为 16 次循环,速度相比简单的逐位检测算法提高了一倍。

 

6.问题


在 cookie 中,每个字节并不能完整的表示8bit的信息量,因为ASCII中存在一些区间是特殊作用的字符(比如头32个)。作为HTTP头的一部分,理论上是不能使用这些字符的。
参考Uuencode算法(http://en.wikipedia.org/wiki/Uuencoding ),一般每个ANSI字符可以编码6bit的信息量。(感谢提醒)

 

demo

 

 

延伸阅读

 

 

javascript 在位操作领域也开始显露头角,国外有参照java流读取设计的 base64 解码器,甚至 deflate 解码器 ,从而产生了直接使用div构建png 图片的演示,非常不错,还有:

 

 Embedding Base64 Image Data into a Webpage


 Use Javascript to Take a Screenshot of a Flash Movie


 Base64 Encoded Images for Internet Explorer


 Parsing Base64 Encoded Binary PNG Images in JavaScript

 

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值