Skip to content

Commit f7bfeca

Browse files
committed
address code review feedback:
- prefer NET over NETCOREAPP when writing #if defines - add ConditionalTheory to make the SkipTestException actually work - ObjectNullMultiple must not allow for 0 inputs - preallocate the List<T> for TFMs other than .NET
1 parent 489c66a commit f7bfeca

File tree

13 files changed

+84
-55
lines changed

13 files changed

+84
-55
lines changed

src/libraries/System.Runtime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/ArrayRecord.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ private protected ArrayRecord(ArrayInfo arrayInfo)
6767
[RequiresDynamicCode("The code for an array of the specified type might not be available.")]
6868
public Array GetArray(Type expectedArrayType, bool allowNulls = true)
6969
{
70-
#if NETCOREAPP
70+
#if NET
7171
ArgumentNullException.ThrowIfNull(expectedArrayType);
7272
#else
7373
if (expectedArrayType is null)
@@ -153,6 +153,7 @@ private protected ArrayRecord(ArrayInfo arrayInfo) : base(arrayInfo)
153153
/// <see langword="true" /> to permit <see langword="null" /> values within the array;
154154
/// otherwise, <see langword="false" />.
155155
/// </param>
156+
/// <returns>An array filled with the data provided in the serialized records.</returns>
156157
public abstract T?[] GetArray(bool allowNulls = true);
157158

158159
#pragma warning disable IL3051 // RequiresDynamicCode is not required in this particualar case

src/libraries/System.Runtime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/ArraySinglePrimitiveRecord.cs

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -63,39 +63,30 @@ internal static IReadOnlyList<T> DecodePrimitiveTypes(BinaryReader reader, int c
6363
{
6464
if (typeof(T) == typeof(byte) && reader.IsDataAvailable(count))
6565
{
66-
T[] result = (T[])(object)reader.ReadBytes(count);
67-
// This might be less than the number of bytes requested if the end of the stream is reached.
68-
if (result.Length != count)
69-
{
70-
ThrowHelper.ThrowEndOfStreamException();
71-
}
72-
return result;
66+
return (T[])(object)reader.ReadBytes(count);
7367
}
68+
// the input is UTF8, so the check does not include sizeof(char)
7469
else if (typeof(T) == typeof(char) && reader.IsDataAvailable(count))
7570
{
76-
T[] result = (T[])(object)reader.ReadChars(count);
77-
if (result.Length != count)
78-
{
79-
ThrowHelper.ThrowEndOfStreamException();
80-
}
81-
return result;
71+
return (T[])(object)reader.ReadChars(count);
8272
}
8373

74+
// For decimals, the input is provided as strings, so we can't compute the required size up-front.
75+
bool canPreAllocate = typeof(T) != typeof(decimal) && reader.IsDataAvailable(count * Unsafe.SizeOf<T>());
8476
// Most of the tests use MemoryStream or FileStream and they both allow for executing the fast path.
8577
// To ensure the slow path is tested as well, the fast path is executed only for optimized builds.
86-
#if NET
87-
if (typeof(T) != typeof(decimal) && typeof(T) != typeof(DateTime) && typeof(T) != typeof(TimeSpan) // not optimized
88-
&& reader.IsDataAvailable(count * Unsafe.SizeOf<T>()))
78+
#if NET && RELEASE
79+
if (canPreAllocate)
8980
{
9081
return DecodePrimitiveTypesToArray(reader, count);
9182
}
9283
#endif
93-
return DecodePrimitiveTypesToList(reader, count);
84+
return DecodePrimitiveTypesToList(reader, count, canPreAllocate);
9485
}
9586

96-
private static List<T> DecodePrimitiveTypesToList(BinaryReader reader, int count)
87+
private static List<T> DecodePrimitiveTypesToList(BinaryReader reader, int count, bool canPreAllocate)
9788
{
98-
List<T> values = [];
89+
List<T> values = new List<T>(canPreAllocate ? count : Math.Min(count, 4));
9990
for (int i = 0; i < count; i++)
10091
{
10192
if (typeof(T) == typeof(byte))
@@ -174,35 +165,37 @@ private static T[] DecodePrimitiveTypesToArray(BinaryReader reader, int count)
174165

175166
if (!BitConverter.IsLittleEndian)
176167
{
177-
if (typeof(T) == typeof(short))
168+
if (typeof(T) == typeof(short) || typeof(T) == typeof(ushort))
178169
{
179170
Span<short> span = MemoryMarshal.Cast<T, short>(result);
180171
BinaryPrimitives.ReverseEndianness(span, span);
181172
}
182-
else if (typeof(T) == typeof(ushort))
183-
{
184-
Span<ushort> span = MemoryMarshal.Cast<T, ushort>(result);
185-
BinaryPrimitives.ReverseEndianness(span, span);
186-
}
187-
else if (typeof(T) == typeof(int) || typeof(T) == typeof(float))
173+
else if (typeof(T) == typeof(int) || typeof(T) == typeof(uint) || typeof(T) == typeof(float))
188174
{
189175
Span<int> span = MemoryMarshal.Cast<T, int>(result);
190176
BinaryPrimitives.ReverseEndianness(span, span);
191177
}
192-
else if (typeof(T) == typeof(uint))
193-
{
194-
Span<uint> span = MemoryMarshal.Cast<T, uint>(result);
195-
BinaryPrimitives.ReverseEndianness(span, span);
196-
}
197-
else if (typeof(T) == typeof(long) || typeof(T) == typeof(double))
178+
else if (typeof(T) == typeof(long) || typeof(T) == typeof(ulong) || typeof(T) == typeof(double)
179+
|| typeof(T) == typeof(DateTime) || typeof(T) == typeof(TimeSpan))
198180
{
199181
Span<long> span = MemoryMarshal.Cast<T, long>(result);
200182
BinaryPrimitives.ReverseEndianness(span, span);
201183
}
202-
else if (typeof(T) == typeof(ulong))
203-
{
204-
Span<ulong> span = MemoryMarshal.Cast<T, ulong>(result);
205-
BinaryPrimitives.ReverseEndianness(span, span);
184+
}
185+
186+
if (typeof(T) == typeof(DateTime) || typeof(T) == typeof(TimeSpan))
187+
{
188+
Span<long> longs = MemoryMarshal.Cast<T, long>(result);
189+
for (int i = 0; i < longs.Length; i++)
190+
{
191+
if (typeof(T) == typeof(DateTime))
192+
{
193+
result[i] = (T)(object)Utils.BinaryReaderExtensions.CreateDateTimeFromData(longs[i]);
194+
}
195+
else
196+
{
197+
result[i] = (T)(object)new TimeSpan(longs[i]);
198+
}
206199
}
207200
}
208201

src/libraries/System.Runtime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/BinaryArrayRecord.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,10 @@ internal static ArrayRecord Decode(BinaryReader reader, RecordMap recordMap, Pay
121121

122122
bool isRectangular = arrayType is BinaryArrayType.Rectangular or BinaryArrayType.RectangularOffset;
123123

124-
if (rank < 1 || rank > 32
124+
// It is an arbitrary limit in the current CoreCLR type loader.
125+
const int MaxSupportedArrayRank = 32;
126+
127+
if (rank < 1 || rank > MaxSupportedArrayRank
125128
|| (rank != 1 && !isRectangular)
126129
|| (rank == 1 && isRectangular))
127130
{

src/libraries/System.Runtime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/ClassInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ internal static ClassInfo Decode(BinaryReader reader)
5757
// however it's impossible to get such output with BinaryFormatter,
5858
// so we prohibit that on purpose.
5959
string memberName = reader.ReadString();
60-
#if NETCOREAPP
60+
#if NET
6161
if (memberNames.TryAdd(memberName, i))
6262
{
6363
continue;

src/libraries/System.Runtime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/ObjectNullMultiple256Record.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.IO;
5+
using System.Runtime.Serialization.BinaryFormat.Utils;
56

67
namespace System.Runtime.Serialization.BinaryFormat;
78

@@ -20,5 +21,14 @@ internal sealed class ObjectNullMultiple256Record : NullsRecord
2021
internal override int NullCount { get; }
2122

2223
internal static ObjectNullMultiple256Record Decode(BinaryReader reader)
23-
=> new(reader.ReadByte());
24+
{
25+
// The NRBF spec for 2.5.6 ObjectNullMultiple allows for 0, but we don't.
26+
byte count = reader.ReadByte();
27+
if (count == 0)
28+
{
29+
ThrowHelper.ThrowInvalidValue(count);
30+
}
31+
32+
return new ObjectNullMultiple256Record(count);
33+
}
2434
}

src/libraries/System.Runtime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/ObjectNullMultipleRecord.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ internal sealed class ObjectNullMultipleRecord : NullsRecord
2323
internal static ObjectNullMultipleRecord Decode(BinaryReader reader)
2424
{
2525
// 2.5.5 ObjectNullMultiple
26-
26+
2727
// NullCount (4 bytes): An INT32 value ... The value MUST be a positive integer.
2828
int count = reader.ReadInt32();
2929
if (count <= 0)

src/libraries/System.Runtime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/PayloadReader.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ static class PayloadReader
2626
/// <returns><see langword="true" /> if it starts with NRBF payload header; otherwise, <see langword="false" />.</returns>
2727
public static bool StartsWithPayloadHeader(byte[] bytes)
2828
{
29-
#if NETCOREAPP
29+
#if NET
3030
ArgumentNullException.ThrowIfNull(bytes);
3131
#else
3232
if (bytes is null)
@@ -37,7 +37,7 @@ public static bool StartsWithPayloadHeader(byte[] bytes)
3737

3838
return bytes.Length >= SerializedStreamHeaderRecord.Size
3939
&& bytes[0] == (byte)RecordType.SerializedStreamHeader
40-
#if NETCOREAPP
40+
#if NET
4141
&& BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(9)) == SerializedStreamHeaderRecord.MajorVersion
4242
&& BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(13)) == SerializedStreamHeaderRecord.MinorVersion;
4343
#else
@@ -58,7 +58,7 @@ public static bool StartsWithPayloadHeader(byte[] bytes)
5858
/// <remarks><para>When this method returns, <paramref name="stream" /> will be restored to its original position.</para></remarks>
5959
public static bool StartsWithPayloadHeader(Stream stream)
6060
{
61-
#if NETCOREAPP
61+
#if NET
6262
ArgumentNullException.ThrowIfNull(stream);
6363
#else
6464
if (stream is null)
@@ -81,7 +81,7 @@ public static bool StartsWithPayloadHeader(Stream stream)
8181

8282
try
8383
{
84-
#if NETCOREAPP
84+
#if NET
8585
stream.ReadExactly(buffer, 0, buffer.Length);
8686
#else
8787
int offset = 0;
@@ -134,7 +134,7 @@ public static SerializationRecord Read(Stream payload, PayloadOptions? options =
134134
/// <inheritdoc cref="Read(Stream, PayloadOptions?, bool)"/>
135135
public static SerializationRecord Read(Stream payload, out IReadOnlyDictionary<int, SerializationRecord> recordMap, PayloadOptions? options = default, bool leaveOpen = false)
136136
{
137-
#if NETCOREAPP
137+
#if NET
138138
ArgumentNullException.ThrowIfNull(payload);
139139
#else
140140
if (payload is null)

src/libraries/System.Runtime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/PrimitiveTypeRecord.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace System.Runtime.Serialization.BinaryFormat;
1212
/// <remarks>
1313
/// <para>
1414
/// The NRBF specification considers the following types to be primitive:
15-
/// <see cref="String"/>, <see cref="bool"/>, <see cref="byte"/>, <see cref="sbyte"/>
15+
/// <see cref="string"/>, <see cref="bool"/>, <see cref="byte"/>, <see cref="sbyte"/>
1616
/// <see cref="char"/>, <see cref="short"/>, <see cref="ushort"/>,
1717
/// <see cref="int"/>, <see cref="uint"/>, <see cref="long"/>, <see cref="ulong"/>,
1818
/// <see cref="float"/>, <see cref="double"/>, <see cref="decimal"/>,

src/libraries/System.Runtime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/RecordMap.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ internal void Add(SerializationRecord record)
4444
}
4545
else
4646
{
47-
#if NETCOREAPP
47+
#if NET
4848
if (_map.TryAdd(record.ObjectId, record))
4949
{
5050
return;
@@ -84,7 +84,7 @@ private CollisionResistantInt32Comparer() { }
8484

8585
public int GetHashCode(int obj)
8686
{
87-
#if NETCOREAPP
87+
#if NET
8888
Span<int> integers = new(ref obj);
8989
#else
9090
Span<int> integers = stackalloc int[1] { obj };

src/libraries/System.Runtime.Serialization.BinaryFormat/src/System/Runtime/Serialization/BinaryFormat/RecordType.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ enum RecordType : byte
2727
/// Class information that references another class record's metadata.
2828
/// </summary>
2929
ClassWithId = 1,
30-
30+
3131
// SystemClassWithMembers and ClassWithMembers are not supported by design (require type loading)
32-
32+
3333
/// <summary>
3434
/// A system class information with type info.
3535
/// </summary>

0 commit comments

Comments
 (0)