Skip to content

Commit b39bdc3

Browse files
authored
Merge pull request #436 from ToddGrun/LimitImmutableArrayUsage
Partially revert earlier commit for always using ReadImmutableArray
2 parents 2b2fd3b + 1259060 commit b39bdc3

File tree

1 file changed

+55
-13
lines changed

1 file changed

+55
-13
lines changed

src/Microsoft.VisualStudio.Composition/Configuration/SerializationContextBase.cs

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ internal abstract class SerializationContextBase : IDisposable
4747
private readonly Func<TypeRef?> readTypeRefDelegate;
4848

4949
private readonly ImmutableDictionary<string, object?>.Builder metadataBuilder = ImmutableDictionary.CreateBuilder<string, object?>();
50+
private readonly Stack<ImmutableArray<TypeRef?>.Builder> typeRefBuilders = new Stack<ImmutableArray<TypeRef?>.Builder>();
5051

5152
private readonly byte[] guidBuffer = new byte[128 / 8];
5253

@@ -223,8 +224,8 @@ protected void Write(MethodRef? methodRef)
223224
var metadataToken = this.ReadCompressedMetadataToken(MetadataTokenType.Method);
224225
var name = this.ReadString();
225226
var isStatic = this.ReadCompressedUInt() != 0;
226-
var parameterTypes = this.ReadImmutableArray(this.reader, this.readTypeRefDelegate);
227-
var genericMethodArguments = this.ReadImmutableArray(this.reader, this.readTypeRefDelegate);
227+
var parameterTypes = this.ReadTypeRefImmutableArray(this.reader, this.readTypeRefDelegate);
228+
var genericMethodArguments = this.ReadTypeRefImmutableArray(this.reader, this.readTypeRefDelegate);
228229
return new MethodRef(declaringType, metadataToken, name, isStatic, parameterTypes!, genericMethodArguments!);
229230
}
230231
else
@@ -476,12 +477,12 @@ protected void Write(TypeRef typeRef)
476477
var fullName = this.ReadString();
477478
var flags = (TypeRefFlags)this.ReadCompressedUInt();
478479
int genericTypeParameterCount = (int)this.ReadCompressedUInt();
479-
var genericTypeArguments = this.ReadImmutableArray(this.reader, this.readTypeRefDelegate);
480+
var genericTypeArguments = this.ReadTypeRefImmutableArray(this.reader, this.readTypeRefDelegate);
480481

481482
var shallow = this.ReadCompressedUInt() != 0;
482483
var baseTypes = shallow
483484
? ImmutableArray<TypeRef?>.Empty
484-
: this.ReadImmutableArray(this.reader, this.readTypeRefDelegate);
485+
: this.ReadTypeRefImmutableArray(this.reader, this.readTypeRefDelegate);
485486

486487
var hasElementType = this.ReadCompressedUInt() != 0;
487488
var elementType = hasElementType
@@ -659,11 +660,6 @@ protected IReadOnlyList<T> ReadList<T>(Func<T> itemReader)
659660
}
660661

661662
protected IReadOnlyList<T> ReadList<T>(BinaryReader reader, Func<T> itemReader)
662-
{
663-
return this.ReadImmutableArray<T>(reader, itemReader);
664-
}
665-
666-
protected ImmutableArray<T> ReadImmutableArray<T>(BinaryReader reader, Func<T> itemReader)
667663
{
668664
using (this.Trace(typeof(T).Name, isArray: true))
669665
{
@@ -677,16 +673,62 @@ protected ImmutableArray<T> ReadImmutableArray<T>(BinaryReader reader, Func<T> i
677673

678674
if (count == 0)
679675
{
680-
return ImmutableArray<T>.Empty;
676+
return Array.Empty<T>();
677+
}
678+
679+
var list = new T[count];
680+
for (int i = 0; i < list.Length; i++)
681+
{
682+
list[i] = itemReader();
683+
}
684+
685+
return list;
686+
}
687+
}
688+
689+
protected ImmutableArray<TypeRef?> ReadTypeRefImmutableArray(BinaryReader reader, Func<TypeRef?> itemReader)
690+
{
691+
using (this.Trace(typeof(TypeRef).Name, isArray: true))
692+
{
693+
uint count = this.ReadCompressedUInt();
694+
695+
switch (count)
696+
{
697+
case 0:
698+
return ImmutableArray<TypeRef?>.Empty;
699+
case 1:
700+
return ImmutableArray.Create(itemReader());
701+
case 2:
702+
return ImmutableArray.Create(itemReader(), itemReader());
703+
case 3:
704+
return ImmutableArray.Create(itemReader(), itemReader(), itemReader());
705+
case 4:
706+
return ImmutableArray.Create(itemReader(), itemReader(), itemReader(), itemReader());
707+
}
708+
709+
if (count > 0xffff)
710+
{
711+
// Probably either file corruption or a bug in serialization.
712+
// Let's not take untold amounts of memory by throwing out suspiciously large lengths.
713+
throw new NotSupportedException();
681714
}
682715

683-
ImmutableArray<T>.Builder list = ImmutableArray.CreateBuilder<T>((int)count);
716+
// Larger arrays need to use a builder to prevent duplicate array allocations.
717+
// Reuse builders to save on GC pressure
718+
ImmutableArray<TypeRef?>.Builder builder = this.typeRefBuilders.Count > 0 ? this.typeRefBuilders.Pop() : ImmutableArray.CreateBuilder<TypeRef?>();
719+
720+
builder.Capacity = (int)count;
684721
for (int i = 0; i < count; i++)
685722
{
686-
list.Add(itemReader());
723+
builder.Add(itemReader());
687724
}
688725

689-
return list.MoveToImmutable();
726+
ImmutableArray<TypeRef?> result = builder.MoveToImmutable();
727+
728+
// Place builder back in cache
729+
this.typeRefBuilders.Push(builder);
730+
731+
return result;
690732
}
691733
}
692734

0 commit comments

Comments
 (0)