UNPKG

jspurefix

Version:
301 lines (267 loc) 7.96 kB
import { Ascii } from './ascii' export class ElasticBuffer { private buffer: Buffer private ptr: number = 0 private stretched: number constructor (public readonly size: number = 6 * 1024, public readonly returnTo: number = 6 * 1024) { this.size = Math.max(1, this.size) this.buffer = Buffer.allocUnsafe(this.size) this.returnTo = Math.max(this.size, this.returnTo) this.stretched = this.size } private static precisionRound (n: number, precision: number): number { const factor: number = Math.pow(10, precision) return Math.round(n * factor) / factor } private static HowManyDigits (v: number): number { v = Math.abs(v) let digits: number = 0 let w: number = v while (w > 0) { ++digits w = Math.floor(w / 10) } return Math.max(digits, 1) } public currentSize (): number { return this.stretched } public getPos (): number { return this.ptr } public get (pos: number): number { return this.buffer[pos] } public writeBoolean (v: boolean): number { this.writeChar(v ? Ascii.Y : Ascii.N) return this.ptr } public switchChar (c: number): number { this.buffer[this.ptr - 1] = c return this.ptr } public saveChar (c: number): number { this.buffer[this.ptr++] = c return this.ptr } public writeChar (c: number): number { if (c > 255) throw new Error(`can't write ${c} to a byte`) this.checkGrowBuffer(1) this.buffer[this.ptr++] = c return this.ptr } public writeString (s: string): number { const begin = this.ptr this.checkGrowBuffer(s.length) const buffer = this.buffer this.ptr += buffer.write(s, begin, s.length, 'ascii') return this.ptr } public writeBuffer (v: Buffer): number { const begin = this.ptr this.checkGrowBuffer(v.length) const buffer = this.buffer const srcLen: number = v.length this.ptr += v.copy(buffer, begin, 0, srcLen) return this.ptr } public writeWholeNumber (n: number): number { const digits: number = ElasticBuffer.HowManyDigits(n) let reserve = digits const sign: number = Math.sign(n) let p: number = Math.pow(10, digits - 1) let v: number = Math.abs(n) if (sign < 0) { reserve++ } this.checkGrowBuffer(reserve) const buffer = this.buffer if (sign < 0) { buffer[this.ptr++] = Ascii.Minus } while (p >= 1) { const d: number = Math.floor(v / p) v -= d * p p /= 10 buffer[this.ptr++] = Ascii.Zero + d } return this.ptr } public writeNumber (v: number, places: number = 13) { const rounded: number = Math.floor(v) const fraction: number = ElasticBuffer.precisionRound(v - rounded, places) if (fraction === 0) { // integer return this.writeWholeNumber(v) } else { const math = require('mathjs') // decimal with fraction turn to string const s: string = math.format(v, { notation: 'fixed' }) return this.writeString(s) } } public reset (): boolean { this.ptr = 0 const shrink = this.stretched > this.returnTo if (shrink) { this.buffer = Buffer.allocUnsafe(this.returnTo) this.stretched = this.size } return shrink } public slice (): Buffer { return this.buffer.slice(0, this.ptr) } public clone (): ElasticBuffer { const cloned = new ElasticBuffer(this.ptr) this.buffer.copy(cloned.buffer, 0, 0, this.ptr) return cloned } public writePaddedHundreds (v: number): number { if (v > 999) throw new Error(`can't write ${v} as hundreds padding`) this.checkGrowBuffer(3) const buffer = this.buffer const zero: number = Ascii.Zero const units: number = v % 10 + zero v = v / 10 const tens: number = v % 10 + zero v = v / 10 buffer[this.ptr++] = v % 10 + zero buffer[this.ptr++] = tens buffer[this.ptr++] = units return this.ptr } public writePaddedTensUnits (v: number): number { if (v > 99) throw new Error(`can't write ${v} as hundreds padding`) this.checkGrowBuffer(2) const buffer = this.buffer const zero: number = Ascii.Zero const units: number = v % 10 + zero v = v / 10 buffer[this.ptr++] = v % 10 + zero buffer[this.ptr++] = units return this.ptr } public patchPaddedNumberAtPos (ptr: number, numToWrite: number, padding: number): void { let digits: number = ElasticBuffer.HowManyDigits(numToWrite) const saved: number = this.ptr this.ptr = ptr const buffer = this.buffer while (digits++ < padding) { buffer[this.ptr++] = Ascii.Zero } this.writeWholeNumber(numToWrite) this.ptr = saved } public toString (ptr: number = this.ptr): string { return this.buffer.toString('ascii', 0, ptr) } public checksum (ptr: number = this.ptr): number { const cks: number = this.sum(ptr) return cks % 256 } public sum (ptr: number = this.ptr): number { let total: number = 0 ptr = Math.min(ptr, this.ptr) const buffer = this.buffer for (let idx: number = 0; idx < ptr; idx++) { total += buffer[idx] } return total } public getWholeNumber (start: number, vend: number): number { const buffer = this.buffer let sign = 1 let raised = vend - start switch (buffer[start]) { case Ascii.Minus: { --raised sign = -1 ++start break } case Ascii.Plus: { --raised ++start break } } let i: number = Math.pow(10, raised) let num: number = 0 let scan: number = start while (scan <= vend) { const p: number = buffer[scan++] const d: number = p - Ascii.Zero num += d * i i /= 10 } return num * sign } public getString (start: number, end: number): string { return this.buffer.toString('ascii', start, end) } public getBuffer (start: number, end: number): Buffer { return this.buffer.slice(start, end) } public getBoolean (start: number): boolean { const b: number = this.buffer[start] return b === Ascii.Y } public getFloat (start: number, vend: number): number { let n: number = 0 let digits: number = 0 let dotPosition: number = 0 const buffer = this.buffer let sign = 1 switch (buffer[start]) { case Ascii.Minus: { sign = -1 start++ break } case Ascii.Plus: { start++ break } } const len = vend - start let i: number = Math.pow(10, len - 1) for (let j: number = start; j <= vend; ++j) { const p: number = buffer[j] if (p >= Ascii.Zero && p <= Ascii.Nine) { const d: number = p - Ascii.Zero ++digits n += d * i i /= 10 } else if (p === Ascii.Dot) { if (dotPosition > 0) { return null } dotPosition = j - start } else if (digits > 0) { return null } } const power: number = dotPosition === 0 ? 0 : len - dotPosition const raised = dotPosition === 0 ? 10 : Math.pow(10, -1 * power) const round = dotPosition === 0 ? 1 : Math.pow(10, power) const val = n * raised * sign return Math.round(val * round) / round } public checkGrowBuffer (required: number): void { let buffer = this.buffer let size = buffer.length const ptr = this.ptr if (size - ptr >= required) { return } while (size - ptr < required) { size *= 2 } const old = buffer buffer = Buffer.allocUnsafe(size) old.copy(buffer, 0, 0, this.ptr) this.buffer = buffer this.stretched = size } }