Как реализовать перечислитель с отложенным потоком? - PullRequest
3 голосов
/ 06 февраля 2012

Я пытаюсь разбить поток байтов на куски увеличивающегося размера.

Исходный поток содержит неизвестное количество байтов и дорог для чтения.Выходными данными перечислителя должны быть байтовые массивы увеличивающегося размера, начиная с 8 КБ до 1 МБ.

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

При реализации этого я заметил, что сделать код коротким и обслуживаемым относительно сложно.Также необходимо помнить о некоторых проблемах, связанных с потоками (например, Stream.Read может не заполнить буфер, даже если он успешно выполнен).

Я не нашел ни одного существующего класса, который помог бы в моем случае,и при этом я не мог найти что-то близкое в сети.Как бы вы реализовали такой класс?

Ответы [ 2 ]

4 голосов
/ 06 февраля 2012
public IEnumerable<BufferWrapper> getBytes(Stream stream)
{
    List<int> bufferSizes = new List<int>() { 8192, 65536, 220160, 1048576 };
    int count = 0;
    int bufferSizePostion = 0;
    byte[] buffer = new byte[bufferSizes[0]];
    bool done = false;
    while (!done)
    {
        BufferWrapper nextResult = new BufferWrapper();
        nextResult.bytesRead = stream.Read(buffer, 0, buffer.Length);
        nextResult.buffer = buffer;
        done = nextResult.bytesRead == 0;
        if (!done)
        {
            yield return nextResult;
            count++;
            if (count > 10 && bufferSizePostion < bufferSizes.Count)
            {
                count = 0;
                bufferSizePostion++;
                buffer = new byte[bufferSizes[bufferSizePostion]];
            }
        }
    }
}

public class BufferWrapper
{
    public byte[] buffer { get; set; }
    public int bytesRead { get; set; }
}

Очевидно, что логика для того, когда увеличивать размер буфера и как выбирать, что это за размер, может быть изменена.

Кто-то может также найти лучший способ обработки последнего буфера для отправки, так как это не самый эффективный способ.

2 голосов
/ 06 февраля 2012

Для справки, реализация, которую я сейчас использую, уже с улучшениями согласно ответу @ Servy

private const int InitialBlockSize = 8 * 1024;
private const int MaximumBlockSize = 1024 * 1024;

private Stream _Stream;
private int _Size = InitialBlockSize;

public byte[] Current
{
    get;
    private set;
}

public bool MoveNext ()
{
    if (_Size < 0) {
        return false;
    }

    var buf = new byte[_Size];
    int count = 0;

    while (count < _Size) {
        int read = _Stream.Read (buf, count, _Size - count);

        if (read == 0) {
            break;
        }

        count += read;
    }

    if (count == _Size) {
        Current = buf;
        if (_Size <= MaximumBlockSize / 2) {
            _Size *= 2;
        }
    }
    else {
        Current = new byte[count];
        Array.Copy (buf, Current, count);
        _Size = -1;
    }

    return true;
}
...