2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2025-06-26 21:13:13 +00:00

Add project files.

This commit is contained in:
Ahmed Zamil
2017-09-15 23:40:03 +03:00
parent 4c95cb1cc6
commit 7ae722ab51
99 changed files with 14687 additions and 0 deletions

253
Esiur/Data/AutoList.cs Normal file
View File

@ -0,0 +1,253 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Esiur.Engine;
using System.Reflection;
namespace Esiur.Data
{
public class AutoList<T, ST> : IEnumerable
{
private readonly object syncRoot = new object();
private List<T> list = new List<T>();
public delegate void Modified(ST sender, int index, T oldValue, T newValue);
public delegate void Added(ST sender, T value);
public delegate void Removed(ST sender, T value);
public delegate void Cleared(ST sender);
public event Modified OnModified;
public event Removed OnRemoved;
public event Cleared OnCleared;
public event Added OnAdd;
ST state;
bool removableList;
/*
IOrderedEnumerable<T> OrderBy<T, TK>(Func<T, TK> keySelector)
{
return list.OrderBy<T,TK>(keySelector);
}
*/
public void Sort()
{
list.Sort();
}
public void Sort(IComparer<T> comparer)
{
list.Sort(comparer);
}
public IEnumerable<T> Where(Func<T, bool> predicate)
{
return list.Where(predicate);
}
/// <summary>
/// Convert AutoList to array
/// </summary>
/// <returns>Array</returns>
public T[] ToArray()
{
// list.OrderBy()
return list.ToArray();
}
/// <summary>
/// Create a new instance of AutoList
/// </summary>
/// <param name="state">State object to be included when an event is raised.</param>
public AutoList(ST state)
{
this.state = state;
#if NETSTANDARD1_5
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
#else
removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
#endif
}
/// <summary>
/// Create a new instance of AutoList
/// </summary>
/// <param name="values">Populate the list with items</param>
/// <returns></returns>
public AutoList(T[] values)
{
#if NETSTANDARD1_5
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
#else
removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
#endif
AddRange(values);
}
/// <summary>
/// Synchronization lock of the list
/// </summary>
public object SyncRoot
{
get
{
return syncRoot;
}
}
/// <summary>
/// First item in the list
/// </summary>
public T First()
{
return list.First();
}
/// <summary>
/// Get an item at a specified index
/// </summary>
public T this[int index]
{
get
{
return list[index];
}
set
{
var oldValue = list[index];
if (removableList)
{
if (oldValue != null)
((IDestructible)oldValue).OnDestroy -= ItemDestroyed;
if (value != null)
((IDestructible)value).OnDestroy += ItemDestroyed;
}
lock (syncRoot)
list[index] = value;
OnModified?.Invoke(state, index, oldValue, value);
}
}
/// <summary>
/// Add item to the list
/// </summary>
public void Add(T value)
{
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy += ItemDestroyed;
lock (syncRoot)
list.Add(value);
OnAdd?.Invoke(state, value);
}
/// <summary>
/// Add an array of items to the list
/// </summary>
public void AddRange(T[] values)
{
foreach (var v in values)
Add(v);
}
private void ItemDestroyed(object sender)
{
Remove((T)sender);
}
public IEnumerator GetEnumerator()
{
return list.GetEnumerator();
}
/// <summary>
/// Clear the list
/// </summary>
public void Clear()
{
if (removableList)
foreach(IDestructible v in list)
if (v!=null)
v.OnDestroy -= ItemDestroyed;
lock (syncRoot)
list.Clear();
OnCleared?.Invoke(state);
}
/// <summary>
/// Remove an item from the list
/// <param name="value">Item to remove</param>
/// </summary>
public void Remove(T value)
{
if (!list.Contains(value))
return;
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy -= ItemDestroyed;
lock (syncRoot)
list.Remove(value);
OnRemoved?.Invoke(state, value);
}
/// <summary>
/// Number of items in the list
/// </summary>
public int Count
{
get { return list.Count; }
}
/// <summary>
/// Check if an item exists in the list
/// </summary>
/// <param name="value">Item to check if exists</param>
public bool Contains(T value)
{
return list.Contains(value);
}
/// <summary>
/// Check if any item of the given array is in the list
/// </summary>
/// <param name="values">Array of items</param>
public bool ContainsAny(T[] values)
{
foreach (var v in values)
if (list.Contains(v))
return true;
return false;
}
/// <summary>
/// Check if any item of the given list is in the list
/// </summary>
/// <param name="values">List of items</param>
public bool ContainsAny(AutoList<T, ST> values)
{
foreach (var v in values)
if (list.Contains((T)v))
return true;
return false;
}
}
}

196
Esiur/Data/BinaryList.cs Normal file
View File

@ -0,0 +1,196 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esiur.Misc;
using System.Reflection;
namespace Esiur.Data
{
/// <summary>
/// BinaryList holds a list of items to be converted to binary for storage and transmission
/// </summary>
public class BinaryList
{
private List<byte> held = new List<byte>();
/// <summary>
/// Create an instance of BinaryList
/// </summary>
public BinaryList()
{
}
/// <summary>
/// Converts parameters to binary in same order
/// </summary>
/// <param name="values">Variables to convert</param>
public static byte[] ToBytes(params object[] values)
{
var held = new List<byte>();
foreach (var i in values)
{
if (i is byte)
held.Add((byte)i);
else
{
#if NETSTANDARD1_5
MethodInfo mi = typeof(DC).GetTypeInfo().GetMethod("ToBytes", new Type[] { i.GetType() });
#else
MethodInfo mi = typeof(DC).GetMethod("ToBytes", new Type[] { i.GetType() });
#endif
if (mi != null)
{
var b = (byte[])mi.Invoke(null, new object[] { i });
held.AddRange(b);
}
}
}
return held.ToArray();
}
/// <summary>
/// Create a new instance of BinaryList
/// </summary>
/// <param name="values">Populate the list items</param>
public BinaryList(params object[] values)
{
AddRange(values);
}
/// <summary>
/// Add an array of items at the end of the list
/// </summary>
/// <param name="values">Array of items</param>
public void AddRange(object[] values)
{
foreach (var i in values)
{
if (i is byte)
held.Add((byte)i);
else
{
#if NETSTANDARD1_5
MethodInfo mi = typeof(DC).GetTypeInfo().GetMethod("ToBytes", new Type[] { i.GetType() });
#else
MethodInfo mi = typeof(DC).GetMethod("ToBytes", new Type[] { i.GetType() });
#endif
if (mi != null)
{
var b = (byte[])mi.Invoke(null, new object[] {i});
held.AddRange(b);
}
}
}
}
/// <summary>
/// Add multiple items at the end of the list
/// </summary>
/// <param name="values">Parameters of items</param>
public void Append(params object[] values)
{
AddRange(values);
}
/// <summary>
/// Insert new items to the list at a specified index
/// </summary>
/// <param name="offset">Position in the list</param>
/// <param name="values">Items to insert</param>
public void Insert(int offset, params object[] values)
{
foreach (var i in values)
{
if (i is byte)
{
held.Insert(offset++, (byte)i);
}
else
{
#if NETSTANDARD1_5
MethodInfo mi = typeof(DC).GetTypeInfo().GetMethod("ToBytes", new Type[] { i.GetType() });
#else
MethodInfo mi = typeof(DC).GetMethod("ToBytes", new Type[] { i.GetType() });
#endif
if (mi != null)
{
var b = (byte[])mi.Invoke(null, new object[] { i });
held.InsertRange(offset, b);
offset += b.Length;
}
}
}
}
/// <summary>
/// Number of the items in the list
/// </summary>
public int Length
{
get
{
return held.Count;
}
}
/*
public void Append(byte data)
{
held.Add(data);
}
public void Append(byte[] data)
{
held.AddRange(data);
}
public void Append(int data)
{
held.AddRange(DC.ToBytes(data));
}
public void Append(uint data)
{
held.AddRange(DC.ToBytes(data));
}
public void Append(float data)
{
held.AddRange(DC.ToBytes(data));
}
public void Append(short data)
{
held.AddRange(DC.ToBytes(data));
}
public void Append(ushort data)
{
held.AddRange(DC.ToBytes(data));
}
public void Append(double data)
{
held.AddRange(DC.ToBytes(data));
}
public void Append(sbyte data)
{
held.Add((byte)data);
}
*/
/// <summary>
/// Convert the list to an array of bytes
/// </summary>
/// <returns>Bytes array</returns>
public byte[] ToArray()
{
return held.ToArray();
}
}
}

991
Esiur/Data/Codec.cs Normal file
View File

@ -0,0 +1,991 @@
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
{
/// <summary>
/// Check if a DataType is an array
/// </summary>
/// <param name="type">DataType to check</param>
/// <returns>True if DataType is an array, otherwise false</returns>
public static bool IsArray(this DataType type)
{
return (((byte)type & 0x80) == 0x80) && (type != DataType.NotModified);
}
/// <summary>
/// Get the element DataType
/// </summary>
/// <example>
/// Passing UInt8Array will return UInt8
/// </example>
/// <param name="type">DataType to get its element DataType</param>
public static DataType GetElementType(this DataType type)
{
return (DataType)((byte)type & 0x7F);
}
/// <summary>
/// Get DataType array of a given Structure
/// </summary>
/// <param name="structure">Structure to get its DataTypes</param>
/// <param name="connection">Distributed connection is required in case a type is at the other end</param>
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;
}
/// <summary>
/// Compare two structures
/// </summary>
/// <param name="initial">Initial structure to compare with</param>
/// <param name="next">Next structure to compare with the initial</param>
/// <param name="connection">DistributedConnection is required in case a structure holds items at the other end</param>
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;
}
/// <summary>
/// Compose an array of structures into an array of bytes
/// </summary>
/// <param name="structures">Array of Structure to compose</param>
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end</param>
/// <param name="prependLength">If true, prepend the length as UInt32 at the beginning of the returned bytes array</param>
/// <returns>Array of bytes in the network byte order</returns>
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();
}
/// <summary>
/// Parse an array of structures
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset</param>
/// <param name="length">Number of bytes to parse</param>
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end</param>
/// <returns>Array of structures</returns>
public static AsyncBag<Structure> ParseStructureArray(byte[] data, uint offset, uint length, DistributedConnection connection)
{
var reply = new AsyncBag<Structure>();
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<Structure>(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<Structure>(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;
}
/// <summary>
/// Compose a structure into an array of bytes
/// </summary>
/// <param name="value">Structure to compose</param>
/// <param name="connection">DistributedConnection is required in case an item in the structure is at the other end</param>
/// <param name="includeKeys">Whether to include the structure keys</param>
/// <param name="includeTypes">Whether to include each item DataType</param>
/// <param name="prependLength">If true, prepend the length as UInt32 at the beginning of the returned bytes array</param>
/// <returns>Array of bytes in the network byte order</returns>
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();
}
/// <summary>
/// Parse a structure
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="length">Number of bytes to parse.</param>
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end.</param>
/// <returns>Value</returns>
public static AsyncReply<Structure> ParseStructure(byte[] data, uint offset, uint contentLength, DistributedConnection connection)
{
string[] pk;
DataType[] pt;
return ParseStructure(data, offset, contentLength, connection, out pk, out pt);
}
/// <summary>
/// Parse a structure
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="length">Number of bytes to parse.</param>
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end.</param>
/// <param name="parsedKeys">Array to store keys in.</param>
/// <param name="parsedTypes">Array to store DataTypes in.</param>
/// <param name="keys">Array of keys, in case the data doesn't include keys</param>
/// <param name="types">Array of DataTypes, in case the data doesn't include DataTypes</param>
/// <returns>Structure</returns>
public static AsyncReply<Structure> 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<Structure>();
var bag = new AsyncBag<object>();
var keylist = new List<string>();
var typelist = new List<DataType>();
if (keys == null)
{
while (length > 0)
{
var len = data[offset++];
keylist.Add(data.GetString(offset, len));
offset += len;
typelist.Add((DataType)data[offset]);
uint rt;
bag.Add(Codec.Parse(data, offset, out rt, connection));
length -= rt + len + 1;
offset += rt;
}
}
else if (types == null)
{
keylist.AddRange(keys);
while (length > 0)
{
typelist.Add((DataType)data[offset]);
uint rt;
bag.Add(Codec.Parse(data, offset, out rt, connection));
length -= rt + 1;
offset += rt + 1;
}
}
else
{
keylist.AddRange(keys);
typelist.AddRange(types);
var i = 0;
while (length > 0)
{
uint rt;
bag.Add(Codec.Parse(data, offset, out rt, connection, types[i]));
length -= rt;
offset += rt;
i++;
}
}
bag.Seal();
bag.Then((res) =>
{
// compose the list
var s = new Structure();
for (var i = 0; i < keylist.Count; i++)
s[keylist[i]] = res[i];
reply.Trigger(s);
});
parsedKeys = keylist.ToArray();
parsedTypes = typelist.ToArray();
return reply;
}
/// <summary>
/// Parse a value
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end.</param>
/// <param name="dataType">DataType, in case the data is not prepended with DataType</param>
/// <returns>Structure</returns>
public static AsyncReply Parse(byte[] data, uint offset, DistributedConnection connection, DataType dataType = DataType.Unspecified)
{
uint size;
return Parse(data, offset, out size, connection);
}
/// <summary>
/// Parse a value
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="size">Output the number of bytes parsed</param>
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end.</param>
/// <param name="dataType">DataType, in case the data is not prepended with DataType</param>
/// <returns>Value</returns>
public static AsyncReply Parse(byte[] data, uint offset, out uint size, DistributedConnection connection, DataType dataType = DataType.Unspecified)
{
var reply = new AsyncReply();
bool isArray;
DataType t;
if (dataType == DataType.Unspecified)
{
size = 1;
dataType = (DataType)data[offset++];
}
else
size = 0;
t = (DataType)((byte)dataType & 0x7F);
isArray = ((byte)dataType & 0x80) == 0x80;
var payloadSize = dataType.Size();// SizeOf();
uint contentLength = 0;
// check if we have the enough data
if (payloadSize == -1)
{
contentLength = data.GetUInt32(offset);
offset += 4;
size += 4 + contentLength;
}
else
size += (uint)payloadSize;
if (isArray)
{
switch (t)
{
// VarArray ?
case DataType.Void:
return ParseVarArray(data, offset, contentLength, connection);
case DataType.Bool:
return new AsyncReply<bool[]>(data.GetBooleanArray(offset, contentLength));
case DataType.UInt8:
return new AsyncReply<byte[]>(data.GetUInt8Array(offset, contentLength));
case DataType.Int8:
return new AsyncReply<sbyte[]>(data.GetInt8Array(offset, contentLength));
case DataType.Char:
return new AsyncReply<char[]>(data.GetCharArray(offset, contentLength));
case DataType.Int16:
return new AsyncReply<short[]>(data.GetInt16Array( offset, contentLength));
case DataType.UInt16:
return new AsyncReply<ushort[]>(data.GetUInt16Array(offset, contentLength));
case DataType.Int32:
return new AsyncReply<int[]>(data.GetInt32Array(offset, contentLength));
case DataType.UInt32:
return new AsyncReply<uint[]>(data.GetUInt32Array(offset, contentLength));
case DataType.Int64:
return new AsyncReply<long[]>(data.GetInt64Array(offset, contentLength));
case DataType.UInt64:
return new AsyncReply<ulong[]>(data.GetUInt64Array(offset, contentLength));
case DataType.Float32:
return new AsyncReply<float[]>(data.GetFloat32Array(offset, contentLength));
case DataType.Float64:
return new AsyncReply<double[]>(data.GetFloat64Array(offset, contentLength));
case DataType.String:
return new AsyncReply<string[]>(data.GetStringArray(offset, contentLength));
case DataType.Resource:
case DataType.DistributedResource:
return ParseResourceArray(data, offset, contentLength, connection);
case DataType.DateTime:
return new AsyncReply<DateTime[]>(data.GetDateTimeArray(offset, contentLength));
case DataType.Structure:
return ParseStructureArray(data, offset, contentLength, connection);
}
}
else
{
switch (t)
{
case DataType.NotModified:
return new AsyncReply<object>(new NotModified());
case DataType.Void:
return new AsyncReply<object>(null);
case DataType.Bool:
return new AsyncReply<bool>(data.GetBoolean(offset));
case DataType.UInt8:
return new AsyncReply<byte>(data[offset]);
case DataType.Int8:
return new AsyncReply<sbyte>((sbyte)data[offset]);
case DataType.Char:
return new AsyncReply<char>(data.GetChar(offset));
case DataType.Int16:
return new AsyncReply<short>(data.GetInt16(offset));
case DataType.UInt16:
return new AsyncReply<ushort>(data.GetUInt16(offset));
case DataType.Int32:
return new AsyncReply<int>(data.GetInt32(offset));
case DataType.UInt32:
return new AsyncReply<uint>(data.GetUInt32(offset));
case DataType.Int64:
return new AsyncReply<long>(data.GetInt64(offset));
case DataType.UInt64:
return new AsyncReply<ulong>(data.GetUInt64(offset));
case DataType.Float32:
return new AsyncReply<float>(data.GetFloat32(offset));
case DataType.Float64:
return new AsyncReply<double>(data.GetFloat64(offset));
case DataType.String:
return new AsyncReply<string>(data.GetString(offset, contentLength));
case DataType.Resource:
return ParseResource(data, offset);
case DataType.DistributedResource:
return ParseDistributedResource(data, offset, connection);
case DataType.DateTime:
return new AsyncReply<DateTime>(data.GetDateTime(offset));
case DataType.Structure:
return ParseStructure(data, offset, contentLength, connection);
}
}
return null;
}
/// <summary>
/// Parse a resource
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <returns>Resource</returns>
public static AsyncReply<IResource> ParseResource(byte[] data, uint offset)
{
return Warehouse.Get(data.GetUInt32(offset));
}
/// <summary>
/// Parse a DistributedResource
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="connection">DistributedConnection is required.</param>
/// <returns>DistributedResource</returns>
public static AsyncReply<DistributedResource> ParseDistributedResource(byte[] data, uint offset, DistributedConnection connection)
{
//var g = data.GetGuid(offset);
//offset += 16;
// find the object
var iid = data.GetUInt32(offset);
return connection.Fetch(iid);// Warehouse.Get(iid);
}
public enum ResourceComparisonResult
{
Null,
Distributed,
DistributedSameClass,
Local,
Same
}
public enum StructureComparisonResult : byte
{
Null,
Structure,
StructureSameKeys,
StructureSameTypes,
Same
}
/// <summary>
/// Check if a resource is local to a given connection.
/// </summary>
/// <param name="resource">Resource to check.</param>
/// <param name="connection">DistributedConnection to check if the resource is local to it.</param>
/// <returns>True, if the resource owner is the given connection, otherwise False.</returns>
static bool IsLocalResource(IResource resource, DistributedConnection connection)
{
if (resource is DistributedResource)
if ((resource as DistributedResource).Connection == connection)
return true;
return false;
}
/// <summary>
/// Compare two resources
/// </summary>
/// <param name="initial">Initial resource to make comparison with.</param>
/// <param name="next">Next resource to compare with the initial.</param>
/// <param name="connection">DistributedConnection is required to check locality.</param>
/// <returns>Null, same, local, distributed or same class distributed.</returns>
public static ResourceComparisonResult Compare(IResource initial, IResource next, DistributedConnection connection)
{
if (next == null)
return ResourceComparisonResult.Null;
if (next == initial)
return ResourceComparisonResult.Same;
if (IsLocalResource(next, connection))
return ResourceComparisonResult.Local;
if (initial == null)
return ResourceComparisonResult.Distributed;
if (initial.Instance.Template.ClassId == next.Instance.Template.ClassId)
return ResourceComparisonResult.DistributedSameClass;
return ResourceComparisonResult.Distributed;
}
/// <summary>
/// Compose a resource
/// </summary>
/// <param name="resource">Resource to compose.</param>
/// <param name="connection">DistributedConnection is required to check locality.</param>
/// <returns>Array of bytes in the network byte order.</returns>
public static byte[] ComposeResource(IResource resource, DistributedConnection connection)
{
if (IsLocalResource(resource, connection))
return DC.ToBytes((resource as DistributedResource).Id);
else
{
return BinaryList.ToBytes(resource.Instance.Template.ClassId, resource.Instance.Id);
}
}
/// <summary>
/// Compose an array of resources
/// </summary>
/// <param name="resources">Array of resources.</param>
/// <param name="connection">DistributedConnection is required to check locality.</param>
/// <param name="prependLength">If True, prepend the length of the output at the beginning.</param>
/// <returns>Array of bytes in the network byte order.</returns>
public static byte[] ComposeResourceArray(IResource[] resources, DistributedConnection connection, bool prependLength = false)
{
if (resources == null || resources?.Length == 0)
return new byte[0];
var rt = new BinaryList();
var comparsion = Compare(null, resources[0], connection);
rt.Append((byte)comparsion);
if (comparsion == ResourceComparisonResult.Local)
rt.Append((resources[0] as DistributedResource).Id);
else if (comparsion == ResourceComparisonResult.Distributed)
{
rt.Append(resources[0].Instance.Template.ClassId);
rt.Append(resources[0].Instance.Id);
}
for (var i = 1; i < resources.Length; i++)
{
comparsion = Compare(resources[i - 1], resources[i], connection);
rt.Append((byte)comparsion);
if (comparsion == ResourceComparisonResult.Local)
rt.Append((resources[0] as DistributedResource).Id);
else if (comparsion == ResourceComparisonResult.Distributed)
{
rt.Append(resources[0].Instance.Template.ClassId);
rt.Append(resources[0].Instance.Id);
}
else if (comparsion == ResourceComparisonResult.DistributedSameClass)
{
rt.Append(resources[0].Instance.Id);
}
}
if (prependLength)
rt.Insert(0, rt.Length);
return rt.ToArray();
}
/// <summary>
/// Parse an array of bytes into array of resources
/// </summary>
/// <param name="data">Array of bytes.</param>
/// <param name="length">Number of bytes to parse.</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="connection">DistributedConnection is required to fetch resources.</param>
/// <returns>Array of resources.</returns>
public static AsyncBag<IResource> ParseResourceArray(byte[] data, uint offset, uint length, DistributedConnection connection)
{
var reply = new AsyncBag<IResource>();
if (length == 0)
{
reply.Seal();
return reply;
}
var end = offset + length;
//
var result = (ResourceComparisonResult)data[offset++];
AsyncReply previous = null;
Guid previousGuid = Guid.Empty;
if (result == ResourceComparisonResult.Null)
previous = new AsyncReply<IResource>(null);
else if (result == ResourceComparisonResult.Local)
{
previous = Warehouse.Get(data.GetUInt32(offset));
offset += 4;
}
else if (result == ResourceComparisonResult.Distributed)
{
previousGuid = data.GetGuid(offset);
offset += 16;
//previous = connection.Fetch(previousGuid, data.GetUInt32(offset));
offset += 4;
}
reply.Add(previous);
while (offset < end)
{
result = (ResourceComparisonResult)data[offset++];
if (result == ResourceComparisonResult.Null)
previous = new AsyncReply<IResource>(null);
//else if (result == ResourceComparisonResult.Same)
// reply.Add(previous);
else if (result == ResourceComparisonResult.Local)
{
// overwrite previous
previous = Warehouse.Get(data.GetUInt32(offset));
offset += 4;
}
else if (result == ResourceComparisonResult.Distributed)
{
// overwrite previous
previousGuid = data.GetGuid(offset);
offset += 16;
//previous = connection.Fetch(previousGuid, data.GetUInt32(offset));
offset += 4;
}
else if (result == ResourceComparisonResult.DistributedSameClass)
{
// overwrite previous
//previous = connection.Fetch(previousGuid, data.GetUInt32(offset));
offset += 4;
}
reply.Add(previous);
}
reply.Seal();
return reply;
}
/// <summary>
/// Compose an array of variables
/// </summary>
/// <param name="array">Variables.</param>
/// <param name="connection">DistributedConnection is required to check locality.</param>
/// <param name="prependLength">If True, prepend the length as UInt32 at the beginning of the output.</param>
/// <returns>Array of bytes in the network byte order.</returns>
public static byte[] ComposeVarArray(object[] array, DistributedConnection connection, bool prependLength = false)
{
var rt = new List<byte>();
for (var i = 0; i < array.Length; i++)
rt.AddRange(Compose(array[i], connection));
if (prependLength)
rt.InsertRange(0, DC.ToBytes(rt.Count));
return rt.ToArray();
}
public static AsyncBag<object> ParseVarArray(byte[] data, DistributedConnection connection)
{
return ParseVarArray(data, 0, (uint)data.Length, connection);
}
/// <summary>
/// Parse an array of bytes into an array of varialbes.
/// </summary>
/// <param name="data">Array of bytes.</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="length">Number of bytes to parse.</param>
/// <param name="connection">DistributedConnection is required to fetch resources.</param>
/// <returns>Array of variables.</returns>
public static AsyncBag<object> ParseVarArray(byte[] data, uint offset, uint length, DistributedConnection connection)
{
var rt = new AsyncBag<object>();
while (length > 0)
{
uint cs;
rt.Add(Parse(data, offset, out cs, connection));
if (cs > 0)
{
offset += (uint)cs;
length -= (uint)cs;
}
else
throw new Exception("Error while parsing structured data");
}
rt.Seal();
return rt;
}
/// <summary>
/// Compose a variable
/// </summary>
/// <param name="value">Value to compose.</param>
/// <param name="connection">DistributedConnection is required to check locality.</param>
/// <param name="prependType">If True, prepend the DataType at the beginning of the output.</param>
/// <returns>Array of bytes in the network byte order.</returns>
public static byte[] Compose(object value, DistributedConnection connection, bool prependType = true)
{
var type = GetDataType(value, connection);
var rt = new BinaryList();
switch (type)
{
case DataType.Void:
// nothing to do;
break;
case DataType.String:
var st = DC.ToBytes((string)value);
rt.Append(st.Length, st);
break;
case DataType.Resource:
rt.Append((value as DistributedResource).Id);
break;
case DataType.DistributedResource:
//rt.Append((value as IResource).Instance.Template.ClassId, (value as IResource).Instance.Id);
rt.Append((value as IResource).Instance.Id);
break;
case DataType.Structure:
rt.Append(ComposeStructure((Structure)value, connection, true, true, true));
break;
case DataType.VarArray:
rt.Append(ComposeVarArray((object[])value, connection, true));
break;
case DataType.ResourceArray:
if (value is IResource[])
rt.Append(ComposeResourceArray((IResource[])value, connection, true));
else
rt.Append(ComposeResourceArray((IResource[])DC.CastConvert(value, typeof(IResource[])), connection, true));
break;
case DataType.StructureArray:
rt.Append(ComposeStructureArray((Structure[])value, connection, true));
break;
default:
rt.Append(value);
if (type.IsArray())
rt.Insert(0, rt.Length);
break;
}
if (prependType)
rt.Insert(0, (byte)type);
return rt.ToArray();
}
/// <summary>
/// Check if a type implements an interface
/// </summary>
/// <param name="type">Sub-class type.</param>
/// <param name="iface">Super-interface type.</param>
/// <returns>True, if <paramref name="type"/> implements <paramref name="iface"/>.</returns>
private static bool ImplementsInterface(Type type, Type iface)
{
while (type != null)
{
#if NETSTANDARD1_5
if (type.GetTypeInfo().GetInterfaces().Contains(iface))
return true;
type = type.GetTypeInfo().BaseType;
#else
if (type.GetInterfaces().Contains(iface))
return true;
type = type.BaseType;
#endif
}
return false;
}
/// <summary>
/// Check if a type inherits another type.
/// </summary>
/// <param name="childType">Child type.</param>
/// <param name="parentType">Parent type.</param>
/// <returns>True, if <paramref name="childType"/> inherits <paramref name="parentType"/>.</returns>
private static bool HasParentType(Type childType, Type parentType)
{
while (childType != null)
{
if (childType == parentType)
return true;
#if NETSTANDARD1_5
childType = childType.GetTypeInfo().BaseType;
#else
childType = childType.BaseType;
#endif
}
return false;
}
/// <summary>
/// Get the DataType of a given value.
/// This function is needed to compose a value.
/// </summary>
/// <param name="value">Value to find its DataType.</param>
/// <param name="connection">DistributedConnection is required to check locality of resources.</param>
/// <returns>DataType.</returns>
public static DataType GetDataType(object value, DistributedConnection connection)
{
if (value == null)
return DataType.Void;
var t = value.GetType();
var isArray = t.IsArray;
if (isArray)
t = t.GetElementType();
DataType type;
if (t == typeof(bool))
type = DataType.Bool;
else if (t == typeof(char))
type = DataType.Char;
else if (t == typeof(byte))
type = DataType.UInt8;
else if (t == typeof(sbyte))
type = DataType.Int8;
else if (t == typeof(short))
type = DataType.Int16;
else if (t == typeof(ushort))
type = DataType.UInt16;
else if (t == typeof(int))
type = DataType.Int32;
else if (t == typeof(uint))
type = DataType.UInt32;
else if (t == typeof(long))
type = DataType.Int64;
else if (t == typeof(ulong))
type = DataType.UInt64;
else if (t == typeof(float))
type = DataType.Float32;
else if (t == typeof(double))
type = DataType.Float64;
else if (t == typeof(decimal))
type = DataType.Decimal;
else if (t == typeof(string))
type = DataType.String;
else if (t == typeof(DateTime))
type = DataType.DateTime;
else if (t == typeof(Structure))
type = DataType.Structure;
//else if (t == typeof(DistributedResource))
// type = DataType.DistributedResource;
else if (ImplementsInterface(t, typeof(IResource)))
{
if (isArray)
return DataType.ResourceArray;
else
{
return IsLocalResource((IResource)value, connection) ? DataType.Resource : DataType.DistributedResource;
}
}
else
return DataType.Void;
if (isArray)
return (DataType)((byte)type | 0x80);
else
return type;
}
}
}

1069
Esiur/Data/DataConverter.cs Normal file

File diff suppressed because it is too large Load Diff

95
Esiur/Data/DataType.cs Normal file
View File

@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Data
{
public enum DataType : byte
{
Void = 0x0,
//Variant,
Bool,
Int8,
UInt8,
Char,
Int16,
UInt16,
Int32,
UInt32,
Int64,
UInt64,
Float32,
Float64,
Decimal,
DateTime,
Resource,
DistributedResource,
ResourceLink,
String,
Structure,
//Stream,
//Array = 0x80,
VarArray = 0x80,
BoolArray,
UInt8Array,
Int8Array,
CharArray,
Int16Array,
UInt16Array,
Int32Array,
UInt32Array,
Int64Array,
UInt64Array,
Float32Array,
Float64Array,
DecimalArray,
DateTimeArray,
ResourceArray,
DistributedResourceArray,
ResourceLinkArray,
StringArray,
StructureArray,
NotModified = 0x7f,
Unspecified = 0xff,
}
public static class DataTypeExpansions
{
public static int Size(this DataType t)
{
switch (t)
{
case DataType.Void:
case DataType.NotModified:
return 0;
case DataType.Bool:
case DataType.UInt8:
case DataType.Int8:
return 1;
case DataType.Char:
case DataType.UInt16:
case DataType.Int16:
return 2;
case DataType.Int32:
case DataType.UInt32:
case DataType.Float32:
case DataType.Resource:
return 4;
case DataType.Int64:
case DataType.UInt64:
case DataType.Float64:
case DataType.DateTime:
return 8;
case DataType.DistributedResource:
return 4;
default:
return -1;
}
}
}
}

204
Esiur/Data/KeyList.cs Normal file
View File

@ -0,0 +1,204 @@
using System;
using System.IO;
using System.Collections;
using System.Security.Cryptography;
using System.Text;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Esiur.Engine;
namespace Esiur.Data
{
public class KeyList<KT, T> : IEnumerable
{
private readonly object syncRoot = new object();
private Dictionary<KT, T> dic;
public delegate void Modified(KT key, T oldValue, T newValue);
public delegate void Added(T value);
public delegate void Removed(KT key, T value);
public delegate void Cleared();
public event Modified OnModified;
public event Removed OnRemoved;
public event Cleared OnCleared;
public event Added OnAdd;
bool removableList;
public object SyncRoot
{
get
{
return syncRoot;
}
}
public T Take(KT key)
{
if (dic.ContainsKey(key))
{
var v = dic[key];
Remove(key);
return v;
}
else
return default(T);
}
public void Sort(Func<KeyValuePair<KT, T>, object> keySelector)
{
dic = dic.OrderBy(keySelector).ToDictionary(x => x.Key, x => x.Value);
}
public T[] ToArray()
{
var a = new T[Count];
dic.Values.CopyTo(a, 0);
return a;
}
public void Add(KT key, T value)
{
lock (syncRoot)
{
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy += ItemDestroyed;
if (dic.ContainsKey(key))
{
var oldValue = dic[key];
if (removableList)
if (oldValue != null)
((IDestructible)oldValue).OnDestroy -= ItemDestroyed;
dic[key] = value;
if (OnModified != null)
OnModified(key, oldValue, value);
}
else
{
dic.Add(key, value);
if (OnAdd != null)
OnAdd(value);
}
}
}
private void ItemDestroyed(object sender)
{
RemoveValue((T)sender);
}
public void RemoveValue(T value)
{
var toRemove = new List<KT>();
foreach (var kv in dic)
if (kv.Value.Equals(value))
toRemove.Add(kv.Key);
foreach (var k in toRemove)
Remove(k);
}
public T this[KT key]
{
get
{
if (dic.ContainsKey(key))
return dic[key];
else
return default(T);
}
set
{
Add(key, value);
}
}
public IEnumerator GetEnumerator()
{
return dic.GetEnumerator();
}
public void Clear()
{
if (removableList)
foreach (IDestructible v in dic.Values)
if (v != null)
v.OnDestroy -= ItemDestroyed;
lock (syncRoot)
dic.Clear();
if (OnCleared != null)
OnCleared();
}
public Dictionary<KT, T>.KeyCollection Keys
{
get { return dic.Keys; }
}
public Dictionary<KT, T>.ValueCollection Values
{
get
{
return dic.Values;
}
}
public void Remove(KT key)
{
if (!dic.ContainsKey(key))
return;
var value = dic[key];
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy -= ItemDestroyed;
lock (syncRoot)
dic.Remove(key);
if (OnRemoved != null)
OnRemoved(key, value);
}
public int Count
{
get { return dic.Count; }
}
public bool Contains(KT Key)
{
return dic.ContainsKey(Key);
}
public bool ContainsKey(KT Key)
{
return dic.ContainsKey(Key);
}
public bool ContainsValue(T Value)
{
return dic.ContainsValue(Value);
}
public KeyList()
{
#if NETSTANDARD1_5
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
#else
removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
#endif
if (typeof(KT) == typeof(string))
dic = (Dictionary<KT, T>)(object)new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
else
dic = new Dictionary<KT, T>();
}
}
}

13
Esiur/Data/NotModified.cs Normal file
View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Data
{
public class NotModified
{
}
}

194
Esiur/Data/StringKeyList.cs Normal file
View File

@ -0,0 +1,194 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Reflection;
namespace Esiur.Data
{
public class StringKeyList : IEnumerable<KeyValuePair<string, string>>
{
//private List<string> m_keys = new List<string>();
//private List<string> m_values = new List<string>();
private List<KeyValuePair<string, string>> m_Variables = new List<KeyValuePair<string, string>>();
private bool allowMultiple;
public delegate void Modified(string Key, string NewValue);
public event Modified OnModified;
public StringKeyList(bool AllowMultipleValues = false)
{
allowMultiple = AllowMultipleValues;
}
public void Add(string Key, string Value)
{
if (OnModified != null)
OnModified(Key, Value);
var key = Key.ToLower();
if (!allowMultiple)
{
foreach(var kv in m_Variables)
{
if (kv.Key.ToLower() == key)
{
m_Variables.Remove(kv);
break;
}
}
}
m_Variables.Add(new KeyValuePair<string, string>(Key, Value));
}
public string this[string Key]
{
get
{
var key = Key.ToLower();
foreach (var kv in m_Variables)
if (kv.Key.ToLower() == key)
return kv.Value;
return null;
}
set
{
var key = Key.ToLower();
if (OnModified != null)
OnModified(Key, value);
foreach (var kv in m_Variables)
if (kv.Key.ToLower() == key)
m_Variables.Remove(kv);
m_Variables.Add(new KeyValuePair<string, string>(Key, value));
}
}
IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
{
//return m_keys.GetEnumerator();
return m_Variables.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return m_Variables.GetEnumerator();
}
public void Clear()
{
if (OnModified != null)
OnModified(null, null);
m_Variables.Clear();
}
/*
public string[] Keys
{
get
{
return m_keys.ToArray();
}
}
//public Dictionary<string, string>.ValueCollection Values
public string[] Values
{
get
{
//return m_Variables.Values;
return m_values.ToArray();
}
}
*/
public List<string> GetValues(string Key)
{
var key = Key.ToLower();
List<string> values = new List<string>();
foreach (var kv in m_Variables)
if (kv.Key.ToLower() == key)
values.Add(kv.Value);
return values;
}
public void RemoveAll(string Key)
{
while (Remove(Key)){}
}
public bool Remove(string Key)
{
var key = Key.ToLower();
foreach(var kv in m_Variables)
{
if (kv.Key.ToLower() == key)
{
if (OnModified != null)
OnModified(Key, null);
m_Variables.Remove(kv);
return true;
}
}
return false;
}
public int Count
{
get { return m_Variables.Count; }
}
public bool ContainsKey(string Key)
{
var key = Key.ToLower();
foreach (var kv in m_Variables)
if (kv.Key.ToLower() == key)
return true;
return false;
}
/*
public bool ContainsKey(string Key)
{
//return m_Variables.ContainsKey(Key);
return m_keys.Contains(Key.ToLower());
}
*/
public bool ContainsValue(string Value)
{
var value = Value.ToLower();
foreach (var kv in m_Variables)
if (kv.Value.ToLower() == value)
return true;
return false;
}
//internal KeyList()
//{
// m_Session = Session;
// m_Server = Server;
//}
}
}

83
Esiur/Data/Structure.cs Normal file
View File

@ -0,0 +1,83 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Esiur.Data;
using Esiur.Misc;
using Esiur.Engine;
namespace Esiur.Data
{
public class Structure : IEnumerable<KeyValuePair<string, object>>
{
private Dictionary<string, object> dic = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
private object syncRoot = new object();
public bool ContainsKey(string key)
{
return dic.ContainsKey(key);
}
public override string ToString()
{
var rt = "";
foreach (var kv in dic)
rt += kv.Key + ": " + kv.Value.ToString() + "\r\n";
return rt.TrimEnd('\r', '\n');
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return dic.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return dic.GetEnumerator();
}
public int Length
{
get { return dic.Count; }
}
public KeyValuePair<string, object> At(int index)
{
return dic.ElementAt(index);
}
public object SyncRoot
{
get { return syncRoot; }
}
public string[] GetKeys()
{
return dic.Keys.ToArray();
}
public object this[string index]
{
get
{
if (dic.ContainsKey(index))
return dic[index];
else
return null;
}
set
{
if (dic.ContainsKey(index))
dic[index] = value;
else
dic.Add(index, value);
}
}
}
}