Одним из решений является создание потокового комбинатора, который завершает поток после прохождения некоторого порога байтов. Вот одна из возможных реализаций:
struct TakeBytes<S> {
inner: S,
seen: usize,
limit: usize,
}
impl<S> Stream for TakeBytes<S>
where
S: Stream<Item = Vec<u8>>,
{
type Item = Vec<u8>;
type Error = S::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if self.seen >= self.limit {
return Ok(Async::Ready(None)); // Stream is over
}
let inner = self.inner.poll();
if let Ok(Async::Ready(Some(ref v))) = inner {
self.seen += v.len();
}
inner
}
}
trait TakeBytesExt: Sized {
fn take_bytes(self, limit: usize) -> TakeBytes<Self>;
}
impl<S> TakeBytesExt for S
where
S: Stream<Item = Vec<u8>>,
{
fn take_bytes(self, limit: usize) -> TakeBytes<Self> {
TakeBytes {
inner: self,
limit,
seen: 0,
}
}
}
Это может быть затем присоединено к потоку до concat2
:
fn limited() -> impl Future<Item = Vec<u8>, Error = ()> {
some_bytes().take_bytes(999).concat2()
}
Эта реализация имеет предостережения:
- работает только для
Vec<u8>
. Конечно, вы можете использовать дженерики, чтобы сделать его более применимым.
- он позволяет вводить больше байтов, чем предел, он просто останавливает поток после этой точки. Эти типы решений зависят от приложения.
Еще одна вещь, которую нужно иметь в виду, это то, что вы хотите попытаться решить эту проблему настолько низко, насколько это возможно - если источник данных уже выделил гигабайт памяти, размещение ограничения не поможет так же.