我正在写一个UnmanagedRewindBuffer
,我想实现缓冲区的动态调整大小。我尝试了几种不同的方法,但似乎无法正确处理。基本思想是:
我分配了一个新的非托管内存块。
创建一个新的UnmanagedMemoryStream
(UMS)。
将内容从旧的UMS复制到新的UMS。
处置旧的UMS并释放旧的已分配块。
用新的UMS和内存块替换旧的UMS和内存块。
这是我的调整大小功能:
private void DynamicallyResizeBuffer(long spaceNeeded)
{
while (_ums.Length < spaceNeeded)
{
// Allocate a new buffer
int length = (int)((double)spaceNeeded * RESIZE_FACTOR);
IntPtr tempMemoryPointer = Marshal.AllocHGlobal(length);
// Set the temporary pointer to null
//MemSet(tempMemoryPointer, length, 0);
byte* bytePointer = (byte*)tempMemoryPointer.ToPointer();
for (int i = 0; i < length; i++)
{
*(bytePointer + i) = 0;
}
// Copy the data
// MoveMemory(bytePointer, _memoryPointer.ToPointer(), _length);
// Create a new UnmanagedMemoryStream
UnmanagedMemoryStream tempUms = new UnmanagedMemoryStream(bytePointer, length, length, FileAccess.ReadWrite);
// Set up the reader and writers
BinaryReader tempReader = new BinaryReader(tempUms);
BinaryWriter tempWriter = new BinaryWriter(tempUms);
// Copy the data
_ums.Position = 0;
tempWriter.Write(ReadBytes(_length));
// I had deleted this line while I was using the writers and
// I forgot to copy it over, but the line was here when I used
// the MoveMemory function
tempUms.Position = _ums.Position;
// Free the old resources
Free(true);
_ums = tempUms;
_reader = tempReader;
_writer = tempWriter;
_length = length;
}
}
这是我调整大小的测试:
public void DynamicResizeTest()
{
Int32 expected32 = 32;
Int32 actual32 = 0;
UInt64 expected64 = 64;
UInt64 actual64 = 0;
string expected = "expected";
string actual = string.Empty;
string actualFromBytes = string.Empty;
byte[] expectedBytes = Encoding.UTF8.GetBytes(expected);
// Create an 4 byte buffer
UnmanagedRewindBuffer ubs = null;
try
{
ubs = new UnmanagedRewindBuffer(4, 1);
ubs.WriteInt32(expected32);
// should dynamically resize for the 64 bit integer
ubs.WriteUInt64(expected64);
ubs.WriteString(expected);
// should dynamically resize for the bytes
ubs.WriteByte(expectedBytes);
ubs.Rewind();
actual32 = ubs.ReadInt32();
actual64 = ubs.ReadUInt64();
actual = ubs.ReadString();
actualFromBytes = Encoding.UTF8.GetString(ubs.ReadBytes(expected.Length));
}
finally
{
if (ubs != null)
{
ubs.Clear();
ubs.Dispose();
}
ubs = null;
}
Assert.AreEqual(expected32, actual32);
Assert.AreEqual(expected64, actual64);
Assert.AreEqual(expected, actual);
Assert.AreEqual(expected, actualFromBytes);
}
我尝试调用
MoveMemory
,这只是kernel32 RtlMoveMemory
的不安全外部,但是当我运行测试时,得到以下结果:actual32 is 32, expected 32
actual64 is 0, expected 64
actual is "", expected "expected"
actualFromBytes is some gibberish, expected "expected"
当我使用读取器/写入器直接从旧的UMS读取到新的UMS时,得到以下结果:
actual32 is 32, expected 32
actual64 is 64, expected 64
actual is "", expected "expected"
actualFromBytes is "\b\0expect", expected "expected"
如果我从一开始就分配了足够的空间,那么读取值就不会有问题,并且可以获得正确的预期结果。
什么是复制数据的正确方法?
更新:
根据Alexi的评论,以下是
Free
方法,该方法处理读取器/写入器和UnmanagedMemoryStream
:private void Free(bool disposeManagedResources)
{
// Dispose unmanaged resources
Marshal.FreeHGlobal(_memoryPointer);
// Dispose managed resources. Should not be called from destructor.
if (disposeManagedResources)
{
_reader.Close();
_writer.Close();
_reader = null;
_writer = null;
_ums.Dispose();
_ums = null;
}
}
最佳答案
您忘记了此作业:
_memoryPointer = tempMemoryPointer;
可能一会儿就没注意到,_memoryPointer指向一个仍包含旧字节的已释放内存块。在Windows堆管理器重新使用该块之前,或者您的代码将覆盖另一个分配拥有的内存之前,请执行以下操作。确切的说,这是无法预测的。您可以在此处从字面上完全理解类名中的“不安全”。