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:
253
Esiur/Data/AutoList.cs
Normal file
253
Esiur/Data/AutoList.cs
Normal 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
196
Esiur/Data/BinaryList.cs
Normal 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
991
Esiur/Data/Codec.cs
Normal 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
1069
Esiur/Data/DataConverter.cs
Normal file
File diff suppressed because it is too large
Load Diff
95
Esiur/Data/DataType.cs
Normal file
95
Esiur/Data/DataType.cs
Normal 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
204
Esiur/Data/KeyList.cs
Normal 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
13
Esiur/Data/NotModified.cs
Normal 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
194
Esiur/Data/StringKeyList.cs
Normal 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
83
Esiur/Data/Structure.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user