UNPKG

5.66 kBJavaScriptView Raw
1class ArrayBufferStream {
2 /**
3 * ArrayBufferStream wraps the built-in javascript ArrayBuffer, adding the ability to access
4 * data in it like a stream, tracking its position.
5 * You can request to read a value from the front of the array, and it will keep track of the position
6 * within the byte array, so that successive reads are consecutive.
7 * The available types to read include:
8 * Uint8, Uint8String, Int16, Uint16, Int32, Uint32
9 * @param {ArrayBuffer} arrayBuffer - array to use as a stream
10 * @param {number} start - the start position in the raw buffer. position
11 * will be relative to the start value.
12 * @param {number} end - the end position in the raw buffer. length and
13 * bytes available will be relative to the end value.
14 * @param {ArrayBufferStream} parent - if passed reuses the parent's
15 * internal objects
16 * @constructor
17 */
18 constructor (
19 arrayBuffer, start = 0, end = arrayBuffer.byteLength,
20 {
21 _uint8View = new Uint8Array(arrayBuffer)
22 } = {}
23 ) {
24 /**
25 * Raw data buffer for stream to read.
26 * @type {ArrayBufferStream}
27 */
28 this.arrayBuffer = arrayBuffer;
29
30 /**
31 * Start position in arrayBuffer. Read values are relative to the start
32 * in the arrayBuffer.
33 * @type {number}
34 */
35 this.start = start;
36
37 /**
38 * End position in arrayBuffer. Length and bytes available are relative
39 * to the start, end, and _position in the arrayBuffer;
40 * @type {number};
41 */
42 this.end = end;
43
44 /**
45 * Cached Uint8Array view of the arrayBuffer. Heavily used for reading
46 * Uint8 values and Strings from the stream.
47 * @type {Uint8Array}
48 */
49 this._uint8View = _uint8View;
50
51 /**
52 * Raw position in the arrayBuffer relative to the beginning of the
53 * arrayBuffer.
54 * @type {number}
55 */
56 this._position = start;
57 }
58
59 /**
60 * Return a new ArrayBufferStream that is a slice of the existing one
61 * @param {number} length - the number of bytes of extract
62 * @return {ArrayBufferStream} the extracted stream
63 */
64 extract (length) {
65 return new ArrayBufferStream(this.arrayBuffer, this._position, this._position + length, this);
66 }
67
68 /**
69 * @return {number} the length of the stream in bytes
70 */
71 getLength () {
72 return this.end - this.start;
73 }
74
75 /**
76 * @return {number} the number of bytes available after the current position in the stream
77 */
78 getBytesAvailable () {
79 return this.end - this._position;
80 }
81
82 /**
83 * Position relative to the start value in the arrayBuffer of this
84 * ArrayBufferStream.
85 * @type {number}
86 */
87 get position () {
88 return this._position - this.start;
89 }
90
91 /**
92 * Set the position to read from in the arrayBuffer.
93 * @type {number}
94 * @param {number} value - new value to set position to
95 */
96 set position (value) {
97 this._position = value + this.start;
98 }
99
100 /**
101 * Read an unsigned 8 bit integer from the stream
102 * @return {number} the next 8 bit integer in the stream
103 */
104 readUint8 () {
105 const val = this._uint8View[this._position];
106 this._position += 1;
107 return val;
108 }
109
110 /**
111 * Read a sequence of bytes of the given length and convert to a string.
112 * This is a convenience method for use with short strings.
113 * @param {number} length - the number of bytes to convert
114 * @return {string} a String made by concatenating the chars in the input
115 */
116 readUint8String (length) {
117 const arr = this._uint8View;
118 let str = '';
119 const end = this._position + length;
120 for (let i = this._position; i < end; i++) {
121 str += String.fromCharCode(arr[i]);
122 }
123 this._position += length;
124 return str;
125 }
126
127 /**
128 * Read a 16 bit integer from the stream
129 * @return {number} the next 16 bit integer in the stream
130 */
131 readInt16 () {
132 const val = new Int16Array(this.arrayBuffer, this._position, 1)[0];
133 this._position += 2; // one 16 bit int is 2 bytes
134 return val;
135 }
136
137 /**
138 * Read an unsigned 16 bit integer from the stream
139 * @return {number} the next unsigned 16 bit integer in the stream
140 */
141 readUint16 () {
142 const val = new Uint16Array(this.arrayBuffer, this._position, 1)[0];
143 this._position += 2; // one 16 bit int is 2 bytes
144 return val;
145 }
146
147 /**
148 * Read a 32 bit integer from the stream
149 * @return {number} the next 32 bit integer in the stream
150 */
151 readInt32 () {
152 let val;
153 if (this._position % 4 === 0) {
154 val = new Int32Array(this.arrayBuffer, this._position, 1)[0];
155 } else {
156 // Cannot read Int32 directly out because offset is not multiple of 4
157 // Need to slice out the values first
158 val = new Int32Array(
159 this.arrayBuffer.slice(this._position, this._position + 4)
160 )[0];
161 }
162 this._position += 4; // one 32 bit int is 4 bytes
163 return val;
164 }
165
166 /**
167 * Read an unsigned 32 bit integer from the stream
168 * @return {number} the next unsigned 32 bit integer in the stream
169 */
170 readUint32 () {
171 const val = new Uint32Array(this.arrayBuffer, this._position, 1)[0];
172 this._position += 4; // one 32 bit int is 4 bytes
173 return val;
174 }
175}
176
177module.exports = ArrayBufferStream;