using System;
using System.Collections.Generic;
using System.Text;
using Esiur.Misc;
using System.ComponentModel;
using Esiur.Data;
using Esiur.Engine;
using Esiur.Net.IIP;
using Esiur.Resource;
using System.Linq;
using System.Reflection;
namespace Esiur.Data
{
public static class Codec
{
///
/// Check if a DataType is an array
///
/// DataType to check
/// True if DataType is an array, otherwise false
public static bool IsArray(this DataType type)
{
return (((byte)type & 0x80) == 0x80) && (type != DataType.NotModified);
}
///
/// Get the element DataType
///
///
/// Passing UInt8Array will return UInt8
///
/// DataType to get its element DataType
public static DataType GetElementType(this DataType type)
{
return (DataType)((byte)type & 0x7F);
}
///
/// Get DataType array of a given Structure
///
/// Structure to get its DataTypes
/// Distributed connection is required in case a type is at the other end
private static DataType[] GetStructureDateTypes(Structure structure, DistributedConnection connection)
{
var keys = structure.GetKeys();
var types = new DataType[keys.Length];
for (var i = 0; i < keys.Length; i++)
types[i] = Codec.GetDataType(structure[keys[i]], connection);
return types;
}
///
/// Compare two structures
///
/// Initial structure to compare with
/// Next structure to compare with the initial
/// DistributedConnection is required in case a structure holds items at the other end
public static StructureComparisonResult Compare(Structure initial, Structure next, DistributedConnection connection)
{
if (next == null)
return StructureComparisonResult.Null;
if (initial == null)
return StructureComparisonResult.Structure;
if (next == initial)
return StructureComparisonResult.Same;
if (initial.Length != next.Length)
return StructureComparisonResult.Structure;
var previousKeys = initial.GetKeys();
var nextKeys = next.GetKeys();
for (var i = 0; i < previousKeys.Length; i++)
if (previousKeys[i] != nextKeys[i])
return StructureComparisonResult.Structure;
var previousTypes = GetStructureDateTypes(initial, connection);
var nextTypes = GetStructureDateTypes(next, connection);
for (var i = 0; i < previousTypes.Length; i++)
if (previousTypes[i] != nextTypes[i])
return StructureComparisonResult.StructureSameKeys;
return StructureComparisonResult.StructureSameTypes;
}
///
/// Compose an array of structures into an array of bytes
///
/// Array of Structure to compose
/// DistributedConnection is required in case a structure in the array holds items at the other end
/// If true, prepend the length as UInt32 at the beginning of the returned bytes array
/// Array of bytes in the network byte order
public static byte[] ComposeStructureArray(Structure[] structures, DistributedConnection connection, bool prependLength = false)
{
if (structures == null || structures?.Length == 0)
return new byte[0];
var rt = new BinaryList();
var comparsion = StructureComparisonResult.Structure;
rt.Append((byte)comparsion);
rt.Append(ComposeStructure(structures[0], connection));
for (var i = 1; i < structures.Length; i++)
{
comparsion = Compare(structures[i - 1], structures[i], connection);
rt.Append((byte)comparsion);
if (comparsion == StructureComparisonResult.Structure)
rt.Append(ComposeStructure(structures[i], connection));
else if (comparsion == StructureComparisonResult.StructureSameKeys)
rt.Append(ComposeStructure(structures[i], connection, false));
else if (comparsion == StructureComparisonResult.StructureSameTypes)
rt.Append(ComposeStructure(structures[i], connection, false, false));
}
if (prependLength)
rt.Insert(0, rt.Length);
return rt.ToArray();
}
///
/// Parse an array of structures
///
/// Bytes array
/// Zero-indexed offset
/// Number of bytes to parse
/// DistributedConnection is required in case a structure in the array holds items at the other end
/// Array of structures
public static AsyncBag ParseStructureArray(byte[] data, uint offset, uint length, DistributedConnection connection)
{
var reply = new AsyncBag();
if (length == 0)
{
reply.Seal();
return reply;
}
var end = offset + length;
var result = (StructureComparisonResult)data[offset++];
AsyncReply previous = null;
string[] previousKeys = null;
DataType[] previousTypes = null;
if (result == StructureComparisonResult.Null)
previous = new AsyncReply(null);
else if (result == StructureComparisonResult.Structure)
{
uint cs = data.GetUInt32(offset);
cs += 4;
previous = ParseStructure(data, offset, cs, connection, out previousKeys, out previousTypes);
offset += cs;
}
reply.Add(previous);
while (offset < end)
{
result = (StructureComparisonResult)data[offset++];
if (result == StructureComparisonResult.Null)
previous = new AsyncReply(null);
else if (result == StructureComparisonResult.Structure)
{
uint cs = data.GetUInt32(offset);
cs += 4;
previous = ParseStructure(data, offset, cs, connection, out previousKeys, out previousTypes);
offset += cs;
}
else if (result == StructureComparisonResult.StructureSameKeys)
{
uint cs = data.GetUInt32(offset);
cs += 4;
previous = ParseStructure(data, offset, cs, connection, out previousKeys, out previousTypes, previousKeys);
offset += cs;
}
else if (result == StructureComparisonResult.StructureSameTypes)
{
uint cs = data.GetUInt32(offset);
cs += 4;
previous = ParseStructure(data, offset, cs, connection, out previousKeys, out previousTypes, previousKeys, previousTypes);
offset += cs;
}
reply.Add(previous);
}
reply.Seal();
return reply;
}
///
/// Compose a structure into an array of bytes
///
/// Structure to compose
/// DistributedConnection is required in case an item in the structure is at the other end
/// Whether to include the structure keys
/// Whether to include each item DataType
/// If true, prepend the length as UInt32 at the beginning of the returned bytes array
/// Array of bytes in the network byte order
public static byte[] ComposeStructure(Structure value, DistributedConnection connection, bool includeKeys = true, bool includeTypes = true, bool prependLength = false)
{
var rt = new BinaryList();
if (includeKeys)
{
foreach (var i in value)
{
var key = DC.ToBytes(i.Key);
rt.Append((byte)key.Length, key, Compose(i.Value, connection));
}
}
else
{
foreach (var i in value)
rt.Append(Compose(i.Value, connection, includeTypes));
}
if (prependLength)
rt.Insert(0, rt.Length);
return rt.ToArray();
}
///
/// Parse a structure
///
/// Bytes array
/// Zero-indexed offset.
/// Number of bytes to parse.
/// DistributedConnection is required in case a structure in the array holds items at the other end.
/// Value
public static AsyncReply ParseStructure(byte[] data, uint offset, uint contentLength, DistributedConnection connection)
{
string[] pk;
DataType[] pt;
return ParseStructure(data, offset, contentLength, connection, out pk, out pt);
}
///
/// Parse a structure
///
/// Bytes array
/// Zero-indexed offset.
/// Number of bytes to parse.
/// DistributedConnection is required in case a structure in the array holds items at the other end.
/// Array to store keys in.
/// Array to store DataTypes in.
/// Array of keys, in case the data doesn't include keys
/// Array of DataTypes, in case the data doesn't include DataTypes
/// Structure
public static AsyncReply ParseStructure(byte[] data, uint offset, uint length, DistributedConnection connection, out string[] parsedKeys, out DataType[] parsedTypes, string[] keys = null, DataType[] types = null)
{
var reply = new AsyncReply();
var bag = new AsyncBag