Files
orca/src/shared/growing-byte-buffer.ts
T
NeilandOrca c419aadb19 oom(01): A1-shared-readers — reintroduce #10179 subset (#10294)
Files: 18 applied, 0 deleted (from 6eb70d8370)

Co-authored-by: Orca <help@stably.ai>
2026-07-24 20:30:31 -07:00

101 lines
3.1 KiB
TypeScript

export class GrowingByteBuffer {
private storage = Buffer.alloc(0)
private length = 0
get byteLength(): number {
return this.length
}
append(bytes: Buffer | Uint8Array): void {
if (bytes.byteLength === 0) {
return
}
const required = this.length + bytes.byteLength
if (required > this.storage.byteLength) {
const capacity = Math.max(required, Math.max(256, this.storage.byteLength * 2))
const next = Buffer.allocUnsafe(capacity)
this.storage.copy(next, 0, 0, this.length)
this.storage = next
}
const source = Buffer.isBuffer(bytes)
? bytes
: Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength)
source.copy(this.storage, this.length)
this.length = required
}
appendRetainedSuffix(bytes: Buffer | Uint8Array, maxBytes: number): void {
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
throw new RangeError('Retained suffix limit must be a non-negative safe integer')
}
if (maxBytes === 0) {
this.clear()
return
}
const source = Buffer.isBuffer(bytes)
? bytes
: Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength)
if (source.byteLength >= maxBytes) {
this.storage = Buffer.from(source.subarray(source.byteLength - maxBytes))
this.length = maxBytes
return
}
const retainedBytes = Math.min(this.length, maxBytes - source.byteLength)
if (retainedBytes < this.length) {
this.storage.copy(this.storage, 0, this.length - retainedBytes, this.length)
this.length = retainedBytes
}
this.append(source)
}
indexOfByte(value: number, byteOffset = 0): number {
return this.storage.subarray(0, this.length).indexOf(value, byteOffset)
}
takePrefixString(byteLength: number, encoding: BufferEncoding = 'utf8'): string {
if (!Number.isSafeInteger(byteLength) || byteLength < 0 || byteLength > this.length) {
throw new RangeError('Prefix length exceeds retained bytes')
}
const value = this.storage.toString(encoding, 0, byteLength)
this.discardPrefix(byteLength)
return value
}
discardPrefix(byteLength: number): void {
if (!Number.isSafeInteger(byteLength) || byteLength < 0 || byteLength > this.length) {
throw new RangeError('Prefix length exceeds retained bytes')
}
if (byteLength === 0) {
return
}
this.storage.copy(this.storage, 0, byteLength, this.length)
this.length -= byteLength
}
retainSuffix(maxBytes: number): void {
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
throw new RangeError('Suffix limit must be a non-negative safe integer')
}
if (this.length <= maxBytes) {
return
}
this.storage.copy(this.storage, 0, this.length - maxBytes, this.length)
this.length = maxBytes
}
toString(encoding: BufferEncoding = 'utf8'): string {
return this.storage.toString(encoding, 0, this.length)
}
takeString(encoding: BufferEncoding = 'utf8'): string {
const value = this.toString(encoding)
this.clear()
return value
}
clear(): void {
this.storage = Buffer.alloc(0)
this.length = 0
}
}