我正在写一个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堆管理器重新使用该块之前,或者您的代码将覆盖另一个分配拥有的内存之前,请执行以下操作。确切的说,这是无法预测的。您可以在此处从字面上完全理解类名中的“不安全”。

08-19 12:37