From 4e87195739f2a5d9a05451b48773c8afdc680765 Mon Sep 17 00:00:00 2001 From: akiyamn Date: Sun, 24 Sep 2023 23:22:21 +1000 Subject: Initial commit (by create-cloudflare CLI) --- node_modules/capnp-ts/src/serialization/segment.ts | 476 +++++++++++++++++++++ 1 file changed, 476 insertions(+) create mode 100644 node_modules/capnp-ts/src/serialization/segment.ts (limited to 'node_modules/capnp-ts/src/serialization/segment.ts') diff --git a/node_modules/capnp-ts/src/serialization/segment.ts b/node_modules/capnp-ts/src/serialization/segment.ts new file mode 100644 index 0000000..b488451 --- /dev/null +++ b/node_modules/capnp-ts/src/serialization/segment.ts @@ -0,0 +1,476 @@ +/** + * @author jdiaz5513 + */ + +import initTrace from "debug"; + +import { MAX_SEGMENT_LENGTH, NATIVE_LITTLE_ENDIAN } from "../constants"; +import { NOT_IMPLEMENTED, SEG_REPLACEMENT_BUFFER_TOO_SMALL, SEG_SIZE_OVERFLOW } from "../errors"; +import { Int64, Uint64 } from "../types"; +import { format, padToWord } from "../util"; +import { Message } from "./message"; +import { Pointer } from "./pointers"; + +const trace = initTrace("capnp:segment"); +trace("load"); + +export class Segment implements DataView { + buffer: ArrayBuffer; + + /** The number of bytes currently allocated in the segment. */ + + byteLength: number; + + /** + * This value should always be zero. It's only here to satisfy the DataView interface. + * + * In the future the Segment implementation (or a child class) may allow accessing the buffer from a nonzero offset, + * but that adds a lot of extra arithmetic. + */ + + byteOffset: number; + + readonly [Symbol.toStringTag] = "Segment" as "DataView"; + + readonly id: number; + + readonly message: Message; + + private _dv: DataView; + + constructor(id: number, message: Message, buffer: ArrayBuffer, byteLength = 0) { + this.id = id; + this.message = message; + this.buffer = buffer; + this._dv = new DataView(buffer); + + this.byteOffset = 0; + this.byteLength = byteLength; + } + + /** + * Attempt to allocate the requested number of bytes in this segment. If this segment is full this method will return + * a pointer to freshly allocated space in another segment from the same message. + * + * @param {number} byteLength The number of bytes to allocate, will be rounded up to the nearest word. + * @returns {Pointer} A pointer to the newly allocated space. + */ + + allocate(byteLength: number): Pointer { + trace("allocate(%d)", byteLength); + + // eslint-disable-next-line @typescript-eslint/no-this-alias + let segment: Segment = this; + + byteLength = padToWord(byteLength); + + if (byteLength > MAX_SEGMENT_LENGTH - 8) { + throw new Error(format(SEG_SIZE_OVERFLOW, byteLength)); + } + + if (!segment.hasCapacity(byteLength)) { + segment = segment.message.allocateSegment(byteLength); + } + + const byteOffset = segment.byteLength; + + segment.byteLength = segment.byteLength + byteLength; + + trace("Allocated %x bytes in %s (requested segment: %s).", byteLength, this, segment); + + return new Pointer(segment, byteOffset); + } + + /** + * Quickly copy a word (8 bytes) from `srcSegment` into this one at the given offset. + * + * @param {number} byteOffset The offset to write the word to. + * @param {Segment} srcSegment The segment to copy the word from. + * @param {number} srcByteOffset The offset from the start of `srcSegment` to copy from. + * @returns {void} + */ + + copyWord(byteOffset: number, srcSegment: Segment, srcByteOffset: number): void { + const value = srcSegment._dv.getFloat64(srcByteOffset, NATIVE_LITTLE_ENDIAN); + + this._dv.setFloat64(byteOffset, value, NATIVE_LITTLE_ENDIAN); + } + + /** + * Quickly copy words from `srcSegment` into this one. + * + * @param {number} byteOffset The offset to start copying into. + * @param {Segment} srcSegment The segment to copy from. + * @param {number} srcByteOffset The start offset to copy from. + * @param {number} wordLength The number of words to copy. + * @returns {void} + */ + + copyWords(byteOffset: number, srcSegment: Segment, srcByteOffset: number, wordLength: number): void { + const dst = new Float64Array(this.buffer, byteOffset, wordLength); + const src = new Float64Array(srcSegment.buffer, srcByteOffset, wordLength); + + dst.set(src); + } + + /** + * Quickly fill a number of words in the buffer with zeroes. + * + * @param {number} byteOffset The first byte to set to zero. + * @param {number} wordLength The number of words (not bytes!) to zero out. + * @returns {void} + */ + + fillZeroWords(byteOffset: number, wordLength: number): void { + new Float64Array(this.buffer, byteOffset, wordLength).fill(0); + } + + /** WARNING: This function is not yet implemented. */ + + getBigInt64(byteOffset: number, littleEndian?: boolean): bigint { + throw new Error(format(NOT_IMPLEMENTED, byteOffset, littleEndian)); + } + + /** WARNING: This function is not yet implemented. */ + + getBigUint64(byteOffset: number, littleEndian?: boolean): bigint { + throw new Error(format(NOT_IMPLEMENTED, byteOffset, littleEndian)); + } + + /** + * Get the total number of bytes available in this segment (the size of its underlying buffer). + * + * @returns {number} The total number of bytes this segment can hold. + */ + + getCapacity(): number { + return this.buffer.byteLength; + } + + /** + * Read a float32 value out of this segment. + * + * @param {number} byteOffset The offset in bytes to the value. + * @returns {number} The value. + */ + + getFloat32(byteOffset: number): number { + return this._dv.getFloat32(byteOffset, true); + } + + /** + * Read a float64 value out of this segment. + * + * @param {number} byteOffset The offset in bytes to the value. + * @returns {number} The value. + */ + + getFloat64(byteOffset: number): number { + return this._dv.getFloat64(byteOffset, true); + } + + /** + * Read an int16 value out of this segment. + * + * @param {number} byteOffset The offset in bytes to the value. + * @returns {number} The value. + */ + + getInt16(byteOffset: number): number { + return this._dv.getInt16(byteOffset, true); + } + + /** + * Read an int32 value out of this segment. + * + * @param {number} byteOffset The offset in bytes to the value. + * @returns {number} The value. + */ + + getInt32(byteOffset: number): number { + return this._dv.getInt32(byteOffset, true); + } + + /** + * Read an int64 value out of this segment. + * + * @param {number} byteOffset The offset in bytes to the value. + * @returns {number} The value. + */ + + getInt64(byteOffset: number): Int64 { + return new Int64(new Uint8Array(this.buffer.slice(byteOffset, byteOffset + 8))); + } + + /** + * Read an int8 value out of this segment. + * + * @param {number} byteOffset The offset in bytes to the value. + * @returns {number} The value. + */ + + getInt8(byteOffset: number): number { + return this._dv.getInt8(byteOffset); + } + + /** + * Read a uint16 value out of this segment. + * + * @param {number} byteOffset The offset in bytes to the value. + * @returns {number} The value. + */ + + getUint16(byteOffset: number): number { + return this._dv.getUint16(byteOffset, true); + } + + /** + * Read a uint32 value out of this segment. + * + * @param {number} byteOffset The offset in bytes to the value. + * @returns {number} The value. + */ + + getUint32(byteOffset: number): number { + return this._dv.getUint32(byteOffset, true); + } + + /** + * Read a uint8 value out of this segment. + * NOTE: this does not copy the memory region, so updates to the underlying buffer will affect the Uint64 value! + * + * @param {number} byteOffset The offset in bytes to the value. + * @returns {number} The value. + */ + + getUint64(byteOffset: number): Uint64 { + return new Uint64(new Uint8Array(this.buffer.slice(byteOffset, byteOffset + 8))); + } + + /** + * Read a uint8 value out of this segment. + * + * @param {number} byteOffset The offset in bytes to the value. + * @returns {number} The value. + */ + + getUint8(byteOffset: number): number { + return this._dv.getUint8(byteOffset); + } + + hasCapacity(byteLength: number): boolean { + trace("hasCapacity(%d)", byteLength); + + // capacity - allocated >= requested + + return this.buffer.byteLength - this.byteLength >= byteLength; + } + + /** + * Quickly check the word at the given offset to see if it is equal to zero. + * + * PERF_V8: Fastest way to do this is by reading the whole word as a `number` (float64) in the _native_ endian format + * and see if it's zero. + * + * Benchmark: http://jsben.ch/#/Pjooc + * + * @param {number} byteOffset The offset to the word. + * @returns {boolean} `true` if the word is zero. + */ + + isWordZero(byteOffset: number): boolean { + return this._dv.getFloat64(byteOffset, NATIVE_LITTLE_ENDIAN) === 0; + } + + /** + * Swap out this segment's underlying buffer with a new one. It's assumed that the new buffer has the same content but + * more free space, otherwise all existing pointers to this segment will be hilariously broken. + * + * @param {ArrayBuffer} buffer The new buffer to use. + * @returns {void} + */ + + replaceBuffer(buffer: ArrayBuffer): void { + trace("replaceBuffer(%p)", buffer); + + if (this.buffer === buffer) return; + + if (buffer.byteLength < this.byteLength) { + throw new Error(SEG_REPLACEMENT_BUFFER_TOO_SMALL); + } + + this._dv = new DataView(buffer); + this.buffer = buffer; + } + + /** WARNING: This function is not yet implemented. */ + + setBigInt64(byteOffset: number, value: bigint, littleEndian?: boolean): void { + throw new Error(format(NOT_IMPLEMENTED, byteOffset, value, littleEndian)); + } + + /** WARNING: This function is not yet implemented. */ + + setBigUint64(byteOffset: number, value: bigint, littleEndian?: boolean): void { + throw new Error(format(NOT_IMPLEMENTED, byteOffset, value, littleEndian)); + } + + /** + * Write a float32 value to the specified offset. + * + * @param {number} byteOffset The offset from the beginning of the buffer. + * @param {number} val The value to store. + * @returns {void} + */ + + setFloat32(byteOffset: number, val: number): void { + this._dv.setFloat32(byteOffset, val, true); + } + + /** + * Write an float64 value to the specified offset. + * + * @param {number} byteOffset The offset from the beginning of the buffer. + * @param {number} val The value to store. + * @returns {void} + */ + + setFloat64(byteOffset: number, val: number): void { + this._dv.setFloat64(byteOffset, val, true); + } + + /** + * Write an int16 value to the specified offset. + * + * @param {number} byteOffset The offset from the beginning of the buffer. + * @param {number} val The value to store. + * @returns {void} + */ + + setInt16(byteOffset: number, val: number): void { + this._dv.setInt16(byteOffset, val, true); + } + + /** + * Write an int32 value to the specified offset. + * + * @param {number} byteOffset The offset from the beginning of the buffer. + * @param {number} val The value to store. + * @returns {void} + */ + + setInt32(byteOffset: number, val: number): void { + this._dv.setInt32(byteOffset, val, true); + } + + /** + * Write an int8 value to the specified offset. + * + * @param {number} byteOffset The offset from the beginning of the buffer. + * @param {number} val The value to store. + * @returns {void} + */ + + setInt8(byteOffset: number, val: number): void { + this._dv.setInt8(byteOffset, val); + } + + /** + * Write an int64 value to the specified offset. + * + * @param {number} byteOffset The offset from the beginning of the buffer. + * @param {Int64} val The value to store. + * @returns {void} + */ + + setInt64(byteOffset: number, val: Int64): void { + this._dv.setUint8(byteOffset, val.buffer[0]); + this._dv.setUint8(byteOffset + 1, val.buffer[1]); + this._dv.setUint8(byteOffset + 2, val.buffer[2]); + this._dv.setUint8(byteOffset + 3, val.buffer[3]); + this._dv.setUint8(byteOffset + 4, val.buffer[4]); + this._dv.setUint8(byteOffset + 5, val.buffer[5]); + this._dv.setUint8(byteOffset + 6, val.buffer[6]); + this._dv.setUint8(byteOffset + 7, val.buffer[7]); + } + + /** + * Write a uint16 value to the specified offset. + * + * @param {number} byteOffset The offset from the beginning of the buffer. + * @param {number} val The value to store. + * @returns {void} + */ + + setUint16(byteOffset: number, val: number): void { + this._dv.setUint16(byteOffset, val, true); + } + + /** + * Write a uint32 value to the specified offset. + * + * @param {number} byteOffset The offset from the beginning of the buffer. + * @param {number} val The value to store. + * @returns {void} + */ + + setUint32(byteOffset: number, val: number): void { + this._dv.setUint32(byteOffset, val, true); + } + + /** + * Write a uint64 value to the specified offset. + * TODO: benchmark other ways to perform this write operation. + * + * @param {number} byteOffset The offset from the beginning of the buffer. + * @param {Uint64} val The value to store. + * @returns {void} + */ + + setUint64(byteOffset: number, val: Uint64): void { + this._dv.setUint8(byteOffset + 0, val.buffer[0]); + this._dv.setUint8(byteOffset + 1, val.buffer[1]); + this._dv.setUint8(byteOffset + 2, val.buffer[2]); + this._dv.setUint8(byteOffset + 3, val.buffer[3]); + this._dv.setUint8(byteOffset + 4, val.buffer[4]); + this._dv.setUint8(byteOffset + 5, val.buffer[5]); + this._dv.setUint8(byteOffset + 6, val.buffer[6]); + this._dv.setUint8(byteOffset + 7, val.buffer[7]); + } + + /** + * Write a uint8 (byte) value to the specified offset. + * + * @param {number} byteOffset The offset from the beginning of the buffer. + * @param {number} val The value to store. + * @returns {void} + */ + + setUint8(byteOffset: number, val: number): void { + this._dv.setUint8(byteOffset, val); + } + + /** + * Write a zero word (8 bytes) to the specified offset. This is slightly faster than calling `setUint64` or + * `setFloat64` with a zero value. + * + * Benchmark: http://jsben.ch/#/dUdPI + * + * @param {number} byteOffset The offset of the word to set to zero. + * @returns {void} + */ + + setWordZero(byteOffset: number): void { + this._dv.setFloat64(byteOffset, 0, NATIVE_LITTLE_ENDIAN); + } + + toString(): string { + return format( + "Segment_id:%d,off:%a,len:%a,cap:%a", + this.id, + this.byteLength, + this.byteOffset, + this.buffer.byteLength + ); + } +} -- cgit v1.2.3