-
Notifications
You must be signed in to change notification settings - Fork 150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce a StreamingBuffer #348
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
use std::io::Write; | ||
use std::vec::Vec; | ||
|
||
use crate::pod::{bytes_of, bytes_of_slice, Pod}; | ||
|
@@ -6,14 +7,18 @@ use crate::pod::{bytes_of, bytes_of_slice, Pod}; | |
#[allow(clippy::len_without_is_empty)] | ||
pub trait WritableBuffer { | ||
/// Returns position/offset for data to be written at. | ||
/// Should only be used in debug assertions | ||
fn len(&self) -> usize; | ||
|
||
/// Reserves specified number of bytes in the buffer. | ||
fn reserve(&mut self, additional: usize) -> Result<(), ()>; | ||
/// Must be called exactly once before writing anything to the buffer. | ||
/// Must be given the exact of the buffer after writing everything, calling with a smaller size | ||
/// may result in a panic, while calling with a bigger size may result in trailing garbage. | ||
fn reserve(&mut self, size: usize) -> Result<(), ()>; | ||
|
||
/// Writes the specified value at the end of the buffer | ||
/// until the buffer has the specified length. | ||
fn resize(&mut self, new_len: usize, value: u8); | ||
/// Writes zero bytes at the end of the buffer until the buffer | ||
/// has the specified length. | ||
fn resize(&mut self, new_len: usize); | ||
|
||
/// Writes the specified slice of bytes at the end of the buffer. | ||
fn write_bytes(&mut self, val: &[u8]); | ||
|
@@ -54,22 +59,74 @@ impl WritableBuffer for Vec<u8> { | |
} | ||
|
||
#[inline] | ||
fn reserve(&mut self, additional: usize) -> Result<(), ()> { | ||
self.reserve(additional); | ||
fn reserve(&mut self, size: usize) -> Result<(), ()> { | ||
assert_eq!(self.len(), 0, "Buffer must be empty"); | ||
self.reserve(size); | ||
Ok(()) | ||
} | ||
|
||
#[inline] | ||
fn resize(&mut self, new_len: usize, value: u8) { | ||
self.resize(new_len, value); | ||
fn resize(&mut self, new_len: usize) { | ||
debug_assert!(new_len >= self.len()); | ||
self.resize(new_len, 0); | ||
} | ||
|
||
#[inline] | ||
fn write_bytes(&mut self, val: &[u8]) { | ||
debug_assert!(self.len() + val.len() <= self.capacity()); | ||
self.extend_from_slice(val) | ||
} | ||
} | ||
|
||
/// A [`WritableBuffer`] that streams data to a [`Write`]r. | ||
/// | ||
/// It is advisable to use a buffered writer like [`BufWriter`](std::io::BufWriter) instead of an | ||
/// unbuffered writer like [`File`](std::fs::File). | ||
#[derive(Debug)] | ||
pub struct StreamingBuffer<W> { | ||
writer: W, | ||
length: usize, | ||
} | ||
|
||
impl<W> StreamingBuffer<W> { | ||
/// Create a new `WriteBuf` backed by the given writer. | ||
pub fn new(writer: W) -> Self { | ||
StreamingBuffer { writer, length: 0 } | ||
} | ||
|
||
/// Unwraps this [`WriteBuf`] giving back the original writer. | ||
pub fn into_inner(self) -> W { | ||
self.writer | ||
} | ||
} | ||
|
||
impl<W: Write> WritableBuffer for StreamingBuffer<W> { | ||
#[inline] | ||
fn len(&self) -> usize { | ||
self.length | ||
} | ||
|
||
#[inline] | ||
fn reserve(&mut self, _additional: usize) -> Result<(), ()> { | ||
Ok(()) | ||
} | ||
|
||
#[inline] | ||
fn resize(&mut self, new_len: usize) { | ||
debug_assert!(new_len >= self.length); | ||
while self.length < new_len { | ||
let write_amt = (new_len - self.length - 1) % 1024 + 1; | ||
self.write_bytes(&[0; 1024][..write_amt]); | ||
} | ||
} | ||
|
||
#[inline] | ||
fn write_bytes(&mut self, val: &[u8]) { | ||
self.writer.write_all(val).unwrap(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure that unwrap is correct here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It isn't. I forgot to change this function to return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It'd be nice if instead of returning a |
||
self.length += val.len(); | ||
} | ||
} | ||
|
||
/// A trait for mutable byte slices. | ||
/// | ||
/// It provides convenience methods for `Pod` types. | ||
|
@@ -99,7 +156,7 @@ pub(crate) fn align_u64(offset: u64, size: u64) -> u64 { | |
|
||
pub(crate) fn write_align(buffer: &mut dyn WritableBuffer, size: usize) { | ||
let new_len = align(buffer.len(), size); | ||
buffer.resize(new_len, 0); | ||
buffer.resize(new_len); | ||
} | ||
|
||
#[cfg(test)] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.