Pull to refresh
14
0
Сергей Пономарев @novar

Программист

Send message
Нет, упомянутый BufferedStream несмотря на название, не решает ничего. В нём нет методов, которые гарантируют наличие в буфере целого блока нужного размера. Также в нём нет доступного всем компонентам буфера и каждый компонент будет создавать свой буфер для вызова метода Read(). Единственное, чем он может помочь — слегка улучшить производительность при чтении по одному байту, но производительность при этом будет всё равно неприемлемо низкой.
«Считалка MD5» существует в базовой библиотеке классов в виде крипто-преобразования System.Security.Cryptography.MD5. Вот готовый код:
//s - это некий IBufferedSource, переданный снаружи
var hashProvider = System.Security.Cryptography.MD5.Create();
var hashingSource = new CryptoTransformingBufferedSource (s, hashProvider, new byte[1024]);
while(hashingSource.FillBuffer() > 0)
{
   //пишем полученные данные в БД
   db.WriteData (hashingSource.Buffer, hashingSource.Offset, hashingSource.Count);
   hashingSource.SkipBuffer (hashingSource.Count);
}
return hashProvider.Hash;
Если данные источника доступны произвольными порциями, то будет логично реализовать FillBuffer() так, чтобы при каждом вызове он дополнял уже имеющиеся в буфере данные (считывая их из источника) до полного заполнения буфера. Если никто не вызовет SkipBuffer() или TrySkip(), то следующий вызов FillBuffer() просто ничего не изменит, потому что буфер уже полон.
Извиняюсь за непонятность. Буду стараться выражаться яснее. Вот аналог который вам нужен:
//s - это некий IBufferedSource, переданный снаружи
//var buf = new byte[4096]; буфер не нужен, он уже встроен в источнике
//читаем данные из потока чанками до размера буфера
while(s.FillBuffer() > 0)
{
   //пишем полученные данные в БД
   db.WriteData(s.Buffer, s.Offset, s.Count);
   s.SkipBuffer (s.Count);
}
Источники могут быть разные, их поведение ограничено контрактом. Контракт гласит, что FillBuffer() должен заполнить буфер насколько возможно больше и вернуть ноль только тогда, когда источник пуст. Сколько именно вернёт FillBuffer() и изменится ли это число при повторных вызовах — не специфицируется и зависит от реализации. Если важно чтобы из источника было доступно какое то количество байт в буфере, то надо пользоваться методом EnsureBuffer(размер), он либо выполнит требования, либо бросит исключение означающее что источник не может предоставить столько данных.
Итак, наш метод получил источник данных в виде ISourceBuffered. Далее, если нам нужен из него некоторый блок данных известного размера, вызываем src.EnsureBuffer(размер_блока). Если размер нам неизвестен, и мы будем смотреть всё что предоставляет источник, то вызываем src.FillBuffer (). После этого мы совершенно произвольно и многократно обращаемся к буферу src.Buffer в пределах ограниченных src.Offset и src.Count. Как только мы потребили некоторые данные из начала буфера, мы их исключаем из дальнейшей обработки вызовом src.SkipBuffer(размер), при этом изменятся свойства src.Offset и src.Count. Далее повторять по необходимости с начала до тех пор, пока не получим все нужные данные либо src.FillBuffer () не вернёт ноль. При этом, в ЛЮБОЙ момент, мы можем создать дочерний SourceBuffered, который будет получать данные из нашего родительского источника и как либо их преобразовывать либо просто ограничивать по размеру. Этот дочерний источник мы можем передать другому компоненту, который будет действовать по этому же сценарию.
По вашему получается, что единственный объект-читатель потока либо должен сам парсить содержимое и превращать в конкретные сущности, что делает его привязанным к этим конкретным сущностям, либо как то передавать считанные данные в методы парсинга отдельных компонентов. Вот именно тому, как передавать эти данные, и посвящена моя статья.
Описываю словами типичный сценарий. Я пишу компонент для чтения иерархической структуры (например, mkv-файла), который получая на вход ISourceBuffered, потребляет его, перебирая все встреченные порции. Для каждой порции, в зависимости от её типа, он может создать дочерний ISourceBuffered (который читает данные внутри родительского) и передать его другому компоненту, который, например, парсит двоичное представление JPEG-изображения. Читая и потребляя дочерний источник, JPEG-компонент также потребляет и родительский (они имеют общий буфер и указатель потребления), что избавляет от двойного чтения и буферирования. При этом компоненты совершенно независимы и создаются в разных проектах. Последовательность вызовов можно посмотреть в примере кода в статье. Для одного потребителя мало что отличается от использования Stream, кроме наличия удобной функции проверки количества и дочитывания необходимого.
Я сам не люблю велосипеды, и очень стараюсь не создавать их. В случае потребления одного Stream разными компонентами есть два отдельных случая и перекрыть из использованием Observer мне не представляется возможным. Случай один, это когда данные потребляются последовательно, одним компонентом следом за другим. Но есть и другой случай: когда одному компоненту (дочернему) нужна часть данных из тех, которые нужны другому (родительскому).
12 ...
8

Information

Rating
Does not participate
Registered
Activity