@@ 0,0 1,155 @@
+//! A rope-based vector where each element has a height. The intended
+//! use is for the elements to be text layout objects.
+
+use xi_rope::tree::{Cursor, Leaf, Node, NodeInfo, TreeBuilder};
+use xi_rope::interval::{Interval, IntervalBounds};
+use std::marker::PhantomData;
+
+#[derive(Clone)]
+pub struct Vector<T: Clone>(Node<VectorInfo<T>>);
+
+// This technically doesn't have to be newtyped, we could impl leaf on
+// Vec directly.
+#[derive(Clone)]
+pub struct VectorLeaf<T> {
+ data: Vec<T>,
+}
+
+// Have to implement by hand because rust issue #26925
+impl<T> Default for VectorLeaf<T> {
+ fn default() -> Self {
+ VectorLeaf { data: Vec::new() }
+ }
+}
+
+#[derive(Clone)]
+pub struct VectorInfo<T> {
+ phantom: PhantomData<T>,
+}
+
+impl<T: Clone> NodeInfo for VectorInfo<T> {
+ type L = VectorLeaf<T>;
+
+ fn accumulate(&mut self, _other: &Self) {}
+
+ fn compute_info(_: &Self::L) -> Self {
+ VectorInfo { phantom: Default::default() }
+ }
+}
+
+const MIN_LEAF: usize = 16;
+const MAX_LEAF: usize = 32;
+
+impl<T: Clone> Leaf for VectorLeaf<T> {
+ fn len(&self) -> usize {
+ self.data.len()
+ }
+
+ fn is_ok_child(&self) -> bool {
+ self.data.len() >= MIN_LEAF
+ }
+
+ fn push_maybe_split(&mut self, other: &Self, iv: Interval) -> Option<Self> {
+ let (start, end) = iv.start_end();
+ self.data.extend_from_slice(&other.data[start..end]);
+ if self.len() <= MAX_LEAF {
+ None
+ } else {
+ let splitpoint = self.len() / 2;
+ let right_vec = self.data.split_off(splitpoint);
+ Some(VectorLeaf { data: right_vec })
+ }
+ }
+}
+
+impl<T: Clone> From<Vec<T>> for Vector<T> {
+ fn from(v: Vec<T>) -> Self {
+ Vector(Node::from_leaf(VectorLeaf { data: v }))
+ }
+}
+
+impl<T: Clone> Vector<T> {
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ pub fn singleton(item: T) -> Vector<T> {
+ vec![item].into()
+ }
+
+ pub fn get(&self, index: usize) -> Option<&T> {
+ let cursor = Cursor::new(&self.0, index);
+ cursor.get_leaf().and_then(|(leaf, offset)| leaf.data.get(offset))
+ }
+
+ // Note: we can do get_mut too, but that requires mutable leaf access.
+
+ pub fn push(&mut self, item: T) {
+ // This could be optimized more.
+ self.0 = Node::concat(self.0.clone(), Self::singleton(item).0)
+ }
+
+ pub fn remove(&mut self, index: usize) {
+ let mut b = TreeBuilder::new();
+ self.push_subseq(&mut b, Interval::new(0, index));
+ self.push_subseq(&mut b, Interval::new(index + 1, self.len()));
+ self.0 = b.build();
+ }
+
+ pub fn set(&mut self, index: usize, value: T) {
+ let mut b = TreeBuilder::new();
+ self.push_subseq(&mut b, Interval::new(0, index));
+ b.push_leaf(VectorLeaf { data: vec![value]});
+ self.push_subseq(&mut b, Interval::new(index + 1, self.len()));
+ self.0 = b.build();
+ }
+
+ pub fn insert(&mut self, index: usize, value: T) {
+ let mut b = TreeBuilder::new();
+ self.push_subseq(&mut b, Interval::new(0, index));
+ b.push_leaf(VectorLeaf { data: vec![value]});
+ self.push_subseq(&mut b, Interval::new(index, self.len()));
+ self.0 = b.build();
+ }
+
+ pub fn iter_chunks(&self, range: impl IntervalBounds) -> ChunkIter<T> {
+ let Interval { start, end } = range.into_interval(self.len());
+
+ ChunkIter { cursor: Cursor::new(&self.0, start), end }
+ }
+
+ fn push_subseq(&self, b: &mut TreeBuilder<VectorInfo<T>>, iv: Interval) {
+ // TODO: if we make the push_subseq method in xi-rope public, we can save some
+ // allocations.
+ b.push(self.0.subseq(iv));
+ }
+}
+
+impl<'a, T: Clone> IntoIterator for &'a Vector<T> {
+ type Item = &'a T;
+
+ type IntoIter = std::iter::Flatten<ChunkIter<'a, T>>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter_chunks(..).flatten()
+ }
+}
+
+pub struct ChunkIter<'a, T: Clone> {
+ cursor: Cursor<'a, VectorInfo<T>>,
+ end: usize,
+}
+
+impl<'a, T: Clone> Iterator for ChunkIter<'a, T> {
+ type Item = &'a [T];
+
+ fn next(&mut self) -> Option<&'a [T]> {
+ if self.cursor.pos() >= self.end {
+ return None;
+ }
+ let (leaf, start_pos) = self.cursor.get_leaf().unwrap();
+ let len = (self.end - self.cursor.pos()).min(leaf.len() - start_pos);
+ self.cursor.next_leaf();
+ Some(&leaf.data[start_pos..start_pos + len])
+ }
+}