diff --git a/Shared/MessagePack/ConverterContext.cs b/Shared/MessagePack/ConverterContext.cs index d374c85..2611326 100644 --- a/Shared/MessagePack/ConverterContext.cs +++ b/Shared/MessagePack/ConverterContext.cs @@ -22,22 +22,22 @@ namespace nanoFramework.MessagePack /// public static class ConverterContext { - private static readonly Type[] _emptyTypes = new Type[0]; + private static readonly Type[] s_emptyTypes = new Type[0]; private static readonly NullConverter s_nullConverter = new(); private static readonly MapConverter s_mapConverter = new(); private static readonly ArrayConverter s_arrayConverter = new(); #if NANOFRAMEWORK_1_0 - private static readonly Hashtable _mappingDictionary = new(); + private static readonly Hashtable s_mappingDictionary = new(); #else - private static readonly ConcurrentDictionary _mappingDictionary = new(); + private static readonly ConcurrentDictionary s_mappingDictionary = new(); #endif - private static readonly Hashtable ConversionTable = new() + private static readonly Hashtable s_conversionTable = new() { { typeof(IDictionary).FullName!, s_mapConverter }, { typeof(Hashtable).FullName!, s_mapConverter }, - { typeof(ArrayList).FullName!, new ArrayListConverter()}, + { typeof(ArrayList).FullName!, new ArrayListConverter() }, { typeof(short).FullName!, new ShortConverter() }, { typeof(ushort).FullName!, new UshortConverter() }, { typeof(int).FullName!, new IntConverter() }, @@ -46,7 +46,7 @@ public static class ConverterContext { typeof(ulong).FullName!, new UlongConverter() }, { typeof(byte).FullName!, new ByteConverter() }, { typeof(sbyte).FullName!, new SbyteConverter() }, - { typeof(float).FullName!, new FloatConverter()}, + { typeof(float).FullName!, new FloatConverter() }, { typeof(double).FullName!, new DoubleConverter() }, { typeof(bool).FullName!, new BoolConverter() }, { typeof(string).FullName!, new StringConverter() }, @@ -55,10 +55,10 @@ public static class ConverterContext { typeof(char).FullName!, new CharConverter() }, { typeof(Guid).FullName!, new GuidConverter() }, { typeof(byte[]).FullName!, new BinaryConverter() }, - { typeof(int[]).FullName!, new SimpleArrayConverter(typeof(int))}, - { typeof(uint[]).FullName!, new SimpleArrayConverter(typeof(uint))}, - { typeof(long[]).FullName!, new SimpleArrayConverter(typeof(long))}, - { typeof(ulong[]).FullName!, new SimpleArrayConverter(typeof(ulong))}, + { typeof(int[]).FullName!, new SimpleArrayConverter(typeof(int)) }, + { typeof(uint[]).FullName!, new SimpleArrayConverter(typeof(uint)) }, + { typeof(long[]).FullName!, new SimpleArrayConverter(typeof(long)) }, + { typeof(ulong[]).FullName!, new SimpleArrayConverter(typeof(ulong)) }, { typeof(float[]).FullName!, new SimpleArrayConverter(typeof(float)) }, { typeof(double[]).FullName!, new SimpleArrayConverter(typeof(double)) }, { typeof(char[]).FullName!, new SimpleArrayConverter(typeof(char)) }, @@ -73,7 +73,7 @@ public static class ConverterContext }; /// - /// Null value converter + /// Null value converter. /// public static IConverter NullConverter => s_nullConverter; @@ -81,16 +81,17 @@ public static class ConverterContext /// Adds new converter to collection to support more types. /// /// Type of object. - /// Converter instance which will be used to convert + /// Converter instance which will be used to convert . + /// Converter by type not support in convertors table. [MethodImpl(MethodImplOptions.Synchronized)] public static void Add(Type type, IConverter converter) { if (type == typeof(object)) { - throw new NotSupportedException($"Converter by type {type.Name} not support in convertors table."); + throw new NotSupportedException(); } - ConversionTable.Add(type.FullName!, converter); + s_conversionTable.Add(type.FullName!, converter); } /// @@ -99,14 +100,14 @@ public static void Add(Type type, IConverter converter) /// Type of object. public static void Remove(Type type) { - ConversionTable.Remove(type.FullName!); + s_conversionTable.Remove(type.FullName!); } /// /// Remove and then adds converter for given type. /// /// Type of object. - /// Converter instance which will be used to convert + /// Converter instance which will be used to convert . [MethodImpl(MethodImplOptions.Synchronized)] public static void Replace(Type type, IConverter converter) { @@ -115,10 +116,11 @@ public static void Replace(Type type, IConverter converter) } /// - /// Return converter by type + /// Return converter by type. /// - /// Type from converter - /// Converter interface + /// Type object from converter. + /// Converter interface or null. + /// If object type is . public static IConverter GetConverter(Type type) { if (type == typeof(object)) @@ -126,9 +128,9 @@ public static IConverter GetConverter(Type type) throw ExceptionUtility.ConverterNotFound(type); } - if (ConversionTable.Contains(type.FullName!)) + if (s_conversionTable.Contains(type.FullName!)) { - return (IConverter)ConversionTable[type.FullName!]!; + return (IConverter)s_conversionTable[type.FullName!]!; } return null!; @@ -142,10 +144,17 @@ internal static Hashtable GetMappingsValues(Type targetType, object value) foreach (var memberMapping in memberMappings) { +#if NANOFRAMEWORK_1_0 + if(!memberMapping.OriginalName!.StartsWith(MemberMapping.SET_)) + { +#endif if (memberMapping.TryGetValue(value, out var memberValue) && memberValue != null) { result.Add(memberMapping.Name!, memberValue); } +#if NANOFRAMEWORK_1_0 + } +#endif } return result; } @@ -156,44 +165,55 @@ internal static void SetMappingsValues(Type targetType, object targetObject, Has foreach (var memberMapping in memberMappings) { +#if NANOFRAMEWORK_1_0 + if(!memberMapping.OriginalName!.StartsWith(MemberMapping.GET_)) + { +#endif if (objectValuesMap.Contains(memberMapping.Name!)) { var memberMpToken = (ArraySegment)objectValuesMap[memberMapping.Name!]!; - var memberValueMapType = memberMapping.GetMemberType(); - var converter = GetConverter(memberValueMapType!); - if (converter != null) - { - memberMapping.SetValue(targetObject, converter.Read(new ByteArrayReader((byte[])memberMpToken))!); - } - else + if (memberMpToken != null) { - if (memberValueMapType!.IsArray) + var memberValueMapType = memberMapping.GetMemberType(); + var converter = GetConverter(memberValueMapType!); + if (converter != null) { - memberMapping.SetValue(targetObject, ArrayConverter.Read(new ByteArrayReader((byte[])memberMpToken), memberValueMapType)!); + memberMapping.SetValue(targetObject, converter.Read(memberMpToken)!); } else { - memberMapping.SetValue(targetObject, DeserializeObject(memberValueMapType!, new ByteArrayReader((byte[])memberMpToken))!); + if (memberValueMapType!.IsArray) + { + memberMapping.SetValue(targetObject, ArrayConverter.Read(memberMpToken, memberValueMapType)!); + } + else + { + + memberMapping.SetValue(targetObject, DeserializeObject(memberValueMapType!, memberMpToken)!); + } } } } +#if NANOFRAMEWORK_1_0 + } +#endif } } internal static object CreateInstance(Type targetType) { - var constructor = targetType.GetConstructor(_emptyTypes) ?? throw new Exception($"Target type {targetType?.FullName} does not have a parameterless constructor"); + var constructor = targetType.GetConstructor(s_emptyTypes) ?? throw new Exception($"Target type {targetType?.FullName} does not have a parameterless constructor"); return constructor.Invoke(null); } internal static MemberMapping[] GetMemberMapping(Type targetType) { #if NANOFRAMEWORK_1_0 - var cached = _mappingDictionary[targetType]; + var cached = s_mappingDictionary[targetType]; if (cached is not MemberMapping[] memberMappings) { #else - if (!_mappingDictionary.TryGetValue(targetType, out var memberMappings)) + if (!s_mappingDictionary.TryGetValue(targetType, out var memberMappings)) { #endif var mappings = new ArrayList(); @@ -204,9 +224,9 @@ internal static MemberMapping[] GetMemberMapping(Type targetType) memberMappings = (MemberMapping[])mappings.ToArray(typeof(MemberMapping)); #if NANOFRAMEWORK_1_0 - ThreadSafeAddItemCache(_mappingDictionary, targetType, memberMappings); + ThreadSafeAddItemCache(s_mappingDictionary, targetType, memberMappings); #else - _mappingDictionary.TryAdd(targetType, memberMappings); + s_mappingDictionary.TryAdd(targetType, memberMappings); #endif } @@ -234,6 +254,16 @@ internal static void SerializeObject(Type type, object value, IMessagePackWriter #nullable enable internal static object? DeserializeObject(Type type, IMessagePackReader reader) { + if (type.Name == typeof(IDictionary).Name || type.Name == typeof(Hashtable).Name) + { + return MapConverter.Read(reader); + } + + if (type.IsArray) + { + return ArrayConverter.Read(reader, type); + } + var objectMap = reader.GetMassagePackObjectTokens(); if (objectMap != null && objectMap is Hashtable targetObjectMap) { @@ -284,18 +314,23 @@ internal static void SerializeObject(Type type, object value, IMessagePackWriter } #if NANOFRAMEWORK_1_0 - [MethodImpl(MethodImplOptions.Synchronized)] private static void ThreadSafeAddItemCache(Hashtable hashtable, object key, object value) { if (!hashtable.Contains(key)) { - try - { - hashtable.Add(key, value); - } - catch (Exception ex) + lock(s_mappingDictionary) { - Debug.WriteLine($"Error added key: '{key}', value: '{value}'\r\n{ex}"); + try + { + if (!hashtable.Contains(key)) + { + hashtable.Add(key, value); + } + } + catch (Exception ex) + { + Debug.WriteLine($"Error added key: '{key}', value: '{value}'\r\n{ex}"); + } } } } diff --git a/Shared/MessagePack/Converters/IConverter.cs b/Shared/MessagePack/Converters/IConverter.cs index d8443c0..97927cd 100644 --- a/Shared/MessagePack/Converters/IConverter.cs +++ b/Shared/MessagePack/Converters/IConverter.cs @@ -7,7 +7,7 @@ namespace nanoFramework.MessagePack.Converters { /// - /// interface. + /// interface. /// public interface IConverter { diff --git a/Shared/MessagePack/Converters/MapConverter.cs b/Shared/MessagePack/Converters/MapConverter.cs index 29892c3..8aca161 100644 --- a/Shared/MessagePack/Converters/MapConverter.cs +++ b/Shared/MessagePack/Converters/MapConverter.cs @@ -53,7 +53,7 @@ private static void Write(IDictionary value, IMessagePackWriter writer) } #nullable enable - private static Hashtable? Read(IMessagePackReader reader) + internal static Hashtable? Read(IMessagePackReader reader) { var length = reader.ReadMapLength(); return ((long)length) > -1 ? ReadMap(reader, length) : null; diff --git a/Shared/MessagePack/Converters/StringConverter.cs b/Shared/MessagePack/Converters/StringConverter.cs index 9268b24..01cef5b 100644 --- a/Shared/MessagePack/Converters/StringConverter.cs +++ b/Shared/MessagePack/Converters/StringConverter.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text; +using nanoFramework.MessagePack.Dto; using nanoFramework.MessagePack.Extensions; using nanoFramework.MessagePack.Stream; using nanoFramework.MessagePack.Utility; @@ -28,27 +29,24 @@ private static void Write(string value, IMessagePackWriter writer) writer.Write(data); } } - - private static string Read(IMessagePackReader reader) +#nullable enable + private static string? Read(IMessagePackReader reader) { - var type = reader.ReadDataType(); + DataTypes type = reader.ReadDataType(); switch (type) { case DataTypes.Null: - return null!; - + return null; case DataTypes.Str8: return ReadString(reader, NumberConverterHelper.ReadUInt8(reader)); - case DataTypes.Str16: return ReadString(reader, NumberConverterHelper.ReadUInt16(reader)); - case DataTypes.Str32: return ReadString(reader, NumberConverterHelper.ReadUInt32(reader)); } - if (TryGetFixStrLength(type, out var length)) + if (TryGetFixStrLength(type, out uint length)) { return ReadString(reader, length); } @@ -58,9 +56,9 @@ private static string Read(IMessagePackReader reader) internal static string ReadString(IMessagePackReader reader, uint length) { - var buffer = (byte[])reader.ReadBytes(length); + ArraySegment arraySegment = reader.ReadBytes(length); - return Utf8.GetString(buffer, 0, buffer.Length); + return Utf8.GetString(arraySegment.SourceBuffer, (int)arraySegment.SourceOffset + arraySegment.Position, (int)arraySegment.Length); } private static bool TryGetFixStrLength(DataTypes type, out uint length) diff --git a/Shared/MessagePack/Dto/ArraySegment.cs b/Shared/MessagePack/Dto/ArraySegment.cs index aed328d..152fad1 100644 --- a/Shared/MessagePack/Dto/ArraySegment.cs +++ b/Shared/MessagePack/Dto/ArraySegment.cs @@ -1,46 +1,66 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections; +using System.Diagnostics; +using System.IO; +using nanoFramework.MessagePack.Stream; using nanoFramework.MessagePack.Utility; namespace nanoFramework.MessagePack.Dto { /// - /// Segment by byte array. + /// Segment by byte to array. /// - public class ArraySegment : IEnumerable, IEnumerator + public class ArraySegment : BaseReader, IEnumerable { private readonly byte[] _buffer; private readonly long _offset; private readonly long _length; + private int _firstGatheredByte; + + /// + /// Gets byte by array segment. + /// + /// The byte index in the array segment. + /// Byte value. + public byte this[int index] + { + get + { + return _buffer[_offset + index]; + } + } + + internal byte[] SourceBuffer => _buffer; + + internal long SourceOffset => _offset; + + internal long Length => _length; + /// /// Gets the current position in the segment. /// - public long Position { get; private set; } = -1; + public int Position { get; private set; } = 0; /// - /// Gets the element corresponding to the current position in the segment. + /// Implicit conversion from byte array to . /// - public object Current + /// Source byte array. + public static implicit operator ArraySegment(byte[] bytes) { - get - { - if (Position >= _length) - { - throw ExceptionUtility.NotEnoughBytes(Position, _length); - } + return new ArraySegment(bytes, 0, bytes.Length); + } - try - { - return _buffer[_offset + Position]; - } - catch - { - throw ExceptionUtility.NotEnoughBytes(_offset + Position, _buffer.Length); - } - } + /// + /// Implicit conversion from to byte array. + /// + /// Source . + public static explicit operator byte[](ArraySegment segment) + { + return segment.ToArray(); } /// @@ -57,30 +77,48 @@ public ArraySegment(byte[] buffer, long offset, long length) } /// - /// Implicit conversion from byte array to . + /// Reads bytes in an array segment. /// - /// - public static implicit operator ArraySegment(byte[] bytes) + /// Required reading length. + /// Segment by byte to current segment. + public override ArraySegment ReadBytes(uint length) { - return new ArraySegment(bytes, 0, bytes.Length); + var segment = new ArraySegment(_buffer, _offset + Position, length); + Position += (int)length; + return segment; } /// - /// Implicit conversion from to byte array. + /// Move the reading position in the array segment. /// - /// Source . - public static explicit operator byte[](ArraySegment segment) + /// Offset in bytes. + /// Offset reference point. + /// Unknown value. + public override void Seek(long offset, SeekOrigin origin) { - return segment.ToArray(); + switch (origin) + { + case SeekOrigin.Begin: + Position = (int)offset; + break; + case SeekOrigin.Current: + Position = (int)(Position + offset); + break; + case SeekOrigin.End: + Position = (int)(_length + offset); + break; + default: + throw new ArgumentOutOfRangeException(); + } } - /// . + /// /// Read one byte from the segment. /// /// The byte read. - public byte ReadByte() + public override byte ReadByte() { - if (++Position >= _length) + if (Position >= _length) { throw ExceptionUtility.NotEnoughBytes(Position, _length); } @@ -90,7 +128,7 @@ public byte ReadByte() throw ExceptionUtility.NotEnoughBytes(_offset + Position, _buffer.Length); } - return _buffer[_offset + Position]; + return _buffer[_offset + Position++]; } /// @@ -99,39 +137,99 @@ public byte ReadByte() /// Enumerator for bytes in segment. public IEnumerator GetEnumerator() { - return this; + return new ArraySegmentEnumerator(this); + } + + private byte[] ToArray() + { + var data = new byte[_length]; + + System.Array.Copy(_buffer, (int)_offset, data, 0, (int)_length); + return data; } /// - /// Go to next byte in array. + /// Stopping the collection of MessagePack token in . /// - /// if the end of the segment is not reached otherwise . - public bool MoveNext() + /// Array segment bytes . +#nullable enable + protected override ArraySegment? StopTokenGathering() { - Position++; - - return Position < _length && Position + _offset < _buffer.Length; + if (_firstGatheredByte <= _length) + { + var result = new ArraySegment(_buffer, (int)_firstGatheredByte + _offset, (int)(Position - _firstGatheredByte)); + _firstGatheredByte = 0; + return result; + } + else + { + _firstGatheredByte = 0; + return null; + } } /// - /// Resets the current position to the beginning of the segment. + /// Start the collection of MessagePack token in . /// - public void Reset() + protected override void StartTokenGathering() { - Position = -1; + _firstGatheredByte = Position; } - private byte[] ToArray() + /// + /// Enumerator in an array segment. + /// + public class ArraySegmentEnumerator : IEnumerator { - var data = new byte[_length]; + private int _position = -1; + private ArraySegment _arraySegment; - ////for (int i = 0; i < data.Length; i++) - ////{ - //// data[i] = _buffer[_offset + i]; - ////} + internal ArraySegmentEnumerator(ArraySegment arraySegment) + { + _arraySegment = arraySegment; + } - System.Array.Copy(_buffer, (int)_offset, data, 0, (int)_length); - return data; + /// + /// Gets the element corresponding to the current position in the segment. + /// + public object Current + { + get + { + if (_position >= _arraySegment._length) + { + throw ExceptionUtility.NotEnoughBytes(_position, _arraySegment._length); + } + + try + { + return _arraySegment._buffer[_arraySegment._offset + _position]; + } + catch + { + throw ExceptionUtility.NotEnoughBytes(_arraySegment._offset + _position, _arraySegment._buffer.Length); + } + } + } + + /// + /// Go to next byte in array. + /// + /// if the end of the segment is not reached otherwise . + public bool MoveNext() + { + _position++; + + return _position < _arraySegment._length && _position + _arraySegment._offset < _arraySegment._buffer.Length; + } + + /// + /// Resets the current position to the beginning of the segment. + /// + public void Reset() + { + _position = -1; + } } } } diff --git a/Shared/MessagePack/Extensions/NumberConverterHelper.cs b/Shared/MessagePack/Extensions/NumberConverterHelper.cs index 94cdc02..1ff2983 100644 --- a/Shared/MessagePack/Extensions/NumberConverterHelper.cs +++ b/Shared/MessagePack/Extensions/NumberConverterHelper.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using nanoFramework.MessagePack.Dto; using nanoFramework.MessagePack.Stream; using nanoFramework.MessagePack.Utility; @@ -42,7 +43,7 @@ internal static sbyte ReadInt8(IMessagePackReader reader) return (sbyte)temp; } - return (sbyte)(temp - byte.MaxValue - 1); + return (sbyte)((int)temp - byte.MaxValue - 1); } internal static byte ReadUInt8(IMessagePackReader reader) @@ -63,7 +64,7 @@ internal static short ReadInt16(IMessagePackReader reader) return (short)temp; } - return (short)(temp - 1 - ushort.MaxValue); + return (short)((int)temp - 1 - ushort.MaxValue); } internal static int ReadInt32(IMessagePackReader reader) @@ -74,7 +75,7 @@ internal static int ReadInt32(IMessagePackReader reader) return (int)temp; } - return (int)(temp - 1 - uint.MaxValue); + return (int)((long)temp - 1 - uint.MaxValue); } internal static uint ReadUInt32(IMessagePackReader reader) @@ -188,11 +189,9 @@ internal static bool TryGetInt32(DataTypes type, IMessagePackReader reader, out case DataTypes.UInt8: result = ReadUInt8(reader); return true; - case DataTypes.UInt16: result = ReadUInt16(reader); return true; - case DataTypes.UInt32: var uintValue = ReadUInt32(reader); @@ -203,19 +202,15 @@ internal static bool TryGetInt32(DataTypes type, IMessagePackReader reader, out } return false; - case DataTypes.Int8: result = ReadInt8(reader); return true; - case DataTypes.Int16: result = ReadInt16(reader); return true; - case DataTypes.Int32: result = ReadInt32(reader); return true; - default: return false; } @@ -241,42 +236,36 @@ internal static bool TryGetInt64(DataTypes type, IMessagePackReader reader, out case DataTypes.UInt8: result = ReadUInt8(reader); return true; - case DataTypes.UInt16: result = ReadUInt16(reader); return true; - case DataTypes.UInt32: result = ReadUInt32(reader); return true; - case DataTypes.UInt64: var ulongValue = ReadUInt64(reader); - if (ulongValue <= long.MaxValue) { result = (long)ulongValue; return true; } - - return false; + else + { + return false; + } case DataTypes.Int8: result = ReadInt8(reader); return true; - case DataTypes.Int16: result = ReadInt16(reader); return true; - case DataTypes.Int32: result = ReadInt32(reader); return true; - case DataTypes.Int64: result = reader.ReadInt64(); return true; - default: return false; } @@ -541,9 +530,9 @@ internal static double ReadDouble(IMessagePackReader reader) return BitConverter.ToDouble(bytes, 0); } - private static byte[] ReadBytes(IMessagePackReader reader, uint length) + private static ArraySegment ReadBytes(IMessagePackReader reader, uint length) { - return (byte[])reader.ReadBytes(length); + return reader.ReadBytes(length); } } } diff --git a/Shared/MessagePack/MessagePackSerializer.cs b/Shared/MessagePack/MessagePackSerializer.cs index 9876044..cc5c035 100644 --- a/Shared/MessagePack/MessagePackSerializer.cs +++ b/Shared/MessagePack/MessagePackSerializer.cs @@ -13,25 +13,28 @@ namespace nanoFramework.MessagePack public static class MessagePackSerializer { /// - /// Serialize object in to MessagePack byte array + /// Serialize object in to MessagePack byte array. /// - /// Source object - /// MessagePack byte array + /// Source object. + /// MessagePack byte array. public static byte[] Serialize(object data) { - var memoryStream = new MemoryStream(); - using var writer = new MemoryStreamWriter(memoryStream); - - Serialize(data, writer); + using (var memoryStream = new MemoryStream()) + { + using (var writer = new MemoryStreamWriter(memoryStream)) + { + Serialize(data, writer); - return memoryStream.ToArray(); + return memoryStream.ToArray(); + } + } } /// - /// Serialize object in to MessagePack stream + /// Serialize object in to MessagePack stream. /// - /// Source object - /// Target MessagePack stream + /// Source object. + /// Target MessagePack stream. public static void Serialize(object data, MemoryStream stream) { var writer = new MemoryStreamWriter(stream); @@ -39,11 +42,11 @@ public static void Serialize(object data, MemoryStream stream) } /// - /// Deserialize MessagePack data in to object + /// Deserialize MessagePack data in to object. /// - /// Target object type - /// MessagePack byte array data - /// Target object + /// Target object type. + /// MessagePack byte array data. + /// An instance of an target object after deserialization or . #nullable enable public static object? Deserialize(Type type, byte[] data) { @@ -52,16 +55,17 @@ public static void Serialize(object data, MemoryStream stream) } /// - /// Deserialize MessagePack data from stream in to object + /// Deserialize MessagePack data from stream in to object. /// - /// Target object type - /// MessagePack data stream - /// + /// Target object type. + /// MessagePack data stream. + /// An instance of an target object after deserialization or . public static object? Deserialize(Type type, MemoryStream stream) { - using var reader = new MemoryStreamReader(stream); - return Deserialize(type, reader); - + using (var reader = new MemoryStreamReader(stream)) + { + return Deserialize(type, reader); + } } private static object? Deserialize(Type type, IMessagePackReader reader) @@ -92,6 +96,5 @@ private static void Serialize(object data, IMessagePackWriter writer) ConverterContext.SerializeObject(type, data, writer); } } - } } diff --git a/Shared/MessagePack/Stream/BaseReader.cs b/Shared/MessagePack/Stream/BaseReader.cs index 851123d..feea42f 100644 --- a/Shared/MessagePack/Stream/BaseReader.cs +++ b/Shared/MessagePack/Stream/BaseReader.cs @@ -8,14 +8,36 @@ namespace nanoFramework.MessagePack.Stream { - internal abstract class BaseReader : IMessagePackReader + /// + /// Base class for MessagePack reader. + /// + public abstract class BaseReader : IMessagePackReader { + /// + /// Read byte from MessagePack. + /// + /// Read a byte. public abstract byte ReadByte(); + /// + /// Read bytes from MessagePack. + /// + /// Length to read. + /// with the bytes read. public abstract ArraySegment ReadBytes(uint length); + /// + /// Moving the current read position in the array. + /// + /// Offset in bytes. + /// Offset reference point. public abstract void Seek(long offset, SeekOrigin origin); + /// + /// Read length MessagePack array. + /// The MessagePacket array type can be or or . + /// + /// Length MessagePack array by read for type array . public uint ReadArrayLength() { var type = ReadDataType(); @@ -38,6 +60,11 @@ public uint ReadArrayLength() throw ExceptionUtility.BadTypeException(type, DataTypes.Array16, DataTypes.Array32, DataTypes.FixArray, DataTypes.Null); } + /// + /// Read length MessagePack map. + /// The MessagePacket map type can be or or . + /// + /// Count elements in map by read for type map . public uint ReadMapLength() { var type = ReadDataType(); @@ -61,11 +88,19 @@ public uint ReadMapLength() throw ExceptionUtility.BadTypeException(type, DataTypes.Map16, DataTypes.Map32, DataTypes.FixMap, DataTypes.Null); } + /// + /// Read MessagePack data type . + /// + /// MessagePack data type . public virtual DataTypes ReadDataType() { return (DataTypes)ReadByte(); } + /// + /// Skip MessagePack item in byte array. + /// + /// Unknown value. public void SkipToken() { var dataType = ReadDataType(); @@ -152,7 +187,12 @@ public void SkipToken() throw new System.ArgumentOutOfRangeException(); } + #nullable enable + /// + /// Read MessagePack item in byte array. + /// + /// contained MessagePack item bytes. public ArraySegment? ReadToken() { StartTokenGathering(); @@ -160,24 +200,13 @@ public void SkipToken() var gatheredBytes = StopTokenGathering(); return gatheredBytes; } + private static bool TryGetLengthFromFixStr(DataTypes type, out uint length) { length = type - DataTypes.FixStr; return type.GetHighBits(3) == DataTypes.FixStr.GetHighBits(3); } - protected static bool TryGetLengthFromFixArray(DataTypes type, out uint length) - { - length = type - DataTypes.FixArray; - return type.GetHighBits(4) == DataTypes.FixArray.GetHighBits(4); - } - - protected static bool TryGetLengthFromFixMap(DataTypes type, out uint length) - { - length = type - DataTypes.FixMap; - return type.GetHighBits(4) == DataTypes.FixMap.GetHighBits(4); - } - private void SkipMapItems(uint count) { while (count > 0) @@ -201,9 +230,40 @@ private void SkipBytes(uint bytesCount) { Seek(bytesCount, SeekOrigin.Current); } - + + /// + /// Try getting length fixed MessagePack array. + /// + /// MessagePack array type . + /// Out length MessagePack array. + /// if luck otherwise . + protected static bool TryGetLengthFromFixArray(DataTypes type, out uint length) + { + length = type - DataTypes.FixArray; + return type.GetHighBits(4) == DataTypes.FixArray.GetHighBits(4); + } + + /// + /// Try getting length fixed MessagePack map. + /// + /// MessagePack map type . + /// Out length MessagePack map. + /// if luck otherwise . + protected static bool TryGetLengthFromFixMap(DataTypes type, out uint length) + { + length = type - DataTypes.FixMap; + return type.GetHighBits(4) == DataTypes.FixMap.GetHighBits(4); + } + + /// + /// Stopping the collection of MessagePack token. + /// + /// bytes. protected abstract ArraySegment? StopTokenGathering(); + /// + /// Start the collection of MessagePack token. + /// protected abstract void StartTokenGathering(); } } diff --git a/Shared/MessagePack/Stream/ByteArrayReader.cs b/Shared/MessagePack/Stream/ByteArrayReader.cs index 4a324fb..656d7e3 100644 --- a/Shared/MessagePack/Stream/ByteArrayReader.cs +++ b/Shared/MessagePack/Stream/ByteArrayReader.cs @@ -9,9 +9,9 @@ namespace nanoFramework.MessagePack.Stream { internal class ByteArrayReader : BaseReader { - private uint _firstGatheredByte; private readonly byte[] _data; + private uint _firstGatheredByte; private uint _offset; public ByteArrayReader(byte[] data) @@ -27,15 +27,6 @@ public override byte ReadByte() public override ArraySegment ReadBytes(uint length) { - //uint i = 0; - //byte[] arraySegment = new byte[length]; - //while(i < length) - //{ - // arraySegment[i] = _data[_offset++]; - // i++; - //} - //return arraySegment; - var segment = new ArraySegment(_data, _offset, length); _offset += length; return segment; @@ -43,18 +34,25 @@ public override ArraySegment ReadBytes(uint length) public override void Seek(long offset, SeekOrigin origin) { - _offset = origin switch + switch (origin) { - SeekOrigin.Begin => (uint)offset, - SeekOrigin.Current => (uint)(_offset + offset), - SeekOrigin.End => (uint)(_data.Length + offset), - _ => throw new ArgumentOutOfRangeException(nameof(origin), origin.ToString()), - }; + case SeekOrigin.Begin: + _offset = (uint)offset; + break; + case SeekOrigin.Current: + _offset = (uint)(_offset + offset); + break; + case SeekOrigin.End: + _offset = (uint)(_data.Length + offset); + break; + default: + throw new ArgumentOutOfRangeException(); + } } #nullable enable protected override ArraySegment? StopTokenGathering() { - if ((_firstGatheredByte + 1) <= _data.Length) + if (_firstGatheredByte <= _data.Length) { return new ArraySegment(_data, (int)_firstGatheredByte, (int)(_offset - _firstGatheredByte)); } diff --git a/Shared/MessagePack/Stream/IMessagePackReader.cs b/Shared/MessagePack/Stream/IMessagePackReader.cs index 34e2b32..efc1a02 100644 --- a/Shared/MessagePack/Stream/IMessagePackReader.cs +++ b/Shared/MessagePack/Stream/IMessagePackReader.cs @@ -49,7 +49,7 @@ public interface IMessagePackReader /// The length of the map. uint ReadMapLength(); - /// . + /// /// Skip item. /// void SkipToken(); diff --git a/Shared/MessagePack/Stream/MemoryStreamReader.cs b/Shared/MessagePack/Stream/MemoryStreamReader.cs index 360a862..c20d38a 100644 --- a/Shared/MessagePack/Stream/MemoryStreamReader.cs +++ b/Shared/MessagePack/Stream/MemoryStreamReader.cs @@ -11,15 +11,16 @@ namespace nanoFramework.MessagePack.Stream { internal sealed class MemoryStreamReader : BaseReader, IDisposable { - private readonly ArrayList _bytesGatheringBuffer = new(); + private readonly MemoryStream _stream; + private readonly ArrayList _bytesGatheringBuffer; + private long _bytesGatheringBufferLength = 0; private bool _bytesGatheringInProgress; - private readonly MemoryStream _stream; - public MemoryStreamReader(MemoryStream stream) { _stream = stream; + _bytesGatheringBuffer = new ArrayList(); } public override byte ReadByte() @@ -33,7 +34,8 @@ public override byte ReadByte() var result = (byte)temp; if (_bytesGatheringInProgress) { - _bytesGatheringBuffer.Add(result); + _bytesGatheringBuffer.Add(new byte[] { result }); + _bytesGatheringBufferLength++; } return result; @@ -45,10 +47,8 @@ public override ArraySegment ReadBytes(uint length) if (_bytesGatheringInProgress) { - foreach (var b in buffer) - { - _bytesGatheringBuffer.Add(b); - } + _bytesGatheringBuffer.Add(buffer); + _bytesGatheringBufferLength += buffer.Length; } return buffer; @@ -59,10 +59,9 @@ public override void Seek(long offset, SeekOrigin origin) if (_bytesGatheringInProgress) { var buffer = ReadBytesInternal((uint)offset); - foreach (var b in buffer) - { - _bytesGatheringBuffer.Add(b); - } + + _bytesGatheringBuffer.Add(buffer); + _bytesGatheringBufferLength += buffer.Length; } else { @@ -75,7 +74,7 @@ public void Dispose() _stream.Dispose(); } - private ArraySegment ReadBytesInternal(uint length) + private byte[] ReadBytesInternal(uint length) { var buffer = new byte[length]; var read = _stream.Read(buffer, 0, buffer.Length); @@ -84,16 +83,25 @@ private ArraySegment ReadBytesInternal(uint length) throw ExceptionUtility.NotEnoughBytes(read, buffer.Length); } - return new(buffer, 0, buffer.Length); + return buffer; } #nullable enable protected override ArraySegment? StopTokenGathering() { - if ((_stream.Position + 1) <= _stream.Length) + if (_stream.Position <= _stream.Length) { _bytesGatheringInProgress = false; - var result = _bytesGatheringBuffer.ToArray(typeof(byte[])); - return new ArraySegment((byte[])result, 0, result.Length); + + byte[] result = new byte[_bytesGatheringBufferLength]; + + int destinationOffset = 0; + foreach (byte[] part in _bytesGatheringBuffer) + { + Array.Copy(part, 0, result, destinationOffset, part.Length); + destinationOffset += part.Length; + } + + return new ArraySegment(result, 0, result.Length); } else { @@ -105,6 +113,7 @@ protected override void StartTokenGathering() { _bytesGatheringInProgress = true; _bytesGatheringBuffer.Clear(); + _bytesGatheringBufferLength = 0; } } } diff --git a/UnitTestShared/Helpers/TestsHelper.cs b/UnitTestShared/Helpers/TestsHelper.cs index 8c71b25..9e3315e 100644 --- a/UnitTestShared/Helpers/TestsHelper.cs +++ b/UnitTestShared/Helpers/TestsHelper.cs @@ -3,6 +3,7 @@ using System; using System.Collections; +using System.Text; using UnitTestShared.TestData; namespace UnitTestShared.Helpers @@ -159,5 +160,49 @@ internal static bool DictionaryEqual(this IDictionary? array1, IDictionary? arra return false; } + + internal static bool CheckTwoDimensionalLongArray(this long[][] source, long[][] destination, out string errorMessage) + { + errorMessage = string.Empty; + + if (source.Length != destination.Length) + { + errorMessage = $"Source array length {source.Length} not equal destination array length {destination.Length}"; + return false; + } + for(int y = 0; y < source.Length; y++) + { + if (source[y].Length != destination[y].Length) + { + errorMessage = $"Source array item index {y} length {source.Length} not equal destination array item index {y} length {destination.Length}"; + return false; + } + else + { + if(!source[y].ArrayEqual(destination[y])) + { + errorMessage = $"Source array item index {y} not equal destination array item index {y}\nSource array items:\n{source[y].JoinToString(", ")}\nDestination array items:\n{destination[y].JoinToString(", ")}"; + return false; + } + + } + } + + return true; + } + + internal static string JoinToString(this long[] objects, string joinString) + { + StringBuilder sb = new(); + foreach (long b in objects) + { + sb.Append(b.ToString()); + sb.Append(joinString); + } + if (sb.Length > 0) + sb.Remove(sb.Length - joinString.Length, joinString.Length); + + return sb.ToString(); + } } } diff --git a/UnitTestShared/TestData/SecureMessageConverter.cs b/UnitTestShared/TestData/SecureMessageConverter.cs index b1b2be6..6f6cc46 100644 --- a/UnitTestShared/TestData/SecureMessageConverter.cs +++ b/UnitTestShared/TestData/SecureMessageConverter.cs @@ -16,10 +16,12 @@ public SecureMessage Read([NotNull] IMessagePackReader reader) { StringBuilder sb = new(); var length = reader.ReadArrayLength(); + var intConverter = ConverterContext.GetConverter(typeof(int)); for (int i = 0; i < length; i++) { - sb.Append(SharedWordDictionary.WordDictionary[i]); + int wordIndex = (int)intConverter.Read(reader)!; + sb.Append(SharedWordDictionary.WordDictionary[wordIndex]); sb.Append(' '); } if (sb.Length > 0) @@ -32,14 +34,15 @@ public void Write(SecureMessage value, [NotNull] IMessagePackWriter writer) { var messageWords = value.Message.Split(' '); - uint length = BitConverter.ToUInt32(BitConverter.GetBytes(messageWords.Length), 0); + uint length = (uint)messageWords.Length; writer.WriteArrayHeader(length); var intConverter = ConverterContext.GetConverter(typeof(int)); foreach (var word in messageWords) { - intConverter.Write(SharedWordDictionary.WordDictionary.IndexOf(word), writer); + int wordIndex = SharedWordDictionary.WordDictionary.IndexOf(word); + intConverter.Write(wordIndex, writer); } } diff --git a/UnitTestShared/TestData/SharedWordDictionary.cs b/UnitTestShared/TestData/SharedWordDictionary.cs index 7226c4b..7555abe 100644 --- a/UnitTestShared/TestData/SharedWordDictionary.cs +++ b/UnitTestShared/TestData/SharedWordDictionary.cs @@ -11,7 +11,7 @@ static SharedWordDictionary() { WordDictionary = new() { - "MessagePak", + "MessagePack", "Hello", "at", "nanoFramework!", diff --git a/UnitTestShared/UnitTests.cs b/UnitTestShared/UnitTests.cs index c67bedf..c2dd11f 100644 --- a/UnitTestShared/UnitTests.cs +++ b/UnitTestShared/UnitTests.cs @@ -8,6 +8,7 @@ using nanoFramework.MessagePack; using UnitTestShared.Helpers; using UnitTestShared.TestData; +using System.IO; #if NANOFRAMEWORK_1_0 using nanoFramework.TestFramework; #endif @@ -110,12 +111,27 @@ public void TestConverterContext() public void ProcessCustomObjectTest() { var test = TestsHelper.GetTestClassObject(); - var result = MessagePackSerializer.Serialize(test); - Debug.WriteLine($"Serialize byte size: {result.Length}"); - var testResult = (TestClass)MessagePackSerializer.Deserialize(typeof(TestClass), result)!; - Assert.IsNotNull(testResult); + var resultBytes = MessagePackSerializer.Serialize(test); + Debug.WriteLine($"Serialize byte size: {resultBytes.Length}"); + var testResult = (TestClass)MessagePackSerializer.Deserialize(typeof(TestClass), resultBytes)!; + Assert.IsNotNull(testResult); Assert.AreEqual(test, testResult); + + using MemoryStream ms = new(resultBytes); + + var msTestResult = (TestClass)MessagePackSerializer.Deserialize(typeof(TestClass), ms)!; + + Assert.IsNotNull(msTestResult); + Assert.AreEqual(testResult, msTestResult); + + using MemoryStream writeMemory = new(); + MessagePackSerializer.Serialize(msTestResult, writeMemory); + byte[] testBytes = writeMemory.ToArray(); + + Assert.AreNotEqual(resultBytes, testBytes); + Assert.IsTrue(resultBytes.ArrayEqual(testBytes)); + } [TestMethod] @@ -140,8 +156,150 @@ public void CustomConverterTest() var recipientSecureMessage = (SecureMessage)MessagePackSerializer.Deserialize(typeof(SecureMessage), buffer)!; Debug.WriteLine($"Message received:\n{recipientSecureMessage.Message}"); + + Assert.AreNotEqual(secureMessage, recipientSecureMessage); + + Assert.AreEqual(secureMessage.Message, recipientSecureMessage.Message); } - } + [TestMethod] + public void PrimitivesTest() + { + byte[] shortBytes = MessagePackSerializer.Serialize(short.MaxValue); + var shortValue = (short)MessagePackSerializer.Deserialize(typeof(short), shortBytes)!; + Assert.AreEqual(short.MaxValue, shortValue); + + byte[] ushortBytes = MessagePackSerializer.Serialize(ushort.MaxValue); + var ushortValue = (ushort)MessagePackSerializer.Deserialize(typeof(ushort), ushortBytes)!; + Assert.AreEqual(ushort.MaxValue, ushortValue); + + byte[] intBytes = MessagePackSerializer.Serialize(int.MaxValue); + var intValue = (int)MessagePackSerializer.Deserialize(typeof(int), intBytes)!; + Assert.AreEqual(int.MaxValue, intValue); + + byte[] uintBytes = MessagePackSerializer.Serialize(uint.MaxValue); + var uintValue = (uint)MessagePackSerializer.Deserialize(typeof(uint), uintBytes)!; + Assert.AreEqual(uint.MaxValue, uintValue); + + byte[] longBytes = MessagePackSerializer.Serialize(long.MaxValue); + var longValue = (long)MessagePackSerializer.Deserialize(typeof(long), longBytes)!; + Assert.AreEqual(long.MaxValue, longValue); + + byte[] ulongBytes = MessagePackSerializer.Serialize(ulong.MaxValue); + var ulongValue = (ulong)MessagePackSerializer.Deserialize(typeof(ulong), ulongBytes)!; + Assert.AreEqual(ulong.MaxValue, ulongValue); + + byte[] byteBytes = MessagePackSerializer.Serialize(byte.MaxValue); + var byteValue = (byte)MessagePackSerializer.Deserialize(typeof(byte), byteBytes)!; + Assert.AreEqual(byte.MaxValue, byteValue); + + byte[] sbyteBytes = MessagePackSerializer.Serialize(sbyte.MaxValue); + var sbyteValue = (sbyte)MessagePackSerializer.Deserialize(typeof(sbyte), sbyteBytes)!; + Assert.AreEqual(sbyte.MaxValue, sbyteValue); + byte[] floatBytes = MessagePackSerializer.Serialize(float.MaxValue); + var floatValue = (float)MessagePackSerializer.Deserialize(typeof(float), floatBytes)!; + Assert.AreEqual(float.MaxValue, floatValue); + + byte[] doubleBytes = MessagePackSerializer.Serialize(double.MaxValue); + var doubleValue = (double)MessagePackSerializer.Deserialize(typeof(double), doubleBytes)!; + Assert.AreEqual(double.MaxValue, doubleValue); + + byte[] boolBytes = MessagePackSerializer.Serialize(true); + var boolValue = (bool)MessagePackSerializer.Deserialize(typeof(bool), boolBytes)!; + Assert.AreEqual(true, boolValue); + + boolBytes = MessagePackSerializer.Serialize(false); + boolValue = (bool)MessagePackSerializer.Deserialize(typeof(bool), boolBytes)!; + Assert.AreEqual(false, boolValue); + + byte[] charBytes = MessagePackSerializer.Serialize(char.MaxValue); + var charValue = (char)MessagePackSerializer.Deserialize(typeof(char), charBytes)!; + Assert.AreEqual(char.MaxValue, charValue); + + string testString = "I'm a UnitTest string!"; + byte[] stringBytes = MessagePackSerializer.Serialize(testString); + var stringValue = (string)MessagePackSerializer.Deserialize(typeof(string), stringBytes)!; + Assert.AreEqual(testString, stringValue); + + DateTime testDateTime = DateTime.UtcNow - TimeSpan.FromHours(1); + byte[] dateTimeBytes = MessagePackSerializer.Serialize(testDateTime); + var dateTimeValue = (DateTime)MessagePackSerializer.Deserialize(typeof(DateTime), dateTimeBytes)!; + Assert.AreEqual(testDateTime, dateTimeValue); + + byte[] timeSpanBytes = MessagePackSerializer.Serialize(TimeSpan.MaxValue); + var timeSpanValue = (TimeSpan)MessagePackSerializer.Deserialize(typeof(TimeSpan), timeSpanBytes)!; + Assert.AreEqual(TimeSpan.MaxValue, timeSpanValue); + + var testGuid = Guid.NewGuid(); + byte[] guidBytes = MessagePackSerializer.Serialize(testGuid); + var guidValue = (Guid)MessagePackSerializer.Deserialize(typeof(Guid), guidBytes)!; + Assert.AreEqual(testGuid, guidValue); + + } + + [TestMethod] + public void ArrayAndHashtableTest() + { + int[] intArray = new int[10] + { + int.MaxValue, + int.MaxValue / 2, + int.MaxValue / 3, + int.MaxValue - 100, + -236499102, + int.MinValue, + -3, + -98747483, + 239390494, + 1 + }; + + var arrayBytes = MessagePackSerializer.Serialize(intArray); + var intArrayValue = (int[])MessagePackSerializer.Deserialize(typeof(int[]), arrayBytes)!; + Assert.IsTrue(intArray.ArrayEqual(intArrayValue)); + + byte[] byteArray = new byte[10] + { + 100, + 111, + byte.MaxValue, + 126, + 45, + 20, + byte.MinValue, + 2, + 1, + 10 + }; + + arrayBytes = MessagePackSerializer.Serialize(byteArray); + var byteArrayValue = (byte[])MessagePackSerializer.Deserialize(typeof(byte[]), arrayBytes)!; + Assert.IsTrue(byteArray.ArrayEqual(byteArrayValue)); + + long[][] twoDimensionalArray = new long[][] + { + new long[]{1, 2, 3, 4, 5}, + new long[]{long.MaxValue -1, -1, -3985948353983, -8923879}, + new long[]{8734832748327, -84393275329, 87348932758, long.MinValue}, + }; + arrayBytes = MessagePackSerializer.Serialize(twoDimensionalArray); + var twoDimensionalArrayValue = (long[][])MessagePackSerializer.Deserialize(typeof(long[][]), arrayBytes)!; + Assert.IsTrue(twoDimensionalArray.CheckTwoDimensionalLongArray(twoDimensionalArrayValue, out string errorMessage), errorMessage); + + Hashtable testHashtable = new() + { + { "gender", "Male" }, + { "snow", "white" }, + { Guid.NewGuid().ToString(), "nanoFramework.MessagePack" } + }; + arrayBytes = MessagePackSerializer.Serialize(testHashtable); + var hashtableValue = (Hashtable)MessagePackSerializer.Deserialize(typeof(Hashtable), arrayBytes)!; + Assert.AreEqual(testHashtable.Count, hashtableValue.Count); + foreach(DictionaryEntry entry in testHashtable) + { + Assert.AreEqual(entry.Value, hashtableValue[entry.Key]); + } + } + } }