⬆️ refactor superkmer to use obipipeline

- Replace manual threading with Pipeline abstraction from `obipipline`
- Remove crossbeam-channel dependency and format detection logic
- Introduce typed `PipelineData` enum for pipeline stages (RawChunk, Norm Chunk, Batch)
- Implement shared normalization and extraction steps as `SharedFn`ƒ
  - Add unsafe Send/Sync impls for PipelineData (Rope ownership is moved, not shared)
- Replace manual reader/worker/output threads with a single Pipeline execution
  - Uses `make_source_fallible!`, shared transform functions, and a sink for output
- Simplify argument handling (remove `--format` flag)
  - Update Cargo.toml: remove crossbeam-channel, add obipipeline
This commit is contained in:
Eric Coissac
2026-04-24 18:17:19 +02:00
parent 75bf980046
commit f1c8fc85c9
10 changed files with 236 additions and 75 deletions
+8 -5
View File
@@ -194,7 +194,10 @@ pub trait RopeCursor<'a> {
/// Use the returned value with [`SeekMode::Rope`] to restore a position,
/// or as a truncation point after a write pass.
fn rope_tell(&self) -> usize {
self.state().current.get().unwrap_or(self.state().offset.get())
self.state()
.current
.get()
.unwrap_or(self.state().offset.get())
}
/// Number of bytes visible through this cursor (`rope.len() - offset`).
@@ -528,13 +531,13 @@ mod tests {
use crate::Rope;
fn rope(data: &[u8]) -> Rope {
let mut r = Rope::new();
let mut r = Rope::new(None);
r.push(data.to_vec());
r
}
fn rope2(a: &[u8], b: &[u8]) -> Rope {
let mut r = Rope::new();
let mut r = Rope::new(None);
r.push(a.to_vec());
r.push(b.to_vec());
r
@@ -703,14 +706,14 @@ mod tests {
#[test]
fn backward_empty_rope_returns_error() {
let r = Rope::new();
let r = Rope::new(None);
let c = r.bw_cursor();
assert!(c.read_next().is_err());
}
#[test]
fn forward_empty_rope_returns_error() {
let r = Rope::new();
let r = Rope::new(None);
let c = r.fw_cursor();
assert!(c.read_next().is_err());
}
+16 -6
View File
@@ -28,6 +28,7 @@ use std::cell::Cell;
///
/// See the [module-level documentation][crate::rope] for a full overview.
pub struct Rope {
pub(crate) mime_type: Option<&'static str>,
pub(crate) blocks: Vec<Vec<Cell<u8>>>,
pub(crate) length: usize,
pub(crate) start_block_idx: Vec<usize>,
@@ -35,8 +36,9 @@ pub struct Rope {
impl Rope {
/// Create an empty rope (no allocations).
pub fn new() -> Self {
pub fn new(mime_type: Option<&'static str>) -> Self {
Self {
mime_type,
blocks: Vec::new(),
length: 0,
start_block_idx: Vec::new(),
@@ -59,6 +61,11 @@ impl Rope {
self.length += block_len;
}
/// The MIME type of the rope, if known.
pub fn mime_type(&self) -> Option<&str> {
self.mime_type.as_deref()
}
/// Total number of blocks.
pub fn n_blocks(&self) -> usize {
self.blocks.len()
@@ -115,7 +122,7 @@ impl Rope {
}
if pos == self.length {
return Ok(Rope::new());
return Ok(Rope::new(self.mime_type.clone()));
}
let (block_idx, from, _) = self.lookup(pos).ok_or_else(|| {
@@ -149,6 +156,7 @@ impl Rope {
self.length = pos;
Ok(Rope {
mime_type: self.mime_type.clone(),
blocks: tail_blocks,
length: tail_length,
start_block_idx: tail_starts,
@@ -185,13 +193,13 @@ mod tests {
}
fn make(data: &[u8]) -> Rope {
let mut r = Rope::new();
let mut r = Rope::new(None);
r.push(data.to_vec());
r
}
fn make2(a: &[u8], b: &[u8]) -> Rope {
let mut r = Rope::new();
let mut r = Rope::new(None);
r.push(a.to_vec());
r.push(b.to_vec());
r
@@ -201,7 +209,7 @@ mod tests {
#[test]
fn empty_rope_is_empty() {
let r = Rope::new();
let r = Rope::new(None);
assert!(r.is_empty());
assert_eq!(r.len(), 0);
assert_eq!(r.n_blocks(), 0);
@@ -213,6 +221,7 @@ mod tests {
assert!(!r.is_empty());
assert_eq!(r.len(), 5);
assert_eq!(r.n_blocks(), 1);
assert_eq!(r.mime_type(), None);
}
#[test]
@@ -220,6 +229,7 @@ mod tests {
let r = make2(b"abc", b"de");
assert_eq!(r.len(), 5);
assert_eq!(r.n_blocks(), 2);
assert_eq!(r.mime_type(), None);
}
#[test]
@@ -261,7 +271,7 @@ mod tests {
#[test]
fn lookup_empty_rope_returns_none() {
assert!(Rope::new().lookup(0).is_none());
assert!(Rope::new(None).lookup(0).is_none());
}
#[test]