WIP
parent
aa65fc9f4e
commit
035be47c45
43
CIDR.cs
43
CIDR.cs
|
@ -13,6 +13,7 @@ using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using LiteDB;
|
||||||
|
|
||||||
namespace ln.types
|
namespace ln.types
|
||||||
{
|
{
|
||||||
|
@ -35,7 +36,7 @@ namespace ln.types
|
||||||
return new CIDR(ip, w);
|
return new CIDR(ip, w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new CIDR(ip,32);
|
return new CIDR(ip, 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly uint _ip;
|
private readonly uint _ip;
|
||||||
|
@ -45,7 +46,7 @@ namespace ln.types
|
||||||
public CIDR Network => new CIDR(_ip & _netmask, _netmask);
|
public CIDR Network => new CIDR(_ip & _netmask, _netmask);
|
||||||
public CIDR Host => new CIDR(_ip, 0xffffffff);
|
public CIDR Host => new CIDR(_ip, 0xffffffff);
|
||||||
|
|
||||||
public int Size => (1 << (32 - MaskWidth));
|
public int Size => (1 << (32 - MaskWidth));
|
||||||
|
|
||||||
public byte[] IPBytes
|
public byte[] IPBytes
|
||||||
{
|
{
|
||||||
|
@ -115,12 +116,12 @@ namespace ln.types
|
||||||
|
|
||||||
for (w = 32; w > 0; w--)
|
for (w = 32; w > 0; w--)
|
||||||
{
|
{
|
||||||
if ((nm & (1 << (int)(32-w))) != 0)
|
if ((nm & (1 << (int)(32 - w))) != 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (uint n = w; n > 0; n--)
|
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");
|
throw new FormatException("Netmask with holes");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +137,7 @@ namespace ln.types
|
||||||
uint newmask = maskFromWidth((uint)(MaskWidth + bits));
|
uint newmask = maskFromWidth((uint)(MaskWidth + bits));
|
||||||
uint nip = _ip;
|
uint nip = _ip;
|
||||||
CIDR[] result = new CIDR[count];
|
CIDR[] result = new CIDR[count];
|
||||||
for (int n=0;n<count;n++)
|
for (int n = 0; n < count; n++)
|
||||||
{
|
{
|
||||||
nip += (uint)(1 << (32 - MaskWidth - bits));
|
nip += (uint)(1 << (32 - MaskWidth - bits));
|
||||||
result[n] = new CIDR(nip, newmask);
|
result[n] = new CIDR(nip, newmask);
|
||||||
|
@ -150,23 +151,32 @@ namespace ln.types
|
||||||
if (_netmask == 0xFFFFFFFF)
|
if (_netmask == 0xFFFFFFFF)
|
||||||
return String.Format("{0}", String.Join(".", IPBytes.Select((x) => x.ToString())));
|
return String.Format("{0}", String.Join(".", IPBytes.Select((x) => x.ToString())));
|
||||||
else
|
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)
|
public static implicit operator IPAddress(CIDR cidr)
|
||||||
{
|
{
|
||||||
return new IPAddress( cidr.IPBytes );
|
return new IPAddress(cidr.IPBytes);
|
||||||
}
|
}
|
||||||
public static implicit operator CIDR(IPAddress iPAddress)
|
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)
|
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<CIDR> candidates)
|
||||||
|
{
|
||||||
|
foreach (CIDR ip in candidates)
|
||||||
|
if (Contains(ip))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return (int)(_ip ^ _netmask);
|
return (int)(_ip ^ _netmask);
|
||||||
|
@ -225,13 +235,17 @@ namespace ln.types
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool ___init = ____init();
|
static CIDR()
|
||||||
static bool ____init()
|
|
||||||
{
|
{
|
||||||
|
BsonMapper.Global.RegisterType<CIDR>(
|
||||||
|
serialize: (ip) => ip.ToString(),
|
||||||
|
deserialize: (bson) => CIDR.Parse(bson.AsString)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
List<JsonConverter> converters = new List<JsonConverter>();
|
List<JsonConverter> converters = new List<JsonConverter>();
|
||||||
converters.Add(new CIDRJsonConverter());
|
converters.Add(new CIDRJsonConverter());
|
||||||
JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Converters = converters };
|
JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Converters = converters };
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -243,15 +257,16 @@ namespace ln.types
|
||||||
return (objectType == typeof(CIDR));
|
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());
|
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());
|
writer.WriteValue((value as CIDR).ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
namespace ln.types
|
namespace ln.types
|
||||||
{
|
{
|
||||||
public static class Extensions
|
public static class Extensions
|
||||||
|
@ -14,5 +15,17 @@ namespace ln.types
|
||||||
|
|
||||||
return true;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<chUri.Length) && (chUri[n] == '#'))
|
||||||
|
{
|
||||||
|
n++;
|
||||||
|
m = n;
|
||||||
|
while (
|
||||||
|
(m < chUri.Length) &&
|
||||||
|
(chUri[m] != '#')
|
||||||
|
)
|
||||||
|
{ m++; }
|
||||||
|
|
||||||
|
Fragment = new string(chUri, n, (m - n));
|
||||||
|
n = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n < chUri.Length)
|
||||||
|
throw new FormatException(String.Format("Malformed URI: {0}", uri));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseAuthority()
|
||||||
|
{
|
||||||
|
int indAt = Authority.IndexOf('@');
|
||||||
|
if (indAt != -1)
|
||||||
|
{
|
||||||
|
UserInfo = Authority.Substring(0, indAt).Split(':');
|
||||||
|
}
|
||||||
|
indAt++;
|
||||||
|
|
||||||
|
int indPort = Authority.IndexOf(':', indAt);
|
||||||
|
|
||||||
|
if (indPort != -1)
|
||||||
|
{
|
||||||
|
Host = Authority.Substring(indAt, indPort - indAt);
|
||||||
|
Port = Authority.Substring(indPort + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Host = Authority.Substring(indAt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
stringBuilder.Append(Scheme);
|
||||||
|
stringBuilder.Append(':');
|
||||||
|
|
||||||
|
if (!String.Empty.Equals(Authority))
|
||||||
|
{
|
||||||
|
stringBuilder.Append("//");
|
||||||
|
if (UserInfo.Length > 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,9 @@
|
||||||
<HintPath>..\packages\Castle.Core.4.3.1\lib\net45\Castle.Core.dll</HintPath>
|
<HintPath>..\packages\Castle.Core.4.3.1\lib\net45\Castle.Core.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System.Configuration" />
|
<Reference Include="System.Configuration" />
|
||||||
|
<Reference Include="LiteDB">
|
||||||
|
<HintPath>..\packages\LiteDB.4.1.4\lib\net40\LiteDB.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
@ -60,12 +63,30 @@
|
||||||
<Compile Include="odb\ODBFileStorage.cs" />
|
<Compile Include="odb\ODBFileStorage.cs" />
|
||||||
<Compile Include="odb\Storage.cs" />
|
<Compile Include="odb\Storage.cs" />
|
||||||
<Compile Include="Extensions.cs" />
|
<Compile Include="Extensions.cs" />
|
||||||
|
<Compile Include="GeoLocation.cs" />
|
||||||
|
<Compile Include="URI.cs" />
|
||||||
|
<Compile Include="odb\ODBCollection.cs" />
|
||||||
|
<Compile Include="odb\ODBDocument.cs" />
|
||||||
|
<Compile Include="odb\values\ODBGuid.cs" />
|
||||||
|
<Compile Include="odb\values\ODBNull.cs" />
|
||||||
|
<Compile Include="odb\values\ODBStringValue.cs" />
|
||||||
|
<Compile Include="odb\values\ODBValue.cs" />
|
||||||
|
<Compile Include="odb\values\ODBInteger.cs" />
|
||||||
|
<Compile Include="odb\values\ODBLong.cs" />
|
||||||
|
<Compile Include="odb\values\ODBDouble.cs" />
|
||||||
|
<Compile Include="odb\ODBMapper.cs" />
|
||||||
|
<Compile Include="odb\ClassMapping.cs" />
|
||||||
|
<Compile Include="odb\values\ODBList.cs" />
|
||||||
|
<Compile Include="odb\ListMapping.cs" />
|
||||||
|
<Compile Include="odb\ODBCollection<>.cs" />
|
||||||
|
<Compile Include="odb\DictionaryMapping.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="sync\" />
|
<Folder Include="sync\" />
|
||||||
<Folder Include="serialize\" />
|
<Folder Include="serialize\" />
|
||||||
<Folder Include="odb\" />
|
<Folder Include="odb\" />
|
||||||
<Folder Include="threads\" />
|
<Folder Include="threads\" />
|
||||||
|
<Folder Include="odb\values\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ln.logging\ln.logging.csproj">
|
<ProjectReference Include="..\ln.logging\ln.logging.csproj">
|
||||||
|
|
|
@ -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<FieldInfo> mappedFields = new List<FieldInfo>();
|
||||||
|
|
||||||
|
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<IgnoreFieldAttribute>() == null)
|
||||||
|
{
|
||||||
|
mappedFields.Add(fieldinfo);
|
||||||
|
if (fieldinfo.GetCustomAttribute<DocumentIDAttribute>() != 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
173
odb/ODB.cs
173
odb/ODB.cs
|
@ -14,165 +14,70 @@ using Castle.DynamicProxy;
|
||||||
using ln.types.serialize;
|
using ln.types.serialize;
|
||||||
using Castle.Components.DictionaryAdapter;
|
using Castle.Components.DictionaryAdapter;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using ln.logging;
|
||||||
|
using ln.types.odb.values;
|
||||||
namespace ln.types.odb
|
namespace ln.types.odb
|
||||||
{
|
{
|
||||||
public class ODB<T> : ODB where T: IPersistent
|
|
||||||
{
|
|
||||||
public T Root
|
|
||||||
{
|
|
||||||
get => (T)RootObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ODB(String basePath)
|
public class ODB : IDisposable
|
||||||
:base(basePath, typeof(T))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ODB
|
|
||||||
{
|
{
|
||||||
public String BasePath { get; set; }
|
public String BasePath { get; set; }
|
||||||
|
|
||||||
public IPersistent RootObject { get; protected set; }
|
Dictionary<string, ODBCollection> collections = new Dictionary<string, ODBCollection>();
|
||||||
|
|
||||||
|
|
||||||
private Type RootType;
|
|
||||||
private Storage Storage { get; set; }
|
|
||||||
|
|
||||||
private Dictionary<Guid, PreparedObject> objectCache = new Dictionary<Guid, PreparedObject>();
|
|
||||||
|
|
||||||
public ODB(string basePath)
|
public ODB(string basePath)
|
||||||
{
|
{
|
||||||
BasePath = Path.GetFullPath(basePath);
|
BasePath = Path.GetFullPath(basePath);
|
||||||
Storage = new ODBFileStorage(basePath);
|
if (!Directory.Exists(BasePath))
|
||||||
|
Directory.CreateDirectory(BasePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ODB(String basePath, Type rootType)
|
public ODBCollection GetCollection(string colName)
|
||||||
: this(basePath)
|
|
||||||
{
|
{
|
||||||
RootType = rootType;
|
if (!collections.ContainsKey(colName))
|
||||||
Initialize();
|
collections[colName] = new ODBCollection(this, colName);
|
||||||
|
|
||||||
|
return collections[colName];
|
||||||
}
|
}
|
||||||
public ODB(String basePath,IPersistent rootObject)
|
|
||||||
: this(basePath)
|
public ODBCollection<T> GetCollection<T>() where T:class
|
||||||
{
|
{
|
||||||
RootType = rootObject.GetType();
|
return new ODBCollection<T>(this);
|
||||||
RootObject = rootObject;
|
}
|
||||||
Initialize();
|
|
||||||
|
|
||||||
|
internal void DisposeCollection(ODBCollection collection)
|
||||||
|
{
|
||||||
|
ODBCollection check = collections[collection.CollectionName];
|
||||||
|
if (check == collection)
|
||||||
|
collections.Remove(collection.CollectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Initialize()
|
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);
|
static ODB()
|
||||||
public void SavePersistent(IPersistent o,bool recurse)
|
|
||||||
{
|
{
|
||||||
lock (this)
|
new ODBDocument();
|
||||||
{
|
new ODBNull();
|
||||||
SaveCollector saveCollector = new SaveCollector(this);
|
new ODBStringValue();
|
||||||
saveCollector.Add(o);
|
new ODBList();
|
||||||
|
new ODBInteger();
|
||||||
foreach (PreparedObject preparedObject in saveCollector.PreparedToSave)
|
new ODBUInteger();
|
||||||
{
|
new ODBLong();
|
||||||
Storage.Store(preparedObject);
|
new ODBULong();
|
||||||
objectCache[preparedObject.PersistenceID] = preparedObject;
|
new ODBDouble();
|
||||||
}
|
new ODBGuid();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public T LoadPersistent<T>(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<PreparedObject> PreparedToSave => preparedObjects.Values;
|
|
||||||
|
|
||||||
private Dictionary<Guid, PreparedObject> preparedObjects = new Dictionary<Guid, PreparedObject>();
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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<ODBDocument>, 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<ODBDocument> 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<ODBDocument> 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<ODBValue>
|
||||||
|
{
|
||||||
|
public Stream StorageStream { get; }
|
||||||
|
public DocumentIndexEntry Head { get; private set; }
|
||||||
|
|
||||||
|
private Dictionary<ODBValue, DocumentIndexEntry> idLookup = new Dictionary<ODBValue, DocumentIndexEntry>();
|
||||||
|
|
||||||
|
public DocumentIndex(Stream stream)
|
||||||
|
{
|
||||||
|
StorageStream = stream;
|
||||||
|
Head = new DocumentIndexEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<DocumentIndexEntry> 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<ODBValue> GetEnumerator()
|
||||||
|
{
|
||||||
|
return idLookup.Keys.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<T> : IEnumerable<T> where T:class
|
||||||
|
{
|
||||||
|
public ODB ODB { get; }
|
||||||
|
public Type ElementType { get; }
|
||||||
|
|
||||||
|
ODBCollection collection;
|
||||||
|
Dictionary<ODBValue, WeakReference<T>> objectCache = new Dictionary<ODBValue, WeakReference<T>>();
|
||||||
|
|
||||||
|
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<T>(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<T> 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<T>(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<T> GetEnumerator()
|
||||||
|
{
|
||||||
|
foreach (ODBValue documentID in collection.Index)
|
||||||
|
{
|
||||||
|
yield return Select(documentID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<ODBValue, ODBValue> properties = new Dictionary<ODBValue, ODBValue>();
|
||||||
|
|
||||||
|
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<nProps;n++)
|
||||||
|
{
|
||||||
|
ODBStringValue propName = ODBValue.Deserialize(bytes,ref offset) as ODBStringValue;
|
||||||
|
ODBValue propValue = ODBValue.Deserialize(bytes, ref offset);
|
||||||
|
properties.Add(propName, propValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset > 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<ODBValue> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,135 +17,143 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
namespace ln.types.odb
|
namespace ln.types.odb
|
||||||
{
|
{
|
||||||
public class ODBFileStorage : Storage
|
//public class ODBFileStorage : Storage
|
||||||
{
|
//{
|
||||||
public String BasePath { get; set; }
|
// public String BasePath { get; set; }
|
||||||
public int StorageVersion { get; } = 0x01;
|
// public int StorageVersion { get; } = 0x01;
|
||||||
|
|
||||||
public ODBFileStorage(string basePath)
|
// public ODBFileStorage(string basePath)
|
||||||
{
|
// {
|
||||||
BasePath = Path.GetFullPath(basePath);
|
// BasePath = Path.GetFullPath(basePath);
|
||||||
if (!Directory.Exists(BasePath))
|
// if (!Directory.Exists(BasePath))
|
||||||
{
|
// {
|
||||||
Directory.CreateDirectory(BasePath);
|
// Directory.CreateDirectory(BasePath);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
private string GetPersistentPath(Guid persistenceID)
|
// private string GetPersistentPath(Guid persistenceID)
|
||||||
{
|
// {
|
||||||
byte[] idBytes = persistenceID.ToByteArray();
|
// byte[] idBytes = persistenceID.ToByteArray();
|
||||||
return Path.Combine(BasePath, BitConverter.ToString(idBytes, 0, 4));
|
// return Path.Combine(BasePath, BitConverter.ToString(idBytes, 0, 4));
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Contains(Guid persistenceID)
|
// public override bool Contains(Guid persistenceID)
|
||||||
{
|
// {
|
||||||
string fn = Path.Combine(GetPersistentPath(persistenceID), String.Format("{0}.0", persistenceID));
|
// string fn = Path.Combine(GetPersistentPath(persistenceID), String.Format("{0}.0", persistenceID));
|
||||||
return File.Exists(fn);
|
// return File.Exists(fn);
|
||||||
}
|
// }
|
||||||
|
|
||||||
public override bool Store(PreparedObject preparedObject)
|
// public override bool Store(PreparedObject preparedObject)
|
||||||
{
|
// {
|
||||||
string targetPath = GetPersistentPath(preparedObject.PersistenceID);
|
// string targetPath = GetPersistentPath(preparedObject.PersistenceID);
|
||||||
String fnbase = Path.Combine(targetPath, preparedObject.PersistenceID.ToString());
|
// String fnbase = Path.Combine(targetPath, preparedObject.PersistenceID.ToString());
|
||||||
|
|
||||||
if (!Directory.Exists(targetPath))
|
// if (!Directory.Exists(targetPath))
|
||||||
Directory.CreateDirectory(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))
|
// if (File.Exists(fnn))
|
||||||
{
|
// File.Delete(fnn);
|
||||||
ToStream(preparedObject, fs);
|
|
||||||
fs.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int n = 5; n > 0; n--)
|
// using (FileStream fs = new FileStream(fnn, FileMode.CreateNew))
|
||||||
{
|
// {
|
||||||
string fn1 = String.Format("{0}.{1}", fnbase, n - 1);
|
// ToStream(preparedObject, fs);
|
||||||
string fn2 = String.Format("{0}.{1}", fnbase, n);
|
// fs.Close();
|
||||||
|
// }
|
||||||
|
|
||||||
if (File.Exists(fn1))
|
// for (int n = 5; n > 0; n--)
|
||||||
File.Move(fn1, fn2);
|
// {
|
||||||
}
|
// 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);
|
// if (File.Exists(fn1))
|
||||||
File.Move(fnn, fn);
|
// {
|
||||||
|
// 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)
|
// public override bool Load(PreparedObject preparedObject)
|
||||||
{
|
// {
|
||||||
string targetPath = GetPersistentPath(preparedObject.PersistenceID);
|
// string targetPath = GetPersistentPath(preparedObject.PersistenceID);
|
||||||
String fnbase = Path.Combine(targetPath, preparedObject.PersistenceID.ToString());
|
// String fnbase = Path.Combine(targetPath, preparedObject.PersistenceID.ToString());
|
||||||
string fn = String.Format("{0}.0", fnbase);
|
// string fn = String.Format("{0}.0", fnbase);
|
||||||
|
|
||||||
using (FileStream fs = new FileStream(fn, FileMode.Open))
|
// using (FileStream fs = new FileStream(fn, FileMode.Open))
|
||||||
{
|
// {
|
||||||
FromStream(fs, preparedObject);
|
// FromStream(fs, preparedObject);
|
||||||
fs.Close();
|
// fs.Close();
|
||||||
}
|
// }
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
private void ToStream(PreparedObject preparedObject,Stream stream)
|
// private void ToStream(PreparedObject preparedObject,Stream stream)
|
||||||
{
|
// {
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
// using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
{
|
// {
|
||||||
writer.Write(StorageVersion);
|
// writer.Write(StorageVersion);
|
||||||
writer.Write(DateTimeOffset.Now.ToUnixTimeMilliseconds());
|
// writer.Write(DateTimeOffset.Now.ToUnixTimeMilliseconds());
|
||||||
writer.Write(preparedObject.PreparedType.Assembly.FullName);
|
// writer.Write(preparedObject.PreparedType.Assembly.GetName().Name);
|
||||||
writer.Write(preparedObject.PreparedType.FullName);
|
// writer.Write(preparedObject.PreparedType.FullName);
|
||||||
|
|
||||||
string[] fieldNames = preparedObject.StoredFieldNames;
|
// string[] fieldNames = preparedObject.StoredFieldNames;
|
||||||
|
|
||||||
writer.Write(fieldNames.Length);
|
// writer.Write(fieldNames.Length);
|
||||||
foreach (String fieldName in fieldNames)
|
// foreach (String fieldName in fieldNames)
|
||||||
{
|
// {
|
||||||
byte[] fieldBytes = preparedObject.GetFieldBytes(fieldName);
|
// byte[] fieldBytes = preparedObject.GetFieldBytes(fieldName);
|
||||||
writer.Write(fieldName);
|
// writer.Write(fieldName);
|
||||||
writer.Write(fieldBytes.Length);
|
// writer.Write(fieldBytes.Length);
|
||||||
writer.Write(fieldBytes);
|
// writer.Write(fieldBytes);
|
||||||
}
|
// }
|
||||||
|
|
||||||
writer.Close();
|
// writer.Close();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
private void FromStream(Stream stream,PreparedObject preparedObject)
|
// private void FromStream(Stream stream,PreparedObject preparedObject)
|
||||||
{
|
// {
|
||||||
using (BinaryReader reader = new BinaryReader(stream))
|
// using (BinaryReader reader = new BinaryReader(stream))
|
||||||
{
|
// {
|
||||||
if (reader.ReadInt32() != StorageVersion)
|
// if (reader.ReadInt32() != StorageVersion)
|
||||||
throw new FormatException("Unsupported Storage Version");
|
// 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 assemblyName = reader.ReadString();
|
||||||
string typeName = reader.ReadString();
|
// string typeName = reader.ReadString();
|
||||||
|
|
||||||
Assembly assembly = Assembly.Load(assemblyName);
|
// Assembly assembly = Assembly.Load(assemblyName);
|
||||||
Type targetType = assembly.GetType(typeName);
|
// 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++)
|
// for (int n = 0; n < nStoredFields; n++)
|
||||||
{
|
// {
|
||||||
string fieldName = reader.ReadString();
|
// string fieldName = reader.ReadString();
|
||||||
int nBytes = reader.ReadInt32();
|
// int nBytes = reader.ReadInt32();
|
||||||
byte[] fieldBytes = reader.ReadBytes(nBytes);
|
// byte[] fieldBytes = reader.ReadBytes(nBytes);
|
||||||
preparedObject.SetFieldBytes(fieldName, fieldBytes);
|
// preparedObject.SetFieldBytes(fieldName, fieldBytes);
|
||||||
}
|
// }
|
||||||
|
|
||||||
reader.Close();
|
// reader.Close();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<T>(ODBMapper mapper, T value);
|
||||||
|
public delegate T ODBUnmap<T>(ODBMapper mapper, ODBValue oval);
|
||||||
|
|
||||||
|
public interface IODBMapping
|
||||||
|
{
|
||||||
|
ODBValue MapValue(ODBMapper mapper, object value);
|
||||||
|
object UnmapValue(ODBMapper mapper, ODBValue oval);
|
||||||
|
}
|
||||||
|
public interface IODBMapping<T>
|
||||||
|
{
|
||||||
|
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<T> : SimpleMapping
|
||||||
|
{
|
||||||
|
public SimpleMapping(ODBMap<T> map, ODBUnmap<T> unmap)
|
||||||
|
:base((mapper, value) => map(mapper,(T)value),(mapper, oval) => unmap(mapper,oval))
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ODBMapper
|
||||||
|
{
|
||||||
|
public static ODBMapper Default { get; } = new ODBMapper();
|
||||||
|
|
||||||
|
Dictionary<Type, IODBMapping> mappings = new Dictionary<Type, IODBMapping>();
|
||||||
|
|
||||||
|
private ODBMapper()
|
||||||
|
{
|
||||||
|
RegisterMapping<string>(
|
||||||
|
(mapper, value) => new ODBStringValue(value),
|
||||||
|
(mapper, oval) => oval.AsString
|
||||||
|
);
|
||||||
|
RegisterMapping<int>(
|
||||||
|
(mapper, value) => new ODBInteger(value),
|
||||||
|
(mapper, oval) => oval.AsInt
|
||||||
|
);
|
||||||
|
RegisterMapping<short>(
|
||||||
|
(mapper, value) => new ODBInteger(value),
|
||||||
|
(mapper, oval) => oval.AsShort
|
||||||
|
);
|
||||||
|
RegisterMapping<byte>(
|
||||||
|
(mapper, value) => new ODBInteger(value),
|
||||||
|
(mapper, oval) => oval.AsByte
|
||||||
|
);
|
||||||
|
|
||||||
|
RegisterMapping<uint>(
|
||||||
|
(mapper, value) => new ODBUInteger(value),
|
||||||
|
(mapper, oval) => oval.AsUInt
|
||||||
|
);
|
||||||
|
RegisterMapping<ushort>(
|
||||||
|
(mapper, value) => new ODBUInteger(value),
|
||||||
|
(mapper, oval) => oval.AsUShort
|
||||||
|
);
|
||||||
|
RegisterMapping<char>(
|
||||||
|
(mapper, value) => new ODBUInteger(value),
|
||||||
|
(mapper, oval) => oval.AsChar
|
||||||
|
);
|
||||||
|
|
||||||
|
RegisterMapping<double>(
|
||||||
|
(mapper, value) => new ODBDouble(value),
|
||||||
|
(mapper, oval) => oval.AsDouble
|
||||||
|
);
|
||||||
|
RegisterMapping<float>(
|
||||||
|
(mapper, value) => new ODBDouble(value),
|
||||||
|
(mapper, oval) => oval.AsFloat
|
||||||
|
);
|
||||||
|
|
||||||
|
RegisterMapping<DateTime>(
|
||||||
|
(mapper, value) => new ODBLong(DateTime.MinValue.Equals(value) ? 0 : new DateTimeOffset(value).ToUnixTimeMilliseconds() ),
|
||||||
|
(mapper, oval) => DateTimeOffset.FromUnixTimeMilliseconds(oval.AsLong).DateTime
|
||||||
|
);
|
||||||
|
RegisterMapping<TimeSpan>(
|
||||||
|
(mapper, value) => new ODBDouble(value.TotalMilliseconds),
|
||||||
|
(mapper, oval) => TimeSpan.FromMilliseconds(oval.AsDouble)
|
||||||
|
);
|
||||||
|
|
||||||
|
RegisterMapping<Guid>(
|
||||||
|
(mapper, value) => new ODBGuid(value),
|
||||||
|
(mapper, oval) => oval.AsGuid
|
||||||
|
);
|
||||||
|
|
||||||
|
RegisterMapping<long>(
|
||||||
|
(mapper, value) => new ODBLong(value),
|
||||||
|
(mapper, oval) => oval.AsLong
|
||||||
|
);
|
||||||
|
RegisterMapping<ulong>(
|
||||||
|
(mapper, value) => new ODBULong(value),
|
||||||
|
(mapper, oval) => oval.AsULong
|
||||||
|
);
|
||||||
|
|
||||||
|
RegisterMapping<bool>(
|
||||||
|
(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<T>(ODBMap<T> map, ODBUnmap<T> unmap)
|
||||||
|
{
|
||||||
|
mappings[typeof(T)] = new SimpleMapping<T>(map, unmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IODBMapping GetMapping<T>() => 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<T>(ODBValue value)
|
||||||
|
{
|
||||||
|
return (T)UnmapValue(typeof(T), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,26 +12,26 @@ using System.IO;
|
||||||
using ln.types.serialize;
|
using ln.types.serialize;
|
||||||
namespace ln.types.odb
|
namespace ln.types.odb
|
||||||
{
|
{
|
||||||
public class ODBObjectReader : ObjectReader
|
//public class ODBObjectReader : ObjectReader
|
||||||
{
|
//{
|
||||||
public ODB ODB { get; }
|
// public ODB ODB { get; }
|
||||||
|
|
||||||
public ODBObjectReader(ODB odb, Stream stream) :
|
// public ODBObjectReader(ODB odb, Stream stream) :
|
||||||
base(stream)
|
// base(stream)
|
||||||
{
|
// {
|
||||||
ODB = odb;
|
// ODB = odb;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public override object QueryReferencedObject(object re)
|
// public override object QueryReferencedObject(object re)
|
||||||
{
|
// {
|
||||||
if (re is Guid)
|
// if (re is Guid)
|
||||||
{
|
// {
|
||||||
Guid persistenceID = (Guid)re;
|
// Guid persistenceID = (Guid)re;
|
||||||
IPersistent persistent = ODB.LoadPersistent(persistenceID);
|
// IPersistent persistent = ODB.LoadPersistent(persistenceID);
|
||||||
return persistent;
|
// return persistent;
|
||||||
}
|
// }
|
||||||
|
|
||||||
return base.QueryReferencedObject(re);
|
// return base.QueryReferencedObject(re);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,30 +14,30 @@ using ln.types.serialize;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
namespace ln.types.odb
|
namespace ln.types.odb
|
||||||
{
|
{
|
||||||
class ODBObjectWriter : ObjectWriter
|
//class ODBObjectWriter : ObjectWriter
|
||||||
{
|
//{
|
||||||
public IPersistent[] ReferencedPersistents => referencedPersistents.Values.ToArray();
|
// public IPersistent[] ReferencedPersistents => referencedPersistents.Values.ToArray();
|
||||||
public Guid[] ReferencedPersistentIDs => referencedPersistents.Keys.ToArray();
|
// public Guid[] ReferencedPersistentIDs => referencedPersistents.Keys.ToArray();
|
||||||
|
|
||||||
private Dictionary<Guid, IPersistent> referencedPersistents = new Dictionary<Guid, IPersistent>();
|
// private Dictionary<Guid, IPersistent> referencedPersistents = new Dictionary<Guid, IPersistent>();
|
||||||
|
|
||||||
public ODBObjectWriter(Stream stream)
|
// public ODBObjectWriter(Stream stream)
|
||||||
: base(stream)
|
// : base(stream)
|
||||||
{}
|
// {}
|
||||||
|
|
||||||
public override object QueryReference(object o)
|
// public override object QueryReference(object o)
|
||||||
{
|
// {
|
||||||
if (o is IPersistent)
|
// if (o is IPersistent)
|
||||||
{
|
// {
|
||||||
IPersistent persistent = o as IPersistent;
|
// IPersistent persistent = o as IPersistent;
|
||||||
Guid persistenceID = persistent.GetPersistenceID();
|
// Guid persistenceID = persistent.GetPersistenceID();
|
||||||
|
|
||||||
if (!referencedPersistents.ContainsKey(persistenceID))
|
// if (!referencedPersistents.ContainsKey(persistenceID))
|
||||||
referencedPersistents.Add(persistenceID, persistent);
|
// referencedPersistents.Add(persistenceID, persistent);
|
||||||
|
|
||||||
return persistenceID;
|
// return persistenceID;
|
||||||
}
|
// }
|
||||||
return base.QueryReference(o);
|
// return base.QueryReference(o);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,12 @@
|
||||||
// *
|
// *
|
||||||
// **/
|
// **/
|
||||||
using System;
|
using System;
|
||||||
|
using LiteDB;
|
||||||
namespace ln.types.odb
|
namespace ln.types.odb
|
||||||
{
|
{
|
||||||
public class Persistent : IPersistent
|
public class Persistent : IPersistent
|
||||||
{
|
{
|
||||||
|
[BsonId]
|
||||||
public Guid PersistenceID { get; private set; }
|
public Guid PersistenceID { get; private set; }
|
||||||
|
|
||||||
public Persistent()
|
public Persistent()
|
||||||
|
|
|
@ -3,163 +3,163 @@ using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
namespace ln.types.odb
|
namespace ln.types.odb
|
||||||
{
|
{
|
||||||
public class PersistentList<T> : IList<T> where T: IPersistent
|
//public class PersistentList<T> : IList<T> where T: IPersistent
|
||||||
{
|
//{
|
||||||
private Dictionary<Guid, T> persistentInstances = new Dictionary<Guid, T>();
|
// private Dictionary<Guid, T> persistentInstances = new Dictionary<Guid, T>();
|
||||||
private List<Guid> index = new List<Guid>();
|
// private List<Guid> index = new List<Guid>();
|
||||||
|
|
||||||
public ODB ODB { get; }
|
// public ODB ODB { get; }
|
||||||
|
|
||||||
public PersistentList(ODB odb)
|
// public PersistentList(ODB odb)
|
||||||
{
|
// {
|
||||||
ODB = odb;
|
// ODB = odb;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public T this[int n]
|
// public T this[int n]
|
||||||
{
|
// {
|
||||||
get {
|
// get {
|
||||||
Guid persistentID = index[n];
|
// Guid persistentID = index[n];
|
||||||
if (!persistentInstances.ContainsKey(persistentID))
|
// if (!persistentInstances.ContainsKey(persistentID))
|
||||||
persistentInstances[persistentID] = (T)ODB.LoadPersistent(persistentID);
|
// persistentInstances[persistentID] = (T)ODB.LoadPersistent(persistentID);
|
||||||
|
|
||||||
return persistentInstances[persistentID];
|
// return persistentInstances[persistentID];
|
||||||
}
|
// }
|
||||||
set {
|
// set {
|
||||||
if (value == null)
|
// if (value == null)
|
||||||
index[n] = Guid.Empty;
|
// index[n] = Guid.Empty;
|
||||||
|
|
||||||
Guid persistenceID = value.GetPersistenceID();
|
// Guid persistenceID = value.GetPersistenceID();
|
||||||
persistentInstances[persistenceID] = value;
|
// persistentInstances[persistenceID] = value;
|
||||||
index[n] = persistenceID;
|
// index[n] = persistenceID;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
public int Count => index.Count;
|
// public int Count => index.Count;
|
||||||
public bool IsReadOnly => false;
|
// public bool IsReadOnly => false;
|
||||||
|
|
||||||
public void Add(T item)
|
// public void Add(T item)
|
||||||
{
|
// {
|
||||||
if (item == null)
|
// if (item == null)
|
||||||
{
|
// {
|
||||||
index.Add(Guid.Empty);
|
// index.Add(Guid.Empty);
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
Guid persistenceID = item.GetPersistenceID();
|
// Guid persistenceID = item.GetPersistenceID();
|
||||||
persistentInstances[persistenceID] = item;
|
// persistentInstances[persistenceID] = item;
|
||||||
index.Add(persistenceID);
|
// index.Add(persistenceID);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
public void Clear()
|
// public void Clear()
|
||||||
{
|
// {
|
||||||
persistentInstances.Clear();
|
// persistentInstances.Clear();
|
||||||
index.Clear();
|
// index.Clear();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public bool Contains(T item)
|
// public bool Contains(T item)
|
||||||
{
|
// {
|
||||||
Guid persistenceID = item.GetPersistenceID();
|
// Guid persistenceID = item.GetPersistenceID();
|
||||||
if (index.Contains(persistenceID))
|
// if (index.Contains(persistenceID))
|
||||||
return true;
|
// return true;
|
||||||
|
|
||||||
foreach (T mine in this)
|
// foreach (T mine in this)
|
||||||
{
|
// {
|
||||||
if (mine.Equals(item))
|
// if (mine.Equals(item))
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public void CopyTo(T[] array, int arrayIndex)
|
// public void CopyTo(T[] array, int arrayIndex)
|
||||||
{
|
// {
|
||||||
foreach (T item in this)
|
// foreach (T item in this)
|
||||||
{
|
// {
|
||||||
array[arrayIndex] = this[arrayIndex];
|
// array[arrayIndex] = this[arrayIndex];
|
||||||
arrayIndex++;
|
// arrayIndex++;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
public IEnumerator<T> GetEnumerator()
|
// public IEnumerator<T> GetEnumerator()
|
||||||
{
|
// {
|
||||||
return new PersistentListEnumerator(this);
|
// return new PersistentListEnumerator(this);
|
||||||
}
|
// }
|
||||||
|
|
||||||
public int IndexOf(T item)
|
// public int IndexOf(T item)
|
||||||
{
|
// {
|
||||||
Guid persistenceID = item == null ? Guid.Empty : item.GetPersistenceID();
|
// Guid persistenceID = item == null ? Guid.Empty : item.GetPersistenceID();
|
||||||
|
|
||||||
int i = index.IndexOf(persistenceID);
|
// int i = index.IndexOf(persistenceID);
|
||||||
if (i < 0)
|
// if (i < 0)
|
||||||
{
|
// {
|
||||||
for (int n=0;n<Count;n++)
|
// for (int n=0;n<Count;n++)
|
||||||
{
|
// {
|
||||||
if (this[n].Equals(item))
|
// if (this[n].Equals(item))
|
||||||
return n;
|
// return n;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return -1;
|
// return -1;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public void Insert(int _index, T item)
|
// public void Insert(int _index, T item)
|
||||||
{
|
// {
|
||||||
Guid persistenceID = item == null ? Guid.Empty : item.GetPersistenceID();
|
// Guid persistenceID = item == null ? Guid.Empty : item.GetPersistenceID();
|
||||||
index.Insert(_index, persistenceID);
|
// index.Insert(_index, persistenceID);
|
||||||
persistentInstances[persistenceID] = item;
|
// persistentInstances[persistenceID] = item;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public bool Remove(T item)
|
// public bool Remove(T item)
|
||||||
{
|
// {
|
||||||
int ind = IndexOf(item);
|
// int ind = IndexOf(item);
|
||||||
if (ind >= 0)
|
// if (ind >= 0)
|
||||||
RemoveAt(ind);
|
// RemoveAt(ind);
|
||||||
|
|
||||||
return ind >= 0;
|
// return ind >= 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public void RemoveAt(int _index)
|
// public void RemoveAt(int _index)
|
||||||
{
|
// {
|
||||||
Guid persistenceID = index[_index];
|
// Guid persistenceID = index[_index];
|
||||||
if (persistentInstances.ContainsKey(persistenceID))
|
// if (persistentInstances.ContainsKey(persistenceID))
|
||||||
persistentInstances.Remove(persistenceID);
|
// persistentInstances.Remove(persistenceID);
|
||||||
index.RemoveAt(_index);
|
// index.RemoveAt(_index);
|
||||||
}
|
// }
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
// IEnumerator IEnumerable.GetEnumerator()
|
||||||
{
|
// {
|
||||||
return new PersistentListEnumerator(this);
|
// return new PersistentListEnumerator(this);
|
||||||
}
|
// }
|
||||||
|
|
||||||
class PersistentListEnumerator : IEnumerator<T>
|
// class PersistentListEnumerator : IEnumerator<T>
|
||||||
{
|
// {
|
||||||
int currentIndex = -1;
|
// int currentIndex = -1;
|
||||||
PersistentList<T> persistentList;
|
// PersistentList<T> persistentList;
|
||||||
|
|
||||||
|
|
||||||
public PersistentListEnumerator(PersistentList<T> persistentList)
|
// public PersistentListEnumerator(PersistentList<T> persistentList)
|
||||||
{
|
// {
|
||||||
this.persistentList = persistentList;
|
// this.persistentList = persistentList;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public T Current => persistentList[currentIndex];
|
// public T Current => persistentList[currentIndex];
|
||||||
object IEnumerator.Current => persistentList[currentIndex];
|
// object IEnumerator.Current => persistentList[currentIndex];
|
||||||
|
|
||||||
public void Dispose()
|
// public void Dispose()
|
||||||
{
|
// {
|
||||||
persistentList = null;
|
// persistentList = null;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public bool MoveNext()
|
// public bool MoveNext()
|
||||||
{
|
// {
|
||||||
currentIndex++;
|
// currentIndex++;
|
||||||
return (currentIndex < persistentList.Count);
|
// return (currentIndex < persistentList.Count);
|
||||||
}
|
// }
|
||||||
|
|
||||||
public void Reset()
|
// public void Reset()
|
||||||
{
|
// {
|
||||||
currentIndex = -1;
|
// currentIndex = -1;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,171 +13,169 @@ using System.CodeDom;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using ln.types.sync;
|
||||||
namespace ln.types.odb
|
namespace ln.types.odb
|
||||||
{
|
{
|
||||||
public class PreparedObject
|
//public class PreparedObject
|
||||||
{
|
//{
|
||||||
public ODB ODB { get; private set; }
|
// public ODB ODB { get; private set; }
|
||||||
|
|
||||||
public DateTime Timestamp { get; private set; }
|
// public DateTime Timestamp { get; private set; }
|
||||||
public Type PreparedType { get; private set; }
|
// public Type PreparedType { get; private set; }
|
||||||
|
|
||||||
public IPersistent Instance { get; set; }
|
// public IPersistent Instance { get; set; }
|
||||||
public Guid PersistenceID { get; private set; }
|
// public Guid PersistenceID { get; private set; }
|
||||||
|
|
||||||
public string[] StoredFieldNames => fieldStore.Keys.ToArray();
|
// public string[] StoredFieldNames => fieldStore.Keys.ToArray();
|
||||||
|
|
||||||
public IEnumerable<Guid> ReferencedPersistentIDs => referencedPersistentIDs;
|
// public IEnumerable<Guid> ReferencedPersistentIDs => referencedPersistentIDs;
|
||||||
public IEnumerable<IPersistent> ReferencedPersistents => referencedPersistents;
|
// public IEnumerable<IPersistent> ReferencedPersistents => referencedPersistents;
|
||||||
|
|
||||||
List<Guid> referencedPersistentIDs = new List<Guid>();
|
// List<Guid> referencedPersistentIDs = new List<Guid>();
|
||||||
HashSet<IPersistent> referencedPersistents = new HashSet<IPersistent>();
|
// HashSet<IPersistent> referencedPersistents = new HashSet<IPersistent>();
|
||||||
Dictionary<string, byte[]> fieldStore = new Dictionary<string, byte[]>();
|
// Dictionary<string, byte[]> fieldStore = new Dictionary<string, byte[]>();
|
||||||
|
|
||||||
public PreparedObject(ODB odb,Type preparedType,Guid persistenceID)
|
// public PreparedObject(ODB odb,Type preparedType,Guid persistenceID)
|
||||||
{
|
// {
|
||||||
ODB = odb;
|
// ODB = odb;
|
||||||
Timestamp = DateTime.Now;
|
// Timestamp = DateTime.Now;
|
||||||
PreparedType = preparedType;
|
// PreparedType = preparedType;
|
||||||
PersistenceID = persistenceID;
|
// PersistenceID = persistenceID;
|
||||||
Instance = null;
|
// Instance = null;
|
||||||
}
|
// }
|
||||||
public PreparedObject(ODB odb, IPersistent o)
|
// public PreparedObject(ODB odb, IPersistent o)
|
||||||
{
|
// {
|
||||||
ODB = odb;
|
// ODB = odb;
|
||||||
Timestamp = DateTime.Now;
|
// Timestamp = DateTime.Now;
|
||||||
PreparedType = o.GetType();
|
// PreparedType = o.GetType();
|
||||||
PersistenceID = o.GetPersistenceID();
|
// PersistenceID = o.GetPersistenceID();
|
||||||
Instance = o;
|
// Instance = o;
|
||||||
|
|
||||||
SyncFromInstance();
|
// SyncFromInstance();
|
||||||
}
|
// }
|
||||||
|
|
||||||
private IEnumerable<FieldInfo> GetAllFields(Type type)
|
// private IEnumerable<FieldInfo> GetAllFields(Type type)
|
||||||
{
|
// {
|
||||||
if (type == typeof(object))
|
// if (type == typeof(object))
|
||||||
{
|
// {
|
||||||
return new FieldInfo[0];
|
// return new FieldInfo[0];
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
return GetAllFields(type.BaseType).Concat(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly));
|
// return GetAllFields(type.BaseType).Concat(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
private void SyncFromInstance()
|
// public void SyncFromInstance()
|
||||||
{
|
// {
|
||||||
referencedPersistentIDs.Clear();
|
// referencedPersistentIDs.Clear();
|
||||||
FieldInfo[] fields = GetAllFields(PreparedType).ToArray();
|
// FieldInfo[] fields = GetAllFields(PreparedType).ToArray();
|
||||||
|
|
||||||
foreach (FieldInfo fieldInfo in fields)
|
// foreach (FieldInfo fieldInfo in fields)
|
||||||
{
|
// {
|
||||||
if (!fieldInfo.IsStatic)
|
// if (!fieldInfo.IsStatic && (fieldInfo.GetCustomAttribute<Unsynced>() == null))
|
||||||
{
|
// {
|
||||||
object value = fieldInfo.GetValue(Instance);
|
// object value = fieldInfo.GetValue(Instance);
|
||||||
if (value == null)
|
// if (value == null)
|
||||||
{
|
// {
|
||||||
fieldStore[fieldInfo.Name] = new byte[0];
|
// fieldStore[fieldInfo.Name] = new byte[0];
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
MemoryStream objectStream = new MemoryStream();
|
// MemoryStream objectStream = new MemoryStream();
|
||||||
ODBObjectWriter objectWriter = new ODBObjectWriter(objectStream);
|
// ODBObjectWriter objectWriter = new ODBObjectWriter(objectStream);
|
||||||
|
|
||||||
objectWriter.Write(value);
|
// objectWriter.Write(value);
|
||||||
|
|
||||||
fieldStore[fieldInfo.Name] = objectStream.ToArray();
|
// fieldStore[fieldInfo.Name] = objectStream.ToArray();
|
||||||
referencedPersistentIDs.AddRange(objectWriter.ReferencedPersistentIDs);
|
// referencedPersistentIDs.AddRange(objectWriter.ReferencedPersistentIDs);
|
||||||
foreach (IPersistent persistent in objectWriter.ReferencedPersistents)
|
// foreach (IPersistent persistent in objectWriter.ReferencedPersistents)
|
||||||
referencedPersistents.Add(persistent);
|
// referencedPersistents.Add(persistent);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
private void SyncToInstance()
|
// public void SyncToInstance()
|
||||||
{
|
// {
|
||||||
foreach (FieldInfo fieldInfo in GetAllFields(PreparedType))
|
// foreach (FieldInfo fieldInfo in GetAllFields(PreparedType))
|
||||||
{
|
// {
|
||||||
if (!fieldInfo.IsStatic)
|
// if (!fieldInfo.IsStatic && (fieldInfo.GetCustomAttribute<Unsynced>() == null))
|
||||||
{
|
// {
|
||||||
byte[] bytes = fieldStore[fieldInfo.Name];
|
// byte[] bytes = fieldStore[fieldInfo.Name];
|
||||||
if (bytes.Length == 0)
|
// if (bytes.Length == 0)
|
||||||
{
|
// {
|
||||||
fieldInfo.SetValue(Instance, null);
|
// fieldInfo.SetValue(Instance, null);
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
MemoryStream objectStream = new MemoryStream(fieldStore[fieldInfo.Name]);
|
// MemoryStream objectStream = new MemoryStream(fieldStore[fieldInfo.Name]);
|
||||||
ODBObjectReader objectReader = new ODBObjectReader(ODB, objectStream);
|
// ODBObjectReader objectReader = new ODBObjectReader(ODB, objectStream);
|
||||||
|
|
||||||
object value = objectReader.Read();
|
// object value = objectReader.Read();
|
||||||
fieldInfo.SetValue(Instance, value);
|
// fieldInfo.SetValue(Instance, value);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
public void ReConfigure(Type targetType,DateTime timestamp)
|
// public void ReConfigure(Type targetType,DateTime timestamp)
|
||||||
{
|
// {
|
||||||
Timestamp = timestamp;
|
// Timestamp = timestamp;
|
||||||
PreparedType = targetType;
|
// PreparedType = targetType;
|
||||||
fieldStore.Clear();
|
// fieldStore.Clear();
|
||||||
Instance = null;
|
// Instance = null;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public byte[] GetFieldBytes(string fieldName)
|
// public byte[] GetFieldBytes(string fieldName)
|
||||||
{
|
// {
|
||||||
return fieldStore[fieldName];
|
// return fieldStore[fieldName];
|
||||||
}
|
// }
|
||||||
public void SetFieldBytes(string fieldName,byte[] bytes)
|
// public void SetFieldBytes(string fieldName,byte[] bytes)
|
||||||
{
|
// {
|
||||||
fieldStore[fieldName] = bytes;
|
// fieldStore[fieldName] = bytes;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public IPersistent CreateInstance()
|
// public IPersistent CreateInstance()
|
||||||
{
|
// {
|
||||||
Instance = (IPersistent)Activator.CreateInstance(PreparedType,true);
|
// Instance = (IPersistent)Activator.CreateInstance(PreparedType,true);
|
||||||
SyncToInstance();
|
// SyncToInstance();
|
||||||
return Instance;
|
// return Instance;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public bool InstanceEquals(PreparedObject other)
|
// public bool InstanceEquals(PreparedObject other)
|
||||||
{
|
// {
|
||||||
if (other == null)
|
// if (other == null)
|
||||||
return false;
|
// return false;
|
||||||
|
|
||||||
if (Equals(other))
|
// if (Equals(other))
|
||||||
{
|
// {
|
||||||
foreach (String fieldName in fieldStore.Keys)
|
// foreach (String fieldName in fieldStore.Keys)
|
||||||
{
|
// {
|
||||||
byte[] me = fieldStore[fieldName];
|
// byte[] me = fieldStore[fieldName];
|
||||||
byte[] you = other.fieldStore[fieldName];
|
// byte[] you = other.fieldStore[fieldName];
|
||||||
|
|
||||||
File.WriteAllBytes("me.bin", me);
|
// if (!me.AreEqual(you))
|
||||||
File.WriteAllBytes("you.bin", you);
|
// return false;
|
||||||
|
// }
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
if (!me.AreEqual(you))
|
// public override int GetHashCode()
|
||||||
return false;
|
// {
|
||||||
}
|
// return PersistenceID.GetHashCode() ^ PreparedType.GetHashCode();
|
||||||
return true;
|
// }
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
// public override bool Equals(object obj)
|
||||||
{
|
// {
|
||||||
return PersistenceID.GetHashCode() ^ PreparedType.GetHashCode();
|
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
using System;
|
using System;
|
||||||
namespace ln.types.odb
|
namespace ln.types.odb
|
||||||
{
|
{
|
||||||
public abstract class Storage
|
//public abstract class Storage
|
||||||
{
|
//{
|
||||||
public Storage()
|
// public Storage()
|
||||||
{
|
// {
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
public abstract bool Contains(Guid persistenceID);
|
// public abstract bool Contains(Guid persistenceID);
|
||||||
|
|
||||||
public abstract bool Store(PreparedObject preparedObject);
|
// public abstract bool Store(PreparedObject preparedObject);
|
||||||
public abstract bool Load(PreparedObject preparedObject);
|
// public abstract bool Load(PreparedObject preparedObject);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<ODBValue> items = new List<ODBValue>();
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Type, ODBValueFactory> valueFactories = new Dictionary<Type, ODBValueFactory>();
|
||||||
|
//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<int, ODBDeserialize> valueDeserializers = new Dictionary<int, ODBDeserialize>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Castle.Core" version="4.3.1" targetFramework="net47" />
|
<package id="Castle.Core" version="4.3.1" targetFramework="net47" />
|
||||||
|
<package id="LiteDB" version="4.1.4" targetFramework="net47" />
|
||||||
<package id="Newtonsoft.Json" version="12.0.1" targetFramework="net47" />
|
<package id="Newtonsoft.Json" version="12.0.1" targetFramework="net47" />
|
||||||
</packages>
|
</packages>
|
|
@ -182,6 +182,7 @@ namespace ln.types.serialize
|
||||||
public object ReadObject()
|
public object ReadObject()
|
||||||
{
|
{
|
||||||
Type type = Read<Type>();
|
Type type = Read<Type>();
|
||||||
|
|
||||||
object o = Activator.CreateInstance(type,true);
|
object o = Activator.CreateInstance(type,true);
|
||||||
|
|
||||||
referencedObjects.Add(o);
|
referencedObjects.Add(o);
|
||||||
|
|
|
@ -214,33 +214,38 @@ namespace ln.types.threads
|
||||||
{
|
{
|
||||||
supervisorThread = Thread.CurrentThread;
|
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)
|
if (CurrentPoolSize - TargetPoolSize > 0)
|
||||||
{
|
{
|
||||||
lock (queuedJobs)
|
lock (queuedJobs)
|
||||||
{
|
{
|
||||||
Monitor.PulseAll(queuedJobs);
|
releaseThreads = CurrentPoolSize - TargetPoolSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CurrentPoolSize - TargetPoolSize > 0)
|
||||||
|
{
|
||||||
|
lock (queuedJobs)
|
||||||
|
{
|
||||||
|
Monitor.PulseAll(queuedJobs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (CurrentPoolSize - TargetPoolSize < 0)
|
||||||
else if (CurrentPoolSize - TargetPoolSize < 0)
|
{
|
||||||
{
|
for (int n = CurrentPoolSize; n < TargetPoolSize; n++)
|
||||||
for (int n = CurrentPoolSize; n < TargetPoolSize; n++)
|
new PoolThread(this);
|
||||||
new PoolThread(this);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
supervisorThread = null;
|
supervisorThread = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue