Skip to content

Commit 86cdb42

Browse files
committed
feat: Add Buffers type.
It allows to more easily manage a form of 'double buffering' to better manage conditional alteration of a source buffer, and to implement conversion pipelines which conditionally transform an input over multiple steps.
1 parent ee47fba commit 86cdb42

File tree

4 files changed

+105
-0
lines changed

4 files changed

+105
-0
lines changed

gix-utils/src/buffers.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use crate::Buffers;
2+
3+
/// Lifecycle
4+
impl Buffers {
5+
/// Use this if there is an input buffer `src` which isn't owned by you, but which should be used as source when
6+
/// asking for [`src_and_dest()`](WithForeignSource::src_and_dest()).
7+
pub fn use_foreign_src<'a, 'src>(&'a mut self, src: &'src [u8]) -> WithForeignSource<'src, 'a> {
8+
self.clear();
9+
WithForeignSource {
10+
ro_src: Some(src),
11+
src: &mut self.src,
12+
dest: &mut self.dest,
13+
}
14+
}
15+
}
16+
17+
impl Buffers {
18+
/// Clear all buffers, which should be called once processing is done.
19+
pub fn clear(&mut self) {
20+
self.src.clear();
21+
self.dest.clear();
22+
}
23+
24+
/// Must be called after every change (i.e. when it's known that `dest` was written.
25+
pub fn swap(&mut self) {
26+
std::mem::swap(&mut self.src, &mut self.dest);
27+
}
28+
}
29+
30+
/// A utility to do buffer-swapping with, similar to [`Buffers`], but with support for a
31+
/// read-only one-time buffer as source.
32+
pub struct WithForeignSource<'src, 'bufs> {
33+
/// The original source buffer, or `None` if already altered.
34+
pub ro_src: Option<&'src [u8]>,
35+
/// The source buffer that will be used after the first call to `swap`.
36+
pub src: &'bufs mut Vec<u8>,
37+
dest: &'bufs mut Vec<u8>,
38+
}
39+
40+
impl<'bufs> WithForeignSource<'_, 'bufs> {
41+
/// Must be called after every change (i.e. when it's known that `dest` was written.
42+
pub fn swap(&mut self) {
43+
self.ro_src.take();
44+
std::mem::swap(&mut self.src, &mut self.dest);
45+
self.dest.clear();
46+
}
47+
/// Obtain `(source, destination)`, which reads from the read-only source exactly once.
48+
pub fn src_and_dest(&mut self) -> (&[u8], &mut Vec<u8>) {
49+
match self.ro_src {
50+
Some(src) => (src, &mut self.dest),
51+
None => (self.src, &mut self.dest),
52+
}
53+
}
54+
}

gix-utils/src/lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,20 @@
66

77
///
88
pub mod backoff;
9+
10+
///
11+
pub mod buffers;
12+
13+
/// A utility to do buffer-swapping with.
14+
///
15+
/// Use `src` to read from and `dest` to write to, and after actually changing data, call [Buffers::swap()].
16+
/// To be able to repeat the process, this time using what was `dest` as `src`, freeing up `dest` for writing once more.
17+
///
18+
/// Note that after each [`Buffers::swap()`], `src` is the most recent version of the data, just like before each swap.
19+
#[derive(Default, Clone)]
20+
pub struct Buffers {
21+
/// The source data, as basis for processing.
22+
pub src: Vec<u8>,
23+
/// The data produced after processing `src`.
24+
pub dest: Vec<u8>,
25+
}

gix-utils/tests/buffers/mod.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use gix_utils::Buffers;
2+
3+
#[test]
4+
fn lifecycle() {
5+
let mut bufs = Buffers::default();
6+
let mut bufs = bufs.use_foreign_src(b"a");
7+
8+
assert_eq!(bufs.ro_src.unwrap(), b"a");
9+
10+
let (src, dest) = bufs.src_and_dest();
11+
12+
assert_eq!(src, b"a");
13+
assert_eq!(dest.len(), 0);
14+
dest.push(b'b');
15+
16+
bufs.swap();
17+
18+
assert_eq!(bufs.src, b"b");
19+
assert!(bufs.ro_src.is_none());
20+
21+
let (src, dest) = bufs.src_and_dest();
22+
assert_eq!(src, b"b");
23+
assert_eq!(
24+
dest.len(),
25+
0,
26+
"the original previously empty source buffer was swapped in "
27+
);
28+
dest.push(b'c');
29+
bufs.swap();
30+
let (src, dest) = bufs.src_and_dest();
31+
assert_eq!(src, b"c");
32+
assert_eq!(dest.len(), 0, "dest always starting out empty");
33+
}

gix-utils/tests/utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
mod backoff;
2+
mod buffers;

0 commit comments

Comments
 (0)