'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.Structure = exports.Unpacker = exports.Packer = undefined; var _iterator = require('babel-runtime/core-js/symbol/iterator'); var _iterator2 = _interopRequireDefault(_iterator); var _from = require('babel-runtime/core-js/array/from'); var _from2 = _interopRequireDefault(_from); var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _typeof2 = require('babel-runtime/helpers/typeof'); var _typeof3 = _interopRequireDefault(_typeof2); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _utf = require('./utf8'); var _utf2 = _interopRequireDefault(_utf); var _integer = require('../integer'); var _integer2 = _interopRequireDefault(_integer); var _error = require('./../error'); var _chunking = require('./chunking'); var _graphTypes = require('../graph-types'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var TINY_STRING = 0x80; /** * Copyright (c) 2002-2018 "Neo4j," * Neo4j Sweden AB [http://neo4j.com] * * This file is part of Neo4j. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var TINY_LIST = 0x90; var TINY_MAP = 0xA0; var TINY_STRUCT = 0xB0; var NULL = 0xC0; var FLOAT_64 = 0xC1; var FALSE = 0xC2; var TRUE = 0xC3; var INT_8 = 0xC8; var INT_16 = 0xC9; var INT_32 = 0xCA; var INT_64 = 0xCB; var STRING_8 = 0xD0; var STRING_16 = 0xD1; var STRING_32 = 0xD2; var LIST_8 = 0xD4; var LIST_16 = 0xD5; var LIST_32 = 0xD6; var BYTES_8 = 0xCC; var BYTES_16 = 0xCD; var BYTES_32 = 0xCE; var MAP_8 = 0xD8; var MAP_16 = 0xD9; var MAP_32 = 0xDA; var STRUCT_8 = 0xDC; var STRUCT_16 = 0xDD; var NODE = 0x4E; var NODE_STRUCT_SIZE = 3; var RELATIONSHIP = 0x52; var RELATIONSHIP_STRUCT_SIZE = 5; var UNBOUND_RELATIONSHIP = 0x72; var UNBOUND_RELATIONSHIP_STRUCT_SIZE = 3; var PATH = 0x50; var PATH_STRUCT_SIZE = 3; /** * A Structure have a signature and fields. * @access private */ var Structure = function () { /** * Create new instance */ function Structure(signature, fields) { (0, _classCallCheck3.default)(this, Structure); this.signature = signature; this.fields = fields; } (0, _createClass3.default)(Structure, [{ key: 'toString', value: function toString() { var fieldStr = ""; for (var i = 0; i < this.fields.length; i++) { if (i > 0) { fieldStr += ", "; } fieldStr += this.fields[i]; } return "Structure(" + this.signature + ", [" + this.fields + "])"; } }]); return Structure; }(); /** * Class to pack * @access private */ var Packer = function () { /** * @constructor * @param {Chunker} channel the chunker backed by a network channel. */ function Packer(channel) { (0, _classCallCheck3.default)(this, Packer); this._ch = channel; this._byteArraysSupported = true; } /** * Creates a packable function out of the provided value * @param x the value to pack * @param onError callback for the case when value cannot be packed * @returns Function */ (0, _createClass3.default)(Packer, [{ key: 'packable', value: function packable(x, onError) { var _this = this; if (x === null) { return function () { return _this._ch.writeUInt8(NULL); }; } else if (x === true) { return function () { return _this._ch.writeUInt8(TRUE); }; } else if (x === false) { return function () { return _this._ch.writeUInt8(FALSE); }; } else if (typeof x == "number") { return function () { return _this.packFloat(x); }; } else if (typeof x == "string") { return function () { return _this.packString(x, onError); }; } else if ((0, _integer.isInt)(x)) { return function () { return _this.packInteger(x); }; } else if (x instanceof Int8Array) { return function () { return _this.packBytes(x, onError); }; } else if (x instanceof Array) { return function () { _this.packListHeader(x.length, onError); for (var _i = 0; _i < x.length; _i++) { _this.packable(x[_i] === undefined ? null : x[_i], onError)(); } }; } else if (isIterable(x)) { return this.packableIterable(x, onError); } else if (x instanceof _graphTypes.Node) { return this._nonPackableValue('It is not allowed to pass nodes in query parameters, given: ' + x, onError); } else if (x instanceof _graphTypes.Relationship) { return this._nonPackableValue('It is not allowed to pass relationships in query parameters, given: ' + x, onError); } else if (x instanceof _graphTypes.Path) { return this._nonPackableValue('It is not allowed to pass paths in query parameters, given: ' + x, onError); } else if (x instanceof Structure) { var packableFields = []; for (var i = 0; i < x.fields.length; i++) { packableFields[i] = this.packable(x.fields[i], onError); } return function () { return _this.packStruct(x.signature, packableFields); }; } else if ((typeof x === 'undefined' ? 'undefined' : (0, _typeof3.default)(x)) == "object") { return function () { var keys = (0, _keys2.default)(x); var count = 0; for (var _i2 = 0; _i2 < keys.length; _i2++) { if (x[keys[_i2]] !== undefined) { count++; } } _this.packMapHeader(count, onError); for (var _i3 = 0; _i3 < keys.length; _i3++) { var key = keys[_i3]; if (x[key] !== undefined) { _this.packString(key); _this.packable(x[key], onError)(); } } }; } else { return this._nonPackableValue('Unable to pack the given value: ' + x, onError); } } }, { key: 'packableIterable', value: function packableIterable(iterable, onError) { try { var array = (0, _from2.default)(iterable); return this.packable(array, onError); } catch (e) { // handle errors from iterable to array conversion onError((0, _error.newError)('Cannot pack given iterable, ' + e.message + ': ' + iterable)); } } /** * Packs a struct * @param signature the signature of the struct * @param packableFields the fields of the struct, make sure you call `packable on all fields` */ }, { key: 'packStruct', value: function packStruct(signature, packableFields, onError) { packableFields = packableFields || []; this.packStructHeader(packableFields.length, signature, onError); for (var i = 0; i < packableFields.length; i++) { packableFields[i](); } } }, { key: 'packInteger', value: function packInteger(x) { var high = x.high, low = x.low; if (x.greaterThanOrEqual(-0x10) && x.lessThan(0x80)) { this._ch.writeInt8(low); } else if (x.greaterThanOrEqual(-0x80) && x.lessThan(-0x10)) { this._ch.writeUInt8(INT_8); this._ch.writeInt8(low); } else if (x.greaterThanOrEqual(-0x8000) && x.lessThan(0x8000)) { this._ch.writeUInt8(INT_16); this._ch.writeInt16(low); } else if (x.greaterThanOrEqual(-0x80000000) && x.lessThan(0x80000000)) { this._ch.writeUInt8(INT_32); this._ch.writeInt32(low); } else { this._ch.writeUInt8(INT_64); this._ch.writeInt32(high); this._ch.writeInt32(low); } } }, { key: 'packFloat', value: function packFloat(x) { this._ch.writeUInt8(FLOAT_64); this._ch.writeFloat64(x); } }, { key: 'packString', value: function packString(x, onError) { var bytes = _utf2.default.encode(x); var size = bytes.length; if (size < 0x10) { this._ch.writeUInt8(TINY_STRING | size); this._ch.writeBytes(bytes); } else if (size < 0x100) { this._ch.writeUInt8(STRING_8); this._ch.writeUInt8(size); this._ch.writeBytes(bytes); } else if (size < 0x10000) { this._ch.writeUInt8(STRING_16); this._ch.writeUInt8(size / 256 >> 0); this._ch.writeUInt8(size % 256); this._ch.writeBytes(bytes); } else if (size < 0x100000000) { this._ch.writeUInt8(STRING_32); this._ch.writeUInt8((size / 16777216 >> 0) % 256); this._ch.writeUInt8((size / 65536 >> 0) % 256); this._ch.writeUInt8((size / 256 >> 0) % 256); this._ch.writeUInt8(size % 256); this._ch.writeBytes(bytes); } else { onError((0, _error.newError)("UTF-8 strings of size " + size + " are not supported")); } } }, { key: 'packListHeader', value: function packListHeader(size, onError) { if (size < 0x10) { this._ch.writeUInt8(TINY_LIST | size); } else if (size < 0x100) { this._ch.writeUInt8(LIST_8); this._ch.writeUInt8(size); } else if (size < 0x10000) { this._ch.writeUInt8(LIST_16); this._ch.writeUInt8((size / 256 >> 0) % 256); this._ch.writeUInt8(size % 256); } else if (size < 0x100000000) { this._ch.writeUInt8(LIST_32); this._ch.writeUInt8((size / 16777216 >> 0) % 256); this._ch.writeUInt8((size / 65536 >> 0) % 256); this._ch.writeUInt8((size / 256 >> 0) % 256); this._ch.writeUInt8(size % 256); } else { onError((0, _error.newError)("Lists of size " + size + " are not supported")); } } }, { key: 'packBytes', value: function packBytes(array, onError) { if (this._byteArraysSupported) { this.packBytesHeader(array.length, onError); for (var i = 0; i < array.length; i++) { this._ch.writeInt8(array[i]); } } else { onError((0, _error.newError)("Byte arrays are not supported by the database this driver is connected to")); } } }, { key: 'packBytesHeader', value: function packBytesHeader(size, onError) { if (size < 0x100) { this._ch.writeUInt8(BYTES_8); this._ch.writeUInt8(size); } else if (size < 0x10000) { this._ch.writeUInt8(BYTES_16); this._ch.writeUInt8((size / 256 >> 0) % 256); this._ch.writeUInt8(size % 256); } else if (size < 0x100000000) { this._ch.writeUInt8(BYTES_32); this._ch.writeUInt8((size / 16777216 >> 0) % 256); this._ch.writeUInt8((size / 65536 >> 0) % 256); this._ch.writeUInt8((size / 256 >> 0) % 256); this._ch.writeUInt8(size % 256); } else { onError((0, _error.newError)('Byte arrays of size ' + size + ' are not supported')); } } }, { key: 'packMapHeader', value: function packMapHeader(size, onError) { if (size < 0x10) { this._ch.writeUInt8(TINY_MAP | size); } else if (size < 0x100) { this._ch.writeUInt8(MAP_8); this._ch.writeUInt8(size); } else if (size < 0x10000) { this._ch.writeUInt8(MAP_16); this._ch.writeUInt8(size / 256 >> 0); this._ch.writeUInt8(size % 256); } else if (size < 0x100000000) { this._ch.writeUInt8(MAP_32); this._ch.writeUInt8((size / 16777216 >> 0) % 256); this._ch.writeUInt8((size / 65536 >> 0) % 256); this._ch.writeUInt8((size / 256 >> 0) % 256); this._ch.writeUInt8(size % 256); } else { onError((0, _error.newError)("Maps of size " + size + " are not supported")); } } }, { key: 'packStructHeader', value: function packStructHeader(size, signature, onError) { if (size < 0x10) { this._ch.writeUInt8(TINY_STRUCT | size); this._ch.writeUInt8(signature); } else if (size < 0x100) { this._ch.writeUInt8(STRUCT_8); this._ch.writeUInt8(size); this._ch.writeUInt8(signature); } else if (size < 0x10000) { this._ch.writeUInt8(STRUCT_16); this._ch.writeUInt8(size / 256 >> 0); this._ch.writeUInt8(size % 256); } else { onError((0, _error.newError)("Structures of size " + size + " are not supported")); } } }, { key: 'disableByteArrays', value: function disableByteArrays() { this._byteArraysSupported = false; } }, { key: '_nonPackableValue', value: function _nonPackableValue(message, onError) { if (onError) { onError((0, _error.newError)(message, _error.PROTOCOL_ERROR)); } return function () { return undefined; }; } }]); return Packer; }(); /** * Class to unpack * @access private */ var Unpacker = function () { /** * @constructor * @param {boolean} disableLosslessIntegers if this unpacker should convert all received integers to native JS numbers. */ function Unpacker() { var disableLosslessIntegers = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; (0, _classCallCheck3.default)(this, Unpacker); this._disableLosslessIntegers = disableLosslessIntegers; } (0, _createClass3.default)(Unpacker, [{ key: 'unpack', value: function unpack(buffer) { var marker = buffer.readUInt8(); var markerHigh = marker & 0xF0; var markerLow = marker & 0x0F; if (marker == NULL) { return null; } var boolean = this._unpackBoolean(marker); if (boolean !== null) { return boolean; } var numberOrInteger = this._unpackNumberOrInteger(marker, buffer); if (numberOrInteger !== null) { if (this._disableLosslessIntegers && (0, _integer.isInt)(numberOrInteger)) { return numberOrInteger.toNumberOrInfinity(); } return numberOrInteger; } var string = this._unpackString(marker, markerHigh, markerLow, buffer); if (string !== null) { return string; } var list = this._unpackList(marker, markerHigh, markerLow, buffer); if (list !== null) { return list; } var byteArray = this._unpackByteArray(marker, buffer); if (byteArray !== null) { return byteArray; } var map = this._unpackMap(marker, markerHigh, markerLow, buffer); if (map !== null) { return map; } var struct = this._unpackStruct(marker, markerHigh, markerLow, buffer); if (struct !== null) { return struct; } throw (0, _error.newError)('Unknown packed value with marker ' + marker.toString(16)); } }, { key: 'unpackInteger', value: function unpackInteger(buffer) { var marker = buffer.readUInt8(); var result = this._unpackInteger(marker, buffer); if (result == null) { throw (0, _error.newError)('Unable to unpack integer value with marker ' + marker.toString(16)); } return result; } }, { key: '_unpackBoolean', value: function _unpackBoolean(marker) { if (marker == TRUE) { return true; } else if (marker == FALSE) { return false; } else { return null; } } }, { key: '_unpackNumberOrInteger', value: function _unpackNumberOrInteger(marker, buffer) { if (marker == FLOAT_64) { return buffer.readFloat64(); } else { return this._unpackInteger(marker, buffer); } } }, { key: '_unpackInteger', value: function _unpackInteger(marker, buffer) { if (marker >= 0 && marker < 128) { return (0, _integer.int)(marker); } else if (marker >= 240 && marker < 256) { return (0, _integer.int)(marker - 256); } else if (marker == INT_8) { return (0, _integer.int)(buffer.readInt8()); } else if (marker == INT_16) { return (0, _integer.int)(buffer.readInt16()); } else if (marker == INT_32) { var b = buffer.readInt32(); return (0, _integer.int)(b); } else if (marker == INT_64) { var high = buffer.readInt32(); var low = buffer.readInt32(); return new _integer2.default(low, high); } else { return null; } } }, { key: '_unpackString', value: function _unpackString(marker, markerHigh, markerLow, buffer) { if (markerHigh == TINY_STRING) { return _utf2.default.decode(buffer, markerLow); } else if (marker == STRING_8) { return _utf2.default.decode(buffer, buffer.readUInt8()); } else if (marker == STRING_16) { return _utf2.default.decode(buffer, buffer.readUInt16()); } else if (marker == STRING_32) { return _utf2.default.decode(buffer, buffer.readUInt32()); } else { return null; } } }, { key: '_unpackList', value: function _unpackList(marker, markerHigh, markerLow, buffer) { if (markerHigh == TINY_LIST) { return this._unpackListWithSize(markerLow, buffer); } else if (marker == LIST_8) { return this._unpackListWithSize(buffer.readUInt8(), buffer); } else if (marker == LIST_16) { return this._unpackListWithSize(buffer.readUInt16(), buffer); } else if (marker == LIST_32) { return this._unpackListWithSize(buffer.readUInt32(), buffer); } else { return null; } } }, { key: '_unpackListWithSize', value: function _unpackListWithSize(size, buffer) { var value = []; for (var i = 0; i < size; i++) { value.push(this.unpack(buffer)); } return value; } }, { key: '_unpackByteArray', value: function _unpackByteArray(marker, buffer) { if (marker == BYTES_8) { return this._unpackByteArrayWithSize(buffer.readUInt8(), buffer); } else if (marker == BYTES_16) { return this._unpackByteArrayWithSize(buffer.readUInt16(), buffer); } else if (marker == BYTES_32) { return this._unpackByteArrayWithSize(buffer.readUInt32(), buffer); } else { return null; } } }, { key: '_unpackByteArrayWithSize', value: function _unpackByteArrayWithSize(size, buffer) { var value = new Int8Array(size); for (var i = 0; i < size; i++) { value[i] = buffer.readInt8(); } return value; } }, { key: '_unpackMap', value: function _unpackMap(marker, markerHigh, markerLow, buffer) { if (markerHigh == TINY_MAP) { return this._unpackMapWithSize(markerLow, buffer); } else if (marker == MAP_8) { return this._unpackMapWithSize(buffer.readUInt8(), buffer); } else if (marker == MAP_16) { return this._unpackMapWithSize(buffer.readUInt16(), buffer); } else if (marker == MAP_32) { return this._unpackMapWithSize(buffer.readUInt32(), buffer); } else { return null; } } }, { key: '_unpackMapWithSize', value: function _unpackMapWithSize(size, buffer) { var value = {}; for (var i = 0; i < size; i++) { var key = this.unpack(buffer); value[key] = this.unpack(buffer); } return value; } }, { key: '_unpackStruct', value: function _unpackStruct(marker, markerHigh, markerLow, buffer) { if (markerHigh == TINY_STRUCT) { return this._unpackStructWithSize(markerLow, buffer); } else if (marker == STRUCT_8) { return this._unpackStructWithSize(buffer.readUInt8(), buffer); } else if (marker == STRUCT_16) { return this._unpackStructWithSize(buffer.readUInt16(), buffer); } else { return null; } } }, { key: '_unpackStructWithSize', value: function _unpackStructWithSize(structSize, buffer) { var signature = buffer.readUInt8(); if (signature == NODE) { return this._unpackNode(structSize, buffer); } else if (signature == RELATIONSHIP) { return this._unpackRelationship(structSize, buffer); } else if (signature == UNBOUND_RELATIONSHIP) { return this._unpackUnboundRelationship(structSize, buffer); } else if (signature == PATH) { return this._unpackPath(structSize, buffer); } else { return this._unpackUnknownStruct(signature, structSize, buffer); } } }, { key: '_unpackNode', value: function _unpackNode(structSize, buffer) { this._verifyStructSize('Node', NODE_STRUCT_SIZE, structSize); return new _graphTypes.Node(this.unpack(buffer), // Identity this.unpack(buffer), // Labels this.unpack(buffer) // Properties ); } }, { key: '_unpackRelationship', value: function _unpackRelationship(structSize, buffer) { this._verifyStructSize('Relationship', RELATIONSHIP_STRUCT_SIZE, structSize); return new _graphTypes.Relationship(this.unpack(buffer), // Identity this.unpack(buffer), // Start Node Identity this.unpack(buffer), // End Node Identity this.unpack(buffer), // Type this.unpack(buffer) // Properties ); } }, { key: '_unpackUnboundRelationship', value: function _unpackUnboundRelationship(structSize, buffer) { this._verifyStructSize('UnboundRelationship', UNBOUND_RELATIONSHIP_STRUCT_SIZE, structSize); return new _graphTypes.UnboundRelationship(this.unpack(buffer), // Identity this.unpack(buffer), // Type this.unpack(buffer) // Properties ); } }, { key: '_unpackPath', value: function _unpackPath(structSize, buffer) { this._verifyStructSize('Path', PATH_STRUCT_SIZE, structSize); var nodes = this.unpack(buffer); var rels = this.unpack(buffer); var sequence = this.unpack(buffer); var segments = []; var prevNode = nodes[0]; for (var i = 0; i < sequence.length; i += 2) { var nextNode = nodes[sequence[i + 1]]; var relIndex = sequence[i]; var rel = void 0; if (relIndex > 0) { rel = rels[relIndex - 1]; if (rel instanceof _graphTypes.UnboundRelationship) { // To avoid duplication, relationships in a path do not contain // information about their start and end nodes, that's instead // inferred from the path sequence. This is us inferring (and, // for performance reasons remembering) the start/end of a rel. rels[relIndex - 1] = rel = rel.bind(prevNode.identity, nextNode.identity); } } else { rel = rels[-relIndex - 1]; if (rel instanceof _graphTypes.UnboundRelationship) { // See above rels[-relIndex - 1] = rel = rel.bind(nextNode.identity, prevNode.identity); } } // Done hydrating one path segment. segments.push(new _graphTypes.PathSegment(prevNode, rel, nextNode)); prevNode = nextNode; } return new _graphTypes.Path(nodes[0], nodes[nodes.length - 1], segments); } }, { key: '_unpackUnknownStruct', value: function _unpackUnknownStruct(signature, structSize, buffer) { var result = new Structure(signature, []); for (var i = 0; i < structSize; i++) { result.fields.push(this.unpack(buffer)); } return result; } }, { key: '_verifyStructSize', value: function _verifyStructSize(structName, expectedSize, actualSize) { if (expectedSize !== actualSize) { throw (0, _error.newError)('Wrong struct size for ' + structName + ', expected ' + expectedSize + ' but was ' + actualSize, _error.PROTOCOL_ERROR); } } }]); return Unpacker; }(); function isIterable(obj) { if (obj == null) { return false; } return typeof obj[_iterator2.default] === 'function'; } exports.Packer = Packer; exports.Unpacker = Unpacker; exports.Structure = Structure;