@@ -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