UNPKG

@deno/kv

Version:

A Deno KV client library optimized for Node.js.

159 lines (158 loc) 6.99 kB
// Copyright 2023 the Deno authors. All rights reserved. MIT license. import { checkEnd, computeBigintMinimumNumberOfBytes, flipBytes, } from "./bytes.js"; // https://github.com/apple/foundationdb/blob/main/design/tuple.md // limited to Uint8Array | string | number | bigint | boolean // https://github.com/denoland/deno/blob/main/ext/kv/codec.rs export function packKey(kvKey) { return new Uint8Array(kvKey.flatMap((v) => [...packKeyPart(v)])); } export function packKeyPart(kvKeyPart) { if (kvKeyPart instanceof Uint8Array) { return new Uint8Array([ Typecode.ByteString, ...encodeZeroWithZeroFF(kvKeyPart), 0, ]); } if (typeof kvKeyPart === "string") { return new Uint8Array([ Typecode.UnicodeString, ...encodeZeroWithZeroFF(new TextEncoder().encode(kvKeyPart)), 0, ]); } if (kvKeyPart === false) return new Uint8Array([Typecode.False]); if (kvKeyPart === true) return new Uint8Array([Typecode.True]); if (typeof kvKeyPart === "bigint") { const neg = kvKeyPart < 0; const abs = neg ? -kvKeyPart : kvKeyPart; const numBytes = BigInt(computeBigintMinimumNumberOfBytes(abs)); const typecode = neg ? (numBytes <= 8n ? (Typecode.IntegerOneByteNegative - Number(numBytes) + 1) : Typecode.IntegerArbitraryByteNegative) : (numBytes <= 8n ? (Typecode.IntegerOneBytePositive + Number(numBytes) - 1) : Typecode.IntegerArbitraryBytePositive); const bytes = [typecode]; if (numBytes > 8n) bytes.push(Number(numBytes)); for (let i = 0n; i < numBytes; i++) { const mask = 0xffn << 8n * (numBytes - i - 1n); const byte = Number((abs & mask) >> (8n * (numBytes - i - 1n))); bytes.push(byte); } if (neg) flipBytes(bytes, 1); return new Uint8Array(bytes); } if (typeof kvKeyPart === "number") { const sub = new Uint8Array(8); new DataView(sub.buffer).setFloat64(0, -Math.abs(kvKeyPart), false); if (kvKeyPart < 0) flipBytes(sub); return new Uint8Array([Typecode.FloatingPointDouble, ...sub]); } throw new Error(`Unsupported keyPart: ${typeof kvKeyPart} ${kvKeyPart}`); } export function unpackKey(bytes) { const rt = []; let pos = 0; while (pos < bytes.length) { const typecode = bytes[pos++]; if (typecode === Typecode.ByteString || typecode === Typecode.UnicodeString) { // Uint8Array or string const newBytes = []; while (pos < bytes.length) { const byte = bytes[pos++]; if (byte === 0 && bytes[pos] === 0xff) { pos++; } else if (byte === 0) { break; } newBytes.push(byte); } rt.push(typecode === Typecode.UnicodeString ? decoder.decode(new Uint8Array(newBytes)) : new Uint8Array(newBytes)); } else if (typecode >= Typecode.IntegerArbitraryByteNegative && typecode <= Typecode.IntegerArbitraryBytePositive) { // bigint const neg = typecode < Typecode.IntegerZero; const numBytes = BigInt((typecode === Typecode.IntegerArbitraryBytePositive || typecode === Typecode.IntegerArbitraryByteNegative) ? (neg ? (0xff - bytes[pos++]) : bytes[pos++]) : Math.abs(typecode - Typecode.IntegerZero)); let val = 0n; for (let i = 0n; i < numBytes; i++) { let byte = bytes[pos++]; if (neg) byte = 0xff - byte; val += BigInt(byte) << ((numBytes - i - 1n) * 8n); } rt.push(neg ? -val : val); } else if (typecode === Typecode.FloatingPointDouble) { // number const sub = new Uint8Array(bytes.subarray(pos, pos + 8)); const neg = sub[0] < 128; if (neg) flipBytes(sub); const num = -new DataView(sub.buffer).getFloat64(0, false); pos += 8; rt.push(neg ? -num : num); } else if (typecode === Typecode.False) { // boolean false rt.push(false); } else if (typecode === Typecode.True) { // boolean true rt.push(true); } else { throw new Error(`Unsupported typecode: ${typecode} in key: [${bytes.join(", ")}] after ${rt.join(", ")}`); } } checkEnd(bytes, pos); return rt; } // const decoder = new TextDecoder(); var Typecode; (function (Typecode) { Typecode[Typecode["ByteString"] = 1] = "ByteString"; Typecode[Typecode["UnicodeString"] = 2] = "UnicodeString"; Typecode[Typecode["IntegerArbitraryByteNegative"] = 11] = "IntegerArbitraryByteNegative"; Typecode[Typecode["IntegerEightByteNegative"] = 12] = "IntegerEightByteNegative"; Typecode[Typecode["IntegerSevenByteNegative"] = 13] = "IntegerSevenByteNegative"; Typecode[Typecode["IntegerSixByteNegative"] = 14] = "IntegerSixByteNegative"; Typecode[Typecode["IntegerFiveByteNegative"] = 15] = "IntegerFiveByteNegative"; Typecode[Typecode["IntegerFourByteNegative"] = 16] = "IntegerFourByteNegative"; Typecode[Typecode["IntegerThreeByteNegative"] = 17] = "IntegerThreeByteNegative"; Typecode[Typecode["IntegerTwoByteNegative"] = 18] = "IntegerTwoByteNegative"; Typecode[Typecode["IntegerOneByteNegative"] = 19] = "IntegerOneByteNegative"; Typecode[Typecode["IntegerZero"] = 20] = "IntegerZero"; Typecode[Typecode["IntegerOneBytePositive"] = 21] = "IntegerOneBytePositive"; Typecode[Typecode["IntegerTwoBytePositive"] = 22] = "IntegerTwoBytePositive"; Typecode[Typecode["IntegerThreeBytePositive"] = 23] = "IntegerThreeBytePositive"; Typecode[Typecode["IntegerFourBytePositive"] = 24] = "IntegerFourBytePositive"; Typecode[Typecode["IntegerFiveBytePositive"] = 25] = "IntegerFiveBytePositive"; Typecode[Typecode["IntegerSixBytePositive"] = 26] = "IntegerSixBytePositive"; Typecode[Typecode["IntegerSevenBytePositive"] = 27] = "IntegerSevenBytePositive"; Typecode[Typecode["IntegerEightBytePositive"] = 28] = "IntegerEightBytePositive"; Typecode[Typecode["IntegerArbitraryBytePositive"] = 29] = "IntegerArbitraryBytePositive"; Typecode[Typecode["FloatingPointDouble"] = 33] = "FloatingPointDouble"; Typecode[Typecode["False"] = 38] = "False"; Typecode[Typecode["True"] = 39] = "True"; })(Typecode || (Typecode = {})); function encodeZeroWithZeroFF(bytes) { const index = bytes.indexOf(0); return index < 0 ? bytes : new Uint8Array([...bytes].flatMap((v) => v === 0 ? [0, 0xff] : [v])); }