JavaScript/TypeScript API
The Tamp WebAssembly package provides JavaScript and TypeScript bindings for the Tamp compression library.
Installation
npm install @brianpugh/tamp
Basic Usage
Simple Compression
Compress/decompress in-memory in a single operation:
import { compress, decompress, compressText, decompressText } from '@brianpugh/tamp';
// Compress binary data
const originalData = new TextEncoder().encode('Hello, World!');
const compressed = await compress(originalData);
const decompressed = await decompress(compressed);
// Compress text directly
const compressedText = await compressText('Hello, World!');
const restoredText = await decompressText(compressedText);
Configuration Options
Customize compression behavior with options:
const options = {
// Describes the size of the decompression buffer in bits.
// A 10-bit window represents a 1024-byte buffer.
// Must be in range [8, 15], representing [256, 32768] byte windows.
window: 12,
// Number of bits occupied in each plaintext symbol.
// For example, if ASCII text is being encoded, then we could set this
// value to 7 to slightly improve compression ratios.
// Must be in range [5, 8].
// For general use, 8 (the whole byte) is appropriate.
literal: 7,
// Enable extended format (RLE, extended match) for better compression ratios.
// The extended format provides better compression for typical data at the
// cost of slightly more complex encoding.
// Default: true
extended: true,
// Enable lazy matching to slightly improve compression (0.5-2.0%) ratios
// at the cost of 50-75% slower compression.
// Most embedded systems will **not** want to use this feature and disable it.
lazy_matching: true,
// To improve compression ratios for very short messages, a custom
// buffer initialization could be used.
// For most use-cases, set this to null.
dictionary: null
};
const compressed = await compress(data, options);
const decompressed = await decompress(compressed, options);
Streaming for Large Data
Use streaming compression for processing large files or data that does not fit in memory:
import { TampCompressor, TampDecompressor, using } from '@brianpugh/tamp';
// Automatic resource management (recommended)
const compressedChunks = [];
await using(new TampCompressor(options), async (compressor) => {
for (const chunk of dataChunks) {
const compressedChunk = await compressor.compress(chunk);
if (compressedChunk.length > 0) {
compressedChunks.push(compressedChunk);
}
}
// Flush remaining data
const finalChunk = await compressor.flush();
if (finalChunk.length > 0) {
compressedChunks.push(finalChunk);
}
});
// Dictionary reset mid-stream; useful if we need to append
// data to a compression stream.
await using(new TampCompressor({ ...options, dictionary_reset: true }), async (compressor) => {
// Compress first segment
compressedChunks.push(await compressor.compress(segment1));
// Reset dictionary
const resetBytes = await compressor.resetDictionary();
if (resetBytes.length > 0) {
compressedChunks.push(resetBytes);
}
// Continue compressing with a fresh dictionary
compressedChunks.push(await compressor.compress(segment2));
compressedChunks.push(await compressor.flush());
});
Web Streams Integration
Use with the Web Streams API for seamless integration with modern web applications:
import { TampCompressionStream, TampDecompressionStream } from '@brianpugh/tamp/streams';
// Compress a file stream
const fileStream = file.stream();
const compressionStream = new TampCompressionStream(options);
const compressedStream = fileStream.pipeThrough(compressionStream);
// Save compressed data
const response = new Response(compressedStream);
const compressedBlob = await response.blob();
// Or chain compression and decompression
const decompressionStream = new TampDecompressionStream(options);
const roundTripStream = fileStream
.pipeThrough(compressionStream)
.pipeThrough(decompressionStream);
Error Handling
The library throws specific error types:
TampError- Base error classCompressionError- Compression operation errorsDecompressionError- Decompression operation errorsExcessBitsError- Data exceeds literal size limits
Custom Configuration
Configure compression parameters by passing in options:
const options = {
window: 12, // Larger window for (usually) better compression
literal: 7, // ASCII text only requires 7 bits.
extended: true, // Enable extended format (RLE, extended match)
lazy_matching: true // Better compression ratios; slower to compress
};
const compressed = await compress(data, options);
const decompressed = await decompress(compressed, options);
Progress Callbacks
Monitor compression progress for large files using progress callbacks. Progress callbacks receive a progressInfo object with detailed compression metrics:
// Basic progress monitoring
const progressCallback = (progressInfo) => {
console.log(`${progressInfo.percent.toFixed(1)}% complete`);
console.log(`Speed: ${(progressInfo.bytesPerSecond / 1024).toFixed(1)} KB/s`);
console.log(`ETA: ${progressInfo.estimatedTimeRemaining.toFixed(1)}s`);
};
const compressed = await compress(largeData, options, progressCallback);
// Conditional abortion example
const abortingCallback = (progressInfo) => {
console.log(`Progress: ${progressInfo.percent.toFixed(1)}%`);
// Throw error to abort compression after 50%
if (progressInfo.percent > 50) {
throw new Error('Compression cancelled by user');
}
};
try {
const compressed = await compress(data, options, abortingCallback);
} catch (error) {
console.log('Compression was aborted by callback');
}
Progress Info Object Fields:
interface ProgressInfo {
bytesProcessed: number; // Number of input bytes processed so far
totalBytes: number; // Total number of input bytes
percent: number; // Completion percentage (0-100)
bytesPerSecond: number; // Processing speed in bytes/second
estimatedTimeRemaining: number; // Estimated seconds remaining
chunksProcessed: number; // Number of chunks processed
elapsedTime: number; // Total elapsed time in seconds
chunkSize: number; // Size of current chunk being processed
outputSize: number; // Size of output written for current chunk
}
The decompressor does not have a progress callback since the operation is usually very very fast.
Utility Functions
WebAssembly Initialization
The WebAssembly module initializes automatically, but you can preload it explicitly:
import { initialize } from '@brianpugh/tamp';
// Preload WebAssembly module (optional)
await initialize();
Dictionary Utilities
Create and manage custom dictionaries for improved compression:
import { initializeDictionary, computeMinPatternSize } from '@brianpugh/tamp';
// Initialize a dictionary buffer with default values
const dictionary = await initializeDictionary(1024); // Must be power of 2
const dict6bit = await initializeDictionary(1024, 6); // Tuned for 6-bit literals
// Compute minimum pattern size for given parameters
const minPatternSize = await computeMinPatternSize(12, 8); // window=12, literal=8
Stream Helpers
Additional utilities for working with streams:
import {
compressStream,
decompressStream,
createReadableStream,
collectStream
} from '@brianpugh/tamp';
// Convert Uint8Array to ReadableStream
const data = new TextEncoder().encode('Hello, World!');
const readableStream = createReadableStream(data, 1024); // 1024 byte chunks
// Compress a readable stream
const compressedStream = compressStream(readableStream, options);
// Decompress a readable stream
const decompressedStream = decompressStream(compressedStream, options);
// Collect stream back to Uint8Array
const result = await collectStream(decompressedStream);
Node.js and Browser Support
The package supports both Node.js (>= 14.0.0) and modern browsers with WebAssembly support. It provides CommonJS and ES Module builds.
// ES Modules
import { compress } from '@brianpugh/tamp';
// CommonJS
const { compress } = require('@brianpugh/tamp');