提交 c9e06393 编写于 作者: P Paul Harrington

Redefine IStream to avoid byte[] allocation in Write

上级 9a342e59
......@@ -334,6 +334,7 @@
<Compile Include="MetadataReference\ReferenceDirective.cs" />
<Compile Include="MetadataReference\UnresolvedMetadataReference.cs" />
<Compile Include="PEWriter\ComMemoryStream.cs" />
<Compile Include="PEWriter\IUnsafeComStream.cs" />
<Compile Include="PEWriter\MetadataHeapsBuilder.cs" />
<Compile Include="PEWriter\ITypeReferenceExtensions.cs" />
<Compile Include="PEWriter\NoPiaReferenceIndexer.cs" />
......
......@@ -2,21 +2,55 @@
using System.IO;
using System.Collections.Generic;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices;
namespace Roslyn.Utilities
{
internal class ComMemoryStream : IStream
/// <summary>
/// A COM IStream implementation over memory. Supports just enough for DiaSymReader's PDB writing.
/// Also tuned for performance:
/// 1. SetSize (and Seek beyond the length) is very fast and doesn't re-allocate the underlying memory.
/// 2. Write is optimized to avoid copying.
/// 3. Doesn't use contiguous memory.
/// </summary>
internal class ComMemoryStream : IUnsafeComStream
{
#if DEBUG
private const int ChunkSize = 509; // Small prime number for debugging chunking
#else
private const int ChunkSize = 32768;
#endif
private List<byte[]> _chunks = new List<byte[]>();
private int _position;
private int _length;
public unsafe void Read(byte[] pv, int cb, IntPtr pcbRead)
public void CopyTo(Stream stream)
{
// If the target stream allows seeking set its length upfront.
// When writing to a large file, it helps to give a hint to the OS how big the file is going to be.
if (stream.CanSeek)
{
stream.SetLength(stream.Position + _length);
}
int chunkIndex = 0;
for (int cb = _length; cb > 0;)
{
int bytesToCopy = Math.Min(ChunkSize, cb);
if (chunkIndex < _chunks.Count)
{
stream.Write(_chunks[chunkIndex++], 0, bytesToCopy);
}
else
{
// Fill remaining space with zero bytes
for (int i = 0; i < bytesToCopy; i++)
{
stream.WriteByte(0);
}
}
cb -= bytesToCopy;
}
}
unsafe void IUnsafeComStream.Read(byte[] pv, int cb, IntPtr pcbRead)
{
int chunkIndex = _position / ChunkSize;
int chunkOffset = _position % ChunkSize;
......@@ -76,7 +110,7 @@ private int SetPosition(int newPos)
return newPos;
}
public unsafe void Seek(long dlibMove, int origin, IntPtr plibNewPosition)
unsafe void IUnsafeComStream.Seek(long dlibMove, int origin, IntPtr plibNewPosition)
{
int newPosition;
......@@ -104,12 +138,12 @@ public unsafe void Seek(long dlibMove, int origin, IntPtr plibNewPosition)
}
}
public void SetSize(long libNewSize)
void IUnsafeComStream.SetSize(long libNewSize)
{
_length = (int)libNewSize;
}
public void Stat(out STATSTG pstatstg, int grfStatFlag)
void IUnsafeComStream.Stat(out STATSTG pstatstg, int grfStatFlag)
{
pstatstg = new STATSTG()
{
......@@ -117,7 +151,7 @@ public void Stat(out STATSTG pstatstg, int grfStatFlag)
};
}
public unsafe void Write(byte[] pv, int cb, IntPtr pcbWritten)
unsafe void IUnsafeComStream.Write(IntPtr pv, int cb, IntPtr pcbWritten)
{
int chunkIndex = _position / ChunkSize;
int chunkOffset = _position % ChunkSize;
......@@ -135,7 +169,7 @@ public unsafe void Write(byte[] pv, int cb, IntPtr pcbWritten)
_chunks.Add(new byte[ChunkSize]);
}
Array.Copy(pv, bytesWritten, _chunks[chunkIndex], chunkOffset, bytesToCopy);
Marshal.Copy(pv + bytesWritten, _chunks[chunkIndex], chunkOffset, bytesToCopy);
bytesWritten += bytesToCopy;
_position += bytesToCopy;
cb -= bytesToCopy;
......@@ -159,65 +193,34 @@ public unsafe void Write(byte[] pv, int cb, IntPtr pcbWritten)
}
}
public void CopyTo(Stream stream)
{
int size = _length;
// If the target stream allows seeking set its length upfront.
// When writing to a large file, it helps to give a hint to the OS how big the file is going to be.
if (stream.CanSeek)
{
stream.SetLength(stream.Position + size);
}
int chunkIndex = 0;
for (int cb = size; cb > 0;)
{
int bytesToCopy = Math.Min(ChunkSize, cb);
if (chunkIndex < _chunks.Count)
{
stream.Write(_chunks[chunkIndex++], 0, bytesToCopy);
}
else
{
// Fill remaining space with zero bytes
for (int i = 0; i < bytesToCopy; i++)
{
stream.WriteByte(0);
}
}
cb -= bytesToCopy;
}
}
public void Commit(int grfCommitFlags)
void IUnsafeComStream.Commit(int grfCommitFlags)
{
}
public void Clone(out IStream ppstm)
void IUnsafeComStream.Clone(out IStream ppstm)
{
throw new NotSupportedException();
}
public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
void IUnsafeComStream.CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
{
throw new NotSupportedException();
}
public void LockRegion(long libOffset, long cb, int lockType)
void IUnsafeComStream.LockRegion(long libOffset, long cb, int lockType)
{
throw new NotSupportedException();
}
public void Revert()
void IUnsafeComStream.Revert()
{
throw new NotSupportedException();
}
public void UnlockRegion(long libOffset, long cb, int lockType)
void IUnsafeComStream.UnlockRegion(long libOffset, long cb, int lockType)
{
throw new NotSupportedException();
}
}
}
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace Roslyn.Utilities
{
/// <summary>
/// This is a re-definition of COM's IStream interface. The important change is that
/// the Write method takes an <see cref="IntPtr"/> instead of a byte[] to avoid the
/// allocation cost when called from native code.
/// </summary>
[Guid("0000000c-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), ComImport]
internal interface IUnsafeComStream
{
// ISequentialStream portion
void Read([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out] byte[] pv, int cb, IntPtr pcbRead);
void Write(IntPtr pv, int cb, IntPtr pcbWritten);
// IStream portion
void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition);
void SetSize(long libNewSize);
void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten);
void Commit(int grfCommitFlags);
void Revert();
void LockRegion(long libOffset, long cb, int dwLockType);
void UnlockRegion(long libOffset, long cb, int dwLockType);
void Stat(out STATSTG pstatstg, int grfStatFlag);
void Clone(out IStream ppstm);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册