export interface Box { left?: number; top?: number; width?: number; height?: number; } export function encodeBox(message: Box): Uint8Array { let bb = popByteBuffer(); _encodeBox(message, bb); return toUint8Array(bb); } function _encodeBox(message: Box, bb: ByteBuffer): void { // optional float left = 1; let $left = message.left; if ($left !== undefined) { writeVarint32(bb, 13); writeFloat(bb, $left); } // optional float top = 2; let $top = message.top; if ($top !== undefined) { writeVarint32(bb, 21); writeFloat(bb, $top); } // optional float width = 3; let $width = message.width; if ($width !== undefined) { writeVarint32(bb, 29); writeFloat(bb, $width); } // optional float height = 4; let $height = message.height; if ($height !== undefined) { writeVarint32(bb, 37); writeFloat(bb, $height); } } export function decodeBox(binary: Uint8Array): Box { return _decodeBox(wrapByteBuffer(binary)); } function _decodeBox(bb: ByteBuffer): Box { let message: Box = {} as any; end_of_message: while (!isAtEnd(bb)) { let tag = readVarint32(bb); switch (tag >>> 3) { case 0: break end_of_message; // optional float left = 1; case 1: { message.left = readFloat(bb); break; } // optional float top = 2; case 2: { message.top = readFloat(bb); break; } // optional float width = 3; case 3: { message.width = readFloat(bb); break; } // optional float height = 4; case 4: { message.height = readFloat(bb); break; } default: skipUnknownField(bb, tag & 7); } } return message; } export interface Point { x?: number; y?: number; } export function encodePoint(message: Point): Uint8Array { let bb = popByteBuffer(); _encodePoint(message, bb); return toUint8Array(bb); } function _encodePoint(message: Point, bb: ByteBuffer): void { // optional float x = 1; let $x = message.x; if ($x !== undefined) { writeVarint32(bb, 13); writeFloat(bb, $x); } // optional float y = 2; let $y = message.y; if ($y !== undefined) { writeVarint32(bb, 21); writeFloat(bb, $y); } } export function decodePoint(binary: Uint8Array): Point { return _decodePoint(wrapByteBuffer(binary)); } function _decodePoint(bb: ByteBuffer): Point { let message: Point = {} as any; end_of_message: while (!isAtEnd(bb)) { let tag = readVarint32(bb); switch (tag >>> 3) { case 0: break end_of_message; // optional float x = 1; case 1: { message.x = readFloat(bb); break; } // optional float y = 2; case 2: { message.y = readFloat(bb); break; } default: skipUnknownField(bb, tag & 7); } } return message; } export interface BallDetection { plane_position?: Point; annotation?: Box; interpolated?: boolean; } export function encodeBallDetection(message: BallDetection): Uint8Array { let bb = popByteBuffer(); _encodeBallDetection(message, bb); return toUint8Array(bb); } function _encodeBallDetection(message: BallDetection, bb: ByteBuffer): void { // optional Point plane_position = 1; let $plane_position = message.plane_position; if ($plane_position !== undefined) { writeVarint32(bb, 10); let nested = popByteBuffer(); _encodePoint($plane_position, nested); writeVarint32(bb, nested.limit); writeByteBuffer(bb, nested); pushByteBuffer(nested); } // optional Box annotation = 2; let $annotation = message.annotation; if ($annotation !== undefined) { writeVarint32(bb, 18); let nested = popByteBuffer(); _encodeBox($annotation, nested); writeVarint32(bb, nested.limit); writeByteBuffer(bb, nested); pushByteBuffer(nested); } // optional bool interpolated = 3; let $interpolated = message.interpolated; if ($interpolated !== undefined) { writeVarint32(bb, 24); writeByte(bb, $interpolated ? 1 : 0); } } export function decodeBallDetection(binary: Uint8Array): BallDetection { return _decodeBallDetection(wrapByteBuffer(binary)); } function _decodeBallDetection(bb: ByteBuffer): BallDetection { let message: BallDetection = {} as any; end_of_message: while (!isAtEnd(bb)) { let tag = readVarint32(bb); switch (tag >>> 3) { case 0: break end_of_message; // optional Point plane_position = 1; case 1: { let limit = pushTemporaryLength(bb); message.plane_position = _decodePoint(bb); bb.limit = limit; break; } // optional Box annotation = 2; case 2: { let limit = pushTemporaryLength(bb); message.annotation = _decodeBox(bb); bb.limit = limit; break; } // optional bool interpolated = 3; case 3: { message.interpolated = !!readByte(bb); break; } default: skipUnknownField(bb, tag & 7); } } return message; } export interface RLEBallDetection { detection?: BallDetection; count?: number; } export function encodeRLEBallDetection(message: RLEBallDetection): Uint8Array { let bb = popByteBuffer(); _encodeRLEBallDetection(message, bb); return toUint8Array(bb); } function _encodeRLEBallDetection( message: RLEBallDetection, bb: ByteBuffer, ): void { // optional BallDetection detection = 1; let $detection = message.detection; if ($detection !== undefined) { writeVarint32(bb, 10); let nested = popByteBuffer(); _encodeBallDetection($detection, nested); writeVarint32(bb, nested.limit); writeByteBuffer(bb, nested); pushByteBuffer(nested); } // optional uint32 count = 2; let $count = message.count; if ($count !== undefined) { writeVarint32(bb, 16); writeVarint32(bb, $count); } } export function decodeRLEBallDetection(binary: Uint8Array): RLEBallDetection { return _decodeRLEBallDetection(wrapByteBuffer(binary)); } function _decodeRLEBallDetection(bb: ByteBuffer): RLEBallDetection { let message: RLEBallDetection = {} as any; end_of_message: while (!isAtEnd(bb)) { let tag = readVarint32(bb); switch (tag >>> 3) { case 0: break end_of_message; // optional BallDetection detection = 1; case 1: { let limit = pushTemporaryLength(bb); message.detection = _decodeBallDetection(bb); bb.limit = limit; break; } // optional uint32 count = 2; case 2: { message.count = readVarint32(bb) >>> 0; break; } default: skipUnknownField(bb, tag & 7); } } return message; } export interface RLEDetectionHistory { detections?: RLEBallDetection[]; } export function encodeRLEDetectionHistory( message: RLEDetectionHistory, ): Uint8Array { let bb = popByteBuffer(); _encodeRLEDetectionHistory(message, bb); return toUint8Array(bb); } function _encodeRLEDetectionHistory( message: RLEDetectionHistory, bb: ByteBuffer, ): void { // repeated RLEBallDetection detections = 1; let array$detections = message.detections; if (array$detections !== undefined) { for (let value of array$detections) { writeVarint32(bb, 10); let nested = popByteBuffer(); _encodeRLEBallDetection(value, nested); writeVarint32(bb, nested.limit); writeByteBuffer(bb, nested); pushByteBuffer(nested); } } } export function decodeRLEDetectionHistory( binary: Uint8Array, ): RLEDetectionHistory { return _decodeRLEDetectionHistory(wrapByteBuffer(binary)); } function _decodeRLEDetectionHistory(bb: ByteBuffer): RLEDetectionHistory { let message: RLEDetectionHistory = {} as any; end_of_message: while (!isAtEnd(bb)) { let tag = readVarint32(bb); switch (tag >>> 3) { case 0: break end_of_message; // repeated RLEBallDetection detections = 1; case 1: { let limit = pushTemporaryLength(bb); let values = message.detections || (message.detections = []); values.push(_decodeRLEBallDetection(bb)); bb.limit = limit; break; } default: skipUnknownField(bb, tag & 7); } } return message; } export interface DetectionHistory { detections?: BallDetection[]; } export function encodeDetectionHistory(message: DetectionHistory): Uint8Array { let bb = popByteBuffer(); _encodeDetectionHistory(message, bb); return toUint8Array(bb); } function _encodeDetectionHistory( message: DetectionHistory, bb: ByteBuffer, ): void { // repeated BallDetection detections = 1; let array$detections = message.detections; if (array$detections !== undefined) { for (let value of array$detections) { writeVarint32(bb, 10); let nested = popByteBuffer(); _encodeBallDetection(value, nested); writeVarint32(bb, nested.limit); writeByteBuffer(bb, nested); pushByteBuffer(nested); } } } export function decodeDetectionHistory(binary: Uint8Array): DetectionHistory { return _decodeDetectionHistory(wrapByteBuffer(binary)); } function _decodeDetectionHistory(bb: ByteBuffer): DetectionHistory { let message: DetectionHistory = {} as any; end_of_message: while (!isAtEnd(bb)) { let tag = readVarint32(bb); switch (tag >>> 3) { case 0: break end_of_message; // repeated BallDetection detections = 1; case 1: { let limit = pushTemporaryLength(bb); let values = message.detections || (message.detections = []); values.push(_decodeBallDetection(bb)); bb.limit = limit; break; } default: skipUnknownField(bb, tag & 7); } } return message; } export interface CollisionInfo { source?: number; ball_identifiers?: { [key: number]: Point }; wall_identifier?: number; frame_index?: number; static?: boolean; } export function encodeCollisionInfo(message: CollisionInfo): Uint8Array { let bb = popByteBuffer(); _encodeCollisionInfo(message, bb); return toUint8Array(bb); } function _encodeCollisionInfo(message: CollisionInfo, bb: ByteBuffer): void { // optional uint32 source = 1; let $source = message.source; if ($source !== undefined) { writeVarint32(bb, 8); writeVarint32(bb, $source); } // optional map ball_identifiers = 2; let map$ball_identifiers = message.ball_identifiers; if (map$ball_identifiers !== undefined) { for (let key in map$ball_identifiers) { let nested = popByteBuffer(); let value = map$ball_identifiers[key]; writeVarint32(nested, 8); writeVarint32(nested, +key); writeVarint32(nested, 18); let nestedValue = popByteBuffer(); _encodePoint(value, nestedValue); writeVarint32(nested, nestedValue.limit); writeByteBuffer(nested, nestedValue); pushByteBuffer(nestedValue); writeVarint32(bb, 18); writeVarint32(bb, nested.offset); writeByteBuffer(bb, nested); pushByteBuffer(nested); } } // optional uint32 wall_identifier = 3; let $wall_identifier = message.wall_identifier; if ($wall_identifier !== undefined) { writeVarint32(bb, 24); writeVarint32(bb, $wall_identifier); } // optional uint32 frame_index = 4; let $frame_index = message.frame_index; if ($frame_index !== undefined) { writeVarint32(bb, 32); writeVarint32(bb, $frame_index); } // optional bool static = 5; let $static = message.static; if ($static !== undefined) { writeVarint32(bb, 40); writeByte(bb, $static ? 1 : 0); } } export function decodeCollisionInfo(binary: Uint8Array): CollisionInfo { return _decodeCollisionInfo(wrapByteBuffer(binary)); } function _decodeCollisionInfo(bb: ByteBuffer): CollisionInfo { let message: CollisionInfo = {} as any; end_of_message: while (!isAtEnd(bb)) { let tag = readVarint32(bb); switch (tag >>> 3) { case 0: break end_of_message; // optional uint32 source = 1; case 1: { message.source = readVarint32(bb) >>> 0; break; } // optional map ball_identifiers = 2; case 2: { let values = message.ball_identifiers || (message.ball_identifiers = {}); let outerLimit = pushTemporaryLength(bb); let key: number | undefined; let value: Point | undefined; end_of_entry: while (!isAtEnd(bb)) { let tag = readVarint32(bb); switch (tag >>> 3) { case 0: break end_of_entry; case 1: { key = readVarint32(bb) >>> 0; break; } case 2: { let valueLimit = pushTemporaryLength(bb); value = _decodePoint(bb); bb.limit = valueLimit; break; } default: skipUnknownField(bb, tag & 7); } } if (key === undefined || value === undefined) throw new Error("Invalid data for map: ball_identifiers"); values[key] = value; bb.limit = outerLimit; break; } // optional uint32 wall_identifier = 3; case 3: { message.wall_identifier = readVarint32(bb) >>> 0; break; } // optional uint32 frame_index = 4; case 4: { message.frame_index = readVarint32(bb) >>> 0; break; } // optional bool static = 5; case 5: { message.static = !!readByte(bb); break; } default: skipUnknownField(bb, tag & 7); } } return message; } export interface Path { start_frame?: number; end_frame?: number; detections?: DetectionHistory; rle_detections?: RLEDetectionHistory; not_present?: boolean; is_static?: boolean; start_info?: CollisionInfo; end_info?: CollisionInfo; } export function encodePath(message: Path): Uint8Array { let bb = popByteBuffer(); _encodePath(message, bb); return toUint8Array(bb); } function _encodePath(message: Path, bb: ByteBuffer): void { // optional uint32 start_frame = 1; let $start_frame = message.start_frame; if ($start_frame !== undefined) { writeVarint32(bb, 8); writeVarint32(bb, $start_frame); } // optional uint32 end_frame = 2; let $end_frame = message.end_frame; if ($end_frame !== undefined) { writeVarint32(bb, 16); writeVarint32(bb, $end_frame); } // optional DetectionHistory detections = 3; let $detections = message.detections; if ($detections !== undefined) { writeVarint32(bb, 26); let nested = popByteBuffer(); _encodeDetectionHistory($detections, nested); writeVarint32(bb, nested.limit); writeByteBuffer(bb, nested); pushByteBuffer(nested); } // optional RLEDetectionHistory rle_detections = 4; let $rle_detections = message.rle_detections; if ($rle_detections !== undefined) { writeVarint32(bb, 34); let nested = popByteBuffer(); _encodeRLEDetectionHistory($rle_detections, nested); writeVarint32(bb, nested.limit); writeByteBuffer(bb, nested); pushByteBuffer(nested); } // optional bool not_present = 5; let $not_present = message.not_present; if ($not_present !== undefined) { writeVarint32(bb, 40); writeByte(bb, $not_present ? 1 : 0); } // optional bool is_static = 6; let $is_static = message.is_static; if ($is_static !== undefined) { writeVarint32(bb, 48); writeByte(bb, $is_static ? 1 : 0); } // optional CollisionInfo start_info = 7; let $start_info = message.start_info; if ($start_info !== undefined) { writeVarint32(bb, 58); let nested = popByteBuffer(); _encodeCollisionInfo($start_info, nested); writeVarint32(bb, nested.limit); writeByteBuffer(bb, nested); pushByteBuffer(nested); } // optional CollisionInfo end_info = 8; let $end_info = message.end_info; if ($end_info !== undefined) { writeVarint32(bb, 66); let nested = popByteBuffer(); _encodeCollisionInfo($end_info, nested); writeVarint32(bb, nested.limit); writeByteBuffer(bb, nested); pushByteBuffer(nested); } } export function decodePath(binary: Uint8Array): Path { return _decodePath(wrapByteBuffer(binary)); } function _decodePath(bb: ByteBuffer): Path { let message: Path = {} as any; end_of_message: while (!isAtEnd(bb)) { let tag = readVarint32(bb); switch (tag >>> 3) { case 0: break end_of_message; // optional uint32 start_frame = 1; case 1: { message.start_frame = readVarint32(bb) >>> 0; break; } // optional uint32 end_frame = 2; case 2: { message.end_frame = readVarint32(bb) >>> 0; break; } // optional DetectionHistory detections = 3; case 3: { let limit = pushTemporaryLength(bb); message.detections = _decodeDetectionHistory(bb); bb.limit = limit; break; } // optional RLEDetectionHistory rle_detections = 4; case 4: { let limit = pushTemporaryLength(bb); message.rle_detections = _decodeRLEDetectionHistory(bb); bb.limit = limit; break; } // optional bool not_present = 5; case 5: { message.not_present = !!readByte(bb); break; } // optional bool is_static = 6; case 6: { message.is_static = !!readByte(bb); break; } // optional CollisionInfo start_info = 7; case 7: { let limit = pushTemporaryLength(bb); message.start_info = _decodeCollisionInfo(bb); bb.limit = limit; break; } // optional CollisionInfo end_info = 8; case 8: { let limit = pushTemporaryLength(bb); message.end_info = _decodeCollisionInfo(bb); bb.limit = limit; break; } default: skipUnknownField(bb, tag & 7); } } return message; } export interface IdentifierHistory { ball_identifier?: number; paths?: Path[]; } export function encodeIdentifierHistory( message: IdentifierHistory, ): Uint8Array { let bb = popByteBuffer(); _encodeIdentifierHistory(message, bb); return toUint8Array(bb); } function _encodeIdentifierHistory( message: IdentifierHistory, bb: ByteBuffer, ): void { // optional uint32 ball_identifier = 1; let $ball_identifier = message.ball_identifier; if ($ball_identifier !== undefined) { writeVarint32(bb, 8); writeVarint32(bb, $ball_identifier); } // repeated Path paths = 2; let array$paths = message.paths; if (array$paths !== undefined) { for (let value of array$paths) { writeVarint32(bb, 18); let nested = popByteBuffer(); _encodePath(value, nested); writeVarint32(bb, nested.limit); writeByteBuffer(bb, nested); pushByteBuffer(nested); } } } export function decodeIdentifierHistory(binary: Uint8Array): IdentifierHistory { return _decodeIdentifierHistory(wrapByteBuffer(binary)); } function _decodeIdentifierHistory(bb: ByteBuffer): IdentifierHistory { let message: IdentifierHistory = {} as any; end_of_message: while (!isAtEnd(bb)) { let tag = readVarint32(bb); switch (tag >>> 3) { case 0: break end_of_message; // optional uint32 ball_identifier = 1; case 1: { message.ball_identifier = readVarint32(bb) >>> 0; break; } // repeated Path paths = 2; case 2: { let limit = pushTemporaryLength(bb); let values = message.paths || (message.paths = []); values.push(_decodePath(bb)); bb.limit = limit; break; } default: skipUnknownField(bb, tag & 7); } } return message; } export interface KeyBallIdentifiers { cue_ball?: number; object_ball?: number; target_ball?: number; contact_sequence?: number[]; } export function encodeKeyBallIdentifiers( message: KeyBallIdentifiers, ): Uint8Array { let bb = popByteBuffer(); _encodeKeyBallIdentifiers(message, bb); return toUint8Array(bb); } function _encodeKeyBallIdentifiers( message: KeyBallIdentifiers, bb: ByteBuffer, ): void { // optional uint32 cue_ball = 1; let $cue_ball = message.cue_ball; if ($cue_ball !== undefined) { writeVarint32(bb, 8); writeVarint32(bb, $cue_ball); } // optional uint32 object_ball = 2; let $object_ball = message.object_ball; if ($object_ball !== undefined) { writeVarint32(bb, 16); writeVarint32(bb, $object_ball); } // optional uint32 target_ball = 3; let $target_ball = message.target_ball; if ($target_ball !== undefined) { writeVarint32(bb, 24); writeVarint32(bb, $target_ball); } // repeated uint32 contact_sequence = 4; let array$contact_sequence = message.contact_sequence; if (array$contact_sequence !== undefined) { let packed = popByteBuffer(); for (let value of array$contact_sequence) { writeVarint32(packed, value); } writeVarint32(bb, 34); writeVarint32(bb, packed.offset); writeByteBuffer(bb, packed); pushByteBuffer(packed); } } export function decodeKeyBallIdentifiers( binary: Uint8Array, ): KeyBallIdentifiers { return _decodeKeyBallIdentifiers(wrapByteBuffer(binary)); } function _decodeKeyBallIdentifiers(bb: ByteBuffer): KeyBallIdentifiers { let message: KeyBallIdentifiers = {} as any; end_of_message: while (!isAtEnd(bb)) { let tag = readVarint32(bb); switch (tag >>> 3) { case 0: break end_of_message; // optional uint32 cue_ball = 1; case 1: { message.cue_ball = readVarint32(bb) >>> 0; break; } // optional uint32 object_ball = 2; case 2: { message.object_ball = readVarint32(bb) >>> 0; break; } // optional uint32 target_ball = 3; case 3: { message.target_ball = readVarint32(bb) >>> 0; break; } // repeated uint32 contact_sequence = 4; case 4: { let values = message.contact_sequence || (message.contact_sequence = []); if ((tag & 7) === 2) { let outerLimit = pushTemporaryLength(bb); while (!isAtEnd(bb)) { values.push(readVarint32(bb) >>> 0); } bb.limit = outerLimit; } else { values.push(readVarint32(bb) >>> 0); } break; } default: skipUnknownField(bb, tag & 7); } } return message; } export interface Shot { identifier_histories?: IdentifierHistory[]; key_balls?: KeyBallIdentifiers; } export function encodeShot(message: Shot): Uint8Array { let bb = popByteBuffer(); _encodeShot(message, bb); return toUint8Array(bb); } function _encodeShot(message: Shot, bb: ByteBuffer): void { // repeated IdentifierHistory identifier_histories = 3; let array$identifier_histories = message.identifier_histories; if (array$identifier_histories !== undefined) { for (let value of array$identifier_histories) { writeVarint32(bb, 26); let nested = popByteBuffer(); _encodeIdentifierHistory(value, nested); writeVarint32(bb, nested.limit); writeByteBuffer(bb, nested); pushByteBuffer(nested); } } // optional KeyBallIdentifiers key_balls = 4; let $key_balls = message.key_balls; if ($key_balls !== undefined) { writeVarint32(bb, 34); let nested = popByteBuffer(); _encodeKeyBallIdentifiers($key_balls, nested); writeVarint32(bb, nested.limit); writeByteBuffer(bb, nested); pushByteBuffer(nested); } } export function decodeShot(binary: Uint8Array): Shot { return _decodeShot(wrapByteBuffer(binary)); } function _decodeShot(bb: ByteBuffer): Shot { let message: Shot = {} as any; end_of_message: while (!isAtEnd(bb)) { let tag = readVarint32(bb); switch (tag >>> 3) { case 0: break end_of_message; // repeated IdentifierHistory identifier_histories = 3; case 3: { let limit = pushTemporaryLength(bb); let values = message.identifier_histories || (message.identifier_histories = []); values.push(_decodeIdentifierHistory(bb)); bb.limit = limit; break; } // optional KeyBallIdentifiers key_balls = 4; case 4: { let limit = pushTemporaryLength(bb); message.key_balls = _decodeKeyBallIdentifiers(bb); bb.limit = limit; break; } default: skipUnknownField(bb, tag & 7); } } return message; } export interface Long { low: number; high: number; unsigned: boolean; } interface ByteBuffer { bytes: Uint8Array; offset: number; limit: number; } function pushTemporaryLength(bb: ByteBuffer): number { let length = readVarint32(bb); let limit = bb.limit; bb.limit = bb.offset + length; return limit; } function skipUnknownField(bb: ByteBuffer, type: number): void { switch (type) { case 0: while (readByte(bb) & 0x80) {} break; case 2: skip(bb, readVarint32(bb)); break; case 5: skip(bb, 4); break; case 1: skip(bb, 8); break; default: throw new Error("Unimplemented type: " + type); } } function stringToLong(value: string): Long { return { low: value.charCodeAt(0) | (value.charCodeAt(1) << 16), high: value.charCodeAt(2) | (value.charCodeAt(3) << 16), unsigned: false, }; } function longToString(value: Long): string { let low = value.low; let high = value.high; return String.fromCharCode( low & 0xffff, low >>> 16, high & 0xffff, high >>> 16, ); } // The code below was modified from https://github.com/protobufjs/bytebuffer.js // which is under the Apache License 2.0. let f32 = new Float32Array(1); let f32_u8 = new Uint8Array(f32.buffer); let f64 = new Float64Array(1); let f64_u8 = new Uint8Array(f64.buffer); function intToLong(value: number): Long { value |= 0; return { low: value, high: value >> 31, unsigned: value >= 0, }; } let bbStack: ByteBuffer[] = []; function popByteBuffer(): ByteBuffer { const bb = bbStack.pop(); if (!bb) return { bytes: new Uint8Array(64), offset: 0, limit: 0 }; bb.offset = bb.limit = 0; return bb; } function pushByteBuffer(bb: ByteBuffer): void { bbStack.push(bb); } function wrapByteBuffer(bytes: Uint8Array): ByteBuffer { return { bytes, offset: 0, limit: bytes.length }; } function toUint8Array(bb: ByteBuffer): Uint8Array { let bytes = bb.bytes; let limit = bb.limit; return bytes.length === limit ? bytes : bytes.subarray(0, limit); } function skip(bb: ByteBuffer, offset: number): void { if (bb.offset + offset > bb.limit) { throw new Error("Skip past limit"); } bb.offset += offset; } function isAtEnd(bb: ByteBuffer): boolean { return bb.offset >= bb.limit; } function grow(bb: ByteBuffer, count: number): number { let bytes = bb.bytes; let offset = bb.offset; let limit = bb.limit; let finalOffset = offset + count; if (finalOffset > bytes.length) { let newBytes = new Uint8Array(finalOffset * 2); newBytes.set(bytes); bb.bytes = newBytes; } bb.offset = finalOffset; if (finalOffset > limit) { bb.limit = finalOffset; } return offset; } function advance(bb: ByteBuffer, count: number): number { let offset = bb.offset; if (offset + count > bb.limit) { throw new Error("Read past limit"); } bb.offset += count; return offset; } function readBytes(bb: ByteBuffer, count: number): Uint8Array { let offset = advance(bb, count); return bb.bytes.subarray(offset, offset + count); } function writeBytes(bb: ByteBuffer, buffer: Uint8Array): void { let offset = grow(bb, buffer.length); bb.bytes.set(buffer, offset); } function readString(bb: ByteBuffer, count: number): string { // Sadly a hand-coded UTF8 decoder is much faster than subarray+TextDecoder in V8 let offset = advance(bb, count); let fromCharCode = String.fromCharCode; let bytes = bb.bytes; let invalid = "\uFFFD"; let text = ""; for (let i = 0; i < count; i++) { let c1 = bytes[i + offset], c2: number, c3: number, c4: number, c: number; // 1 byte if ((c1 & 0x80) === 0) { text += fromCharCode(c1); } // 2 bytes else if ((c1 & 0xe0) === 0xc0) { if (i + 1 >= count) text += invalid; else { c2 = bytes[i + offset + 1]; if ((c2 & 0xc0) !== 0x80) text += invalid; else { c = ((c1 & 0x1f) << 6) | (c2 & 0x3f); if (c < 0x80) text += invalid; else { text += fromCharCode(c); i++; } } } } // 3 bytes else if ((c1 & 0xf0) == 0xe0) { if (i + 2 >= count) text += invalid; else { c2 = bytes[i + offset + 1]; c3 = bytes[i + offset + 2]; if (((c2 | (c3 << 8)) & 0xc0c0) !== 0x8080) text += invalid; else { c = ((c1 & 0x0f) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f); if (c < 0x0800 || (c >= 0xd800 && c <= 0xdfff)) text += invalid; else { text += fromCharCode(c); i += 2; } } } } // 4 bytes else if ((c1 & 0xf8) == 0xf0) { if (i + 3 >= count) text += invalid; else { c2 = bytes[i + offset + 1]; c3 = bytes[i + offset + 2]; c4 = bytes[i + offset + 3]; if (((c2 | (c3 << 8) | (c4 << 16)) & 0xc0c0c0) !== 0x808080) text += invalid; else { c = ((c1 & 0x07) << 0x12) | ((c2 & 0x3f) << 0x0c) | ((c3 & 0x3f) << 0x06) | (c4 & 0x3f); if (c < 0x10000 || c > 0x10ffff) text += invalid; else { c -= 0x10000; text += fromCharCode((c >> 10) + 0xd800, (c & 0x3ff) + 0xdc00); i += 3; } } } } else text += invalid; } return text; } function writeString(bb: ByteBuffer, text: string): void { // Sadly a hand-coded UTF8 encoder is much faster than TextEncoder+set in V8 let n = text.length; let byteCount = 0; // Write the byte count first for (let i = 0; i < n; i++) { let c = text.charCodeAt(i); if (c >= 0xd800 && c <= 0xdbff && i + 1 < n) { c = (c << 10) + text.charCodeAt(++i) - 0x35fdc00; } byteCount += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4; } writeVarint32(bb, byteCount); let offset = grow(bb, byteCount); let bytes = bb.bytes; // Then write the bytes for (let i = 0; i < n; i++) { let c = text.charCodeAt(i); if (c >= 0xd800 && c <= 0xdbff && i + 1 < n) { c = (c << 10) + text.charCodeAt(++i) - 0x35fdc00; } if (c < 0x80) { bytes[offset++] = c; } else { if (c < 0x800) { bytes[offset++] = ((c >> 6) & 0x1f) | 0xc0; } else { if (c < 0x10000) { bytes[offset++] = ((c >> 12) & 0x0f) | 0xe0; } else { bytes[offset++] = ((c >> 18) & 0x07) | 0xf0; bytes[offset++] = ((c >> 12) & 0x3f) | 0x80; } bytes[offset++] = ((c >> 6) & 0x3f) | 0x80; } bytes[offset++] = (c & 0x3f) | 0x80; } } } function writeByteBuffer(bb: ByteBuffer, buffer: ByteBuffer): void { let offset = grow(bb, buffer.limit); let from = bb.bytes; let to = buffer.bytes; // This for loop is much faster than subarray+set on V8 for (let i = 0, n = buffer.limit; i < n; i++) { from[i + offset] = to[i]; } } function readByte(bb: ByteBuffer): number { return bb.bytes[advance(bb, 1)]; } function writeByte(bb: ByteBuffer, value: number): void { let offset = grow(bb, 1); bb.bytes[offset] = value; } function readFloat(bb: ByteBuffer): number { let offset = advance(bb, 4); let bytes = bb.bytes; // Manual copying is much faster than subarray+set in V8 f32_u8[0] = bytes[offset++]; f32_u8[1] = bytes[offset++]; f32_u8[2] = bytes[offset++]; f32_u8[3] = bytes[offset++]; return f32[0]; } function writeFloat(bb: ByteBuffer, value: number): void { let offset = grow(bb, 4); let bytes = bb.bytes; f32[0] = value; // Manual copying is much faster than subarray+set in V8 bytes[offset++] = f32_u8[0]; bytes[offset++] = f32_u8[1]; bytes[offset++] = f32_u8[2]; bytes[offset++] = f32_u8[3]; } function readDouble(bb: ByteBuffer): number { let offset = advance(bb, 8); let bytes = bb.bytes; // Manual copying is much faster than subarray+set in V8 f64_u8[0] = bytes[offset++]; f64_u8[1] = bytes[offset++]; f64_u8[2] = bytes[offset++]; f64_u8[3] = bytes[offset++]; f64_u8[4] = bytes[offset++]; f64_u8[5] = bytes[offset++]; f64_u8[6] = bytes[offset++]; f64_u8[7] = bytes[offset++]; return f64[0]; } function writeDouble(bb: ByteBuffer, value: number): void { let offset = grow(bb, 8); let bytes = bb.bytes; f64[0] = value; // Manual copying is much faster than subarray+set in V8 bytes[offset++] = f64_u8[0]; bytes[offset++] = f64_u8[1]; bytes[offset++] = f64_u8[2]; bytes[offset++] = f64_u8[3]; bytes[offset++] = f64_u8[4]; bytes[offset++] = f64_u8[5]; bytes[offset++] = f64_u8[6]; bytes[offset++] = f64_u8[7]; } function readInt32(bb: ByteBuffer): number { let offset = advance(bb, 4); let bytes = bb.bytes; return ( bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24) ); } function writeInt32(bb: ByteBuffer, value: number): void { let offset = grow(bb, 4); let bytes = bb.bytes; bytes[offset] = value; bytes[offset + 1] = value >> 8; bytes[offset + 2] = value >> 16; bytes[offset + 3] = value >> 24; } function readInt64(bb: ByteBuffer, unsigned: boolean): Long { return { low: readInt32(bb), high: readInt32(bb), unsigned, }; } function writeInt64(bb: ByteBuffer, value: Long): void { writeInt32(bb, value.low); writeInt32(bb, value.high); } function readVarint32(bb: ByteBuffer): number { let c = 0; let value = 0; let b: number; do { b = readByte(bb); if (c < 32) value |= (b & 0x7f) << c; c += 7; } while (b & 0x80); return value; } function writeVarint32(bb: ByteBuffer, value: number): void { value >>>= 0; while (value >= 0x80) { writeByte(bb, (value & 0x7f) | 0x80); value >>>= 7; } writeByte(bb, value); } function readVarint64(bb: ByteBuffer, unsigned: boolean): Long { let part0 = 0; let part1 = 0; let part2 = 0; let b: number; b = readByte(bb); part0 = b & 0x7f; if (b & 0x80) { b = readByte(bb); part0 |= (b & 0x7f) << 7; if (b & 0x80) { b = readByte(bb); part0 |= (b & 0x7f) << 14; if (b & 0x80) { b = readByte(bb); part0 |= (b & 0x7f) << 21; if (b & 0x80) { b = readByte(bb); part1 = b & 0x7f; if (b & 0x80) { b = readByte(bb); part1 |= (b & 0x7f) << 7; if (b & 0x80) { b = readByte(bb); part1 |= (b & 0x7f) << 14; if (b & 0x80) { b = readByte(bb); part1 |= (b & 0x7f) << 21; if (b & 0x80) { b = readByte(bb); part2 = b & 0x7f; if (b & 0x80) { b = readByte(bb); part2 |= (b & 0x7f) << 7; } } } } } } } } } return { low: part0 | (part1 << 28), high: (part1 >>> 4) | (part2 << 24), unsigned, }; } function writeVarint64(bb: ByteBuffer, value: Long): void { let part0 = value.low >>> 0; let part1 = ((value.low >>> 28) | (value.high << 4)) >>> 0; let part2 = value.high >>> 24; // ref: src/google/protobuf/io/coded_stream.cc let size = part2 === 0 ? part1 === 0 ? part0 < 1 << 14 ? part0 < 1 << 7 ? 1 : 2 : part0 < 1 << 21 ? 3 : 4 : part1 < 1 << 14 ? part1 < 1 << 7 ? 5 : 6 : part1 < 1 << 21 ? 7 : 8 : part2 < 1 << 7 ? 9 : 10; let offset = grow(bb, size); let bytes = bb.bytes; switch (size) { case 10: bytes[offset + 9] = (part2 >>> 7) & 0x01; case 9: bytes[offset + 8] = size !== 9 ? part2 | 0x80 : part2 & 0x7f; case 8: bytes[offset + 7] = size !== 8 ? (part1 >>> 21) | 0x80 : (part1 >>> 21) & 0x7f; case 7: bytes[offset + 6] = size !== 7 ? (part1 >>> 14) | 0x80 : (part1 >>> 14) & 0x7f; case 6: bytes[offset + 5] = size !== 6 ? (part1 >>> 7) | 0x80 : (part1 >>> 7) & 0x7f; case 5: bytes[offset + 4] = size !== 5 ? part1 | 0x80 : part1 & 0x7f; case 4: bytes[offset + 3] = size !== 4 ? (part0 >>> 21) | 0x80 : (part0 >>> 21) & 0x7f; case 3: bytes[offset + 2] = size !== 3 ? (part0 >>> 14) | 0x80 : (part0 >>> 14) & 0x7f; case 2: bytes[offset + 1] = size !== 2 ? (part0 >>> 7) | 0x80 : (part0 >>> 7) & 0x7f; case 1: bytes[offset] = size !== 1 ? part0 | 0x80 : part0 & 0x7f; } } function readVarint32ZigZag(bb: ByteBuffer): number { let value = readVarint32(bb); // ref: src/google/protobuf/wire_format_lite.h return (value >>> 1) ^ -(value & 1); } function writeVarint32ZigZag(bb: ByteBuffer, value: number): void { // ref: src/google/protobuf/wire_format_lite.h writeVarint32(bb, (value << 1) ^ (value >> 31)); } function readVarint64ZigZag(bb: ByteBuffer): Long { let value = readVarint64(bb, /* unsigned */ false); let low = value.low; let high = value.high; let flip = -(low & 1); // ref: src/google/protobuf/wire_format_lite.h return { low: ((low >>> 1) | (high << 31)) ^ flip, high: (high >>> 1) ^ flip, unsigned: false, }; } function writeVarint64ZigZag(bb: ByteBuffer, value: Long): void { let low = value.low; let high = value.high; let flip = high >> 31; // ref: src/google/protobuf/wire_format_lite.h writeVarint64(bb, { low: (low << 1) ^ flip, high: ((high << 1) | (low >>> 31)) ^ flip, unsigned: false, }); }