Skip to content

Commit c4a996a

Browse files
committed
alloc: Reduce unnecessary Vec allocations and indirections
* Changed literal_probs array from a Vec<Vec<u16>> to a Vec2D backed by a contiguous Vec<u16>. * BitTrees in LenDecoder and DecoderState are now stored inline
1 parent 6e1f0d7 commit c4a996a

File tree

4 files changed

+125
-10
lines changed

4 files changed

+125
-10
lines changed

src/decode/lzma.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::decode::lzbuffer::{LzBuffer, LzCircularBuffer};
22
use crate::decode::rangecoder;
33
use crate::decode::rangecoder::RangeDecoder;
4+
use crate::decode::vec2d::Vec2D;
45
use crate::decompress::Options;
56
use crate::decompress::UnpackedSize;
67
use crate::error;
@@ -167,8 +168,8 @@ pub(crate) struct DecoderState {
167168
partial_input_buf: std::io::Cursor<[u8; MAX_REQUIRED_INPUT]>,
168169
pub(crate) lzma_props: LzmaProperties,
169170
unpacked_size: Option<u64>,
170-
literal_probs: Vec<Vec<u16>>,
171-
pos_slot_decoder: Vec<rangecoder::BitTree>,
171+
literal_probs: Vec2D<u16>,
172+
pos_slot_decoder: [rangecoder::BitTree; 4],
172173
align_decoder: rangecoder::BitTree,
173174
pos_decoders: [u16; 115],
174175
is_match: [u16; 192], // true = LZ, false = literal
@@ -190,8 +191,13 @@ impl DecoderState {
190191
partial_input_buf: std::io::Cursor::new([0; MAX_REQUIRED_INPUT]),
191192
lzma_props,
192193
unpacked_size,
193-
literal_probs: vec![vec![0x400; 0x300]; 1 << (lzma_props.lc + lzma_props.lp)],
194-
pos_slot_decoder: vec![rangecoder::BitTree::new(6); 4],
194+
literal_probs: Vec2D::init(1 << (lzma_props.lc + lzma_props.lp), 0x300, 0x400),
195+
pos_slot_decoder: [
196+
rangecoder::BitTree::new(6),
197+
rangecoder::BitTree::new(6),
198+
rangecoder::BitTree::new(6),
199+
rangecoder::BitTree::new(6),
200+
],
195201
align_decoder: rangecoder::BitTree::new(4),
196202
pos_decoders: [0x400; 115],
197203
is_match: [0x400; 192],
@@ -211,10 +217,10 @@ impl DecoderState {
211217
new_props.validate();
212218
if self.lzma_props.lc + self.lzma_props.lp == new_props.lc + new_props.lp {
213219
// We can reset here by filling the existing buffer with 0x400.
214-
self.literal_probs.iter_mut().for_each(|v| v.fill(0x400))
220+
self.literal_probs.fill(0x400);
215221
} else {
216222
// We need to reallocate because of the new size of `lc+lp`.
217-
self.literal_probs = vec![vec![0x400; 0x300]; 1 << (new_props.lc + new_props.lp)];
223+
self.literal_probs = Vec2D::init(1 << (new_props.lc + new_props.lp), 0x300, 0x400);
218224
}
219225

220226
self.lzma_props = new_props;

src/decode/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod lzma2;
66
pub mod options;
77
pub mod rangecoder;
88
pub mod util;
9+
pub mod vec2d;
910
pub mod xz;
1011

1112
#[cfg(feature = "stream")]

src/decode/rangecoder.rs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ impl BitTree {
190190
pub struct LenDecoder {
191191
choice: u16,
192192
choice2: u16,
193-
low_coder: Vec<BitTree>,
194-
mid_coder: Vec<BitTree>,
193+
low_coder: [BitTree; 16],
194+
mid_coder: [BitTree; 16],
195195
high_coder: BitTree,
196196
}
197197

@@ -200,8 +200,42 @@ impl LenDecoder {
200200
LenDecoder {
201201
choice: 0x400,
202202
choice2: 0x400,
203-
low_coder: vec![BitTree::new(3); 16],
204-
mid_coder: vec![BitTree::new(3); 16],
203+
low_coder: [
204+
BitTree::new(3),
205+
BitTree::new(3),
206+
BitTree::new(3),
207+
BitTree::new(3),
208+
BitTree::new(3),
209+
BitTree::new(3),
210+
BitTree::new(3),
211+
BitTree::new(3),
212+
BitTree::new(3),
213+
BitTree::new(3),
214+
BitTree::new(3),
215+
BitTree::new(3),
216+
BitTree::new(3),
217+
BitTree::new(3),
218+
BitTree::new(3),
219+
BitTree::new(3),
220+
],
221+
mid_coder: [
222+
BitTree::new(3),
223+
BitTree::new(3),
224+
BitTree::new(3),
225+
BitTree::new(3),
226+
BitTree::new(3),
227+
BitTree::new(3),
228+
BitTree::new(3),
229+
BitTree::new(3),
230+
BitTree::new(3),
231+
BitTree::new(3),
232+
BitTree::new(3),
233+
BitTree::new(3),
234+
BitTree::new(3),
235+
BitTree::new(3),
236+
BitTree::new(3),
237+
BitTree::new(3),
238+
],
205239
high_coder: BitTree::new(8),
206240
}
207241
}

src/decode/vec2d.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use std::ops::{Index, IndexMut};
2+
3+
/// A 2 dimensional matrix in row-major order backed by a contiguous `Vec`
4+
#[derive(Debug)]
5+
pub struct Vec2D<T> {
6+
data: Vec<T>,
7+
cols: usize,
8+
rows: usize,
9+
}
10+
11+
impl<T> Vec2D<T> {
12+
/// Initialize a grid of size `rows * cols` with the given data element.
13+
pub fn init(rows: usize, cols: usize, data: T) -> Vec2D<T>
14+
where
15+
T: Clone,
16+
{
17+
Vec2D {
18+
data: vec![data; rows * cols],
19+
cols,
20+
rows,
21+
}
22+
}
23+
24+
/// Fills the grid with elements by cloning `value`.
25+
pub fn fill(&mut self, value: T)
26+
where
27+
T: Clone,
28+
{
29+
self.data.fill(value)
30+
}
31+
}
32+
33+
impl<T> Index<usize> for Vec2D<T> {
34+
type Output = [T];
35+
36+
fn index(&self, idx: usize) -> &Self::Output {
37+
if idx < self.rows {
38+
let start_idx = idx * self.cols;
39+
&self.data[start_idx..start_idx + self.cols]
40+
} else {
41+
panic!("row index {:?} out of bounds.", idx);
42+
}
43+
}
44+
}
45+
46+
impl<T> IndexMut<usize> for Vec2D<T> {
47+
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
48+
&mut self.data[(idx * self.cols)..]
49+
}
50+
}
51+
52+
#[cfg(test)]
53+
mod test {
54+
use super::*;
55+
56+
#[test]
57+
fn vec2d_fill() {
58+
let mut vec2d = Vec2D::init(3, 3, 0);
59+
vec2d.fill(7);
60+
assert_eq!(vec2d[0], [7, 7, 7]);
61+
assert_eq!(vec2d[1], [7, 7, 7]);
62+
}
63+
64+
#[test]
65+
fn vec2d_index() {
66+
let vec2d = Vec2D {
67+
data: vec![0, 1, 2, 3, 4, 5],
68+
rows: 3,
69+
cols: 3,
70+
};
71+
assert_eq!(vec2d[0], [0, 1, 2]);
72+
assert_eq!(vec2d[1], [3, 4, 5]);
73+
}
74+
}

0 commit comments

Comments
 (0)