如何将数据从一个UnamangedMemoryStream复制到另一个
我正在编写一个UnmanagedRewindBuffer
,我想实现缓冲区的动态调整大小。我尝试了几种不同的东西,但我似乎无法做到。其基本思路是:如何将数据从一个UnamangedMemoryStream复制到另一个
- 我分配一个新的非托管内存块。
- 创建一个新的
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
,这仅仅是一个不安全的extern到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"
如果我从一开始就分配足够的空间,那我也没问题读取数值并获得正确的预期结果。
什么是复制数据的正确方法?
更新:
每阿列克谢的评论,这里是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堆管理器重新使用该块或者您的代码覆盖另一个分配所拥有的内存。恰恰当这种情况发生时是不可预测的。你可以在这里毫不夸张地在课堂名称中“不安全”。
宾果!谢谢汉斯!我不能相信我错过了...我一直坚持这一天! – Kiril 2011-06-08 17:46:22
第一个猜测 - 你是不是处置StreamWriter的 - 数据可能不提交给基础流。 您也可能会丢失更新UnmanagedRewindBuffer中位置的代码...
第二个猜测:读者在错误的流中创建。
注意:考虑使用Stream.CopyTo(.Net 4 - http://msdn.microsoft.com/en-us/library/system.io.stream.copyto.aspx)来复制流。 3.5检查How do I copy the contents of one stream to another?。
@Alexi,对不起,我删除了更新读写器拷贝位置的代码(虽然它不影响它),但我忘了发布它。但是,当我使用MoveMemory函数时,我确实更新了位置。我目前也在使用.NET 3.5。 – Kiril 2011-06-08 16:19:22
BinaryReader tempReader = new BinaryReader(tempUms); - 不应该是BinaryReader tempReader = new BinaryReader(_ums); ? – 2011-06-08 16:42:30
我正在处理_ums,所以我无法复制该引用(如果将它传递给tempReader,会发生这种情况)。我将_ums中的所有内容复制到tempUms,所以它现在应该包含所有的数据,读者应该没问题。 – Kiril 2011-06-08 16:48:49
为什么不使用MemoryStream? – dtb 2011-06-08 15:53:36
@dtb我需要控制内存分配,并且还使用了一个C++ dll,它接受一个指针并从中读取数据。我用数据填充缓冲区,取指针,将它发送给C++ dll,并用它做一些事情。 – Kiril 2011-06-08 16:02:33