xitdb
Version:
An immutable database
1,717 lines (1,655 loc) • 125 kB
JavaScript
// @bun
// src/tag.ts
var Tag;
((Tag2) => {
Tag2[Tag2["NONE"] = 0] = "NONE";
Tag2[Tag2["INDEX"] = 1] = "INDEX";
Tag2[Tag2["ARRAY_LIST"] = 2] = "ARRAY_LIST";
Tag2[Tag2["LINKED_ARRAY_LIST"] = 3] = "LINKED_ARRAY_LIST";
Tag2[Tag2["HASH_MAP"] = 4] = "HASH_MAP";
Tag2[Tag2["KV_PAIR"] = 5] = "KV_PAIR";
Tag2[Tag2["BYTES"] = 6] = "BYTES";
Tag2[Tag2["SHORT_BYTES"] = 7] = "SHORT_BYTES";
Tag2[Tag2["UINT"] = 8] = "UINT";
Tag2[Tag2["INT"] = 9] = "INT";
Tag2[Tag2["FLOAT"] = 10] = "FLOAT";
Tag2[Tag2["HASH_SET"] = 11] = "HASH_SET";
Tag2[Tag2["COUNTED_HASH_MAP"] = 12] = "COUNTED_HASH_MAP";
Tag2[Tag2["COUNTED_HASH_SET"] = 13] = "COUNTED_HASH_SET";
})(Tag ||= {});
function tagValueOf(n) {
if (n < 0 || n > 13) {
throw new Error(`Invalid tag value: ${n}`);
}
return n;
}
// src/slot.ts
class Slot {
static LENGTH = 9;
value;
tag;
full;
constructor(value = 0n, tag = 0 /* NONE */, full = false) {
this.value = typeof value === "bigint" ? value : BigInt(value);
this.tag = tag;
this.full = full;
}
withTag(tag) {
return new Slot(this.value, tag, this.full);
}
withFull(full) {
return new Slot(this.value, this.tag, full);
}
empty() {
return this.tag === 0 /* NONE */ && !this.full;
}
toBytes() {
const buffer = new ArrayBuffer(Slot.LENGTH);
const view = new DataView(buffer);
let tagInt = this.full ? 128 : 0;
tagInt = tagInt | this.tag;
view.setUint8(0, tagInt);
view.setBigInt64(1, this.value, false);
return new Uint8Array(buffer);
}
static fromBytes(bytes) {
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
const tagByte = view.getUint8(0);
const full = (tagByte & 128) !== 0;
const tag = tagValueOf(tagByte & 127);
const value = view.getBigInt64(1, false);
return new Slot(value, tag, full);
}
equals(other) {
return this.value === other.value && this.tag === other.tag && this.full === other.full;
}
}
// src/slot-pointer.ts
class SlotPointer {
position;
slot;
constructor(position, slot) {
this.position = position;
this.slot = slot;
}
withSlot(slot) {
return new SlotPointer(this.position, slot);
}
}
// src/exceptions.ts
class DatabaseException extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}
class NotImplementedException extends DatabaseException {
}
class UnreachableException extends DatabaseException {
}
class InvalidDatabaseException extends DatabaseException {
}
class InvalidVersionException extends DatabaseException {
}
class InvalidHashSizeException extends DatabaseException {
}
class KeyNotFoundException extends DatabaseException {
}
class WriteNotAllowedException extends DatabaseException {
}
class UnexpectedTagException extends DatabaseException {
}
class CursorNotWriteableException extends DatabaseException {
}
class ExpectedTxStartException extends DatabaseException {
}
class KeyOffsetExceededException extends DatabaseException {
}
class PathPartMustBeAtEndException extends DatabaseException {
}
class StreamTooLongException extends DatabaseException {
}
class EndOfStreamException extends DatabaseException {
}
class InvalidOffsetException extends DatabaseException {
}
class InvalidTopLevelTypeException extends DatabaseException {
}
class ExpectedUnsignedLongException extends DatabaseException {
}
class NoAvailableSlotsException extends DatabaseException {
}
class MustSetNewSlotsToFullException extends DatabaseException {
}
class EmptySlotException extends DatabaseException {
}
class ExpectedRootNodeException extends DatabaseException {
}
class InvalidFormatTagSizeException extends DatabaseException {
}
class UnexpectedWriterPositionException extends DatabaseException {
}
class MaxShiftExceededException extends DatabaseException {
}
class Uint64OverflowException extends DatabaseException {
}
class Int64OverflowException extends DatabaseException {
}
// src/writeable-data.ts
var UINT64_MAX = 2n ** 64n - 1n;
var INT64_MIN = -(2n ** 63n);
var INT64_MAX = 2n ** 63n - 1n;
class Uint {
value;
constructor(value) {
const bigintValue = BigInt(value);
if (bigintValue < 0n || bigintValue > UINT64_MAX) {
throw new Uint64OverflowException;
}
this.value = bigintValue;
}
}
class Int {
value;
constructor(value) {
const bigintValue = BigInt(value);
if (bigintValue < INT64_MIN || bigintValue > INT64_MAX) {
throw new Int64OverflowException;
}
this.value = bigintValue;
}
}
class Float {
value;
constructor(value) {
this.value = value;
}
}
class Bytes {
value;
formatTag;
constructor(value, formatTag) {
if (typeof value === "string") {
this.value = new TextEncoder().encode(value);
} else {
this.value = value;
}
if (formatTag === undefined || formatTag === null) {
this.formatTag = null;
} else if (typeof formatTag === "string") {
const encoded = new TextEncoder().encode(formatTag);
if (encoded.length !== 2) {
throw new InvalidFormatTagSizeException;
}
this.formatTag = encoded;
} else {
if (formatTag.length !== 2) {
throw new InvalidFormatTagSizeException;
}
this.formatTag = formatTag;
}
}
isShort() {
const totalSize = this.formatTag !== null ? 6 : 8;
if (this.value.length > totalSize)
return false;
for (const b of this.value) {
if (b === 0)
return false;
}
return true;
}
}
// src/core-memory.ts
class CoreMemory {
memory;
constructor() {
this.memory = new RandomAccessMemory;
}
reader() {
return this.memory;
}
writer() {
return this.memory;
}
length() {
return this.memory.size();
}
seek(pos) {
this.memory.seek(pos);
}
position() {
return this.memory.getPosition();
}
setLength(len) {
this.memory.setLength(len);
}
flush() {}
sync() {}
}
class RandomAccessMemory {
buffer;
_position = 0;
_count = 0;
constructor(initialSize = 1024) {
this.buffer = new Uint8Array(initialSize);
}
ensureCapacity(minCapacity) {
if (minCapacity > this.buffer.length) {
let newCapacity = this.buffer.length * 2;
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
const newBuffer = new Uint8Array(newCapacity);
newBuffer.set(this.buffer.subarray(0, this._count));
this.buffer = newBuffer;
}
}
size() {
return this._count;
}
seek(pos) {
if (pos > this._count) {
this._position = this._count;
} else {
this._position = pos;
}
}
getPosition() {
return this._position;
}
setLength(len) {
if (len === 0) {
this.reset();
} else {
if (len > this._count)
throw new Error("Cannot extend length");
this._count = len;
if (this._position > len) {
this._position = len;
}
}
}
reset() {
this._count = 0;
this._position = 0;
}
toByteArray() {
return this.buffer.slice(0, this._count);
}
write(data) {
const pos = this._position;
if (pos < this._count) {
const bytesBeforeEnd = Math.min(data.length, this._count - pos);
for (let i = 0;i < bytesBeforeEnd; i++) {
this.buffer[pos + i] = data[i];
}
if (bytesBeforeEnd < data.length) {
const bytesAfterEnd = data.length - bytesBeforeEnd;
this.ensureCapacity(this._count + bytesAfterEnd);
this.buffer.set(data.subarray(bytesBeforeEnd), this._count);
this._count += bytesAfterEnd;
}
} else {
this.ensureCapacity(this._count + data.length);
this.buffer.set(data, this._count);
this._count += data.length;
}
this._position = pos + data.length;
}
writeByte(v) {
this.write(new Uint8Array([v & 255]));
}
writeShort(v) {
const buffer = new ArrayBuffer(2);
const view = new DataView(buffer);
view.setInt16(0, v, false);
this.write(new Uint8Array(buffer));
}
writeLong(v) {
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setBigInt64(0, BigInt(v), false);
this.write(new Uint8Array(buffer));
}
readFully(b) {
const pos = this._position;
if (pos + b.length > this._count) {
throw new Error("End of stream");
}
b.set(this.buffer.subarray(pos, pos + b.length));
this._position = pos + b.length;
}
readByte() {
const bytes = new Uint8Array(1);
this.readFully(bytes);
return bytes[0];
}
readShort() {
const bytes = new Uint8Array(2);
this.readFully(bytes);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
return view.getInt16(0, false);
}
readInt() {
const bytes = new Uint8Array(4);
this.readFully(bytes);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
return view.getInt32(0, false);
}
readLong() {
const bytes = new Uint8Array(8);
this.readFully(bytes);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
return Number(view.getBigInt64(0, false));
}
}
// src/core-file.ts
import * as fs from "fs";
class CoreFile {
filePath;
_position = 0;
fd;
constructor(filePath) {
this.filePath = filePath;
try {
fs.accessSync(filePath);
} catch {
fs.writeFileSync(filePath, new Uint8Array(0));
}
this.fd = fs.openSync(filePath, "r+");
}
reader() {
return new FileDataReader(this);
}
writer() {
return new FileDataWriter(this);
}
length() {
const stats = fs.fstatSync(this.fd);
return stats.size;
}
seek(pos) {
this._position = pos;
}
position() {
return this._position;
}
setLength(len) {
fs.ftruncateSync(this.fd, len);
}
flush() {}
sync() {
fs.fsyncSync(this.fd);
}
[Symbol.dispose]() {
fs.closeSync(this.fd);
}
}
class FileDataReader {
core;
constructor(core) {
this.core = core;
}
readFully(b) {
const position = this.core.position();
fs.readSync(this.core.fd, b, 0, b.length, position);
this.core.seek(position + b.length);
}
readByte() {
const bytes = new Uint8Array(1);
this.readFully(bytes);
return bytes[0];
}
readShort() {
const bytes = new Uint8Array(2);
this.readFully(bytes);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
return view.getInt16(0, false);
}
readInt() {
const bytes = new Uint8Array(4);
this.readFully(bytes);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
return view.getInt32(0, false);
}
readLong() {
const bytes = new Uint8Array(8);
this.readFully(bytes);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
return Number(view.getBigInt64(0, false));
}
}
class FileDataWriter {
core;
constructor(core) {
this.core = core;
}
write(buffer) {
const position = this.core.position();
fs.writeSync(this.core.fd, buffer, 0, buffer.length, position);
this.core.seek(position + buffer.length);
}
writeByte(v) {
this.write(new Uint8Array([v & 255]));
}
writeShort(v) {
const buffer = new ArrayBuffer(2);
const view = new DataView(buffer);
view.setInt16(0, v, false);
this.write(new Uint8Array(buffer));
}
writeLong(v) {
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setBigInt64(0, BigInt(v), false);
this.write(new Uint8Array(buffer));
}
}
// src/core-buffered-file.ts
class CoreBufferedFile {
file;
constructor(filePath, bufferSize) {
this.file = new RandomAccessBufferedFile(filePath, bufferSize);
}
reader() {
return this.file;
}
writer() {
return this.file;
}
length() {
return this.file.length();
}
seek(pos) {
this.file.seek(pos);
}
position() {
return this.file.position();
}
setLength(len) {
this.file.setLength(len);
}
flush() {
this.file.flush();
}
sync() {
this.file.sync();
}
[Symbol.dispose]() {
this.file.file[Symbol.dispose]();
}
}
var DEFAULT_BUFFER_SIZE = 8 * 1024 * 1024;
class RandomAccessBufferedFile {
file;
memory;
bufferSize;
filePos;
memoryPos;
constructor(filePath, bufferSize = DEFAULT_BUFFER_SIZE) {
this.file = new CoreFile(filePath);
this.memory = new CoreMemory;
this.bufferSize = bufferSize;
this.filePos = 0;
this.memoryPos = 0;
}
seek(pos) {
if (pos > this.memoryPos + this.memory.length()) {
this.flush();
}
this.filePos = pos;
if (this.memory.length() === 0) {
this.memoryPos = pos;
}
}
length() {
return Math.max(this.memoryPos + this.memory.length(), this.file.length());
}
position() {
return this.filePos;
}
setLength(len) {
this.flush();
this.file.setLength(len);
this.filePos = Math.min(len, this.filePos);
}
flush() {
if (this.memory.length() > 0) {
this.file.seek(this.memoryPos);
this.file.writer().write(this.memory.memory.toByteArray());
this.memoryPos = 0;
this.memory.memory.reset();
}
}
sync() {
this.flush();
this.file.sync();
}
write(buffer) {
if (this.memory.length() + buffer.length > this.bufferSize) {
this.flush();
}
if (this.filePos >= this.memoryPos && this.filePos <= this.memoryPos + this.memory.length()) {
this.memory.seek(this.filePos - this.memoryPos);
this.memory.memory.write(buffer);
} else {
this.file.seek(this.filePos);
this.file.writer().write(buffer);
}
this.filePos += buffer.length;
}
writeByte(v) {
this.write(new Uint8Array([v & 255]));
}
writeShort(v) {
const buffer = new ArrayBuffer(2);
const view = new DataView(buffer);
view.setInt16(0, v & 65535, false);
this.write(new Uint8Array(buffer));
}
writeLong(v) {
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setBigInt64(0, BigInt(v), false);
this.write(new Uint8Array(buffer));
}
readFully(buffer) {
let pos = 0;
if (this.filePos < this.memoryPos) {
const sizeBeforeMem = Math.min(this.memoryPos - this.filePos, buffer.length);
const tempBuffer = new Uint8Array(sizeBeforeMem);
this.file.seek(this.filePos);
this.file.reader().readFully(tempBuffer);
buffer.set(tempBuffer, pos);
pos += sizeBeforeMem;
this.filePos += sizeBeforeMem;
}
if (pos === buffer.length)
return;
if (this.filePos >= this.memoryPos && this.filePos < this.memoryPos + this.memory.length()) {
const memPos = this.filePos - this.memoryPos;
const sizeInMem = Math.min(this.memory.length() - memPos, buffer.length - pos);
this.memory.seek(memPos);
const memBuffer = new Uint8Array(sizeInMem);
this.memory.memory.readFully(memBuffer);
buffer.set(memBuffer, pos);
pos += sizeInMem;
this.filePos += sizeInMem;
}
if (pos === buffer.length)
return;
if (this.filePos >= this.memoryPos + this.memory.length()) {
const sizeAfterMem = buffer.length - pos;
const tempBuffer = new Uint8Array(sizeAfterMem);
this.file.seek(this.filePos);
this.file.reader().readFully(tempBuffer);
buffer.set(tempBuffer, pos);
pos += sizeAfterMem;
this.filePos += sizeAfterMem;
}
}
readByte() {
const bytes = new Uint8Array(1);
this.readFully(bytes);
return bytes[0];
}
readShort() {
const bytes = new Uint8Array(2);
this.readFully(bytes);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
return view.getInt16(0, false);
}
readInt() {
const bytes = new Uint8Array(4);
this.readFully(bytes);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
return view.getInt32(0, false);
}
readLong() {
const bytes = new Uint8Array(8);
this.readFully(bytes);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
return Number(view.getBigInt64(0, false));
}
}
// src/hasher.ts
import { createHash } from "crypto";
class Hasher {
algorithm;
nodeAlgorithm;
id;
digestLength;
constructor(algorithm, id = 0) {
this.algorithm = algorithm;
this.id = id;
switch (algorithm) {
case "SHA-1":
this.digestLength = 20;
this.nodeAlgorithm = "sha1";
break;
case "SHA-256":
this.digestLength = 32;
this.nodeAlgorithm = "sha256";
break;
case "SHA-384":
this.digestLength = 48;
this.nodeAlgorithm = "sha384";
break;
case "SHA-512":
this.digestLength = 64;
this.nodeAlgorithm = "sha512";
break;
default:
throw new Error(`Unsupported hash algorithm: ${algorithm}`);
}
}
digest(data) {
return new Uint8Array(createHash(this.nodeAlgorithm).update(data).digest());
}
static stringToId(hashIdName) {
const bytes = new TextEncoder().encode(hashIdName);
if (bytes.length !== 4) {
throw new Error("Name must be exactly four bytes long");
}
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
return view.getInt32(0, false);
}
static idToString(id) {
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setInt32(0, id, false);
return new TextDecoder().decode(new Uint8Array(buffer));
}
}
// src/read-cursor.ts
class KeyValuePairCursor {
valueCursor;
keyCursor;
hash;
constructor(valueCursor, keyCursor, hash) {
this.valueCursor = valueCursor;
this.keyCursor = keyCursor;
this.hash = hash;
}
}
class ReadCursor {
slotPtr;
db;
constructor(slotPtr, db) {
this.slotPtr = slotPtr;
this.db = db;
}
slot() {
return this.slotPtr.slot;
}
readPath(path) {
try {
const slotPtr = this.db.readSlotPointer(0 /* READ_ONLY */, path, 0, this.slotPtr);
return new ReadCursor(slotPtr, this.db);
} catch (e) {
if (e instanceof KeyNotFoundException) {
return null;
}
throw e;
}
}
readPathSlot(path) {
try {
const slotPtr = this.db.readSlotPointer(0 /* READ_ONLY */, path, 0, this.slotPtr);
if (!slotPtr.slot.empty()) {
return slotPtr.slot;
} else {
return null;
}
} catch (e) {
if (e instanceof KeyNotFoundException) {
return null;
}
throw e;
}
}
readUint() {
if (this.slotPtr.slot.tag !== 8 /* UINT */) {
throw new UnexpectedTagException;
}
if (this.slotPtr.slot.value < 0n)
throw new ExpectedUnsignedLongException;
return Number(this.slotPtr.slot.value);
}
readInt() {
if (this.slotPtr.slot.tag !== 9 /* INT */) {
throw new UnexpectedTagException;
}
return Number(this.slotPtr.slot.value);
}
readFloat() {
if (this.slotPtr.slot.tag !== 10 /* FLOAT */) {
throw new UnexpectedTagException;
}
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setBigInt64(0, this.slotPtr.slot.value, false);
return view.getFloat64(0, false);
}
readBytes(maxSizeMaybe = null) {
const bytesObj = this.readBytesObject(maxSizeMaybe);
return bytesObj.value;
}
readBytesObject(maxSizeMaybe = null) {
const reader = this.db.core.reader();
switch (this.slotPtr.slot.tag) {
case 0 /* NONE */:
return new Bytes(new Uint8Array(0));
case 6 /* BYTES */: {
this.db.core.seek(Number(this.slotPtr.slot.value));
const valueSize = reader.readLong();
if (maxSizeMaybe !== null && valueSize > maxSizeMaybe) {
throw new StreamTooLongException;
}
const startPosition = this.db.core.position();
const value = new Uint8Array(valueSize);
reader.readFully(value);
let formatTag = null;
if (this.slotPtr.slot.full) {
this.db.core.seek(startPosition + valueSize);
formatTag = new Uint8Array(2);
reader.readFully(formatTag);
}
return new Bytes(value, formatTag);
}
case 7 /* SHORT_BYTES */: {
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setBigInt64(0, this.slotPtr.slot.value, false);
const bytes = new Uint8Array(buffer);
const totalSize = this.slotPtr.slot.full ? bytes.length - 2 : bytes.length;
let valueSize = 0;
for (const b of bytes) {
if (b === 0 || valueSize === totalSize)
break;
valueSize += 1;
}
if (maxSizeMaybe !== null && valueSize > maxSizeMaybe) {
throw new StreamTooLongException;
}
let formatTag = null;
if (this.slotPtr.slot.full) {
formatTag = bytes.slice(totalSize, bytes.length);
}
return new Bytes(bytes.slice(0, valueSize), formatTag);
}
default:
throw new UnexpectedTagException;
}
}
readKeyValuePair() {
const reader = this.db.core.reader();
if (this.slotPtr.slot.tag !== 5 /* KV_PAIR */) {
throw new UnexpectedTagException;
}
this.db.core.seek(Number(this.slotPtr.slot.value));
const kvPairBytes = new Uint8Array(KeyValuePair.length(this.db.header.hashSize));
reader.readFully(kvPairBytes);
const kvPair = KeyValuePair.fromBytes(kvPairBytes, this.db.header.hashSize);
const hashPos = Number(this.slotPtr.slot.value);
const keySlotPos = hashPos + this.db.header.hashSize;
const valueSlotPos = keySlotPos + Slot.LENGTH;
return new KeyValuePairCursor(new ReadCursor(new SlotPointer(valueSlotPos, kvPair.valueSlot), this.db), new ReadCursor(new SlotPointer(keySlotPos, kvPair.keySlot), this.db), kvPair.hash);
}
reader() {
const reader = this.db.core.reader();
switch (this.slotPtr.slot.tag) {
case 6 /* BYTES */: {
this.db.core.seek(Number(this.slotPtr.slot.value));
const size = reader.readLong();
const startPosition = this.db.core.position();
return new Reader(this, size, startPosition, 0);
}
case 7 /* SHORT_BYTES */: {
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setBigInt64(0, this.slotPtr.slot.value, false);
const bytes = new Uint8Array(buffer);
const totalSize = this.slotPtr.slot.full ? bytes.length - 2 : bytes.length;
let valueSize = 0;
for (const b of bytes) {
if (b === 0 || valueSize === totalSize)
break;
valueSize += 1;
}
const startPosition = this.slotPtr.position + 1;
return new Reader(this, valueSize, startPosition, 0);
}
default:
throw new UnexpectedTagException;
}
}
count() {
const reader = this.db.core.reader();
switch (this.slotPtr.slot.tag) {
case 0 /* NONE */:
return 0;
case 2 /* ARRAY_LIST */: {
this.db.core.seek(Number(this.slotPtr.slot.value));
const headerBytes = new Uint8Array(ArrayListHeader.LENGTH);
reader.readFully(headerBytes);
const header = ArrayListHeader.fromBytes(headerBytes);
return header.size;
}
case 3 /* LINKED_ARRAY_LIST */: {
this.db.core.seek(Number(this.slotPtr.slot.value));
const headerBytes = new Uint8Array(LinkedArrayListHeader.LENGTH);
reader.readFully(headerBytes);
const header = LinkedArrayListHeader.fromBytes(headerBytes);
return header.size;
}
case 6 /* BYTES */: {
this.db.core.seek(Number(this.slotPtr.slot.value));
return reader.readLong();
}
case 7 /* SHORT_BYTES */: {
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setBigInt64(0, this.slotPtr.slot.value, false);
const bytes = new Uint8Array(buffer);
const totalSize = this.slotPtr.slot.full ? bytes.length - 2 : bytes.length;
let size = 0;
for (const b of bytes) {
if (b === 0 || size === totalSize)
break;
size += 1;
}
return size;
}
case 12 /* COUNTED_HASH_MAP */:
case 13 /* COUNTED_HASH_SET */: {
this.db.core.seek(Number(this.slotPtr.slot.value));
return reader.readLong();
}
default:
throw new UnexpectedTagException;
}
}
*[Symbol.iterator]() {
const iterator = this.iterator();
while (iterator.hasNext()) {
const next = iterator.next();
if (next !== null) {
yield next;
}
}
}
iterator() {
const iterator = new CursorIterator(this);
iterator.init();
return iterator;
}
}
class Reader {
parent;
size;
startPosition;
relativePosition;
constructor(parent, size, startPosition, relativePosition) {
this.parent = parent;
this.size = size;
this.startPosition = startPosition;
this.relativePosition = relativePosition;
}
read(buffer) {
if (this.size < this.relativePosition)
throw new EndOfStreamException;
this.parent.db.core.seek(this.startPosition + this.relativePosition);
const readSize = Math.min(buffer.length, this.size - this.relativePosition);
if (readSize === 0)
return -1;
const reader = this.parent.db.core.reader();
const tempBuffer = new Uint8Array(readSize);
reader.readFully(tempBuffer);
buffer.set(tempBuffer);
this.relativePosition += readSize;
return readSize;
}
readFully(buffer) {
if (this.size < this.relativePosition || this.size - this.relativePosition < buffer.length) {
throw new EndOfStreamException;
}
this.parent.db.core.seek(this.startPosition + this.relativePosition);
const reader = this.parent.db.core.reader();
reader.readFully(buffer);
this.relativePosition += buffer.length;
}
readByte() {
const bytes = new Uint8Array(1);
this.readFully(bytes);
return bytes[0];
}
readShort() {
const readSize = 2;
const bytes = new Uint8Array(readSize);
this.readFully(bytes);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
return view.getInt16(0, false);
}
readInt() {
const readSize = 4;
const bytes = new Uint8Array(readSize);
this.readFully(bytes);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
return view.getInt32(0, false);
}
readLong() {
const readSize = 8;
const bytes = new Uint8Array(readSize);
this.readFully(bytes);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
return Number(view.getBigInt64(0, false));
}
seek(position) {
if (position > this.size) {
throw new InvalidOffsetException;
}
this.relativePosition = position;
}
}
class IteratorLevel {
position;
block;
index;
constructor(position, block, index) {
this.position = position;
this.block = block;
this.index = index;
}
}
class CursorIterator {
cursor;
size = 0;
index = 0;
stack = [];
nextCursorMaybe = null;
constructor(cursor) {
this.cursor = cursor;
}
init() {
switch (this.cursor.slotPtr.slot.tag) {
case 0 /* NONE */:
this.size = 0;
this.index = 0;
this.stack = [];
break;
case 2 /* ARRAY_LIST */: {
const position = Number(this.cursor.slotPtr.slot.value);
this.cursor.db.core.seek(position);
const reader = this.cursor.db.core.reader();
const headerBytes = new Uint8Array(ArrayListHeader.LENGTH);
reader.readFully(headerBytes);
const header = ArrayListHeader.fromBytes(headerBytes);
this.size = this.cursor.count();
this.index = 0;
this.stack = this.initStack(this.cursor, header.ptr, INDEX_BLOCK_SIZE);
break;
}
case 3 /* LINKED_ARRAY_LIST */: {
const position = Number(this.cursor.slotPtr.slot.value);
this.cursor.db.core.seek(position);
const reader = this.cursor.db.core.reader();
const headerBytes = new Uint8Array(LinkedArrayListHeader.LENGTH);
reader.readFully(headerBytes);
const header = LinkedArrayListHeader.fromBytes(headerBytes);
this.size = this.cursor.count();
this.index = 0;
this.stack = this.initStack(this.cursor, header.ptr, LINKED_ARRAY_LIST_INDEX_BLOCK_SIZE);
break;
}
case 4 /* HASH_MAP */:
case 11 /* HASH_SET */:
this.size = 0;
this.index = 0;
this.stack = this.initStack(this.cursor, Number(this.cursor.slotPtr.slot.value), INDEX_BLOCK_SIZE);
break;
case 12 /* COUNTED_HASH_MAP */:
case 13 /* COUNTED_HASH_SET */:
this.size = 0;
this.index = 0;
this.stack = this.initStack(this.cursor, Number(this.cursor.slotPtr.slot.value) + 8, INDEX_BLOCK_SIZE);
break;
default:
throw new UnexpectedTagException;
}
}
initStack(cursor, position, blockSize) {
cursor.db.core.seek(position);
const reader = cursor.db.core.reader();
const indexBlockBytes = new Uint8Array(blockSize);
reader.readFully(indexBlockBytes);
const indexBlock = new Array(SLOT_COUNT);
const slotSize = blockSize / SLOT_COUNT;
for (let i = 0;i < SLOT_COUNT; i++) {
const slotBytes = indexBlockBytes.slice(i * slotSize, i * slotSize + Slot.LENGTH);
indexBlock[i] = Slot.fromBytes(slotBytes);
}
return [new IteratorLevel(position, indexBlock, 0)];
}
hasNext() {
switch (this.cursor.slotPtr.slot.tag) {
case 0 /* NONE */:
return false;
case 2 /* ARRAY_LIST */:
return this.index < this.size;
case 3 /* LINKED_ARRAY_LIST */:
return this.index < this.size;
case 4 /* HASH_MAP */:
case 11 /* HASH_SET */:
case 12 /* COUNTED_HASH_MAP */:
case 13 /* COUNTED_HASH_SET */:
if (this.nextCursorMaybe === null) {
this.nextCursorMaybe = this.nextInternal(INDEX_BLOCK_SIZE);
}
return this.nextCursorMaybe !== null;
default:
return false;
}
}
next() {
switch (this.cursor.slotPtr.slot.tag) {
case 0 /* NONE */:
return null;
case 2 /* ARRAY_LIST */:
if (!this.hasNext())
return null;
this.index += 1;
return this.nextInternal(INDEX_BLOCK_SIZE);
case 3 /* LINKED_ARRAY_LIST */:
if (!this.hasNext())
return null;
this.index += 1;
return this.nextInternal(LINKED_ARRAY_LIST_INDEX_BLOCK_SIZE);
case 4 /* HASH_MAP */:
case 11 /* HASH_SET */:
case 12 /* COUNTED_HASH_MAP */:
case 13 /* COUNTED_HASH_SET */:
if (this.nextCursorMaybe !== null) {
const nextCursor = this.nextCursorMaybe;
this.nextCursorMaybe = null;
return nextCursor;
} else {
return this.nextInternal(INDEX_BLOCK_SIZE);
}
default:
throw new UnexpectedTagException;
}
}
nextInternal(blockSize) {
while (this.stack.length > 0) {
const level = this.stack[this.stack.length - 1];
if (level.index === level.block.length) {
this.stack.pop();
if (this.stack.length > 0) {
this.stack[this.stack.length - 1].index += 1;
}
continue;
} else {
const nextSlot = level.block[level.index];
if (nextSlot.tag === 1 /* INDEX */) {
const nextPos = Number(nextSlot.value);
this.cursor.db.core.seek(nextPos);
const reader = this.cursor.db.core.reader();
const indexBlockBytes = new Uint8Array(blockSize);
reader.readFully(indexBlockBytes);
const indexBlock = new Array(SLOT_COUNT);
const slotSize = blockSize / SLOT_COUNT;
for (let i = 0;i < SLOT_COUNT; i++) {
const slotBytes = indexBlockBytes.slice(i * slotSize, i * slotSize + Slot.LENGTH);
indexBlock[i] = Slot.fromBytes(slotBytes);
}
this.stack.push(new IteratorLevel(nextPos, indexBlock, 0));
continue;
} else {
this.stack[this.stack.length - 1].index += 1;
if (!nextSlot.empty()) {
const position = level.position + level.index * Slot.LENGTH;
return new ReadCursor(new SlotPointer(position, nextSlot), this.cursor.db);
} else {
continue;
}
}
}
}
return null;
}
}
// src/write-cursor.ts
class WriteKeyValuePairCursor extends KeyValuePairCursor {
valueCursor;
keyCursor;
constructor(valueCursor, keyCursor, hash) {
super(valueCursor, keyCursor, hash);
this.valueCursor = valueCursor;
this.keyCursor = keyCursor;
}
}
class WriteCursor extends ReadCursor {
constructor(slotPtr, db) {
super(slotPtr, db);
}
writePath(path) {
const slotPtr = this.db.readSlotPointer(1 /* READ_WRITE */, path, 0, this.slotPtr);
if (this.db.txStart === null) {
this.db.core.sync();
}
return new WriteCursor(slotPtr, this.db);
}
write(data) {
const cursor = this.writePath([new WriteData(data)]);
this.slotPtr = cursor.slotPtr;
}
writeIfEmpty(data) {
if (this.slotPtr.slot.empty()) {
this.write(data);
}
}
readKeyValuePair() {
const kvPairCursor = super.readKeyValuePair();
return new WriteKeyValuePairCursor(new WriteCursor(kvPairCursor.valueCursor.slotPtr, this.db), new WriteCursor(kvPairCursor.keyCursor.slotPtr, this.db), kvPairCursor.hash);
}
writer() {
const writer = this.db.core.writer();
const ptrPos = this.db.core.length();
this.db.core.seek(ptrPos);
writer.writeLong(0);
const startPosition = this.db.core.length();
return new Writer(this, 0, new Slot(ptrPos, 6 /* BYTES */), startPosition, 0);
}
*[Symbol.iterator]() {
const iterator = this.iterator();
while (iterator.hasNext()) {
const next = iterator.next();
if (next !== null) {
yield next;
}
}
}
iterator() {
const iterator = new WriteCursorIterator(this);
iterator.init();
return iterator;
}
}
class Writer {
parent;
size;
slot;
startPosition;
relativePosition;
formatTag = null;
constructor(parent, size, slot, startPosition, relativePosition) {
this.parent = parent;
this.size = size;
this.slot = slot;
this.startPosition = startPosition;
this.relativePosition = relativePosition;
}
write(buffer) {
if (this.size < this.relativePosition)
throw new EndOfStreamException;
this.parent.db.core.seek(this.startPosition + this.relativePosition);
const writer = this.parent.db.core.writer();
writer.write(buffer);
this.relativePosition += buffer.length;
if (this.relativePosition > this.size) {
this.size = this.relativePosition;
}
}
finish() {
const writer = this.parent.db.core.writer();
if (this.formatTag !== null) {
this.slot = this.slot.withFull(true);
const formatTagPos = this.parent.db.core.length();
this.parent.db.core.seek(formatTagPos);
if (this.startPosition + this.size !== formatTagPos)
throw new UnexpectedWriterPositionException;
writer.write(this.formatTag);
}
this.parent.db.core.seek(Number(this.slot.value));
writer.writeLong(this.size);
if (this.parent.slotPtr.position === null)
throw new CursorNotWriteableException;
const position = this.parent.slotPtr.position;
this.parent.db.core.seek(position);
writer.write(this.slot.toBytes());
this.parent.slotPtr = this.parent.slotPtr.withSlot(this.slot);
}
seek(position) {
if (position <= this.size) {
this.relativePosition = position;
}
}
}
class WriteCursorIterator extends CursorIterator {
constructor(cursor) {
super(cursor);
}
next() {
const readCursor = super.next();
if (readCursor !== null) {
return new WriteCursor(readCursor.slotPtr, readCursor.db);
} else {
return null;
}
}
}
// src/database.ts
var VERSION = 0;
var MAGIC_NUMBER = new Uint8Array([120, 105, 116]);
var BIT_COUNT = 4;
var SLOT_COUNT = 1 << BIT_COUNT;
var MASK = BigInt(SLOT_COUNT - 1);
var INDEX_BLOCK_SIZE = Slot.LENGTH * SLOT_COUNT;
var LINKED_ARRAY_LIST_SLOT_LENGTH = 8 + Slot.LENGTH;
var LINKED_ARRAY_LIST_INDEX_BLOCK_SIZE = LINKED_ARRAY_LIST_SLOT_LENGTH * SLOT_COUNT;
var MAX_BRANCH_LENGTH = 16;
var WriteMode;
((WriteMode2) => {
WriteMode2[WriteMode2["READ_ONLY"] = 0] = "READ_ONLY";
WriteMode2[WriteMode2["READ_WRITE"] = 1] = "READ_WRITE";
})(WriteMode ||= {});
class Header {
hashId;
hashSize;
version;
tag;
magicNumber;
static LENGTH = 12;
constructor(hashId, hashSize, version, tag, magicNumber) {
this.hashId = hashId;
this.hashSize = hashSize;
this.version = version;
this.tag = tag;
this.magicNumber = magicNumber;
}
toBytes() {
const buffer = new ArrayBuffer(Header.LENGTH);
const view = new DataView(buffer);
const arr = new Uint8Array(buffer);
arr.set(this.magicNumber, 0);
view.setUint8(3, this.tag);
view.setInt16(4, this.version, false);
view.setInt16(6, this.hashSize, false);
view.setInt32(8, this.hashId, false);
return arr;
}
static read(core) {
const reader = core.reader();
const magicNumber = new Uint8Array(3);
reader.readFully(magicNumber);
const tagByte = reader.readByte();
const tag = tagValueOf(tagByte & 127);
const version = reader.readShort();
const hashSize = reader.readShort();
const hashId = reader.readInt();
return new Header(hashId, hashSize, version, tag, magicNumber);
}
write(core) {
const writer = core.writer();
writer.write(this.toBytes());
}
validate() {
if (!arraysEqual(this.magicNumber, MAGIC_NUMBER)) {
throw new InvalidDatabaseException;
}
if (this.version > VERSION) {
throw new InvalidVersionException;
}
}
withTag(tag) {
return new Header(this.hashId, this.hashSize, this.version, tag, this.magicNumber);
}
}
class ArrayListHeader {
ptr;
size;
static LENGTH = 16;
constructor(ptr, size) {
this.ptr = ptr;
this.size = size;
}
toBytes() {
const buffer = new ArrayBuffer(ArrayListHeader.LENGTH);
const view = new DataView(buffer);
view.setBigInt64(0, BigInt(this.size), false);
view.setBigInt64(8, BigInt(this.ptr), false);
return new Uint8Array(buffer);
}
static fromBytes(bytes) {
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
const size = Number(view.getBigInt64(0, false));
checkLong(size);
const ptr = Number(view.getBigInt64(8, false));
checkLong(ptr);
return new ArrayListHeader(ptr, size);
}
withPtr(ptr) {
return new ArrayListHeader(ptr, this.size);
}
}
class TopLevelArrayListHeader {
fileSize;
parent;
static LENGTH = 8 + ArrayListHeader.LENGTH;
constructor(fileSize, parent) {
this.fileSize = fileSize;
this.parent = parent;
}
toBytes() {
const buffer = new ArrayBuffer(TopLevelArrayListHeader.LENGTH);
const view = new DataView(buffer);
const arr = new Uint8Array(buffer);
arr.set(this.parent.toBytes(), 0);
view.setBigInt64(ArrayListHeader.LENGTH, BigInt(this.fileSize), false);
return arr;
}
}
class LinkedArrayListHeader {
shift;
ptr;
size;
static LENGTH = 17;
constructor(shift, ptr, size) {
this.shift = shift;
this.ptr = ptr;
this.size = size;
}
toBytes() {
const buffer = new ArrayBuffer(LinkedArrayListHeader.LENGTH);
const view = new DataView(buffer);
view.setBigInt64(0, BigInt(this.size), false);
view.setBigInt64(8, BigInt(this.ptr), false);
view.setUint8(16, this.shift & 63);
return new Uint8Array(buffer);
}
static fromBytes(bytes) {
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
const size = Number(view.getBigInt64(0, false));
checkLong(size);
const ptr = Number(view.getBigInt64(8, false));
checkLong(ptr);
const shift = view.getUint8(16) & 63;
return new LinkedArrayListHeader(shift, ptr, size);
}
withPtr(ptr) {
return new LinkedArrayListHeader(this.shift, ptr, this.size);
}
}
class KeyValuePair {
valueSlot;
keySlot;
hash;
constructor(valueSlot, keySlot, hash) {
this.valueSlot = valueSlot;
this.keySlot = keySlot;
this.hash = hash;
}
static length(hashSize) {
return hashSize + Slot.LENGTH * 2;
}
toBytes() {
const buffer = new Uint8Array(KeyValuePair.length(this.hash.length));
buffer.set(this.hash, 0);
buffer.set(this.keySlot.toBytes(), this.hash.length);
buffer.set(this.valueSlot.toBytes(), this.hash.length + Slot.LENGTH);
return buffer;
}
static fromBytes(bytes, hashSize) {
const hash = bytes.slice(0, hashSize);
const keySlotBytes = bytes.slice(hashSize, hashSize + Slot.LENGTH);
const keySlot = Slot.fromBytes(keySlotBytes);
const valueSlotBytes = bytes.slice(hashSize + Slot.LENGTH, hashSize + Slot.LENGTH * 2);
const valueSlot = Slot.fromBytes(valueSlotBytes);
return new KeyValuePair(valueSlot, keySlot, hash);
}
}
class LinkedArrayListSlot2 {
size;
slot;
static LENGTH = 8 + Slot.LENGTH;
constructor(size, slot) {
this.size = size;
this.slot = slot;
}
withSize(size) {
return new LinkedArrayListSlot2(size, this.slot);
}
toBytes() {
const buffer = new ArrayBuffer(LinkedArrayListSlot2.LENGTH);
const view = new DataView(buffer);
const arr = new Uint8Array(buffer);
arr.set(this.slot.toBytes(), 0);
view.setBigInt64(Slot.LENGTH, BigInt(this.size), false);
return arr;
}
static fromBytes(bytes) {
const slotBytes = bytes.slice(0, Slot.LENGTH);
const slot = Slot.fromBytes(slotBytes);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
const size = Number(view.getBigInt64(Slot.LENGTH, false));
checkLong(size);
return new LinkedArrayListSlot2(size, slot);
}
}
class LinkedArrayListSlotPointer {
slotPtr;
leafCount;
constructor(slotPtr, leafCount) {
this.slotPtr = slotPtr;
this.leafCount = leafCount;
}
withSlotPointer(slotPtr) {
return new LinkedArrayListSlotPointer(slotPtr, this.leafCount);
}
}
class LinkedArrayListBlockInfo {
block;
i;
parentSlot;
constructor(block, i, parentSlot) {
this.block = block;
this.i = i;
this.parentSlot = parentSlot;
}
}
class HashMapGetKVPair {
hash;
kind = "kv_pair";
constructor(hash) {
this.hash = hash;
}
}
class HashMapGetKey {
hash;
kind = "key";
constructor(hash) {
this.hash = hash;
}
}
class HashMapGetValue {
hash;
kind = "value";
constructor(hash) {
this.hash = hash;
}
}
class ArrayListInit {
kind = "ArrayListInit";
readSlotPointer(db, isTopLevel, writeMode, path, pathI, slotPtr) {
if (writeMode === 0 /* READ_ONLY */)
throw new WriteNotAllowedException;
if (isTopLevel) {
const writer = db.core.writer();
if (db.header.tag === 0 /* NONE */) {
db.core.seek(Header.LENGTH);
const arrayListPtr = Header.LENGTH + TopLevelArrayListHeader.LENGTH;
writer.write(new TopLevelArrayListHeader(0, new ArrayListHeader(arrayListPtr, 0)).toBytes());
writer.write(new Uint8Array(INDEX_BLOCK_SIZE));
db.core.seek(0);
db.header = db.header.withTag(2 /* ARRAY_LIST */);
writer.write(db.header.toBytes());
}
const nextSlotPtr = slotPtr.withSlot(slotPtr.slot.withTag(2 /* ARRAY_LIST */));
return db.readSlotPointer(writeMode, path, pathI + 1, nextSlotPtr);
}
if (slotPtr.position === null)
throw new CursorNotWriteableException;
const position = slotPtr.position;
switch (slotPtr.slot.tag) {
case 0 /* NONE */: {
const writer = db.core.writer();
let arrayListStart = db.core.length();
db.core.seek(arrayListStart);
const arrayListPtr = arrayListStart + ArrayListHeader.LENGTH;
writer.write(new ArrayListHeader(arrayListPtr, 0).toBytes());
writer.write(new Uint8Array(INDEX_BLOCK_SIZE));
const nextSlotPtr = new SlotPointer(position, new Slot(arrayListStart, 2 /* ARRAY_LIST */));
db.core.seek(position);
writer.write(nextSlotPtr.slot.toBytes());
return db.readSlotPointer(writeMode, path, pathI + 1, nextSlotPtr);
}
case 2 /* ARRAY_LIST */: {
const reader = db.core.reader();
const writer = db.core.writer();
let arrayListStart = Number(slotPtr.slot.value);
if (db.txStart !== null) {
if (arrayListStart < db.txStart) {
db.core.seek(arrayListStart);
const headerBytes = new Uint8Array(ArrayListHeader.LENGTH);
reader.readFully(headerBytes);
const header = ArrayListHeader.fromBytes(headerBytes);
db.core.seek(header.ptr);
const arrayListIndexBlock = new Uint8Array(INDEX_BLOCK_SIZE);
reader.readFully(arrayListIndexBlock);
arrayListStart = db.core.length();
db.core.seek(arrayListStart);
const nextArrayListPtr = arrayListStart + ArrayListHeader.LENGTH;
const newHeader = header.withPtr(nextArrayListPtr);
writer.write(newHeader.toBytes());
writer.write(arrayListIndexBlock);
}
} else if (db.header.tag === 2 /* ARRAY_LIST */) {
throw new ExpectedTxStartException;
}
const nextSlotPtr = new SlotPointer(position, new Slot(arrayListStart, 2 /* ARRAY_LIST */));
db.core.seek(position);
writer.write(nextSlotPtr.slot.toBytes());
return db.readSlotPointer(writeMode, path, pathI + 1, nextSlotPtr);
}
default:
throw new UnexpectedTagException;
}
}
}
class ArrayListGet2 {
index;
kind = "ArrayListGet";
constructor(index) {
this.index = index;
}
readSlotPointer(db, isTopLevel, writeMode, path, pathI, slotPtr) {
const tag = isTopLevel ? db.header.tag : slotPtr.slot.tag;
switch (tag) {
case 0 /* NONE */:
throw new KeyNotFoundException;
case 2 /* ARRAY_LIST */:
break;
default:
throw new UnexpectedTagException;
}
const nextArrayListStart = Number(slotPtr.slot.value);
let index = this.index;
db.core.seek(nextArrayListStart);
const reader = db.core.reader();
const headerBytes = new Uint8Array(ArrayListHeader.LENGTH);
reader.readFully(headerBytes);
const header = ArrayListHeader.fromBytes(headerBytes);
if (index >= header.size || index < -header.size) {
throw new KeyNotFoundException;
}
const key = index < 0 ? header.size - Math.abs(index) : index;
const lastKey = header.size - 1;
const shift = lastKey < SLOT_COUNT ? 0 : Math.floor(Math.log(lastKey) / Math.log(SLOT_COUNT));
const finalSlotPtr = db.readArrayListSlot(header.ptr, key, shift, writeMode, isTopLevel);
return db.readSlotPointer(writeMode, path, pathI + 1, finalSlotPtr);
}
}
class ArrayListAppend {
kind = "ArrayListAppend";
readSlotPointer(db, isTopLevel, writeMode, path, pathI, slotPtr) {
if (writeMode === 0 /* READ_ONLY */)
throw new WriteNotAllowedException;
const tag = isTopLevel ? db.header.tag : slotPtr.slot.tag;
if (tag !== 2 /* ARRAY_LIST */)
throw new UnexpectedTagException;
const reader = db.core.reader();
const nextArrayListStart = Number(slotPtr.slot.value);
db.core.seek(nextArrayListStart);
const headerBytes = new Uint8Array(ArrayListHeader.LENGTH);
reader.readFully(headerBytes);
const origHeader = ArrayListHeader.fromBytes(headerBytes);
const appendResult = db.readArrayListSlotAppend(origHeader, writeMode, isTopLevel);
const finalSlotPtr = db.readSlotPointer(writeMode, path, pathI + 1, appendResult.slotPtr);
const writer = db.core.writer();
if (isTopLevel) {
db.core.flush();
const fileSize = db.core.length();
const header = new TopLevelArrayListHeader(fileSize, appendResult.header);
db.core.seek(nextArrayListStart);
writer.write(header.toBytes());
} else {
db.core.seek(nextArrayListStart);
writer.write(appendResult.header.toBytes());
}
return finalSlotPtr;
}
}
class ArrayListSlice {
size;
kind = "ArrayListSlice";
constructor(size) {
this.size = size;
}
readSlotPointer(db, isTopLevel, writeMode, path, pathI, slotPtr) {
if (writeMode === 0 /* READ_ONLY */)
throw new WriteNotAllowedException;
if (slotPtr.slot.tag !== 2 /* ARRAY_LIST */)
throw new UnexpectedTagException;
const reader = db.core.reader();
const nextArrayListStart = Number(slotPtr.slot.value);
db.core.seek(nextArrayListStart);
const headerBytes = new Uint8Array(ArrayListHeader.LENGTH);
reader.readFully(headerBytes);
const origHeader = ArrayListHeader.fromBytes(headerBytes);
const sliceHeader = db.readArrayListSlice(origHeader, this.size);
const finalSlotPtr = db.readSlotPointer(writeMode, path, pathI + 1, slotPtr);
const writer = db.core.writer();
db.core.seek(nextArrayListStart);
writer.write(sliceHeader.toBytes());
return finalSlotPtr;
}
}
class LinkedArrayListInit {
kind = "LinkedArrayListInit";
readSlotPointer(db, isTopLevel, writeMode, path, pathI, slotPtr) {
if (writeMode === 0 /* READ_ONLY */)
throw new WriteNotAllowedException;
if (isTopLevel)
throw new InvalidTopLevelTypeException;
if (slotPtr.position === null)
throw new CursorNotWriteableException;
const position = slotPtr.position;
switch (slotPtr.slot.tag) {
case 0 /* NONE */: {
const writer = db.core.writer();
const arrayListStart = db.core.length();
db.core.seek(arrayListStart);
const arrayListPtr = arrayListStart + LinkedArrayListHeader.LENGTH;
writer.write(new LinkedA