概述

JavaScript 中提供了 ArrayBufferUint8ArrayTypedArray 可以对二进制数据进行操作。而 Node.js 中还提供了一个 Buffer 类用于二进制数据的操作。但这些对象没有自动伸缩性,不方便流式操作,相互转换也比较麻烦。

为方便操作二进制数据,hprose 提供了一个 BytesIO 对象。BytesIO 是一个可自动伸缩的,可流式读写数据的工具类。

该工具类还可以对 StringArrayBufferUint8ArrayBuffer 进行快速的相互转换。

创建 BytesIO 对象

  1. new BytesIO();
  2. new BytesIO(data);
  3. new BytesIO(capacity);
  4. new BytesIO(data[, byteOffset[, length]]);

不带参数的构造器创建一个空的 BytesIO 对象。空的 BytesIO 对象可以进行写操作。

带 1 个参数的构造器,其中 data 参数可以是 StringBytesIOUint8ArrayArrayBufferBuffer、其它 TypedArray 类型的对象或是元素是纯整数且数字范围是 0-255 的普通数组。

如果参数为 1 个整数值 capacity,则表示预分配 capacity 个字节的空间,当写入比较大量的数据时,预分配合适的空间将会有效的提高运行效率。

带 2-3 个参数的构造器,其中 data 参数为 ArrayBuffer 类型的对象,byteOffset 为起始偏移量,length 为数据长度。

方法

toString 方法

  1. BytesIO.toString(data);

该方法的功能是将 data 转换为字符串返回,data 本身不会被修改。

其中,data 参数可以为:StringBufferBytesIOArrayBufferUint8Array 类型的对象或保存有 charcode 的数字数组。

如果是 String 类型的对象,则直接返回该对象。

如果是 BufferBytesIOArrayBufferUint8Array 类型的对象,则按照 UTF-8 编码对其中的二进制数据进行解析并返回解析后的 String 对象。

如果这些二进制数据不能按照 UTF-8 编码解析成字符串,会抛出异常。

如果是保存有 charcode 的数字数组,则按照 UTF-16 编码进行解析,并返回解析后的 String 对象。

toBuffer 方法

  1. BytesIO.toBuffer(data);

该方法的功能是将 data 转换为 Buffer 类型的对象返回,data 本身不会被修改。

其中,data 参数可以为:BufferStringBytesIOArrayBufferUint8Array 类型的对象,或是元素是纯整数且数字范围是 0-255 的普通数组。

如果是 Buffer 类型的对象,则直接返回该对象。

如果是 String 类型的对象,则返回 UTF-8 编码的 Buffer 对象。

如果是 BytesIOArrayBufferUint8Array 类型的对象,或是元素是纯整数且数字范围是 0-255 的普通数组,则将其中的二进制数据转换为 Buffer 对象返回。

BytesIO.prototype 上的属性

下面我们用 bytesIO 这个变量名来指代 BytesIO 的实例对象。

length 属性

  1. bytesIO.length;

只读属性,返回当前 bytesIO 对象的长度。

capacity 属性

  1. bytesIO.capacity;

只读属性,返回当前 bytesIO 对象的容量,当写入数据超过该容量时,bytesIO 对象会自动扩容,capacity 的数值也会相应改变。

position 属性

  1. bytesIO.position;

只读属性,该属性表示在对 bytesIO 对象进行读取操作时的当前位置。当 position 为 0 时,表示位于数据的开头,当 positionlength 相同时,表示已没有可读取的数据。

bytes 属性

  1. bytesIO.bytes;

只读属性,该属性返回 bytesIO 对象内部二进制数据的一个 Uint8Array 的共享视图。因为没有数据复制操作,所以返回该属性非常快速。注意对该属性所返回的数据进行写操作是不安全的,它会连同 bytesIO 对象内部的数据一起修改。

BytesIO.prototype 上的方法

下面我们同样用 bytesIO 这个变量名来指代 BytesIO 的实例对象。

mark 方法

  1. bytesIO.mark();

保存当前的读写位置。如果对该方法进行多次调用,则只有最后一次保存的读写位置可以被恢复。

无返回值。

reset 方法

  1. bytesIO.reset();

恢复由 mark 方法保存的读写位置。

无返回值。

clear 方法

  1. bytesIO.reset();

清空 bytesIO 对象,并将读写位置归零。

无返回值。

writeByte 方法

  1. bytesIO.writeByte(byte);

bytesIO 对象的末尾写入一个字节。

byte 的范围是 0-255 的整数。

执行成功后,bytesIO 对象的 length 属性将会相应加 1。如果容量不足,将会自动扩容。

无返回值。

writeInt32BE 方法

  1. bytesIO.writeInt32BE(int32);

bytesIO 对象的末尾按照 BigEndian 方式写入一个32位有符号整数(4个字节)。

int32 的范围是从 -2147483648 到 2147483647,超出范围会抛出 TypeError('value is out of bounds') 的异常。

执行成功后,bytesIO 对象的 length 属性将会相应加 4。如果容量不足,将会自动扩容。

无返回值。

writeUInt32BE 方法

  1. bytesIO.writeUInt32BE(uint32);

bytesIO 对象的末尾按照 BigEndian 方式写入一个32位无符号整数(4个字节)。

uint32 的范围是从 0 到 4294967295,超出范围会抛出 TypeError('value is out of bounds') 的异常。

执行成功后,bytesIO 对象的 length 属性将会相应加 4。如果容量不足,将会自动扩容。

无返回值。

writeInt32LE 方法

  1. bytesIO.writeInt32LE(int32);

bytesIO 对象的末尾按照 LittleEndian 方式写入一个32位有符号整数(4个字节)。

int32 的范围是从 -2147483648 到 2147483647,超出范围会抛出 TypeError('value is out of bounds') 的异常。

执行成功后,bytesIO 对象的 length 属性将会相应加 4。如果容量不足,将会自动扩容。

无返回值。

writeUInt32LE 方法

  1. bytesIO.writeUInt32LE(uint32);

bytesIO 对象的末尾按照 LittleEndian 方式写入一个32位无符号整数(4个字节)。

uint32 的范围是从 0 到 4294967295,超出范围会抛出 TypeError('value is out of bounds') 的异常。

执行成功后,bytesIO 对象的 length 属性将会相应加 4。如果容量不足,将会自动扩容。

无返回值。

write 方法

  1. bytesIO.write(data);

bytesIO 对象的末尾写入二进制数据 data

其中 data 可以是 ArrayBufferUint8ArrayBytesIOBuffer 类型的对象,或元素是纯整数且数字范围是 0-255 的普通数组。

执行成功后,bytesIO 对象的 length 属性将会增加相应的长度。如果容量不足,将会自动扩容。

无返回值。

writeAsciiString 方法

  1. bytesIO.writeAsciiString(str);

bytesIO 对象的末尾写入 ASCII 编码的字符串数据 str

其中 str 中的每个字符的 charcode 编码范围为 0-255。

执行成功后,bytesIO 对象的 length 属性将会增加相应的长度。如果容量不足,将会自动扩容。

无返回值。

writeString 方法

  1. bytesIO.writeString(str);

bytesIO 对象的末尾按照 UTF-8 编码的方式写入字符串数据 str

执行成功后,bytesIO 对象的 length 属性将会增加相应的长度。如果容量不足,将会自动扩容。

无返回值。

readByte 方法

  1. bytesIO.readByte();

bytesIO 对象的当前位置读取一个字节,并返回。

执行成功后,bytesIO 对象的 position 属性将会相应加 1。

如果当前位置已在结尾,则返回 -1。

readInt32BE 方法

  1. bytesIO.readInt32BE();

bytesIO 对象的当前位置读取 4 个字节,按照 BigEndian 编码方式转换为一个 32 位有符号整型数,并返回。

执行成功后,bytesIO 对象的 position 属性将会相应加 4。

如果当前位置到结尾不足 4 个字节,则抛出 Error('EOF') 异常。

readUInt32BE 方法

  1. bytesIO.readUInt32BE();

bytesIO 对象的当前位置读取 4 个字节,按照 BigEndian 编码方式转换为一个 32 位无符号整型数,并返回。

执行成功后,bytesIO 对象的 position 属性将会相应加 4。

如果当前位置到结尾不足 4 个字节,则抛出 Error('EOF') 异常。

readInt32LE 方法

  1. bytesIO.readInt32LE();

bytesIO 对象的当前位置读取 4 个字节,按照 LittleEndian 编码方式转换为一个 32 位有符号整型数,并返回。

执行成功后,bytesIO 对象的 position 属性将会相应加 4。

如果当前位置到结尾不足 4 个字节,则抛出 Error('EOF') 异常。

readUInt32LE 方法

  1. bytesIO.readUInt32LE();

bytesIO 对象的当前位置读取 4 个字节,按照 LittleEndian 编码方式转换为一个 32 位无符号整型数,并返回。

执行成功后,bytesIO 对象的 position 属性将会相应加 4。

如果当前位置到结尾不足 4 个字节,则抛出 Error('EOF') 异常。

read 方法

  1. bytesIO.read(n);

bytesIO 对象的当前位置读取 n 个字节,并返回。返回结果是一个 Uint8Array 类型的共享视图。

执行成功后,bytesIO 对象的 position 属性将会相应加 n

如果当前位置到结尾不足 n 个字节,则读取全部剩余字节并返回,bytesIO 对象的 position 属性将被设置为与 length 相同。

因为返回结果是 bytesIO 对象内部数据的一部分的共享视图,因此该操作速度很快。但是对读取结果进行写操作是不安全的,它会影响 bytesIO 的内部数据。因此,如果需要对返回结果进行写操作,最好是使用 new Uint8Array(data) 来获取一个数据副本,在该副本上进行写操作,其中 data 表示返回数据。

skip 方法

  1. bytesIO.skip(n);

bytesIO 对象的当前位置略过 n 个字节,并返回实际略过的字节数。

执行成功后,bytesIO 对象的 position 属性将会相应加 n。

如果当前位置到结尾不足 n 个字节,则略过全部剩余字节并返回略过的剩余字节数,bytesIO 对象的 position 属性将被设置为与 length 相同。

readBytes 方法

  1. bytesIO.readBytes(tag);

bytesIO 对象的当前位置开始读取,直到遇到与 tag 相同的字节为止,并以 Uint8Array 类型的对象返回读取到的二进制数据。

执行成功后,bytesIO 对象的 position 属性将会相应增加读取到的字节数。

读取到的二进制数据中包含有最后的 tag 字节。

如果读取到结尾仍然没有遇到与 tag 相同的字节,则返回所有剩余的字节,bytesIO 对象的 position 属性将被设置为与 length 相同。

返回结果是 bytesIO 对象内部数据的一部分的共享视图,因此该操作速度很快。但是对读取结果进行写操作是不安全的,它会影响 bytesIO 的内部数据。因此,如果需要对返回结果进行写操作,最好是使用 new Uint8Array(data) 来获取一个数据副本,在该副本上进行写操作,其中 data 表示返回数据。

readUntil 方法

  1. bytesIO.readUntil(tag);

bytesIO 对象的当前位置开始读取,直到遇到与 tag 相同的字节为止,并将读取到的二进制数据按照 UTF-8 编码解析为字符串返回。

执行成功后,bytesIO 对象的 position 属性将会相应增加读取到的字节数。

读取到的数据中不包含有最后的 tag 字节。但在位置计算上会算上 tag 字节。

如果读取到结尾仍然没有遇到与 tag 相同的字节,则所有剩余的数据按照 UTF-8 编码解析为字符串返回。bytesIO 对象的 position 属性将被设置为与 length 相同。

readAsciiString 方法

  1. bytesIO.readAsciiString(n);

读取 n 个字节,并按照 ASCII 编码方式解析为字符串返回。

执行成功后,bytesIO 对象的 position 属性将会相应增加读取到的字节数。

如果当前位置到结尾不足 n 个字节,则读取全部剩余字节并按照 ASCII 编码方式解析为字符串返回,bytesIO 对象的 position 属性将被设置为与 length 相同。

readString 方法

  1. bytesIO.readString(n);

读取 n 个字符,并按照 UTF-8 编码方式解析为字符串返回。这里的 n 指的是最后读取到的字符串的 length 长度,而不是读取的字节数。

执行成功后,bytesIO 对象的 position 属性将会相应增加读取到的字节数。

如果当前位置到结尾不足 n 个字符,则读取全部剩余字节并按照 UTF-8 编码方式解析为字符串返回,bytesIO 对象的 position 属性将被设置为与 length 相同。

如果读取到的二进制数据不能按照 UTF-8 编码解析成字符串,会抛出异常。

readStringAsBytes 方法

  1. bytesIO.readStringAsBytes(n);

该功能与上面的 readString 方法类似,但是返回的结果是 UTF-8 编码的 Uint8Array 实例对象,同 readString 方法相比,因为减少了解码过程,所以该方法速度要明显快于 readString 方法。

另外要注意,本方法的返回结果是 bytesIO 对象内部数据的一部分的共享视图,因此对读取结果进行写操作是不安全的,它会影响 bytesIO 的内部数据。如果需要对返回结果进行写操作,最好是使用 new Uint8Array(data) 来获取一个数据副本,在该副本上进行写操作,其中 data 表示返回数据。

takeBytes 方法

  1. bytesIO.takeBytes();

该方法的返回值跟 bytes 属性返回值相同。区别是该方法在返回值后,bytesIO 中的数据会被清空。因此返回的数据可以安全进行写操作。

toBytes 方法

  1. bytesIO.toBytes();

该方法的返回值是 bytes 属性返回值的副本。因此返回的数据可以安全进行写操作。但因为要创建副本,因此,速度上要比使用 bytes 属性慢一些。

toBuffer 方法

  1. bytesIO.toBuffer();

该方法返回一个 Buffer 类型的对象,其中的数据跟 bytesIO 对象中的二进制数据相同。

toString 方法

  1. bytesIO.toString();

该方法将对 bytesIO 对象中的所有二进制数据按照 UTF-8 编码进行解析并返回字符串。

如果 bytesIO 对象中的二进制数据不能按照 UTF-8 编码解析成字符串,会抛出异常。

clone 方法

  1. bytesIO.clone();

返回 bytesIO 对象的副本。

trunc 方法

  1. bytesIO.trunc();

bytesIO 对象中已经读取的数据部分进行截断处理,只保留未读取的数据,读取偏移量将归零,数据长度将变为剩余数据的长度。如果之前执行过 mark 方法,mark 方法保存的位置信息也会被归零。

原文: https://github.com/hprose/hprose-nodejs/wiki/%E8%BE%93%E5%85%A5%E8%BE%93%E5%87%BA%E2%80%94%E2%80%94BytesIO