mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2026-04-04 12:28:21 +00:00
Layout
This commit is contained in:
313
Libraries/Esiur/Data/AutoList.cs
Normal file
313
Libraries/Esiur/Data/AutoList.cs
Normal file
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using Esiur.Core;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
public class AutoList<T, ST> : IEnumerable<T>, ICollection, ICollection<T>
|
||||
{
|
||||
|
||||
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;
|
||||
|
||||
bool removableList;
|
||||
|
||||
|
||||
public ST State { get; set; }
|
||||
/*
|
||||
IOrderedEnumerable<T> OrderBy<T, TK>(Func<T, TK> keySelector)
|
||||
{
|
||||
return list.OrderBy<T,TK>(keySelector);
|
||||
}
|
||||
*/
|
||||
|
||||
public void Sort()
|
||||
{
|
||||
lock (syncRoot)
|
||||
list.Sort();
|
||||
}
|
||||
|
||||
public void Sort(IComparer<T> comparer)
|
||||
{
|
||||
lock (syncRoot)
|
||||
list.Sort(comparer);
|
||||
}
|
||||
|
||||
public void Sort(Comparison<T> comparison)
|
||||
{
|
||||
lock (syncRoot)
|
||||
list.Sort(comparison);
|
||||
}
|
||||
|
||||
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 = default(ST))
|
||||
{
|
||||
State = state;
|
||||
#if NETSTANDARD
|
||||
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(ST state, T[] values)
|
||||
{
|
||||
State = state;
|
||||
#if NETSTANDARD
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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 bool Remove(T value)
|
||||
{
|
||||
if (!list.Contains(value))
|
||||
return false;
|
||||
|
||||
if (removableList)
|
||||
if (value != null)
|
||||
((IDestructible)value).OnDestroy -= ItemDestroyed;
|
||||
|
||||
lock (syncRoot)
|
||||
list.Remove(value);
|
||||
|
||||
OnRemoved?.Invoke(State, value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of items in the list
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get { return list.Count; }
|
||||
}
|
||||
|
||||
public bool IsSynchronized => (list as ICollection).IsSynchronized;
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<T>)list).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<T>)list).GetEnumerator();
|
||||
}
|
||||
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
lock (syncRoot)
|
||||
(list as ICollection).CopyTo(array, index);
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
lock (syncRoot)
|
||||
list.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
//bool ICollection<T>.Remove(T item)
|
||||
//{
|
||||
// lock(syncRoot)
|
||||
// return Remove(item);
|
||||
//}
|
||||
}
|
||||
290
Libraries/Esiur/Data/BinaryList.cs
Normal file
290
Libraries/Esiur/Data/BinaryList.cs
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Misc;
|
||||
using System.Reflection;
|
||||
using Esiur.Core;
|
||||
|
||||
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> list = new List<byte>();
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of BinaryList
|
||||
/// </summary>
|
||||
public BinaryList()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Endian Endian { get; set; } = Endian.Little;
|
||||
|
||||
public int Length => list.Count;
|
||||
|
||||
public BinaryList AddDateTime(DateTime value)
|
||||
{
|
||||
list.AddRange(DC.ToBytes(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList InsertDateTime(int position, DateTime value)
|
||||
{
|
||||
list.InsertRange(position, DC.ToBytes(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public BinaryList AddUUID(Uuid value)
|
||||
{
|
||||
list.AddRange(value.Data);
|
||||
return this;
|
||||
|
||||
}
|
||||
//public BinaryList AddGuid(Guid value)
|
||||
//{
|
||||
// list.AddRange(DC.ToBytes(value));
|
||||
// return this;
|
||||
//}
|
||||
|
||||
public BinaryList InsertGuid(int position, Guid value)
|
||||
{
|
||||
list.InsertRange(position, DC.ToBytes(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public BinaryList AddUInt8Array(byte[] value)
|
||||
{
|
||||
list.AddRange(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public BinaryList InsertUInt8Array(int position, byte[] value)
|
||||
{
|
||||
list.InsertRange(position, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public BinaryList AddHex(string value)
|
||||
{
|
||||
return this.AddUInt8Array(DC.FromHex(value, null));
|
||||
}
|
||||
|
||||
public BinaryList InsertHex(int position, string value)
|
||||
{
|
||||
return this.InsertUInt8Array(position, DC.FromHex(value, null));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public BinaryList AddString(string value)
|
||||
{
|
||||
list.AddRange(DC.ToBytes(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList InsertString(int position, string value)
|
||||
{
|
||||
list.InsertRange(position, DC.ToBytes(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public BinaryList InsertUInt8(int position, byte value)
|
||||
{
|
||||
list.Insert(position, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList AddUInt8(byte value)
|
||||
{
|
||||
list.Add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList AddInt8(sbyte value)
|
||||
{
|
||||
list.Add((byte)value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList InsertInt8(int position, sbyte value)
|
||||
{
|
||||
list.Insert(position, (byte)value);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public BinaryList AddChar(char value)
|
||||
{
|
||||
list.AddRange(DC.ToBytes(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList InsertChar(int position, char value)
|
||||
{
|
||||
list.InsertRange(position, DC.ToBytes(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList AddBoolean(bool value)
|
||||
{
|
||||
list.AddRange(DC.ToBytes(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList InsertBoolean(int position, bool value)
|
||||
{
|
||||
list.InsertRange(position, DC.ToBytes(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList AddUInt16(ushort value)
|
||||
{
|
||||
list.AddRange(DC.ToBytes(value, Endian));
|
||||
return this;
|
||||
}
|
||||
public BinaryList InsertUInt16(int position, ushort value)
|
||||
{
|
||||
list.InsertRange(position, DC.ToBytes(value, Endian));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public BinaryList AddInt16(short value)
|
||||
{
|
||||
list.AddRange(DC.ToBytes(value, Endian));
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList InsertInt16(int position, short value)
|
||||
{
|
||||
list.InsertRange(position, DC.ToBytes(value, Endian));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public BinaryList AddUInt32(uint value)
|
||||
{
|
||||
list.AddRange(DC.ToBytes(value, Endian));
|
||||
return this;
|
||||
}
|
||||
public BinaryList InsertUInt32(int position, uint value)
|
||||
{
|
||||
list.InsertRange(position, DC.ToBytes(value, Endian));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public BinaryList AddInt32(int value)
|
||||
{
|
||||
list.AddRange(DC.ToBytes(value, Endian));
|
||||
return this;
|
||||
}
|
||||
public BinaryList InsertInt32(int position, int value)
|
||||
{
|
||||
list.InsertRange(position, DC.ToBytes(value, Endian));
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList AddUInt64(ulong value)
|
||||
{
|
||||
list.AddRange(DC.ToBytes(value, Endian));
|
||||
return this;
|
||||
}
|
||||
public BinaryList InsertUInt64(int position, ulong value)
|
||||
{
|
||||
list.InsertRange(position, DC.ToBytes(value, Endian));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public BinaryList AddInt64(long value)
|
||||
{
|
||||
list.AddRange(DC.ToBytes(value, Endian));
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList InsertInt64(int position, long value)
|
||||
{
|
||||
list.InsertRange(position, DC.ToBytes(value, Endian));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public BinaryList AddFloat32(float value)
|
||||
{
|
||||
list.AddRange(value.ToBytes(Endian));
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList InsertFloat32(int position, float value)
|
||||
{
|
||||
list.InsertRange(position, value.ToBytes(Endian));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public BinaryList AddFloat64(double value)
|
||||
{
|
||||
list.AddRange(value.ToBytes(Endian));
|
||||
return this;
|
||||
}
|
||||
|
||||
public BinaryList InsertFloat64(int position, double value)
|
||||
{
|
||||
list.InsertRange(position, value.ToBytes(Endian));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the list to an array of bytes
|
||||
/// </summary>
|
||||
/// <returns>Bytes array</returns>
|
||||
public byte[] ToArray()
|
||||
{
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public virtual AsyncReply<object[]> Done()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
586
Libraries/Esiur/Data/Codec.cs
Normal file
586
Libraries/Esiur/Data/Codec.cs
Normal file
@@ -0,0 +1,586 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Esiur.Core;
|
||||
using Esiur.Resource;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Collections;
|
||||
using Esiur.Protocol;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public static class Codec
|
||||
{
|
||||
|
||||
//delegate AsyncReply AsyncParser(byte[] data, uint offset, uint length, EpConnection connection, uint[] requestSequence);
|
||||
|
||||
delegate object AsyncParser(ParsedTdu tdu, EpConnection connection, uint[] requestSequence);
|
||||
delegate object SyncParser(ParsedTdu tdu, Warehouse warehouse);
|
||||
|
||||
static AsyncParser[][] FixedAsyncParsers = new AsyncParser[][]
|
||||
{
|
||||
new AsyncParser[]{
|
||||
DataDeserializer.NullParserAsync,
|
||||
DataDeserializer.BooleanFalseParserAsync,
|
||||
DataDeserializer.BooleanTrueParserAsync,
|
||||
DataDeserializer.NotModifiedParserAsync,
|
||||
},
|
||||
new AsyncParser[]{
|
||||
DataDeserializer.UInt8ParserAsync,
|
||||
DataDeserializer.Int8ParserAsync,
|
||||
DataDeserializer.Char8ParserAsync,
|
||||
DataDeserializer.LocalResourceParser8Async,
|
||||
DataDeserializer.ResourceParser8Async,
|
||||
},
|
||||
new AsyncParser[]{
|
||||
DataDeserializer.UInt16ParserAsync,
|
||||
DataDeserializer.Int16ParserAsync,
|
||||
DataDeserializer.Char16ParserAsync,
|
||||
DataDeserializer.LocalResourceParser16Async,
|
||||
DataDeserializer.ResourceParser16Async,
|
||||
},
|
||||
new AsyncParser[]{
|
||||
DataDeserializer.UInt32ParserAsync,
|
||||
DataDeserializer.Int32ParserAsync,
|
||||
DataDeserializer.Float32ParserAsync,
|
||||
DataDeserializer.LocalResourceParser32Async,
|
||||
DataDeserializer.ResourceParser32Async,
|
||||
},
|
||||
new AsyncParser[]{
|
||||
DataDeserializer.UInt64ParserAsync,
|
||||
DataDeserializer.Int64ParserAsync,
|
||||
DataDeserializer.Float64ParserAsync,
|
||||
DataDeserializer.DateTimeParserAsync,
|
||||
},
|
||||
new AsyncParser[]
|
||||
{
|
||||
DataDeserializer.UInt128ParserAsync, // uint 128
|
||||
DataDeserializer.Int128ParserAsync, // int 128
|
||||
DataDeserializer.Decimal128ParserAsync,
|
||||
DataDeserializer.UUIDParserAsync
|
||||
}
|
||||
};
|
||||
|
||||
static AsyncParser[] DynamicAsyncParsers = new AsyncParser[]
|
||||
{
|
||||
DataDeserializer.RawDataParserAsync,
|
||||
DataDeserializer.StringParserAsync,
|
||||
DataDeserializer.ListParserAsync,
|
||||
DataDeserializer.ResourceListParserAsync,
|
||||
DataDeserializer.RecordListParserAsync,
|
||||
DataDeserializer.ResourceLinkParserAsync,
|
||||
};
|
||||
|
||||
static AsyncParser[] TypedAsyncParsers = new AsyncParser[]
|
||||
{
|
||||
DataDeserializer.RecordParserAsync,
|
||||
DataDeserializer.TypedListParserAsync,
|
||||
DataDeserializer.TypedMapParserAsync,
|
||||
DataDeserializer.TupleParserAsync,
|
||||
DataDeserializer.EnumParserAsync,
|
||||
DataDeserializer.ConstantParserAsync,
|
||||
};
|
||||
|
||||
static AsyncParser[] ExtendedAsyncParsers = new AsyncParser[]
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
static SyncParser[][] FixedParsers = new SyncParser[][]
|
||||
{
|
||||
new SyncParser[]{
|
||||
DataDeserializer.NullParser,
|
||||
DataDeserializer.BooleanFalseParser,
|
||||
DataDeserializer.BooleanTrueParser,
|
||||
DataDeserializer.NotModifiedParser,
|
||||
},
|
||||
new SyncParser[]{
|
||||
DataDeserializer.UInt8Parser,
|
||||
DataDeserializer.Int8Parser,
|
||||
DataDeserializer.Char8Parser,
|
||||
DataDeserializer.LocalResourceParser8,
|
||||
DataDeserializer.ResourceParser8,
|
||||
},
|
||||
new SyncParser[]{
|
||||
DataDeserializer.UInt16Parser,
|
||||
DataDeserializer.Int16Parser,
|
||||
DataDeserializer.Char16Parser,
|
||||
DataDeserializer.LocalResourceParser16,
|
||||
DataDeserializer.ResourceParser16,
|
||||
},
|
||||
new SyncParser[]{
|
||||
DataDeserializer.UInt32Parser,
|
||||
DataDeserializer.Int32Parser,
|
||||
DataDeserializer.Float32Parser,
|
||||
DataDeserializer.LocalResourceParser32,
|
||||
DataDeserializer.ResourceParser32,
|
||||
},
|
||||
new SyncParser[]{
|
||||
DataDeserializer.UInt64Parser,
|
||||
DataDeserializer.Int64Parser,
|
||||
DataDeserializer.Float64Parser,
|
||||
DataDeserializer.DateTimeParser,
|
||||
},
|
||||
new SyncParser[]
|
||||
{
|
||||
DataDeserializer.UInt128Parser, // uint 128
|
||||
DataDeserializer.Int128Parser, // int 128
|
||||
DataDeserializer.Decimal128Parser,
|
||||
DataDeserializer.UUIDParser
|
||||
}
|
||||
};
|
||||
|
||||
static SyncParser[] DynamicParsers = new SyncParser[]
|
||||
{
|
||||
DataDeserializer.RawDataParser,
|
||||
DataDeserializer.StringParser,
|
||||
DataDeserializer.ListParser,
|
||||
DataDeserializer.ResourceListParser,
|
||||
DataDeserializer.RecordListParser,
|
||||
DataDeserializer.ResourceLinkParser,
|
||||
// @TODO: Map and MapList parsers to be added
|
||||
};
|
||||
|
||||
static SyncParser[] TypedParsers = new SyncParser[]
|
||||
{
|
||||
DataDeserializer.RecordParser,
|
||||
DataDeserializer.TypedListParser,
|
||||
DataDeserializer.TypedMapParser,
|
||||
DataDeserializer.TupleParser,
|
||||
DataDeserializer.EnumParser,
|
||||
DataDeserializer.ConstantParser,
|
||||
};
|
||||
|
||||
static SyncParser[] ExtendedParsers = new SyncParser[]
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
/// <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">EpConnection 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 (uint, object) ParseAsync(byte[] data, uint offset, EpConnection connection, uint[] requestSequence)
|
||||
{
|
||||
|
||||
var tdu = ParsedTdu.Parse(data, offset, (uint)data.Length);
|
||||
|
||||
if (tdu.Class == TduClass.Invalid)
|
||||
throw new NullReferenceException("DataType can't be parsed.");
|
||||
|
||||
if (tdu.Class == TduClass.Fixed)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence));
|
||||
}
|
||||
else if (tdu.Class == TduClass.Dynamic)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence));
|
||||
}
|
||||
else if (tdu.Class == TduClass.Typed)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence));
|
||||
}
|
||||
else // if (tt.Class == TDUClass.Extension)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static (uint, object) ParseAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence)
|
||||
{
|
||||
if (tdu.Class == TduClass.Invalid)
|
||||
throw new NullReferenceException("DataType can't be parsed.");
|
||||
|
||||
if (tdu.Class == TduClass.Fixed)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, FixedAsyncParsers[tdu.Exponent][tdu.Index](tdu, connection, requestSequence));
|
||||
}
|
||||
else if (tdu.Class == TduClass.Dynamic)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, DynamicAsyncParsers[tdu.Index](tdu, connection, requestSequence));
|
||||
}
|
||||
else if (tdu.Class == TduClass.Typed)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, TypedAsyncParsers[tdu.Index](tdu, connection, requestSequence));
|
||||
}
|
||||
else // if (tt.Class == TDUClass.Extension)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, ExtendedAsyncParsers[tdu.Index](tdu, connection, requestSequence));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static (uint, object) ParseSync(ParsedTdu tdu, Warehouse warehouse)
|
||||
{
|
||||
if (tdu.Class == TduClass.Fixed)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, FixedParsers[tdu.Exponent][tdu.Index](tdu, warehouse));
|
||||
}
|
||||
else if (tdu.Class == TduClass.Dynamic)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, DynamicParsers[tdu.Index](tdu, warehouse));
|
||||
}
|
||||
else if (tdu.Class == TduClass.Typed)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, TypedParsers[tdu.Index](tdu, warehouse));
|
||||
}
|
||||
else // Extension
|
||||
{
|
||||
return ((uint)tdu.TotalLength, ExtendedParsers[tdu.Index](tdu, warehouse));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static (uint, object) ParseSync(byte[] data, uint offset, Warehouse warehouse)
|
||||
{
|
||||
var tdu = ParsedTdu.Parse(data, offset, (uint)data.Length);
|
||||
|
||||
if (tdu.Class == TduClass.Invalid)
|
||||
throw new NullReferenceException("DataType can't be parsed.");
|
||||
|
||||
|
||||
if (tdu.Class == TduClass.Fixed)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, FixedParsers[tdu.Exponent][tdu.Index](tdu, warehouse));
|
||||
}
|
||||
else if (tdu.Class == TduClass.Dynamic)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, DynamicParsers[tdu.Index](tdu, warehouse));
|
||||
}
|
||||
else if (tdu.Class == TduClass.Typed)
|
||||
{
|
||||
return ((uint)tdu.TotalLength, TypedParsers[tdu.Index](tdu, warehouse));
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((uint)tdu.TotalLength, ExtendedParsers[tdu.Index](tdu, warehouse));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Check if a resource is local to a given connection.
|
||||
/// </summary>
|
||||
/// <param name="resource">Resource to check.</param>
|
||||
/// <param name="connection">EpConnection to check if the resource is local to it.</param>
|
||||
/// <returns>True, if the resource owner is the given connection, otherwise False.</returns>
|
||||
public static bool IsLocalResource(IResource resource, EpConnection connection)
|
||||
{
|
||||
if (resource == null)
|
||||
throw new NullReferenceException("Resource is null.");
|
||||
|
||||
if (resource is EpResource)
|
||||
if (((EpResource)(resource)).DistributedResourceConnection == connection)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public delegate Tdu Composer(object value, Warehouse warehouse, EpConnection connection);
|
||||
|
||||
public static Dictionary<Type, Composer> Composers = new Dictionary<Type, Composer>()
|
||||
{
|
||||
// Fixed
|
||||
[typeof(bool)] = DataSerializer.BoolComposer,
|
||||
[typeof(bool?)] = DataSerializer.BoolComposer,
|
||||
[typeof(NotModified)] = DataSerializer.NotModifiedComposer,
|
||||
[typeof(byte)] = DataSerializer.UInt8Composer,
|
||||
[typeof(byte?)] = DataSerializer.UInt8Composer,
|
||||
[typeof(sbyte)] = DataSerializer.Int8Composer,
|
||||
[typeof(sbyte?)] = DataSerializer.Int8Composer,
|
||||
[typeof(char)] = DataSerializer.Char16Composer,
|
||||
[typeof(char?)] = DataSerializer.Char16Composer,
|
||||
[typeof(short)] = DataSerializer.Int16Composer,
|
||||
[typeof(short?)] = DataSerializer.Int16Composer,
|
||||
[typeof(ushort)] = DataSerializer.UInt16Composer,
|
||||
[typeof(ushort?)] = DataSerializer.UInt16Composer,
|
||||
[typeof(int)] = DataSerializer.Int32Composer,
|
||||
[typeof(int?)] = DataSerializer.Int32Composer,
|
||||
[typeof(uint)] = DataSerializer.UInt32Composer,
|
||||
[typeof(uint?)] = DataSerializer.UInt32Composer,
|
||||
[typeof(float)] = DataSerializer.Float32Composer,
|
||||
[typeof(float?)] = DataSerializer.Float32Composer,
|
||||
[typeof(long)] = DataSerializer.Int64Composer,
|
||||
[typeof(long?)] = DataSerializer.Int64Composer,
|
||||
[typeof(ulong)] = DataSerializer.UInt64Composer,
|
||||
[typeof(ulong?)] = DataSerializer.UInt64Composer,
|
||||
[typeof(double)] = DataSerializer.Float64Composer,
|
||||
[typeof(double?)] = DataSerializer.Float64Composer,
|
||||
[typeof(DateTime)] = DataSerializer.DateTimeComposer,
|
||||
[typeof(DateTime?)] = DataSerializer.DateTimeComposer,
|
||||
[typeof(decimal)] = DataSerializer.Decimal128Composer,
|
||||
[typeof(decimal?)] = DataSerializer.Decimal128Composer,
|
||||
[typeof(byte[])] = DataSerializer.RawDataComposerFromArray,
|
||||
//[typeof(byte?[])] = DataSerializer.RawDataComposerFromArray,
|
||||
[typeof(List<byte>)] = DataSerializer.RawDataComposerFromList,
|
||||
//[typeof(List<byte?>)] = DataSerializer.RawDataComposerFromList,
|
||||
[typeof(string)] = DataSerializer.StringComposer,
|
||||
[typeof(ResourceLink)] = DataSerializer.ResourceLinkComposer,
|
||||
[typeof(Uuid)] = DataSerializer.UUIDComposer,
|
||||
// Special
|
||||
[typeof(object[])] = DataSerializer.ListComposer,
|
||||
[typeof(List<object>)] = DataSerializer.ListComposer,
|
||||
[typeof(VarList<object>)] = DataSerializer.ListComposer,
|
||||
[typeof(IResource[])] = DataSerializer.ResourceListComposer,
|
||||
[typeof(IResource?[])] = DataSerializer.ResourceListComposer,
|
||||
[typeof(List<IResource>)] = DataSerializer.ResourceListComposer,
|
||||
[typeof(List<IResource?>)] = DataSerializer.ResourceListComposer,
|
||||
[typeof(VarList<IResource>)] = DataSerializer.ResourceListComposer,
|
||||
[typeof(VarList<IResource?>)] = DataSerializer.ResourceListComposer,
|
||||
[typeof(IRecord[])] = DataSerializer.RecordListComposer,
|
||||
[typeof(IRecord?[])] = DataSerializer.RecordListComposer,
|
||||
[typeof(List<IRecord>)] = DataSerializer.RecordListComposer,
|
||||
[typeof(List<IRecord?>)] = DataSerializer.RecordListComposer,
|
||||
[typeof(VarList<IRecord>)] = DataSerializer.RecordListComposer,
|
||||
[typeof(VarList<IRecord?>)] = DataSerializer.RecordListComposer,
|
||||
[typeof(Map<object, object>)] = DataSerializer.MapComposer,
|
||||
[typeof(Map<object?, object>)] = DataSerializer.MapComposer,
|
||||
[typeof(Map<object, object?>)] = DataSerializer.MapComposer,
|
||||
[typeof(Map<object?, object?>)] = DataSerializer.MapComposer,
|
||||
[typeof(PropertyValue[])] = DataSerializer.PropertyValueArrayComposer
|
||||
// Typed
|
||||
// [typeof(bool[])] = (value, con) => DataSerializer.TypedListComposer((IEnumerable)value, typeof(bool), con),
|
||||
// [typeof(bool?[])] = (value, con) => (TransmissionDataUnitIdentifier.TypedList, new byte[] { (byte)value }),
|
||||
// [typeof(List<bool>)] = (value, con) => (TransmissionDataUnitIdentifier.TypedList, new byte[] { (byte)value }),
|
||||
// [typeof(List<bool?>)] = (value, con) => (TransmissionDataUnitIdentifier.TypedList, new byte[] { (byte)value }),
|
||||
|
||||
// [typeof(byte?[])] = (value, con) => (TransmissionDataUnitIdentifier.TypedList, new byte[] { (byte)value }),
|
||||
// [typeof(List<bool?>)] = (value, con) => (TransmissionDataUnitIdentifier.TypedList, new byte[] { (byte)value }),
|
||||
|
||||
};
|
||||
|
||||
|
||||
internal static Tdu
|
||||
ComposeInternal(object valueOrSource, Warehouse warehouse, EpConnection connection)
|
||||
{
|
||||
if (valueOrSource == null)
|
||||
return new Tdu(TduIdentifier.Null, null, 0);
|
||||
|
||||
var type = valueOrSource.GetType();
|
||||
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
|
||||
var genericType = type.GetGenericTypeDefinition();
|
||||
if (genericType == typeof(PropertyContext<>))
|
||||
{
|
||||
valueOrSource = ((IPropertyContext)valueOrSource).GetValue(connection);
|
||||
}
|
||||
else if (genericType == typeof(Func<>))
|
||||
{
|
||||
var args = genericType.GetGenericArguments();
|
||||
if (args.Length == 2 && args[0] == typeof(EpConnection))
|
||||
{
|
||||
//Func<EpConnection, EpConnection> a;
|
||||
//a.Invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valueOrSource is IUserType)
|
||||
valueOrSource = ((IUserType)valueOrSource).Get();
|
||||
|
||||
if (valueOrSource == null)
|
||||
return new Tdu(TduIdentifier.Null, null, 0);
|
||||
|
||||
|
||||
type = valueOrSource.GetType();
|
||||
|
||||
|
||||
if (Composers.ContainsKey(type))
|
||||
{
|
||||
return Composers[type](valueOrSource, warehouse, connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Codec.ImplementsInterface(type, typeof(IResource)))
|
||||
{
|
||||
return DataSerializer.ResourceComposer(valueOrSource, warehouse, connection);
|
||||
}
|
||||
else if (Codec.ImplementsInterface(type, typeof(IRecord)))
|
||||
{
|
||||
return DataSerializer.RecordComposer(valueOrSource, warehouse, connection);
|
||||
}
|
||||
else if (type.IsGenericType)
|
||||
{
|
||||
var genericType = type.GetGenericTypeDefinition();
|
||||
if (genericType == typeof(List<>)
|
||||
|| genericType == typeof(VarList<>)
|
||||
|| genericType == typeof(IList<>))
|
||||
{
|
||||
var args = type.GetGenericArguments();
|
||||
//if (Composers.ContainsKey(args[0]))
|
||||
//{
|
||||
return DataSerializer.TypedListComposer((IEnumerable)valueOrSource, args[0], warehouse, connection);
|
||||
//}
|
||||
}
|
||||
else if (genericType == typeof(Map<,>))
|
||||
{
|
||||
var args = type.GetGenericArguments();
|
||||
|
||||
return DataSerializer.TypedMapComposer(valueOrSource, args[0], args[1], warehouse, connection);
|
||||
|
||||
}
|
||||
else if (genericType == typeof(Dictionary<,>))
|
||||
{
|
||||
var args = type.GetGenericArguments();
|
||||
|
||||
return DataSerializer.TypedDictionaryComposer(valueOrSource, args[0], args[1], warehouse, connection);
|
||||
|
||||
}
|
||||
|
||||
else if (genericType == typeof(ValueTuple<,>)
|
||||
|| genericType == typeof(ValueTuple<,,>)
|
||||
|| genericType == typeof(ValueTuple<,,,>)
|
||||
|| genericType == typeof(ValueTuple<,,,,>)
|
||||
|| genericType == typeof(ValueTuple<,,,,,>)
|
||||
|| genericType == typeof(ValueTuple<,,,,,,>)
|
||||
)
|
||||
{
|
||||
return DataSerializer.TupleComposer(valueOrSource, warehouse, connection);
|
||||
}
|
||||
}
|
||||
else if (type.IsArray)
|
||||
{
|
||||
var elementType = type.GetElementType();
|
||||
|
||||
//if (Composers.ContainsKey(elementType))
|
||||
//{
|
||||
return DataSerializer.TypedListComposer((IEnumerable)valueOrSource, elementType, warehouse, connection);
|
||||
|
||||
//}
|
||||
}
|
||||
else if (type.IsEnum)
|
||||
{
|
||||
return DataSerializer.EnumComposer(valueOrSource, warehouse, connection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new Tdu(TduIdentifier.Null, null, 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compose a variable
|
||||
/// </summary>
|
||||
/// <param name="value">Value to compose.</param>
|
||||
/// <param name="connection">EpConnection 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 valueOrSource, Warehouse warehouse, EpConnection connection)//, bool prependType = true)
|
||||
{
|
||||
var tdu = ComposeInternal(valueOrSource, warehouse, connection);
|
||||
return tdu.Composed;
|
||||
}
|
||||
|
||||
public static bool IsAnonymous(Type type)
|
||||
{
|
||||
// Detect anonymous types
|
||||
var info = type.GetTypeInfo();
|
||||
var hasCompilerGeneratedAttribute = info.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Count() > 0;
|
||||
var nameContainsAnonymousType = type.FullName.Contains("AnonymousType");
|
||||
return hasCompilerGeneratedAttribute && nameContainsAnonymousType;
|
||||
}
|
||||
|
||||
|
||||
public static Type? GetGenericType(Type type, Type ifaceType, int argument = 0)
|
||||
{
|
||||
if (ifaceType.IsAssignableFrom(type))
|
||||
{
|
||||
var col = type.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == ifaceType)
|
||||
.FirstOrDefault()?
|
||||
.GetGenericArguments()
|
||||
.FirstOrDefault() ?? null;
|
||||
|
||||
return col;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static bool ImplementsInterface(Type type, Type iface)
|
||||
{
|
||||
while (type != null)
|
||||
{
|
||||
if (type == iface)
|
||||
return true;
|
||||
|
||||
#if NETSTANDARD
|
||||
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;
|
||||
}
|
||||
|
||||
public static bool InheritsClass(Type type, Type parent)
|
||||
=> type.IsSubclassOf(parent);
|
||||
|
||||
/// <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 NETSTANDARD
|
||||
childType = childType.GetTypeInfo().BaseType;
|
||||
#else
|
||||
childType = childType.BaseType;
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
866
Libraries/Esiur/Data/DataConverter.cs
Normal file
866
Libraries/Esiur/Data/DataConverter.cs
Normal file
@@ -0,0 +1,866 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Reflection;
|
||||
using Esiur.Data;
|
||||
using Esiur.Core;
|
||||
using Esiur.Resource;
|
||||
using System.Collections;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
public static class DC // Data Converter
|
||||
{
|
||||
|
||||
|
||||
public static object CastConvertOld(object value, Type destinationType)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
var sourceType = value.GetType();
|
||||
|
||||
if (destinationType == sourceType)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sourceType.IsArray && (destinationType.IsArray || destinationType == typeof(object)))
|
||||
{
|
||||
destinationType = destinationType.GetElementType();
|
||||
|
||||
var v = value as Array;
|
||||
|
||||
var rt = Array.CreateInstance(destinationType, v.Length);
|
||||
|
||||
for (var i = 0; i < rt.Length; i++)
|
||||
{
|
||||
rt.SetValue(CastConvertOld(v.GetValue(i), destinationType), i);
|
||||
}
|
||||
|
||||
return rt;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
var underType = Nullable.GetUnderlyingType(destinationType);
|
||||
if (underType != null)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
else
|
||||
destinationType = underType;
|
||||
}
|
||||
|
||||
if (destinationType.IsInstanceOfType(value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else if (typeof(IUserType).IsAssignableFrom(destinationType))
|
||||
{
|
||||
var rt = Activator.CreateInstance(destinationType) as IUserType;
|
||||
rt.Set(value);
|
||||
return rt;
|
||||
}
|
||||
//else if (sourceType == typeof(Structure) && sourceType.IsAssignableFrom(destinationType))
|
||||
//{
|
||||
// return Structure.FromStructure((Structure)value, destinationType);
|
||||
//}
|
||||
else if (destinationType.IsEnum)
|
||||
{
|
||||
return Enum.ToObject(destinationType, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Convert.ChangeType(value, destinationType);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw ex;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static byte[] ToBytes(sbyte value)
|
||||
{
|
||||
return new byte[1] { (byte)value };
|
||||
}
|
||||
|
||||
public static byte[] ToBytes(byte value)
|
||||
{
|
||||
return new byte[1] { value };
|
||||
}
|
||||
|
||||
public static byte[] ToBytes(IPAddress ip)
|
||||
{
|
||||
return ip.GetAddressBytes();
|
||||
}
|
||||
|
||||
public static byte[] ToBytes(PhysicalAddress mac)
|
||||
{
|
||||
return mac.GetAddressBytes();
|
||||
}
|
||||
|
||||
public static byte[] ToBytes(bool value)
|
||||
{
|
||||
return new byte[1] { value ? (byte)1 : (byte)0 };
|
||||
}
|
||||
|
||||
public static byte ToByte(bool value)
|
||||
{
|
||||
return value ? (byte)1 : (byte)0;
|
||||
}
|
||||
|
||||
public static byte ToByte(sbyte value)
|
||||
{
|
||||
return (byte)value;
|
||||
}
|
||||
|
||||
public static byte[] ToBytes(byte[] value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
public static byte[] ToBytes(bool[] value)
|
||||
{
|
||||
|
||||
byte[] ba = new byte[value.Length];
|
||||
|
||||
for (int i = 0; i < ba.Length; i++)
|
||||
ba[i] = DC.ToByte(value[i]);
|
||||
|
||||
return ba;
|
||||
}
|
||||
|
||||
public static byte[] ToBytes(sbyte[] value)
|
||||
{
|
||||
|
||||
byte[] ba = new byte[value.Length];
|
||||
|
||||
for (int i = 0; i < ba.Length; i++)
|
||||
ba[i] = DC.ToByte(value[i]);
|
||||
|
||||
return ba;
|
||||
}
|
||||
|
||||
public static byte[] ToBytes(char value)
|
||||
{
|
||||
byte[] ret = BitConverter.GetBytes(value);
|
||||
Array.Reverse(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static byte[] ToBytes(Guid value)
|
||||
{
|
||||
return value.ToByteArray();
|
||||
}
|
||||
|
||||
|
||||
public static void Append(ref byte[] dst, byte[] src)
|
||||
{
|
||||
Append(ref dst, src, (uint)0, (uint)src.Length);
|
||||
}
|
||||
|
||||
public static void Append(ref byte[] dst, byte[] src, uint srcOffset, uint length)
|
||||
{
|
||||
var dstOffset = dst.Length;
|
||||
Array.Resize<byte>(ref dst, dstOffset + (int)length);
|
||||
Buffer.BlockCopy(src, (int)srcOffset, dst, dstOffset, (int)length);
|
||||
}
|
||||
|
||||
public static byte[] Combine(byte[] src1, uint src1Offset, uint src1Length, byte[] src2, uint src2Offset, uint src2Length)
|
||||
{
|
||||
var rt = new byte[src1Length + src2Length];
|
||||
Buffer.BlockCopy(src1, (int)src1Offset, rt, 0, (int)src1Length);
|
||||
Buffer.BlockCopy(src2, (int)src2Offset, rt, (int)src1Length, (int)src2Length);
|
||||
return rt;
|
||||
}
|
||||
|
||||
public static byte[] Merge(params byte[][] arrays)
|
||||
{
|
||||
var s = arrays.Sum(x => x.Length);
|
||||
var r = new byte[s];
|
||||
var offset = 0;
|
||||
foreach (var array in arrays)
|
||||
{
|
||||
Buffer.BlockCopy(array, 0, r, offset, array.Length);
|
||||
offset += array.Length;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static byte[] ToBytes(this string[] value)
|
||||
{
|
||||
List<byte> rt = new List<byte>();
|
||||
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
byte[] ba = ToBytes(value[i]);
|
||||
// add string length
|
||||
rt.AddRange(ToBytes(ba.Length, Endian.Little));
|
||||
// add encoded string
|
||||
rt.AddRange(ba);
|
||||
}
|
||||
|
||||
return rt.ToArray();
|
||||
}
|
||||
|
||||
|
||||
public static unsafe byte[] ToBytes(this int value, Endian endian)
|
||||
{
|
||||
var rt = new byte[4];
|
||||
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = rt)
|
||||
*((int*)ptr) = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte* p = (byte*)&value;
|
||||
|
||||
rt[0] = *(p + 3);
|
||||
rt[1] = *(p + 2);
|
||||
rt[2] = *(p + 1);
|
||||
rt[3] = *(p + 0);
|
||||
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static unsafe byte[] ToBytes(this short value, Endian endian)
|
||||
{
|
||||
var rt = new byte[2];
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = rt)
|
||||
*((short*)ptr) = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte* p = (byte*)&value;
|
||||
|
||||
rt[0] = *(p + 1);
|
||||
rt[1] = *(p + 0);
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
public static unsafe byte[] ToBytes(this float value, Endian endian)
|
||||
|
||||
{
|
||||
var rt = new byte[4];
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = rt)
|
||||
*((float*)ptr) = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte* p = (byte*)&value;
|
||||
rt[0] = *(p + 3);
|
||||
rt[1] = *(p + 2);
|
||||
rt[2] = *(p + 1);
|
||||
rt[3] = *(p);
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static byte[] ToBytes(this string value)
|
||||
{
|
||||
return Encoding.UTF8.GetBytes(value);
|
||||
}
|
||||
|
||||
public unsafe static byte[] ToBytes(this double value, Endian endian)
|
||||
{
|
||||
var rt = new byte[8];
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = rt)
|
||||
*((double*)ptr) = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte* p = (byte*)&value;
|
||||
|
||||
rt[0] = *(p + 7);
|
||||
rt[1] = *(p + 6);
|
||||
rt[2] = *(p + 5);
|
||||
rt[3] = *(p + 4);
|
||||
rt[4] = *(p + 3);
|
||||
rt[5] = *(p + 2);
|
||||
rt[6] = *(p + 1);
|
||||
rt[7] = *(p + 0);
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
public static unsafe byte[] ToBytes(this long value, Endian endian)
|
||||
{
|
||||
var rt = new byte[8];
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = rt)
|
||||
*((long*)ptr) = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte* p = (byte*)&value;
|
||||
|
||||
rt[0] = *(p + 7);
|
||||
rt[1] = *(p + 6);
|
||||
rt[2] = *(p + 5);
|
||||
rt[3] = *(p + 4);
|
||||
rt[4] = *(p + 3);
|
||||
rt[5] = *(p + 2);
|
||||
rt[6] = *(p + 1);
|
||||
rt[7] = *(p + 0);
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
public static unsafe byte[] ToBytes(this DateTime value)
|
||||
{
|
||||
var rt = new byte[8];
|
||||
var v = value.ToUniversalTime().Ticks;
|
||||
|
||||
fixed (byte* ptr = rt)
|
||||
*((long*)ptr) = v;
|
||||
return rt;
|
||||
}
|
||||
|
||||
|
||||
public static unsafe byte[] ToBytes(this ulong value, Endian endia)
|
||||
{
|
||||
var rt = new byte[8];
|
||||
if (endia == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = rt)
|
||||
*((ulong*)ptr) = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
byte* p = (byte*)&value;
|
||||
|
||||
rt[0] = *(p + 7);
|
||||
rt[1] = *(p + 6);
|
||||
rt[2] = *(p + 5);
|
||||
rt[3] = *(p + 4);
|
||||
rt[4] = *(p + 3);
|
||||
rt[5] = *(p + 2);
|
||||
rt[6] = *(p + 1);
|
||||
rt[7] = *(p + 0);
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
public static unsafe byte[] ToBytes(this uint value, Endian endian)
|
||||
{
|
||||
var rt = new byte[4];
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = rt)
|
||||
*((uint*)ptr) = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
byte* p = (byte*)&value;
|
||||
|
||||
rt[0] = *(p + 3);
|
||||
rt[1] = *(p + 2);
|
||||
rt[2] = *(p + 1);
|
||||
rt[3] = *(p + 0);
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
public static unsafe byte[] ToBytes(this ushort value, Endian endian)
|
||||
{
|
||||
var rt = new byte[2];
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = rt)
|
||||
*((ushort*)ptr) = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte* p = (byte*)&value;
|
||||
|
||||
rt[0] = *(p + 1);
|
||||
rt[1] = *(p);
|
||||
}
|
||||
return rt;
|
||||
}
|
||||
|
||||
public static unsafe byte[] ToBytes(this decimal value, Endian endian)
|
||||
{
|
||||
var rt = new byte[16];
|
||||
fixed (byte* ptr = rt)
|
||||
*((decimal*)ptr) = value;
|
||||
|
||||
if (endian == Endian.Big)
|
||||
Array.Reverse(rt);
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
public static string ToHex(this byte[] ba)
|
||||
{
|
||||
if (ba == null)
|
||||
return "";
|
||||
return ToHex(ba, 0, (uint)ba.Length);
|
||||
}
|
||||
|
||||
public static string ToHex(this byte[] ba, uint offset, uint length, string separator = " ")
|
||||
{
|
||||
if (separator == null)
|
||||
separator = "";
|
||||
|
||||
return string.Join(separator, ba.Skip((int)offset).Take((int)length).Select(x => x.ToString("x2")).ToArray());
|
||||
}
|
||||
|
||||
public static byte[] FromHex(string hexString, string separator = " ")
|
||||
{
|
||||
var rt = new List<byte>();
|
||||
|
||||
if (separator == null)
|
||||
{
|
||||
for (var i = 0; i < hexString.Length; i += 2)
|
||||
rt.Add(Convert.ToByte(hexString.Substring(i, 2), 16));
|
||||
}
|
||||
else
|
||||
{
|
||||
var hexes = hexString.Split(new string[] { separator }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var h in hexes)
|
||||
rt.Add(Convert.ToByte(h, 16));
|
||||
}
|
||||
|
||||
return rt.ToArray();
|
||||
}
|
||||
|
||||
public static string FlagsEnumToString<T>(ulong value)
|
||||
{
|
||||
|
||||
string rt = typeof(T).Name + ":";
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
ulong bit = (ulong)(Convert.ToUInt64(Math.Pow(2, i)) & value);
|
||||
if (bit != 0)
|
||||
{
|
||||
rt += " " + Enum.GetName(typeof(T), bit);
|
||||
}
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
public static bool TryParse<T>(object Input, out T Results)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if NETSTANDARD
|
||||
var tryParse = typeof(T).GetTypeInfo().GetDeclaredMethod("TryParse");
|
||||
if ((bool)tryParse.Invoke(null, new object[] { Input, null }))
|
||||
{
|
||||
var parse = typeof(T).GetTypeInfo().GetDeclaredMethod("Parse");
|
||||
|
||||
Results = (T)parse.Invoke(null, new object[] { Input });
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
if ((bool)typeof(T).InvokeMember("TryParse", BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, null, null, new object[] { Input, null }))
|
||||
{
|
||||
Results = (T)typeof(T).InvokeMember("Parse", BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, null, null, new object[] { Input });
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
else
|
||||
{
|
||||
Results = default(T);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch //Exception ex)
|
||||
{
|
||||
Results = default(T);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static DateTime FromUnixTime(uint seconds)
|
||||
{
|
||||
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds((double)seconds);
|
||||
}
|
||||
|
||||
public static DateTime FromUnixTime(ulong milliseconds)
|
||||
{
|
||||
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds((double)milliseconds);
|
||||
}
|
||||
|
||||
|
||||
public static sbyte GetInt8(this byte[] data, uint offset)
|
||||
{
|
||||
return (sbyte)data[offset];
|
||||
}
|
||||
|
||||
|
||||
public static int Mod(this int value, int divisor)
|
||||
{
|
||||
var rt = value % divisor;
|
||||
if (rt < 0) return rt + divisor;
|
||||
return rt;
|
||||
}
|
||||
|
||||
public static ulong RotateLeft(this ulong value, int shift)
|
||||
{
|
||||
return value << shift | value >> (64 - shift);
|
||||
}
|
||||
|
||||
public static ulong RotateRight(this ulong value, int shift)
|
||||
{
|
||||
return value >> shift | value << (64 - shift);
|
||||
}
|
||||
|
||||
public static uint RotateLeft(this uint value, int shift)
|
||||
{
|
||||
return value << shift | value >> (32 - shift);
|
||||
}
|
||||
|
||||
public static uint RotateRight(this uint value, int shift)
|
||||
{
|
||||
return value >> shift | value << (32 - shift);
|
||||
}
|
||||
|
||||
public static ushort RotateLeft(this ushort value, int shift)
|
||||
{
|
||||
return (ushort)(value << shift | value >> (16 - shift));
|
||||
}
|
||||
|
||||
public static ushort RotateRight(this ushort value, int shift)
|
||||
{
|
||||
return (ushort)(value >> shift | value << (16 - shift));
|
||||
}
|
||||
|
||||
public static byte RotateLeft(this byte value, int shift)
|
||||
{
|
||||
return (byte)(value << shift | value >> (8 - shift));
|
||||
}
|
||||
|
||||
public static byte RotateRight(this byte value, int shift)
|
||||
{
|
||||
return (byte)(value >> shift | value << (8 - shift));
|
||||
}
|
||||
|
||||
|
||||
public static byte GetUInt8(this byte[] data, uint offset)
|
||||
{
|
||||
return data[offset];
|
||||
}
|
||||
|
||||
public static unsafe short GetInt16(this byte[] data, uint offset, Endian endian)
|
||||
{
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = &data[offset])
|
||||
return *(short*)ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (Int16)((data[offset] << 8) | data[offset + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static unsafe ushort GetUInt16(this byte[] data, uint offset, Endian endian)
|
||||
{
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = &data[offset])
|
||||
return *(ushort*)ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (UInt16)((data[offset] << 8) | data[offset + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe int GetInt32(this byte[] data, uint offset, Endian endian)
|
||||
{
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = &data[offset])
|
||||
return *(int*)ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (Int32)((data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3]);
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe uint GetUInt32(this byte[] data, uint offset, Endian endian)
|
||||
{
|
||||
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = &data[offset])
|
||||
return *(uint*)ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (uint)((data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static unsafe ulong GetUInt64(this byte[] data, uint offset, Endian endian)
|
||||
{
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = &data[offset])
|
||||
return *(ulong*)ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt64 rt = 0;
|
||||
byte* p = (byte*)&rt;
|
||||
|
||||
*(p + 7) = data[offset++];
|
||||
*(p + 6) = data[offset++];
|
||||
*(p + 5) = data[offset++];
|
||||
*(p + 4) = data[offset++];
|
||||
*(p + 3) = data[offset++];
|
||||
*(p + 2) = data[offset++];
|
||||
*(p + 1) = data[offset++];
|
||||
*(p) = data[offset++];
|
||||
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static unsafe long GetInt64(this byte[] data, uint offset, Endian endian)
|
||||
{
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = &data[offset])
|
||||
return *(long*)ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
Int64 rt = 0;
|
||||
byte* p = (byte*)&rt;
|
||||
|
||||
*(p + 7) = data[offset++];
|
||||
*(p + 6) = data[offset++];
|
||||
*(p + 5) = data[offset++];
|
||||
*(p + 4) = data[offset++];
|
||||
*(p + 3) = data[offset++];
|
||||
*(p + 2) = data[offset++];
|
||||
*(p + 1) = data[offset++];
|
||||
*(p) = data[offset++];
|
||||
|
||||
return rt;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe float GetFloat32(this byte[] data, uint offset, Endian endian)
|
||||
{
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = &data[offset])
|
||||
return *(float*)ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
float rt = 0;
|
||||
byte* p = (byte*)&rt;
|
||||
*p = data[offset + 3];
|
||||
*(p + 1) = data[offset + 2];
|
||||
*(p + 2) = data[offset + 1];
|
||||
*(p + 3) = data[offset];
|
||||
return rt;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static unsafe double GetFloat64(this byte[] data, uint offset, Endian endian)
|
||||
{
|
||||
if (endian == Endian.Little)
|
||||
{
|
||||
fixed (byte* ptr = &data[offset])
|
||||
return *(double*)ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
double rt = 0;
|
||||
byte* p = (byte*)&rt;
|
||||
|
||||
*(p + 7) = data[offset++];
|
||||
*(p + 6) = data[offset++];
|
||||
*(p + 5) = data[offset++];
|
||||
*(p + 4) = data[offset++];
|
||||
*(p + 3) = data[offset++];
|
||||
*(p + 2) = data[offset++];
|
||||
*(p + 1) = data[offset++];
|
||||
*(p) = data[offset++];
|
||||
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static bool GetBoolean(this byte[] data, uint offset)
|
||||
{
|
||||
return data[offset] > 0;
|
||||
}
|
||||
|
||||
public static char GetChar(this byte[] data, uint offset)
|
||||
{
|
||||
return Convert.ToChar(((data[offset] << 8) | data[offset + 1]));
|
||||
}
|
||||
|
||||
|
||||
public static string GetString(this byte[] data, uint offset, uint length)
|
||||
{
|
||||
return Encoding.UTF8.GetString(data, (int)offset, (int)length);
|
||||
}
|
||||
|
||||
public static string[] GetStringArray(this byte[] data, uint offset, uint length)
|
||||
{
|
||||
List<string> ar = new List<string>();
|
||||
|
||||
uint i = 0;
|
||||
|
||||
while (i < length)
|
||||
{
|
||||
var cl = GetUInt32(data, offset + i, Endian.Little);
|
||||
i += 4;
|
||||
ar.Add(Encoding.UTF8.GetString(data, (int)(offset + i), (int)cl));
|
||||
i += cl;
|
||||
}
|
||||
|
||||
return ar.ToArray();
|
||||
}
|
||||
|
||||
public static Uuid GetUUID(this byte[] data, uint offset)
|
||||
{
|
||||
return new Uuid(data, offset);
|
||||
}
|
||||
|
||||
//public static Guid GetGuid(this byte[] data, uint offset)
|
||||
//{
|
||||
// return new Guid(Clip(data, offset, 16));
|
||||
//}
|
||||
|
||||
public static DateTime GetDateTime(this byte[] data, uint offset, Endian endian)
|
||||
{
|
||||
var ticks = GetInt64(data, offset, endian);
|
||||
return new DateTime(ticks, DateTimeKind.Utc);
|
||||
}
|
||||
|
||||
|
||||
public static IPAddress GetIPv4Address(this byte[] data, uint offset)
|
||||
{
|
||||
return new IPAddress((long)GetUInt32(data, offset, Endian.Little));
|
||||
}
|
||||
|
||||
|
||||
public static IPAddress GetIPv6Address(this byte[] data, uint offset)
|
||||
{
|
||||
return new IPAddress(Clip(data, offset, 16));
|
||||
}
|
||||
|
||||
public static byte[] Clip(this byte[] data, uint offset, uint length)
|
||||
{
|
||||
if (data.Length < offset + length)
|
||||
throw new ArgumentException("Length exceeds array boundary.");
|
||||
|
||||
// if (length == data.Length && offset == 0)
|
||||
// return data.ToArray();
|
||||
|
||||
var b = new byte[length];
|
||||
Buffer.BlockCopy(data, (int)offset, b, 0, (int)length);
|
||||
return b;
|
||||
}
|
||||
|
||||
public static string ToISODateTime(this DateTime date)
|
||||
{
|
||||
return date.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
public static uint ToUnixTime(this DateTime date)
|
||||
{
|
||||
return (uint)(date - new DateTime(1970, 1, 1)).TotalSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1600
Libraries/Esiur/Data/DataDeserializer.cs
Normal file
1600
Libraries/Esiur/Data/DataDeserializer.cs
Normal file
File diff suppressed because it is too large
Load Diff
1005
Libraries/Esiur/Data/DataSerializer.cs
Normal file
1005
Libraries/Esiur/Data/DataSerializer.cs
Normal file
File diff suppressed because it is too large
Load Diff
12
Libraries/Esiur/Data/Endian.cs
Normal file
12
Libraries/Esiur/Data/Endian.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public enum Endian
|
||||
{
|
||||
Big,
|
||||
Little
|
||||
}
|
||||
}
|
||||
137
Libraries/Esiur/Data/Gvwie/GroupInt16Codec.cs
Normal file
137
Libraries/Esiur/Data/Gvwie/GroupInt16Codec.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Esiur.Data.Gvwie;
|
||||
|
||||
public static class GroupInt16Codec
|
||||
{
|
||||
// ----------------- Encoder -----------------
|
||||
public static byte[] Encode(IList<short> values)
|
||||
{
|
||||
var dst = new List<byte>(values.Count); // close lower bound
|
||||
int i = 0;
|
||||
|
||||
while (i < values.Count)
|
||||
{
|
||||
ushort zz = ZigZag16(values[i]);
|
||||
|
||||
// Fast path: single byte with 7-bit ZigZag
|
||||
if (zz <= 0x7Fu)
|
||||
{
|
||||
dst.Add((byte)zz); // MSB=0 implicitly
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Group path: up to 64 items sharing width (1 or 2 bytes)
|
||||
int start = i;
|
||||
int count = 1;
|
||||
int width = (zz <= 0xFFu) ? 1 : 2;
|
||||
|
||||
while (count < 64 && (i + count) < values.Count)
|
||||
{
|
||||
ushort z2 = ZigZag16(values[i + count]);
|
||||
int w2 = (z2 <= 0xFFu) ? 1 : 2;
|
||||
if (w2 > width) width = w2; // widen as needed
|
||||
count++;
|
||||
}
|
||||
|
||||
// Header: 1 | (count-1)[6 bits] | (width-1)[1 bit]
|
||||
byte header = 0x80;
|
||||
header |= (byte)(((count - 1) & 0x3F) << 1);
|
||||
header |= (byte)((width - 1) & 0x01);
|
||||
dst.Add(header);
|
||||
|
||||
// Payload: count ZigZag magnitudes, LE, 'width' bytes each
|
||||
for (int k = 0; k < count; k++)
|
||||
{
|
||||
ushort z = ZigZag16(values[start + k]);
|
||||
WriteLE(dst, z, width);
|
||||
}
|
||||
|
||||
i += count;
|
||||
}
|
||||
|
||||
return dst.ToArray();
|
||||
}
|
||||
|
||||
// ----------------- Decoder -----------------
|
||||
public static short[] Decode(ReadOnlySpan<byte> src)
|
||||
{
|
||||
var result = new List<short>();
|
||||
int pos = 0;
|
||||
|
||||
while (pos < src.Length)
|
||||
{
|
||||
byte h = src[pos++];
|
||||
|
||||
if ((h & 0x80) == 0)
|
||||
{
|
||||
// Fast path: 7-bit ZigZag
|
||||
ushort zz7 = (ushort)(h & 0x7F);
|
||||
result.Add(UnZigZag16(zz7));
|
||||
continue;
|
||||
}
|
||||
|
||||
int count = ((h >> 1) & 0x3F) + 1; // 1..64
|
||||
int width = (h & 0x01) + 1; // 1..2
|
||||
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
uint raw = ReadLE(src, ref pos, width);
|
||||
if (width > 2 && (raw >> 16) != 0)
|
||||
throw new OverflowException("Decoded ZigZag value exceeds 16-bit range.");
|
||||
|
||||
ushort u = (ushort)raw;
|
||||
short val = UnZigZag16(u);
|
||||
result.Add(val);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
// ----------------- Helpers -----------------
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ushort ZigZag16(short v)
|
||||
{
|
||||
// (v << 1) ^ (v >> 15), result as unsigned 16-bit
|
||||
return (ushort)(((uint)(ushort)v << 1) ^ (uint)((int)v >> 15));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static short UnZigZag16(ushort u)
|
||||
{
|
||||
// (u >> 1) ^ -(u & 1), narrowed to 16-bit signed
|
||||
return (short)((u >> 1) ^ (ushort)-(short)(u & 1));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WriteLE(List<byte> dst, ushort value, int width)
|
||||
{
|
||||
// width is 1 or 2
|
||||
dst.Add((byte)(value & 0xFF));
|
||||
if (width == 2) dst.Add((byte)(value >> 8));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint ReadLE(ReadOnlySpan<byte> src, ref int pos, int width)
|
||||
{
|
||||
if ((uint)(pos + width) > (uint)src.Length)
|
||||
throw new ArgumentException("Buffer underflow while reading group payload.");
|
||||
|
||||
uint v = src[pos++];
|
||||
if (width == 2)
|
||||
{
|
||||
v |= (uint)src[pos++] << 8;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
129
Libraries/Esiur/Data/Gvwie/GroupInt32Codec.cs
Normal file
129
Libraries/Esiur/Data/Gvwie/GroupInt32Codec.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Collections;
|
||||
|
||||
namespace Esiur.Data.Gvwie;
|
||||
|
||||
public static class GroupInt32Codec
|
||||
{
|
||||
// ----------------- Encoder -----------------
|
||||
public static byte[] Encode(IList<int> values)
|
||||
{
|
||||
//var values = value as int[];
|
||||
|
||||
var dst = new List<byte>(values.Count * 2);
|
||||
int i = 0;
|
||||
|
||||
while (i < values.Count)
|
||||
{
|
||||
uint zz = ZigZag32(values[i]);
|
||||
|
||||
// Fast path: single byte (MSB=0) when zigzag fits in 7 bits
|
||||
if (zz <= 0x7Fu)
|
||||
{
|
||||
dst.Add((byte)zz);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Group: up to 32 items sharing a common width (1..4 bytes)
|
||||
int start = i;
|
||||
int count = 1;
|
||||
int width = WidthFromZigZag(zz);
|
||||
|
||||
while (count < 32 && (i + count) < values.Count)
|
||||
{
|
||||
uint z2 = ZigZag32(values[i + count]);
|
||||
int w2 = WidthFromZigZag(z2);
|
||||
width = Math.Max(width, w2); // widen as needed
|
||||
count++;
|
||||
}
|
||||
|
||||
// Header: 1 | (count-1)[5 bits] | (width-1)[2 bits]
|
||||
byte header = 0x80;
|
||||
header |= (byte)(((count - 1) & 0x1F) << 2);
|
||||
header |= (byte)((width - 1) & 0x03);
|
||||
dst.Add(header);
|
||||
|
||||
// Payload: 'count' zigzag values, LE, 'width' bytes each
|
||||
for (int k = 0; k < count; k++)
|
||||
WriteLE(dst, ZigZag32(values[start + k]), width);
|
||||
|
||||
i += count;
|
||||
}
|
||||
|
||||
return dst.ToArray();
|
||||
}
|
||||
|
||||
// ----------------- Decoder -----------------
|
||||
public static int[] Decode(ReadOnlySpan<byte> src)
|
||||
{
|
||||
var result = new List<int>();
|
||||
int pos = 0;
|
||||
|
||||
while (pos < src.Length)
|
||||
{
|
||||
byte h = src[pos++];
|
||||
|
||||
if ((h & 0x80) == 0)
|
||||
{
|
||||
// Fast path: 7-bit ZigZag in low bits
|
||||
uint zz7 = (uint)(h & 0x7F);
|
||||
result.Add(UnZigZag32(zz7));
|
||||
continue;
|
||||
}
|
||||
|
||||
int count = ((h >> 2) & 0x1F) + 1; // 1..32
|
||||
int width = (h & 0x03) + 1; // 1..4
|
||||
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
uint raw = (uint)ReadLE(src, ref pos, width);
|
||||
int val = UnZigZag32(raw);
|
||||
result.Add(val);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
// ----------------- Helpers -----------------
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint ZigZag32(int v) => (uint)((v << 1) ^ (v >> 31));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int UnZigZag32(uint u) => (int)((u >> 1) ^ (uint)-(int)(u & 1));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int WidthFromZigZag(uint z)
|
||||
{
|
||||
if (z <= 0xFFu) return 1;
|
||||
if (z <= 0xFFFFu) return 2;
|
||||
if (z <= 0xFFFFFFu) return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WriteLE(List<byte> dst, uint value, int width)
|
||||
{
|
||||
for (int i = 0; i < width; i++)
|
||||
dst.Add((byte)((value >> (8 * i)) & 0xFF));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong ReadLE(ReadOnlySpan<byte> src, ref int pos, int width)
|
||||
{
|
||||
if ((uint)(pos + width) > (uint)src.Length)
|
||||
throw new ArgumentException("Buffer underflow while reading group payload.");
|
||||
|
||||
ulong v = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
v |= (ulong)src[pos++] << (8 * i);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
135
Libraries/Esiur/Data/Gvwie/GroupInt64Codec.cs
Normal file
135
Libraries/Esiur/Data/Gvwie/GroupInt64Codec.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Esiur.Data.Gvwie;
|
||||
|
||||
public static class GroupInt64Codec
|
||||
{
|
||||
// ----------------- Encoder -----------------
|
||||
public static byte[] Encode(IList<long> values)
|
||||
{
|
||||
var dst = new List<byte>(values.Count * 2);
|
||||
int i = 0;
|
||||
|
||||
while (i < values.Count)
|
||||
{
|
||||
ulong zz = ZigZag64(values[i]);
|
||||
|
||||
// Fast path: 1 byte when ZigZag fits in 7 bits
|
||||
if (zz <= 0x7Ful)
|
||||
{
|
||||
dst.Add((byte)zz); // MSB = 0 implicitly
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Group path: up to 16 items sharing a common width (1..8 bytes)
|
||||
int start = i;
|
||||
int count = 1;
|
||||
int width = WidthFromZigZag(zz);
|
||||
|
||||
while (count < 16 && (i + count) < values.Count)
|
||||
{
|
||||
ulong z2 = ZigZag64(values[i + count]);
|
||||
int w2 = WidthFromZigZag(z2);
|
||||
width = Math.Max(width, w2); // widen as needed
|
||||
count++;
|
||||
}
|
||||
|
||||
// Header: 1 | (count-1)[4 bits] | (width-1)[3 bits]
|
||||
byte header = 0x80;
|
||||
header |= (byte)(((count - 1) & 0x0F) << 3);
|
||||
header |= (byte)((width - 1) & 0x07);
|
||||
dst.Add(header);
|
||||
|
||||
// Payload: 'count' ZigZag values, LE, 'width' bytes each
|
||||
for (int k = 0; k < count; k++)
|
||||
{
|
||||
ulong z = ZigZag64(values[start + k]);
|
||||
WriteLE(dst, z, width);
|
||||
}
|
||||
|
||||
i += count;
|
||||
}
|
||||
|
||||
return dst.ToArray();
|
||||
}
|
||||
|
||||
// ----------------- Decoder -----------------
|
||||
public static long[] Decode(ReadOnlySpan<byte> src)
|
||||
{
|
||||
var result = new List<long>();
|
||||
int pos = 0;
|
||||
|
||||
while (pos < src.Length)
|
||||
{
|
||||
byte h = src[pos++];
|
||||
|
||||
if ((h & 0x80) == 0)
|
||||
{
|
||||
// Fast path: 7-bit ZigZag
|
||||
ulong zz7 = (ulong)(h & 0x7F);
|
||||
result.Add(UnZigZag64(zz7));
|
||||
continue;
|
||||
}
|
||||
|
||||
int count = ((h >> 3) & 0x0F) + 1; // 1..16
|
||||
int width = (h & 0x07) + 1; // 1..8
|
||||
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
ulong raw = ReadLE(src, ref pos, width);
|
||||
long val = UnZigZag64(raw);
|
||||
result.Add(val);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
// ----------------- Helpers -----------------
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong ZigZag64(long v) => (ulong)((v << 1) ^ (v >> 63));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static long UnZigZag64(ulong u) => (long)((u >> 1) ^ (ulong)-(long)(u & 1));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int WidthFromZigZag(ulong z)
|
||||
{
|
||||
if (z <= 0xFFUL) return 1;
|
||||
if (z <= 0xFFFFUL) return 2;
|
||||
if (z <= 0xFFFFFFUL) return 3;
|
||||
if (z <= 0xFFFFFFFFUL) return 4;
|
||||
if (z <= 0xFFFFFFFFFFUL) return 5;
|
||||
if (z <= 0xFFFFFFFFFFFFUL) return 6;
|
||||
if (z <= 0xFFFFFFFFFFFFFFUL) return 7;
|
||||
return 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WriteLE(List<byte> dst, ulong value, int width)
|
||||
{
|
||||
for (int i = 0; i < width; i++)
|
||||
dst.Add((byte)((value >> (8 * i)) & 0xFF));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong ReadLE(ReadOnlySpan<byte> src, ref int pos, int width)
|
||||
{
|
||||
if ((uint)(pos + width) > (uint)src.Length)
|
||||
throw new ArgumentException("Buffer underflow while reading group payload.");
|
||||
|
||||
ulong v = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
v |= (ulong)src[pos++] << (8 * i);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
121
Libraries/Esiur/Data/Gvwie/GroupUInt16Codec.cs
Normal file
121
Libraries/Esiur/Data/Gvwie/GroupUInt16Codec.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Esiur.Data.Gvwie;
|
||||
|
||||
|
||||
public static class GroupUInt16Codec
|
||||
{
|
||||
// ----------------- Encoder -----------------
|
||||
public static byte[] Encode(IList<ushort> values)
|
||||
{
|
||||
if (values is null) throw new ArgumentNullException(nameof(values));
|
||||
|
||||
var dst = new List<byte>(values.Count * 2);
|
||||
int i = 0;
|
||||
|
||||
while (i < values.Count)
|
||||
{
|
||||
ushort v = values[i];
|
||||
|
||||
// Fast path: single byte for 0..127
|
||||
if (v <= 0x7F)
|
||||
{
|
||||
dst.Add((byte)v); // MSB=0 implicitly
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Group path: up to 16 items sharing a common width (1..2 bytes for uint16)
|
||||
int start = i;
|
||||
int count = 1;
|
||||
int width = WidthFromUnsigned(v);
|
||||
|
||||
while (count < 16 && (i + count) < values.Count)
|
||||
{
|
||||
ushort v2 = values[i + count];
|
||||
int w2 = WidthFromUnsigned(v2);
|
||||
if (w2 > width) width = w2; // widen group if needed
|
||||
count++;
|
||||
}
|
||||
|
||||
// Header: 1 | (count-1)[4b] | (width-1)[3b]
|
||||
byte header = 0x80;
|
||||
header |= (byte)(((count - 1) & 0xF) << 3);
|
||||
header |= (byte)((width - 1) & 0x7);
|
||||
dst.Add(header);
|
||||
|
||||
// Payload
|
||||
for (int k = 0; k < count; k++)
|
||||
{
|
||||
WriteLE(dst, values[start + k], width);
|
||||
}
|
||||
|
||||
i += count;
|
||||
}
|
||||
|
||||
return dst.ToArray();
|
||||
}
|
||||
|
||||
// ----------------- Decoder -----------------
|
||||
public static ushort[] Decode(ReadOnlySpan<byte> src)
|
||||
{
|
||||
var result = new List<ushort>();
|
||||
int pos = 0;
|
||||
|
||||
while (pos < src.Length)
|
||||
{
|
||||
byte h = src[pos++];
|
||||
|
||||
if ((h & 0x80) == 0)
|
||||
{
|
||||
// Fast path byte (0..127)
|
||||
result.Add(h);
|
||||
continue;
|
||||
}
|
||||
|
||||
int count = ((h >> 3) & 0xF) + 1; // 1..16
|
||||
int width = (h & 0x7) + 1; // 1..8 (expect 1..2)
|
||||
|
||||
if (width > 2)
|
||||
throw new NotSupportedException($"Width {width} bytes exceeds uint16 capacity.");
|
||||
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
uint val = (uint)ReadLE(src, ref pos, width);
|
||||
if (val > 0xFFFFu)
|
||||
throw new OverflowException("Decoded value exceeds UInt16 range.");
|
||||
result.Add((ushort)val);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
// ----------------- Helpers -----------------
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int WidthFromUnsigned(ushort v) => (v <= 0xFF) ? 1 : 2;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WriteLE(List<byte> dst, ushort value, int width)
|
||||
{
|
||||
// width is 1 or 2
|
||||
dst.Add((byte)(value & 0xFF));
|
||||
if (width == 2) dst.Add((byte)(value >> 8));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong ReadLE(ReadOnlySpan<byte> src, ref int pos, int width)
|
||||
{
|
||||
if (pos + width > src.Length)
|
||||
throw new ArgumentException("Buffer underflow while reading payload.");
|
||||
|
||||
ulong v = src[pos++]; // first byte (LSB)
|
||||
if (width == 2) v |= (ulong)src[pos++] << 8;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
125
Libraries/Esiur/Data/Gvwie/GroupUInt32Codec.cs
Normal file
125
Libraries/Esiur/Data/Gvwie/GroupUInt32Codec.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Esiur.Data.Gvwie;
|
||||
|
||||
public static class GroupUInt32Codec
|
||||
{
|
||||
// ----------------- Encoder -----------------
|
||||
public static byte[] Encode(IList<uint> values)
|
||||
{
|
||||
if (values is null) throw new ArgumentNullException(nameof(values));
|
||||
|
||||
var dst = new List<byte>(values.Count * 2);
|
||||
int i = 0;
|
||||
|
||||
while (i < values.Count)
|
||||
{
|
||||
uint v = values[i];
|
||||
|
||||
// Fast path: single byte for 0..127
|
||||
if (v <= 0x7Fu)
|
||||
{
|
||||
dst.Add((byte)v); // MSB=0 implicitly
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Group path: up to 16 items sharing a common width (1..4 bytes for uint32)
|
||||
int start = i;
|
||||
int count = 1;
|
||||
int width = WidthFromUnsigned(v);
|
||||
|
||||
while (count < 16 && (i + count) < values.Count)
|
||||
{
|
||||
uint v2 = values[i + count];
|
||||
int w2 = WidthFromUnsigned(v2);
|
||||
if (w2 > width) width = w2;
|
||||
count++;
|
||||
}
|
||||
|
||||
// Header: 1 | (count-1)[4b] | (width-1)[3b]
|
||||
byte header = 0x80;
|
||||
header |= (byte)(((count - 1) & 0xF) << 3);
|
||||
header |= (byte)((width - 1) & 0x7);
|
||||
dst.Add(header);
|
||||
|
||||
// Payload
|
||||
for (int k = 0; k < count; k++)
|
||||
{
|
||||
WriteLE(dst, values[start + k], width);
|
||||
}
|
||||
|
||||
i += count;
|
||||
}
|
||||
|
||||
return dst.ToArray();
|
||||
}
|
||||
|
||||
// ----------------- Decoder -----------------
|
||||
public static uint[] Decode(ReadOnlySpan<byte> src)
|
||||
{
|
||||
var result = new List<uint>();
|
||||
int pos = 0;
|
||||
|
||||
while (pos < src.Length)
|
||||
{
|
||||
byte h = src[pos++];
|
||||
|
||||
if ((h & 0x80) == 0)
|
||||
{
|
||||
// Fast path byte (0..127)
|
||||
result.Add(h);
|
||||
continue;
|
||||
}
|
||||
|
||||
int count = ((h >> 3) & 0xF) + 1; // 1..16
|
||||
int width = (h & 0x7) + 1; // 1..8 (we expect 1..4)
|
||||
|
||||
if (width > 4)
|
||||
throw new NotSupportedException($"Width {width} bytes exceeds uint32 capacity.");
|
||||
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
uint val = (uint)ReadLE(src, ref pos, width);
|
||||
result.Add(val);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
// ----------------- Helpers -----------------
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int WidthFromUnsigned(uint v)
|
||||
{
|
||||
if (v <= 0xFFu) return 1;
|
||||
if (v <= 0xFFFFu) return 2;
|
||||
if (v <= 0xFFFFFFu) return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WriteLE(List<byte> dst, uint value, int width)
|
||||
{
|
||||
for (int i = 0; i < width; i++)
|
||||
dst.Add((byte)((value >> (8 * i)) & 0xFF));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong ReadLE(ReadOnlySpan<byte> src, ref int pos, int width)
|
||||
{
|
||||
if (pos + width > src.Length)
|
||||
throw new ArgumentException("Buffer underflow while reading payload.");
|
||||
|
||||
ulong v = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
v |= (ulong)src[pos++] << (8 * i);
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
133
Libraries/Esiur/Data/Gvwie/GroupUInt64Codec.cs
Normal file
133
Libraries/Esiur/Data/Gvwie/GroupUInt64Codec.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Esiur.Data.Gvwie;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
public static class GroupUInt64Codec
|
||||
{
|
||||
// ----------------- Encoder -----------------
|
||||
public static byte[] Encode(IList<ulong> values)
|
||||
{
|
||||
if (values is null) throw new ArgumentNullException(nameof(values));
|
||||
|
||||
var dst = new List<byte>(values.Count * 2);
|
||||
int i = 0;
|
||||
|
||||
while (i < values.Count)
|
||||
{
|
||||
ulong v = values[i];
|
||||
|
||||
// Fast path: single byte for 0..127
|
||||
if (v <= 0x7FUL)
|
||||
{
|
||||
dst.Add((byte)v); // MSB = 0 implicitly
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Group path: up to 16 items sharing max width (1..8 bytes)
|
||||
int start = i;
|
||||
int count = 1;
|
||||
int width = WidthFromUnsigned(v);
|
||||
|
||||
while (count < 16 && (i + count) < values.Count)
|
||||
{
|
||||
ulong v2 = values[i + count];
|
||||
int w2 = WidthFromUnsigned(v2);
|
||||
if (w2 > width) width = w2;
|
||||
count++;
|
||||
}
|
||||
|
||||
// Header: 1 | (count-1)[4b] | (width-1)[3b]
|
||||
byte header = 0x80;
|
||||
header |= (byte)(((count - 1) & 0xF) << 3);
|
||||
header |= (byte)((width - 1) & 0x7);
|
||||
dst.Add(header);
|
||||
|
||||
// Payload
|
||||
for (int k = 0; k < count; k++)
|
||||
WriteLE(dst, values[start + k], width);
|
||||
|
||||
i += count;
|
||||
}
|
||||
|
||||
return dst.ToArray();
|
||||
}
|
||||
|
||||
// ----------------- Decoder -----------------
|
||||
public static ulong[] Decode(ReadOnlySpan<byte> src)
|
||||
{
|
||||
var result = new List<ulong>();
|
||||
int pos = 0;
|
||||
|
||||
while (pos < src.Length)
|
||||
{
|
||||
byte h = src[pos++];
|
||||
|
||||
if ((h & 0x80) == 0)
|
||||
{
|
||||
// Fast path byte (0..127)
|
||||
result.Add(h);
|
||||
continue;
|
||||
}
|
||||
|
||||
int count = ((h >> 3) & 0xF) + 1; // 1..16
|
||||
int width = (h & 0x7) + 1; // 1..8
|
||||
|
||||
if (width < 1 || width > 8)
|
||||
throw new NotSupportedException($"Invalid width {width} in header.");
|
||||
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
ulong val = ReadLE(src, ref pos, width);
|
||||
result.Add(val);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
// ----------------- Helpers -----------------
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int WidthFromUnsigned(ulong v)
|
||||
{
|
||||
if (v <= 0xFFUL) return 1;
|
||||
if (v <= 0xFFFFUL) return 2;
|
||||
if (v <= 0xFFFFFFUL) return 3;
|
||||
if (v <= 0xFFFFFFFFUL) return 4;
|
||||
if (v <= 0xFFFFFFFFFFUL) return 5;
|
||||
if (v <= 0xFFFFFFFFFFFFUL) return 6;
|
||||
if (v <= 0xFFFFFFFFFFFFFFUL) return 7;
|
||||
return 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WriteLE(List<byte> dst, ulong value, int width)
|
||||
{
|
||||
for (int i = 0; i < width; i++)
|
||||
dst.Add((byte)((value >> (8 * i)) & 0xFF));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong ReadLE(ReadOnlySpan<byte> src, ref int pos, int width)
|
||||
{
|
||||
if (pos + width > src.Length)
|
||||
throw new ArgumentException("Buffer underflow while reading payload.");
|
||||
|
||||
ulong v = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
v |= (ulong)src[pos++] << (8 * i);
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
20
Libraries/Esiur/Data/IDynamicResource.cs
Normal file
20
Libraries/Esiur/Data/IDynamicResource.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Esiur.Core;
|
||||
using Esiur.Data.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public interface IDynamicResource
|
||||
{
|
||||
public PropertyValue[] SerializeResource();
|
||||
public Map<byte, PropertyValue> SerializeResourceAfter(ulong age);
|
||||
|
||||
public object GetResourceProperty(byte index);
|
||||
public AsyncReply SetResourcePropertyAsync(byte index, object value);
|
||||
public void SetResourceProperty(byte index, object value);
|
||||
|
||||
public TypeDef ResourceDefinition { get; }
|
||||
}
|
||||
}
|
||||
10
Libraries/Esiur/Data/IRecord.cs
Normal file
10
Libraries/Esiur/Data/IRecord.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
public interface IRecord
|
||||
{
|
||||
|
||||
}
|
||||
13
Libraries/Esiur/Data/IUserType.cs
Normal file
13
Libraries/Esiur/Data/IUserType.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data;
|
||||
public interface IUserType
|
||||
{
|
||||
object Get();
|
||||
void Set(object value);
|
||||
|
||||
object SetAndGet(object value);
|
||||
}
|
||||
|
||||
18
Libraries/Esiur/Data/Int128.cs
Normal file
18
Libraries/Esiur/Data/Int128.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public struct Int128
|
||||
{
|
||||
public Int128( ulong lsb, ulong msb)
|
||||
{
|
||||
this.MSB = msb;
|
||||
this.LSB = lsb;
|
||||
}
|
||||
|
||||
public ulong MSB { get; set; }
|
||||
public ulong LSB { get; set; }
|
||||
}
|
||||
}
|
||||
242
Libraries/Esiur/Data/KeyList.cs
Normal file
242
Libraries/Esiur/Data/KeyList.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
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.Core;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
public class KeyList<KT, T> : IEnumerable<KeyValuePair<KT, T>>
|
||||
{
|
||||
private readonly object syncRoot = new object();
|
||||
private Dictionary<KT, T> dic;
|
||||
|
||||
public delegate void Modified(KT key, T oldValue, T newValue, KeyList<KT, T> sender);
|
||||
public delegate void Added(T value, KeyList<KT, T> sender);
|
||||
public delegate void Removed(KT key, T value, KeyList<KT, T> sender);
|
||||
public delegate void Cleared(KeyList<KT, T> sender);
|
||||
|
||||
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, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
dic.Add(key, value);
|
||||
|
||||
if (OnAdd != null)
|
||||
OnAdd(value, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<KeyValuePair<KT, T>> GetEnumerator()
|
||||
{
|
||||
return dic.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.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(this);
|
||||
}
|
||||
|
||||
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, this);
|
||||
}
|
||||
|
||||
public object Owner
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
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(object owner = null)
|
||||
{
|
||||
#if NETSTANDARD
|
||||
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
|
||||
#else
|
||||
removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
|
||||
#endif
|
||||
|
||||
this.Owner = owner;
|
||||
|
||||
if (typeof(KT) == typeof(string))
|
||||
dic = (Dictionary<KT, T>)(object)new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
|
||||
else
|
||||
dic = new Dictionary<KT, T>();
|
||||
}
|
||||
}
|
||||
264
Libraries/Esiur/Data/Map.cs
Normal file
264
Libraries/Esiur/Data/Map.cs
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Misc;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
//public class Map : IEnumerable<KeyValuePair<object, object>>
|
||||
//{
|
||||
// private Dictionary<object, object> dic = new();
|
||||
|
||||
// public IEnumerator<KeyValuePair<object, object>> GetEnumerator()
|
||||
// {
|
||||
// return dic.GetEnumerator();
|
||||
// }
|
||||
|
||||
// IEnumerator IEnumerable.GetEnumerator()
|
||||
// {
|
||||
// return dic.GetEnumerator();
|
||||
// }
|
||||
//}
|
||||
|
||||
public interface IMap
|
||||
{
|
||||
public void Add(object key, object value);
|
||||
//public void Remove(object key);
|
||||
//public void Clear();
|
||||
//public bool ContainsKey(object key);
|
||||
public object[] Serialize();
|
||||
|
||||
public IEnumerable GetKeys();
|
||||
public IEnumerable GetValues();
|
||||
}
|
||||
|
||||
public class Map<KT, VT> : Dictionary<KT, VT>, IMap // IEnumerable<KeyValuePair<KT, VT>>
|
||||
{
|
||||
|
||||
//private Dictionary<KT, VT> dic = new Dictionary<KT, VT>();
|
||||
private object syncRoot = new object();
|
||||
|
||||
//public static implicit operator Map<KT, VT>(Dictionary<KT, VT> dictionary)
|
||||
// => new Map<KT, VT>() { dic = dictionary };
|
||||
|
||||
//public static implicit operator Dictionary<KT, VT>(Map<KT, VT> map)
|
||||
// => map.ToDictionary();
|
||||
|
||||
//Change map types
|
||||
public Map<NewKeyType, NewValueType> Select<NewKeyType, NewValueType>
|
||||
(Func<KeyValuePair<KT, VT>, KeyValuePair<NewKeyType, NewValueType>> selector)
|
||||
{
|
||||
var rt = new Map<NewKeyType, NewValueType>();
|
||||
foreach (var kv in this)
|
||||
{
|
||||
var nt = selector(kv);
|
||||
rt.Add(nt.Key, nt.Value);
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
//public bool ContainsKey(KT key)
|
||||
//{
|
||||
// return dic.ContainsKey(key);
|
||||
//}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var rt = "";
|
||||
foreach (var kv in this)
|
||||
rt += kv.Key + ": " + kv.Value.ToString() + " \r\n";
|
||||
|
||||
return rt.TrimEnd('\r', '\n');
|
||||
}
|
||||
|
||||
//public Map(Map<KT,VT> source)
|
||||
//{
|
||||
// dic = source.dic;
|
||||
//}
|
||||
public Map()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//public static Map<KT,VT> FromMap(Map<KT,VT> source, Type destinationType)
|
||||
//{
|
||||
// var rt = Activator.CreateInstance(destinationType) as Map<KT, VT>;
|
||||
// rt.dic = source.dic;
|
||||
// return rt;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
// public static explicit operator Map<string, object>(ExpandoObject obj) => FromDynamic(obj);
|
||||
|
||||
public static Map<string, object> FromDynamic(ExpandoObject obj)
|
||||
{
|
||||
var rt = new Map<string, object>();
|
||||
foreach (var kv in obj)
|
||||
rt[kv.Key] = kv.Value;
|
||||
return rt;
|
||||
}
|
||||
|
||||
//public static Map<KT, VT> FromDictionary(Dictionary<KT, VT> dictionary)
|
||||
// => new Map<KT, VT>() { dic = dictionary };
|
||||
|
||||
//public Dictionary<KT, VT> ToDictionary() => dic;
|
||||
|
||||
public static Map<string,object> FromObject(object obj)
|
||||
{
|
||||
var type = obj.GetType();
|
||||
|
||||
var st = new Map<string,object>();
|
||||
|
||||
var pi = type.GetTypeInfo().GetProperties().Where(x => x.CanRead);
|
||||
foreach (var p in pi)
|
||||
st[p.Name] = p.GetValue(obj);
|
||||
|
||||
var fi = type.GetTypeInfo().GetFields().Where(x => x.IsPublic);
|
||||
foreach (var f in fi)
|
||||
st[f.Name] = f.GetValue(obj);
|
||||
|
||||
return st;
|
||||
|
||||
|
||||
// if (obj is Structure)
|
||||
// return obj as Structure;
|
||||
// else //if (Codec.IsAnonymous(type))
|
||||
// {
|
||||
// var st = new Structure();
|
||||
|
||||
// var pi = type.GetTypeInfo().GetProperties().Where(x => x.CanRead);
|
||||
// foreach (var p in pi)
|
||||
// st[p.Name] = p.GetValue(obj);
|
||||
|
||||
// var fi = type.GetTypeInfo().GetFields().Where(x => x.IsPublic);
|
||||
// foreach (var f in fi)
|
||||
// st[f.Name] = f.GetValue(obj);
|
||||
|
||||
// return st;
|
||||
// }
|
||||
// //else
|
||||
// // return null;
|
||||
}
|
||||
|
||||
//public IEnumerator<KeyValuePair<KT, VT>> GetEnumerator()
|
||||
//{
|
||||
// return dic.GetEnumerator();
|
||||
//}
|
||||
|
||||
//IEnumerator IEnumerable.GetEnumerator()
|
||||
//{
|
||||
// return dic.GetEnumerator();
|
||||
//}
|
||||
|
||||
//public int Length
|
||||
//{
|
||||
// get { return dic.Count; }
|
||||
//}
|
||||
|
||||
//public KeyValuePair<KT, VT> At(int index)
|
||||
//{
|
||||
// return dic.ElementAt(index);
|
||||
//}
|
||||
|
||||
public object SyncRoot
|
||||
{
|
||||
get { return syncRoot; }
|
||||
}
|
||||
|
||||
//public KT[] GetKeys() => dic.Keys.ToArray();
|
||||
|
||||
//public void Add(KT key, VT value)
|
||||
//{
|
||||
// if (dic.ContainsKey(key))
|
||||
// dic[key] = value;
|
||||
// else
|
||||
// dic.Add(key, value);
|
||||
// }
|
||||
|
||||
public void Add(object key, object value)
|
||||
{
|
||||
base.Add((KT)key, (VT)value);
|
||||
}
|
||||
|
||||
//public void Remove(object key)
|
||||
//{
|
||||
// Remove((KT)key);
|
||||
//}
|
||||
|
||||
//public void Clear()
|
||||
//{
|
||||
// dic.Clear();
|
||||
//}
|
||||
|
||||
//public bool ContainsKey(object key)
|
||||
//{
|
||||
// return ContainsKey((KT)key);
|
||||
//}
|
||||
|
||||
public object[] Serialize()
|
||||
{
|
||||
var rt = new List<object>();
|
||||
foreach(var kv in this)
|
||||
{
|
||||
rt.Add(kv.Key);
|
||||
rt.Add(kv.Value);
|
||||
}
|
||||
|
||||
return rt.ToArray();
|
||||
}
|
||||
|
||||
public IEnumerable GetKeys() => Keys.ToArray();
|
||||
public IEnumerable GetValues() => Values.ToArray();
|
||||
|
||||
//public VT this[KT index]
|
||||
//{
|
||||
// get
|
||||
// {
|
||||
// if (dic.ContainsKey(index))
|
||||
// return dic[index];
|
||||
// else
|
||||
// return default;
|
||||
// }
|
||||
// set
|
||||
// {
|
||||
// if (dic.ContainsKey(index))
|
||||
// dic[index] = value;
|
||||
// else
|
||||
// dic.Add(index, value);
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
35
Libraries/Esiur/Data/NotModified.cs
Normal file
35
Libraries/Esiur/Data/NotModified.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Data;
|
||||
public class NotModified
|
||||
{
|
||||
public static NotModified Default { get; set; } = new NotModified();
|
||||
}
|
||||
64
Libraries/Esiur/Data/NullabilityInfo.cs
Normal file
64
Libraries/Esiur/Data/NullabilityInfo.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that represents nullability info
|
||||
/// </summary>
|
||||
public sealed class NullabilityInfo
|
||||
{
|
||||
internal NullabilityInfo(Type type, NullabilityState readState, NullabilityState writeState,
|
||||
NullabilityInfo? elementType, NullabilityInfo[] typeArguments)
|
||||
{
|
||||
Type = type;
|
||||
ReadState = readState;
|
||||
WriteState = writeState;
|
||||
ElementType = elementType;
|
||||
GenericTypeArguments = typeArguments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="System.Type" /> of the member or generic parameter
|
||||
/// to which this NullabilityInfo belongs
|
||||
/// </summary>
|
||||
public Type Type { get; }
|
||||
/// <summary>
|
||||
/// The nullability read state of the member
|
||||
/// </summary>
|
||||
public NullabilityState ReadState { get; internal set; }
|
||||
/// <summary>
|
||||
/// The nullability write state of the member
|
||||
/// </summary>
|
||||
public NullabilityState WriteState { get; internal set; }
|
||||
/// <summary>
|
||||
/// If the member type is an array, gives the <see cref="NullabilityInfo" /> of the elements of the array, null otherwise
|
||||
/// </summary>
|
||||
public NullabilityInfo? ElementType { get; }
|
||||
/// <summary>
|
||||
/// If the member type is a generic type, gives the array of <see cref="NullabilityInfo" /> for each type parameter
|
||||
/// </summary>
|
||||
public NullabilityInfo[] GenericTypeArguments { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An enum that represents nullability state
|
||||
/// </summary>
|
||||
public enum NullabilityState
|
||||
{
|
||||
/// <summary>
|
||||
/// Nullability context not enabled (oblivious)
|
||||
/// </summary>
|
||||
Unknown,
|
||||
/// <summary>
|
||||
/// Non nullable value or reference type
|
||||
/// </summary>
|
||||
NotNull,
|
||||
/// <summary>
|
||||
/// Nullable value or reference type
|
||||
/// </summary>
|
||||
Nullable
|
||||
}
|
||||
}
|
||||
714
Libraries/Esiur/Data/NullabilityInfoContext.cs
Normal file
714
Libraries/Esiur/Data/NullabilityInfoContext.cs
Normal file
@@ -0,0 +1,714 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using Esiur.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides APIs for populating nullability information/context from reflection members:
|
||||
/// <see cref="ParameterInfo"/>, <see cref="FieldInfo"/>, <see cref="PropertyInfo"/> and <see cref="EventInfo"/>.
|
||||
/// </summary>
|
||||
public sealed class NullabilityInfoContext
|
||||
{
|
||||
private const string CompilerServicesNameSpace = "System.Runtime.CompilerServices";
|
||||
private readonly Dictionary<Module, NotAnnotatedStatus> _publicOnlyModules = new();
|
||||
private readonly Dictionary<MemberInfo, NullabilityState> _context = new();
|
||||
|
||||
internal static bool IsSupported { get; } =
|
||||
AppContext.TryGetSwitch("System.Reflection.NullabilityInfoContext.IsSupported", out bool isSupported) ? isSupported : true;
|
||||
|
||||
[Flags]
|
||||
private enum NotAnnotatedStatus
|
||||
{
|
||||
None = 0x0, // no restriction, all members annotated
|
||||
Private = 0x1, // private members not annotated
|
||||
Internal = 0x2 // internal members not annotated
|
||||
}
|
||||
|
||||
private NullabilityState? GetNullableContext(MemberInfo? memberInfo)
|
||||
{
|
||||
while (memberInfo != null)
|
||||
{
|
||||
if (_context.TryGetValue(memberInfo, out NullabilityState state))
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
foreach (CustomAttributeData attribute in memberInfo.GetCustomAttributesData())
|
||||
{
|
||||
if (attribute.AttributeType.Name == "NullableContextAttribute" &&
|
||||
attribute.AttributeType.Namespace == CompilerServicesNameSpace &&
|
||||
attribute.ConstructorArguments.Count == 1)
|
||||
{
|
||||
state = TranslateByte(attribute.ConstructorArguments[0].Value);
|
||||
_context.Add(memberInfo, state);
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
memberInfo = memberInfo.DeclaringType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates <see cref="NullabilityInfo" /> for the given <see cref="ParameterInfo" />.
|
||||
/// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's
|
||||
/// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state.
|
||||
/// </summary>
|
||||
/// <param name="parameterInfo">The parameter which nullability info gets populated</param>
|
||||
/// <exception cref="ArgumentNullException">If the parameterInfo parameter is null</exception>
|
||||
/// <returns><see cref="NullabilityInfo" /></returns>
|
||||
public NullabilityInfo Create(ParameterInfo parameterInfo)
|
||||
{
|
||||
if (parameterInfo == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
EnsureIsSupported();
|
||||
|
||||
IList<CustomAttributeData> attributes = parameterInfo.GetCustomAttributesData();
|
||||
NullableAttributeStateParser parser = parameterInfo.Member is MethodBase method && IsPrivateOrInternalMethodAndAnnotationDisabled(method)
|
||||
? NullableAttributeStateParser.Unknown
|
||||
: CreateParser(attributes);
|
||||
NullabilityInfo nullability = GetNullabilityInfo(parameterInfo.Member, parameterInfo.ParameterType, parser);
|
||||
|
||||
if (nullability.ReadState != NullabilityState.Unknown)
|
||||
{
|
||||
CheckParameterMetadataType(parameterInfo, nullability);
|
||||
}
|
||||
|
||||
CheckNullabilityAttributes(nullability, attributes);
|
||||
return nullability;
|
||||
}
|
||||
|
||||
private void CheckParameterMetadataType(ParameterInfo parameter, NullabilityInfo nullability)
|
||||
{
|
||||
if (parameter.Member is MethodInfo method)
|
||||
{
|
||||
MethodInfo metaMethod = GetMethodMetadataDefinition(method);
|
||||
ParameterInfo? metaParameter = null;
|
||||
if (string.IsNullOrEmpty(parameter.Name))
|
||||
{
|
||||
metaParameter = metaMethod.ReturnParameter;
|
||||
}
|
||||
else
|
||||
{
|
||||
ParameterInfo[] parameters = metaMethod.GetParameters();
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
if (parameter.Position == i &&
|
||||
parameter.Name == parameters[i].Name)
|
||||
{
|
||||
metaParameter = parameters[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (metaParameter != null)
|
||||
{
|
||||
CheckGenericParameters(nullability, metaMethod, metaParameter.ParameterType, parameter.Member.ReflectedType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodInfo GetMethodMetadataDefinition(MethodInfo method)
|
||||
{
|
||||
if (method.IsGenericMethod && !method.IsGenericMethodDefinition)
|
||||
{
|
||||
method = method.GetGenericMethodDefinition();
|
||||
}
|
||||
|
||||
return (MethodInfo)GetMemberMetadataDefinition(method);
|
||||
}
|
||||
|
||||
private static void CheckNullabilityAttributes(NullabilityInfo nullability, IList<CustomAttributeData> attributes)
|
||||
{
|
||||
var codeAnalysisReadState = NullabilityState.Unknown;
|
||||
var codeAnalysisWriteState = NullabilityState.Unknown;
|
||||
|
||||
foreach (CustomAttributeData attribute in attributes)
|
||||
{
|
||||
if (attribute.AttributeType.Namespace == "System.Diagnostics.CodeAnalysis")
|
||||
{
|
||||
if (attribute.AttributeType.Name == "NotNullAttribute")
|
||||
{
|
||||
codeAnalysisReadState = NullabilityState.NotNull;
|
||||
}
|
||||
else if ((attribute.AttributeType.Name == "MaybeNullAttribute" ||
|
||||
attribute.AttributeType.Name == "MaybeNullWhenAttribute") &&
|
||||
codeAnalysisReadState == NullabilityState.Unknown &&
|
||||
!IsValueTypeOrValueTypeByRef(nullability.Type))
|
||||
{
|
||||
codeAnalysisReadState = NullabilityState.Nullable;
|
||||
}
|
||||
else if (attribute.AttributeType.Name == "DisallowNullAttribute")
|
||||
{
|
||||
codeAnalysisWriteState = NullabilityState.NotNull;
|
||||
}
|
||||
else if (attribute.AttributeType.Name == "AllowNullAttribute" &&
|
||||
codeAnalysisWriteState == NullabilityState.Unknown &&
|
||||
!IsValueTypeOrValueTypeByRef(nullability.Type))
|
||||
{
|
||||
codeAnalysisWriteState = NullabilityState.Nullable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (codeAnalysisReadState != NullabilityState.Unknown)
|
||||
{
|
||||
nullability.ReadState = codeAnalysisReadState;
|
||||
}
|
||||
if (codeAnalysisWriteState != NullabilityState.Unknown)
|
||||
{
|
||||
nullability.WriteState = codeAnalysisWriteState;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates <see cref="NullabilityInfo" /> for the given <see cref="PropertyInfo" />.
|
||||
/// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's
|
||||
/// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state.
|
||||
/// </summary>
|
||||
/// <param name="propertyInfo">The parameter which nullability info gets populated</param>
|
||||
/// <exception cref="ArgumentNullException">If the propertyInfo parameter is null</exception>
|
||||
/// <returns><see cref="NullabilityInfo" /></returns>
|
||||
public NullabilityInfo Create(PropertyInfo propertyInfo)
|
||||
{
|
||||
if (propertyInfo == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
EnsureIsSupported();
|
||||
|
||||
MethodInfo? getter = propertyInfo.GetGetMethod(true);
|
||||
MethodInfo? setter = propertyInfo.GetSetMethod(true);
|
||||
bool annotationsDisabled = (getter == null || IsPrivateOrInternalMethodAndAnnotationDisabled(getter))
|
||||
&& (setter == null || IsPrivateOrInternalMethodAndAnnotationDisabled(setter));
|
||||
NullableAttributeStateParser parser = annotationsDisabled ? NullableAttributeStateParser.Unknown : CreateParser(propertyInfo.GetCustomAttributesData());
|
||||
NullabilityInfo nullability = GetNullabilityInfo(propertyInfo, propertyInfo.PropertyType, parser);
|
||||
|
||||
if (getter != null)
|
||||
{
|
||||
CheckNullabilityAttributes(nullability, getter.ReturnParameter.GetCustomAttributesData());
|
||||
}
|
||||
else
|
||||
{
|
||||
nullability.ReadState = NullabilityState.Unknown;
|
||||
}
|
||||
|
||||
if (setter != null)
|
||||
{
|
||||
CheckNullabilityAttributes(nullability, setter.GetParameters().Last().GetCustomAttributesData());
|
||||
}
|
||||
else
|
||||
{
|
||||
nullability.WriteState = NullabilityState.Unknown;
|
||||
}
|
||||
|
||||
return nullability;
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Populates <see cref="NullabilityInfo" /> for the given <see cref="MethodInfo" />.
|
||||
///// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's
|
||||
///// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state.
|
||||
///// </summary>
|
||||
///// <param name="propertyInfo">The parameter which nullability info gets populated</param>
|
||||
///// <exception cref="ArgumentNullException">If the propertyInfo parameter is null</exception>
|
||||
///// <returns><see cref="NullabilityInfo" /></returns>
|
||||
//public NullabilityInfo Create(MethodInfo memberInfo)
|
||||
//{
|
||||
// if (memberInfo == null)
|
||||
// throw new ArgumentNullException();
|
||||
|
||||
// EnsureIsSupported();
|
||||
|
||||
|
||||
// bool annotationsDisabled = IsPrivateOrInternalMethodAndAnnotationDisabled(memberInfo);
|
||||
|
||||
// NullableAttributeStateParser parser = annotationsDisabled ? NullableAttributeStateParser.Unknown : CreateParser(memberInfo.GetCustomAttributesData());
|
||||
// NullabilityInfo nullability = GetNullabilityInfo(memberInfo, memberInfo.ReturnType, parser);
|
||||
|
||||
|
||||
// CheckNullabilityAttributes(nullability, memberInfo.ReturnParameter.GetCustomAttributesData());
|
||||
|
||||
// return nullability;
|
||||
//}
|
||||
|
||||
private bool IsPrivateOrInternalMethodAndAnnotationDisabled(MethodBase method)
|
||||
{
|
||||
if ((method.IsPrivate || method.IsFamilyAndAssembly || method.IsAssembly) &&
|
||||
IsPublicOnly(method.IsPrivate, method.IsFamilyAndAssembly, method.IsAssembly, method.Module))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates <see cref="NullabilityInfo" /> for the given <see cref="EventInfo" />.
|
||||
/// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's
|
||||
/// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state.
|
||||
/// </summary>
|
||||
/// <param name="eventInfo">The parameter which nullability info gets populated</param>
|
||||
/// <exception cref="ArgumentNullException">If the eventInfo parameter is null</exception>
|
||||
/// <returns><see cref="NullabilityInfo" /></returns>
|
||||
public NullabilityInfo Create(EventInfo eventInfo)
|
||||
{
|
||||
if (eventInfo == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
EnsureIsSupported();
|
||||
|
||||
return GetNullabilityInfo(eventInfo, eventInfo.EventHandlerType!, CreateParser(eventInfo.GetCustomAttributesData()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates <see cref="NullabilityInfo" /> for the given <see cref="FieldInfo" />
|
||||
/// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's
|
||||
/// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state.
|
||||
/// </summary>
|
||||
/// <param name="fieldInfo">The parameter which nullability info gets populated</param>
|
||||
/// <exception cref="ArgumentNullException">If the fieldInfo parameter is null</exception>
|
||||
/// <returns><see cref="NullabilityInfo" /></returns>
|
||||
public NullabilityInfo Create(FieldInfo fieldInfo)
|
||||
{
|
||||
if (fieldInfo == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
EnsureIsSupported();
|
||||
|
||||
IList<CustomAttributeData> attributes = fieldInfo.GetCustomAttributesData();
|
||||
NullableAttributeStateParser parser = IsPrivateOrInternalFieldAndAnnotationDisabled(fieldInfo) ? NullableAttributeStateParser.Unknown : CreateParser(attributes);
|
||||
NullabilityInfo nullability = GetNullabilityInfo(fieldInfo, fieldInfo.FieldType, parser);
|
||||
CheckNullabilityAttributes(nullability, attributes);
|
||||
return nullability;
|
||||
}
|
||||
|
||||
private static void EnsureIsSupported()
|
||||
{
|
||||
if (!IsSupported)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsPrivateOrInternalFieldAndAnnotationDisabled(FieldInfo fieldInfo)
|
||||
{
|
||||
if ((fieldInfo.IsPrivate || fieldInfo.IsFamilyAndAssembly || fieldInfo.IsAssembly) &&
|
||||
IsPublicOnly(fieldInfo.IsPrivate, fieldInfo.IsFamilyAndAssembly, fieldInfo.IsAssembly, fieldInfo.Module))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsPublicOnly(bool isPrivate, bool isFamilyAndAssembly, bool isAssembly, Module module)
|
||||
{
|
||||
if (!_publicOnlyModules.TryGetValue(module, out NotAnnotatedStatus value))
|
||||
{
|
||||
value = PopulateAnnotationInfo(module.GetCustomAttributesData());
|
||||
_publicOnlyModules.Add(module, value);
|
||||
}
|
||||
|
||||
if (value == NotAnnotatedStatus.None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((isPrivate || isFamilyAndAssembly) && value.HasFlag(NotAnnotatedStatus.Private) ||
|
||||
isAssembly && value.HasFlag(NotAnnotatedStatus.Internal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static NotAnnotatedStatus PopulateAnnotationInfo(IList<CustomAttributeData> customAttributes)
|
||||
{
|
||||
foreach (CustomAttributeData attribute in customAttributes)
|
||||
{
|
||||
if (attribute.AttributeType.Name == "NullablePublicOnlyAttribute" &&
|
||||
attribute.AttributeType.Namespace == CompilerServicesNameSpace &&
|
||||
attribute.ConstructorArguments.Count == 1)
|
||||
{
|
||||
if (attribute.ConstructorArguments[0].Value is bool boolValue && boolValue)
|
||||
{
|
||||
return NotAnnotatedStatus.Internal | NotAnnotatedStatus.Private;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NotAnnotatedStatus.Private;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NotAnnotatedStatus.None;
|
||||
}
|
||||
|
||||
private NullabilityInfo GetNullabilityInfo(MemberInfo memberInfo, Type type, NullableAttributeStateParser parser)
|
||||
{
|
||||
int index = 0;
|
||||
NullabilityInfo nullability = GetNullabilityInfo(memberInfo, type, parser, ref index);
|
||||
|
||||
if (nullability.ReadState != NullabilityState.Unknown)
|
||||
{
|
||||
TryLoadGenericMetaTypeNullability(memberInfo, nullability);
|
||||
}
|
||||
|
||||
return nullability;
|
||||
}
|
||||
|
||||
private NullabilityInfo GetNullabilityInfo(MemberInfo memberInfo, Type type, NullableAttributeStateParser parser, ref int index)
|
||||
{
|
||||
NullabilityState state = NullabilityState.Unknown;
|
||||
NullabilityInfo? elementState = null;
|
||||
NullabilityInfo[] genericArgumentsState = Array.Empty<NullabilityInfo>();
|
||||
Type underlyingType = type;
|
||||
|
||||
if (underlyingType.IsByRef || underlyingType.IsPointer)
|
||||
{
|
||||
underlyingType = underlyingType.GetElementType()!;
|
||||
}
|
||||
|
||||
if (underlyingType.IsValueType)
|
||||
{
|
||||
if (Nullable.GetUnderlyingType(underlyingType) is { } nullableUnderlyingType)
|
||||
{
|
||||
underlyingType = nullableUnderlyingType;
|
||||
state = NullabilityState.Nullable;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = NullabilityState.NotNull;
|
||||
}
|
||||
|
||||
if (underlyingType.IsGenericType)
|
||||
{
|
||||
++index;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!parser.ParseNullableState(index++, ref state)
|
||||
&& GetNullableContext(memberInfo) is { } contextState)
|
||||
{
|
||||
state = contextState;
|
||||
}
|
||||
|
||||
if (underlyingType.IsArray)
|
||||
{
|
||||
elementState = GetNullabilityInfo(memberInfo, underlyingType.GetElementType()!, parser, ref index);
|
||||
}
|
||||
}
|
||||
|
||||
if (underlyingType.IsGenericType)
|
||||
{
|
||||
Type[] genericArguments = underlyingType.GetGenericArguments();
|
||||
genericArgumentsState = new NullabilityInfo[genericArguments.Length];
|
||||
|
||||
for (int i = 0; i < genericArguments.Length; i++)
|
||||
{
|
||||
genericArgumentsState[i] = GetNullabilityInfo(memberInfo, genericArguments[i], parser, ref index);
|
||||
}
|
||||
}
|
||||
|
||||
return new NullabilityInfo(type, state, state, elementState, genericArgumentsState);
|
||||
}
|
||||
|
||||
private static NullableAttributeStateParser CreateParser(IList<CustomAttributeData> customAttributes)
|
||||
{
|
||||
foreach (CustomAttributeData attribute in customAttributes)
|
||||
{
|
||||
if (attribute.AttributeType.Name == "NullableAttribute" &&
|
||||
attribute.AttributeType.Namespace == CompilerServicesNameSpace &&
|
||||
attribute.ConstructorArguments.Count == 1)
|
||||
{
|
||||
return new NullableAttributeStateParser(attribute.ConstructorArguments[0].Value);
|
||||
}
|
||||
}
|
||||
|
||||
return new NullableAttributeStateParser(null);
|
||||
}
|
||||
|
||||
private void TryLoadGenericMetaTypeNullability(MemberInfo memberInfo, NullabilityInfo nullability)
|
||||
{
|
||||
MemberInfo? metaMember = GetMemberMetadataDefinition(memberInfo);
|
||||
Type? metaType = null;
|
||||
if (metaMember is FieldInfo field)
|
||||
{
|
||||
metaType = field.FieldType;
|
||||
}
|
||||
else if (metaMember is PropertyInfo property)
|
||||
{
|
||||
metaType = GetPropertyMetaType(property);
|
||||
}
|
||||
|
||||
if (metaType != null)
|
||||
{
|
||||
CheckGenericParameters(nullability, metaMember!, metaType, memberInfo.ReflectedType);
|
||||
}
|
||||
}
|
||||
|
||||
private static MemberInfo GetMemberMetadataDefinition(MemberInfo member)
|
||||
{
|
||||
Type? type = member.DeclaringType;
|
||||
if ((type != null) && type.IsGenericType && !type.IsGenericTypeDef)
|
||||
{
|
||||
return GetMemberWithSameMetadataDefinitionAs(type.GetGenericTypeDef(), member);
|
||||
}
|
||||
|
||||
return member;
|
||||
}
|
||||
|
||||
static bool HasSameMetadataDefinitionAs(MemberInfo mi, MemberInfo other) { throw new NotImplementedException(); }
|
||||
|
||||
static MemberInfo GetMemberWithSameMetadataDefinitionAs(Type type, MemberInfo member)
|
||||
{
|
||||
if (member == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
const BindingFlags all = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
|
||||
foreach (MemberInfo myMemberInfo in type.GetMembers(all))
|
||||
{
|
||||
if (HasSameMetadataDefinitionAs(myMemberInfo, member))
|
||||
{
|
||||
return myMemberInfo;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
private static Type GetPropertyMetaType(PropertyInfo property)
|
||||
{
|
||||
if (property.GetGetMethod(true) is MethodInfo method)
|
||||
{
|
||||
return method.ReturnType;
|
||||
}
|
||||
|
||||
return property.GetSetMethod(true)!.GetParameters()[0].ParameterType;
|
||||
}
|
||||
|
||||
private void CheckGenericParameters(NullabilityInfo nullability, MemberInfo metaMember, Type metaType, Type? reflectedType)
|
||||
{
|
||||
if (metaType.IsGenericParameter)
|
||||
{
|
||||
if (nullability.ReadState == NullabilityState.NotNull)
|
||||
{
|
||||
TryUpdateGenericParameterNullability(nullability, metaType, reflectedType);
|
||||
}
|
||||
}
|
||||
else if (metaType.ContainsGenericParameters)
|
||||
{
|
||||
if (nullability.GenericTypeArguments.Length > 0)
|
||||
{
|
||||
Type[] genericArguments = metaType.GetGenericArguments();
|
||||
|
||||
for (int i = 0; i < genericArguments.Length; i++)
|
||||
{
|
||||
CheckGenericParameters(nullability.GenericTypeArguments[i], metaMember, genericArguments[i], reflectedType);
|
||||
}
|
||||
}
|
||||
else if (nullability.ElementType is { } elementNullability && metaType.IsArray)
|
||||
{
|
||||
CheckGenericParameters(elementNullability, metaMember, metaType.GetElementType()!, reflectedType);
|
||||
}
|
||||
// We could also follow this branch for metaType.IsPointer, but since pointers must be unmanaged this
|
||||
// will be a no-op regardless
|
||||
else if (metaType.IsByRef)
|
||||
{
|
||||
CheckGenericParameters(nullability, metaMember, metaType.GetElementType()!, reflectedType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryUpdateGenericParameterNullability(NullabilityInfo nullability, Type genericParameter, Type? reflectedType)
|
||||
{
|
||||
Debug.Assert(genericParameter.IsGenericParameter);
|
||||
|
||||
if (reflectedType is not null
|
||||
&& !IsGenericMethodParameter(genericParameter)
|
||||
&& TryUpdateGenericTypeParameterNullabilityFromReflectedType(nullability, genericParameter, reflectedType, reflectedType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsValueTypeOrValueTypeByRef(nullability.Type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var state = NullabilityState.Unknown;
|
||||
if (CreateParser(genericParameter.GetCustomAttributesData()).ParseNullableState(0, ref state))
|
||||
{
|
||||
nullability.ReadState = state;
|
||||
nullability.WriteState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GetNullableContext(genericParameter) is { } contextState)
|
||||
{
|
||||
nullability.ReadState = contextState;
|
||||
nullability.WriteState = contextState;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsGenericMethodParameter(Type genericParameter) => genericParameter.IsGenericParameter && genericParameter.DeclaringMethod != null;
|
||||
|
||||
private bool TryUpdateGenericTypeParameterNullabilityFromReflectedType(NullabilityInfo nullability, Type genericParameter, Type context, Type reflectedType)
|
||||
{
|
||||
Debug.Assert(genericParameter.IsGenericParameter && !IsGenericMethodParameter(genericParameter));
|
||||
|
||||
Type contextTypeDef = context.IsGenericType && !context.IsGenericTypeDef ? context.GetGenericTypeDef() : context;
|
||||
if (genericParameter.DeclaringType == contextTypeDef)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Type? baseType = contextTypeDef.BaseType;
|
||||
if (baseType is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!baseType.IsGenericType
|
||||
|| (baseType.IsGenericTypeDef ? baseType : baseType.GetGenericTypeDef()) != genericParameter.DeclaringType)
|
||||
{
|
||||
return TryUpdateGenericTypeParameterNullabilityFromReflectedType(nullability, genericParameter, baseType, reflectedType);
|
||||
}
|
||||
|
||||
Type[] genericArguments = baseType.GetGenericArguments();
|
||||
Type genericArgument = genericArguments[genericParameter.GenericParameterPosition];
|
||||
if (genericArgument.IsGenericParameter)
|
||||
{
|
||||
return TryUpdateGenericParameterNullability(nullability, genericArgument, reflectedType);
|
||||
}
|
||||
|
||||
NullableAttributeStateParser parser = CreateParser(contextTypeDef.GetCustomAttributesData());
|
||||
int nullabilityStateIndex = 1; // start at 1 since index 0 is the type itself
|
||||
for (int i = 0; i < genericParameter.GenericParameterPosition; i++)
|
||||
{
|
||||
nullabilityStateIndex += CountNullabilityStates(genericArguments[i]);
|
||||
}
|
||||
return TryPopulateNullabilityInfo(nullability, parser, ref nullabilityStateIndex);
|
||||
|
||||
static int CountNullabilityStates(Type type)
|
||||
{
|
||||
Type underlyingType = Nullable.GetUnderlyingType(type) ?? type;
|
||||
if (underlyingType.IsGenericType)
|
||||
{
|
||||
int count = 1;
|
||||
foreach (Type genericArgument in underlyingType.GetGenericArguments())
|
||||
{
|
||||
count += CountNullabilityStates(genericArgument);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
if (underlyingType.HasElementType)
|
||||
{
|
||||
return (underlyingType.IsArray ? 1 : 0) + CountNullabilityStates(underlyingType.GetElementType()!);
|
||||
}
|
||||
|
||||
return type.IsValueType ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryPopulateNullabilityInfo(NullabilityInfo nullability, NullableAttributeStateParser parser, ref int index)
|
||||
{
|
||||
bool isValueType = IsValueTypeOrValueTypeByRef(nullability.Type);
|
||||
if (!isValueType)
|
||||
{
|
||||
var state = NullabilityState.Unknown;
|
||||
if (!parser.ParseNullableState(index, ref state))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
nullability.ReadState = state;
|
||||
nullability.WriteState = state;
|
||||
}
|
||||
|
||||
if (!isValueType || (Nullable.GetUnderlyingType(nullability.Type) ?? nullability.Type).IsGenericType)
|
||||
{
|
||||
index++;
|
||||
}
|
||||
|
||||
if (nullability.GenericTypeArguments.Length > 0)
|
||||
{
|
||||
foreach (NullabilityInfo genericTypeArgumentNullability in nullability.GenericTypeArguments)
|
||||
{
|
||||
TryPopulateNullabilityInfo(genericTypeArgumentNullability, parser, ref index);
|
||||
}
|
||||
}
|
||||
else if (nullability.ElementType is { } elementTypeNullability)
|
||||
{
|
||||
TryPopulateNullabilityInfo(elementTypeNullability, parser, ref index);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static NullabilityState TranslateByte(object? value)
|
||||
{
|
||||
return value is byte b ? TranslateByte(b) : NullabilityState.Unknown;
|
||||
}
|
||||
|
||||
private static NullabilityState TranslateByte(byte b) =>
|
||||
b switch
|
||||
{
|
||||
1 => NullabilityState.NotNull,
|
||||
2 => NullabilityState.Nullable,
|
||||
_ => NullabilityState.Unknown
|
||||
};
|
||||
|
||||
private static bool IsValueTypeOrValueTypeByRef(Type type) =>
|
||||
type.IsValueType || ((type.IsByRef || type.IsPointer) && type.GetElementType()!.IsValueType);
|
||||
|
||||
private readonly struct NullableAttributeStateParser
|
||||
{
|
||||
private static readonly object UnknownByte = (byte)0;
|
||||
|
||||
private readonly object? _nullableAttributeArgument;
|
||||
|
||||
public NullableAttributeStateParser(object? nullableAttributeArgument)
|
||||
{
|
||||
this._nullableAttributeArgument = nullableAttributeArgument;
|
||||
}
|
||||
|
||||
public static NullableAttributeStateParser Unknown => new(UnknownByte);
|
||||
|
||||
public bool ParseNullableState(int index, ref NullabilityState state)
|
||||
{
|
||||
switch (this._nullableAttributeArgument)
|
||||
{
|
||||
case byte b:
|
||||
state = TranslateByte(b);
|
||||
return true;
|
||||
case ReadOnlyCollection<CustomAttributeTypedArgument> args
|
||||
when index < args.Count && args[index].Value is byte elementB:
|
||||
state = TranslateByte(elementB);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Libraries/Esiur/Data/NullableAttribute.cs
Normal file
23
Libraries/Esiur/Data/NullableAttribute.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, Inherited = false)]
|
||||
public sealed class NullableAttribute : Attribute
|
||||
{
|
||||
/// <summary>Flags specifying metadata related to nullable reference types.</summary>
|
||||
public readonly byte[] NullableFlags;
|
||||
|
||||
/// <summary>Initializes the attribute.</summary>
|
||||
/// <param name="value">The flags value.</param>
|
||||
public NullableAttribute(byte value)
|
||||
{
|
||||
NullableFlags = new[] { value };
|
||||
}
|
||||
|
||||
/// <summary>Initializes the attribute.</summary>
|
||||
/// <param name="value">The flags value.</param>
|
||||
public NullableAttribute(byte[] value)
|
||||
{
|
||||
NullableFlags = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Libraries/Esiur/Data/NullableContextAttribute.cs
Normal file
16
Libraries/Esiur/Data/NullableContextAttribute.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
|
||||
public sealed class NullableContextAttribute : Attribute
|
||||
{
|
||||
/// <summary>Flag specifying metadata related to nullable reference types.</summary>
|
||||
public readonly byte Flag;
|
||||
|
||||
/// <summary>Initializes the attribute.</summary>
|
||||
/// <param name="value">The flag value.</param>
|
||||
public NullableContextAttribute(byte value)
|
||||
{
|
||||
Flag = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
149
Libraries/Esiur/Data/ParsedTdu.cs
Normal file
149
Libraries/Esiur/Data/ParsedTdu.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public struct ParsedTdu
|
||||
{
|
||||
public TduIdentifier Identifier;
|
||||
public int Index;
|
||||
public TduClass Class;
|
||||
public uint Offset;
|
||||
public ulong ContentLength;
|
||||
public byte[] Data;
|
||||
public byte Exponent;
|
||||
public ulong TotalLength;
|
||||
public byte[] Metadata;
|
||||
public uint Ends;
|
||||
|
||||
public static ParsedTdu Parse(byte[] data, uint offset, uint ends)
|
||||
{
|
||||
|
||||
var h = data[offset++];
|
||||
|
||||
var cls = (TduClass)(h >> 6);
|
||||
|
||||
if (cls == TduClass.Fixed)
|
||||
{
|
||||
var exp = (h & 0x38) >> 3;
|
||||
|
||||
if (exp == 0)
|
||||
return new ParsedTdu()
|
||||
{
|
||||
Identifier = (TduIdentifier)h,
|
||||
Data = data,
|
||||
Offset = offset,
|
||||
Class = cls,
|
||||
Exponent = (byte)exp,
|
||||
Index = (byte)h & 0x7,
|
||||
ContentLength = 0,
|
||||
TotalLength = 1,
|
||||
Ends = ends
|
||||
};
|
||||
|
||||
ulong cl = (ulong)(1 << (exp - 1));
|
||||
|
||||
if (ends - offset < cl)
|
||||
return new ParsedTdu()
|
||||
{
|
||||
Class = TduClass.Invalid,
|
||||
TotalLength = (cl - (ends - offset))
|
||||
};
|
||||
|
||||
//offset += (uint)cl;
|
||||
|
||||
return new ParsedTdu()
|
||||
{
|
||||
Identifier = (TduIdentifier)h,
|
||||
Data = data,
|
||||
Offset = offset,
|
||||
Class = cls,
|
||||
ContentLength = cl,
|
||||
TotalLength = 1 + cl,
|
||||
Exponent = (byte)exp,
|
||||
Index = (byte)h & 0x7,
|
||||
Ends = ends
|
||||
};
|
||||
}
|
||||
else if (cls == TduClass.Typed)
|
||||
{
|
||||
ulong cll = (ulong)(h >> 3) & 0x7;
|
||||
|
||||
if (ends - offset < cll)
|
||||
return new ParsedTdu()
|
||||
{
|
||||
Class = TduClass.Invalid,
|
||||
TotalLength = (cll - (ends - offset))
|
||||
};
|
||||
|
||||
ulong cl = 0;
|
||||
|
||||
for (uint i = 0; i < cll; i++)
|
||||
cl = cl << 8 | data[offset++];
|
||||
|
||||
if (ends - offset < cl)
|
||||
return new ParsedTdu()
|
||||
{
|
||||
TotalLength = (cl - (ends - offset)),
|
||||
Class = TduClass.Invalid,
|
||||
};
|
||||
|
||||
var metaData = DC.Clip(data, offset + 1, data[offset]);
|
||||
offset += data[offset] + (uint)1;
|
||||
|
||||
|
||||
return new ParsedTdu()
|
||||
{
|
||||
Identifier = (TduIdentifier)(h & 0xC7),
|
||||
Data = data,
|
||||
Offset = offset,
|
||||
Class = cls,
|
||||
ContentLength = cl - 1 - (uint)metaData.Length,
|
||||
TotalLength = 1 + cl + cll,
|
||||
Index = (byte)h & 0x7,
|
||||
Metadata = metaData,
|
||||
Ends = ends
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong cll = (ulong)(h >> 3) & 0x7;
|
||||
|
||||
if (ends - offset < cll)
|
||||
return new ParsedTdu()
|
||||
{
|
||||
Class = TduClass.Invalid,
|
||||
TotalLength = (cll - (ends - offset))
|
||||
};
|
||||
|
||||
ulong cl = 0;
|
||||
|
||||
for (uint i = 0; i < cll; i++)
|
||||
cl = cl << 8 | data[offset++];
|
||||
|
||||
if (ends - offset < cl)
|
||||
return new ParsedTdu()
|
||||
{
|
||||
Class = TduClass.Invalid,
|
||||
TotalLength = (cl - (ends - offset))
|
||||
};
|
||||
|
||||
|
||||
return
|
||||
new ParsedTdu()
|
||||
{
|
||||
Identifier = (TduIdentifier)(h & 0xC7),
|
||||
Data = data,
|
||||
Offset = offset,
|
||||
Class = cls,
|
||||
ContentLength = cl,
|
||||
TotalLength = 1 + cl + cll,
|
||||
Index = (byte)h & 0x7,
|
||||
Ends = ends
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
34
Libraries/Esiur/Data/PropertyValue.cs
Normal file
34
Libraries/Esiur/Data/PropertyValue.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
public class PropertyValue
|
||||
{
|
||||
/// <summary>
|
||||
/// Get or set the value.
|
||||
/// </summary>
|
||||
public object Value { get; set; }
|
||||
/// <summary>
|
||||
/// Get or set date of modification or occurrence.
|
||||
/// </summary>
|
||||
public DateTime? Date { get; set; }
|
||||
/// <summary>
|
||||
/// Get or set property age.
|
||||
/// </summary>
|
||||
public ulong? Age { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of PropertyValue.
|
||||
/// </summary>
|
||||
/// <param name="value">Value.</param>
|
||||
/// <param name="age">Age.</param>
|
||||
/// <param name="date">Date.</param>
|
||||
public PropertyValue(object value, ulong? age, DateTime? date)
|
||||
{
|
||||
Value = value;
|
||||
Age = age;
|
||||
Date = date;
|
||||
}
|
||||
}
|
||||
9
Libraries/Esiur/Data/Record.cs
Normal file
9
Libraries/Esiur/Data/Record.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data;
|
||||
public class Record : KeyList<string, object>, IRecord
|
||||
{
|
||||
|
||||
}
|
||||
12
Libraries/Esiur/Data/ResourceArrayType.cs
Normal file
12
Libraries/Esiur/Data/ResourceArrayType.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
public enum ResourceArrayType
|
||||
{
|
||||
Dynamic = 0x0,
|
||||
Static = 0x10,
|
||||
Wrapper = 0x20,
|
||||
}
|
||||
18
Libraries/Esiur/Data/ResourceId.cs
Normal file
18
Libraries/Esiur/Data/ResourceId.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public struct ResourceId
|
||||
{
|
||||
public bool Local;
|
||||
public uint Id;
|
||||
|
||||
public ResourceId(bool local, uint id)
|
||||
{
|
||||
this.Id = id;
|
||||
this.Local = local;
|
||||
}
|
||||
}
|
||||
}
|
||||
98
Libraries/Esiur/Data/ResourceJsonConverter.cs
Normal file
98
Libraries/Esiur/Data/ResourceJsonConverter.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2021 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using Esiur.Resource;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
class ResourceJsonConverter : JsonConverter<IResource>
|
||||
{
|
||||
public override IResource Read(
|
||||
ref Utf8JsonReader reader,
|
||||
Type typeToConvert,
|
||||
JsonSerializerOptions options)
|
||||
{
|
||||
return (IResource)JsonSerializer.Deserialize(ref reader, typeof(IResource), options);
|
||||
}
|
||||
|
||||
|
||||
public override void Write(
|
||||
Utf8JsonWriter writer,
|
||||
IResource resource,
|
||||
JsonSerializerOptions options)
|
||||
{
|
||||
|
||||
writer.WriteStartObject();
|
||||
|
||||
foreach (var pt in resource.Instance.Definition.Properties)
|
||||
{
|
||||
var rt = pt.PropertyInfo.GetValue(resource, null);
|
||||
if (rt != null && rt.GetType().IsGenericType)
|
||||
continue;
|
||||
|
||||
writer.WritePropertyName(options.PropertyNamingPolicy?.ConvertName(pt.Name) ?? pt.Name);
|
||||
|
||||
if (rt is IResource)
|
||||
JsonSerializer.Serialize(writer, (IResource)rt, options);
|
||||
else
|
||||
JsonSerializer.Serialize(writer, rt, options);
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DoubleJsonConverter : JsonConverter<double>
|
||||
{
|
||||
public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.String && reader.GetString() == "NaN")
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
|
||||
return reader.GetDouble(); // JsonException thrown if reader.TokenType != JsonTokenType.Number
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
{
|
||||
writer.WriteStringValue("NaN");
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteNumberValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
Libraries/Esiur/Data/ResourceLink.cs
Normal file
27
Libraries/Esiur/Data/ResourceLink.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public class ResourceLink
|
||||
{
|
||||
readonly string value;
|
||||
|
||||
public ResourceLink(string value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
public static implicit operator string(ResourceLink d)
|
||||
{
|
||||
return d.value;
|
||||
}
|
||||
public static implicit operator ResourceLink(string d)
|
||||
{
|
||||
return new ResourceLink(d);
|
||||
}
|
||||
|
||||
public override string ToString() => value;
|
||||
|
||||
}
|
||||
}
|
||||
27
Libraries/Esiur/Data/ResourceLinkGeneric.cs
Normal file
27
Libraries/Esiur/Data/ResourceLinkGeneric.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public class ResourceLink<T>
|
||||
{
|
||||
readonly string value;
|
||||
|
||||
public ResourceLink(string value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
public static implicit operator string(ResourceLink<T> d)
|
||||
{
|
||||
return d.value;
|
||||
}
|
||||
public static implicit operator ResourceLink<T>(string d)
|
||||
{
|
||||
return new ResourceLink<T>(d);
|
||||
}
|
||||
|
||||
public override string ToString() => value;
|
||||
|
||||
}
|
||||
}
|
||||
273
Libraries/Esiur/Data/ResourceList.cs
Normal file
273
Libraries/Esiur/Data/ResourceList.cs
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2020 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using Esiur.Core;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
public class ResourceList<T, ST> : IEnumerable<T>, ICollection, ICollection<T>
|
||||
{
|
||||
|
||||
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, int index, 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;
|
||||
|
||||
public void Sort()
|
||||
{
|
||||
list.Sort();
|
||||
}
|
||||
|
||||
public void Sort(IComparer<T> comparer)
|
||||
{
|
||||
list.Sort(comparer);
|
||||
}
|
||||
|
||||
public void Sort(Comparison<T> comparison)
|
||||
{
|
||||
list.Sort(comparison);
|
||||
}
|
||||
|
||||
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 ResourceList(ST state)
|
||||
{
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of AutoList
|
||||
/// </summary>
|
||||
/// <param name="values">Populate the list with items</param>
|
||||
/// <returns></returns>
|
||||
public ResourceList(ST state, T[] values)
|
||||
{
|
||||
this.state = state;
|
||||
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];
|
||||
|
||||
lock (syncRoot)
|
||||
list[index] = value;
|
||||
|
||||
OnModified?.Invoke(state, index, oldValue, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add item to the list
|
||||
/// </summary>
|
||||
public void Add(T value)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the list
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
|
||||
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)
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
lock (syncRoot)
|
||||
{
|
||||
index = list.IndexOf(value);
|
||||
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
list.RemoveAt(index);
|
||||
|
||||
|
||||
}
|
||||
|
||||
OnRemoved?.Invoke(state, index, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of items in the list
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get { return list.Count; }
|
||||
}
|
||||
|
||||
public bool IsSynchronized => (list as ICollection).IsSynchronized;
|
||||
|
||||
public bool IsReadOnly => throw new NotImplementedException();
|
||||
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<T>)list).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<T>)list).GetEnumerator();
|
||||
}
|
||||
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
(list as ICollection).CopyTo(array, index);
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
list.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
bool ICollection<T>.Remove(T item)
|
||||
{
|
||||
return list.Remove(item);
|
||||
}
|
||||
}
|
||||
560
Libraries/Esiur/Data/RuntimeCaster.cs
Normal file
560
Libraries/Esiur/Data/RuntimeCaster.cs
Normal file
@@ -0,0 +1,560 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq.Expressions;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
// --- Policies & Options (NET Standard 2.0 compatible) --------------------
|
||||
|
||||
public enum NaNInfinityPolicy
|
||||
{
|
||||
Throw,
|
||||
NullIfNullable,
|
||||
CoerceZero
|
||||
}
|
||||
|
||||
public sealed class RuntimeCastOptions
|
||||
{
|
||||
// Reusable default to avoid per-call allocations
|
||||
public static readonly RuntimeCastOptions Default = new RuntimeCastOptions();
|
||||
|
||||
public bool CheckedNumeric { get; set; } = true;
|
||||
|
||||
public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture;
|
||||
|
||||
public DateTimeStyles DateTimeStyles { get; set; } =
|
||||
DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal;
|
||||
|
||||
public bool EnumIgnoreCase { get; set; } = true;
|
||||
public bool EnumMustBeDefined { get; set; } = false;
|
||||
|
||||
public NaNInfinityPolicy NaNInfinityPolicy { get; set; } = NaNInfinityPolicy.Throw;
|
||||
}
|
||||
|
||||
// --- Core Caster ----------------------------------------------------------
|
||||
|
||||
public static class RuntimeCaster
|
||||
{
|
||||
// (fromType, toType) -> converter(value, options)
|
||||
private static readonly ConcurrentDictionary<(Type from, Type to),
|
||||
Func<object?, RuntimeCastOptions, object?>> _cache =
|
||||
new ConcurrentDictionary<(Type, Type), Func<object?, RuntimeCastOptions, object?>>();
|
||||
|
||||
// Numeric-only compiled converters
|
||||
private static readonly ConcurrentDictionary<(Type from, Type to, bool @checked),
|
||||
Func<object, object>> _numericCache =
|
||||
new ConcurrentDictionary<(Type, Type, bool), Func<object, object>>();
|
||||
|
||||
// Per-element converters for collections
|
||||
private static readonly ConcurrentDictionary<(Type from, Type to),
|
||||
Func<object?, RuntimeCastOptions, object?>> _elemConvCache =
|
||||
new ConcurrentDictionary<(Type, Type), Func<object?, RuntimeCastOptions, object?>>();
|
||||
|
||||
// --------- Zero-allocation convenience overloads ---------
|
||||
public static object? Cast(object? value, Type toType)
|
||||
=> Cast(value, toType, RuntimeCastOptions.Default);
|
||||
|
||||
public static object? CastSequence(object? value, Type toType)
|
||||
=> CastSequence(value, toType, RuntimeCastOptions.Default);
|
||||
|
||||
// --------- Main API (options accepted if you want different policies) ---------
|
||||
public static object? Cast(object? value, Type toType, RuntimeCastOptions? options)
|
||||
{
|
||||
if (toType == null) throw new ArgumentNullException(nameof(toType));
|
||||
var opts = options ?? RuntimeCastOptions.Default;
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
if (IsNonNullableValueType(toType))
|
||||
throw new InvalidCastException("Cannot cast null to non-nullable " + toType + ".");
|
||||
return null;
|
||||
}
|
||||
|
||||
var fromType = value.GetType();
|
||||
if (toType.IsAssignableFrom(fromType)) return value;
|
||||
|
||||
var fn = _cache.GetOrAdd((fromType, toType), k => BuildConverter(k.from, k.to));
|
||||
return fn(value, opts);
|
||||
}
|
||||
|
||||
public static object? CastSequence(object? value, Type toType, RuntimeCastOptions? options)
|
||||
{
|
||||
var opts = options ?? RuntimeCastOptions.Default;
|
||||
if (value == null) return null;
|
||||
|
||||
var fromType = value.GetType();
|
||||
var toUnderlying = Nullable.GetUnderlyingType(toType) ?? toType;
|
||||
|
||||
if (!IsSupportedSeqType(fromType) || !IsSupportedSeqType(toUnderlying))
|
||||
throw new InvalidCastException("Only 1D arrays and List<T> are supported. " + fromType + " → " + toType);
|
||||
|
||||
var fromElem = GetElementType(fromType)!;
|
||||
var toElem = GetElementType(toUnderlying)!;
|
||||
|
||||
// Fast path: same element type
|
||||
if (fromElem == toElem)
|
||||
{
|
||||
if (fromType.IsArray && IsListType(toUnderlying))
|
||||
return ArrayToListDirect((Array)value, toUnderlying);
|
||||
|
||||
if (IsListType(fromType) && toUnderlying.IsArray)
|
||||
return ListToArrayDirect((IList)value, toElem);
|
||||
|
||||
if (fromType.IsArray && toUnderlying.IsArray)
|
||||
return ArrayToArrayDirect((Array)value, toElem);
|
||||
|
||||
if (IsListType(fromType) && IsListType(toUnderlying))
|
||||
return ListToListDirect((IList)value, toUnderlying, toElem);
|
||||
}
|
||||
|
||||
// General path with per-element converter
|
||||
var elemConv = _elemConvCache.GetOrAdd((fromElem, toElem),
|
||||
k => (object? elem, RuntimeCastOptions o) =>
|
||||
{
|
||||
if (elem == null) return null;
|
||||
return Cast(elem, toElem, o);
|
||||
});
|
||||
|
||||
if (fromType.IsArray && IsListType(toUnderlying))
|
||||
return ArrayToListConverted((Array)value, toUnderlying, toElem, elemConv, opts);
|
||||
|
||||
if (IsListType(fromType) && toUnderlying.IsArray)
|
||||
return ListToArrayConverted((IList)value, toElem, elemConv, opts);
|
||||
|
||||
if (fromType.IsArray && toUnderlying.IsArray)
|
||||
return ArrayToArrayConverted((Array)value, toElem, elemConv, opts);
|
||||
|
||||
if (IsListType(fromType) && IsListType(toUnderlying))
|
||||
return ListToListConverted((IList)value, toUnderlying, toElem, elemConv, opts);
|
||||
|
||||
throw new InvalidCastException("Unsupported sequence cast " + fromType + " → " + toType + ".");
|
||||
}
|
||||
|
||||
// ------------------------ Builder ------------------------
|
||||
|
||||
private static Func<object?, RuntimeCastOptions, object?> BuildConverter(Type fromType, Type toType)
|
||||
{
|
||||
return (value, opts) => ConvertCore(value!, fromType, toType, opts);
|
||||
}
|
||||
|
||||
// ------------------------ Core Routing ------------------------
|
||||
|
||||
private static object? ConvertCore(object value, Type fromType, Type toType, RuntimeCastOptions opts)
|
||||
{
|
||||
var toUnderlying = Nullable.GetUnderlyingType(toType) ?? toType;
|
||||
var fromUnderlying = Nullable.GetUnderlyingType(fromType) ?? fromType;
|
||||
|
||||
// Collections early
|
||||
{
|
||||
bool handled;
|
||||
var coll = ConvertCollectionsIfAny(value, fromType, toType, opts, out handled);
|
||||
if (handled) return coll;
|
||||
}
|
||||
|
||||
// Enum
|
||||
if (toUnderlying.IsEnum)
|
||||
return ConvertToEnum(value, fromUnderlying, toType, toUnderlying, opts);
|
||||
|
||||
// Guid
|
||||
if (toUnderlying == typeof(Guid))
|
||||
return ConvertToGuid(value, fromUnderlying, toType);
|
||||
|
||||
// Date/Time
|
||||
if (toUnderlying == typeof(DateTime))
|
||||
return ConvertToDateTime(value, fromUnderlying, toType, opts);
|
||||
|
||||
if (toUnderlying == typeof(DateTimeOffset))
|
||||
return ConvertToDateTimeOffset(value, fromUnderlying, toType, opts);
|
||||
|
||||
// float/double -> decimal with policy
|
||||
if (toUnderlying == typeof(decimal) &&
|
||||
(fromUnderlying == typeof(float) || fromUnderlying == typeof(double)))
|
||||
{
|
||||
bool useNull;
|
||||
var dec = ConvertFloatDoubleToDecimal(value, fromUnderlying, opts, out useNull);
|
||||
if (toType != toUnderlying) // Nullable<decimal>
|
||||
return useNull ? null : (decimal?)dec;
|
||||
if (useNull) throw new OverflowException("NaN/Infinity cannot be converted to decimal.");
|
||||
return dec;
|
||||
}
|
||||
|
||||
// Numeric -> Numeric
|
||||
if (IsNumeric(fromUnderlying) && IsNumeric(toUnderlying))
|
||||
{
|
||||
var nc = _numericCache.GetOrAdd((fromUnderlying, toUnderlying, opts.CheckedNumeric),
|
||||
k => BuildNumericConverter(k.from, k.to, k.@checked));
|
||||
var result = nc(value);
|
||||
if (toType != toUnderlying) return BoxNullable(result, toUnderlying);
|
||||
return result;
|
||||
}
|
||||
|
||||
// To string
|
||||
if (toUnderlying == typeof(string))
|
||||
return value != null ? value.ToString() : null;
|
||||
|
||||
// TypeConverter(target)
|
||||
var tc = System.ComponentModel.TypeDescriptor.GetConverter(toUnderlying);
|
||||
if (tc.CanConvertFrom(fromUnderlying))
|
||||
{
|
||||
var r = tc.ConvertFrom(null, opts.Culture, value);
|
||||
if (toType != toUnderlying) return BoxNullable(r!, toUnderlying);
|
||||
return r!;
|
||||
}
|
||||
|
||||
// TypeConverter(source)
|
||||
var tc2 = System.ComponentModel.TypeDescriptor.GetConverter(fromUnderlying);
|
||||
if (tc2.CanConvertTo(toUnderlying))
|
||||
{
|
||||
var r = tc2.ConvertTo(null, opts.Culture, value, toUnderlying);
|
||||
if (toType != toUnderlying) return BoxNullable(r!, toUnderlying);
|
||||
return r!;
|
||||
}
|
||||
|
||||
// Convert.ChangeType fallback
|
||||
try
|
||||
{
|
||||
var r = Convert.ChangeType(value, toUnderlying, opts.Culture);
|
||||
if (toType != toUnderlying) return BoxNullable(r!, toUnderlying);
|
||||
return r!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (toUnderlying.IsInstanceOfType(value))
|
||||
{
|
||||
if (toType != toUnderlying) return BoxNullable(value, toUnderlying);
|
||||
return value;
|
||||
}
|
||||
throw new InvalidCastException("Cannot cast " + fromType + " to " + toType + ".");
|
||||
}
|
||||
}
|
||||
|
||||
private static object? ConvertCollectionsIfAny(object value, Type fromType, Type toType, RuntimeCastOptions opts, out bool handled)
|
||||
{
|
||||
handled = false;
|
||||
var toUnderlying = Nullable.GetUnderlyingType(toType) ?? toType;
|
||||
|
||||
if (!IsSupportedSeqType(fromType) || !IsSupportedSeqType(toUnderlying))
|
||||
return value;
|
||||
|
||||
handled = true;
|
||||
return CastSequence(value, toUnderlying, opts);
|
||||
}
|
||||
|
||||
// ------------------------ Numeric Helpers ------------------------
|
||||
|
||||
private static Func<object, object> BuildNumericConverter(Type from, Type to, bool @checked)
|
||||
{
|
||||
var p = Expression.Parameter(typeof(object), "v");
|
||||
Expression val = from.IsValueType ? (Expression)Expression.Unbox(p, from)
|
||||
: (Expression)Expression.Convert(p, from);
|
||||
|
||||
Expression body;
|
||||
try
|
||||
{
|
||||
body = @checked ? (Expression)Expression.ConvertChecked(val, to)
|
||||
: (Expression)Expression.Convert(val, to);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
throw new InvalidCastException("Numeric conversion not supported: " + from + " -> " + to);
|
||||
}
|
||||
|
||||
Expression boxed = to.IsValueType ? (Expression)Expression.Convert(body, typeof(object)) : body;
|
||||
return Expression.Lambda<Func<object, object>>(boxed, p).Compile();
|
||||
}
|
||||
|
||||
private static bool IsNumeric(Type t)
|
||||
{
|
||||
t = Nullable.GetUnderlyingType(t) ?? t;
|
||||
return t == typeof(byte) || t == typeof(sbyte) ||
|
||||
t == typeof(short) || t == typeof(ushort) ||
|
||||
t == typeof(int) || t == typeof(uint) ||
|
||||
t == typeof(long) || t == typeof(ulong) ||
|
||||
t == typeof(float) || t == typeof(double) ||
|
||||
t == typeof(decimal);
|
||||
}
|
||||
|
||||
private static bool IsNonNullableValueType(Type t)
|
||||
{
|
||||
return t.IsValueType && Nullable.GetUnderlyingType(t) == null;
|
||||
}
|
||||
|
||||
private static object BoxNullable(object value, Type underlying)
|
||||
{
|
||||
var nt = typeof(Nullable<>).MakeGenericType(underlying);
|
||||
var ctor = nt.GetConstructor(new[] { underlying });
|
||||
return ctor!.Invoke(new[] { value });
|
||||
}
|
||||
|
||||
// ------------------------ NaN/∞ to decimal ------------------------
|
||||
|
||||
private static decimal ConvertFloatDoubleToDecimal(object value, Type fromUnderlying, RuntimeCastOptions opts, out bool useNull)
|
||||
{
|
||||
useNull = false;
|
||||
|
||||
if (fromUnderlying == typeof(float))
|
||||
{
|
||||
var f = (float)value;
|
||||
if (float.IsNaN(f) || float.IsInfinity(f))
|
||||
{
|
||||
if (opts.NaNInfinityPolicy == NaNInfinityPolicy.NullIfNullable) { useNull = true; return 0m; }
|
||||
if (opts.NaNInfinityPolicy == NaNInfinityPolicy.CoerceZero) return 0m;
|
||||
throw new OverflowException("Cannot convert NaN/Infinity to decimal.");
|
||||
}
|
||||
return opts.CheckedNumeric ? checked((decimal)f) : (decimal)f;
|
||||
}
|
||||
else
|
||||
{
|
||||
var d = (double)value;
|
||||
if (double.IsNaN(d) || double.IsInfinity(d))
|
||||
{
|
||||
if (opts.NaNInfinityPolicy == NaNInfinityPolicy.NullIfNullable) { useNull = true; return 0m; }
|
||||
if (opts.NaNInfinityPolicy == NaNInfinityPolicy.CoerceZero) return 0m;
|
||||
throw new OverflowException("Cannot convert NaN/Infinity to decimal.");
|
||||
}
|
||||
return opts.CheckedNumeric ? checked((decimal)d) : (decimal)d;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------ Enum ------------------------
|
||||
// Note: .NET Standard 2.0 lacks non-generic TryParse(Type, …, ignoreCase).
|
||||
// We use Enum.Parse(Type, string, bool ignoreCase) with try/catch.
|
||||
|
||||
private static object? ConvertToEnum(object value, Type fromUnderlying, Type toType, Type enumType, RuntimeCastOptions opts)
|
||||
{
|
||||
bool wrapNullable = toType != enumType;
|
||||
|
||||
if (fromUnderlying == typeof(string))
|
||||
{
|
||||
object parsed;
|
||||
try
|
||||
{
|
||||
parsed = Enum.Parse(enumType, (string)value, opts.EnumIgnoreCase);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
throw new InvalidCastException("Cannot parse '" + value + "' to " + enumType.Name + ".");
|
||||
}
|
||||
|
||||
if (opts.EnumMustBeDefined && !Enum.IsDefined(enumType, parsed))
|
||||
throw new InvalidCastException("Value '" + value + "' is not a defined member of " + enumType.Name + ".");
|
||||
|
||||
return wrapNullable ? BoxNullable(parsed, enumType) : parsed;
|
||||
}
|
||||
|
||||
if (IsNumeric(fromUnderlying))
|
||||
{
|
||||
var et = Enum.GetUnderlyingType(enumType);
|
||||
var numConv = _numericCache.GetOrAdd((fromUnderlying, et, true),
|
||||
k => BuildNumericConverter(k.from, k.to, k.@checked));
|
||||
var integral = numConv(value);
|
||||
|
||||
var enumObj = Enum.ToObject(enumType, integral);
|
||||
if (opts.EnumMustBeDefined && !Enum.IsDefined(enumType, enumObj))
|
||||
throw new InvalidCastException("Numeric value " + integral + " is not a defined member of " + enumType.Name + ".");
|
||||
|
||||
return wrapNullable ? BoxNullable(enumObj, enumType) : enumObj;
|
||||
}
|
||||
|
||||
throw new InvalidCastException("Cannot cast " + fromUnderlying + " to enum " + enumType.Name + ".");
|
||||
}
|
||||
|
||||
// ------------------------ Guid ------------------------
|
||||
|
||||
private static object? ConvertToGuid(object value, Type fromUnderlying, Type toType)
|
||||
{
|
||||
bool wrapNullable = toType != typeof(Guid);
|
||||
|
||||
if (fromUnderlying == typeof(string))
|
||||
{
|
||||
Guid g;
|
||||
if (!Guid.TryParse((string)value, out g))
|
||||
throw new InvalidCastException("Cannot parse '" + value + "' to Guid.");
|
||||
return wrapNullable ? (Guid?)g : g;
|
||||
}
|
||||
|
||||
if (fromUnderlying == typeof(byte[]))
|
||||
{
|
||||
var bytes = (byte[])value;
|
||||
if (bytes.Length != 16)
|
||||
throw new InvalidCastException("Guid requires a 16-byte array.");
|
||||
var g = new Guid(bytes);
|
||||
return wrapNullable ? (Guid?)g : g;
|
||||
}
|
||||
|
||||
throw new InvalidCastException("Cannot cast " + fromUnderlying + " to Guid.");
|
||||
}
|
||||
|
||||
// ------------------------ DateTime / DateTimeOffset ------------------------
|
||||
|
||||
private static object? ConvertToDateTime(object value, Type fromUnderlying, Type toType, RuntimeCastOptions opts)
|
||||
{
|
||||
bool wrapNullable = toType != typeof(DateTime);
|
||||
|
||||
if (fromUnderlying == typeof(string))
|
||||
{
|
||||
DateTime dt;
|
||||
if (!DateTime.TryParse((string)value, opts.Culture, opts.DateTimeStyles, out dt))
|
||||
throw new InvalidCastException("Cannot parse '" + value + "' to DateTime.");
|
||||
return wrapNullable ? (DateTime?)dt : dt;
|
||||
}
|
||||
|
||||
if (fromUnderlying == typeof(long))
|
||||
{
|
||||
var dt = new DateTime((long)value, DateTimeKind.Unspecified);
|
||||
return wrapNullable ? (DateTime?)dt : dt;
|
||||
}
|
||||
|
||||
if (fromUnderlying == typeof(double))
|
||||
{
|
||||
var d = (double)value;
|
||||
if (double.IsNaN(d) || double.IsInfinity(d))
|
||||
throw new InvalidCastException("Cannot convert NaN/Infinity to DateTime.");
|
||||
var dt = DateTime.FromOADate(d);
|
||||
return wrapNullable ? (DateTime?)dt : dt;
|
||||
}
|
||||
|
||||
throw new InvalidCastException("Cannot cast " + fromUnderlying + " to DateTime.");
|
||||
}
|
||||
|
||||
private static object? ConvertToDateTimeOffset(object value, Type fromUnderlying, Type toType, RuntimeCastOptions opts)
|
||||
{
|
||||
bool wrapNullable = toType != typeof(DateTimeOffset);
|
||||
|
||||
if (fromUnderlying == typeof(string))
|
||||
{
|
||||
DateTimeOffset dto;
|
||||
if (!DateTimeOffset.TryParse((string)value, opts.Culture, opts.DateTimeStyles, out dto))
|
||||
throw new InvalidCastException("Cannot parse '" + value + "' to DateTimeOffset.");
|
||||
return wrapNullable ? (DateTimeOffset?)dto : dto;
|
||||
}
|
||||
|
||||
if (fromUnderlying == typeof(long))
|
||||
{
|
||||
var dto = new DateTimeOffset(new DateTime((long)value, DateTimeKind.Unspecified));
|
||||
return wrapNullable ? (DateTimeOffset?)dto : dto;
|
||||
}
|
||||
|
||||
if (fromUnderlying == typeof(double))
|
||||
{
|
||||
var d = (double)value;
|
||||
if (double.IsNaN(d) || double.IsInfinity(d))
|
||||
throw new InvalidCastException("Cannot convert NaN/Infinity to DateTimeOffset.");
|
||||
var dt = DateTime.FromOADate(d);
|
||||
var dto = new DateTimeOffset(dt);
|
||||
return wrapNullable ? (DateTimeOffset?)dto : dto;
|
||||
}
|
||||
|
||||
throw new InvalidCastException("Cannot cast " + fromUnderlying + " to DateTimeOffset.");
|
||||
}
|
||||
|
||||
// ------------------------ Collections (arrays/lists) ------------------------
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsListType(Type t)
|
||||
{
|
||||
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsSupportedSeqType(Type t)
|
||||
{
|
||||
return (t.IsArray && t.GetArrayRank() == 1) || IsListType(t);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Type? GetElementType(Type t)
|
||||
{
|
||||
if (t.IsArray) return t.GetElementType();
|
||||
if (IsListType(t)) return t.GetGenericArguments()[0];
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fast-path (same element types)
|
||||
private static object ArrayToListDirect(Array src, Type listTarget)
|
||||
{
|
||||
var list = (IList)Activator.CreateInstance(listTarget, src.Length)!;
|
||||
foreach (var e in src) list.Add(e);
|
||||
return list;
|
||||
}
|
||||
|
||||
private static object ListToArrayDirect(IList src, Type elemType)
|
||||
{
|
||||
var arr = Array.CreateInstance(elemType, src.Count);
|
||||
for (int i = 0; i < src.Count; i++) arr.SetValue(src[i], i);
|
||||
return arr;
|
||||
}
|
||||
|
||||
private static object ArrayToArrayDirect(Array src, Type elemType)
|
||||
{
|
||||
var dst = Array.CreateInstance(elemType, src.Length);
|
||||
Array.Copy(src, dst, src.Length);
|
||||
return dst;
|
||||
}
|
||||
|
||||
private static object ListToListDirect(IList src, Type listTarget, Type elemType)
|
||||
{
|
||||
var list = (IList)Activator.CreateInstance(listTarget, src.Count)!;
|
||||
for (int i = 0; i < src.Count; i++) list.Add(src[i]);
|
||||
return list;
|
||||
}
|
||||
|
||||
// Converted element paths
|
||||
private static object ArrayToListConverted(
|
||||
Array src, Type listTarget, Type toElem,
|
||||
Func<object?, RuntimeCastOptions, object?> elemConv, RuntimeCastOptions opts)
|
||||
{
|
||||
var list = (IList)Activator.CreateInstance(listTarget, src.Length)!;
|
||||
for (int i = 0; i < src.Length; i++)
|
||||
{
|
||||
var v = src.GetValue(i);
|
||||
list.Add(elemConv(v, opts));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static object ListToArrayConverted(
|
||||
IList src, Type toElem,
|
||||
Func<object?, RuntimeCastOptions, object?> elemConv, RuntimeCastOptions opts)
|
||||
{
|
||||
var arr = Array.CreateInstance(toElem, src.Count);
|
||||
for (int i = 0; i < src.Count; i++)
|
||||
{
|
||||
var v = src[i];
|
||||
arr.SetValue(elemConv(v, opts), i);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
private static object ArrayToArrayConverted(
|
||||
Array src, Type toElem,
|
||||
Func<object?, RuntimeCastOptions, object?> elemConv, RuntimeCastOptions opts)
|
||||
{
|
||||
var dst = Array.CreateInstance(toElem, src.Length);
|
||||
for (int i = 0; i < src.Length; i++)
|
||||
{
|
||||
var v = src.GetValue(i);
|
||||
dst.SetValue(elemConv(v, opts), i);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
private static object ListToListConverted(
|
||||
IList src, Type listTarget, Type toElem,
|
||||
Func<object?, RuntimeCastOptions, object?> elemConv, RuntimeCastOptions opts)
|
||||
{
|
||||
var dst = (IList)Activator.CreateInstance(listTarget, src.Count)!;
|
||||
for (int i = 0; i < src.Count; i++)
|
||||
{
|
||||
var v = src[i];
|
||||
dst.Add(elemConv(v, opts));
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
|
||||
183
Libraries/Esiur/Data/StringKeyList.cs
Normal file
183
Libraries/Esiur/Data/StringKeyList.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
|
||||
public class StringKeyList : IEnumerable<KeyValuePair<string, 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);
|
||||
|
||||
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
|
||||
{
|
||||
key = key.ToLower();
|
||||
foreach (var kv in m_Variables)
|
||||
if (kv.Key.ToLower() == key)
|
||||
return kv.Value;
|
||||
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
key = key.ToLower();
|
||||
|
||||
var toRemove = m_Variables.Where(x => x.Key.ToLower() == key).ToArray();
|
||||
|
||||
foreach (var item in toRemove)
|
||||
m_Variables.Remove(item);
|
||||
|
||||
|
||||
m_Variables.Add(new KeyValuePair<string, string>(key, value));
|
||||
|
||||
OnModified?.Invoke(key, value);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.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 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
key = key.ToLower();
|
||||
foreach (var kv in m_Variables)
|
||||
if (kv.Key.ToLower() == key)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public bool ContainsValue(string value)
|
||||
{
|
||||
value = value.ToLower();
|
||||
foreach (var kv in m_Variables)
|
||||
if (kv.Value.ToLower() == value)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
313
Libraries/Esiur/Data/Tdu.cs
Normal file
313
Libraries/Esiur/Data/Tdu.cs
Normal file
@@ -0,0 +1,313 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Schema;
|
||||
using static Esiur.Data.Codec;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
// Transmission Data Unit
|
||||
public struct Tdu
|
||||
{
|
||||
public TduIdentifier Identifier;
|
||||
//public int Index;
|
||||
public TduClass Class;
|
||||
//public ulong ContentLength;
|
||||
|
||||
public byte[] Composed;
|
||||
//public uint Offset;
|
||||
|
||||
public byte[] Metadata;
|
||||
|
||||
public uint ContentOffset;
|
||||
|
||||
//public ulong Size
|
||||
//{
|
||||
// get
|
||||
// {
|
||||
// if (TotalSize != ulong.MaxValue)
|
||||
// return TotalSize;
|
||||
// else
|
||||
// {
|
||||
// if (ContentLength <= 0xFF)
|
||||
// return 2 + ContentLength;
|
||||
// else if (ContentLength <= 0xFF_FF)
|
||||
// return 3 + ContentLength;
|
||||
// else if (ContentLength <= 0xFF_FF_FF)
|
||||
// return 4 + ContentLength;
|
||||
// else if (ContentLength <= 0xFF_FF_FF_FF)
|
||||
// return 5 + ContentLength;
|
||||
// else if (ContentLength <= 0xFF_FF_FF_FF_FF)
|
||||
// return 6 + ContentLength;
|
||||
// else if (ContentLength <= 0xFF_FF_FF_FF_FF_FF)
|
||||
// return 7 + ContentLength;
|
||||
// else if (ContentLength <= 0xFF_FF_FF_FF_FF_FF_FF)
|
||||
// return 8 + ContentLength;
|
||||
// else //if (ContentLength <= 0xFF_FF_FF_FF_FF_FF_FF_FF)
|
||||
// return 9 + ContentLength;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//private ulong TotalSize;
|
||||
|
||||
public Tdu()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Tdu(TduIdentifier identifier)
|
||||
{
|
||||
Identifier = identifier;
|
||||
Composed = new byte[0];
|
||||
}
|
||||
|
||||
public Tdu(TduIdentifier identifier,
|
||||
byte[] data, ulong length, byte[] metadata = null)
|
||||
{
|
||||
Identifier = identifier;
|
||||
//Index = (byte)identifier & 0x7;
|
||||
Class = (TduClass)((byte)identifier >> 6);
|
||||
Metadata = metadata;
|
||||
|
||||
|
||||
if (Class == TduClass.Fixed)
|
||||
{
|
||||
if (length == 0)
|
||||
Composed = new byte[1] { (byte)identifier };
|
||||
else
|
||||
Composed = DC.Combine(new byte[] { (byte)Identifier }, 0, 1, data, 0, (uint)length);
|
||||
}
|
||||
else if (Class == TduClass.Dynamic
|
||||
|| Class == TduClass.Extension)
|
||||
{
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
Composed = new byte[1] { (byte)Identifier };
|
||||
}
|
||||
else if (length <= 0xFF)
|
||||
{
|
||||
Composed = new byte[2 + length];
|
||||
Composed[0] = (byte)((byte)Identifier | 0x8);
|
||||
Composed[1] = (byte)length;
|
||||
ContentOffset = 2;
|
||||
Buffer.BlockCopy(data, 0, Composed, 2, (int)length);
|
||||
}
|
||||
else if (length <= 0xFF_FF)
|
||||
{
|
||||
Composed = new byte[3 + length];
|
||||
Composed[0] = (byte)((byte)Identifier | 0x10);
|
||||
Composed[1] = (byte)((length >> 8) & 0xFF);
|
||||
Composed[2] = (byte)(length & 0xFF);
|
||||
ContentOffset = 3;
|
||||
|
||||
Buffer.BlockCopy(data, 0, Composed, 3, (int)length);
|
||||
}
|
||||
else if (length <= 0xFF_FF_FF)
|
||||
{
|
||||
Composed = new byte[4 + length];
|
||||
Composed[0] = (byte)((byte)Identifier | 0x18);
|
||||
Composed[1] = (byte)((length >> 16) & 0xFF);
|
||||
Composed[2] = (byte)((length >> 8) & 0xFF);
|
||||
Composed[3] = (byte)(length & 0xFF);
|
||||
ContentOffset = 4;
|
||||
|
||||
Buffer.BlockCopy(data, 0, Composed, 4, (int)length);
|
||||
}
|
||||
else if (length <= 0xFF_FF_FF_FF)
|
||||
{
|
||||
Composed = new byte[5 + length];
|
||||
Composed[0] = (byte)((byte)Identifier | 0x20);
|
||||
Composed[1] = (byte)((length >> 24) & 0xFF);
|
||||
Composed[2] = (byte)((length >> 16) & 0xFF);
|
||||
Composed[3] = (byte)((length >> 8) & 0xFF);
|
||||
Composed[4] = (byte)(length & 0xFF);
|
||||
ContentOffset = 5;
|
||||
|
||||
Buffer.BlockCopy(data, 0, Composed, 5, (int)length);
|
||||
}
|
||||
else if (length <= 0xFF_FF_FF_FF_FF)
|
||||
{
|
||||
Composed = new byte[6 + length];
|
||||
Composed[0] = (byte)((byte)Identifier | 0x28);
|
||||
Composed[1] = (byte)((length >> 32) & 0xFF);
|
||||
Composed[2] = (byte)((length >> 24) & 0xFF);
|
||||
Composed[3] = (byte)((length >> 16) & 0xFF);
|
||||
Composed[4] = (byte)((length >> 8) & 0xFF);
|
||||
Composed[5] = (byte)(length & 0xFF);
|
||||
ContentOffset = 6;
|
||||
|
||||
Buffer.BlockCopy(data, 0, Composed, 6, (int)length);
|
||||
}
|
||||
else if (length <= 0xFF_FF_FF_FF_FF_FF)
|
||||
{
|
||||
Composed = new byte[7 + length];
|
||||
Composed[0] = (byte)((byte)Identifier | 0x30);
|
||||
Composed[1] = (byte)((length >> 40) & 0xFF);
|
||||
Composed[2] = (byte)((length >> 32) & 0xFF);
|
||||
Composed[3] = (byte)((length >> 24) & 0xFF);
|
||||
Composed[4] = (byte)((length >> 16) & 0xFF);
|
||||
Composed[5] = (byte)((length >> 8) & 0xFF);
|
||||
Composed[6] = (byte)(length & 0xFF);
|
||||
ContentOffset = 7;
|
||||
Buffer.BlockCopy(data, 0, Composed, 7, (int)length);
|
||||
}
|
||||
else //if (len <= 0xFF_FF_FF_FF_FF_FF_FF)
|
||||
{
|
||||
Composed = new byte[8 + length];
|
||||
Composed[0] = (byte)((byte)Identifier | 0x38);
|
||||
Composed[1] = (byte)((length >> 48) & 0xFF);
|
||||
Composed[2] = (byte)((length >> 40) & 0xFF);
|
||||
Composed[3] = (byte)((length >> 32) & 0xFF);
|
||||
Composed[4] = (byte)((length >> 24) & 0xFF);
|
||||
Composed[5] = (byte)((length >> 16) & 0xFF);
|
||||
Composed[6] = (byte)((length >> 8) & 0xFF);
|
||||
Composed[7] = (byte)(length & 0xFF);
|
||||
ContentOffset = 8;
|
||||
Buffer.BlockCopy(data, 0, Composed, 8, (int)length);
|
||||
}
|
||||
}
|
||||
else if (Class == TduClass.Typed)
|
||||
{
|
||||
if (metadata == null)
|
||||
throw new Exception("Metadata must be provided for types.");
|
||||
|
||||
|
||||
if (metadata.Length > 0xFF)
|
||||
throw new Exception("Metadata can't exceed 255 bytes in length.");
|
||||
|
||||
var metaLen = (byte)metadata.Length;
|
||||
|
||||
var len = 1 + (ulong)metaLen + length;
|
||||
|
||||
|
||||
if (length == 0 && (metadata == null || metadata.Length == 0))
|
||||
{
|
||||
Composed = new byte[1] { (byte)Identifier };
|
||||
throw new Exception("Need check");
|
||||
}
|
||||
else if (metadata.Length > 0xFF)
|
||||
{
|
||||
throw new Exception("Metadata can't exceed 255 bytes in length.");
|
||||
}
|
||||
else if (length <= 0xFF)
|
||||
{
|
||||
Composed = new byte[2 + len];
|
||||
Composed[0] = (byte)((byte)Identifier | 0x8);
|
||||
Composed[1] = (byte)len;
|
||||
Composed[2] = metaLen;
|
||||
ContentOffset = metaLen + (uint)3;
|
||||
|
||||
Buffer.BlockCopy(metadata, 0, Composed, 3, metaLen);
|
||||
Buffer.BlockCopy(data, 0, Composed, 3 + metaLen, (int)length);
|
||||
}
|
||||
else if (len <= 0xFF_FF)
|
||||
{
|
||||
Composed = new byte[3 + len];
|
||||
Composed[0] = (byte)((byte)identifier | 0x10);
|
||||
Composed[1] = (byte)((len >> 8) & 0xFF);
|
||||
Composed[2] = (byte)(len & 0xFF);
|
||||
Composed[3] = metaLen;
|
||||
ContentOffset = metaLen + (uint)4;
|
||||
|
||||
Buffer.BlockCopy(metadata, 0, Composed, 4, metaLen);
|
||||
Buffer.BlockCopy(data, 0, Composed, 4 + metaLen, (int)length);
|
||||
}
|
||||
else if (len <= 0xFF_FF_FF)
|
||||
{
|
||||
Composed = new byte[4 + len];
|
||||
Composed[0] = (byte)((byte)identifier | 0x18);
|
||||
Composed[1] = (byte)((len >> 16) & 0xFF);
|
||||
Composed[2] = (byte)((len >> 8) & 0xFF);
|
||||
Composed[3] = (byte)(len & 0xFF);
|
||||
Composed[4] = metaLen;
|
||||
ContentOffset = metaLen + (uint)5;
|
||||
|
||||
Buffer.BlockCopy(metadata, 0, Composed, 5, metaLen);
|
||||
Buffer.BlockCopy(data, 0, Composed, 5 + metaLen, (int)length);
|
||||
|
||||
}
|
||||
else if (len <= 0xFF_FF_FF_FF)
|
||||
{
|
||||
Composed = new byte[5 + len];
|
||||
Composed[0] = (byte)((byte)identifier | 0x20);
|
||||
Composed[1] = (byte)((len >> 24) & 0xFF);
|
||||
Composed[2] = (byte)((len >> 16) & 0xFF);
|
||||
Composed[3] = (byte)((len >> 8) & 0xFF);
|
||||
Composed[4] = (byte)(len & 0xFF);
|
||||
Composed[5] = metaLen;
|
||||
ContentOffset = metaLen + (uint)6;
|
||||
|
||||
Buffer.BlockCopy(metadata, 0, Composed, 6, metaLen);
|
||||
Buffer.BlockCopy(data, 0, Composed, 6 + metaLen, (int)length);
|
||||
}
|
||||
else if (len <= 0xFF_FF_FF_FF_FF)
|
||||
{
|
||||
Composed = new byte[6 + len];
|
||||
Composed[0] = (byte)((byte)identifier | 0x28);
|
||||
Composed[1] = (byte)((len >> 32) & 0xFF);
|
||||
Composed[2] = (byte)((len >> 24) & 0xFF);
|
||||
Composed[3] = (byte)((len >> 16) & 0xFF);
|
||||
Composed[4] = (byte)((len >> 8) & 0xFF);
|
||||
Composed[5] = (byte)(len & 0xFF);
|
||||
Composed[6] = metaLen;
|
||||
ContentOffset = metaLen + (uint)7;
|
||||
|
||||
Buffer.BlockCopy(metadata, 0, Composed, 7, metaLen);
|
||||
Buffer.BlockCopy(data, 0, Composed, 7 + metaLen, (int)length);
|
||||
}
|
||||
else if (len <= 0xFF_FF_FF_FF_FF_FF)
|
||||
{
|
||||
Composed = new byte[7 + len];
|
||||
Composed[0] = (byte)((byte)identifier | 0x30);
|
||||
Composed[1] = (byte)((len >> 40) & 0xFF);
|
||||
Composed[2] = (byte)((len >> 32) & 0xFF);
|
||||
Composed[3] = (byte)((len >> 24) & 0xFF);
|
||||
Composed[4] = (byte)((len >> 16) & 0xFF);
|
||||
Composed[5] = (byte)((len >> 8) & 0xFF);
|
||||
Composed[6] = (byte)(len & 0xFF);
|
||||
Composed[7] = metaLen;
|
||||
ContentOffset = metaLen + (uint)8;
|
||||
|
||||
Buffer.BlockCopy(metadata, 0, Composed, 8, metaLen);
|
||||
Buffer.BlockCopy(data, 0, Composed, 8 + metaLen, (int)length);
|
||||
}
|
||||
else //if (len <= 0xFF_FF_FF_FF_FF_FF_FF)
|
||||
{
|
||||
Composed = new byte[8 + len];
|
||||
Composed[0] = (byte)((byte)identifier | 0x38);
|
||||
Composed[1] = (byte)((len >> 48) & 0xFF);
|
||||
Composed[2] = (byte)((len >> 40) & 0xFF);
|
||||
Composed[3] = (byte)((len >> 32) & 0xFF);
|
||||
Composed[4] = (byte)((len >> 24) & 0xFF);
|
||||
Composed[5] = (byte)((len >> 16) & 0xFF);
|
||||
Composed[6] = (byte)((len >> 8) & 0xFF);
|
||||
Composed[7] = (byte)(len & 0xFF);
|
||||
Composed[8] = metaLen;
|
||||
ContentOffset = metaLen + (uint)9;
|
||||
|
||||
Buffer.BlockCopy(metadata, 0, Composed, 9, metaLen);
|
||||
Buffer.BlockCopy(data, 0, Composed, 9 + metaLen, (int)length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public bool MatchType(Tdu with)
|
||||
{
|
||||
if (Identifier != with.Identifier)
|
||||
return false;
|
||||
|
||||
if (Class != TduClass.Typed || with.Class != TduClass.Typed)
|
||||
return false;
|
||||
|
||||
if (!Metadata.SequenceEqual(with.Metadata))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
15
Libraries/Esiur/Data/TduClass.cs
Normal file
15
Libraries/Esiur/Data/TduClass.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public enum TduClass
|
||||
{
|
||||
Fixed = 0x0,
|
||||
Dynamic = 0x1,
|
||||
Typed = 0x2,
|
||||
Extension = 0x3,
|
||||
Invalid
|
||||
}
|
||||
}
|
||||
64
Libraries/Esiur/Data/TduIdentifier.cs
Normal file
64
Libraries/Esiur/Data/TduIdentifier.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public enum TduIdentifier
|
||||
{
|
||||
Null = 0x0,
|
||||
False = 0x1,
|
||||
True = 0x2,
|
||||
NotModified = 0x3,
|
||||
Infinity = 0x4,
|
||||
UInt8 = 0x8,
|
||||
Int8 = 0x9,
|
||||
Char8 = 0xA,
|
||||
LocalResource8 = 0xB,
|
||||
RemoteResource8 = 0xC,
|
||||
LocalProcedure8 = 0xD,
|
||||
RemoteProcedure8 = 0xE,
|
||||
UInt16 = 0x10,
|
||||
Int16 = 0x11,
|
||||
Char16 = 0x12,
|
||||
LocalResource16 = 0x13,
|
||||
RemoteResource16 = 0x14,
|
||||
LocalProcedure16 = 0x15,
|
||||
RemoteProcedure16 = 0x16,
|
||||
UInt32 = 0x18,
|
||||
Int32 = 0x19,
|
||||
Float32 = 0x1A,
|
||||
LocalResource32 = 0x1B,
|
||||
RemoteResource32 = 0x1C,
|
||||
LocalProcedure32 = 0x1D,
|
||||
RemoteProcedure32 = 0x1E,
|
||||
UInt64 = 0x20,
|
||||
Int64 = 0x21,
|
||||
Float64 = 0x22,
|
||||
DateTime = 0x23,
|
||||
UInt128 = 0x28,
|
||||
Int128 = 0x29,
|
||||
Decimal128 = 0x2A,
|
||||
UUID = 0x2B,
|
||||
|
||||
RawData = 0x40,
|
||||
String = 0x41,
|
||||
List = 0x42,
|
||||
ResourceList = 0x43,
|
||||
RecordList = 0x44,
|
||||
ResourceLink = 0x45,
|
||||
Map = 0x46,
|
||||
MapList = 0x47,
|
||||
|
||||
Record = 0x80,
|
||||
TypedList = 0x81,
|
||||
TypedMap = 0x82,
|
||||
TypedTuple = 0x83,
|
||||
TypedEnum = 0x84,
|
||||
TypedConstant = 0x85,
|
||||
|
||||
TypeContinuation = 0xC0,
|
||||
TypeOfTarget = 0xC1,
|
||||
|
||||
}
|
||||
}
|
||||
596
Libraries/Esiur/Data/Tru.cs
Normal file
596
Libraries/Esiur/Data/Tru.cs
Normal file
@@ -0,0 +1,596 @@
|
||||
using Esiur.Core;
|
||||
using Esiur.Data.Types;
|
||||
using Esiur.Resource;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public class Tru
|
||||
{
|
||||
|
||||
static TruIdentifier[] refTypes = new TruIdentifier[]
|
||||
{
|
||||
TruIdentifier.Dynamic,
|
||||
TruIdentifier.RawData,
|
||||
TruIdentifier.String,
|
||||
TruIdentifier.Resource,
|
||||
TruIdentifier.Record,
|
||||
TruIdentifier.Map,
|
||||
TruIdentifier.List,
|
||||
TruIdentifier.TypedList,
|
||||
TruIdentifier.TypedMap,
|
||||
TruIdentifier.Tuple2,
|
||||
TruIdentifier.Tuple3,
|
||||
TruIdentifier.Tuple4,
|
||||
TruIdentifier.Tuple5,
|
||||
TruIdentifier.Tuple6,
|
||||
TruIdentifier.Tuple7,
|
||||
TruIdentifier.TypedRecord,
|
||||
TruIdentifier.TypedResource
|
||||
};
|
||||
|
||||
static Map<TduIdentifier, TruIdentifier> typesMap = new Map<TduIdentifier, TruIdentifier>()
|
||||
{
|
||||
[TduIdentifier.UInt8] = TruIdentifier.UInt8,
|
||||
[TduIdentifier.Int8] = TruIdentifier.Int8,
|
||||
[TduIdentifier.UInt16] = TruIdentifier.UInt16,
|
||||
[TduIdentifier.Int16] = TruIdentifier.Int16,
|
||||
[TduIdentifier.UInt32] = TruIdentifier.UInt32,
|
||||
[TduIdentifier.Int32] = TruIdentifier.Int32,
|
||||
[TduIdentifier.UInt64] = TruIdentifier.UInt64,
|
||||
[TduIdentifier.Int64] = TruIdentifier.Int64,
|
||||
[TduIdentifier.UInt128] = TruIdentifier.UInt128,
|
||||
[TduIdentifier.Int128] = TruIdentifier.Int128,
|
||||
[TduIdentifier.Char8] = TruIdentifier.Char,
|
||||
[TduIdentifier.DateTime] = TruIdentifier.DateTime,
|
||||
[TduIdentifier.Float32] = TruIdentifier.Float32,
|
||||
[TduIdentifier.Float64] = TruIdentifier.Float64,
|
||||
[TduIdentifier.Decimal128] = TruIdentifier.Decimal,
|
||||
[TduIdentifier.False] = TruIdentifier.Bool,
|
||||
[TduIdentifier.True] = TruIdentifier.Bool,
|
||||
[TduIdentifier.Map] = TruIdentifier.Map,
|
||||
[TduIdentifier.List] = TruIdentifier.List,
|
||||
[TduIdentifier.RawData] = TruIdentifier.RawData,
|
||||
[TduIdentifier.Record] = TruIdentifier.Record,
|
||||
[TduIdentifier.String] = TruIdentifier.String,
|
||||
};
|
||||
|
||||
|
||||
|
||||
public void SetNull(List<byte> flags)
|
||||
{
|
||||
if (refTypes.Contains(Identifier))
|
||||
{
|
||||
Nullable = (flags.FirstOrDefault() == 2);
|
||||
if (flags.Count > 0)
|
||||
flags.RemoveAt(0);
|
||||
}
|
||||
|
||||
if (SubTypes != null)
|
||||
foreach (var st in SubTypes)
|
||||
st.SetNull(flags);
|
||||
}
|
||||
|
||||
public void SetNull(byte flag)
|
||||
{
|
||||
if (refTypes.Contains(Identifier))
|
||||
{
|
||||
Nullable = (flag == 2);
|
||||
}
|
||||
|
||||
if (SubTypes != null)
|
||||
foreach (var st in SubTypes)
|
||||
st.SetNull(flag);
|
||||
}
|
||||
|
||||
|
||||
public void SetNotNull(List<byte> flags)
|
||||
{
|
||||
if (refTypes.Contains(Identifier))
|
||||
{
|
||||
Nullable = (flags.FirstOrDefault() != 1);
|
||||
if (flags.Count > 0)
|
||||
flags.RemoveAt(0);
|
||||
}
|
||||
|
||||
if (SubTypes != null)
|
||||
foreach (var st in SubTypes)
|
||||
st.SetNotNull(flags);
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (SubTypes != null && SubTypes.Length > 0)
|
||||
return Identifier.ToString() + "<" + String.Join(",", SubTypes.Select(x => x.ToString())) + ">" + (Nullable ? "?" : "");
|
||||
return Identifier.ToString() + (Nullable ? "?" : "");
|
||||
}
|
||||
public void SetNotNull(byte flag)
|
||||
{
|
||||
if (refTypes.Contains(Identifier))
|
||||
{
|
||||
Nullable = (flag != 1);
|
||||
}
|
||||
|
||||
if (SubTypes != null)
|
||||
foreach (var st in SubTypes)
|
||||
st.SetNotNull(flag);
|
||||
}
|
||||
|
||||
public Type? GetRuntimeType(Warehouse warehouse)
|
||||
{
|
||||
|
||||
if (Identifier == TruIdentifier.TypedList)
|
||||
{
|
||||
var sub = SubTypes?[0].GetRuntimeType(warehouse);
|
||||
if (sub == null)
|
||||
return null;
|
||||
|
||||
var rt = sub.MakeArrayType();
|
||||
|
||||
return rt;
|
||||
}
|
||||
else if (Identifier == TruIdentifier.TypedMap)
|
||||
{
|
||||
var subs = SubTypes.Select(x => x.GetRuntimeType(warehouse)).ToArray();
|
||||
var rt = typeof(Map<,>).MakeGenericType(subs);
|
||||
return rt;
|
||||
}
|
||||
|
||||
return Identifier switch
|
||||
{
|
||||
(TruIdentifier.Void) => typeof(void),
|
||||
(TruIdentifier.Dynamic) => typeof(object),
|
||||
(TruIdentifier.Bool) => Nullable ? typeof(bool?) : typeof(bool),
|
||||
(TruIdentifier.Char) => Nullable ? typeof(char?) : typeof(char),
|
||||
(TruIdentifier.UInt8) => Nullable ? typeof(byte?) : typeof(byte),
|
||||
(TruIdentifier.Int8) => Nullable ? typeof(sbyte?) : typeof(sbyte),
|
||||
(TruIdentifier.Int16) => Nullable ? typeof(short?) : typeof(short),
|
||||
(TruIdentifier.UInt16) => Nullable ? typeof(ushort?) : typeof(ushort),
|
||||
(TruIdentifier.Int32) => Nullable ? typeof(int?) : typeof(int),
|
||||
(TruIdentifier.UInt32) => Nullable ? typeof(uint?) : typeof(uint),
|
||||
(TruIdentifier.Int64) => Nullable ? typeof(ulong?) : typeof(long),
|
||||
(TruIdentifier.UInt64) => Nullable ? typeof(ulong?) : typeof(ulong),
|
||||
(TruIdentifier.Float32) => Nullable ? typeof(float?) : typeof(float),
|
||||
(TruIdentifier.Float64) => Nullable ? typeof(double?) : typeof(double),
|
||||
(TruIdentifier.Decimal) => Nullable ? typeof(decimal?) : typeof(decimal),
|
||||
(TruIdentifier.String) => typeof(string),
|
||||
(TruIdentifier.DateTime) => Nullable ? typeof(DateTime?) : typeof(DateTime),
|
||||
(TruIdentifier.Resource) => typeof(IResource),
|
||||
(TruIdentifier.Record) => typeof(IRecord),
|
||||
(TruIdentifier.TypedRecord) => warehouse.GetTypeDefById((Uuid)UUID!, TypeDefKind.Record)?.DefinedType,
|
||||
(TruIdentifier.TypedResource) => warehouse.GetTypeDefById((Uuid)UUID!, TypeDefKind.Resource)?.DefinedType,
|
||||
(TruIdentifier.Enum) => warehouse.GetTypeDefById((Uuid)UUID!, TypeDefKind.Enum)?.DefinedType,
|
||||
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
public TruIdentifier Identifier;
|
||||
public bool Nullable;
|
||||
public Uuid? UUID;
|
||||
//public RepresentationType? SubType1; // List + Map
|
||||
//public RepresentationType? SubType2; // Map
|
||||
//public RepresentationType? SubType3; // No types yet
|
||||
|
||||
public Tru[]? SubTypes = null;
|
||||
|
||||
public Tru ToNullable()
|
||||
{
|
||||
return new Tru(Identifier, true, UUID, SubTypes);
|
||||
}
|
||||
|
||||
public bool IsTyped()
|
||||
{
|
||||
if (Identifier == TruIdentifier.TypedList && SubTypes[0].Identifier == TruIdentifier.UInt8)
|
||||
return false;
|
||||
|
||||
if (Identifier == TruIdentifier.TypedResource)
|
||||
return false;
|
||||
|
||||
return (UUID != null) || (SubTypes != null && SubTypes.Length > 0);
|
||||
}
|
||||
|
||||
public bool Match(Tru other)
|
||||
{
|
||||
//if (UUID == null && (SubTypes == null || SubTypes.Length == 0))
|
||||
// return false;
|
||||
|
||||
if (other.Identifier != Identifier)
|
||||
return false;
|
||||
if (other.UUID != UUID)
|
||||
return false;
|
||||
|
||||
if (other.SubTypes != null)
|
||||
{
|
||||
if (other.SubTypes.Length != (SubTypes?.Length ?? -1))
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < SubTypes?.Length; i++)
|
||||
if (!SubTypes[i].Match(other.SubTypes[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public (TduIdentifier, byte[]) GetMetadata()
|
||||
{
|
||||
switch (Identifier)
|
||||
{
|
||||
case TruIdentifier.TypedList:
|
||||
return (TduIdentifier.TypedList, SubTypes[0].Compose());
|
||||
case TruIdentifier.TypedRecord:
|
||||
return (TduIdentifier.Record, UUID?.Data);
|
||||
case TruIdentifier.TypedMap:
|
||||
return (TduIdentifier.TypedMap,
|
||||
SubTypes[0].Compose().Concat(SubTypes[1].Compose()).ToArray());
|
||||
case TruIdentifier.Enum:
|
||||
return (TduIdentifier.TypedEnum, UUID?.Data);
|
||||
|
||||
default:
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
//public TDUIdentifier GetTDUIdentifer()
|
||||
//{
|
||||
// switch (Identifier)
|
||||
// {
|
||||
|
||||
// case TRUIdentifier.TypedList: return TDUIdentifier.TypedList
|
||||
|
||||
// case TRUIdentifier.Int8: return TDUIdentifier.Int8;
|
||||
// case TRUIdentifier.Int16: return TDUIdentifier.Int16;
|
||||
// case TRUIdentifier.Int32: return TDUIdentifier.Int32;
|
||||
// case TRUIdentifier.Int64: return TDUIdentifier.Int64;
|
||||
|
||||
// case TRUIdentifier.UInt8: return TDUIdentifier.UInt8;
|
||||
// case TRUIdentifier.UInt16: return TDUIdentifier.UInt16;
|
||||
// case TRUIdentifier.UInt32: return TDUIdentifier.UInt32;
|
||||
// case TRUIdentifier.UInt64: return TDUIdentifier.UInt64;
|
||||
|
||||
// case TRUIdentifier.String: return TDUIdentifier.String;
|
||||
// case TRUIdentifier.Float32: return TDUIdentifier.Float32;
|
||||
// case TRUIdentifier.Float64: return TDUIdentifier.Float64;
|
||||
// case TRUIdentifier. }
|
||||
//}
|
||||
|
||||
private static Dictionary<Type, Tru> _cache = new Dictionary<Type, Tru>();
|
||||
|
||||
public static Tru? FromType(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
return new Tru(TruIdentifier.Void, true);
|
||||
|
||||
if (_cache.ContainsKey(type))
|
||||
return _cache[type];
|
||||
|
||||
var nullable = false;
|
||||
|
||||
var nullType = System.Nullable.GetUnderlyingType(type);
|
||||
|
||||
if (nullType != null)
|
||||
{
|
||||
type = nullType;
|
||||
nullable = true;
|
||||
}
|
||||
|
||||
Tru? tru = null;
|
||||
|
||||
if (type == typeof(IResource))
|
||||
{
|
||||
return new Tru(TruIdentifier.Resource, nullable);
|
||||
}
|
||||
else if (type == typeof(IRecord) || type == typeof(Record))
|
||||
{
|
||||
return new Tru(TruIdentifier.Record, nullable);
|
||||
}
|
||||
else if (type == typeof(Map<object, object>)
|
||||
|| type == typeof(Dictionary<object, object>))
|
||||
{
|
||||
return new Tru(TruIdentifier.Map, nullable);
|
||||
}
|
||||
else if (Codec.ImplementsInterface(type, typeof(IResource)))
|
||||
{
|
||||
tru = new Tru(
|
||||
TruIdentifier.TypedResource,
|
||||
nullable,
|
||||
TypeDef.GetTypeUUID(type)
|
||||
);
|
||||
}
|
||||
else if (Codec.ImplementsInterface(type, typeof(IRecord)))
|
||||
{
|
||||
tru = new Tru(
|
||||
TruIdentifier.TypedRecord,
|
||||
nullable,
|
||||
TypeDef.GetTypeUUID(type)
|
||||
);
|
||||
}
|
||||
else if (type.IsGenericType)
|
||||
{
|
||||
var genericType = type.GetGenericTypeDefinition();
|
||||
|
||||
if (genericType == typeof(List<>)
|
||||
|| genericType == typeof(VarList<>)
|
||||
|| genericType == typeof(IList<>))
|
||||
{
|
||||
var args = type.GetGenericArguments();
|
||||
if (args[0] == typeof(object))
|
||||
{
|
||||
tru = new Tru(TruIdentifier.List, nullable);
|
||||
}
|
||||
else
|
||||
{
|
||||
var subType = FromType(args[0]);
|
||||
if (subType == null) // unrecongnized type
|
||||
return null;
|
||||
|
||||
|
||||
tru = new Tru(TruIdentifier.TypedList, nullable, null,
|
||||
new Tru[] { subType });
|
||||
|
||||
}
|
||||
}
|
||||
else if (genericType == typeof(Map<,>)
|
||||
|| genericType == typeof(Dictionary<,>))
|
||||
{
|
||||
var args = type.GetGenericArguments();
|
||||
if (args[0] == typeof(object) && args[1] == typeof(object))
|
||||
{
|
||||
tru = new Tru(TruIdentifier.Map, nullable);
|
||||
}
|
||||
else
|
||||
{
|
||||
var subType1 = FromType(args[0]);
|
||||
if (subType1 == null)
|
||||
return null;
|
||||
|
||||
var subType2 = FromType(args[1]);
|
||||
if (subType2 == null)
|
||||
return null;
|
||||
|
||||
tru = new Tru(TruIdentifier.TypedMap, nullable, null,
|
||||
new Tru[] { subType1, subType2 });
|
||||
|
||||
}
|
||||
}
|
||||
else if (genericType == typeof(ResourceLink<>))
|
||||
{
|
||||
var args = type.GetGenericArguments();
|
||||
|
||||
return FromType(args[0]);
|
||||
}
|
||||
else if (genericType == typeof(ValueTuple<,>))
|
||||
{
|
||||
var args = type.GetGenericArguments();
|
||||
var subTypes = new Tru[args.Length];
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
var t = FromType(args[i]);
|
||||
if (t == null)
|
||||
return null;
|
||||
subTypes[i] = t;
|
||||
}
|
||||
|
||||
|
||||
tru = new Tru(TruIdentifier.Tuple2, nullable, null, subTypes);
|
||||
|
||||
}
|
||||
else if (genericType == typeof(ValueTuple<,,>))
|
||||
{
|
||||
var args = type.GetGenericArguments();
|
||||
var subTypes = new Tru[args.Length];
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
var t = FromType(args[i]);
|
||||
if (t == null)
|
||||
return null;
|
||||
subTypes[i] = t;
|
||||
}
|
||||
|
||||
tru = new Tru(TruIdentifier.Tuple3, nullable, null, subTypes);
|
||||
|
||||
}
|
||||
else if (genericType == typeof(ValueTuple<,,,>))
|
||||
{
|
||||
|
||||
var args = type.GetGenericArguments();
|
||||
var subTypes = new Tru[args.Length];
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
var t = FromType(args[i]);
|
||||
if (t == null)
|
||||
return null;
|
||||
subTypes[i] = t;
|
||||
}
|
||||
|
||||
tru = new Tru(TruIdentifier.Tuple4, nullable, null, subTypes);
|
||||
}
|
||||
else if (genericType == typeof(ValueTuple<,,,,>))
|
||||
{
|
||||
var args = type.GetGenericArguments();
|
||||
var subTypes = new Tru[args.Length];
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
var t = FromType(args[i]);
|
||||
if (t == null)
|
||||
return null;
|
||||
subTypes[i] = t;
|
||||
}
|
||||
|
||||
tru = new Tru(TruIdentifier.Tuple5, nullable, null, subTypes);
|
||||
}
|
||||
else if (genericType == typeof(ValueTuple<,,,,,>))
|
||||
{
|
||||
var args = type.GetGenericArguments();
|
||||
var subTypes = new Tru[args.Length];
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
var t = FromType(args[i]);
|
||||
if (t == null)
|
||||
return null;
|
||||
subTypes[i] = t;
|
||||
}
|
||||
|
||||
tru = new Tru(TruIdentifier.Tuple6, nullable, null, subTypes);
|
||||
}
|
||||
else if (genericType == typeof(ValueTuple<,,,,,,>))
|
||||
{
|
||||
var args = type.GetGenericArguments();
|
||||
var subTypes = new Tru[args.Length];
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
var t = FromType(args[i]);
|
||||
if (t == null)
|
||||
return null;
|
||||
subTypes[i] = t;
|
||||
}
|
||||
|
||||
tru = new Tru(TruIdentifier.Tuple7, nullable, null, subTypes);
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
else if (type.IsArray)
|
||||
{
|
||||
var elementType = type.GetElementType();
|
||||
if (elementType == typeof(object))
|
||||
tru = new Tru(TruIdentifier.List, nullable);
|
||||
else
|
||||
{
|
||||
var subType = FromType(elementType);
|
||||
|
||||
if (subType == null)
|
||||
return null;
|
||||
|
||||
tru = new Tru(TruIdentifier.TypedList, nullable, null,
|
||||
new Tru[] { subType });
|
||||
|
||||
}
|
||||
}
|
||||
else if (type.IsEnum)
|
||||
{
|
||||
tru = new Tru(TruIdentifier.Enum, nullable, TypeDef.GetTypeUUID(type));
|
||||
}
|
||||
else if (type.IsInterface)
|
||||
{
|
||||
return null; // other interfaces are not supported
|
||||
}
|
||||
|
||||
//else if (typeof(Structure).IsAssignableFrom(t) || t == typeof(ExpandoObject) => TRUIdentifier.Structure)
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
if (tru != null)
|
||||
{
|
||||
_cache.Add(type, tru);
|
||||
return tru;
|
||||
}
|
||||
|
||||
// last check
|
||||
return type switch
|
||||
{
|
||||
_ when type == typeof(void) => new Tru(TruIdentifier.Void, nullable),
|
||||
_ when type == typeof(object) => new Tru(TruIdentifier.Dynamic, nullable),
|
||||
_ when type == typeof(bool) => new Tru(TruIdentifier.Bool, nullable),
|
||||
_ when type == typeof(char) => new Tru(TruIdentifier.Char, nullable),
|
||||
_ when type == typeof(byte) => new Tru(TruIdentifier.UInt8, nullable),
|
||||
_ when type == typeof(sbyte) => new Tru(TruIdentifier.Int8, nullable),
|
||||
_ when type == typeof(short) => new Tru(TruIdentifier.Int16, nullable),
|
||||
_ when type == typeof(ushort) => new Tru(TruIdentifier.UInt16, nullable),
|
||||
_ when type == typeof(int) => new Tru(TruIdentifier.Int32, nullable),
|
||||
_ when type == typeof(uint) => new Tru(TruIdentifier.UInt32, nullable),
|
||||
_ when type == typeof(long) => new Tru(TruIdentifier.Int64, nullable),
|
||||
_ when type == typeof(ulong) => new Tru(TruIdentifier.UInt64, nullable),
|
||||
_ when type == typeof(float) => new Tru(TruIdentifier.Float32, nullable),
|
||||
_ when type == typeof(double) => new Tru(TruIdentifier.Float64, nullable),
|
||||
_ when type == typeof(decimal) => new Tru(TruIdentifier.Decimal, nullable),
|
||||
_ when type == typeof(string) => new Tru(TruIdentifier.String, nullable),
|
||||
_ when type == typeof(DateTime) => new Tru(TruIdentifier.DateTime, nullable),
|
||||
_ when type == typeof(ResourceLink) => new Tru(TruIdentifier.Resource, nullable),
|
||||
_ => null
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public Tru(TruIdentifier identifier, bool nullable, Uuid? uuid = null, Tru[]? subTypes = null)
|
||||
{
|
||||
Nullable = nullable;
|
||||
Identifier = identifier;
|
||||
UUID = uuid;
|
||||
SubTypes = subTypes;
|
||||
}
|
||||
|
||||
public byte[] Compose()
|
||||
{
|
||||
var rt = new BinaryList();
|
||||
|
||||
if (Nullable)
|
||||
rt.AddUInt8((byte)(0x80 | (byte)Identifier));
|
||||
else
|
||||
rt.AddUInt8((byte)Identifier);
|
||||
|
||||
if (UUID != null)
|
||||
rt.AddUInt8Array(UUID.Value.Data);
|
||||
|
||||
if (SubTypes != null)
|
||||
for (var i = 0; i < SubTypes.Length; i++)
|
||||
rt.AddUInt8Array(SubTypes[i].Compose());
|
||||
|
||||
return rt.ToArray();
|
||||
}
|
||||
|
||||
public static (uint, Tru) Parse(byte[] data, uint offset)
|
||||
{
|
||||
var oOffset = offset;
|
||||
|
||||
var header = data[offset++];
|
||||
bool nullable = (header & 0x80) > 0;
|
||||
var identifier = (TruIdentifier)(header & 0x7F);
|
||||
|
||||
|
||||
if ((header & 0x40) > 0)
|
||||
{
|
||||
|
||||
var hasUUID = (header & 0x4) > 0;
|
||||
var subsCount = (header >> 3) & 0x7;
|
||||
|
||||
Uuid? uuid = null;
|
||||
|
||||
if (hasUUID)
|
||||
{
|
||||
uuid = data.GetUUID(offset);
|
||||
offset += 16;
|
||||
}
|
||||
|
||||
var subs = new Tru[subsCount];
|
||||
|
||||
for (var i = 0; i < subsCount; i++)
|
||||
{
|
||||
(var len, subs[i]) = Tru.Parse(data, offset);
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return (offset - oOffset, new Tru(identifier, nullable, uuid, subs));
|
||||
}
|
||||
else
|
||||
{
|
||||
return (1, new Tru(identifier, nullable));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
46
Libraries/Esiur/Data/TruIdentifier.cs
Normal file
46
Libraries/Esiur/Data/TruIdentifier.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public enum TruIdentifier
|
||||
{
|
||||
Void = 0x0,
|
||||
Dynamic = 0x1,
|
||||
Bool = 0x2,
|
||||
UInt8,
|
||||
Int8,
|
||||
Char,
|
||||
UInt16,
|
||||
Int16,
|
||||
UInt32,
|
||||
Int32,
|
||||
Float32,
|
||||
UInt64,
|
||||
Int64,
|
||||
Float64,
|
||||
DateTime,
|
||||
UInt128,
|
||||
Int128,
|
||||
Decimal,
|
||||
String,
|
||||
RawData,
|
||||
Resource,
|
||||
Record,
|
||||
List,
|
||||
Map,
|
||||
Enum = 0x44,
|
||||
TypedResource = 0x45, // Followed by UUID
|
||||
TypedRecord = 0x46, // Followed by UUID
|
||||
TypedList = 0x48, // Followed by element type
|
||||
Tuple2 = 0x50, // Followed by element type
|
||||
TypedMap = 0x51, // Followed by key type and value type
|
||||
Tuple3 = 0x58,
|
||||
Tuple4 = 0x60,
|
||||
Tuple5 = 0x68,
|
||||
Tuple6 = 0x70,
|
||||
Tuple7 = 0x78
|
||||
}
|
||||
|
||||
}
|
||||
99
Libraries/Esiur/Data/Types/ArgumentDef.cs
Normal file
99
Libraries/Esiur/Data/Types/ArgumentDef.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using Esiur.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Esiur.Data.Types;
|
||||
|
||||
public class ArgumentDef
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public bool Optional { get; set; }
|
||||
|
||||
public Tru Type { get; set; }
|
||||
|
||||
public ParameterInfo ParameterInfo { get; set; }
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
public Map<string, string> Annotations { get; set; }
|
||||
|
||||
public static (uint, ArgumentDef) Parse(byte[] data, uint offset, int index)
|
||||
{
|
||||
var optional = (data[offset] & 0x1) == 0x1;
|
||||
var hasAnnotations = (data[offset++] & 0x2) == 0x2;
|
||||
|
||||
var cs = (uint)data[offset++];
|
||||
var name = data.GetString(offset, cs);
|
||||
offset += cs;
|
||||
var (size, type) = Tru.Parse(data, offset);
|
||||
|
||||
offset += size;
|
||||
|
||||
Map<string, string> annotations = null;
|
||||
|
||||
if (hasAnnotations)
|
||||
{
|
||||
//var acs = data.GetUInt32(offset, Endian.Little);
|
||||
//offset += 2;
|
||||
var (l, a) = Codec.ParseSync(data, offset, null);
|
||||
// for saftey, Map<string, string> might change in the future
|
||||
if (a is Map<string, string> ann)
|
||||
annotations = ann;
|
||||
|
||||
cs += l;
|
||||
}
|
||||
|
||||
return (cs + 2 + size, new ArgumentDef()
|
||||
{
|
||||
Name = name,
|
||||
Index = index,
|
||||
Type = type,
|
||||
Optional = optional,
|
||||
Annotations = annotations
|
||||
});
|
||||
}
|
||||
|
||||
public ArgumentDef()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Optional)
|
||||
return $"[{Name}: {Type}]";
|
||||
else
|
||||
return $"{Name}: {Type} ";
|
||||
}
|
||||
|
||||
public byte[] Compose()
|
||||
{
|
||||
var name = DC.ToBytes(Name);
|
||||
|
||||
if (Annotations == null)
|
||||
{
|
||||
return new BinaryList()
|
||||
.AddUInt8(Optional ? (byte)1 : (byte)0)
|
||||
.AddUInt8((byte)name.Length)
|
||||
.AddUInt8Array(name)
|
||||
.AddUInt8Array(Type.Compose())
|
||||
.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
var exp = Codec.Compose(Annotations, null, null);
|
||||
|
||||
return new BinaryList()
|
||||
.AddUInt8((byte)(0x2 | (Optional ? 1 : 0)))
|
||||
.AddUInt8((byte)name.Length)
|
||||
.AddUInt8Array(name)
|
||||
.AddUInt8Array(Type.Compose())
|
||||
.AddUInt8Array(exp)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Libraries/Esiur/Data/Types/AttributeDef.cs
Normal file
32
Libraries/Esiur/Data/Types/AttributeDef.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Esiur.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Data.Types;
|
||||
|
||||
public class AttributeDef : MemberDef
|
||||
{
|
||||
|
||||
public PropertyInfo PropertyInfo
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
public static AttributeDef MakeAttributeDef(Type type, PropertyInfo pi, byte index, string name, TypeDef typeDef)
|
||||
{
|
||||
return new AttributeDef()
|
||||
{
|
||||
Index = index,
|
||||
Inherited = pi.DeclaringType != type,
|
||||
Name = name,
|
||||
PropertyInfo = pi,
|
||||
Definition = typeDef
|
||||
};
|
||||
}
|
||||
}
|
||||
138
Libraries/Esiur/Data/Types/ConstantDef.cs
Normal file
138
Libraries/Esiur/Data/Types/ConstantDef.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Esiur.Data;
|
||||
using Esiur.Resource;
|
||||
|
||||
namespace Esiur.Data.Types;
|
||||
|
||||
public class ConstantDef : MemberDef
|
||||
{
|
||||
public object Value { get; set; }
|
||||
|
||||
public Map<string, string> Annotations { get; set; }
|
||||
public Tru ValueType { get; set; }
|
||||
|
||||
public FieldInfo FieldInfo { get; set; }
|
||||
|
||||
|
||||
public static (uint, ConstantDef) Parse(byte[] data, uint offset, byte index, bool inherited)
|
||||
{
|
||||
var oOffset = offset;
|
||||
|
||||
var hasAnnotation = ((data[offset++] & 0x10) == 0x10);
|
||||
|
||||
var name = data.GetString(offset + 1, data[offset]);
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
var (dts, valueType) = Tru.Parse(data, offset);
|
||||
|
||||
offset += dts;
|
||||
|
||||
(dts, var value) = Codec.ParseSync(data, offset, Warehouse.Default);
|
||||
|
||||
offset += dts;
|
||||
|
||||
Map<string, string> annotations = null;
|
||||
|
||||
// arguments
|
||||
if (hasAnnotation) // Annotation ?
|
||||
{
|
||||
var (len, anns) = Codec.ParseSync(data, offset, null);
|
||||
|
||||
if (anns is Map<string, string> map)
|
||||
annotations = map;
|
||||
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return (offset - oOffset, new ConstantDef()
|
||||
{
|
||||
Index = index,
|
||||
Name = name,
|
||||
Inherited = inherited,
|
||||
ValueType = valueType,
|
||||
Value = value,
|
||||
Annotations = annotations
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public byte[] Compose()
|
||||
{
|
||||
var name = DC.ToBytes(Name);
|
||||
|
||||
var hdr = Inherited ? (byte)0x80 : (byte)0;
|
||||
|
||||
|
||||
if (Annotations != null)
|
||||
{
|
||||
var exp = Codec.Compose(Annotations, null, null);// DC.ToBytes(Annotation);
|
||||
hdr |= 0x70;
|
||||
return new BinaryList()
|
||||
.AddUInt8(hdr)
|
||||
.AddUInt8((byte)name.Length)
|
||||
.AddUInt8Array(name)
|
||||
.AddUInt8Array(ValueType.Compose())
|
||||
.AddUInt8Array(Codec.Compose(Value, null, null))
|
||||
.AddInt32(exp.Length)
|
||||
.AddUInt8Array(exp)
|
||||
.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
hdr |= 0x60;
|
||||
|
||||
return new BinaryList()
|
||||
.AddUInt8(hdr)
|
||||
.AddUInt8((byte)name.Length)
|
||||
.AddUInt8Array(name)
|
||||
.AddUInt8Array(ValueType.Compose())
|
||||
.AddUInt8Array(Codec.Compose(Value, null, null))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static ConstantDef MakeConstantDef(Type type, FieldInfo ci, byte index = 0, string customName = null, TypeDef typeDef = null)
|
||||
{
|
||||
var annotationAttrs = ci.GetCustomAttributes<AnnotationAttribute>(true);
|
||||
|
||||
var valueType = Tru.FromType(ci.FieldType);
|
||||
|
||||
if (valueType == null)
|
||||
throw new Exception($"Unsupported type `{ci.FieldType}` in constant `{type.Name}.{ci.Name}`");
|
||||
|
||||
var value = ci.GetValue(null);
|
||||
|
||||
if (typeDef?.Kind == TypeDefKind.Enum)
|
||||
value = Convert.ChangeType(value, ci.FieldType.GetEnumUnderlyingType());
|
||||
|
||||
Map<string, string> annotations = null;
|
||||
|
||||
if (annotationAttrs != null && annotationAttrs.Count() > 0)
|
||||
{
|
||||
annotations = new Map<string, string>();
|
||||
foreach (var attr in annotationAttrs)
|
||||
annotations.Add(attr.Key, attr.Value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return new ConstantDef()
|
||||
{
|
||||
Name = customName,
|
||||
Index = index,
|
||||
Inherited = ci.DeclaringType != type,
|
||||
ValueType = valueType,
|
||||
Value = value,
|
||||
FieldInfo = ci,
|
||||
Annotations = annotations,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
9
Libraries/Esiur/Data/Types/CustomEventOccurredEvent.cs
Normal file
9
Libraries/Esiur/Data/Types/CustomEventOccurredEvent.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data.Types;
|
||||
|
||||
internal class CustomEventOccurredEvent
|
||||
{
|
||||
}
|
||||
1
Libraries/Esiur/Data/Types/DefinitionDataType.cs
Normal file
1
Libraries/Esiur/Data/Types/DefinitionDataType.cs
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
180
Libraries/Esiur/Data/Types/EventDef.cs
Normal file
180
Libraries/Esiur/Data/Types/EventDef.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Resource;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Data.Types;
|
||||
|
||||
public class EventDef : MemberDef
|
||||
{
|
||||
|
||||
public Map<string, string> Annotations
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name}: {ArgumentType}";
|
||||
}
|
||||
|
||||
public bool Subscribable { get; set; }
|
||||
|
||||
public EventInfo EventInfo { get; set; }
|
||||
|
||||
public Tru ArgumentType { get; set; }
|
||||
|
||||
|
||||
public static (uint, EventDef) Parse(byte[] data, uint offset, byte index, bool inherited)
|
||||
{
|
||||
var oOffset = offset;
|
||||
|
||||
var hasAnnotation = ((data[offset] & 0x10) == 0x10);
|
||||
var subscribable = ((data[offset++] & 0x8) == 0x8);
|
||||
|
||||
var name = data.GetString(offset + 1, data[offset]);
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
var (dts, argType) = Tru.Parse(data, offset);
|
||||
|
||||
offset += dts;
|
||||
|
||||
// Annotation ?
|
||||
Map<string, string> annotations = null;
|
||||
|
||||
if (hasAnnotation)
|
||||
{
|
||||
var (len, anns) = Codec.ParseSync(data, offset, null);
|
||||
|
||||
if (anns is Map<string, string> map)
|
||||
annotations = map;
|
||||
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return (offset - oOffset, new EventDef()
|
||||
{
|
||||
Index = index,
|
||||
Name = name,
|
||||
Inherited = inherited,
|
||||
ArgumentType = argType,
|
||||
Subscribable = subscribable,
|
||||
Annotations = annotations
|
||||
});
|
||||
}
|
||||
|
||||
public byte[] Compose()
|
||||
{
|
||||
var name = Name.ToBytes();
|
||||
|
||||
var hdr = Inherited ? (byte)0x80 : (byte)0;
|
||||
|
||||
if (Subscribable)
|
||||
hdr |= 0x8;
|
||||
|
||||
if (Annotations != null)
|
||||
{
|
||||
var exp = Codec.Compose(Annotations, null, null); //( DC.ToBytes(Annotation);
|
||||
hdr |= 0x50;
|
||||
return new BinaryList()
|
||||
.AddUInt8(hdr)
|
||||
.AddUInt8((byte)name.Length)
|
||||
.AddUInt8Array(name)
|
||||
.AddUInt8Array(ArgumentType.Compose())
|
||||
.AddInt32(exp.Length)
|
||||
.AddUInt8Array(exp)
|
||||
.ToArray();
|
||||
}
|
||||
else
|
||||
hdr |= 0x40;
|
||||
|
||||
return new BinaryList()
|
||||
.AddUInt8(hdr)
|
||||
.AddUInt8((byte)name.Length)
|
||||
.AddUInt8Array(name)
|
||||
.AddUInt8Array(ArgumentType.Compose())
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
|
||||
public static EventDef MakeEventDef(Type type, EventInfo ei, byte index, string name, TypeDef schema)
|
||||
{
|
||||
|
||||
if (!ei.EventHandlerType.IsGenericType)
|
||||
throw new Exception($"Unsupported event handler type in event `{type.Name}.{ei.Name}`");
|
||||
|
||||
if (ei.EventHandlerType.GetGenericTypeDefinition() != typeof(ResourceEventHandler<>)
|
||||
&& ei.EventHandlerType.GetGenericTypeDefinition() != typeof(CustomResourceEventHandler<>))
|
||||
throw new Exception($"Unsupported event handler type in event `{type.Name}.{ei.Name}`");
|
||||
|
||||
|
||||
var argType = ei.EventHandlerType.GenericTypeArguments[0];
|
||||
var evtType = Tru.FromType(argType);
|
||||
|
||||
if (evtType == null)
|
||||
throw new Exception($"Unsupported type `{argType}` in event `{type.Name}.{ei.Name}`");
|
||||
|
||||
var annotationAttrs = ei.GetCustomAttributes<AnnotationAttribute>(true);
|
||||
var subscribableAttr = ei.GetCustomAttribute<SubscribableAttribute>(true);
|
||||
|
||||
//evtType.Nullable = new NullabilityInfoContext().Create(ei).ReadState is NullabilityState.Nullable;
|
||||
|
||||
var nullableAttr = ei.GetCustomAttributes().FirstOrDefault(x => x.GetType().Name == "System.Runtime.CompilerServices.NullableAttribute");// .GetCustomAttribute<NullableAttribute>(true);
|
||||
var nullableContextAttr = ei.GetCustomAttributes().FirstOrDefault(x => x.GetType().Name == "System.Runtime.CompilerServices.NullableContextAttribute");// ei.GetCustomAttribute<NullableContextAttribute>(true);
|
||||
|
||||
|
||||
var nullableAttrFlags = (nullableAttr?.GetType().GetField("NullableFlags")?.GetValue(nullableAttr) as byte[] ?? new byte[0]).ToList();
|
||||
var nullableContextAttrFlag = (byte)(nullableContextAttr?.GetType().GetField("Flag")?.GetValue(nullableContextAttr) ?? (byte)0);
|
||||
|
||||
//var flags = nullableAttr?.Flags?.ToList() ?? new List<byte>();
|
||||
//var flags = ((byte[])nullableAttr?.NullableFlags ?? new byte[0]).ToList();
|
||||
|
||||
// skip the eventHandler class
|
||||
if (nullableAttrFlags.Count > 1)
|
||||
nullableAttrFlags = nullableAttrFlags.Skip(1).ToList();
|
||||
|
||||
if (nullableContextAttrFlag == 2)
|
||||
{
|
||||
if (nullableAttrFlags.Count == 1)
|
||||
evtType.SetNotNull(nullableAttrFlags.FirstOrDefault());
|
||||
else
|
||||
evtType.SetNotNull(nullableAttrFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nullableAttrFlags.Count == 1)
|
||||
evtType.SetNull(nullableAttrFlags.FirstOrDefault());
|
||||
else
|
||||
evtType.SetNull(nullableAttrFlags);
|
||||
}
|
||||
|
||||
Map<string, string> annotations = null;
|
||||
|
||||
if (annotationAttrs != null && annotationAttrs.Count() > 0)
|
||||
{
|
||||
annotations = new Map<string, string>();
|
||||
foreach (var attr in annotationAttrs)
|
||||
annotations.Add(attr.Key, attr.Value);
|
||||
}
|
||||
|
||||
|
||||
return new EventDef()
|
||||
{
|
||||
Name = name,
|
||||
ArgumentType = evtType,
|
||||
Index = index,
|
||||
Inherited = ei.DeclaringType != type,
|
||||
Annotations = annotations,
|
||||
EventInfo = ei,
|
||||
Subscribable = subscribableAttr != null
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
305
Libraries/Esiur/Data/Types/FunctionDef.cs
Normal file
305
Libraries/Esiur/Data/Types/FunctionDef.cs
Normal file
@@ -0,0 +1,305 @@
|
||||
using Esiur.Core;
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
using Esiur.Resource;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Data.Types;
|
||||
|
||||
public class FunctionDef : MemberDef
|
||||
{
|
||||
|
||||
public Map<string, string> Annotations
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
//public bool IsVoid
|
||||
//{
|
||||
// get;
|
||||
// set;
|
||||
//}
|
||||
|
||||
public Tru ReturnType { get; set; }
|
||||
|
||||
public bool IsStatic { get; set; }
|
||||
|
||||
public ArgumentDef[] Arguments { get; set; }
|
||||
|
||||
public MethodInfo MethodInfo
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
public static (uint, FunctionDef) Parse(byte[] data, uint offset, byte index, bool inherited)
|
||||
{
|
||||
|
||||
var oOffset = offset;
|
||||
|
||||
var isStatic = ((data[offset] & 0x4) == 0x4);
|
||||
var hasAnnotation = ((data[offset++] & 0x10) == 0x10);
|
||||
|
||||
var name = data.GetString(offset + 1, data[offset]);
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
// return type
|
||||
var (rts, returnType) = Tru.Parse(data, offset);
|
||||
offset += rts;
|
||||
|
||||
// arguments count
|
||||
var argsCount = data[offset++];
|
||||
List<ArgumentDef> arguments = new();
|
||||
|
||||
for (var a = 0; a < argsCount; a++)
|
||||
{
|
||||
var (cs, argType) = ArgumentDef.Parse(data, offset, a);
|
||||
arguments.Add(argType);
|
||||
offset += cs;
|
||||
}
|
||||
|
||||
Map<string, string> annotations = null;
|
||||
|
||||
// arguments
|
||||
if (hasAnnotation) // Annotation ?
|
||||
{
|
||||
var (len, anns) = Codec.ParseSync(data, offset, null);
|
||||
|
||||
if (anns is Map<string, string> map)
|
||||
annotations = map;
|
||||
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return (offset - oOffset, new FunctionDef()
|
||||
{
|
||||
Index = index,
|
||||
Name = name,
|
||||
Arguments = arguments.ToArray(),
|
||||
IsStatic = isStatic,
|
||||
Inherited = inherited,
|
||||
Annotations = annotations,
|
||||
ReturnType = returnType,
|
||||
});
|
||||
}
|
||||
|
||||
public byte[] Compose()
|
||||
{
|
||||
|
||||
var name = DC.ToBytes(Name);
|
||||
|
||||
var bl = new BinaryList()
|
||||
.AddUInt8((byte)name.Length)
|
||||
.AddUInt8Array(name)
|
||||
.AddUInt8Array(ReturnType.Compose())
|
||||
.AddUInt8((byte)Arguments.Length);
|
||||
|
||||
for (var i = 0; i < Arguments.Length; i++)
|
||||
bl.AddUInt8Array(Arguments[i].Compose());
|
||||
|
||||
|
||||
if (Annotations != null)
|
||||
{
|
||||
var exp = Codec.Compose(Annotations, null, null);// DC.ToBytes(Annotation);
|
||||
bl.AddUInt8Array(exp);
|
||||
bl.InsertUInt8(0, (byte)((Inherited ? (byte)0x90 : (byte)0x10) | (IsStatic ? 0x4 : 0)));
|
||||
}
|
||||
else
|
||||
bl.InsertUInt8(0, (byte)((Inherited ? (byte)0x80 : (byte)0x0) | (IsStatic ? 0x4 : 0)));
|
||||
|
||||
return bl.ToArray();
|
||||
}
|
||||
|
||||
|
||||
public static FunctionDef MakeFunctionDef(Type type, MethodInfo mi, byte index, string name, TypeDef schema)
|
||||
{
|
||||
|
||||
var genericRtType = mi.ReturnType.IsGenericType ? mi.ReturnType.GetGenericTypeDefinition() : null;
|
||||
|
||||
Tru rtType;
|
||||
|
||||
if (genericRtType == typeof(AsyncReply<>))
|
||||
{
|
||||
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0]);
|
||||
}
|
||||
else if (genericRtType == typeof(Task<>))
|
||||
{
|
||||
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0]);
|
||||
}
|
||||
else if (genericRtType == typeof(IEnumerable<>) || genericRtType == typeof(IAsyncEnumerable<>))
|
||||
{
|
||||
// get export
|
||||
rtType = Tru.FromType(mi.ReturnType.GetGenericArguments()[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mi.ReturnType == typeof(Task))
|
||||
rtType = Tru.FromType(null);
|
||||
else
|
||||
rtType = Tru.FromType(mi.ReturnType);
|
||||
}
|
||||
|
||||
if (rtType == null)
|
||||
throw new Exception($"Unsupported type `{mi.ReturnType}` in method `{type.Name}.{mi.Name}` return");
|
||||
|
||||
var annotationAttrs = mi.GetCustomAttributes<AnnotationAttribute>(true);
|
||||
|
||||
//var nullabilityInfoContext = new NullabilityInfoContext();
|
||||
//rtType.Nullable = nullabilityInfoContext.Create(mi.ReturnParameter).WriteState is NullabilityState.Nullable;
|
||||
|
||||
|
||||
var nullableAttr = mi.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute");
|
||||
var nullableContextAttr = mi.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableContextAttribute");
|
||||
|
||||
var nullableAttrFlags = (nullableAttr?.GetType().GetField("NullableFlags")?.GetValue(nullableAttr) as byte[] ?? new byte[0]).ToList();
|
||||
var nullableContextAttrFlag = (byte)(nullableContextAttr?.GetType().GetField("Flag")?.GetValue(nullableContextAttr) ?? (byte)0);
|
||||
|
||||
//var flags = ((byte[])nullableAttr?.NullableFlags ?? new byte[0]).ToList();
|
||||
|
||||
//var rtNullableAttr = mi.ReturnTypeCustomAttributes.GetCustomAttributes(typeof(NullableAttribute), true).FirstOrDefault() as NullableAttribute;
|
||||
var rtNullableAttr = mi.ReturnTypeCustomAttributes.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute");
|
||||
|
||||
|
||||
|
||||
// var rtNullableContextAttr = mi.ReturnTypeCustomAttributes
|
||||
// .GetCustomAttributes(typeof(NullableContextAttribute), true)
|
||||
// .FirstOrDefault() as NullableContextAttribute
|
||||
// ?? nullableContextAttr;
|
||||
|
||||
|
||||
var rtNullableContextAttr = mi.ReturnTypeCustomAttributes
|
||||
.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().Name == "NullableContextAttribute")
|
||||
?? nullableContextAttr;
|
||||
|
||||
var rtNullableAttrFlags = (rtNullableAttr?.GetType().GetField("NullableFlags")?.GetValue(rtNullableAttr) as byte[] ?? new byte[0]).ToList();
|
||||
var rtNullableContextAttrFlag = (byte)(rtNullableContextAttr?.GetType().GetField("Flag")?.GetValue(rtNullableContextAttr) ?? (byte)0);
|
||||
|
||||
//var rtFlags = rtNullableAttr?.Flags?.ToList() ?? new List<byte>();
|
||||
//var rtFlags = ((byte[])rtNullableAttr?.NullableFlags ?? new byte[0]).ToList();
|
||||
|
||||
if (rtNullableAttrFlags.Count > 0 && genericRtType == typeof(AsyncReply<>))
|
||||
rtNullableAttrFlags.RemoveAt(0);
|
||||
|
||||
if (rtNullableContextAttrFlag == 2)
|
||||
{
|
||||
if (rtNullableAttrFlags.Count == 1)
|
||||
rtType.SetNotNull(rtNullableAttrFlags.FirstOrDefault());
|
||||
else
|
||||
rtType.SetNotNull(rtNullableAttrFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rtNullableAttrFlags.Count == 1)
|
||||
rtType.SetNull(rtNullableAttrFlags.FirstOrDefault());
|
||||
else
|
||||
rtType.SetNull(rtNullableAttrFlags);
|
||||
}
|
||||
|
||||
var args = mi.GetParameters();
|
||||
|
||||
if (args.Length > 0)
|
||||
{
|
||||
if (args.Last().ParameterType == typeof(EpConnection)
|
||||
|| args.Last().ParameterType == typeof(InvocationContext))
|
||||
args = args.Take(args.Count() - 1).ToArray();
|
||||
}
|
||||
|
||||
var arguments = args.Select(x =>
|
||||
{
|
||||
var argType = Tru.FromType(x.ParameterType);
|
||||
|
||||
if (argType == null)
|
||||
throw new Exception($"Unsupported type `{x.ParameterType}` in method `{type.Name}.{mi.Name}` parameter `{x.Name}`");
|
||||
|
||||
|
||||
var argAnnotationAttrs = x.GetCustomAttributes<AnnotationAttribute>(true);
|
||||
|
||||
|
||||
var argNullableAttr = x.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute");
|
||||
var argNullableContextAttr = x.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableContextAttr");
|
||||
|
||||
var argNullableAttrFlags = (argNullableAttr?.GetType().GetField("NullableFlags")?.GetValue(argNullableAttr) as byte[] ?? new byte[0]).ToList();
|
||||
var argNullableContextAttrFlag = (byte)(argNullableAttr?.GetType().GetField("Flag")?.GetValue(argNullableAttr) ?? (byte)0);
|
||||
|
||||
if (argNullableContextAttrFlag == 2)
|
||||
{
|
||||
if (argNullableAttrFlags.Count == 1)
|
||||
argType.SetNotNull(argNullableAttrFlags.FirstOrDefault());
|
||||
else
|
||||
argType.SetNotNull(argNullableAttrFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (argNullableAttrFlags.Count == 1)
|
||||
argType.SetNull(argNullableAttrFlags.FirstOrDefault());
|
||||
else
|
||||
argType.SetNull(argNullableAttrFlags);
|
||||
}
|
||||
|
||||
Map<string, string> argAnn = null;
|
||||
|
||||
if (argAnnotationAttrs != null && argAnnotationAttrs.Count() > 0)
|
||||
{
|
||||
argAnn = new Map<string, string>();
|
||||
foreach (var attr in argAnnotationAttrs)
|
||||
argAnn.Add(attr.Key, attr.Value);
|
||||
}
|
||||
|
||||
return new ArgumentDef()
|
||||
{
|
||||
Name = x.Name,
|
||||
Type = argType,
|
||||
ParameterInfo = x,
|
||||
Optional = x.IsOptional,
|
||||
Annotations = argAnn
|
||||
};
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
Map<string, string> annotations = null;
|
||||
|
||||
if (annotationAttrs != null && annotationAttrs.Count() > 0)
|
||||
{
|
||||
annotations = new Map<string, string>();
|
||||
foreach (var attr in annotationAttrs)
|
||||
annotations.Add(attr.Key, attr.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
annotations = new Map<string, string>();
|
||||
annotations.Add("", "(" + String.Join(",",
|
||||
mi.GetParameters().Where(x => x.ParameterType != typeof(EpConnection))
|
||||
.Select(x => "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name);
|
||||
|
||||
}
|
||||
|
||||
return new FunctionDef()
|
||||
{
|
||||
Name = name,
|
||||
Index = index,
|
||||
Inherited = mi.DeclaringType != type,
|
||||
IsStatic = mi.IsStatic,
|
||||
ReturnType = rtType,
|
||||
Arguments = arguments,
|
||||
MethodInfo = mi,
|
||||
Annotations = annotations
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
//return = "(" + String.Join(",", mi.GetParameters().Where(x => x.ParameterType != typeof(EpConnection)).Select(x => "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name;
|
||||
|
||||
return $"{ReturnType} {Name}({string.Join(", ", Arguments.Select(a => a.ToString()))})";
|
||||
}
|
||||
|
||||
}
|
||||
90
Libraries/Esiur/Data/Types/MemberData.cs
Normal file
90
Libraries/Esiur/Data/Types/MemberData.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using Esiur.Resource;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data.Types;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class MemberData
|
||||
{
|
||||
public MemberInfo Info;
|
||||
public string Name;
|
||||
public int Order;
|
||||
//public bool Inherited;
|
||||
public MemberData? Parent;
|
||||
public MemberData? Child;
|
||||
public byte Index;
|
||||
|
||||
public PropertyPermission PropertyPermission;
|
||||
|
||||
//public ExportAttribute ExportAttribute;
|
||||
|
||||
|
||||
//public string Name => ExportAttribute?.Name ?? Info.Name;
|
||||
|
||||
public MemberData(MemberInfo info, int order)
|
||||
{
|
||||
var exportAttr = info.GetCustomAttribute<ExportAttribute>();
|
||||
|
||||
if (info is PropertyInfo pi)
|
||||
{
|
||||
if (exportAttr != null && exportAttr.Permission.HasValue)
|
||||
{
|
||||
if ((exportAttr.Permission == PropertyPermission.Write
|
||||
|| exportAttr.Permission == PropertyPermission.ReadWrite) && !pi.CanWrite)
|
||||
{
|
||||
throw new Exception($"Property '{pi.Name}' does not have a setter, but ExportAttribute specifies it as writable.");
|
||||
}
|
||||
|
||||
if ((exportAttr.Permission == PropertyPermission.Read
|
||||
|| exportAttr.Permission == PropertyPermission.ReadWrite) && !pi.CanRead)
|
||||
{
|
||||
throw new Exception($"Property '{pi.Name}' does not have a getter, but ExportAttribute specifies it as readable.");
|
||||
}
|
||||
|
||||
this.PropertyPermission = exportAttr.Permission.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.PropertyPermission = (pi.CanRead && pi.CanWrite) ? PropertyPermission.ReadWrite
|
||||
: pi.CanWrite ? PropertyPermission.Write
|
||||
: PropertyPermission.Read;
|
||||
}
|
||||
}
|
||||
|
||||
this.Name = exportAttr?.Name ?? info.Name;
|
||||
this.Info = info;
|
||||
this.Order = order;
|
||||
}
|
||||
|
||||
public MemberInfo GetMemberInfo()
|
||||
{
|
||||
var rt = Info;
|
||||
var md = Child;
|
||||
while (md != null)
|
||||
{
|
||||
rt = Info;
|
||||
md = md.Child;
|
||||
}
|
||||
return rt;
|
||||
}
|
||||
|
||||
//public string? GetAnnotation()
|
||||
//{
|
||||
// string? rt = null;
|
||||
// var md = this;
|
||||
// while (md != null)
|
||||
// {
|
||||
// var annotationAttr = md.Info.GetCustomAttribute<AnnotationAttribute>();
|
||||
// if (annotationAttr != null)
|
||||
// rt = annotationAttr.Annotation;
|
||||
// md = md.Child;
|
||||
// }
|
||||
|
||||
// return rt;
|
||||
//}
|
||||
}
|
||||
|
||||
21
Libraries/Esiur/Data/Types/MemberDef.cs
Normal file
21
Libraries/Esiur/Data/Types/MemberDef.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Esiur.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Data.Types;
|
||||
public class MemberDef
|
||||
{
|
||||
|
||||
public byte Index { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool Inherited { get; set; }
|
||||
public TypeDef Definition { get; set; }
|
||||
|
||||
|
||||
public string Fullname => Definition.Name + "." + Name;
|
||||
|
||||
}
|
||||
|
||||
285
Libraries/Esiur/Data/Types/PropertyDef.cs
Normal file
285
Libraries/Esiur/Data/Types/PropertyDef.cs
Normal file
@@ -0,0 +1,285 @@
|
||||
using Esiur.Data;
|
||||
using Esiur.Protocol;
|
||||
using Esiur.Resource;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Data.Types;
|
||||
|
||||
public class PropertyDef : MemberDef
|
||||
{
|
||||
public Map<string, string> Annotations { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
public PropertyInfo PropertyInfo
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Tru ValueType { get; set; }
|
||||
|
||||
|
||||
/*
|
||||
public bool Serilize
|
||||
{
|
||||
get;set;
|
||||
}
|
||||
*/
|
||||
//bool ReadOnly;
|
||||
//EPTypes::DataType ReturnType;
|
||||
public PropertyPermission Permission
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
//public bool IsNullable { get; set; }
|
||||
|
||||
public bool HasHistory
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/*
|
||||
public PropertyType Mode
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}*/
|
||||
|
||||
//public string ReadAnnotation
|
||||
//{
|
||||
// get;
|
||||
// set;
|
||||
//}
|
||||
|
||||
//public string WriteAnnotation
|
||||
//{
|
||||
// get;
|
||||
// set;
|
||||
//}
|
||||
|
||||
/*
|
||||
public bool Storable
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}*/
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name}: {ValueType}";
|
||||
}
|
||||
|
||||
public static (uint, PropertyDef) Parse(byte[] data, uint offset, byte index, bool inherited)
|
||||
{
|
||||
var oOffset = offset;
|
||||
|
||||
|
||||
|
||||
var hasAnnotation = ((data[offset] & 0x8) == 0x8);
|
||||
var hasHistory = ((data[offset] & 1) == 1);
|
||||
var permission = (PropertyPermission)((data[offset++] >> 1) & 0x3);
|
||||
var name = data.GetString(offset + 1, data[offset]);
|
||||
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
var (dts, valueType) = Tru.Parse(data, offset);
|
||||
|
||||
offset += dts;
|
||||
|
||||
Map<string, string> annotations = null;
|
||||
|
||||
// arguments
|
||||
if (hasAnnotation) // Annotation ?
|
||||
{
|
||||
var (len, anns) = Codec.ParseSync(data, offset, null);
|
||||
|
||||
if (anns is Map<string, string> map)
|
||||
annotations = map;
|
||||
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return (offset - oOffset, new PropertyDef()
|
||||
{
|
||||
Index = index,
|
||||
Name = name,
|
||||
Inherited = inherited,
|
||||
Permission = permission,
|
||||
HasHistory = hasHistory,
|
||||
ValueType = valueType,
|
||||
Annotations = annotations
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public byte[] Compose()
|
||||
{
|
||||
var name = DC.ToBytes(Name);
|
||||
|
||||
var pv = ((byte)(Permission) << 1) | (HasHistory ? 1 : 0);
|
||||
|
||||
if (Inherited)
|
||||
pv |= 0x80;
|
||||
|
||||
//if (WriteAnnotation != null && ReadAnnotation != null)
|
||||
//{
|
||||
// var rexp = DC.ToBytes(ReadAnnotation);
|
||||
// var wexp = DC.ToBytes(WriteAnnotation);
|
||||
// return new BinaryList()
|
||||
// .AddUInt8((byte)(0x38 | pv))
|
||||
// .AddUInt8((byte)name.Length)
|
||||
// .AddUInt8Array(name)
|
||||
// .AddUInt8Array(ValueType.Compose())
|
||||
// .AddInt32(wexp.Length)
|
||||
// .AddUInt8Array(wexp)
|
||||
// .AddInt32(rexp.Length)
|
||||
// .AddUInt8Array(rexp)
|
||||
// .ToArray();
|
||||
//}
|
||||
//else if (WriteAnnotation != null)
|
||||
//{
|
||||
// var wexp = DC.ToBytes(WriteAnnotation);
|
||||
// return new BinaryList()
|
||||
// .AddUInt8((byte)(0x30 | pv))
|
||||
// .AddUInt8((byte)name.Length)
|
||||
// .AddUInt8Array(name)
|
||||
// .AddUInt8Array(ValueType.Compose())
|
||||
// .AddInt32(wexp.Length)
|
||||
// .AddUInt8Array(wexp)
|
||||
// .ToArray();
|
||||
//}
|
||||
//else if (ReadAnnotation != null)
|
||||
//{
|
||||
// var rexp = DC.ToBytes(ReadAnnotation);
|
||||
// return new BinaryList()
|
||||
// .AddUInt8((byte)(0x28 | pv))
|
||||
// .AddUInt8((byte)name.Length)
|
||||
// .AddUInt8Array(name)
|
||||
// .AddUInt8Array(ValueType.Compose())
|
||||
// .AddInt32(rexp.Length)
|
||||
// .AddUInt8Array(rexp)
|
||||
// .ToArray();
|
||||
//}
|
||||
if (Annotations != null)
|
||||
{
|
||||
var rexp = Codec.Compose(Annotations, null, null);
|
||||
return new BinaryList()
|
||||
.AddUInt8((byte)(0x28 | pv))
|
||||
.AddUInt8((byte)name.Length)
|
||||
.AddUInt8Array(name)
|
||||
.AddUInt8Array(ValueType.Compose())
|
||||
.AddUInt8Array(rexp)
|
||||
.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
return new BinaryList()
|
||||
.AddUInt8((byte)(0x20 | pv))
|
||||
.AddUInt8((byte)name.Length)
|
||||
.AddUInt8Array(name)
|
||||
.AddUInt8Array(ValueType.Compose())
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static PropertyDef MakePropertyDef(Type type, PropertyInfo pi, string name, byte index, PropertyPermission permission, TypeDef schema)
|
||||
{
|
||||
var genericPropType = pi.PropertyType.IsGenericType ? pi.PropertyType.GetGenericTypeDefinition() : null;
|
||||
|
||||
var propType = genericPropType == typeof(PropertyContext<>) ?
|
||||
Tru.FromType(pi.PropertyType.GetGenericArguments()[0]) :
|
||||
Tru.FromType(pi.PropertyType);
|
||||
|
||||
if (propType == null)
|
||||
throw new Exception($"Unsupported type `{pi.PropertyType}` in property `{type.Name}.{pi.Name}`");
|
||||
|
||||
var annotationAttrs = pi.GetCustomAttributes<AnnotationAttribute>(true);
|
||||
var storageAttr = pi.GetCustomAttribute<StorageAttribute>(true);
|
||||
|
||||
//var nullabilityContext = new NullabilityInfoContext();
|
||||
//propType.Nullable = nullabilityContext.Create(pi).ReadState is NullabilityState.Nullable;
|
||||
|
||||
var nullableAttr = pi.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute");
|
||||
var nullableContextAttr = pi.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableContextAttribute");
|
||||
|
||||
var nullableAttrFlags = (nullableAttr?.GetType().GetField("NullableFlags")?.GetValue(nullableAttr) as byte[] ?? new byte[0]).ToList();
|
||||
var nullableContextAttrFlag = (byte)(nullableContextAttr?.GetType().GetField("Flag")?.GetValue(nullableContextAttr) ?? (byte)0);
|
||||
|
||||
|
||||
//var nullableAttr = pi.GetCustomAttribute<NullableAttribute>(true);
|
||||
//var flags = ((byte[]) nullableAttr?.NullableFlags ?? new byte[0]).ToList();
|
||||
|
||||
if (nullableAttrFlags.Count > 0 && genericPropType == typeof(PropertyContext<>))
|
||||
nullableAttrFlags.RemoveAt(0);
|
||||
|
||||
if (nullableContextAttrFlag == 2)
|
||||
{
|
||||
if (nullableAttrFlags.Count == 1)
|
||||
propType.SetNotNull(nullableAttrFlags.FirstOrDefault());
|
||||
else
|
||||
propType.SetNotNull(nullableAttrFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nullableAttrFlags.Count == 1)
|
||||
propType.SetNull(nullableAttrFlags.FirstOrDefault());
|
||||
else
|
||||
propType.SetNull(nullableAttrFlags);
|
||||
}
|
||||
|
||||
|
||||
Map<string, string> annotations = null;
|
||||
|
||||
if (annotationAttrs != null && annotationAttrs.Count() > 0)
|
||||
{
|
||||
annotations = new Map<string, string>();
|
||||
foreach (var attr in annotationAttrs)
|
||||
annotations.Add(attr.Key, attr.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
annotations = new Map<string, string>();
|
||||
annotations.Add("", GetTypeAnnotationName(pi.PropertyType));
|
||||
}
|
||||
|
||||
|
||||
return new PropertyDef()
|
||||
{
|
||||
Name = name,
|
||||
Index = index,
|
||||
Inherited = pi.DeclaringType != type,
|
||||
ValueType = propType,
|
||||
PropertyInfo = pi,
|
||||
HasHistory = storageAttr == null ? false : storageAttr.Mode == StorageMode.History,
|
||||
Permission = permission,
|
||||
Annotations = annotations,
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static string GetTypeAnnotationName(Type type)
|
||||
{
|
||||
var nullType = Nullable.GetUnderlyingType(type);
|
||||
if (nullType == null)
|
||||
return type.Name;
|
||||
else
|
||||
return type.Name + "?";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
759
Libraries/Esiur/Data/Types/TypeDef.cs
Normal file
759
Libraries/Esiur/Data/Types/TypeDef.cs
Normal file
@@ -0,0 +1,759 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using Esiur.Misc;
|
||||
using Esiur.Data;
|
||||
using Esiur.Core;
|
||||
using System.Security.Cryptography;
|
||||
using Esiur.Proxy;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Esiur.Resource;
|
||||
using Esiur.Protocol;
|
||||
|
||||
namespace Esiur.Data.Types;
|
||||
|
||||
|
||||
public class TypeDef
|
||||
{
|
||||
|
||||
protected Uuid typeId;
|
||||
protected Uuid? parentId;
|
||||
|
||||
public Map<string, string> Annotations { get; set; }
|
||||
|
||||
string typeName;
|
||||
List<FunctionDef> functions = new List<FunctionDef>();
|
||||
List<EventDef> events = new List<EventDef>();
|
||||
List<PropertyDef> properties = new List<PropertyDef>();
|
||||
List<AttributeDef> attributes = new List<AttributeDef>();
|
||||
List<ConstantDef> constants = new();
|
||||
int version;
|
||||
TypeDefKind typeDefKind;
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return typeName;
|
||||
}
|
||||
|
||||
|
||||
protected byte[] content;
|
||||
|
||||
public Uuid? ParentId => parentId;
|
||||
|
||||
public byte[] Content
|
||||
{
|
||||
get { return content; }
|
||||
}
|
||||
|
||||
public TypeDefKind Kind => typeDefKind;
|
||||
|
||||
|
||||
public Type DefinedType { get; set; }
|
||||
public Type ParentDefinedType { get; set; }
|
||||
|
||||
|
||||
public EventDef GetEventDefByName(string eventName)
|
||||
{
|
||||
foreach (var i in events)
|
||||
if (i.Name == eventName)
|
||||
return i;
|
||||
return null;
|
||||
}
|
||||
|
||||
public EventDef GetEventDefByIndex(byte index)
|
||||
{
|
||||
foreach (var i in events)
|
||||
if (i.Index == index)
|
||||
return i;
|
||||
return null;
|
||||
}
|
||||
|
||||
public FunctionDef GetFunctionDefByName(string functionName)
|
||||
{
|
||||
foreach (var i in functions)
|
||||
if (i.Name == functionName)
|
||||
return i;
|
||||
return null;
|
||||
}
|
||||
public FunctionDef GetFunctionDefByIndex(byte index)
|
||||
{
|
||||
foreach (var i in functions)
|
||||
if (i.Index == index)
|
||||
return i;
|
||||
return null;
|
||||
}
|
||||
|
||||
public PropertyDef GetPropertyDefByIndex(byte index)
|
||||
{
|
||||
foreach (var i in properties)
|
||||
if (i.Index == index)
|
||||
return i;
|
||||
return null;
|
||||
}
|
||||
|
||||
public PropertyDef GetPropertyDefByName(string propertyName)
|
||||
{
|
||||
foreach (var i in properties)
|
||||
if (i.Name == propertyName)
|
||||
return i;
|
||||
return null;
|
||||
}
|
||||
|
||||
public AttributeDef GetAttributeDef(string attributeName)
|
||||
{
|
||||
foreach (var i in attributes)
|
||||
if (i.Name == attributeName)
|
||||
return i;
|
||||
return null;
|
||||
}
|
||||
|
||||
public Uuid Id
|
||||
{
|
||||
get { return typeId; }
|
||||
}
|
||||
public string Name
|
||||
{
|
||||
get { return typeName; }
|
||||
}
|
||||
|
||||
|
||||
public FunctionDef[] Functions
|
||||
{
|
||||
get { return functions.ToArray(); }
|
||||
}
|
||||
|
||||
public EventDef[] Events
|
||||
{
|
||||
get { return events.ToArray(); }
|
||||
}
|
||||
|
||||
public PropertyDef[] Properties
|
||||
{
|
||||
get { return properties.ToArray(); }
|
||||
}
|
||||
|
||||
public ConstantDef[] Constants => constants.ToArray();
|
||||
|
||||
public TypeDef()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static Uuid GetTypeUUID(Type type)
|
||||
{
|
||||
var attr = type.GetCustomAttribute<TypeIdAttribute>();
|
||||
if (attr != null)
|
||||
return attr.Id;
|
||||
|
||||
var tn = Encoding.UTF8.GetBytes(GetTypeName(type));
|
||||
var hash = SHA256.Create().ComputeHash(tn).Clip(0, 16);
|
||||
hash[6] = (byte)((hash[6] & 0xF) | 0x80);
|
||||
hash[8] = (byte)((hash[8] & 0xF) | 0x80);
|
||||
|
||||
var rt = new Uuid(hash);
|
||||
return rt;
|
||||
}
|
||||
|
||||
static Type[] GetDistributedTypes(Type type)
|
||||
{
|
||||
if (type.IsArray)
|
||||
return GetDistributedTypes(type.GetElementType());
|
||||
else if (type.IsEnum)
|
||||
return new Type[] { type };
|
||||
else if (Codec.ImplementsInterface(type, typeof(IRecord))
|
||||
|| Codec.ImplementsInterface(type, typeof(IResource)))
|
||||
{
|
||||
return new Type[] { type };
|
||||
}
|
||||
else if (type.IsGenericType)
|
||||
{
|
||||
var genericType = type.GetGenericTypeDefinition();
|
||||
var genericTypeArgs = type.GetGenericArguments();
|
||||
|
||||
if (genericType == typeof(List<>)
|
||||
|| genericType == typeof(PropertyContext<>)
|
||||
|| genericType == typeof(AsyncReply<>)
|
||||
|| genericType == typeof(ResourceLink<>))
|
||||
{
|
||||
return GetDistributedTypes(genericTypeArgs[0]);
|
||||
}
|
||||
else if (genericType == typeof(Tuple<>)
|
||||
|| genericType == typeof(Map<,>))
|
||||
{
|
||||
var rt = new List<Type>();
|
||||
for (var i = 0; i < genericTypeArgs.Length; i++)
|
||||
{
|
||||
var depTypes = GetDistributedTypes(genericTypeArgs[i]);
|
||||
foreach (var depType in depTypes)
|
||||
if (!rt.Contains(depType))
|
||||
rt.Add(depType);
|
||||
}
|
||||
|
||||
return rt.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new Type[0];
|
||||
}
|
||||
|
||||
|
||||
public static TypeDef[] GetDependencies(TypeDef schema, Warehouse warehouse)
|
||||
{
|
||||
|
||||
var list = new List<TypeDef>();
|
||||
|
||||
// Add self
|
||||
list.Add(schema);
|
||||
|
||||
|
||||
Action<TypeDef, List<TypeDef>> getDependenciesFunc = null;
|
||||
|
||||
getDependenciesFunc = (TypeDef sch, List<TypeDef> bag) =>
|
||||
{
|
||||
if (schema.DefinedType == null)
|
||||
return;
|
||||
|
||||
// Add parents
|
||||
var parentType = sch.ParentDefinedType;
|
||||
|
||||
// Get parents
|
||||
while (parentType != null)
|
||||
{
|
||||
var parentTypeDef = warehouse.GetTypeDefByType(parentType);
|
||||
if (parentTypeDef != null)
|
||||
{
|
||||
list.Add(parentTypeDef);
|
||||
parentType = parentTypeDef.ParentDefinedType;
|
||||
}
|
||||
}
|
||||
|
||||
// functions
|
||||
foreach (var f in sch.functions)
|
||||
{
|
||||
var functionReturnTypes = GetDistributedTypes(f.MethodInfo.ReturnType);
|
||||
|
||||
foreach (var functionReturnType in functionReturnTypes)
|
||||
{
|
||||
var functionReturnTypeDef = warehouse.GetTypeDefByType(functionReturnType);
|
||||
if (functionReturnTypeDef != null)
|
||||
{
|
||||
if (!bag.Contains(functionReturnTypeDef))
|
||||
{
|
||||
list.Add(functionReturnTypeDef);
|
||||
getDependenciesFunc(functionReturnTypeDef, bag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var args = f.MethodInfo.GetParameters();
|
||||
|
||||
for (var i = 0; i < args.Length - 1; i++)
|
||||
{
|
||||
var fpTypes = GetDistributedTypes(args[i].ParameterType);
|
||||
|
||||
foreach (var fpType in fpTypes)
|
||||
{
|
||||
var fpt = warehouse.GetTypeDefByType(fpType);
|
||||
if (fpt != null)
|
||||
{
|
||||
if (!bag.Contains(fpt))
|
||||
{
|
||||
bag.Add(fpt);
|
||||
getDependenciesFunc(fpt, bag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// skip EpConnection argument
|
||||
if (args.Length > 0)
|
||||
{
|
||||
var last = args.Last();
|
||||
if (last.ParameterType != typeof(EpConnection))
|
||||
{
|
||||
|
||||
var fpTypes = GetDistributedTypes(last.ParameterType);
|
||||
|
||||
foreach (var fpType in fpTypes)
|
||||
{
|
||||
var fpt = warehouse.GetTypeDefByType(fpType);
|
||||
if (fpt != null)
|
||||
{
|
||||
if (!bag.Contains(fpt))
|
||||
{
|
||||
bag.Add(fpt);
|
||||
getDependenciesFunc(fpt, bag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// properties
|
||||
foreach (var p in sch.properties)
|
||||
{
|
||||
var propertyTypes = GetDistributedTypes(p.PropertyInfo.PropertyType);
|
||||
|
||||
foreach (var propertyType in propertyTypes)
|
||||
{
|
||||
var propertyTypeDef = warehouse.GetTypeDefByType(propertyType);
|
||||
if (propertyTypeDef != null)
|
||||
{
|
||||
if (!bag.Contains(propertyTypeDef))
|
||||
{
|
||||
bag.Add(propertyTypeDef);
|
||||
getDependenciesFunc(propertyTypeDef, bag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// events
|
||||
foreach (var e in sch.events)
|
||||
{
|
||||
var eventTypes = GetDistributedTypes(e.EventInfo.EventHandlerType.GenericTypeArguments[0]);
|
||||
|
||||
foreach (var eventType in eventTypes)
|
||||
{
|
||||
var eventTypeDef = warehouse.GetTypeDefByType(eventType);
|
||||
|
||||
if (eventTypeDef != null)
|
||||
{
|
||||
if (!bag.Contains(eventTypeDef))
|
||||
{
|
||||
bag.Add(eventTypeDef);
|
||||
getDependenciesFunc(eventTypeDef, bag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getDependenciesFunc(schema, list);
|
||||
return list.Distinct().ToArray();
|
||||
}
|
||||
|
||||
|
||||
public static string GetTypeName(Type type, char separator = '.')
|
||||
{
|
||||
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var index = type.Name.IndexOf("`");
|
||||
var name = $"{type.Namespace}{separator}{((index > -1) ? type.Name.Substring(0, index) : type.Name)}Of";
|
||||
foreach (var t in type.GenericTypeArguments)
|
||||
name += GetTypeName(t, '_');
|
||||
|
||||
return name;
|
||||
}
|
||||
else
|
||||
return $"{type.Namespace?.Replace('.', separator) ?? "Global"}{separator}{type.Name}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public bool IsWrapper { get; private set; }
|
||||
|
||||
public TypeDef(Type type, Warehouse warehouse = null)
|
||||
{
|
||||
if (Codec.ImplementsInterface(type, typeof(IResource)))
|
||||
typeDefKind = TypeDefKind.Resource;
|
||||
else if (Codec.ImplementsInterface(type, typeof(IRecord)))
|
||||
typeDefKind = TypeDefKind.Record;
|
||||
else if (type.IsEnum)
|
||||
typeDefKind = TypeDefKind.Enum;
|
||||
else
|
||||
throw new Exception("Type must implement IResource, IRecord or inherit from DistributedResource.");
|
||||
|
||||
IsWrapper = Codec.InheritsClass(type, typeof(EpResource));
|
||||
|
||||
type = ResourceProxy.GetBaseType(type);
|
||||
|
||||
DefinedType = type;
|
||||
|
||||
typeName = GetTypeName(type);
|
||||
|
||||
// set guid
|
||||
typeId = GetTypeUUID(type);
|
||||
|
||||
if (warehouse != null)
|
||||
warehouse.RegisterTypeDef(this);
|
||||
|
||||
var hierarchy = GetHierarchy(type);
|
||||
|
||||
if (hierarchy.ContainsKey(MemberTypes.Field))
|
||||
{
|
||||
foreach (var cd in hierarchy[MemberTypes.Field])
|
||||
{
|
||||
constants.Add(ConstantDef.MakeConstantDef
|
||||
(type, (FieldInfo)cd.GetMemberInfo(), cd.Index, cd.Name, this));
|
||||
}
|
||||
}
|
||||
|
||||
if (hierarchy.ContainsKey(MemberTypes.Property))
|
||||
{
|
||||
foreach (var pd in hierarchy[MemberTypes.Property])
|
||||
{
|
||||
properties.Add(PropertyDef.MakePropertyDef
|
||||
(type, (PropertyInfo)pd.GetMemberInfo(), pd.Name, pd.Index, pd.PropertyPermission, this));
|
||||
}
|
||||
}
|
||||
|
||||
if (typeDefKind == TypeDefKind.Resource)
|
||||
{
|
||||
if (hierarchy.ContainsKey(MemberTypes.Method))
|
||||
{
|
||||
foreach (var fd in hierarchy[MemberTypes.Method])
|
||||
{
|
||||
functions.Add(FunctionDef.MakeFunctionDef
|
||||
(type, (MethodInfo)fd.GetMemberInfo(), fd.Index, fd.Name, this));
|
||||
}
|
||||
}
|
||||
|
||||
if (hierarchy.ContainsKey(MemberTypes.Event))
|
||||
{
|
||||
foreach (var ed in hierarchy[MemberTypes.Event])
|
||||
{
|
||||
events.Add(EventDef.MakeEventDef
|
||||
(type, (EventInfo)ed.GetMemberInfo(), ed.Index, ed.Name, this));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// add attributes
|
||||
var attrs = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(x => x.GetCustomAttribute<AttributeAttribute>() != null);
|
||||
|
||||
foreach (var attr in attrs)
|
||||
{
|
||||
var attrAttr = attr.GetCustomAttribute<AttributeAttribute>();
|
||||
|
||||
attributes.Add(AttributeDef
|
||||
.MakeAttributeDef(type, attr, 0, attrAttr?.Name ?? attr.Name, this));
|
||||
}
|
||||
|
||||
|
||||
// bake it binarily
|
||||
var b = new BinaryList();
|
||||
|
||||
// find the first parent type that implements IResource
|
||||
|
||||
|
||||
var hasParent = HasParent(type);
|
||||
var classAnnotations = type.GetCustomAttributes<AnnotationAttribute>(false);
|
||||
|
||||
|
||||
var hasClassAnnotation = (classAnnotations != null) && (classAnnotations.Count() > 0);
|
||||
|
||||
var typeNameBytes = DC.ToBytes(typeName);
|
||||
|
||||
b.AddUInt8((byte)((hasParent ? 0x80 : 0) | (hasClassAnnotation ? 0x40 : 0x0) | (byte)typeDefKind))
|
||||
.AddUUID(typeId)
|
||||
.AddUInt8((byte)typeNameBytes.Length)
|
||||
.AddUInt8Array(typeNameBytes);
|
||||
|
||||
if (hasParent)
|
||||
{
|
||||
// find the first parent type that implements IResource
|
||||
ParentDefinedType = ResourceProxy.GetBaseType(type.BaseType);
|
||||
var parentId = GetTypeUUID(ParentDefinedType);
|
||||
b.AddUUID(parentId);
|
||||
}
|
||||
|
||||
if (hasClassAnnotation)
|
||||
{
|
||||
Annotations = new Map<string, string>();
|
||||
|
||||
foreach (var ann in classAnnotations)
|
||||
Annotations.Add(ann.Key, ann.Value);
|
||||
|
||||
var classAnnotationBytes = Codec.Compose (Annotations, null, null);
|
||||
|
||||
b.AddUInt8Array(classAnnotationBytes);
|
||||
|
||||
}
|
||||
|
||||
b.AddInt32(version)
|
||||
.AddUInt16((ushort)(functions.Count + properties.Count + events.Count + constants.Count));
|
||||
|
||||
foreach (var ft in functions)
|
||||
b.AddUInt8Array(ft.Compose());
|
||||
foreach (var pt in properties)
|
||||
b.AddUInt8Array(pt.Compose());
|
||||
foreach (var et in events)
|
||||
b.AddUInt8Array(et.Compose());
|
||||
foreach (var ct in constants)
|
||||
b.AddUInt8Array(ct.Compose());
|
||||
|
||||
content = b.ToArray();
|
||||
|
||||
}
|
||||
|
||||
public static bool HasParent(Type type)
|
||||
{
|
||||
var parent = type.BaseType;
|
||||
|
||||
|
||||
while (parent != null)
|
||||
{
|
||||
if (parent == typeof(Esiur.Resource.Resource)
|
||||
|| parent == typeof(Record)
|
||||
|| parent == typeof(EntryPoint))
|
||||
return false;
|
||||
|
||||
if (parent.GetInterfaces().Contains(typeof(IResource))
|
||||
|| parent.GetInterfaces().Contains(typeof(IRecord)))
|
||||
return true;
|
||||
|
||||
parent = parent.BaseType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static Dictionary<MemberTypes, List<MemberData>> GetHierarchy(Type type)
|
||||
{
|
||||
var members = new List<MemberData>();
|
||||
|
||||
var order = 0;
|
||||
|
||||
while (type != null)
|
||||
{
|
||||
var classIsPublic = type.IsEnum || (type.GetCustomAttribute<ExportAttribute>() != null);
|
||||
|
||||
if (classIsPublic)
|
||||
{
|
||||
// get public instance members only.
|
||||
var mis = type.GetMembers(BindingFlags.Public | BindingFlags.Instance
|
||||
| BindingFlags.DeclaredOnly | BindingFlags.Static)
|
||||
.Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field
|
||||
|| x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method)
|
||||
.Where(x => !(x is FieldInfo c && !c.IsStatic))
|
||||
.Where(x => x.GetCustomAttribute<IgnoreAttribute>() == null)
|
||||
.Where(x => x.Name != "Instance")
|
||||
.Where(x => x.Name != "Trigger")
|
||||
.Where(x => !(x is MethodInfo m && m.IsSpecialName))
|
||||
.Where(x => !(x is EventInfo e &&
|
||||
!(e.EventHandlerType.IsGenericType &&
|
||||
(e.EventHandlerType.GetGenericTypeDefinition() == typeof(ResourceEventHandler<>)
|
||||
|| e.EventHandlerType.GetGenericTypeDefinition() == typeof(CustomResourceEventHandler<>))
|
||||
)
|
||||
))
|
||||
.Select(x => new MemberData(
|
||||
info: x,
|
||||
order: order
|
||||
))
|
||||
.OrderBy(x => x.Name);
|
||||
|
||||
members.AddRange(mis.ToArray());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// allow private and public members that are marked with [Export] attribute.
|
||||
var mis = type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
| BindingFlags.DeclaredOnly | BindingFlags.Static)
|
||||
.Where(x => x.MemberType == MemberTypes.Property || x.MemberType == MemberTypes.Field
|
||||
|| x.MemberType == MemberTypes.Event || x.MemberType == MemberTypes.Method)
|
||||
.Where(x => !(x is FieldInfo c && !c.IsStatic))
|
||||
.Where(x => x.GetCustomAttribute<ExportAttribute>() != null)
|
||||
.Where(x => !(x is MethodInfo m && m.IsSpecialName))
|
||||
.Select(x => new MemberData(
|
||||
info: x,
|
||||
order: order
|
||||
))
|
||||
.OrderBy(x => x.Name);
|
||||
|
||||
members.AddRange(mis.ToArray());
|
||||
|
||||
}
|
||||
|
||||
type = type.BaseType;
|
||||
|
||||
if (type == null
|
||||
|| type == typeof(Esiur.Resource.Resource)
|
||||
|| type == typeof(Record)
|
||||
|| type == typeof(EntryPoint))
|
||||
break;
|
||||
|
||||
if (type.GetInterfaces().Contains(typeof(IResource))
|
||||
|| type.GetInterfaces().Contains(typeof(IRecord)))
|
||||
{
|
||||
order++;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// round 2: check for duplicates
|
||||
for (var i = 0; i < members.Count; i++)
|
||||
{
|
||||
var mi = members[i];
|
||||
for (var j = i + 1; j < members.Count; j++)
|
||||
{
|
||||
var pi = members[j];
|
||||
if (pi.Info.MemberType != mi.Info.MemberType)
|
||||
continue;
|
||||
|
||||
//if (ci.Info.Name == mi.Info.Name && ci.Order == mi.Order)
|
||||
// throw new Exception($"Method overload is not supported. Method '{ci.Info.Name}'.");
|
||||
|
||||
if (pi.Name == mi.Name)
|
||||
{
|
||||
if (pi.Order == mi.Order)
|
||||
throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'.");
|
||||
else
|
||||
{
|
||||
// @TODO: check for return type and parameters they must match
|
||||
if (pi.Info.Name != mi.Info.Name)
|
||||
throw new Exception($"Duplicate definitions for members public name '{mi.Info.DeclaringType.Name}:{mi.Info.Name}' and '{pi.Info.DeclaringType.Name}:{pi.Info.Name}'.");
|
||||
}
|
||||
|
||||
mi.Parent = pi;
|
||||
pi.Child = mi;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// assign indexes
|
||||
var groups = members.Where(x => x.Parent == null)
|
||||
.OrderBy(x => x.Name).OrderByDescending(x => x.Order)
|
||||
.GroupBy(x => x.Info.MemberType);
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
byte index = 0;
|
||||
foreach (var mi in group)
|
||||
{
|
||||
//if (mi.Parent == null)
|
||||
mi.Index = index++;
|
||||
}
|
||||
}
|
||||
|
||||
var rt = groups.ToDictionary(g => g.Key, g => g.ToList());
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
|
||||
public static TypeDef Parse(byte[] data)
|
||||
{
|
||||
return Parse(data, 0, (uint)data.Length);
|
||||
}
|
||||
|
||||
|
||||
public static TypeDef Parse(byte[] data, uint offset, uint contentLength)
|
||||
{
|
||||
|
||||
uint ends = offset + contentLength;
|
||||
|
||||
uint oOffset = offset;
|
||||
|
||||
// start parsing...
|
||||
|
||||
var od = new TypeDef();
|
||||
od.content = data.Clip(offset, contentLength);
|
||||
|
||||
var hasParent = (data[offset] & 0x80) > 0;
|
||||
var hasClassAnnotation = (data[offset] & 0x40) > 0;
|
||||
|
||||
od.typeDefKind = (TypeDefKind)(data[offset++] & 0xF);
|
||||
|
||||
od.typeId = data.GetUUID(offset);
|
||||
offset += 16;
|
||||
od.typeName = data.GetString(offset + 1, data[offset]);
|
||||
offset += (uint)data[offset] + 1;
|
||||
|
||||
|
||||
if (hasParent)
|
||||
{
|
||||
od.parentId = data.GetUUID(offset);
|
||||
offset += 16;
|
||||
}
|
||||
|
||||
if (hasClassAnnotation)
|
||||
{
|
||||
var (len, anns) = Codec.ParseSync(data, offset, null);
|
||||
|
||||
if (anns is Map<string, string> annotations)
|
||||
od.Annotations = annotations;
|
||||
|
||||
offset += len;
|
||||
}
|
||||
|
||||
od.version = data.GetInt32(offset, Endian.Little);
|
||||
offset += 4;
|
||||
|
||||
ushort methodsCount = data.GetUInt16(offset, Endian.Little);
|
||||
offset += 2;
|
||||
|
||||
byte functionIndex = 0;
|
||||
byte propertyIndex = 0;
|
||||
byte eventIndex = 0;
|
||||
byte constantIndex = 0;
|
||||
|
||||
for (int i = 0; i < methodsCount; i++)
|
||||
{
|
||||
var inherited = (data[offset] & 0x80) > 0;
|
||||
var type = (data[offset] >> 5) & 0x3;
|
||||
|
||||
if (type == 0) // function
|
||||
{
|
||||
var (len, ft) = FunctionDef.Parse(data, offset, functionIndex++, inherited);
|
||||
offset += len;
|
||||
od.functions.Add(ft);
|
||||
}
|
||||
else if (type == 1) // property
|
||||
{
|
||||
var (len, pt) = PropertyDef.Parse(data, offset, propertyIndex++, inherited);
|
||||
offset += len;
|
||||
od.properties.Add(pt);
|
||||
|
||||
}
|
||||
else if (type == 2) // Event
|
||||
{
|
||||
var (len, et) = EventDef.Parse(data, offset, eventIndex++, inherited);
|
||||
offset += len;
|
||||
od.events.Add(et);
|
||||
}
|
||||
// constant
|
||||
else if (type == 3)
|
||||
{
|
||||
var (len, ct) = ConstantDef.Parse(data, offset, constantIndex++, inherited);
|
||||
offset += len;
|
||||
od.constants.Add(ct);
|
||||
}
|
||||
}
|
||||
|
||||
return od;
|
||||
}
|
||||
|
||||
public Map<byte, object> CastProperties(Map<string, object> properties)
|
||||
{
|
||||
var rt = new Map<byte, object>();
|
||||
foreach (var kv in properties)
|
||||
{
|
||||
var pt = GetPropertyDefByName(kv.Key);
|
||||
if (pt == null) continue;
|
||||
rt.Add(pt.Index, kv.Value);
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
13
Libraries/Esiur/Data/Types/TypeDefKind.cs
Normal file
13
Libraries/Esiur/Data/Types/TypeDefKind.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data.Types;
|
||||
|
||||
public enum TypeDefKind : byte
|
||||
{
|
||||
Resource,
|
||||
Record,
|
||||
Enum,
|
||||
Function
|
||||
}
|
||||
18
Libraries/Esiur/Data/UInt128.cs
Normal file
18
Libraries/Esiur/Data/UInt128.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public struct UInt128
|
||||
{
|
||||
public UInt128(ulong lsb, ulong msb)
|
||||
{
|
||||
this.MSB = msb;
|
||||
this.LSB = lsb;
|
||||
}
|
||||
|
||||
public ulong MSB { get;set; }
|
||||
public ulong LSB { get;set; }
|
||||
}
|
||||
}
|
||||
144
Libraries/Esiur/Data/Uuid.cs
Normal file
144
Libraries/Esiur/Data/Uuid.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.FlowAnalysis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
//[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct Uuid
|
||||
{
|
||||
//4e7db2d8-a785-1b99-1854-4b4018bc5677
|
||||
//byte a1;
|
||||
//byte a2;
|
||||
//byte a3;
|
||||
//byte a4;
|
||||
//byte b1;
|
||||
//byte b2;
|
||||
//byte c1;
|
||||
//byte c2;
|
||||
//byte d1;
|
||||
//byte d2;
|
||||
//byte e1;
|
||||
//byte e2;
|
||||
//byte e3;
|
||||
//byte e4;
|
||||
//byte e5;
|
||||
//byte e6;
|
||||
|
||||
public byte[] Data { get; private set; }
|
||||
|
||||
|
||||
public Uuid(byte[] data, uint offset)
|
||||
{
|
||||
if (offset + 16 > data.Length)
|
||||
throw new Exception("UUID data size must be at least 16 bytes");
|
||||
|
||||
Data = DC.Clip(data, offset, 16);
|
||||
|
||||
//a1 = data[offset++];
|
||||
//a2 = data[offset++];
|
||||
//a3 = data[offset++];
|
||||
//a4 = data[offset++];
|
||||
//b1 = data[offset++];
|
||||
//b2 = data[offset++];
|
||||
//c1 = data[offset++];
|
||||
//c2 = data[offset++];
|
||||
//d1 = data[offset++];
|
||||
//d2 = data[offset++];
|
||||
//e1 = data[offset++];
|
||||
//e2 = data[offset++];
|
||||
//e3 = data[offset++];
|
||||
//e4 = data[offset++];
|
||||
//e5 = data[offset++];
|
||||
//e6 = data[offset++];
|
||||
}
|
||||
|
||||
public unsafe override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
fixed (byte* p = Data)
|
||||
{
|
||||
ulong u0 = *(ulong*)p;
|
||||
ulong u1 = *(ulong*)(p + 8);
|
||||
|
||||
// simple mixing of two 64-bit halves
|
||||
return ((int)u0 ^ (int)(u0 >> 32)) ^
|
||||
((int)u1 ^ (int)(u1 >> 32));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Uuid(byte[] data) {
|
||||
|
||||
if (data.Length != 16)
|
||||
throw new Exception("UUID data size must be 16 bytes");
|
||||
|
||||
Data = data;
|
||||
//a1 = data[0];
|
||||
//a2 = data[1];
|
||||
//a3 = data[2];
|
||||
//a4 = data[3];
|
||||
//b1 = data[4];
|
||||
//b2 = data[5];
|
||||
//c1 = data[6];
|
||||
//c2 = data[7];
|
||||
//d1 = data[8];
|
||||
//d2 = data[9];
|
||||
//e1 = data[10];
|
||||
//e2 = data[11];
|
||||
//e3 = data[12];
|
||||
//e4 = data[13];
|
||||
//e5 = data[14];
|
||||
//e6 = data[15];
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
return $"{DC.ToHex(Data, 0, 4, null)}-{DC.ToHex(Data, 4, 2, null)}-{DC.ToHex(Data, 6, 2, null)}-{DC.ToHex(Data, 8, 2, null)}-{DC.ToHex(Data, 10, 6, null)}";
|
||||
|
||||
//return $"{a1.ToString("x2")}{a2.ToString("x2")}{a3.ToString("x2")}{a4.ToString("x2")}-{b1.ToString("x2")}{b2.ToString("x2")}-{c1.ToString("x2")}{c2.ToString("x2")}-{d1.ToString("x2")}{d2.ToString("x2")}-{e1.ToString("x2")}{e2.ToString("x2")}{e3.ToString("x2")}{e4.ToString("x2")}{e5.ToString("x2")}{e6.ToString("x2")}";
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is Uuid b)
|
||||
return Data.SequenceEqual(b.Data);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool operator == (Uuid a, Uuid b)
|
||||
{
|
||||
return a.Data.SequenceEqual(b.Data);
|
||||
|
||||
//return a.a1 == b.a1
|
||||
// && a.a2 == b.a2
|
||||
// && a.a3 == b.a3
|
||||
// && a.a4 == b.a4
|
||||
// && a.b1 == b.b1
|
||||
// && a.b2 == b.b2
|
||||
// && a.c1 == b.c1
|
||||
// && a.c2 == b.c2
|
||||
// && a.d1 == b.d1
|
||||
// && a.d2 == b.d2
|
||||
// && a.e1 == b.e1
|
||||
// && a.e2 == b.e2
|
||||
// && a.e3 == b.e3
|
||||
// && a.e4 == b.e4
|
||||
// && a.e5 == b.e5
|
||||
// && a.e6 == b.e6;
|
||||
}
|
||||
|
||||
public static bool operator !=(Uuid a, Uuid b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
20
Libraries/Esiur/Data/VarInfo.cs
Normal file
20
Libraries/Esiur/Data/VarInfo.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
struct VarInfo
|
||||
{
|
||||
public string Pre;
|
||||
public string Post;
|
||||
public string VarName;
|
||||
|
||||
public string Build()
|
||||
{
|
||||
return Regex.Escape(Pre) + @"(?<" + VarName + @">[^\{]*)" + Regex.Escape(Post);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
108
Libraries/Esiur/Data/VarList.cs
Normal file
108
Libraries/Esiur/Data/VarList.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using Esiur.Core;
|
||||
using Esiur.Resource;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Esiur.Data
|
||||
{
|
||||
public class VarList<T> : IEnumerable<T>, ICollection, ICollection<T>
|
||||
{
|
||||
string propertyName;
|
||||
IResource resource;
|
||||
|
||||
List<T> list = new List<T>();
|
||||
|
||||
public VarList(IResource resource, [CallerMemberName] string propertyName = "")
|
||||
{
|
||||
this.resource = resource;
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
public VarList()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public int Count => list.Count;
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public bool IsSynchronized => true;
|
||||
|
||||
|
||||
public object SyncRoot { get; } = new object();
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
list.Add(item);
|
||||
|
||||
resource?.Instance?.Modified(propertyName);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
list.Clear();
|
||||
resource?.Instance?.Modified(propertyName);
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return list.Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
list.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
(list as ICollection).CopyTo(array, index);
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return list.GetEnumerator();
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
if ( list.Remove(item))
|
||||
{
|
||||
resource?.Instance?.Modified(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return list.GetEnumerator();
|
||||
}
|
||||
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return list[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
//var oldValue = list[index];
|
||||
|
||||
lock (SyncRoot)
|
||||
list[index] = value;
|
||||
|
||||
resource?.Instance?.Modified(propertyName);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user