@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
250 lines • 12 kB
JavaScript
import { chunkyStore } from '@dstanesc/store-chunky-bytes';
import { EdgeDecoder, IndexDecoder, OFFSET_INCREMENTS, PropDecoder, PropValueDecoder, VertexDecoder, } from './serde';
import fastEqual from 'fast-deep-equal/es6';
const compactRanges = (recordSize, maxSize) => {
const ranges = [];
const add = (range) => {
if (ranges.length > 0) {
const prevRange = ranges[ranges.length - 1];
if (prevRange.end === range.start) {
const prevRange = ranges[ranges.length - 1];
prevRange.end = range.end;
}
else
ranges.push({ start: range.start, end: range.end });
}
else
ranges.push({ start: range.start, end: range.end });
};
const get = () => ranges;
const getAdjusted = () => {
return ranges.map((range) => {
const lastBefore = lastOffsetBefore(range.start);
const nextAfter = nextOffsetAfter(range.end) + recordSize;
const endActual = nextAfter > maxSize ? maxSize : nextAfter;
return { start: lastBefore, end: endActual };
});
};
const lastOffsetBefore = (offset) => {
const quotient = Math.floor(offset / recordSize);
return quotient * recordSize;
};
const nextOffsetAfter = (offset) => {
const quotient = Math.floor(offset / recordSize);
return (quotient + 1) * recordSize;
};
return { add, get, getAdjusted };
};
const deltaFactory = ({ linkCodec, valueCodec, }) => {
const { encode: linkEncode, decode: linkDecode } = linkCodec;
const { encode: valueEncode, decode: valueDecode } = valueCodec;
const { read } = chunkyStore();
const baselineDelta = async ({ baseRoot, baseIndex, baseStore, currentRoot, currentIndex, currentStore, }) => {
const { vertexRoot: baseVertexRoot, vertexOffset: baseVertexOffset, vertexIndex: baseVertexIndex, edgeRoot: baseEdgeRoot, edgeOffset: baseEdgeOffset, edgeIndex: baseEdgeIndex, propRoot: basePropRoot, propOffset: basePropOffset, propIndex: basePropIndex, valueRoot: baseValueRoot, valueOffset: baseValueOffset, valueIndex: baseValueIndex, indexRoot: baseIndexRoot, indexOffset: baseIndexOffset, indexIndex: baseIndexIndex, } = baseIndex;
const { vertexRoot: currentVertexRoot, vertexOffset: currentVertexOffset, vertexIndex: currentVertexIndex, edgeRoot: currentEdgeRoot, edgeOffset: currentEdgeOffset, edgeIndex: currentEdgeIndex, propRoot: currentPropRoot, propOffset: currentPropOffset, propIndex: currentPropIndex, valueRoot: currentValueRoot, valueOffset: currentValueOffset, valueIndex: currentValueIndex, indexRoot: currentIndexRoot, indexOffset: currentIndexOffset, indexIndex: currentIndexIndex, } = currentIndex;
const { added: verticesAdded, updated: verticesUpdated, updateBaseline: verticesBaseline, } = await baselineChangesRecords({
recordSize: OFFSET_INCREMENTS.VERTEX_INCREMENT,
baseRoot: baseVertexRoot,
baseIndex: baseVertexIndex,
baseStore,
baseValueRoot: undefined,
baseValueIndex: undefined,
currentRoot: currentVertexRoot,
currentIndex: currentVertexIndex,
currentStore,
currentValueRoot: undefined,
currentValueIndex: undefined,
recordsDecode: async ({ bytes, valueRoot, valueIndex, blockStore, }) => new VertexDecoder(bytes).read(),
});
const { added: edgesAdded, updated: edgesUpdated, updateBaseline: edgesBaseline, } = await baselineChangesRecords({
recordSize: OFFSET_INCREMENTS.EDGE_INCREMENT,
baseRoot: baseEdgeRoot,
baseIndex: baseEdgeIndex,
baseStore,
baseValueRoot: undefined,
baseValueIndex: undefined,
currentRoot: currentEdgeRoot,
currentIndex: currentEdgeIndex,
currentStore,
currentValueRoot: undefined,
currentValueIndex: undefined,
recordsDecode: async ({ bytes, valueRoot, valueIndex, blockStore, }) => new EdgeDecoder(bytes).read(),
});
const { added: propsAdded, updated: propsUpdated, updateBaseline: propsBaseline, } = await baselineChangesRecords({
recordSize: OFFSET_INCREMENTS.PROP_INCREMENT,
baseRoot: basePropRoot,
baseIndex: basePropIndex,
baseStore,
baseValueRoot: baseValueRoot,
baseValueIndex: baseValueIndex,
currentRoot: currentPropRoot,
currentIndex: currentPropIndex,
currentStore,
currentValueRoot: currentValueRoot,
currentValueIndex: currentValueIndex,
recordsDecode: async ({ bytes, valueRoot, valueIndex, blockStore, }) => {
const valueGet = async ({ root, index }, { propRef, ref, length }) => {
const bytes = await read(ref, length, {
root,
index,
decode: linkDecode,
get: blockStore.get,
});
return new PropValueDecoder(bytes, valueDecode).readValue({
propRef,
ref,
length,
});
};
return await new PropDecoder(bytes, (ref) => valueGet({ root: valueRoot, index: valueIndex }, ref)).read();
},
});
const { added: indicesAdded, updated: indicesUpdated, updateBaseline: indicesBaseline, } = await baselineChangesRecords({
recordSize: OFFSET_INCREMENTS.PROP_INCREMENT,
baseRoot: baseIndexRoot,
baseIndex: baseIndexIndex,
baseStore,
baseValueRoot: undefined,
baseValueIndex: undefined,
currentRoot: currentIndexRoot,
currentIndex: currentIndexIndex,
currentStore,
currentValueRoot: undefined,
currentValueIndex: undefined,
recordsDecode: async ({ bytes, valueRoot, valueIndex, blockStore, }) => await new IndexDecoder(bytes, linkDecode).read(),
});
return {
vertices: {
added: verticesAdded,
updated: verticesUpdated,
updateBaseline: verticesBaseline,
},
edges: {
added: edgesAdded,
updated: edgesUpdated,
updateBaseline: edgesBaseline,
},
props: {
added: propsAdded,
updated: propsUpdated,
updateBaseline: propsBaseline,
},
indices: {
added: indicesAdded,
updated: indicesUpdated,
updateBaseline: indicesBaseline,
},
};
};
const baselineChangesRecords = async ({ recordSize, baseRoot, baseIndex, baseStore, baseValueRoot, baseValueIndex, currentRoot, currentIndex, currentStore, currentValueRoot, currentValueIndex, recordsDecode, }) => {
const { startOffsets: baseStartOffsets, indexSize: baseIndexSize, byteArraySize: baseByteArraySize, } = baseIndex.indexStruct;
const { startOffsets: currentStartOffsets, indexSize: currentIndexSize, byteArraySize: currentByteArraySize, } = currentIndex.indexStruct;
const { add: addRange, get: getRanges, getAdjusted: getAdjustedRanges, } = compactRanges(recordSize, currentByteArraySize);
let currentBlock = undefined;
let updateMode = false;
for (const [currOffset, currCid] of currentStartOffsets) {
if (currentBlock !== undefined)
currentBlock.end = currOffset;
if (updateMode)
addRange(currentBlock);
if (currOffset <= baseByteArraySize) {
const baseCid = baseStartOffsets.get(currOffset);
if (baseCid !== undefined) {
if (currCid.toString() === baseCid.toString()) {
updateMode = false;
}
else {
updateMode = true;
}
}
else {
updateMode = true;
}
}
else {
updateMode = true;
}
currentBlock = { start: currOffset, end: undefined, cid: currCid };
}
if (updateMode) {
currentBlock.end = currentByteArraySize;
addRange(currentBlock);
}
const adjustedRanges = getAdjustedRanges();
const added = new Map();
const updated = new Map();
const updateBaseline = new Map();
for (const range of adjustedRanges) {
const currentRangeBytes = await read(range.start, range.end - range.start, {
root: currentRoot,
index: currentIndex,
decode: linkDecode,
get: currentStore.get,
});
const currentRecords = await recordsDecode({
bytes: currentRangeBytes,
valueRoot: currentValueRoot,
valueIndex: currentValueIndex,
blockStore: currentStore,
});
if (range.end <= baseByteArraySize) {
const baseRangeBytes = await read(range.start, range.end - range.start, {
root: baseRoot,
index: baseIndex,
decode: linkDecode,
get: baseStore.get,
});
const baseRecords = await recordsDecode({
bytes: baseRangeBytes,
valueRoot: baseValueRoot,
valueIndex: baseValueIndex,
blockStore: baseStore,
});
for (let i = 0; i < currentRecords.length; i++) {
const currentRecord = currentRecords[i];
const baseRecord = baseRecords[i];
if (!fastEqual(currentRecord, baseRecord)) {
updated.set(currentRecord.offset, currentRecord);
updateBaseline.set(currentRecord.offset, baseRecord);
}
}
}
else if (range.start < baseByteArraySize) {
const baseRangeBytes = await read(range.start, baseByteArraySize - range.start, {
root: baseRoot,
index: baseIndex,
decode: linkDecode,
get: baseStore.get,
});
const baseRecords = await recordsDecode({
bytes: baseRangeBytes,
valueRoot: baseValueRoot,
valueIndex: baseValueIndex,
blockStore: baseStore,
});
for (let i = 0; i < baseRecords.length; i++) {
const currentRecord = currentRecords[i];
const baseRecord = baseRecords[i];
if (!fastEqual(currentRecord, baseRecord)) {
updated.set(currentRecord.offset, currentRecord);
updateBaseline.set(currentRecord.offset, baseRecord);
}
}
for (let i = baseRecords.length; i < currentRecords.length; i++) {
const currentRecord = currentRecords[i];
added.set(currentRecord.offset, currentRecord);
}
}
else {
for (let i = 0; i < currentRecords.length; i++) {
const currentRecord = currentRecords[i];
added.set(currentRecord.offset, currentRecord);
}
}
}
return { added, updated, updateBaseline };
};
return { baselineDelta, baselineChangesRecords };
};
export { deltaFactory };
//# sourceMappingURL=delta.js.map