♻️ refactor rope implementation to use obikrope
- rename `obirope` → `obikroper`
- replace legacy rope with new in-place, Cell-based implementation
- add ForwardCursor/Backward Cursor & SeekMode support (no more BytesMut)
- update all dependents:
- obiread: switch to Rope + cursors, remove tape.rs
• chunk iterator yields `Rope` instead of Vec<Bytes>
- obiskbuilder: use ForwardCursor over Rope
- remove bytes dependency from affected crates
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "obikrope"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion2 = { version = "3", features = ["cargo_bench_support"] }
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.11.1"
|
||||
@@ -0,0 +1,310 @@
|
||||
use std::cell::Cell;
|
||||
|
||||
use crate::{Rope, RopeError};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum SeekMode {
|
||||
Absolute,
|
||||
Relative,
|
||||
RelativeToEnd,
|
||||
}
|
||||
|
||||
// ── shared state ──────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Clone)]
|
||||
struct CursorState<'a> {
|
||||
block_idx: Cell<usize>,
|
||||
block_start: Cell<usize>,
|
||||
block_end: Cell<usize>,
|
||||
block: Cell<&'a [Cell<u8>]>,
|
||||
initialized: Cell<bool>,
|
||||
current: Cell<Option<usize>>,
|
||||
}
|
||||
|
||||
impl<'a> CursorState<'a> {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
block_idx: Cell::new(0),
|
||||
block_start: Cell::new(0),
|
||||
block_end: Cell::new(0),
|
||||
block: Cell::new(&[]),
|
||||
initialized: Cell::new(false),
|
||||
current: Cell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, rope: &'a Rope, i: usize) -> Option<u8> {
|
||||
if !self.initialized.get() || i < self.block_start.get() || i >= self.block_end.get() {
|
||||
let (bi, bs, be) = rope.lookup(i)?;
|
||||
self.block_idx.set(bi);
|
||||
self.block_start.set(bs);
|
||||
self.block_end.set(be);
|
||||
self.block.set(rope.get_block(bi)?);
|
||||
self.initialized.set(true);
|
||||
}
|
||||
Some(self.block.get()[i - self.block_start.get()].get())
|
||||
}
|
||||
|
||||
fn set(&self, rope: &'a Rope, i: usize, value: u8) -> Result<(), RopeError> {
|
||||
if !self.initialized.get() || i < self.block_start.get() || i >= self.block_end.get() {
|
||||
let (bi, bs, be) = rope.lookup(i).ok_or(
|
||||
RopeError::OutOfBounds(format!("index out of bounds: i={} > {}", i, rope.len())),
|
||||
)?;
|
||||
self.block_idx.set(bi);
|
||||
self.block_start.set(bs);
|
||||
self.block_end.set(be);
|
||||
self.block.set(rope.get_block(bi).ok_or(RopeError::BlockNotFound(format!(
|
||||
"Cannot find block for index {}",
|
||||
i
|
||||
)))?);
|
||||
self.initialized.set(true);
|
||||
}
|
||||
self.block.get()[i - self.block_start.get()].set(value);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ── trait ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub trait RopeCursor {
|
||||
fn get(&self, i: usize) -> Option<u8>;
|
||||
fn set(&self, i: usize, value: u8) -> Result<(), RopeError>;
|
||||
fn peek(&self) -> Option<u8>;
|
||||
fn poke(&self, value: u8) -> Result<(), RopeError>;
|
||||
fn tell(&self) -> Option<usize>;
|
||||
fn seek(&self, pos: isize, mode: SeekMode) -> Result<usize, RopeError>;
|
||||
fn rewind(&self, go_back_of: usize) -> Result<(), RopeError>;
|
||||
fn len(&self) -> usize;
|
||||
fn read_next(&self) -> Result<u8, RopeError>;
|
||||
}
|
||||
|
||||
// ── ForwardCursor ─────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ForwardCursor<'a> {
|
||||
rope: &'a Rope,
|
||||
state: CursorState<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ForwardCursor<'a> {
|
||||
pub fn new(rope: &'a Rope) -> Self {
|
||||
Self { rope, state: CursorState::new() }
|
||||
}
|
||||
|
||||
pub fn read_ahead(&self, ahead: usize) -> Result<u8, RopeError> {
|
||||
let pos = self.state.current.get().ok_or(RopeError::CurrentNotSet)?;
|
||||
self.state
|
||||
.get(self.rope, pos + ahead)
|
||||
.ok_or(RopeError::OutOfBounds(format!(
|
||||
"index out of bounds: i={} + {} > {}",
|
||||
pos, ahead, self.rope.len()
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn write(&self, value: u8) -> Result<(), RopeError> {
|
||||
let pos = self.state.current.get().unwrap_or(0);
|
||||
self.state.set(self.rope, pos, value)?;
|
||||
self.state.current.set(Some(pos + 1));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> ForwardIter<'a, '_> {
|
||||
ForwardIter { cursor: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RopeCursor for ForwardCursor<'a> {
|
||||
fn get(&self, i: usize) -> Option<u8> {
|
||||
self.state.get(self.rope, i)
|
||||
}
|
||||
|
||||
fn set(&self, i: usize, value: u8) -> Result<(), RopeError> {
|
||||
self.state.set(self.rope, i, value)
|
||||
}
|
||||
|
||||
fn peek(&self) -> Option<u8> {
|
||||
self.state.get(self.rope, self.state.current.get()?)
|
||||
}
|
||||
|
||||
fn poke(&self, value: u8) -> Result<(), RopeError> {
|
||||
let pos = self.state.current.get().ok_or(RopeError::CurrentNotSet)?;
|
||||
self.state.set(self.rope, pos, value)
|
||||
}
|
||||
|
||||
fn tell(&self) -> Option<usize> {
|
||||
self.state.current.get()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.rope.len()
|
||||
}
|
||||
|
||||
fn read_next(&self) -> Result<u8, RopeError> {
|
||||
let next_pos = match self.state.current.get() {
|
||||
Some(i) => i + 1,
|
||||
None => 0,
|
||||
};
|
||||
let value = self.state
|
||||
.get(self.rope, next_pos)
|
||||
.ok_or(RopeError::OutOfBounds(format!(
|
||||
"index out of bounds: i={} > {}",
|
||||
next_pos, self.rope.len()
|
||||
)))?;
|
||||
self.state.current.set(Some(next_pos));
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn seek(&self, pos: isize, mode: SeekMode) -> Result<usize, RopeError> {
|
||||
let pos = match mode {
|
||||
SeekMode::Absolute => pos,
|
||||
SeekMode::Relative => self.state.current.get().ok_or(RopeError::CurrentNotSet)? as isize + pos,
|
||||
SeekMode::RelativeToEnd => self.rope.len() as isize - pos,
|
||||
};
|
||||
if pos < 0 {
|
||||
return Err(RopeError::OutOfBounds(format!("index out of bounds: i={} < 0", pos)));
|
||||
}
|
||||
self.state.current.set(Some(pos as usize));
|
||||
Ok(pos as usize)
|
||||
}
|
||||
|
||||
fn rewind(&self, go_back_of: usize) -> Result<(), RopeError> {
|
||||
self.seek(-(go_back_of as isize), SeekMode::Relative)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ForwardCursor<'_> {
|
||||
type Item = u8;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.read_next().ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ForwardIter<'a, 'b> {
|
||||
cursor: &'b ForwardCursor<'a>,
|
||||
}
|
||||
|
||||
impl Iterator for ForwardIter<'_, '_> {
|
||||
type Item = u8;
|
||||
fn next(&mut self) -> Option<u8> {
|
||||
self.cursor.read_next().ok()
|
||||
}
|
||||
}
|
||||
|
||||
// ── BackwardCursor ────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BackwardCursor<'a> {
|
||||
rope: &'a Rope,
|
||||
state: CursorState<'a>,
|
||||
}
|
||||
|
||||
impl<'a> BackwardCursor<'a> {
|
||||
pub fn new(rope: &'a Rope) -> Self {
|
||||
Self { rope, state: CursorState::new() }
|
||||
}
|
||||
|
||||
pub fn read_behind(&self, behind: usize) -> Result<u8, RopeError> {
|
||||
let pos = self.state.current.get().ok_or(RopeError::CurrentNotSet)?;
|
||||
let target = pos
|
||||
.checked_add(behind)
|
||||
.filter(|&t| t < self.rope.len())
|
||||
.ok_or(RopeError::OutOfBounds(format!(
|
||||
"index out of bounds: i={} + {} > {}",
|
||||
pos, behind, self.rope.len()
|
||||
)))?;
|
||||
self.state
|
||||
.get(self.rope, target)
|
||||
.ok_or(RopeError::OutOfBounds(format!(
|
||||
"index out of bounds: i={} + {} > {}",
|
||||
pos, behind, self.rope.len()
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> BackwardIter<'a, '_> {
|
||||
BackwardIter { cursor: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RopeCursor for BackwardCursor<'a> {
|
||||
fn get(&self, i: usize) -> Option<u8> {
|
||||
self.state.get(self.rope, i)
|
||||
}
|
||||
|
||||
fn set(&self, i: usize, value: u8) -> Result<(), RopeError> {
|
||||
self.state.set(self.rope, i, value)
|
||||
}
|
||||
|
||||
fn peek(&self) -> Option<u8> {
|
||||
self.state.get(self.rope, self.state.current.get()?)
|
||||
}
|
||||
|
||||
fn poke(&self, value: u8) -> Result<(), RopeError> {
|
||||
let pos = self.state.current.get().ok_or(RopeError::CurrentNotSet)?;
|
||||
self.state.set(self.rope, pos, value)
|
||||
}
|
||||
|
||||
fn tell(&self) -> Option<usize> {
|
||||
self.state.current.get()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.rope.len()
|
||||
}
|
||||
|
||||
fn read_next(&self) -> Result<u8, RopeError> {
|
||||
let next_pos = match self.state.current.get() {
|
||||
None => self.rope.len().checked_sub(1).ok_or(RopeError::OutOfBounds(
|
||||
"BackwardCursor: rope is empty".to_string(),
|
||||
))?,
|
||||
Some(0) => return Err(RopeError::OutOfBounds(
|
||||
"BackwardCursor: already at beginning".to_string(),
|
||||
)),
|
||||
Some(i) => i - 1,
|
||||
};
|
||||
let value = self.state
|
||||
.get(self.rope, next_pos)
|
||||
.ok_or(RopeError::OutOfBounds(format!(
|
||||
"BackwardCursor: index out of bounds at i={}",
|
||||
next_pos
|
||||
)))?;
|
||||
self.state.current.set(Some(next_pos));
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn seek(&self, pos: isize, mode: SeekMode) -> Result<usize, RopeError> {
|
||||
let pos = match mode {
|
||||
SeekMode::Absolute => pos,
|
||||
SeekMode::Relative => self.state.current.get().ok_or(RopeError::CurrentNotSet)? as isize - pos,
|
||||
SeekMode::RelativeToEnd => self.rope.len() as isize - pos,
|
||||
};
|
||||
if pos < 0 {
|
||||
return Err(RopeError::OutOfBounds(format!("index out of bounds: i={} < 0", pos)));
|
||||
}
|
||||
self.state.current.set(Some(pos as usize));
|
||||
Ok(pos as usize)
|
||||
}
|
||||
|
||||
fn rewind(&self, go_back_of: usize) -> Result<(), RopeError> {
|
||||
self.seek(-(go_back_of as isize), SeekMode::Relative)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for BackwardCursor<'_> {
|
||||
type Item = u8;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.read_next().ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BackwardIter<'a, 'b> {
|
||||
cursor: &'b BackwardCursor<'a>,
|
||||
}
|
||||
|
||||
impl Iterator for BackwardIter<'_, '_> {
|
||||
type Item = u8;
|
||||
fn next(&mut self) -> Option<u8> {
|
||||
self.cursor.read_next().ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#[derive(Debug)]
|
||||
pub enum RopeError {
|
||||
OutOfBounds(String),
|
||||
BlockNotFound(String),
|
||||
CurrentNotSet,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod rope;
|
||||
|
||||
pub use {
|
||||
cursor::BackwardCursor, cursor::ForwardCursor, cursor::RopeCursor, cursor::SeekMode,
|
||||
error::RopeError, rope::Rope,
|
||||
};
|
||||
@@ -0,0 +1,114 @@
|
||||
use crate::{BackwardCursor, ForwardCursor, RopeError};
|
||||
use std::cell::Cell;
|
||||
|
||||
pub struct Rope {
|
||||
pub(crate) blocks: Vec<Vec<Cell<u8>>>,
|
||||
pub(crate) length: usize,
|
||||
pub(crate) start_block_idx: Vec<usize>,
|
||||
}
|
||||
|
||||
impl Rope {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
blocks: Vec::new(),
|
||||
length: 0,
|
||||
start_block_idx: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, block: Vec<u8>) {
|
||||
let block_len = block.len();
|
||||
self.start_block_idx.push(self.length);
|
||||
// Safety: Cell<u8> has the same memory layout as u8 (guaranteed by the language)
|
||||
let cell_block: Vec<Cell<u8>> = unsafe {
|
||||
let mut v = std::mem::ManuallyDrop::new(block);
|
||||
Vec::from_raw_parts(v.as_mut_ptr() as *mut Cell<u8>, v.len(), v.capacity())
|
||||
};
|
||||
self.blocks.push(cell_block);
|
||||
self.length += block_len;
|
||||
}
|
||||
|
||||
pub fn n_blocks(&self) -> usize {
|
||||
self.blocks.len()
|
||||
}
|
||||
|
||||
pub(crate) fn get_block(&self, block_idx: usize) -> Option<&[Cell<u8>]> {
|
||||
self.blocks.get(block_idx).map(Vec::as_slice)
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.length
|
||||
}
|
||||
|
||||
pub(crate) fn lookup(&self, i: usize) -> Option<(usize, usize, usize)> {
|
||||
if i >= self.length || self.blocks.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let block_idx = self.start_block_idx.partition_point(|&s| s <= i) - 1;
|
||||
let from = self.start_block_idx[block_idx];
|
||||
let to = if block_idx + 1 < self.blocks.len() {
|
||||
self.start_block_idx[block_idx + 1]
|
||||
} else {
|
||||
self.length
|
||||
};
|
||||
Some((block_idx, from, to))
|
||||
}
|
||||
|
||||
pub fn split_off(&mut self, pos: usize) -> Result<Rope, RopeError> {
|
||||
if pos > self.length {
|
||||
return Err(RopeError::OutOfBounds(format!(
|
||||
"split_at: pos={} > length={}",
|
||||
pos, self.length
|
||||
)));
|
||||
}
|
||||
|
||||
// pos == length: tail is empty.
|
||||
if pos == self.length {
|
||||
return Ok(Rope::new());
|
||||
}
|
||||
|
||||
let (block_idx, from, _) = self.lookup(pos).ok_or_else(|| {
|
||||
RopeError::OutOfBounds(format!("split_at: lookup failed at pos={}", pos))
|
||||
})?;
|
||||
let cut_offset = pos - from;
|
||||
|
||||
// Keep block_idx in self temporarily, split it, move remainder to tail.
|
||||
let mut tail_blocks = self.blocks.split_off(block_idx + 1);
|
||||
self.start_block_idx.truncate(block_idx + 1);
|
||||
|
||||
let tail_part = self.blocks[block_idx].split_off(cut_offset);
|
||||
if !tail_part.is_empty() {
|
||||
tail_blocks.insert(0, tail_part);
|
||||
}
|
||||
|
||||
let mut tail_length = 0;
|
||||
let tail_starts: Vec<usize> = tail_blocks
|
||||
.iter()
|
||||
.map(|b| {
|
||||
let s = tail_length;
|
||||
tail_length += b.len();
|
||||
s
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.length = pos;
|
||||
|
||||
Ok(Rope {
|
||||
blocks: tail_blocks,
|
||||
length: tail_length,
|
||||
start_block_idx: tail_starts,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.blocks.is_empty()
|
||||
}
|
||||
|
||||
pub fn fw_cursor(&self) -> ForwardCursor<'_> {
|
||||
ForwardCursor::new(self)
|
||||
}
|
||||
|
||||
pub fn bw_cursor(&self) -> BackwardCursor<'_> {
|
||||
BackwardCursor::new(self)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user