diff options
4 files changed, 429 insertions, 111 deletions
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/BinaryMessage.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/BinaryMessage.cs index 8eb7fd4a5c..59cb40333b 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/BinaryMessage.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/BinaryMessage.cs @@ -228,53 +228,105 @@ namespace MonoDevelop.Core.Execution void WriteArray (BinaryWriter bw, object val) { Array array = (Array)val; - bw.Write (array.Length); - - var et = val.GetType ().GetElementType (); - - if (et == typeof(byte)) { - bw.Write ((byte)TypeCode.Byte); - bw.Write ((byte [])val); - } else if (et == typeof(short)) { - bw.Write ((byte)TypeCode.Int16); - foreach (var v in (short [])val) - bw.Write (v); - } else if (et == typeof(int)) { - bw.Write ((byte)TypeCode.Int32); - foreach (var v in (int [])val) - bw.Write (v); - } else if (et == typeof(long)) { - bw.Write ((byte)TypeCode.Int64); - foreach (var v in (long [])val) - bw.Write (v); - } else if (et == typeof(float)) { - bw.Write ((byte)TypeCode.Single); - foreach (var v in (float [])val) - bw.Write (v); - } else if (et == typeof(double)) { - bw.Write ((byte)TypeCode.Double); - foreach (var v in (double [])val) - bw.Write (v); - } else if (et == typeof(string)) { - bw.Write ((byte)TypeCode.String); - foreach (var v in (string [])val) - bw.Write (v ?? NullString); - } else if (et == typeof(bool)) { - bw.Write ((byte)TypeCode.Boolean); - foreach (var v in (bool [])val) - bw.Write (v); - } else if (et == typeof(DateTime)) { - bw.Write ((byte)TypeCode.DateTime); - foreach (var v in (DateTime [])val) - bw.Write (v.Ticks); - } else if (et == typeof(TimeSpan)) { - bw.Write ((byte)TypeCode.TimeSpan); - foreach (var v in (TimeSpan [])val) - bw.Write (v.Ticks); + int rank = array.Rank; + bw.Write (array.Rank); + for (int i = 0; i < rank; ++i) + bw.Write (array.GetLength (rank)); + + var type = val.GetType (); + var et = type.GetElementType (); + + if (rank == 1) { + if (et == typeof (byte)) { + bw.Write ((byte)TypeCode.Byte); + bw.Write ((byte [])val); + } else if (et == typeof (short)) { + bw.Write ((byte)TypeCode.Int16); + foreach (var v in (short [])val) + bw.Write (v); + } else if (et == typeof (int)) { + bw.Write ((byte)TypeCode.Int32); + foreach (var v in (int [])val) + bw.Write (v); + } else if (et == typeof (long)) { + bw.Write ((byte)TypeCode.Int64); + foreach (var v in (long [])val) + bw.Write (v); + } else if (et == typeof (float)) { + bw.Write ((byte)TypeCode.Single); + foreach (var v in (float [])val) + bw.Write (v); + } else if (et == typeof (double)) { + bw.Write ((byte)TypeCode.Double); + foreach (var v in (double [])val) + bw.Write (v); + } else if (et == typeof (string)) { + bw.Write ((byte)TypeCode.String); + foreach (var v in (string [])val) + bw.Write (v ?? NullString); + } else if (et == typeof (bool)) { + bw.Write ((byte)TypeCode.Boolean); + foreach (var v in (bool [])val) + bw.Write (v); + } else if (et == typeof (DateTime)) { + bw.Write ((byte)TypeCode.DateTime); + foreach (var v in (DateTime [])val) + bw.Write (v.Ticks); + } else if (et == typeof (TimeSpan)) { + bw.Write ((byte)TypeCode.TimeSpan); + foreach (var v in (TimeSpan [])val) + bw.Write (v.Ticks); + } else { + bw.Write ((byte)TypeCode.Object); + foreach (var elem in array) + WriteValue (bw, elem); + } } else { - bw.Write ((byte)TypeCode.Object); - foreach (var elem in array) - WriteValue (bw, elem); + if (et == typeof (byte)) { + bw.Write ((byte)TypeCode.Byte); + foreach (byte v in array) + bw.Write (v); + } else if (et == typeof (short)) { + bw.Write ((byte)TypeCode.Int16); + foreach (short v in array) + bw.Write (v); + } else if (et == typeof (int)) { + bw.Write ((byte)TypeCode.Int32); + foreach (int v in array) + bw.Write (v); + } else if (et == typeof (long)) { + bw.Write ((byte)TypeCode.Int64); + foreach (long v in array) + bw.Write (v); + } else if (et == typeof (float)) { + bw.Write ((byte)TypeCode.Single); + foreach (float v in array) + bw.Write (v); + } else if (et == typeof (double)) { + bw.Write ((byte)TypeCode.Double); + foreach (double v in array) + bw.Write (v); + } else if (et == typeof (string)) { + bw.Write ((byte)TypeCode.String); + foreach (string v in array) + bw.Write (v ?? NullString); + } else if (et == typeof (bool)) { + bw.Write ((byte)TypeCode.Boolean); + foreach (bool v in array) + bw.Write (v); + } else if (et == typeof (DateTime)) { + bw.Write ((byte)TypeCode.DateTime); + foreach (DateTime v in array) + bw.Write (v.Ticks); + } else if (et == typeof (TimeSpan)) { + bw.Write ((byte)TypeCode.TimeSpan); + foreach (TimeSpan v in array) + bw.Write (v.Ticks); + } else { + bw.Write ((byte)TypeCode.Object); + foreach (var elem in array) + WriteValue (bw, elem); + } } } @@ -340,78 +392,207 @@ namespace MonoDevelop.Core.Execution throw new NotSupportedException ("code: " + t); } - static object ReadArray (BinaryReader br) + internal class MultiDimensionalIterator { - int count = br.ReadInt32 (); - var type = (TypeCode)br.ReadByte (); + Array arr; + readonly int [] indices, lengths; + bool first; - switch (type) { - case TypeCode.Object: { - var a = new object [count]; - for (int n = 0; n < count; n++) - a [n] = ReadValue (br); - return a; - } - case TypeCode.Double: { - var a = new double [count]; - for (int n = 0; n < count; n++) - a [n] = br.ReadDouble (); - return a; - } - case TypeCode.Byte: { - return br.ReadBytes (count); - } - case TypeCode.Int16: { - var a = new short [count]; - for (int n = 0; n < count; n++) - a [n] = br.ReadInt16 (); - return a; - } - case TypeCode.Int32: { - var a = new int [count]; - for (int n = 0; n < count; n++) - a [n] = br.ReadInt32 (); - return a; - } - case TypeCode.Int64: { - var a = new long [count]; - for (int n = 0; n < count; n++) - a [n] = br.ReadInt64 (); - return a; - } - case TypeCode.Single: { - var a = new float [count]; - for (int n = 0; n < count; n++) - a [n] = br.ReadSingle (); - return a; + public MultiDimensionalIterator (Array arr) + { + this.arr = arr; + lengths = new int [arr.Rank]; + indices = new int [arr.Rank]; + first = true; + + for (int i = 0; i < arr.Rank; ++i) + lengths [i] = arr.GetLength (i); + } + + public void Fill (Func<object> fill) + { + (bool, int []) res; + while ((res = TryMoveNext ()).Item1) { + arr.SetValue (fill (), res.Item2); } - case TypeCode.String: { - var a = new string [count]; - for (int n = 0; n < count; n++) { - string s = br.ReadString (); - if (s == NullString) - s = null; - a [n] = s; + } + + public (bool, int []) TryMoveNext () + { + return TryMoveNextInternal (indices.Length - 1); + } + + (bool, int []) TryMoveNextInternal (int lastRank) + { + if (lastRank == -1) + return (false, indices); + + if (lengths [lastRank] != 0) { + if (first) { + first = false; + return (true, indices); } - return a; + + indices [lastRank]++; + // We reached the last element, try bumping previous dimension + if (indices [lastRank] != lengths [lastRank]) + return (true, indices); + + // We're done. + if (lastRank == 0) + return (false, indices); + + for (int i = lastRank; i < indices.Length; ++i) + indices [i] = 0; + } - case TypeCode.Boolean: { - var a = new bool [count]; - for (int n = 0; n < count; n++) - a [n] = br.ReadBoolean (); - return a; + int newRank = lastRank - 1; + return TryMoveNextInternal (newRank); + } + } + + static object ReadArray (BinaryReader br) + { + int rank = br.ReadInt32 (); + int[] lengths = new int [rank]; + + var type = (TypeCode)br.ReadByte (); + + if (rank == 1) { + int count = lengths [0]; + switch (type) { + case TypeCode.Object: { + var a = new object [count]; + for (int n = 0; n < count; n++) + a [n] = ReadValue (br); + return a; + } + case TypeCode.Double: { + var a = new double [count]; + for (int n = 0; n < count; n++) + a [n] = br.ReadDouble (); + return a; + } + case TypeCode.Byte: { + return br.ReadBytes (count); + } + case TypeCode.Int16: { + var a = new short [count]; + for (int n = 0; n < count; n++) + a [n] = br.ReadInt16 (); + return a; + } + case TypeCode.Int32: { + var a = new int [count]; + for (int n = 0; n < count; n++) + a [n] = br.ReadInt32 (); + return a; + } + case TypeCode.Int64: { + var a = new long [count]; + for (int n = 0; n < count; n++) + a [n] = br.ReadInt64 (); + return a; + } + case TypeCode.Single: { + var a = new float [count]; + for (int n = 0; n < count; n++) + a [n] = br.ReadSingle (); + return a; + } + case TypeCode.String: { + var a = new string [count]; + for (int n = 0; n < count; n++) { + string s = br.ReadString (); + if (s == NullString) + s = null; + a [n] = s; + } + return a; + } + case TypeCode.Boolean: { + var a = new bool [count]; + for (int n = 0; n < count; n++) + a [n] = br.ReadBoolean (); + return a; + } + case TypeCode.DateTime: { + var a = new DateTime [count]; + for (int n = 0; n < count; n++) + a [n] = new DateTime (br.ReadInt64 ()); + return a; + } + case TypeCode.TimeSpan: { + var a = new TimeSpan [count]; + for (int n = 0; n < count; n++) + a [n] = new TimeSpan (br.ReadInt64 ()); + return a; + } } - case TypeCode.DateTime: { - var a = new DateTime [count]; - for (int n = 0; n < count; n++) - a [n] = new DateTime (br.ReadInt64 ()); - return a; + } else { + int [] indices = new int [lengths.Length]; + switch (type) { + case TypeCode.Object: { + var a = Array.CreateInstance (typeof (object), lengths); + new MultiDimensionalIterator (a).Fill (() => ReadValue (br)); + return a; } - case TypeCode.TimeSpan: { - var a = new TimeSpan [count]; - for (int n = 0; n < count; n++) - a [n] = new TimeSpan (br.ReadInt64 ()); - return a; + case TypeCode.Double: { + var a = Array.CreateInstance (typeof (double), lengths); + new MultiDimensionalIterator (a).Fill (() => br.ReadDouble ()); + return a; + } + case TypeCode.Byte: { + var a = Array.CreateInstance (typeof (byte), lengths); + new MultiDimensionalIterator (a).Fill (() => br.ReadByte ()); + return a; + //return br.ReadBytes (count); + } + case TypeCode.Int16: { + var a = Array.CreateInstance (typeof (short), lengths); + new MultiDimensionalIterator (a).Fill (() => br.ReadInt16 ()); + return a; + } + case TypeCode.Int32: { + var a = Array.CreateInstance (typeof (int), lengths); + new MultiDimensionalIterator (a).Fill (() => br.ReadInt32 ()); + return a; + } + case TypeCode.Int64: { + var a = Array.CreateInstance (typeof (long), lengths); + new MultiDimensionalIterator (a).Fill (() => br.ReadInt64 ()); + return a; + } + case TypeCode.Single: { + var a = Array.CreateInstance (typeof (float), lengths); + new MultiDimensionalIterator (a).Fill (() => br.ReadSingle ()); + return a; + } + case TypeCode.String: { + var a = Array.CreateInstance (typeof (string), lengths); + new MultiDimensionalIterator (a).Fill (() => { + string s = br.ReadString (); + if (s == NullString) + s = null; + return s; + }); + return a; + } + case TypeCode.Boolean: { + var a = Array.CreateInstance (typeof (bool), lengths); + new MultiDimensionalIterator (a).Fill (() => br.ReadBoolean ()); + return a; + } + case TypeCode.DateTime: { + var a = Array.CreateInstance (typeof (DateTime), lengths); + new MultiDimensionalIterator (a).Fill (() => new DateTime (br.ReadInt64 ())); + return a; + } + case TypeCode.TimeSpan: { + var a = Array.CreateInstance (typeof (TimeSpan), lengths); + new MultiDimensionalIterator (a).Fill (() => new TimeSpan (br.ReadInt64 ())); + return a; + } } } throw new NotSupportedException ("Array of " + type); diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Execution/BinaryMessageTests.cs b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Execution/BinaryMessageTests.cs new file mode 100644 index 0000000000..16a02315ad --- /dev/null +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Execution/BinaryMessageTests.cs @@ -0,0 +1,125 @@ +// +// BinaryMessage.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2017 +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using NUnit.Framework; + +namespace MonoDevelop.Core.Execution +{ + [TestFixture] + public class BinaryMessageTests + { + public class Data + { + public Data (Array arr, int expectedEnumerations, int[][] expectedValues) + { + ExpectedEnumerations = expectedEnumerations; + Array = arr; + } + + public int ExpectedEnumerations { get; } + public Array Array { get; } + } + + public Data [] TestCase = { + new Data (new int[1, 2, 3], 6, new int[][] { + new int[] {0, 0, 0}, + new int[] {0, 0, 1}, + new int[] {0, 0, 2}, + new int[] {0, 1, 0}, + new int[] {0, 1, 1}, + new int[] {0, 1, 2}, + }), + new Data (new int[0, 0, 0], 0, new int[][] {}), + new Data (new int[0, 0, 1], 1, new int[][] { new int[] { 0, 0, 0 } }), + new Data (new int[3, 3, 3], 27, new int[][] { + new int[] {0, 0, 0}, + new int[] {0, 0, 1}, + new int[] {0, 0, 2}, + + new int[] {0, 1, 0}, + new int[] {0, 1, 1}, + new int[] {0, 1, 2}, + + new int[] {0, 2, 0}, + new int[] {0, 2, 1}, + new int[] {0, 2, 2}, + + new int[] {1, 0, 0}, + new int[] {1, 0, 1}, + new int[] {1, 0, 2}, + + new int[] {1, 1, 0}, + new int[] {1, 1, 1}, + new int[] {1, 1, 2}, + + new int[] {1, 2, 0}, + new int[] {1, 2, 1}, + new int[] {1, 2, 2}, + + new int[] {2, 0, 0}, + new int[] {2, 0, 1}, + new int[] {2, 0, 2}, + + new int[] {2, 1, 0}, + new int[] {2, 1, 1}, + new int[] {2, 1, 2}, + + new int[] {2, 2, 0}, + new int[] {2, 2, 1}, + new int[] {2, 2, 2}, + }), + }; + + [TestCaseSource ("TestCase")] + public void TestIteration (Data data) + { + var iter = new BinaryMessage.MultiDimensionalIterator (data.Array); + int count = 0; + (bool, int []) res; + while ((res = iter.TryMoveNext ()).Item1) { + count++; + } + + Assert.AreEqual (data.ExpectedEnumerations, count); + } + + [Test] + public void TestFill () + { + int count = 0; + var toFill = new int [3, 3, 3]; + + var iter = new BinaryMessage.MultiDimensionalIterator (toFill); + iter.Fill (() => count++); + + int expected = 0; + foreach (var val in toFill) { + Assert.AreEqual (expected, val); + expected++; + } + } + } +} diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj index 8cbe6811d0..97681c5e92 100644 --- a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj @@ -37,6 +37,9 @@ <HintPath>..\..\packages\Microsoft.CodeAnalysis.CSharp.2.3.2\lib\netstandard1.3\Microsoft.CodeAnalysis.CSharp.dll</HintPath> <Private>False</Private> </Reference> + <Reference Include="System.ValueTuple"> + <HintPath>..\..\packages\System.ValueTuple.4.3.1\lib\netstandard1.0\System.ValueTuple.dll</HintPath> + </Reference> </ItemGroup> <ItemGroup> <Compile Include="Test.cs" /> @@ -90,11 +93,13 @@ <Compile Include="MonoDevelop.Projects\GenericProjectTests.cs" /> <Compile Include="MonoDevelop.Projects\NetStandardProjectTests.cs" /> <Compile Include="MonoDevelop.Projects\BuilderManagerTests.cs" /> + <Compile Include="MonoDevelop.Core.Execution\BinaryMessageTests.cs" /> </ItemGroup> <ItemGroup> <Folder Include="MonoDevelop.Core\" /> <Folder Include="MonoDevelop.Core.Assemblies\" /> <Folder Include="MonoDevelop.Projects\" /> + <Folder Include="MonoDevelop.Core.Execution\" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\src\core\MonoDevelop.Core\MonoDevelop.Core.csproj"> @@ -116,5 +121,8 @@ <Name>GuiUnit_NET_4_5</Name> </ProjectReference> </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>
\ No newline at end of file diff --git a/main/tests/MonoDevelop.Core.Tests/packages.config b/main/tests/MonoDevelop.Core.Tests/packages.config new file mode 100644 index 0000000000..9958f16fbb --- /dev/null +++ b/main/tests/MonoDevelop.Core.Tests/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="System.ValueTuple" version="4.3.1" targetFramework="net461" /> +</packages>
\ No newline at end of file |