/*!
 * TSBuffer v2.2.10
 * -----------------------------------------
 * MIT LICENSE
 * KingWorks (C) Copyright 2025
 * https://github.com/k8w/tsbuffer
 */
'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

require('k8w-extend-native');
var tslib = require('tslib');
var tsbufferValidator = require('tsbuffer-validator');
var tsbufferSchema = require('tsbuffer-schema');

var Base64Util = /** @class */ (function () {
    function Base64Util() {
    }
    Base64Util.bufferToBase64 = function (buf) {
        if (typeof Buffer !== "undefined") {
            return Buffer.from(buf).toString("base64");
        }
        var base64 = "";
        var len = buf.length;
        for (var i = 0; i < len; i += 3) {
            base64 += base64Chars[buf[i] >> 2];
            base64 += base64Chars[((buf[i] & 3) << 4) | (buf[i + 1] >> 4)];
            base64 += base64Chars[((buf[i + 1] & 15) << 2) | (buf[i + 2] >> 6)];
            base64 += base64Chars[buf[i + 2] & 63];
        }
        if (len % 3 === 2) {
            base64 = base64.substring(0, base64.length - 1) + "=";
        }
        else if (len % 3 === 1) {
            base64 = base64.substring(0, base64.length - 2) + "==";
        }
        return base64;
    };
    Base64Util.base64ToBuffer = function (base64) {
        if (typeof Buffer !== "undefined") {
            return new Uint8Array(Buffer.from(base64, "base64"));
        }
        var bufferLength = base64.length * 0.75, len = base64.length, p = 0;
        var encoded1, encoded2, encoded3, encoded4;
        if (base64[base64.length - 1] === "=") {
            bufferLength--;
            if (base64[base64.length - 2] === "=") {
                bufferLength--;
            }
        }
        var buf = new Uint8Array(bufferLength);
        for (var i = 0; i < len; i += 4) {
            encoded1 = lookup[base64.charCodeAt(i)];
            encoded2 = lookup[base64.charCodeAt(i + 1)];
            encoded3 = lookup[base64.charCodeAt(i + 2)];
            encoded4 = lookup[base64.charCodeAt(i + 3)];
            buf[p++] = (encoded1 << 2) | (encoded2 >> 4);
            buf[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
            buf[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
        }
        return buf;
    };
    return Base64Util;
}());
/*base64*/
var base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Use a lookup table to find the index.
var lookup = typeof Uint8Array === "undefined" ? [] : new Uint8Array(256);
for (var i = 0; i < base64Chars.length; i++) {
    lookup[base64Chars.charCodeAt(i)] = i;
}
/*end*/

var CoderUtil = /** @class */ (function () {
    function CoderUtil() {
    }
    CoderUtil.isJsonCompatible = function (schema, type, protoHelper) {
        var _this = this;
        var schemaInfo = schema;
        var key = type === "encode" ? "isJsonEncodable" : "isJsonDecodable";
        if (schemaInfo[key] === undefined) {
            // Just for avoding circular recursive, the value should be overwriten below
            schemaInfo[key] = true;
            switch (schema.type) {
                case tsbufferSchema.SchemaType.Array:
                    schemaInfo[key] = this.isJsonCompatible(schema.elementType, type, protoHelper);
                    break;
                case tsbufferSchema.SchemaType.Tuple:
                    schemaInfo[key] = schema.elementTypes.every(function (v) {
                        return _this.isJsonCompatible(v, type, protoHelper);
                    });
                    break;
                case tsbufferSchema.SchemaType.Interface:
                    var flatSchema = protoHelper.getFlatInterfaceSchema(schema);
                    schemaInfo[key] = flatSchema.properties.every(function (v) {
                        return _this.isJsonCompatible(v.type, type, protoHelper);
                    });
                    if (flatSchema.indexSignature) {
                        schemaInfo[key] =
                            schemaInfo[key] &&
                                this.isJsonCompatible(flatSchema.indexSignature.type, type, protoHelper);
                    }
                    break;
                case tsbufferSchema.SchemaType.IndexedAccess:
                case tsbufferSchema.SchemaType.Reference: {
                    var parsed = protoHelper.parseReference(schema);
                    schemaInfo[key] = this.isJsonCompatible(parsed, type, protoHelper);
                    break;
                }
                case tsbufferSchema.SchemaType.Union:
                case tsbufferSchema.SchemaType.Intersection:
                    schemaInfo[key] = schema.members.every(function (v) {
                        return _this.isJsonCompatible(v.type, type, protoHelper);
                    });
                    break;
                case tsbufferSchema.SchemaType.NonNullable:
                    schemaInfo[key] = this.isJsonCompatible(schema.target, type, protoHelper);
                    break;
                case tsbufferSchema.SchemaType.Pick:
                case tsbufferSchema.SchemaType.Partial:
                case tsbufferSchema.SchemaType.Omit:
                case tsbufferSchema.SchemaType.Overwrite: {
                    var parsed = protoHelper.parseMappedType(schema);
                    if (parsed.type === tsbufferSchema.SchemaType.Interface) {
                        var flatSchema_1 = protoHelper.getFlatInterfaceSchema(schema);
                        schemaInfo[key] = flatSchema_1.properties.every(function (v) {
                            return _this.isJsonCompatible(v.type, type, protoHelper);
                        });
                        if (flatSchema_1.indexSignature) {
                            schemaInfo[key] =
                                schemaInfo[key] &&
                                    this.isJsonCompatible(flatSchema_1.indexSignature.type, type, protoHelper);
                        }
                    }
                    else if (parsed.type === tsbufferSchema.SchemaType.Union || parsed.type === tsbufferSchema.SchemaType.Intersection) {
                        schemaInfo[key] = parsed.members.every(function (v) {
                            return _this.isJsonCompatible(v.type, type, protoHelper);
                        });
                    }
                    break;
                }
                case tsbufferSchema.SchemaType.Custom:
                case tsbufferSchema.SchemaType.Date:
                case tsbufferSchema.SchemaType.Buffer:
                    schemaInfo[key] = false;
                    break;
                default:
                    schemaInfo[key] = true;
                    break;
            }
        }
        return schemaInfo[key];
    };
    return CoderUtil;
}());

/** @internal */
var IdBlockUtil = /** @class */ (function () {
    function IdBlockUtil() {
    }
    IdBlockUtil.getPayloadLengthInfo = function (parsedSchema, protoHelper) {
        switch (parsedSchema.type) {
            case tsbufferSchema.SchemaType.Boolean:
            case tsbufferSchema.SchemaType.Enum:
                return { lengthType: LengthType.Varint };
            case tsbufferSchema.SchemaType.Number:
                if (!parsedSchema.scalarType ||
                    parsedSchema.scalarType.includes("64") ||
                    parsedSchema.scalarType === "double") {
                    return { lengthType: LengthType.Bit64 };
                }
                else if (parsedSchema.scalarType && parsedSchema.scalarType.startsWith("big")) {
                    return { lengthType: LengthType.LengthDelimited };
                }
                else {
                    return { lengthType: LengthType.Varint };
                }
            case tsbufferSchema.SchemaType.Buffer:
            case tsbufferSchema.SchemaType.String:
            case tsbufferSchema.SchemaType.Any:
            case tsbufferSchema.SchemaType.Object:
                return { lengthType: LengthType.LengthDelimited };
            case tsbufferSchema.SchemaType.Interface:
            case tsbufferSchema.SchemaType.Pick:
            case tsbufferSchema.SchemaType.Partial:
            case tsbufferSchema.SchemaType.Omit:
            case tsbufferSchema.SchemaType.Union:
            case tsbufferSchema.SchemaType.Intersection:
                return { lengthType: LengthType.IdBlock };
            case tsbufferSchema.SchemaType.Array:
            case tsbufferSchema.SchemaType.Overwrite:
            case tsbufferSchema.SchemaType.Tuple:
                return {
                    lengthType: LengthType.LengthDelimited,
                    needLengthPrefix: true,
                };
            case tsbufferSchema.SchemaType.Literal:
                return {
                    lengthType: LengthType.LengthDelimited,
                    needLengthPrefix: false,
                };
            case tsbufferSchema.SchemaType.Date:
                return { lengthType: LengthType.Varint };
            case tsbufferSchema.SchemaType.NonNullable:
                return this.getPayloadLengthInfo(protoHelper.parseReference(parsedSchema.target), protoHelper);
            case tsbufferSchema.SchemaType.Custom:
                return { lengthType: LengthType.LengthDelimited };
            default:
                // @ts-expect-error
                throw new Error("Unrecognized schema type: ".concat(parsedSchema.type));
        }
    };
    return IdBlockUtil;
}());
/** @internal */
var LengthType;
(function (LengthType) {
    LengthType[LengthType["LengthDelimited"] = 0] = "LengthDelimited";
    LengthType[LengthType["Varint"] = 1] = "Varint";
    LengthType[LengthType["Bit64"] = 2] = "Bit64";
    LengthType[LengthType["IdBlock"] = 3] = "IdBlock";
})(LengthType || (LengthType = {}));

/** @internal */
var SchemaUtil = /** @class */ (function () {
    function SchemaUtil() {
    }
    /** type类型是否能编码为该literal */
    SchemaUtil.canBeLiteral = function (schema, literal) {
        var _this = this;
        if (schema.type === tsbufferSchema.SchemaType.Union) {
            return schema.members.some(function (v) { return _this.canBeLiteral(v.type, literal); });
        }
        if (schema.type === tsbufferSchema.SchemaType.Any) {
            return true;
        }
        if (schema.type === tsbufferSchema.SchemaType.Literal && schema.literal === literal) {
            return true;
        }
        return false;
    };
    return SchemaUtil;
}());

var TypedArrays = {
    Int8Array: Int8Array,
    Int16Array: Int16Array,
    Int32Array: Int32Array,
    Uint8Array: Uint8Array,
    Uint16Array: Uint16Array,
    Uint32Array: Uint32Array,
    Float32Array: Float32Array,
    Float64Array: Float64Array,
};

/**!
 * From [protobuf.js](https://github.com/protobufjs/protobuf.js/blob/master/lib/utf8/index.js)
 */
var Utf8CoderJS = {
    measureLength: function (str) {
        var len = 0, c = 0;
        for (var i = 0; i < str.length; ++i) {
            c = str.charCodeAt(i);
            if (c < 128)
                len += 1;
            else if (c < 2048)
                len += 2;
            else if ((c & 0xfc00) === 0xd800 && (str.charCodeAt(i + 1) & 0xfc00) === 0xdc00) {
                ++i;
                len += 4;
            }
            else
                len += 3;
        }
        return len;
    },
    write: function (str, buf, pos) {
        var start = pos, c1, // character 1
        c2; // character 2
        for (var i = 0; i < str.length; ++i) {
            c1 = str.charCodeAt(i);
            if (c1 < 128) {
                buf[pos++] = c1;
            }
            else if (c1 < 2048) {
                buf[pos++] = (c1 >> 6) | 192;
                buf[pos++] = (c1 & 63) | 128;
            }
            else if ((c1 & 0xfc00) === 0xd800 && ((c2 = str.charCodeAt(i + 1)) & 0xfc00) === 0xdc00) {
                c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff);
                ++i;
                buf[pos++] = (c1 >> 18) | 240;
                buf[pos++] = ((c1 >> 12) & 63) | 128;
                buf[pos++] = ((c1 >> 6) & 63) | 128;
                buf[pos++] = (c1 & 63) | 128;
            }
            else {
                buf[pos++] = (c1 >> 12) | 224;
                buf[pos++] = ((c1 >> 6) & 63) | 128;
                buf[pos++] = (c1 & 63) | 128;
            }
        }
        return pos - start;
    },
    read: function (buf, pos, length) {
        if (length < 1) {
            return "";
        }
        var str = "";
        for (var i = pos, end = pos + length; i < end;) {
            var t = buf[i++];
            if (t <= 0x7f) {
                str += String.fromCharCode(t);
            }
            else if (t >= 0xc0 && t < 0xe0) {
                str += String.fromCharCode(((t & 0x1f) << 6) | (buf[i++] & 0x3f));
            }
            else if (t >= 0xe0 && t < 0xf0) {
                str += String.fromCharCode(((t & 0xf) << 12) | ((buf[i++] & 0x3f) << 6) | (buf[i++] & 0x3f));
            }
            else if (t >= 0xf0) {
                var t2 = (((t & 7) << 18) |
                    ((buf[i++] & 0x3f) << 12) |
                    ((buf[i++] & 0x3f) << 6) |
                    (buf[i++] & 0x3f)) -
                    0x10000;
                str += String.fromCharCode(0xd800 + (t2 >> 10));
                str += String.fromCharCode(0xdc00 + (t2 & 0x3ff));
            }
        }
        return str;
    },
};
var Utf8CoderNode = {
    measureLength: function (str) { return Buffer.byteLength(str, "utf-8"); },
    write: function (str, buf, pos) {
        return Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength).write(str, pos, "utf-8");
    },
    read: function (buf, pos, length) {
        return Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength).toString("utf-8", pos, pos + length);
    },
};
/**
 * 自动判断环境，选择使用NodeJS Native方法编码或是JS编码
 */
var Utf8Coder = typeof Buffer !== "undefined" && Buffer.from && Buffer.prototype.write
    ? Utf8CoderNode
    : Utf8CoderJS;

/** @internal */
var Varint64 = /** @class */ (function () {
    function Varint64(high, low, byteLength) {
        this.uint32s = new Uint32Array([high, low]);
        if (byteLength !== undefined) {
            this._byteLength = byteLength;
        }
    }
    Varint64.from = function (value) {
        if (value === 0) {
            return this.Zero;
        }
        var sign = value < 0;
        if (sign) {
            value = -value;
        }
        var lo = value >>> 0, hi = ((value - lo) / 4294967296) >>> 0;
        if (sign) {
            hi = ~hi >>> 0;
            lo = ~lo >>> 0;
            if (++lo > 4294967295) {
                lo = 0;
                if (++hi > 4294967295)
                    hi = 0;
            }
        }
        return new Varint64(hi, lo);
    };
    Varint64.prototype.toNumber = function (unsigned) {
        if (!unsigned && this.uint32s[0] >>> 31) {
            var low = (~this.uint32s[1] + 1) >>> 0, high = ~this.uint32s[0] >>> 0;
            if (!low)
                high = (high + 1) >>> 0;
            return -(low + high * 4294967296);
        }
        return this.uint32s[1] + this.uint32s[0] * 4294967296;
    };
    Varint64.prototype.zzEncode = function () {
        var mask = this.uint32s[0] >> 31;
        this.uint32s[0] = (((this.uint32s[0] << 1) | (this.uint32s[1] >>> 31)) ^ mask) >>> 0;
        this.uint32s[1] = ((this.uint32s[1] << 1) ^ mask) >>> 0;
        return this;
    };
    Varint64.prototype.zzDecode = function () {
        var mask = -(this.uint32s[1] & 1);
        this.uint32s[1] = (((this.uint32s[1] >>> 1) | (this.uint32s[0] << 31)) ^ mask) >>> 0;
        this.uint32s[0] = ((this.uint32s[0] >>> 1) ^ mask) >>> 0;
        return this;
    };
    Object.defineProperty(Varint64.prototype, "byteLength", {
        get: function () {
            if (this._byteLength === undefined) {
                var part0 = this.uint32s[1], part1 = ((this.uint32s[1] >>> 28) | (this.uint32s[0] << 4)) >>> 0, part2 = this.uint32s[0] >>> 24;
                this._byteLength =
                    part2 === 0
                        ? part1 === 0
                            ? part0 < 16384
                                ? part0 < 128
                                    ? 1
                                    : 2
                                : part0 < 2097152
                                    ? 3
                                    : 4
                            : part1 < 16384
                                ? part1 < 128
                                    ? 5
                                    : 6
                                : part1 < 2097152
                                    ? 7
                                    : 8
                        : part2 < 128
                            ? 9
                            : 10;
            }
            return this._byteLength;
        },
        enumerable: false,
        configurable: true
    });
    /**
     * 编码
     * @param buf
     * @param pos
     * @returns 编码后最新的pos
     */
    Varint64.prototype.writeToBuffer = function (buf, pos) {
        while (this.uint32s[0]) {
            buf[pos++] = (this.uint32s[1] & 127) | 128;
            this.uint32s[1] = ((this.uint32s[1] >>> 7) | (this.uint32s[0] << 25)) >>> 0;
            this.uint32s[0] >>>= 7;
        }
        while (this.uint32s[1] > 127) {
            buf[pos++] = (this.uint32s[1] & 127) | 128;
            this.uint32s[1] = this.uint32s[1] >>> 7;
        }
        buf[pos++] = this.uint32s[1];
        return pos;
    };
    Varint64.readFromBuffer = function (buf, pos) {
        var startPos = pos;
        var hi = 0, lo = 0;
        var i = 0;
        if (buf.byteLength - pos > 4) {
            // fast route (lo)
            for (; i < 4; ++i) {
                // 1st..4th
                lo = (lo | ((buf[pos] & 127) << (i * 7))) >>> 0;
                if (buf[pos++] < 128)
                    return new Varint64(hi, lo, pos - startPos);
            }
            // 5th
            lo = (lo | ((buf[pos] & 127) << 28)) >>> 0;
            hi = (hi | ((buf[pos] & 127) >> 4)) >>> 0;
            if (buf[pos++] < 128)
                return new Varint64(hi, lo, pos - startPos);
            i = 0;
        }
        else {
            for (; i < 3; ++i) {
                /* istanbul ignore if */
                if (pos >= buf.byteLength)
                    throw new Error("Read varint error: index out of range");
                // 1st..3th
                lo = (lo | ((buf[pos] & 127) << (i * 7))) >>> 0;
                if (buf[pos++] < 128)
                    return new Varint64(hi, lo, pos - startPos);
            }
            // 4th
            lo = (lo | ((buf[pos++] & 127) << (i * 7))) >>> 0;
            return new Varint64(hi, lo, pos - startPos);
        }
        if (buf.byteLength - pos > 4) {
            // fast route (hi)
            for (; i < 5; ++i) {
                // 6th..10th
                hi = (hi | ((buf[pos] & 127) << (i * 7 + 3))) >>> 0;
                if (buf[pos++] < 128)
                    return new Varint64(hi, lo, pos - startPos);
            }
        }
        else {
            for (; i < 5; ++i) {
                /* istanbul ignore if */
                if (pos >= buf.byteLength)
                    throw new Error("Read varint error: index out of range");
                // 6th..10th
                hi = (hi | ((buf[pos] & 127) << (i * 7 + 3))) >>> 0;
                if (buf[pos++] < 128)
                    return new Varint64(hi, lo, pos - startPos);
            }
        }
        /* istanbul ignore next */
        throw Error("invalid varint encoding");
    };
    Varint64.Zero = new Varint64(0, 0);
    return Varint64;
}());

var BufferReader = /** @class */ (function () {
    function BufferReader() {
        this._pos = 0;
    }
    BufferReader.prototype.load = function (buf, pos) {
        if (pos === void 0) { pos = 0; }
        this._buf = buf;
        this._pos = pos;
        this._view = new DataView(buf.buffer);
    };
    BufferReader.prototype.readVarint = function () {
        var varint = Varint64.readFromBuffer(this._buf, this._pos);
        this._pos += varint.byteLength;
        return varint;
    };
    BufferReader.prototype.readUint = function () {
        return this.readVarint().toNumber(true);
    };
    BufferReader.prototype.readInt = function () {
        return this.readVarint().zzDecode().toNumber();
    };
    BufferReader.prototype.readDouble = function () {
        var pos = this._pos;
        this._pos += 8;
        return this._view.getFloat64(this._buf.byteOffset + pos);
    };
    BufferReader.prototype.readString = function () {
        var strByteLength = this.readUint();
        var str = Utf8Coder.read(this._buf, this._pos, strByteLength);
        this._pos += strByteLength;
        return str;
    };
    BufferReader.prototype.readBuffer = function () {
        var bufByteLength = this.readUint();
        var buf = this._buf.subarray(this._pos, this._pos + bufByteLength);
        this._pos += bufByteLength;
        return buf;
    };
    BufferReader.prototype.skip = function (byteLength) {
        this._pos += byteLength;
    };
    BufferReader.prototype.skipByLengthType = function (lengthType) {
        if (lengthType === LengthType.Bit64) {
            this._pos += 8;
        }
        else if (lengthType === LengthType.Varint) {
            this.readVarint();
        }
        else if (lengthType === LengthType.LengthDelimited) {
            var bufByteLength = this.readUint();
            this._pos += bufByteLength;
        }
        else if (lengthType === LengthType.IdBlock) {
            this.skipIdBlock();
        }
        else {
            throw new Error("Unknown lengthType: " + lengthType);
        }
    };
    BufferReader.prototype.skipIdBlock = function () {
        var idNum = this.readUint();
        for (var i = 0; i < idNum; ++i) {
            var id = this.readUint();
            var lengthType = id & 3;
            this.skipByLengthType(lengthType);
        }
    };
    BufferReader.prototype.readBoolean = function () {
        var value = this._view.getUint8(this._buf.byteOffset + this._pos++);
        if (value === 255) {
            return true;
        }
        else if (value === 0) {
            return false;
        }
        else {
            throw new Error("Invalid boolean encoding [".concat(value, "] at pos ").concat(this._pos - 1));
        }
    };
    Object.defineProperty(BufferReader.prototype, "unreadByteLength", {
        get: function () {
            return this._buf.byteLength - this._pos;
        },
        enumerable: false,
        configurable: true
    });
    BufferReader.prototype.dispose = function () {
        this._buf = this._view = undefined;
    };
    return BufferReader;
}());

/** @internal */
var Decoder = /** @class */ (function () {
    function Decoder(options) {
        this._options = options;
        this._reader = new BufferReader();
        this._validator = options.validator;
    }
    Decoder.prototype.decode = function (buffer, schema) {
        this._reader.load(buffer);
        return this._read(schema);
    };
    Decoder.prototype.decodeJSON = function (json, schema) {
        var _this = this;
        if (json === null ||
            CoderUtil.isJsonCompatible(schema, "decode", this._validator.protoHelper)) {
            return json;
        }
        // 递归 只处理 ArrayBuffer、Date、ObjectId
        switch (schema.type) {
            case tsbufferSchema.SchemaType.Array:
                if (!Array.isArray(json)) {
                    break;
                }
                return json.map(function (v) { return _this.decodeJSON(v, schema.elementType); });
            case tsbufferSchema.SchemaType.Tuple:
                if (!Array.isArray(json)) {
                    break;
                }
                return json.map(function (v, i) { return _this.decodeJSON(v, schema.elementTypes[i]); });
            case tsbufferSchema.SchemaType.Interface:
                if (json.constructor !== Object) {
                    break;
                }
                json = Object.assign({}, json);
                var flatSchema = this._validator.protoHelper.getFlatInterfaceSchema(schema);
                var _loop_1 = function (key) {
                    var property = flatSchema.properties.find(function (v) { return v.name === key; });
                    if (property) {
                        json[key] = this_1.decodeJSON(json[key], property.type);
                    }
                    else if (flatSchema.indexSignature) {
                        json[key] = this_1.decodeJSON(json[key], flatSchema.indexSignature.type);
                    }
                };
                var this_1 = this;
                for (var key in json) {
                    _loop_1(key);
                }
                return json;
            case tsbufferSchema.SchemaType.Date:
                if (typeof json !== "string" && typeof json !== "number") {
                    break;
                }
                return new Date(json);
            case tsbufferSchema.SchemaType.Partial:
            case tsbufferSchema.SchemaType.Pick:
            case tsbufferSchema.SchemaType.Omit:
            case tsbufferSchema.SchemaType.Overwrite:
                var parsed = this._validator.protoHelper.parseMappedType(schema);
                if (parsed.type === tsbufferSchema.SchemaType.Interface) {
                    if (json.constructor !== Object) {
                        break;
                    }
                    json = Object.assign({}, json);
                    var flatSchema_1 = this._validator.protoHelper.getFlatInterfaceSchema(schema);
                    var _loop_2 = function (key) {
                        var property = flatSchema_1.properties.find(function (v) { return v.name === key; });
                        if (property) {
                            json[key] = this_2.decodeJSON(json[key], property.type);
                        }
                        else if (flatSchema_1.indexSignature) {
                            json[key] = this_2.decodeJSON(json[key], flatSchema_1.indexSignature.type);
                        }
                    };
                    var this_2 = this;
                    for (var key in json) {
                        _loop_2(key);
                    }
                    return json;
                }
                else if (parsed.type === tsbufferSchema.SchemaType.Union || parsed.type === tsbufferSchema.SchemaType.Intersection) {
                    for (var _i = 0, _a = parsed.members; _i < _a.length; _i++) {
                        var member = _a[_i];
                        json = this.decodeJSON(json, member.type);
                    }
                    return json;
                }
                break;
            case tsbufferSchema.SchemaType.Buffer:
                if (typeof json !== "string") {
                    break;
                }
                var uint8Arr = Base64Util.base64ToBuffer(json);
                return this._getBufferValue(uint8Arr, schema);
            case tsbufferSchema.SchemaType.IndexedAccess:
            case tsbufferSchema.SchemaType.Reference:
            case tsbufferSchema.SchemaType.Keyof:
                return this.decodeJSON(json, this._validator.protoHelper.parseReference(schema));
            case tsbufferSchema.SchemaType.Union:
            case tsbufferSchema.SchemaType.Intersection: {
                // 逐个编码 然后合并 （失败的会原值返回，所以不影响结果）
                for (var _b = 0, _c = schema.members; _b < _c.length; _b++) {
                    var member = _c[_b];
                    json = this.decodeJSON(json, member.type);
                }
                return json;
            }
            case tsbufferSchema.SchemaType.NonNullable:
                return this.decodeJSON(json, schema.target);
            case tsbufferSchema.SchemaType.Custom:
                if (schema.decodeJSON) {
                    return schema.decodeJSON(json);
                }
                break;
            default:
                schema.type;
        }
        return json;
    };
    Decoder.prototype._read = function (schema) {
        switch (schema.type) {
            case tsbufferSchema.SchemaType.Boolean:
                return this._reader.readBoolean();
            case tsbufferSchema.SchemaType.Number:
                return this._readNumber(schema);
            case tsbufferSchema.SchemaType.String:
                return this._reader.readString();
            case tsbufferSchema.SchemaType.Array: {
                var output = [];
                // 数组长度：Varint
                var length_1 = this._reader.readUint();
                for (var i = 0; i < length_1; ++i) {
                    var item = this._read(schema.elementType);
                    output.push(item);
                }
                return output;
            }
            case tsbufferSchema.SchemaType.Tuple: {
                if (schema.elementTypes.length > 64) {
                    throw new Error("Elements oversized, maximum supported tuple elements is 64, now get " +
                        schema.elementTypes.length);
                }
                var output = [];
                // PayloadMask: Varint64
                var payloadMask = this._reader.readVarint();
                // 计算maskIndices
                var maskIndices = [];
                // Low
                for (var i = 0; i < 32; ++i) {
                    if (payloadMask.uint32s[1] & (1 << i)) {
                        maskIndices.push(i);
                    }
                }
                // High
                for (var i = 0; i < 32; ++i) {
                    if (payloadMask.uint32s[0] & (1 << i)) {
                        maskIndices.push(i + 32);
                    }
                }
                if (!maskIndices.length) {
                    return [];
                }
                var maxIndex = maskIndices.last();
                for (var i = 0, nextMaskIndex = 0, next = maskIndices[0]; i <= maxIndex; ++i) {
                    if (i === next) {
                        output[i] = this._read(schema.elementTypes[i]);
                        ++nextMaskIndex;
                        next = maskIndices[nextMaskIndex];
                    }
                    else {
                        output[i] = undefined;
                    }
                }
                // undefined as null
                for (var i = 0; i < schema.elementTypes.length; ++i) {
                    if (this._undefinedAsNull(output[i], schema.elementTypes[i], schema.optionalStartIndex !== undefined && i >= schema.optionalStartIndex)) {
                        output[i] = null;
                    }
                }
                return output;
            }
            case tsbufferSchema.SchemaType.Enum:
                var enumId_1 = this._reader.readVarint().toNumber();
                var enumItem = schema.members.find(function (v) { return v.id === enumId_1; });
                if (!enumItem) {
                    throw new Error("Invalid enum encoding: unexpected id ".concat(enumId_1));
                }
                return enumItem.value;
            case tsbufferSchema.SchemaType.Any:
            case tsbufferSchema.SchemaType.Object:
                var jsonStr = this._reader.readString();
                if (jsonStr === "undefined") {
                    return undefined;
                }
                return JSON.parse(jsonStr);
            case tsbufferSchema.SchemaType.Literal:
                return schema.literal;
            case tsbufferSchema.SchemaType.Interface:
                return this._readInterface(schema);
            case tsbufferSchema.SchemaType.Buffer:
                var uint8Arr = this._reader.readBuffer();
                return this._getBufferValue(uint8Arr, schema);
            case tsbufferSchema.SchemaType.IndexedAccess:
            case tsbufferSchema.SchemaType.Reference:
            case tsbufferSchema.SchemaType.Keyof:
                return this._read(this._validator.protoHelper.parseReference(schema));
            case tsbufferSchema.SchemaType.Partial:
            case tsbufferSchema.SchemaType.Pick:
            case tsbufferSchema.SchemaType.Omit:
            case tsbufferSchema.SchemaType.Overwrite:
                var parsed = this._validator.protoHelper.parseMappedType(schema);
                if (parsed.type === tsbufferSchema.SchemaType.Interface) {
                    return this._readPureMappedType(schema);
                }
                else {
                    return this._readUnionOrIntersection(parsed);
                }
            case tsbufferSchema.SchemaType.Union:
            case tsbufferSchema.SchemaType.Intersection:
                return this._readUnionOrIntersection(schema);
            case tsbufferSchema.SchemaType.Date:
                return new Date(this._reader.readUint());
            case tsbufferSchema.SchemaType.NonNullable:
                return this._read(schema.target);
            case tsbufferSchema.SchemaType.Custom:
                if (!schema.decode) {
                    throw new Error("Missing decode method for CustomTypeSchema");
                }
                var buf = this._reader.readBuffer();
                return schema.decode(buf);
            default:
                // @ts-expect-error
                throw new Error("Unrecognized schema type: ".concat(schema.type));
        }
    };
    /**
     * PureMappedType 每一层的target 都是MappedType或Interface（最终层）
     */
    Decoder.prototype._readPureMappedType = function (schema) {
        var output;
        var overwrite;
        if (schema.type === "Overwrite") {
            // Overwrite Block
            overwrite = this._read(schema.overwrite);
        }
        var parsedTarget = this._validator.protoHelper.parseReference(schema.target);
        if (parsedTarget.type === "Interface") {
            output = this._readInterface(parsedTarget);
        }
        else if (parsedTarget.type === "Pick" ||
            parsedTarget.type === "Omit" ||
            parsedTarget.type === "Partial" ||
            parsedTarget.type === "Overwrite") {
            output = this._readPureMappedType(parsedTarget);
        }
        else {
            throw new Error("Invalid PureMappedType child: " + schema.type);
        }
        // filter key
        if (schema.type === "Pick") {
            // 把Pick以外的剔除
            for (var key in output) {
                if (schema.keys.indexOf(key) === -1) {
                    delete output[key];
                }
            }
        }
        else if (schema.type === "Omit") {
            // 剔除Omit
            for (var key in output) {
                if (schema.keys.indexOf(key) > -1) {
                    delete output[key];
                }
            }
        }
        else if (schema.type === "Overwrite") {
            Object.assign(output, overwrite);
        }
        // Partial 原样返回
        return output;
    };
    Decoder.prototype._readNumber = function (schema) {
        // 默认为double
        var scalarType = schema.scalarType || "double";
        switch (scalarType) {
            // 定长编码
            case "double":
                return this._reader.readDouble();
            // Varint编码
            case "int":
                return this._reader.readInt();
            case "uint":
                return this._reader.readUint();
            default:
                throw new Error("Scalar type not support : " + scalarType);
        }
    };
    Decoder.prototype._readInterface = function (schema) {
        var output = {};
        var flatSchema = this._validator.protoHelper.getFlatInterfaceSchema(schema);
        // BlockID数量
        var blockIdNum = this._reader.readUint();
        var _loop_3 = function (i) {
            // ReadBlock
            var readBlockId = this_3._reader.readUint();
            var lengthType = readBlockId & 3;
            var blockId = readBlockId >> 2;
            // indexSignature
            if (blockId === 0) {
                if (flatSchema.indexSignature) {
                    var type = flatSchema.indexSignature.type;
                    var fieldName = this_3._reader.readString();
                    this_3._skipIdLengthPrefix(this_3._validator.protoHelper.parseReference(type));
                    output[fieldName] = this_3._read(type);
                }
                // indexSignature未定义，可能是新协议，此处兼容，根据lengthType跳过
                else {
                    // skip fieldName
                    this_3._reader.skipByLengthType(LengthType.LengthDelimited);
                    // skipPayload
                    this_3._reader.skipByLengthType(lengthType);
                }
            }
            // extend block
            else if (blockId <= 9) {
                var extendId_1 = blockId - 1;
                var extend = schema.extends && schema.extends.find(function (v) { return v.id === extendId_1; });
                if (extend) {
                    this_3._skipIdLengthPrefix(this_3._validator.protoHelper.parseReference(extend.type));
                    var extendValue = this_3._read(extend.type);
                    Object.assign(output, extendValue);
                }
                // 未知的extendId 可能是新协议 跳过
                else {
                    // skipPayload
                    this_3._reader.skipByLengthType(lengthType);
                }
            }
            // property
            else {
                var propertyId_1 = blockId - 10;
                var property = schema.properties && schema.properties.find(function (v) { return v.id === propertyId_1; });
                if (property) {
                    this_3._skipIdLengthPrefix(this_3._validator.protoHelper.parseReference(property.type));
                    output[property.name] = this_3._read(property.type);
                }
                // 未知的PropertyID 可能是新协议 跳过
                else {
                    // skipPayload
                    this_3._reader.skipByLengthType(lengthType);
                }
            }
        };
        var this_3 = this;
        for (var i = 0; i < blockIdNum; ++i) {
            _loop_3();
        }
        // Literal property 由于不编码 将其补回
        // undefined as null
        for (var _i = 0, _a = flatSchema.properties; _i < _a.length; _i++) {
            var property = _a[_i];
            if (output.hasOwnProperty(property.name)) {
                continue;
            }
            // Literal
            var parsedType = this._validator.protoHelper.parseReference(property.type);
            if (parsedType.type === "Literal") {
                output[property.name] = parsedType.literal;
                continue;
            }
            // undefined as null
            if (this._undefinedAsNull(output[property.name], parsedType, property.optional)) {
                output[property.name] = null;
                continue;
            }
        }
        return output;
    };
    /** @internal 是否该null值小于当做undefined编码 */
    Decoder.prototype._undefinedAsNull = function (value, type, isOptional) {
        return (value === undefined &&
            this._options.undefinedAsNull &&
            !SchemaUtil.canBeLiteral(type, undefined) &&
            !isOptional &&
            SchemaUtil.canBeLiteral(type, null));
    };
    Decoder.prototype._skipIdLengthPrefix = function (parsedSchema) {
        var lengthInfo = IdBlockUtil.getPayloadLengthInfo(parsedSchema, this._validator.protoHelper);
        if (lengthInfo.needLengthPrefix) {
            // skip length prefix
            this._reader.skipByLengthType(LengthType.Varint);
        }
    };
    Decoder.prototype._readUnionOrIntersection = function (schema) {
        var output;
        var idNum = this._reader.readUint();
        var _loop_4 = function (i) {
            var readId = this_4._reader.readUint();
            var lengthType = readId & 3;
            var id = readId >> 2;
            var member = schema.members.find(function (v) { return v.id === id; });
            // 不可识别的Member，可能是新协议，跳过使兼容
            if (!member) {
                this_4._reader.skipByLengthType(lengthType);
                return "continue";
            }
            this_4._skipIdLengthPrefix(this_4._validator.protoHelper.parseReference(member.type));
            var value = this_4._read(member.type);
            if (this_4._isObject(output) && this_4._isObject(value)) {
                Object.assign(output, value);
            }
            else {
                output = value;
            }
        };
        var this_4 = this;
        for (var i = 0; i < idNum; ++i) {
            _loop_4();
        }
        if (this._undefinedAsNull(output, schema)) {
            output = null;
        }
        return output;
    };
    Decoder.prototype._isObject = function (value) {
        return typeof value === "object" && value !== null;
    };
    Decoder.prototype._getBufferValue = function (uint8Arr, schema) {
        if (schema.arrayType) {
            if (schema.arrayType === "BigInt64Array" || schema.arrayType === "BigUint64Array") {
                throw new Error("Unsupported arrayType: " + schema.arrayType);
            }
            // Uint8Array 性能最高
            else if (schema.arrayType === "Uint8Array") {
                return uint8Arr;
            }
            // 其余TypedArray 可能需要内存拷贝 性能次之
            else {
                var typedArr = TypedArrays[schema.arrayType];
                // 字节对齐，可以直接转，无需拷贝内存
                if (uint8Arr.byteOffset % typedArr.BYTES_PER_ELEMENT === 0) {
                    return new typedArr(uint8Arr.buffer, uint8Arr.byteOffset, uint8Arr.byteLength / typedArr.BYTES_PER_ELEMENT);
                }
                // 字节不对齐，不能直接转，只能拷贝内存后再生成
                else {
                    var arrBuf = uint8Arr.buffer.slice(uint8Arr.byteOffset, uint8Arr.byteOffset + uint8Arr.byteLength);
                    return new typedArr(arrBuf);
                }
            }
        }
        else {
            return uint8Arr.byteLength === uint8Arr.buffer.byteLength && uint8Arr.byteOffset === 0
                ? uint8Arr.buffer
                : uint8Arr.buffer.slice(uint8Arr.byteOffset, uint8Arr.byteOffset + uint8Arr.byteLength);
        }
    };
    return Decoder;
}());

/** @internal */
var Config = {
    interface: {
        maxExtendsNum: 9,
    },
};

/**
 * 用Op来串联 next
 * Op包含 function next length
 * 先度量长度再执行编码
 * 一次性编码
 * 使用BufferPool
 * writer.uint32(xx).string(xxx).finish();
 * @internal
 */
var BufferWriter = /** @class */ (function () {
    function BufferWriter() {
        this._ops = [];
    }
    Object.defineProperty(BufferWriter.prototype, "ops", {
        get: function () {
            return this._ops;
        },
        enumerable: false,
        configurable: true
    });
    BufferWriter.prototype.clear = function () {
        this._ops = [];
    };
    BufferWriter.prototype.push = function (req) {
        this._ops.push(this.req2op(req));
        return this;
    };
    BufferWriter.prototype.req2op = function (req) {
        if (req.type === "string" || req.type === "buffer") {
            var valueLength = this.measureLength(req);
            // Length
            this.push({ type: "varint", value: Varint64.from(valueLength) });
            // Value
            return tslib.__assign(tslib.__assign({}, req), { length: valueLength });
        }
        else {
            var length_1 = this.measureLength(req);
            return tslib.__assign(tslib.__assign({}, req), { length: length_1 });
        }
    };
    BufferWriter.prototype.measureLength = function (req) {
        switch (req.type) {
            case "varint":
                return req.value.byteLength;
            case "string":
                return Utf8Coder.measureLength(req.value);
            case "buffer":
                return req.value.byteLength;
            case "double":
                return 8;
            case "boolean":
                return 1;
            default:
                return NaN;
        }
    };
    BufferWriter.prototype.finish = function () {
        var byteLength = this._ops.sum(function (v) { return v.length; });
        var pos = 0;
        var buf = new Uint8Array(byteLength);
        var view = new DataView(buf.buffer);
        for (var _i = 0, _a = this._ops; _i < _a.length; _i++) {
            var op = _a[_i];
            switch (op.type) {
                case "varint":
                    var newPos = op.value.writeToBuffer(buf, pos);
                    if (newPos !== pos + op.length) {
                        throw new Error("Error varint measuredLength ".concat(op.length, ", actual is ").concat(newPos - pos, ", value is ").concat(op.value.toNumber()));
                    }
                    break;
                case "double":
                    view.setFloat64(buf.byteOffset + pos, op.value);
                    break;
                case "string":
                    var encLen = Utf8Coder.write(op.value, buf, pos);
                    if (encLen !== op.length) {
                        throw new Error("Expect ".concat(op.length, " bytes but encoded ").concat(encLen, " bytes"));
                    }
                    break;
                case "buffer":
                    buf.subarray(pos, pos + op.length).set(op.value);
                    break;
                case "boolean":
                    view.setUint8(buf.byteOffset + pos, op.value ? 255 : 0);
                    break;
            }
            pos += op.length;
        }
        return buf;
    };
    return BufferWriter;
}());

/** @internal */
var Encoder = /** @class */ (function () {
    function Encoder(options) {
        this._options = options;
        this._writer = new BufferWriter();
        this._validator = options.validator;
    }
    Encoder.prototype.encode = function (value, schema) {
        this._writer.clear();
        this._write(value, schema);
        return this._writer.finish();
    };
    Encoder.prototype.encodeJSON = function (value, schema) {
        var _this = this;
        // JSON 能直接传输的类型，直接跳过
        if (typeof value !== "object" ||
            value === null ||
            CoderUtil.isJsonCompatible(schema, "encode", this._validator.protoHelper)) {
            return value;
        }
        switch (schema.type) {
            case tsbufferSchema.SchemaType.Array:
                if (!Array.isArray(value)) {
                    break;
                }
                return value.map(function (v) { return _this.encodeJSON(v, schema.elementType); });
            case tsbufferSchema.SchemaType.Tuple: {
                if (!Array.isArray(value)) {
                    break;
                }
                return value.map(function (v, i) { return _this.encodeJSON(v, schema.elementTypes[i]); });
            }
            case tsbufferSchema.SchemaType.Interface: {
                if (value.constructor !== Object) {
                    break;
                }
                value = Object.assign({}, value);
                var flatSchema = this._validator.protoHelper.getFlatInterfaceSchema(schema);
                var _loop_1 = function (key) {
                    var property = flatSchema.properties.find(function (v) { return v.name === key; });
                    if (property) {
                        value[key] = this_1.encodeJSON(value[key], property.type);
                    }
                    else if (flatSchema.indexSignature) {
                        value[key] = this_1.encodeJSON(value[key], flatSchema.indexSignature.type);
                    }
                };
                var this_1 = this;
                for (var key in value) {
                    _loop_1(key);
                }
                return value;
            }
            case tsbufferSchema.SchemaType.Partial:
            case tsbufferSchema.SchemaType.Pick:
            case tsbufferSchema.SchemaType.Omit:
            case tsbufferSchema.SchemaType.Overwrite:
                var parsed = this._validator.protoHelper.parseMappedType(schema);
                return this.encodeJSON(value, parsed);
            case tsbufferSchema.SchemaType.Buffer:
                if (!(value instanceof ArrayBuffer) && !ArrayBuffer.isView(value)) {
                    break;
                }
                if (schema.arrayType) {
                    if (schema.arrayType === "Uint8Array") {
                        return Base64Util.bufferToBase64(value);
                    }
                    var view = value;
                    var buf = view.byteLength === view.buffer.byteLength && view.byteOffset === 0
                        ? view.buffer
                        : view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
                    return Base64Util.bufferToBase64(new Uint8Array(buf));
                }
                else {
                    return Base64Util.bufferToBase64(new Uint8Array(value));
                }
            case tsbufferSchema.SchemaType.IndexedAccess:
            case tsbufferSchema.SchemaType.Reference:
            case tsbufferSchema.SchemaType.Keyof:
                return this.encodeJSON(value, this._validator.protoHelper.parseReference(schema));
            case tsbufferSchema.SchemaType.Union:
            case tsbufferSchema.SchemaType.Intersection: {
                // 逐个编码 然后合并 （失败的会原值返回，所以不影响结果）
                for (var _i = 0, _a = schema.members; _i < _a.length; _i++) {
                    var member = _a[_i];
                    value = this.encodeJSON(value, member.type);
                }
                return value;
            }
            case tsbufferSchema.SchemaType.NonNullable:
                return this.encodeJSON(value, schema.target);
            case tsbufferSchema.SchemaType.Date:
                if (!(value instanceof Date)) {
                    break;
                }
                return value.toJSON();
            case tsbufferSchema.SchemaType.Custom:
                if (schema.encodeJSON) {
                    return schema.encodeJSON(value);
                }
                else if (typeof (value === null || value === void 0 ? void 0 : value.toJSON) === "function") {
                    return value.toJSON();
                }
                else if (typeof (value === null || value === void 0 ? void 0 : value.toString) === "function") {
                    return value.toString();
                }
                return value;
            default:
                schema.type;
        }
        return value;
    };
    Encoder.prototype._write = function (value, schema, options) {
        switch (schema.type) {
            case tsbufferSchema.SchemaType.Boolean:
                this._writer.push({ type: "boolean", value: value });
                break;
            case tsbufferSchema.SchemaType.Number:
                this._writeNumber(value, schema);
                break;
            case tsbufferSchema.SchemaType.String:
                this._writer.push({ type: "string", value: value });
                break;
            case tsbufferSchema.SchemaType.Array: {
                var _v = value;
                // 数组长度：Varint
                this._writer.push({ type: "varint", value: Varint64.from(_v.length) });
                // Element Payload
                for (var i = 0; i < _v.length; ++i) {
                    this._write(_v[i], schema.elementType);
                }
                break;
            }
            case tsbufferSchema.SchemaType.Tuple: {
                if (schema.elementTypes.length > 64) {
                    throw new Error("Elements oversized, maximum supported tuple elements is 64, now get " +
                        schema.elementTypes.length);
                }
                var _v = value;
                // 计算maskPos（要编码的值的index）
                var maskIndices = [];
                for (var i = 0; i < _v.length; ++i) {
                    // undefined 不编码
                    // null as undefined
                    if (_v[i] === undefined || this._nullAsUndefined(_v[i], schema.elementTypes[i])) {
                        continue;
                    }
                    maskIndices.push(i);
                }
                // 生成PayloadMask：Varint64
                var lo = 0;
                var hi = 0;
                for (var _i = 0, maskIndices_1 = maskIndices; _i < maskIndices_1.length; _i++) {
                    var v = maskIndices_1[_i];
                    if (v < 32) {
                        lo |= 1 << v;
                    }
                    else {
                        hi |= 1 << (v - 32);
                    }
                }
                this._writer.push({ type: "varint", value: new Varint64(hi, lo) });
                // Element Payload
                for (var _a = 0, maskIndices_2 = maskIndices; _a < maskIndices_2.length; _a++) {
                    var i = maskIndices_2[_a];
                    this._write(_v[i], schema.elementTypes[i]);
                }
                break;
            }
            case tsbufferSchema.SchemaType.Enum:
                var enumItem = schema.members.find(function (v) { return v.value === value; });
                if (!enumItem) {
                    throw new Error("Unexpect enum value: ".concat(value));
                }
                this._writer.push({ type: "varint", value: Varint64.from(enumItem.id) });
                break;
            case tsbufferSchema.SchemaType.Any:
                if (value === undefined) {
                    this._writer.push({ type: "string", value: "undefined" });
                }
                else {
                    this._writer.push({ type: "string", value: JSON.stringify(value) });
                }
                break;
            case tsbufferSchema.SchemaType.Object:
                this._writer.push({ type: "string", value: JSON.stringify(value) });
                break;
            case tsbufferSchema.SchemaType.Literal:
                break;
            case tsbufferSchema.SchemaType.Interface:
                this._writeInterface(value, schema, options);
                break;
            case tsbufferSchema.SchemaType.Buffer:
                this._writeBuffer(value);
                break;
            case tsbufferSchema.SchemaType.IndexedAccess:
            case tsbufferSchema.SchemaType.Reference:
            case tsbufferSchema.SchemaType.Keyof:
                this._write(value, this._validator.protoHelper.parseReference(schema), options);
                break;
            case tsbufferSchema.SchemaType.Partial:
            case tsbufferSchema.SchemaType.Pick:
            case tsbufferSchema.SchemaType.Omit:
            case tsbufferSchema.SchemaType.Overwrite:
                var parsed = this._validator.protoHelper.parseMappedType(schema);
                if (parsed.type === tsbufferSchema.SchemaType.Interface) {
                    this._writePureMappedType(value, schema, options);
                }
                else if (parsed.type === tsbufferSchema.SchemaType.Union) {
                    this._writeUnion(value, parsed, options === null || options === void 0 ? void 0 : options.skipFields);
                }
                else if (parsed.type === tsbufferSchema.SchemaType.Intersection) {
                    this._writeIntersection(value, parsed, options === null || options === void 0 ? void 0 : options.skipFields);
                }
                break;
            case tsbufferSchema.SchemaType.Union:
                this._writeUnion(value, schema, options === null || options === void 0 ? void 0 : options.skipFields);
                break;
            case tsbufferSchema.SchemaType.Intersection:
                this._writeIntersection(value, schema, options === null || options === void 0 ? void 0 : options.skipFields);
                break;
            case tsbufferSchema.SchemaType.Date:
                this._writer.push({ type: "varint", value: Varint64.from(value.getTime()) });
                break;
            case tsbufferSchema.SchemaType.NonNullable:
                this._write(value, schema.target, options);
                break;
            case tsbufferSchema.SchemaType.Custom:
                if (!schema.encode) {
                    throw new Error("Missing encode method for CustomTypeSchema");
                }
                var buf = schema.encode(value);
                // 以 Buffer 形式写入
                this._writeBuffer(buf);
                break;
            default:
                // @ts-expect-error
                throw new Error("Unrecognized schema type: ".concat(schema.type));
        }
    };
    Encoder.prototype._writePureMappedType = function (value, schema, options) {
        if (!options) {
            options = {};
        }
        if (schema.type === "Pick") {
            // 已存在 取交集
            if (options.pickFields) {
                var newPickFields = {};
                for (var _i = 0, _a = schema.keys; _i < _a.length; _i++) {
                    var v = _a[_i];
                    if (options.pickFields[v]) {
                        newPickFields[v] = 1;
                    }
                }
                options.pickFields = newPickFields;
            }
            // 不存在 初始化
            else {
                options.pickFields = {};
                for (var _b = 0, _c = schema.keys; _b < _c.length; _b++) {
                    var v = _c[_b];
                    options.pickFields[v] = 1;
                }
            }
        }
        else if (schema.type === "Omit") {
            // 不存在 初始化
            if (!(options === null || options === void 0 ? void 0 : options.skipFields)) {
                if (!options) {
                    options = {};
                }
                options.skipFields = {};
            }
            // 取并集
            for (var _d = 0, _e = schema.keys; _d < _e.length; _d++) {
                var v = _e[_d];
                options.skipFields[v] = 1;
            }
        }
        else if (schema.type === "Overwrite") {
            var parsed = this._parseOverwrite(value, schema);
            // 写入Overwrite部分
            this._write(parsed.overwriteValue, parsed.overwrite, options);
        }
        else if (schema.type === "Partial") ;
        else {
            // @ts-expect-error
            throw new Error("Invalid PureMappedType child: " + schema.type);
        }
        // Write Interface
        var parsedTarget = this._validator.protoHelper.parseReference(schema.target);
        if (parsedTarget.type === "Interface") {
            this._writeInterface(value, parsedTarget, options);
        }
        else {
            this._writePureMappedType(value, parsedTarget, options);
        }
    };
    Encoder.prototype._writeNumber = function (value, schema) {
        // 默认为double
        var scalarType = schema.scalarType || "double";
        switch (scalarType) {
            // 定长编码
            case "double":
                this._writer.push({ type: scalarType, value: value });
                break;
            // Varint编码
            case "int":
                this._writer.push({ type: "varint", value: Varint64.from(value).zzEncode() });
                break;
            case "uint":
                this._writer.push({ type: "varint", value: Varint64.from(value) });
                break;
            default:
                throw new Error("Scalar type not support : " + scalarType);
        }
    };
    Encoder.prototype._writeInterface = function (value, schema, options) {
        // skipFields默认值
        if (!options) {
            options = {};
        }
        if (!options.skipFields) {
            options.skipFields = {};
        }
        // 记录起始op位置，用于最后插入BlockID数量
        var opStartOps = this._writer.ops.length;
        var blockIdCount = 0;
        // 以下，interface
        // extends
        if (schema.extends) {
            // 支持的继承数量有上限
            if (schema.extends.length > Config.interface.maxExtendsNum) {
                throw new Error("Max support ".concat(Config.interface.maxExtendsNum, " extends, actual: ").concat(schema.extends.length));
            }
            for (var _i = 0, _a = schema.extends; _i < _a.length; _i++) {
                var extend = _a[_i];
                // BlockID = extend.id + 1
                var blockId = extend.id + 1;
                this._writer.push({ type: "varint", value: Varint64.from(blockId) });
                var blockIdPos = this._writer.ops.length - 1;
                // 写入extend interface前 writeOps的长度
                var opsLengthBeforeWrite = this._writer.ops.length;
                // extend Block
                var parsedExtend = this._validator.protoHelper.parseReference(extend.type);
                this._writeInterface(value, parsedExtend, tslib.__assign(tslib.__assign({}, options), { 
                    // 确保indexSignature是在最小层级编码
                    skipIndexSignature: !!schema.indexSignature || options.skipIndexSignature }));
                // 写入前后writeOps只增加了一个（block length），说明该extend并未写入任何property字段，取消编码这个block
                if (this._writer.ops.length === opsLengthBeforeWrite + 1) {
                    // 移除BlockID
                    this._writer.ops.splice(this._writer.ops.length - 2, 2);
                }
                // extend写入成功 blockId数量+1
                else {
                    ++blockIdCount;
                    this._processIdWithLengthType(blockIdPos, extend.type);
                }
            }
        }
        // property
        if (schema.properties) {
            for (var _b = 0, _c = schema.properties; _b < _c.length; _b++) {
                var property = _c[_b];
                var parsedType = this._validator.protoHelper.parseReference(property.type);
                var propValue = value[property.name];
                // PickFields
                if (options.pickFields && !options.pickFields[property.name]) {
                    continue;
                }
                // Literal不编码 直接跳过
                if (parsedType.type === "Literal") {
                    options.skipFields[property.name] = 1;
                    continue;
                }
                // null as undefined
                if (this._nullAsUndefined(propValue, property.type)) {
                    propValue = undefined;
                }
                // undefined不编码
                if (propValue === undefined) {
                    continue;
                }
                // SkipFields
                if (options.skipFields[property.name]) {
                    continue;
                }
                options.skipFields[property.name] = 1;
                var blockId = property.id + Config.interface.maxExtendsNum + 1;
                // BlockID (propertyID)
                this._writer.push({ type: "varint", value: Varint64.from(blockId) });
                var blockIdPos = this._writer.ops.length - 1;
                // Value Payload
                this._write(propValue, parsedType);
                ++blockIdCount;
                this._processIdWithLengthType(blockIdPos, parsedType);
            }
        }
        // indexSignature
        if (!options.skipIndexSignature) {
            var flat = this._validator.protoHelper.getFlatInterfaceSchema(schema);
            if (flat.indexSignature) {
                for (var key in value) {
                    if (value[key] === undefined ||
                        this._nullAsUndefined(value[key], flat.indexSignature.type)) {
                        continue;
                    }
                    // PickFields
                    if (options.pickFields && !options.pickFields[key]) {
                        continue;
                    }
                    // SkipFields
                    if (options.skipFields[key]) {
                        continue;
                    }
                    options.skipFields[key] = 1;
                    // BlockID == 0
                    this._writer.push({ type: "varint", value: Varint64.from(0) });
                    var blockIdPos = this._writer.ops.length - 1;
                    // 字段名
                    this._writer.push({ type: "string", value: key });
                    var lengthPrefixPos = this._writer.ops.length;
                    // Value Payload
                    this._write(value[key], flat.indexSignature.type);
                    ++blockIdCount;
                    this._processIdWithLengthType(blockIdPos, flat.indexSignature.type, lengthPrefixPos);
                }
            }
        }
        this._writer.ops.splice(opStartOps, 0, this._writer.req2op({ type: "varint", value: Varint64.from(blockIdCount) }));
    };
    /** @internal 是否该null值小于当做undefined编码 */
    Encoder.prototype._nullAsUndefined = function (value, type) {
        return value === null && this._options.nullAsUndefined && !SchemaUtil.canBeLiteral(type, null);
        // && SchemaUtil.canBeLiteral(type, undefined)  一定为true 因为先validate过了
    };
    Encoder.prototype._parseOverwrite = function (value, schema) {
        var skipFields = {};
        // 解引用
        var target = this._validator.protoHelper.parseReference(schema.target);
        var overwrite = this._validator.protoHelper.parseReference(schema.overwrite);
        var flatTarget = this._validator.protoHelper.getFlatInterfaceSchema(target);
        var flatOverwrite = this._validator.protoHelper.getFlatInterfaceSchema(overwrite);
        // 先区分哪些字段进入Target块，哪些字段进入Overwrite块
        var overwriteValue = {};
        var targetValue = {};
        // Overwrite块 property
        if (flatOverwrite.properties) {
            // 只要Overwrite中有此Property，即在Overwrite块编码
            for (var _i = 0, _a = flatOverwrite.properties; _i < _a.length; _i++) {
                var property = _a[_i];
                // undefined不编码，跳过SkipFIelds
                if (value[property.name] !== undefined && !skipFields[property.name]) {
                    overwriteValue[property.name] = value[property.name];
                    skipFields[property.name] = 1;
                }
            }
        }
        // Target块 property
        if (flatTarget.properties) {
            for (var _b = 0, _c = flatTarget.properties; _b < _c.length; _b++) {
                var property = _c[_b];
                // undefined不编码，跳过SkipFields
                if (value[property.name] !== undefined && !skipFields[property.name]) {
                    targetValue[property.name] = value[property.name];
                    skipFields[property.name] = 1;
                }
            }
        }
        // indexSignature
        var indexSignatureWriteValue; // indexSignature要写入的目标（overwrite或target）
        var indexSignature;
        // IndexSignature，优先使用Overwrite的
        if (flatOverwrite.indexSignature) {
            indexSignature = flatOverwrite.indexSignature;
            indexSignatureWriteValue = overwriteValue;
        }
        else if (flatTarget.indexSignature) {
            indexSignature = flatTarget.indexSignature;
            indexSignatureWriteValue = targetValue;
        }
        if (indexSignature) {
            for (var key in value) {
                if (skipFields[key]) {
                    continue;
                }
                indexSignatureWriteValue[key] = value[key];
                skipFields[key] = 1;
            }
        }
        // 编码，此处不再需要SkipFields，因为已经筛选过
        return {
            target: target,
            targetValue: targetValue,
            overwrite: overwrite,
            overwriteValue: overwriteValue,
        };
    };
    Encoder.prototype._writeUnion = function (value, schema, skipFields, unionProperties) {
        // 计算unionProperties
        // if (!unionProperties) {
        //     unionProperties = skipFields ? Object.keys(skipFields) : [];
        // }
        // this._validator.protoHelper.getUnionProperties(schema).forEach(v => {
        //     unionProperties!.binaryInsert(v, true);
        // })
        if (skipFields === void 0) { skipFields = {}; }
        // 记住编码起点
        var encodeStartPos = this._writer.ops.length;
        var idNum = 0;
        // null as undefined
        if (this._nullAsUndefined(value, schema)) {
            value = undefined;
        }
        for (var _i = 0, _a = schema.members; _i < _a.length; _i++) {
            var member = _a[_i];
            // 验证该member是否可以编码
            var vRes = this._validator.validate(value, member.type, {
                // 禁用excessPropertyChecks（以代替unionProperties）
                excessPropertyChecks: false,
                // 启用strictNullChecks（null as undefined已经前置处理）
                // strictNullChecks: true
            });
            if (vRes.isSucc) {
                // 编码
                // Part2: ID
                this._writer.push({ type: "varint", value: Varint64.from(member.id) });
                var idPos = this._writer.ops.length - 1;
                // Part3: Payload
                if (member.type.type === "Union") {
                    this._writeUnion(value, member.type, skipFields);
                }
                else {
                    this._write(value, member.type, {
                        skipFields: skipFields,
                    });
                }
                idNum++;
                this._processIdWithLengthType(idPos, member.type);
                // 非object的value，类型一定互斥，只编码一个足矣
                if (typeof value !== "object") {
                    break;
                }
            }
        }
        // 已经编码
        if (idNum > 0) {
            // 前置ID数量
            this._writer.ops.splice(encodeStartPos, 0, this._writer.req2op({ type: "varint", value: Varint64.from(idNum) }));
            return;
        }
        else {
            // 未编码，没有任何条件满足，抛出异常
            throw new Error("Non member is satisfied for union type");
        }
    };
    Encoder.prototype._writeIntersection = function (value, schema, skipFields) {
        if (skipFields === void 0) { skipFields = {}; }
        // ID数量（member数量）
        this._writer.push({ type: "varint", value: Varint64.from(schema.members.length) });
        // 按Member依次编码
        for (var _i = 0, _a = schema.members; _i < _a.length; _i++) {
            var member = _a[_i];
            // ID
            this._writer.push({ type: "varint", value: Varint64.from(member.id) });
            var idPos = this._writer.ops.length - 1;
            // 编码块 - 为每个 member 创建新的 skipFields
            var memberSkipFields = tslib.__assign({}, skipFields);
            this._write(value, member.type, {
                skipFields: memberSkipFields,
            });
            this._processIdWithLengthType(idPos, member.type);
        }
    };
    Encoder.prototype._writeBuffer = function (value) {
        // ArrayBuffer 转为Uint8Array
        if (value instanceof ArrayBuffer) {
            this._writer.push({ type: "buffer", value: new Uint8Array(value) });
        }
        // Uint8Array 直接写入
        else if (value instanceof Uint8Array) {
            this._writer.push({ type: "buffer", value: value });
        }
        // 其它TypedArray 转为Uint8Array
        else {
            var key = value.constructor.name;
            var arrType = TypedArrays[key];
            var uint8Arr = new Uint8Array(value.buffer, value.byteOffset, value.length * arrType.BYTES_PER_ELEMENT);
            this._writer.push({ type: "buffer", value: uint8Arr });
        }
    };
    // private _writeIdBlocks(blocks: IDBlockItem[]) {
    //     // 字段数量: Varint
    //     this._writer.push({ type: 'varint', value: Varint64.from(blocks.length) });
    //     // 依次编码
    //     for (let item of blocks) {
    //         // ID
    //         this._writer.push({ type: 'varint', value: Varint64.from(item.id) });
    //         // Payload
    //         this._write(item.value, item.schema)
    //     }
    // }
    /**
     * 重新处理ID位，使其加入末位长度信息2Bit
     * @param idPos
     */
    Encoder.prototype._processIdWithLengthType = function (idPos, payloadType, lengthPrefixPos) {
        var idOp = this._writer.ops[idPos];
        if (idOp.type !== "varint") {
            throw new Error("Error idPos: " + idPos);
        }
        // 解引用
        var parsedSchema = this._validator.protoHelper.parseReference(payloadType);
        var lengthInfo = IdBlockUtil.getPayloadLengthInfo(parsedSchema, this._validator.protoHelper);
        var newId = (idOp.value.toNumber() << 2) + lengthInfo.lengthType;
        this._writer.ops[idPos] = this._writer.req2op({
            type: "varint",
            value: Varint64.from(newId),
        });
        if (lengthInfo.needLengthPrefix) {
            var payloadByteLength = this._writer.ops.filter(function (v, i) { return i > idPos; }).sum(function (v) { return v.length; });
            this._writer.ops.splice(lengthPrefixPos == undefined ? idPos + 1 : lengthPrefixPos, 0, this._writer.req2op({
                type: "varint",
                value: Varint64.from(payloadByteLength),
            }));
        }
    };
    return Encoder;
}());

/**
 * @public
 */
var TSBuffer = /** @class */ (function () {
    function TSBuffer(proto, options) {
        /** @internal 默认配置 */
        this._options = {
            excessPropertyChecks: true,
            strictNullChecks: false,
            skipEncodeValidate: false,
            skipDecodeValidate: false,
            cloneProto: true,
        };
        // but `options.validatorOptions` has higher priority to validate process (don't affect encode)
        this._options = tslib.__assign(tslib.__assign({}, this._options), options);
        this._proto = this._options.cloneProto ? Object.merge({}, proto) : proto;
        Object.assign(this._proto, Object.merge({}, options === null || options === void 0 ? void 0 : options.customTypes));
        this._validator = new tsbufferValidator.TSBufferValidator(this._proto, {
            excessPropertyChecks: this._options.excessPropertyChecks,
            strictNullChecks: this._options.strictNullChecks,
            cloneProto: false,
        });
        this.validate = this._validator.validate.bind(this._validator);
        this.prune = this._validator.prune.bind(this._validator);
        this._encoder = new Encoder({
            validator: this._validator,
            // if !strictNullChecks, then encoder can convert null to undefined
            nullAsUndefined: !this._options.strictNullChecks,
        });
        this._decoder = new Decoder({
            validator: this._validator,
            // if !strictNullChecks, then decoder can convert undefined to null
            undefinedAsNull: !this._options.strictNullChecks,
        });
    }
    /**
     * 编码
     * @param value - 要编码的值
     * @param schemaOrId - Schema 或 SchemaID，例如`a/b.ts`下的`Test`类型，其ID为`a/b/Test`
     */
    TSBuffer.prototype.encode = function (value, schemaOrId, options) {
        var _a;
        var schema;
        if (typeof schemaOrId === "string") {
            schema = this._proto[schemaOrId];
            if (!schema) {
                return { isSucc: false, errMsg: "Cannot find schema\uFF1A ".concat(schemaOrId) };
            }
        }
        else {
            schema = schemaOrId;
        }
        // validate before encode
        if (!((_a = options === null || options === void 0 ? void 0 : options.skipValidate) !== null && _a !== void 0 ? _a : this._options.skipEncodeValidate)) {
            var vRes = this._validator.validate(value, schema, {
                // 禁用excessPropertyChecks，因为不会编码excess property
                excessPropertyChecks: false,
            });
            if (!vRes.isSucc) {
                return vRes;
            }
        }
        var buf;
        try {
            buf = this._encoder.encode(value, schema);
        }
        catch (e) {
            return { isSucc: false, errMsg: e.message };
        }
        return { isSucc: true, buf: buf };
    };
    /**
     * 解码
     * @param buf - 待解码的二进制数据
     * @param schemaOrId - Schema 或 SchemaID，例如`a/b.ts`下的`Test`类型，其ID为`a/b/Test`
     */
    TSBuffer.prototype.decode = function (buf, schemaOrId, options) {
        var _a;
        var schema;
        if (typeof schemaOrId === "string") {
            schema = this._proto[schemaOrId];
            if (!schema) {
                return { isSucc: false, errMsg: "Cannot find schema\uFF1A ".concat(schemaOrId), errPhase: undefined };
            }
        }
        else {
            schema = schemaOrId;
        }
        var value;
        try {
            value = this._decoder.decode(buf, schema);
        }
        catch (e) {
            return { isSucc: false, errMsg: e.message, errPhase: "decode" };
        }
        if (!((_a = options === null || options === void 0 ? void 0 : options.skipValidate) !== null && _a !== void 0 ? _a : this._options.skipDecodeValidate)) {
            var vRes = this._validator.validate(value, schema);
            if (!vRes.isSucc) {
                return tslib.__assign(tslib.__assign({}, vRes), { errPhase: "validate" });
            }
        }
        return { isSucc: true, value: value };
    };
    /**
     * 编码为 JSON Object，根据协议将 JSON 不支持的格式（如 ArrayBuffer、Date、ObjectId）转换成 JSON 可传输的格式
     * @param value
     * @param schemaOrId
     * @param options
     */
    TSBuffer.prototype.encodeJSON = function (value, schemaOrId, options) {
        var _a;
        var schema;
        if (typeof schemaOrId === "string") {
            schema = this._proto[schemaOrId];
            if (!schema) {
                return { isSucc: false, errMsg: "Cannot find schema\uFF1A ".concat(schemaOrId) };
            }
        }
        else {
            schema = schemaOrId;
        }
        // validate before encode
        if (!((_a = options === null || options === void 0 ? void 0 : options.skipValidate) !== null && _a !== void 0 ? _a : this._options.skipEncodeValidate)) {
            var vRes = this._validator.prune(value, schema);
            if (!vRes.isSucc) {
                return vRes;
            }
            value = vRes.pruneOutput;
        }
        // TODO schema 里没有 Buffer 和 Custom 的自动跳过
        var json;
        try {
            json = this._encoder.encodeJSON(value, schema);
        }
        catch (e) {
            return { isSucc: false, errMsg: e.message };
        }
        return { isSucc: true, json: json };
    };
    /**
     * 从 JSON Object 解码，根据协议将 ArrayBuffer、Date、ObjectId 等类型从 JSON 中还原
     * @param json - JSON Object (是 JSON 对象，而非 JSON 字符串)
     * @param schemaOrId
     * @param options
     */
    TSBuffer.prototype.decodeJSON = function (json, schemaOrId, options) {
        var _a;
        var schema;
        if (typeof schemaOrId === "string") {
            schema = this._proto[schemaOrId];
            if (!schema) {
                return { isSucc: false, errMsg: "Cannot find schema\uFF1A ".concat(schemaOrId), errPhase: undefined };
            }
        }
        else {
            schema = schemaOrId;
        }
        // TODO schema 里没有 Buffer 和 Custom 的自动跳过
        var value;
        try {
            value = this._decoder.decodeJSON(json, schema);
        }
        catch (e) {
            return { isSucc: false, errMsg: e.message, errPhase: "decode" };
        }
        if (!((_a = options === null || options === void 0 ? void 0 : options.skipValidate) !== null && _a !== void 0 ? _a : this._options.skipDecodeValidate)) {
            var vRes = this._validator.prune(value, schema);
            if (!vRes.isSucc) {
                vRes.errPhase = "validate";
                return vRes;
            }
            return { isSucc: true, value: vRes.pruneOutput };
        }
        return { isSucc: true, value: value };
    };
    return TSBuffer;
}());

exports.Base64Util = Base64Util;
exports.TSBuffer = TSBuffer;
