1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
4 using System.Runtime.Serialization.Formatters.Binary;
5 using System.Runtime.Serialization;
6 using System.Collections;
8 using System.Diagnostics;
9 using System.Diagnostics.CodeAnalysis;
11 namespace System.ComponentModel.Design
14 /// Provides support for design-time license context serialization.
16 public class DesigntimeLicenseContextSerializer
18 internal const byte BinaryWriterMagic = 255;
20 private static bool EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization { get; } = AppContext.TryGetSwitch("System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization", out bool isEnabled) ? isEnabled : false;
23 private DesigntimeLicenseContextSerializer()
28 /// Serializes the licenses within the specified design-time license context
29 /// using the specified key and output stream.
31 public static void Serialize(Stream o, string cryptoKey, DesigntimeLicenseContext context)
33 if (EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization)
35 SerializeWithBinaryFormatter(o, cryptoKey, context);
39 using (BinaryWriter writer = new BinaryWriter(o, encoding: Text.Encoding.UTF8, leaveOpen: true))
41 writer.Write(BinaryWriterMagic); // flag to identify BinaryWriter
42 writer.Write(cryptoKey);
43 writer.Write(context._savedLicenseKeys.Count);
44 foreach (DictionaryEntry keyAndValue in context._savedLicenseKeys)
46 writer.Write(keyAndValue.Key.ToString()!);
47 writer.Write(keyAndValue.Value!.ToString()!);
53 private static void SerializeWithBinaryFormatter(Stream o, string cryptoKey, DesigntimeLicenseContext context)
55 IFormatter formatter = new BinaryFormatter();
56 #pragma warning disable SYSLIB0011
57 #pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
58 formatter.Serialize(o, new object[] { cryptoKey, context._savedLicenseKeys });
59 #pragma warning restore IL2026
60 #pragma warning restore SYSLIB0011
63 private sealed class StreamWrapper : Stream
65 private Stream _stream;
66 private bool _readFirstByte;
67 internal byte _firstByte;
68 public StreamWrapper(Stream stream)
71 _readFirstByte = false;
75 public override bool CanRead => _stream.CanRead;
77 public override bool CanSeek => _stream.CanSeek;
79 public override bool CanWrite => _stream.CanWrite;
81 public override long Length => _stream.Length;
83 public override long Position { get => _stream.Position; set => _stream.Position = value; }
85 public override void Flush() => _stream.Flush();
87 public override int Read(byte[] buffer, int offset, int count) =>
88 Read(new Span<byte>(buffer, offset, count));
90 public override int Read(Span<byte> buffer)
92 Debug.Assert(_stream.Position != 0, "Expected the first byte to be read first");
93 if (_stream.Position == 1)
95 Debug.Assert(_readFirstByte);
96 // Add the first byte read by ReadByte into buffer here
97 buffer[0] = _firstByte;
98 return _stream.Read(buffer.Slice(1)) + 1;
100 return _stream.Read(buffer);
103 public override long Seek(long offset, SeekOrigin origin) => _stream.Seek(offset, origin);
105 public override void SetLength(long value) => _stream.SetLength(value);
107 public override void Write(byte[] buffer, int offset, int count) => _stream.Write(buffer, offset, count);
109 public override int ReadByte()
111 byte read = (byte)_stream.ReadByte();
113 _readFirstByte = true;
119 /// During deserialization, the stream passed in may be binary formatted or may have used binary writer. This is a quick test to discern between them.
121 private static bool StreamIsBinaryFormatted(StreamWrapper stream)
123 // For binary formatter, the first byte is the SerializationHeaderRecord and has a value 0
124 int firstByte = stream.ReadByte();
133 private static void DeserializeUsingBinaryFormatter(StreamWrapper wrappedStream, string cryptoKey, RuntimeLicenseContext context)
135 if (EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization)
137 #pragma warning disable SYSLIB0011
138 IFormatter formatter = new BinaryFormatter();
140 #pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
141 object obj = formatter.Deserialize(wrappedStream);
142 #pragma warning restore IL2026
143 #pragma warning restore SYSLIB0011
145 if (obj is object[] value)
147 if (value[0] is string && (string)value[0] == cryptoKey)
149 context._savedLicenseKeys = (Hashtable)value[1];
155 throw new NotSupportedException(SR.BinaryFormatterMessage);
159 internal static void Deserialize(Stream o, string cryptoKey, RuntimeLicenseContext context)
161 StreamWrapper wrappedStream = new StreamWrapper(o);
162 if (StreamIsBinaryFormatted(wrappedStream))
164 DeserializeUsingBinaryFormatter(wrappedStream, cryptoKey, context);
168 using (BinaryReader reader = new BinaryReader(wrappedStream, encoding: Text.Encoding.UTF8, leaveOpen: true))
170 byte binaryWriterIdentifier = wrappedStream._firstByte;
171 Debug.Assert(binaryWriterIdentifier == BinaryWriterMagic, $"Expected the first byte to be {BinaryWriterMagic}");
172 string streamCryptoKey = reader.ReadString();
173 int numEntries = reader.ReadInt32();
174 if (streamCryptoKey == cryptoKey)
176 context._savedLicenseKeys!.Clear();
177 for (int i = 0; i < numEntries; i++)
179 string key = reader.ReadString();
180 string value = reader.ReadString();
181 context._savedLicenseKeys.Add(key, value);