use crate::changestore::ChangeStore;
use crate::pristine::*;
use crate::vertex_buffer;
use crate::vertex_buffer::change_message;
use crate::{HashMap, HashSet};
pub(super) struct Diff {
pub inode: Position<Option<ChangeId>>,
pub path: String,
pub contents_a: Vec<u8>,
pub pos_a: Vec<Vertex>,
pub missing_eol: HashSet<usize>,
pub marker: HashMap<usize, ConflictMarker>,
conflict_stack: Vec<Conflict>,
pub conflict_ends: Vec<ConflictEnds>,
pub cyclic_conflict_bytes: Vec<(usize, usize)>,
}
#[derive(Debug, Clone)]
pub struct Conflict {
pub counter: usize,
pub side: usize,
pub conflict_type: ConflictType,
}
#[derive(Debug, Clone, Copy)]
pub enum ConflictType {
Root,
Order,
Zombie,
Cyclic,
}
#[derive(Debug)]
pub struct ConflictEnds {
pub start: usize,
pub end: usize,
pub end_pos: usize,
pub conflict_type: ConflictType,
}
#[derive(Debug, PartialEq, Eq)]
pub enum ConflictMarker {
Begin,
Next,
End,
}
#[derive(Debug)]
pub struct Vertex {
pub pos: usize,
pub vertex: crate::pristine::Vertex<ChangeId>,
pub before_conflict: bool,
pub conflict: usize,
}
impl Diff {
pub fn new(
inode: Position<Option<ChangeId>>,
path: String,
graph: &crate::alive::Graph,
) -> Self {
Diff {
inode,
path,
pos_a: Vec::with_capacity(2 * graph.len_vertices()),
contents_a: Vec::with_capacity(graph.len_bytes()),
missing_eol: HashSet::default(),
conflict_ends: vec![ConflictEnds {
start: 0,
end: 0,
end_pos: 0,
conflict_type: ConflictType::Root,
}],
marker: HashMap::default(),
conflict_stack: vec![Conflict {
counter: 0,
side: 0,
conflict_type: ConflictType::Root,
}],
cyclic_conflict_bytes: Vec::new(),
}
}
}
impl Diff {
pub fn vertex(
&self,
i: usize,
pos: usize,
end_pos: usize,
) -> crate::pristine::Vertex<ChangeId> {
let mut v = self.pos_a[i].vertex;
assert!(!v.is_root());
if pos > self.pos_a[i].pos {
v.start = ChangePosition(self.pos_a[i].vertex.start.0 + (pos - self.pos_a[i].pos))
}
if i + 1 >= self.pos_a.len() || end_pos < self.pos_a[i + 1].pos {
v.end = ChangePosition(self.pos_a[i].vertex.start.0 + (end_pos - self.pos_a[i].pos))
}
v
}
pub fn position(&self, i: usize, pos: usize) -> crate::pristine::Position<ChangeId> {
let mut v = self.pos_a[i].vertex.start_pos();
if pos > self.pos_a[i].pos {
v.pos = ChangePosition(self.pos_a[i].vertex.start.0 + (pos - self.pos_a[i].pos))
}
v
}
}
impl Diff {
fn begin_conflict_(&mut self, conflict_type: ConflictType) {
self.conflict_stack.push(Conflict {
counter: self.conflict_ends.len(),
side: 0,
conflict_type,
});
let len = match self.contents_a.last() {
Some(&b'\n') | None => self.contents_a.len(),
_ => {
self.missing_eol.insert(self.contents_a.len());
self.contents_a.len() + 1
}
};
self.conflict_ends.push(ConflictEnds {
start: self.pos_a.len(),
end: self.pos_a.len(),
end_pos: len,
conflict_type,
});
self.marker.insert(len, ConflictMarker::Begin);
}
}
impl vertex_buffer::VertexBuffer for Diff {
fn output_line<E, C>(&mut self, v: crate::pristine::Vertex<ChangeId>, c: C) -> Result<(), E>
where
E: From<std::io::Error>,
C: FnOnce(&mut [u8]) -> Result<(), E>,
{
if v == crate::pristine::Vertex::BOTTOM {
return Ok(());
}
let len = self.contents_a.len();
self.contents_a.resize(len + (v.end - v.start), 0);
c(&mut self.contents_a[len..])?;
self.pos_a.push(Vertex {
pos: len,
vertex: v,
before_conflict: false,
conflict: self.conflict_stack.last().unwrap().counter,
});
Ok(())
}
fn begin_conflict<C: ChangeStore>(
&mut self,
id: usize,
side: Option<(&C, &[&Hash])>,
) -> Result<(), std::io::Error> {
self.begin_conflict_(ConflictType::Order);
self.output_conflict_marker(vertex_buffer::START_MARKER, id, side)
}
fn begin_cyclic_conflict<C: ChangeStore>(&mut self, id: usize) -> Result<(), std::io::Error> {
let len = self.contents_a.len();
self.begin_conflict_(ConflictType::Cyclic);
self.cyclic_conflict_bytes.push((len, len));
self.output_conflict_marker::<C>(vertex_buffer::START_MARKER, id, None)
}
fn begin_zombie_conflict<C: ChangeStore>(
&mut self,
id: usize,
add_del: Option<(&C, &[&Hash])>,
) -> Result<(), std::io::Error> {
self.begin_conflict_(ConflictType::Zombie);
self.output_conflict_marker(vertex_buffer::START_MARKER, id, add_del)
}
fn end_conflict<C: ChangeStore>(&mut self, id: usize) -> Result<(), std::io::Error> {
let len = match self.contents_a.last() {
Some(&b'\n') | None => self.contents_a.len(),
_ => {
self.missing_eol.insert(self.contents_a.len());
self.contents_a.len() + 1
}
};
let chunk = self.pos_a.len();
self.output_conflict_marker::<C>(vertex_buffer::END_MARKER, id, None)?;
let conflict = self.conflict_stack.pop().unwrap();
self.marker.insert(len, ConflictMarker::End);
self.conflict_ends[conflict.counter].end_pos = len;
self.conflict_ends[conflict.counter].end = chunk;
Ok(())
}
fn end_cyclic_conflict<C: ChangeStore>(&mut self, id: usize) -> Result<(), std::io::Error> {
debug!("end_cyclic_conflict");
self.end_conflict::<C>(id)?;
self.cyclic_conflict_bytes.last_mut().unwrap().1 = self.contents_a.len();
Ok(())
}
fn conflict_next<C: ChangeStore>(
&mut self,
id: usize,
side: Option<(&C, &[&Hash])>,
) -> Result<(), std::io::Error> {
let len = match self.contents_a.last() {
Some(&b'\n') | None => self.contents_a.len(),
_ => {
self.missing_eol.insert(self.contents_a.len());
self.contents_a.len() + 1
}
};
self.conflict_stack.last_mut().unwrap().side += 1;
self.marker.insert(len, ConflictMarker::Next);
self.output_conflict_marker(vertex_buffer::SEPARATOR, id, side)
}
fn output_conflict_marker<C: ChangeStore>(
&mut self,
marker: &str,
id: usize,
sides: Option<(&C, &[&Hash])>,
) -> Result<(), std::io::Error> {
if let Some(line) = self.pos_a.last_mut() {
line.before_conflict = true
}
debug!(
"output_conflict_marker {:?} {:?}",
self.contents_a.last(),
marker
);
match self.contents_a.last() {
Some(&b'\n') | None => {}
_ => self.contents_a.push(b'\n'),
}
let pos = self.contents_a.len();
use std::io::Write;
write!(self.contents_a, "{} {}", marker, id)?;
match sides {
Some((changes, sides)) => {
for side in sides {
let h = side.to_base32();
write!(
self.contents_a,
" [{} {}]",
h.split_at(8).0,
change_message(changes, side)
)?;
}
}
None => (),
};
self.contents_a.write_all(b"\n")?;
self.pos_a.push(Vertex {
pos,
vertex: crate::pristine::Vertex::ROOT,
before_conflict: false,
conflict: self.conflict_stack.last().unwrap().counter,
});
Ok(())
}
}
impl Diff {
pub fn last_vertex_containing(&self, pos: usize) -> usize {
match self.pos_a.binary_search_by(|l| l.pos.cmp(&pos)) {
Ok(mut i) => loop {
if i + 1 >= self.pos_a.len() {
return i;
}
if self.pos_a[i].pos == self.pos_a[i + 1].pos {
i += 1
} else {
return i;
}
},
Err(i) => {
assert!(i > 0);
i - 1
}
}
}
pub fn first_vertex_containing(&self, pos: usize) -> usize {
match self.pos_a.binary_search_by(|l| l.pos.cmp(&pos)) {
Ok(mut i) => loop {
if i == 0 {
return 0;
}
if self.pos_a[i].pos == self.pos_a[i - 1].pos {
i -= 1
} else {
return i;
}
},
Err(i) => {
assert!(i > 0);
let len = self.pos_a[i - 1].vertex.end - self.pos_a[i - 1].vertex.start;
if pos < self.pos_a[i - 1].pos + len || len == 0 {
i - 1
} else {
i
}
}
}
}
}