409 lines
9.6 KiB
Markdown
409 lines
9.6 KiB
Markdown
|
# ByteBuffer
|
|||
|
|
|||
|
[](https://www.npmjs.org/package/byte-buffer)
|
|||
|

|
|||
|
[](LICENSE.md)
|
|||
|

|
|||
|
[](https://codeclimate.com/github/timkurvers/byte-buffer)
|
|||
|
[](https://codeclimate.com/github/timkurvers/byte-buffer)
|
|||
|
|
|||
|
Wrapper for JavaScript's ArrayBuffer/DataView maintaining index and default
|
|||
|
endianness. Supports arbitrary reading/writing, implicit growth, clipping,
|
|||
|
cloning and reversing as well as UTF-8 characters and NULL-terminated C-strings.
|
|||
|
|
|||
|
## Installation
|
|||
|
|
|||
|
ByteBuffer is available via [npm]:
|
|||
|
|
|||
|
```shell
|
|||
|
npm install byte-buffer
|
|||
|
```
|
|||
|
|
|||
|
Or for usage in the browser:
|
|||
|
|
|||
|
- `dist/byte-buffer.js`
|
|||
|
- `dist/byte-buffer.min.js`
|
|||
|
|
|||
|
## Usage
|
|||
|
|
|||
|
As an ECMAScript module:
|
|||
|
|
|||
|
```javascript
|
|||
|
import ByteBuffer from 'byte-buffer';
|
|||
|
|
|||
|
const b = new ByteBuffer();
|
|||
|
```
|
|||
|
|
|||
|
In CommonJS environments:
|
|||
|
|
|||
|
```javascript
|
|||
|
const ByteBuffer = require('byte-buffer');
|
|||
|
|
|||
|
const b = new ByteBuffer();
|
|||
|
```
|
|||
|
|
|||
|
Available in the global scope when included in browser environments:
|
|||
|
|
|||
|
```javascript
|
|||
|
const b = new ByteBuffer();
|
|||
|
```
|
|||
|
|
|||
|
## API
|
|||
|
|
|||
|
ByteBuffer's API borrows heavily from Adobe's [IDataInput] and [IDataOutput] as
|
|||
|
well as David Flanagan's [BufferView].
|
|||
|
|
|||
|
The concept of separate buffers and views - as outlined in MDN's [JavaScript
|
|||
|
typed arrays] - is *not* used. ByteBuffer handles this separation for you.
|
|||
|
|
|||
|
### Constants
|
|||
|
|
|||
|
Use the following constants to indicate endianness:
|
|||
|
|
|||
|
```javascript
|
|||
|
ByteBuffer.BIG_ENDIAN
|
|||
|
ByteBuffer.LITTLE_ENDIAN
|
|||
|
```
|
|||
|
|
|||
|
### Construction
|
|||
|
|
|||
|
```javascript
|
|||
|
new ByteBuffer(1) // Buffer of one byte with big-endian byte order
|
|||
|
new ByteBuffer(1, ByteBuffer.LITTLE_ENDIAN) // Little-endian byte order instead
|
|||
|
```
|
|||
|
|
|||
|
ByteBuffers may also be constructed from other byte-aware sources:
|
|||
|
|
|||
|
```javascript
|
|||
|
new ByteBuffer(new ArrayBuffer(2))
|
|||
|
new ByteBuffer(new Uint8Array(3))
|
|||
|
new ByteBuffer(new DataView(new ArrayBuffer(4)))
|
|||
|
new ByteBuffer(new ByteBuffer(5))
|
|||
|
```
|
|||
|
|
|||
|
Or from generic sequences:
|
|||
|
|
|||
|
```javascript
|
|||
|
new ByteBuffer([0, 1, 2, 3])
|
|||
|
```
|
|||
|
|
|||
|
After construction a ByteBuffer's read/write index is always at the front of the
|
|||
|
buffer. Hereafter ```b``` is assumed to be an instance of ByteBuffer.
|
|||
|
|
|||
|
### Properties
|
|||
|
|
|||
|
```javascript
|
|||
|
b.buffer // Reference to internal ArrayBuffer
|
|||
|
b.buffer = new ArrayBuffer(3) // Sets new buffer
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.raw // Reference to raw buffer (read-only)
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.view // Reference to internal DataView (read-only)
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.length // Number of bytes in the buffer (read-only)
|
|||
|
b.byteLength // Alias
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.order // Buffer's current default byte order
|
|||
|
b.order = ByteBuffer.BIG_ENDIAN // Sets byte order
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.available // Number of available bytes (read-only)
|
|||
|
```
|
|||
|
|
|||
|
### Index Manipulation
|
|||
|
|
|||
|
ByteBuffer maintains a read/write index to simplify usage.
|
|||
|
|
|||
|
```javascript
|
|||
|
b.index // Current read/write index
|
|||
|
b.index = 4 // Sets index
|
|||
|
```
|
|||
|
|
|||
|
If the index is out of bounds, a RangeError will be thrown.
|
|||
|
|
|||
|
```javascript
|
|||
|
b.front() // Sets index to front of the buffer
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.end() // Sets index to end of the buffer
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.seek(10) // Forwards ten bytes
|
|||
|
b.seek(-2) // Backs two bytes
|
|||
|
```
|
|||
|
|
|||
|
These methods may be chained:
|
|||
|
|
|||
|
```javascript
|
|||
|
b.front().seek(2)
|
|||
|
```
|
|||
|
|
|||
|
### Read API
|
|||
|
|
|||
|
All read methods default to the ByteBuffer's byte order if not given.
|
|||
|
|
|||
|
```javascript
|
|||
|
b.readByte()
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.readUnsignedByte()
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.readShort() // Buffer's default byte order
|
|||
|
b.readShort(ByteBuffer.LITTLE_ENDIAN) // Explicit byte order
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.readUnsignedShort()
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.readInt()
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.readUnsignedInt()
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.readFloat()
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.readDouble()
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.read(6) // Reads 6 bytes
|
|||
|
b.read() // Reads all remaining bytes
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.readString(5) // Reads 5 bytes as a string
|
|||
|
b.readString() // Reads all remaining bytes as a string
|
|||
|
b.readUTFChars() // Alias
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.readCString() // Reads string up to NULL-byte or end of buffer
|
|||
|
```
|
|||
|
|
|||
|
### Write API
|
|||
|
|
|||
|
All write methods default to the ByteBuffer's byte order if not given.
|
|||
|
|
|||
|
```javascript
|
|||
|
b.writeByte(10)
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.writeUnsignedByte(-10)
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.writeShort(-2048)
|
|||
|
b.writeShort(-2048, ByteBuffer.LITTLE_ENDIAN) // Explicit byte order
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.writeUnsignedShort(4096)
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.writeInt(-524288)
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.writeUnsignedInt(1048576)
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.writeFloat(13.37)
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.writeDouble(1048576.89)
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.write([1, 2, 3])
|
|||
|
b.write(new ArrayBuffer(2))
|
|||
|
b.write(new Uint8Array(3))
|
|||
|
b.write(new ByteBuffer(5))
|
|||
|
```
|
|||
|
|
|||
|
Additionally, all the above write methods may be chained:
|
|||
|
|
|||
|
```javascript
|
|||
|
b.writeShort(0x2020).write([1, 2, 3])
|
|||
|
```
|
|||
|
|
|||
|
The following string related methods do not return the buffer itself, but rather
|
|||
|
provide the number of bytes that were written to it. More on this under implicit
|
|||
|
growth strategy a bit further down.
|
|||
|
|
|||
|
```javascript
|
|||
|
b.writeString('ByteBuffer') // Writes given string and returns number of bytes
|
|||
|
b.writeUTFChars('ByteBuffer') // Alias
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.writeCString('ByteBuffer') // Writes given string and returns number of bytes (including NULL-byte)
|
|||
|
```
|
|||
|
|
|||
|
### Size Manipulation
|
|||
|
|
|||
|
#### Growth
|
|||
|
|
|||
|
The buffer may be grown at the front or at the end. When prepending, the buffer's
|
|||
|
index is adjusted accordingly.
|
|||
|
|
|||
|
```javascript
|
|||
|
b.prepend(2) // Prepends given number of bytes
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.append(2) // Appends given number of bytes
|
|||
|
```
|
|||
|
|
|||
|
#### Implicit Growth
|
|||
|
|
|||
|
This feature allows a ByteBuffer to grow implicitly when writing arbitrary data.
|
|||
|
Since every implicit growth requires the buffer to be rebuilt from scratch, care
|
|||
|
must be taken when using this feature. Writing low byte-length pieces of data in
|
|||
|
rapid succession is not recommended.
|
|||
|
|
|||
|
To protect the unaware from harm, this feature needs to be explicitly enabled:
|
|||
|
|
|||
|
```javascript
|
|||
|
b = new ByteBuffer(2, ByteBuffer.BIG_ENDIAN, true) // Last argument indicates implicit growth strategy
|
|||
|
b.writeUnsignedInt(2345102) // Implicitly makes room for 4 bytes - by growing with 2 - prior to writing
|
|||
|
```
|
|||
|
|
|||
|
The implicit growth strategy can also be enabled and disabled after construction:
|
|||
|
|
|||
|
```javascript
|
|||
|
b.implicitGrowth = true/false
|
|||
|
```
|
|||
|
|
|||
|
Implicit growth is a must when dealing with UTF-8 encoded strings, as dealing
|
|||
|
with arbitrary user data - e.g. names or addresses - *may* include various
|
|||
|
characters that require to be encoded in multiple bytes, which would be
|
|||
|
relatively verbose to calculate beforehand.
|
|||
|
|
|||
|
#### Clipping
|
|||
|
|
|||
|
The buffer may be truncated at the front, end or both. Both arguments are
|
|||
|
optional and may be negative in which case the offsets are calculated from the
|
|||
|
respective boundaries of the buffer. The `begin`-argument defaults to the current
|
|||
|
index, allowing efficient clipping in various scenarios, e.g. when used in
|
|||
|
combination with network sockets to shift off read data. The `end`-argument
|
|||
|
defaults to the end of the buffer.
|
|||
|
|
|||
|
```javascript
|
|||
|
b.clip(2, -2)
|
|||
|
b.clip(-2, 4)
|
|||
|
```
|
|||
|
|
|||
|
### Miscellaneous
|
|||
|
|
|||
|
```javascript
|
|||
|
b.slice(2, 4) // Independent clone of given slice of the buffer
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.clone() // Independent clone of the entire buffer
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.reverse() // Reverses buffer in place
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.toArray() // Changes to this array are not backed
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.toHex() // Hexadecimal representation of this buffer, e.g: 42 79 74 65 42 75 66 66 65 72
|
|||
|
```
|
|||
|
|
|||
|
```javascript
|
|||
|
b.toASCII() // ASCII representation of this buffer, e.g: B y t e B u f f e r
|
|||
|
```
|
|||
|
|
|||
|
## Development & Contribution
|
|||
|
|
|||
|
ByteBuffer is written in [ES2015+], modularized using [ECMAScript Modules],
|
|||
|
compiled by [Babel], bundled with [rollup] and tested through [Jest].
|
|||
|
|
|||
|
Getting this toolchain up and running, is easy and straight-forward:
|
|||
|
|
|||
|
1. Get the code:
|
|||
|
|
|||
|
```shell
|
|||
|
git clone git://github.com/timkurvers/byte-buffer.git
|
|||
|
```
|
|||
|
|
|||
|
2. Download and install [Node.js] – including `npm` – for your platform.
|
|||
|
|
|||
|
3. Install dependencies:
|
|||
|
|
|||
|
```shell
|
|||
|
npm install
|
|||
|
```
|
|||
|
|
|||
|
4. Run `npm test:watch` which will run tests when source files change.
|
|||
|
|
|||
|
When contributing, please:
|
|||
|
|
|||
|
- Fork the repository
|
|||
|
- Accompany each logical unit of operation with at least one test
|
|||
|
- Open a pull request
|
|||
|
- Do *not* include any distribution files (such as `dist/byte-buffer.js`)
|
|||
|
|
|||
|
## Alternative Comparisons
|
|||
|
|
|||
|
### Christopher Chedeau's [jDataView]
|
|||
|
|
|||
|
- Maintains read-index and supports seeking
|
|||
|
- Various string/char utilities (may support UTF-8)
|
|||
|
- Does *not* support writing values
|
|||
|
- Does *not* support NULL-terminated C-strings
|
|||
|
- Does *not* support growing, clipping, cloning and reversing
|
|||
|
- Supports a wide range of browsers/setups
|
|||
|
|
|||
|
### David Flanagan's [BufferView]
|
|||
|
|
|||
|
- Supports reading/writing values
|
|||
|
- Maintains index and supports seeking
|
|||
|
- Supports UTF-8 characters
|
|||
|
- Does *not* support NULL-terminated C-strings
|
|||
|
- Does *not* support growing, clipping, cloning and reversing as view and buffer
|
|||
|
are immutable
|
|||
|
|
|||
|
[Babel]: https://babeljs.io/
|
|||
|
[BufferView]: https://github.com/davidflanagan/BufferView
|
|||
|
[ECMAScript Modules]: https://nodejs.org/api/esm.html#esm_ecmascript_modules
|
|||
|
[ES2015+]: https://babeljs.io/docs/learn-es2015/
|
|||
|
[IDataInput]: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/IDataInput.html
|
|||
|
[IDataOutput]: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/IDataOutput.html
|
|||
|
[JavaScript typed arrays]: https://developer.mozilla.org/en/JavaScript_typed_arrays
|
|||
|
[Jest]: https://jestjs.io/
|
|||
|
[Node.js]: http://nodejs.org/#download
|
|||
|
[jDataView]: https://github.com/vjeux/jDataView/
|
|||
|
[npm]: https://www.npmjs.com
|
|||
|
[rollup]: https://rollupjs.org
|