From 035be47c4555c6caf4a0b7189bff7c1a4685d083 Mon Sep 17 00:00:00 2001 From: Harald Wolff Date: Tue, 26 Mar 2019 12:54:02 +0100 Subject: [PATCH] WIP --- CIDR.cs | 43 ++-- Extensions.cs | 13 ++ GeoLocation.cs | 34 +++ URI.cs | 246 ++++++++++++++++++++ ln.types.csproj | 21 ++ odb/ClassMapping.cs | 109 +++++++++ odb/DictionaryMapping.cs | 66 ++++++ odb/ListMapping.cs | 59 +++++ odb/ODB.cs | 173 ++++----------- odb/ODBCollection.cs | 419 +++++++++++++++++++++++++++++++++++ odb/ODBCollection<>.cs | 109 +++++++++ odb/ODBDocument.cs | 109 +++++++++ odb/ODBFileStorage.cs | 210 +++++++++--------- odb/ODBMapper.cs | 220 ++++++++++++++++++ odb/ODBObjectReader.cs | 38 ++-- odb/ODBObjectWriter.cs | 42 ++-- odb/Persistent.cs | 2 + odb/PersistentList.cs | 264 +++++++++++----------- odb/PreparedObject.cs | 284 ++++++++++++------------ odb/Storage.cs | 18 +- odb/values/ODBDouble.cs | 28 +++ odb/values/ODBGuid.cs | 36 +++ odb/values/ODBInteger.cs | 53 +++++ odb/values/ODBList.cs | 79 +++++++ odb/values/ODBLong.cs | 52 +++++ odb/values/ODBNull.cs | 32 +++ odb/values/ODBStringValue.cs | 36 +++ odb/values/ODBValue.cs | 150 +++++++++++++ packages.config | 1 + serialize/ObjectReader.cs | 1 + threads/Pool.cs | 37 ++-- 31 files changed, 2395 insertions(+), 589 deletions(-) create mode 100644 GeoLocation.cs create mode 100644 URI.cs create mode 100644 odb/ClassMapping.cs create mode 100644 odb/DictionaryMapping.cs create mode 100644 odb/ListMapping.cs create mode 100644 odb/ODBCollection.cs create mode 100644 odb/ODBCollection<>.cs create mode 100644 odb/ODBDocument.cs create mode 100644 odb/ODBMapper.cs create mode 100644 odb/values/ODBDouble.cs create mode 100644 odb/values/ODBGuid.cs create mode 100644 odb/values/ODBInteger.cs create mode 100644 odb/values/ODBList.cs create mode 100644 odb/values/ODBLong.cs create mode 100644 odb/values/ODBNull.cs create mode 100644 odb/values/ODBStringValue.cs create mode 100644 odb/values/ODBValue.cs diff --git a/CIDR.cs b/CIDR.cs index 919268f..d5c6a94 100644 --- a/CIDR.cs +++ b/CIDR.cs @@ -13,6 +13,7 @@ using System.Linq; using System.Collections.Generic; using System.Collections; using Newtonsoft.Json; +using LiteDB; namespace ln.types { @@ -35,7 +36,7 @@ namespace ln.types return new CIDR(ip, w); } } - return new CIDR(ip,32); + return new CIDR(ip, 32); } private readonly uint _ip; @@ -45,7 +46,7 @@ namespace ln.types public CIDR Network => new CIDR(_ip & _netmask, _netmask); public CIDR Host => new CIDR(_ip, 0xffffffff); - public int Size => (1 << (32 - MaskWidth)); + public int Size => (1 << (32 - MaskWidth)); public byte[] IPBytes { @@ -115,12 +116,12 @@ namespace ln.types for (w = 32; w > 0; w--) { - if ((nm & (1 << (int)(32-w))) != 0) + if ((nm & (1 << (int)(32 - w))) != 0) break; } for (uint n = w; n > 0; n--) { - if ((nm & (1 << (int)(32-n))) == 0) + if ((nm & (1 << (int)(32 - n))) == 0) throw new FormatException("Netmask with holes"); } @@ -136,7 +137,7 @@ namespace ln.types uint newmask = maskFromWidth((uint)(MaskWidth + bits)); uint nip = _ip; CIDR[] result = new CIDR[count]; - for (int n=0;n x.ToString()))); else - return String.Format("{0}/{1}", String.Join(".", IPBytes.Select((x) => x.ToString())),getNetWidth(_netmask)); + return String.Format("{0}/{1}", String.Join(".", IPBytes.Select((x) => x.ToString())), getNetWidth(_netmask)); } public static implicit operator IPAddress(CIDR cidr) { - return new IPAddress( cidr.IPBytes ); + return new IPAddress(cidr.IPBytes); } public static implicit operator CIDR(IPAddress iPAddress) { - return new CIDR(BitConverter.ToUInt32(iPAddress.GetAddressBytes().Reverse().ToArray(),0),0xFFFFFFFF); + return new CIDR(BitConverter.ToUInt32(iPAddress.GetAddressBytes().Reverse().ToArray(), 0), 0xFFFFFFFF); } public bool Contains(CIDR you) { - return (you.MaskWidth >= MaskWidth) && ((you._ip & _netmask)==(_ip & _netmask)); + return (you.MaskWidth >= MaskWidth) && ((you._ip & _netmask) == (_ip & _netmask)); } + public bool Contains(IEnumerable candidates) + { + foreach (CIDR ip in candidates) + if (Contains(ip)) + return true; + return false; + } + + public override int GetHashCode() { return (int)(_ip ^ _netmask); @@ -225,13 +235,17 @@ namespace ln.types } - static bool ___init = ____init(); - static bool ____init() + static CIDR() { + BsonMapper.Global.RegisterType( + serialize: (ip) => ip.ToString(), + deserialize: (bson) => CIDR.Parse(bson.AsString) + ); + + List converters = new List(); converters.Add(new CIDRJsonConverter()); JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Converters = converters }; - return true; } } @@ -243,15 +257,16 @@ namespace ln.types return (objectType == typeof(CIDR)); } - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer) { return CIDR.Parse(reader.ReadAsString()); } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) { writer.WriteValue((value as CIDR).ToString()); } + } } diff --git a/Extensions.cs b/Extensions.cs index 085b5ed..8143dde 100644 --- a/Extensions.cs +++ b/Extensions.cs @@ -1,4 +1,5 @@ using System; +using System.IO; namespace ln.types { public static class Extensions @@ -14,5 +15,17 @@ namespace ln.types return true; } + + public static int ReadInteger(this Stream stream) + { + byte[] b = new byte[4]; + stream.Read(b, 0, 4); + return BitConverter.ToInt32(b,0); + } + public static void WriteInteger(this Stream stream,int i) + { + stream.Write(BitConverter.GetBytes(i), 0, 4); + } + } } diff --git a/GeoLocation.cs b/GeoLocation.cs new file mode 100644 index 0000000..b4d5273 --- /dev/null +++ b/GeoLocation.cs @@ -0,0 +1,34 @@ +// /** +// * File: GeoLocation.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +namespace ln.types +{ + public struct GeoLocation + { + public double Latitude; + public double Longitude; + + public GeoLocation(double latitude,double longitude) + { + Latitude = latitude; + Longitude = longitude; + } + + public override string ToString() + { + return String.Format("{0:F}{1}{2:F}{3}", + (Latitude < 0) ? -Latitude : Latitude, + (Latitude < 0) ? 'S':'N', + (Longitude < 0) ? -Longitude : Longitude, + (Longitude < 0) ? 'W' : 'E' + ); + } + } +} diff --git a/URI.cs b/URI.cs new file mode 100644 index 0000000..9cb1506 --- /dev/null +++ b/URI.cs @@ -0,0 +1,246 @@ +// /** +// * File: URI.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.Text; +namespace ln.types +{ + /** + * Quick and Dirty RFC3986 URI + * + **/ + + public class URI + { + public String Scheme { get; private set; } = String.Empty; + public String Authority { get; private set; } = String.Empty; + + public String[] UserInfo { get; private set; } = new String[0]; + public String Host { get; private set; } = String.Empty; + public String Port { get; private set; } = String.Empty; + + public String Path { get; private set; } = String.Empty; + public String Query { get; private set; } = String.Empty; + public String Fragment { get; private set; } = String.Empty; + + private URI() + { + } + + public URI(String uri) + { + Parse(uri); + ParseAuthority(); + } + public URI(String scheme, String authority, string path) + { + Parse(String.Format("{0}://{1}{2}", scheme, authority, path)); + ParseAuthority(); + } + public URI(String scheme,String authority,string path,string query,string fragment) + { + Scheme = scheme; + Authority = authority; + Path = path; + Query = query; + Fragment = fragment; + ParseAuthority(); + } + + public URI(URI uri, string path) + { + Parse(String.Format("{0}://{1}{2}", uri.Scheme, uri.Authority, path)); + ParseAuthority(); + } + + public URI Follow(String path) + { + if (path.StartsWith("/",StringComparison.InvariantCulture)) + { + return new URI(Scheme, Authority, path); + } else if (Path.EndsWith("/",StringComparison.InvariantCulture) || path.StartsWith("?",StringComparison.InvariantCulture) || path.StartsWith("#", StringComparison.InvariantCulture)) + { + return new URI(Scheme, Authority, String.Format("{0}{1}", Path, path)); + } + else + { + int indSlash = Path.LastIndexOf('/'); + return new URI(Scheme, Authority, String.Format("{0}/{1}",Path.Substring(0,indSlash),path)); + } + } + + + private void Parse(String uri) + { + char[] chUri = uri.ToCharArray(); + int n = 0; + int m = 0; + + // Scheme + + while ((chUri[n] != ':') && ((++n) < chUri.Length)) { }; + + if (n < 2) + throw new FormatException(String.Format("URL malformed: {0}",uri)); + + Scheme = new string(chUri, 0, n); + n++; + + if (n < chUri.Length) + { + if ((chUri.Length - n > 1) && (chUri[n] == '/') && (chUri[n+1] == '/')) + { + // Authority + n += 2; + m = n; + + while ( + (m < chUri.Length) && + (chUri[m] != '/') && + (chUri[m] != '?') && + (chUri[m] != '#') + ) { m++; } + + Authority = new string(chUri, n, (m - n)); + n = m; + } + + // Path + m = n; + while ( + (m < chUri.Length) && + (chUri[m] != '?') && + (chUri[m] != '#') + ) + { m++; } + + Path = new string(chUri, n, (m - n)); + n = m; + + if (n < chUri.Length) + { + if (chUri[n] == '?') + { + n++; + m = n; + while ( + (m < chUri.Length) && + (chUri[m] != '#') + ) + { m++; } + + Query = new string(chUri, n, (m - n)); + n = m; + } + if ((n 0) + { + stringBuilder.Append(UserInfo[0]); + stringBuilder.Append('@'); + } + + stringBuilder.Append(Host); + + if (!String.Empty.Equals(Port)) + { + stringBuilder.Append(':'); + stringBuilder.Append(Port); + } + } + + stringBuilder.Append(Path); + + if (!string.Empty.Equals(Query)) + { + stringBuilder.Append("?"); + stringBuilder.Append(Query); + } + if (!string.Empty.Equals(Fragment)) + { + stringBuilder.Append('#'); + stringBuilder.Append(Fragment); + } + + return stringBuilder.ToString(); + } + + private int GetHashCode(String s) + { + return (s == null) ? -1 : s.GetHashCode(); + } + + public override int GetHashCode() + { + return GetHashCode(Scheme) ^ GetHashCode(Authority) ^ GetHashCode(Path) ^ GetHashCode(Query) ^ GetHashCode(Fragment); + } + + public override bool Equals(object obj) + { + if (obj is URI) + { + URI other = obj as URI; + return Scheme.Equals(other.Scheme) && Authority.Equals(other.Authority) && Path.Equals(other.Path) && Query.Equals(other.Query) && Fragment.Equals(other.Fragment); + } + return false; + } + + } +} diff --git a/ln.types.csproj b/ln.types.csproj index 479328d..ab7b6ba 100644 --- a/ln.types.csproj +++ b/ln.types.csproj @@ -38,6 +38,9 @@ ..\packages\Castle.Core.4.3.1\lib\net45\Castle.Core.dll + + ..\packages\LiteDB.4.1.4\lib\net40\LiteDB.dll + @@ -60,12 +63,30 @@ + + + + + + + + + + + + + + + + + + diff --git a/odb/ClassMapping.cs b/odb/ClassMapping.cs new file mode 100644 index 0000000..d5873cb --- /dev/null +++ b/odb/ClassMapping.cs @@ -0,0 +1,109 @@ +using System; +using ln.types.odb.values; +using System.Reflection; +using System.Collections.Generic; +using ln.logging; + +namespace ln.types.odb +{ + public class DocumentIDAttribute : Attribute + { + } + + public class ClassMapping : IODBMapping + { + public Type MappedType { get; } + public String IDField { get; private set; } + + List mappedFields = new List(); + + public ClassMapping(Type type) + { + Logging.Log(LogLevel.DEBUG, "Constructing ClassMapping for {0}",type); + + MappedType = type; + AddFields(type); + } + + private void AddFields(Type type) + { + foreach (FieldInfo fieldinfo in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) + { + if (fieldinfo.GetCustomAttribute() == null) + { + mappedFields.Add(fieldinfo); + if (fieldinfo.GetCustomAttribute() != null) + { + IDField = fieldinfo.Name; + } + } + } + if ((type != null) && !type.IsValueType && (!typeof(object).Equals(type.BaseType))) + { + AddFields(type.BaseType); + } + } + + public object UnmapValue(ODBMapper mapper,ODBValue oval) + { + ODBDocument document = oval as ODBDocument; + object o = Activator.CreateInstance(MappedType, true); + + foreach (FieldInfo fieldInfo in mappedFields) + { + object fv = ODBMapper.Default.UnmapValue(fieldInfo.FieldType,document[fieldInfo.Name]); + fieldInfo.SetValue(o, fv); + } + return o; + } + + public ODBValue MapValue(ODBMapper mapper,object value) + { + ODBDocument document = new ODBDocument(); + document["__asm__"] = value.GetType().Assembly.GetName().Name; + document["__type__"] = value.GetType().FullName; + + foreach (FieldInfo fieldInfo in mappedFields) + { + object fv = fieldInfo.GetValue(value); + ODBValue ov = ODBMapper.Default.MapValue(fv); + document[fieldInfo.Name] = ov; + } + + if (IDField != null) + { + document.ID = document[IDField]; + } + return document; + } + } + + public class ObjectMapping : IODBMapping + { + + public ODBValue MapValue(ODBMapper mapper, object value) + { + return new ODBDocument(); + } + + public object UnmapValue(ODBMapper mapper, ODBValue oval) + { + if (oval is ODBDocument) + { + + + ODBDocument document = oval.AsDocument; + if (!document.Contains("__type__")) + return new object(); + + Type dType = Assembly.Load(document["__asm__"].AsString).GetType(document["__type__"].AsString); + return mapper.UnmapValue(dType, oval); + } + else + { + return oval.Value; + } + } + } + +} diff --git a/odb/DictionaryMapping.cs b/odb/DictionaryMapping.cs new file mode 100644 index 0000000..56e7212 --- /dev/null +++ b/odb/DictionaryMapping.cs @@ -0,0 +1,66 @@ +using System; +using ln.types.odb.values; +using System.Linq; +using System.Collections; +using System.Reflection; +using Castle.Components.DictionaryAdapter; +using System.Collections.Generic; + +namespace ln.types.odb +{ + public class DictionaryMapping : IODBMapping + { + public DictionaryMapping() + { + } + + public ODBValue MapValue(ODBMapper mapper, object value) + { + Type dType = value.GetType(); + + if (dType.GetInterfaces().Contains(typeof(IDictionary))) + { + IDictionary dictionary = value as IDictionary; + ODBDocument document = new ODBDocument(); + + document["__asm__"] = value.GetType().Assembly.GetName().Name; + document["__type__"] = value.GetType().FullName; + + foreach (object key in dictionary.Keys) + { + object v = dictionary[key]; + document[mapper.MapValue(key)] = mapper.MapValue(v); + } + return document; + } + throw new NotImplementedException(); + } + + public object UnmapValue(ODBMapper mapper, ODBValue oval) + { + ODBDocument document = oval.AsDocument; + Type dType = Assembly.Load(document["__asm__"].AsString).GetType(document["__type__"].AsString); + + if (dType.IsGenericType) + { + IDictionary dictionary = (IDictionary)Activator.CreateInstance(dType, true); + + if (dType.GetGenericTypeDefinition().Equals(typeof(Dictionary<,>))) + { + Type kType = dType.GetGenericArguments()[0]; + Type vType = dType.GetGenericArguments()[1]; + + foreach (ODBValue key in document.Keys) + { + if (!key.Equals("__asm__") && !key.Equals("__type__")) + dictionary.Add(mapper.UnmapValue(kType, key), mapper.UnmapValue(vType, document[key])); + } + + return dictionary; + } + } + + throw new NotSupportedException(); + } + } +} diff --git a/odb/ListMapping.cs b/odb/ListMapping.cs new file mode 100644 index 0000000..a5d6ca4 --- /dev/null +++ b/odb/ListMapping.cs @@ -0,0 +1,59 @@ +// /** +// * File: ListMapping.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using ln.types.odb.values; +using System.Collections; +using System.Linq; + +namespace ln.types.odb +{ + public class ListMapping : IODBMapping + { + public Type TargetType { get; } + + public ListMapping(Type targetType) + { + TargetType = targetType; + } + + public object UnmapValue(ODBMapper mapper,ODBValue oval) + { + ODBList list = oval as ODBList; + + if (TargetType.IsArray) + { + Array a = Array.CreateInstance(TargetType.GetElementType(), list.Count); + for (int n = 0; n < list.Count; n++) + a.SetValue(list[n], n); + return a; + } else if (TargetType.GetInterfaces().Contains(typeof(IList))) + { + IList ilist = (IList)Activator.CreateInstance(TargetType, true); + for (int n = 0; n < list.Count; n++) + ilist.Add(list[n]); + return ilist; + } + + throw new NotImplementedException(); + } + + public ODBValue MapValue(ODBMapper mapper,object value) + { + ODBList list = new ODBList(); + + foreach (object item in (IEnumerable)value) + { + list.Add(ODBMapper.Default.MapValue(item)); + } + + return list; + } + } +} diff --git a/odb/ODB.cs b/odb/ODB.cs index 66cbf0f..b94fa86 100644 --- a/odb/ODB.cs +++ b/odb/ODB.cs @@ -14,165 +14,70 @@ using Castle.DynamicProxy; using ln.types.serialize; using Castle.Components.DictionaryAdapter; using System.Linq; +using ln.logging; +using ln.types.odb.values; namespace ln.types.odb { - public class ODB : ODB where T: IPersistent - { - public T Root - { - get => (T)RootObject; - } - public ODB(String basePath) - :base(basePath, typeof(T)) - { - } - } - - public class ODB + public class ODB : IDisposable { public String BasePath { get; set; } - public IPersistent RootObject { get; protected set; } - - - private Type RootType; - private Storage Storage { get; set; } - - private Dictionary objectCache = new Dictionary(); + Dictionary collections = new Dictionary(); public ODB(string basePath) { BasePath = Path.GetFullPath(basePath); - Storage = new ODBFileStorage(basePath); + if (!Directory.Exists(BasePath)) + Directory.CreateDirectory(BasePath); } - public ODB(String basePath, Type rootType) - : this(basePath) + public ODBCollection GetCollection(string colName) { - RootType = rootType; - Initialize(); + if (!collections.ContainsKey(colName)) + collections[colName] = new ODBCollection(this, colName); + + return collections[colName]; } - public ODB(String basePath,IPersistent rootObject) - : this(basePath) + + public ODBCollection GetCollection() where T:class { - RootType = rootObject.GetType(); - RootObject = rootObject; - Initialize(); + return new ODBCollection(this); + } + + + internal void DisposeCollection(ODBCollection collection) + { + ODBCollection check = collections[collection.CollectionName]; + if (check == collection) + collections.Remove(collection.CollectionName); } private void Initialize() { - if (RootObject == null) - { - string rootHint = Path.Combine(BasePath, "root.hint"); - if (File.Exists(rootHint)) - { - string rootID = File.ReadAllText(rootHint); - Guid persistenceID = Guid.Parse(rootID); - RootObject = LoadPersistent(persistenceID); - } - else - { - RootObject = (IPersistent)Activator.CreateInstance(RootType); - string rootID = RootObject.GetPersistenceID().ToString(); - SavePersistent(RootObject); - File.WriteAllText(rootHint, rootID); - } - } } - public bool Contains(Guid persistenceID) + public void Dispose() { - return Storage.Contains(persistenceID) || objectCache.ContainsKey(persistenceID); + foreach (ODBCollection col in collections.Values.ToArray()) + { + col.Dispose(); + } } - public void SavePersistent(IPersistent o) => SavePersistent(o, true); - public void SavePersistent(IPersistent o,bool recurse) + static ODB() { - lock (this) - { - SaveCollector saveCollector = new SaveCollector(this); - saveCollector.Add(o); - - foreach (PreparedObject preparedObject in saveCollector.PreparedToSave) - { - Storage.Store(preparedObject); - objectCache[preparedObject.PersistenceID] = preparedObject; - } - } + new ODBDocument(); + new ODBNull(); + new ODBStringValue(); + new ODBList(); + new ODBInteger(); + new ODBUInteger(); + new ODBLong(); + new ODBULong(); + new ODBDouble(); + new ODBGuid(); } - public T LoadPersistent(Guid persistenceID) => (T)LoadPersistent(persistenceID); - public IPersistent LoadPersistent(Guid persistenceID) - { - if (!Contains(persistenceID)) - throw new KeyNotFoundException(); - - PreparedObject preparedObject = GetCachedPersistent(persistenceID); - if (preparedObject == null) - { - preparedObject = new PreparedObject(this, typeof(IPersistent), persistenceID); - if (!Storage.Load(preparedObject)) - throw new IOException(String.Format("unable to load IPersistent({0})",persistenceID)); - - preparedObject.CreateInstance(); - objectCache.Add(persistenceID,preparedObject); - } - - return preparedObject.Instance; - } - - - - - - - - private PreparedObject GetCachedPersistent(Guid persistenceID) - { - lock (this) - { - if (objectCache.ContainsKey(persistenceID)) - { - return objectCache[persistenceID]; - } - } - return null; - } - - - class SaveCollector - { - public ODB ODB { get; } - - public IEnumerable PreparedToSave => preparedObjects.Values; - - private Dictionary preparedObjects = new Dictionary(); - - public SaveCollector(ODB odb) - { - ODB = odb; - } - - public void Add(IPersistent persistent) - { - PreparedObject preparedObject = new PreparedObject(ODB,persistent); - PreparedObject cachedObject = ODB.GetCachedPersistent(preparedObject.PersistenceID); - - if (!preparedObject.InstanceEquals(cachedObject) && !preparedObjects.ContainsKey(preparedObject.PersistenceID)) - { - preparedObjects.Add(preparedObject.PersistenceID, preparedObject); - - foreach (IPersistent referencedPersistent in preparedObject.ReferencedPersistents) - { - Add(referencedPersistent); - } - } - } - - - - } } -} +} \ No newline at end of file diff --git a/odb/ODBCollection.cs b/odb/ODBCollection.cs new file mode 100644 index 0000000..b050555 --- /dev/null +++ b/odb/ODBCollection.cs @@ -0,0 +1,419 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using ln.types.odb.values; +using System.IO; +using System.Net.Mime; +using System.Security.AccessControl; +using System.Linq; +using System.ComponentModel.Design.Serialization; +using System.ComponentModel; +using ln.logging; +namespace ln.types.odb +{ + public class ODBCollection : IEnumerable, IDisposable + { + public ODB ODB { get; } + public String CollectionName { get; } + + public DocumentIndex Index => documentIndex; + + FileStream fileStream; + DocumentIndex documentIndex; + + internal ODBCollection(ODB odb,string collectionName) + { + ODB = odb; + CollectionName = collectionName; + + Initialize(); + } + + private void Initialize() + { + fileStream = new FileStream(Path.Combine(ODB.BasePath, String.Format("{0}.col",CollectionName)),FileMode.OpenOrCreate,FileAccess.ReadWrite); + documentIndex = new DocumentIndex(fileStream); + } + + public void Close() + { + Dispose(); + } + + public bool Insert(ODBDocument document) + { + DocumentIndex.DocumentIndexEntry die = documentIndex.Lookup(document.ID); + if (die == null) + { + byte[] docBytes = document.ToStorage(); + die = documentIndex.FindUnused(docBytes.Length); + + die.Update(document.ID,docBytes); + + return true; + } + return false; + } + public bool Update(ODBDocument document) + { + DocumentIndex.DocumentIndexEntry die = documentIndex.Lookup(document.ID); + if (die != null) + { + byte[] docBytes = document.ToStorage(); + if (die.BufferLength < docBytes.Length) + { + DocumentIndex.DocumentIndexEntry ndie = documentIndex.FindUnused(docBytes.Length); + die.DocumentID = ODBNull.Instance; + ndie.Update(document.ID, docBytes); + die.Release(); + } + else + { + die.Update(document.ID, docBytes); + } + return true; + } + return false; + } + public bool Upsert(ODBDocument document) + { + byte[] docBytes = document.ToStorage(); + DocumentIndex.DocumentIndexEntry die = documentIndex.Lookup(document.ID); + DocumentIndex.DocumentIndexEntry rdie = null; + + if ((die == null) || (die.BufferLength < docBytes.Length)) + { + rdie = die; + die = documentIndex.FindUnused(docBytes.Length); + } + if (rdie != null) + rdie.DocumentID = ODBNull.Instance; + die.Update(document.ID, docBytes); + if (rdie != null) + rdie.Release(); + + return true; + } + + public ODBDocument GetDocumentByID(ODBValue id) + { + Logging.Log(LogLevel.DEBUG, "ODBCollection.GetDocumentByID(): {0}",id); + DocumentIndex.DocumentIndexEntry die = documentIndex.Lookup(id); + if (die != null) + { + byte[] storageBytes = die.ReadStorageBytes(); + ODBDocument document = new ODBDocument(storageBytes, 0, storageBytes.Length); + return document; + } + return null; + } + + public IEnumerable Find(string propertyName, ODBValue value) + { + foreach (ODBDocument document in this) + { + if (value.Equals(document[propertyName])) + yield return document; + } + } + public ODBDocument FindOne(string propertyName, ODBValue value) + { + foreach (ODBDocument document in this) + { + if (value.Equals(document[propertyName])) + return document; + } + return null; + } + + + public IEnumerator GetEnumerator() + { + foreach (ODBValue id in documentIndex.ToArray()) + { + yield return GetDocumentByID(id); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Dispose() + { + ODB.DisposeCollection(this); + + if (fileStream != null) + { + fileStream.Dispose(); + fileStream = null; + } + + documentIndex = null; + } + + + public class DocumentIndex : IEnumerable + { + public Stream StorageStream { get; } + public DocumentIndexEntry Head { get; private set; } + + private Dictionary idLookup = new Dictionary(); + + public DocumentIndex(Stream stream) + { + StorageStream = stream; + Head = new DocumentIndexEntry(this); + } + + public IEnumerable IndexEntries + { + get + { + DocumentIndexEntry entry = Head; + while (entry != null) + { + yield return entry; + entry = entry.Next; + } + } + } + + public DocumentIndexEntry Lookup(ODBValue ID) + { + foreach (DocumentIndexEntry die in IndexEntries) + { + if (ID.Equals(die.DocumentID)) + return die; + } + return null; + } + + public DocumentIndexEntry FindUnused(int minLength) + { + foreach (DocumentIndexEntry die in IndexEntries) + { + if (die.IsUnused && (die.BufferLength > minLength)) + return die; + if (die.Next == null) + return die.Extend(minLength); + } + return null; + } + + public class DocumentIndexEntry + { + public DocumentIndex Index { get; } + + public DocumentIndexEntry Next { get; private set; } + public DocumentIndexEntry Last { get; private set; } + + public long Offset { get; private set; } + public int BufferLength { get; private set; } + + public long NextOffset => Offset + BufferLength + 4; + + private ODBValue documentID; + public ODBValue DocumentID + { + get => documentID; + set + { + lock (Index) + { + if (!ODBNull.Instance.Equals(documentID)) + { + Index.idLookup.Remove(documentID); + } + documentID = value; + if (!ODBNull.Instance.Equals(documentID)) + { + Index.idLookup.Add(documentID, this); + } + } + } + } + + public bool IsUnused => (ODBNull.Instance.Equals(documentID)); + + public DocumentIndexEntry(DocumentIndex index) + : this(index, 0) + { } + private DocumentIndexEntry(DocumentIndex index, long offset) + { + Index = index; + Offset = offset; + Read(); + } + + private DocumentIndexEntry(DocumentIndexEntry lastEntry,int bufferLength) + { + Last = lastEntry; + Last.Next = this; + + Index = lastEntry.Index; + Offset = lastEntry.NextOffset; + BufferLength = bufferLength; + + WriteHeader(); + Release(); + } + private DocumentIndexEntry(DocumentIndexEntry lastEntry) + { + Last = lastEntry; + Index = lastEntry.Index; + Offset = lastEntry.NextOffset; + + if (Last.Next != null) + { + Next = Last.Next; + Last.Next = this; + Next.Last = this; + + BufferLength = (int)(Next.Offset - Offset - 4); + WriteHeader(); + Release(); + } + else + { + Last.Next = this; + Read(); + } + + } + + + private void Read() + { + lock (Index) + { + if (Offset >= Index.StorageStream.Length) + { + BufferLength = 0; + DocumentID = ODBNull.Instance; + } + else + { + Index.StorageStream.Position = Offset; + BufferLength = Index.StorageStream.ReadInteger(); + + Index.StorageStream.Position = Offset + 4; + + DocumentID = ODBValue.Read(Index.StorageStream); + + if (NextOffset < Index.StorageStream.Length) + { + Next = new DocumentIndexEntry(this); + } + } + } + } + + public void Update(ODBValue id,byte[] storageBytes) + { + lock (Index) + { + if (BufferLength < storageBytes.Length) + throw new ArgumentOutOfRangeException(); + + if (BufferLength > (storageBytes.Length + 32)) + { + Split(storageBytes.Length); + } + + Index.StorageStream.Position = Offset + 4; + Index.StorageStream.Write(storageBytes, 0, storageBytes.Length); + DocumentID = id; + } + } + + public void Release() + { + lock (Index) + { + DocumentID = ODBNull.Instance; + + Index.StorageStream.Position = Offset + 4; + Index.StorageStream.Write(new byte[BufferLength], 0, BufferLength); + + if ((Next != null) && Next.IsUnused) + { + Combine(); + } + if ((Last != null) && Last.IsUnused) + { + Last.Combine(); + } + } + } + + public DocumentIndexEntry Extend(int length) + { + if (Next == null) + { + if ((Last == null) && (IsUnused)) + { + BufferLength = length; + + WriteHeader(); + Release(); + + return this; + } + return new DocumentIndexEntry(this, length); + } + throw new NotSupportedException(); + } + + private void Split(int length) + { + BufferLength = length; + DocumentIndexEntry dieInsert = new DocumentIndexEntry(this); + WriteHeader(); + } + private void Combine() + { + BufferLength += Next.BufferLength + 4; + + Next = Next.Next; + if (Next != null) + Next.Last = this; + + WriteHeader(); + } + + private void WriteHeader() + { + lock (Index) + { + Index.StorageStream.Position = Offset; + Index.StorageStream.WriteInteger(BufferLength); + } + } + + public byte[] ReadStorageBytes() + { + byte[] buffer = new byte[BufferLength]; + lock (Index) + { + Index.StorageStream.Position = Offset + 4; + Index.StorageStream.Read(buffer, 0, BufferLength); + } + return buffer; + } + } + + public IEnumerator GetEnumerator() + { + return idLookup.Keys.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + } +} diff --git a/odb/ODBCollection<>.cs b/odb/ODBCollection<>.cs new file mode 100644 index 0000000..b022194 --- /dev/null +++ b/odb/ODBCollection<>.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using ln.types.odb.values; +namespace ln.types.odb +{ + public class ODBCollection : IEnumerable where T:class + { + public ODB ODB { get; } + public Type ElementType { get; } + + ODBCollection collection; + Dictionary> objectCache = new Dictionary>(); + + internal ODBCollection(ODB odb) + { + ODB = odb; + ElementType = typeof(T); + collection = new ODBCollection(odb, ElementType.FullName); + } + + public T this[ODBValue documentID] + { + get => Select(documentID); + } + + + public T GetCachedObject(ODBValue documentID) + { + if (objectCache.ContainsKey(documentID)) + { + T o = null; + if (objectCache[documentID].TryGetTarget(out o)) + return o; + } + return null; + } + + public void TouchCache(ODBValue documentID,T o) + { + if ((o == null)&&objectCache.ContainsKey(documentID)) + { + objectCache.Remove(documentID); + } + else if (o != null) + { + objectCache[documentID] = new WeakReference(o); + } + } + + public T SelectOne(string fieldName, object value) + { + ODBDocument document = collection.FindOne(fieldName, ODBMapper.Default.MapValue(value)); + if (document != null) + { + return Select(document.ID); + } + return null; + } + public IEnumerable Select(string fieldName, object value) + { + foreach (ODBDocument document in collection.Find(fieldName, ODBMapper.Default.MapValue(value))) + { + yield return Select(document.ID); + } + } + + public T Select(ODBValue documentID) + { + T o = GetCachedObject(documentID); + if (o == null) + { + ODBDocument document = collection.GetDocumentByID(documentID); + o = ODBMapper.Default.ToNativeValue(document); + TouchCache(documentID, o); + } + return o; + } + + public bool Insert(T o) + { + ODBDocument document = ODBMapper.Default.MapValue(o) as ODBDocument; + return collection.Insert(document); + } + public bool Update(T o) + { + ODBDocument document = ODBMapper.Default.MapValue(o) as ODBDocument; + return collection.Update(document); + } + public bool Upsert(T o) + { + ODBDocument document = ODBMapper.Default.MapValue(o) as ODBDocument; + return collection.Upsert(document); + } + + public IEnumerator GetEnumerator() + { + foreach (ODBValue documentID in collection.Index) + { + yield return Select(documentID); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/odb/ODBDocument.cs b/odb/ODBDocument.cs new file mode 100644 index 0000000..8e4feb9 --- /dev/null +++ b/odb/ODBDocument.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.IO; +using ln.types.odb.values; +using System.Linq; +namespace ln.types.odb +{ + public class ODBDocument : ODBValue + { + private Dictionary properties = new Dictionary(); + + public ODBDocument() + :base(0x1000) + { + } + + public ODBValue ID { get; set; } = new ODBGuid(); + + public ODBDocument(byte[] bytes,int offset,int length) + :this() + { + int endOffset = offset + length; + + ID = ODBValue.Deserialize(bytes, ref offset); + int nProps = BitConverter.ToInt32(bytes, offset); + offset += 4; + + for (int n=0;n endOffset) + throw new FormatException("ODBDocument deserialization read behind end of buffer"); + } + + public ODBValue this[ODBValue propName] + { + get + { + if (properties.ContainsKey(propName)) + return properties[propName]; + return ODBNull.Instance; + } + set + { + if (ODBNull.Instance.Equals(value) && properties.ContainsKey(propName)) + { + properties.Remove(propName); + } + else + { + properties[propName] = value; + } + } + } + + public IEnumerable Keys => properties.Keys; + + public bool Contains(ODBStringValue propName) + { + return !ODBNull.Instance.Equals(this[propName]); + } + + public override byte[] ToStorage() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + + ID.Store(writer); + writer.Write(properties.Count); + + foreach (ODBStringValue propName in properties.Keys) + { + ODBValue propValue = properties[propName]; + propName.Store(writer); + propValue.Store(writer); + } + + return stream.ToArray(); + } + + public override string ToString() + { + return String.Format("[ODBDocument ID={0} {1}]", ID.ToString(),String.Join(" ",properties.Select(kv=> String.Format("{0}={1}",kv.Key,kv.Value)))); + } + + public override int GetHashCode() + { + return ID.GetHashCode(); + } + public override bool Equals(object obj) + { + if (obj is ODBDocument) + { + ODBDocument you = obj as ODBDocument; + return ID.Equals(you.ID); + } + return false; + } + + static ODBDocument() + { + RegisterDeserializer(0x1000, (b,o,l) => new ODBDocument(b,o,l)); + } + } +} diff --git a/odb/ODBFileStorage.cs b/odb/ODBFileStorage.cs index 6d0e652..5f25740 100644 --- a/odb/ODBFileStorage.cs +++ b/odb/ODBFileStorage.cs @@ -17,135 +17,143 @@ using System.Linq; using System.Reflection; namespace ln.types.odb { - public class ODBFileStorage : Storage - { - public String BasePath { get; set; } - public int StorageVersion { get; } = 0x01; + //public class ODBFileStorage : Storage + //{ + // public String BasePath { get; set; } + // public int StorageVersion { get; } = 0x01; - public ODBFileStorage(string basePath) - { - BasePath = Path.GetFullPath(basePath); - if (!Directory.Exists(BasePath)) - { - Directory.CreateDirectory(BasePath); - } - } + // public ODBFileStorage(string basePath) + // { + // BasePath = Path.GetFullPath(basePath); + // if (!Directory.Exists(BasePath)) + // { + // Directory.CreateDirectory(BasePath); + // } + // } - private string GetPersistentPath(Guid persistenceID) - { - byte[] idBytes = persistenceID.ToByteArray(); - return Path.Combine(BasePath, BitConverter.ToString(idBytes, 0, 4)); - } + // private string GetPersistentPath(Guid persistenceID) + // { + // byte[] idBytes = persistenceID.ToByteArray(); + // return Path.Combine(BasePath, BitConverter.ToString(idBytes, 0, 4)); + // } - public override bool Contains(Guid persistenceID) - { - string fn = Path.Combine(GetPersistentPath(persistenceID), String.Format("{0}.0", persistenceID)); - return File.Exists(fn); - } + // public override bool Contains(Guid persistenceID) + // { + // string fn = Path.Combine(GetPersistentPath(persistenceID), String.Format("{0}.0", persistenceID)); + // return File.Exists(fn); + // } - public override bool Store(PreparedObject preparedObject) - { - string targetPath = GetPersistentPath(preparedObject.PersistenceID); - String fnbase = Path.Combine(targetPath, preparedObject.PersistenceID.ToString()); + // public override bool Store(PreparedObject preparedObject) + // { + // string targetPath = GetPersistentPath(preparedObject.PersistenceID); + // String fnbase = Path.Combine(targetPath, preparedObject.PersistenceID.ToString()); - if (!Directory.Exists(targetPath)) - Directory.CreateDirectory(targetPath); + // if (!Directory.Exists(targetPath)) + // Directory.CreateDirectory(targetPath); - String fnn = String.Format("{0}.new", fnbase); + // String fnn = String.Format("{0}.new", fnbase); - using (FileStream fs = new FileStream(fnn, FileMode.CreateNew)) - { - ToStream(preparedObject, fs); - fs.Close(); - } + // if (File.Exists(fnn)) + // File.Delete(fnn); - for (int n = 5; n > 0; n--) - { - string fn1 = String.Format("{0}.{1}", fnbase, n - 1); - string fn2 = String.Format("{0}.{1}", fnbase, n); + // using (FileStream fs = new FileStream(fnn, FileMode.CreateNew)) + // { + // ToStream(preparedObject, fs); + // fs.Close(); + // } - if (File.Exists(fn1)) - File.Move(fn1, fn2); - } + // for (int n = 5; n > 0; n--) + // { + // string fn1 = String.Format("{0}.{1}", fnbase, n - 1); + // string fn2 = String.Format("{0}.{1}", fnbase, n); - string fn = String.Format("{0}.{1}", fnbase, 0); - File.Move(fnn, fn); + // if (File.Exists(fn1)) + // { + // if (File.Exists(fn2)) + // File.Delete(fn2); - return true; - } + // File.Move(fn1, fn2); + // } + // } + + // string fn = String.Format("{0}.{1}", fnbase, 0); + // File.Move(fnn, fn); + + // return true; + // } - public override bool Load(PreparedObject preparedObject) - { - string targetPath = GetPersistentPath(preparedObject.PersistenceID); - String fnbase = Path.Combine(targetPath, preparedObject.PersistenceID.ToString()); - string fn = String.Format("{0}.0", fnbase); + // public override bool Load(PreparedObject preparedObject) + // { + // string targetPath = GetPersistentPath(preparedObject.PersistenceID); + // String fnbase = Path.Combine(targetPath, preparedObject.PersistenceID.ToString()); + // string fn = String.Format("{0}.0", fnbase); - using (FileStream fs = new FileStream(fn, FileMode.Open)) - { - FromStream(fs, preparedObject); - fs.Close(); - } - return true; - } + // using (FileStream fs = new FileStream(fn, FileMode.Open)) + // { + // FromStream(fs, preparedObject); + // fs.Close(); + // } + // return true; + // } - private void ToStream(PreparedObject preparedObject,Stream stream) - { - using (BinaryWriter writer = new BinaryWriter(stream)) - { - writer.Write(StorageVersion); - writer.Write(DateTimeOffset.Now.ToUnixTimeMilliseconds()); - writer.Write(preparedObject.PreparedType.Assembly.FullName); - writer.Write(preparedObject.PreparedType.FullName); + // private void ToStream(PreparedObject preparedObject,Stream stream) + // { + // using (BinaryWriter writer = new BinaryWriter(stream)) + // { + // writer.Write(StorageVersion); + // writer.Write(DateTimeOffset.Now.ToUnixTimeMilliseconds()); + // writer.Write(preparedObject.PreparedType.Assembly.GetName().Name); + // writer.Write(preparedObject.PreparedType.FullName); - string[] fieldNames = preparedObject.StoredFieldNames; + // string[] fieldNames = preparedObject.StoredFieldNames; - writer.Write(fieldNames.Length); - foreach (String fieldName in fieldNames) - { - byte[] fieldBytes = preparedObject.GetFieldBytes(fieldName); - writer.Write(fieldName); - writer.Write(fieldBytes.Length); - writer.Write(fieldBytes); - } + // writer.Write(fieldNames.Length); + // foreach (String fieldName in fieldNames) + // { + // byte[] fieldBytes = preparedObject.GetFieldBytes(fieldName); + // writer.Write(fieldName); + // writer.Write(fieldBytes.Length); + // writer.Write(fieldBytes); + // } - writer.Close(); - } - } + // writer.Close(); + // } + // } - private void FromStream(Stream stream,PreparedObject preparedObject) - { - using (BinaryReader reader = new BinaryReader(stream)) - { - if (reader.ReadInt32() != StorageVersion) - throw new FormatException("Unsupported Storage Version"); + // private void FromStream(Stream stream,PreparedObject preparedObject) + // { + // using (BinaryReader reader = new BinaryReader(stream)) + // { + // if (reader.ReadInt32() != StorageVersion) + // throw new FormatException("Unsupported Storage Version"); - DateTime timestamp = DateTimeOffset.FromUnixTimeMilliseconds(reader.ReadInt64()).DateTime; + // DateTime timestamp = DateTimeOffset.FromUnixTimeMilliseconds(reader.ReadInt64()).DateTime; - string assemblyName = reader.ReadString(); - string typeName = reader.ReadString(); + // string assemblyName = reader.ReadString(); + // string typeName = reader.ReadString(); - Assembly assembly = Assembly.Load(assemblyName); - Type targetType = assembly.GetType(typeName); + // Assembly assembly = Assembly.Load(assemblyName); + // Type targetType = assembly.GetType(typeName); - preparedObject.ReConfigure(targetType,timestamp); + // preparedObject.ReConfigure(targetType,timestamp); - int nStoredFields = reader.ReadInt32(); + // int nStoredFields = reader.ReadInt32(); - for (int n = 0; n < nStoredFields; n++) - { - string fieldName = reader.ReadString(); - int nBytes = reader.ReadInt32(); - byte[] fieldBytes = reader.ReadBytes(nBytes); - preparedObject.SetFieldBytes(fieldName, fieldBytes); - } + // for (int n = 0; n < nStoredFields; n++) + // { + // string fieldName = reader.ReadString(); + // int nBytes = reader.ReadInt32(); + // byte[] fieldBytes = reader.ReadBytes(nBytes); + // preparedObject.SetFieldBytes(fieldName, fieldBytes); + // } - reader.Close(); - } - } + // reader.Close(); + // } + // } - } + //} } diff --git a/odb/ODBMapper.cs b/odb/ODBMapper.cs new file mode 100644 index 0000000..ca2fc09 --- /dev/null +++ b/odb/ODBMapper.cs @@ -0,0 +1,220 @@ +using System; +using ln.types.odb.values; +using System.Collections.Generic; +using System.Runtime.Remoting.Messaging; +using System.Reflection; +using System.Linq; +using System.Collections; +namespace ln.types.odb +{ + public class IgnoreFieldAttribute : Attribute + { + } + + public delegate ODBValue ODBMap(ODBMapper mapper, object value); + public delegate object ODBUnmap(ODBMapper mapper, ODBValue oval); + public delegate ODBValue ODBMap(ODBMapper mapper, T value); + public delegate T ODBUnmap(ODBMapper mapper, ODBValue oval); + + public interface IODBMapping + { + ODBValue MapValue(ODBMapper mapper, object value); + object UnmapValue(ODBMapper mapper, ODBValue oval); + } + public interface IODBMapping + { + ODBValue MapValue(ODBMapper mapper, T value); + T UnmapValue(ODBMapper mapper, ODBValue oval); + } + + public class SimpleMapping : IODBMapping + { + ODBMap map; + ODBUnmap unmap; + + public SimpleMapping(ODBMap map,ODBUnmap unmap) + { + this.map = map; + this.unmap = unmap; + } + + public ODBValue MapValue(ODBMapper mapper, object value) + { + return map(mapper, value); + } + + public object UnmapValue(ODBMapper mapper, ODBValue oval) + { + return unmap(mapper, oval); + } + } + public class SimpleMapping : SimpleMapping + { + public SimpleMapping(ODBMap map, ODBUnmap unmap) + :base((mapper, value) => map(mapper,(T)value),(mapper, oval) => unmap(mapper,oval)) + {} + } + + public class ODBMapper + { + public static ODBMapper Default { get; } = new ODBMapper(); + + Dictionary mappings = new Dictionary(); + + private ODBMapper() + { + RegisterMapping( + (mapper, value) => new ODBStringValue(value), + (mapper, oval) => oval.AsString + ); + RegisterMapping( + (mapper, value) => new ODBInteger(value), + (mapper, oval) => oval.AsInt + ); + RegisterMapping( + (mapper, value) => new ODBInteger(value), + (mapper, oval) => oval.AsShort + ); + RegisterMapping( + (mapper, value) => new ODBInteger(value), + (mapper, oval) => oval.AsByte + ); + + RegisterMapping( + (mapper, value) => new ODBUInteger(value), + (mapper, oval) => oval.AsUInt + ); + RegisterMapping( + (mapper, value) => new ODBUInteger(value), + (mapper, oval) => oval.AsUShort + ); + RegisterMapping( + (mapper, value) => new ODBUInteger(value), + (mapper, oval) => oval.AsChar + ); + + RegisterMapping( + (mapper, value) => new ODBDouble(value), + (mapper, oval) => oval.AsDouble + ); + RegisterMapping( + (mapper, value) => new ODBDouble(value), + (mapper, oval) => oval.AsFloat + ); + + RegisterMapping( + (mapper, value) => new ODBLong(DateTime.MinValue.Equals(value) ? 0 : new DateTimeOffset(value).ToUnixTimeMilliseconds() ), + (mapper, oval) => DateTimeOffset.FromUnixTimeMilliseconds(oval.AsLong).DateTime + ); + RegisterMapping( + (mapper, value) => new ODBDouble(value.TotalMilliseconds), + (mapper, oval) => TimeSpan.FromMilliseconds(oval.AsDouble) + ); + + RegisterMapping( + (mapper, value) => new ODBGuid(value), + (mapper, oval) => oval.AsGuid + ); + + RegisterMapping( + (mapper, value) => new ODBLong(value), + (mapper, oval) => oval.AsLong + ); + RegisterMapping( + (mapper, value) => new ODBULong(value), + (mapper, oval) => oval.AsULong + ); + + RegisterMapping( + (mapper, value) => new ODBInteger(value ? -1 : 0), + (mapper, oval) => oval.AsBool + ); + + RegisterMapping(typeof(object),new ObjectMapping()); + } + + public void RegisterMapping(Type nativeType,IODBMapping mapping) + { + mappings[nativeType] = mapping; + } + public void RegisterMapping(Type nativeType, ODBMap map, ODBUnmap unmap) + { + mappings[nativeType] = new SimpleMapping(map, unmap); + } + public void RegisterMapping(ODBMap map, ODBUnmap unmap) + { + mappings[typeof(T)] = new SimpleMapping(map, unmap); + } + + public IODBMapping GetMapping() => null; + public IODBMapping GetMapping(Type type) + { + if (mappings.ContainsKey(type)) + return mappings[type]; + + if (typeof(string).Equals(type)) + { + return null; + } + else if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Dictionary<,>))) + { + mappings.Add(type, new DictionaryMapping()); + return mappings[type]; + } + else if (type.GetInterfaces().Contains(typeof(IDictionary))) + { + mappings.Add(type, new DictionaryMapping()); + return mappings[type]; + } + else if (type.IsArray) + { + mappings.Add(type, new ListMapping(type)); + return mappings[type]; + } + else if (!type.IsPrimitive) + { + mappings.Add(type, new ClassMapping(type)); + return mappings[type]; + } + + return null; + } + + public virtual ODBValue MapValue(object value) + { + if (value == null) + return ODBNull.Instance; + + IODBMapping mapping = GetMapping(value.GetType()); + if (mapping != null) + return mappings[value.GetType()].MapValue(this,value); + + throw new NotSupportedException(String.Format("Can't map {0} ({1})",value.GetType(),value)); + } + public virtual object UnmapValue(Type targetType,ODBValue value) + { + if (ODBNull.Instance.Equals(value)) + return null; + + if (value is ODBDocument) + { + ODBDocument doc = value as ODBDocument; + String asmname = doc["__asm__"].AsString; + String typename = doc["__type__"].AsString; + + targetType = Assembly.Load(asmname).GetType(typename); + } + + IODBMapping mapping = GetMapping(targetType); + if (mapping != null) + return mappings[targetType].UnmapValue(this,value); + + return Convert.ChangeType(value.Value, targetType); + } + public virtual T ToNativeValue(ODBValue value) + { + return (T)UnmapValue(typeof(T), value); + } + + } +} diff --git a/odb/ODBObjectReader.cs b/odb/ODBObjectReader.cs index bb670c8..81fd00b 100644 --- a/odb/ODBObjectReader.cs +++ b/odb/ODBObjectReader.cs @@ -12,26 +12,26 @@ using System.IO; using ln.types.serialize; namespace ln.types.odb { - public class ODBObjectReader : ObjectReader - { - public ODB ODB { get; } + //public class ODBObjectReader : ObjectReader + //{ + // public ODB ODB { get; } - public ODBObjectReader(ODB odb, Stream stream) : - base(stream) - { - ODB = odb; - } + // public ODBObjectReader(ODB odb, Stream stream) : + // base(stream) + // { + // ODB = odb; + // } - public override object QueryReferencedObject(object re) - { - if (re is Guid) - { - Guid persistenceID = (Guid)re; - IPersistent persistent = ODB.LoadPersistent(persistenceID); - return persistent; - } + // public override object QueryReferencedObject(object re) + // { + // if (re is Guid) + // { + // Guid persistenceID = (Guid)re; + // IPersistent persistent = ODB.LoadPersistent(persistenceID); + // return persistent; + // } - return base.QueryReferencedObject(re); - } - } + // return base.QueryReferencedObject(re); + // } + //} } diff --git a/odb/ODBObjectWriter.cs b/odb/ODBObjectWriter.cs index 7221585..dc81181 100644 --- a/odb/ODBObjectWriter.cs +++ b/odb/ODBObjectWriter.cs @@ -14,30 +14,30 @@ using ln.types.serialize; using System.Linq; namespace ln.types.odb { - class ODBObjectWriter : ObjectWriter - { - public IPersistent[] ReferencedPersistents => referencedPersistents.Values.ToArray(); - public Guid[] ReferencedPersistentIDs => referencedPersistents.Keys.ToArray(); + //class ODBObjectWriter : ObjectWriter + //{ + // public IPersistent[] ReferencedPersistents => referencedPersistents.Values.ToArray(); + // public Guid[] ReferencedPersistentIDs => referencedPersistents.Keys.ToArray(); - private Dictionary referencedPersistents = new Dictionary(); + // private Dictionary referencedPersistents = new Dictionary(); - public ODBObjectWriter(Stream stream) - : base(stream) - {} + // public ODBObjectWriter(Stream stream) + // : base(stream) + // {} - public override object QueryReference(object o) - { - if (o is IPersistent) - { - IPersistent persistent = o as IPersistent; - Guid persistenceID = persistent.GetPersistenceID(); + // public override object QueryReference(object o) + // { + // if (o is IPersistent) + // { + // IPersistent persistent = o as IPersistent; + // Guid persistenceID = persistent.GetPersistenceID(); - if (!referencedPersistents.ContainsKey(persistenceID)) - referencedPersistents.Add(persistenceID, persistent); + // if (!referencedPersistents.ContainsKey(persistenceID)) + // referencedPersistents.Add(persistenceID, persistent); - return persistenceID; - } - return base.QueryReference(o); - } - } + // return persistenceID; + // } + // return base.QueryReference(o); + // } + //} } diff --git a/odb/Persistent.cs b/odb/Persistent.cs index ef31728..0727038 100644 --- a/odb/Persistent.cs +++ b/odb/Persistent.cs @@ -8,10 +8,12 @@ // * // **/ using System; +using LiteDB; namespace ln.types.odb { public class Persistent : IPersistent { + [BsonId] public Guid PersistenceID { get; private set; } public Persistent() diff --git a/odb/PersistentList.cs b/odb/PersistentList.cs index a7aa69c..9e22a94 100644 --- a/odb/PersistentList.cs +++ b/odb/PersistentList.cs @@ -3,163 +3,163 @@ using System.Collections; using System.Collections.Generic; namespace ln.types.odb { - public class PersistentList : IList where T: IPersistent - { - private Dictionary persistentInstances = new Dictionary(); - private List index = new List(); + //public class PersistentList : IList where T: IPersistent + //{ + // private Dictionary persistentInstances = new Dictionary(); + // private List index = new List(); - public ODB ODB { get; } + // public ODB ODB { get; } - public PersistentList(ODB odb) - { - ODB = odb; - } + // public PersistentList(ODB odb) + // { + // ODB = odb; + // } - public T this[int n] - { - get { - Guid persistentID = index[n]; - if (!persistentInstances.ContainsKey(persistentID)) - persistentInstances[persistentID] = (T)ODB.LoadPersistent(persistentID); + // public T this[int n] + // { + // get { + // Guid persistentID = index[n]; + // if (!persistentInstances.ContainsKey(persistentID)) + // persistentInstances[persistentID] = (T)ODB.LoadPersistent(persistentID); - return persistentInstances[persistentID]; - } - set { - if (value == null) - index[n] = Guid.Empty; + // return persistentInstances[persistentID]; + // } + // set { + // if (value == null) + // index[n] = Guid.Empty; - Guid persistenceID = value.GetPersistenceID(); - persistentInstances[persistenceID] = value; - index[n] = persistenceID; - } - } + // Guid persistenceID = value.GetPersistenceID(); + // persistentInstances[persistenceID] = value; + // index[n] = persistenceID; + // } + // } - public int Count => index.Count; - public bool IsReadOnly => false; + // public int Count => index.Count; + // public bool IsReadOnly => false; - public void Add(T item) - { - if (item == null) - { - index.Add(Guid.Empty); - } - else - { - Guid persistenceID = item.GetPersistenceID(); - persistentInstances[persistenceID] = item; - index.Add(persistenceID); - } - } + // public void Add(T item) + // { + // if (item == null) + // { + // index.Add(Guid.Empty); + // } + // else + // { + // Guid persistenceID = item.GetPersistenceID(); + // persistentInstances[persistenceID] = item; + // index.Add(persistenceID); + // } + // } - public void Clear() - { - persistentInstances.Clear(); - index.Clear(); - } + // public void Clear() + // { + // persistentInstances.Clear(); + // index.Clear(); + // } - public bool Contains(T item) - { - Guid persistenceID = item.GetPersistenceID(); - if (index.Contains(persistenceID)) - return true; + // public bool Contains(T item) + // { + // Guid persistenceID = item.GetPersistenceID(); + // if (index.Contains(persistenceID)) + // return true; - foreach (T mine in this) - { - if (mine.Equals(item)) - return true; - } - return false; - } + // foreach (T mine in this) + // { + // if (mine.Equals(item)) + // return true; + // } + // return false; + // } - public void CopyTo(T[] array, int arrayIndex) - { - foreach (T item in this) - { - array[arrayIndex] = this[arrayIndex]; - arrayIndex++; - } - } + // public void CopyTo(T[] array, int arrayIndex) + // { + // foreach (T item in this) + // { + // array[arrayIndex] = this[arrayIndex]; + // arrayIndex++; + // } + // } - public IEnumerator GetEnumerator() - { - return new PersistentListEnumerator(this); - } + // public IEnumerator GetEnumerator() + // { + // return new PersistentListEnumerator(this); + // } - public int IndexOf(T item) - { - Guid persistenceID = item == null ? Guid.Empty : item.GetPersistenceID(); + // public int IndexOf(T item) + // { + // Guid persistenceID = item == null ? Guid.Empty : item.GetPersistenceID(); - int i = index.IndexOf(persistenceID); - if (i < 0) - { - for (int n=0;n= 0) - RemoveAt(ind); + // public bool Remove(T item) + // { + // int ind = IndexOf(item); + // if (ind >= 0) + // RemoveAt(ind); - return ind >= 0; - } + // return ind >= 0; + // } - public void RemoveAt(int _index) - { - Guid persistenceID = index[_index]; - if (persistentInstances.ContainsKey(persistenceID)) - persistentInstances.Remove(persistenceID); - index.RemoveAt(_index); - } + // public void RemoveAt(int _index) + // { + // Guid persistenceID = index[_index]; + // if (persistentInstances.ContainsKey(persistenceID)) + // persistentInstances.Remove(persistenceID); + // index.RemoveAt(_index); + // } - IEnumerator IEnumerable.GetEnumerator() - { - return new PersistentListEnumerator(this); - } + // IEnumerator IEnumerable.GetEnumerator() + // { + // return new PersistentListEnumerator(this); + // } - class PersistentListEnumerator : IEnumerator - { - int currentIndex = -1; - PersistentList persistentList; + // class PersistentListEnumerator : IEnumerator + // { + // int currentIndex = -1; + // PersistentList persistentList; - public PersistentListEnumerator(PersistentList persistentList) - { - this.persistentList = persistentList; - } + // public PersistentListEnumerator(PersistentList persistentList) + // { + // this.persistentList = persistentList; + // } - public T Current => persistentList[currentIndex]; - object IEnumerator.Current => persistentList[currentIndex]; + // public T Current => persistentList[currentIndex]; + // object IEnumerator.Current => persistentList[currentIndex]; - public void Dispose() - { - persistentList = null; - } + // public void Dispose() + // { + // persistentList = null; + // } - public bool MoveNext() - { - currentIndex++; - return (currentIndex < persistentList.Count); - } + // public bool MoveNext() + // { + // currentIndex++; + // return (currentIndex < persistentList.Count); + // } - public void Reset() - { - currentIndex = -1; - } - } + // public void Reset() + // { + // currentIndex = -1; + // } + // } - } + //} } diff --git a/odb/PreparedObject.cs b/odb/PreparedObject.cs index ca3c82b..a11e8a8 100644 --- a/odb/PreparedObject.cs +++ b/odb/PreparedObject.cs @@ -13,171 +13,169 @@ using System.CodeDom; using System.Reflection; using System.IO; using System.Linq; +using ln.types.sync; namespace ln.types.odb { - public class PreparedObject - { - public ODB ODB { get; private set; } + //public class PreparedObject + //{ + // public ODB ODB { get; private set; } - public DateTime Timestamp { get; private set; } - public Type PreparedType { get; private set; } + // public DateTime Timestamp { get; private set; } + // public Type PreparedType { get; private set; } - public IPersistent Instance { get; set; } - public Guid PersistenceID { get; private set; } + // public IPersistent Instance { get; set; } + // public Guid PersistenceID { get; private set; } - public string[] StoredFieldNames => fieldStore.Keys.ToArray(); + // public string[] StoredFieldNames => fieldStore.Keys.ToArray(); - public IEnumerable ReferencedPersistentIDs => referencedPersistentIDs; - public IEnumerable ReferencedPersistents => referencedPersistents; + // public IEnumerable ReferencedPersistentIDs => referencedPersistentIDs; + // public IEnumerable ReferencedPersistents => referencedPersistents; - List referencedPersistentIDs = new List(); - HashSet referencedPersistents = new HashSet(); - Dictionary fieldStore = new Dictionary(); + // List referencedPersistentIDs = new List(); + // HashSet referencedPersistents = new HashSet(); + // Dictionary fieldStore = new Dictionary(); - public PreparedObject(ODB odb,Type preparedType,Guid persistenceID) - { - ODB = odb; - Timestamp = DateTime.Now; - PreparedType = preparedType; - PersistenceID = persistenceID; - Instance = null; - } - public PreparedObject(ODB odb, IPersistent o) - { - ODB = odb; - Timestamp = DateTime.Now; - PreparedType = o.GetType(); - PersistenceID = o.GetPersistenceID(); - Instance = o; + // public PreparedObject(ODB odb,Type preparedType,Guid persistenceID) + // { + // ODB = odb; + // Timestamp = DateTime.Now; + // PreparedType = preparedType; + // PersistenceID = persistenceID; + // Instance = null; + // } + // public PreparedObject(ODB odb, IPersistent o) + // { + // ODB = odb; + // Timestamp = DateTime.Now; + // PreparedType = o.GetType(); + // PersistenceID = o.GetPersistenceID(); + // Instance = o; - SyncFromInstance(); - } + // SyncFromInstance(); + // } - private IEnumerable GetAllFields(Type type) - { - if (type == typeof(object)) - { - return new FieldInfo[0]; - } - else - { - return GetAllFields(type.BaseType).Concat(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)); - } - } + // private IEnumerable GetAllFields(Type type) + // { + // if (type == typeof(object)) + // { + // return new FieldInfo[0]; + // } + // else + // { + // return GetAllFields(type.BaseType).Concat(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)); + // } + // } - private void SyncFromInstance() - { - referencedPersistentIDs.Clear(); - FieldInfo[] fields = GetAllFields(PreparedType).ToArray(); + // public void SyncFromInstance() + // { + // referencedPersistentIDs.Clear(); + // FieldInfo[] fields = GetAllFields(PreparedType).ToArray(); - foreach (FieldInfo fieldInfo in fields) - { - if (!fieldInfo.IsStatic) - { - object value = fieldInfo.GetValue(Instance); - if (value == null) - { - fieldStore[fieldInfo.Name] = new byte[0]; - } - else - { - MemoryStream objectStream = new MemoryStream(); - ODBObjectWriter objectWriter = new ODBObjectWriter(objectStream); + // foreach (FieldInfo fieldInfo in fields) + // { + // if (!fieldInfo.IsStatic && (fieldInfo.GetCustomAttribute() == null)) + // { + // object value = fieldInfo.GetValue(Instance); + // if (value == null) + // { + // fieldStore[fieldInfo.Name] = new byte[0]; + // } + // else + // { + // MemoryStream objectStream = new MemoryStream(); + // ODBObjectWriter objectWriter = new ODBObjectWriter(objectStream); - objectWriter.Write(value); + // objectWriter.Write(value); - fieldStore[fieldInfo.Name] = objectStream.ToArray(); - referencedPersistentIDs.AddRange(objectWriter.ReferencedPersistentIDs); - foreach (IPersistent persistent in objectWriter.ReferencedPersistents) - referencedPersistents.Add(persistent); - } - } - } - } - private void SyncToInstance() - { - foreach (FieldInfo fieldInfo in GetAllFields(PreparedType)) - { - if (!fieldInfo.IsStatic) - { - byte[] bytes = fieldStore[fieldInfo.Name]; - if (bytes.Length == 0) - { - fieldInfo.SetValue(Instance, null); - } - else - { - MemoryStream objectStream = new MemoryStream(fieldStore[fieldInfo.Name]); - ODBObjectReader objectReader = new ODBObjectReader(ODB, objectStream); + // fieldStore[fieldInfo.Name] = objectStream.ToArray(); + // referencedPersistentIDs.AddRange(objectWriter.ReferencedPersistentIDs); + // foreach (IPersistent persistent in objectWriter.ReferencedPersistents) + // referencedPersistents.Add(persistent); + // } + // } + // } + // } + // public void SyncToInstance() + // { + // foreach (FieldInfo fieldInfo in GetAllFields(PreparedType)) + // { + // if (!fieldInfo.IsStatic && (fieldInfo.GetCustomAttribute() == null)) + // { + // byte[] bytes = fieldStore[fieldInfo.Name]; + // if (bytes.Length == 0) + // { + // fieldInfo.SetValue(Instance, null); + // } + // else + // { + // MemoryStream objectStream = new MemoryStream(fieldStore[fieldInfo.Name]); + // ODBObjectReader objectReader = new ODBObjectReader(ODB, objectStream); - object value = objectReader.Read(); - fieldInfo.SetValue(Instance, value); - } - } - } - } + // object value = objectReader.Read(); + // fieldInfo.SetValue(Instance, value); + // } + // } + // } + // } - public void ReConfigure(Type targetType,DateTime timestamp) - { - Timestamp = timestamp; - PreparedType = targetType; - fieldStore.Clear(); - Instance = null; - } + // public void ReConfigure(Type targetType,DateTime timestamp) + // { + // Timestamp = timestamp; + // PreparedType = targetType; + // fieldStore.Clear(); + // Instance = null; + // } - public byte[] GetFieldBytes(string fieldName) - { - return fieldStore[fieldName]; - } - public void SetFieldBytes(string fieldName,byte[] bytes) - { - fieldStore[fieldName] = bytes; - } + // public byte[] GetFieldBytes(string fieldName) + // { + // return fieldStore[fieldName]; + // } + // public void SetFieldBytes(string fieldName,byte[] bytes) + // { + // fieldStore[fieldName] = bytes; + // } - public IPersistent CreateInstance() - { - Instance = (IPersistent)Activator.CreateInstance(PreparedType,true); - SyncToInstance(); - return Instance; - } + // public IPersistent CreateInstance() + // { + // Instance = (IPersistent)Activator.CreateInstance(PreparedType,true); + // SyncToInstance(); + // return Instance; + // } - public bool InstanceEquals(PreparedObject other) - { - if (other == null) - return false; + // public bool InstanceEquals(PreparedObject other) + // { + // if (other == null) + // return false; - if (Equals(other)) - { - foreach (String fieldName in fieldStore.Keys) - { - byte[] me = fieldStore[fieldName]; - byte[] you = other.fieldStore[fieldName]; + // if (Equals(other)) + // { + // foreach (String fieldName in fieldStore.Keys) + // { + // byte[] me = fieldStore[fieldName]; + // byte[] you = other.fieldStore[fieldName]; - File.WriteAllBytes("me.bin", me); - File.WriteAllBytes("you.bin", you); + // if (!me.AreEqual(you)) + // return false; + // } + // return true; + // } + // return false; + // } - if (!me.AreEqual(you)) - return false; - } - return true; - } - return false; - } + // public override int GetHashCode() + // { + // return PersistenceID.GetHashCode() ^ PreparedType.GetHashCode(); + // } - public override int GetHashCode() - { - return PersistenceID.GetHashCode() ^ PreparedType.GetHashCode(); - } + // public override bool Equals(object obj) + // { + // if (obj is PreparedObject) + // { + // PreparedObject you = obj as PreparedObject; + // return PreparedType.Equals(you.PreparedType) && PersistenceID.Equals(you.PersistenceID); + // } + // return false; + // } - public override bool Equals(object obj) - { - if (obj is PreparedObject) - { - PreparedObject you = obj as PreparedObject; - return PreparedType.Equals(you.PreparedType) && PersistenceID.Equals(you.PersistenceID); - } - return false; - } - - } + //} } diff --git a/odb/Storage.cs b/odb/Storage.cs index 8a02bf2..45b2c21 100644 --- a/odb/Storage.cs +++ b/odb/Storage.cs @@ -1,19 +1,19 @@ using System; namespace ln.types.odb { - public abstract class Storage - { - public Storage() - { - } + //public abstract class Storage + //{ + // public Storage() + // { + // } - public abstract bool Contains(Guid persistenceID); + // public abstract bool Contains(Guid persistenceID); - public abstract bool Store(PreparedObject preparedObject); - public abstract bool Load(PreparedObject preparedObject); + // public abstract bool Store(PreparedObject preparedObject); + // public abstract bool Load(PreparedObject preparedObject); - } + //} } diff --git a/odb/values/ODBDouble.cs b/odb/values/ODBDouble.cs new file mode 100644 index 0000000..a79daf0 --- /dev/null +++ b/odb/values/ODBDouble.cs @@ -0,0 +1,28 @@ +using System; +namespace ln.types.odb.values +{ + public class ODBDouble : ODBValue + { + public ODBDouble() + :base(0x18) + { + } + public ODBDouble(double value) + : this() + { + Value = value; + } + + public override byte[] ToStorage() + { + return BitConverter.GetBytes(AsDouble); + } + + static ODBDouble() + { + RegisterDeserializer(0x0018, (b, o, l) => BitConverter.ToDouble(b, o)); + //RegisterValueFactory(typeof(double), v => new ODBDouble((double)v)); + //RegisterValueFactory(typeof(float), v => new ODBDouble((double)v)); + } + } +} diff --git a/odb/values/ODBGuid.cs b/odb/values/ODBGuid.cs new file mode 100644 index 0000000..c68e5ae --- /dev/null +++ b/odb/values/ODBGuid.cs @@ -0,0 +1,36 @@ +using System; +using System.Linq; +namespace ln.types.odb.values +{ + public class ODBGuid : ODBValue + { + public ODBGuid() + :base(0x03) + { + Value = Guid.NewGuid(); + } + public ODBGuid(Guid guid) + :this() + { + Value = guid; + } + + public override byte[] ToStorage() + { + return AsGuid.ToByteArray(); + } + + static Guid FromByteArray(byte[] b,int offset) + { + byte[] s = new byte[16]; + Array.Copy(b, offset, s, 0, 16); + return new Guid(s); + } + + static ODBGuid() + { + RegisterDeserializer(0x03, (b,o,l) => FromByteArray(b,o)); + } + + } +} diff --git a/odb/values/ODBInteger.cs b/odb/values/ODBInteger.cs new file mode 100644 index 0000000..9b73d69 --- /dev/null +++ b/odb/values/ODBInteger.cs @@ -0,0 +1,53 @@ +using System; +using System.Runtime.CompilerServices; +namespace ln.types.odb.values +{ + public class ODBInteger : ODBValue + { + public ODBInteger() + : base(0x10) + { + } + public ODBInteger(int i) + : this() + { + Value = i; + } + + public override bool AsBool => AsInt != 0; + + public override byte[] ToStorage() => BitConverter.GetBytes(AsInt); + + static ODBInteger() + { + RegisterDeserializer(0x10, (b, o, l) => BitConverter.ToInt32(b, o)); + //RegisterValueFactory(typeof(int), v => new ODBInteger((int)v)); + //RegisterValueFactory(typeof(short), v => new ODBInteger((int)(short)v)); + //RegisterValueFactory(typeof(byte), v => new ODBInteger((int)(byte)v)); + } + + } + public class ODBUInteger : ODBValue + { + public ODBUInteger() + : base(0x11) + { + } + public ODBUInteger(uint i) + : this() + { + Value = i; + } + + public override byte[] ToStorage() => BitConverter.GetBytes(AsUInt); + + static ODBUInteger() + { + RegisterDeserializer(0x11, (b, o, l) => BitConverter.ToUInt32(b, o)); + //RegisterValueFactory(typeof(uint), v => new ODBUInteger((uint)v)); + //RegisterValueFactory(typeof(ushort), v => new ODBUInteger((uint)(ushort)v)); + //RegisterValueFactory(typeof(char), v => new ODBUInteger((uint)(char)v)); + } + + } +} diff --git a/odb/values/ODBList.cs b/odb/values/ODBList.cs new file mode 100644 index 0000000..22cdfa7 --- /dev/null +++ b/odb/values/ODBList.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Collections; +using System.Runtime.CompilerServices; +namespace ln.types.odb.values +{ + public class ODBList : ODBValue + { + List items = new List(); + + public ODBList() + :base(0x02) + { + } + public ODBList(byte[] bytes,int offset,int length) + :this() + { + MemoryStream stream = new MemoryStream(bytes, offset, length); + int nItems = stream.ReadInteger(); + for (int n = 0; n < nItems; n++) + items.Add(ODBValue.Read(stream)); + } + + public ODBValue this[int i] + { + get => items[i]; + set => items[i] = value; + } + + public void Add(ODBValue value) + { + items.Add(value); + } + public void Remove(ODBValue value) + { + items.Remove(value); + } + public void RemoveAt(int i) + { + items.RemoveAt(i); + } + + public int Count => items.Count; + + public override object Value { + get + { + object[] a = new object[items.Count]; + ((IList)items).CopyTo(a, 0); + return a; + } + protected set + { + throw new NotSupportedException(); + } + } + + public override byte[] ToStorage() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + + writer.Write(items.Count); + + foreach (ODBValue value in items) + value.Store(writer); + + return stream.ToArray(); + } + + static ODBList() + { + RegisterDeserializer(0x02, (b, o, l) => new ODBList(b,o,l)); + } + + + } +} diff --git a/odb/values/ODBLong.cs b/odb/values/ODBLong.cs new file mode 100644 index 0000000..1761039 --- /dev/null +++ b/odb/values/ODBLong.cs @@ -0,0 +1,52 @@ +using System; +using System.Runtime.CompilerServices; +namespace ln.types.odb.values +{ + public class ODBLong : ODBValue + { + public ODBLong() + : base(0x12) + { + } + public ODBLong(long i) + : this() + { + Value = i; + } + + public override byte[] ToStorage() => BitConverter.GetBytes(AsLong); + + public override DateTime AsDateTime => DateTimeOffset.FromUnixTimeMilliseconds(AsLong).DateTime; + public override TimeSpan AsTimeSpan => TimeSpan.FromMilliseconds(AsDouble); + + + static ODBLong() + { + RegisterDeserializer(0x12, (b, o, l) => BitConverter.ToInt64(b, o)); + //RegisterValueFactory(typeof(long), v => new ODBLong((long)v)); + //RegisterValueFactory(typeof(DateTime), v => new ODBLong(new DateTimeOffset((DateTime)v).ToUnixTimeMilliseconds())); + } + + } + public class ODBULong : ODBValue + { + public ODBULong() + : base(0x13) + { + } + public ODBULong(ulong i) + : this() + { + Value = i; + } + + public override byte[] ToStorage() => BitConverter.GetBytes(AsULong); + + static ODBULong() + { + RegisterDeserializer(0x13, (b, o, l) => BitConverter.ToUInt64(b, o)); + //RegisterValueFactory(typeof(ulong), v => new ODBULong((ulong)v)); + } + + } +} diff --git a/odb/values/ODBNull.cs b/odb/values/ODBNull.cs new file mode 100644 index 0000000..00df3c7 --- /dev/null +++ b/odb/values/ODBNull.cs @@ -0,0 +1,32 @@ +using System; +namespace ln.types.odb.values +{ + + public class ODBNull : ODBValue + { + public static readonly ODBNull Instance = new ODBNull(); + + public ODBNull() + : base(0x00) + { } + + public override byte[] ToStorage() + { + return new byte[0]; + } + + static ODBNull() + { + RegisterDeserializer(0x00, (b,o,l) => Instance); + } + + public override int GetHashCode() + { + return 0; + } + public override bool Equals(object obj) + { + return (obj == null) || (obj is ODBNull); + } + } +} diff --git a/odb/values/ODBStringValue.cs b/odb/values/ODBStringValue.cs new file mode 100644 index 0000000..ce6ca2c --- /dev/null +++ b/odb/values/ODBStringValue.cs @@ -0,0 +1,36 @@ +using System; +using System.Text; +namespace ln.types.odb.values +{ + public class ODBStringValue : ODBValue + { + public ODBStringValue() + : base(0x01) + { } + + public ODBStringValue(String text) + : this() + { + Value = text; + } + + public override byte[] ToStorage() + { + return Encoding.UTF8.GetBytes(AsString); + } + + + public static implicit operator ODBStringValue(String text) + { + return new ODBStringValue(text); + } + + + static ODBStringValue() + { + RegisterDeserializer(0x01, (b, o, l) => new ODBStringValue(Encoding.UTF8.GetString(b, o, l))); +// RegisterValueFactory(typeof(string), v => new ODBStringValue((string)v)); + } + + } +} diff --git a/odb/values/ODBValue.cs b/odb/values/ODBValue.cs new file mode 100644 index 0000000..a14d68a --- /dev/null +++ b/odb/values/ODBValue.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.IO; + +/** + * typeCode list + * + * 0x0000 ODBNull + * 0x0001 ODBStringValue + * 0x0002 ODBList + * 0x0003 ODBGuid + * + * 0x0010 ODBInteger + * 0x0011 ODBUInteger + * 0x0012 ODBLong + * 0x0013 ODBULong + * + * 0x0018 ODBDouble + * + * 0x1000 ODBDocument + * + * + * + **/ + + + +namespace ln.types.odb.values +{ + public delegate ODBValue ODBValueFactory(object value); + public delegate ODBValue ODBDeserialize(byte[] storageBytes, int offset, int length); + + public abstract class ODBValue + { + int storageTypeCode; + + public virtual object Value { get; protected set; } + + protected ODBValue(int storageTypeCode) + { + this.storageTypeCode = storageTypeCode; + } + + protected ODBValue(int storageTypeCode, object value) + : this(storageTypeCode) + { + Value = value; + } + + public abstract byte[] ToStorage(); + + public object AsObject => Value; + + public virtual string AsString => (string)Value; + + public virtual bool AsBool => (bool)Value; + public virtual byte AsByte => (byte)Value; + public virtual char AsChar => (char)Value; + public virtual short AsShort => (short)Value; + public virtual int AsInt => (int)Value; + public virtual long AsLong => (long)Value; + public virtual ushort AsUShort => (ushort)Value; + public virtual uint AsUInt => (uint)Value; + public virtual ulong AsULong => (ulong)Value; + + public virtual double AsDouble => (double)Value; + public virtual float AsFloat => (float)Value; + + public virtual Guid AsGuid => (Guid)Value; + public virtual DateTime AsDateTime => (DateTime)Value; + public virtual TimeSpan AsTimeSpan => (TimeSpan)Value; + + public virtual ODBDocument AsDocument => (ODBDocument)this; + + public virtual void Store(BinaryWriter storage) + { + byte[] storageBytes = ToStorage(); + + storage.Write(storageTypeCode); + storage.Write(storageBytes.Length); + storage.Write(storageBytes, 0, storageBytes.Length); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + public override bool Equals(object obj) + { + if (obj is ODBValue) + { + ODBValue you = obj as ODBValue; + return Value.Equals(you.Value); + } + return Value.Equals(obj); + } + + + + //static Dictionary valueFactories = new Dictionary(); + //public static void RegisterValueFactory(Type type, ODBValueFactory factory) + //{ + // valueFactories.Add(type, factory); + //} + + public static implicit operator ODBValue(ValueType v) + { + return ODBMapper.Default.MapValue(v); + } + public static implicit operator ODBValue(String v) + { + return ODBMapper.Default.MapValue(v); + } + + public static ODBValue FromNative(object v) + { + return ODBMapper.Default.MapValue(v); + } + + static Dictionary valueDeserializers = new Dictionary(); + public static void RegisterDeserializer(int storageTypeCode, ODBDeserialize deserialize) + { + valueDeserializers.Add(storageTypeCode, deserialize); + } + + public static ODBValue Deserialize(byte[] buffer, ref int offset) + { + int storageTypeCode = BitConverter.ToInt32(buffer, offset); + int storageLength = BitConverter.ToInt32(buffer, offset + 4); + ODBValue value = valueDeserializers[storageTypeCode](buffer, offset + 8, storageLength); + offset += 8 + storageLength; + return value; + } + + public static ODBValue Read(Stream stream) + { + int storageTypeCode = stream.ReadInteger(); + int storageLength = stream.ReadInteger(); + byte[] b = new byte[storageLength]; + stream.Read(b, 0, storageLength); + + return valueDeserializers[storageTypeCode](b, 0, storageLength); + } + + public override string ToString() + { + return String.Format("[ODBValue Value={0}]", Value); + } + } +} diff --git a/packages.config b/packages.config index 98af45e..24c86e9 100644 --- a/packages.config +++ b/packages.config @@ -1,5 +1,6 @@  + \ No newline at end of file diff --git a/serialize/ObjectReader.cs b/serialize/ObjectReader.cs index 113b416..92520fc 100644 --- a/serialize/ObjectReader.cs +++ b/serialize/ObjectReader.cs @@ -182,6 +182,7 @@ namespace ln.types.serialize public object ReadObject() { Type type = Read(); + object o = Activator.CreateInstance(type,true); referencedObjects.Add(o); diff --git a/threads/Pool.cs b/threads/Pool.cs index 92f6c3a..a793823 100644 --- a/threads/Pool.cs +++ b/threads/Pool.cs @@ -214,33 +214,38 @@ namespace ln.types.threads { supervisorThread = Thread.CurrentThread; - while (CurrentPoolSize > 0) + lock (supervisorThread) { - while (CurrentPoolSize != TargetPoolSize) - { - if (CurrentPoolSize - TargetPoolSize > 0) - { - lock (queuedJobs) - { - releaseThreads = CurrentPoolSize - TargetPoolSize; - } + while (CurrentPoolSize > 0) + { + Monitor.Wait(supervisorThread, 1000); + + while (CurrentPoolSize != TargetPoolSize) + { if (CurrentPoolSize - TargetPoolSize > 0) { lock (queuedJobs) { - Monitor.PulseAll(queuedJobs); + releaseThreads = CurrentPoolSize - TargetPoolSize; + } + + if (CurrentPoolSize - TargetPoolSize > 0) + { + lock (queuedJobs) + { + Monitor.PulseAll(queuedJobs); + } } } - } - else if (CurrentPoolSize - TargetPoolSize < 0) - { - for (int n = CurrentPoolSize; n < TargetPoolSize; n++) - new PoolThread(this); + else if (CurrentPoolSize - TargetPoolSize < 0) + { + for (int n = CurrentPoolSize; n < TargetPoolSize; n++) + new PoolThread(this); + } } } } - supervisorThread = null; }