protobufjs
Version:
Protocol Buffers for JavaScript & TypeScript.
1,236 lines (1,139 loc) • 40.8 kB
JavaScript
"use strict";
var protobuf = require("../light");
var Type = protobuf.Type,
Enum = protobuf.Enum,
Field = protobuf.Field,
Reader = protobuf.Reader,
types = protobuf.types,
util = protobuf.util;
var textformat = protobuf.textformat = module.exports = {};
/**
* Maximum recursion depth for formatting length-delimited unknown fields.
* @name unknownRecursionLimit
* @type {number}
*/
textformat.unknownRecursionLimit = 10;
var punct = {
"{": true,
"}": true,
"<": true,
">": true,
":": true,
"[": true,
"]": true,
",": true,
";": true,
"-": true
};
var identRe = /^[A-Za-z_][A-Za-z0-9_]*$/,
numberStartRe = /[0-9]/,
wordCharRe = /[A-Za-z0-9_]/,
hexRe = /^[0-9A-Fa-f]$/,
octRe = /^[0-7]$/,
floatRe = /^(?:[0-9]+\.[0-9]*(?:[eE][+-]?[0-9]+)?[fF]?|\.[0-9]+(?:[eE][+-]?[0-9]+)?[fF]?|[0-9]+[eE][+-]?[0-9]+[fF]?|[0-9]+[fF])/,
hexIntRe = /^0[xX][0-9A-Fa-f]+/,
octIntRe = /^0[0-7]+/,
decIntRe = /^(?:0|[1-9][0-9]*)/;
/**
* Parses protobuf text format using the specified reflected message type.
* @param {Type} type Reflected message type
* @param {string} text Text format input
* @returns {Message<{}>} Message instance
* @private
*/
function parseText(type, text) {
if (!(type instanceof Type))
throw TypeError("type must be a Type");
type.root.resolveAll();
var parser = new Parser(String(text));
var object = parser.parseMessage(type, null, 0);
parser.expectEnd();
var err = verifyTextMessage(type, object),
message;
if (err)
throw Error(err);
message = type.create(object);
return message;
}
/**
* Text format options.
* @interface ITextFormatOptions
* @property {boolean} [unknowns=false] Also includes and formats unknown fields.
*/
/**
* Formats a message as protobuf text format using the specified reflected type.
* @param {Type} type Reflected message type
* @param {Message<{}>|Object.<string,*>} message Message instance or plain object
* @param {ITextFormatOptions} [options] Text format options
* @returns {string} Text format output
* @private
*/
function formatText(type, message, options) {
if (!(type instanceof Type))
throw TypeError("type must be a Type");
type.root.resolveAll();
var lines = [];
writeMessage(type, message, lines, 0, options || {}, 0);
return lines.join("\n");
}
/**
* Parses this type from protobuf text format.
* @param {string} text Text format input
* @returns {Message<{}>} Message instance
*/
Type.prototype.fromText = function fromText(text) {
return parseText(this, text);
};
/**
* Formats a message of this type as protobuf text format.
* @param {Message<{}>|Object.<string,*>} message Message instance or plain object
* @param {ITextFormatOptions} [options] Text format options
* @returns {string} Text format output
*/
Type.prototype.toText = function toText(message, options) {
return formatText(this, message, options);
};
function Tokenizer(source) {
this.source = source;
this.pos = 0;
this.len = source.length;
this.line = 1;
this.stack = [];
}
Tokenizer.prototype.error = function error(message) {
return Error(message + " (line " + this.line + ")");
};
Tokenizer.prototype.peek = function peek() {
var token = this.next();
this.push(token);
return token;
};
Tokenizer.prototype.push = function push(token) {
if (token)
this.stack.unshift(token);
};
Tokenizer.prototype.next = function next() {
if (this.stack.length)
return this.stack.shift();
this.skipSpace();
if (this.pos >= this.len)
return null;
var ch = this.source.charAt(this.pos);
if (ch === "\"" || ch === "'")
return this.readString(ch);
if (ch === "." && numberStartRe.test(this.source.charAt(this.pos + 1)) || numberStartRe.test(ch))
return this.readNumber();
if (punct[ch]) {
++this.pos;
return { type: "punct", value: ch, raw: ch, line: this.line };
}
return this.readWord();
};
Tokenizer.prototype.expect = function expect(value) {
var token = this.next();
if (!token || token.value !== value)
throw this.error("expected '" + value + "'");
return token;
};
Tokenizer.prototype.skip = function skip(value) {
var token = this.next();
if (token && token.value === value)
return true;
this.push(token);
return false;
};
Tokenizer.prototype.skipSpace = function skipSpace() {
var ch;
while (this.pos < this.len) {
ch = this.source.charAt(this.pos);
if (ch === "#") {
while (this.pos < this.len && this.source.charAt(this.pos) !== "\n")
++this.pos;
} else if (ch === " " || ch === "\t" || ch === "\v" || ch === "\f" || ch === "\r") {
++this.pos;
} else if (ch === "\n") {
++this.pos;
++this.line;
} else
break;
}
};
Tokenizer.prototype.readWord = function readWord() {
var start = this.pos,
ch;
while (this.pos < this.len) {
ch = this.source.charAt(this.pos);
if (ch === "#" || ch === "\"" || ch === "'" || punct[ch] || /\s/.test(ch))
break;
++this.pos;
}
if (start === this.pos)
throw this.error("illegal character '" + this.source.charAt(this.pos) + "'");
var raw = this.source.substring(start, this.pos);
return { type: "word", value: raw, raw: raw, line: this.line };
};
Tokenizer.prototype.readNumber = function readNumber() {
var rest = this.source.substring(this.pos),
matches = [
floatRe.exec(rest),
hexIntRe.exec(rest),
octIntRe.exec(rest),
decIntRe.exec(rest)
],
raw = null;
for (var i = 0; i < matches.length; ++i)
if (matches[i] && (!raw || matches[i][0].length > raw.length))
raw = matches[i][0];
if (!raw)
throw this.error("illegal number");
this.pos += raw.length;
if (this.pos < this.len && wordCharRe.test(this.source.charAt(this.pos)))
throw this.error("illegal number '" + raw + this.source.charAt(this.pos) + "'");
return { type: "number", value: raw, raw: raw, line: this.line };
};
Tokenizer.prototype.readString = function readString(quote) {
var bytes = [],
ch,
code;
++this.pos;
while (this.pos < this.len) {
ch = this.source.charAt(this.pos++);
if (ch === quote)
return { type: "string", value: bytes, raw: quote, line: this.line };
if (ch === "\n")
throw this.error("illegal string");
if (ch !== "\\") {
code = ch.charCodeAt(0);
if ((code & 0xFC00) === 0xD800 && this.pos < this.len) {
var next = this.source.charCodeAt(this.pos);
if ((next & 0xFC00) === 0xDC00) {
++this.pos;
code = 0x10000 + ((code & 0x03FF) << 10) + (next & 0x03FF);
}
}
pushUtf8(bytes, code);
continue;
}
if (this.pos >= this.len)
throw this.error("illegal string escape");
ch = this.source.charAt(this.pos++);
switch (ch) {
case "a": bytes.push(7); break;
case "b": bytes.push(8); break;
case "f": bytes.push(12); break;
case "n": bytes.push(10); break;
case "r": bytes.push(13); break;
case "t": bytes.push(9); break;
case "v": bytes.push(11); break;
case "?": bytes.push(63); break;
case "\\": bytes.push(92); break;
case "'": bytes.push(39); break;
case "\"": bytes.push(34); break;
case "x":
case "X":
bytes.push(this.readHexEscape());
break;
case "u":
pushUtf8(bytes, this.readCodePoint(4));
break;
case "U":
pushUtf8(bytes, this.readCodePoint(8));
break;
default:
if (octRe.test(ch)) {
bytes.push(this.readOctEscape(ch));
break;
}
throw this.error("illegal string escape '\\" + ch + "'");
}
}
throw this.error("unterminated string");
};
Tokenizer.prototype.readHexEscape = function readHexEscape() {
var value = 0,
count = 0,
ch;
while (count < 2 && this.pos < this.len && hexRe.test(ch = this.source.charAt(this.pos))) {
value = value * 16 + parseInt(ch, 16);
++this.pos;
++count;
}
if (!count)
throw this.error("illegal hex escape");
return value;
};
Tokenizer.prototype.readOctEscape = function readOctEscape(first) {
var value = parseInt(first, 8),
count = 1,
ch;
while (count < 3 && this.pos < this.len && octRe.test(ch = this.source.charAt(this.pos))) {
value = value * 8 + parseInt(ch, 8);
++this.pos;
++count;
}
return value & 255;
};
Tokenizer.prototype.readCodePoint = function readCodePoint(size) {
var raw = this.source.substring(this.pos, this.pos + size);
if (raw.length !== size || !/^[0-9A-Fa-f]+$/.test(raw))
throw this.error("illegal unicode escape");
this.pos += size;
var code = parseInt(raw, 16);
if (code > 0x10FFFF || code >= 0xD800 && code <= 0xDFFF)
throw this.error("illegal unicode escape");
return code;
};
function Parser(source) {
this.tn = new Tokenizer(source);
}
Parser.prototype.error = function error(message) {
throw this.tn.error(message);
};
Parser.prototype.expectEnd = function expectEnd() {
var token = this.tn.next();
if (token)
this.error("unexpected token '" + token.value + "'");
};
Parser.prototype.parseMessage = function parseMessage(type, end, depth) {
if (depth > util.recursionLimit)
this.error("max depth exceeded");
var object = {},
seen = {};
for (;;) {
var token = this.tn.peek();
if (!token) {
if (end)
this.error("expected '" + end + "'");
return object;
}
if (end && token.value === end) {
this.tn.next();
return object;
}
if (isSeparator(token))
this.error("unexpected separator");
this.parseField(type, object, seen, depth);
token = this.tn.peek();
if (token && isSeparator(token)) {
this.tn.next();
token = this.tn.peek();
if (token && isSeparator(token))
this.error("unexpected separator");
}
}
};
Parser.prototype.parseField = function parseField(type, object, seen, depth) {
var info = this.readFieldName(type);
if (info.any) {
this.parseAny(type, info.name, object, seen, depth);
return;
}
if (!info.field) {
this.skipReservedValue(depth);
return;
}
var field = info.field.resolve();
if (field.map) {
this.parseMessageFieldDelimiter();
this.parseMapField(field, object, depth);
} else if (field.resolvedType instanceof Type) {
this.parseMessageFieldDelimiter();
if (this.tn.skip("[")) {
if (!field.repeated)
this.error(field.fullName + ": list value specified for singular field");
if (!this.tn.skip("]")) {
do {
addField(object, seen, field, this.parseMessageValue(field.resolvedType, depth));
} while (this.tn.skip(","));
this.tn.expect("]");
}
} else
addField(object, seen, field, this.parseMessageValue(field.resolvedType, depth));
} else {
this.tn.expect(":");
if (this.tn.skip("[")) {
if (!field.repeated)
this.error(field.fullName + ": list value specified for singular field");
if (!this.tn.skip("]")) {
do {
addField(object, seen, field, this.parseScalar(field));
} while (this.tn.skip(","));
this.tn.expect("]");
}
} else
addField(object, seen, field, this.parseScalar(field));
}
};
Parser.prototype.readFieldName = function readFieldName(type) {
var token = this.tn.next();
if (!token)
this.error("expected field name");
if (token.value === "[") {
var bracketName = this.readBracketName();
if (type.fullName === ".google.protobuf.Any" && bracketName.indexOf("/") >= 0)
return { any: true, name: bracketName };
return { field: lookupExtension(type, bracketName), name: bracketName };
}
if (token.type !== "word" || !identRe.test(token.value))
this.error("illegal field name '" + token.value + "'");
var field = lookupField(type, token.value);
if (!field) {
if (type.isReservedName(token.value))
return { name: token.value };
this.error(type.fullName + ": unknown field '" + token.value + "'");
}
return { field: field, name: token.value };
};
Parser.prototype.readBracketName = function readBracketName() {
var parts = [],
token;
while (token = this.tn.next()) {
if (token.value === "]")
return parts.join("");
parts.push(token.raw || token.value);
}
this.error("unterminated bracketed field name");
return null;
};
Parser.prototype.parseAny = function parseAny(type, typeUrl, object, seen, depth) {
this.parseMessageFieldDelimiter();
validateTypeUrl(typeUrl);
var typeName = typeUrl.substring(typeUrl.lastIndexOf("/") + 1),
embeddedType = type.root.lookupType(typeName.charAt(0) === "." ? typeName : "." + typeName),
embedded = this.parseMessageValue(embeddedType, depth),
typeUrlField = lookupField(type, "type_url") || lookupField(type, "typeUrl"),
valueField = lookupField(type, "value");
addField(object, seen, typeUrlField, typeUrl);
addField(object, seen, valueField, embeddedType.encode(embedded).finish());
};
Parser.prototype.parseMapField = function parseMapField(field, object, depth) {
if (!object[field.name])
object[field.name] = {};
if (this.tn.skip("[")) {
if (!this.tn.skip("]")) {
do {
addMapEntry(field, object[field.name], this.parseMapEntry(field, depth + 1));
} while (this.tn.skip(","));
this.tn.expect("]");
}
} else
addMapEntry(field, object[field.name], this.parseMapEntry(field, depth + 1));
};
Parser.prototype.parseMessageFieldDelimiter = function parseMessageFieldDelimiter() {
this.tn.skip(":");
};
Parser.prototype.parseMapEntry = function parseMapEntry(field, depth) {
if (depth > util.recursionLimit)
this.error("max depth exceeded");
var end;
if (this.tn.skip("{"))
end = "}";
else if (this.tn.skip("<"))
end = ">";
else
this.error("expected map entry");
var entry = {},
token,
valueField = mapValueField(field),
keyField = mapKeyField(field);
for (;;) {
token = this.tn.peek();
if (!token)
this.error("expected '" + end + "'");
if (token.value === end) {
this.tn.next();
return entry;
}
if (isSeparator(token))
this.error("unexpected separator");
token = this.tn.next();
if (token.type !== "word")
this.error("expected map field name");
if (token.value === "key") {
this.tn.expect(":");
entry.key = this.parseScalar(keyField);
} else if (token.value === "value") {
if (valueField.resolvedType instanceof Type) {
this.parseMessageFieldDelimiter();
entry.value = this.parseMessageValue(valueField.resolvedType, depth);
} else {
this.tn.expect(":");
entry.value = this.parseScalar(valueField);
}
} else
this.error("unknown map field '" + token.value + "'");
token = this.tn.peek();
if (token && isSeparator(token)) {
this.tn.next();
token = this.tn.peek();
if (token && isSeparator(token))
this.error("unexpected separator");
}
}
};
Parser.prototype.parseMessageValue = function parseMessageValue(type, depth) {
var end;
if (this.tn.skip("{"))
end = "}";
else if (this.tn.skip("<"))
end = ">";
else
this.error("expected message value");
return this.parseMessage(type, end, depth + 1);
};
Parser.prototype.parseScalar = function parseScalar(field) {
if (field.type === "string" || field.type === "bytes") {
var bytes = this.readStringBytes();
return field.type === "bytes" ? util.newBuffer(bytes) : utf8Read(bytes, true);
}
var sign = 1;
if (this.tn.skip("-"))
sign = -1;
var token = this.tn.next();
if (!token)
this.error("expected value");
if (field.resolvedType instanceof Enum)
return parseEnum(field, token, sign);
switch (field.type) {
case "double":
case "float":
return parseFloatValue(token, sign);
case "bool":
return parseBool(token, sign);
case "int32":
case "sint32":
case "sfixed32":
return parseInteger(token, sign, false, 32);
case "uint32":
case "fixed32":
return parseInteger(token, sign, true, 32);
case "int64":
case "sint64":
case "sfixed64":
return parseInteger(token, sign, false, 64);
case "uint64":
case "fixed64":
return parseInteger(token, sign, true, 64);
default:
this.error(field.fullName + ": unsupported scalar type");
}
return undefined;
};
Parser.prototype.readStringBytes = function readStringBytes() {
var token = this.tn.next(),
bytes = [];
if (!token || token.type !== "string")
this.error("expected string");
Array.prototype.push.apply(bytes, token.value);
while ((token = this.tn.peek()) && token.type === "string") {
token = this.tn.next();
Array.prototype.push.apply(bytes, token.value);
}
return bytes;
};
Parser.prototype.skipReservedValue = function skipReservedValue(depth) {
this.tn.skip(":");
var token = this.tn.peek();
if (!token)
return;
if (token.value === "{") {
this.skipBalanced("{", "}", depth);
} else if (token.value === "<") {
this.skipBalanced("<", ">", depth);
} else if (token.value === "[") {
this.skipBalanced("[", "]", depth);
} else if (token.type === "string") {
this.readStringBytes();
} else {
if (token.value === "-")
this.tn.next();
this.tn.next();
}
};
Parser.prototype.skipBalanced = function skipBalanced(open, close, depth) {
var balance = 0,
token;
do {
token = this.tn.next();
if (!token)
this.error("expected '" + close + "'");
if (token.value === open) {
if (depth + ++balance > util.recursionLimit)
this.error("max depth exceeded");
}
else if (token.value === close)
--balance;
} while (balance);
};
function addField(object, seen, field, value) {
if (field.repeated) {
(object[field.name] || (object[field.name] = [])).push(value);
return;
}
if (Object.prototype.hasOwnProperty.call(seen, field.name))
throw Error(field.fullName + ": multiple values");
if (field.partOf) {
var oneofName = "$oneof:" + field.partOf.name;
if (seen[oneofName])
throw Error(field.partOf.name + ": multiple values");
seen[oneofName] = true;
}
seen[field.name] = true;
object[field.name] = value;
}
function addMapEntry(field, map, entry) {
var keyField = mapKeyField(field),
valueField = mapValueField(field),
key = Object.prototype.hasOwnProperty.call(entry, "key")
? entry.key
: defaultScalarValue(keyField),
value = Object.prototype.hasOwnProperty.call(entry, "value")
? entry.value
: defaultMapValue(valueField);
map[mapKey(keyField, key)] = value;
}
function defaultMapValue(field) {
if (field.resolvedType instanceof Type)
return field.resolvedType.create();
if (field.resolvedType instanceof Enum)
return field.typeDefault;
return defaultScalarValue(field);
}
function defaultScalarValue(field) {
if (field.type === "string")
return "";
if (field.type === "bytes")
return util.newBuffer([]);
if (field.type === "bool")
return false;
return field.defaultValue;
}
function mapKeyField(field) {
return {
name: "key",
fullName: field.fullName + ".key",
type: field.keyType,
resolvedType: null,
defaultValue: field.keyType === "bool" ? false : field.keyType === "string" ? "" : 0
};
}
function mapValueField(field) {
return {
name: "value",
fullName: field.fullName + ".value",
type: field.type,
resolvedType: field.resolvedType,
defaultValue: field.resolvedType instanceof Enum ? field.resolvedType.values[Object.keys(field.resolvedType.values)[0]] : field.typeDefault
};
}
function mapKey(field, value) {
if (types.long[field.type] !== undefined)
return String(value);
if (field.type === "bool")
return value ? "true" : "false";
return String(value);
}
function lookupField(type, name) {
var fields = type.fieldsArray;
for (var i = 0; i < fields.length; ++i) {
var field = fields[i].resolve();
if (field.name === name || underScore(field.name) === name)
return field;
if (field.delimited && field.resolvedType instanceof Type) {
var groupName = field.resolvedType.name,
lowerName = name.toLowerCase(),
compactName = compact(name);
if (groupName === name || underScore(groupName) === name
|| field.name.toLowerCase() === lowerName
|| groupName.toLowerCase() === lowerName
|| compact(field.name) === compactName
|| compact(groupName) === compactName)
return field;
}
}
return null;
}
function lookupExtension(type, name) {
var fullName = name.charAt(0) === "." ? name : "." + name,
camelName = camelCaseLastPath(fullName),
field = type.get(fullName) || type.root.lookup(fullName, Field)
|| type.get(camelName) || type.root.lookup(camelName, Field);
if (!field)
field = lookupExtensionField(type, fullName);
if (field && field.extend !== undefined && field.extensionField)
field = field.extensionField;
if (!field || field.parent !== type)
throw Error(type.fullName + ": unknown extension '" + name + "'");
return field;
}
function camelCaseLastPath(name) {
var dot = name.lastIndexOf(".");
return name.substring(0, dot + 1) + util.camelCase(name.substring(dot + 1));
}
function parseEnum(field, token, sign) {
if (sign < 0) {
if (token.type !== "number")
throw Error(field.fullName + ": enum value expected");
return checkEnumValue(field, parseInteger(token, sign, false, 32));
}
if (token.type === "word") {
var value = field.resolvedType.values[token.value];
if (value === undefined)
throw Error(field.fullName + ": enum value expected");
return value;
}
return checkEnumValue(field, parseInteger(token, sign, false, 32));
}
function parseBool(token, sign) {
if (sign < 0)
throw Error("bool value expected");
if (token.type === "word") {
switch (token.value) {
case "true":
case "True":
case "t":
return true;
case "false":
case "False":
case "f":
return false;
}
} else if (token.type === "number") {
var value = parseInteger(token, 1, true, 32);
if (value === 0)
return false;
if (value === 1)
return true;
}
throw Error("bool value expected");
}
function parseFloatValue(token, sign) {
if (token.type === "word") {
switch (token.value.toLowerCase()) {
case "inf":
case "infinity":
return sign * Infinity;
case "nan":
return NaN;
}
throw Error("float value expected");
}
if (token.type !== "number")
throw Error("float value expected");
var raw = token.value;
if (/^0[xX]/.test(raw) || /^0[0-7]/.test(raw) && raw.length > 1)
throw Error("float value expected");
if (/[fF]$/.test(raw))
raw = raw.substring(0, raw.length - 1);
return sign * Number(raw);
}
function parseInteger(token, sign, unsigned, bits) {
if (token.type !== "number")
throw Error("integer value expected");
if (sign < 0 && unsigned)
throw Error("unsigned integer value expected");
var raw = token.value,
radix = 10,
digits = raw;
if (/^0[xX]/.test(raw)) {
radix = 16;
digits = raw.substring(2);
} else if (/^0[0-7]/.test(raw) && raw.length > 1) {
radix = 8;
digits = raw.substring(1);
} else if (!/^(?:0|[1-9][0-9]*)$/.test(raw))
throw Error("integer value expected");
if (typeof util.global.BigInt === "function")
return parseIntegerBigInt(digits, radix, sign, unsigned, bits);
var value = parseInt(digits, radix) * sign;
if (bits === 64 && util.Long)
return util.Long.fromString(String(value), unsigned);
return value;
}
function parseIntegerBigInt(digits, radix, sign, unsigned, bits) {
var value,
BigInt = util.global.BigInt;
if (radix === 16)
value = BigInt("0x" + digits);
else if (radix === 8)
value = BigInt("0o" + digits);
else
value = BigInt(digits);
if (sign < 0)
value = -value;
var min,
max;
if (bits === 32) {
min = unsigned ? BigInt(0) : -BigInt(2147483648);
max = unsigned ? BigInt(4294967295) : BigInt(2147483647);
} else {
min = unsigned ? BigInt(0) : -BigInt("9223372036854775808");
max = unsigned ? BigInt("18446744073709551615") : BigInt("9223372036854775807");
}
if (value < min || value > max)
throw Error((unsigned ? "unsigned " : "") + "integer value out of range");
if (bits === 64 && util.Long)
return util.Long.fromString(value.toString(), unsigned, 10);
return Number(value);
}
function writeMessage(type, message, lines, indent, options, depth) {
if (depth > util.recursionLimit)
throw Error("max depth exceeded");
var fields = type.fieldsArray.slice().sort(util.compareFieldsById);
for (var i = 0; i < fields.length; ++i) {
var field = fields[i].resolve(),
value = message[field.name];
if (value == null || !Object.prototype.hasOwnProperty.call(message, field.name))
continue;
if (field.map)
writeMapField(field, value, lines, indent, options, depth);
else if (field.repeated)
for (var j = 0; j < value.length; ++j)
writeField(field, value[j], lines, indent, options, depth);
else
writeField(field, value, lines, indent, options, depth);
}
if (options.unknowns && message.$unknowns != null && Object.prototype.hasOwnProperty.call(message, "$unknowns"))
writeUnknowns(message.$unknowns, lines, indent);
}
function writeMapField(field, map, lines, indent, options, depth) {
var keys = Object.keys(map).sort(),
keyField = mapKeyField(field),
valueField = mapValueField(field),
name = formatFieldName(field),
sp = spaces(indent);
for (var i = 0; i < keys.length; ++i) {
if (depth + 1 > util.recursionLimit)
throw Error("max depth exceeded");
var key = keyField.long ? util.longFromKey(keys[i], keyField.type === "uint64" || keyField.type === "fixed64") : keys[i];
if (keyField.type === "bool")
key = util.boolFromKey(keys[i]);
lines.push(sp + name + " {");
lines.push(spaces(indent + 2) + "key: " + formatScalar(keyField, key));
if (valueField.resolvedType instanceof Type) {
lines.push(spaces(indent + 2) + "value {");
writeMessage(valueField.resolvedType, map[keys[i]], lines, indent + 4, options, depth + 2);
lines.push(spaces(indent + 2) + "}");
} else
lines.push(spaces(indent + 2) + "value: " + formatScalar(valueField, map[keys[i]]));
lines.push(sp + "}");
}
}
function writeField(field, value, lines, indent, options, depth) {
var name = formatFieldName(field),
sp = spaces(indent);
if (field.resolvedType instanceof Type) {
lines.push(sp + name + " {");
writeMessage(field.resolvedType, value, lines, indent + 2, options, depth + 1);
lines.push(sp + "}");
} else
lines.push(sp + name + ": " + formatScalar(field, value));
}
function writeUnknowns(unknowns, lines, indent) {
for (var i = 0; i < unknowns.length; ++i) {
var reader = Reader.create(unknowns[i]);
writeUnknownFieldSet(reader, lines, indent, undefined, textformat.unknownRecursionLimit);
if (reader.pos !== reader.len)
throw Error("invalid unknown field data");
}
}
function writeUnknownFieldSet(reader, lines, indent, endGroup, recursionBudget) {
if (recursionBudget < 0)
throw Error("max depth exceeded");
while (reader.pos < reader.len) {
var tag = reader.tag(),
fieldNumber = tag >>> 3,
wireType = tag & 7;
if (!fieldNumber)
throw Error("illegal tag: field number 0");
if (wireType === 4) {
if (fieldNumber !== endGroup)
throw Error("invalid end group tag");
return;
}
writeUnknownField(reader, fieldNumber, wireType, lines, indent, recursionBudget);
}
if (endGroup !== undefined)
throw Error("missing end group tag");
}
function writeUnknownField(reader, fieldNumber, wireType, lines, indent, recursionBudget) {
var sp = spaces(indent),
lo;
switch (wireType) {
case 0:
lines.push(sp + fieldNumber + ": " + readUnknownVarint(reader));
break;
case 1:
lo = reader.fixed32();
lines.push(sp + fieldNumber + ": 0x" + hexPad(reader.fixed32(), 8) + hexPad(lo, 8));
break;
case 2:
writeUnknownLengthDelimited(reader, fieldNumber, lines, indent, recursionBudget);
break;
case 3:
lines.push(sp + fieldNumber + " {");
writeUnknownFieldSet(reader, lines, indent + 2, fieldNumber, recursionBudget - 1);
lines.push(sp + "}");
break;
case 5:
lines.push(sp + fieldNumber + ": 0x" + hexPad(reader.fixed32(), 8));
break;
default:
throw Error("invalid wire type " + wireType);
}
}
function writeUnknownLengthDelimited(reader, fieldNumber, lines, indent, recursionBudget) {
var value = reader.bytes(),
nested = value.length && recursionBudget > 0
? tryFormatUnknownMessage(value, indent + 2, recursionBudget - 1)
: null,
sp = spaces(indent);
if (nested) {
lines.push(sp + fieldNumber + " {");
for (var i = 0; i < nested.length; ++i)
lines.push(nested[i]);
lines.push(sp + "}");
} else
lines.push(sp + fieldNumber + ": " + quoteBytes(value));
}
function tryFormatUnknownMessage(bytes, indent, recursionBudget) {
var reader = Reader.create(bytes),
lines = [];
try {
writeUnknownFieldSet(reader, lines, indent, undefined, recursionBudget);
return reader.pos === reader.len ? lines : null;
} catch (e) {
return null;
}
}
function readUnknownVarint(reader) {
var BigInt = util.global.BigInt;
if (typeof BigInt !== "function")
return String(reader.uint64());
var value = BigInt(0),
shift = BigInt(0),
b;
for (var i = 0; i < 10; ++i) {
if (reader.pos >= reader.len)
throw Error("invalid varint encoding");
b = reader.buf[reader.pos++];
value += BigInt(b & 127) << shift;
if (b < 128)
return value.toString();
shift += BigInt(7);
}
throw Error("invalid varint encoding");
}
function hexPad(value, length) {
var str = value.toString(16);
while (str.length < length)
str = "0" + str;
return str;
}
function formatFieldName(field) {
if (field.declaringField || field.name.charAt(0) === ".")
return "[" + formatExtensionName(field) + "]";
if (field.delimited && field.resolvedType instanceof Type && compact(field.name) === compact(field.resolvedType.name))
return field.resolvedType.name;
return underScore(field.name);
}
function formatExtensionName(field) {
var name = field.name.replace(/^\./, ""),
dot = name.lastIndexOf("."),
path = name.substring(0, dot + 1),
last = name.substring(dot + 1);
if (field.delimited && field.resolvedType instanceof Type && field.resolvedType.group)
last = last.toLowerCase();
else
last = underScore(last);
return path + last;
}
function formatScalar(field, value) {
if (field.resolvedType instanceof Enum) {
var name = field.resolvedType.valuesById[value];
return name === undefined ? String(value) : name;
}
switch (field.type) {
case "string":
return quoteBytes(utf8Bytes(String(value)));
case "bytes":
return quoteBytes(bytesValue(value));
case "bool":
return value ? "true" : "false";
case "double":
case "float":
if (isNaN(value))
return "nan";
if (value === 0 && 1 / value === -Infinity)
return "-0";
if (value === Infinity)
return "inf";
if (value === -Infinity)
return "-inf";
return String(value);
default:
return String(value);
}
}
function bytesValue(value) {
if (typeof value === "string") {
var length = util.base64.length(value),
buffer = util.newBuffer(length);
util.base64.decode(value, buffer, 0);
return buffer;
}
return value;
}
function quoteBytes(bytes) {
var out = "\"",
oct;
for (var i = 0; i < bytes.length; ++i) {
var b = bytes[i];
switch (b) {
case 9: out += "\\t"; break;
case 10: out += "\\n"; break;
case 13: out += "\\r"; break;
case 34: out += "\\\""; break;
case 92: out += "\\\\"; break;
default:
if (b >= 32 && b <= 126)
out += String.fromCharCode(b);
else {
oct = b.toString(8);
out += "\\" + (oct.length < 2 ? "00" : oct.length < 3 ? "0" : "") + oct;
}
break;
}
}
return out + "\"";
}
function spaces(indent) {
var str = "";
while (str.length < indent)
str += " ";
return str;
}
function utf8Read(bytes, strict) {
if (strict && !validUtf8(bytes))
throw Error("invalid UTF-8 string");
var buffer = util.newBuffer(bytes);
return util.utf8.read(buffer, 0, buffer.length);
}
function validUtf8(bytes) {
for (var i = 0, b; i < bytes.length;) {
b = bytes[i++];
if (b <= 0x7F)
continue;
if (b >= 0xC2 && b <= 0xDF) {
if ((b = bytes[i++]) < 0x80 || b > 0xBF)
return false;
} else if (b === 0xE0) {
if ((b = bytes[i++]) < 0xA0 || b > 0xBF || (b = bytes[i++]) < 0x80 || b > 0xBF)
return false;
} else if (b >= 0xE1 && b <= 0xEC || b >= 0xEE && b <= 0xEF) {
if ((b = bytes[i++]) < 0x80 || b > 0xBF || (b = bytes[i++]) < 0x80 || b > 0xBF)
return false;
} else if (b === 0xED) {
if ((b = bytes[i++]) < 0x80 || b > 0x9F || (b = bytes[i++]) < 0x80 || b > 0xBF)
return false;
} else if (b === 0xF0) {
if ((b = bytes[i++]) < 0x90 || b > 0xBF || (b = bytes[i++]) < 0x80 || b > 0xBF || (b = bytes[i++]) < 0x80 || b > 0xBF)
return false;
} else if (b >= 0xF1 && b <= 0xF3) {
if ((b = bytes[i++]) < 0x80 || b > 0xBF || (b = bytes[i++]) < 0x80 || b > 0xBF || (b = bytes[i++]) < 0x80 || b > 0xBF)
return false;
} else if (b === 0xF4) {
if ((b = bytes[i++]) < 0x80 || b > 0x8F || (b = bytes[i++]) < 0x80 || b > 0xBF || (b = bytes[i++]) < 0x80 || b > 0xBF)
return false;
} else
return false;
}
return true;
}
function utf8Bytes(str) {
var buffer = util.newBuffer(util.utf8.length(str));
util.utf8.write(str, buffer, 0);
return buffer;
}
function pushUtf8(bytes, code) {
if (code < 0x80) {
bytes.push(code);
} else if (code < 0x800) {
bytes.push(code >> 6 | 192, code & 63 | 128);
} else if (code < 0x10000) {
bytes.push(code >> 12 | 224, code >> 6 & 63 | 128, code & 63 | 128);
} else {
bytes.push(code >> 18 | 240, code >> 12 & 63 | 128, code >> 6 & 63 | 128, code & 63 | 128);
}
}
function verifyTextMessage(type, message) {
var fields = type.fieldsArray;
for (var i = 0; i < fields.length; ++i) {
var field = fields[i].resolve(),
value = message[field.name];
if (value == null || !Object.prototype.hasOwnProperty.call(message, field.name)) {
if (field.required)
return field.fullName + ": missing required field";
continue;
}
if (field.map) {
if (field.resolvedType instanceof Type)
for (var key in value)
if (Object.prototype.hasOwnProperty.call(value, key)) {
var mapErr = verifyTextMessage(field.resolvedType, value[key]);
if (mapErr)
return mapErr;
}
} else if (field.repeated) {
if (field.resolvedType instanceof Type)
for (var j = 0; j < value.length; ++j) {
var repeatedErr = verifyTextMessage(field.resolvedType, value[j]);
if (repeatedErr)
return repeatedErr;
}
} else if (field.resolvedType instanceof Type) {
var err = verifyTextMessage(field.resolvedType, value);
if (err)
return err;
}
}
return null;
}
function isSeparator(token) {
return token.value === "," || token.value === ";";
}
function validateTypeUrl(typeUrl) {
for (var i = 0; i < typeUrl.length; ++i)
if (typeUrl.charAt(i) === "%") {
if (i + 2 >= typeUrl.length || !hexRe.test(typeUrl.charAt(i + 1)) || !hexRe.test(typeUrl.charAt(i + 2)))
throw Error("invalid Any type URL");
i += 2;
}
}
function lookupExtensionField(type, fullName) {
var fields = type.fieldsArray;
for (var i = 0; i < fields.length; ++i) {
var field = fields[i].resolve();
if (field.name.charAt(0) === "." && "." + formatExtensionName(field) === fullName)
return field;
}
return null;
}
function checkEnumValue(field, value) {
if (isClosedEnum(field) && field.resolvedType.valuesById[value] === undefined)
throw Error(field.fullName + ": enum value expected");
return value;
}
function isClosedEnum(field) {
return field.resolvedType
&& field.resolvedType._features
&& field.resolvedType._features.enum_type === "CLOSED";
}
function compact(str) {
return str.replace(/_/g, "").toLowerCase();
}
function underScore(str) {
return str.substring(0, 1)
+ str.substring(1)
.replace(/([A-Z])(?=[a-z]|$)/g, function($0, $1) { return "_" + $1.toLowerCase(); });
}