From 86e097362c5b4489f84e6f6442cfef876502c68d Mon Sep 17 00:00:00 2001 From: Lajos Meszaros Date: Sun, 25 Dec 2022 02:05:27 +0100 Subject: [PATCH] replace unnecessary data movement via buffers with methods to edit single bytes in ExpandingBuffer --- src/ExpandingBuffer.ts | 64 ++++++++++++++++++++++++++++++++++-------- src/Explode.ts | 12 ++++---- src/Implode.ts | 24 +++++++--------- src/constants.ts | 1 - 4 files changed, 67 insertions(+), 34 deletions(-) diff --git a/src/ExpandingBuffer.ts b/src/ExpandingBuffer.ts index d90a7d0..c7c34ad 100644 --- a/src/ExpandingBuffer.ts +++ b/src/ExpandingBuffer.ts @@ -2,8 +2,6 @@ import { Buffer } from 'node:buffer' import { EMPTY_BUFFER } from './constants' import { clamp } from './functions' -let cntr = 0 - export class ExpandingBuffer { #heap: Buffer #startIndex: number = 0 @@ -33,6 +31,44 @@ export class ExpandingBuffer { return this.#heap.byteLength } + /** + * Set a single byte of the stored data + * + * If offset is negative, then the method calculates the index from the end backwards + */ + setByte(offset: number, value: number) { + if (offset < 0) { + if (this.#endIndex + offset < this.#startIndex) { + return + } + + this.#heap[this.#endIndex + offset] = value + return + } + + if (this.#startIndex + offset >= this.#endIndex) { + this.#heap[this.#startIndex + offset] = value + } + } + + appendByte(value: number) { + if (this.#endIndex + 1 < this.heapSize()) { + this.#heap[this.#endIndex] = value + this.#endIndex += 1 + return + } + + const blockSize = 0x1000 + + const currentData = this.#getActualData() + + this.#heap = Buffer.allocUnsafe((Math.ceil((currentData.byteLength + 1) / blockSize) + 1) * blockSize) + currentData.copy(this.#heap, 0) + this.#heap[currentData.byteLength] = value + this.#startIndex = 0 + this.#endIndex = currentData.byteLength + 1 + } + append(newData: Buffer) { if (this.#endIndex + newData.byteLength < this.heapSize()) { newData.copy(this.#heap, this.#endIndex) @@ -113,11 +149,13 @@ export class ExpandingBuffer { * When the heap gets empty it also resets the indices as a cleanup */ dropStart(numberOfBytes: number) { - if (numberOfBytes > 0) { - this.#startIndex += numberOfBytes - if (this.#startIndex >= this.#endIndex) { - this.clear() - } + if (numberOfBytes <= 0) { + return + } + + this.#startIndex += numberOfBytes + if (this.#startIndex >= this.#endIndex) { + this.clear() } } @@ -129,11 +167,13 @@ export class ExpandingBuffer { * When the heap gets empty it also resets the indices as a cleanup */ dropEnd(numberOfBytes: number) { - if (numberOfBytes > 0) { - this.#endIndex -= numberOfBytes - if (this.#startIndex >= this.#endIndex) { - this.clear() - } + if (numberOfBytes <= 0) { + return + } + + this.#endIndex -= numberOfBytes + if (this.#startIndex >= this.#endIndex) { + this.clear() } } diff --git a/src/Explode.ts b/src/Explode.ts index c92c556..a70f998 100644 --- a/src/Explode.ts +++ b/src/Explode.ts @@ -96,7 +96,6 @@ export class Explode { #asciiTable2D34: number[] = repeat(0, 0x100) #asciiTable2E34: number[] = repeat(0, 0x80) #asciiTable2EB4: number[] = repeat(0, 0x100) - #reusableByte: Buffer = Buffer.allocUnsafe(1) constructor(config: Config = {}) { this.#verbose = config?.verbose ?? false @@ -334,27 +333,26 @@ export class Explode { let nextLiteral = this.#decodeNextLiteral() while (nextLiteral !== LITERAL_END_STREAM) { - let addition: Buffer - if (nextLiteral >= 0x100) { const repeatLength = nextLiteral - 0xfe const minusDistance = this.#decodeDistance(repeatLength) const availableData = this.#outputBuffer.read(this.#outputBuffer.size() - minusDistance, repeatLength) + let addition: Buffer + if (repeatLength > minusDistance) { const multipliedData = repeat(availableData, Math.ceil(repeatLength / availableData.length)) addition = Buffer.concat(multipliedData).subarray(0, repeatLength) } else { addition = availableData } + + this.#outputBuffer.append(addition) } else { - this.#reusableByte[0] = nextLiteral - addition = this.#reusableByte + this.#outputBuffer.appendByte(nextLiteral) } - this.#outputBuffer.append(addition) - this.#backup() nextLiteral = this.#decodeNextLiteral() diff --git a/src/Implode.ts b/src/Implode.ts index e77d8b1..cdbbf85 100644 --- a/src/Implode.ts +++ b/src/Implode.ts @@ -12,7 +12,6 @@ import { LenBits, LenCode, LONGEST_ALLOWED_REPETITION, - SINGLE_ZERO_BUFFER, } from './constants' import { InvalidCompressionTypeError, InvalidDictionarySizeError } from './errors' import { ExpandingBuffer } from './ExpandingBuffer' @@ -75,7 +74,6 @@ export class Implode { #outBits: number = 0 #nChBits: number[] = repeat(0, 0x306) #nChCodes: number[] = repeat(0, 0x306) - #reusableByte: Buffer = Buffer.allocUnsafe(1) constructor(compressionType: Compression, dictionarySize: DictionarySize, config: Config) { if (!(compressionType in Compression) || compressionType === Compression.Unknown) { @@ -130,9 +128,7 @@ export class Implode { instance.#outputBuffer.flushStart(numberOfBytes) if (instance.#outBits === 0) { - // set last byte to 0 - instance.#outputBuffer.dropEnd(1) - instance.#outputBuffer.append(SINGLE_ZERO_BUFFER) + instance.#outputBuffer.setByte(-1, 0) } callback(null, output) @@ -253,7 +249,8 @@ export class Implode { // ------------------------------- - this.#inputBuffer.dropStart(this.#inputBuffer.size()) + // this.#inputBuffer.dropStart(this.#inputBuffer.size()) + this.#inputBuffer.clear() } if (this.#streamEnded) { @@ -338,7 +335,9 @@ export class Implode { } } - this.#outputBuffer.append(Buffer.from([this.#compressionType, this.#dictionarySize, 0])) + this.#outputBuffer.appendByte(this.#compressionType) + this.#outputBuffer.appendByte(this.#dictionarySize) + this.#outputBuffer.appendByte(0) this.#outBits = 0 } @@ -352,21 +351,18 @@ export class Implode { const outBits = this.#outBits const lastBytes = this.#outputBuffer.readByte(this.#outputBuffer.size() - 1) - this.#outputBuffer.dropEnd(1) - this.#reusableByte[0] = lastBytes | getLowestNBits(8, bitBuffer << outBits) - this.#outputBuffer.append(this.#reusableByte) + this.#outputBuffer.setByte(-1, lastBytes | getLowestNBits(8, bitBuffer << outBits)) this.#outBits = this.#outBits + nBits if (this.#outBits > 8) { - bitBuffer = bitBuffer >> (8 - outBits) - this.#reusableByte[0] = getLowestNBits(8, bitBuffer) - this.#outputBuffer.append(this.#reusableByte) this.#outBits = getLowestNBits(3, this.#outBits) + bitBuffer = bitBuffer >> (8 - outBits) + this.#outputBuffer.appendByte(getLowestNBits(8, bitBuffer)) } else { this.#outBits = getLowestNBits(3, this.#outBits) if (this.#outBits === 0) { - this.#outputBuffer.append(SINGLE_ZERO_BUFFER) + this.#outputBuffer.appendByte(0) } } } diff --git a/src/constants.ts b/src/constants.ts index 7c4b00e..71eb719 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -21,7 +21,6 @@ export enum DictionarySize { } export const EMPTY_BUFFER = Buffer.from([]) -export const SINGLE_ZERO_BUFFER = Buffer.from([0]) export const LONGEST_ALLOWED_REPETITION = 0x204