Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
FillBuffer изменяет содержимое буфера?//s - это некий Stream, переданный снаружи
var buf = new byte[4096];
//читаем данные из потока чанками до 4к
while((var count = s.Read(buf, 0, 4096)) > 0)
{
//пишем полученные данные в БД
db.WriteData(buf, 0, count);
}
ISourceBuffered (иными словами, если s — ISourceBuffered, то как будет выглядеть код)?//s - это некий IBufferedSource, переданный снаружи
//var buf = new byte[4096]; буфер не нужен, он уже встроен в источнике
//читаем данные из потока чанками до размера буфера
while(s.FillBuffer() > 0)
{
//пишем полученные данные в БД
db.WriteData(s.Buffer, s.Offset, s.Count);
s.SkipBuffer (s.Count);
}
IBufferedSource — поток неопределенно гигантского размера (пара гигабайт), и, естественно, read-only/forward-only. Каково предполагаемое поведение реализации FillBuffer в этом случае (буфер предполагаем на несколько порядков меньше размеров потока)?IBufferedSource.//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;
//s - это некий IBufferedSource, переданный снаружи
var fileWritingSource = new FileWritingBufferedSource (s, имя_файла);
while(fileWritingSource.FillBuffer() > 0)
{
//пишем полученные данные в БД
db.WriteData (fileWritingSource.Buffer, fileWritingSource.Offset, fileWritingSource.Count);
hashingSource.SkipBuffer (fileWritingSource.Count);
}Затруднение номер один: если данные одного и того же источника нужны в нескольких компонентах, то после того как один компонент считал какие то данные из Stream, то он их «потребил», и другим компонентам они уже никак не достанутся.
Эта проблема решается в BufferedSource, давая возможность считывать данные произвольно большими кусками, потребляя только то, что нужно каждому компоненту и не оглядываясь на присутствие других потребителей.
Проблема неполной доступности блоков информации принципиально решаема, но ценой возникновения других проблем. Решение первое — читать данные по одному байту пока не наберём нужное количество, чревато критическим падением производительности. Решение второе — перед каждым обращением к буферу проверять, не достигли ли мы его границы и если достигли, то повторять чтение. Проверки, чтение Stream и последующая коррекция индекса в буфере перед каждым обращением сильно засоряют код и провоцируют многочисленные ошибки. Это можно вынести в отдельный метод, очистив смысловой код, но каждый компонент всё равно придется отдельно снабжать этим методом.
Также в нём нет доступного всем компонентам буфера
Единственное, чем он может помочь — слегка улучшить производительность при чтении по одному байту, но производительность при этом будет всё равно неприемлемо низкой.
BufferedStream работает строго по описанному у вас второму решению — при чтении проверяются границы внутреннего буфера, и если он «переистощен», то читается дальше из источника. Иными словами, он позволяет потребителю читать кусками той длины, которая ему удобна, но при этом забирать данные от поставщика теми квантами, которые обоснованы соображениями производительности.ReadByte) оказывают существенное влияние на общую производительность системы при работе с реальным I/O? Не поверю, извините. Результаты профилирования есть?ReadByte, надо вызывать Read с конкретной нужной вам (т.е., определенной прилк-логикой) длиной, а BufferedStream позаботится о том, чтобы вы более эффективно работали с источником.BinaryReader, но тут бывают нюансы)var buf = new byte[1024];
s.Read (buf, 0, 1024);
int bufIdx = 0;
// тут каким то образом мы обработали первые 1021 байт в буфере, осталось 3. bufIdx == 1021
// далее нам надо взять 32-х битное число, то есть 4 байта
if (bufIdx > (1024-4))
{
Array.Copy (buf, bufIdx, buf, 0, 1024-bufIdx);
if (s.Read (buf, 1024-bufIdx, bufIdx) < 4) throw new InvalidOperationException ("не хватило данных");
bufIdx = 0;
}
BitConverter.ToInt32 (buf, bufIdx)
s.FillBuffer ();
// тут каким то образом мы пропустили первые 1021 байт в буфере, осталось 3
// далее нам надо взять 32-х битное число, то есть 4 байта
s.EnsureBuffer (4);
BitConverter.ToInt32 (s.Buffer, s.Offset)
Удобный двоичный источник данных взамен Stream