fix: dts TS类型修复
CI / test (18.x) (push) Successful in 10m0s
CI / test (20.x) (push) Successful in 9m58s
CI / test (22.x) (push) Successful in 9m56s
CI / test (24.x) (push) Successful in 9m54s

This commit is contained in:
thzxx
2026-07-27 22:06:45 +08:00
parent ce02cbefa0
commit d799e968ab
9 changed files with 708 additions and 109 deletions
+219 -30
View File
@@ -3072,6 +3072,114 @@ class OPFSBackend {
}
}
/**
* AriaEngine Page Header — 页面头部编解码
* @module engine/aria/page/header
*/
/**
* 初始化新页面的 Header。
*/
function initPageHeader(buf, pageId, type) {
const view = new DataView(buf);
view.setUint32(0, pageId, false);
view.setUint8(4, type);
view.setUint16(5, PAGE_HEADER_SIZE, false); // freeStart = header 之后
view.setUint16(7, buf.byteLength, false); // freeEnd = 页面末尾
view.setUint16(9, 0, false); // slotCount = 0
view.setUint32(11, 0, false); // checksum = 0
view.setUint8(15, 0);
}
/**
* AriaEngine File Manager — 页面文件管理 + PageIO 实现
* @module engine/aria/store/file_manager
*
* 负责管理页面文件的生命周期:分配/释放页面 ID,读写页面。
*/
// ---------------------------------------------------------------------------
// FileManager (implements PageIO)
// ---------------------------------------------------------------------------
class FileManager {
constructor(backend) {
this.nextPageId = 0;
this.metaLoaded = false;
this.dbName = '';
this.backend = backend;
}
/** 初始化:从存储中读取元数据 */
async init(dbName) {
this.dbName = dbName;
const meta = await this.backend.read('__aria_meta');
if (meta && meta instanceof ArrayBuffer && meta.byteLength >= 4) {
const view = new DataView(meta);
this.nextPageId = view.getUint32(0, false);
}
else {
this.nextPageId = 1;
await this.saveMeta();
}
this.metaLoaded = true;
}
// ---- PageIO ----
async readPage(pageId) {
const key = `pg_${pageId}`;
const data = await this.backend.read(key);
if (!data) {
// 第一次访问:创建新页面
return this.createEmptyPage(pageId, PageType.DATA);
}
// 确保大小正确
if (data.byteLength < PAGE_SIZE) {
const padded = new ArrayBuffer(PAGE_SIZE);
new Uint8Array(padded).set(new Uint8Array(data));
return padded;
}
return data;
}
async writePage(pageId, data) {
const key = `pg_${pageId}`;
await this.backend.write(key, data);
}
async allocatePageId() {
const id = this.nextPageId++;
await this.saveMeta();
return id;
}
async freePageId(_pageId) {
// 简化实现:不回收 pageId
const key = `pg_${_pageId}`;
await this.backend.delete(key);
}
// ---- 表页面分配 ----
/**
* 分配一个新的表元数据页面。
*/
async allocateTableRootPage() {
const pageId = await this.allocatePageId();
const data = new ArrayBuffer(PAGE_SIZE);
initPageHeader(data, pageId, PageType.META);
await this.writePage(pageId, data);
return pageId;
}
// ---- 辅助 ----
async saveMeta() {
const buf = new ArrayBuffer(8);
new DataView(buf).setUint32(0, this.nextPageId, false);
await this.backend.write('__aria_meta', buf);
}
createEmptyPage(pageId, type) {
const buf = new ArrayBuffer(PAGE_SIZE);
initPageHeader(buf, pageId, type);
return buf;
}
/** 清空所有数据 */
async clearAll() {
await this.backend.clear();
this.nextPageId = 1;
await this.saveMeta();
}
}
/**
* AriaEngine MVCC — 多版本并发控制
* @module engine/aria/transaction/mvcc
@@ -3272,24 +3380,6 @@ class MVCCManager {
}
}
/**
* AriaEngine Page Header — 页面头部编解码
* @module engine/aria/page/header
*/
/**
* 初始化新页面的 Header。
*/
function initPageHeader(buf, pageId, type) {
const view = new DataView(buf);
view.setUint32(0, pageId, false);
view.setUint8(4, type);
view.setUint16(5, PAGE_HEADER_SIZE, false); // freeStart = header 之后
view.setUint16(7, buf.byteLength, false); // freeEnd = 页面末尾
view.setUint16(9, 0, false); // slotCount = 0
view.setUint32(11, 0, false); // checksum = 0
view.setUint8(15, 0);
}
/**
* AriaEngine Page Format — 页面格式整合层
* @module engine/aria/page/format
@@ -3633,6 +3723,94 @@ class BufferPool {
}
}
/**
* AriaEngine LZ4 Compression — 简化 LZ4 压缩/解压
* @module engine/aria/compression/lz4
*
* Token 格式(1 字节):
* hi 4bit = litLen (0-15)
* lo 4bit = matchField (0-15, 实际匹配 = field+4)
*
* 字面量-匹配序列: [token] [litLen bytes] [2B LE offset]
* 末尾纯字面量: [token with lo=0] [litLen bytes] ← 仅在流末尾出现
*/
const MIN_MATCH = 4;
function compressLZ4(input) {
if (input.byteLength < MIN_MATCH)
return input;
const maxOut = input.byteLength + (input.byteLength >> 8) + 32;
const out = new Uint8Array(maxOut);
let si = 0, di = 0;
let litStart = 0;
while (si < input.byteLength) {
// 搜索最长 backward match
let bestLen = 0, bestOff = 0;
const searchStart = Math.max(0, si - 65535);
for (let p = searchStart; p < si; p++) {
let ml = 0;
while (si + ml < input.byteLength && p + ml < si &&
input[p + ml] === input[si + ml] && ml < 255)
ml++;
if (ml >= MIN_MATCH && ml > bestLen) {
bestLen = ml;
bestOff = si - p;
}
}
if (bestLen >= MIN_MATCH && (si - litStart) <= 15) {
// 有匹配 → 输出组合 token(字面量+匹配)
const litLen = si - litStart;
const matchField = Math.min(bestLen - MIN_MATCH, 15);
out[di++] = ((litLen & 0x0F) << 4) | (matchField & 0x0F);
for (let j = 0; j < litLen; j++)
out[di++] = input[litStart + j];
out[di++] = bestOff & 0xFF;
out[di++] = (bestOff >> 8) & 0xFF;
si += bestLen;
litStart = si;
}
else {
// 无匹配或字面量已满 15 → 继续累积(不单独输出,等下个匹配合并)
si++;
}
}
// 输出末尾纯字面量(matchField=0,无 offset
let remaining = si - litStart;
while (remaining > 0) {
const chunk = Math.min(remaining, 15);
out[di++] = (chunk & 0x0F) << 4; // lo=0 表示无匹配/无 offset
for (let j = 0; j < chunk; j++)
out[di++] = input[litStart + j];
remaining -= chunk;
litStart += chunk;
}
return di >= input.byteLength ? input : out.slice(0, di);
}
function decompressLZ4(input, originalSize) {
const out = new Uint8Array(originalSize);
let si = 0, di = 0;
while (si < input.byteLength && di < originalSize) {
const token = input[si++];
const litLen = (token >> 4) & 0x0F;
const matchField = token & 0x0F;
// 复制字面量
for (let i = 0; i < litLen && si < input.byteLength && di < originalSize; i++) {
out[di++] = input[si++];
}
if (di >= originalSize || si >= input.byteLength)
break;
// 非末尾 → 必有 offset + 匹配(即使 matchField==0 也复制 MIN_MATCH 字节)
if (si + 1 < input.byteLength) {
const offset = input[si++] | (input[si++] << 8);
const matchLen = matchField + MIN_MATCH;
for (let i = 0; i < matchLen && di < originalSize; i++) {
out[di] = out[di - offset];
di++;
}
}
}
return out;
}
/**
* AriaEngine — 自研页面式存储引擎主类
* @module engine/aria/index
@@ -3680,14 +3858,10 @@ class AriaEngine {
this.backend = new MemoryBackend();
}
await this.backend.open(dbName);
// 2a. 初始化 Buffer Pool(页面缓存)
const pageIO = {
readPage: async (pageId) => this.backend.read(`pg_${pageId}`),
writePage: async (pageId, data) => this.backend.write(`pg_${pageId}`, data),
allocatePageId: async () => Date.now(),
freePageId: async () => { },
};
this.bufferPool = new BufferPool(pageIO, this.config.bufferPoolPages);
// 2a. FileManager (PageIO 实现) + Buffer Pool
const fileManager = new FileManager(this.backend);
await fileManager.init(dbName);
this.bufferPool = new BufferPool(fileManager, this.config.bufferPoolPages);
// 2. 构建 SSTableStore
const sstableStore = this.createSSTableStore();
// 3. 初始化主 LSMPK 索引)
@@ -4026,7 +4200,7 @@ class AriaEngine {
async beginTransaction() {
if (this.currentTxnId)
throw new DatabaseError('Transaction already in progress', 'TX_ACTIVE');
this.currentTxnId = Date.now();
this.currentTxnId = this.mvcc.beginTransaction();
this.txnSnapshot = new Map();
this.wal.append({
type: WALRecordType.BEGIN,
@@ -4048,6 +4222,7 @@ class AriaEngine {
}
}
}
this.mvcc.commitTransaction(this.currentTxnId);
this.wal.append({
type: WALRecordType.COMMIT,
txnId: this.currentTxnId,
@@ -4061,6 +4236,7 @@ class AriaEngine {
async rollbackTransaction() {
if (!this.currentTxnId)
throw new DatabaseError('No active transaction', 'TX_NONE');
this.mvcc.rollbackTransaction(this.currentTxnId);
this.txnSnapshot = null;
this.wal.append({
type: WALRecordType.ROLLBACK,
@@ -4210,12 +4386,25 @@ class AriaEngine {
const META_KEY = '__aria_lsm_meta';
return {
save: async (id, data) => {
const buf = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
let buf = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
// 压缩(若启用)
if (this.config.compression) {
const compressed = compressLZ4(new Uint8Array(buf));
buf = compressed.buffer.slice(compressed.byteOffset, compressed.byteOffset + compressed.byteLength);
}
await this.backend.write(`sst_${id}`, buf);
},
load: async (id) => {
const buf = await this.backend.read(`sst_${id}`);
return buf ? new Uint8Array(buf) : null;
const raw = await this.backend.read(`sst_${id}`);
if (!raw)
return null;
let buf = new Uint8Array(raw);
// 解压(若启用)
if (this.config.compression) {
const decompressed = decompressLZ4(buf, buf.length * 2); // 估计原始大小
buf = decompressed;
}
return buf;
},
delete: async (id) => {
await this.backend.delete(`sst_${id}`);