/* 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.Text; using Esiur.Misc; using System.ComponentModel; using Esiur.Data; using Esiur.Core; using Esiur.Net.IIP; using Esiur.Resource; using System.Linq; using System.Reflection; using Esiur.Resource.Template; using System.Runtime.CompilerServices; using System.Collections; using System.Dynamic; namespace Esiur.Data { public static class Codec { /// /// Check if a DataType is an array /// /// DataType to check /// True if DataType is an array, otherwise false public static bool IsArray(this DataType type) { return (((byte)type & 0x80) == 0x80) && (type != DataType.NotModified); } /// /// Get the element DataType /// /// /// Passing UInt8Array will return UInt8 /// /// DataType to get its element DataType public static DataType GetElementType(this DataType type) { return (DataType)((byte)type & 0x7F); } /// /// Get DataType array of a given Structure /// /// Structure to get its DataTypes /// Distributed connection is required in case a type is at the other end private static DataType[] GetStructureDateTypes(Structure structure, DistributedConnection connection) { var keys = structure.GetKeys(); var types = new DataType[keys.Length]; for (var i = 0; i < keys.Length; i++) { types[i] = Codec.GetDataType(structure[keys[i]], connection).type; } return types; } /// /// Compare two structures /// /// Initial structure to compare with /// Next structure to compare with the initial /// DistributedConnection is required in case a structure holds items at the other end public static StructureComparisonResult Compare(Structure initial, Structure next, DistributedConnection connection) { if (next == null) return StructureComparisonResult.Null; if (initial == null) return StructureComparisonResult.Structure; if (next == initial) return StructureComparisonResult.Same; if (initial.Length != next.Length) return StructureComparisonResult.Structure; var previousKeys = initial.GetKeys(); var nextKeys = next.GetKeys(); for (var i = 0; i < previousKeys.Length; i++) if (previousKeys[i] != nextKeys[i]) return StructureComparisonResult.Structure; var previousTypes = GetStructureDateTypes(initial, connection); var nextTypes = GetStructureDateTypes(next, connection); for (var i = 0; i < previousTypes.Length; i++) if (previousTypes[i] != nextTypes[i]) return StructureComparisonResult.StructureSameKeys; return StructureComparisonResult.StructureSameTypes; } /// /// Compose an array of structures into an array of bytes /// /// Array of Structure to compose /// DistributedConnection is required in case a structure in the array holds items at the other end /// If true, prepend the length as UInt32 at the beginning of the returned bytes array /// Array of bytes in the network byte order public static byte[] ComposeStructureArray(Structure[] structures, DistributedConnection connection, bool prependLength = false) { if (structures == null || structures?.Length == 0) return prependLength ? new byte[] { 0, 0, 0, 0 } : new byte[0]; var rt = new BinaryList(); var comparsion = StructureComparisonResult.Structure; rt.AddUInt8((byte)comparsion) .AddUInt8Array(ComposeStructure(structures[0], connection, true, true, true)); for (var i = 1; i < structures.Length; i++) { comparsion = Compare(structures[i - 1], structures[i], connection); rt.AddUInt8((byte)comparsion); if (comparsion == StructureComparisonResult.Structure) rt.AddUInt8Array(ComposeStructure(structures[i], connection, true, true, true)); else if (comparsion == StructureComparisonResult.StructureSameKeys) rt.AddUInt8Array(ComposeStructure(structures[i], connection, false, true, true)); else if (comparsion == StructureComparisonResult.StructureSameTypes) rt.AddUInt8Array(ComposeStructure(structures[i], connection, false, false, true)); } if (prependLength) rt.InsertInt32(0, rt.Length); return rt.ToArray(); } /// /// Parse an array of structures /// /// Bytes array /// Zero-indexed offset /// Number of bytes to parse /// DistributedConnection is required in case a structure in the array holds items at the other end /// Array of structures public static AsyncBag ParseStructureArray(byte[] data, uint offset, uint length, DistributedConnection connection) { var reply = new AsyncBag(); if (length == 0) { reply.Seal(); return reply; } var end = offset + length; var result = (StructureComparisonResult)data[offset++]; AsyncReply previous = null; // string[] previousKeys = null; // DataType[] previousTypes = null; Structure.StructureMetadata metadata = new Structure.StructureMetadata(); if (result == StructureComparisonResult.Null) previous = new AsyncReply(null); else if (result == StructureComparisonResult.Structure) { uint cs = data.GetUInt32(offset); offset += 4; previous = ParseStructure(data, offset, cs, connection, out metadata); offset += cs; } reply.Add(previous); while (offset < end) { result = (StructureComparisonResult)data[offset++]; if (result == StructureComparisonResult.Null) previous = new AsyncReply(null); else if (result == StructureComparisonResult.Structure) { uint cs = data.GetUInt32(offset); offset += 4; previous = ParseStructure(data, offset, cs, connection, out metadata);// out previousKeys, out previousTypes); offset += cs; } else if (result == StructureComparisonResult.StructureSameKeys) { uint cs = data.GetUInt32(offset); offset += 4; previous = ParseStructure(data, offset, cs, connection, out metadata, metadata.Keys); offset += cs; } else if (result == StructureComparisonResult.StructureSameTypes) { uint cs = data.GetUInt32(offset); offset += 4; previous = ParseStructure(data, offset, cs, connection, out metadata, metadata.Keys, metadata.Types); offset += cs; } reply.Add(previous); } reply.Seal(); return reply; } /// /// Compose a structure into an array of bytes /// /// Structure to compose /// DistributedConnection is required in case an item in the structure is at the other end /// Whether to include the structure keys /// Whether to include each item DataType /// If true, prepend the length as UInt32 at the beginning of the returned bytes array /// Array of bytes in the network byte order public static byte[] ComposeStructure(Structure value, DistributedConnection connection, bool includeKeys = true, bool includeTypes = true, bool prependLength = false) { var rt = new BinaryList(); if (includeKeys) { foreach (var i in value) { var key = DC.ToBytes(i.Key); rt.AddUInt8((byte)key.Length) .AddUInt8Array(key) .AddUInt8Array(Compose(i.Value, connection)); } } else { foreach (var i in value) rt.AddUInt8Array(Compose(i.Value, connection, includeTypes)); } if (prependLength) rt.InsertInt32(0, rt.Length); return rt.ToArray(); } /// /// Parse a structure /// /// Bytes array /// Zero-indexed offset. /// Number of bytes to parse. /// DistributedConnection is required in case a structure in the array holds items at the other end. /// Value public static AsyncReply ParseStructure(byte[] data, uint offset, uint contentLength, DistributedConnection connection) { Structure.StructureMetadata metadata; return ParseStructure(data, offset, contentLength, connection, out metadata); } /// /// Parse a structure /// /// Bytes array /// Zero-indexed offset. /// Number of bytes to parse. /// DistributedConnection is required in case a structure in the array holds items at the other end. /// Array to store keys in. /// Array to store DataTypes in. /// Array of keys, in case the data doesn't include keys /// Array of DataTypes, in case the data doesn't include DataTypes /// Structure public static AsyncReply ParseStructure(byte[] data, uint offset, uint length, DistributedConnection connection, out Structure.StructureMetadata metadata, string[] keys = null, DataType[] types = null)// out string[] parsedKeys, out DataType[] parsedTypes, string[] keys = null, DataType[] types = null) { var reply = new AsyncReply(); var bag = new AsyncBag(); var keylist = new List(); var typelist = new List(); if (keys == null) { while (length > 0) { var len = data[offset++]; keylist.Add(data.GetString(offset, len)); offset += len; typelist.Add((DataType)data[offset]); uint rt; bag.Add(Codec.Parse(data, offset, out rt, connection)); length -= rt + len + 1; offset += rt; } } else if (types == null) { keylist.AddRange(keys); while (length > 0) { typelist.Add((DataType)data[offset]); uint rt; bag.Add(Codec.Parse(data, offset, out rt, connection)); length -= rt; offset += rt; } } else { keylist.AddRange(keys); typelist.AddRange(types); var i = 0; while (length > 0) { uint rt; bag.Add(Codec.Parse(data, offset, out rt, connection, types[i])); length -= rt; offset += rt; i++; } } bag.Seal(); bag.Then((res) => { // compose the list var s = new Structure(); for (var i = 0; i < keylist.Count; i++) s[keylist[i]] = res[i]; reply.Trigger(s); }); metadata = new Structure.StructureMetadata() { Keys = keylist.ToArray(), Types = typelist.ToArray() }; return reply; } /// /// Parse a value /// /// Bytes array /// Zero-indexed offset. /// DistributedConnection is required in case a structure in the array holds items at the other end. /// DataType, in case the data is not prepended with DataType /// Structure public static AsyncReply Parse(byte[] data, uint offset, DistributedConnection connection, DataType dataType = DataType.Unspecified) { uint size; return Parse(data, offset, out size, connection); } /// /// Parse a value /// /// Bytes array /// Zero-indexed offset. /// Output the number of bytes parsed /// DistributedConnection is required in case a structure in the array holds items at the other end. /// DataType, in case the data is not prepended with DataType /// Value public static AsyncReply Parse(byte[] data, uint offset, out uint size, DistributedConnection connection, DataType dataType = DataType.Unspecified) { bool isArray; DataType t; if (dataType == DataType.Unspecified) { size = 1; dataType = (DataType)data[offset++]; } else size = 0; t = (DataType)((byte)dataType & 0x7F); isArray = ((byte)dataType & 0x80) == 0x80; var payloadSize = dataType.Size();// SizeOf(); uint contentLength = 0; // check if we have the enough data if (payloadSize == -1) { contentLength = data.GetUInt32(offset); offset += 4; size += 4 + contentLength; } else size += (uint)payloadSize; if (isArray) { switch (t) { // VarArray ? case DataType.Void: return ParseVarArray(data, offset, contentLength, connection); case DataType.Bool: return new AsyncReply(data.GetBooleanArray(offset, contentLength)); case DataType.UInt8: return new AsyncReply(data.GetUInt8Array(offset, contentLength)); case DataType.Int8: return new AsyncReply(data.GetInt8Array(offset, contentLength)); case DataType.Char: return new AsyncReply(data.GetCharArray(offset, contentLength)); case DataType.Int16: return new AsyncReply(data.GetInt16Array(offset, contentLength)); case DataType.UInt16: return new AsyncReply(data.GetUInt16Array(offset, contentLength)); case DataType.Int32: return new AsyncReply(data.GetInt32Array(offset, contentLength)); case DataType.UInt32: return new AsyncReply(data.GetUInt32Array(offset, contentLength)); case DataType.Int64: return new AsyncReply(data.GetInt64Array(offset, contentLength)); case DataType.UInt64: return new AsyncReply(data.GetUInt64Array(offset, contentLength)); case DataType.Float32: return new AsyncReply(data.GetFloat32Array(offset, contentLength)); case DataType.Float64: return new AsyncReply(data.GetFloat64Array(offset, contentLength)); case DataType.String: return new AsyncReply(data.GetStringArray(offset, contentLength)); case DataType.Resource: case DataType.DistributedResource: return ParseResourceArray(data, offset, contentLength, connection); case DataType.DateTime: return new AsyncReply(data.GetDateTimeArray(offset, contentLength)); case DataType.Structure: return ParseStructureArray(data, offset, contentLength, connection); } } else { switch (t) { case DataType.NotModified: return new AsyncReply(new NotModified()); case DataType.Void: return new AsyncReply(null); case DataType.Bool: return new AsyncReply(data.GetBoolean(offset)); case DataType.UInt8: return new AsyncReply(data[offset]); case DataType.Int8: return new AsyncReply((sbyte)data[offset]); case DataType.Char: return new AsyncReply(data.GetChar(offset)); case DataType.Int16: return new AsyncReply(data.GetInt16(offset)); case DataType.UInt16: return new AsyncReply(data.GetUInt16(offset)); case DataType.Int32: return new AsyncReply(data.GetInt32(offset)); case DataType.UInt32: return new AsyncReply(data.GetUInt32(offset)); case DataType.Int64: return new AsyncReply(data.GetInt64(offset)); case DataType.UInt64: return new AsyncReply(data.GetUInt64(offset)); case DataType.Float32: return new AsyncReply(data.GetFloat32(offset)); case DataType.Float64: return new AsyncReply(data.GetFloat64(offset)); case DataType.String: return new AsyncReply(data.GetString(offset, contentLength)); case DataType.Resource: return ParseResource(data, offset); case DataType.DistributedResource: return ParseDistributedResource(data, offset, connection); case DataType.DateTime: return new AsyncReply(data.GetDateTime(offset)); case DataType.Structure: return ParseStructure(data, offset, contentLength, connection); } } return null; } /// /// Parse a resource /// /// Bytes array /// Zero-indexed offset. /// Resource public static AsyncReply ParseResource(byte[] data, uint offset) { return Warehouse.GetById(data.GetUInt32(offset)); } /// /// Parse a DistributedResource /// /// Bytes array /// Zero-indexed offset. /// DistributedConnection is required. /// DistributedResource public static AsyncReply ParseDistributedResource(byte[] data, uint offset, DistributedConnection connection) { //var g = data.GetGuid(offset); //offset += 16; // find the object var iid = data.GetUInt32(offset); return connection.Fetch(iid);// Warehouse.Get(iid); } public enum ResourceComparisonResult { Null, Distributed, Local, Same } public enum StructureComparisonResult : byte { Null, Structure, StructureSameKeys, StructureSameTypes, Same } /// /// Check if a resource is local to a given connection. /// /// Resource to check. /// DistributedConnection to check if the resource is local to it. /// True, if the resource owner is the given connection, otherwise False. public static bool IsLocalResource(IResource resource, DistributedConnection connection) { if (resource is DistributedResource) if ((resource as DistributedResource).Connection == connection) return true; return false; } /// /// Compare two resources /// /// Initial resource to make comparison with. /// Next resource to compare with the initial. /// DistributedConnection is required to check locality. /// Null, same, local, distributed or same class distributed. public static ResourceComparisonResult Compare(IResource initial, IResource next, DistributedConnection connection) { if (next == null) return ResourceComparisonResult.Null; else if (next == initial) return ResourceComparisonResult.Same; else if (IsLocalResource(next, connection)) return ResourceComparisonResult.Local; else return ResourceComparisonResult.Distributed; } /// /// Compose a resource /// /// Resource to compose. /// DistributedConnection is required to check locality. /// Array of bytes in the network byte order. public static byte[] ComposeResource(IResource resource, DistributedConnection connection) { if (IsLocalResource(resource, connection)) return DC.ToBytes((resource as DistributedResource).Id); else { return new BinaryList().AddGuid(resource.Instance.Template.ClassId).AddUInt32(resource.Instance.Id).ToArray(); //return BinaryList.ToBytes(resource.Instance.Template.ClassId, resource.Instance.Id); } } /// /// Compose an array of resources /// /// Array of resources. /// DistributedConnection is required to check locality. /// If True, prepend the length of the output at the beginning. /// Array of bytes in the network byte order. public static byte[] ComposeResourceArray(IResource[] resources, DistributedConnection connection, bool prependLength = false) { if (resources == null || resources?.Length == 0) return prependLength ? new byte[] { 0, 0, 0, 0 } : new byte[0]; var rt = new BinaryList(); var comparsion = Compare(null, resources[0], connection); rt.AddUInt8((byte)comparsion); if (comparsion == ResourceComparisonResult.Local) rt.AddUInt32((resources[0] as DistributedResource).Id); else if (comparsion == ResourceComparisonResult.Distributed) rt.AddUInt32(resources[0].Instance.Id); for (var i = 1; i < resources.Length; i++) { comparsion = Compare(resources[i - 1], resources[i], connection); rt.AddUInt8((byte)comparsion); if (comparsion == ResourceComparisonResult.Local) rt.AddUInt32((resources[i] as DistributedResource).Id); else if (comparsion == ResourceComparisonResult.Distributed) rt.AddUInt32(resources[i].Instance.Id); } if (prependLength) rt.InsertInt32(0, rt.Length); return rt.ToArray(); } /// /// Parse an array of bytes into array of resources /// /// Array of bytes. /// Number of bytes to parse. /// Zero-indexed offset. /// DistributedConnection is required to fetch resources. /// Array of resources. public static AsyncBag ParseResourceArray(byte[] data, uint offset, uint length, DistributedConnection connection) { var reply = new AsyncBag(); if (length == 0) { reply.Seal(); return reply; } var end = offset + length; // var result = (ResourceComparisonResult)data[offset++]; AsyncReply previous = null; if (result == ResourceComparisonResult.Null) previous = new AsyncReply(null); else if (result == ResourceComparisonResult.Local) { previous = Warehouse.GetById(data.GetUInt32(offset)); offset += 4; } else if (result == ResourceComparisonResult.Distributed) { previous = connection.Fetch(data.GetUInt32(offset)); offset += 4; } reply.Add(previous); while (offset < end) { result = (ResourceComparisonResult)data[offset++]; AsyncReply current = null; if (result == ResourceComparisonResult.Null) { current = new AsyncReply(null); } else if (result == ResourceComparisonResult.Same) { current = previous; } else if (result == ResourceComparisonResult.Local) { current = Warehouse.GetById(data.GetUInt32(offset)); offset += 4; } else if (result == ResourceComparisonResult.Distributed) { current = connection.Fetch(data.GetUInt32(offset)); offset += 4; } reply.Add(current); previous = current; } reply.Seal(); return reply; } /// /// Compose an array of variables /// /// Variables. /// DistributedConnection is required to check locality. /// If True, prepend the length as UInt32 at the beginning of the output. /// Array of bytes in the network byte order. public static byte[] ComposeVarArray(Array array, DistributedConnection connection, bool prependLength = false) { var rt = new List(); for (var i = 0; i < array.Length; i++) rt.AddRange(Compose(array.GetValue(i), connection)); if (prependLength) rt.InsertRange(0, DC.ToBytes(rt.Count)); return rt.ToArray(); } /// /// Parse an array of variables. /// /// Array of bytes. /// DistributedConnection is required to fetch resources. /// Array of variables. public static AsyncBag ParseVarArray(byte[] data, DistributedConnection connection) { return ParseVarArray(data, 0, (uint)data.Length, connection); } /// /// Parse an array of bytes into an array of varialbes. /// /// Array of bytes. /// Zero-indexed offset. /// Number of bytes to parse. /// DistributedConnection is required to fetch resources. /// Array of variables. public static AsyncBag ParseVarArray(byte[] data, uint offset, uint length, DistributedConnection connection) { var rt = new AsyncBag(); while (length > 0) { uint cs; rt.Add(Parse(data, offset, out cs, connection)); if (cs > 0) { offset += (uint)cs; length -= (uint)cs; } else throw new Exception("Error while parsing structured data"); } rt.Seal(); return rt; } /// /// Compose an array of property values. /// /// PropertyValue array. /// DistributedConnection is required to check locality. /// If True, prepend the length as UInt32 at the beginning of the output. /// Array of bytes in the network byte order. /// //, bool includeAge = true public static byte[] ComposePropertyValueArray(PropertyValue[] array, DistributedConnection connection, bool prependLength = false) { var rt = new List(); for (var i = 0; i < array.Length; i++) rt.AddRange(ComposePropertyValue(array[i], connection)); if (prependLength) rt.InsertRange(0, DC.ToBytes(rt.Count)); return rt.ToArray(); } /// /// Compose a property value. /// /// Property value /// DistributedConnection is required to check locality. /// Array of bytes in the network byte order. public static byte[] ComposePropertyValue(PropertyValue propertyValue, DistributedConnection connection)//, bool includeAge = true) { return new BinaryList() .AddUInt64(propertyValue.Age) .AddDateTime(propertyValue.Date) .AddUInt8Array(Compose(propertyValue.Value, connection)) .ToArray(); // age, date, value //if (includeAge) // return BinaryList.ToBytes(propertyValue.Age, propertyValue.Date, Compose(propertyValue.Value, connection)); //else // return BinaryList.ToBytes(propertyValue.Date, Compose(propertyValue.Value, connection)); } /// /// Parse property value. /// /// Array of bytes. /// Zero-indexed offset. /// DistributedConnection is required to fetch resources. /// Output content size. /// PropertyValue. public static AsyncReply ParsePropertyValue(byte[] data, uint offset, out uint cs, DistributedConnection connection)//, bool ageIncluded = true) { var reply = new AsyncReply(); var age = data.GetUInt64(offset); offset += 8; DateTime date = data.GetDateTime(offset); offset += 8; uint valueSize; Parse(data, offset, out valueSize, connection).Then(value => { reply.Trigger(new PropertyValue(value, age, date)); }); cs = 16 + valueSize; return reply; } /// /// Parse an array of PropertyValue. /// /// Array of bytes. /// DistributedConnection is required to fetch resources. /// Array of variables. public static AsyncBag ParsePropertyValueArray(byte[] data, DistributedConnection connection) { return ParsePropertyValueArray(data, 0, (uint)data.Length, connection); } /// /// Parse resource history /// /// Array of bytes. /// Zero-indexed offset. /// Number of bytes to parse. /// Resource /// Starting age. /// Ending age. /// DistributedConnection is required to fetch resources. /// public static AsyncReply> ParseHistory(byte[] data, uint offset, uint length, IResource resource, DistributedConnection connection) { //var count = (int)toAge - (int)fromAge; var list = new KeyList(); var reply = new AsyncReply>(); var bagOfBags = new AsyncBag(); var ends = offset + length; while (offset < ends) { var index = data[offset++]; var pt = resource.Instance.Template.GetPropertyTemplateByIndex(index); list.Add(pt, null); var cs = DC.GetUInt32(data, offset); offset += 4; bagOfBags.Add(ParsePropertyValueArray(data, offset, cs, connection)); offset += cs; } bagOfBags.Seal(); bagOfBags.Then(x => { for (var i = 0; i < list.Count; i++) list[list.Keys.ElementAt(i)] = x[i]; reply.Trigger(list); }); return reply; } /// /// Compose resource history /// /// History /// DistributedConnection is required to fetch resources. /// public static byte[] ComposeHistory(KeyList history, DistributedConnection connection, bool prependLength = false) { var rt = new BinaryList(); for (var i = 0; i < history.Count; i++) rt.AddUInt8(history.Keys.ElementAt(i).Index) .AddUInt8Array(ComposePropertyValueArray(history.Values.ElementAt(i), connection, true)); // rt.Append((byte)history.Keys.ElementAt(i).Index, // ComposePropertyValueArray(history.Values.ElementAt(i), connection, true)); if (prependLength) rt.InsertInt32(0, rt.Length); return rt.ToArray(); } /// /// Parse an array of PropertyValue. /// /// Array of bytes. /// Zero-indexed offset. /// Number of bytes to parse. /// DistributedConnection is required to fetch resources. /// Whether property age is represented in the data. /// public static AsyncBag ParsePropertyValueArray(byte[] data, uint offset, uint length, DistributedConnection connection)//, bool ageIncluded = true) { var rt = new AsyncBag(); while (length > 0) { uint cs; rt.Add(ParsePropertyValue(data, offset, out cs, connection));//, ageIncluded)); if (cs > 0) { offset += (uint)cs; length -= (uint)cs; } else throw new Exception("Error while parsing ValueInfo structured data"); } rt.Seal(); return rt; } /// /// Compose a variable /// /// Value to compose. /// DistributedConnection is required to check locality. /// If True, prepend the DataType at the beginning of the output. /// Array of bytes in the network byte order. public static byte[] Compose(object valueOrSource, DistributedConnection connection, bool prependType = true) { var (type, value) = GetDataType(valueOrSource, connection); var rt = new BinaryList(); switch (type) { case DataType.Void: // nothing to do; break; case DataType.String: var st = DC.ToBytes((string)value); rt.AddInt32(st.Length).AddUInt8Array(st); break; case DataType.Resource: rt.AddUInt32((value as DistributedResource).Id); break; case DataType.DistributedResource: //rt.Append((value as IResource).Instance.Template.ClassId, (value as IResource).Instance.Id); rt.AddUInt32((value as IResource).Instance.Id); break; case DataType.Structure: rt.AddUInt8Array(ComposeStructure((Structure)value, connection, true, true, true)); break; case DataType.VarArray: rt.AddUInt8Array(ComposeVarArray((Array)value, connection, true)); break; case DataType.ResourceArray: if (value is IResource[]) rt.AddUInt8Array(ComposeResourceArray((IResource[])value, connection, true)); else rt.AddUInt8Array(ComposeResourceArray((IResource[])DC.CastConvert(value, typeof(IResource[])), connection, true)); break; case DataType.StructureArray: rt.AddUInt8Array(ComposeStructureArray((Structure[])value, connection, true)); break; default: rt.Add(type, value); if (type.IsArray()) rt.InsertInt32(0, rt.Length); break; } if (prependType) rt.InsertUInt8(0, (byte)type); return rt.ToArray(); } 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; } /// /// Check if a type implements an interface /// /// Sub-class type. /// Super-interface type. /// True, if implements . public static bool ImplementsInterface(Type type, Type iface) { /* if (iface.GetTypeInfo().IsGenericType) { //var x = (type.GetTypeInfo().GetInterfaces().Any(x => x.GetTypeInfo().IsGenericType Contains(iface)) iface = iface.GetTypeInfo().GetGenericTypeDefinition(); //if (type.GetTypeInfo().IsGenericType) // type = while (type != null) { if (type == iface) return true; #if NETSTANDARD if (type.GetTypeInfo().GetInterfaces().Contains(iface))// (x=>x.GetTypeInfo().IsGenericType (iface)) return true; type = type.GetTypeInfo().BaseType; #else if (type.GetInterfaces().Contains(iface)) return true; type = type.BaseType; #endif } } else */ //{ 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; } /// /// Check if a type inherits another type. /// /// Child type. /// Parent type. /// True, if inherits . 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; } /// /// Get the DataType of a given value. /// This function is needed to compose a value. /// /// Value to find its DataType. /// DistributedConnection is required to check locality of resources. /// DataType. public static (DataType type, object value) GetDataType(object value, DistributedConnection connection) { if (value == null) return (DataType.Void, null); if (value is IUserType) value = (value as IUserType).Get(); // value = (List<>)value.ToArray(); if (value is Func) //if (connection != null) value = (value as Func)(connection); //else // return (DataType.Void, null); else if (value is DistributedPropertyContext) { try { //if (connection != null) value = (value as DistributedPropertyContext).Method(connection); //else } catch(Exception ex) { Global.Log(ex); return (DataType.Void, null); } // return (DataType.Void, null); } if (value == null) return (DataType.Void, null); var t = value.GetType(); // Convert ICollection to Array if (!t.IsArray && typeof(ICollection).IsAssignableFrom(t)) { var col = t.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)); if (col.Count() == 0) return (DataType.Void, null); var elementType = col.First().GetGenericArguments()[0]; value = new ArrayList((ICollection)value).ToArray(elementType); t = value.GetType(); } var isArray = t.IsArray; if (isArray) { t = t.GetElementType(); if (t.IsEnum) { var src = value as Array; t = t.GetEnumUnderlyingType(); var dst = Array.CreateInstance(t, src.Length); src.CopyTo(dst, 0); value = dst; } } else if (t.IsEnum) { t = t.GetEnumUnderlyingType(); value = Convert.ChangeType(value, t); } DataType type; if (t == typeof(bool)) type = DataType.Bool; else if (t == typeof(char)) type = DataType.Char; else if (t == typeof(byte)) type = DataType.UInt8; else if (t == typeof(sbyte)) type = DataType.Int8; else if (t == typeof(short)) type = DataType.Int16; else if (t == typeof(ushort)) type = DataType.UInt16; else if (t == typeof(int)) type = DataType.Int32; else if (t == typeof(uint)) type = DataType.UInt32; else if (t == typeof(long)) type = DataType.Int64; else if (t == typeof(ulong)) type = DataType.UInt64; else if (t == typeof(float)) type = DataType.Float32; else if (t == typeof(double)) type = DataType.Float64; else if (t == typeof(decimal)) type = DataType.Decimal; else if (t == typeof(string)) type = DataType.String; else if (t == typeof(DateTime)) type = DataType.DateTime; else if (typeof(Structure).IsAssignableFrom(t) || t == typeof(ExpandoObject)) type = DataType.Structure; //else if (t == typeof(DistributedResource)) // type = DataType.DistributedResource; else if (ImplementsInterface(t, typeof(IResource))) { if (isArray) return (DataType.ResourceArray, value); else { return (IsLocalResource((IResource)value, connection) ? DataType.Resource : DataType.DistributedResource, value); } } else type = DataType.Void; if (isArray) return ((DataType)((byte)type | 0x80), value); else return (type, value); } } }