@deno/kv
Version:
A Deno KV client library optimized for Node.js.
179 lines (178 loc) • 6.23 kB
JavaScript
;
// Copyright 2023 the Deno authors. All rights reserved. MIT license.
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnknownV8 = exports.encodeV8 = exports.decodeV8 = exports.makeLimitedV8Serializer = void 0;
// https://chromium.googlesource.com/v8/v8/+/refs/heads/main/src/objects/value-serializer.cc
const bytes_js_1 = require("./bytes.js");
/**
* Returns encodeV8, decodeV8 functions that support V8-compatible serialization for limited set of types (strings, null, undefined, boolean).
*
* Consider using JSON.parse/stringify to marshall values to save when using this serializer.
*
* All other values will throw during encode/decode.
*/
function makeLimitedV8Serializer() {
return { encodeV8, decodeV8 };
}
exports.makeLimitedV8Serializer = makeLimitedV8Serializer;
function decodeV8(bytes, { wrapUnknownValues = false } = {}) {
if (bytes.length === 0)
throw new Error(`decode error: empty input`);
let pos = 0;
const kVersion = bytes[pos++];
if (kVersion !== SerializationTag.kVersion && wrapUnknownValues) {
return new UnknownV8(bytes);
}
if (kVersion !== SerializationTag.kVersion) {
throw new Error(`decode error: Unsupported kVersion ${kVersion} [${[...bytes].join(", ")}]`);
}
const version = bytes[pos++];
if (version !== kLatestVersion) {
throw new Error(`decode error: Unsupported version ${version}`);
}
const tag = bytes[pos++];
if (tag === SerializationTag.kOneByteString) {
const len = bytes[pos++];
const arr = bytes.subarray(pos, pos + len);
const rt = new TextDecoder().decode(arr);
pos += len;
(0, bytes_js_1.checkEnd)(bytes, pos);
return rt;
}
else if (tag === SerializationTag.kTwoByteString) {
const len = bytes[pos++];
const arr = bytes.subarray(pos, pos + len);
const rt = new TextDecoder("utf-16").decode(arr);
pos += len;
(0, bytes_js_1.checkEnd)(bytes, pos);
return rt;
}
else if (tag === SerializationTag.kNull) {
(0, bytes_js_1.checkEnd)(bytes, pos);
return null;
}
else if (tag === SerializationTag.kUndefined) {
(0, bytes_js_1.checkEnd)(bytes, pos);
return undefined;
}
else if (tag === SerializationTag.kTrue) {
(0, bytes_js_1.checkEnd)(bytes, pos);
return true;
}
else if (tag === SerializationTag.kFalse) {
(0, bytes_js_1.checkEnd)(bytes, pos);
return false;
}
else if (tag === SerializationTag.kBigInt && bytes.length === 4 && bytes[3] === 0) {
return 0n;
}
else if (wrapUnknownValues) {
return new UnknownV8(bytes);
}
else {
throw new Error(`decode error: Unsupported v8 tag ${tag} ('${String.fromCharCode(tag)}') at ${pos} in [${bytes.join(", ")}]`);
}
}
exports.decodeV8 = decodeV8;
function encodeV8(value) {
if (value instanceof UnknownV8) {
return value.bytes;
}
else if (typeof value === "string") {
const chars = [...value];
if (chars.every(isOneByteChar)) {
const charCodes = chars.map((v) => v.charCodeAt(0));
return new Uint8Array([
SerializationTag.kVersion,
kLatestVersion,
SerializationTag.kOneByteString,
charCodes.length,
...charCodes,
]);
}
const bytes = [];
for (let i = 0; i < value.length; i++) {
const charCode = value.charCodeAt(i);
const msb = (charCode & 0xff00) >> 8;
const lsb = charCode & 0x00ff;
bytes.push(lsb);
bytes.push(msb);
}
return new Uint8Array([
SerializationTag.kVersion,
kLatestVersion,
SerializationTag.kTwoByteString,
value.length * 2,
...bytes,
]);
}
else if (value === null) {
return new Uint8Array([
SerializationTag.kVersion,
kLatestVersion,
SerializationTag.kNull,
]);
}
else if (value === undefined) {
return new Uint8Array([
SerializationTag.kVersion,
kLatestVersion,
SerializationTag.kUndefined,
]);
}
else if (value === true) {
return new Uint8Array([
SerializationTag.kVersion,
kLatestVersion,
SerializationTag.kTrue,
]);
}
else if (value === false) {
return new Uint8Array([
SerializationTag.kVersion,
kLatestVersion,
SerializationTag.kFalse,
]);
}
else if (value === 0n) {
return new Uint8Array([
SerializationTag.kVersion,
kLatestVersion,
SerializationTag.kBigInt,
0,
]);
}
throw new Error(`encode error: Unsupported v8 value ${typeof value} ${value}`);
}
exports.encodeV8 = encodeV8;
//
const kLatestVersion = 15;
var SerializationTag;
(function (SerializationTag) {
SerializationTag[SerializationTag["kVersion"] = 255] = "kVersion";
SerializationTag[SerializationTag["kOneByteString"] = '"'.charCodeAt(0)] = "kOneByteString";
SerializationTag[SerializationTag["kTwoByteString"] = "c".charCodeAt(0)] = "kTwoByteString";
SerializationTag[SerializationTag["kNull"] = "0".charCodeAt(0)] = "kNull";
SerializationTag[SerializationTag["kUndefined"] = "_".charCodeAt(0)] = "kUndefined";
SerializationTag[SerializationTag["kTrue"] = "T".charCodeAt(0)] = "kTrue";
SerializationTag[SerializationTag["kFalse"] = "F".charCodeAt(0)] = "kFalse";
SerializationTag[SerializationTag["kBigInt"] = "Z".charCodeAt(0)] = "kBigInt";
})(SerializationTag || (SerializationTag = {}));
function isOneByteChar(char) {
const cp = char.codePointAt(0);
return cp >= 0 && cp <= 0xff;
}
//
/** Raw V8-serialized bytes */
class UnknownV8 {
constructor(bytes) {
Object.defineProperty(this, "bytes", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.bytes = bytes;
}
}
exports.UnknownV8 = UnknownV8;