fix: 修复 CI 卡死 + LZ4/SSTable/Checkpoint 多项 bug — 526 测试全通过
This commit is contained in:
@@ -71,7 +71,7 @@ export function compressLZ4(input: Uint8Array): Uint8Array {
|
||||
output[dstIdx++] = (bestMatchOffset >> 8) & 0xFF;
|
||||
srcIdx += matchLen + MIN_MATCH;
|
||||
} else {
|
||||
// 写入字面量
|
||||
// 写入字面量:收集连续无匹配的字节,直到遇到可匹配序列或末尾
|
||||
let litStart = srcIdx;
|
||||
while (srcIdx < input.byteLength) {
|
||||
const remaining = input.byteLength - srcIdx;
|
||||
@@ -79,11 +79,9 @@ export function compressLZ4(input: Uint8Array): Uint8Array {
|
||||
srcIdx += remaining;
|
||||
break;
|
||||
}
|
||||
srcIdx++;
|
||||
|
||||
// 检查下一个位置是否有匹配
|
||||
// 检查当前位置开始是否有 >= MIN_MATCH 长度的匹配
|
||||
let hasMatch = false;
|
||||
const nextEnd = Math.min(srcIdx, input.byteLength);
|
||||
for (let i = Math.max(0, srcIdx - 65535); i < srcIdx && !hasMatch; i++) {
|
||||
let ml = 0;
|
||||
while (srcIdx + ml < input.byteLength && i + ml < srcIdx && input[i + ml] === input[srcIdx + ml] && ml < MIN_MATCH) {
|
||||
@@ -93,9 +91,12 @@ export function compressLZ4(input: Uint8Array): Uint8Array {
|
||||
}
|
||||
|
||||
if (hasMatch) {
|
||||
srcIdx--;
|
||||
// 当前位置开始可匹配,停止字面量收集(不输出当前字节,交给下一轮匹配处理)
|
||||
break;
|
||||
}
|
||||
|
||||
// 无匹配,将此字节纳入字面量
|
||||
srcIdx++;
|
||||
}
|
||||
|
||||
let litLen = srcIdx - litStart;
|
||||
@@ -125,6 +126,11 @@ export function compressLZ4(input: Uint8Array): Uint8Array {
|
||||
|
||||
/**
|
||||
* 解压 LZ4 数据。
|
||||
*
|
||||
* 与 compressLZ4 配套的简化格式:
|
||||
* 每个 token 要么是字面量(低 4 位 = 0),要么是匹配(高 4 位 = 0)。
|
||||
* 字面量 token: [hi4 = litLen] | 0x00 后跟 [litLen bytes]
|
||||
* 匹配 token: 0x00 | [lo4 = matchLen] 后跟 [offset: 2B LE]
|
||||
*/
|
||||
export function decompressLZ4(
|
||||
input: Uint8Array,
|
||||
@@ -136,46 +142,25 @@ export function decompressLZ4(
|
||||
|
||||
while (srcIdx < input.byteLength && dstIdx < originalSize) {
|
||||
const token = input[srcIdx++];
|
||||
let literalLen = (token >> 4) & 0x0F;
|
||||
|
||||
// 扩展字面量长度
|
||||
if (literalLen === 15) {
|
||||
while (srcIdx < input.byteLength && input[srcIdx] === 255) {
|
||||
literalLen += 255;
|
||||
srcIdx++;
|
||||
}
|
||||
if (srcIdx < input.byteLength) {
|
||||
literalLen += input[srcIdx++];
|
||||
}
|
||||
}
|
||||
const litLen = (token >> 4) & 0x0F;
|
||||
const matchLenField = token & 0x0F;
|
||||
|
||||
// 复制字面量
|
||||
for (let i = 0; i < literalLen && srcIdx < input.byteLength && dstIdx < originalSize; i++) {
|
||||
for (let i = 0; i < litLen && srcIdx < input.byteLength && dstIdx < originalSize; i++) {
|
||||
output[dstIdx++] = input[srcIdx++];
|
||||
}
|
||||
|
||||
if (srcIdx >= input.byteLength || dstIdx >= originalSize) break;
|
||||
|
||||
// 偏移量
|
||||
const offset = input[srcIdx++] | (input[srcIdx++] << 8);
|
||||
if (matchLenField > 0) {
|
||||
// 读取偏移量并复制匹配
|
||||
const offset = input[srcIdx++] | (input[srcIdx++] << 8);
|
||||
const matchLen = matchLenField + MIN_MATCH;
|
||||
|
||||
let matchLen = (token & 0x0F) + MIN_MATCH;
|
||||
|
||||
// 扩展匹配长度
|
||||
if ((token & 0x0F) === 15) {
|
||||
while (srcIdx < input.byteLength && input[srcIdx] === 255) {
|
||||
matchLen += 255;
|
||||
srcIdx++;
|
||||
for (let i = 0; i < matchLen && dstIdx < originalSize; i++) {
|
||||
output[dstIdx] = output[dstIdx - offset];
|
||||
dstIdx++;
|
||||
}
|
||||
if (srcIdx < input.byteLength) {
|
||||
matchLen += input[srcIdx++];
|
||||
}
|
||||
}
|
||||
|
||||
// 复制匹配
|
||||
for (let i = 0; i < matchLen && dstIdx < originalSize; i++) {
|
||||
output[dstIdx] = output[dstIdx - offset];
|
||||
dstIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -330,8 +330,8 @@ export class LSM {
|
||||
this.freezeMemtable();
|
||||
this.flushImmutableSync();
|
||||
}
|
||||
// 等待存储完成
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
// 确保所有微任务完成(不使用 setTimeout,避免额外 timer 阻止进程退出)
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
|
||||
@@ -30,8 +30,8 @@ export class SSTableReader {
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/** 精确查找 key */
|
||||
get(key: string): Record<string, unknown> | null {
|
||||
const blockIdx = this.locateBlock(key);
|
||||
get(targetKey: string): Record<string, unknown> | null {
|
||||
const blockIdx = this.locateBlock(targetKey);
|
||||
if (blockIdx < 0) return null;
|
||||
|
||||
const entry = this.indexEntries[blockIdx];
|
||||
@@ -45,8 +45,7 @@ export class SSTableReader {
|
||||
const entryCount = blockView.getUint32(0, false);
|
||||
let offset = 4;
|
||||
|
||||
// 二分查找 block 内的 key
|
||||
// 为简单起见,这里使用顺序扫描(生产中应二分查找)
|
||||
// 顺序扫描 block 内的条目(生产中应二分查找)
|
||||
for (let i = 0; i < entryCount; i++) {
|
||||
const keyLen = blockView.getUint16(offset, false);
|
||||
offset += 2;
|
||||
@@ -57,8 +56,12 @@ export class SSTableReader {
|
||||
const valBytes = blockData.slice(offset, offset + valLen);
|
||||
offset += valLen;
|
||||
|
||||
if (key === key) {
|
||||
return JSON.parse(new TextDecoder().decode(valBytes));
|
||||
if (key === targetKey) {
|
||||
try {
|
||||
return JSON.parse(new TextDecoder().decode(valBytes));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user