UNPKG

@dstanesc/o-o-o-o-o-o-o

Version:

O-O-O-O-O-O-O is a collection of content addressed persistent data structures

772 lines 23.7 kB
import { fastCloneVersionDetails } from './clone'; const REF_EXISTS = 0x1; const TYPE_EXISTS = 0x1; const LINK_EXISTS = 0x1; class BinaryEncoder { constructor(size) { this.buffer = new Uint8Array(size); this.cursor = 0; } content() { return this.buffer.subarray(0, this.cursor); // identical w/ fixed size buffer } /* * String, fixed size, first 4 bytes hold the actual size */ // writeStringFixedSize(value: string, size: number) { // size + 4 // const bytes = pack(value) // if (bytes.length > size) throw new Error(`String too large - ${bytes.length}, max allowed ${size} bytes`) // const length = bytes.length // this.writeUInt(length) // const fixedSizeBuffer = new Uint8Array(size) // fixedSizeBuffer.set(bytes, 0) // return this.writeBytes(fixedSizeBuffer) // } writeByte(byte) { this.buffer[this.cursor] = byte; this.cursor += 1; } writeBytes(bytes) { const start = this.cursor; this.buffer.set(bytes, start); this.cursor += bytes.byteLength; return this.cursor; } /* * Unsigned 32-bit integer, little endian */ writeUInt(value) { if (value < 0 || value > 0xffffffff) throw new Error('Integer out of range'); const start = this.cursor; this.buffer[start] = value & 0xff; this.buffer[start + 1] = value >>> 8; this.buffer[start + 2] = value >>> 16; this.buffer[start + 3] = value >>> 24; this.cursor += 4; return this.cursor; } /* * 32-bit integer, little endian */ writeInt(value) { if (value < -0x80000000 || value > 0x7fffffff) throw new Error('Integer out of range'); const start = this.cursor; this.buffer[start] = value & 0xff; this.buffer[start + 1] = value >>> 8; this.buffer[start + 2] = value >>> 16; this.buffer[start + 3] = value >>> 24; this.cursor += 4; return this.cursor; } writeReserved(flags) { return this.writeByte(flags); } writeRefExists() { let flags = 0; flags |= REF_EXISTS; return this.writeByte(flags); } writeOffset(offset) { //if (this.cursor !== offset) throw new Error(`Writing invalid offset ${offset}, current offset is ${this.cursor}`) return this.writeUInt(offset); } writeRef(ref) { // 5 this.writeRefExists(); // 1 return this.writeUInt(ref); // 4 } writeTypeExists() { let flags = 0; flags |= TYPE_EXISTS; return this.writeByte(flags); } writeStatus(flag) { let flags = 0; flags |= flag; return this.writeByte(flags); } writeType(type) { //5 this.writeTypeExists(); // 1 return this.writeUInt(type); // 4 } writeLinkExists() { let flags = 0; flags |= LINK_EXISTS; return this.writeByte(flags); } writeLink(link) { this.writeBytes(link.bytes); } skipBytes(length) { this.cursor += length; return this.cursor; } skipReserved() { return this.skipBytes(1); } skipUInt() { // 4 return this.skipBytes(4); } skipRef() { // 5 return this.skipBytes(5); } skipType() { // 5 return this.skipBytes(5); } } class BinaryDecoder { constructor(buffer) { this.buffer = buffer; this.cursor = 0; } readByte() { const start = this.cursor; this.cursor += 1; return this.buffer[start]; } readBytes(length) { const start = this.cursor; this.cursor += length; const bytes = this.buffer.subarray(start, this.cursor); return bytes; } // readStringFixedSize(bufferSize: number) { // const stringSize = this.readUInt() // const stringBytes = this.readBytes(stringSize) // this.cursor += bufferSize - stringSize // return unpack(stringBytes) // } /* * Unsigned 32-bit integer, little endian */ readUInt() { const start = this.cursor; const value = (this.buffer[start] | (this.buffer[start + 1] << 8) | (this.buffer[start + 2] << 16)) + this.buffer[start + 3] * 0x1000000; this.cursor += 4; return value; } /* * 32-bit integer, little endian */ readInt() { const start = this.cursor; const value = this.buffer[start] | (this.buffer[start + 1] << 8) | (this.buffer[start + 2] << 16) | (this.buffer[start + 3] << 24); this.cursor += 4; return value; } readOffset() { return this.readUInt(); } readRefExists() { // 1 return this.readByte(); } readTypeExists() { // 1 return this.readByte(); } readLinkExists() { // 1 return this.readByte(); } readStatus() { // 1 const flags = this.readByte(); return flags; } readRef() { // 5 const flags = this.readRefExists(); // 1 if (flags & REF_EXISTS) { return this.readOffset(); // 4 } else { this.skipUInt(); return undefined; } } readType() { // 5 const flags = this.readTypeExists(); // 1 if (flags & TYPE_EXISTS) { return this.readUInt(); // 4 } else { this.skipUInt(); return undefined; } } readLink(decode) { const bytes = this.readBytes(36); return decode(bytes); } readOptionalLink(decode) { const flags = this.readLinkExists(); // 1 if (flags & LINK_EXISTS) { return this.readLink(decode); } else { this.skipBytes(36); return undefined; } } skipBytes(length) { this.cursor += length; } skipReserved() { // 1 return this.skipBytes(1); } skipUInt() { // 4 return this.skipBytes(4); } skipRef() { // 5 return this.skipBytes(5); } skipType() { // 5 return this.skipBytes(5); } } const VERTEX_SIZE_BYTES = 25; class VertexEncoder extends BinaryEncoder { constructor(vertices) { super(vertices.length * VERTEX_SIZE_BYTES); this.vertices = vertices; } writeVertex(vertex) { // 20 this.writeOffset(vertex.offset); // 4 if (vertex.type !== undefined) // 5 this.writeType(vertex.type); else this.skipType(); if (vertex.nextEdge !== undefined) // 5 this.writeRef(vertex.nextEdge); else this.skipRef(); if (vertex.nextProp !== undefined) // 5 this.writeRef(vertex.nextProp); else this.skipRef(); if (vertex.nextIndex !== undefined) // 5 this.writeRef(vertex.nextIndex); else this.skipRef(); this.writeStatus(vertex.status); // 1 } write() { for (const vertex of this.vertices) this.writeVertex(vertex); return this.content(); } } class VertexDecoder extends BinaryDecoder { readVertex() { const offset = this.readOffset(); const type = this.readType(); const nextEdge = this.readRef(); const nextProp = this.readRef(); const nextIndex = this.readRef(); const status = this.readStatus(); const vertex = { status, offset }; if (type !== undefined) vertex.type = type; if (nextEdge !== undefined) vertex.nextEdge = nextEdge; if (nextProp !== undefined) vertex.nextProp = nextProp; if (nextIndex !== undefined) vertex.nextIndex = nextIndex; return vertex; } read() { if (this.buffer.byteLength % VERTEX_SIZE_BYTES !== 0) throw new Error('Invalid vertex serialization'); const size = Math.trunc(this.buffer.byteLength / VERTEX_SIZE_BYTES); const vertices = []; for (let i = 0; i < size; i++) { vertices.push(this.readVertex()); } return vertices; } } const EDGE_SIZE_BYTES = 45; class EdgeEncoder extends BinaryEncoder { constructor(edges) { super(edges.length * EDGE_SIZE_BYTES); this.edges = edges; } writeEdge(edge) { this.writeOffset(edge.offset); // 4 if (edge.type !== undefined) // 5 this.writeType(edge.type); else this.skipType(); this.writeRef(edge.source); // 5 this.writeRef(edge.target); // 5 if (edge.sourcePrev !== undefined) // 5 this.writeRef(edge.sourcePrev); else this.skipRef(); if (edge.sourceNext !== undefined) // 5 this.writeRef(edge.sourceNext); else this.skipRef(); if (edge.targetPrev !== undefined) // 5 this.writeRef(edge.targetPrev); else this.skipRef(); if (edge.targetNext !== undefined) // 5 this.writeRef(edge.targetNext); else this.skipRef(); if (edge.nextProp !== undefined) // 5 this.writeRef(edge.nextProp); else this.skipRef(); this.writeStatus(edge.status); // 1 } write() { for (const edge of this.edges) this.writeEdge(edge); return this.content(); } } class EdgeDecoder extends BinaryDecoder { readEdge() { const offset = this.readOffset(); const type = this.readType(); const source = this.readRef(); const target = this.readRef(); const sourcePrev = this.readRef(); const sourceNext = this.readRef(); const targetPrev = this.readRef(); const targetNext = this.readRef(); const nextProp = this.readRef(); const status = this.readStatus(); const edge = { status, offset, source, target }; if (type !== undefined) edge.type = type; if (sourcePrev !== undefined) edge.sourcePrev = sourcePrev; if (sourceNext !== undefined) edge.sourceNext = sourceNext; if (targetPrev !== undefined) edge.targetPrev = targetPrev; if (targetNext !== undefined) edge.targetNext = targetNext; if (nextProp !== undefined) edge.nextProp = nextProp; return edge; } read() { if (this.buffer.byteLength % EDGE_SIZE_BYTES !== 0) throw new Error('Invalid edge serialization'); const size = Math.trunc(this.buffer.byteLength / EDGE_SIZE_BYTES); const edges = []; for (let i = 0; i < size; i++) { edges.push(this.readEdge()); } return edges; } } const PROP_SIZE_BYTES = 32; class PropEncoder extends BinaryEncoder { constructor(props, refs) { super(props.length * PROP_SIZE_BYTES); this.props = props; this.refs = refs; } async writeValue(propRef, value) { // 13 const valueRef = this.refs.get(propRef); this.writeOffset(propRef); // 4 if (valueRef !== undefined) { this.writeRef(valueRef.ref); // 5 this.writeUInt(valueRef.length); // 4 } else { this.skipRef(); this.skipUInt(); } } async writeProp(prop) { // 32 this.writeOffset(prop.offset); // 4 if (prop.type !== undefined) // 5 this.writeType(prop.type); else this.skipType(); this.writeUInt(prop.key); // 4 await this.writeValue(prop.offset, prop.value); // 13 if (prop.nextProp !== undefined) // 5 this.writeRef(prop.nextProp); else this.skipRef(); this.writeStatus(prop.status); // 1 } async write() { for (const prop of this.props) await this.writeProp(prop); return this.content(); } } class PropRecordDecoder extends BinaryDecoder { constructor(buffer) { super(buffer); } async readPropValueRef() { const propRef = this.readOffset(); const ref = this.readRef(); if (ref !== undefined) { const length = this.readUInt(); const valueRef = { propRef, ref, length }; return valueRef; } else { this.skipUInt(); return undefined; } } async readProp() { const offset = this.readOffset(); //4 const type = this.readType(); //5 const key = this.readUInt(); //4 const valueRef = await this.readPropValueRef(); // 13 const nextProp = this.readRef(); //5 const status = this.readStatus(); //1 const propRecord = { status, offset, key, valueRef }; if (type !== undefined) propRecord.type = type; if (nextProp !== undefined) propRecord.nextProp = nextProp; return propRecord; } async read() { if (this.buffer.byteLength % PROP_SIZE_BYTES !== 0) throw new Error('Invalid prop serialization'); const size = Math.trunc(this.buffer.byteLength / PROP_SIZE_BYTES); const props = []; for (let i = 0; i < size; i++) { props.push(await this.readProp()); } return props; } } class PropDecoder extends BinaryDecoder { constructor(buffer, valueGet) { super(buffer); this.valueGet = valueGet; } async readPropValue() { const propRef = this.readOffset(); const ref = this.readRef(); if (ref !== undefined) { const length = this.readUInt(); const valueRef = { propRef, ref, length }; return await this.valueGet(valueRef); } else { this.skipUInt(); return undefined; } } async readProp() { const offset = this.readOffset(); //4 const type = this.readType(); //5 const key = this.readUInt(); //4 const value = await this.readPropValue(); // 13 const nextProp = this.readRef(); //5 const status = this.readStatus(); //1 const prop = { status, offset, key, value }; if (type !== undefined) prop.type = type; if (nextProp !== undefined) prop.nextProp = nextProp; return prop; } async read() { if (this.buffer.byteLength % PROP_SIZE_BYTES !== 0) throw new Error('Invalid prop serialization'); const size = Math.trunc(this.buffer.byteLength / PROP_SIZE_BYTES); const props = []; for (let i = 0; i < size; i++) { props.push(await this.readProp()); } return props; } } class PropValueEncoder extends BinaryEncoder { constructor(refOffset, props, valueEncode) { super(props .map((prop) => valueEncode(prop.value).byteLength) .reduce((a, b) => a + b, 0)); this.refOffset = refOffset; this.props = props; this.valueEncode = valueEncode; } writeValue(propRef, value) { const bytes = this.valueEncode(value); const ref = this.cursor + this.refOffset; const length = bytes.byteLength; this.writeBytes(bytes); return { propRef, ref, length }; } writeProp(prop) { const valueRef = this.writeValue(prop.offset, prop.value); return valueRef; } write() { const refs = new Map(); for (const prop of this.props) { const valueRef = this.writeProp(prop); refs.set(prop.offset, valueRef); } const buf = this.content(); return { buf, refs }; } } class PropValueDecoder extends BinaryDecoder { constructor(buffer, valueDecode) { super(buffer); this.valueDecode = valueDecode; } readValue(valueRef) { const bytes = this.readBytes(valueRef.length); return this.valueDecode(bytes); } read(valueRefs) { const values = new Map(); for (const valueRef of valueRefs) { const value = this.readValue(valueRef); values.set(valueRef.propRef, value); } return values; } } const INDEX_SIZE_BYTES = 56; class IndexEncoder extends BinaryEncoder { constructor(indices) { super(indices.length * INDEX_SIZE_BYTES); this.indices = indices; } async writeIndex(index) { // 56 this.writeOffset(index.offset); // 4 if (index.type !== undefined) // 5 this.writeType(index.type); else this.skipType(); this.writeType(index.key); // 5 this.writeLink(index.value); // 36 if (index.nextIndex !== undefined) // 5 this.writeRef(index.nextIndex); else this.skipRef(); this.writeStatus(index.status); // 1 } async write() { for (const index of this.indices) await this.writeIndex(index); return this.content(); } } class IndexDecoder extends BinaryDecoder { constructor(buffer, linkDecode) { super(buffer); this.linkDecode = linkDecode; } async readIndex() { const offset = this.readOffset(); const type = this.readType(); const key = this.readType(); const value = this.readLink(this.linkDecode); const nextIndex = this.readRef(); const status = this.readStatus(); const index = { status, offset, key, value }; if (type !== undefined) index.type = type; if (nextIndex !== undefined) index.nextIndex = nextIndex; return index; } async read() { if (this.buffer.byteLength % INDEX_SIZE_BYTES !== 0) throw new Error('Invalid index serialization'); const size = Math.trunc(this.buffer.byteLength / INDEX_SIZE_BYTES); const indices = []; for (let i = 0; i < size; i++) { indices.push(await this.readIndex()); } return indices; } } class RootEncoder extends BinaryEncoder { constructor({ vertexRoot, vertexOffset, edgeRoot, edgeOffset, propRoot, propOffset, valueRoot, valueOffset, indexRoot, indexOffset, }) { super(200); this.vertexRoot = vertexRoot; this.vertexOffset = vertexOffset; this.edgeRoot = edgeRoot; this.edgeOffset = edgeOffset; this.propRoot = propRoot; this.propOffset = propOffset; this.valueRoot = valueRoot; this.valueOffset = valueOffset; this.indexRoot = indexRoot; this.indexOffset = indexOffset; } write() { this.writeUInt(this.vertexOffset); //4 this.writeUInt(this.edgeOffset); //4 this.writeUInt(this.propOffset); //4 this.writeUInt(this.valueOffset); //4 this.writeUInt(this.indexOffset); //4 this.writeLink(this.vertexRoot); //36 this.writeLink(this.edgeRoot); //36 this.writeLink(this.propRoot); //36 this.writeLink(this.valueRoot); //36 this.writeLink(this.indexRoot); //36 return this; } } class RootDecoder extends BinaryDecoder { constructor(buffer, linkDecode) { super(buffer); this.linkDecode = linkDecode; } read() { const vertexOffset = this.readOffset(); const edgeOffset = this.readOffset(); const propOffset = this.readOffset(); const valueOffset = this.readOffset(); const indexOffset = this.readOffset(); const vertexRoot = this.readLink(this.linkDecode); const edgeRoot = this.readLink(this.linkDecode); const propRoot = this.readLink(this.linkDecode); const valueRoot = this.readLink(this.linkDecode); const indexRoot = this.readLink(this.linkDecode); return { vertexRoot, vertexOffset, edgeRoot, edgeOffset, propRoot, propOffset, valueRoot, valueOffset, indexRoot, indexOffset, }; } } const VERSION_CONST_SIZE_BYTES = 114; // 77 const VERSION_ID_SIZE_BYTES = 36; class VersionEncoder extends BinaryEncoder { constructor(id, versions, valueEncode) { super(VERSION_ID_SIZE_BYTES + versions .map((version) => valueEncode(version.details).byteLength + VERSION_CONST_SIZE_BYTES) .reduce((a, b) => a + b, 0)); this.id = id; this.versions = versions; this.valueEncode = valueEncode; } writeDetails(details) { const purgedDetails = fastCloneVersionDetails(details); const bytes = this.valueEncode(purgedDetails); const length = bytes.byteLength; this.writeUInt(length); // 4 this.writeBytes(bytes); // n } async writeVersion(version) { this.writeLink(version.root); // 36 if (version.parent !== undefined) { this.writeLinkExists(); // 1 this.writeLink(version.parent); // 36 } else this.skipBytes(37); if (version.mergeParent !== undefined) { this.writeLinkExists(); // 1 this.writeLink(version.mergeParent); // 36 } else this.skipBytes(37); this.writeDetails(version.details); // 4 + n } write() { this.writeLink(this.id); // 36 for (const version of this.versions) this.writeVersion(version); return this.content(); } } class VersionDecoder extends BinaryDecoder { constructor(buffer, linkDecode, valueDecode) { super(buffer); this.linkDecode = linkDecode; this.valueDecode = valueDecode; } readVersionDetails() { const length = this.readUInt(); const bytes = this.readBytes(length); return fastCloneVersionDetails(this.valueDecode(bytes)); } readVersion() { const root = this.readLink(this.linkDecode); const parent = this.readOptionalLink(this.linkDecode); const mergeParent = this.readOptionalLink(this.linkDecode); const details = this.readVersionDetails(); const version = { root, details }; if (parent !== undefined) version.parent = parent; if (mergeParent !== undefined) version.mergeParent = mergeParent; return version; } read() { const id = this.readLink(this.linkDecode); const versions = []; while (this.cursor < this.buffer.byteLength) versions.push(this.readVersion()); return { id, versions }; } } const OFFSET_INCREMENTS = { VERTEX_INCREMENT: VERTEX_SIZE_BYTES, EDGE_INCREMENT: EDGE_SIZE_BYTES, PROP_INCREMENT: PROP_SIZE_BYTES, INDEX_INCREMENT: INDEX_SIZE_BYTES, }; export { BinaryEncoder, BinaryDecoder, VertexEncoder, VertexDecoder, EdgeEncoder, EdgeDecoder, PropRecordDecoder, PropEncoder, PropDecoder, PropValueEncoder, PropValueDecoder, IndexEncoder, IndexDecoder, RootEncoder, RootDecoder, VersionEncoder, VersionDecoder, OFFSET_INCREMENTS, }; //# sourceMappingURL=serde.js.map