mutable-buffer
Version:
A mutable buffer library for node.js
327 lines (272 loc) • 8.36 kB
text/typescript
const DEFAULT_INITIAL_SIZE = 1024;
const DEFAULT_BLOCK_SIZE = 1024;
type WriteData = BaseMutableBuffer | string | Buffer | ArrayLike<number> | ArrayBuffer | SharedArrayBuffer;
export class BaseMutableBuffer {
static Buffer: typeof Buffer;
static readonly target: string;
protected _initialSize: number;
protected _blockSize: number;
constructor(size?: number, blockSize?: number) {
this._initialSize = size ?? DEFAULT_INITIAL_SIZE;
this._blockSize = blockSize ?? DEFAULT_BLOCK_SIZE;
this._buffer = this.Buffer.allocUnsafe(this._initialSize);
this._size = 0;
}
protected _size: number;
get size() {
return this._size;
}
protected _buffer: Buffer;
get buffer(): Buffer {
return this._buffer;
}
get Buffer(): typeof Buffer {
return (<any>this.constructor).Buffer;
}
get nativeBuffer() {
return this._buffer;
}
static create(size?: number, blockSize?: number) {
return new this(size, blockSize);
}
//resize internal buffer if not enough size left
_ensure(size: number) {
const remaining = this._buffer.length - this._size;
if (remaining < size) {
const factor = Math.ceil((size - remaining) / this._blockSize);
const prev = this._buffer;
this._buffer = this.Buffer.allocUnsafe(prev.length + this._blockSize * factor);
prev.copy(this._buffer);
}
}
capacity() {
return this._buffer.length;
}
clear() {
this._size = 0;
}
/**
*
* @param targetOrCreate The target buffer or creating or slice buffer.
* 1. Buffer: The target buffer to render;
* 2. true: Create new buffer and copy all cached data to it;
* 3 false: Slice the cached data from internal buffer, The result cloud be be changed if current MutableBuffer has been reused.
*/
render(targetOrCreate?: Buffer | boolean): Buffer {
if (targetOrCreate) {
const answer = isBuffer(targetOrCreate) ? targetOrCreate : this.Buffer.allocUnsafe(this.size);
this._buffer.copy(answer, 0, 0, this._size);
return answer;
}
return this._buffer.slice(0, this._size);
}
flush(targetOrCreate?: Buffer | boolean) {
const result = this.render(targetOrCreate);
this.clear();
return result;
}
write(source: WriteData, encoding?: BufferEncoding): number;
write(source: WriteData, ...args: any[]): number;
write(source: WriteData, ...args: any[]): number {
if (isBuffer(source)) {
this._ensure(source.length);
source.copy(this._buffer, this._size);
this._size += source.length;
} else if (Array.isArray(source)) {
this._ensure(source.length);
for (let i = 0; i < source.length; i++) {
this._buffer[this._size + i] = source[i];
}
this._size += source.length;
} else if (isMutableBuffer(source)) {
this._ensure(source.size);
source.buffer.copy(this._buffer, this._size);
this._size += source.size;
} else {
const last = args.length > 0 ? args[args.length - 1] : undefined;
const encoding = typeof last === 'string' ? (last as BufferEncoding) : undefined;
source = source + '';
const len = this.Buffer.byteLength(source, encoding);
this._ensure(len);
this._buffer.write(source, this._size, len, encoding);
this._size += len;
}
return this.size;
}
writeCString(data?: string | Buffer, encoding?: BufferEncoding) {
//just write a 0 for empty or null strings
if (!data) {
this._ensure(1);
} else if (isBuffer(data)) {
this._ensure(data.length);
data.copy(this._buffer, this._size);
this._size += data.length;
} else {
const len = this.Buffer.byteLength(data, encoding);
this._ensure(len + 1); //+1 for null terminator
this._buffer.write(data, this._size, len, encoding);
this._size += len;
}
this._buffer[this._size++] = 0; // null terminator
return this.size;
}
writeChar(c: string) {
this._ensure(1);
this._buffer.write(c, this._size, 1);
this._size++;
return this.size;
}
writeUIntLE(value: number, byteLength: number) {
this._ensure(byteLength >>> 0);
this._size = this._buffer.writeUIntLE(value, this._size, byteLength);
return this.size;
}
writeUIntBE(value: number, byteLength: number) {
this._ensure(byteLength >>> 0);
this._size = this._buffer.writeUIntBE(value, this._size, byteLength);
return this.size;
}
writeUInt8(value: number) {
this._ensure(1);
this._size = this._buffer.writeUInt8(value, this._size);
return this.size;
}
writeUInt16LE(value: number) {
this._ensure(2);
this._size = this._buffer.writeUInt16LE(value, this._size);
return this.size;
}
writeUInt16BE(value: number) {
this._ensure(2);
this._size = this._buffer.writeUInt16BE(value, this._size);
return this.size;
}
writeUInt32LE(value: number) {
this._ensure(4);
this._size = this._buffer.writeUInt32LE(value, this._size);
return this.size;
}
writeUInt32BE(value: number) {
this._ensure(4);
this._size = this._buffer.writeUInt32BE(value, this._size);
return this.size;
}
writeIntLE(value: number, byteLength: number) {
this._ensure(byteLength >>> 0);
this._size = this._buffer.writeIntLE(value, this._size, byteLength);
return this.size;
}
writeIntBE(value: number, byteLength: number) {
this._ensure(byteLength >>> 0);
this._size = this._buffer.writeIntBE(value, this._size, byteLength);
return this.size;
}
writeInt8(value: number) {
this._ensure(1);
this._size = this._buffer.writeInt8(value, this._size);
return this.size;
}
writeInt16LE(value: number) {
this._ensure(2);
this._size = this._buffer.writeInt16LE(value, this._size);
return this.size;
}
writeInt16BE(value: number) {
this._ensure(2);
this._size = this._buffer.writeInt16BE(value, this._size);
return this.size;
}
writeInt32LE(value: number) {
this._ensure(4);
this._size = this._buffer.writeInt32LE(value, this._size);
return this.size;
}
writeInt32BE(value: number) {
this._ensure(4);
this._size = this._buffer.writeInt32BE(value, this._size);
return this.size;
}
writeFloatLE(value: number) {
this._ensure(4);
this._size = this._buffer.writeFloatLE(value, this._size);
return this.size;
}
writeFloatBE(value: number) {
this._ensure(4);
this._size = this._buffer.writeFloatBE(value, this._size);
return this.size;
}
writeDoubleLE(value: number) {
this._ensure(8);
this._size = this._buffer.writeDoubleLE(value, this._size);
return this.size;
}
writeDoubleBE(value: number) {
this._ensure(8);
this._size = this._buffer.writeDoubleBE(value, this._size);
return this.size;
}
trim() {
if (this.size <= 0) {
return this.size;
}
let begin = 0;
let end = 0;
for (let i = 0; i < this.size; i++) {
if (this._buffer[i]) {
begin = i;
break;
}
}
for (let i = this.size; i > 0; i--) {
if (this._buffer[i - 1]) {
end = i;
break;
}
}
if (begin === 0 && end === this.size) {
return this.size;
}
this._buffer = this._buffer.slice(begin, end);
this._size = end - begin;
return this.size;
}
trimLeft() {
if (this.size <= 0 || this._buffer[0]) {
return this.size;
}
for (let i = 0; i < this.size; i++) {
if (this._buffer[i]) {
this._buffer = this._buffer.slice(i);
this._size = this.size - i;
return this.size;
}
}
if (this.size > 0) {
this._size = 0;
}
return this.size;
}
trimRight() {
if (this.size <= 0 || this._buffer[this.size - 1]) {
return this.size;
}
for (let i = this.size; i > 0; i--) {
if (this._buffer[i - 1]) {
this._buffer = this._buffer.slice(0, i);
this._size = i;
return this.size;
}
}
if (this.size > 0) {
this._size = 0;
}
return this.size;
}
}
function isBuffer(obj: any): obj is Buffer {
return typeof obj?.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj);
}
function isMutableBuffer(obj: any): obj is BaseMutableBuffer {
return obj?.buffer && obj.size && typeof obj.render === 'function';
}