/** * AriaEngine Slot Directory — 页面内行槽位管理 * @module engine/aria/page/slot * * Slot 目录从页面 Header 之后向下增长,每条记录 4 字节。 */ import { PAGE_HEADER_SIZE, SLOT_ENTRY_SIZE, PAGE_SIZE, type SlotEntry, } from '../types'; // --------------------------------------------------------------------------- // Slot 读写 // --------------------------------------------------------------------------- /** * 读取 slot 号对应的 SlotEntry。 */ export function getSlotEntry(buf: ArrayBuffer, slotIndex: number): SlotEntry { const offset = PAGE_HEADER_SIZE + slotIndex * SLOT_ENTRY_SIZE; const view = new DataView(buf); return { offset: view.getUint16(offset, false), length: view.getUint16(offset + 2, false), }; } /** * 写入 slot 号对应的 SlotEntry。 */ export function setSlotEntry( buf: ArrayBuffer, slotIndex: number, entry: SlotEntry, ): void { const offset = PAGE_HEADER_SIZE + slotIndex * SLOT_ENTRY_SIZE; const view = new DataView(buf); view.setUint16(offset, entry.offset, false); view.setUint16(offset + 2, entry.length, false); } /** * 获取页面中所有 slot 条目。 */ export function getAllSlots( buf: ArrayBuffer, slotCount: number, ): SlotEntry[] { const entries: SlotEntry[] = []; for (let i = 0; i < slotCount; i++) { entries.push(getSlotEntry(buf, i)); } return entries; } // --------------------------------------------------------------------------- // Slot 空间计算 // --------------------------------------------------------------------------- /** 获取 slot 目录占用的总字节数 */ export function getSlotDirectorySize(slotCount: number): number { return slotCount * SLOT_ENTRY_SIZE; } /** 获取可用空闲空间(字节) */ export function getFreeSpace(buf: ArrayBuffer): number { const view = new DataView(buf); const freeStart = view.getUint16(5, false); // slot 区之后 const freeEnd = view.getUint16(7, false); // 数据区之前 return freeEnd - freeStart; } /** 检查是否有足够空间存放长度为 len 的行 */ export function hasEnoughSpace(buf: ArrayBuffer, len: number): boolean { const slotCount = new DataView(buf).getUint16(9, false); const neededSlotSize = (slotCount + 1) * SLOT_ENTRY_SIZE; const freeStart = PAGE_HEADER_SIZE + neededSlotSize; const freeEnd = new DataView(buf).getUint16(7, false); return freeEnd - freeStart >= len; } // --------------------------------------------------------------------------- // 插入 / 删除 slot // --------------------------------------------------------------------------- /** * 在页面中分配一个 slot 并写入行数据。 * 返回分配的 slot 索引,失败返回 -1。 */ export function allocateSlot( buf: ArrayBuffer, rowData: Uint8Array, ): number { const view = new DataView(buf); const slotCount = view.getUint16(9, false); const neededSlotSpace = (slotCount + 1) * SLOT_ENTRY_SIZE; const freeStart = PAGE_HEADER_SIZE + neededSlotSpace; const freeEnd = view.getUint16(7, false); if (freeEnd - freeStart < rowData.byteLength) { return -1; // 空间不足 } // 将数据放入页面底部 const dataOffset = freeEnd - rowData.byteLength; const dest = new Uint8Array(buf, dataOffset, rowData.byteLength); dest.set(rowData); // 写入 slot 条目 setSlotEntry(buf, slotCount, { offset: dataOffset, length: rowData.byteLength }); // 更新 header view.setUint16(5, freeStart, false); // freeStart view.setUint16(7, dataOffset, false); // freeEnd view.setUint16(9, slotCount + 1, false); // slotCount return slotCount; } /** * 从页面中删除指定 slot 的数据(标记为无效,offset 置 0)。 * 注:简化实现,不做 slot 压缩。 */ export function freeSlot(buf: ArrayBuffer, slotIndex: number): void { setSlotEntry(buf, slotIndex, { offset: 0, length: 0 }); } /** * 压缩页面槽位:移除已删除 slot,整理碎片空间。 * 将有效数据紧凑排列,释放空洞。 */ export function compactSlots(buf: ArrayBuffer): number { const view = new DataView(buf); const slotCount = view.getUint16(9, false); if (slotCount === 0) return 0; // 收集有效 slot(offset>0 的) const validSlots: { index: number; offset: number; length: number; data: Uint8Array }[] = []; for (let i = 0; i < slotCount; i++) { const entry = getSlotEntry(buf, i); if (entry.offset > 0 && entry.length > 0) { const data = new Uint8Array(buf, entry.offset, entry.length); validSlots.push({ index: i, offset: entry.offset, length: entry.length, data: new Uint8Array(data) }); } } if (validSlots.length === slotCount) return 0; // 无碎片 // 从页面底部重新紧凑排列 let dataEnd = PAGE_SIZE; const newSlots: { offset: number; length: number }[] = []; for (let i = validSlots.length - 1; i >= 0; i--) { const s = validSlots[i]; dataEnd -= s.length; new Uint8Array(buf).set(s.data, dataEnd); newSlots.unshift({ offset: dataEnd, length: s.length }); } // 重写 slot directory view.setUint16(9, validSlots.length, false); // slotCount view.setUint16(7, dataEnd, false); // freeEnd for (let i = 0; i < validSlots.length; i++) { setSlotEntry(buf, i, newSlots[i]); } // 清除剩余 slot 条目 for (let i = validSlots.length; i < slotCount; i++) { setSlotEntry(buf, i, { offset: 0, length: 0 }); } return slotCount - validSlots.length; // 回收的 slot 数量 } /** * 读取指定 slot 的行数据。 */ export function readSlotData(buf: ArrayBuffer, slotIndex: number): Uint8Array | null { const entry = getSlotEntry(buf, slotIndex); if (entry.offset === 0 || entry.length === 0) return null; return new Uint8Array(buf, entry.offset, entry.length); }