open-collaboration-protocol
Version:
Open Collaboration Protocol implementation, part of the Open Collaboration Tools project
81 lines (76 loc) • 3.24 kB
text/typescript
// ******************************************************************************
// Copyright 2024 TypeFox GmbH
// This program and the accompanying materials are made available under the
// terms of the MIT License, which is available in the project root.
// ******************************************************************************
import * as fflate from 'fflate';
export namespace Compression {
export type Algorithm = 'none' | 'gzip' | (string & {});
export function bestFit(algs: Algorithm[]): Algorithm;
export function bestFit(algs: Algorithm[][]): Algorithm;
export function bestFit(algs: Algorithm[][] | Algorithm[]): Algorithm {
if (algs.length === 0) {
return 'none';
} else if (typeof algs[0] === 'string') {
return algs[0];
} else {
const nested = algs as Algorithm[][];
const ranking = new Map<Algorithm, number>();
const first = algs[0];
for (let i = 0; i < first.length; i++) {
const alg = first[i];
if (alg !== 'none') {
ranking.set(alg, i);
}
}
for (let i = 1; i < nested.length; i++) {
const list = nested[i];
const found = new Set<Algorithm>();
for (let j = 0; j < list.length; j++) {
const alg = list[j];
if (alg !== 'none' && ranking.has(alg)) {
const rank = ranking.get(alg) ?? 0;
ranking.set(alg, rank + j);
found.add(alg);
}
}
for (const alg of ranking.keys()) {
if (!found.has(alg)) {
ranking.delete(alg);
}
}
if (ranking.size === 0) {
break;
}
}
let best: Algorithm = 'none';
let bestScore = Infinity;
for (const [alg, score] of ranking.entries()) {
if (score < bestScore) {
best = alg;
bestScore = score;
}
}
return best;
}
}
export async function compress(data: Uint8Array, alg: Algorithm): Promise<Uint8Array> {
if (alg === 'none' || alg === undefined) {
return data;
} else if (alg === 'gzip') {
// The sync version is way faster than the async version
// As each async call spawns a new worker - which is slow
// Our message size is small enough to not block the main thread
return fflate.gzipSync(data);
}
throw new Error('Unsupported compression algorithm: ' + alg);
}
export async function decompress(data: Uint8Array, alg: Algorithm): Promise<Uint8Array> {
if (alg === 'none' || alg === undefined) {
return data;
} else if (alg === 'gzip') {
return fflate.gunzipSync(data);
}
throw new Error('Unsupported compression algorithm: ' + alg);
}
}