Skip to content

Instantly share code, notes, and snippets.

@ZenLiuCN
Last active June 26, 2024 05:21
Show Gist options
  • Save ZenLiuCN/41a3d8e7b05cd6d9b544ea493332387d to your computer and use it in GitHub Desktop.
Save ZenLiuCN/41a3d8e7b05cd6d9b544ea493332387d to your computer and use it in GitHub Desktop.
a brower only protobuf's buffer writer and reader, see protobuf.js for the orignal project (github.com/protobufjs/protobuf.js)
const charCodeAt = String.prototype.charCodeAt
const fromCharCode = String.fromCharCode;
export const MaxInt = 4294967296
export const utf8 = {
length(string) {
let len = 0,
c = 0;
for (let i = 0; i < string.length; ++i) {
c = charCodeAt.call(string, i)
if (c < 128)
len += 1;
else if (c < 2048)
len += 2;
else if ((c & 0xFC00) === 0xD800 && (charCodeAt.call(string, i + 1) & 0xFC00) === 0xDC00) {
++i;
len += 4;
} else
len += 3;
}
return len;
}
, read(buffer, start, end) {
if (end - start < 1) {
return "";
}
let str = "";
for (let i = start; i < end;) {
let t = buffer[i++];
if (t <= 0x7F) {
str += fromCharCode(t);
} else if (t >= 0xC0 && t < 0xE0) {
str += fromCharCode((t & 0x1F) << 6 | buffer[i++] & 0x3F);
} else if (t >= 0xE0 && t < 0xF0) {
str += fromCharCode((t & 0xF) << 12 | (buffer[i++] & 0x3F) << 6 | buffer[i++] & 0x3F);
} else if (t >= 0xF0) {
let t2 = ((t & 7) << 18 | (buffer[i++] & 0x3F) << 12 | (buffer[i++] & 0x3F) << 6 | buffer[i++] & 0x3F) - 0x10000;
str += fromCharCode(0xD800 + (t2 >> 10));
str += fromCharCode(0xDC00 + (t2 & 0x3FF));
}
}
return str;
}
, write(string, buffer, offset) {
let start = offset,
c1, // character 1
c2; // character 2
for (let i = 0; i < string.length; ++i) {
c1 = charCodeAt.call(string, i)
if (c1 < 128) {
buffer[offset++] = c1;
} else if (c1 < 2048) {
buffer[offset++] = c1 >> 6 | 192;
buffer[offset++] = c1 & 63 | 128;
} else if ((c1 & 0xFC00) === 0xD800 && ((c2 = charCodeAt.call(string, i + 1)) & 0xFC00) === 0xDC00) {
c1 = 0x10000 + ((c1 & 0x03FF) << 10) + (c2 & 0x03FF);
++i;
buffer[offset++] = c1 >> 18 | 240;
buffer[offset++] = c1 >> 12 & 63 | 128;
buffer[offset++] = c1 >> 6 & 63 | 128;
buffer[offset++] = c1 & 63 | 128;
} else {
buffer[offset++] = c1 >> 12 | 224;
buffer[offset++] = c1 >> 6 & 63 | 128;
buffer[offset++] = c1 & 63 | 128;
}
}
return offset - start;
}
}
export const isString = (value) => typeof value === "string" || value instanceof String
export interface Long {
low: number
high: number
unsigned: boolean
}
export class LongBits {
static zeroHash = "\0\0\0\0\0\0\0\0";
static zero = new LongBits(0, 0)
lo: number
hi: number
constructor(lo, hi) {
/**
* Low bits.
* @type {number}
*/
this.lo = lo >>> 0;
/**
* High bits.
* @type {number}
*/
this.hi = hi >>> 0;
}
/**
* Constructs new long bits from the specified number.
* @param {number} value Value
* @returns {LongBits} Instance
*/
static fromNumber(value) {
if (value === 0)
return LongBits.zero;
let sign = value < 0;
if (sign)
value = -value;
let lo = value >>> 0,
hi = (value - lo) / MaxInt >>> 0;
if (sign) {
hi = ~hi >>> 0;
lo = ~lo >>> 0;
if (++lo > 4294967295) {
lo = 0;
if (++hi > 4294967295)
hi = 0;
}
}
return new LongBits(lo, hi);
}
/**
* Constructs new long bits from a number, long or string.
* @param {Long|number|string} value Value
* @returns {LongBits} Instance
*/
static from(value) {
if (typeof value === "number")
return LongBits.fromNumber(value);
if (isString(value)) {
return LongBits.fromNumber(parseInt(value, 10));
}
return value.low || value.high ? new LongBits(value.low >>> 0, value.high >>> 0) : LongBits.zero;
}
/**
* Converts this long bits to a possibly unsafe JavaScript number.
* @param {boolean} [unsigned=false] Whether unsigned or not
* @returns {number} Possibly unsafe number
*/
toNumber(unsigned: boolean): number {
if (!unsigned && this.hi >>> 31) {
let lo = ~this.lo + 1 >>> 0,
hi = ~this.hi >>> 0;
if (!lo)
hi = hi + 1 >>> 0;
return -(lo + hi * MaxInt);
}
return this.lo + this.hi * MaxInt;
}
/**
* Converts this long bits to a long.
* @param {boolean} [unsigned=false] Whether unsigned or not
* @returns {Long} Long
*/
toLong(unsigned: boolean): Long {
return {low: this.lo | 0, high: this.hi | 0, unsigned: Boolean(unsigned)}
}
/**
* Constructs new long bits from the specified 8 characters long hash.
* @param {string} hash Hash
* @returns {LongBits} Bits
*/
fromHash(hash) {
if (hash === LongBits.zeroHash)
return LongBits.zero;
return new LongBits(
(charCodeAt.call(hash, 0)
| charCodeAt.call(hash, 1) << 8
| charCodeAt.call(hash, 2) << 16
| charCodeAt.call(hash, 3) << 24) >>> 0
,
(charCodeAt.call(hash, 4)
| charCodeAt.call(hash, 5) << 8
| charCodeAt.call(hash, 6) << 16
| charCodeAt.call(hash, 7) << 24) >>> 0
);
}
/**
* Converts this long bits to a 8 characters long hash.
* @returns {string} Hash
*/
toHash() {
return String.fromCharCode(
this.lo & 255,
this.lo >>> 8 & 255,
this.lo >>> 16 & 255,
this.lo >>> 24,
this.hi & 255,
this.hi >>> 8 & 255,
this.hi >>> 16 & 255,
this.hi >>> 24
);
}
/**
* Zig-zag encodes this long bits.
* @returns {LongBits} `this`
*/
zzEncode() {
let mask = this.hi >> 31;
this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
this.lo = (this.lo << 1 ^ mask) >>> 0;
return this;
}
/**
* Zig-zag decodes this long bits.
* @returns {LongBits} `this`
*/
zzDecode() {
let mask = -(this.lo & 1);
this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
this.hi = (this.hi >>> 1 ^ mask) >>> 0;
return this;
}
/**
* Calculates the length of this longbits when encoded as a varint.
* @returns {number} Length
*/
length() {
let part0 = this.lo,
part1 = (this.lo >>> 28 | this.hi << 4) >>> 0,
part2 = this.hi >>> 24;
return part2 === 0
? part1 === 0
? part0 < 16384
? part0 < 128 ? 1 : 2
: part0 < 2097152 ? 3 : 4
: part1 < 16384
? part1 < 128 ? 5 : 6
: part1 < 2097152 ? 7 : 8
: part2 < 128 ? 9 : 10;
}
}
LongBits.zero.toNumber = function () {
return 0;
}
LongBits.zero.zzDecode = function () {
return this;
}
LongBits.zero.zzEncode = LongBits.zero.zzDecode
LongBits.zero.length = function () {
return 1;
}
export const base64: {
length(string: string): number
encode(buffer: Uint8Array , start: number, end: number): string
decode(string: string, buffer: Uint8Array, offset: number): number
} = (() => {
const invalidEncoding = "invalid encoding"
// Base64 encoding table
const b64 = new Array(64);
// Base64 decoding table
const s64 = new Array(123);
// 65..90, 97..122, 48..57, 43, 47
for (let i = 0; i < 64;)
s64[b64[i] = i < 26 ? i + 65 : i < 52 ? i + 71 : i < 62 ? i - 4 : i - 59 | 43] = i++;
return {
length(string: string): number {
let p = string.length;
if (!p)
return 0;
let n = 0;
while (--p % 4 > 1 && string.charAt(p) === "=")
++n;
return Math.ceil(string.length * 3) / 4 - n;
}
, encode(buffer: ArrayBuffer | Array<number>, start: number, end: number): string {
let parts = null,
chunk = [];
let i = 0, // output index
j = 0, // goto index
t; // temporary
while (start < end) {
let b = buffer[start++];
switch (j) {
case 0:
chunk[i++] = b64[b >> 2];
t = (b & 3) << 4;
j = 1;
break;
case 1:
chunk[i++] = b64[t | b >> 4];
t = (b & 15) << 2;
j = 2;
break;
case 2:
chunk[i++] = b64[t | b >> 6];
chunk[i++] = b64[b & 63];
j = 0;
break;
}
if (i > 8191) {
(parts || (parts = [])).push(fromCharCode.apply(String, chunk));
i = 0;
}
}
if (j) {
chunk[i++] = b64[t];
chunk[i++] = 61;
if (j === 1)
chunk[i++] = 61;
}
if (parts) {
if (i)
parts.push(String.fromCharCode.apply(String, chunk.slice(0, i)));
return parts.join("");
}
return fromCharCode.apply(String, chunk.slice(0, i));
}
, decode(string: string, buffer: Array<number> | ArrayBuffer, offset: number) {
let start = offset;
let j = 0, // goto index
t; // temporary
for (let i = 0; i < string.length;) {
let c = string.charCodeAt(i++);
if (c === 61 && j > 1)
break;
if ((c = s64[c]) === undefined)
throw Error(invalidEncoding);
switch (j) {
case 0:
t = c;
j = 1;
break;
case 1:
buffer[offset++] = t << 2 | (c & 48) >> 4;
t = c;
j = 2;
break;
case 2:
buffer[offset++] = (t & 15) << 4 | (c & 60) >> 2;
t = c;
j = 3;
break;
case 3:
buffer[offset++] = (t & 3) << 6 | c;
j = 0;
break;
}
}
if (j === 1)
throw Error(invalidEncoding);
return offset - start;
}
}
})()
export const float: {
writeFloatLE(val: number, buf: Uint8Array, pos: number)
writeFloatBE(val: number, buf: Uint8Array, pos: number)
readFloatLE(buf: Uint8Array, pos: number): number
readFloatBE(buf: Uint8Array, pos: number): number
readDoubleLE(buf: Uint8Array, pos: number): number
readDoubleBE(buf: Uint8Array, pos: number): number
writeDoubleLE(val: number, buf: Uint8Array, pos: number)
writeDoubleBE(val: number, buf: Uint8Array, pos: number)
} = (() => {
const f32 = new Float32Array([-0]),
f8b = new Uint8Array(f32.buffer),
le = f8b[3] === 128;
const writeFloat_f32_cpy = (val, buf, pos) => {
f32[0] = val;
buf[pos] = f8b[0];
buf[pos + 1] = f8b[1];
buf[pos + 2] = f8b[2];
buf[pos + 3] = f8b[3];
}
const writeFloat_f32_rev = (val, buf, pos) => {
f32[0] = val;
buf[pos] = f8b[3];
buf[pos + 1] = f8b[2];
buf[pos + 2] = f8b[1];
buf[pos + 3] = f8b[0];
}
const readFloat_f32_cpy = (buf, pos) => {
f8b[0] = buf[pos];
f8b[1] = buf[pos + 1];
f8b[2] = buf[pos + 2];
f8b[3] = buf[pos + 3];
return f32[0];
}
const readFloat_f32_rev = (buf, pos) => {
f8b[3] = buf[pos];
f8b[2] = buf[pos + 1];
f8b[1] = buf[pos + 2];
f8b[0] = buf[pos + 3];
return f32[0];
}
const f64 = new Float64Array([-0]),
d8b = new Uint8Array(f64.buffer),
dle = d8b[7] === 128;
const writeDouble_f64_cpy = (val, buf, pos) => {
f64[0] = val
buf[pos] = d8b[0]
buf[pos + 1] = d8b[1]
buf[pos + 2] = d8b[2]
buf[pos + 3] = d8b[3]
buf[pos + 4] = d8b[4]
buf[pos + 5] = d8b[5]
buf[pos + 6] = d8b[6]
buf[pos + 7] = d8b[7]
}
const writeDouble_f64_rev = (val, buf, pos) => {
f64[0] = val
buf[pos] = d8b[7]
buf[pos + 1] = d8b[6]
buf[pos + 2] = d8b[5]
buf[pos + 3] = d8b[4]
buf[pos + 4] = d8b[3]
buf[pos + 5] = d8b[2]
buf[pos + 6] = d8b[1]
buf[pos + 7] = d8b[0]
}
const readDouble_f64_cpy = (buf, pos) => {
d8b[0] = buf[pos];
d8b[1] = buf[pos + 1];
d8b[2] = buf[pos + 2];
d8b[3] = buf[pos + 3];
d8b[4] = buf[pos + 4];
d8b[5] = buf[pos + 5];
d8b[6] = buf[pos + 6];
d8b[7] = buf[pos + 7];
return f64[0];
}
const readDouble_f64_rev = (buf, pos) => {
d8b[7] = buf[pos]
d8b[6] = buf[pos + 1]
d8b[5] = buf[pos + 2]
d8b[4] = buf[pos + 3]
d8b[3] = buf[pos + 4]
d8b[2] = buf[pos + 5]
d8b[1] = buf[pos + 6]
d8b[0] = buf[pos + 7]
return f64[0]
}
return {
writeFloatLE: le ? writeFloat_f32_cpy : writeFloat_f32_rev,
writeFloatBE: le ? writeFloat_f32_rev : writeFloat_f32_cpy,
readFloatLE: le ? readFloat_f32_cpy : readFloat_f32_rev,
readFloatBE: le ? readFloat_f32_rev : readFloat_f32_cpy,
readDoubleLE: dle ? readDouble_f64_cpy : readDouble_f64_rev,
readDoubleBE: dle ? readDouble_f64_rev : readDouble_f64_cpy,
writeDoubleLE: dle ? writeDouble_f64_cpy : writeDouble_f64_rev,
writeDoubleBE: dle ? writeDouble_f64_rev : writeDouble_f64_cpy,
}
})()
export const pool = (alloc: (size: number) => Uint8Array, slice: Function, size?: number) => {
let SIZE = size || 8192;
let MAX = SIZE >>> 1;
let slab = null;
let offset = SIZE;
return (size: number) => {
if (size < 1 || size > MAX)
return alloc(size)
if (offset + size > SIZE) {
slab = alloc(SIZE)
offset = 0
}
let buf = slice.call(slab, offset, offset += size);
if (offset & 7) // align to 32 bit
offset = (offset | 7) + 1;
return buf
};
}
export const Uint8ArrayPool: (size: number) => Uint8Array = pool((size: number) => new Uint8Array(size), Uint8Array.prototype.subarray)
interface Operate {
next: Operate | undefined
fn: Operation
len: number
val: any
}
type Operation = (val: any, buf: Uint8Array, pos: number) => void
class Op implements Operate {
next: Op | undefined
constructor(public fn: Operation, public len: number, public val: any) {
this.next = undefined;
}
}
class State {
/**
* Current head.
*/
head: Operate
/**
* Current tail.
*/
tail: Operate
/**
* Current buffer length.
* @type {number}
*/
len: number
/**
* Next state.
* @type {State|null}
*/
next: State
constructor(writer: Writer) {
this.head = writer.head
this.tail = writer.tail
this.len = writer.len
this.next = writer.states
}
}
class VarintOp implements Operate {
next: Operate | undefined
constructor(public len: number, public val: any) {
this.next = undefined;
}
fn = writeVarint32
}
const noop: Operation = (val, buf, pos) => {
}
const writeVarint64: Operation = (val, buf, pos) => {
while (val.hi) {
buf[pos++] = val.lo & 127 | 128;
val.lo = (val.lo >>> 7 | val.hi << 25) >>> 0;
val.hi >>>= 7;
}
while (val.lo > 127) {
buf[pos++] = val.lo & 127 | 128;
val.lo = val.lo >>> 7;
}
buf[pos++] = val.lo;
}
const writeByte: Operation = (val, buf, pos) => {
buf[pos] = val & 255;
}
const writeVarint32: Operation = (val, buf, pos) => {
while (val > 127) {
buf[pos++] = val & 127 | 128;
val >>>= 7;
}
buf[pos] = val;
}
const writeFixed32: Operation = (val, buf, pos) => {
buf[pos] = val & 255;
buf[pos + 1] = val >>> 8 & 255;
buf[pos + 2] = val >>> 16 & 255;
buf[pos + 3] = val >>> 24;
}
const writeBytes: Operation = (val, buf, pos) => {
buf.set(val, pos);
}
export class Writer {
public static create(): Writer {
return new Writer()
}
public static alloc = Uint8ArrayPool
/**
* Current length.
* @type {number}
*/
public len: number
/**
* Operations head.
* @type {Object}
*/
public head: Op
/**
* Operations tail
* @type {Object}
*/
public tail: Op
/**
* Linked forked states.
* @type {Object|null}
*/
public states: State | null
constructor() {
this.len = 0;
this.head = new Op(noop, 0, 0);
this.tail = this.head;
this.states = null;
}
private _push(fn, len, val) {
this.tail = this.tail.next = new Op(fn, len, val);
this.len += len;
return this;
}
uint32(value: number): Writer {
// here, the call to this.push has been inlined and a varint specific Op subclass is used.
// uint32 is by far the most frequently used operation and benefits significantly from this.
this.len += (this.tail = this.tail.next = new VarintOp(
(value = value >>> 0) < 128 ? 1
: value < 16384 ? 2
: value < 2097152 ? 3
: value < 268435456 ? 4
: 5,
value)).len;
return this;
};
int32(value: number): Writer {
return value < 0
? this._push(writeVarint64, 10, LongBits.fromNumber(value)) // 10 bytes per spec
: this.uint32(value);
}
sint32(value: number): Writer {
return this.uint32((value << 1 ^ value >> 31) >>> 0);
}
uint64(value: Long | number | string): Writer {
let bits = LongBits.from(value);
return this._push(writeVarint64, bits.length(), bits);
}
int64 = this.uint64
sint64(value: Long | number | string): Writer {
let bits = LongBits.from(value).zzEncode();
return this._push(writeVarint64, bits.length(), bits);
};
bool(value: boolean): Writer {
return this._push(writeByte, 1, value ? 1 : 0);
};
fixed32(value: number): Writer {
return this._push(writeFixed32, 4, value >>> 0);
};
sfixed32 = this.fixed32
fixed64(value: Long | number | string): Writer {
let bits = LongBits.from(value);
return this._push(writeFixed32, 4, bits.lo)._push(writeFixed32, 4, bits.hi);
};
sfixed64 = this.fixed64
float(value: number) {
return this._push(float.writeFloatLE, 4, value);
};
double(value: number) {
return this._push(float.writeDoubleLE, 8, value);
};
bytes(value: Uint8Array | string) {
let len = value.length >>> 0;
if (!len) return this._push(writeByte, 1, 0);
if (isString(value)) {
let buf = Writer.alloc(len = base64.length(value as string));
base64.decode(value as string, buf, 0);
value = buf;
}
return this.uint32(len)._push(writeBytes, len, value);
};
string(value: string) {
let len = utf8.length(value);
return len
? this.uint32(len)._push(utf8.write, len, value)
: this._push(writeByte, 1, 0);
};
fork(): Writer {
this.states = new State(this);
this.head = this.tail = new Op(noop, 0, 0);
this.len = 0;
return this;
};
reset(): Writer {
if (this.states) {
this.head = this.states.head;
this.tail = this.states.tail;
this.len = this.states.len;
this.states = this.states.next;
} else {
this.head = this.tail = new Op(noop, 0, 0);
this.len = 0;
}
return this;
};
ldelim(): Writer {
let head = this.head,
tail = this.tail,
len = this.len;
this.reset().uint32(len);
if (len) {
this.tail.next = head.next; // skip noop
this.tail = tail;
this.len += len;
}
return this;
};
finish(): Uint8Array {
let head = this.head.next, // skip noop
buf = Writer.alloc(this.len),
pos = 0;
while (head) {
head.fn(head.val, buf, pos);
pos += head.len;
head = head.next;
}
// this.head = this.tail = null;
return buf;
};
}
function indexOutOfRange(reader, writeLength?) {
return RangeError("index out of range: " + reader.pos + " + " + (writeLength || 1) + " > " + reader.len);
}
function readLongVarint() {
// tends to deopt with local vars for octet etc.
let bits = new LongBits(0, 0);
let i = 0;
if (this.len - this.pos > 4) { // fast route (lo)
for (; i < 4; ++i) {
// 1st..4th
bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0;
if (this.buf[this.pos++] < 128)
return bits;
}
// 5th
bits.lo = (bits.lo | (this.buf[this.pos] & 127) << 28) >>> 0;
bits.hi = (bits.hi | (this.buf[this.pos] & 127) >> 4) >>> 0;
if (this.buf[this.pos++] < 128)
return bits;
i = 0;
} else {
for (; i < 3; ++i) {
/* istanbul ignore if */
if (this.pos >= this.len)
throw indexOutOfRange(this);
// 1st..3th
bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0;
if (this.buf[this.pos++] < 128)
return bits;
}
// 4th
bits.lo = (bits.lo | (this.buf[this.pos++] & 127) << i * 7) >>> 0;
return bits;
}
if (this.len - this.pos > 4) { // fast route (hi)
for (; i < 5; ++i) {
// 6th..10th
bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0;
if (this.buf[this.pos++] < 128)
return bits;
}
} else {
for (; i < 5; ++i) {
/* istanbul ignore if */
if (this.pos >= this.len)
throw indexOutOfRange(this);
// 6th..10th
bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0;
if (this.buf[this.pos++] < 128)
return bits;
}
}
/* istanbul ignore next */
throw Error("invalid varint encoding");
}
export class Reader {
public static create(buffer: Uint8Array ): Reader {
return new Reader(buffer)
}
public pos: number = 0
public len: number = 0
constructor(public buf: Uint8Array ) {
this.len = buf.length;
}
private _slice = Uint8Array.prototype.subarray
uint32 = (function () {
let value = 4294967295; // optimizer type-hint, tends to deopt otherwise (?!)
return function () {
value = (this.buf[this.pos] & 127) >>> 0;
if (this.buf[this.pos++] < 128) return value;
value = (value | (this.buf[this.pos] & 127) << 7) >>> 0;
if (this.buf[this.pos++] < 128) return value;
value = (value | (this.buf[this.pos] & 127) << 14) >>> 0;
if (this.buf[this.pos++] < 128) return value;
value = (value | (this.buf[this.pos] & 127) << 21) >>> 0;
if (this.buf[this.pos++] < 128) return value;
value = (value | (this.buf[this.pos] & 15) << 28) >>> 0;
if (this.buf[this.pos++] < 128) return value;
if ((this.pos += 5) > this.len) {
this.pos = this.len;
throw indexOutOfRange(this, 10);
}
return value;
};
})()
int32(): number {
return this.uint32() | 0
}
sint32(): number {
const value = this.uint32();
return value >>> 1 ^ -(value & 1) | 0;
}
bool(): boolean {
return this.uint32() !== 0;
}
static readFixed32_end(buf, end) { // note that this uses `end`, not `pos`
return (buf[end - 4]
| buf[end - 3] << 8
| buf[end - 2] << 16
| buf[end - 1] << 24) >>> 0;
}
fixed32(): number {
if (this.pos + 4 > this.len)
throw indexOutOfRange(this, 4);
return Reader.readFixed32_end(this.buf, this.pos += 4);
}
sfixed32(): number {
if (this.pos + 4 > this.len)
throw indexOutOfRange(this, 4);
return Reader.readFixed32_end(this.buf, this.pos += 4) | 0;
}
float() {
if (this.pos + 4 > this.len)
throw indexOutOfRange(this, 4);
const value = float.readFloatLE(this.buf, this.pos);
this.pos += 4;
return value;
}
double() {
if (this.pos + 8 > this.len) throw indexOutOfRange(this, 4);
const value = float.readDoubleLE(this.buf, this.pos);
this.pos += 8;
return value;
};
bytes() {
let length = this.uint32(),
start = this.pos,
end = this.pos + length;
if (end > this.len)
throw indexOutOfRange(this, length);
this.pos += length;
if (Array.isArray(this.buf)) // plain array
return this.buf.slice(start, end);
if (start === end) { // fix for IE 10/Win8 and others' subarray returning array of size 1
return new this.buf.constructor(0)
}
return this._slice.call(this.buf, start, end);
}
string() {
let bytes = this.bytes();
return utf8.read(bytes, 0, bytes.length);
}
/**
* Skips the specified number of bytes if specified, otherwise skips a varint.
* @param {number?} [length] Length if known, otherwise a varint is assumed
* @returns {Reader} `this`
*/
skip(length?:number):Reader {
if (typeof length === "number") {
if (this.pos + length > this.len)
throw indexOutOfRange(this, length);
this.pos += length;
} else {
do {
if (this.pos >= this.len)
throw indexOutOfRange(this);
} while (this.buf[this.pos++] & 128);
}
return this;
}
int64(long:boolean):number|Long {
return readLongVarint.call(this)[long?ln:num](false);
}
uint64(long:boolean):number|Long {
return readLongVarint.call(this)[long?ln:num](true);
}
sint64(long:boolean):number |Long{
return readLongVarint.call(this).zzDecode()[long?ln:num](false);
}
fixed64(long:boolean):number|Long {
return readFixed64.call(this)[long?ln:num](true);
}
sfixed64(long:boolean):number|Long {
return readFixed64.call(this)[long?ln:num](false);
}
}
const ln='toLong'
const num='toNumber'
function readFixed64(/*{Reader}this*/) {
if (this.pos + 8 > this.len) throw indexOutOfRange(this, 8);
return new LongBits(Reader.readFixed32_end(this.buf, this.pos += 4), Reader.readFixed32_end(this.buf, this.pos += 4));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment