理解ArrayBuffer存储

1,682 阅读6分钟
ArrayBuffer对象代表储存二进制数据的一段内存,它不能直接读写,只能通过视图(TypedArray视图和DataView视图)来读写,视图的作用是以指定格式解读二进制数据。


const buf = new ArrayBuffer(32);

上面代码生成了一段 32 字节的内存区域,每个字节的值默认都是 0

const buffer = new ArrayBuffer(12);

const x1 = new Int32Array(buffer);
x1[0] = -434393088;

查看buffer内容

其中[[Int32Array]]=[-434393088,0,0];

每个位置占4个字节,接下来我们看下其他几个是怎么得出来的

[[Int8Array]]: Int8Array(12) [0, -80, 27, -26, 0, 0, 0, 0, 0, 0, 0, 0]

[[Int16Array]]: Int16Array(6) [-20480, -6629, 0, 0, 0, 0]

[[Uint8Array]]: Uint8Array(12) [0, 176, 27, 230, 0, 0, 0, 0, 0, 0, 0, 0]

负数在计算机中的存储是以补码存储的

原码求补码

正整数的补码是其二进制表示,与原码相同

负整数的补码,将其原码除符号位外的所有位取反(0变1,1变0,符号位为1不变)后加1

例:求-5的补码。
-5对应正数5(10000101)→所有位取反(11111010)→加1(11111011)

接下来我们看下-434393088的补码是多少

-434393088对应原码

                  (1001 1001     1110  0100      0101 0000       0000  0000)→

所有位取反(1110 0110    0001 1011        1010 1111          1111  1111)

   →补码=加1(1110 0110    0001 1011        1011 0000         0000 0000);

计算机都采用小端字节序(little endian),相对重要的字节排在后面的内存地址,相对不重要字节排在前面的内存地址,所以就得到了上面的结果

所以-434393088的存储是 0000  0000     1011 0000     0001 1011     1110  0110 

 接着分析[[Int8Array]]结果:

[[Int8Array]]: Int8Array(12) [0, -80, 27, -26, 0, 0, 0, 0, 0, 0, 0, 0]                   

第一个字节是0,没问题.

第二个字节是1011 0000,1开始的判断为负数,负数存储的是补码所以求其原码      (1011 0000)补码 =>反码减1 (1010 1111) =>取反为原码(1101 0000) 即-80

第三个字节0001 1011 以0开始所以是正数为27 

第四个字节1110 0110,1开始的判断为负数,负数存储的是补码所以求其原码 (1110  0110)补码 =>反码减1 (1110 0101) =>取反为原码(1001 1010) 即-26;

接下来分析[[Int16Array]]结果:

[[Int16Array]]: Int16Array(6) [-20480, -6629, 0, 0, 0, 0],Int16占用2个字节,读取时两个字节

第一个结果是(0000  0000      1011 0000 ),又由于采用小端存储取出结果为

                       (1011  0000       0000 0000 )

1开始的判断为负数,负数存储的是补码所以求其原码 (1011 0000  0000 0000)补码 =>反码减1 (1010 1111    1111 1111) =>取反为原码(1101 0000 0000 0000) 即-20480;

第二个结果是(0001 1011    1110 0110 ),又由于采用小端存储取出结果为

                        (1110 0110     0001 1011)

1开始的判断为负数,负数存储的是补码所以求其原码 (1110 0110      0001 1011)补码 =>反码减1 (1110 0110    0001 1010) =>取反为原码(1001 1001     1110 0101) 即-6629;


接着分析[[Uint8Array]]结果:

[[Uint8Array]]: Uint8Array(12) [0, 176, 27, 230, 0, 0, 0, 0, 0, 0, 0, 0]

每个占用一个字节,又是无符号位,所以不存在负数,相对来说比较简单

0000 0000     1011 0000     0001 1011    1110 0110 

    得到0                得到176          得到27           得到230

这样就能搞清楚ArrayBuffer


ArrayBuffer 与字符串的互相转换

function str2ab(input){
  const encoder = new TextEncoder()
  const view = encoder.encode(input)
  return view
}

根据Unicode和UTF-8关系

Unicode符号范围     |        UTF-8编码方式
(十六进制)        |              (二进制)
----------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

根据上面的关系,写下buffer转换为UTF-8

function ab2str(input){
		let strArr=[];
 	    for(let i=0;i<input.length;){
		let charcode0=input[i] & 0b11111000;
		if(charcode0==240){
			let str4=(input[i] & 0b00000111)<<18;
			let str3=(input[i+1] & 0b00111111)<<12;	
			let str2=(input[i+2] & 0b00111111)<<6;
			let str1=(input[i+3] & 0b00111111);	
			strArr.push(String.fromCharCode(str4+str3+str2+str1));
			i+=4;
			continue;
		}
		charcode0=input[i] & 0b11110000;
		if(charcode0==224){
			let str3=(input[i] & 0b00001111)<<12;
			let str2=(input[i+1] & 0b00111111)<<6;
			let str1=(input[i+2] & 0b00111111);	
			strArr.push(String.fromCharCode(str3+str2+str1));
			i+=3;
			continue;
		}
		charcode0=input[i] & 0b11100000;
		if(charcode0==192){
			let str2=(input[i] & 0b00011111)<<6;
			let str1=(input[i+1] & 0b00111111);
			strArr.push(String.fromCharCode(str2+str1));
			i+=2;
			continue;
		}
		charcode0=input[i];
		if(charcode0<=127){
			strArr.push(String.fromCharCode(input[i]));
			i+=1;
			continue;
		}
	};
	return strArr.join("");