@platformatic/kafka
Version:
Modern and performant client for Apache Kafka
311 lines (310 loc) • 9.31 kB
JavaScript
import { EMPTY_BUFFER, INT16_SIZE, INT32_SIZE, INT64_SIZE, INT8_SIZE, UUID_SIZE } from "./definitions.js";
import { DynamicBuffer } from "./dynamic-buffer.js";
import { Writer } from "./writer.js";
const instanceIdentifier = Symbol('plt.kafka.reader.instanceIdentifier');
export class Reader {
buffer;
position;
length;
[instanceIdentifier];
static isReader(target) {
return target?.[instanceIdentifier] === true;
}
static from(buffer) {
if (Writer.isWriter(buffer)) {
return new Reader(buffer.dynamicBuffer);
}
else if (Buffer.isBuffer(buffer)) {
buffer = new DynamicBuffer(buffer);
}
return new Reader(buffer);
}
constructor(buffer) {
this.buffer = buffer;
this.position = 0;
this.length = this.buffer.length;
this[instanceIdentifier] = true;
}
get remaining() {
return this.length - this.position;
}
reset(buffer) {
if (buffer) {
if (Buffer.isBuffer(buffer)) {
buffer = new DynamicBuffer(buffer);
}
this.buffer = buffer;
}
this.position = 0;
}
inspect() {
return this.buffer
.subarray(this.position)
.toString('hex')
.replaceAll(/(.{4})/g, '$1 ')
.trim();
}
skip(length) {
this.position += length;
return this;
}
peekUnsignedInt8(position) {
return this.buffer.readUInt8(position ?? this.position);
}
peekUnsignedInt16(position) {
return this.buffer.readUInt16BE(position ?? this.position);
}
peekUnsignedInt32(position) {
return this.buffer.readUInt32BE(position ?? this.position);
}
peekUnsignedInt64(position) {
return this.buffer.readBigUInt64BE(position ?? this.position);
}
peekUnsignedVarInt(position) {
return this.buffer.readUnsignedVarInt(position ?? this.position)[0];
}
peekUnsignedVarInt64(position) {
return this.buffer.readUnsignedVarInt64(position ?? this.position)[0];
}
peekInt8(position) {
return this.buffer.readInt8(position ?? this.position);
}
peekInt16(position) {
return this.buffer.readInt16BE(position ?? this.position);
}
peekInt32(position) {
return this.buffer.readInt32BE(position ?? this.position);
}
peekInt64(position) {
return this.buffer.readBigInt64BE(position ?? this.position);
}
peekFloat64(position) {
return this.buffer.readDoubleBE(position ?? this.position);
}
peekVarInt(position) {
return this.buffer.readVarInt(position ?? this.position)[0];
}
peekVarInt64(position) {
return this.buffer.readVarInt64(position ?? this.position)[0];
}
peekBoolean(position) {
return this.buffer.readInt8(position ?? this.position) === 1;
}
peekUUID(position) {
position ??= this.position;
return this.buffer.toString('hex', position, position + UUID_SIZE);
}
readUnsignedInt8() {
const value = this.peekUnsignedInt8();
this.position += INT8_SIZE;
return value;
}
readUnsignedInt16() {
const value = this.peekUnsignedInt16();
this.position += INT16_SIZE;
return value;
}
readUnsignedInt32() {
const value = this.peekUnsignedInt32();
this.position += INT32_SIZE;
return value;
}
readUnsignedInt64() {
const value = this.peekUnsignedInt64();
this.position += INT64_SIZE;
return value;
}
readUnsignedVarInt() {
const [value, read] = this.buffer.readUnsignedVarInt(this.position);
this.position += read;
return value;
}
readUnsignedVarInt64() {
const [value, read] = this.buffer.readUnsignedVarInt64(this.position);
this.position += read;
return value;
}
readInt8() {
const value = this.peekInt8();
this.position += INT8_SIZE;
return value;
}
readInt16() {
const value = this.peekInt16();
this.position += INT16_SIZE;
return value;
}
readInt32() {
const value = this.peekInt32();
this.position += INT32_SIZE;
return value;
}
readInt64() {
const value = this.peekInt64();
this.position += INT64_SIZE;
return value;
}
readFloat64() {
const value = this.peekFloat64();
this.position += INT64_SIZE;
return value;
}
readVarInt() {
const [value, read] = this.buffer.readVarInt(this.position);
this.position += read;
return value;
}
readVarInt64() {
const [value, read] = this.buffer.readVarInt64(this.position);
this.position += read;
return value;
}
readBoolean() {
const value = this.peekUnsignedInt8();
this.position += INT8_SIZE;
return value === 1;
}
readNullableString(compact = true, encoding = 'utf-8') {
let length;
if (compact) {
length = this.readUnsignedVarInt();
if (length === 0) {
return null;
}
length--;
}
else {
length = this.readInt16();
if (length === -1) {
return null;
}
}
const value = this.buffer.toString(encoding, this.position, this.position + length);
this.position += length;
return value;
}
readString(compact = true, encoding = 'utf-8') {
return this.readNullableString(compact, encoding) || '';
}
readUUID() {
const value = this.peekUUID();
this.position += UUID_SIZE;
return value.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, '$1-$2-$3-$4-$5');
}
readNullableBytes(compact = true) {
let length;
if (compact) {
length = this.readUnsignedVarInt();
if (length === 0) {
return null;
}
length--;
}
else {
length = this.readInt32();
if (length === -1) {
return null;
}
}
const value = this.buffer.slice(this.position, this.position + length);
this.position += length;
return value;
}
readBytes(compact = true) {
return this.readNullableBytes(compact) || EMPTY_BUFFER;
}
readVarIntBytes() {
let length = this.readVarInt();
if (length === -1) {
length = 0;
}
const value = this.buffer.slice(this.position, this.position + length);
this.position += length;
return value;
}
readNullableArray(reader, compact = true, discardTrailingTaggedFields = true) {
let length;
if (compact) {
length = this.readUnsignedVarInt();
if (length === 0) {
return null;
}
length--;
}
else {
length = this.readInt32();
if (length === -1) {
return null;
}
}
const value = [];
for (let i = 0; i < length; i++) {
value.push(reader(this, i));
if (discardTrailingTaggedFields) {
this.readTaggedFields();
}
}
return value;
}
readNullableMap(reader, compact = true, discardTrailingTaggedFields = true) {
let length;
if (compact) {
length = this.readUnsignedVarInt();
if (length === 0) {
return null;
}
length--;
}
else {
length = this.readInt32();
if (length === -1) {
return null;
}
}
const map = new Map();
for (let i = 0; i < length; i++) {
const [key, value] = reader(this, i);
map.set(key, value);
if (discardTrailingTaggedFields) {
this.readTaggedFields();
}
}
return map;
}
readArray(reader, compact = true, discardTrailingTaggedFields = true) {
return this.readNullableArray(reader, compact, discardTrailingTaggedFields) || [];
}
readMap(reader, compact = true, discardTrailingTaggedFields = true) {
return this.readNullableMap(reader, compact, discardTrailingTaggedFields) ?? new Map();
}
readVarIntArray(reader) {
const length = this.readVarInt();
const value = [];
for (let i = 0; i < length; i++) {
value.push(reader(this, i));
}
return value;
}
readVarIntMap(reader) {
const length = this.readVarInt();
const map = new Map();
for (let i = 0; i < length; i++) {
const [key, value] = reader(this, i);
map.set(key, value);
}
return map;
}
readNullableStruct(reader) {
if (this.readInt8() === -1) {
return null;
}
return reader();
}
// TODO(ShogunPanda): Tagged fields are not supported yet
readTaggedFields() {
const length = this.readVarInt();
if (length > 0) {
this.skip(length);
}
}
}