UNPKG

mastercache

Version:

Multi-tier cache module for Node.js. Redis, Upstash, CloudfareKV, File, in-memory and others drivers

170 lines (143 loc) 4.71 kB
import type { TransportEncoder } from '@boringnode/bus/types/main'; import { CacheBusMessageType } from '../../types/bus'; import type { CacheBusMessage } from '../../types/bus'; /** * A Binary Encoder that encodes and decodes CacheBusMessage * * The encoding is as follows: * - The bus ID is encoded as a UTF8 string and directly appended to the resulting buffer. * Note that the length of the bus ID should be specified in the constructor. * * - The message type is encoded as a single byte, with 0x01 for 'Set' message, and 0x02 for a 'Delete' message * * - The keys are encoded as follows: * - A 4-byte big-endian integer representing the length of the key in bytes * - The key itself * * - These components are concatenated together in the order busId -> type -> keys * */ export class BinaryEncoder implements TransportEncoder { #busIdLength: number; /** * We assume the bus ID is a string of length 24 by default. * Because this is the default length of a cuid */ constructor(busIdLength = 24) { this.#busIdLength = busIdLength; } protected busMessageTypeToNum(type: CacheBusMessageType): number { if (type === CacheBusMessageType.Set) return 0x01; if (type === CacheBusMessageType.Clear) return 0x02; return 0x03; } protected numToBusMessageType(num: number): CacheBusMessageType { if (num === 0x01) return CacheBusMessageType.Set; if (num === 0x02) return CacheBusMessageType.Clear; return CacheBusMessageType.Delete; } /** * Encode the given message into a Buffer */ encode(message: any): string | Buffer { const payload = message.payload as Omit<CacheBusMessage, 'busId'>; /** * Compute the total size needed for storing the keys */ const totalKeysLength = payload.keys.reduce( (sum, key) => sum + 4 + Buffer.byteLength(key, 'utf8'), 0, ); const namespaceKeyLength = payload.namespace ? Buffer.byteLength(payload.namespace, 'utf8') : 0; const totalLength = this.#busIdLength + 1 + 4 + namespaceKeyLength + totalKeysLength; /** * Allocate a single buffer for the entire message */ const buffer = Buffer.alloc(totalLength); /** * 1. write the bus ID */ buffer.write(message.busId, 0, this.#busIdLength, 'utf8'); /** * 2. write the message type. 0x01 for 'Set' message, and 0x02 for a 'Delete' message */ buffer.writeUInt8(this.busMessageTypeToNum(payload.type), this.#busIdLength); /** * 3. Write the namespace */ let offset = this.#busIdLength + 1; /** * Write the length of the namespace key */ buffer.writeUInt32BE(namespaceKeyLength, offset); offset += 4; /** * Write the namespace itself, if not empty */ if (payload.namespace) { buffer.write(payload.namespace, offset, namespaceKeyLength, 'utf8'); offset += namespaceKeyLength; } /** * 4. Write the keys */ for (const key of payload.keys) { /** * Compute the length of the key in bytes and write it as a 4-byte big-endian integer */ const keyLength = Buffer.byteLength(key, 'utf8'); buffer.writeUInt32BE(keyLength, offset); offset += 4; /** * Write the key itself */ buffer.write(key, offset, keyLength, 'utf8'); offset += keyLength; } return buffer; } /** * Decode the given Buffer into a CacheBusMessage */ decode(data: string | Buffer): any { let offset = 0; const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data, 'binary'); /** * First #busIdLength bytes are the bus ID */ const busId = buffer.toString('utf8', offset, this.#busIdLength); offset += this.#busIdLength; /** * Then comes the message type as a single byte */ const typeValue = buffer.readUInt8(offset++); const type = this.numToBusMessageType(typeValue); /** * Then the namespace */ const namespaceKeyLength = buffer.readUInt32BE(offset); offset += 4; const namespace = namespaceKeyLength ? buffer.toString('utf8', offset, offset + namespaceKeyLength) : ''; offset += namespaceKeyLength; /** * Finally, the keys */ const keys = []; while (offset < buffer.length) { /** * First 4 bytes are the length of the key in bytes */ const keyLength = buffer.readUInt32BE(offset); offset += 4; /** * Then comes the key itself */ const key = buffer.toString('utf8', offset, offset + keyLength); offset += keyLength; keys.push(key); } return { busId, payload: { keys, type, namespace } }; } }