@zenithcore/core
Version:
Core functionality for ZenithKernel framework
407 lines (344 loc) • 11.4 kB
text/typescript
/**
* Enhanced OST Compression System for Zenith Framework
*
* Implements advanced features from archipelagoui:
* - Streaming support
* - Adaptive window sizing
* - Parallel processing
* - Memory optimization
* - ZSTD integration
*/
import { ZSTDCompress as zstdCompress, ZSTDDecompress as zstdDecompress } from 'simple-zstd';
export interface EnhancedOSTConfig {
compressionMethod: 'huffman' | 'zstd' | 'brotli' | 'adaptive';
compressionLevel: number;
windowSize: number;
adaptiveWindowSizing: boolean;
parallelProcessing: boolean;
maxWorkers: number;
streamingEnabled: boolean;
chunkSize: number;
memoryOptimization: boolean;
maxMemoryUsage: number; // in MB
}
export interface OSTSegment {
id: string;
data: Uint8Array;
hash: string;
compressionRatio: number;
originalSize: number;
compressedSize: number;
}
export interface CompressionMetrics {
totalTime: number;
compressionRatio: number;
throughput: number; // MB/s
memoryUsage: number;
segmentCount: number;
}
const DEFAULT_ENHANCED_CONFIG: EnhancedOSTConfig = {
compressionMethod: 'adaptive',
compressionLevel: 6,
windowSize: 32768,
adaptiveWindowSizing: true,
parallelProcessing: true,
maxWorkers: 4,
streamingEnabled: true,
chunkSize: 65536,
memoryOptimization: true,
maxMemoryUsage: 256
};
export class EnhancedOSTCompression {
private config: EnhancedOSTConfig;
private workerPool: Worker[] = [];
private compressionMetrics: CompressionMetrics = {
totalTime: 0,
compressionRatio: 0,
throughput: 0,
memoryUsage: 0,
segmentCount: 0
};
constructor(config: Partial<EnhancedOSTConfig> = {}) {
this.config = { ...DEFAULT_ENHANCED_CONFIG, ...config };
if (this.config.parallelProcessing) {
this.initializeWorkerPool();
}
}
/**
* Enhanced compression with streaming and parallel processing
*/
async compressStream(data: Uint8Array): Promise<{
segments: OSTSegment[];
metadata: {
config: EnhancedOSTConfig;
metrics: CompressionMetrics;
totalSize: number;
compressedSize: number;
};
}> {
const startTime = performance.now();
const originalSize = data.length;
// Step 1: Adaptive chunking
const chunks = this.adaptiveChunking(data);
// Step 2: Parallel compression
const segments = this.config.parallelProcessing
? await this.compressSegmentsConcurrently(chunks)
: await this.compressSegmentsSequentially(chunks);
// Step 3: Calculate metrics
const endTime = performance.now();
const totalCompressedSize = segments.reduce((acc, seg) => acc + seg.compressedSize, 0);
this.compressionMetrics = {
totalTime: endTime - startTime,
compressionRatio: originalSize / totalCompressedSize,
throughput: (originalSize / 1024 / 1024) / ((endTime - startTime) / 1000),
memoryUsage: this.getCurrentMemoryUsage(),
segmentCount: segments.length
};
return {
segments,
metadata: {
config: this.config,
metrics: this.compressionMetrics,
totalSize: originalSize,
compressedSize: totalCompressedSize
}
};
}
/**
* Enhanced decompression with streaming support
*/
async decompressStream(segments: OSTSegment[]): Promise<Uint8Array> {
const startTime = performance.now();
// Parallel decompression if enabled
const decompressedChunks = this.config.parallelProcessing
? await this.decompressSegmentsConcurrently(segments)
: await this.decompressSegmentsSequentially(segments);
// Merge chunks back into original data
const totalSize = decompressedChunks.reduce((acc, chunk) => acc + chunk.length, 0);
const result = new Uint8Array(totalSize);
let offset = 0;
for (const chunk of decompressedChunks) {
result.set(chunk, offset);
offset += chunk.length;
}
const endTime = performance.now();
console.debug(`Decompression completed in ${endTime - startTime}ms`);
return result;
}
/**
* Adaptive chunking based on content analysis
*/
private adaptiveChunking(data: Uint8Array): Uint8Array[] {
if (!this.config.adaptiveWindowSizing) {
return this.fixedChunking(data);
}
const chunks: Uint8Array[] = [];
let offset = 0;
while (offset < data.length) {
const windowSize = this.calculateOptimalWindowSize(data, offset);
const chunkEnd = Math.min(offset + windowSize, data.length);
chunks.push(data.slice(offset, chunkEnd));
offset = chunkEnd;
}
return chunks;
}
/**
* Fixed-size chunking for consistent performance
*/
private fixedChunking(data: Uint8Array): Uint8Array[] {
const chunks: Uint8Array[] = [];
const chunkSize = this.config.chunkSize;
for (let i = 0; i < data.length; i += chunkSize) {
chunks.push(data.slice(i, i + chunkSize));
}
return chunks;
}
/**
* Calculate optimal window size based on content entropy
*/
private calculateOptimalWindowSize(data: Uint8Array, offset: number): number {
const baseSize = this.config.windowSize;
const sampleSize = Math.min(1024, data.length - offset);
const sample = data.slice(offset, offset + sampleSize);
// Calculate entropy
const entropy = this.calculateEntropy(sample);
// Adjust window size based on entropy
// Higher entropy = smaller windows for better compression
// Lower entropy = larger windows for better performance
const entropyFactor = 1 - (entropy / 8); // Normalize to 0-1
const adjustedSize = Math.floor(baseSize * (0.5 + entropyFactor));
return Math.max(4096, Math.min(adjustedSize, 131072)); // 4KB to 128KB range
}
/**
* Calculate Shannon entropy of data
*/
private calculateEntropy(data: Uint8Array): number {
const frequencies = new Array(256).fill(0);
for (const byte of data) {
frequencies[byte]++;
}
let entropy = 0;
const length = data.length;
for (const freq of frequencies) {
if (freq > 0) {
const probability = freq / length;
entropy -= probability * Math.log2(probability);
}
}
return entropy;
}
/**
* Compress segments concurrently using worker pool
*/
private async compressSegmentsConcurrently(chunks: Uint8Array[]): Promise<OSTSegment[]> {
if (this.workerPool.length === 0) {
return this.compressSegmentsSequentially(chunks);
}
const segments: OSTSegment[] = [];
const promises: Promise<OSTSegment>[] = [];
for (let i = 0; i < chunks.length; i++) {
const worker = this.workerPool[i % this.workerPool.length];
promises.push(this.compressChunkWithWorker(chunks[i], i.toString(), worker));
}
const results = await Promise.all(promises);
return results;
}
/**
* Compress segments sequentially
*/
private async compressSegmentsSequentially(chunks: Uint8Array[]): Promise<OSTSegment[]> {
const segments: OSTSegment[] = [];
for (let i = 0; i < chunks.length; i++) {
const segment = await this.compressChunk(chunks[i], i.toString());
segments.push(segment);
}
return segments;
}
/**
* Compress a single chunk
*/
private async compressChunk(data: Uint8Array, id: string): Promise<OSTSegment> {
const originalSize = data.length;
let compressedData: Uint8Array;
// Choose compression method
const method = this.config.compressionMethod === 'adaptive'
? this.selectOptimalCompressionMethod(data)
: this.config.compressionMethod;
switch (method) {
case 'zstd':
compressedData = await zstdCompress(data, this.config.compressionLevel);
break;
case 'huffman':
compressedData = await this.huffmanCompress(data);
break;
case 'brotli':
compressedData = await this.brotliCompress(data);
break;
default:
compressedData = data;
}
const hash = await this.calculateHash(data);
const compressionRatio = originalSize / compressedData.length;
return {
id,
data: compressedData,
hash,
compressionRatio,
originalSize,
compressedSize: compressedData.length
};
}
/**
* Select optimal compression method based on data characteristics
*/
private selectOptimalCompressionMethod(data: Uint8Array): 'huffman' | 'zstd' | 'brotli' {
const entropy = this.calculateEntropy(data.slice(0, Math.min(1024, data.length)));
// High entropy data (> 7.5) - use ZSTD for speed
if (entropy > 7.5) return 'zstd';
// Medium entropy data (4-7.5) - use Brotli for balance
if (entropy > 4) return 'brotli';
// Low entropy data (< 4) - use Huffman for maximum compression
return 'huffman';
}
/**
* Initialize worker pool for parallel processing
*/
private initializeWorkerPool(): void {
// Worker pool implementation would go here
// For now, we'll use sequential processing
console.debug('Worker pool initialization skipped - using sequential processing');
}
/**
* Compress chunk using worker
*/
private async compressChunkWithWorker(data: Uint8Array, id: string, worker: Worker): Promise<OSTSegment> {
// Worker-based compression would go here
// For now, fall back to direct compression
return this.compressChunk(data, id);
}
/**
* Decompress segments concurrently
*/
private async decompressSegmentsConcurrently(segments: OSTSegment[]): Promise<Uint8Array[]> {
const promises = segments.map(segment => this.decompressSegment(segment));
return Promise.all(promises);
}
/**
* Decompress segments sequentially
*/
private async decompressSegmentsSequentially(segments: OSTSegment[]): Promise<Uint8Array[]> {
const results: Uint8Array[] = [];
for (const segment of segments) {
results.push(await this.decompressSegment(segment));
}
return results;
}
/**
* Decompress a single segment
*/
private async decompressSegment(segment: OSTSegment): Promise<Uint8Array> {
// Implementation would determine compression method from segment metadata
// For now, assume ZSTD
return zstdDecompress(segment.data);
}
/**
* Placeholder implementations for compression methods
*/
private async huffmanCompress(data: Uint8Array): Promise<Uint8Array> {
// Huffman compression implementation
return data; // Placeholder
}
private async brotliCompress(data: Uint8Array): Promise<Uint8Array> {
// Brotli compression implementation
return data; // Placeholder
}
/**
* Calculate hash of data
*/
private async calculateHash(data: Uint8Array): Promise<string> {
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
return Array.from(new Uint8Array(hashBuffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
/**
* Get current memory usage (placeholder)
*/
private getCurrentMemoryUsage(): number {
// Implementation would use process.memoryUsage() in Node.js
return 0;
}
/**
* Get compression metrics
*/
getMetrics(): CompressionMetrics {
return { ...this.compressionMetrics };
}
/**
* Cleanup resources
*/
dispose(): void {
this.workerPool.forEach(worker => worker.terminate());
this.workerPool = [];
}
}