Skip to content

Commit 473cad8

Browse files
[SRM] Refactor reading from streams. (#111323)
1 parent 9b24fb6 commit 473cad8

16 files changed

+121
-280
lines changed

src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ The System.Reflection.Metadata library is built-in as part of the shared framewo
104104
<Compile Include="System\Reflection\Internal\MemoryBlocks\MemoryBlockProvider.cs" />
105105
<Compile Include="System\Reflection\Internal\MemoryBlocks\MemoryMappedFileBlock.cs" />
106106
<Compile Include="System\Reflection\Internal\MemoryBlocks\NativeHeapMemoryBlock.cs" />
107-
<Compile Include="System\Reflection\Internal\MemoryBlocks\StreamConstraints.cs" />
108107
<Compile Include="System\Reflection\Internal\MemoryBlocks\StreamMemoryBlockProvider.cs" />
109108
<Compile Include="System\Reflection\Internal\Utilities\BitArithmetic.cs" />
110109
<Compile Include="System\Reflection\Internal\Utilities\StringUtils.cs" />
@@ -113,7 +112,6 @@ The System.Reflection.Metadata library is built-in as part of the shared framewo
113112
<Compile Include="System\Reflection\Internal\Utilities\StreamExtensions.netcoreapp.cs" Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'" />
114113
<Compile Include="System\Reflection\Internal\Utilities\StreamExtensions.netstandard2.0.cs" Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'" />
115114
<Compile Include="System\Reflection\Internal\Utilities\Hash.cs" />
116-
<Compile Include="System\Reflection\Internal\Utilities\ImmutableMemoryStream.cs" />
117115
<Compile Include="System\Reflection\Internal\Utilities\MemoryBlock.cs" />
118116
<Compile Include="System\Reflection\Internal\Utilities\PooledStringBuilder.cs" />
119117
<Compile Include="System\Reflection\Internal\Utilities\ObjectPool`1.cs" />

src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/AbstractMemoryBlock.cs

Lines changed: 6 additions & 0 deletions
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.Collections.Immutable;
5+
using System.IO;
56
using System.Reflection.Metadata;
67

78
namespace System.Reflection.Internal
@@ -23,6 +24,11 @@ internal abstract class AbstractMemoryBlock : IDisposable
2324

2425
public unsafe BlobReader GetReader() => new BlobReader(Pointer, Size);
2526

27+
/// <summary>
28+
/// Creates a new stream wrapping the block's memory.
29+
/// </summary>
30+
public unsafe Stream GetStream() => new UnmanagedMemoryStream(Pointer, Size);
31+
2632
/// <summary>
2733
/// Returns the content of the entire memory block.
2834
/// </summary>

src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/ByteArrayMemoryProvider.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,6 @@ protected override AbstractMemoryBlock GetMemoryBlockImpl(int start, int size)
3434
return new ByteArrayMemoryBlock(this, start, size);
3535
}
3636

37-
public override Stream GetStream(out StreamConstraints constraints)
38-
{
39-
constraints = new StreamConstraints(null, 0, Size);
40-
return new ImmutableMemoryStream(_array);
41-
}
42-
4337
internal unsafe byte* Pointer
4438
{
4539
get

src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/ExternalMemoryBlockProvider.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,6 @@ protected override AbstractMemoryBlock GetMemoryBlockImpl(int start, int size)
3333
return new ExternalMemoryBlock(this, _memory + start, size);
3434
}
3535

36-
public override Stream GetStream(out StreamConstraints constraints)
37-
{
38-
constraints = new StreamConstraints(null, 0, _size);
39-
return new UnmanagedMemoryStream(_memory, _size);
40-
}
41-
4236
protected override void Dispose(bool disposing)
4337
{
4438
Debug.Assert(disposing);

src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/MemoryBlockProvider.cs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
45
using System.IO;
56
using System.Reflection.PortableExecutable;
67

@@ -39,12 +40,21 @@ public AbstractMemoryBlock GetMemoryBlock(int start, int size)
3940
protected abstract AbstractMemoryBlock GetMemoryBlockImpl(int start, int size);
4041

4142
/// <summary>
42-
/// Gets a seekable and readable <see cref="Stream"/> that can be used to read all data.
43-
/// The operations on the stream has to be done under a lock of <see cref="StreamConstraints.GuardOpt"/> if non-null.
44-
/// The image starts at <see cref="StreamConstraints.ImageStart"/> and has size <see cref="StreamConstraints.ImageSize"/>.
45-
/// It is the caller's responsibility not to read outside those bounds.
43+
/// Gets the <see cref="Stream"/> backing the <see cref="MemoryBlockProvider"/>, if there is one.
4644
/// </summary>
47-
public abstract Stream GetStream(out StreamConstraints constraints);
45+
/// <remarks>
46+
/// It is the caller's responsibility to use <paramref name="stream"/> only
47+
/// while locking on <paramref name="streamGuard"/>, and not read outside the
48+
/// bounds defined by <paramref name="imageStart"/> and <paramref name="imageSize"/>.
49+
/// </remarks>
50+
public virtual bool TryGetUnderlyingStream([NotNullWhen(true)] out Stream? stream, out long imageStart, out int imageSize, [NotNullWhen(true)] out object? streamGuard)
51+
{
52+
stream = null;
53+
imageStart = 0;
54+
imageSize = 0;
55+
streamGuard = null;
56+
return false;
57+
}
4858

4959
/// <summary>
5060
/// The size of the data.

src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamConstraints.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamMemoryBlockProvider.cs

Lines changed: 16 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ internal sealed class StreamMemoryBlockProvider : MemoryBlockProvider
2828
private readonly object _streamGuard;
2929

3030
private readonly bool _leaveOpen;
31-
private bool _useMemoryMap;
31+
private readonly bool _useMemoryMap;
3232

3333
private readonly long _imageStart;
3434
private readonly int _imageSize;
@@ -73,13 +73,7 @@ internal static unsafe NativeHeapMemoryBlock ReadMemoryBlockNoLock(Stream stream
7373
try
7474
{
7575
stream.Seek(start, SeekOrigin.Begin);
76-
77-
int bytesRead = 0;
78-
79-
if ((bytesRead = stream.Read(block.Pointer, size)) != size)
80-
{
81-
stream.CopyTo(block.Pointer + bytesRead, size - bytesRead);
82-
}
76+
stream.ReadExactly(block.Pointer, size);
8377

8478
fault = false;
8579
}
@@ -94,19 +88,23 @@ internal static unsafe NativeHeapMemoryBlock ReadMemoryBlockNoLock(Stream stream
9488
return block;
9589
}
9690

91+
public override bool TryGetUnderlyingStream([NotNullWhen(true)] out Stream? stream, out long imageStart, out int imageSize, [NotNullWhen(true)] out object? streamGuard)
92+
{
93+
stream = _stream;
94+
imageStart = _imageStart;
95+
imageSize = _imageSize;
96+
streamGuard = _streamGuard;
97+
return true;
98+
}
99+
97100
/// <exception cref="IOException">Error while reading from the stream.</exception>
98101
protected override AbstractMemoryBlock GetMemoryBlockImpl(int start, int size)
99102
{
100103
long absoluteStart = _imageStart + start;
101104

102105
if (_useMemoryMap && size > MemoryMapThreshold)
103106
{
104-
if (TryCreateMemoryMappedFileBlock(absoluteStart, size, out MemoryMappedFileBlock? block))
105-
{
106-
return block;
107-
}
108-
109-
_useMemoryMap = false;
107+
return CreateMemoryMappedFileBlock(absoluteStart, size);
110108
}
111109

112110
lock (_streamGuard)
@@ -115,26 +113,18 @@ protected override AbstractMemoryBlock GetMemoryBlockImpl(int start, int size)
115113
}
116114
}
117115

118-
public override Stream GetStream(out StreamConstraints constraints)
119-
{
120-
constraints = new StreamConstraints(_streamGuard, _imageStart, _imageSize);
121-
return _stream;
122-
}
123-
124116
/// <exception cref="IOException">IO error while mapping memory or not enough memory to create the mapping.</exception>
125-
private unsafe bool TryCreateMemoryMappedFileBlock(long start, int size, [NotNullWhen(true)] out MemoryMappedFileBlock? block)
117+
private unsafe MemoryMappedFileBlock CreateMemoryMappedFileBlock(long start, int size)
126118
{
127119
if (_lazyMemoryMap == null)
128120
{
129-
// leave the underlying stream open. It will be closed by the Dispose method.
130-
MemoryMappedFile newMemoryMap;
131-
132121
// CreateMemoryMap might modify the stream (calls FileStream.Flush)
133122
lock (_streamGuard)
134123
{
135124
try
136125
{
137-
newMemoryMap =
126+
// leave the underlying stream open. It will be closed by the Dispose method.
127+
_lazyMemoryMap ??=
138128
MemoryMappedFile.CreateFromFile(
139129
fileStream: (FileStream)_stream,
140130
mapName: null,
@@ -148,17 +138,6 @@ private unsafe bool TryCreateMemoryMappedFileBlock(long start, int size, [NotNul
148138
throw new IOException(e.Message, e);
149139
}
150140
}
151-
152-
if (newMemoryMap == null)
153-
{
154-
block = null;
155-
return false;
156-
}
157-
158-
if (Interlocked.CompareExchange(ref _lazyMemoryMap, newMemoryMap, null) != null)
159-
{
160-
newMemoryMap.Dispose();
161-
}
162141
}
163142

164143
MemoryMappedViewAccessor accessor;
@@ -168,14 +147,7 @@ private unsafe bool TryCreateMemoryMappedFileBlock(long start, int size, [NotNul
168147
accessor = _lazyMemoryMap.CreateViewAccessor(start, size, MemoryMappedFileAccess.Read);
169148
}
170149

171-
if (accessor == null)
172-
{
173-
block = null;
174-
return false;
175-
}
176-
177-
block = new MemoryMappedFileBlock(accessor, accessor.SafeMemoryMappedViewHandle, accessor.PointerOffset, size);
178-
return true;
150+
return new MemoryMappedFileBlock(accessor, accessor.SafeMemoryMappedViewHandle, accessor.PointerOffset, size);
179151
}
180152
}
181153
}

src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/ImmutableMemoryStream.cs

Lines changed: 0 additions & 124 deletions
This file was deleted.

src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/StreamExtensions.cs

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,6 @@ namespace System.Reflection.Internal
99
{
1010
internal static partial class StreamExtensions
1111
{
12-
// From System.IO.Stream.CopyTo:
13-
// We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
14-
// The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
15-
// improvement in Copy performance.
16-
internal const int StreamCopyBufferSize = 81920;
17-
18-
/// <summary>
19-
/// Copies specified amount of data from given stream to a target memory pointer.
20-
/// </summary>
21-
/// <exception cref="IOException">unexpected stream end.</exception>
22-
internal static unsafe void CopyTo(this Stream source, byte* destination, int size)
23-
{
24-
byte[] buffer = new byte[Math.Min(StreamCopyBufferSize, size)];
25-
while (size > 0)
26-
{
27-
int readSize = Math.Min(size, buffer.Length);
28-
int bytesRead = source.Read(buffer, 0, readSize);
29-
30-
if (bytesRead <= 0 || bytesRead > readSize)
31-
{
32-
throw new IOException(SR.UnexpectedStreamEnd);
33-
}
34-
35-
Marshal.Copy(buffer, 0, (IntPtr)destination, bytesRead);
36-
37-
destination += bytesRead;
38-
size -= bytesRead;
39-
}
40-
}
41-
4212
/// <summary>
4313
/// Attempts to read all of the requested bytes from the stream into the buffer
4414
/// </summary>

0 commit comments

Comments
 (0)