From 58c1ae0a396ffdef2036af76f695f1b83a51f249 Mon Sep 17 00:00:00 2001 From: Matt Warren Date: Wed, 19 Oct 2016 20:19:09 +0000 Subject: [PATCH] non recursive serialization --- .../Errors/XmlSyntaxDiagnosticInfo.cs | 4 +- .../Portable/Syntax/CSharpSyntaxNode.cs | 2 +- .../ObjectSerializationTests.cs | 892 +++++++++++------- .../Core/Portable/CodeAnalysis.csproj | 6 +- .../Portable/Diagnostic/DiagnosticInfo.cs | 8 +- .../Diagnostic/LocalizableResourceString.cs | 4 +- .../Core/Portable/Serialization/DataKind.cs | 81 ++ .../Portable/Serialization/ObjectReader.cs | 624 +----------- .../Serialization/ObjectReaderWriterBase.cs | 82 -- .../Portable/Serialization/ObjectWriter.cs | 707 +------------- .../Serialization/ObjectWriterData.cs | 9 +- .../Serialization/StreamObjectReader.cs | 771 +++++++++++++++ .../Serialization/StreamObjectWriter.cs | 886 +++++++++++++++++ .../Core/Portable/Serialization/Variant.cs | 417 ++++++++ .../Portable/Serialization/VariantKind.cs | 31 + .../Core/Portable/Syntax/SyntaxNode.cs | 2 +- .../Portable/Syntax/VisualBasicSyntaxNode.vb | 2 +- .../TodoComment/TodoCommentState.cs | 4 +- .../Test/Utilities/BloomFilterTests.cs | 4 +- .../DesignerAttributeState.cs | 4 +- .../VisualStudioDiagnosticAnalyzerExecutor.cs | 2 +- .../SemanticVersionTrackingService.cs | 4 +- .../Core/Next/Remote/JsonRpcSession.cs | 2 +- .../EsentPersistentStorage_IdentifierTable.cs | 8 +- .../Diagnostics/DiagnosticDataSerializer.cs | 4 +- .../AbstractReferenceSerializationService.cs | 4 +- .../Core/Portable/Execution/CustomAsset.cs | 2 +- .../SymbolTreeInfo_Serialization.cs | 4 +- .../SyntaxTree/AbstractPersistableState.cs | 6 +- .../Workspace/Solution/Checksum_Factory.cs | 6 +- .../Core/Portable/Workspaces.csproj | 18 +- .../CoreTest/Execution/Extensions.cs | 4 +- .../Execution/SnapshotSerializationTests.cs | 8 +- .../CoreTest/FindAllDeclarationsTests.cs | 4 +- src/Workspaces/CoreTest/SerializationTests.cs | 4 +- .../CodeAnalysisService_Diagnostics.cs | 2 +- .../SnapshotService.JsonRpcAssetSource.cs | 2 +- 37 files changed, 2831 insertions(+), 1793 deletions(-) create mode 100644 src/Compilers/Core/Portable/Serialization/DataKind.cs delete mode 100644 src/Compilers/Core/Portable/Serialization/ObjectReaderWriterBase.cs create mode 100644 src/Compilers/Core/Portable/Serialization/StreamObjectReader.cs create mode 100644 src/Compilers/Core/Portable/Serialization/StreamObjectWriter.cs create mode 100644 src/Compilers/Core/Portable/Serialization/Variant.cs create mode 100644 src/Compilers/Core/Portable/Serialization/VariantKind.cs diff --git a/src/Compilers/CSharp/Portable/Errors/XmlSyntaxDiagnosticInfo.cs b/src/Compilers/CSharp/Portable/Errors/XmlSyntaxDiagnosticInfo.cs index e278bdec10f..80a859b0220 100644 --- a/src/Compilers/CSharp/Portable/Errors/XmlSyntaxDiagnosticInfo.cs +++ b/src/Compilers/CSharp/Portable/Errors/XmlSyntaxDiagnosticInfo.cs @@ -26,7 +26,7 @@ internal XmlSyntaxDiagnosticInfo(int offset, int width, XmlParseErrorCode code, protected override void WriteTo(ObjectWriter writer) { base.WriteTo(writer); - writer.WriteCompressedUInt((uint)_xmlErrorCode); + writer.WriteUInt32((uint)_xmlErrorCode); } protected override Func GetReader() @@ -37,7 +37,7 @@ protected override void WriteTo(ObjectWriter writer) private XmlSyntaxDiagnosticInfo(ObjectReader reader) : base(reader) { - _xmlErrorCode = (XmlParseErrorCode)reader.ReadCompressedUInt(); + _xmlErrorCode = (XmlParseErrorCode)reader.ReadUInt32(); } #endregion diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs index 8f0d8b69d07..3a57e4919be 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs @@ -204,7 +204,7 @@ public static SyntaxNode DeserializeFrom(Stream stream, CancellationToken cancel throw new InvalidOperationException(CSharpResources.TheStreamCannotBeReadFrom); } - using (var reader = new ObjectReader(stream, defaultData: GetDefaultObjectReaderData(), binder: s_defaultBinder)) + using (var reader = new StreamObjectReader(stream, defaultData: GetDefaultObjectReaderData(), binder: s_defaultBinder)) { var root = (Syntax.InternalSyntax.CSharpSyntaxNode)reader.ReadValue(); return root.CreateRed(); diff --git a/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs b/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs index 54f93a272f5..93c8fba9370 100644 --- a/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs @@ -8,13 +8,347 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using System.Collections; namespace Microsoft.CodeAnalysis.UnitTests { public sealed class ObjectSerializationTests { + private void TestRoundTrip(Action writeAction, Action readAction) + { + var stream = new MemoryStream(); + var writer = new StreamObjectWriter(stream); + + writeAction(writer); + writer.Dispose(); + + stream.Position = 0; + using (var reader = new StreamObjectReader(stream, binder: writer.Binder)) + { + readAction(reader); + } + } + + private T RoundTrip(T value, Action writeAction, Func readAction) + { + var stream = new MemoryStream(); + var writer = new StreamObjectWriter(stream); + + writeAction(writer, value); + writer.Dispose(); + + stream.Position = 0; + using (var reader = new StreamObjectReader(stream, binder: writer.Binder)) + { + return (T)readAction(reader); + } + } + + private void TestRoundTrip(T value, Action writeAction, Func readAction) + { + var newValue = RoundTrip(value, writeAction, readAction); + Assert.True(Equalish(value, newValue)); + } + + private T RoundTripValue(T value) + { + return RoundTrip(value, (w, v) => w.WriteValue(v), r => (T)r.ReadValue()); + } + + private void TestRoundTripValue(T value) + { + var newValue = RoundTripValue(value); + Assert.True(Equalish(value, newValue)); + } + + private static bool Equalish(T value1, T value2) + { + return object.Equals(value1, value2) + || (value1 is Array && value2 is Array && ArrayEquals((Array)(object)value1, (Array)(object)value2)); + } + + private static bool ArrayEquals(Array seq1, Array seq2) + { + if (seq1 == null && seq2 == null) + { + return true; + } + else if (seq1 == null || seq2 == null) + { + return false; + } + + if (seq1.Length != seq2.Length) + { + return false; + } + + for (int i = 0; i < seq1.Length; i++) + { + if (!Equalish(seq1.GetValue(i), seq2.GetValue(i))) + { + return false; + } + } + + return true; + } + + private class TypeWithOneMember : IObjectWritable, IObjectReadable, IEquatable> + { + private T _member; + + public TypeWithOneMember(T value) + { + _member = value; + } + + private TypeWithOneMember(ObjectReader reader) + { + _member = (T)reader.ReadValue(); + } + + void IObjectWritable.WriteTo(ObjectWriter writer) + { + writer.WriteValue(_member); + } + + Func IObjectReadable.GetReader() => (r) => new TypeWithOneMember(r); + + public override Int32 GetHashCode() + { + if (_member == null) + { + return 0; + } + else + { + return _member.GetHashCode(); + } + } + + public override Boolean Equals(Object obj) + { + return Equals(obj as TypeWithOneMember); + } + + public bool Equals(TypeWithOneMember other) + { + return other != null && Equalish(_member, other._member); + } + } + + private class TypeWithTwoMembers : IObjectWritable, IObjectReadable, IEquatable> + { + private T _member1; + private S _member2; + + public TypeWithTwoMembers(T value1, S value2) + { + _member1 = value1; + _member2 = value2; + } + + private TypeWithTwoMembers(ObjectReader reader) + { + _member1 = (T)reader.ReadValue(); + _member2 = (S)reader.ReadValue(); + } + + void IObjectWritable.WriteTo(ObjectWriter writer) + { + writer.WriteValue(_member1); + writer.WriteValue(_member2); + } + + Func IObjectReadable.GetReader() => (r) => new TypeWithTwoMembers(r); + + public override int GetHashCode() + { + if (_member1 == null) + { + return 0; + } + else + { + return _member1.GetHashCode(); + } + } + + public override Boolean Equals(Object obj) + { + return Equals(obj as TypeWithTwoMembers); + } + + public bool Equals(TypeWithTwoMembers other) + { + return other != null + && Equalish(_member1, other._member1) + && Equalish(_member2, other._member2); + } + } + + // this type simulates a class with many members.. + // it serializes each member individually, not as an array. + private class TypeWithManyMembers : IObjectWritable, IObjectReadable, IEquatable> + { + private T[] _members; + + public TypeWithManyMembers(T[] values) + { + _members = values; + } + + private TypeWithManyMembers(ObjectReader reader) + { + var count = reader.ReadInt32(); + _members = new T[count]; + + for (int i = 0; i < count; i++) + { + _members[i] = (T)reader.ReadValue(); + } + } + + void IObjectWritable.WriteTo(ObjectWriter writer) + { + writer.WriteInt32(_members.Length); + + for (int i = 0; i < _members.Length; i++) + { + writer.WriteValue(_members[i]); + } + } + + Func IObjectReadable.GetReader() => (r) => new TypeWithManyMembers(r); + + public override int GetHashCode() + { + return _members.Length; + } + + public override Boolean Equals(Object obj) + { + return Equals(obj as TypeWithManyMembers); + } + + public bool Equals(TypeWithManyMembers other) + { + if (other == null) + { + return false; + } + + if (_members.Length != other._members.Length) + { + return false; + } + + return Equalish(_members, other._members); + } + } + + private void TestRoundTripMember(T value) + { + TestRoundTripValue(new TypeWithOneMember(value)); + } + + private void TestRoundTripMembers(T value1, S value2) + { + TestRoundTripValue(new TypeWithTwoMembers(value1, value2)); + } + + private void TestRoundTripMembers(params T[] values) + { + TestRoundTripValue(new TypeWithManyMembers(values)); + } + [Fact] - public void TestRoundTripPrimitiveArray() + public void TestValueInt32() + { + TestRoundTripValue(123); + } + + [Fact] + public void TestMemberInt32() + { + TestRoundTripMember(123); + } + + [Fact] + public void TestMemberIntString() + { + TestRoundTripMembers(123, "Hello"); + } + + [Fact] + public void TestManyMembersInt32() + { + TestRoundTripMembers(Enumerable.Range(0, 1000).ToArray()); + } + + [Fact] + public void TestSmallArrayMember() + { + TestRoundTripMember(Enumerable.Range(0, 3).ToArray()); + } + + [Fact] + public void TestEmptyArrayMember() + { + TestRoundTripMember(new int[] { }); + } + + [Fact] + public void TestNullArrayMember() + { + TestRoundTripMember(null); + } + + [Fact] + public void TestLargeArrayMember() + { + TestRoundTripMember(Enumerable.Range(0, 1000).ToArray()); + } + + [Fact] + public void TestEnumMember() + { + TestRoundTripMember(EByte.Value); + } + + [Fact] + public void TestPrimitiveArrayValues() + { + TestRoundTrip(w => TestWritingPrimitiveArrays(w), r => TestReadingPrimitiveArrays(r)); + } + + [Fact] + public void TestPrimitiveArrayMembers() + { + TestRoundTrip(w => w.WriteValue(new PrimitiveArrayMemberTest()), r => r.ReadValue()); + } + + public class PrimitiveArrayMemberTest : IObjectWritable, IObjectReadable + { + public PrimitiveArrayMemberTest() + { + } + + private PrimitiveArrayMemberTest(ObjectReader reader) + { + TestReadingPrimitiveArrays(reader); + } + + void IObjectWritable.WriteTo(ObjectWriter writer) + { + TestWritingPrimitiveArrays(writer); + } + + Func IObjectReadable.GetReader() => (r) => new PrimitiveArrayMemberTest(r); + } + + private static void TestWritingPrimitiveArrays(ObjectWriter writer) { var inputBool = new bool[] { true, false }; var inputByte = new byte[] { 1, 2, 3, 4, 5 }; @@ -31,9 +365,6 @@ public void TestRoundTripPrimitiveArray() var inputUShort = new ushort[] { 1, 2, 3, 4, 5 }; var inputString = new string[] { "h", "e", "l", "l", "o" }; - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); - writer.WriteValue(inputBool); writer.WriteValue(inputByte); writer.WriteValue(inputChar); @@ -48,11 +379,25 @@ public void TestRoundTripPrimitiveArray() writer.WriteValue(inputULong); writer.WriteValue(inputUShort); writer.WriteValue(inputString); + } - writer.Dispose(); + private static void TestReadingPrimitiveArrays(ObjectReader reader) + { + var inputBool = new bool[] { true, false }; + var inputByte = new byte[] { 1, 2, 3, 4, 5 }; + var inputChar = new char[] { 'h', 'e', 'l', 'l', 'o' }; + var inputDecimal = new decimal[] { 1.0M, 2.0M, 3.0M, 4.0M, 5.0M }; + var inputDouble = new double[] { 1.0, 2.0, 3.0, 4.0, 5.0 }; + var inputFloat = new float[] { 1.0F, 2.0F, 3.0F, 4.0F, 5.0F }; + var inputInt = new int[] { -1, -2, -3, -4, -5 }; + var inputLong = new long[] { 1, 2, 3, 4, 5 }; + var inputSByte = new sbyte[] { -1, -2, -3, -4, -5 }; + var inputShort = new short[] { -1, -2, -3, -4, -5 }; + var inputUInt = new uint[] { 1, 2, 3, 4, 5 }; + var inputULong = new ulong[] { 1, 2, 3, 4, 5 }; + var inputUShort = new ushort[] { 1, 2, 3, 4, 5 }; + var inputString = new string[] { "h", "e", "l", "l", "o" }; - stream.Position = 0; - var reader = new ObjectReader(stream); Assert.True(Enumerable.SequenceEqual(inputBool, (bool[])reader.ReadValue())); Assert.True(Enumerable.SequenceEqual(inputByte, (byte[])reader.ReadValue())); Assert.True(Enumerable.SequenceEqual(inputChar, (char[])reader.ReadValue())); @@ -67,12 +412,10 @@ public void TestRoundTripPrimitiveArray() Assert.True(Enumerable.SequenceEqual(inputULong, (ulong[])reader.ReadValue())); Assert.True(Enumerable.SequenceEqual(inputUShort, (ushort[])reader.ReadValue())); Assert.True(Enumerable.SequenceEqual(inputString, (string[])reader.ReadValue())); - - reader.Dispose(); } [Fact] - public void TestRoundTripBooleanArray() + public void TestBooleanArrays() { for (var i = 0; i < 1000; i++) { @@ -83,45 +426,128 @@ public void TestRoundTripBooleanArray() inputBool[j] = j % 2 == 0; } - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); - - writer.WriteValue(inputBool); + TestRoundTripValue(inputBool); + TestRoundTripMember(inputBool); + } + } - writer.Dispose(); + [Fact] + public void TestFalseBooleanArray() + { + var inputBool = Enumerable.Repeat(false, 1000).ToArray(); + TestRoundTripValue(inputBool); + TestRoundTripMember(inputBool); + } - stream.Position = 0; - var reader = new ObjectReader(stream); - Assert.True(Enumerable.SequenceEqual(inputBool, (bool[])reader.ReadValue())); + [Fact] + public void TestPrimitiveValues() + { + TestRoundTripValue(true); + TestRoundTripValue(false); + TestRoundTripValue(Byte.MaxValue); + TestRoundTripValue(SByte.MaxValue); + TestRoundTripValue(Int16.MaxValue); + TestRoundTripValue(Int32.MaxValue); + TestRoundTripValue(Byte.MaxValue); + TestRoundTripValue(Int16.MaxValue); + TestRoundTripValue(Int64.MaxValue); + TestRoundTripValue(UInt16.MaxValue); + TestRoundTripValue(UInt32.MaxValue); + TestRoundTripValue(UInt64.MaxValue); + TestRoundTripValue(Decimal.MaxValue); + TestRoundTripValue(Double.MaxValue); + TestRoundTripValue(Single.MaxValue); + TestRoundTripValue('X'); + TestRoundTripValue("YYY"); + TestRoundTripValue("\uD800\uDC00"); // valid surrogate pair + TestRoundTripValue("\uDC00\uD800"); // invalid surrogate pair + TestRoundTripValue("\uD800"); // incomplete surrogate pair + TestRoundTripValue(DateTime.Now); + TestRoundTripValue(null); + TestRoundTripValue(ConsoleColor.Cyan); + TestRoundTripValue(EByte.Value); + TestRoundTripValue(ESByte.Value); + TestRoundTripValue(EShort.Value); + TestRoundTripValue(EUShort.Value); + TestRoundTripValue(EInt.Value); + TestRoundTripValue(EUInt.Value); + TestRoundTripValue(ELong.Value); + TestRoundTripValue(EULong.Value); + TestRoundTripValue(typeof(object)); + } - reader.Dispose(); - } + [Fact] + public void TestPrimitiveMemberValues() + { + TestRoundTripMember(true); + TestRoundTripMember(false); + TestRoundTripMember(Byte.MaxValue); + TestRoundTripMember(SByte.MaxValue); + TestRoundTripMember(Int16.MaxValue); + TestRoundTripMember(Int32.MaxValue); + TestRoundTripMember(Byte.MaxValue); + TestRoundTripMember(Int16.MaxValue); + TestRoundTripMember(Int64.MaxValue); + TestRoundTripMember(UInt16.MaxValue); + TestRoundTripMember(UInt32.MaxValue); + TestRoundTripMember(UInt64.MaxValue); + TestRoundTripMember(Decimal.MaxValue); + TestRoundTripMember(Double.MaxValue); + TestRoundTripMember(Single.MaxValue); + TestRoundTripMember('X'); + TestRoundTripMember("YYY"); + TestRoundTripMember("\uD800\uDC00"); // valid surrogate pair + TestRoundTripMember("\uDC00\uD800"); // invalid surrogate pair + TestRoundTripMember("\uD800"); // incomplete surrogate pair + TestRoundTripMember(DateTime.Now); + TestRoundTripMember(null); + TestRoundTripMember(ConsoleColor.Cyan); + TestRoundTripMember(EByte.Value); + TestRoundTripMember(ESByte.Value); + TestRoundTripMember(EShort.Value); + TestRoundTripMember(EUShort.Value); + TestRoundTripMember(EInt.Value); + TestRoundTripMember(EUInt.Value); + TestRoundTripMember(ELong.Value); + TestRoundTripMember(EULong.Value); + TestRoundTripMember(typeof(object)); } [Fact] - public void TestRoundTripFalseBooleanArray() + public void TestPrimitiveAPIs() { - var inputBool = Enumerable.Repeat(false, 1000).ToArray(); + TestRoundTrip(w => TestWritingPrimitiveAPIs(w), r => TestReadingPrimitiveAPIs(r)); + } - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); + [Fact] + public void TestPrimitiveMemberAPIs() + { + TestRoundTrip(w => w.WriteValue(new PrimitiveMemberTest()), r => r.ReadValue()); + } - writer.WriteValue(inputBool); + public class PrimitiveMemberTest : IObjectWritable, IObjectReadable + { + public PrimitiveMemberTest() + { + } - writer.Dispose(); + private PrimitiveMemberTest(ObjectReader reader) + { + TestReadingPrimitiveAPIs(reader); + } - stream.Position = 0; - var reader = new ObjectReader(stream); - Assert.True(Enumerable.SequenceEqual(inputBool, (bool[])reader.ReadValue())); + void IObjectWritable.WriteTo(ObjectWriter writer) + { + TestWritingPrimitiveAPIs(writer); + } - reader.Dispose(); + Func IObjectReadable.GetReader() => (r) => new PrimitiveMemberTest(r); } - [Fact] - public void TestRoundTripPrimitives() + private static readonly DateTime _testNow = DateTime.Now; + + private static void TestWritingPrimitiveAPIs(ObjectWriter writer) { - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); writer.WriteBoolean(true); writer.WriteBoolean(false); writer.WriteByte(Byte.MaxValue); @@ -142,15 +568,11 @@ public void TestRoundTripPrimitives() writer.WriteString("\uD800\uDC00"); // valid surrogate pair writer.WriteString("\uDC00\uD800"); // invalid surrogate pair writer.WriteString("\uD800"); // incomplete surrogate pair - writer.WriteCompressedUInt(Byte.MaxValue >> 2); // 6 bits - writer.WriteCompressedUInt(UInt16.MaxValue >> 2); // 14 bits - writer.WriteCompressedUInt(UInt32.MaxValue >> 2); // 30 bits - var dt = DateTime.Now; - writer.WriteDateTime(dt); - writer.Dispose(); + writer.WriteDateTime(_testNow); + } - stream.Position = 0; - var reader = new ObjectReader(stream); + private static void TestReadingPrimitiveAPIs(ObjectReader reader) + { Assert.Equal(true, reader.ReadBoolean()); Assert.Equal(false, reader.ReadBoolean()); Assert.Equal(Byte.MaxValue, reader.ReadByte()); @@ -171,18 +593,42 @@ public void TestRoundTripPrimitives() Assert.Equal("\uD800\uDC00", reader.ReadString()); // valid surrogate pair Assert.Equal("\uDC00\uD800", reader.ReadString()); // invalid surrogate pair Assert.Equal("\uD800", reader.ReadString()); // incomplete surrogate pair - Assert.Equal((UInt32)(Byte.MaxValue >> 2), reader.ReadCompressedUInt()); - Assert.Equal((UInt32)(UInt16.MaxValue >> 2), reader.ReadCompressedUInt()); - Assert.Equal(UInt32.MaxValue >> 2, reader.ReadCompressedUInt()); - Assert.Equal(dt, reader.ReadDateTime()); - reader.Dispose(); + Assert.Equal(_testNow, reader.ReadDateTime()); } [Fact] - public void TestRoundTripPrimitivesAsValues() + public void TestPrimitivesValue() + { + TestRoundTrip(w => TestWritingPrimitiveValues(w), r => TestReadingPrimitiveValues(r)); + } + + [Fact] + public void TestPrimitiveValueAPIs() + { + TestRoundTrip(w => w.WriteValue(new PrimitiveValueTest()), r => r.ReadValue()); + } + + public class PrimitiveValueTest : IObjectWritable, IObjectReadable + { + public PrimitiveValueTest() + { + } + + private PrimitiveValueTest(ObjectReader reader) + { + TestReadingPrimitiveValues(reader); + } + + void IObjectWritable.WriteTo(ObjectWriter writer) + { + TestWritingPrimitiveValues(writer); + } + + Func IObjectReadable.GetReader() => (r) => new PrimitiveValueTest(r); + } + + private static void TestWritingPrimitiveValues(ObjectWriter writer) { - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); writer.WriteValue(true); writer.WriteValue(false); writer.WriteValue(Byte.MaxValue); @@ -200,9 +646,9 @@ public void TestRoundTripPrimitivesAsValues() writer.WriteValue(Single.MaxValue); writer.WriteValue('X'); writer.WriteValue("YYY"); - writer.WriteString("\uD800\uDC00"); // valid surrogate pair - writer.WriteString("\uDC00\uD800"); // invalid surrogate pair - writer.WriteString("\uD800"); // incomplete surrogate pair + writer.WriteValue("\uD800\uDC00"); // valid surrogate pair + writer.WriteValue("\uDC00\uD800"); // invalid surrogate pair + writer.WriteValue("\uD800"); // incomplete surrogate pair writer.WriteValue(null); writer.WriteValue(ConsoleColor.Cyan); writer.WriteValue(EByte.Value); @@ -214,12 +660,11 @@ public void TestRoundTripPrimitivesAsValues() writer.WriteValue(ELong.Value); writer.WriteValue(EULong.Value); writer.WriteValue(typeof(object)); - var date = DateTime.Now; - writer.WriteValue(date); - writer.Dispose(); + writer.WriteValue(_testNow); + } - stream.Position = 0; - var reader = new ObjectReader(stream, binder: writer.Binder); + private static void TestReadingPrimitiveValues(ObjectReader reader) + { Assert.Equal(true, (bool)reader.ReadValue()); Assert.Equal(false, (bool)reader.ReadValue()); Assert.Equal(Byte.MaxValue, (Byte)reader.ReadValue()); @@ -251,8 +696,7 @@ public void TestRoundTripPrimitivesAsValues() Assert.Equal(ELong.Value, reader.ReadValue()); Assert.Equal(EULong.Value, reader.ReadValue()); Assert.Equal(typeof(object), (Type)reader.ReadValue()); - Assert.Equal(date, (DateTime)reader.ReadValue()); - reader.Dispose(); + Assert.Equal(_testNow, (DateTime)reader.ReadValue()); } public enum EByte : byte @@ -301,23 +745,13 @@ public void TestRoundTripCharacters() // round trip all possible characters as a string for (int i = ushort.MinValue; i <= ushort.MaxValue; i++) { - RoundTripCharacter((char)i); + TestRoundTripChar((char)i); } } - private void RoundTripCharacter(Char ch) + private void TestRoundTripChar(Char ch) { - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); - writer.WriteChar(ch); - writer.Dispose(); - - stream.Position = 0; - var reader = new ObjectReader(stream); - var readch = reader.ReadChar(); - reader.Dispose(); - - Assert.Equal(ch, readch); + TestRoundTrip(ch, (w, v) => w.WriteChar(v), r => r.ReadChar()); } [Fact] @@ -326,7 +760,7 @@ public void TestRoundTripStringCharacters() // round trip all possible characters as a string for (int i = ushort.MinValue; i <= ushort.MaxValue; i++) { - RoundTripStringCharacter((ushort)i); + TestRoundTripStringCharacter((ushort)i); } // round trip single string with all possible characters @@ -336,27 +770,17 @@ public void TestRoundTripStringCharacters() sb.Append((char)i); } - RoundTripString(sb.ToString()); + TestRoundTripString(sb.ToString()); } - private void RoundTripString(string text) + private void TestRoundTripString(string text) { - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); - writer.WriteString(text); - writer.Dispose(); - - stream.Position = 0; - var reader = new ObjectReader(stream); - var readText = reader.ReadString(); - reader.Dispose(); - - Assert.Equal(text, readText); + TestRoundTrip(text, (w, v) => w.WriteString(v), r => r.ReadString()); } - private void RoundTripStringCharacter(ushort code) + private void TestRoundTripStringCharacter(ushort code) { - RoundTripString(new String((char)code, 1)); + TestRoundTripString(new String((char)code, 1)); } [Fact] @@ -373,37 +797,7 @@ public void TestRoundTripArrays() private void TestRoundTripArray(T[] values) { - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); - writer.WriteValue(values); - writer.Dispose(); - - stream.Position = 0; - var reader = new ObjectReader(stream, binder: writer.Binder); - var readValues = (T[])reader.ReadValue(); - reader.Dispose(); - - Assert.Equal(true, values.SequenceEqual(readValues)); - } - - [Fact] - public void TestRoundTripWritableObject() - { - var instance = new WritableClass(123, "456"); - - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); - writer.WriteValue(instance); - writer.Dispose(); - - stream.Position = 0; - var reader = new ObjectReader(stream, binder: writer.Binder); - var instance2 = (WritableClass)reader.ReadValue(); - reader.Dispose(); - - Assert.NotNull(instance2); - Assert.Equal(instance.X, instance2.X); - Assert.Equal(instance.Y, instance2.Y); + TestRoundTripValue(values); } [Fact] @@ -411,15 +805,15 @@ public void TestObjectMapLimits() { using (var stream = new MemoryStream()) { - var instances = new List(); + var instances = new List>(); // We need enough items to exercise all sizes of ObjectRef for (int i = 0; i < ushort.MaxValue + 1; i++) { - instances.Add(new WritableClass(i, i.ToString())); + instances.Add(new TypeWithTwoMembers(i, i.ToString())); } - var writer = new ObjectWriter(stream); + var writer = new StreamObjectWriter(stream); // Write each instance twice. The second time around, they'll become ObjectRefs for (int pass = 0; pass < 2; pass++) { @@ -433,253 +827,32 @@ public void TestObjectMapLimits() writer.Dispose(); stream.Position = 0; - using (var reader = new ObjectReader(stream, binder: binder)) + using (var reader = new StreamObjectReader(stream, binder: binder)) { for (int pass = 0; pass < 2; pass++) { foreach (var instance in instances) { - var obj = (WritableClass)reader.ReadValue(); + var obj = reader.ReadValue(); Assert.NotNull(obj); - Assert.Equal(obj.X, instance.X); - Assert.Equal(obj.Y, instance.Y); + Assert.True(Equalish(obj, instance)); } } } } } - private class WritableClass : IObjectWritable, IObjectReadable - { - internal readonly int X; - internal readonly string Y; - - public WritableClass(int x, string y) - { - this.X = x; - this.Y = y; - } - - private WritableClass(ObjectReader reader) - { - this.X = reader.ReadInt32(); - this.Y = reader.ReadString(); - } - - public void WriteTo(ObjectWriter writer) - { - writer.WriteInt32(this.X); - writer.WriteString(this.Y); - } - - Func IObjectReadable.GetReader() - { - return (r) => new WritableClass(r); - } - } - -#if false - [Fact] - public void TestRoundTripSerializableObject() - { - var instance = new SerializableClass(123, "456"); - - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); - writer.WriteValue(instance); - writer.Dispose(); - - stream.Position = 0; - var reader = new ObjectReader(stream); - var instance2 = (SerializableClass)reader.ReadValue(); - reader.Dispose(); - - Assert.NotNull(instance2); - Assert.Equal(instance.X, instance2.X); - Assert.Equal(instance.Y, instance2.Y); - } - - private class SerializableClass : ISerializable - { - internal readonly int X; - internal readonly string Y; - - public SerializableClass(int x, string y) - { - this.X = x; - this.Y = y; - } - - private SerializableClass(SerializationInfo info, StreamingContext context) - { - this.X = info.GetInt32("x"); - this.Y = info.GetString("y"); - } - - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - info.AddValue("x", this.X); - info.AddValue("y", this.Y); - } - } - - [Fact] - public void TestRoundTripLocation() - { - var instance = new SpecialLocation(); - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); - writer.WriteValue(instance); - writer.Dispose(); - - stream.Position = 0; - var reader = new ObjectReader(stream); - var instance2 = (Location)reader.ReadValue(); - reader.Dispose(); - - Assert.NotNull(instance2); - Assert.Equal(instance2.GetType(), typeof(SerializedLocation)); - Assert.Equal(instance.Kind, instance2.Kind); - Assert.Equal(instance.IsInSource, instance2.IsInSource); - Assert.Equal(instance.SourceSpan, instance2.SourceSpan); - Assert.Equal(instance.Kind, instance2.Kind); - } - - private class SpecialLocation : Location - { - private TextSpan textSpan = new TextSpan(10, 20); - - public override LocationKind Kind - { - get - { - return LocationKind.None; - } - } - - public override TextSpan SourceSpan - { - get - { - return textSpan; - } - } - - public override bool Equals(object obj) - { - throw new NotImplementedException(); - } - - public override int GetHashCode() - { - return 1; - } - } - - [Fact] - public void TestRoundTripRawSerializableObject() - { - var instance = new RawSerializableClass(123, "456"); - - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); - writer.WriteValue(instance); - writer.Dispose(); - - stream.Position = 0; - var reader = new ObjectReader(stream); - var instance2 = (RawSerializableClass)reader.ReadValue(); - reader.Dispose(); - - Assert.NotNull(instance2); - Assert.Equal(instance.X, instance2.X); - Assert.Equal(instance.Y, instance2.Y); - } - - [Serializable] - private class RawSerializableClass - { - internal readonly int X; - internal readonly string Y; - - public RawSerializableClass(int x, string y) - { - this.X = x; - this.Y = y; - } - } - - [Fact] - public void TestRoundTripSingletonClass() - { - var instance = SingletonClass.Singleton; - - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); - writer.WriteValue(instance); - writer.Dispose(); - - stream.Position = 0; - var reader = new ObjectReader(stream); - var instance2 = (SingletonClass)reader.ReadValue(); - reader.Dispose(); - - Assert.Same(instance, instance2); - } - - [Serializable] - private class SingletonClass : IObjectReference - { - internal readonly int X; - internal readonly string Y; - - private SingletonClass(int x, string y) - { - this.X = x; - this.Y = y; - } - - public static readonly SingletonClass Singleton = new SingletonClass(999, "999"); - - public object GetRealObject(StreamingContext context) - { - return Singleton; - } - } -#endif - [Fact] public void TestRoundTripGraph() { var oneNode = new Node("one"); - TestRoundTripGraphCore(oneNode); + TestRoundTripValue(oneNode); - TestRoundTripGraphCore(new Node("a", new Node("b"), new Node("c"))); - TestRoundTripGraphCore(new Node("x", oneNode, oneNode, oneNode, oneNode)); -#if false // cycles not supported - var cyclicNode = new Node("cyclic", oneNode); - cyclicNode.Children[0] = cyclicNode; - TestRoundTripGraph(cyclicNode); -#endif + TestRoundTripValue(new Node("a", new Node("b"), new Node("c"))); + TestRoundTripValue(new Node("x", oneNode, oneNode, oneNode, oneNode)); } - private void TestRoundTripGraphCore(Node graph) - { - var stream = new MemoryStream(); - var writer = new ObjectWriter(stream); - writer.WriteValue(graph); - writer.Dispose(); - - stream.Position = 0; - var reader = new ObjectReader(stream, binder: writer.Binder); - var newGraph = (Node)reader.ReadValue(); - reader.Dispose(); - - Assert.NotNull(newGraph); - Assert.Equal(true, graph.IsEquivalentTo(newGraph)); - } - - private class Node : IObjectWritable, IObjectReadable + private class Node : IObjectWritable, IObjectReadable, IEquatable { internal readonly string Name; internal readonly Node[] Children; @@ -704,14 +877,21 @@ public void WriteTo(ObjectWriter writer) writer.WriteValue(this.Children); } - Func IObjectReadable.GetReader() + Func IObjectReadable.GetReader() => (r) => new Node(r); + + public override Int32 GetHashCode() + { + return this.Name != null ? this.Name.GetHashCode() : 0; + } + + public override Boolean Equals(Object obj) { - return (r) => new Node(r); + return Equals(obj as Node); } - public bool IsEquivalentTo(Node node) + public bool Equals(Node node) { - if (this.Name != node.Name) + if (node == null || this.Name != node.Name) { return false; } @@ -723,7 +903,7 @@ public bool IsEquivalentTo(Node node) for (int i = 0; i < this.Children.Length; i++) { - if (!this.Children[i].IsEquivalentTo(node.Children[i])) + if (!this.Children[i].Equals(node.Children[i])) { return false; } diff --git a/src/Compilers/Core/Portable/CodeAnalysis.csproj b/src/Compilers/Core/Portable/CodeAnalysis.csproj index a863dc80436..09baf4b81eb 100644 --- a/src/Compilers/Core/Portable/CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/CodeAnalysis.csproj @@ -46,6 +46,11 @@ PreserveNewest false + + + + + @@ -556,7 +561,6 @@ - diff --git a/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs b/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs index 4c6825f2ece..8f406a889ea 100644 --- a/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs +++ b/src/Compilers/Core/Portable/Diagnostic/DiagnosticInfo.cs @@ -141,12 +141,12 @@ void IObjectWritable.WriteTo(ObjectWriter writer) protected virtual void WriteTo(ObjectWriter writer) { writer.WriteValue(_messageProvider); - writer.WriteCompressedUInt((uint)_errorCode); + writer.WriteUInt32((uint)_errorCode); writer.WriteInt32((int)_effectiveSeverity); writer.WriteInt32((int)_defaultSeverity); int count = _arguments?.Length ?? 0; - writer.WriteCompressedUInt((uint)count); + writer.WriteUInt32((uint)count); if (count > 0) { @@ -170,11 +170,11 @@ protected virtual void WriteTo(ObjectWriter writer) protected DiagnosticInfo(ObjectReader reader) { _messageProvider = (CommonMessageProvider)reader.ReadValue(); - _errorCode = (int)reader.ReadCompressedUInt(); + _errorCode = (int)reader.ReadUInt32(); _effectiveSeverity = (DiagnosticSeverity)reader.ReadInt32(); _defaultSeverity = (DiagnosticSeverity)reader.ReadInt32(); - var count = (int)reader.ReadCompressedUInt(); + var count = (int)reader.ReadUInt32(); if (count == 0) { _arguments = Array.Empty(); diff --git a/src/Compilers/Core/Portable/Diagnostic/LocalizableResourceString.cs b/src/Compilers/Core/Portable/Diagnostic/LocalizableResourceString.cs index 3bf387f0d18..d13d30fc376 100644 --- a/src/Compilers/Core/Portable/Diagnostic/LocalizableResourceString.cs +++ b/src/Compilers/Core/Portable/Diagnostic/LocalizableResourceString.cs @@ -72,7 +72,7 @@ private LocalizableResourceString(ObjectReader reader) _nameOfLocalizableResource = reader.ReadString(); _resourceManager = new ResourceManager(_resourceSource); - var length = (int)reader.ReadCompressedUInt(); + var length = (int)reader.ReadUInt32(); if (length == 0) { _formatArguments = Array.Empty(); @@ -99,7 +99,7 @@ void IObjectWritable.WriteTo(ObjectWriter writer) writer.WriteValue(_resourceSource); writer.WriteString(_nameOfLocalizableResource); var length = (uint)_formatArguments.Length; - writer.WriteCompressedUInt(length); + writer.WriteUInt32(length); for (int i = 0; i < length; i++) { writer.WriteString(_formatArguments[i]); diff --git a/src/Compilers/Core/Portable/Serialization/DataKind.cs b/src/Compilers/Core/Portable/Serialization/DataKind.cs new file mode 100644 index 00000000000..0b8d506e7fe --- /dev/null +++ b/src/Compilers/Core/Portable/Serialization/DataKind.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Roslyn.Utilities +{ + internal enum DataKind : byte + { + Null, + Type, + TypeRef, // type ref id as 4 bytes + TypeRef_B, // type ref id as 1 byte + TypeRef_S, // type ref id as 2 bytes + Object_W, // IObjectWritable + ObjectRef, // object ref id as 4 bytes + ObjectRef_B, // object ref id as 1 byte + ObjectRef_S, // object ref id as 2 bytes + StringUtf8, // string in UTF8 encoding + StringUtf16, // string in UTF16 encoding + StringRef, // string ref id as 4-bytes + StringRef_B, // string ref id as 1-byte + StringRef_S, // string ref id as 2-bytes + Boolean_T, // boolean true + Boolean_F, // boolean false + Char, + Int8, + Int16, + Int32, // int encoded as 4 bytes + Int32_B, // int encoded as 1 byte + Int32_S, // int encoded as 2 bytes + Int32_0, // int 0 + Int32_1, // int 1 + Int32_2, // int 2 + Int32_3, // int 3, + Int32_4, // int 4, + Int32_5, // int 5, + Int32_6, // int 6, + Int32_7, // int 7, + Int32_8, // int 8, + Int32_9, // int 9, + Int32_10, // int 10, + Int64, + UInt8, + UInt16, + UInt32, // uint encoded as 4 bytes + UInt32_B, // uint encoded as 1 byte + UInt32_S, // uint encoded as 2 bytes + UInt32_0, // uint 0 + UInt32_1, // uint 1 + UInt32_2, // uint 2 + UInt32_3, // uint 3, + UInt32_4, // uint 4, + UInt32_5, // uint 5, + UInt32_6, // uint 6, + UInt32_7, // uint 7, + UInt32_8, // uint 8, + UInt32_9, // uint 9, + UInt32_10, // uint 10, + UInt64, + Float4, + Float8, + Decimal, + DateTime, + Enum, + + Array, // array with # elements encoded as compressed int + Array_0, // array with zero elements + Array_1, // array with one element + Array_2, // array with two elements + Array_3, // array with three elements + + ValueArray, // value array with # elements encoded as a compressed int + ValueArray_0, + ValueArray_1, + ValueArray_2, + ValueArray_3, + + BooleanType, // boolean type marker + StringType, // string type marker + + End // end of object serialization + } +} \ No newline at end of file diff --git a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs index 34029178e74..939184cf098 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectReader.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectReader.cs @@ -1,616 +1,26 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Diagnostics; -using System.IO; -using System.Text; -using Microsoft.CodeAnalysis; namespace Roslyn.Utilities { - /// - /// A class that reads both primitive values and non-cyclical object graphs from a stream that was constructed using - /// the ObjectWriter class. - /// - internal sealed class ObjectReader : ObjectReaderWriterBase, IDisposable + internal abstract class ObjectReader { - private readonly BinaryReader _reader; - private readonly ObjectReaderData _dataMap; - private readonly ObjectBinder _binder; - - internal ObjectReader( - Stream stream, - ObjectReaderData defaultData = null, - ObjectBinder binder = null) - { - // String serialization assumes both reader and writer to be of the same endianness. - // It can be adjusted for BigEndian if needed. - Debug.Assert(BitConverter.IsLittleEndian); - - _reader = new BinaryReader(stream, Encoding.UTF8); - _dataMap = new ObjectReaderData(defaultData); - _binder = binder; - } - - public void Dispose() - { - _dataMap.Dispose(); - } - - /// - /// Read a Boolean value from the stream. This value must have been written using . - /// - public bool ReadBoolean() - { - return _reader.ReadBoolean(); - } - - /// - /// Read a Byte value from the stream. This value must have been written using . - /// - public byte ReadByte() - { - return _reader.ReadByte(); - } - - /// - /// Read a Char value from the stream. This value must have been written using . - /// - public char ReadChar() - { - // char was written as UInt16 because BinaryWriter fails on characters that are unicode surrogates. - return (char)_reader.ReadUInt16(); - } - - /// - /// Read a Decimal value from the stream. This value must have been written using . - /// - public decimal ReadDecimal() - { - return _reader.ReadDecimal(); - } - - /// - /// Read a Double value from the stream. This value must have been written using . - /// - public double ReadDouble() - { - return _reader.ReadDouble(); - } - - /// - /// Read a Single value from the stream. This value must have been written using . - /// - public float ReadSingle() - { - return _reader.ReadSingle(); - } - - /// - /// Read a Int32 value from the stream. This value must have been written using . - /// - public int ReadInt32() - { - return _reader.ReadInt32(); - } - - /// - /// Read a Int64 value from the stream. This value must have been written using . - /// - public long ReadInt64() - { - return _reader.ReadInt64(); - } - - /// - /// Read a SByte value from the stream. This value must have been written using . - /// - public sbyte ReadSByte() - { - return _reader.ReadSByte(); - } - - /// - /// Read a Int16 value from the stream. This value must have been written using . - /// - public short ReadInt16() - { - return _reader.ReadInt16(); - } - - /// - /// Read a UInt32 value from the stream. This value must have been written using . - /// - public uint ReadUInt32() - { - return _reader.ReadUInt32(); - } - - /// - /// Read a UInt64 value from the stream. This value must have been written using . - /// - public ulong ReadUInt64() - { - return _reader.ReadUInt64(); - } - - /// - /// Read a UInt16 value from the stream. This value must have been written using . - /// - public ushort ReadUInt16() - { - return _reader.ReadUInt16(); - } - - /// - /// Read a DateTime value from the stream. This value must have been written using the . - /// - public DateTime ReadDateTime() - { - return DateTime.FromBinary(this.ReadInt64()); - } - - /// - /// Read a compressed 30-bit integer value from the stream. This value must have been written using . - /// - public uint ReadCompressedUInt() - { - var info = _reader.ReadByte(); - byte marker = (byte)(info & ByteMarkerMask); - byte byte0 = (byte)(info & ~ByteMarkerMask); - - if (marker == Byte1Marker) - { - return byte0; - } - - if (marker == Byte2Marker) - { - var byte1 = _reader.ReadByte(); - return (((uint)byte0) << 8) | byte1; - } - - if (marker == Byte4Marker) - { - var byte1 = _reader.ReadByte(); - var byte2 = _reader.ReadByte(); - var byte3 = _reader.ReadByte(); - - return (((uint)byte0) << 24) | (((uint)byte1) << 16) | (((uint)byte2) << 8) | byte3; - } - - throw ExceptionUtilities.UnexpectedValue(marker); - } - - /// - /// Read a value from the stream. The value must have been written using ObjectWriter.WriteValue. - /// - public object ReadValue() - { - var kind = (DataKind)_reader.ReadByte(); - switch (kind) - { - case DataKind.Null: - return null; - case DataKind.Boolean_T: - return Boxes.BoxedTrue; - case DataKind.Boolean_F: - return Boxes.BoxedFalse; - case DataKind.Int8: - return _reader.ReadSByte(); - case DataKind.UInt8: - return _reader.ReadByte(); - case DataKind.Int16: - return _reader.ReadInt16(); - case DataKind.UInt16: - return _reader.ReadUInt16(); - case DataKind.Int32: - return _reader.ReadInt32(); - case DataKind.Int32_B: - return (int)_reader.ReadByte(); - case DataKind.Int32_S: - return (int)_reader.ReadUInt16(); - case DataKind.Int32_Z: - return Boxes.BoxedInt32Zero; - case DataKind.UInt32: - return _reader.ReadUInt32(); - case DataKind.Int64: - return _reader.ReadInt64(); - case DataKind.UInt64: - return _reader.ReadUInt64(); - case DataKind.Float4: - return _reader.ReadSingle(); - case DataKind.Float8: - return _reader.ReadDouble(); - case DataKind.Decimal: - return _reader.ReadDecimal(); - case DataKind.DateTime: - return this.ReadDateTime(); - case DataKind.Char: - return this.ReadChar(); - case DataKind.StringUtf8: - case DataKind.StringUtf16: - case DataKind.StringRef: - case DataKind.StringRef_B: - case DataKind.StringRef_S: - return ReadString(kind); - case DataKind.Object_W: - case DataKind.ObjectRef: - case DataKind.ObjectRef_B: - case DataKind.ObjectRef_S: - return ReadObject(kind); - case DataKind.Type: - case DataKind.TypeRef: - case DataKind.TypeRef_B: - case DataKind.TypeRef_S: - return ReadType(kind); - case DataKind.Enum: - return ReadEnum(); - case DataKind.Array: - case DataKind.Array_0: - case DataKind.Array_1: - case DataKind.Array_2: - case DataKind.Array_3: - return ReadArray(kind); - default: - throw ExceptionUtilities.UnexpectedValue(kind); - } - } - - /// - /// Read a String value from the stream. This value must have been written using ObjectWriter.WriteString. - /// - public string ReadString() - { - var kind = (DataKind)_reader.ReadByte(); - return kind == DataKind.Null ? null : ReadString(kind); - } - - private string ReadString(DataKind kind) - { - switch (kind) - { - case DataKind.StringRef_B: - return (string)_dataMap.GetValue(_reader.ReadByte()); - - case DataKind.StringRef_S: - return (string)_dataMap.GetValue(_reader.ReadUInt16()); - - case DataKind.StringRef: - return (string)_dataMap.GetValue(_reader.ReadInt32()); - - case DataKind.StringUtf16: - case DataKind.StringUtf8: - return ReadStringLiteral(kind); - - default: - throw ExceptionUtilities.UnexpectedValue(kind); - } - } - - private unsafe string ReadStringLiteral(DataKind kind) - { - int id = _dataMap.GetNextId(); - string value; - if (kind == DataKind.StringUtf8) - { - value = _reader.ReadString(); - } - else - { - // This is rare, just allocate UTF16 bytes for simplicity. - - int characterCount = (int)ReadCompressedUInt(); - byte[] bytes = _reader.ReadBytes(characterCount * sizeof(char)); - fixed (byte* bytesPtr = bytes) - { - value = new string((char*)bytesPtr, 0, characterCount); - } - } - - _dataMap.AddValue(id, value); - return value; - } - - private Array ReadArray(DataKind kind) - { - int length; - switch (kind) - { - case DataKind.Array_0: - length = 0; - break; - case DataKind.Array_1: - length = 1; - break; - case DataKind.Array_2: - length = 2; - break; - case DataKind.Array_3: - length = 3; - break; - default: - length = (int)this.ReadCompressedUInt(); - break; - } - - var elementKind = (DataKind)_reader.ReadByte(); - - // optimization for primitive type array - Type elementType; - if (s_reverseTypeMap.TryGetValue(elementKind, out elementType)) - { - return this.ReadPrimitiveTypeArrayElements(elementType, elementKind, length); - } - - // custom type case - elementType = this.ReadType(elementKind); - - Array array = Array.CreateInstance(elementType, length); - for (int i = 0; i < length; i++) - { - var value = this.ReadValue(); - array.SetValue(value, i); - } - - return array; - } - - private Array ReadPrimitiveTypeArrayElements(Type type, DataKind kind, int length) - { - Debug.Assert(s_reverseTypeMap[kind] == type); - - // optimizations for supported array type by binary reader - if (type == typeof(byte)) - { - return _reader.ReadBytes(length); - } - - if (type == typeof(char)) - { - return _reader.ReadChars(length); - } - - // optimizations for string where object reader/writer has its own mechanism to - // reduce duplicated strings - if (type == typeof(string)) - { - return ReadPrimitiveTypeArrayElements(length, ReadString); - } - - if (type == typeof(bool)) - { - return ReadBooleanArray(length); - } - - // otherwise, read elements directly from underlying binary writer - switch (kind) - { - case DataKind.Int8: - return ReadPrimitiveTypeArrayElements(length, _reader.ReadSByte); - case DataKind.Int16: - return ReadPrimitiveTypeArrayElements(length, _reader.ReadInt16); - case DataKind.Int32: - return ReadPrimitiveTypeArrayElements(length, _reader.ReadInt32); - case DataKind.Int64: - return ReadPrimitiveTypeArrayElements(length, _reader.ReadInt64); - case DataKind.UInt16: - return ReadPrimitiveTypeArrayElements(length, _reader.ReadUInt16); - case DataKind.UInt32: - return ReadPrimitiveTypeArrayElements(length, _reader.ReadUInt32); - case DataKind.UInt64: - return ReadPrimitiveTypeArrayElements(length, _reader.ReadUInt64); - case DataKind.Float4: - return ReadPrimitiveTypeArrayElements(length, _reader.ReadSingle); - case DataKind.Float8: - return ReadPrimitiveTypeArrayElements(length, _reader.ReadDouble); - case DataKind.Decimal: - return ReadPrimitiveTypeArrayElements(length, _reader.ReadDecimal); - default: - throw ExceptionUtilities.UnexpectedValue(kind); - } - } - - private bool[] ReadBooleanArray(int length) - { - if (length == 0) - { - // simple check - return Array.Empty(); - } - - var array = new bool[length]; - var wordLength = BitVector.WordsRequired(length); - - var count = 0; - for (var i = 0; i < wordLength; i++) - { - var word = _reader.ReadUInt32(); - - for (var p = 0; p < BitVector.BitsPerWord; p++) - { - if (count >= length) - { - return array; - } - - array[count++] = BitVector.IsTrue(word, p); - } - } - - return array; - } - - private static T[] ReadPrimitiveTypeArrayElements(int length, Func read) - { - if (length == 0) - { - // quick check - return Array.Empty(); - } - - var array = new T[length]; - for (var i = 0; i < array.Length; i++) - { - array[i] = read(); - } - - return array; - } - - private Type ReadType() - { - var kind = (DataKind)_reader.ReadByte(); - return ReadType(kind); - } - - private Type ReadType(DataKind kind) - { - switch (kind) - { - case DataKind.TypeRef_B: - return (Type)_dataMap.GetValue(_reader.ReadByte()); - - case DataKind.TypeRef_S: - return (Type)_dataMap.GetValue(_reader.ReadUInt16()); - - case DataKind.TypeRef: - return (Type)_dataMap.GetValue(_reader.ReadInt32()); - - case DataKind.Type: - int id = _dataMap.GetNextId(); - var assemblyName = this.ReadString(); - var typeName = this.ReadString(); - - if (_binder == null) - { - throw NoBinderException(typeName); - } - - var type = _binder.GetType(assemblyName, typeName); - _dataMap.AddValue(id, type); - return type; - - default: - throw ExceptionUtilities.UnexpectedValue(kind); - } - } - - private object ReadEnum() - { - var enumType = this.ReadType(); - var type = Enum.GetUnderlyingType(enumType); - - if (type == typeof(int)) - { - return Enum.ToObject(enumType, _reader.ReadInt32()); - } - - if (type == typeof(short)) - { - return Enum.ToObject(enumType, _reader.ReadInt16()); - } - - if (type == typeof(byte)) - { - return Enum.ToObject(enumType, _reader.ReadByte()); - } - - if (type == typeof(long)) - { - return Enum.ToObject(enumType, _reader.ReadInt64()); - } - - if (type == typeof(sbyte)) - { - return Enum.ToObject(enumType, _reader.ReadSByte()); - } - - if (type == typeof(ushort)) - { - return Enum.ToObject(enumType, _reader.ReadUInt16()); - } - - if (type == typeof(uint)) - { - return Enum.ToObject(enumType, _reader.ReadUInt32()); - } - - if (type == typeof(ulong)) - { - return Enum.ToObject(enumType, _reader.ReadUInt64()); - } - - throw ExceptionUtilities.UnexpectedValue(enumType); - } - - private object ReadObject(DataKind kind) - { - switch (kind) - { - case DataKind.ObjectRef_B: - return _dataMap.GetValue(_reader.ReadByte()); - - case DataKind.ObjectRef_S: - return _dataMap.GetValue(_reader.ReadUInt16()); - - case DataKind.ObjectRef: - return _dataMap.GetValue(_reader.ReadInt32()); - - case DataKind.Object_W: - return this.ReadReadableObject(); - - case DataKind.Array: - return this.ReadArray(kind); - - default: - throw ExceptionUtilities.UnexpectedValue(kind); - } - } - - private object ReadReadableObject() - { - int id = _dataMap.GetNextId(); - - Type type = this.ReadType(); - var instance = CreateInstance(type); - - _dataMap.AddValue(id, instance); - return instance; - } - - private object CreateInstance(Type type) - { - if (_binder == null) - { - return NoBinderException(type.FullName); - } - - var reader = _binder.GetReader(type); - if (reader == null) - { - return NoReaderException(type.FullName); - } - - return reader(this); - } - - private static Exception NoBinderException(string typeName) - { -#if COMPILERCORE - throw new InvalidOperationException(string.Format(CodeAnalysisResources.NoBinderException, typeName)); -#else - throw new InvalidOperationException(string.Format(Microsoft.CodeAnalysis.WorkspacesResources.Cannot_deserialize_type_0_no_binder_supplied, typeName)); -#endif - } - - private static Exception NoReaderException(string typeName) - { -#if COMPILERCORE - throw new InvalidOperationException(string.Format(CodeAnalysisResources.NoReaderException, typeName)); -#else - throw new InvalidOperationException(string.Format(Microsoft.CodeAnalysis.WorkspacesResources.Cannot_deserialize_type_0_it_has_no_deserialization_reader, typeName)); -#endif - } + public abstract bool ReadBoolean(); + public abstract byte ReadByte(); + public abstract char ReadChar(); + public abstract decimal ReadDecimal(); + public abstract double ReadDouble(); + public abstract float ReadSingle(); + public abstract int ReadInt32(); + public abstract long ReadInt64(); + public abstract sbyte ReadSByte(); + public abstract short ReadInt16(); + public abstract uint ReadUInt32(); + public abstract ulong ReadUInt64(); + public abstract ushort ReadUInt16(); + public abstract DateTime ReadDateTime(); + public abstract string ReadString(); + public abstract object ReadValue(); } } diff --git a/src/Compilers/Core/Portable/Serialization/ObjectReaderWriterBase.cs b/src/Compilers/Core/Portable/Serialization/ObjectReaderWriterBase.cs deleted file mode 100644 index 17a0b08ac76..00000000000 --- a/src/Compilers/Core/Portable/Serialization/ObjectReaderWriterBase.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; - -namespace Roslyn.Utilities -{ - internal class ObjectReaderWriterBase - { - // we have s_typeMap and s_reversedTypeMap since there is no bidirectional map in compiler - protected static readonly ImmutableDictionary s_typeMap = ImmutableDictionary.CreateRange( - new KeyValuePair[] - { - KeyValuePair.Create(typeof(bool), DataKind.BooleanType), - KeyValuePair.Create(typeof(char), DataKind.Char), - KeyValuePair.Create(typeof(string), DataKind.StringType), - KeyValuePair.Create(typeof(sbyte), DataKind.Int8), - KeyValuePair.Create(typeof(short), DataKind.Int16), - KeyValuePair.Create(typeof(int), DataKind.Int32), - KeyValuePair.Create(typeof(long), DataKind.Int64), - KeyValuePair.Create(typeof(byte), DataKind.UInt8), - KeyValuePair.Create(typeof(ushort), DataKind.UInt16), - KeyValuePair.Create(typeof(uint), DataKind.UInt32), - KeyValuePair.Create(typeof(ulong), DataKind.UInt64), - KeyValuePair.Create(typeof(float), DataKind.Float4), - KeyValuePair.Create(typeof(double), DataKind.Float8), - KeyValuePair.Create(typeof(decimal), DataKind.Decimal), - }); - - protected static readonly ImmutableDictionary s_reverseTypeMap = s_typeMap.ToImmutableDictionary(kv => kv.Value, kv => kv.Key); - - internal enum DataKind : byte - { - Null, - Type, - TypeRef, // type ref id as 4 bytes - TypeRef_B, // type ref id as 1 byte - TypeRef_S, // type ref id as 2 bytes - Object_W, // IObjectWritable - ObjectRef, // object ref id as 4 bytes - ObjectRef_B, // object ref id as 1 byte - ObjectRef_S, // object ref id as 2 bytes - StringUtf8, // string in UTF8 encoding - StringUtf16, // string in UTF16 encoding - StringRef, // string ref id as 4-bytes - StringRef_B, // string ref id as 1-byte - StringRef_S, // string ref id as 2-bytes - Boolean_T, // boolean true - Boolean_F, // boolean false - Char, - Int8, - Int16, - Int32, // int32 encoded as 4 bytes - Int32_B, // int32 encoded as 1 byte - Int32_S, // int32 encoded as 2 bytes - Int32_Z, // int32 zero - Int64, - UInt8, - UInt16, - UInt32, - UInt64, - Float4, - Float8, - Decimal, - DateTime, - Enum, - Array, // array with # elements encoded as compressed int - Array_0, // array with zero elements - Array_1, // array with one element - Array_2, // array with two elements - Array_3, // array with three elements - BooleanType, // boolean type marker - StringType // string type marker - } - - internal static readonly byte ByteMarkerMask = 3 << 6; - internal static readonly byte Byte1Marker = 0; - internal static readonly byte Byte2Marker = 1 << 6; - internal static readonly byte Byte4Marker = 2 << 6; - } -} diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index a14ee5f5801..c14699d10fb 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -1,699 +1,26 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Diagnostics; -using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; -using Microsoft.CodeAnalysis; namespace Roslyn.Utilities { - /// - /// A class that writes both primitive values and non-cyclical object graphs to a stream that may be - /// later read back using the ObjectReader class. - /// - internal sealed class ObjectWriter : ObjectReaderWriterBase, IDisposable + internal abstract class ObjectWriter { - private readonly BinaryWriter _writer; - private readonly ObjectWriterData _dataMap; - private readonly RecordingObjectBinder _binder; - private readonly CancellationToken _cancellationToken; - - internal ObjectWriter( - Stream stream, - ObjectWriterData defaultData = null, - RecordingObjectBinder binder = null, - CancellationToken cancellationToken = default(CancellationToken)) - { - // String serialization assumes both reader and writer to be of the same endianness. - // It can be adjusted for BigEndian if needed. - Debug.Assert(BitConverter.IsLittleEndian); - - _writer = new BinaryWriter(stream, Encoding.UTF8); - _dataMap = new ObjectWriterData(defaultData); - _binder = binder ?? new SimpleRecordingObjectBinder(); - _cancellationToken = cancellationToken; - } - - public ObjectBinder Binder - { - get { return _binder; } - } - - public void Dispose() - { - _dataMap.Dispose(); - } - - /// - /// Writes a Boolean value to the stream. - /// - public void WriteBoolean(bool value) - { - _writer.Write(value); - } - - /// - /// Writes a Byte value to the stream. - /// - public void WriteByte(byte value) - { - _writer.Write(value); - } - - /// - /// Writes a Char value to the stream. - /// - public void WriteChar(char ch) - { - // write as UInt16 because binary writer fails on chars that are unicode surrogates - _writer.Write((ushort)ch); - } - - /// - /// Writes a Decimal value to the stream. - /// - public void WriteDecimal(decimal value) - { - _writer.Write(value); - } - - /// - /// Writes a Double value to the stream. - /// - public void WriteDouble(double value) - { - _writer.Write(value); - } - - /// - /// Writes a Single value to the stream. - /// - public void WriteSingle(float value) - { - _writer.Write(value); - } - - /// - /// Writes a Int32 value to the stream. - /// - public void WriteInt32(int value) - { - _writer.Write(value); - } - - /// - /// Writes a Int64 value to the stream. - /// - public void WriteInt64(long value) - { - _writer.Write(value); - } - - /// - /// Writes a SByte value to the stream. - /// - public void WriteSByte(sbyte value) - { - _writer.Write(value); - } - - /// - /// Writes a Int16 value to the stream. - /// - public void WriteInt16(short value) - { - _writer.Write(value); - } - - /// - /// Writes a UInt32 value to the stream. - /// - public void WriteUInt32(uint value) - { - _writer.Write(value); - } - - /// - /// Writes a UInt64 value to the stream. - /// - public void WriteUInt64(ulong value) - { - _writer.Write(value); - } - - /// - /// Writes a UInt16 value to the stream. - /// - public void WriteUInt16(ushort value) - { - _writer.Write(value); - } - - /// - /// Writes a DateTime value to the stream. - /// - public void WriteDateTime(DateTime value) - { - this.WriteInt64(value.ToBinary()); - } - - /// - /// Writes a compressed 30 bit integer to the stream. (not 32 bit) - /// - public void WriteCompressedUInt(uint value) - { - if (value <= (byte.MaxValue >> 2)) - { - _writer.Write((byte)value); - } - else if (value <= (ushort.MaxValue >> 2)) - { - byte byte0 = (byte)(((value >> 8) & 0xFF) | Byte2Marker); - byte byte1 = (byte)(value & 0xFF); - - // high-bytes to low-bytes - _writer.Write(byte0); - _writer.Write(byte1); - } - else if (value <= (uint.MaxValue >> 2)) - { - // high-bytes to low-bytes - byte byte0 = (byte)(((value >> 24) & 0xFF) | Byte4Marker); - byte byte1 = (byte)((value >> 16) & 0xFF); - byte byte2 = (byte)((value >> 8) & 0xFF); - byte byte3 = (byte)(value & 0xFF); - - // hit-bits with 4-byte marker - _writer.Write(byte0); - _writer.Write(byte1); - _writer.Write(byte2); - _writer.Write(byte3); - } - else - { -#if COMPILERCORE - throw new ArgumentException(CodeAnalysisResources.ValueTooLargeToBeRepresented); -#else - throw new ArgumentException(WorkspacesResources.Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer); -#endif - } - } - - /// - /// Writes a String value to the stream. - /// - public unsafe void WriteString(string value) - { - if (value == null) - { - _writer.Write((byte)DataKind.Null); - } - else - { - int id; - if (_dataMap.TryGetId(value, out id)) - { - Debug.Assert(id >= 0); - if (id <= byte.MaxValue) - { - _writer.Write((byte)DataKind.StringRef_B); - _writer.Write((byte)id); - } - else if (id <= ushort.MaxValue) - { - _writer.Write((byte)DataKind.StringRef_S); - _writer.Write((ushort)id); - } - else - { - _writer.Write((byte)DataKind.StringRef); - _writer.Write(id); - } - } - else - { - _dataMap.Add(value); - - if (value.IsValidUnicodeString()) - { - // Usual case - the string can be encoded as UTF8: - // We can use the UTF8 encoding of the binary writer. - - _writer.Write((byte)DataKind.StringUtf8); - _writer.Write(value); - } - else - { - _writer.Write((byte)DataKind.StringUtf16); - - // This is rare, just allocate UTF16 bytes for simplicity. - - byte[] bytes = new byte[(uint)value.Length * sizeof(char)]; - fixed (char* valuePtr = value) - { - Marshal.Copy((IntPtr)valuePtr, bytes, 0, bytes.Length); - } - - WriteCompressedUInt((uint)value.Length); - _writer.Write(bytes); - } - } - } - } - - /// - /// Writes any value (primitive or object graph) to the stream. - /// - public void WriteValue(object value) - { - if (value == null) - { - _writer.Write((byte)DataKind.Null); - } - else - { - var type = value.GetType(); - if (type.GetTypeInfo().IsEnum) - { - WriteEnum(value, type); - } - else if (type == typeof(bool)) - { - if ((bool)value) - { - _writer.Write((byte)DataKind.Boolean_T); - } - else - { - _writer.Write((byte)DataKind.Boolean_F); - } - } - else if (type == typeof(int)) - { - int v = (int)value; - if (v == 0) - { - _writer.Write((byte)DataKind.Int32_Z); - } - else if (v >= 0 && v < byte.MaxValue) - { - _writer.Write((byte)DataKind.Int32_B); - _writer.Write((byte)v); - } - else if (v >= 0 && v < ushort.MaxValue) - { - _writer.Write((byte)DataKind.Int32_S); - _writer.Write((ushort)v); - } - else - { - _writer.Write((byte)DataKind.Int32); - _writer.Write(v); - } - } - else if (type == typeof(string)) - { - this.WriteString((string)value); - } - else if (type == typeof(short)) - { - _writer.Write((byte)DataKind.Int16); - _writer.Write((short)value); - } - else if (type == typeof(long)) - { - _writer.Write((byte)DataKind.Int64); - _writer.Write((long)value); - } - else if (type == typeof(char)) - { - _writer.Write((byte)DataKind.Char); - this.WriteChar((char)value); - } - else if (type == typeof(sbyte)) - { - _writer.Write((byte)DataKind.Int8); - _writer.Write((sbyte)value); - } - else if (type == typeof(byte)) - { - _writer.Write((byte)DataKind.UInt8); - _writer.Write((byte)value); - } - else if (type == typeof(ushort)) - { - _writer.Write((byte)DataKind.UInt16); - _writer.Write((ushort)value); - } - else if (type == typeof(uint)) - { - _writer.Write((byte)DataKind.UInt32); - _writer.Write((uint)value); - } - else if (type == typeof(ulong)) - { - _writer.Write((byte)DataKind.UInt64); - _writer.Write((ulong)value); - } - else if (type == typeof(decimal)) - { - _writer.Write((byte)DataKind.Decimal); - _writer.Write((decimal)value); - } - else if (type == typeof(float)) - { - _writer.Write((byte)DataKind.Float4); - _writer.Write((float)value); - } - else if (type == typeof(double)) - { - _writer.Write((byte)DataKind.Float8); - _writer.Write((double)value); - } - else if (type == typeof(DateTime)) - { - _writer.Write((byte)DataKind.DateTime); - this.WriteDateTime((DateTime)value); - } - else if (type.IsArray) - { - this.WriteArray((Array)value); - } - else if (value is Type) - { - this.WriteType((Type)value); - } - else - { - this.WriteObject(value); - } - } - } - - private void WriteEnum(object value, Type enumType) - { - _writer.Write((byte)DataKind.Enum); - this.WriteType(enumType); - - var type = Enum.GetUnderlyingType(enumType); - - if (type == typeof(int)) - { - _writer.Write((int)value); - } - else if (type == typeof(short)) - { - _writer.Write((short)value); - } - else if (type == typeof(byte)) - { - _writer.Write((byte)value); - } - else if (type == typeof(long)) - { - _writer.Write((long)value); - } - else if (type == typeof(sbyte)) - { - _writer.Write((sbyte)value); - } - else if (type == typeof(ushort)) - { - _writer.Write((ushort)value); - } - else if (type == typeof(uint)) - { - _writer.Write((uint)value); - } - else if (type == typeof(ulong)) - { - _writer.Write((ulong)value); - } - else - { - throw ExceptionUtilities.UnexpectedValue(type); - } - } - - private void WriteArray(Array instance) - { - if (instance.Rank > 1) - { -#if COMPILERCORE - throw new InvalidOperationException(CodeAnalysisResources.ArraysWithMoreThanOneDimensionCannotBeSerialized); -#else - throw new InvalidOperationException(WorkspacesResources.Arrays_with_more_than_one_dimension_cannot_be_serialized); -#endif - } - - int length = instance.GetLength(0); - - switch (length) - { - case 0: - _writer.Write((byte)DataKind.Array_0); - break; - case 1: - _writer.Write((byte)DataKind.Array_1); - break; - case 2: - _writer.Write((byte)DataKind.Array_2); - break; - case 3: - _writer.Write((byte)DataKind.Array_3); - break; - default: - _writer.Write((byte)DataKind.Array); - this.WriteCompressedUInt((uint)length); - break; - } - - // get type of array - var elementType = instance.GetType().GetElementType(); - - // optimization for primitive type array - DataKind elementKind; - if (s_typeMap.TryGetValue(elementType, out elementKind)) - { - this.WritePrimitiveType(elementType, elementKind); - this.WritePrimitiveTypeArrayElements(elementType, elementKind, instance); - - return; - } - - // custom type case - this.WriteType(elementType); - foreach (var value in instance) - { - this.WriteValue(value); - } - } - - private void WritePrimitiveTypeArrayElements(Type type, DataKind kind, Array instance) - { - Debug.Assert(s_typeMap[type] == kind); - - // optimization for type underlying binary writer knows about - if (type == typeof(byte)) - { - _writer.Write((byte[])instance); - return; - } - - if (type == typeof(char)) - { - _writer.Write((char[])instance); - return; - } - - // optimization for string which object writer has - // its own optimization to reduce repeated string - if (type == typeof(string)) - { - WritePrimitiveTypeArrayElements((string[])instance, WriteString); - return; - } - - // optimization for bool array - if (type == typeof(bool)) - { - WriteBooleanArray((bool[])instance); - return; - } - - // otherwise, write elements directly to underlying binary writer - switch (kind) - { - case DataKind.Int8: - WritePrimitiveTypeArrayElements((sbyte[])instance, _writer.Write); - return; - case DataKind.Int16: - WritePrimitiveTypeArrayElements((short[])instance, _writer.Write); - return; - case DataKind.Int32: - WritePrimitiveTypeArrayElements((int[])instance, _writer.Write); - return; - case DataKind.Int64: - WritePrimitiveTypeArrayElements((long[])instance, _writer.Write); - return; - case DataKind.UInt16: - WritePrimitiveTypeArrayElements((ushort[])instance, _writer.Write); - return; - case DataKind.UInt32: - WritePrimitiveTypeArrayElements((uint[])instance, _writer.Write); - return; - case DataKind.UInt64: - WritePrimitiveTypeArrayElements((ulong[])instance, _writer.Write); - return; - case DataKind.Float4: - WritePrimitiveTypeArrayElements((float[])instance, _writer.Write); - return; - case DataKind.Float8: - WritePrimitiveTypeArrayElements((double[])instance, _writer.Write); - return; - case DataKind.Decimal: - WritePrimitiveTypeArrayElements((decimal[])instance, _writer.Write); - return; - default: - throw ExceptionUtilities.UnexpectedValue(kind); - } - } - - private void WriteBooleanArray(bool[] array) - { - // convert bool array to bit array - var bits = BitVector.Create(array.Length); - for (var i = 0; i < array.Length; i++) - { - bits[i] = array[i]; - } - - // send over bit array - foreach (var word in bits.Words()) - { - _writer.Write(word); - } - } - - private static void WritePrimitiveTypeArrayElements(T[] array, Action write) - { - for (var i = 0; i < array.Length; i++) - { - write(array[i]); - } - } - - private void WritePrimitiveType(Type type, DataKind kind) - { - Debug.Assert(s_typeMap[type] == kind); - _writer.Write((byte)kind); - } - - private void WriteType(Type type) - { - int id; - if (_dataMap.TryGetId(type, out id)) - { - Debug.Assert(id >= 0); - if (id <= byte.MaxValue) - { - _writer.Write((byte)DataKind.TypeRef_B); - _writer.Write((byte)id); - } - else if (id <= ushort.MaxValue) - { - _writer.Write((byte)DataKind.TypeRef_S); - _writer.Write((ushort)id); - } - else - { - _writer.Write((byte)DataKind.TypeRef); - _writer.Write(id); - } - } - else - { - _dataMap.Add(type); - - _binder?.Record(type); - - _writer.Write((byte)DataKind.Type); - - string assemblyName = type.GetTypeInfo().Assembly.FullName; - string typeName = type.FullName; - - // assembly name - this.WriteString(assemblyName); - - // type name - this.WriteString(typeName); - } - } - - private void WriteObject(object instance) - { - _cancellationToken.ThrowIfCancellationRequested(); - - // write object ref if we already know this instance - int id; - if (_dataMap.TryGetId(instance, out id)) - { - Debug.Assert(id >= 0); - if (id <= byte.MaxValue) - { - _writer.Write((byte)DataKind.ObjectRef_B); - _writer.Write((byte)id); - } - else if (id <= ushort.MaxValue) - { - _writer.Write((byte)DataKind.ObjectRef_S); - _writer.Write((ushort)id); - } - else - { - _writer.Write((byte)DataKind.ObjectRef); - _writer.Write(id); - } - } - else - { - // otherwise add this instance to the map - _dataMap.Add(instance); - - var iwriteable = instance as IObjectWritable; - if (iwriteable != null) - { - this.WriteWritableObject(iwriteable); - return; - } - - throw NotWritableException(instance.GetType().FullName); - } - } - - private void WriteWritableObject(IObjectWritable instance) - { - _writer.Write((byte)DataKind.Object_W); - - Type type = instance.GetType(); - this.WriteType(type); - - _binder?.Record(instance); - - instance.WriteTo(this); - } - - private static Exception NotWritableException(string typeName) - { -#if COMPILERCORE - throw new InvalidOperationException(string.Format(CodeAnalysisResources.NotWritableException, typeName)); -#else - throw new InvalidOperationException(string.Format(WorkspacesResources.The_type_0_cannot_be_written_it_does_not_implement_IObjectWritable, typeName)); -#endif - } + public abstract void WriteBoolean(bool value); + public abstract void WriteByte(byte value); + public abstract void WriteChar(char ch); + public abstract void WriteDecimal(decimal value); + public abstract void WriteDouble(double value); + public abstract void WriteSingle(float value); + public abstract void WriteInt32(int value); + public abstract void WriteInt64(long value); + public abstract void WriteSByte(sbyte value); + public abstract void WriteInt16(short value); + public abstract void WriteUInt32(uint value); + public abstract void WriteUInt64(ulong value); + public abstract void WriteUInt16(ushort value); + public abstract void WriteDateTime(DateTime value); + public abstract void WriteString(string value); + public abstract void WriteValue(object value); } } diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriterData.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriterData.cs index 109091452c5..27795660f18 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriterData.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriterData.cs @@ -45,11 +45,12 @@ public void Dispose() if (_valueToIdMap.Count > 1024) { DictionaryPool.ForgetTrackedObject(_valueToIdMap); - return; } - - _valueToIdMap.Clear(); - DictionaryPool.Free(_valueToIdMap); + else + { + _valueToIdMap.Clear(); + DictionaryPool.Free(_valueToIdMap); + } } public bool TryGetId(object value, out int id) diff --git a/src/Compilers/Core/Portable/Serialization/StreamObjectReader.cs b/src/Compilers/Core/Portable/Serialization/StreamObjectReader.cs new file mode 100644 index 00000000000..7b613823dbf --- /dev/null +++ b/src/Compilers/Core/Portable/Serialization/StreamObjectReader.cs @@ -0,0 +1,771 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using Microsoft.CodeAnalysis; + +namespace Roslyn.Utilities +{ + /// + /// A class that deserializes objects from a stream. + /// + internal sealed class StreamObjectReader : ObjectReader, IDisposable + { + private readonly BinaryReader _reader; + private readonly ObjectReaderData _dataMap; + private readonly ObjectBinder _binder; + private readonly Stack _valueStack; + private readonly ListReader _valueReader; + + internal StreamObjectReader( + Stream stream, + ObjectReaderData defaultData = null, + ObjectBinder binder = null) + { + // String serialization assumes both reader and writer to be of the same endianness. + // It can be adjusted for BigEndian if needed. + Debug.Assert(BitConverter.IsLittleEndian); + + _reader = new BinaryReader(stream, Encoding.UTF8); + _dataMap = new ObjectReaderData(defaultData); + _binder = binder; + _valueStack = new Stack(); + _valueReader = new ListReader(); + } + + public void Dispose() + { + _dataMap.Dispose(); + } + + public override bool ReadBoolean() + { + return Consume().AsBoolean(); + } + + public override byte ReadByte() + { + return Consume().AsByte(); + } + + public override char ReadChar() + { + return Consume().AsChar(); + } + + public override decimal ReadDecimal() + { + return Consume().AsDecimal(); + } + + public override double ReadDouble() + { + return Consume().AsDouble(); + } + + public override float ReadSingle() + { + return Consume().AsSingle(); + } + + public override int ReadInt32() + { + return Consume().AsInt32(); + } + + public override long ReadInt64() + { + return Consume().AsInt64(); + } + + public override sbyte ReadSByte() + { + return Consume().AsSByte(); + } + + public override short ReadInt16() + { + return Consume().AsInt16(); + } + + public override uint ReadUInt32() + { + return Consume().AsUInt32(); + } + + public override ulong ReadUInt64() + { + return Consume().AsUInt64(); + } + + public override ushort ReadUInt16() + { + return Consume().AsUInt16(); + } + + public override DateTime ReadDateTime() + { + return Consume().AsDateTime(); + } + + public override string ReadString() + { + var v = Consume(); + if (v.Kind == VariantKind.Null) + { + return null; + } + else + { + return v.AsString(); + } + } + + public override object ReadValue() + { + return Consume().ToBoxedObject(); + } + + private Variant Consume() + { + while (true) + { + var kind = (DataKind)_reader.ReadByte(); + switch (kind) + { + case DataKind.Null: + _valueStack.Push(Variant.Null); + break; + case DataKind.Boolean_T: + _valueStack.Push(Variant.FromBoolean(true)); + break; + case DataKind.Boolean_F: + _valueStack.Push(Variant.FromBoolean(false)); + break; + case DataKind.Int8: + _valueStack.Push(Variant.FromSByte(_reader.ReadSByte())); + break; + case DataKind.UInt8: + _valueStack.Push(Variant.FromByte(_reader.ReadByte())); + break; + case DataKind.Int16: + _valueStack.Push(Variant.FromInt16(_reader.ReadInt16())); + break; + case DataKind.UInt16: + _valueStack.Push(Variant.FromUInt16(_reader.ReadUInt16())); + break; + case DataKind.Int32: + _valueStack.Push(Variant.FromInt32(_reader.ReadInt32())); + break; + case DataKind.Int32_B: + _valueStack.Push(Variant.FromInt32((int)_reader.ReadByte())); + break; + case DataKind.Int32_S: + _valueStack.Push(Variant.FromInt32((int)_reader.ReadUInt16())); + break; + case DataKind.Int32_0: + case DataKind.Int32_1: + case DataKind.Int32_2: + case DataKind.Int32_3: + case DataKind.Int32_4: + case DataKind.Int32_5: + case DataKind.Int32_6: + case DataKind.Int32_7: + case DataKind.Int32_8: + case DataKind.Int32_9: + case DataKind.Int32_10: + _valueStack.Push(Variant.FromInt32((int)kind - (int)DataKind.Int32_0)); + break; + case DataKind.UInt32: + _valueStack.Push(Variant.FromUInt32(_reader.ReadUInt32())); + break; + case DataKind.UInt32_B: + _valueStack.Push(Variant.FromUInt32((uint)_reader.ReadByte())); + break; + case DataKind.UInt32_S: + _valueStack.Push(Variant.FromUInt32((uint)_reader.ReadUInt16())); + break; + case DataKind.UInt32_0: + case DataKind.UInt32_1: + case DataKind.UInt32_2: + case DataKind.UInt32_3: + case DataKind.UInt32_4: + case DataKind.UInt32_5: + case DataKind.UInt32_6: + case DataKind.UInt32_7: + case DataKind.UInt32_8: + case DataKind.UInt32_9: + case DataKind.UInt32_10: + _valueStack.Push(Variant.FromUInt32((uint)((int)kind - (int)DataKind.UInt32_0))); + break; + case DataKind.Int64: + _valueStack.Push(Variant.FromInt64(_reader.ReadInt64())); + break; + case DataKind.UInt64: + _valueStack.Push(Variant.FromUInt64(_reader.ReadUInt64())); + break; + case DataKind.Float4: + _valueStack.Push(Variant.FromSingle(_reader.ReadSingle())); + break; + case DataKind.Float8: + _valueStack.Push(Variant.FromDouble(_reader.ReadDouble())); + break; + case DataKind.Decimal: + _valueStack.Push(Variant.FromDecimal(_reader.ReadDecimal())); + break; + case DataKind.DateTime: + _valueStack.Push(Variant.FromDateTime(DateTime.FromBinary(_reader.ReadInt64()))); + break; + case DataKind.Char: + // read as ushort because writer fails on chars that are unicode surrogates + _valueStack.Push(Variant.FromChar((char)_reader.ReadUInt16())); + break; + case DataKind.StringUtf8: + case DataKind.StringUtf16: + case DataKind.StringRef: + case DataKind.StringRef_B: + case DataKind.StringRef_S: + _valueStack.Push(Variant.FromString(ConsumeString(kind))); + break; + case DataKind.Object_W: + _valueStack.Push(Variant.FromObject(ConsumeReadableObject())); + break; + case DataKind.ObjectRef: + _valueStack.Push(Variant.FromObject(_dataMap.GetValue(_reader.ReadInt32()))); + break; + case DataKind.ObjectRef_B: + _valueStack.Push(Variant.FromObject(_dataMap.GetValue(_reader.ReadByte()))); + break; + case DataKind.ObjectRef_S: + _valueStack.Push(Variant.FromObject(_dataMap.GetValue(_reader.ReadUInt16()))); + break; + case DataKind.Type: + case DataKind.TypeRef: + case DataKind.TypeRef_B: + case DataKind.TypeRef_S: + _valueStack.Push(Variant.FromType(ConsumeType(kind))); + break; + case DataKind.Enum: + _valueStack.Push(Variant.FromBoxedEnum(ConsumeEnum())); + break; + case DataKind.Array: + case DataKind.Array_0: + case DataKind.Array_1: + case DataKind.Array_2: + case DataKind.Array_3: + case DataKind.ValueArray: + case DataKind.ValueArray_0: + case DataKind.ValueArray_1: + case DataKind.ValueArray_2: + case DataKind.ValueArray_3: + _valueStack.Push(Variant.FromArray(ConsumeArray(kind))); + break; + case DataKind.End: + return _valueStack.Pop(); + default: + throw ExceptionUtilities.UnexpectedValue(kind); + } + } + } + + private class ListReader : ObjectReader + { + internal readonly List _list; + internal int _index; + + public ListReader() + { + _list = new List(); + } + + private Variant Next() + { + return _list[_index++]; + } + + public override bool ReadBoolean() + { + return Next().AsBoolean(); + } + + public override byte ReadByte() + { + return Next().AsByte(); + } + + public override char ReadChar() + { + return Next().AsChar(); + } + + public override decimal ReadDecimal() + { + return Next().AsDecimal(); + } + + public override double ReadDouble() + { + return Next().AsDouble(); + } + + public override float ReadSingle() + { + return Next().AsSingle(); + } + + public override int ReadInt32() + { + return Next().AsInt32(); + } + + public override long ReadInt64() + { + return Next().AsInt64(); + } + + public override sbyte ReadSByte() + { + return Next().AsSByte(); + } + + public override short ReadInt16() + { + return Next().AsInt16(); + } + + public override uint ReadUInt32() + { + return Next().AsUInt32(); + } + + public override ulong ReadUInt64() + { + return Next().AsUInt64(); + } + + public override ushort ReadUInt16() + { + return Next().AsUInt16(); + } + + public override DateTime ReadDateTime() + { + return Next().AsDateTime(); + } + + public override String ReadString() + { + var next = Next(); + if (next.Kind == VariantKind.Null) + { + return null; + } + else + { + return next.AsString(); + } + } + + public override Object ReadValue() + { + return Next().ToBoxedObject(); + } + } + + private uint ConsumeCompressedUInt() + { + var info = _reader.ReadByte(); + byte marker = (byte)(info & StreamObjectWriter.ByteMarkerMask); + byte byte0 = (byte)(info & ~StreamObjectWriter.ByteMarkerMask); + + if (marker == StreamObjectWriter.Byte1Marker) + { + return byte0; + } + + if (marker == StreamObjectWriter.Byte2Marker) + { + var byte1 = _reader.ReadByte(); + return (((uint)byte0) << 8) | byte1; + } + + if (marker == StreamObjectWriter.Byte4Marker) + { + var byte1 = _reader.ReadByte(); + var byte2 = _reader.ReadByte(); + var byte3 = _reader.ReadByte(); + + return (((uint)byte0) << 24) | (((uint)byte1) << 16) | (((uint)byte2) << 8) | byte3; + } + + throw ExceptionUtilities.UnexpectedValue(marker); + } + + private string ConsumeString() + { + var kind = (DataKind)_reader.ReadByte(); + return kind == DataKind.Null ? null : ConsumeString(kind); + } + + private string ConsumeString(DataKind kind) + { + switch (kind) + { + case DataKind.StringRef_B: + return (string)_dataMap.GetValue(_reader.ReadByte()); + + case DataKind.StringRef_S: + return (string)_dataMap.GetValue(_reader.ReadUInt16()); + + case DataKind.StringRef: + return (string)_dataMap.GetValue(_reader.ReadInt32()); + + case DataKind.StringUtf16: + case DataKind.StringUtf8: + return ConsumeStringLiteral(kind); + + default: + throw ExceptionUtilities.UnexpectedValue(kind); + } + } + + private unsafe string ConsumeStringLiteral(DataKind kind) + { + int id = _dataMap.GetNextId(); + string value; + if (kind == DataKind.StringUtf8) + { + value = _reader.ReadString(); + } + else + { + // This is rare, just allocate UTF16 bytes for simplicity. + + int characterCount = (int)ConsumeCompressedUInt(); + byte[] bytes = _reader.ReadBytes(characterCount * sizeof(char)); + fixed (byte* bytesPtr = bytes) + { + value = new string((char*)bytesPtr, 0, characterCount); + } + } + + _dataMap.AddValue(id, value); + return value; + } + + private Array ConsumeArray(DataKind kind) + { + int length; + switch (kind) + { + case DataKind.Array_0: + case DataKind.ValueArray_0: + length = 0; + break; + case DataKind.Array_1: + case DataKind.ValueArray_1: + length = 1; + break; + case DataKind.Array_2: + case DataKind.ValueArray_2: + length = 2; + break; + case DataKind.Array_3: + case DataKind.ValueArray_3: + length = 3; + break; + default: + length = (int)this.ConsumeCompressedUInt(); + break; + } + + var elementKind = (DataKind)_reader.ReadByte(); + + // optimization for primitive type array + Type elementType; + if (StreamObjectWriter.s_reverseTypeMap.TryGetValue(elementKind, out elementType)) + { + return this.ConsumePrimitiveTypeArrayElements(elementType, elementKind, length); + } + else + { + // custom type case + elementType = this.ConsumeType(elementKind); + + if (_valueStack.Count < length) + { + throw new InvalidOperationException($"Deserialization for array expects to read {length} elements, but only {_valueStack.Count} elements are available."); + } + + Array array = Array.CreateInstance(elementType, length); + + // values are on stack in reverse order + for (int i = length - 1; i >= 0; i--) + { + var value = _valueStack.Pop().ToBoxedObject(); + array.SetValue(value, i); + } + + return array; + } + } + + private Array ConsumePrimitiveTypeArrayElements(Type type, DataKind kind, int length) + { + Debug.Assert(StreamObjectWriter.s_reverseTypeMap[kind] == type); + + // optimizations for supported array type by binary reader + if (type == typeof(byte)) + { + return _reader.ReadBytes(length); + } + + if (type == typeof(char)) + { + return _reader.ReadChars(length); + } + + // optimizations for string where object reader/writer has its own mechanism to + // reduce duplicated strings + if (type == typeof(string)) + { + return ReadPrimitiveTypeArrayElements(length, ConsumeString); + } + + if (type == typeof(bool)) + { + return ConsumeBooleanArray(length); + } + + // otherwise, read elements directly from underlying binary writer + switch (kind) + { + case DataKind.Int8: + return ReadPrimitiveTypeArrayElements(length, _reader.ReadSByte); + case DataKind.Int16: + return ReadPrimitiveTypeArrayElements(length, _reader.ReadInt16); + case DataKind.Int32: + return ReadPrimitiveTypeArrayElements(length, _reader.ReadInt32); + case DataKind.Int64: + return ReadPrimitiveTypeArrayElements(length, _reader.ReadInt64); + case DataKind.UInt16: + return ReadPrimitiveTypeArrayElements(length, _reader.ReadUInt16); + case DataKind.UInt32: + return ReadPrimitiveTypeArrayElements(length, _reader.ReadUInt32); + case DataKind.UInt64: + return ReadPrimitiveTypeArrayElements(length, _reader.ReadUInt64); + case DataKind.Float4: + return ReadPrimitiveTypeArrayElements(length, _reader.ReadSingle); + case DataKind.Float8: + return ReadPrimitiveTypeArrayElements(length, _reader.ReadDouble); + case DataKind.Decimal: + return ReadPrimitiveTypeArrayElements(length, _reader.ReadDecimal); + default: + throw ExceptionUtilities.UnexpectedValue(kind); + } + } + + private bool[] ConsumeBooleanArray(int length) + { + if (length == 0) + { + // simple check + return Array.Empty(); + } + + var array = new bool[length]; + var wordLength = BitVector.WordsRequired(length); + + var count = 0; + for (var i = 0; i < wordLength; i++) + { + var word = _reader.ReadUInt32(); + + for (var p = 0; p < BitVector.BitsPerWord; p++) + { + if (count >= length) + { + return array; + } + + array[count++] = BitVector.IsTrue(word, p); + } + } + + return array; + } + + private static T[] ReadPrimitiveTypeArrayElements(int length, Func read) + { + if (length == 0) + { + // quick check + return Array.Empty(); + } + + var array = new T[length]; + for (var i = 0; i < array.Length; i++) + { + array[i] = read(); + } + + return array; + } + + private Type ConsumeType() + { + var kind = (DataKind)_reader.ReadByte(); + return ConsumeType(kind); + } + + private Type ConsumeType(DataKind kind) + { + switch (kind) + { + case DataKind.TypeRef_B: + return (Type)_dataMap.GetValue(_reader.ReadByte()); + + case DataKind.TypeRef_S: + return (Type)_dataMap.GetValue(_reader.ReadUInt16()); + + case DataKind.TypeRef: + return (Type)_dataMap.GetValue(_reader.ReadInt32()); + + case DataKind.Type: + int id = _dataMap.GetNextId(); + var assemblyName = this.ConsumeString(); + var typeName = this.ConsumeString(); + + if (_binder == null) + { + throw NoBinderException(typeName); + } + + var type = _binder.GetType(assemblyName, typeName); + _dataMap.AddValue(id, type); + return type; + + default: + throw ExceptionUtilities.UnexpectedValue(kind); + } + } + + private object ConsumeEnum() + { + var enumType = this.ConsumeType(); + var type = Enum.GetUnderlyingType(enumType); + + if (type == typeof(int)) + { + return Enum.ToObject(enumType, _reader.ReadInt32()); + } + + if (type == typeof(short)) + { + return Enum.ToObject(enumType, _reader.ReadInt16()); + } + + if (type == typeof(byte)) + { + return Enum.ToObject(enumType, _reader.ReadByte()); + } + + if (type == typeof(long)) + { + return Enum.ToObject(enumType, _reader.ReadInt64()); + } + + if (type == typeof(sbyte)) + { + return Enum.ToObject(enumType, _reader.ReadSByte()); + } + + if (type == typeof(ushort)) + { + return Enum.ToObject(enumType, _reader.ReadUInt16()); + } + + if (type == typeof(uint)) + { + return Enum.ToObject(enumType, _reader.ReadUInt32()); + } + + if (type == typeof(ulong)) + { + return Enum.ToObject(enumType, _reader.ReadUInt64()); + } + + throw ExceptionUtilities.UnexpectedValue(enumType); + } + + private object ConsumeReadableObject() + { + int id = _dataMap.GetNextId(); + + Type type = this.ConsumeType(); + uint memberCount = this.ConsumeCompressedUInt(); + + if (_binder == null) + { + return NoBinderException(type.FullName); + } + + var reader = _binder.GetReader(type); + if (reader == null) + { + return NoReaderException(type.FullName); + } + + // member values from value stack, copy them into value reader. + if (_valueStack.Count < memberCount) + { + throw new InvalidOperationException($"Deserialization constructor for '{type.Name}' is expected to read {memberCount} values, but only {_valueStack.Count} are available."); + } + + var list = _valueReader._list; + _valueReader._index = 0; + list.Clear(); + + // take members from value stack + for (int i = 0; i < memberCount; i++) + { + list.Add(_valueStack.Pop()); + } + + // reverse list so that first member to be read is first + list.Reverse(); + + // invoke the deserialization constructor to create instance and read & assign members + var instance = reader(_valueReader); + + if (_valueReader._index != memberCount) + { + throw new InvalidOperationException($"Deserialization constructor for '{type.Name}' was expected to read {memberCount} values, but read {_valueReader._index} values instead."); + } + + _dataMap.AddValue(id, instance); + return instance; + } + + private static Exception NoBinderException(string typeName) + { +#if COMPILERCORE + throw new InvalidOperationException(string.Format(CodeAnalysisResources.NoBinderException, typeName)); +#else + throw new InvalidOperationException(string.Format(Microsoft.CodeAnalysis.WorkspacesResources.Cannot_deserialize_type_0_no_binder_supplied, typeName)); +#endif + } + + private static Exception NoReaderException(string typeName) + { +#if COMPILERCORE + throw new InvalidOperationException(string.Format(CodeAnalysisResources.NoReaderException, typeName)); +#else + throw new InvalidOperationException(string.Format(Microsoft.CodeAnalysis.WorkspacesResources.Cannot_deserialize_type_0_it_has_no_deserialization_reader, typeName)); +#endif + } + } +} diff --git a/src/Compilers/Core/Portable/Serialization/StreamObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/StreamObjectWriter.cs new file mode 100644 index 00000000000..cf56ec5b113 --- /dev/null +++ b/src/Compilers/Core/Portable/Serialization/StreamObjectWriter.cs @@ -0,0 +1,886 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using Microsoft.CodeAnalysis; + +namespace Roslyn.Utilities +{ + /// + /// A class that serializes objects to a stream. + /// + internal sealed class StreamObjectWriter : ObjectWriter, IDisposable + { + private readonly BinaryWriter _writer; + private readonly ObjectWriterData _dataMap; + private readonly RecordingObjectBinder _binder; + private readonly CancellationToken _cancellationToken; + private readonly Stack _valueStack; + private readonly ListWriter _valueWriter; + + internal StreamObjectWriter( + Stream stream, + ObjectWriterData defaultData = null, + RecordingObjectBinder binder = null, + CancellationToken cancellationToken = default(CancellationToken)) + { + // String serialization assumes both reader and writer to be of the same endianness. + // It can be adjusted for BigEndian if needed. + Debug.Assert(BitConverter.IsLittleEndian); + + _writer = new BinaryWriter(stream, Encoding.UTF8); + _dataMap = new ObjectWriterData(defaultData); + _binder = binder ?? new SimpleRecordingObjectBinder(); + _cancellationToken = cancellationToken; + _valueStack = new Stack(); + _valueWriter = new ListWriter(); + } + + public ObjectBinder Binder + { + get { return _binder; } + } + + public void Dispose() + { + _dataMap.Dispose(); + } + + public override void WriteBoolean(bool value) + { + _valueStack.Push(Variant.FromBoolean(value)); + Emit(); + } + + public override void WriteByte(byte value) + { + _valueStack.Push(Variant.FromByte(value)); + Emit(); + } + + public override void WriteChar(char ch) + { + _valueStack.Push(Variant.FromChar(ch)); + Emit(); + } + + public override void WriteDecimal(decimal value) + { + _valueStack.Push(Variant.FromDecimal(value)); + Emit(); + } + + public override void WriteDouble(double value) + { + _valueStack.Push(Variant.FromDouble(value)); + Emit(); + } + + public override void WriteSingle(float value) + { + _valueStack.Push(Variant.FromSingle(value)); + Emit(); + } + + public override void WriteInt32(int value) + { + _valueStack.Push(Variant.FromInt32(value)); + Emit(); + } + + public override void WriteInt64(long value) + { + _valueStack.Push(Variant.FromInt64(value)); + Emit(); + } + + public override void WriteSByte(sbyte value) + { + _valueStack.Push(Variant.FromSByte(value)); + Emit(); + } + + public override void WriteInt16(short value) + { + _valueStack.Push(Variant.FromInt16(value)); + Emit(); + } + + public override void WriteUInt32(uint value) + { + _valueStack.Push(Variant.FromUInt32(value)); + Emit(); + } + + public override void WriteUInt64(ulong value) + { + _valueStack.Push(Variant.FromUInt64(value)); + Emit(); + } + + public override void WriteUInt16(ushort value) + { + _valueStack.Push(Variant.FromUInt16(value)); + Emit(); + } + + public override void WriteDateTime(DateTime value) + { + _valueStack.Push(Variant.FromDateTime(value)); + Emit(); + } + + public override void WriteString(string value) + { + if (value == null) + { + _valueStack.Push(Variant.Null); + } + else + { + _valueStack.Push(Variant.FromString(value)); + } + + Emit(); + } + + public override void WriteValue(object value) + { + _valueStack.Push(Variant.FromBoxedObject(value)); + Emit(); + } + + private void Emit() + { + // keep reducing to primitives value that can be written to stream + while (_valueStack.Count > 0) + { + _cancellationToken.ThrowIfCancellationRequested(); + + var value = _valueStack.Pop(); + EmitOrReduce(value); + } + + // write end marker for end of object serialization + _writer.Write((byte)DataKind.End); + } + + private void EmitOrReduce(Variant value) + { + switch (value.Kind) + { + case VariantKind.Null: + _writer.Write((byte)DataKind.Null); + break; + + case VariantKind.Boolean: + _writer.Write((byte)(value.AsBoolean() ? DataKind.Boolean_T : DataKind.Boolean_F)); + break; + + case VariantKind.Byte: + _writer.Write((byte)DataKind.UInt8); + _writer.Write(value.AsByte()); + break; + + case VariantKind.SByte: + _writer.Write((byte)DataKind.Int8); + _writer.Write(value.AsSByte()); + break; + + case VariantKind.Int16: + _writer.Write((byte)DataKind.Int16); + _writer.Write(value.AsInt16()); + break; + + case VariantKind.UInt16: + _writer.Write((byte)DataKind.UInt16); + _writer.Write(value.AsUInt16()); + break; + + case VariantKind.Int32: + { + var v = value.AsInt32(); + if (v >= 0 && v <= 10) + { + _writer.Write((byte)((int)DataKind.Int32_0 + v)); + } + else if (v >= 0 && v < byte.MaxValue) + { + _writer.Write((byte)DataKind.Int32_B); + _writer.Write((byte)v); + } + else if (v >= 0 && v < ushort.MaxValue) + { + _writer.Write((byte)DataKind.Int32_S); + _writer.Write((ushort)v); + } + else + { + _writer.Write((byte)DataKind.Int32); + _writer.Write(v); + } + } + break; + + case VariantKind.UInt32: + { + var v = value.AsUInt32(); + if (v >= 0 && v <= 10) + { + _writer.Write((byte)((int)DataKind.UInt32_0 + v)); + } + else if (v >= 0 && v < byte.MaxValue) + { + _writer.Write((byte)DataKind.UInt32_B); + _writer.Write((byte)v); + } + else if (v >= 0 && v < ushort.MaxValue) + { + _writer.Write((byte)DataKind.UInt32_S); + _writer.Write((ushort)v); + } + else + { + _writer.Write((byte)DataKind.UInt32); + _writer.Write(v); + } + } + break; + + case VariantKind.Int64: + _writer.Write((byte)DataKind.Int64); + _writer.Write(value.AsInt64()); + break; + + case VariantKind.UInt64: + _writer.Write((byte)DataKind.UInt64); + _writer.Write(value.AsUInt64()); + break; + + case VariantKind.Decimal: + _writer.Write((byte)DataKind.Decimal); + _writer.Write(value.AsDecimal()); + break; + + case VariantKind.Float4: + _writer.Write((byte)DataKind.Float4); + _writer.Write(value.AsSingle()); + break; + + case VariantKind.Float8: + _writer.Write((byte)DataKind.Float8); + _writer.Write(value.AsDouble()); + break; + + case VariantKind.DateTime: + _writer.Write((byte)DataKind.DateTime); + _writer.Write(value.AsDateTime().ToBinary()); + break; + + case VariantKind.Char: + _writer.Write((byte)DataKind.Char); + _writer.Write((ushort)value.AsChar()); // write as ushort because write fails on chars that are unicode surrogates + break; + + case VariantKind.String: + EmitString(value.AsString()); + break; + + case VariantKind.BoxedEnum: + var e = value.AsBoxedEnum(); + EmitEnum(e, e.GetType()); + break; + + case VariantKind.Type: + EmitType(value.AsType()); + break; + + case VariantKind.Array: + EmitArray(value.AsArray()); + break; + + case VariantKind.ArrayHeader: + EmitArrayHeader(value.AsArrayHeader()); + break; + + case VariantKind.Object: + EmitOrReduceObject(value.AsObject()); + break; + + case VariantKind.ObjectHeader: + uint memberCount; + EmitObjectHeader(value.AsObjectHeader(out memberCount), memberCount); + break; + } + } + + private class ListWriter : ObjectWriter + { + internal readonly List _list; + + public ListWriter() + { + _list = new List(); + } + + public override void WriteBoolean(bool value) + { + _list.Add(Variant.FromBoolean(value)); + } + + public override void WriteByte(byte value) + { + _list.Add(Variant.FromByte(value)); + } + + public override void WriteChar(char ch) + { + _list.Add(Variant.FromChar(ch)); + } + + public override void WriteDecimal(decimal value) + { + _list.Add(Variant.FromDecimal(value)); + } + + public override void WriteDouble(double value) + { + _list.Add(Variant.FromDouble(value)); + } + + public override void WriteSingle(float value) + { + _list.Add(Variant.FromSingle(value)); + } + + public override void WriteInt32(int value) + { + _list.Add(Variant.FromInt32(value)); + } + + public override void WriteInt64(long value) + { + _list.Add(Variant.FromInt64(value)); + } + + public override void WriteSByte(sbyte value) + { + _list.Add(Variant.FromSByte(value)); + } + + public override void WriteInt16(short value) + { + _list.Add(Variant.FromInt16(value)); + } + + public override void WriteUInt32(uint value) + { + _list.Add(Variant.FromUInt32(value)); + } + + public override void WriteUInt64(ulong value) + { + _list.Add(Variant.FromUInt64(value)); + } + + public override void WriteUInt16(ushort value) + { + _list.Add(Variant.FromUInt16(value)); + } + + public override void WriteDateTime(DateTime value) + { + _list.Add(Variant.FromDateTime(value)); + } + + public override void WriteString(string value) + { + if (value == null) + { + _list.Add(Variant.Null); + } + else + { + _list.Add(Variant.FromString(value)); + } + } + + public override void WriteValue(object value) + { + _list.Add(Variant.FromBoxedObject(value)); + } + } + + private void EmitCompressedUInt(uint value) + { + if (value <= (byte.MaxValue >> 2)) + { + _writer.Write((byte)value); + } + else if (value <= (ushort.MaxValue >> 2)) + { + byte byte0 = (byte)(((value >> 8) & 0xFF) | Byte2Marker); + byte byte1 = (byte)(value & 0xFF); + + // high-bytes to low-bytes + _writer.Write(byte0); + _writer.Write(byte1); + } + else if (value <= (uint.MaxValue >> 2)) + { + // high-bytes to low-bytes + byte byte0 = (byte)(((value >> 24) & 0xFF) | Byte4Marker); + byte byte1 = (byte)((value >> 16) & 0xFF); + byte byte2 = (byte)((value >> 8) & 0xFF); + byte byte3 = (byte)(value & 0xFF); + + // hit-bits with 4-byte marker + _writer.Write(byte0); + _writer.Write(byte1); + _writer.Write(byte2); + _writer.Write(byte3); + } + else + { +#if COMPILERCORE + throw new ArgumentException(CodeAnalysisResources.ValueTooLargeToBeRepresented); +#else + throw new ArgumentException(WorkspacesResources.Value_too_large_to_be_represented_as_a_30_bit_unsigned_integer); +#endif + } + } + + private unsafe void EmitString(string value) + { + if (value == null) + { + _writer.Write((byte)DataKind.Null); + } + else + { + int id; + if (_dataMap.TryGetId(value, out id)) + { + Debug.Assert(id >= 0); + if (id <= byte.MaxValue) + { + _writer.Write((byte)DataKind.StringRef_B); + _writer.Write((byte)id); + } + else if (id <= ushort.MaxValue) + { + _writer.Write((byte)DataKind.StringRef_S); + _writer.Write((ushort)id); + } + else + { + _writer.Write((byte)DataKind.StringRef); + _writer.Write(id); + } + } + else + { + _dataMap.Add(value); + + if (value.IsValidUnicodeString()) + { + // Usual case - the string can be encoded as UTF8: + // We can use the UTF8 encoding of the binary writer. + + _writer.Write((byte)DataKind.StringUtf8); + _writer.Write(value); + } + else + { + _writer.Write((byte)DataKind.StringUtf16); + + // This is rare, just allocate UTF16 bytes for simplicity. + + byte[] bytes = new byte[(uint)value.Length * sizeof(char)]; + fixed (char* valuePtr = value) + { + Marshal.Copy((IntPtr)valuePtr, bytes, 0, bytes.Length); + } + + EmitCompressedUInt((uint)value.Length); + _writer.Write(bytes); + } + } + } + } + + private void EmitEnum(object value, Type enumType) + { + _writer.Write((byte)DataKind.Enum); + this.EmitType(enumType); + + var type = Enum.GetUnderlyingType(enumType); + + if (type == typeof(int)) + { + _writer.Write((int)value); + } + else if (type == typeof(short)) + { + _writer.Write((short)value); + } + else if (type == typeof(byte)) + { + _writer.Write((byte)value); + } + else if (type == typeof(long)) + { + _writer.Write((long)value); + } + else if (type == typeof(sbyte)) + { + _writer.Write((sbyte)value); + } + else if (type == typeof(ushort)) + { + _writer.Write((ushort)value); + } + else if (type == typeof(uint)) + { + _writer.Write((uint)value); + } + else if (type == typeof(ulong)) + { + _writer.Write((ulong)value); + } + else + { + throw ExceptionUtilities.UnexpectedValue(type); + } + } + + private void EmitArray(Array array) + { + var elementType = array.GetType().GetElementType(); + + DataKind elementKind; + if (s_typeMap.TryGetValue(elementType, out elementKind)) + { + int length = array.GetLength(0); + + switch (length) + { + case 0: + _writer.Write((byte)DataKind.Array_0); + break; + case 1: + _writer.Write((byte)DataKind.Array_1); + break; + case 2: + _writer.Write((byte)DataKind.Array_2); + break; + case 3: + _writer.Write((byte)DataKind.Array_3); + break; + default: + _writer.Write((byte)DataKind.Array); + this.EmitCompressedUInt((uint)length); + break; + } + + this.EmitPrimitiveType(elementType, elementKind); + this.EmitPrimitiveTypeArrayElements(elementType, elementKind, array); + } + else + { + var list = _valueWriter._list; + list.Clear(); + + foreach (var element in array) + { + _valueWriter.WriteValue(element); + } + + // push header first so we write it to stream last + _valueStack.Push(Variant.FromArrayHeader(array)); + + // push elements in reverse order so we process first element first + for (int i = list.Count - 1; i >= 0; i--) + { + _valueStack.Push(list[i]); + } + } + } + + private void EmitArrayHeader(Array array) + { + int length = array.GetLength(0); + + switch (length) + { + case 0: + _writer.Write((byte)DataKind.ValueArray_0); + break; + case 1: + _writer.Write((byte)DataKind.ValueArray_1); + break; + case 2: + _writer.Write((byte)DataKind.ValueArray_2); + break; + case 3: + _writer.Write((byte)DataKind.ValueArray_3); + break; + default: + _writer.Write((byte)DataKind.ValueArray); + this.EmitCompressedUInt((uint)length); + break; + } + + var elementType = array.GetType().GetElementType(); + this.EmitType(elementType); + } + + private void EmitPrimitiveTypeArrayElements(Type type, DataKind kind, Array instance) + { + Debug.Assert(s_typeMap[type] == kind); + + // optimization for type underlying binary writer knows about + if (type == typeof(byte)) + { + _writer.Write((byte[])instance); + } + else if (type == typeof(char)) + { + _writer.Write((char[])instance); + } + else if (type == typeof(string)) + { + // optimization for string which object writer has + // its own optimization to reduce repeated string + EmitPrimitiveTypeArrayElements((string[])instance, EmitString); + } + else if (type == typeof(bool)) + { + // optimization for bool array + EmitBooleanArray((bool[])instance); + } + else + { + // otherwise, write elements directly to underlying binary writer + switch (kind) + { + case DataKind.Int8: + EmitPrimitiveTypeArrayElements((sbyte[])instance, _writer.Write); + return; + case DataKind.Int16: + EmitPrimitiveTypeArrayElements((short[])instance, _writer.Write); + return; + case DataKind.Int32: + EmitPrimitiveTypeArrayElements((int[])instance, _writer.Write); + return; + case DataKind.Int64: + EmitPrimitiveTypeArrayElements((long[])instance, _writer.Write); + return; + case DataKind.UInt16: + EmitPrimitiveTypeArrayElements((ushort[])instance, _writer.Write); + return; + case DataKind.UInt32: + EmitPrimitiveTypeArrayElements((uint[])instance, _writer.Write); + return; + case DataKind.UInt64: + EmitPrimitiveTypeArrayElements((ulong[])instance, _writer.Write); + return; + case DataKind.Float4: + EmitPrimitiveTypeArrayElements((float[])instance, _writer.Write); + return; + case DataKind.Float8: + EmitPrimitiveTypeArrayElements((double[])instance, _writer.Write); + return; + case DataKind.Decimal: + EmitPrimitiveTypeArrayElements((decimal[])instance, _writer.Write); + return; + default: + throw ExceptionUtilities.UnexpectedValue(kind); + } + } + } + + private void EmitBooleanArray(bool[] array) + { + // convert bool array to bit array + var bits = BitVector.Create(array.Length); + for (var i = 0; i < array.Length; i++) + { + bits[i] = array[i]; + } + + // send over bit array + foreach (var word in bits.Words()) + { + _writer.Write(word); + } + } + + private static void EmitPrimitiveTypeArrayElements(T[] array, Action write) + { + for (var i = 0; i < array.Length; i++) + { + write(array[i]); + } + } + + private void EmitPrimitiveType(Type type, DataKind kind) + { + Debug.Assert(s_typeMap[type] == kind); + _writer.Write((byte)kind); + } + + private void EmitType(Type type) + { + int id; + if (_dataMap.TryGetId(type, out id)) + { + Debug.Assert(id >= 0); + if (id <= byte.MaxValue) + { + _writer.Write((byte)DataKind.TypeRef_B); + _writer.Write((byte)id); + } + else if (id <= ushort.MaxValue) + { + _writer.Write((byte)DataKind.TypeRef_S); + _writer.Write((ushort)id); + } + else + { + _writer.Write((byte)DataKind.TypeRef); + _writer.Write(id); + } + } + else + { + _dataMap.Add(type); + + _binder?.Record(type); + + _writer.Write((byte)DataKind.Type); + + string assemblyName = type.GetTypeInfo().Assembly.FullName; + string typeName = type.FullName; + + // assembly name + this.EmitString(assemblyName); + + // type name + this.EmitString(typeName); + } + } + + private void EmitOrReduceObject(object instance) + { + _cancellationToken.ThrowIfCancellationRequested(); + + // write object ref if we already know this instance + int id; + if (_dataMap.TryGetId(instance, out id)) + { + Debug.Assert(id >= 0); + if (id <= byte.MaxValue) + { + _writer.Write((byte)DataKind.ObjectRef_B); + _writer.Write((byte)id); + } + else if (id <= ushort.MaxValue) + { + _writer.Write((byte)DataKind.ObjectRef_S); + _writer.Write((ushort)id); + } + else + { + _writer.Write((byte)DataKind.ObjectRef); + _writer.Write(id); + } + } + else + { + var iwriteable = instance as IObjectWritable; + if (iwriteable != null) + { + this.ReduceWritableObject(iwriteable); + return; + } + + throw NotWritableException(instance.GetType().FullName); + } + } + + private void ReduceWritableObject(IObjectWritable instance) + { + var list = _valueWriter._list; + list.Clear(); + + instance.WriteTo(_valueWriter); + + // push first so we write header after all members + _valueStack.Push(Variant.FromObjectHeader(instance, (uint)list.Count)); + + // push all members in reverse order so we process the first member written first + for (int i = list.Count - 1; i >= 0; i--) + { + _valueStack.Push(list[i]); + } + } + + private void EmitObjectHeader(object instance, uint memberCount) + { + _dataMap.Add(instance); + + _writer.Write((byte)DataKind.Object_W); + + Type type = instance.GetType(); + this.EmitType(type); + this.EmitCompressedUInt(memberCount); + + _binder?.Record(instance); + } + + private static Exception NotWritableException(string typeName) + { +#if COMPILERCORE + throw new InvalidOperationException(string.Format(CodeAnalysisResources.NotWritableException, typeName)); +#else + throw new InvalidOperationException(string.Format(WorkspacesResources.The_type_0_cannot_be_written_it_does_not_implement_IObjectWritable, typeName)); +#endif + } + + // we have s_typeMap and s_reversedTypeMap since there is no bidirectional map in compiler + internal static readonly ImmutableDictionary s_typeMap = ImmutableDictionary.CreateRange( + new KeyValuePair[] + { + KeyValuePair.Create(typeof(bool), DataKind.BooleanType), + KeyValuePair.Create(typeof(char), DataKind.Char), + KeyValuePair.Create(typeof(string), DataKind.StringType), + KeyValuePair.Create(typeof(sbyte), DataKind.Int8), + KeyValuePair.Create(typeof(short), DataKind.Int16), + KeyValuePair.Create(typeof(int), DataKind.Int32), + KeyValuePair.Create(typeof(long), DataKind.Int64), + KeyValuePair.Create(typeof(byte), DataKind.UInt8), + KeyValuePair.Create(typeof(ushort), DataKind.UInt16), + KeyValuePair.Create(typeof(uint), DataKind.UInt32), + KeyValuePair.Create(typeof(ulong), DataKind.UInt64), + KeyValuePair.Create(typeof(float), DataKind.Float4), + KeyValuePair.Create(typeof(double), DataKind.Float8), + KeyValuePair.Create(typeof(decimal), DataKind.Decimal), + }); + + internal static readonly ImmutableDictionary s_reverseTypeMap = s_typeMap.ToImmutableDictionary(kv => kv.Value, kv => kv.Key); + + // byte marker for encoding compressed uint + internal static readonly byte ByteMarkerMask = 3 << 6; + internal static readonly byte Byte1Marker = 0; + internal static readonly byte Byte2Marker = 1 << 6; + internal static readonly byte Byte4Marker = 2 << 6; + } +} diff --git a/src/Compilers/Core/Portable/Serialization/Variant.cs b/src/Compilers/Core/Portable/Serialization/Variant.cs new file mode 100644 index 00000000000..23b41ffa74a --- /dev/null +++ b/src/Compilers/Core/Portable/Serialization/Variant.cs @@ -0,0 +1,417 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis; +using System; +using System.Diagnostics; +using System.Reflection; + +namespace Roslyn.Utilities +{ + internal struct Variant + { + public readonly VariantKind Kind; + private readonly decimal _image; + private readonly object _instance; + + private Variant(VariantKind kind, decimal image, object instance = null) + { + Kind = kind; + _image = image; + _instance = instance; + } + + public static readonly Variant Null = new Variant(VariantKind.Null, image: 0, instance: null); + + public static Variant FromBoolean(bool value) + { + return new Variant(VariantKind.Boolean, value ? 1 : 0); + } + + public static Variant FromSByte(sbyte value) + { + return new Variant(VariantKind.SByte, value); + } + + public static Variant FromByte(byte value) + { + return new Variant(VariantKind.Byte, value); + } + + public static Variant FromInt16(short value) + { + return new Variant(VariantKind.Int16, value); + } + + public static Variant FromUInt16(ushort value) + { + return new Variant(VariantKind.UInt16, value); + } + + public static Variant FromInt32(int value) + { + return new Variant(VariantKind.Int32, value); + } + + public static Variant FromInt64(long value) + { + return new Variant(VariantKind.Int64, value); + } + + public static Variant FromUInt32(uint value) + { + return new Variant(VariantKind.UInt32, value); + } + + public static Variant FromUInt64(ulong value) + { + return new Variant(VariantKind.UInt64, value); + } + + public static Variant FromSingle(float value) + { + return new Variant(VariantKind.Float4, BitConverter.DoubleToInt64Bits(value)); + } + + public static Variant FromDouble(double value) + { + return new Variant(VariantKind.Float8, BitConverter.DoubleToInt64Bits(value)); + } + + public static Variant FromChar(char value) + { + return new Variant(VariantKind.Char, value); + } + + public static Variant FromString(string value) + { + return new Variant(VariantKind.String, image: 0, instance: value); + } + + public static Variant FromDateTime(DateTime value) + { + return new Variant(VariantKind.DateTime, value.ToBinary()); + } + + public static Variant FromDecimal(Decimal value) + { + return new Variant(VariantKind.Decimal, value); + } + + public static Variant FromBoxedEnum(object value) + { + return new Variant(VariantKind.BoxedEnum, image: 0, instance: value); + } + + public static Variant FromType(Type value) + { + return new Variant(VariantKind.Type, image: 0, instance: value); + } + + public static Variant FromArray(Array array) + { + return new Variant(VariantKind.Array, image: 0, instance: array); + } + + public static Variant FromArrayHeader(Array array) + { + return new Variant(VariantKind.ArrayHeader, image: 0, instance: array); + } + + public static Variant FromObject(object value) + { + return new Variant(VariantKind.Object, image: 0, instance: value); + } + + public static Variant FromObjectHeader(object value, uint memberCount) + { + return new Variant(VariantKind.ObjectHeader, image: memberCount, instance: value); + } + + public bool AsBoolean() + { + Debug.Assert(Kind == VariantKind.Boolean); + return _image != 0; + } + + public sbyte AsSByte() + { + Debug.Assert(Kind == VariantKind.SByte); + return (sbyte)_image; + } + + public byte AsByte() + { + Debug.Assert(Kind == VariantKind.Byte); + return (byte)_image; + } + + public short AsInt16() + { + Debug.Assert(Kind == VariantKind.Int16); + return (short)_image; + } + + public ushort AsUInt16() + { + Debug.Assert(Kind == VariantKind.UInt16); + return (ushort)_image; + } + + public int AsInt32() + { + Debug.Assert(Kind == VariantKind.Int32); + return (int)_image; + } + + public uint AsUInt32() + { + Debug.Assert(Kind == VariantKind.UInt32); + return (uint)_image; + } + + public long AsInt64() + { + Debug.Assert(Kind == VariantKind.Int64); + return (long)_image; + } + + public ulong AsUInt64() + { + Debug.Assert(Kind == VariantKind.UInt64); + return (ulong)_image; + } + + public decimal AsDecimal() + { + Debug.Assert(Kind == VariantKind.Decimal); + return _image; + } + + public float AsSingle() + { + Debug.Assert(Kind == VariantKind.Float4); + return (float)BitConverter.Int64BitsToDouble((long)_image); + } + + public double AsDouble() + { + Debug.Assert(Kind == VariantKind.Float8); + return BitConverter.Int64BitsToDouble((long)_image); + } + + public char AsChar() + { + Debug.Assert(Kind == VariantKind.Char); + return (char)_image; + } + + public string AsString() + { + Debug.Assert(Kind == VariantKind.String); + return (string)_instance; + } + + public DateTime AsDateTime() + { + Debug.Assert(Kind == VariantKind.DateTime); + return DateTime.FromBinary((long)_image); + } + + public object AsObject() + { + Debug.Assert(Kind == VariantKind.Object); + return _instance; + } + + public object AsObjectHeader() + { + uint memberCount; + return AsObjectHeader(out memberCount); + } + + public object AsObjectHeader(out uint memberCount) + { + Debug.Assert(Kind == VariantKind.ObjectHeader); + memberCount = (uint)_image; + return _instance; + } + + public object AsBoxedEnum() + { + Debug.Assert(Kind == VariantKind.BoxedEnum); + return _instance; + } + + public Type AsType() + { + Debug.Assert(Kind == VariantKind.Type); + return (Type)_instance; + } + + public Array AsArray() + { + Debug.Assert(Kind == VariantKind.Array); + return (Array)_instance; + } + + public Array AsArrayHeader() + { + Debug.Assert(Kind == VariantKind.ArrayHeader); + return (Array)_instance; + } + + public static Variant FromBoxedObject(object value) + { + if (value == null) + { + return Variant.Null; + } + else + { + var type = value.GetType(); + var typeInfo = type.GetTypeInfo(); + + if (typeInfo.IsEnum) + { + return FromBoxedEnum(value); + } + else if (type == typeof(bool)) + { + return FromBoolean((bool)value); + } + else if (type == typeof(int)) + { + return FromInt32((int)value); + } + else if (type == typeof(string)) + { + return FromString((string)value); + } + else if (type == typeof(short)) + { + return FromInt16((short)value); + } + else if (type == typeof(long)) + { + return FromInt64((long)value); + } + else if (type == typeof(char)) + { + return FromChar((char)value); + } + else if (type == typeof(sbyte)) + { + return FromSByte((sbyte)value); + } + else if (type == typeof(byte)) + { + return FromByte((byte)value); + } + else if (type == typeof(ushort)) + { + return FromUInt16((ushort)value); + } + else if (type == typeof(uint)) + { + return FromUInt32((uint)value); + } + else if (type == typeof(ulong)) + { + return FromUInt64((ulong)value); + } + else if (type == typeof(decimal)) + { + return FromDecimal((decimal)value); + } + else if (type == typeof(float)) + { + return FromSingle((float)value); + } + else if (type == typeof(double)) + { + return FromDouble((double)value); + } + else if (type == typeof(DateTime)) + { + return FromDateTime((DateTime)value); + } + else if (type.IsArray) + { + var instance = (Array)value; + + if (instance.Rank > 1) + { +#if COMPILERCORE + throw new InvalidOperationException(CodeAnalysisResources.ArraysWithMoreThanOneDimensionCannotBeSerialized); +#else + throw new InvalidOperationException(WorkspacesResources.Arrays_with_more_than_one_dimension_cannot_be_serialized); +#endif + } + + return Variant.FromArray(instance); + } + else if (value is Type) + { + return Variant.FromType((Type)value); + } + else + { + return Variant.FromObject(value); + } + } + } + + public object ToBoxedObject() + { + switch (this.Kind) + { + case VariantKind.Array: + return this.AsArray(); + case VariantKind.ArrayHeader: + return this.AsArrayHeader(); + case VariantKind.Boolean: + return this.AsBoolean(); + case VariantKind.BoxedEnum: + return this.AsBoxedEnum(); + case VariantKind.Byte: + return this.AsByte(); + case VariantKind.Char: + return this.AsChar(); + case VariantKind.DateTime: + return this.AsDateTime(); + case VariantKind.Decimal: + return this.AsDecimal(); + case VariantKind.Float4: + return this.AsSingle(); + case VariantKind.Float8: + return this.AsDouble(); + case VariantKind.Int16: + return this.AsInt16(); + case VariantKind.Int32: + return this.AsInt32(); + case VariantKind.Int64: + return this.AsInt64(); + case VariantKind.Null: + return null; + case VariantKind.Object: + return this.AsObject(); + case VariantKind.ObjectHeader: + return this.AsObjectHeader(); + case VariantKind.SByte: + return this.AsSByte(); + case VariantKind.String: + return this.AsString(); + case VariantKind.Type: + return this.AsType(); + case VariantKind.UInt16: + return this.AsUInt16(); + case VariantKind.UInt32: + return this.AsUInt32(); + case VariantKind.UInt64: + return this.AsUInt64(); + default: + throw ExceptionUtilities.UnexpectedValue(this.Kind); + } + } + } +} \ No newline at end of file diff --git a/src/Compilers/Core/Portable/Serialization/VariantKind.cs b/src/Compilers/Core/Portable/Serialization/VariantKind.cs new file mode 100644 index 00000000000..d543a5d9aae --- /dev/null +++ b/src/Compilers/Core/Portable/Serialization/VariantKind.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Roslyn.Utilities +{ + internal enum VariantKind + { + None = 0, + Null, + Boolean, + SByte, + Byte, + Int16, + UInt16, + Int32, + UInt32, + Int64, + UInt64, + Decimal, + Float4, + Float8, + Char, + String, + DateTime, + Object, + ObjectHeader, + BoxedEnum, + Array, + ArrayHeader, + Type + } +} \ No newline at end of file diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs index 0fc209d20cd..712057e6994 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs @@ -1248,7 +1248,7 @@ public virtual void SerializeTo(Stream stream, CancellationToken cancellationTok throw new InvalidOperationException(CodeAnalysisResources.TheStreamCannotBeWrittenTo); } - using (var writer = new ObjectWriter(stream, GetDefaultObjectWriterData(), binder: s_defaultBinder, cancellationToken: cancellationToken)) + using (var writer = new StreamObjectWriter(stream, GetDefaultObjectWriterData(), binder: s_defaultBinder, cancellationToken: cancellationToken)) { writer.WriteValue(this.Green); } diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb index bf6ac93474e..fb2f80f9ed9 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb @@ -132,7 +132,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ''' Deserialize a syntax node from a byte stream. ''' Public Shared Function DeserializeFrom(stream As IO.Stream, Optional cancellationToken As CancellationToken = Nothing) As SyntaxNode - Using reader = New ObjectReader(stream, defaultData:=GetDefaultObjectReaderData(), binder:=s_defaultBinder) + Using reader = New StreamObjectReader(stream, defaultData:=GetDefaultObjectReaderData(), binder:=s_defaultBinder) Return DirectCast(reader.ReadValue(), InternalSyntax.VisualBasicSyntaxNode).CreateRed(Nothing, 0) End Using End Function diff --git a/src/EditorFeatures/Core/Implementation/TodoComment/TodoCommentState.cs b/src/EditorFeatures/Core/Implementation/TodoComment/TodoCommentState.cs index 52cfeff4fe3..1fdf6676c22 100644 --- a/src/EditorFeatures/Core/Implementation/TodoComment/TodoCommentState.cs +++ b/src/EditorFeatures/Core/Implementation/TodoComment/TodoCommentState.cs @@ -35,7 +35,7 @@ protected override Data TryGetExistingData(Stream stream, Document value, Cancel var list = SharedPools.Default>().AllocateAndClear(); try { - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { var format = reader.ReadString(); if (!string.Equals(format, FormatVersion)) @@ -63,7 +63,7 @@ protected override Data TryGetExistingData(Stream stream, Document value, Cancel protected override void WriteTo(Stream stream, Data data, CancellationToken cancellationToken) { - using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) + using (var writer = new StreamObjectWriter(stream, cancellationToken: cancellationToken)) { writer.WriteString(FormatVersion); data.TextVersion.WriteTo(writer); diff --git a/src/EditorFeatures/Test/Utilities/BloomFilterTests.cs b/src/EditorFeatures/Test/Utilities/BloomFilterTests.cs index d6b7e43d8c7..942c240b337 100644 --- a/src/EditorFeatures/Test/Utilities/BloomFilterTests.cs +++ b/src/EditorFeatures/Test/Utilities/BloomFilterTests.cs @@ -115,14 +115,14 @@ public void TestSerialization() var stream = new MemoryStream(); var bloomFilter = new BloomFilter(0.001, false, new[] { "Hello, World" }); - using (var writer = new ObjectWriter(stream)) + using (var writer = new StreamObjectWriter(stream)) { bloomFilter.WriteTo(writer); } stream.Position = 0; - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { var rehydratedFilter = BloomFilter.ReadFrom(reader); Assert.True(bloomFilter.IsEquivalent(rehydratedFilter)); diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeState.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeState.cs index faee6d0f7f4..f2ff56e891c 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeState.cs +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeState.cs @@ -33,7 +33,7 @@ protected override Data TryGetExistingData(Stream stream, Document value, Cancel { try { - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { var format = reader.ReadString(); if (!string.Equals(format, FormatVersion, StringComparison.InvariantCulture)) @@ -56,7 +56,7 @@ protected override Data TryGetExistingData(Stream stream, Document value, Cancel protected override void WriteTo(Stream stream, Data data, CancellationToken cancellationToken) { - using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) + using (var writer = new StreamObjectWriter(stream, cancellationToken: cancellationToken)) { writer.WriteString(FormatVersion); data.TextVersion.WriteTo(writer); diff --git a/src/VisualStudio/Core/Def/Implementation/Diagnostics/VisualStudioDiagnosticAnalyzerExecutor.cs b/src/VisualStudio/Core/Def/Implementation/Diagnostics/VisualStudioDiagnosticAnalyzerExecutor.cs index dd4ad4464b3..834a7f45b09 100644 --- a/src/VisualStudio/Core/Def/Implementation/Diagnostics/VisualStudioDiagnosticAnalyzerExecutor.cs +++ b/src/VisualStudio/Core/Def/Implementation/Diagnostics/VisualStudioDiagnosticAnalyzerExecutor.cs @@ -140,7 +140,7 @@ private CompilationWithAnalyzers CreateAnalyzerDriver(CompilationWithAnalyzers a // handling of cancellation and exception var version = await DiagnosticIncrementalAnalyzer.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { return DiagnosticResultSerializer.Deserialize(reader, analyzerMap, project, version, cancellationToken); } diff --git a/src/VisualStudio/Core/Def/Implementation/Versions/SemanticVersionTrackingService.cs b/src/VisualStudio/Core/Def/Implementation/Versions/SemanticVersionTrackingService.cs index 087aae3921f..0dbc767e6db 100644 --- a/src/VisualStudio/Core/Def/Implementation/Versions/SemanticVersionTrackingService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Versions/SemanticVersionTrackingService.cs @@ -134,7 +134,7 @@ private static bool TryReadFrom(Project project, string keyName, out Versions ve try { - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { var formatVersion = reader.ReadInt32(); if (formatVersion != SerializationFormat) @@ -207,7 +207,7 @@ private static async Task WriteToDependentSemanticVersionAsync(IPersistentStorag IPersistentStorage storage, string keyName, VersionStamp projectVersion, VersionStamp semanticVersion, CancellationToken cancellationToken) { using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) + using (var writer = new StreamObjectWriter(stream, cancellationToken: cancellationToken)) { writer.WriteInt32(SerializationFormat); projectVersion.WriteTo(writer); diff --git a/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs b/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs index f4d005c8563..48684e330d9 100644 --- a/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs +++ b/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs @@ -130,7 +130,7 @@ public async Task RequestAssetAsync(int serviceId, byte[][] checksums, string st using (Logger.LogBlock(FunctionId.JsonRpcSession_RequestAssetAsync, streamName, _source.Token)) using (var stream = await DirectStream.GetAsync(streamName, _source.Token).ConfigureAwait(false)) { - using (var writer = new ObjectWriter(stream)) + using (var writer = new StreamObjectWriter(stream)) { writer.WriteInt32(serviceId); diff --git a/src/Workspaces/Core/Desktop/Workspace/Esent/EsentPersistentStorage_IdentifierTable.cs b/src/Workspaces/Core/Desktop/Workspace/Esent/EsentPersistentStorage_IdentifierTable.cs index 1655f6e4de7..219a58369bf 100644 --- a/src/Workspaces/Core/Desktop/Workspace/Esent/EsentPersistentStorage_IdentifierTable.cs +++ b/src/Workspaces/Core/Desktop/Workspace/Esent/EsentPersistentStorage_IdentifierTable.cs @@ -85,7 +85,7 @@ private VersionStamp GetIdentifierSetVersion(EsentStorage.Key key, int identifie return VersionStamp.Default; } - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { return VersionStamp.ReadFrom(reader); } @@ -149,7 +149,7 @@ private bool ReadIdentifierPositions(EsentStorage.Key key, int identifierId, Lis return true; } - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { var formatVersion = reader.ReadString(); if (formatVersion != IdentifierSetSerializationVersion) @@ -240,7 +240,7 @@ private bool WriteIdentifierLocations(EsentStorage.Key key, Document document, V identifierId = identifierMap[identifier]; using (var stream = accessor.GetWriteStream(key, identifierId)) - using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) + using (var writer = new StreamObjectWriter(stream, cancellationToken: cancellationToken)) { writer.WriteString(IdentifierSetSerializationVersion); WriteList(writer, positions); @@ -298,7 +298,7 @@ private void Free(Dictionary> map) accessor.PrepareBatchOneInsert(); using (var stream = accessor.GetWriteStream(key, identifierId)) - using (var writer = new ObjectWriter(stream)) + using (var writer = new StreamObjectWriter(stream)) { version.WriteTo(writer); } diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs index ab86e28596d..d3d7a295697 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs @@ -39,7 +39,7 @@ public DiagnosticDataSerializer(VersionStamp analyzerVersion, VersionStamp versi public async Task SerializeAsync(object documentOrProject, string key, ImmutableArray items, CancellationToken cancellationToken) { using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) + using (var writer = new StreamObjectWriter(stream, cancellationToken: cancellationToken)) { WriteTo(writer, items, cancellationToken); @@ -68,7 +68,7 @@ public async Task>> DeserializeAsync(ob return null; } - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { // we return StrongBox rather than ImmutableArray due to task lib's issue with allocations // when returning default(value type) diff --git a/src/Workspaces/Core/Portable/Execution/AbstractReferenceSerializationService.cs b/src/Workspaces/Core/Portable/Execution/AbstractReferenceSerializationService.cs index 978db3126f0..e0f6903cb05 100644 --- a/src/Workspaces/Core/Portable/Execution/AbstractReferenceSerializationService.cs +++ b/src/Workspaces/Core/Portable/Execution/AbstractReferenceSerializationService.cs @@ -47,7 +47,7 @@ public Checksum CreateChecksum(MetadataReference reference, CancellationToken ca public Checksum CreateChecksum(AnalyzerReference reference, CancellationToken cancellationToken) { using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) + using (var writer = new StreamObjectWriter(stream, cancellationToken: cancellationToken)) { WriteTo(reference, writer, cancellationToken); @@ -180,7 +180,7 @@ private void WritePortableExecutableReferencePropertiesTo(PortableExecutableRefe private Checksum CreatePortableExecutableReferenceChecksum(PortableExecutableReference reference, CancellationToken cancellationToken) { using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) + using (var writer = new StreamObjectWriter(stream, cancellationToken: cancellationToken)) { WritePortableExecutableReferencePropertiesTo(reference, writer, cancellationToken); WriteMvidsTo(TryGetMetadata(reference), writer, cancellationToken); diff --git a/src/Workspaces/Core/Portable/Execution/CustomAsset.cs b/src/Workspaces/Core/Portable/Execution/CustomAsset.cs index 84b49a17160..408c9969948 100644 --- a/src/Workspaces/Core/Portable/Execution/CustomAsset.cs +++ b/src/Workspaces/Core/Portable/Execution/CustomAsset.cs @@ -40,7 +40,7 @@ public override Task WriteObjectToAsync(ObjectWriter writer, CancellationToken c private static Checksum CreateChecksumFromStreamWriter(string kind, Action writer) { using (var stream = SerializableBytes.CreateWritableStream()) - using (var objectWriter = new ObjectWriter(stream)) + using (var objectWriter = new StreamObjectWriter(stream)) { objectWriter.WriteString(kind); writer(objectWriter, CancellationToken.None); diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index f727f42da46..64d3666d595 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -100,7 +100,7 @@ internal partial class SymbolTreeInfo { if (stream != null) { - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { // We have some previously persisted data. Attempt to read it back. // If we're able to, and the version of the persisted data matches @@ -129,7 +129,7 @@ internal partial class SymbolTreeInfo if (result != null) { using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) + using (var writer = new StreamObjectWriter(stream, cancellationToken: cancellationToken)) { writeObject(writer, result); stream.Position = 0; diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/AbstractPersistableState.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/AbstractPersistableState.cs index db68e193da4..bd06a22494f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/AbstractPersistableState.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/AbstractPersistableState.cs @@ -53,7 +53,7 @@ private static bool TryReadVersion(ObjectReader reader, string formatVersion, ou return null; } - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { VersionStamp persistVersion; if (TryReadVersion(reader, formatVersion, out persistVersion) && @@ -77,7 +77,7 @@ private static bool TryReadVersion(ObjectReader reader, string formatVersion, ou // attempt to load from persisted state using (var storage = persistentStorageService.GetStorage(document.Project.Solution)) using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) + using (var writer = new StreamObjectWriter(stream, cancellationToken: cancellationToken)) { data.WriteVersion(writer, formatVersion); data.WriteTo(writer); @@ -100,7 +100,7 @@ protected static async Task PrecalculatedAsync(Document document, string p { if (stream != null) { - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { VersionStamp persistVersion; return TryReadVersion(reader, formatVersion, out persistVersion) && diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs index 6ea4937f3bc..0b21a71a2ed 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs @@ -27,7 +27,7 @@ public static Checksum Create(Stream stream) public static Checksum Create(string kind, Checksum checksum) { using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream)) + using (var writer = new StreamObjectWriter(stream)) { writer.WriteString(kind); checksum.WriteTo(writer); @@ -40,7 +40,7 @@ public static Checksum Create(string kind, TChecksums checksums) where TChecksums : IEnumerable { using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream)) + using (var writer = new StreamObjectWriter(stream)) { writer.WriteString(kind); @@ -56,7 +56,7 @@ public static Checksum Create(string kind, TChecksums checksums) public static Checksum Create(T value, string kind, Serializer serializer) { using (var stream = SerializableBytes.CreateWritableStream()) - using (var objectWriter = new ObjectWriter(stream)) + using (var objectWriter = new StreamObjectWriter(stream)) { objectWriter.WriteString(kind); serializer.Serialize(value, objectWriter, CancellationToken.None); diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index 5d9b254a910..93276820232 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -179,6 +179,9 @@ InternalUtilities\WeakReferenceExtensions.cs + + Serialization\DataKind.cs + Serialization\FixedObjectBinder.cs @@ -197,9 +200,6 @@ Serialization\ObjectReaderData.cs - - Serialization\ObjectReaderWriterBase.cs - Serialization\ObjectWriter.cs @@ -290,6 +290,18 @@ + + Serialization\StreamObjectReader.cs + + + Serialization\StreamObjectWriter.cs + + + Serialization\Variant.cs + + + Serialization\VariantKind.cs + Shared\UnicodeCharacterUtilities.cs diff --git a/src/Workspaces/CoreTest/Execution/Extensions.cs b/src/Workspaces/CoreTest/Execution/Extensions.cs index 2a9fd02559b..732b7f3aa39 100644 --- a/src/Workspaces/CoreTest/Execution/Extensions.cs +++ b/src/Workspaces/CoreTest/Execution/Extensions.cs @@ -21,13 +21,13 @@ public static async Task GetValueAsync(this ISolutionSynchronizationServic var syncObject = service.GetRemotableData(checksum, CancellationToken.None); using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream)) + using (var writer = new StreamObjectWriter(stream)) { // serialize asset to bits await syncObject.WriteObjectToAsync(writer, CancellationToken.None).ConfigureAwait(false); stream.Position = 0; - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { // deserialize bits to object var serializer = syncService.Serializer_TestOnly; diff --git a/src/Workspaces/CoreTest/Execution/SnapshotSerializationTests.cs b/src/Workspaces/CoreTest/Execution/SnapshotSerializationTests.cs index 9c530a95452..75a7846e649 100644 --- a/src/Workspaces/CoreTest/Execution/SnapshotSerializationTests.cs +++ b/src/Workspaces/CoreTest/Execution/SnapshotSerializationTests.cs @@ -448,12 +448,12 @@ private static async Task VerifyOptionSetsAsync(Workspace workspace, string lang var asset = assetBuilder.Build(workspace.Options, language, CancellationToken.None); using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream)) + using (var writer = new StreamObjectWriter(stream)) { await asset.WriteObjectToAsync(writer, CancellationToken.None).ConfigureAwait(false); stream.Position = 0; - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { var recovered = serializer.Deserialize(asset.Kind, reader, CancellationToken.None); var assetFromStorage = assetBuilder.Build(recovered, language, CancellationToken.None); @@ -556,12 +556,12 @@ private async Task GetSolutionAsync(ISolutionSynchronizationService se private static async Task CloneAssetAsync(Serializer serializer, RemotableData asset) { using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream)) + using (var writer = new StreamObjectWriter(stream)) { await asset.WriteObjectToAsync(writer, CancellationToken.None).ConfigureAwait(false); stream.Position = 0; - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { var recovered = serializer.Deserialize(asset.Kind, reader, CancellationToken.None); var assetFromStorage = SolutionAsset.Create(serializer.CreateChecksum(recovered, CancellationToken.None), recovered, serializer); diff --git a/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs b/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs index f811c5a3d7c..f25c8fe4bf3 100644 --- a/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs +++ b/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs @@ -547,13 +547,13 @@ public async Task TestSymbolTreeInfoSerialization() using (var writerStream = new MemoryStream()) { - using (var writer = new ObjectWriter(writerStream)) + using (var writer = new StreamObjectWriter(writerStream)) { info.WriteTo(writer); } using (var readerStream = new MemoryStream(writerStream.ToArray())) - using (var reader = new ObjectReader(readerStream)) + using (var reader = new StreamObjectReader(readerStream)) { var readInfo = SymbolTreeInfo.ReadSymbolTreeInfo_ForTestingPurposesOnly(reader); diff --git a/src/Workspaces/CoreTest/SerializationTests.cs b/src/Workspaces/CoreTest/SerializationTests.cs index 0f8c1de59be..877170ab951 100644 --- a/src/Workspaces/CoreTest/SerializationTests.cs +++ b/src/Workspaces/CoreTest/SerializationTests.cs @@ -42,13 +42,13 @@ public void TestNameSimplificationAnnotationSerialization() public void VersionStamp_RoundTripText() { using (var writerStream = new MemoryStream()) - using (var writer = new ObjectWriter(writerStream)) + using (var writer = new StreamObjectWriter(writerStream)) { var versionStamp = VersionStamp.Create(); versionStamp.WriteTo(writer); using (var readerStream = new MemoryStream(writerStream.ToArray())) - using (var reader = new ObjectReader(readerStream)) + using (var reader = new StreamObjectReader(readerStream)) { var deserializedVersionStamp = VersionStamp.ReadFrom(reader); diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs index d818ce6043d..032898389eb 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs @@ -71,7 +71,7 @@ private async Task SerializeDiagnosticResultAsync(string streamName, DiagnosticA using (RoslynLogger.LogBlock(FunctionId.CodeAnalysisService_SerializeDiagnosticResultAsync, GetResultLogInfo, result, CancellationToken)) using (var stream = await DirectStream.GetAsync(streamName, CancellationToken).ConfigureAwait(false)) { - using (var writer = new ObjectWriter(stream)) + using (var writer = new StreamObjectWriter(stream)) { DiagnosticResultSerializer.Serialize(writer, result, CancellationToken); } diff --git a/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.JsonRpcAssetSource.cs b/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.JsonRpcAssetSource.cs index 4fc1dce8db4..81b725ff5c9 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.JsonRpcAssetSource.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.JsonRpcAssetSource.cs @@ -58,7 +58,7 @@ public JsonRpcAssetSource(JsonRpc rpc, TraceSource logger, CancellationToken ass Stream stream, TraceSource logger, int serviceId, ISet checksums, CancellationToken cancellationToken) { var results = new List>(); - using (var reader = new ObjectReader(stream)) + using (var reader = new StreamObjectReader(stream)) { var responseServiceId = reader.ReadInt32(); Contract.ThrowIfFalse(serviceId == responseServiceId); -- GitLab