Skip to content

Commit b136363

Browse files
committed
Use on-stack buffer to avoid single-byte writes
1 parent 4ce5e09 commit b136363

File tree

2 files changed

+86
-44
lines changed

2 files changed

+86
-44
lines changed

src/encoder.rs

Lines changed: 85 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -184,33 +184,36 @@ impl<W: Write> Encoder<W> {
184184
frame.needs_user_input,
185185
frame.transparent,
186186
))?;
187-
let writer = self.w.as_mut().unwrap();
188-
writer.write_le(Block::Image as u8)?;
189-
writer.write_le(frame.left)?;
190-
writer.write_le(frame.top)?;
191-
writer.write_le(frame.width)?;
192-
writer.write_le(frame.height)?;
193187
let mut flags = 0;
194188
if frame.interlaced {
195189
flags |= 0b0100_0000;
196190
}
197-
match frame.palette {
191+
let palette = match frame.palette {
198192
Some(ref palette) => {
199193
flags |= 0b1000_0000;
200194
let num_colors = palette.len() / 3;
201195
if num_colors > 256 {
202196
return Err(EncodingError::from(EncodingFormatError::TooManyColors));
203197
}
204198
flags |= flag_size(num_colors);
205-
writer.write_le(flags)?;
206-
self.write_color_table(palette)
199+
Some(palette)
207200
},
208-
None => if !self.global_palette {
209-
Err(EncodingError::from(EncodingFormatError::MissingColorPalette))
210-
} else {
211-
writer.write_le(flags).map_err(Into::into)
212-
}
201+
None if self.global_palette => None,
202+
_ => return Err(EncodingError::from(EncodingFormatError::MissingColorPalette))
203+
};
204+
let mut tmp = tmp_buf::<10>();
205+
tmp.write_le(Block::Image as u8)?;
206+
tmp.write_le(frame.left)?;
207+
tmp.write_le(frame.top)?;
208+
tmp.write_le(frame.width)?;
209+
tmp.write_le(frame.height)?;
210+
tmp.write_le(flags)?;
211+
let writer = self.writer()?;
212+
tmp.finish(&mut *writer)?;
213+
if let Some(palette) = palette {
214+
writer.write_all(palette)?;
213215
}
216+
Ok(())
214217
}
215218

216219
fn write_image_block(&mut self, data: &[u8]) -> Result<(), EncodingError> {
@@ -219,7 +222,7 @@ impl<W: Write> Encoder<W> {
219222
.map_err(|_| io::Error::from(io::ErrorKind::OutOfMemory))?;
220223
lzw_encode(data, &mut self.buffer);
221224

222-
let writer = self.w.as_mut().unwrap();
225+
let writer = self.w.as_mut().ok_or(io::Error::from(io::ErrorKind::Unsupported))?;
223226
Self::write_encoded_image_block(writer, &self.buffer)
224227
}
225228

@@ -243,13 +246,13 @@ impl<W: Write> Encoder<W> {
243246
}
244247

245248
fn write_color_table(&mut self, table: &[u8]) -> Result<(), EncodingError> {
246-
let writer = self.w.as_mut().unwrap();
249+
let writer = self.writer()?;
247250
let num_colors = table.len() / 3;
248251
if num_colors > 256 {
249252
return Err(EncodingError::from(EncodingFormatError::TooManyColors));
250253
}
251-
let size = flag_size(num_colors);
252254
writer.write_all(&table[..num_colors * 3])?;
255+
let size = flag_size(num_colors);
253256
// Waste some space as of gif spec
254257
for _ in 0..((2 << size) - num_colors) {
255258
writer.write_all(&[0, 0, 0])?;
@@ -267,26 +270,30 @@ impl<W: Write> Encoder<W> {
267270
if let Repetitions(Repeat::Finite(0)) = extension {
268271
return Ok(());
269272
}
270-
let writer = self.w.as_mut().unwrap();
273+
let writer = self.writer()?;
271274
writer.write_le(Block::Extension as u8)?;
272275
match extension {
273276
Control { flags, delay, trns } => {
274-
writer.write_le(Extension::Control as u8)?;
275-
writer.write_le(4u8)?;
276-
writer.write_le(flags)?;
277-
writer.write_le(delay)?;
278-
writer.write_le(trns)?;
277+
let mut tmp = tmp_buf::<6>();
278+
tmp.write_le(Extension::Control as u8)?;
279+
tmp.write_le(4u8)?;
280+
tmp.write_le(flags)?;
281+
tmp.write_le(delay)?;
282+
tmp.write_le(trns)?;
283+
tmp.finish(&mut *writer)?;
279284
}
280285
Repetitions(repeat) => {
281-
writer.write_le(Extension::Application as u8)?;
282-
writer.write_le(11u8)?;
283-
writer.write_all(b"NETSCAPE2.0")?;
284-
writer.write_le(3u8)?;
285-
writer.write_le(1u8)?;
286-
match repeat {
287-
Repeat::Finite(no) => writer.write_le(no)?,
288-
Repeat::Infinite => writer.write_le(0u16)?,
289-
}
286+
let mut tmp = tmp_buf::<17>();
287+
tmp.write_le(Extension::Application as u8)?;
288+
tmp.write_le(11u8)?;
289+
tmp.write_all(b"NETSCAPE2.0")?;
290+
tmp.write_le(3u8)?;
291+
tmp.write_le(1u8)?;
292+
tmp.write_le(match repeat {
293+
Repeat::Finite(no) => no,
294+
Repeat::Infinite => 0u16,
295+
})?;
296+
tmp.finish(&mut *writer)?;
290297
}
291298
}
292299
writer.write_le(0u8).map_err(Into::into)
@@ -298,7 +305,7 @@ impl<W: Write> Encoder<W> {
298305
/// identifier (e.g. `Extension::Application as u8`). `data` are the extension payload blocks. If any
299306
/// contained slice has a lenght > 255 it is automatically divided into sub-blocks.
300307
pub fn write_raw_extension(&mut self, func: AnyExtension, data: &[&[u8]]) -> io::Result<()> {
301-
let writer = self.w.as_mut().unwrap();
308+
let writer = self.writer()?;
302309
writer.write_le(Block::Extension as u8)?;
303310
writer.write_le(func.0)?;
304311
for block in data {
@@ -323,19 +330,20 @@ impl<W: Write> Encoder<W> {
323330
}
324331

325332
self.write_frame_header(frame)?;
326-
let writer = self.w.as_mut().unwrap();
333+
let writer = self.writer()?;
327334
Self::write_encoded_image_block(writer, &frame.buffer)
328335
}
329336

330337
/// Writes the logical screen desriptor
331338
fn write_screen_desc(&mut self, flags: u8) -> io::Result<()> {
332-
let writer = self.w.as_mut().unwrap();
333-
writer.write_all(b"GIF89a")?;
334-
writer.write_le(self.width)?;
335-
writer.write_le(self.height)?;
336-
writer.write_le(flags)?; // packed field
337-
writer.write_le(0u8)?; // bg index
338-
writer.write_le(0u8) // aspect ratio
339+
let mut tmp = tmp_buf::<13>();
340+
tmp.write_all(b"GIF89a")?;
341+
tmp.write_le(self.width)?;
342+
tmp.write_le(self.height)?;
343+
tmp.write_le(flags)?; // packed field
344+
tmp.write_le(0u8)?; // bg index
345+
tmp.write_le(0u8)?; // aspect ratio
346+
tmp.finish(self.writer()?)
339347
}
340348

341349
/// Gets a reference to the writer instance used by this encoder.
@@ -353,12 +361,17 @@ impl<W: Write> Encoder<W> {
353361
/// Finishes writing, and returns the `io::Write` instance used by this encoder
354362
pub fn into_inner(mut self) -> io::Result<W> {
355363
self.write_trailer()?;
356-
Ok(self.w.take().unwrap())
364+
self.w.take().ok_or(io::Error::from(io::ErrorKind::Unsupported))
357365
}
358366

359367
/// Write the final tailer.
360368
fn write_trailer(&mut self) -> io::Result<()> {
361-
self.w.as_mut().unwrap().write_le(Block::Trailer as u8)
369+
self.writer()?.write_le(Block::Trailer as u8)
370+
}
371+
372+
#[inline]
373+
fn writer(&mut self) -> io::Result<&mut W> {
374+
self.w.as_mut().ok_or(io::Error::from(io::ErrorKind::Unsupported))
362375
}
363376
}
364377

@@ -438,6 +451,35 @@ fn flag_size(size: usize) -> u8 {
438451
}
439452
}
440453

454+
struct Buf<const N: usize> {
455+
buf: [u8; N], pos: usize
456+
}
457+
458+
impl<const N: usize> Write for Buf<N> {
459+
#[inline(always)]
460+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
461+
let len = buf.len();
462+
let pos = self.pos;
463+
self.buf[pos.. pos + len].copy_from_slice(buf);
464+
self.pos += len;
465+
Ok(len)
466+
}
467+
468+
fn flush(&mut self) -> io::Result<()> { Ok(()) }
469+
}
470+
471+
fn tmp_buf<const N: usize>() -> Buf<N> {
472+
Buf { buf: [0; N], pos: 0 }
473+
}
474+
475+
impl<const N: usize> Buf<N> {
476+
#[inline(always)]
477+
fn finish(&mut self, mut w: impl Write) -> io::Result<()> {
478+
debug_assert_eq!(self.pos, N);
479+
w.write_all(&self.buf)
480+
}
481+
}
482+
441483
#[test]
442484
fn error_cast() {
443485
let _ : Box<dyn error::Error> = EncodingError::from(EncodingFormatError::MissingColorPalette).into();

src/traits.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub trait WriteBytesExt<T> {
1515
}
1616

1717
impl<W: io::Write + ?Sized> WriteBytesExt<u8> for W {
18-
#[inline]
18+
#[inline(always)]
1919
fn write_le(&mut self, n: u8) -> io::Result<()> {
2020
self.write_all(&[n])
2121
}

0 commit comments

Comments
 (0)