mirror of
https://github.com/rhinstaller/node-cache.git
synced 2026-11-03 21:49:47 -09:00
418 lines
16 KiB
JavaScript
418 lines
16 KiB
JavaScript
"use strict";
|
|
// Copyright 2021-2026 Buf Technologies, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.makeReadContext = makeReadContext;
|
|
exports.fromBinary = fromBinary;
|
|
exports.mergeFromBinary = mergeFromBinary;
|
|
exports.readField = readField;
|
|
const descriptors_js_1 = require("./descriptors.js");
|
|
const scalar_js_1 = require("./reflect/scalar.js");
|
|
const error_js_1 = require("./reflect/error.js");
|
|
const unsafe_js_1 = require("./reflect/unsafe.js");
|
|
const message_js_1 = require("./reflect/message.js");
|
|
const create_js_1 = require("./create.js");
|
|
const binary_encoding_js_1 = require("./wire/binary-encoding.js");
|
|
const varint_js_1 = require("./wire/varint.js");
|
|
/**
|
|
* @private Only exported for getExtension()
|
|
*/
|
|
function makeReadContext(options) {
|
|
return Object.assign(Object.assign({ readUnknownFields: true, recursionLimit: 100 }, options), { depth: 0 });
|
|
}
|
|
/**
|
|
* Parse serialized binary data.
|
|
*/
|
|
function fromBinary(schema, bytes, options) {
|
|
const message = (0, create_js_1.create)(schema);
|
|
compiledReader(schema).read(message, new binary_encoding_js_1.BinaryReader(bytes), makeReadContext(options), bytes.byteLength);
|
|
return message;
|
|
}
|
|
/**
|
|
* Parse from binary data, merging fields.
|
|
*
|
|
* Repeated fields are appended. Map entries are added, overwriting
|
|
* existing keys.
|
|
*
|
|
* If a message field is already present, it will be merged with the
|
|
* new data.
|
|
*/
|
|
function mergeFromBinary(schema, target, bytes, options) {
|
|
if (target.$typeName !== schema.typeName &&
|
|
schema.fields.length > 0) {
|
|
throw new error_js_1.FieldError(schema.fields[0], `cannot use ${schema.fields[0]} with message ${target.$typeName}`, "ForeignFieldError");
|
|
}
|
|
compiledReader(schema).read(target, new binary_encoding_js_1.BinaryReader(bytes), makeReadContext(options), bytes.byteLength);
|
|
return target;
|
|
}
|
|
const compiledReaders = new WeakMap();
|
|
/**
|
|
* Return the compiled decoder for a message, compiling it on first use.
|
|
*/
|
|
function compiledReader(desc) {
|
|
let compiled = compiledReaders.get(desc);
|
|
if (compiled === undefined) {
|
|
compiled = compileMessage(desc);
|
|
}
|
|
return compiled;
|
|
}
|
|
function compileMessage(desc) {
|
|
const descString = String(desc);
|
|
const fieldReaders = new Map();
|
|
const compiled = {
|
|
read: compileMessageReader(descString, fieldReaders),
|
|
readGroup: compileGroupReader(descString, fieldReaders),
|
|
};
|
|
// Register before compiling fields, so that recursive message types
|
|
// resolve to this instance instead of compiling endlessly.
|
|
compiledReaders.set(desc, compiled);
|
|
for (const field of desc.fields) {
|
|
fieldReaders.set(field.number, compileFieldReader(field));
|
|
}
|
|
return compiled;
|
|
}
|
|
/**
|
|
* Create a decoder for a length-prefixed message body, dispatching wire
|
|
* records to the compiled field decoders by field number.
|
|
*/
|
|
function compileMessageReader(descString, fieldReaders) {
|
|
return (message, reader, ctx, length) => {
|
|
var _a;
|
|
if (++ctx.depth > ctx.recursionLimit) {
|
|
throw new Error(`cannot decode ${descString} from binary: maximum recursion depth of ${ctx.recursionLimit} reached`);
|
|
}
|
|
const end = reader.pos + length;
|
|
const unknownFields = (_a = message.$unknown) !== null && _a !== void 0 ? _a : [];
|
|
while (reader.pos < end) {
|
|
const [fieldNo, wireType] = reader.tag();
|
|
const fieldReader = fieldReaders.get(fieldNo);
|
|
if (fieldReader === undefined) {
|
|
// Use remaining recursion budget for skipping nested groups
|
|
const data = reader.skip(wireType, fieldNo, ctx.recursionLimit - ctx.depth);
|
|
if (ctx.readUnknownFields) {
|
|
unknownFields.push({ no: fieldNo, wireType, data });
|
|
}
|
|
continue;
|
|
}
|
|
fieldReader(message, reader, ctx, wireType);
|
|
}
|
|
if (unknownFields.length > 0) {
|
|
message.$unknown = unknownFields;
|
|
}
|
|
ctx.depth--;
|
|
};
|
|
}
|
|
/**
|
|
* Create a decoder for a message with the delimited encoding (group),
|
|
* reading until the EndGroup tag, like compileMessageReader.
|
|
*/
|
|
function compileGroupReader(descString, fieldReaders) {
|
|
return (message, reader, ctx, fieldNo) => {
|
|
var _a;
|
|
if (++ctx.depth > ctx.recursionLimit) {
|
|
throw new Error(`cannot decode ${descString} from binary: maximum recursion depth of ${ctx.recursionLimit} reached`);
|
|
}
|
|
let recordFieldNo;
|
|
let wireType;
|
|
const unknownFields = (_a = message.$unknown) !== null && _a !== void 0 ? _a : [];
|
|
while (reader.pos < reader.len) {
|
|
[recordFieldNo, wireType] = reader.tag();
|
|
if (wireType == binary_encoding_js_1.WireType.EndGroup) {
|
|
break;
|
|
}
|
|
const fieldReader = fieldReaders.get(recordFieldNo);
|
|
if (fieldReader === undefined) {
|
|
// Use remaining recursion budget for skipping nested groups
|
|
const data = reader.skip(wireType, recordFieldNo, ctx.recursionLimit - ctx.depth);
|
|
if (ctx.readUnknownFields) {
|
|
unknownFields.push({ no: recordFieldNo, wireType, data });
|
|
}
|
|
continue;
|
|
}
|
|
fieldReader(message, reader, ctx, wireType);
|
|
}
|
|
if (wireType != binary_encoding_js_1.WireType.EndGroup || recordFieldNo !== fieldNo) {
|
|
throw new Error("invalid end group tag");
|
|
}
|
|
if (unknownFields.length > 0) {
|
|
message.$unknown = unknownFields;
|
|
}
|
|
ctx.depth--;
|
|
};
|
|
}
|
|
/**
|
|
* @private Only exported for getExtension()
|
|
*/
|
|
function readField(message, reader, field, wireType, ctx) {
|
|
compileFieldReader(field)(message[unsafe_js_1.unsafeLocal], reader, ctx, wireType);
|
|
}
|
|
function compileFieldReader(field) {
|
|
switch (field.fieldKind) {
|
|
case "scalar":
|
|
return compileScalarFieldReader(field);
|
|
case "enum":
|
|
return compileEnumFieldReader(field);
|
|
case "message":
|
|
return compileMessageFieldReader(field);
|
|
case "list":
|
|
return compileListFieldReader(field);
|
|
case "map":
|
|
return compileMapFieldReader(field);
|
|
}
|
|
}
|
|
function compileScalarFieldReader(field) {
|
|
const readScalar = compileScalarReader(field.scalar, field.utf8Validation, field.longAsString);
|
|
const localName = field.localName;
|
|
if (field.oneof) {
|
|
const oneofLocalName = field.oneof.localName;
|
|
return (message, reader) => {
|
|
message[oneofLocalName] = {
|
|
case: localName,
|
|
value: readScalar(reader),
|
|
};
|
|
};
|
|
}
|
|
return (message, reader) => {
|
|
message[localName] = readScalar(reader);
|
|
};
|
|
}
|
|
function compileEnumFieldReader(field) {
|
|
var _a;
|
|
const localName = field.localName;
|
|
const oneofLocalName = (_a = field.oneof) === null || _a === void 0 ? void 0 : _a.localName;
|
|
if (field.enum.open) {
|
|
if (oneofLocalName !== undefined) {
|
|
return (message, reader) => {
|
|
message[oneofLocalName] = { case: localName, value: reader.int32() };
|
|
};
|
|
}
|
|
return (message, reader) => {
|
|
message[localName] = reader.int32();
|
|
};
|
|
}
|
|
// Closed enums: unknown values are stored as unknown fields.
|
|
const values = field.enum.values;
|
|
const fieldNo = field.number;
|
|
return (message, reader, ctx, wireType) => {
|
|
var _a;
|
|
const val = reader.int32();
|
|
if (values.some((v) => v.number === val)) {
|
|
if (oneofLocalName !== undefined) {
|
|
message[oneofLocalName] = { case: localName, value: val };
|
|
}
|
|
else {
|
|
message[localName] = val;
|
|
}
|
|
}
|
|
else if (ctx.readUnknownFields) {
|
|
const bytes = [];
|
|
(0, varint_js_1.varint32write)(val, bytes);
|
|
const unknownFields = (_a = message.$unknown) !== null && _a !== void 0 ? _a : [];
|
|
unknownFields.push({
|
|
no: fieldNo,
|
|
wireType,
|
|
data: new Uint8Array(bytes),
|
|
});
|
|
message.$unknown = unknownFields;
|
|
}
|
|
};
|
|
}
|
|
function compileMessageFieldReader(field) {
|
|
const localName = field.localName;
|
|
const { toMessage, toLocal } = (0, message_js_1.localMessageMapper)(field);
|
|
const readChild = compileChildReader(field);
|
|
if (field.oneof) {
|
|
const oneofLocalName = field.oneof.localName;
|
|
return (message, reader, ctx) => {
|
|
const oneof = message[oneofLocalName];
|
|
const child = toMessage(oneof.case === localName ? oneof.value : undefined);
|
|
readChild(child, reader, ctx);
|
|
message[oneofLocalName] = { case: localName, value: toLocal(child) };
|
|
};
|
|
}
|
|
return (message, reader, ctx) => {
|
|
const child = toMessage(message[localName]);
|
|
readChild(child, reader, ctx);
|
|
message[localName] = toLocal(child);
|
|
};
|
|
}
|
|
/**
|
|
* Compile a decoder for the wire format of a message field, honoring the
|
|
* delimited encoding of the field.
|
|
*/
|
|
function compileChildReader(field) {
|
|
const compiledChild = compiledReader(field.message);
|
|
if (field.delimitedEncoding) {
|
|
const fieldNo = field.number;
|
|
return (child, reader, ctx) => compiledChild.readGroup(child, reader, ctx, fieldNo);
|
|
}
|
|
return (child, reader, ctx) => compiledChild.read(child, reader, ctx, reader.uint32());
|
|
}
|
|
function compileListFieldReader(field) {
|
|
const localName = field.localName;
|
|
if (field.listKind == "message") {
|
|
const { toMessage, toLocal } = (0, message_js_1.localMessageMapper)(field);
|
|
const readChild = compileChildReader(field);
|
|
return (message, reader, ctx) => {
|
|
const child = toMessage(undefined);
|
|
readChild(child, reader, ctx);
|
|
message[localName].push(toLocal(child));
|
|
};
|
|
}
|
|
const scalarType = field.listKind == "enum" ? descriptors_js_1.ScalarType.INT32 : field.scalar;
|
|
const longAsString = field.listKind == "scalar" ? field.longAsString : false;
|
|
const readScalar = compileScalarReader(scalarType, field.utf8Validation, longAsString);
|
|
const packedPossible = scalarType != descriptors_js_1.ScalarType.STRING && scalarType != descriptors_js_1.ScalarType.BYTES;
|
|
return (message, reader, ctx, wireType) => {
|
|
const items = message[localName];
|
|
if (wireType == binary_encoding_js_1.WireType.LengthDelimited && packedPossible) {
|
|
const end = reader.uint32() + reader.pos;
|
|
while (reader.pos < end) {
|
|
items.push(readScalar(reader));
|
|
}
|
|
}
|
|
else {
|
|
items.push(readScalar(reader));
|
|
}
|
|
};
|
|
}
|
|
function compileMapFieldReader(field) {
|
|
const localName = field.localName;
|
|
const readKey = compileScalarReader(field.mapKey, field.utf8Validation, false);
|
|
const keyZero = (0, scalar_js_1.scalarZeroValue)(field.mapKey, false);
|
|
let readValue;
|
|
let valueDefault;
|
|
switch (field.mapKind) {
|
|
case "scalar": {
|
|
const scalar = field.scalar;
|
|
const readScalar = compileScalarReader(scalar, field.utf8Validation, false);
|
|
readValue = (reader) => readScalar(reader);
|
|
// Bytes zero values are created per entry, so that entries do not share
|
|
// one instance.
|
|
if (scalar == descriptors_js_1.ScalarType.BYTES) {
|
|
valueDefault = () => new Uint8Array(0);
|
|
}
|
|
else {
|
|
const zero = (0, scalar_js_1.scalarZeroValue)(scalar, false);
|
|
valueDefault = () => zero;
|
|
}
|
|
break;
|
|
}
|
|
case "enum": {
|
|
const zero = field.enum.values[0].number;
|
|
readValue = (reader) => reader.int32();
|
|
valueDefault = () => zero;
|
|
break;
|
|
}
|
|
case "message": {
|
|
const { toMessage, toLocal } = (0, message_js_1.localMessageMapper)(field);
|
|
const readChild = compiledReader(field.message).read;
|
|
// A repeated value field within one entry merges into the previous
|
|
// value, like any other singular message field.
|
|
readValue = (reader, ctx, existing) => {
|
|
const child = toMessage(existing);
|
|
readChild(child, reader, ctx, reader.uint32());
|
|
return toLocal(child);
|
|
};
|
|
valueDefault = () => toLocal(toMessage(undefined));
|
|
break;
|
|
}
|
|
}
|
|
return (message, reader, ctx) => {
|
|
const record = message[localName];
|
|
let key;
|
|
let val;
|
|
// Read the length of the map entry, which is a varint.
|
|
const len = reader.uint32();
|
|
// Calculate end AFTER advancing reader.pos (above), so that reader.pos is
|
|
// at the start of the map entry.
|
|
const end = reader.pos + len;
|
|
while (reader.pos < end) {
|
|
// Map entries have the key in field 1, and the value in field 2.
|
|
const [fieldNo] = reader.tag();
|
|
switch (fieldNo) {
|
|
case 1:
|
|
key = readKey(reader);
|
|
break;
|
|
case 2:
|
|
val = readValue(reader, ctx, val);
|
|
break;
|
|
}
|
|
}
|
|
if (key === undefined) {
|
|
key = keyZero;
|
|
}
|
|
if (val === undefined) {
|
|
val = valueDefault();
|
|
}
|
|
// Map keys may be booleans, numbers, or bigints. Object keys are strings, so
|
|
// they are converted implicitly. setOwn() is needed because a key can be
|
|
// "__proto__".
|
|
(0, unsafe_js_1.setOwn)(record, key, val);
|
|
};
|
|
}
|
|
/**
|
|
* Returns a reader for a scalar value. For 64-bit integers, BinaryReader
|
|
* already returns the local representation (bigint or string), so, unlike in
|
|
* the reflection layer, no validation is needed here.
|
|
*/
|
|
function compileScalarReader(type, utf8Validation, longAsString) {
|
|
switch (type) {
|
|
case descriptors_js_1.ScalarType.STRING:
|
|
return (reader) => reader.string(utf8Validation);
|
|
case descriptors_js_1.ScalarType.BOOL:
|
|
return (reader) => reader.bool();
|
|
case descriptors_js_1.ScalarType.DOUBLE:
|
|
return (reader) => reader.double();
|
|
case descriptors_js_1.ScalarType.FLOAT:
|
|
return (reader) => reader.float();
|
|
case descriptors_js_1.ScalarType.INT32:
|
|
return (reader) => reader.int32();
|
|
case descriptors_js_1.ScalarType.INT64:
|
|
if (longAsString) {
|
|
return (reader) => String(reader.int64());
|
|
}
|
|
return (reader) => reader.int64();
|
|
case descriptors_js_1.ScalarType.UINT64:
|
|
if (longAsString) {
|
|
return (reader) => String(reader.uint64());
|
|
}
|
|
return (reader) => reader.uint64();
|
|
case descriptors_js_1.ScalarType.FIXED64:
|
|
if (longAsString) {
|
|
return (reader) => String(reader.fixed64());
|
|
}
|
|
return (reader) => reader.fixed64();
|
|
case descriptors_js_1.ScalarType.BYTES:
|
|
return (reader) => reader.bytes();
|
|
case descriptors_js_1.ScalarType.FIXED32:
|
|
return (reader) => reader.fixed32();
|
|
case descriptors_js_1.ScalarType.SFIXED32:
|
|
return (reader) => reader.sfixed32();
|
|
case descriptors_js_1.ScalarType.SFIXED64:
|
|
if (longAsString) {
|
|
return (reader) => String(reader.sfixed64());
|
|
}
|
|
return (reader) => reader.sfixed64();
|
|
case descriptors_js_1.ScalarType.SINT64:
|
|
if (longAsString) {
|
|
return (reader) => String(reader.sint64());
|
|
}
|
|
return (reader) => reader.sint64();
|
|
case descriptors_js_1.ScalarType.UINT32:
|
|
return (reader) => reader.uint32();
|
|
case descriptors_js_1.ScalarType.SINT32:
|
|
return (reader) => reader.sint32();
|
|
}
|
|
}
|