2011-06-08 56 views
0

我正在编写一个UnmanagedRewindBuffer,我想实现缓冲区的动态调整大小。我尝试了几种不同的东西,但我似乎无法做到。其基本思路是:如何将数据从一个UnamangedMemoryStream复制到另一个

  1. 我分配一个新的非托管内存块。
  2. 创建一个新的UnmanagedMemoryStream(UMS)。
  3. 将旧UMS的内容复制到新的UMS。
  4. 处置旧的UMS并释放旧分配的块。
  5. 将旧的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; 
    } 
} 
+0

为什么不使用MemoryStream? – dtb 2011-06-08 15:53:36

+0

@dtb我需要控制内存分配,并且还使用了一个C++ dll,它接受一个指针并从中读取数据。我用数据填充缓冲区,取指针,将它发送给C++ dll,并用它做一些事情。 – Kiril 2011-06-08 16:02:33

回答

2

你忘了这个任务:

_memoryPointer = tempMemoryPointer; 

,可以被忽视了一会儿,_memoryPointer指向仍然包含老字节的释放内存块。直到Windows堆管理器重新使用该块或者您的代码覆盖另一个分配所拥有的内存。恰恰当这种情况发生时是不可预测的。你可以在这里毫不夸张地在课堂名称中“不安全”。

+0

宾果!谢谢汉斯!我不能相信我错过了...我一直坚持这一天! – Kiril 2011-06-08 17:46:22

0

第一个猜测 - 你是不是处置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?

+0

@Alexi,对不起,我删除了更新读写器拷贝位置的代码(虽然它不影响它),但我忘了发布它。但是,当我使用MoveMemory函数时,我确实更新了位置。我目前也在使用.NET 3.5。 – Kiril 2011-06-08 16:19:22

+0

BinaryReader tempReader = new BinaryReader(tempUms); - 不应该是BinaryReader tempReader = new BinaryReader(_ums); ? – 2011-06-08 16:42:30

+0

我正在处理_ums,所以我无法复制该引用(如果将它传递给tempReader,会发生这种情况)。我将_ums中的所有内容复制到tempUms,所以它现在应该包含所有的数据,读者应该没问题。 – Kiril 2011-06-08 16:48:49

相关问题