Files
dbmgr-hmos/data/src/main/ets/grid/LobBuffer.ets
T
2026-10-19 00:11:38 +08:00

118 lines
3.4 KiB
Plaintext

/**
* 大字段(LOB)分块缓冲(1.7.7)。
*
* 大文本/二进制按固定大小分块加载与编辑:
* - 只请求可视范围内的块,避免一次性读取整列;
* - 记录已加载块,read 拼接时若跨未加载块则抛错(提示先加载)。
*/
export class LobBuffer {
private readonly totalSize: number;
private readonly chunkSize: number;
private readonly loaded: Map<number, string> = new Map<number, string>();
constructor(totalSize: number, chunkSize: number) {
if (chunkSize < 1) {
throw new Error('chunkSize must be >= 1');
}
this.totalSize = totalSize < 0 ? 0 : totalSize;
this.chunkSize = chunkSize;
}
size(): number {
return this.totalSize;
}
chunkCount(): number {
return Math.ceil(this.totalSize / this.chunkSize);
}
chunkLength(index: number): number {
if (index < 0 || index >= this.chunkCount()) {
return 0;
}
const start: number = index * this.chunkSize;
const remaining: number = this.totalSize - start;
return remaining < this.chunkSize ? remaining : this.chunkSize;
}
/** 覆盖 [offset, offset+length) 需要的块号。 */
chunksFor(offset: number, length: number): number[] {
if (this.totalSize === 0 || length <= 0) {
return [];
}
const start: number = offset < 0 ? 0 : offset;
const end: number = Math.min(this.totalSize, start + length);
if (end <= start) {
return [];
}
const first: number = Math.floor(start / this.chunkSize);
const last: number = Math.floor((end - 1) / this.chunkSize);
const out: number[] = [];
for (let index = first; index <= last; index++) {
out.push(index);
}
return out;
}
setChunk(index: number, data: string): void {
if (index < 0 || index >= this.chunkCount()) {
return;
}
this.loaded.set(index, data);
}
isLoaded(index: number): boolean {
return this.loaded.has(index);
}
loadedChunks(): number {
return this.loaded.size;
}
loadedBytes(): number {
let bytes: number = 0;
this.loaded.forEach((data: string, index: number) => {
bytes += data.length;
});
return bytes;
}
/** 是否需要加载(覆盖范围内有未加载块)。 */
needsLoad(offset: number, length: number): boolean {
const chunks: number[] = this.chunksFor(offset, length);
for (let i = 0; i < chunks.length; i++) {
if (!this.loaded.has(chunks[i])) {
return true;
}
}
return false;
}
/** 读取区间;跨未加载块时抛错。 */
read(offset: number, length: number): string {
const start: number = offset < 0 ? 0 : offset;
const end: number = Math.min(this.totalSize, start + length);
if (end <= start) {
return '';
}
const chunks: number[] = this.chunksFor(start, end - start);
for (let i = 0; i < chunks.length; i++) {
if (!this.loaded.has(chunks[i])) {
throw new Error('chunk ' + chunks[i] + ' not loaded');
}
}
let out: string = '';
for (let i = 0; i < chunks.length; i++) {
out += this.loaded.get(chunks[i]) as string;
}
const sliceStart: number = start - chunks[0] * this.chunkSize;
const sliceEnd: number = sliceStart + (end - start);
return out.substring(sliceStart, sliceEnd);
}
/** 全部块已加载。 */
isComplete(): boolean {
return this.chunkCount() === 0 || this.loaded.size === this.chunkCount();
}
}