UNPKG

@deepkit/bson

Version:

Deepkit BSON parser

348 lines 12.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseParser = void 0; exports.readUint32LE = readUint32LE; exports.readInt32LE = readInt32LE; exports.readFloat64LE = readFloat64LE; exports.parseObject = parseObject; exports.parseArray = parseArray; exports.deserializeBSONWithoutOptimiser = deserializeBSONWithoutOptimiser; /* * 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 utils_js_1 = require("./utils.js"); const strings_js_1 = require("./strings.js"); const type_1 = require("@deepkit/type"); const model_js_1 = require("./model.js"); function readUint32LE(buffer, offset) { return (buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24) >>> 0); } readUint32LE.__type = ['buffer', 'offset', 'readUint32LE', 'PW2!\'2"\'/#']; function readInt32LE(buffer, offset) { return (buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)); } readInt32LE.__type = ['buffer', 'offset', 'readInt32LE', 'PW2!\'2"\'/#']; const float64Buffer = new ArrayBuffer(8); const u32 = new Uint32Array(float64Buffer); const f64 = new Float64Array(float64Buffer); function readFloat64LE(buffer, offset) { u32[0] = buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24); u32[1] = buffer[offset + 4] | (buffer[offset + 5] << 8) | (buffer[offset + 6] << 16) | (buffer[offset + 7] << 24); return f64[0]; } readFloat64LE.__type = ['buffer', 'offset', 'readFloat64LE', 'PW2!\'2"\'/#']; /** * This is the (slowest) base parser which parses all property names as utf8. */ class BaseParser { constructor(buffer, offset = 0) { this.buffer = buffer; this.offset = offset; this.size = buffer.byteLength; } peek(elementType, type) { const offset = this.offset; const v = this.parse(elementType, type); this.offset = offset; return v; } parse(elementType, type) { switch (elementType) { case utils_js_1.BSONType.STRING: return this.parseString(); case utils_js_1.BSONType.NUMBER: return this.parseNumber(); case utils_js_1.BSONType.OID: return this.parseOid(); case utils_js_1.BSONType.INT: return this.parseInt(); case utils_js_1.BSONType.DATE: return this.parseDate(); case utils_js_1.BSONType.LONG: case utils_js_1.BSONType.TIMESTAMP: return this.parseLong(); case utils_js_1.BSONType.BOOLEAN: return this.parseBoolean(); case utils_js_1.BSONType.NULL: return null; case utils_js_1.BSONType.UNDEFINED: return undefined; case utils_js_1.BSONType.BINARY: return this.parseBinary(type); case utils_js_1.BSONType.REGEXP: return this.parseRegExp(); case utils_js_1.BSONType.OBJECT: return parseObject(this); case utils_js_1.BSONType.ARRAY: return parseArray(this); default: throw new type_1.SerializationError('Unsupported BSON type ' + elementType); } } parseRegExp() { const source = this.eatString(this.stringSize()); const options = this.eatString(this.stringSize()); return new RegExp(source, options.replace('s', 'g')); } /** * read the content without moving the parser offset. */ read(elementType, type) { const start = this.offset; try { return this.parse(elementType, type); } finally { this.offset = start; } } parseBoolean() { return this.eatByte() === 1; } parseLong() { const lowBits = this.eatInt32(); const highBits = this.eatInt32(); return BigInt(highBits) * BigInt(utils_js_1.TWO_PWR_32_DBL_N) + (BigInt(lowBits >>> 0)); } parseString() { return this.eatString(this.eatUInt32()); } parseBinaryBigInt() { let size = this.eatUInt32(); const subType = this.eatByte(); if (subType === utils_js_1.BSON_BINARY_SUBTYPE_BYTE_ARRAY) { size = this.eatUInt32(); } const nextPosition = this.offset + size; const v = this.readBigIntBinary(size); this.offset = nextPosition; return v; } parseSignedBinaryBigInt() { let size = this.eatUInt32(); const subType = this.eatByte(); if (subType === utils_js_1.BSON_BINARY_SUBTYPE_BYTE_ARRAY) { size = this.eatUInt32(); } const nextPosition = this.offset + size; const v = this.readSignedBigIntBinary(size); this.offset = nextPosition; return v; } parseBinary(type) { let size = this.eatUInt32(); const subType = this.eatByte(); if (subType === utils_js_1.BSON_BINARY_SUBTYPE_UUID) { const nextPosition = this.offset + size; const v = this.parseUUID(); this.offset = nextPosition; return v; } if (subType === utils_js_1.BSON_BINARY_SUBTYPE_BYTE_ARRAY) { size = this.eatUInt32(); } const b = this.buffer.slice(this.offset, this.offset + size); this.seek(size); if (type && type.kind === type_1.ReflectionKind.class && type.classType === ArrayBuffer) { return (0, type_1.nodeBufferToArrayBuffer)(b); } if (type && type.kind === type_1.ReflectionKind.class) { const typedArrayConstructor = type.classType; return new typedArrayConstructor((0, type_1.nodeBufferToArrayBuffer)(b)); } return b; } readBigIntBinary(size) { if (size === 0) return BigInt(0); //todo: check if that is faster than the string concatenation // let r = BigInt(0); // const n8 = BigInt(8); // for (let i = 0; i < size; i++) { // if (i !== 0) r = r << n8; // r += BigInt(this.buffer[this.offset + i]); // } // return r; let s = ''; for (let i = 0; i < size; i++) { s += model_js_1.hexTable[this.buffer[this.offset + i]]; } return BigInt('0x' + s); } readSignedBigIntBinary(size) { if (size === 0) return BigInt(0); let s = ''; const signum = this.buffer[this.offset]; for (let i = 1; i < size; i++) { s += model_js_1.hexTable[this.buffer[this.offset + i]]; } //255 means negative if (signum === 255) return BigInt('0x' + s) * BigInt(-1); return BigInt('0x' + s); } parseNumber() { return this.eatDouble(); } parseOid() { const offset = this.offset, b = this.buffer; let o = model_js_1.hexTable[b[offset]] + model_js_1.hexTable[b[offset + 1]] + model_js_1.hexTable[b[offset + 2]] + model_js_1.hexTable[b[offset + 3]] + model_js_1.hexTable[b[offset + 4]] + model_js_1.hexTable[b[offset + 5]] + model_js_1.hexTable[b[offset + 6]] + model_js_1.hexTable[b[offset + 7]] + model_js_1.hexTable[b[offset + 8]] + model_js_1.hexTable[b[offset + 9]] + model_js_1.hexTable[b[offset + 10]] + model_js_1.hexTable[b[offset + 11]]; this.seek(12); return o; } parseUUID() { //e.g. bef8de96-41fe-442f-b70c-c3a150f8c96c // 4 2 2 2 6 const offset = this.offset, b = this.buffer; let o = model_js_1.hexTable[b[offset]] + model_js_1.hexTable[b[offset + 1]] + model_js_1.hexTable[b[offset + 2]] + model_js_1.hexTable[b[offset + 3]] + '-' + model_js_1.hexTable[b[offset + 4]] + model_js_1.hexTable[b[offset + 5]] + '-' + model_js_1.hexTable[b[offset + 6]] + model_js_1.hexTable[b[offset + 7]] + '-' + model_js_1.hexTable[b[offset + 8]] + model_js_1.hexTable[b[offset + 9]] + '-' + model_js_1.hexTable[b[offset + 10]] + model_js_1.hexTable[b[offset + 11]] + model_js_1.hexTable[b[offset + 12]] + model_js_1.hexTable[b[offset + 13]] + model_js_1.hexTable[b[offset + 14]] + model_js_1.hexTable[b[offset + 15]]; this.seek(16); return o; } parseInt() { return this.eatInt32(); } parseDate() { const lowBits = this.eatInt32(); const highBits = this.eatInt32(); return new Date(highBits * utils_js_1.TWO_PWR_32_DBL_N + (lowBits >>> 0)); } peekUInt32() { return readUint32LE(this.buffer, this.offset); } /** * Returns the size including \0. */ stringSize() { let end = this.offset; while (this.buffer[end] !== 0 && end < this.size) end++; end++; //null return end - this.offset; } eatObjectPropertyName() { let end = this.offset; while (this.buffer[end] !== 0) { if (end >= this.size) throw new type_1.SerializationError('Unexpected end of buffer'); end++; } const s = (0, strings_js_1.decodeUTF8)(this.buffer, this.offset, end); this.offset = end + 1; return s; } seek(size) { this.offset += size; } eatByte() { return this.buffer[this.offset++]; } eatInt32() { const value = readInt32LE(this.buffer, this.offset); this.offset += 4; return value; } eatUInt32() { const value = readUint32LE(this.buffer, this.offset); this.offset += 4; return value; } eatDouble() { const value = readFloat64LE(this.buffer, this.offset); this.offset += 8; if (isNaN(value)) return 0; return value; } /** * Size includes the \0. If not existend, increase by 1. */ eatString(size) { this.offset += size; return (0, strings_js_1.decodeUTF8)(this.buffer, this.offset - size, this.offset - 1); } } exports.BaseParser = BaseParser; BaseParser.__type = ['size', 'buffer', 'offset', () => 0, 'constructor', 'elementType', 'Type', 'type', 'peek', 'parse', 'parseRegExp', 'read', 'parseBoolean', 'parseLong', 'parseString', 'parseBinaryBigInt', 'parseSignedBinaryBigInt', 'parseBinary', 'readBigIntBinary', 'readSignedBigIntBinary', 'parseNumber', 'parseOid', 'parseUUID', 'parseInt', 'parseDate', 'peekUInt32', 'stringSize', 'eatObjectPropertyName', 'seek', 'eatByte', 'eatInt32', 'eatUInt32', 'eatDouble', 'eatString', 'BaseParser', '\'3!PW2":\'2#:>$"0%P\'2&"w\'2(8"0)P\'2&"w\'2(8"0*PA0+P\'2&"w\'2(8"0,P"0-P"0.P"0/P*00P*01P"w\'2(8"02P\'2!*03P\'2!*04P"05P&06P&07P"08P"09P\'0:P\'0;P"0<P\'2!"0=P\'0>P\'0?P\'0@P\'0AP\'2!&0B5wC']; function parseObject(parser) { const result = {}; const end = parser.eatUInt32() + parser.offset; while (parser.offset < end) { const elementType = parser.eatByte(); if (elementType === 0) break; const name = parser.eatObjectPropertyName(); result[name] = parser.parse(elementType); } return result; } parseObject.__type = [() => BaseParser, 'parser', 'parseObject', 'PP7!2""/#']; function parseArray(parser) { const result = []; const end = parser.eatUInt32() + parser.offset; for (let i = 0; parser.offset < end; i++) { const elementType = parser.eatByte(); if (elementType === 0) break; //arrays are represented as objects, so we skip the key name, since we have `i` parser.seek((0, utils_js_1.digitByteSize)(i)); result.push(parser.parse(elementType)); } return result; } parseArray.__type = [() => BaseParser, 'parser', 'parseArray', 'PP7!2""F/#']; function deserializeBSONWithoutOptimiser(buffer, offset = 0) { return parseObject(new BaseParser(buffer, offset)); } deserializeBSONWithoutOptimiser.__type = ['buffer', 'offset', 'deserializeBSONWithoutOptimiser', 'PW2!"2""/#']; //# sourceMappingURL=bson-parser.js.map