@deepkit/bson
Version:
Deepkit BSON parser
1,116 lines • 83.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.__ΩBSONSizer = exports.__ΩBSONSerializer = exports.__ΩBSONSerializerState = exports.bsonBinarySerializer = exports.BSONBinarySerializer = exports.DigitByteRuntimeCode = exports.Writer = exports.AutoBuffer = exports.__ΩAutoBufferSerializer = exports.__ΩAutoBufferSerializerState = exports.ValueWithBSONSerializer = exports.JS_INT_MIN = exports.JS_INT_MAX = void 0;
exports.hexToByte = hexToByte;
exports.uuidStringToByte = uuidStringToByte;
exports.stringByteLength = stringByteLength;
exports.getValueSize = getValueSize;
exports.wrapValue = wrapValue;
exports.wrapObjectId = wrapObjectId;
exports.wrapUUID = wrapUUID;
exports.writeUint32LE = writeUint32LE;
exports.writeInt32LE = writeInt32LE;
exports.writeFloat64LE = writeFloat64LE;
exports.createBSONSizer = createBSONSizer;
exports.serializeBSONWithoutOptimiser = serializeBSONWithoutOptimiser;
exports.getBSONSerializer = getBSONSerializer;
exports.getBSONSizer = getBSONSizer;
exports.serializeBSON = serializeBSON;
const __ΩArrayBufferView = [() => __ΩArrayBufferLike, 'TArrayBuffer', 'buffer', 'byteLength', 'byteOffset', 'ArrayBufferView', 'n!c"Pe"!4#9\'4$9\'4%9Mw&y'];
const __ΩArrayBufferLike = [() => __ΩArrayBufferTypes, () => __ΩArrayBufferTypes, 'ArrayBufferLike', 'n!n"gfw#y'];
const __ΩArrayBufferTypes = ['ArrayBuffer', 'ArrayBufferTypes', 'P_4!Mw"y'];
function __assignType(fn, args) {
fn.__type = args;
return fn;
}
/*
* Deepkit Framework
* Copyright (C) 2021 Deepkit UG, Marc J. Schmidt
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the MIT License.
*
* You should have received a copy of the MIT License along with this program.
*/
const core_1 = require("@deepkit/core");
/*@ts-ignore*/
var { __ΩReceiveType, __ΩMongoId, __ΩUUID, __ΩTypeClass, __ΩTypeObjectLiteral, __ΩTypeBigInt, __ΩTypeLiteral, __ΩTypeArray, __ΩTypeTuple } = require('@deepkit/type');
const type_1 = require("@deepkit/type");
const bson_deserializer_templates_js_1 = require("./bson-deserializer-templates.js");
const continuation_js_1 = require("./continuation.js");
/*@ts-ignore*/
var { __ΩBSONType } = require('./utils.js');
const utils_js_1 = require("./utils.js");
// BSON MAX VALUES
const BSON_INT32_MAX = 0x7fffffff;
const BSON_INT32_MIN = -0x80000000;
// JS MAX PRECISE VALUES
exports.JS_INT_MAX = 0x20000000000000; // Any integer up to 2^53 can be precisely represented by a double.
exports.JS_INT_MIN = -0x20000000000000; // Any integer down to -2^53 can be precisely represented by a double.
const LONG_MAX = 'undefined' !== typeof BigInt ? BigInt('9223372036854775807') : 9223372036854775807;
const LONG_MIN = 'undefined' !== typeof BigInt ? BigInt('-9223372036854775807') : -9223372036854775807;
function hexToByte(hex, index = 0, offset = 0) {
let code1 = hex.charCodeAt(index * 2 + offset) - 48;
if (code1 > 9)
code1 -= 39;
let code2 = hex.charCodeAt((index * 2) + offset + 1) - 48;
if (code2 > 9)
code2 -= 39;
return code1 * 16 + code2;
}
hexToByte.__type = ['hex', 'index', () => 0, 'offset', () => 0, 'hexToByte', 'P&2!\'2">#\'2$>%\'/&'];
function uuidStringToByte(hex, index = 0) {
let offset = 0;
//e.g. bef8de96-41fe-442f-b70c-c3a150f8c96c
if (index > 3)
offset += 1;
if (index > 5)
offset += 1;
if (index > 7)
offset += 1;
if (index > 9)
offset += 1;
return hexToByte(hex, index, offset);
}
uuidStringToByte.__type = ['hex', 'index', () => 0, 'uuidStringToByte', 'P&2!\'2">#\'/$'];
function stringByteLength(str) {
if (!str)
return 0;
let size = 0;
for (let i = 0; i < str.length; i++) {
const c = str.charCodeAt(i);
// surrogate pair
if (c >= 0xD800 && c <= 0xDBFF && i + 1 < str.length) {
const lo = str.charCodeAt(i + 1);
if (lo >= 0xDC00 && lo <= 0xDFFF) {
// surrogate pair is a 4-byte character in UTF-8
size += 4;
// move past the low surrogate since it's part of the character
i++;
continue;
}
}
if (c < 128)
size += 1;
else if (c > 127 && c < 2048)
size += 2;
else
size += 3;
}
return size;
}
stringByteLength.__type = ['str', 'stringByteLength', 'P&2!\'/"'];
function getBinaryBigIntSize(value) {
let hex = value.toString(16);
if (hex[0] === '-')
hex = hex.slice(1);
if (hex === '0')
return 4 + 1;
if (hex.length % 2)
hex = '0' + hex;
return 4 + 1 + Math.ceil(hex.length / 2);
}
getBinaryBigIntSize.__type = ['value', 'getBinaryBigIntSize', 'P*2!\'/"'];
function getSignedBinaryBigIntSize(value) {
let hex = value.toString(16);
if (hex[0] === '-')
hex = hex.slice(1);
if (hex === '0')
return 4 + 1;
if (hex.length % 2)
hex = '0' + hex;
return 4 + 1 + 1 + Math.ceil(hex.length / 2);
}
getSignedBinaryBigIntSize.__type = ['value', 'getSignedBinaryBigIntSize', 'P*2!\'/"'];
function getValueSize(value) {
if (value instanceof ValueWithBSONSerializer) {
if ((0, type_1.isUUIDType)(value.type)) {
return 4 + 1 + 16;
}
else if ((0, type_1.isMongoIdType)(value.type)) {
return 12;
}
else if ((0, type_1.isBinaryBigIntType)(value.type)) {
const binaryBigInt = type_1.binaryBigIntAnnotation.getFirst(value.type);
return binaryBigInt === 0 /* BinaryBigIntType.unsigned */ ? getBinaryBigIntSize(value.value) : getSignedBinaryBigIntSize(value.value);
}
else {
return getValueSize(value.value);
}
}
else if ('boolean' === typeof value) {
return 1;
}
else if ('string' === typeof value) {
//size + content + null
return 4 + stringByteLength(value) + 1;
}
else if ('bigint' === typeof value) {
//per default bigint will be serialized as long, to be compatible with default mongo driver and mongo database.
return 8;
}
else if ('number' === typeof value) {
if (Math.floor(value) === value) {
//it's an int
if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) {
//32bit
return 4;
}
else if (value >= exports.JS_INT_MIN && value <= exports.JS_INT_MAX) {
//double, 64bit
return 8;
}
else {
//long
return 8;
}
}
else {
//double
return 8;
}
}
else if (value instanceof Date) {
return 8;
}
else if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
let size = 4; //size
size += 1; //sub type
size += value.byteLength;
return size;
}
else if ((0, core_1.isArray)(value)) {
let size = 4; //object size
for (let i = 0; i < value.length; i++) {
size += 1; //element type
size += (0, utils_js_1.digitByteSize)(i); //element name
size += getValueSize(value[i]);
}
size += 1; //null
return size;
}
else if (value && value['_bsontype'] === 'Binary') {
let size = 4; //size
size += 1; //sub type
size += value.buffer.byteLength;
return size;
}
else if (value instanceof RegExp) {
return stringByteLength(value.source) + 1
+
(value.global ? 1 : 0) +
(value.ignoreCase ? 1 : 0) +
(value.multiline ? 1 : 0) +
1;
}
else if ((0, core_1.isObject)(value)) {
let size = 4; //object size
for (let i in value) {
if (!(0, core_1.hasProperty)(value, i))
continue;
size += 1; //element type
size += stringByteLength(i) + 1; //element name + null
size += getValueSize(value[i]);
}
size += 1; //null
return size;
} //isObject() should be last
return 0;
}
getValueSize.__type = ['value', 'getValueSize', 'P"2!\'/"'];
class ValueWithBSONSerializer {
constructor(value, type) {
this.value = value;
this.type = type;
}
}
exports.ValueWithBSONSerializer = ValueWithBSONSerializer;
ValueWithBSONSerializer.__type = ['value', 'Type', 'type', 'constructor', 'ValueWithBSONSerializer', 'P"2!:"w"2#:"0$5w%'];
function wrapValue(value, type = wrapValue.Ω?.[0]) {
wrapValue.Ω = undefined;
return new ValueWithBSONSerializer(value, (0, type_1.resolveReceiveType)(type));
}
wrapValue.__type = ['value', () => __ΩReceiveType, 'type', 'wrapValue', 'P"2!"o""2#8"/$'];
const mongoIdType = (0, type_1.typeOf)([], [() => __ΩMongoId, 'n!']);
function wrapObjectId(value) {
return new ValueWithBSONSerializer(value, mongoIdType);
}
wrapObjectId.__type = ['value', 'wrapObjectId', 'P&2!"/"'];
const uuidIdType = (0, type_1.typeOf)([], [() => __ΩUUID, 'n!']);
function wrapUUID(value) {
return new ValueWithBSONSerializer(value, uuidIdType);
}
wrapUUID.__type = ['value', 'wrapUUID', 'P&2!"/"'];
function writeUint32LE(buffer, offset, value) {
buffer[offset] = value & 0xff;
buffer[offset + 1] = (value >> 8) & 0xff;
buffer[offset + 2] = (value >> 16) & 0xff;
buffer[offset + 3] = (value >> 24) & 0xff;
}
writeUint32LE.__type = ['buffer', 'offset', 'value', 'writeUint32LE', 'PW2!\'2"\'2#"/$'];
function writeInt32LE(buffer, offset, value) {
// writing is the same as unsigned
return writeUint32LE(buffer, offset, value);
}
writeInt32LE.__type = ['buffer', 'offset', 'value', 'writeInt32LE', 'PW2!\'2"\'2#"/$'];
const float64Buffer = new ArrayBuffer(8);
const u32 = new Uint32Array(float64Buffer);
const f64 = new Float64Array(float64Buffer);
function writeFloat64LE(buffer, offset, value) {
f64[0] = value;
buffer[offset] = u32[0] & 0xff;
buffer[offset + 1] = (u32[0] >> 8) & 0xff;
buffer[offset + 2] = (u32[0] >> 16) & 0xff;
buffer[offset + 3] = (u32[0] >> 24) & 0xff;
buffer[offset + 4] = u32[1] & 0xff;
buffer[offset + 5] = (u32[1] >> 8) & 0xff;
buffer[offset + 6] = (u32[1] >> 16) & 0xff;
buffer[offset + 7] = (u32[1] >> 24) & 0xff;
}
writeFloat64LE.__type = ['buffer', 'offset', 'value', 'writeFloat64LE', 'PW2!\'2"\'2#$/$'];
const __ΩAutoBufferSerializerState = [() => Writer, 'writer', 'AutoBufferSerializerState', 'PP7!4"8Mw#y'];
exports.__ΩAutoBufferSerializerState = __ΩAutoBufferSerializerState;
const __ΩAutoBufferSerializer = ['data', () => __ΩAutoBufferSerializerState, 'state', '', 'AutoBufferSerializer', 'P"2!n"2#$/$w%y'];
exports.__ΩAutoBufferSerializer = __ΩAutoBufferSerializer;
class AutoBuffer {
constructor(prepend = 0, initialSize = 256) {
this.prepend = prepend;
this.initialSize = initialSize;
this._buffer = new Uint8Array(this.initialSize);
this.state = { writer: new Writer(this._buffer) };
}
get buffer() {
return this._buffer.subarray(0, this.size);
}
get size() {
return this.state.writer.offset;
}
setOffset(offset) {
this.state.writer.offset = offset;
}
apply(serializer, data) {
this.state.writer.offset = this.prepend;
serializer(data, this.state);
if (this.prepend + this.state.writer.offset > this._buffer.byteLength) {
// increase buffer and retry
const nextBuffer = new Uint8Array(this.prepend + this.state.writer.offset);
if (this.prepend)
nextBuffer.set(this._buffer.subarray(0, this.prepend));
this._buffer = nextBuffer;
this.state.writer.buffer = this._buffer;
this.state.writer.reset();
this.state.writer.offset = this.prepend;
serializer(data, this.state);
}
}
}
exports.AutoBuffer = AutoBuffer;
AutoBuffer.__type = ['_buffer', function () { return new Uint8Array(this.initialSize); }, 'state', function () { return { writer: new Writer(this._buffer) }; }, 'prepend', () => 0, 'initialSize', () => 256, 'constructor', 'offset', 'setOffset', () => __ΩAutoBufferSerializer, 'serializer', 'data', 'apply', 'AutoBuffer', 'W3!>"!3#>$P\'2%:>&\'2\';>("0)!!P\'2*"0+Pn,2-"2."0/5w0'];
class Writer {
constructor(buffer, offset = 0) {
this.buffer = buffer;
this.offset = offset;
this.typeOffset = 0;
}
reset() {
this.offset = 0;
this.typeOffset = 0;
}
/**
* If typeOffset is defined, the type will be written at this offset.
* Useful for writing type information for members in array/object literals.
*/
writeType(v) {
if (this.typeOffset !== 0) {
this.buffer[this.typeOffset] = v;
this.typeOffset = 0;
}
}
resetWriteType() {
this.typeOffset = 0;
}
prepareWriteType() {
this.typeOffset = this.offset;
// It might be that a subsequent content writer is omitted (circular reference),
// then we want undefined to be written as type.
this.buffer[this.offset] = utils_js_1.BSONType.UNDEFINED;
this.offset += 1;
}
writeUint32(v) {
writeUint32LE(this.buffer, this.offset, v);
this.offset += 4;
}
writeInt32(v) {
writeInt32LE(this.buffer, this.offset, v);
this.offset += 4;
}
writeDouble(v) {
writeFloat64LE(this.buffer, this.offset, v);
this.offset += 8;
}
writeDelayedSize(v, position) {
writeUint32LE(this.buffer, position, v);
}
writeByte(v) {
this.buffer[this.offset++] = v;
}
writeBuffer(buffer, offset = 0) {
// buffer.copy(this.buffer, this.buffer.byteOffset + this.offset);
for (let i = offset; i < buffer.byteLength; i++) {
this.buffer[this.offset++] = buffer[i];
}
// this.offset += buffer.byteLength;
}
writeNull() {
this.writeByte(0);
}
writeAsciiString(str) {
str = 'string' === typeof str ? str : '' + str;
for (let i = 0; i < str.length; i++) {
this.buffer[this.offset++] = str.charCodeAt(i);
}
}
writeString(str) {
if (!str)
return;
if (typeof str !== 'string')
return;
for (let i = 0; i < str.length; i++) {
const c = str.charCodeAt(i);
// surrogate pairs for characters outside the BMP
if (c >= 0xD800 && c <= 0xDBFF && i + 1 < str.length) {
const hi = c;
const lo = str.charCodeAt(i + 1);
if (lo >= 0xDC00 && lo <= 0xDFFF) {
// combine the surrogate pair and subtract 0x10000 for UTF-8 encoding
const codePoint = ((hi - 0xD800) * 0x400) + (lo - 0xDC00) + 0x10000;
this.buffer[this.offset++] = (codePoint >> 18) | 240;
this.buffer[this.offset++] = ((codePoint >> 12) & 63) | 128;
this.buffer[this.offset++] = ((codePoint >> 6) & 63) | 128;
this.buffer[this.offset++] = (codePoint & 63) | 128;
// skip the next code unit, since it's part of the surrogate pair
i++;
continue;
}
}
if (c < 128) {
this.buffer[this.offset++] = c;
}
else if (c > 127 && c < 2048) {
this.buffer[this.offset++] = (c >> 6) | 192;
this.buffer[this.offset++] = ((c & 63) | 128);
}
else {
this.buffer[this.offset++] = (c >> 12) | 224;
this.buffer[this.offset++] = ((c >> 6) & 63) | 128;
this.buffer[this.offset++] = (c & 63) | 128;
}
}
}
getBigIntBSONType(value) {
if (BSON_INT32_MIN <= value && value <= BSON_INT32_MAX) {
return utils_js_1.BSONType.INT;
}
else if (LONG_MIN <= value && value <= LONG_MAX) {
return utils_js_1.BSONType.LONG;
}
else {
return utils_js_1.BSONType.BINARY;
}
}
writeBigIntLong(value) {
if (value < 0) {
this.writeInt32(~Number(-value % BigInt(utils_js_1.TWO_PWR_32_DBL_N)) + 1 | 0); //low
this.writeInt32(~(Number(-value / BigInt(utils_js_1.TWO_PWR_32_DBL_N))) | 0); //high
}
else {
this.writeInt32(Number(value % BigInt(utils_js_1.TWO_PWR_32_DBL_N)) | 0); //low
this.writeInt32(Number(value / BigInt(utils_js_1.TWO_PWR_32_DBL_N)) | 0); //high
}
}
writeBigIntBinary(value) {
//custom binary
let hex = value.toString(16);
if (hex[0] === '-')
hex = hex.slice(1);
if (hex === '0') {
this.writeUint32(0);
this.writeByte(utils_js_1.BSON_BINARY_SUBTYPE_DEFAULT);
return;
}
if (hex.length % 2)
hex = '0' + hex;
let size = Math.ceil(hex.length / 2);
this.writeUint32(size);
this.writeByte(utils_js_1.BSON_BINARY_SUBTYPE_DEFAULT);
for (let i = 0; i < size; i++) {
this.buffer[this.offset++] = hexToByte(hex, i);
}
}
writeSignedBigIntBinary(value) {
//custom binary
let hex = value.toString(16);
let signum = 0;
if (hex[0] === '-') {
//negative number
signum = 1;
hex = hex.slice(1);
}
if (hex === '0') {
this.writeUint32(0);
this.writeByte(utils_js_1.BSON_BINARY_SUBTYPE_DEFAULT);
return;
}
if (hex.length % 2)
hex = '0' + hex;
let size = Math.ceil(hex.length / 2);
this.writeUint32(1 + size);
this.writeByte(utils_js_1.BSON_BINARY_SUBTYPE_DEFAULT);
this.buffer[this.offset++] = signum === 1 ? 255 : 0; //0xff means negative, 0 means positive
for (let i = 0; i < size; i++) {
this.buffer[this.offset++] = hexToByte(hex, i);
}
}
writeLong(value) {
if (value > 9223372036854775807)
value = 9223372036854775807;
if (value < -9223372036854775807)
value = -9223372036854775807;
if (value < 0) {
this.writeInt32(~(-value % utils_js_1.TWO_PWR_32_DBL_N) + 1 | 0); //low
this.writeInt32(~(-value / utils_js_1.TWO_PWR_32_DBL_N) | 0); //high
}
else {
this.writeInt32((value % utils_js_1.TWO_PWR_32_DBL_N) | 0); //low
this.writeInt32((value / utils_js_1.TWO_PWR_32_DBL_N) | 0); //high
}
}
writeUUID(value) {
this.writeUint32(16);
this.writeByte(utils_js_1.BSON_BINARY_SUBTYPE_UUID);
this.buffer[this.offset + 0] = uuidStringToByte(value, 0);
this.buffer[this.offset + 1] = uuidStringToByte(value, 1);
this.buffer[this.offset + 2] = uuidStringToByte(value, 2);
this.buffer[this.offset + 3] = uuidStringToByte(value, 3);
//-
this.buffer[this.offset + 4] = uuidStringToByte(value, 4);
this.buffer[this.offset + 5] = uuidStringToByte(value, 5);
//-
this.buffer[this.offset + 6] = uuidStringToByte(value, 6);
this.buffer[this.offset + 7] = uuidStringToByte(value, 7);
//-
this.buffer[this.offset + 8] = uuidStringToByte(value, 8);
this.buffer[this.offset + 9] = uuidStringToByte(value, 9);
//-
this.buffer[this.offset + 10] = uuidStringToByte(value, 10);
this.buffer[this.offset + 11] = uuidStringToByte(value, 11);
this.buffer[this.offset + 12] = uuidStringToByte(value, 12);
this.buffer[this.offset + 13] = uuidStringToByte(value, 13);
this.buffer[this.offset + 14] = uuidStringToByte(value, 14);
this.buffer[this.offset + 15] = uuidStringToByte(value, 15);
this.offset += 16;
}
writeObjectId(value) {
this.buffer[this.offset + 0] = hexToByte(value, 0);
this.buffer[this.offset + 1] = hexToByte(value, 1);
this.buffer[this.offset + 2] = hexToByte(value, 2);
this.buffer[this.offset + 3] = hexToByte(value, 3);
this.buffer[this.offset + 4] = hexToByte(value, 4);
this.buffer[this.offset + 5] = hexToByte(value, 5);
this.buffer[this.offset + 6] = hexToByte(value, 6);
this.buffer[this.offset + 7] = hexToByte(value, 7);
this.buffer[this.offset + 8] = hexToByte(value, 8);
this.buffer[this.offset + 9] = hexToByte(value, 9);
this.buffer[this.offset + 10] = hexToByte(value, 10);
this.buffer[this.offset + 11] = hexToByte(value, 11);
this.offset += 12;
}
write(value) {
if (value instanceof ValueWithBSONSerializer) {
if (value.value !== undefined && value.value !== null) {
if ((0, type_1.isUUIDType)(value.type)) {
this.writeType(utils_js_1.BSONType.BINARY);
this.writeUUID(value.value);
return;
}
else if ((0, type_1.isMongoIdType)(value.type)) {
this.writeType(utils_js_1.BSONType.OID);
this.writeObjectId(value.value);
return;
}
else if ((0, type_1.isBinaryBigIntType)(value.type)) {
this.writeType(utils_js_1.BSONType.BINARY);
const binary = type_1.binaryBigIntAnnotation.getFirst(value.type);
if (binary === 1 /* BinaryBigIntType.signed */) {
this.writeSignedBigIntBinary(value.value);
}
else {
this.writeBigIntBinary(value.value);
}
return;
}
}
this.write(value.value);
}
else if ('boolean' === typeof value) {
this.writeType(utils_js_1.BSONType.BOOLEAN);
this.writeByte(value ? 1 : 0);
}
else if (value instanceof RegExp) {
this.writeType(utils_js_1.BSONType.REGEXP);
this.writeString(value.source);
this.writeNull();
if (value.ignoreCase)
this.writeString('i');
if (value.global)
this.writeString('s'); //BSON does not use the RegExp flag format
if (value.multiline)
this.writeString('m');
this.writeNull();
}
else if ('string' === typeof value) {
//size + content + null
this.writeType(utils_js_1.BSONType.STRING);
const start = this.offset;
this.offset += 4; //size placeholder
this.writeString(value);
this.writeByte(0); //null
this.writeDelayedSize(this.offset - start - 4, start);
}
else if ('number' === typeof value) {
if (Math.floor(value) === value && value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) {
//32bit int
this.writeType(utils_js_1.BSONType.INT);
this.writeInt32(value);
}
else {
//double
this.writeType(utils_js_1.BSONType.NUMBER);
this.writeDouble(value);
}
}
else if (value instanceof Date) {
this.writeType(utils_js_1.BSONType.DATE);
this.writeLong(value.valueOf());
}
else if ('bigint' === typeof value) {
// This is only called for bigint in any structures.
this.writeType(utils_js_1.BSONType.LONG);
this.writeBigIntLong(value);
}
else if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
this.writeType(utils_js_1.BSONType.BINARY);
this.writeArrayBuffer(value);
}
else if ((0, core_1.isArray)(value)) {
this.writeType(utils_js_1.BSONType.ARRAY);
const start = this.offset;
this.offset += 4; //size
for (let i = 0; i < value.length; i++) {
this.prepareWriteType();
this.writeAsciiString('' + i);
this.writeByte(0);
this.write(value[i]);
}
this.writeNull();
this.writeDelayedSize(this.offset - start, start);
}
else if (value === undefined) {
this.writeType(utils_js_1.BSONType.UNDEFINED);
}
else if (value === null) {
this.writeType(utils_js_1.BSONType.NULL);
}
else if ((0, core_1.isObject)(value)) {
this.writeType(utils_js_1.BSONType.OBJECT);
const start = this.offset;
this.offset += 4; //size
for (let i in value) {
if (!(0, core_1.hasProperty)(value, i))
continue;
this.prepareWriteType();
this.writeString(i);
this.writeByte(0);
this.write(value[i]);
}
this.writeNull();
this.writeDelayedSize(this.offset - start, start);
}
else {
//the sizer incldues the type and name, so we have to write that
this.writeType(utils_js_1.BSONType.UNDEFINED);
}
}
writeArrayBuffer(value) {
let view = value instanceof ArrayBuffer ? new Uint8Array(value) : new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
if (value['_bsontype'] === 'Binary') {
view = value.buffer;
}
this.writeUint32(value.byteLength);
this.writeByte(utils_js_1.BSON_BINARY_SUBTYPE_DEFAULT);
for (let i = 0; i < value.byteLength; i++) {
this.buffer[this.offset++] = view[i];
}
}
}
exports.Writer = Writer;
Writer.__type = ['typeOffset', function () { return 0; }, 'buffer', 'offset', () => 0, 'constructor', 'reset', 'v', 'writeType', 'resetWriteType', 'prepareWriteType', 'writeUint32', 'writeInt32', 'writeDouble', 'position', 'writeDelayedSize', 'writeByte', () => 0, 'writeBuffer', 'writeNull', 'str', 'writeAsciiString', 'writeString', 'value', 'getBigIntBSONType', 'writeBigIntLong', 'writeBigIntBinary', 'writeSignedBigIntBinary', 'writeLong', 'writeUUID', 'writeObjectId', 'write', () => __ΩArrayBufferView, 'writeArrayBuffer', 'Writer', '\'3!>"PW2#:\'2$:>%"0&P"0\'P\'2("0)P"0*P"0+P\'2("0,P\'2("0-P\'2("0.P\'2(\'2/"00P\'2("01PW2#\'2$>2"03P"04PP&\'J25"06P&25"07P*28\'09P*28"0:P*28"0;P*28"0<P\'28"0=P&28"0>P&28"0?P"28$0@PP_nAJ28"0B5wC'];
function getNameWriterCode(name) {
const nameSetter = [];
//todo: support utf8 names
for (let i = 0; i < name.length; i++) {
nameSetter.push(`state.writer.buffer[state.writer.offset++] = ${name.charCodeAt(i)};`);
}
return `
//write name: '${name}'
${nameSetter.join('\n')}
state.writer.writeByte(0); //null
`;
}
getNameWriterCode.__type = ['name', 'getNameWriterCode', 'P&2!&/"'];
function sizerObjectLiteral(type, state, options) {
handleObjectLiteral(type, state, 'sizer', options);
}
sizerObjectLiteral.__type = [() => __ΩTypeClass, () => __ΩTypeObjectLiteral, 'type', () => type_1.TemplateState, 'state', () => __ΩBSONSerializerOptions, 'options', 'sizerObjectLiteral', 'PPn!n"J2#P7$2%n&2\'"/('];
function serializeObjectLiteral(type, state, options) {
handleObjectLiteral(type, state, 'serialization', options);
}
serializeObjectLiteral.__type = [() => __ΩTypeClass, () => __ΩTypeObjectLiteral, 'type', () => type_1.TemplateState, 'state', () => __ΩBSONSerializerOptions, 'options', 'serializeObjectLiteral', 'PPn!n"J2#P7$2%n&2\'"/('];
function handleObjectLiteral(type, state, target, options) {
let before = 'state.size += 4; //object size';
let after = 'state.size += 1; //null';
if (target === 'serialization') {
const start = state.compilerContext.reserveName('start');
before = `
var ${start} = state.writer.offset;
state.writer.offset += 4; //size`;
after = `
state.writer.writeNull();
state.writer.writeDelayedSize(state.writer.offset - ${start}, ${start});`;
}
//emdedded for the moment disabled. treat it as normal property.
// const embedded = embeddedAnnotation.getFirst(type);
// if (embedded) {
// if (type.kind !== ReflectionKind.class) throw new SerializationError(`Object literals can not be embedded`, collapsePath(state.path));
// const constructorProperties = getConstructorProperties(type);
// if (!constructorProperties.properties.length) throw new BSONError(`Can not embed class ${getClassName(type.classType)} since it has no constructor properties`);
//
// if (constructorProperties.properties.length === 1) {
// const first = constructorProperties.properties[0];
// let name = getNameExpression(state.namingStrategy.getPropertyName(first), state);
// const setter = getEmbeddedAccessor(type, false, '', state.namingStrategy, first, embedded);
// state.addCode(executeTemplates(state.fork('', new ContainerAccessor(state.accessor, name)).forPropertyName(setter || state.propertyName), first.type));
// } else {
// const lines: string[] = [];
// const containerProperty = getEmbeddedProperty(type);
//
// for (const property of constructorProperties.properties) {
// const setter = getEmbeddedAccessor(type, true, '', state.namingStrategy, property, embedded);
// lines.push(executeTemplates(state.fork('', new ContainerAccessor(state.accessor, JSON.stringify(property.name))).forPropertyName(setter), property.type));
// }
//
// if (containerProperty) {
// state.addCode(`
// ${lines.join('\n')}
// `);
// } else {
// if (target === 'serialization') {
// serializePropertyNameAware(type, state, BSONType.OBJECT, `'object' === typeof ${state.accessor}`, `
// //embedded class with multiple properties
// ${before}
// ${lines.join('\n')}
// ${after}
// `);
// } else {
// sizerPropertyNameAware(type, state, `'object' === typeof ${state.accessor}`, `
// //embedded class with multiple properties
// ${before}
// ${lines.join('\n')}
// ${after}
// `);
// }
// }
// }
// return;
// }
//following line resets propertyName, so we have to store it before
const propertyName = state.propertyName;
if (target === 'serialization') {
serializePropertyNameAware(type, state, utils_js_1.BSONType.OBJECT, `'object' === typeof ${state.accessor}`, '');
}
else {
sizerPropertyNameAware(type, state, `'object' === typeof ${state.accessor}`, '');
}
const lines = [];
const signatures = [];
const existing = [];
state.setContext({ unpopulatedSymbol: type_1.unpopulatedSymbol });
for (const member of (0, type_1.resolveTypeMembers)(type)) {
if (member.kind === type_1.ReflectionKind.indexSignature) {
if (type_1.excludedAnnotation.isExcluded(member.type, state.registry.serializer.name))
continue;
signatures.push(member);
}
if (!(0, type_1.isPropertyMemberType)(member))
continue;
if (!(0, utils_js_1.isSerializable)(member.type))
continue;
const propertyName = String(state.namingStrategy.getPropertyName(member, state.registry.serializer.name));
const readName = (0, type_1.getNameExpression)((0, type_1.memberNameToString)(member.name), state);
existing.push(readName);
//back references are only serialized when it's not forMongoDatabase
if ((0, type_1.isBackReferenceType)(member.type) && options.forMongoDatabase === true)
continue;
if (type_1.excludedAnnotation.isExcluded(member.type, state.registry.serializer.name))
continue;
const accessor = `${state.accessor}[${readName}]`;
const propertyState = state.fork('', accessor).extendPath(propertyName);
const setUndefined = (0, type_1.isOptional)(member)
? (0, type_1.executeTemplates)(propertyState.fork(), { kind: type_1.ReflectionKind.undefined })
: (0, type_1.isNullable)(member) ? (0, type_1.executeTemplates)(propertyState.fork(), { kind: type_1.ReflectionKind.null }) : '';
const template = (0, type_1.executeTemplates)(propertyState.fork(), member.type);
if (!template) {
// console.error('missing template for member', member.name, 'of', member.type);
// throw new BSONError(`No template found for ${String(member.name)}: ${ReflectionKind[member.type.kind]}`);
}
let nameWriter = ``;
if (target === 'serialization') {
nameWriter = `
state.writer.prepareWriteType();
${propertyNameWrite(propertyName)}
`;
}
else if (target === 'sizer') {
nameWriter = `
//type + name + null
state.size += 1 + ${stringByteLength(propertyName)} + 1;
`;
}
const optional = (0, type_1.isOptional)(member) || (0, type_1.hasDefaultValue)(member);
let converter = `
${nameWriter}
if (${accessor} === unpopulatedSymbol) {
//don't do anything since not loaded
} else if (${optional} && (${accessor} === undefined || ${accessor} === null)) {
${setUndefined}
} else {
${template}
}
`;
if (optional) {
lines.push(`
if (${readName} in ${state.accessor}) {
${converter}
}
`);
}
else {
lines.push(converter);
}
}
if (signatures.length) {
const i = state.compilerContext.reserveName('i');
const existingCheck = existing.map(__assignType(v => `${i} === ${v}`, ['v', '', 'P"2!"/"'])).join(' || ') || 'false';
const signatureLines = [];
state.setContext({ stringByteLength });
(0, type_1.sortSignatures)(signatures);
for (const signature of signatures) {
const accessor = new type_1.ContainerAccessor(state.accessor, i);
const propertyState = state.fork(undefined, accessor).extendPath(new type_1.RuntimeCode(i));
const setUndefined = (0, type_1.isOptional)(signature.type)
? (0, type_1.executeTemplates)(propertyState.fork(), { kind: type_1.ReflectionKind.undefined })
: (0, type_1.isNullable)(signature.type) ? (0, type_1.executeTemplates)(propertyState.fork(), { kind: type_1.ReflectionKind.null }) : '';
let nameWriter = ``;
if (target === 'serialization') {
nameWriter = `
state.writer.prepareWriteType();
state.writer.writeString(${i});
state.writer.writeByte(0);
`;
}
else if (target === 'sizer') {
nameWriter = `
//type + name + null
state.size += 1 + stringByteLength(${i}) + 1;
`;
}
signatureLines.push(`else if (${(0, type_1.getIndexCheck)(state.compilerContext, i, signature.index)}) {
${nameWriter}
if (${accessor} === undefined) {
${setUndefined}
} else {
${(0, type_1.executeTemplates)(propertyState, signature.type)}
}
}`);
}
state.setContext({ hasProperty: core_1.hasProperty });
//the index signature type could be: string, number, symbol.
//or a literal when it was constructed by a mapped type.
lines.push(`
for (const ${i} in ${state.accessor}) {
if (!hasProperty(${state.accessor}, ${i})) continue;
if (${existingCheck}) continue;
if (false) {} ${signatureLines.join(' ')}
}
`);
}
state.addCode(`
//handle objectLiteral via propertyName="${(0, type_1.getPropertyNameString)(propertyName)}"
${before}
${lines.join('\n')}
${after}
`);
if (type.kind === type_1.ReflectionKind.class && type_1.referenceAnnotation.hasAnnotations(type)) {
state.setContext({ isObject: core_1.isObject, isReferenceInstance: type_1.isReferenceInstance, isReferenceHydrated: type_1.isReferenceHydrated });
const reflection = type_1.ReflectionClass.from(type.classType);
//the primary key is serialised for unhydrated references
const index = (0, type_1.getNameExpression)(reflection.getPrimary().getName(), state);
const primaryKey = reflection.getPrimary().getType();
//if a reference or forMongoDatabase=true only the foreign primary key is serialized
state.replaceTemplate(`
if ((${options.forMongoDatabase === true}) || (isReferenceInstance(${state.accessor}) && !isReferenceHydrated(${state.accessor}))) {
${(0, type_1.executeTemplates)(state.fork(state.setter, `${state.accessor}[${index}]`).forPropertyName(propertyName), primaryKey)}
} else {
${state.template}
}
`);
}
}
handleObjectLiteral.__type = [() => __ΩTypeClass, () => __ΩTypeObjectLiteral, 'type', () => type_1.TemplateState, 'state', "serialization", "sizer", 'target', () => __ΩBSONSerializerOptions, 'options', 'handleObjectLiteral', 'PPn!n"J2#P7$2%P.&.\'J2(n)2*"/+'];
function propertyNameWrite(propertyName) {
if (propertyName) {
if (propertyName instanceof type_1.RuntimeCode) {
return `
state.writer.writeString(${propertyName.code});
state.writer.writeByte(0);
`;
}
else {
return getNameWriterCode(propertyName);
}
}
return '';
}
propertyNameWrite.__type = [() => type_1.RuntimeCode, 'propertyName', 'propertyNameWrite', 'PP&P7!J2"8"/#'];
function serializePropertyNameAware(type, state, bsonType, typeChecker, code) {
state.template = `
//serializer for ${type.kind}, via propertyName="${(0, type_1.getPropertyNameString)(state.propertyName)}"
${typeChecker ? `if (!(${typeChecker})) ${state.throwCode(type)}` : ''}
state.writer.writeType(${bsonType}); //BSON type = ${utils_js_1.BSONType[bsonType]}
${code}
`;
}
serializePropertyNameAware.__type = ['Type', 'type', () => type_1.TemplateState, 'state', () => __ΩBSONType, 'bsonType', 'typeChecker', 'code', 'serializePropertyNameAware', 'P"w!2"P7#2$n%2&&2\'&2($/)'];
class DigitByteRuntimeCode extends type_1.RuntimeCode {
constructor(code) {
super(code);
this.code = code;
}
}
exports.DigitByteRuntimeCode = DigitByteRuntimeCode;
DigitByteRuntimeCode.__type = [() => type_1.RuntimeCode, 'code', 'constructor', 'DigitByteRuntimeCode', 'P7!P&2":"0#5w$'];
function sizerPropertyNameAware(type, state, typeChecker, code) {
const checker = typeChecker ? `if (!(${typeChecker})) ${state.throwCode(type)}` : '';
state.template = `
${checker}
${state.template}
${code}
`;
}
sizerPropertyNameAware.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'typeChecker', 'code', 'sizerPropertyNameAware', 'P"w!2"P7#2$&2%&2&$/\''];
function sizerAny(type, state) {
state.setContext({ getValueSize });
sizerPropertyNameAware(type, state, ``, `state.size += getValueSize(${state.accessor});`);
}
sizerAny.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'sizerAny', 'P"w!2"P7#2$"/%'];
function serializeAny(type, state) {
state.addCode(`
state.writer.write(${state.accessor});
`);
}
serializeAny.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'serializeAny', 'P"w!2"P7#2$"/%'];
function sizerBoolean(type, state) {
sizerPropertyNameAware(type, state, `typeof ${state.accessor} === 'boolean'`, `
state.size += 1;
`);
}
sizerBoolean.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'sizerBoolean', 'P"w!2"P7#2$"/%'];
function sizerNumber(type, state) {
state.setContext({ getValueSize });
//per default bigint will be serialized as long, to be compatible with default mongo driver and mongo database.
//We should add a new annotation, maybe like `bigint & Binary` to make it binary (unlimited size)
sizerPropertyNameAware(type, state, `typeof ${state.accessor} === 'bigint' || (typeof ${state.accessor} === 'number' && !Number.isNaN(${state.accessor}))`, `
state.size += getValueSize(${state.accessor});
`);
}
sizerNumber.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'sizerNumber', 'P"w!2"P7#2$"/%'];
function serializeBoolean(type, state) {
serializePropertyNameAware(type, state, utils_js_1.BSONType.BOOLEAN, `typeof ${state.accessor} === 'boolean'`, `
state.writer.writeByte(${state.accessor} ? 1 : 0);
`);
}
serializeBoolean.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'serializeBoolean', 'P"w!2"P7#2$"/%'];
function serializeString(type, state) {
if (type_1.uuidAnnotation.getFirst(type)) {
serializePropertyNameAware(type, state, utils_js_1.BSONType.BINARY, `typeof ${state.accessor} === 'string' && ${state.accessor}.length === 36`, `state.writer.writeUUID(${state.accessor});`);
return;
}
if (type_1.mongoIdAnnotation.getFirst(type)) {
serializePropertyNameAware(type, state, utils_js_1.BSONType.OID, `typeof ${state.accessor} === 'string' && ${state.accessor}.length === 24`, `state.writer.writeObjectId(${state.accessor});`);
return;
}
const start = state.compilerContext.reserveName('start');
serializePropertyNameAware(type, state, utils_js_1.BSONType.STRING, `typeof ${state.accessor} === 'string'`, `
var ${start} = state.writer.offset;
state.writer.offset += 4; //size placeholder
state.writer.writeString(${state.accessor});
state.writer.writeByte(0); //null
state.writer.writeDelayedSize(state.writer.offset - ${start} - 4, ${start});
`);
}
serializeString.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'serializeString', 'P"w!2"P7#2$"/%'];
function sizeString(type, state) {
if (type_1.uuidAnnotation.getFirst(type)) {
sizerPropertyNameAware(type, state, `typeof ${state.accessor} === 'string' && ${state.accessor}.length === 36`, `
state.size += 4 + 1 + 16;
`);
return;
}
if (type_1.mongoIdAnnotation.getFirst(type)) {
sizerPropertyNameAware(type, state, `typeof ${state.accessor} === 'string' && ${state.accessor}.length === 24`, `
state.size += 12;
`);
return;
}
state.setContext({ getValueSize });
sizerPropertyNameAware(type, state, `typeof ${state.accessor} === 'string'`, `
state.size += getValueSize(${state.accessor});
`);
}
sizeString.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'sizeString', 'P"w!2"P7#2$"/%'];
function serializeNumber(type, state) {
state.addCode(`
if ('bigint' === typeof ${state.accessor}) {
//long
state.writer.writeType(${utils_js_1.BSONType.LONG});
state.writer.writeBigIntLong(${state.accessor});
} else if ('number' === typeof ${state.accessor} && !Number.isNaN(${state.accessor})) {
if (Math.floor(${state.accessor}) === ${state.accessor} && ${state.accessor} >= ${BSON_INT32_MIN} && ${state.accessor} <= ${BSON_INT32_MAX}) {
//32bit int
state.writer.writeType(${utils_js_1.BSONType.INT});
state.writer.writeInt32(${state.accessor});
} else {
//double, 64bit
state.writer.writeType(${utils_js_1.BSONType.NUMBER});
state.writer.writeDouble(${state.accessor});
}
}
`);
}
serializeNumber.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'serializeNumber', 'P"w!2"P7#2$"/%'];
function sizerBigInt(type, state) {
const binaryBigInt = type_1.binaryBigIntAnnotation.getFirst(type);
if (binaryBigInt !== undefined) {
state.setContext({ getBinaryBigIntSize, getSignedBinaryBigIntSize });
const bigIntSize = binaryBigInt === 0 /* BinaryBigIntType.unsigned */ ? 'getBinaryBigIntSize' : 'getSignedBinaryBigIntSize';
//per default bigint will be serialized as long, to be compatible with default mongo driver and mongo database.
//We should add a new annotation, maybe like `bigint & Binary` to make it binary (unlimited size)
sizerPropertyNameAware(type, state, `typeof ${state.accessor} === 'bigint' || (typeof ${state.accessor} === 'number' && !Number.isNaN(${state.accessor}))`, `
state.size += ${bigIntSize}(${state.accessor});
`);
}
else {
sizerNumber(type, state);
}
}
sizerBigInt.__type = [() => __ΩTypeBigInt, 'type', () => type_1.TemplateState, 'state', 'sizerBigInt', 'Pn!2"P7#2$"/%'];
function serializeBigInt(type, state) {
const binaryBigInt = type_1.binaryBigIntAnnotation.getFirst(type);
if (binaryBigInt !== undefined) {
const writeBigInt = binaryBigInt === 0 /* BinaryBigIntType.unsigned */ ? 'writeBigIntBinary' : 'writeSignedBigIntBinary';
state.addCode(`
if ('bigint' === typeof ${state.accessor} || ('number' === typeof ${state.accessor} && !Number.isNaN(${state.accessor}))) {
//long
state.writer.writeType(${utils_js_1.BSONType.BINARY});
state.writer.${writeBigInt}(${state.accessor});
}`);
}
else {
serializeNumber(type, state);
}
}
serializeBigInt.__type = [() => __ΩTypeBigInt, 'type', () => type_1.TemplateState, 'state', 'serializeBigInt', 'Pn!2"P7#2$"/%'];
function sizerRegExp(type, state) {
state.setContext({ stringByteLength });
sizerPropertyNameAware(type, state, `${state.accessor} instanceof RegExp`, `
state.size += stringByteLength(${state.accessor}.source) + 1
+
(${state.accessor}.global ? 1 : 0) +
(${state.accessor}.ignoreCase ? 1 : 0) +
(${state.accessor}.multiline ? 1 : 0) +
1;
`);
}
sizerRegExp.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'sizerRegExp', 'P"w!2"P7#2$"/%'];
function serializeRegExp(type, state) {
serializePropertyNameAware(type, state, utils_js_1.BSONType.REGEXP, `${state.accessor} instanceof RegExp`, `
state.writer.writeString(${state.accessor}.source);
state.writer.writeNull();
if (${state.accessor}.ignoreCase) state.writer.writeString('i');
if (${state.accessor}.global) state.writer.writeString('s'); //BSON does not use the RegExp flag format
if (${state.accessor}.multiline) state.writer.writeString('m');
state.writer.writeNull();
`);
}
serializeRegExp.__type = ['Type', 'type', () => type_1.TemplateState, 'state', 'serializeRegExp', 'P"w!2"P7#2$"/%'];
function sizerLiteral(type, state) {
if ('string' === typeof type.literal) {
sizeString(type, state);
}
else if ('number' === typeof type.literal || 'bigint' === typeof type.literal) {
sizerNumber(type, state);
}
else if ('boolean' === typeof type.literal) {
sizerBoolean(type, state);
}
else if (type.literal instanceof RegExp) {
sizerRegExp(type, state);
}
}
sizerLiteral.__type = [() => __ΩTypeLiteral, 'type', () => type_1.TemplateState, 'state', 'sizerLiteral', 'Pn!2"P7#2$"/%'];
function serializeLiteral(type, state) {
if ('string' === typeof type.literal) {
serializeString(type, state);
}
else if ('number' === typeof type.literal || 'bigint' === typeof type.literal) {
serializeNumber(type, state);
}
else if ('boolean' === typeof type.literal) {
serializeBoolean(type, state);
}
else if (type.literal instanceof RegExp) {
serializeRegExp(type, state);
}
}
serializeLiteral.__type = [() => __ΩTypeLiteral, 'type', () => type_1.TemplateState, 'state', 'serializeLiteral', 'Pn!2"P7#2$"/%'];
function sizerBinary(type, state) {
state.setContext({ ArrayBuffer });
sizerPropertyNameAware(type, state, `${state.accessor} instanceof ArrayBuffer || ArrayBuffer.isView(${state.accessor})`, `
state.size += 4 + 1 + ${state.accessor}.byteLength;
`);
}
sizerBinary.__type = [() => __ΩTypeClass, 'type', () => type_1.TemplateState, 'state', 'sizerBinary', 'Pn!2"P7#2$"/%'];
function serializeBinary(type, state) {
state.setContext({ ArrayBuffer });
serializePropertyNameAware(type, state, utils_js_1.BSONType.BINARY, `${state.accessor} instanceof ArrayBuffer || ArrayBuffer.isView(${state.accessor})`, `
state.writer.writeArrayBuffer(${state.accessor});
`);
}
serializeBinary.__type = [() => __ΩTypeClass, 'type', () => type_1.TemplateState, 'state', 'serializeBinary', 'Pn!2"P7#2$"/%'];
function sizerArray(type, state) {
const elementType = type.type;
state.setContext({ isIterable: core_1.isIterable });
const i = state.compilerContext.reserveName('arrayItemIndex');
const item = state.compilerContext.reserveName('item');
state.setContext({ digitByteSize: utils_js_1.digitByteSize });
const memberState = state.fork('', item).extendPath(