Как скопировать данные из одного UnamangedMemoryStream в другой - PullRequest
0 голосов
/ 08 июня 2011

Я пишу UnmanagedRewindBuffer и хочу реализовать динамическое изменение размера буфера.Я пробовал несколько разных вещей, но я не могу понять, что это правильно.Основная идея заключается в том, что:

  1. Я выделяю новый блок неуправляемой памяти.
  2. Создайте новый UnmanagedMemoryStream (UMS).
  3. Скопируйте содержимое изстарая UMS на новую UMS.
  4. Утилизируйте старую UMS и освободите старый выделенный блок.
  5. Замените старый 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, что является небезопасным внешнимв ядро32 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;
    }
}

Ответы [ 2 ]

2 голосов
/ 08 июня 2011

Вы забыли это назначение:

_memoryPointer = tempMemoryPointer;

Это может остаться незамеченным некоторое время, _memoryPointer указывает на освобожденный блок памяти, который все еще содержит старые байты. Пока диспетчер кучи Windows повторно не использует блок или ваш код не перезапишет память, принадлежащую другому выделению. Точно, когда это произойдет, непредсказуемо. Вы можете использовать слово «unsafe» в названии класса буквально здесь.

0 голосов
/ 08 июня 2011

Первое предположение - вы не утилизируете StreamWriter - данные могут не передаваться в основной поток. Вам также может не хватать кода, который обновляет позицию в вашем UnmanagedRewindBuffer ...

Второе предположение: считыватель создан в неправильном потоке.

Примечание: рассмотрите возможность использования Stream.CopyTo (.Net 4 - http://msdn.microsoft.com/en-us/library/system.io.stream.copyto.aspx) для копирования потока. Для проверки 3.5 проверьте Как скопировать содержимое одного потока в другой? .

...