blob: 5731127a4af90cb6b439d7426cef54dd649b63e6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Config, LATIN1 } from "../type";
import { isNodeEnv } from "../util";
import { PlatformBuffer, alloc, fromUint8Array } from "../platformBuffer";
import { readLatin1String } from "./string";
export class BinaryReader {
private sliceStringEnable;
private cursor = 0;
private dataView!: DataView;
private platformBuffer!: PlatformBuffer;
private bigString = "";
private byteLength = 0;
constructor(config: Config) {
this.sliceStringEnable = isNodeEnv && config.useSliceString;
}
reset(ab: Uint8Array) {
this.platformBuffer = fromUint8Array(ab);
this.byteLength = this.platformBuffer.byteLength;
this.dataView = new DataView(this.platformBuffer.buffer, this.platformBuffer.byteOffset, this.byteLength);
if (this.sliceStringEnable) {
this.bigString = this.platformBuffer.toString("latin1", 0, this.byteLength);
}
this.cursor = 0;
}
uint8() {
return this.dataView.getUint8(this.cursor++);
}
int8() {
return this.dataView.getInt8(this.cursor++);
}
uint16() {
const result = this.dataView.getUint16(this.cursor, true);
this.cursor += 2;
return result;
}
int16() {
const result = this.dataView.getInt16(this.cursor, true);
this.cursor += 2;
return result;
}
skip(len: number) {
this.cursor += len;
}
int32() {
const result = this.dataView.getInt32(this.cursor, true);
this.cursor += 4;
return result;
}
uint32() {
const result = this.dataView.getUint32(this.cursor, true);
this.cursor += 4;
return result;
}
int64() {
const result = this.dataView.getBigInt64(this.cursor, true);
this.cursor += 8;
return result;
}
uint64() {
const result = this.dataView.getBigUint64(this.cursor, true);
this.cursor += 8;
return result;
}
sliInt64() {
const i = this.dataView.getUint32(this.cursor, true);
if ((i & 0b1) != 0b1) {
this.cursor += 4;
return BigInt(i >> 1);
}
this.cursor += 1;
return this.varInt64();
}
float32() {
const result = this.dataView.getFloat32(this.cursor, true);
this.cursor += 4;
return result;
}
float64() {
const result = this.dataView.getFloat64(this.cursor, true);
this.cursor += 8;
return result;
}
stringUtf8At(start: number, len: number) {
return this.platformBuffer.toString("utf8", start, start + len);
}
stringUtf8(len: number) {
const result = this.platformBuffer.toString("utf8", this.cursor, this.cursor + len);
this.cursor += len;
return result;
}
stringOfVarUInt32() {
const isLatin1 = this.uint8() === LATIN1;
const len = this.varUInt32();
return isLatin1 ? this.stringLatin1(len) : this.stringUtf8(len);
}
stringLatin1(len: number) {
if (this.sliceStringEnable) {
return this.stringLatin1Fast(len);
}
return this.stringLatin1Slow(len);
}
private stringLatin1Fast(len: number) {
const result = this.bigString.substring(this.cursor, this.cursor + len);
this.cursor += len;
return result;
}
private stringLatin1Slow(len: number) {
const rawCursor = this.cursor;
this.cursor += len;
return readLatin1String(this.platformBuffer, len, rawCursor);
}
buffer(len: number) {
const result = alloc(len);
this.platformBuffer.copy(result, 0, this.cursor, this.cursor + len);
this.cursor += len;
return result;
}
bufferRef(len: number) {
const result = this.platformBuffer.subarray(this.cursor, this.cursor + len);
this.cursor += len;
return result;
}
varUInt32() {
// Reduce memory reads as much as possible. Reading a uint32 at once is far faster than reading four uint8s separately.
if (this.byteLength - this.cursor >= 5) {
const fourByteValue = this.dataView.getUint32(this.cursor++, true);
// | 1bit + 7bits | 1bit + 7bits | 1bit + 7bits | 1bit + 7bits |
let result = fourByteValue & 0x7f;
if ((fourByteValue & 0x80) != 0) {
this.cursor++;
// 0x3f80: 0b1111111 << 7
result |= (fourByteValue >>> 1) & 0x3f80;
if ((fourByteValue & 0x8000) != 0) {
this.cursor++;
// 0x1fc000: 0b1111111 << 14
result |= (fourByteValue >>> 2) & 0x1fc000;
if ((fourByteValue & 0x800000) != 0) {
this.cursor++;
// 0xfe00000: 0b1111111 << 21
result |= (fourByteValue >>> 3) & 0xfe00000;
if ((fourByteValue & 0x80000000) != 0) {
result |= (this.uint8()) << 28;
}
}
}
}
return result;
}
let byte = this.uint8();
let result = byte & 0x7f;
if ((byte & 0x80) != 0) {
byte = this.uint8();
result |= (byte & 0x7f) << 7;
if ((byte & 0x80) != 0) {
byte = this.uint8();
result |= (byte & 0x7f) << 14;
if ((byte & 0x80) != 0) {
byte = this.uint8();
result |= (byte & 0x7f) << 21;
if ((byte & 0x80) != 0) {
byte = this.uint8();
result |= (byte) << 28;
}
}
}
}
return result;
}
varInt32() {
const v = this.varUInt32();
return (v >> 1) ^ -(v & 1); // zigZag decode
}
bigUInt8() {
return BigInt(this.uint8() >>> 0);
}
varUInt64() {
// Creating BigInts is too performance-intensive; we'll use uint32 instead.
if (this.byteLength - this.cursor < 8) {
let byte = this.bigUInt8();
let result = byte & 0x7fn;
if ((byte & 0x80n) != 0n) {
byte = this.bigUInt8();
result |= (byte & 0x7fn) << 7n;
if ((byte & 0x80n) != 0n) {
byte = this.bigUInt8();
result |= (byte & 0x7fn) << 14n;
if ((byte & 0x80n) != 0n) {
byte = this.bigUInt8();
result |= (byte & 0x7fn) << 21n;
if ((byte & 0x80n) != 0n) {
byte = this.bigUInt8();
result |= (byte & 0x7fn) << 28n;
if ((byte & 0x80n) != 0n) {
byte = this.bigUInt8();
result |= (byte & 0x7fn) << 35n;
if ((byte & 0x80n) != 0n) {
byte = this.bigUInt8();
result |= (byte & 0x7fn) << 42n;
if ((byte & 0x80n) != 0n) {
byte = this.bigUInt8();
result |= (byte & 0x7fn) << 49n;
if ((byte & 0x80n) != 0n) {
byte = this.bigUInt8();
result |= (byte) << 56n;
}
}
}
}
}
}
}
}
return result;
}
const l32 = this.dataView.getUint32(this.cursor++, true);
let byte = l32 & 0xff;
let rl28 = byte & 0x7f;
let rh28 = 0;
if ((byte & 0x80) != 0) {
byte = l32 & 0xff00 >> 8;
this.cursor++;
rl28 |= (byte & 0x7f) << 7;
if ((byte & 0x80) != 0) {
byte = l32 & 0xff0000 >> 16;
this.cursor++;
rl28 |= (byte & 0x7f) << 14;
if ((byte & 0x80) != 0) {
byte = l32 & 0xff000000 >> 24;
this.cursor++;
rl28 |= (byte & 0x7f) << 21;
if ((byte & 0x80) != 0) {
const h32 = this.dataView.getUint32(this.cursor++, true);
byte = h32 & 0xff;
rh28 |= (byte & 0x7f);
if ((byte & 0x80) != 0) {
byte = h32 & 0xff00 >> 8;
this.cursor++;
rh28 |= (byte & 0x7f) << 7;
if ((byte & 0x80) != 0) {
byte = h32 & 0xff0000 >> 16;
this.cursor++;
rh28 |= (byte & 0x7f) << 14;
if ((byte & 0x80) != 0) {
byte = h32 & 0xff000000 >> 24;
this.cursor++;
rh28 |= (byte & 0x7f) << 21;
if ((byte & 0x80) != 0) {
return (BigInt(this.uint8()) << 56n) | BigInt(rh28) << 28n | BigInt(rl28);
}
}
}
}
}
}
}
}
return BigInt(rh28) << 28n | BigInt(rl28);
}
varInt64() {
const v = this.varUInt64();
return (v >> 1n) ^ -(v & 1n); // zigZag decode
}
float16() {
const asUint16 = this.uint16();
const sign = asUint16 >> 15;
const exponent = (asUint16 >> 10) & 0x1F;
const mantissa = asUint16 & 0x3FF;
// IEEE 754-2008
if (exponent === 0) {
if (mantissa === 0) {
// +-0
return sign === 0 ? 0 : -0;
} else {
// Denormalized number
return (sign === 0 ? 1 : -1) * mantissa * 2 ** (1 - 15 - 10);
}
} else if (exponent === 31) {
if (mantissa === 0) {
// Infinity
return sign === 0 ? Infinity : -Infinity;
} else {
// NaN
return NaN;
}
} else {
// Normalized number
return (sign === 0 ? 1 : -1) * (1 + mantissa * 2 ** -10) * 2 ** (exponent - 15);
}
}
getCursor() {
return this.cursor;
}
setCursor(v: number) {
this.cursor = v;
}
}