diff --git a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
index db28f8c469..dc4a06d2a7 100644
--- a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
+++ b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
@@ -10,7 +10,6 @@
Properties\SharedAssemblyInfo.cs
-
diff --git a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj
index a9c6b9e9a0..871c9cd995 100644
--- a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj
+++ b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj
@@ -1,10 +1,9 @@
- netstandard1.3
+ netstandard2.0
AVALONIA_REMOTE_PROTOCOL;$(DefineConstants)
-
\ No newline at end of file
diff --git a/src/Avalonia.Remote.Protocol/BsonStreamTransport.cs b/src/Avalonia.Remote.Protocol/BsonStreamTransport.cs
index 82765f98f6..604703cac8 100644
--- a/src/Avalonia.Remote.Protocol/BsonStreamTransport.cs
+++ b/src/Avalonia.Remote.Protocol/BsonStreamTransport.cs
@@ -2,9 +2,8 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
-using Newtonsoft.Json;
using System.Threading.Tasks;
-using Newtonsoft.Json.Bson;
+using Metsys.Bson;
namespace Avalonia.Remote.Protocol
{
@@ -20,8 +19,7 @@ namespace Avalonia.Remote.Protocol
private readonly object _lock = new object();
private bool _writeOperationPending;
private bool _readingAlreadyStarted;
- private bool _writerIsBroken;
- static readonly JsonSerializer Serializer = new JsonSerializer();
+ private bool _writerIsBroken;
private static readonly byte[] ZeroLength = new byte[4];
public BsonStreamTransportConnection(IMessageTypeResolver resolver, Stream inputStream, Stream outputStream, Action disposeCallback)
@@ -79,7 +77,8 @@ namespace Avalonia.Remote.Protocol
var guid = new Guid(guidBytes);
var buffer = new byte[length];
await ReadExact(buffer).ConfigureAwait(false);
- var message = Serializer.Deserialize(new BsonReader(new MemoryStream(buffer)), _resolver.GetByGuid(guid));
+ var message = Deserializer.Deserialize(new BinaryReader(new MemoryStream(buffer)),
+ _resolver.GetByGuid(guid));
OnMessage?.Invoke(this, message);
}
}
@@ -107,8 +106,8 @@ namespace Avalonia.Remote.Protocol
_outputBlock.SetLength(0);
_outputBlock.Write(ZeroLength, 0, 4);
_outputBlock.Write(guid, 0, guid.Length);
- var writer = new BsonWriter(_outputBlock);
- Serializer.Serialize(writer, data);
+ var serialized = Serializer.Serialize(data);
+ _outputBlock.Write(serialized, 0, serialized.Length);
_outputBlock.Seek(0, SeekOrigin.Begin);
var length = BitConverter.GetBytes((int)_outputBlock.Length - 20);
_outputBlock.Write(length, 0, length.Length);
diff --git a/src/Avalonia.Remote.Protocol/MetsysBson.cs b/src/Avalonia.Remote.Protocol/MetsysBson.cs
new file mode 100644
index 0000000000..eb6445c3a5
--- /dev/null
+++ b/src/Avalonia.Remote.Protocol/MetsysBson.cs
@@ -0,0 +1,1632 @@
+/*
+Copyright (c) 2010, Karl Seguin - http://www.openmymind.net/
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Code imported from https://github.com/elaberge/Metsys.Bson without any changes
+
+*/
+
+using System.Text;
+using System.Text.RegularExpressions;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Net;
+using System.Security.Cryptography;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System;
+using System.Runtime.Serialization;
+
+using Metsys.Bson.Configuration;
+// ReSharper disable All
+
+namespace Metsys.Bson
+{
+ internal enum Types
+ {
+ Double = 1,
+ String = 2,
+ Object = 3,
+ Array = 4,
+ Binary = 5,
+ Undefined = 6,
+ ObjectId = 7,
+ Boolean = 8,
+ DateTime = 9,
+ Null = 10,
+ Regex = 11,
+ Reference = 12,
+ Code = 13,
+ Symbol = 14,
+ ScopedCode = 15,
+ Int32 = 16,
+ Timestamp = 17,
+ Int64 = 18,
+ }
+}
+
+namespace Metsys.Bson
+{
+
+ internal class Serializer
+ {
+ private static readonly IDictionary _typeMap = new Dictionary
+ {
+ {typeof (int), Types.Int32},
+ {typeof (long), Types.Int64},
+ {typeof (bool), Types.Boolean},
+ {typeof (string), Types.String},
+ {typeof (double), Types.Double},
+ {typeof (Guid), Types.Binary},
+ {typeof (Regex), Types.Regex},
+ {typeof (DateTime), Types.DateTime},
+ {typeof (float), Types.Double},
+ {typeof (byte[]), Types.Binary},
+ {typeof (ObjectId), Types.ObjectId},
+ {typeof (ScopedCode), Types.ScopedCode}
+ };
+
+ private readonly BinaryWriter _writer;
+ private Document _current;
+
+ public static byte[] Serialize(T document)
+ {
+ var type = document.GetType();
+ if (type.IsValueType ||
+ (typeof(IEnumerable).IsAssignableFrom(type) && typeof(IDictionary).IsAssignableFrom(type) == false)
+ )
+ {
+ throw new BsonException("Root type must be an object");
+ }
+ using (var ms = new MemoryStream(250))
+ using (var writer = new BinaryWriter(ms))
+ {
+ new Serializer(writer).WriteDocument(document);
+ return ms.ToArray();
+ }
+ }
+ public static byte[] Serialize(object document)
+ {
+ var type = document.GetType();
+ if (type.IsValueType ||
+ (typeof(IEnumerable).IsAssignableFrom(type) && typeof(IDictionary).IsAssignableFrom(type) == false)
+ )
+ {
+ throw new BsonException("Root type must be an object");
+ }
+ using (var ms = new MemoryStream(250))
+ using (var writer = new BinaryWriter(ms))
+ {
+ new Serializer(writer).WriteDocument(document);
+ return ms.ToArray();
+ }
+ }
+
+ private Serializer(BinaryWriter writer)
+ {
+ _writer = writer;
+ }
+
+ private void NewDocument()
+ {
+ var old = _current;
+ _current = new Document { Parent = old, Length = (int)_writer.BaseStream.Position, Digested = 4 };
+ _writer.Write(0); // length placeholder
+ }
+ private void EndDocument(bool includeEeo)
+ {
+ var old = _current;
+ if (includeEeo)
+ {
+ Written(1);
+ _writer.Write((byte)0);
+ }
+
+ _writer.Seek(_current.Length, SeekOrigin.Begin);
+ _writer.Write(_current.Digested); // override the document length placeholder
+ _writer.Seek(0, SeekOrigin.End); // back to the end
+ _current = _current.Parent;
+ if (_current != null)
+ {
+ Written(old.Digested);
+ }
+ }
+
+ private void Written(int length)
+ {
+ _current.Digested += length;
+ }
+
+ private void WriteDocument(object document)
+ {
+ NewDocument();
+ WriteObject(document);
+ EndDocument(true);
+ }
+
+ private void WriteObject(object document)
+ {
+ var asDictionary = document as IDictionary;
+ if (asDictionary != null)
+ {
+ Write(asDictionary);
+ return;
+ }
+
+ var typeHelper = TypeHelper.GetHelperForType(document.GetType());
+ foreach (var property in typeHelper.GetProperties())
+ {
+ if (property.Ignored) { continue; }
+ var name = property.Name;
+ var value = property.Getter(document);
+ if (value == null && property.IgnoredIfNull)
+ {
+ continue;
+ }
+ SerializeMember(name, value);
+ }
+ }
+
+ private void SerializeMember(string name, object value)
+ {
+ if (value == null)
+ {
+ Write(Types.Null);
+ WriteName(name);
+ return;
+ }
+
+ var type = value.GetType();
+ if (type.IsEnum)
+ {
+ type = Enum.GetUnderlyingType(type);
+ }
+
+ Types storageType;
+ if (!_typeMap.TryGetValue(type, out storageType))
+ {
+ // this isn't a simple type;
+ Write(name, value);
+ return;
+ }
+
+ Write(storageType);
+ WriteName(name);
+ switch (storageType)
+ {
+ case Types.Int32:
+ Written(4);
+ _writer.Write((int)value);
+ return;
+ case Types.Int64:
+ Written(8);
+ _writer.Write((long)value);
+ return;
+ case Types.String:
+ Write((string)value);
+ return;
+ case Types.Double:
+ Written(8);
+ if (value is float)
+ {
+ _writer.Write(Convert.ToDouble((float)value));
+ }
+ else
+ {
+ _writer.Write((double)value);
+ }
+
+ return;
+ case Types.Boolean:
+ Written(1);
+ _writer.Write((bool)value ? (byte)1 : (byte)0);
+ return;
+ case Types.DateTime:
+ Written(8);
+ _writer.Write((long)((DateTime)value).ToUniversalTime().Subtract(Helper.Epoch).TotalMilliseconds);
+ return;
+ case Types.Binary:
+ WriteBinnary(value);
+ return;
+ case Types.ScopedCode:
+ Write((ScopedCode)value);
+ return;
+ case Types.ObjectId:
+ Written(((ObjectId)value).Value.Length);
+ _writer.Write(((ObjectId)value).Value);
+ return;
+ case Types.Regex:
+ Write((Regex)value);
+ break;
+ }
+ }
+
+ private void Write(string name, object value)
+ {
+ if (value is IDictionary)
+ {
+ Write(Types.Object);
+ WriteName(name);
+ NewDocument();
+ Write((IDictionary)value);
+ EndDocument(true);
+ }
+ else if (value is IEnumerable)
+ {
+ Write(Types.Array);
+ WriteName(name);
+ NewDocument();
+ Write((IEnumerable)value);
+ EndDocument(true);
+ }
+ else
+ {
+ Write(Types.Object);
+ WriteName(name);
+ WriteDocument(value); // Write manages new/end document
+ }
+ }
+
+ private void Write(IEnumerable enumerable)
+ {
+ var index = 0;
+ foreach (var value in enumerable)
+ {
+ SerializeMember((index++).ToString(), value);
+ }
+ }
+
+ private void Write(IDictionary dictionary)
+ {
+ foreach (var key in dictionary.Keys)
+ {
+ SerializeMember((string)key, dictionary[key]);
+ }
+ }
+
+ private void WriteBinnary(object value)
+ {
+ if (value is byte[])
+ {
+ var bytes = (byte[])value;
+ var length = bytes.Length;
+ _writer.Write(length + 4);
+ _writer.Write((byte)2);
+ _writer.Write(length);
+ _writer.Write(bytes);
+ Written(9 + length);
+ }
+ else if (value is Guid)
+ {
+ var guid = (Guid)value;
+ var bytes = guid.ToByteArray();
+ _writer.Write(bytes.Length);
+ _writer.Write((byte)3);
+ _writer.Write(bytes);
+ Written(5 + bytes.Length);
+ }
+ }
+
+ private void Write(Types type)
+ {
+ _writer.Write((byte)type);
+ Written(1);
+ }
+
+ private void WriteName(string name)
+ {
+ var bytes = Encoding.UTF8.GetBytes(name);
+ _writer.Write(bytes);
+ _writer.Write((byte)0);
+ Written(bytes.Length + 1);
+ }
+
+ private void Write(string name)
+ {
+ var bytes = Encoding.UTF8.GetBytes(name);
+ _writer.Write(bytes.Length + 1);
+ _writer.Write(bytes);
+ _writer.Write((byte)0);
+ Written(bytes.Length + 5); // stringLength + length + null byte
+ }
+
+ private void Write(Regex regex)
+ {
+ WriteName(regex.ToString());
+
+ var options = string.Empty;
+ if ((regex.Options & RegexOptions.ECMAScript) == RegexOptions.ECMAScript)
+ {
+ options = string.Concat(options, 'e');
+ }
+
+ if ((regex.Options & RegexOptions.IgnoreCase) == RegexOptions.IgnoreCase)
+ {
+ options = string.Concat(options, 'i');
+ }
+
+ if ((regex.Options & RegexOptions.CultureInvariant) == RegexOptions.CultureInvariant)
+ {
+ options = string.Concat(options, 'l');
+ }
+
+ if ((regex.Options & RegexOptions.Multiline) == RegexOptions.Multiline)
+ {
+ options = string.Concat(options, 'm');
+ }
+
+ if ((regex.Options & RegexOptions.Singleline) == RegexOptions.Singleline)
+ {
+ options = string.Concat(options, 's');
+ }
+
+ options = string.Concat(options, 'u'); // all .net regex are unicode regex, therefore:
+ if ((regex.Options & RegexOptions.IgnorePatternWhitespace) == RegexOptions.IgnorePatternWhitespace)
+ {
+ options = string.Concat(options, 'w');
+ }
+
+ if ((regex.Options & RegexOptions.ExplicitCapture) == RegexOptions.ExplicitCapture)
+ {
+ options = string.Concat(options, 'x');
+ }
+
+ WriteName(options);
+ }
+
+ private void Write(ScopedCode value)
+ {
+ NewDocument();
+ Write(value.CodeString);
+ WriteDocument(value.Scope);
+ EndDocument(false);
+ }
+ }
+}
+namespace Metsys.Bson
+{
+ internal class ScopedCode
+ {
+ public string CodeString { get; set; }
+ public object Scope { get; set; }
+ }
+
+ internal class ScopedCode : ScopedCode
+ {
+ public new T Scope { get; set; }
+ }
+}
+
+namespace Metsys.Bson
+{
+ internal static class ObjectIdGenerator
+ {
+ private static readonly object _inclock = new object();
+
+ private static int _counter;
+ private static readonly byte[] _machineHash = GenerateHostHash();
+ private static readonly byte[] _processId = BitConverter.GetBytes(GenerateProcId());
+
+ public static byte[] Generate()
+ {
+ var oid = new byte[12];
+ var copyidx = 0;
+
+ Array.Copy(BitConverter.GetBytes(GenerateTime()), 0, oid, copyidx, 4);
+ copyidx += 4;
+
+ Array.Copy(_machineHash, 0, oid, copyidx, 3);
+ copyidx += 3;
+
+ Array.Copy(_processId, 0, oid, copyidx, 2);
+ copyidx += 2;
+
+ Array.Copy(BitConverter.GetBytes(GenerateInc()), 0, oid, copyidx, 3);
+ return oid;
+ }
+
+ private static int GenerateTime()
+ {
+ var now = DateTime.Now.ToUniversalTime();
+
+ var nowtime = new DateTime(Helper.Epoch.Year, Helper.Epoch.Month, Helper.Epoch.Day, now.Hour, now.Minute, now.Second, now.Millisecond);
+ var diff = nowtime - Helper.Epoch;
+ return Convert.ToInt32(Math.Floor(diff.TotalMilliseconds));
+ }
+
+ private static int GenerateInc()
+ {
+ lock (_inclock)
+ {
+ return _counter++;
+ }
+ }
+
+ private static byte[] GenerateHostHash()
+ {
+ using (var md5 = MD5.Create())
+ {
+ var host = Dns.GetHostName();
+ return md5.ComputeHash(Encoding.Default.GetBytes(host));
+ }
+ }
+ private static int GenerateProcId()
+ {
+ var proc = Process.GetCurrentProcess();
+ return proc.Id;
+ }
+ }
+}
+
+namespace Metsys.Bson
+{
+ internal class ObjectId
+ {
+ private string _string;
+
+ public ObjectId()
+ {
+ }
+
+ public ObjectId(string value) : this(DecodeHex(value))
+ {
+ }
+
+ internal ObjectId(byte[] value)
+ {
+ Value = value;
+ }
+
+ public static ObjectId Empty
+ {
+ get { return new ObjectId("000000000000000000000000"); }
+ }
+
+ public byte[] Value { get; private set; }
+
+ public static ObjectId NewObjectId()
+ {
+ // TODO: generate random-ish bits.
+ return new ObjectId { Value = ObjectIdGenerator.Generate() };
+ }
+
+ public static bool TryParse(string value, out ObjectId id)
+ {
+ id = Empty;
+ if (value == null || value.Length != 24)
+ {
+ return false;
+ }
+
+ try
+ {
+ id = new ObjectId(value);
+ return true;
+ }
+ catch (FormatException)
+ {
+ return false;
+ }
+ }
+
+ public static bool operator ==(ObjectId a, ObjectId b)
+ {
+ if (ReferenceEquals(a, b))
+ {
+ return true;
+ }
+
+ if (((object)a == null) || ((object)b == null))
+ {
+ return false;
+ }
+
+ return a.Equals(b);
+ }
+
+ public static bool operator !=(ObjectId a, ObjectId b)
+ {
+ return !(a == b);
+ }
+
+ public override int GetHashCode()
+ {
+ return Value != null ? ToString().GetHashCode() : 0;
+ }
+
+ public override string ToString()
+ {
+ if (_string == null && Value != null)
+ {
+ _string = BitConverter.ToString(Value).Replace("-", string.Empty).ToLower();
+ }
+
+ return _string;
+ }
+
+ public override bool Equals(object o)
+ {
+ var other = o as ObjectId;
+ return Equals(other);
+ }
+
+ public bool Equals(ObjectId other)
+ {
+ return other != null && ToString() == other.ToString();
+ }
+
+ protected static byte[] DecodeHex(string val)
+ {
+ var chars = val.ToCharArray();
+ var numberChars = chars.Length;
+ var bytes = new byte[numberChars / 2];
+
+ for (var i = 0; i < numberChars; i += 2)
+ {
+ bytes[i / 2] = Convert.ToByte(new string(chars, i, 2), 16);
+ }
+
+ return bytes;
+ }
+
+ public static implicit operator string(ObjectId oid)
+ {
+ return oid == null ? null : oid.ToString();
+ }
+ public static implicit operator ObjectId(string oidString)
+ {
+ return new ObjectId(oidString);
+ }
+ }
+}
+
+namespace Metsys.Bson
+{
+ public interface IExpando
+ {
+ IDictionary Expando { get; }
+ }
+}
+
+namespace Metsys.Bson
+{
+ internal class MagicProperty
+ {
+ private readonly PropertyInfo _property;
+ private readonly string _name;
+ private readonly bool _ignored;
+ public readonly bool _ignoredIfNull;
+
+ public Type Type
+ {
+ get { return _property.PropertyType; }
+ }
+ public string Name
+ {
+ get { return _name; }
+ }
+ public bool Ignored
+ {
+ get { return _ignored; }
+ }
+ public bool IgnoredIfNull
+ {
+ get { return _ignoredIfNull; }
+ }
+
+ public Action