726 lines
24 KiB
JavaScript
726 lines
24 KiB
JavaScript
|
"use strict";
|
||
|
// The default Buffer size if one is not provided.
|
||
|
const DEFAULT_SMARTBUFFER_SIZE = 4096;
|
||
|
// The default string encoding to use for reading/writing strings.
|
||
|
const DEFAULT_SMARTBUFFER_ENCODING = 'utf8';
|
||
|
class SmartBuffer {
|
||
|
/**
|
||
|
* Creates a new SmartBuffer instance.
|
||
|
*
|
||
|
* @param arg1 { Number | BufferEncoding | Buffer | SmartBufferOptions }
|
||
|
* @param arg2 { BufferEncoding }
|
||
|
*/
|
||
|
constructor(arg1, arg2) {
|
||
|
this.length = 0;
|
||
|
this.encoding = DEFAULT_SMARTBUFFER_ENCODING;
|
||
|
this.writeOffset = 0;
|
||
|
this.readOffset = 0;
|
||
|
// Initial buffer size provided
|
||
|
if (typeof arg1 === 'number') {
|
||
|
if (Number.isFinite(arg1) && Number.isInteger(arg1) && arg1 > 0) {
|
||
|
this.buff = Buffer.allocUnsafe(arg1);
|
||
|
}
|
||
|
else {
|
||
|
throw new Error('Invalid size provided. Size must be a valid integer greater than zero.');
|
||
|
}
|
||
|
}
|
||
|
else if (typeof arg1 === 'string') {
|
||
|
if (Buffer.isEncoding(arg1)) {
|
||
|
this.buff = Buffer.allocUnsafe(DEFAULT_SMARTBUFFER_SIZE);
|
||
|
this.encoding = arg1;
|
||
|
}
|
||
|
else {
|
||
|
throw new Error('Invalid encoding provided. Please specify a valid encoding the internal Node.js Buffer supports.');
|
||
|
}
|
||
|
}
|
||
|
else if (arg1 instanceof Buffer) {
|
||
|
this.buff = arg1;
|
||
|
this.length = arg1.length;
|
||
|
}
|
||
|
else if (SmartBuffer.isSmartBufferOptions(arg1)) {
|
||
|
// Checks for encoding
|
||
|
if (arg1.encoding) {
|
||
|
if (Buffer.isEncoding(arg1.encoding)) {
|
||
|
this.encoding = arg1.encoding;
|
||
|
}
|
||
|
else {
|
||
|
throw new Error('Invalid encoding provided. Please specify a valid encoding the internal Node.js Buffer supports.');
|
||
|
}
|
||
|
}
|
||
|
// Checks for initial size length
|
||
|
if (arg1.size) {
|
||
|
if (Number.isFinite(arg1.size) && Number.isInteger(arg1.size) && arg1.size > 0) {
|
||
|
this.buff = Buffer.allocUnsafe(arg1.size);
|
||
|
}
|
||
|
else {
|
||
|
throw new Error('Invalid size provided. Size must be a valid integer greater than zero.');
|
||
|
}
|
||
|
}
|
||
|
else if (arg1.buff) {
|
||
|
if (arg1.buff instanceof Buffer) {
|
||
|
this.buff = arg1.buff;
|
||
|
this.length = arg1.buff.length;
|
||
|
}
|
||
|
else {
|
||
|
throw new Error('Invalid buffer provided in SmartBufferOptions.');
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
this.buff = Buffer.allocUnsafe(DEFAULT_SMARTBUFFER_SIZE);
|
||
|
}
|
||
|
}
|
||
|
else if (typeof arg1 === 'object') {
|
||
|
throw new Error('Invalid object supplied to SmartBuffer constructor.');
|
||
|
}
|
||
|
else {
|
||
|
this.buff = Buffer.allocUnsafe(DEFAULT_SMARTBUFFER_SIZE);
|
||
|
}
|
||
|
// Check for encoding (Buffer, Encoding) constructor.
|
||
|
if (typeof arg2 === 'string') {
|
||
|
if (Buffer.isEncoding(arg2)) {
|
||
|
this.encoding = arg2;
|
||
|
}
|
||
|
else {
|
||
|
throw new Error('Invalid encoding provided. Please specify a valid encoding the internal Node.js Buffer supports.');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Creates a new SmartBuffer instance with the provided internal Buffer size and optional encoding.
|
||
|
*
|
||
|
* @param size { Number } The size of the internal Buffer.
|
||
|
* @param encoding { String } The BufferEncoding to use for strings.
|
||
|
*
|
||
|
* @return { SmartBuffer }
|
||
|
*/
|
||
|
static fromSize(size, encoding) {
|
||
|
return new this({
|
||
|
size: size,
|
||
|
encoding: encoding
|
||
|
});
|
||
|
}
|
||
|
/**
|
||
|
* Creates a new SmartBuffer instance with the provided Buffer and optional encoding.
|
||
|
*
|
||
|
* @param buffer { Buffer } The Buffer to use as the internal Buffer value.
|
||
|
* @param encoding { String } The BufferEncoding to use for strings.
|
||
|
*
|
||
|
* @return { SmartBuffer }
|
||
|
*/
|
||
|
static fromBuffer(buff, encoding) {
|
||
|
return new this({
|
||
|
buff: buff,
|
||
|
encoding: encoding
|
||
|
});
|
||
|
}
|
||
|
/**
|
||
|
* Creates a new SmartBuffer instance with the provided SmartBufferOptions options.
|
||
|
*
|
||
|
* @param options { SmartBufferOptions } The options to use when creating the SmartBuffer instance.
|
||
|
*/
|
||
|
static fromOptions(options) {
|
||
|
return new this(options);
|
||
|
}
|
||
|
/**
|
||
|
* Ensures that the internal Buffer is large enough to write data.
|
||
|
*
|
||
|
* @param minLength { Number } The minimum length of the data that needs to be written.
|
||
|
* @param offset { Number } The offset of the data to be written.
|
||
|
*/
|
||
|
ensureWriteable(minLength, offset) {
|
||
|
const offsetVal = typeof offset === 'number' ? offset : 0;
|
||
|
// Ensure there is enough internal Buffer capacity.
|
||
|
this.ensureCapacity(this.length + minLength + offsetVal);
|
||
|
// If offset is provided, copy data into appropriate location in regards to the offset.
|
||
|
if (typeof offset === 'number') {
|
||
|
this.buff.copy(this.buff, offsetVal + minLength, offsetVal, this.buff.length);
|
||
|
}
|
||
|
// Adjust instance length.
|
||
|
this.length = Math.max(this.length + minLength, offsetVal + minLength);
|
||
|
}
|
||
|
/**
|
||
|
* Ensures that the internal Buffer is large enough to write at least the given amount of data.
|
||
|
*
|
||
|
* @param minLength { Number } The minimum length of the data needs to be written.
|
||
|
*/
|
||
|
ensureCapacity(minLength) {
|
||
|
const oldLength = this.buff.length;
|
||
|
if (minLength > oldLength) {
|
||
|
let data = this.buff;
|
||
|
let newLength = (oldLength * 3) / 2 + 1;
|
||
|
if (newLength < minLength) {
|
||
|
newLength = minLength;
|
||
|
}
|
||
|
this.buff = Buffer.allocUnsafe(newLength);
|
||
|
data.copy(this.buff, 0, 0, oldLength);
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Reads a numeric number value using the provided function.
|
||
|
*
|
||
|
* @param func { Function(offset: number) => number } The function to read data on the internal Buffer with.
|
||
|
* @param byteSize { Number } The number of bytes read.
|
||
|
*
|
||
|
* @param { Number }
|
||
|
*/
|
||
|
readNumberValue(func, byteSize) {
|
||
|
// Call Buffer.readXXXX();
|
||
|
const value = func.call(this.buff, this.readOffset);
|
||
|
// Adjust internal read offset
|
||
|
this.readOffset += byteSize;
|
||
|
return value;
|
||
|
}
|
||
|
/**
|
||
|
* Writes a numeric number value using the provided function.
|
||
|
*
|
||
|
* @param func { Function(offset: number, offset?) => number} The function to write data on the internal Buffer with.
|
||
|
* @param byteSize { Number } The number of bytes written.
|
||
|
* @param value { Number } The number value to write.
|
||
|
* @param offset { Number } the offset to write the number at.
|
||
|
*
|
||
|
*/
|
||
|
writeNumberValue(func, byteSize, value, offset) {
|
||
|
const offsetVal = typeof offset === 'number' ? offset : this.writeOffset;
|
||
|
// Ensure there is enough internal Buffer capacity. (raw offset is passed)
|
||
|
this.ensureWriteable(byteSize, offset);
|
||
|
// Call buffer.writeXXXX();
|
||
|
func.call(this.buff, value, offsetVal);
|
||
|
// Adjusts internal write offset
|
||
|
this.writeOffset += byteSize;
|
||
|
}
|
||
|
// Signed integers
|
||
|
/**
|
||
|
* Reads an Int8 value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readInt8() {
|
||
|
return this.readNumberValue(Buffer.prototype.readUInt8, 1);
|
||
|
}
|
||
|
/**
|
||
|
* Reads an Int16BE value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readInt16BE() {
|
||
|
return this.readNumberValue(Buffer.prototype.readUInt16BE, 2);
|
||
|
}
|
||
|
/**
|
||
|
* Reads an Int16LE value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readInt16LE() {
|
||
|
return this.readNumberValue(Buffer.prototype.readUInt16LE, 2);
|
||
|
}
|
||
|
/**
|
||
|
* Reads an Int32BE value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readInt32BE() {
|
||
|
return this.readNumberValue(Buffer.prototype.readUInt32BE, 4);
|
||
|
}
|
||
|
/**
|
||
|
* Reads an Int32LE value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readInt32LE() {
|
||
|
return this.readNumberValue(Buffer.prototype.readUInt32LE, 4);
|
||
|
}
|
||
|
/**
|
||
|
* Writes an Int8 value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeInt8(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeInt8, 1, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Writes an Int16BE value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeInt16BE(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeInt16BE, 2, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Writes an Int16LE value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeInt16LE(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeInt16LE, 2, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Writes an Int32BE value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeInt32BE(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeInt32BE, 4, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Writes an Int32LE value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeInt32LE(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeInt32LE, 4, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
// Unsigned Integers
|
||
|
/**
|
||
|
* Reads an UInt8 value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readUInt8() {
|
||
|
return this.readNumberValue(Buffer.prototype.readUInt8, 1);
|
||
|
}
|
||
|
/**
|
||
|
* Reads an UInt16BE value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readUInt16BE() {
|
||
|
return this.readNumberValue(Buffer.prototype.readUInt16BE, 2);
|
||
|
}
|
||
|
/**
|
||
|
* Reads an UInt16LE value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readUInt16LE() {
|
||
|
return this.readNumberValue(Buffer.prototype.readUInt16LE, 2);
|
||
|
}
|
||
|
/**
|
||
|
* Reads an UInt32BE value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readUInt32BE() {
|
||
|
return this.readNumberValue(Buffer.prototype.readUInt32BE, 4);
|
||
|
}
|
||
|
/**
|
||
|
* Reads an UInt32LE value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readUInt32LE() {
|
||
|
return this.readNumberValue(Buffer.prototype.readUInt32LE, 4);
|
||
|
}
|
||
|
/**
|
||
|
* Writes an UInt8 value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeUInt8(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeUInt8, 1, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Writes an UInt16BE value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeUInt16BE(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeUInt16BE, 2, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Writes an UInt16LE value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeUInt16LE(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeUInt16LE, 2, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Writes an UInt32BE value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeUInt32BE(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeUInt32BE, 4, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Writes an UInt32LE value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeUInt32LE(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeUInt32LE, 4, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
// Floating Point
|
||
|
/**
|
||
|
* Reads an FloatBE value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readFloatBE() {
|
||
|
return this.readNumberValue(Buffer.prototype.readFloatBE, 4);
|
||
|
}
|
||
|
/**
|
||
|
* Reads an FloatLE value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readFloatLE() {
|
||
|
return this.readNumberValue(Buffer.prototype.readFloatLE, 4);
|
||
|
}
|
||
|
/**
|
||
|
* Writes a FloatBE value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeFloatBE(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeFloatBE, 4, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Writes a FloatLE value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeFloatLE(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeFloatLE, 4, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
// Double Floating Point
|
||
|
/**
|
||
|
* Reads an DoublEBE value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readDoubleBE() {
|
||
|
return this.readNumberValue(Buffer.prototype.readDoubleBE, 8);
|
||
|
}
|
||
|
/**
|
||
|
* Reads an DoubleLE value from the current read position.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
readDoubleLE() {
|
||
|
return this.readNumberValue(Buffer.prototype.readDoubleLE, 8);
|
||
|
}
|
||
|
/**
|
||
|
* Writes a DoubleBE value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeDoubleBE(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeDoubleBE, 8, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Writes a DoubleLE value to the current write position (or at optional offset).
|
||
|
*
|
||
|
* @param value { Number } The value to write.
|
||
|
* @param offset { Number } The offset to write the value at.
|
||
|
*
|
||
|
* @return this
|
||
|
*/
|
||
|
writeDoubleLE(value, offset) {
|
||
|
this.writeNumberValue(Buffer.prototype.writeDoubleLE, 8, value, offset);
|
||
|
return this;
|
||
|
}
|
||
|
// Strings
|
||
|
/**
|
||
|
* Reads a String from the current read position.
|
||
|
*
|
||
|
* @param length { Number } The number of bytes to read as a String.
|
||
|
* @param encoding { String } The BufferEncoding to use for the string (Defaults to instance level encoding).
|
||
|
*
|
||
|
* @return { String }
|
||
|
*/
|
||
|
readString(length, encoding) {
|
||
|
const lengthVal = Math.min(length, this.length - this.readOffset) || this.length - this.readOffset;
|
||
|
const value = this.buff.slice(this.readOffset, this.readOffset + lengthVal).toString(encoding || this.encoding);
|
||
|
this.readOffset += lengthVal;
|
||
|
return value;
|
||
|
}
|
||
|
/**
|
||
|
* Writes a String to the current write position.
|
||
|
*
|
||
|
* @param value { String } The String value to write.
|
||
|
* @param arg2 { Number | String } The offset to write the string to, or the BufferEncoding to use.
|
||
|
* @param encoding { String } The BufferEncoding to use for writing strings (defaults to instance encoding).
|
||
|
*/
|
||
|
writeString(value, arg2, encoding) {
|
||
|
let offsetVal = this.writeOffset;
|
||
|
let encodingVal = this.encoding;
|
||
|
// Check for offset
|
||
|
if (typeof arg2 === 'number') {
|
||
|
offsetVal = arg2;
|
||
|
}
|
||
|
else if (typeof arg2 === 'string') {
|
||
|
if (Buffer.isEncoding(arg2)) {
|
||
|
encodingVal = arg2;
|
||
|
}
|
||
|
else {
|
||
|
throw new Error('Invalid encoding provided. Please specify a valid encoding the internal Node.js Buffer supports.');
|
||
|
}
|
||
|
}
|
||
|
// Check for encoding (third param)
|
||
|
if (typeof encoding === 'string') {
|
||
|
if (Buffer.isEncoding(encoding)) {
|
||
|
encodingVal = encoding;
|
||
|
}
|
||
|
else {
|
||
|
throw new Error('Invalid encoding provided. Please specify a valid encoding the internal Node.js Buffer supports.');
|
||
|
}
|
||
|
}
|
||
|
// Calculate bytelength of string.
|
||
|
const byteLength = Buffer.byteLength(value, encodingVal);
|
||
|
// Ensure there is enough internal Buffer capacity.
|
||
|
this.ensureWriteable(byteLength, offsetVal);
|
||
|
// Write value
|
||
|
this.buff.write(value, offsetVal, byteLength, encodingVal);
|
||
|
// Increment internal Buffer write offset;
|
||
|
this.writeOffset += byteLength;
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Reads a null-terminated String from the current read position.
|
||
|
*
|
||
|
* @param encoding { String } The BufferEncoding to use for the string (Defaults to instance level encoding).
|
||
|
*
|
||
|
* @return { String }
|
||
|
*/
|
||
|
readStringNT(encoding) {
|
||
|
// Set null character position to the end SmartBuffer instance.
|
||
|
let nullPos = this.length;
|
||
|
// Find next null character (if one is not found, default from above is used)
|
||
|
for (let i = this.readOffset; i < this.length; i++) {
|
||
|
if (this.buff[i] === 0x00) {
|
||
|
nullPos = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// Read string value
|
||
|
const value = this.buff.slice(this.readOffset, nullPos);
|
||
|
// Increment internal Buffer read offset
|
||
|
this.readOffset = nullPos + 1;
|
||
|
return value.toString(encoding || this.encoding);
|
||
|
}
|
||
|
/**
|
||
|
* Writes a null-terminated String to the current write position.
|
||
|
*
|
||
|
* @param value { String } The String value to write.
|
||
|
* @param arg2 { Number | String } The offset to write the string to, or the BufferEncoding to use.
|
||
|
* @param encoding { String } The BufferEncoding to use for writing strings (defaults to instance encoding).
|
||
|
*/
|
||
|
writeStringNT(value, offset, encoding) {
|
||
|
// Write Values
|
||
|
this.writeString(value, offset, encoding);
|
||
|
this.writeUInt8(0x00, (typeof offset === 'number' ? offset + value.length : this.writeOffset));
|
||
|
}
|
||
|
// Buffers
|
||
|
/**
|
||
|
* Reads a Buffer from the internal read position.
|
||
|
*
|
||
|
* @param length { Number } The length of data to read as a Buffer.
|
||
|
*
|
||
|
* @return { Buffer }
|
||
|
*/
|
||
|
readBuffer(length) {
|
||
|
const lengthVal = typeof length === 'number' ? length : this.length;
|
||
|
const endPoint = Math.min(this.length, this.readOffset + lengthVal);
|
||
|
// Read buffer value
|
||
|
const value = this.buff.slice(this.readOffset, endPoint);
|
||
|
// Increment internal Buffer read offset
|
||
|
this.readOffset = endPoint;
|
||
|
return value;
|
||
|
}
|
||
|
/**
|
||
|
* Writes a Buffer to the current write position.
|
||
|
*
|
||
|
* @param value { Buffer } The Buffer to write.
|
||
|
* @param offset { Number } The offset to write the Buffer to.
|
||
|
*/
|
||
|
writeBuffer(value, offset) {
|
||
|
const offsetVal = typeof offset === 'number' ? offset : this.writeOffset;
|
||
|
// Ensure there is enough internal Buffer capacity.
|
||
|
this.ensureWriteable(value.length, offsetVal);
|
||
|
// Write buffer value
|
||
|
value.copy(this.buff, offsetVal);
|
||
|
// Increment internal Buffer write offset
|
||
|
this.writeOffset += value.length;
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Reads a null-terminated Buffer from the current read poisiton.
|
||
|
*
|
||
|
* @return { Buffer }
|
||
|
*/
|
||
|
readBufferNT() {
|
||
|
// Set null character position to the end SmartBuffer instance.
|
||
|
let nullPos = this.length;
|
||
|
// Find next null character (if one is not found, default from above is used)
|
||
|
for (let i = this.readOffset; i < this.length; i++) {
|
||
|
if (this.buff[i] === 0x00) {
|
||
|
nullPos = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// Read value
|
||
|
const value = this.buff.slice(this.readOffset, nullPos);
|
||
|
// Increment internal Buffer read offset
|
||
|
this.readOffset = nullPos + 1;
|
||
|
return value;
|
||
|
}
|
||
|
/**
|
||
|
* Writes a null-terminated Buffer to the current write position.
|
||
|
*
|
||
|
* @param value { Buffer } The Buffer to write.
|
||
|
* @param offset { Number } The offset to write the Buffer to.
|
||
|
*/
|
||
|
writeBufferNT(value, offset) {
|
||
|
// Write Values
|
||
|
this.writeBuffer(value, offset);
|
||
|
this.writeUInt8(0, (typeof offset === 'number' ? offset + value.length : this.writeOffset));
|
||
|
return this;
|
||
|
}
|
||
|
/**
|
||
|
* Clears the SmartBuffer instance to its original empty state.
|
||
|
*/
|
||
|
clear() {
|
||
|
this.writeOffset = 0;
|
||
|
this.readOffset = 0;
|
||
|
this.length = 0;
|
||
|
}
|
||
|
/**
|
||
|
* Gets the remaining data left to be read from the SmartBuffer instance.
|
||
|
*
|
||
|
* @return { Number }
|
||
|
*/
|
||
|
remaining() {
|
||
|
return this.length - this.readOffset;
|
||
|
}
|
||
|
/**
|
||
|
* Moves the read offset forward.
|
||
|
*
|
||
|
* @param amount { Number } The amount to move the read offset forward by.
|
||
|
*/
|
||
|
skip(amount) {
|
||
|
if (this.readOffset + amount > this.length) {
|
||
|
throw new Error('Target position is beyond the bounds of the SmartBuffer size.');
|
||
|
}
|
||
|
this.readOffset += amount;
|
||
|
}
|
||
|
/**
|
||
|
* Moves the read offset backwards.
|
||
|
*
|
||
|
* @param amount { Number } The amount to move the read offset backwards by.
|
||
|
*/
|
||
|
rewind(amount) {
|
||
|
if (this.readOffset - amount < 0) {
|
||
|
throw new Error('Target position is beyond the bounds of the SmartBuffer size.');
|
||
|
}
|
||
|
this.readOffset -= amount;
|
||
|
}
|
||
|
/**
|
||
|
* Moves the read offset to a specific position.
|
||
|
*
|
||
|
* @param position { Number } The position to move the read offset to.
|
||
|
*/
|
||
|
skipTo(position) {
|
||
|
this.moveTo(position);
|
||
|
}
|
||
|
/**
|
||
|
* Moves the read offset to a specific position.
|
||
|
*
|
||
|
* @param position { Number } The position to move the read offset to.
|
||
|
*/
|
||
|
moveTo(position) {
|
||
|
if (position > this.length) {
|
||
|
throw new Error('Target position is beyond the bounds of the SmartBuffer size.');
|
||
|
}
|
||
|
this.readOffset = position;
|
||
|
}
|
||
|
/**
|
||
|
* Gets the value of the internal managed Buffer
|
||
|
*
|
||
|
* @param { Buffer }
|
||
|
*/
|
||
|
toBuffer() {
|
||
|
return this.buff.slice(0, this.length);
|
||
|
}
|
||
|
/**
|
||
|
* Gets the String value of the internal managed Buffer
|
||
|
*
|
||
|
* @param encoding { String } The BufferEncoding to display the Buffer as (defaults to instance level encoding).
|
||
|
*/
|
||
|
toString(encoding) {
|
||
|
const encodingVal = typeof encoding === 'string' ? encoding : this.encoding;
|
||
|
if (Buffer.isEncoding(encodingVal)) {
|
||
|
return this.buff.toString(encodingVal, 0, this.length);
|
||
|
}
|
||
|
else {
|
||
|
throw new Error('Invalid encoding provided. Please specify a valid encoding the internal Node.js Buffer supports.');
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Destroys the SmartBuffer instance.
|
||
|
*/
|
||
|
destroy() {
|
||
|
this.clear();
|
||
|
}
|
||
|
/**
|
||
|
* Type checking function that determines if an object is a SmartBufferOptions object.
|
||
|
*/
|
||
|
static isSmartBufferOptions(options) {
|
||
|
const castOptions = options;
|
||
|
return castOptions && (castOptions.encoding !== undefined || castOptions.size !== undefined || castOptions.buff !== undefined);
|
||
|
}
|
||
|
}
|
||
|
module.exports = SmartBuffer;
|
||
|
//# sourceMappingURL=smartbuffer.js.map
|