/*
* Copyright (c) 2017 Ahmed Kh. Zamil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Created by Ahmed Zamil on 25/07/2017.
*/
"use strict";
import AsyncBag from '../Core/AsyncBag.js';
import AsyncReply from '../Core/AsyncReply.js';
import PropertyValue from './PropertyValue.js';
import {DC, BL} from './DC.js';
import BinaryList from './BinaryList.js';
import DistributedPropertyContext from '../Net/IIP/DistributedPropertyContext.js';
import DistributedResource from '../Net/IIP/DistributedResource.js'
import IResource from '../Resource/IResource.js';
import IRecord from './IRecord.js';
import Record from './Record.js';
import ResourceArrayType from './ResourceArrayType.js';
import Warehouse from '../Resource/Warehouse.js';
import TemplateType from '../Resource/Template/TemplateType.js';
import NotModified from './NotModified.js';
import KeyList from './KeyList.js';
import DataSerializer from './DataSerializer.js';
import DataDeserializer from './DataDeserializer.js';
import TypedList from './TypedList.js';
import TypedMap from './TypedMap.js';
import IEnum from './IEnum.js';
import {TransmissionType, TransmissionTypeIdentifier, TransmissionTypeClass} from './TransmissionType.js';
import { Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128, Float32, Float64, Float128, Char16, Char8 } from './ExtendedTypes.js';
import PropertyValueArray from './PropertyValueArray.js';
import RecordArray from './RecordArray.js';
import ResourceArray from './ResourceArray.js';
import Tuple from './Tuple.js';
export class CodecComposeResults {
//final int transmissionTypeIdentifier;
//final DC data;
constructor(transmissionTypeIdentifier, data) {
this.transmissionTypeIdentifier = transmissionTypeIdentifier;
this.data = data;
}
}
export class CodecParseResults {
//final AsyncReply reply;
//final int size;
constructor(size, reply){
this.size = size;
this.reply = reply;
}
}
export default class Codec {
//AsyncReply Parser(byte[] data, uint offset, uint length, DistributedConnection connection);
static fixedParsers = [
[
DataDeserializer.nullParser,
DataDeserializer.booleanFalseParser,
DataDeserializer.booleanTrueParser,
DataDeserializer.notModifiedParser,
],
[
DataDeserializer.byteParser,
DataDeserializer.sByteParser,
DataDeserializer.char8Parser,
],
[
DataDeserializer.int16Parser,
DataDeserializer.uInt16Parser,
DataDeserializer.char16Parser,
],
[
DataDeserializer.int32Parser,
DataDeserializer.uInt32Parser,
DataDeserializer.float32Parser,
DataDeserializer.resourceParser,
DataDeserializer.localResourceParser,
],
[
DataDeserializer.int64Parser,
DataDeserializer.uInt64Parser,
DataDeserializer.float64Parser,
DataDeserializer.dateTimeParser,
],
[
DataDeserializer.int128Parser, // int 128
DataDeserializer.uInt128Parser, // uint 128
DataDeserializer.float128Parser,
]
];
static dynamicParsers = [
DataDeserializer.rawDataParser,
DataDeserializer.stringParser,
DataDeserializer.listParser,
DataDeserializer.resourceListParser,
DataDeserializer.recordListParser,
];
static typedParsers = [
DataDeserializer.recordParser,
DataDeserializer.typedListParser,
DataDeserializer.typedMapParser,
DataDeserializer.tupleParser,
DataDeserializer.enumParser,
DataDeserializer.constantParser,
];
///
/// Parse a value
///
/// Bytes array
/// Zero-indexed offset.
/// Output the number of bytes parsed
/// DistributedConnection is required in case a structure in the array holds items at the other end.
/// DataType, in case the data is not prepended with DataType
/// Value
static parse(
data, offset, connection,
requestSequence, dataType = null) {
let len = 0;
if (dataType == null) {
var parsedDataTyped = TransmissionType.parse(data, offset, data.length);
len = parsedDataTyped.size;
dataType = parsedDataTyped.type;
offset = dataType?.offset ?? 0;
} else
len = dataType.contentLength;
if (dataType != null) {
if (dataType.classType == TransmissionTypeClass.Fixed) {
return new CodecParseResults(
len,
Codec.fixedParsers[dataType.exponent][dataType.index](
data, dataType.offset, dataType.contentLength, connection, requestSequence));
} else if (dataType.classType == TransmissionTypeClass.Dynamic) {
return new CodecParseResults(
len,
Codec.dynamicParsers[dataType.index](
data, dataType.offset, dataType.contentLength, connection, requestSequence));
} else //if (tt.Class == TransmissionTypeClass.Typed)
{
return new CodecParseResults(
len,
Codec.typedParsers[dataType.index](
data, dataType.offset, dataType.contentLength, connection, requestSequence));
}
}
throw Error("Can't parse transmission type.");
}
static mapFromObject(map){
var rt = new Map();
for(var i in map)
rt.set(i, map[i]);
}
static composers = {
// Fixed
[Boolean]: DataSerializer.boolComposer,
[NotModified]: DataSerializer.notModifiedComposer,
[Char8]: DataSerializer.char8Composer,
[Char16]: DataSerializer.char16Composer,
[Int64]: DataSerializer.int64Composer,
[UInt64]: DataSerializer.uInt64Composer,
[Int32]: DataSerializer.int32Composer,
[UInt32]: DataSerializer.uInt32Composer,
[Int16]: DataSerializer.int16Composer,
[UInt16]: DataSerializer.uInt16Composer,
[Int8]: DataSerializer.int8Composer,
[UInt8]: DataSerializer.uInt8Composer,
[Float32]: DataSerializer.float32Composer,
[Float64]: DataSerializer.float64Composer,
[Float128]: DataSerializer.float128Composer,
[Number]: DataSerializer.numberComposer,
[Date]: DataSerializer.dateTimeComposer,
[DC]: DataSerializer.rawDataComposer,
[Uint8Array]: DataSerializer.rawDataComposer,
[String]: DataSerializer.stringComposer,
// Special
[Array]: DataSerializer.listComposer,
[ResourceArray]: DataSerializer.resourceListComposer,
[RecordArray]: DataSerializer.recordListComposer,
[Map]: DataSerializer.mapComposer,
[PropertyValueArray]: DataSerializer.propertyValueArrayComposer
// Typed
};
static getListType(list) {
if (list instanceof TypedList)
return TypedList.getType(list);
else
return Object;
}
static getMapTypes(map) {
if (map instanceof TypedMap)
return TypedMap.getTypes(map);
else
return [Object, Object];
}
///
/// Compose a variable
///
/// Value to compose.
/// DistributedConnection is required to check locality.
/// If True, prepend the DataType at the beginning of the output.
/// Array of bytes in the network byte order.
static compose(valueOrSource, connection) {
if (valueOrSource == null)
return TransmissionType.compose(TransmissionTypeIdentifier.Null, new DC(0));
var type = valueOrSource.constructor;
// if (type.)
// {
// var genericType = type.GetGenericTypeDefinition();
// if (genericType == typeof(DistributedPropertyContext<>))
// {
// valueOrSource = ((IDistributedPropertyContext)valueOrSource).GetValue(connection);
// }
// else if (genericType == typeof(Func<>))
// {
// var args = genericType.GetGenericArguments();
// if (args.Length == 2 && args[0] == typeof(DistributedConnection))
// {
// //Func a;
// //a.Invoke()
// }
// }
// }
// if (valueOrSource is IUserType)
// valueOrSource = (valueOrSource as IUserType).Get();
//if (valueOrSource is Func)
// valueOrSource = (valueOrSource as Func)(connection);
// if (valueOrSource == null)
// return TransmissionType.Compose(TransmissionTypeIdentifier.Null, null);
// type = valueOrSource.GetType();
if (this.composers[type] != undefined) {
let results = this.composers[type](valueOrSource, connection);
return TransmissionType.compose(results.identifier, results.data);
} else {
if (valueOrSource instanceof TypedList) {
let genericType = this.getListType(valueOrSource);
let results = DataSerializer.typedListComposer(
valueOrSource, genericType, connection);
return TransmissionType.compose(results.identifier, results.data);
} else if (valueOrSource instanceof TypedMap) {
let genericTypes =TypedMap.getTypes(valueOrSource);
let results = DataSerializer.typedMapComposer(
valueOrSource, genericTypes[0], genericTypes[1], connection);
return TransmissionType.compose(results.identifier, results.data);
} else if (valueOrSource instanceof IResource) {
let results =
DataSerializer.resourceComposer(valueOrSource, connection);
return TransmissionType.compose(results.identifier, results.data);
} else if (valueOrSource instanceof IRecord) {
let results = DataSerializer.recordComposer(valueOrSource, connection);
return TransmissionType.compose(results.identifier, results.data);
} else if (valueOrSource instanceof IEnum) {
let results = DataSerializer.enumComposer(valueOrSource, connection);
return TransmissionType.compose(results.identifier, results.data);
}
else if (valueOrSource instanceof Tuple) {
let results = DataSerializer.tupleComposer(valueOrSource, connection);
return TransmissionType.compose(results.identifier, results.data);
}
}
return TransmissionType.compose(TransmissionTypeIdentifier.Null, new DC(0));
}
///
/// Check if a resource is local to a given connection.
///
/// Resource to check.
/// DistributedConnection to check if the resource is local to it.
/// True, if the resource owner is the given connection, otherwise False.
static isLocalResource( resource, connection) {
if (connection == null) return false;
if (resource instanceof DistributedResource) {
if (resource._p.connection == connection) return true;
}
return false;
}
}