/** * @author jdiaz5513 */ import initTrace from "debug"; import { decodeUtf8, encodeUtf8 } from "../../util"; import { ListElementSize } from "../list-element-size"; import { List, initList } from "./list"; import { Pointer, validate, isNull, getContent, erase } from "./pointer"; import { PointerType } from "./pointer-type"; const trace = initTrace("capnp:text"); trace("load"); export class Text extends List { static fromPointer(pointer: Pointer): Text { validate(PointerType.LIST, pointer, ListElementSize.BYTE); return textFromPointerUnchecked(pointer); } /** * Read a utf-8 encoded string value from this pointer. * * @param {number} [index] The index at which to start reading; defaults to zero. * @returns {string} The string value. */ get(index = 0): string { if (index !== 0) { trace("Called get() on %s with a strange index (%d).", this, index); } if (isNull(this)) return ""; const c = getContent(this); // Remember to exclude the NUL byte. return decodeUtf8( new Uint8Array( c.segment.buffer, c.byteOffset + index, this.getLength() - index ) ); } /** * Get the number of utf-8 encoded bytes in this text. This does **not** include the NUL byte. * * @returns {number} The number of bytes allocated for the text. */ getLength(): number { return super.getLength() - 1; } /** * Write a utf-8 encoded string value starting at the specified index. * * @param {number} index The index at which to start copying the string. Note that if this is not zero the bytes * before `index` will be left as-is. All bytes after `index` will be overwritten. * @param {string} value The string value to set. * @returns {void} */ set(index: number, value: string): void { if (index !== 0) { trace("Called set() on %s with a strange index (%d).", this, index); } const src = encodeUtf8(value); const dstLength = src.byteLength + index; let c: Pointer; let original: Uint8Array | undefined; // TODO: Consider reusing existing space if list is already initialized and there's enough room for the value. if (!isNull(this)) { c = getContent(this); // Only copy bytes that will remain after copying. Everything after `index` should end up truncated. let originalLength = this.getLength(); if (originalLength >= index) { originalLength = index; } else { trace( "%d byte gap exists between original text and new text in %s.", index - originalLength, this ); } original = new Uint8Array( c.segment.buffer.slice( c.byteOffset, c.byteOffset + Math.min(originalLength, index) ) ); erase(this); } // Always allocate an extra byte for the NUL byte. initList(ListElementSize.BYTE, dstLength + 1, this); c = getContent(this); const dst = new Uint8Array(c.segment.buffer, c.byteOffset, dstLength); if (original) dst.set(original); dst.set(src, index); } toString(): string { return `Text_${super.toString()}`; } } function textFromPointerUnchecked(pointer: Pointer): Text { return new Text( pointer.segment, pointer.byteOffset, pointer._capnp.depthLimit ); }