hamok
Version:
Lightweight Distributed Object Storage on RAFT consensus algorithm
181 lines (138 loc) • 4.83 kB
text/typescript
export interface HamokEncoder<U, R> {
encode(data: U): R;
}
export interface HamokDecoder<U, R> {
decode(data: R): U;
}
const EMPTY_MAP = new Map();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const EMPTY_SET = new Set<any>();
export function createHamokCodec<U, R>(encode: (input: U) => R, decode: (input: R) => U): HamokCodec<U, R> {
return {
encode,
decode,
};
}
export function createHamokJsonBinaryCodec<T>(): HamokCodec<T, Uint8Array> {
const encoder = new TextEncoder();
const decoder = new TextDecoder();
return {
encode: (data: T) => {
const jsonString = JSON.stringify(data);
const encoded = encoder.encode(jsonString);
return encoded;
},
decode: (data: Uint8Array) => {
const jsonString = decoder.decode(data);
const decoded = JSON.parse(jsonString);
return decoded;
},
};
}
export function createHamokJsonStringCodec<T>(): HamokCodec<T, string> {
return {
encode: (data: T) => JSON.stringify(data),
decode: (data: string) => JSON.parse(data),
};
}
export interface HamokCodec<U, R> extends HamokEncoder<U, R>, HamokDecoder<U, R> {}
/* eslint-disable @typescript-eslint/no-explicit-any */
export class FacadedHamokCodec<TIn = any, TOut = any> implements HamokCodec<TIn, TOut> {
/* eslint-disable @typescript-eslint/no-explicit-any */
public static wrap<U = any, R = any>(codec: HamokCodec<U, R>): FacadedHamokCodec<U, R> {
const facadedCodec = new FacadedHamokCodec(codec);
return facadedCodec;
}
private _codec: HamokCodec<TIn, TOut>;
private constructor(firstEncoder: HamokCodec<TIn, TOut>) {
this._codec = firstEncoder;
}
encode(data: TIn): TOut {
return this._codec.encode(data);
}
decode(data: TOut): TIn {
return this._codec.decode(data);
}
public then<TNextOut = TOut>(nextCodec: HamokCodec<TOut, TNextOut>): FacadedHamokCodec<TIn, TNextOut> {
const actualCodec = this._codec;
return FacadedHamokCodec.wrap<TIn, TNextOut>({
encode(data: TIn): TNextOut {
const encodedValue: TOut = actualCodec.encode(data);
return nextCodec.encode(encodedValue);
},
decode(data: TNextOut): TIn {
const decodedValue: TOut = nextCodec.decode(data);
return actualCodec.decode(decodedValue);
},
});
}
}
export function encodeCollection<K>(keys: IterableIterator<K>, keyCodec: HamokCodec<K, Uint8Array>): Uint8Array[] {
const result: Uint8Array[] = [];
for (const key of keys) {
const encodedKey = keyCodec.encode(key);
result.push(encodedKey);
}
return result;
}
export function decodeCollection<K>(keys: IterableIterator<Uint8Array>, keyCodec: HamokCodec<K, Uint8Array>): K[] {
const result: K[] = [];
for (const key of keys) {
const decodedKey = keyCodec.decode(key);
result.push(decodedKey);
}
return result;
}
export function encodeSet<K>(keys: ReadonlySet<K>, keyCodec: HamokCodec<K, Uint8Array>): Uint8Array[] {
if (keys.size < 1) {
return [];
}
return encodeCollection(keys.values(), keyCodec);
}
export function decodeSet<K>(keys: Uint8Array[], keyCodec: HamokCodec<K, Uint8Array>): ReadonlySet<K> {
if (keys.length < 1) {
return EMPTY_SET;
}
return new Set(decodeCollection(keys.values(), keyCodec));
}
export function encodeMap<K, V>(entries: ReadonlyMap<K, V>, keyCodec: HamokCodec<K, Uint8Array>, valueCodec: HamokCodec<V, Uint8Array>): [Uint8Array[], Uint8Array[]] {
if (entries.size < 1) {
return [ [], [] ];
}
const encodedKeys: Uint8Array[] = [];
const encodedValues: Uint8Array[] = [];
for (const [ key, value ] of entries) {
const encodedKey = keyCodec.encode(key);
const encodedValue = valueCodec.encode(value);
encodedKeys.push(encodedKey);
encodedValues.push(encodedValue);
}
return [ encodedKeys, encodedValues ];
}
export function decodeMap<K, V>(keys: Uint8Array[], values: Uint8Array[], keyCodec: HamokCodec<K, Uint8Array>, valueCodec: HamokCodec<V, Uint8Array>): ReadonlyMap<K, V> {
if (keys.length < 1 || values.length < 1) {
return EMPTY_MAP;
}
const result = new Map<K, V>();
const length = Math.min(keys.length, values.length);
for (let i = 0; i < length; ++i) {
const key = keys[i];
const value = values[i];
const decodedKey = keyCodec.decode(key);
const decodedValue = valueCodec.decode(value);
result.set(decodedKey, decodedValue);
}
return result;
}
export function encodeNumber(value: number): Uint8Array {
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setInt32(0, value);
return new Uint8Array(buffer);
}
export function createStrToUint8ArrayCodec(encoding: BufferEncoding = 'utf-8'): HamokCodec<string, Uint8Array> {
return {
encode: (data: string) => Buffer.from(data, encoding),
decode: (data: Uint8Array) => Buffer.from(data).toString(encoding),
};
}