1 | /**
|
2 | * Efficient schema-less binary decoding with support for variable length encoding.
|
3 | *
|
4 | * Use [lib0/decoding] with [lib0/encoding]. Every encoding function has a corresponding decoding function.
|
5 | *
|
6 | * Encodes numbers in little-endian order (least to most significant byte order)
|
7 | * and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/)
|
8 | * which is also used in Protocol Buffers.
|
9 | *
|
10 | * ```js
|
11 | * // encoding step
|
12 | * const encoder = encoding.createEncoder()
|
13 | * encoding.writeVarUint(encoder, 256)
|
14 | * encoding.writeVarString(encoder, 'Hello world!')
|
15 | * const buf = encoding.toUint8Array(encoder)
|
16 | * ```
|
17 | *
|
18 | * ```js
|
19 | * // decoding step
|
20 | * const decoder = decoding.createDecoder(buf)
|
21 | * decoding.readVarUint(decoder) // => 256
|
22 | * decoding.readVarString(decoder) // => 'Hello world!'
|
23 | * decoding.hasContent(decoder) // => false - all data is read
|
24 | * ```
|
25 | *
|
26 | * @module decoding
|
27 | */
|
28 |
|
29 | import * as binary from './binary.js'
|
30 | import * as math from './math.js'
|
31 | import * as number from './number.js'
|
32 | import * as string from './string.js'
|
33 | import * as error from './error.js'
|
34 | import * as encoding from './encoding.js'
|
35 |
|
36 | const errorUnexpectedEndOfArray = error.create('Unexpected end of array')
|
37 | const errorIntegerOutOfRange = error.create('Integer out of Range')
|
38 |
|
39 | /**
|
40 | * A Decoder handles the decoding of an Uint8Array.
|
41 | */
|
42 | export class Decoder {
|
43 | /**
|
44 | * @param {Uint8Array} uint8Array Binary data to decode
|
45 | */
|
46 | constructor (uint8Array) {
|
47 | /**
|
48 | * Decoding target.
|
49 | *
|
50 | * @type {Uint8Array}
|
51 | */
|
52 | this.arr = uint8Array
|
53 | /**
|
54 | * Current decoding position.
|
55 | *
|
56 | * @type {number}
|
57 | */
|
58 | this.pos = 0
|
59 | }
|
60 | }
|
61 |
|
62 | /**
|
63 | * @function
|
64 | * @param {Uint8Array} uint8Array
|
65 | * @return {Decoder}
|
66 | */
|
67 | export const createDecoder = uint8Array => new Decoder(uint8Array)
|
68 |
|
69 | /**
|
70 | * @function
|
71 | * @param {Decoder} decoder
|
72 | * @return {boolean}
|
73 | */
|
74 | export const hasContent = decoder => decoder.pos !== decoder.arr.length
|
75 |
|
76 | /**
|
77 | * Clone a decoder instance.
|
78 | * Optionally set a new position parameter.
|
79 | *
|
80 | * @function
|
81 | * @param {Decoder} decoder The decoder instance
|
82 | * @param {number} [newPos] Defaults to current position
|
83 | * @return {Decoder} A clone of `decoder`
|
84 | */
|
85 | export const clone = (decoder, newPos = decoder.pos) => {
|
86 | const _decoder = createDecoder(decoder.arr)
|
87 | _decoder.pos = newPos
|
88 | return _decoder
|
89 | }
|
90 |
|
91 | /**
|
92 | * Create an Uint8Array view of the next `len` bytes and advance the position by `len`.
|
93 | *
|
94 | * Important: The Uint8Array still points to the underlying ArrayBuffer. Make sure to discard the result as soon as possible to prevent any memory leaks.
|
95 | * Use `buffer.copyUint8Array` to copy the result into a new Uint8Array.
|
96 | *
|
97 | * @function
|
98 | * @param {Decoder} decoder The decoder instance
|
99 | * @param {number} len The length of bytes to read
|
100 | * @return {Uint8Array}
|
101 | */
|
102 | export const readUint8Array = (decoder, len) => {
|
103 | const view = new Uint8Array(decoder.arr.buffer, decoder.pos + decoder.arr.byteOffset, len)
|
104 | decoder.pos += len
|
105 | return view
|
106 | }
|
107 |
|
108 | /**
|
109 | * Read variable length Uint8Array.
|
110 | *
|
111 | * Important: The Uint8Array still points to the underlying ArrayBuffer. Make sure to discard the result as soon as possible to prevent any memory leaks.
|
112 | * Use `buffer.copyUint8Array` to copy the result into a new Uint8Array.
|
113 | *
|
114 | * @function
|
115 | * @param {Decoder} decoder
|
116 | * @return {Uint8Array}
|
117 | */
|
118 | export const readVarUint8Array = decoder => readUint8Array(decoder, readVarUint(decoder))
|
119 |
|
120 | /**
|
121 | * Read the rest of the content as an ArrayBuffer
|
122 | * @function
|
123 | * @param {Decoder} decoder
|
124 | * @return {Uint8Array}
|
125 | */
|
126 | export const readTailAsUint8Array = decoder => readUint8Array(decoder, decoder.arr.length - decoder.pos)
|
127 |
|
128 | /**
|
129 | * Skip one byte, jump to the next position.
|
130 | * @function
|
131 | * @param {Decoder} decoder The decoder instance
|
132 | * @return {number} The next position
|
133 | */
|
134 | export const skip8 = decoder => decoder.pos++
|
135 |
|
136 | /**
|
137 | * Read one byte as unsigned integer.
|
138 | * @function
|
139 | * @param {Decoder} decoder The decoder instance
|
140 | * @return {number} Unsigned 8-bit integer
|
141 | */
|
142 | export const readUint8 = decoder => decoder.arr[decoder.pos++]
|
143 |
|
144 | /**
|
145 | * Read 2 bytes as unsigned integer.
|
146 | *
|
147 | * @function
|
148 | * @param {Decoder} decoder
|
149 | * @return {number} An unsigned integer.
|
150 | */
|
151 | export const readUint16 = decoder => {
|
152 | const uint =
|
153 | decoder.arr[decoder.pos] +
|
154 | (decoder.arr[decoder.pos + 1] << 8)
|
155 | decoder.pos += 2
|
156 | return uint
|
157 | }
|
158 |
|
159 | /**
|
160 | * Read 4 bytes as unsigned integer.
|
161 | *
|
162 | * @function
|
163 | * @param {Decoder} decoder
|
164 | * @return {number} An unsigned integer.
|
165 | */
|
166 | export const readUint32 = decoder => {
|
167 | const uint =
|
168 | (decoder.arr[decoder.pos] +
|
169 | (decoder.arr[decoder.pos + 1] << 8) +
|
170 | (decoder.arr[decoder.pos + 2] << 16) +
|
171 | (decoder.arr[decoder.pos + 3] << 24)) >>> 0
|
172 | decoder.pos += 4
|
173 | return uint
|
174 | }
|
175 |
|
176 | /**
|
177 | * Read 4 bytes as unsigned integer in big endian order.
|
178 | * (most significant byte first)
|
179 | *
|
180 | * @function
|
181 | * @param {Decoder} decoder
|
182 | * @return {number} An unsigned integer.
|
183 | */
|
184 | export const readUint32BigEndian = decoder => {
|
185 | const uint =
|
186 | (decoder.arr[decoder.pos + 3] +
|
187 | (decoder.arr[decoder.pos + 2] << 8) +
|
188 | (decoder.arr[decoder.pos + 1] << 16) +
|
189 | (decoder.arr[decoder.pos] << 24)) >>> 0
|
190 | decoder.pos += 4
|
191 | return uint
|
192 | }
|
193 |
|
194 | /**
|
195 | * Look ahead without incrementing the position
|
196 | * to the next byte and read it as unsigned integer.
|
197 | *
|
198 | * @function
|
199 | * @param {Decoder} decoder
|
200 | * @return {number} An unsigned integer.
|
201 | */
|
202 | export const peekUint8 = decoder => decoder.arr[decoder.pos]
|
203 |
|
204 | /**
|
205 | * Look ahead without incrementing the position
|
206 | * to the next byte and read it as unsigned integer.
|
207 | *
|
208 | * @function
|
209 | * @param {Decoder} decoder
|
210 | * @return {number} An unsigned integer.
|
211 | */
|
212 | export const peekUint16 = decoder =>
|
213 | decoder.arr[decoder.pos] +
|
214 | (decoder.arr[decoder.pos + 1] << 8)
|
215 |
|
216 | /**
|
217 | * Look ahead without incrementing the position
|
218 | * to the next byte and read it as unsigned integer.
|
219 | *
|
220 | * @function
|
221 | * @param {Decoder} decoder
|
222 | * @return {number} An unsigned integer.
|
223 | */
|
224 | export const peekUint32 = decoder => (
|
225 | decoder.arr[decoder.pos] +
|
226 | (decoder.arr[decoder.pos + 1] << 8) +
|
227 | (decoder.arr[decoder.pos + 2] << 16) +
|
228 | (decoder.arr[decoder.pos + 3] << 24)
|
229 | ) >>> 0
|
230 |
|
231 | /**
|
232 | * Read unsigned integer (32bit) with variable length.
|
233 | * 1/8th of the storage is used as encoding overhead.
|
234 | * * numbers < 2^7 is stored in one bytlength
|
235 | * * numbers < 2^14 is stored in two bylength
|
236 | *
|
237 | * @function
|
238 | * @param {Decoder} decoder
|
239 | * @return {number} An unsigned integer.length
|
240 | */
|
241 | export const readVarUint = decoder => {
|
242 | let num = 0
|
243 | let mult = 1
|
244 | const len = decoder.arr.length
|
245 | while (decoder.pos < len) {
|
246 | const r = decoder.arr[decoder.pos++]
|
247 | // num = num | ((r & binary.BITS7) << len)
|
248 | num = num + (r & binary.BITS7) * mult // shift $r << (7*#iterations) and add it to num
|
249 | mult *= 128 // next iteration, shift 7 "more" to the left
|
250 | if (r < binary.BIT8) {
|
251 | return num
|
252 | }
|
253 | /* c8 ignore start */
|
254 | if (num > number.MAX_SAFE_INTEGER) {
|
255 | throw errorIntegerOutOfRange
|
256 | }
|
257 | /* c8 ignore stop */
|
258 | }
|
259 | throw errorUnexpectedEndOfArray
|
260 | }
|
261 |
|
262 | /**
|
263 | * Read signed integer (32bit) with variable length.
|
264 | * 1/8th of the storage is used as encoding overhead.
|
265 | * * numbers < 2^7 is stored in one bytlength
|
266 | * * numbers < 2^14 is stored in two bylength
|
267 | * @todo This should probably create the inverse ~num if number is negative - but this would be a breaking change.
|
268 | *
|
269 | * @function
|
270 | * @param {Decoder} decoder
|
271 | * @return {number} An unsigned integer.length
|
272 | */
|
273 | export const readVarInt = decoder => {
|
274 | let r = decoder.arr[decoder.pos++]
|
275 | let num = r & binary.BITS6
|
276 | let mult = 64
|
277 | const sign = (r & binary.BIT7) > 0 ? -1 : 1
|
278 | if ((r & binary.BIT8) === 0) {
|
279 | // don't continue reading
|
280 | return sign * num
|
281 | }
|
282 | const len = decoder.arr.length
|
283 | while (decoder.pos < len) {
|
284 | r = decoder.arr[decoder.pos++]
|
285 | // num = num | ((r & binary.BITS7) << len)
|
286 | num = num + (r & binary.BITS7) * mult
|
287 | mult *= 128
|
288 | if (r < binary.BIT8) {
|
289 | return sign * num
|
290 | }
|
291 | /* c8 ignore start */
|
292 | if (num > number.MAX_SAFE_INTEGER) {
|
293 | throw errorIntegerOutOfRange
|
294 | }
|
295 | /* c8 ignore stop */
|
296 | }
|
297 | throw errorUnexpectedEndOfArray
|
298 | }
|
299 |
|
300 | /**
|
301 | * Look ahead and read varUint without incrementing position
|
302 | *
|
303 | * @function
|
304 | * @param {Decoder} decoder
|
305 | * @return {number}
|
306 | */
|
307 | export const peekVarUint = decoder => {
|
308 | const pos = decoder.pos
|
309 | const s = readVarUint(decoder)
|
310 | decoder.pos = pos
|
311 | return s
|
312 | }
|
313 |
|
314 | /**
|
315 | * Look ahead and read varUint without incrementing position
|
316 | *
|
317 | * @function
|
318 | * @param {Decoder} decoder
|
319 | * @return {number}
|
320 | */
|
321 | export const peekVarInt = decoder => {
|
322 | const pos = decoder.pos
|
323 | const s = readVarInt(decoder)
|
324 | decoder.pos = pos
|
325 | return s
|
326 | }
|
327 |
|
328 | /**
|
329 | * We don't test this function anymore as we use native decoding/encoding by default now.
|
330 | * Better not modify this anymore..
|
331 | *
|
332 | * Transforming utf8 to a string is pretty expensive. The code performs 10x better
|
333 | * when String.fromCodePoint is fed with all characters as arguments.
|
334 | * But most environments have a maximum number of arguments per functions.
|
335 | * For effiency reasons we apply a maximum of 10000 characters at once.
|
336 | *
|
337 | * @function
|
338 | * @param {Decoder} decoder
|
339 | * @return {String} The read String.
|
340 | */
|
341 | /* c8 ignore start */
|
342 | export const _readVarStringPolyfill = decoder => {
|
343 | let remainingLen = readVarUint(decoder)
|
344 | if (remainingLen === 0) {
|
345 | return ''
|
346 | } else {
|
347 | let encodedString = String.fromCodePoint(readUint8(decoder)) // remember to decrease remainingLen
|
348 | if (--remainingLen < 100) { // do not create a Uint8Array for small strings
|
349 | while (remainingLen--) {
|
350 | encodedString += String.fromCodePoint(readUint8(decoder))
|
351 | }
|
352 | } else {
|
353 | while (remainingLen > 0) {
|
354 | const nextLen = remainingLen < 10000 ? remainingLen : 10000
|
355 | // this is dangerous, we create a fresh array view from the existing buffer
|
356 | const bytes = decoder.arr.subarray(decoder.pos, decoder.pos + nextLen)
|
357 | decoder.pos += nextLen
|
358 | // Starting with ES5.1 we can supply a generic array-like object as arguments
|
359 | encodedString += String.fromCodePoint.apply(null, /** @type {any} */ (bytes))
|
360 | remainingLen -= nextLen
|
361 | }
|
362 | }
|
363 | return decodeURIComponent(escape(encodedString))
|
364 | }
|
365 | }
|
366 | /* c8 ignore stop */
|
367 |
|
368 | /**
|
369 | * @function
|
370 | * @param {Decoder} decoder
|
371 | * @return {String} The read String
|
372 | */
|
373 | export const _readVarStringNative = decoder =>
|
374 | /** @type any */ (string.utf8TextDecoder).decode(readVarUint8Array(decoder))
|
375 |
|
376 | /**
|
377 | * Read string of variable length
|
378 | * * varUint is used to store the length of the string
|
379 | *
|
380 | * @function
|
381 | * @param {Decoder} decoder
|
382 | * @return {String} The read String
|
383 | *
|
384 | */
|
385 | /* c8 ignore next */
|
386 | export const readVarString = string.utf8TextDecoder ? _readVarStringNative : _readVarStringPolyfill
|
387 |
|
388 | /**
|
389 | * @param {Decoder} decoder
|
390 | * @return {Uint8Array}
|
391 | */
|
392 | export const readTerminatedUint8Array = decoder => {
|
393 | const encoder = encoding.createEncoder()
|
394 | let b
|
395 | while (true) {
|
396 | b = readUint8(decoder)
|
397 | if (b === 0) {
|
398 | return encoding.toUint8Array(encoder)
|
399 | }
|
400 | if (b === 1) {
|
401 | b = readUint8(decoder)
|
402 | }
|
403 | encoding.write(encoder, b)
|
404 | }
|
405 | }
|
406 |
|
407 | /**
|
408 | * @param {Decoder} decoder
|
409 | * @return {string}
|
410 | */
|
411 | export const readTerminatedString = decoder => string.decodeUtf8(readTerminatedUint8Array(decoder))
|
412 |
|
413 | /**
|
414 | * Look ahead and read varString without incrementing position
|
415 | *
|
416 | * @function
|
417 | * @param {Decoder} decoder
|
418 | * @return {string}
|
419 | */
|
420 | export const peekVarString = decoder => {
|
421 | const pos = decoder.pos
|
422 | const s = readVarString(decoder)
|
423 | decoder.pos = pos
|
424 | return s
|
425 | }
|
426 |
|
427 | /**
|
428 | * @param {Decoder} decoder
|
429 | * @param {number} len
|
430 | * @return {DataView}
|
431 | */
|
432 | export const readFromDataView = (decoder, len) => {
|
433 | const dv = new DataView(decoder.arr.buffer, decoder.arr.byteOffset + decoder.pos, len)
|
434 | decoder.pos += len
|
435 | return dv
|
436 | }
|
437 |
|
438 | /**
|
439 | * @param {Decoder} decoder
|
440 | */
|
441 | export const readFloat32 = decoder => readFromDataView(decoder, 4).getFloat32(0, false)
|
442 |
|
443 | /**
|
444 | * @param {Decoder} decoder
|
445 | */
|
446 | export const readFloat64 = decoder => readFromDataView(decoder, 8).getFloat64(0, false)
|
447 |
|
448 | /**
|
449 | * @param {Decoder} decoder
|
450 | */
|
451 | export const readBigInt64 = decoder => /** @type {any} */ (readFromDataView(decoder, 8)).getBigInt64(0, false)
|
452 |
|
453 | /**
|
454 | * @param {Decoder} decoder
|
455 | */
|
456 | export const readBigUint64 = decoder => /** @type {any} */ (readFromDataView(decoder, 8)).getBigUint64(0, false)
|
457 |
|
458 | /**
|
459 | * @type {Array<function(Decoder):any>}
|
460 | */
|
461 | const readAnyLookupTable = [
|
462 | decoder => undefined, // CASE 127: undefined
|
463 | decoder => null, // CASE 126: null
|
464 | readVarInt, // CASE 125: integer
|
465 | readFloat32, // CASE 124: float32
|
466 | readFloat64, // CASE 123: float64
|
467 | readBigInt64, // CASE 122: bigint
|
468 | decoder => false, // CASE 121: boolean (false)
|
469 | decoder => true, // CASE 120: boolean (true)
|
470 | readVarString, // CASE 119: string
|
471 | decoder => { // CASE 118: object<string,any>
|
472 | const len = readVarUint(decoder)
|
473 | /**
|
474 | * @type {Object<string,any>}
|
475 | */
|
476 | const obj = {}
|
477 | for (let i = 0; i < len; i++) {
|
478 | const key = readVarString(decoder)
|
479 | obj[key] = readAny(decoder)
|
480 | }
|
481 | return obj
|
482 | },
|
483 | decoder => { // CASE 117: array<any>
|
484 | const len = readVarUint(decoder)
|
485 | const arr = []
|
486 | for (let i = 0; i < len; i++) {
|
487 | arr.push(readAny(decoder))
|
488 | }
|
489 | return arr
|
490 | },
|
491 | readVarUint8Array // CASE 116: Uint8Array
|
492 | ]
|
493 |
|
494 | /**
|
495 | * @param {Decoder} decoder
|
496 | */
|
497 | export const readAny = decoder => readAnyLookupTable[127 - readUint8(decoder)](decoder)
|
498 |
|
499 | /**
|
500 | * T must not be null.
|
501 | *
|
502 | * @template T
|
503 | */
|
504 | export class RleDecoder extends Decoder {
|
505 | /**
|
506 | * @param {Uint8Array} uint8Array
|
507 | * @param {function(Decoder):T} reader
|
508 | */
|
509 | constructor (uint8Array, reader) {
|
510 | super(uint8Array)
|
511 | /**
|
512 | * The reader
|
513 | */
|
514 | this.reader = reader
|
515 | /**
|
516 | * Current state
|
517 | * @type {T|null}
|
518 | */
|
519 | this.s = null
|
520 | this.count = 0
|
521 | }
|
522 |
|
523 | read () {
|
524 | if (this.count === 0) {
|
525 | this.s = this.reader(this)
|
526 | if (hasContent(this)) {
|
527 | this.count = readVarUint(this) + 1 // see encoder implementation for the reason why this is incremented
|
528 | } else {
|
529 | this.count = -1 // read the current value forever
|
530 | }
|
531 | }
|
532 | this.count--
|
533 | return /** @type {T} */ (this.s)
|
534 | }
|
535 | }
|
536 |
|
537 | export class IntDiffDecoder extends Decoder {
|
538 | /**
|
539 | * @param {Uint8Array} uint8Array
|
540 | * @param {number} start
|
541 | */
|
542 | constructor (uint8Array, start) {
|
543 | super(uint8Array)
|
544 | /**
|
545 | * Current state
|
546 | * @type {number}
|
547 | */
|
548 | this.s = start
|
549 | }
|
550 |
|
551 | /**
|
552 | * @return {number}
|
553 | */
|
554 | read () {
|
555 | this.s += readVarInt(this)
|
556 | return this.s
|
557 | }
|
558 | }
|
559 |
|
560 | export class RleIntDiffDecoder extends Decoder {
|
561 | /**
|
562 | * @param {Uint8Array} uint8Array
|
563 | * @param {number} start
|
564 | */
|
565 | constructor (uint8Array, start) {
|
566 | super(uint8Array)
|
567 | /**
|
568 | * Current state
|
569 | * @type {number}
|
570 | */
|
571 | this.s = start
|
572 | this.count = 0
|
573 | }
|
574 |
|
575 | /**
|
576 | * @return {number}
|
577 | */
|
578 | read () {
|
579 | if (this.count === 0) {
|
580 | this.s += readVarInt(this)
|
581 | if (hasContent(this)) {
|
582 | this.count = readVarUint(this) + 1 // see encoder implementation for the reason why this is incremented
|
583 | } else {
|
584 | this.count = -1 // read the current value forever
|
585 | }
|
586 | }
|
587 | this.count--
|
588 | return /** @type {number} */ (this.s)
|
589 | }
|
590 | }
|
591 |
|
592 | export class UintOptRleDecoder extends Decoder {
|
593 | /**
|
594 | * @param {Uint8Array} uint8Array
|
595 | */
|
596 | constructor (uint8Array) {
|
597 | super(uint8Array)
|
598 | /**
|
599 | * @type {number}
|
600 | */
|
601 | this.s = 0
|
602 | this.count = 0
|
603 | }
|
604 |
|
605 | read () {
|
606 | if (this.count === 0) {
|
607 | this.s = readVarInt(this)
|
608 | // if the sign is negative, we read the count too, otherwise count is 1
|
609 | const isNegative = math.isNegativeZero(this.s)
|
610 | this.count = 1
|
611 | if (isNegative) {
|
612 | this.s = -this.s
|
613 | this.count = readVarUint(this) + 2
|
614 | }
|
615 | }
|
616 | this.count--
|
617 | return /** @type {number} */ (this.s)
|
618 | }
|
619 | }
|
620 |
|
621 | export class IncUintOptRleDecoder extends Decoder {
|
622 | /**
|
623 | * @param {Uint8Array} uint8Array
|
624 | */
|
625 | constructor (uint8Array) {
|
626 | super(uint8Array)
|
627 | /**
|
628 | * @type {number}
|
629 | */
|
630 | this.s = 0
|
631 | this.count = 0
|
632 | }
|
633 |
|
634 | read () {
|
635 | if (this.count === 0) {
|
636 | this.s = readVarInt(this)
|
637 | // if the sign is negative, we read the count too, otherwise count is 1
|
638 | const isNegative = math.isNegativeZero(this.s)
|
639 | this.count = 1
|
640 | if (isNegative) {
|
641 | this.s = -this.s
|
642 | this.count = readVarUint(this) + 2
|
643 | }
|
644 | }
|
645 | this.count--
|
646 | return /** @type {number} */ (this.s++)
|
647 | }
|
648 | }
|
649 |
|
650 | export class IntDiffOptRleDecoder extends Decoder {
|
651 | /**
|
652 | * @param {Uint8Array} uint8Array
|
653 | */
|
654 | constructor (uint8Array) {
|
655 | super(uint8Array)
|
656 | /**
|
657 | * @type {number}
|
658 | */
|
659 | this.s = 0
|
660 | this.count = 0
|
661 | this.diff = 0
|
662 | }
|
663 |
|
664 | /**
|
665 | * @return {number}
|
666 | */
|
667 | read () {
|
668 | if (this.count === 0) {
|
669 | const diff = readVarInt(this)
|
670 | // if the first bit is set, we read more data
|
671 | const hasCount = diff & 1
|
672 | this.diff = math.floor(diff / 2) // shift >> 1
|
673 | this.count = 1
|
674 | if (hasCount) {
|
675 | this.count = readVarUint(this) + 2
|
676 | }
|
677 | }
|
678 | this.s += this.diff
|
679 | this.count--
|
680 | return this.s
|
681 | }
|
682 | }
|
683 |
|
684 | export class StringDecoder {
|
685 | /**
|
686 | * @param {Uint8Array} uint8Array
|
687 | */
|
688 | constructor (uint8Array) {
|
689 | this.decoder = new UintOptRleDecoder(uint8Array)
|
690 | this.str = readVarString(this.decoder)
|
691 | /**
|
692 | * @type {number}
|
693 | */
|
694 | this.spos = 0
|
695 | }
|
696 |
|
697 | /**
|
698 | * @return {string}
|
699 | */
|
700 | read () {
|
701 | const end = this.spos + this.decoder.read()
|
702 | const res = this.str.slice(this.spos, end)
|
703 | this.spos = end
|
704 | return res
|
705 | }
|
706 | }
|