blob: 3686bb43c27a285e75acb18f6bdd978916629b5a [file] [log] [blame]
/*
* 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 Decimal = require('decimal.js');
const Errors = require('../Errors');
const ComplexObjectType = require('../ObjectType').ComplexObjectType;
const BinaryUtils = require('./BinaryUtils');
class BinaryWriter {
static async writeString(buffer, value) {
await BinaryWriter.writeObject(buffer, value, BinaryUtils.TYPE_CODE.STRING);
}
static async writeObject(buffer, object, objectType = null, writeObjectType = true) {
BinaryUtils.checkCompatibility(object, objectType);
if (object === null) {
buffer.writeByte(BinaryUtils.TYPE_CODE.NULL);
return;
}
objectType = objectType ? objectType : BinaryUtils.calcObjectType(object);
const objectTypeCode = BinaryUtils.getTypeCode(objectType);
if (writeObjectType) {
buffer.writeByte(objectTypeCode);
}
switch (objectTypeCode) {
case BinaryUtils.TYPE_CODE.BYTE:
case BinaryUtils.TYPE_CODE.SHORT:
case BinaryUtils.TYPE_CODE.INTEGER:
case BinaryUtils.TYPE_CODE.FLOAT:
case BinaryUtils.TYPE_CODE.DOUBLE:
buffer.writeNumber(object, objectTypeCode);
break;
case BinaryUtils.TYPE_CODE.LONG:
buffer.writeLong(object);
break;
case BinaryUtils.TYPE_CODE.CHAR:
buffer.writeChar(object);
break;
case BinaryUtils.TYPE_CODE.BOOLEAN:
buffer.writeBoolean(object);
break;
case BinaryUtils.TYPE_CODE.STRING:
buffer.writeString(object);
break;
case BinaryUtils.TYPE_CODE.UUID:
BinaryWriter._writeUUID(buffer, object);
break;
case BinaryUtils.TYPE_CODE.DATE:
buffer.writeDate(object);
break;
case BinaryUtils.TYPE_CODE.ENUM:
await BinaryWriter._writeEnum(buffer, object);
break;
case BinaryUtils.TYPE_CODE.DECIMAL:
BinaryWriter._writeDecimal(buffer, object);
break;
case BinaryUtils.TYPE_CODE.TIMESTAMP:
BinaryWriter._writeTimestamp(buffer, object);
break;
case BinaryUtils.TYPE_CODE.TIME:
BinaryWriter._writeTime(buffer, object);
break;
case BinaryUtils.TYPE_CODE.BYTE_ARRAY:
case BinaryUtils.TYPE_CODE.SHORT_ARRAY:
case BinaryUtils.TYPE_CODE.INTEGER_ARRAY:
case BinaryUtils.TYPE_CODE.LONG_ARRAY:
case BinaryUtils.TYPE_CODE.FLOAT_ARRAY:
case BinaryUtils.TYPE_CODE.DOUBLE_ARRAY:
case BinaryUtils.TYPE_CODE.CHAR_ARRAY:
case BinaryUtils.TYPE_CODE.BOOLEAN_ARRAY:
case BinaryUtils.TYPE_CODE.STRING_ARRAY:
case BinaryUtils.TYPE_CODE.UUID_ARRAY:
case BinaryUtils.TYPE_CODE.DATE_ARRAY:
case BinaryUtils.TYPE_CODE.OBJECT_ARRAY:
case BinaryUtils.TYPE_CODE.ENUM_ARRAY:
case BinaryUtils.TYPE_CODE.DECIMAL_ARRAY:
case BinaryUtils.TYPE_CODE.TIMESTAMP_ARRAY:
case BinaryUtils.TYPE_CODE.TIME_ARRAY:
await BinaryWriter._writeArray(buffer, object, objectType, objectTypeCode);
break;
case BinaryUtils.TYPE_CODE.COLLECTION:
await BinaryWriter._writeCollection(buffer, object, objectType);
break;
case BinaryUtils.TYPE_CODE.MAP:
await BinaryWriter._writeMap(buffer, object, objectType);
break;
case BinaryUtils.TYPE_CODE.BINARY_OBJECT:
await BinaryWriter._writeBinaryObject(buffer, object, objectType);
break;
case BinaryUtils.TYPE_CODE.COMPLEX_OBJECT:
await BinaryWriter._writeComplexObject(buffer, object, objectType);
break;
default:
throw Errors.IgniteClientError.unsupportedTypeError(objectType);
}
}
static _writeUUID(buffer, value) {
buffer.writeBuffer(Buffer.from(value));
}
static async _writeEnum(buffer, enumValue) {
await enumValue._write(buffer);
}
static _writeDecimal(buffer, decimal) {
let strValue = decimal.toExponential();
let expIndex = strValue.indexOf('e');
if (expIndex < 0) {
expIndex = strValue.indexOf('E');
}
let scale = 0;
if (expIndex >= 0) {
scale = parseInt(strValue.substring(expIndex + 1));
strValue = strValue.substring(0, expIndex);
}
const isNegative = strValue.startsWith('-');
if (isNegative) {
strValue = strValue.substring(1);
}
const dotIndex = strValue.indexOf('.');
if (dotIndex >= 0) {
scale -= strValue.length - dotIndex - 1;
strValue = strValue.substring(0, dotIndex) + strValue.substring(dotIndex + 1);
}
scale = -scale;
let hexValue = new Decimal(strValue).toHexadecimal().substring(2);
hexValue = ((hexValue.length % 2 !== 0) ? '000' : '00') + hexValue;
const valueBuffer = Buffer.from(hexValue, 'hex');
if (isNegative) {
valueBuffer[0] |= 0x80;
}
buffer.writeInteger(scale);
buffer.writeInteger(valueBuffer.length);
buffer.writeBuffer(valueBuffer);
}
static _writeTimestamp(buffer, timestamp) {
buffer.writeDate(timestamp);
buffer.writeInteger(timestamp.getNanos());
}
static _writeTime(buffer, time) {
const midnight = new Date(time);
midnight.setHours(0, 0, 0, 0);
buffer.writeLong(time.getTime() - midnight.getTime());
}
static async _writeArray(buffer, array, arrayType, arrayTypeCode) {
const BinaryType = require('./BinaryType');
const elementType = BinaryUtils.getArrayElementType(arrayType);
const keepElementType = BinaryUtils.keepArrayElementType(arrayTypeCode);
if (arrayTypeCode === BinaryUtils.TYPE_CODE.OBJECT_ARRAY) {
buffer.writeInteger(elementType instanceof ComplexObjectType ?
BinaryType._calculateId(elementType._typeName) : -1);
}
buffer.writeInteger(array.length);
for (let elem of array) {
await BinaryWriter.writeObject(buffer, elem, elementType, keepElementType);
}
}
static async _writeCollection(buffer, collection, collectionType) {
buffer.writeInteger(collection instanceof Set ? collection.size : collection.length);
buffer.writeByte(collectionType._subType);
for (let element of collection) {
await BinaryWriter.writeObject(buffer, element, collectionType._elementType);
}
}
static async _writeMap(buffer, map, mapType) {
buffer.writeInteger(map.size);
buffer.writeByte(mapType._subType);
for (let [key, value] of map.entries()) {
await BinaryWriter.writeObject(buffer, key, mapType._keyType);
await BinaryWriter.writeObject(buffer, value, mapType._valueType);
}
}
static async _writeBinaryObject(buffer, binaryObject) {
buffer.position = buffer.position - 1;
await binaryObject._write(buffer);
}
static async _writeComplexObject(buffer, object, objectType) {
const BinaryObject = require('../BinaryObject');
await BinaryWriter._writeBinaryObject(buffer, await BinaryObject.fromObject(object, objectType));
}
}
module.exports = BinaryWriter;