UNPKG

apache-ignite-client

Version:

NodeJS Client for Apache Ignite

484 lines (421 loc) 14.4 kB
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ 'use strict'; const Util = require('util'); const Long = require('long'); const ComplexObjectType = require('../ObjectType').ComplexObjectType; const BinaryTypeStorage = require('./BinaryTypeStorage'); const BinaryUtils = require('./BinaryUtils'); const BinaryCommunicator = require('./BinaryCommunicator'); const Errors = require('../Errors'); class BinaryType { constructor(name) { this._name = name; this._id = BinaryType._calculateId(name); this._fields = new Map(); this._schemas = new Map(); this._isEnum = false; this._enumValues = null; } get id() { return this._id; } get name() { return this._name; } get fields() { return [...this._fields.values()]; } getField(fieldId) { return this._fields.get(fieldId); } hasField(fieldId) { return this._fields.has(fieldId); } removeField(fieldId) { return this._fields.delete(fieldId); } setField(field) { this._fields.set(field.id, field); } hasSchema(schemaId) { return this._schemas.has(schemaId); } addSchema(schema) { if (!this.hasSchema(schema.id)) { this._schemas.set(schema.id, schema); } } getSchema(schemaId) { return this._schemas.get(schemaId); } merge(binaryType, binarySchema) { let fieldId; for (let field of binaryType.fields) { fieldId = field.id; if (this.hasField(fieldId)) { if (this.getField(fieldId).typeCode !== field.typeCode) { throw Errors.IgniteClientError.serializationError( true, Util.format('type conflict for field "%s" of complex object type "%s"'), field.name, this._name); } } else { this.setField(field); } } this.addSchema(binarySchema); } clone() { const result = new BinaryType(); result._name = this._name; result._id = this._id; result._fields = new Map(this._fields.entries()); result._schemas = new Map(this._schemas.entries()); result._isEnum = this._isEnum; return result; } isValid() { for (let field of this._fields.values()) { if (!field.isValid()) { return false; } } return this._name !== null; } static _calculateId(name) { return BinaryUtils.hashCodeLowerCase(name); } async _write(buffer) { // type id buffer.writeInteger(this._id); // type name BinaryCommunicator.writeString(buffer, this._name); // affinity key field name BinaryCommunicator.writeString(buffer, null); // fields count buffer.writeInteger(this._fields.size); // fields for (let field of this._fields.values()) { await field._write(buffer); } await this._writeEnum(buffer); // schemas count buffer.writeInteger(this._schemas.size); for (let schema of this._schemas.values()) { await schema._write(buffer); } } async _writeEnum(buffer) { buffer.writeBoolean(this._isEnum); if (this._isEnum) { const length = this._enumValues ? this._enumValues.length : 0; buffer.writeInteger(length); if (length > 0) { for (let [key, value] of this._enumValues) { BinaryCommunicator.writeString(buffer, key); buffer.writeInteger(value); } } } } async _read(buffer) { // type id this._id = buffer.readInteger(); // type name this._name = BinaryCommunicator.readString(buffer); // affinity key field name BinaryCommunicator.readString(buffer); // fields count const fieldsCount = buffer.readInteger(); // fields let field; for (let i = 0; i < fieldsCount; i++) { field = new BinaryField(null, null); await field._read(buffer); this.setField(field); } await this._readEnum(buffer); // schemas count const schemasCount = buffer.readInteger(); // schemas let schema; for (let i = 0; i < schemasCount; i++) { schema = new BinarySchema(); await schema._read(buffer); this.addSchema(schema); } } async _readEnum(buffer) { this._isEnum = buffer.readBoolean(); if (this._isEnum) { const valuesCount = buffer.readInteger(); this._enumValues = new Array(valuesCount); for (let i = 0; i < valuesCount; i++) { this._enumValues[i] = [BinaryCommunicator.readString(buffer), buffer.readInteger()]; } } } } /** FNV1 hash offset basis. */ const FNV1_OFFSET_BASIS = 0x811C9DC5; /** FNV1 hash prime. */ const FNV1_PRIME = 0x01000193; class BinarySchema { constructor() { this._id = BinarySchema._schemaInitialId(); this._fieldIds = new Set(); this._isValid = true; } get id() { return this._id; } get fieldIds() { return [...this._fieldIds]; } finalize() { if (!this._isValid) { this._id = BinarySchema._schemaInitialId(); for (let fieldId of this._fieldIds) { this._id = BinarySchema._updateSchemaId(this._id, fieldId); } this._isValid = true; } } clone() { const result = new BinarySchema(); result._id = this._id; result._fieldIds = new Set(this._fieldIds); result._isValid = this._isValid; return result; } addField(fieldId) { if (!this.hasField(fieldId)) { this._fieldIds.add(fieldId); if (this._isValid) { this._id = BinarySchema._updateSchemaId(this._id, fieldId); } } } removeField(fieldId) { if (this._fieldIds.delete(fieldId)) { this._isValid = false; } } hasField(fieldId) { return this._fieldIds.has(fieldId); } static _schemaInitialId() { return FNV1_OFFSET_BASIS | 0; } static _updateSchemaId(schemaId, fieldId) { schemaId = BinarySchema._updateSchemaIdPart(schemaId, fieldId & 0xFF); schemaId = BinarySchema._updateSchemaIdPart(schemaId, (fieldId >> 8) & 0xFF); schemaId = BinarySchema._updateSchemaIdPart(schemaId, (fieldId >> 16) & 0xFF); schemaId = BinarySchema._updateSchemaIdPart(schemaId, (fieldId >> 24) & 0xFF); return schemaId; } static _updateSchemaIdPart(schemaId, fieldIdPart) { schemaId = schemaId ^ fieldIdPart; schemaId = Long.fromValue(schemaId).multiply(FNV1_PRIME).getLowBits(); return schemaId; } async _write(buffer) { this.finalize(); // schema id buffer.writeInteger(this._id); // fields count buffer.writeInteger(this._fieldIds.size); // field ids for (let fieldId of this._fieldIds) { buffer.writeInteger(fieldId); } } async _read(buffer) { // schema id this._id = buffer.readInteger(); // fields count const fieldsCount = buffer.readInteger(); // field ids for (let i = 0; i < fieldsCount; i++) { this._fieldIds.add(buffer.readInteger()); } } } class BinaryField { constructor(name, typeCode) { this._name = name; this._id = BinaryField._calculateId(name); this._typeCode = typeCode; } get id() { return this._id; } get name() { return this._name; } get typeCode() { return this._typeCode; } isValid() { return this._name !== null; } static _calculateId(name) { return BinaryUtils.hashCodeLowerCase(name); } async _write(buffer) { // field name BinaryCommunicator.writeString(buffer, this._name); // type code buffer.writeInteger(this._typeCode); // field id buffer.writeInteger(this._id); } async _read(buffer) { // field name this._name = BinaryCommunicator.readString(buffer); // type code this._typeCode = buffer.readInteger(); // field id this._id = buffer.readInteger(); } } class BinaryTypeBuilder { static fromTypeName(typeName) { let result = new BinaryTypeBuilder(); result._init(typeName); return result; } static async fromTypeId(communicator, typeId, schemaId) { let result = new BinaryTypeBuilder(); let type = await communicator.typeStorage.getType(typeId, schemaId); if (type) { result._type = type; if (schemaId !== null) { result._schema = type.getSchema(schemaId); if (!result._schema) { throw Errors.IgniteClientError.serializationError( false, Util.format('schema id "%d" specified for complex object of type "%s" not found', schemaId, type.name)); } result._fromStorage = true; } else { result._schema = new BinarySchema(); } return result; } result._init(null); result._type._id = typeId; return result; } static fromObject(jsObject, complexObjectType = null) { if (complexObjectType) { return BinaryTypeBuilder.fromComplexObjectType(complexObjectType, jsObject); } else { const result = new BinaryTypeBuilder(); result._fromComplexObjectType(new ComplexObjectType(jsObject), jsObject); return result; } } static fromComplexObjectType(complexObjectType, jsObject) { let result = new BinaryTypeBuilder(); const typeInfo = BinaryTypeStorage.getByComplexObjectType(complexObjectType); if (typeInfo) { result._type = typeInfo[0]; result._schema = typeInfo[1]; result._fromStorage = true; } else { result._fromComplexObjectType(complexObjectType, jsObject); BinaryTypeStorage.setByComplexObjectType(complexObjectType, result._type, result._schema); } return result; } getTypeId() { return this._type.id; } getTypeName() { return this._type.name; } getSchemaId() { return this._schema.id; } getFields() { return this._type.fields; } getField(fieldId) { return this._type._fields.get(fieldId); } setField(fieldName, fieldTypeCode = null) { const fieldId = BinaryField._calculateId(fieldName); if (!this._type.hasField(fieldId) || !this._schema.hasField(fieldId) || this._type.getField(fieldId).typeCode !== fieldTypeCode) { this._beforeModify(); this._type.setField(new BinaryField(fieldName, fieldTypeCode)); this._schema.addField(fieldId); } } removeField(fieldName) { const fieldId = BinaryField._calculateId(fieldName); if (this._type.hasField(fieldId)) { this._beforeModify(); this._type.removeField(fieldId); this._schema.removeField(fieldId); } } async finalize(communicator) { this._schema.finalize(); await communicator.typeStorage.addType(this._type, this._schema); } constructor() { this._type = null; this._schema = null; this._fromStorage = false; } _fromComplexObjectType(complexObjectType, jsObject) { this._init(complexObjectType._typeName); if (complexObjectType._template) { this._setFields(complexObjectType, complexObjectType._template, jsObject); } } _init(typeName) { this._type = new BinaryType(typeName); this._schema = new BinarySchema(); } _beforeModify() { if (this._fromStorage) { this._type = this._type.clone(); this._schema = this._schema.clone(); this._fromStorage = false; } } _setFields(complexObjectType, objectTemplate, jsObject) { let fieldType; for (let fieldName of BinaryUtils.getJsObjectFieldNames(objectTemplate)) { fieldType = complexObjectType._getFieldType(fieldName); if (!fieldType && jsObject[fieldName]) { fieldType = BinaryUtils.calcObjectType(jsObject[fieldName]); } this.setField(fieldName, BinaryUtils.getTypeCode(fieldType)); } } } module.exports = BinaryType; module.exports.BinaryField = BinaryField; module.exports.BinaryTypeBuilder = BinaryTypeBuilder;