From 1da1b7878d035fe6ba5efccef1e6abf8aa360872 Mon Sep 17 00:00:00 2001 From: GabrielTofvesson Date: Mon, 9 Apr 2018 20:49:05 +0200 Subject: [PATCH] Implemented floating point value serialization Implemented floating point value deserialization Separated Elliptic Curve Point class to its own file --- Client/BinaryCollector.cs | 58 ++++++++++++++++++------ Client/BinaryDistributor.cs | 68 ++++++++++++++++++++++++++++ Client/Client.csproj | 1 + Client/Program.cs | 10 +++- Common/Common.csproj | 1 + Common/Cryptography/EllipticCurve.cs | 22 ++------- Common/Cryptography/Point.cs | 27 +++++++++++ 7 files changed, 151 insertions(+), 36 deletions(-) create mode 100644 Client/BinaryDistributor.cs create mode 100644 Common/Cryptography/Point.cs diff --git a/Client/BinaryCollector.cs b/Client/BinaryCollector.cs index b2a08e3..14f3bb0 100644 --- a/Client/BinaryCollector.cs +++ b/Client/BinaryCollector.cs @@ -14,6 +14,9 @@ namespace Client // Collects reusable private static readonly List> expired = new List>(); + private static readonly byte[] holder = new byte[8]; + private static readonly float[] holder_f = new float[1]; + private static readonly double[] holder_d = new double[1]; private static readonly List supportedTypes = new List() { typeof(bool), @@ -93,10 +96,11 @@ namespace Client private static void Serialize(T t, byte[] writeTo, ref long bitOffset) { Type type = t.GetType(); + bool size = false; if (type.IsArray) { var array = t as Array; - Serialize(array.Length, writeTo, ref bitOffset); + Serialize((short)array.Length, writeTo, ref bitOffset); foreach (var element in array) Serialize(element, writeTo, ref bitOffset); } @@ -111,9 +115,25 @@ namespace Client WriteDynamic(writeTo, dec_hi.GetValue(t), 4, bitOffset + 64); WriteDynamic(writeTo, dec_flags.GetValue(t), 4, bitOffset + 96); } - else if(type == typeof(float)) + else if((size = type == typeof(float)) || type == typeof(double)) { - + int bytes = size ? 4 : 8; + Array type_holder = size ? holder_f as Array : holder_d as Array; // Fetch the preallocated array + lock (type_holder) + lock (holder) + { + type_holder.SetValue(t, 0); // Insert the value to convert into the preallocated holder array + Buffer.BlockCopy(type_holder, 0, holder, 0, bytes); // Perform an internal copy to the byte-based holder + for (int i = 0; i < bytes; ++i) + WriteByte(writeTo, holder[i], bitOffset + (i * 8)); // Write the converted value to the output array + } + } + else + { + dynamic value = t; + int type_size = Marshal.SizeOf(typeof(T)); + for (int i = 0; i < type_size; ++i) + WriteByte(writeTo, (byte)(value >> (int)(i * 8)), bitOffset + (i * 8)); } bitOffset += offset; } @@ -126,25 +146,25 @@ namespace Client if (type.IsArray) { Type elementType = type.GetElementType(); - long allocSize = GetBitAllocation(elementType); - var array = t as Array; - count += 2; // Int16 array size. Arrays shouldn't be syncing more than 65k elements - - if (allocSize != 0) // The array contents is known: compute the data size - count += allocSize * array.Length; - else // Unknown array contents type: iteratively assess serialization size - foreach (var element in t as Array) - count += GetBitCount(element); + count += 16; // Int16 array size. Arrays shouldn't be syncing more than 65k elements + foreach (var element in t as Array) + count += GetBitCount(element); + } + else if (IsSupportedType(type)) + { + long ba = GetBitAllocation(type); + if (ba == 0) count += Encoding.UTF8.GetByteCount(t as string); + else if (t is bool || t is float || t is double || t is decimal) count += ba; + else count += BytesToRead(t, Marshal.SizeOf(t)) * 8; } - else if(IsSupportedType(type)) count += GetBitAllocation(type); //else // Debug.LogWarning("MLAPI: The type \"" + b.GetType() + "\" is not supported by the Binary Serializer. It will be ignored"); return count; } private static void WriteBit(byte[] b, bool bit, long index) - => b[index / 8] = (byte)((b[index / 8] & (1 << (int)(index % 8))) | (bit ? 1 << (int)(index % 8) : 0)); + => b[index / 8] = (byte)((b[index / 8] & ~(1 << (int)(index % 8))) | (bit ? 1 << (int)(index % 8) : 0)); private static void WriteByte(byte[] b, byte value, long index) { int byteIndex = (int)(index / 8); @@ -154,7 +174,7 @@ namespace Client b[byteIndex] = (byte)((b[byteIndex] & lower_mask) | (value << shift)); if(shift != 0 && byteIndex + 1 < b.Length) - b[byteIndex + 1] = (byte)((b[byteIndex + 1] & upper_mask) | (value << (8 - shift))); + b[byteIndex + 1] = (byte)((b[byteIndex + 1] & upper_mask) | (value >> (8 - shift))); } private static void WriteDynamic(byte[] b, dynamic value, int byteCount, long index) { @@ -162,6 +182,14 @@ namespace Client WriteByte(b, (byte)((value >> (8 * i)) & 0xFF), index + (8 * i)); } + private static int BytesToRead(dynamic integer, int maxBytes) + { + for (int i = 0; i < maxBytes; ++i) + if ((integer >> (8 * i)) & 0xFF <= 127) + return i + 1; + return maxBytes; + } + // Supported datatypes for serialization private static bool IsSupportedType(Type t) => supportedTypes.Contains(t); diff --git a/Client/BinaryDistributor.cs b/Client/BinaryDistributor.cs new file mode 100644 index 0000000..ebbf626 --- /dev/null +++ b/Client/BinaryDistributor.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Client +{ + public class BinaryDistributor + { + private static readonly byte[] holder = new byte[8]; + private static readonly float[] holder_f = new float[1]; + private static readonly double[] holder_d = new double[1]; + + private readonly byte[] readFrom; + private long bitCount = 0; + public BinaryDistributor(byte[] readFrom) => this.readFrom = readFrom; + + public bool ReadBit() + { + bool result = (readFrom[bitCount / 8] & (byte)(1 << (int)(bitCount % 8))) != 0; + ++bitCount; + return result; + } + + public byte ReadByte() + { + int shift = (int)(bitCount % 8); + int index = (int)(bitCount / 8); + byte lower_mask = (byte)(0xFF << shift); + byte upper_mask = (byte)~lower_mask; + byte result = (byte)(((readFrom[index] & lower_mask) >> shift) | (shift == 0 ? 0 : (readFrom[index + 1] & upper_mask) << (8 - shift))); + bitCount += 8; + return result; + } + + public float ReadFloat() => ReadFloating(); + public double ReadDouble() => ReadFloating(); + public float[] ReadFloatArray() => ReadFloatingArray(); + public double[] ReadDoubleArray() => ReadFloatingArray(); + + private T[] ReadFloatingArray() + { + short size = (short)(ReadByte() | (ReadByte() << 8)); + T[] result = new T[size]; + for (short s = 0; s < size; ++s) + result[s] = ReadFloating(); + return result; + } + + private T ReadFloating() + { + int size = Marshal.SizeOf(typeof(T)); + Array type_holder = size == 4 ? holder_f as Array: holder_d as Array; + T result; + lock(type_holder) + lock (holder) + { + for (int i = 0; i < size; ++i) + holder.SetValue(ReadByte(), i); + Buffer.BlockCopy(holder, 0, type_holder, 0, size); + result = (T) type_holder.GetValue(0); + } + return result; + } + } +} diff --git a/Client/Client.csproj b/Client/Client.csproj index 85807e1..e4afd23 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -44,6 +44,7 @@ + diff --git a/Client/Program.cs b/Client/Program.cs index 8867aad..94349bf 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -18,14 +18,20 @@ namespace ConsoleForms // Set up timestamps in debug output DebugStream = new TimeStampWriter(DebugStream, "HH:mm:ss.fff"); + + byte[] serialized; - using (BinaryCollector collector = new BinaryCollector(1)) + using (BinaryCollector collector = new BinaryCollector(2)) { - collector.Push(5f); + collector.Push(true); + collector.Push(new double[] { 6.0, 5.0 }); serialized = collector.ToArray(); } + BinaryDistributor bd = new BinaryDistributor(serialized); + bool bit = bd.ReadBit(); + double[] result = bd.ReadDoubleArray(); Padding p = new AbsolutePadding(2, 2, 1, 1); diff --git a/Common/Common.csproj b/Common/Common.csproj index 17ac4f9..ce38efd 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -70,6 +70,7 @@ + diff --git a/Common/Cryptography/EllipticCurve.cs b/Common/Cryptography/EllipticCurve.cs index 10cfbb3..3d7f404 100644 --- a/Common/Cryptography/EllipticCurve.cs +++ b/Common/Cryptography/EllipticCurve.cs @@ -1,4 +1,6 @@ -using System; +//#define SAFE_MATH + +using System; using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -8,24 +10,6 @@ using Tofvesson.Crypto; namespace Common.Cryptography { - public class Point - { - public static readonly Point POINT_AT_INFINITY = new Point(); - public BigInteger X { get; private set; } - public BigInteger Y { get; private set; } - private bool pai = false; - public Point(BigInteger x, BigInteger y) - { - X = x; - Y = y; - } - private Point() { pai = true; } // Accessing corrdinates causes undocumented behaviour - public override string ToString() - { - return pai ? "(POINT_AT_INFINITY)" : "(" + X + ", " + Y + ")"; - } - } - public class EllipticCurve { public enum CurveType { Weierstrass, Montgomery } diff --git a/Common/Cryptography/Point.cs b/Common/Cryptography/Point.cs new file mode 100644 index 0000000..fcd24aa --- /dev/null +++ b/Common/Cryptography/Point.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; + +namespace Common.Cryptography +{ + public class Point + { + public static readonly Point POINT_AT_INFINITY = new Point(); + public BigInteger X { get; private set; } + public BigInteger Y { get; private set; } + private bool pai = false; + public Point(BigInteger x, BigInteger y) + { + X = x; + Y = y; + } + private Point() { pai = true; } // Accessing corrdinates causes undocumented behaviour + public override string ToString() + { + return pai ? "(POINT_AT_INFINITY)" : "(" + X + ", " + Y + ")"; + } + } +}