dev_timestamp
Harald Wolff 2019-03-21 13:07:08 +00:00
parent 26276074cc
commit aa65fc9f4e
9 changed files with 319 additions and 183 deletions

18
Extensions.cs 100644
View File

@ -0,0 +1,18 @@
using System;
namespace ln.types
{
public static class Extensions
{
public static bool AreEqual<T>(this T[] me,T[] you)
{
if (me.Length != you.Length)
return false;
for (int n = 0; n < me.Length; n++)
if (!me[n].Equals(you[n]))
return false;
return true;
}
}
}

View File

@ -57,6 +57,9 @@
<Compile Include="odb\PreparedObject.cs" />
<Compile Include="odb\ODBObjectWriter.cs" />
<Compile Include="odb\ODBObjectReader.cs" />
<Compile Include="odb\ODBFileStorage.cs" />
<Compile Include="odb\Storage.cs" />
<Compile Include="Extensions.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="sync\" />

View File

@ -16,7 +16,7 @@ using Castle.Components.DictionaryAdapter;
using System.Linq;
namespace ln.types.odb
{
public class ODB<T> : ODB
public class ODB<T> : ODB where T: IPersistent
{
public T Root
{
@ -29,40 +29,35 @@ namespace ln.types.odb
}
}
public partial class ODB
public class ODB
{
public object RootObject { get; protected set; }
public String BasePath { get; set; }
private ProxyGenerator proxyGenerator = new ProxyGenerator();
private Dictionary<Guid, WeakReference<IPersistent>> persistentObjects = new Dictionary<Guid, WeakReference<IPersistent>>();
private Dictionary<Guid, IPersistent> persistentProxies = new Dictionary<Guid, IPersistent>();
public IPersistent RootObject { get; protected set; }
private Type RootType;
private Storage Storage { get; set; }
private Dictionary<Guid, PreparedObject> objectCache = new Dictionary<Guid, PreparedObject>();
public ODB(string basePath)
{
BasePath = Path.GetFullPath(basePath);
if (!Directory.Exists(BasePath))
{
Directory.CreateDirectory(BasePath);
}
Storage = new ODBFileStorage(basePath);
}
public ODB(String basePath, Type rootType)
: this(basePath)
{
RootType = rootType;
Initialize();
}
public ODB(String basePath,object rootObject)
public ODB(String basePath,IPersistent rootObject)
: this(basePath)
{
RootType = rootObject.GetType();
RootObject = rootObject;
Initialize();
}
@ -73,84 +68,59 @@ namespace ln.types.odb
string rootHint = Path.Combine(BasePath, "root.hint");
if (File.Exists(rootHint))
{
using (ObjectReader objectReader = new ObjectReader(rootHint))
{
Guid persistenceID = objectReader.Read<Guid>();
RootObject = GetPersistent(persistenceID);
}
string rootID = File.ReadAllText(rootHint);
Guid persistenceID = Guid.Parse(rootID);
RootObject = LoadPersistent(persistenceID);
}
else
{
RootObject = Activator.CreateInstance(RootType);
RootObject = (IPersistent)Activator.CreateInstance(RootType);
string rootID = RootObject.GetPersistenceID().ToString();
SavePersistent(RootObject);
File.WriteAllText(rootHint, rootID);
}
}
}
public bool Contains(Guid persistenceID)
{
string fn = Path.Combine(GetPersistentPath(persistenceID), String.Format("{0}.0",persistenceID));
return File.Exists(fn);
}
IPersistent LoadPersistent(Guid guid)
{
IPersistent o = GetCachedPersistent(guid);
if (o == null)
{
throw new NotImplementedException();
}
return o;
return Storage.Contains(persistenceID) || objectCache.ContainsKey(persistenceID);
}
public void SavePersistent(IPersistent o) => SavePersistent(o, true);
public void SavePersistent(IPersistent o,bool recurse)
{
SaveCollector saveCollector = new SaveCollector(this);
saveCollector.AddPersistent(o, recurse);
foreach (Guid persistenceID in saveCollector.GetCollectedPersistenceIDs())
lock (this)
{
SaveBytes(persistenceID, saveCollector.GetPersistentBytes(persistenceID));
SaveCollector saveCollector = new SaveCollector(this);
saveCollector.Add(o);
foreach (PreparedObject preparedObject in saveCollector.PreparedToSave)
{
Storage.Store(preparedObject);
objectCache[preparedObject.PersistenceID] = preparedObject;
}
}
}
/* File I/O */
private string GetPersistentPath(Guid persistenceID)
public T LoadPersistent<T>(Guid persistenceID) => (T)LoadPersistent(persistenceID);
public IPersistent LoadPersistent(Guid persistenceID)
{
byte[] idBytes = persistenceID.ToByteArray();
return Path.Combine(BasePath, BitConverter.ToString(idBytes, 0, 4));
}
if (!Contains(persistenceID))
throw new KeyNotFoundException();
private void SaveBytes(Guid persistenceID,byte[] bytes)
{
String p = GetPersistentPath(persistenceID);
String fnbase = Path.Combine(p, persistenceID.ToString());
if (!Directory.Exists(p))
Directory.CreateDirectory(p);
String fnn = String.Format("{0}.new", fnbase);
using (FileStream fs = new FileStream(fnn, FileMode.CreateNew))
PreparedObject preparedObject = GetCachedPersistent(persistenceID);
if (preparedObject == null)
{
fs.Write(bytes,0,bytes.Length);
fs.Close();
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);
}
for (int n=5;n>0;n--)
{
string fn1 = String.Format("{0}.{1}",fnbase,n-1);
string fn2 = String.Format("{0}.{1}", fnbase, n);
if (File.Exists(fn1))
File.Move(fn1, fn2);
}
string fn = String.Format("{0}.{1}", fnbase, 0);
File.Move(fnn, fn);
return preparedObject.Instance;
}
@ -159,119 +129,50 @@ namespace ln.types.odb
private IPersistent GetCachedPersistent(Guid persistenceID)
private PreparedObject GetCachedPersistent(Guid persistenceID)
{
lock (this)
{
if (persistentObjects.ContainsKey(persistenceID))
if (objectCache.ContainsKey(persistenceID))
{
WeakReference<IPersistent> weak = persistentObjects[persistenceID];
IPersistent persistent = null;
if (weak.TryGetTarget(out persistent))
{
return persistent;
}
else
{
persistentObjects.Remove(persistenceID);
}
return objectCache[persistenceID];
}
}
return null;
}
/**
* GetPersistent(..): Return Instance of an IPersistent (may be a proxy)
*
**/
public IPersistent GetPersistent(Guid persistenceID)
{
if (Guid.Empty.Equals(persistenceID))
return null;
if (persistentObjects.ContainsKey(persistenceID))
{
IPersistent o = GetCachedPersistent(persistenceID);
if (o == null)
{
o = LoadPersistent(persistenceID);
}
return o;
}
return null;
}
class SaveCollector
{
public ODB ODB { get; }
public Guid[] GetCollectedPersistenceIDs()
{
return persistentBytes.Keys.ToArray();
}
public IEnumerable<PreparedObject> PreparedToSave => preparedObjects.Values;
public byte[] GetPersistentBytes(Guid persistenceID)
{
return persistentBytes[persistenceID];
}
private Dictionary<Guid, byte[]> persistentBytes = new Dictionary<Guid, byte[]>();
private Dictionary<Guid, PreparedObject> preparedObjects = new Dictionary<Guid, PreparedObject>();
public SaveCollector(ODB odb)
{
ODB = odb;
}
public void AddPersistent(IPersistent persistent,bool recurse)
public void Add(IPersistent persistent)
{
MemoryStream objectStream = new MemoryStream();
ODBObjectWriter objectWriter = new ODBObjectWriter(ODB, objectStream);
objectWriter.Write(persistent);
PreparedObject preparedObject = new PreparedObject(ODB,persistent);
PreparedObject cachedObject = ODB.GetCachedPersistent(preparedObject.PersistenceID);
persistentBytes.Add(persistent.GetPersistenceID(), objectStream.ToArray());
if (recurse)
if (!preparedObject.InstanceEquals(cachedObject) && !preparedObjects.ContainsKey(preparedObject.PersistenceID))
{
foreach (IPersistent referencedPersistent in objectWriter.ReferencedPersistents)
preparedObjects.Add(preparedObject.PersistenceID, preparedObject);
foreach (IPersistent referencedPersistent in preparedObject.ReferencedPersistents)
{
AddPersistent(referencedPersistent,recurse);
Add(referencedPersistent);
}
}
else
{
foreach (IPersistent referencedPersistent in objectWriter.ReferencedPersistents)
{
if (!ODB.Contains(referencedPersistent.GetPersistenceID()))
{
AddPersistent(referencedPersistent, recurse);
}
}
}
}
}
class LazyInterceptor : IInterceptor
{
ODB odb;
WeakReference<IPersistent> target = null;
public LazyInterceptor(ODB odb, Guid persistentID)
{
this.odb = odb;
}
public void Intercept(IInvocation invocation)
{
object t = target;
throw new NotImplementedException();
}
}
}
}

View File

@ -0,0 +1,151 @@
// /**
// * File: ODB.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.IO;
using System.Collections.Generic;
using Castle.DynamicProxy;
using ln.types.serialize;
using Castle.Components.DictionaryAdapter;
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 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));
}
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());
if (!Directory.Exists(targetPath))
Directory.CreateDirectory(targetPath);
String fnn = String.Format("{0}.new", fnbase);
using (FileStream fs = new FileStream(fnn, FileMode.CreateNew))
{
ToStream(preparedObject, fs);
fs.Close();
}
for (int n = 5; n > 0; n--)
{
string fn1 = String.Format("{0}.{1}", fnbase, n - 1);
string fn2 = String.Format("{0}.{1}", fnbase, n);
if (File.Exists(fn1))
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);
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);
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.Close();
}
}
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;
string assemblyName = reader.ReadString();
string typeName = reader.ReadString();
Assembly assembly = Assembly.Load(assemblyName);
Type targetType = assembly.GetType(typeName);
preparedObject.ReConfigure(targetType,timestamp);
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);
}
reader.Close();
}
}
}
}

View File

@ -27,7 +27,7 @@ namespace ln.types.odb
if (re is Guid)
{
Guid persistenceID = (Guid)re;
IPersistent persistent = ODB.GetPersistent(persistenceID);
IPersistent persistent = ODB.LoadPersistent(persistenceID);
return persistent;
}

View File

@ -20,7 +20,7 @@ namespace ln.types.odb
get {
Guid persistentID = index[n];
if (!persistentInstances.ContainsKey(persistentID))
persistentInstances[persistentID] = (T)ODB.GetPersistent(persistentID);
persistentInstances[persistentID] = (T)ODB.LoadPersistent(persistentID);
return persistentInstances[persistentID];
}

View File

@ -12,6 +12,7 @@ using System.Collections.Generic;
using System.CodeDom;
using System.Reflection;
using System.IO;
using System.Linq;
namespace ln.types.odb
{
public class PreparedObject
@ -24,7 +25,13 @@ namespace ln.types.odb
public IPersistent Instance { get; set; }
public Guid PersistenceID { get; private set; }
List<Guid> referencedPersistents = new List<Guid>();
public string[] StoredFieldNames => fieldStore.Keys.ToArray();
public IEnumerable<Guid> ReferencedPersistentIDs => referencedPersistentIDs;
public IEnumerable<IPersistent> ReferencedPersistents => referencedPersistents;
List<Guid> referencedPersistentIDs = new List<Guid>();
HashSet<IPersistent> referencedPersistents = new HashSet<IPersistent>();
Dictionary<string, byte[]> fieldStore = new Dictionary<string, byte[]>();
public PreparedObject(ODB odb,Type preparedType,Guid persistenceID)
@ -46,49 +53,77 @@ namespace ln.types.odb
SyncFromInstance();
}
private IEnumerable<FieldInfo> 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()
{
referencedPersistents.Clear();
referencedPersistentIDs.Clear();
FieldInfo[] fields = GetAllFields(PreparedType).ToArray();
foreach (FieldInfo fieldInfo in PreparedType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
foreach (FieldInfo fieldInfo in fields)
{
object value = fieldInfo.GetValue(Instance);
if (value == null)
if (!fieldInfo.IsStatic)
{
fieldStore[fieldInfo.Name] = new byte[0];
}
else
{
MemoryStream objectStream = new MemoryStream();
ODBObjectWriter objectWriter = new ODBObjectWriter(objectStream);
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();
referencedPersistents.AddRange(objectWriter.ReferencedPersistentIDs);
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 PreparedType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
foreach (FieldInfo fieldInfo in GetAllFields(PreparedType))
{
byte[] bytes = fieldStore[fieldInfo.Name];
if (bytes.Length == 0)
if (!fieldInfo.IsStatic)
{
fieldInfo.SetValue(Instance, null);
} else
{
MemoryStream objectStream = new MemoryStream(fieldStore[fieldInfo.Name]);
ODBObjectReader objectReader = new ODBObjectReader(ODB, objectStream);
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 byte[] GetFieldBytes(string fieldName)
{
@ -108,11 +143,20 @@ namespace ln.types.odb
public bool InstanceEquals(PreparedObject other)
{
if (other == null)
return false;
if (Equals(other))
{
foreach (String fieldName in fieldStore.Keys)
{
if (!Object.Equals(fieldStore[fieldName], other.fieldStore[fieldName]))
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;

19
odb/Storage.cs 100644
View File

@ -0,0 +1,19 @@
using System;
namespace ln.types.odb
{
public abstract class Storage
{
public Storage()
{
}
public abstract bool Contains(Guid persistenceID);
public abstract bool Store(PreparedObject preparedObject);
public abstract bool Load(PreparedObject preparedObject);
}
}

View File

@ -173,7 +173,7 @@ namespace ln.types.serialize
private void WriteType(Type type)
{
WriteByte('T');
WriteValue(type.Assembly.FullName);
WriteValue(type.Assembly.GetName().Name);
if (type.IsGenericType)
WriteValue(type.GetGenericTypeDefinition().FullName);
else