Laya Byte

二进制数组在 JavaScript 中其实很早就存在,在 WebGL 中为了高效地和显卡进行数据交换,ES6 为浏览器引入了 TypedArray 和 DataView 两个操作底层二进制数据的视图。因为具有直接操作内存的能力而不用进行转换,在处理 WebGL 二进制数据上性能远高于 Array。

JavaScript中将二进制数据分为三部分:

类型名称描述设计目的
ArrayBuffer数组缓冲区代表内存中一段二进制数据-
TypedArray类型化数组代表读写简单的二进制数据本机数据传输
DataView数据视图代表读写复杂的二进制数据网络数据传输

在设计目的上,ArrayBuffer对象的各种类型化视图TypedArray是用来向网卡、声卡之类的本机设备传送数据,所以本机的字节序就可以了。而DataView的设计目的是用来处理网络设备传来的数据,所以大端字节序或小端字节序是可以自行设定的。DataView视图提供更多操作选项,而且支持设定字节序。

TypedArray 类型化数组与 DataView 数据视图只是底层数据缓冲区的视图,若缓冲区的数据发生变化,视图也会改变。

数据类型字节长度含义CTypedArrayDataView
Int81Byte 8Bit8位有符号整数charInt8ArrayInt8
Uint81Byte 8Bit8位无符号整数unsigned charUint8ArrayUint8
Uint8C1Byte 8Bit8位无符号整数(自动过滤溢出)unsigned charUint8ClampedArray不支持
Int162Byte 16Bit16位有符号整数shortInt16ArrayInt16
Uint162Byte 16Bit16位无符号整数unsigned shortUint16ArrayUint16
Int324Byte 32Bit32位有符号整数intInt32ArrayInt32
Uint324Byte 32Bit32位无符号整数unsigned intUint32ArrayUint32
Float324Byte 32Bit32位浮点数floatFloat32ArrayFloat32
Float648Byte 64Bit64位浮点数doubleFloat64ArrayFloat64

ArrayBuffer

JavaScript中的数值的类型都是使用 Float64 进行存储的,在内存中占据 8 字节 64 位。如果只存储 1 这个数值,同样需要申请 8字节的空间,这相当于浪费了 63Bit。

ArrayBuffer 的作用是创建一个二进制数据的缓冲区,创建之后不能直接对缓冲区进行操作,必须构建视图,通过视图对数据进行操作。

ArrayBuffer 并非一个新的数据类型而是新的的接口,通过构造函数可以返回一段包含指定字节数量的内存地址。

ArrayBuffer 代表内存中的一段二进制数据,在创建缓冲区时时按字节位为单位。

属性描述
byteLength内存区域的字节长度
const n = 32;
let buf = new ArrayBuffer(n);
if(buf.byteLength != n){
  console.log("内存空间分配失败");
}

ArrayBuffer提供的slice()方法用于分配新内存

方法描述
slice(start=0, end=this.byteLength)分配新内存,将原内存start到end部分复制,返回此段新内存区域。
let buf = new ArrayBuffer(32);
let newbuf = buf.slice(0, 3);

ArrayBuffer代表内存中的一段二进制数据,它是无法直接操作的,需要使用视图(TypedArray或DataView)按照一定格式对其进行解读。

构造一段内存来存放二进制数据,需要注意的是由于内存是有限的稀缺资源,所以在分配内存空间是需要根据实际情况来处理。

//分配32字节的内存空间用来存放数据,默认全部位0。
let buf = new ArrayBuffer(32);

//将内存空间转换为视图
let dataview = new DataView(buf);

//获取第一个字节的值
dataview.getUint8(0);

无论使用哪一种视图,实例化的内存如果是共享的,则所有写入操作会修改每个视图。

let buf = new ArrayBuffer(32);
let u8 = new Uint8Array(buf);
let u16 = new Uint16Array(buf);
console.log(u8[0]);
u16[0] = -1;
console.log(u8[0]);

TypedArray

TypedArray 类型化数组具有一个DataView()的构造函数并接收一个ArrayBuffer数组缓冲区作为参数,用于视图化该内存区域。

// TypedArray 类型化数组构造函数的参数
Constructor(buffer, start = 0, length = buffer.byteLength - start * 8)

TypedArray 也可以接收一个数组参数,实例化该数组为二进制内容,得到的值也是一个数组,可以直接数组访问符[]获取每个索引位置的内容,该数组同样具有length属性。

TypedArray 类型化数组与 DataView 数据视图的区别在于,TypedArray 类型化数组是特定类型的视图,实际上并没有一个构造器叫做TypedArray,其使用主要分为9各不同的类型。

TypedArray的构造函数有9种

数据类型字节长度含义CTypedArrayDataView
Int81Byte 8Bit8位有符号整数charInt8ArrayInt8
Uint81Byte 8Bit8位无符号整数unsigned charUint8ArrayUint8
Uint8C1Byte 8Bit8位无符号整数(自动过滤溢出)unsigned charUint8ClampedArray不支持
Int162Byte 16Bit16位有符号整数shortInt16ArrayInt16
Uint162Byte 16Bit16位无符号整数unsigned shortUint16ArrayUint16
Int324Byte 32Bit32位有符号整数intInt32ArrayInt32
Uint324Byte 32Bit32位无符号整数unsigned intUint32ArrayUint32
Float324Byte 32Bit32位浮点数floatFloat32ArrayFloat32
Float648Byte 64Bit64位浮点数doubleFloat64ArrayFloat64
  • TypedArray 类型化数组的不同构造函数对内存会进行不同位数的格式化,以得到对应类型值的数据。
  • TypedArray类型化数组不同于普通数组,不支持稀疏数组,默认值为0。
  • TypedArray类型化数组的同一个数组只能存放同一种类型的变量

例如:划分一块 ArrayBuffer 得到C语言中的结构体

// C语言结构体
struct User{
  char name[16];
  char gender;
  int age;
  float score;
}
let buf = new ArrayBuffer(24);
let name = new Uint8Array(buf, 0, 16);
let gender = new Uint8Array(buf, 16, 1);
let age = new Uint16Array(buf, 18, 1);
let score = new Float32Array(buf, 20, 1);

DataView

DataView数据视图是一个可以从ArrayBuffer数组缓冲区对象中读取多种数值类型的底层接口,使用时无需考虑不同平台的字节序问题。

DataView是一种通用视图,不仅仅可以对指定字节端进行读写,还可以在同一缓冲区内使用各不同类型的数据,以提高内存利用效率。

let buf = new ArrayBuffer(16);
// from byte 12 for the next 4 byte
let dv = new DataView(buf, 12, 4);
//put 32 in slot 12
dv.setInt8(12, 32);
// output
console.log(dv.getInt8(0));

DataView数据视图具有一个构造函数可接收一个ArrayBuffer数组缓冲区参数,用于视图化该段内存。

当一段内存拥有多种数据时,复合视图使用起来会不太方便,此时更适合使用DataView数据视图。由于DataView数据视图可以自定义高位优先和低位优先,因此可以读取的数据就更多了。

new DataView(buffer[, byteOffset[, byteLength]])
参数描述
buffer一个ArrayBuffer或SharedArrayBuffer对象,DataView对象的数据源。
byteOffsetDataView对象的第一个字节在buffer中的偏移量,若未指定则默认从第一个字节开始。
byteLengthDataView对象的字节长度,若未指定则默认与buffer的长度相同。

DataView 数据视图实例化后会返回一个DataView对象,用于呈现指定的缓冲区内数据。可以将返回的对象想象成一个二进制ArrayBuffer的解释器,它直到如何在读取或写入时正确地转换字节码,这意味着它能在二进制层面处理整数与浮点数转换、字节顺序等相关的细节问题。

DataView数据视图的构造函数的参数与TypedArray类型化数组一样

Constructor(buffer, start = 0, length = buffer.byteLength - start * 8);

DataView数据视图格式化读取ArrayBuffer数组缓冲区数据的方式

方法描述
getInt8(start, isLittleEndian=false)从start字节处开始读取1个字节并返回8位有符号整数,默认高位优先。
getUint8(start, isLittleEndian=false)从start字节处开始读取1个字节并返回8位无符号整数,默认高位优先。
getInt16(start, isLittleEndian=false)从start字节处读取2个字节并返回16位有符号整数,默认高位优先。
getUint16(start, isLittleEndian=false)从start字节处读取2个字节并返回16位无符号整数,默认高位优先。
getInt32(start, isLittleEndian=false)从start字节处读取4个字节并返回32位有符号整数,默认高位优先。
getUint32(start, isLittleEndian=false)从start字节处读取4个字节并返回32位无符号整数,默认高位优先。
getFloat32(start, isLittleEndian=false)从start字节处读取4个字节并返回32位浮点数,默认高位优先。
getFloat64(start, isLittleEndian=false)从start字节处读取8个字节并返回64位浮点数,默认高位优先。

格式化写入ArrayBuffer数组缓存数据

方法描述
setInt8(start, value, isLittleEndian=false)在start字节位置写入1个字节8位有符号整数value,默认高位优先。
setUint8(start, value, isLittleEndian=false)在start字节位置写入1个字节8位无符号整数value,默认高位优先。
setInt16(start, value, isLittleEndian=false)在start字节位置写入2个字节16位有符号整数value,默认高位优先。
setUint16(start, value, isLittleEndian=false)在start字节位置写入2个字节16位无符号整数value,默认高位优先。
setInt32(start, value, isLittleEndian=false)在start字节位置写入4个字节32位有符号整数value,默认高位优先。
setUint32(start, value, isLittleEndian=false)在start字节位置写入4个字节32位无符号整数value,默认高位优先。
setFloat32(start, value, isLittleEndian=false)在start字节位置写入4个字节32位浮点数value,默认高位优先。
setFloat64(start, value, isLittleEndian=false)在start字节位置写入8个字节64位浮点数value,默认高位优先。

如果不确定正在使用的计算机的字节序,可使用一下方式进行判断。


let isLittleEndian = (()=>{
  let buf = new ArrayBuffer(2);
  new DataView(buf).setInt16(0, 256, true);
  return new Int16Array(buf)[0] === 256;
})();

Laya.Byte

Laya项目开发中对二进制的操作是不可或缺的,Laya的 Byte 在参考ActionScript3.0的二进制数组 ByteArray 的同时承接了H5的 TypedArray 类型化数组的特点。ByteArray 字节数组是二进制数据组成的序列,其中每个元素都是由 8Bit 二进制位组成。

Laya的Byte封装的其实就是H5的类型化数组,开发者可以参考MDN官方的API来进行扩展。

项目描述
Packagelaya.utils
ClassLaya.Byte
InheritanceByte / Object

Laya的Byte类提供用于优化读取、写入以及处理二进制数据的方法和属性。Byte类适用于需要在字节层访问数据的高级开发人员。

构造函数

// 创建一个字节类的实例
let byte = new Laya.Byte(data?:any);

构造函数参数 data 用于指定初始化的元素数量,或者用于初始化的 TypedArray 类型化数组对象、ArrayBuffer 对象。如果为 null 则预分配一定的内存空间,当可用空间不足时会优先使用此部分内存,如果仍然不够则会重新分配所需内存空间。

属性描述
BIG_ENDIAN主机字节序,大端字节序。
LITTLE_ENDIAN主机字节序,小端字节序。

主机字节序 Endian 是CPU存放数据的两种不同顺序,包括大端字节序 BIG_ENDIAN 和小端字节序 LITTLE_ENDIAN 。可以通过 getSystemEndian() 方法获取当前系统的字节序类型。

  • 大端字节序 BIG_ENDIAN 地址低位存储值的高位,地址高位存储值的低位,又称为网络字节序。
  • 小端字节序 LITTLE_ENDIAN 地址低位存储值的低位,地址高位存储值的高位。
存取器描述
buffer获取当前对象的ArrayBuffer数据,只包含有效数据部分。
endian字节实例的字节序类型
pos移动或返回字节对象的读写指针的当前位置
bytesAvailable从字节流的当前位置到末尾,读取的数据的字节流。
length字节对象的长度,以字节为单位。
方法描述
clear()清除字节数组的内容
getUTFBytes(len?:number):string从字节流中读取一个由length参数指定的长度的UTF-8字节序列并返回一个字符串,一般读取的是由writeUTFBytes方法写入的字符串。
getUTFString():string从字节流中读取一个UTF-8字符串,假定字符串的前缀是一个无符号的短整型,以此字节表示要读取的长度。对应的写入方法是writeUTFString
readArrayBuffer(length:number):ArrayBuffer读取ArrayBuffer数据
readByte():number从字节流中读取待符号的字节,返回值的范围是从-128~127。
readFloat32():number从字节流的当前字节偏移位置读取一个IEEE754单精度32位浮点数
readFloat32Array(start:number, length:number):any从字节流中start参数指定的位置开始读取length参数指定的字节数的数据,用于创建一个Float32Array对象并返回此对象。
readFloat64():number从字节流的当前字节偏移量位置处读取一个IEEE754双精度64位浮点数
readInt16():number从字节流的当前字节偏移量位置处读取一个Int16值
readInt16Array(start:number, length:number):any从字节流中start参数指定的位置开始,读取length参数指定的字节数的数据,用于创建一个Int16Array对象并返回此对象。
readInt32():number从字节流的当前字节偏移量位置处读取一个Int32值
readString():string常用于解析固定格式的字节流,先从字节流的当前字节偏移位置处读取一个Uint16值,然后以此值为长度读取此长度的字符串。
readUint16():number从字节流的当前字节偏移量位置处读取一个Uint16值
readUint32():number从字节流的当前字节偏移量位置处读取一个Uint32值
readUint8():number从字节流的当前字节偏移量位置处读取一个Uint8值
readUint8Array(start:number, length:number):Uint8Array从字节流中start参数指定的位置开始读取length参数指定的字节数的数据用于创建一个Uint8Array对象并返回此对象。
writeArrayBuffer(arraybuffer:any, offset?:number, length?:number):void将指定arraybuffer对象中以offset为起始偏移量,length为长度的字节序列写入字节流。
writeByte(value:number):void在字节流中写入一个字节
writeFloat32(value:number):void在字节流的当前字节偏移量位置处写入一个IEEE754单精度32位浮点数
writeFloat64(value:number):void在字节流的当前字节偏移量位置处写入一个IEEE754双精度64位浮点数
writeInt16(value:number):void在字节流的当前字节偏移量位置处写入指定的Int16值
writeInt32(value:number):void在字节流的当前字节偏移量位置处写入指定的Int32值
writeUTFBytes(value:string):void将UTF-8字符串写入字节流
writeUTFString(value:string):void将UTF-8字符串写入字节流
writeUint16(value:number):void在字节流的当前字节偏移量位置处写入指定的Uint16值
writeUint32(value:number):void在字节流的当前字节偏移量位置处写入Uint32值
writeUint8(value:number):void在字节流的当前字节偏移量位置处写入指定的Uint8值
getSystemEndian():string获取当前主机的字节序
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值