use super::intern::{StrId, StrInterner};
use super::payload::{Content, Segment};
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct ContentRange {
pub(crate) start: u32,
pub(crate) len: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct SegRange {
pub(crate) start: u32,
pub(crate) len: u32,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct NodeStore {
base: Option<Arc<Self>>,
string_offset: u32,
content_offset: u32,
segment_offset: u32,
pub(crate) interner: StrInterner,
contents: Vec<Content>,
segments: Vec<Segment>,
}
impl NodeStore {
pub(crate) fn layered(base: Arc<Self>) -> Self {
let string_offset = base
.string_offset
.checked_add(u32::try_from(base.interner.len()).expect("string count fits u32"))
.expect("string count fits u32");
let content_offset = base
.content_offset
.checked_add(u32::try_from(base.contents.len()).expect("content count fits u32"))
.expect("content count fits u32");
let segment_offset = base
.segment_offset
.checked_add(u32::try_from(base.segments.len()).expect("segment count fits u32"))
.expect("segment count fits u32");
Self {
base: Some(base),
string_offset,
content_offset,
segment_offset,
interner: StrInterner::default(),
contents: Vec::new(),
segments: Vec::new(),
}
}
#[cfg(test)]
#[must_use]
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn intern(&mut self, s: &str) -> StrId {
let local = self.interner.intern(s);
StrId(
self.string_offset
.checked_add(local.0)
.expect("string count fits u32"),
)
}
#[must_use]
pub(crate) fn resolve_str(&self, id: StrId) -> &str {
let mut store = self;
loop {
if id.0 >= store.string_offset {
return store.interner.resolve(StrId(id.0 - store.string_offset));
}
store = store.base.as_deref().expect("string id belongs to store");
}
}
pub(crate) fn push_contents(&mut self, items: &[Content]) -> ContentRange {
let start = self
.content_offset
.checked_add(
u32::try_from(self.contents.len()).expect("content pool exceeds u32 entry count"),
)
.expect("content pool exceeds u32 entry count");
let len = u32::try_from(items.len()).expect("content run exceeds u32 length");
self.contents.extend_from_slice(items);
ContentRange { start, len }
}
pub(crate) fn push_segments(&mut self, items: &[Segment]) -> SegRange {
let start = self
.segment_offset
.checked_add(
u32::try_from(self.segments.len()).expect("segment pool exceeds u32 entry count"),
)
.expect("segment pool exceeds u32 entry count");
let len = u32::try_from(items.len()).expect("segment run exceeds u32 length");
self.segments.extend_from_slice(items);
SegRange { start, len }
}
#[must_use]
pub(crate) fn resolve_content_range(&self, range: ContentRange) -> &[Content] {
let mut store = self;
loop {
if range.start >= store.content_offset {
let start = (range.start - store.content_offset) as usize;
return &store.contents[start..start + range.len as usize];
}
store = store
.base
.as_deref()
.expect("content range belongs to store");
}
}
#[must_use]
pub(crate) fn resolve_seg_range(&self, range: SegRange) -> &[Segment] {
let mut store = self;
loop {
if range.start >= store.segment_offset {
let start = (range.start - store.segment_offset) as usize;
return &store.segments[start..start + range.len as usize];
}
store = store
.base
.as_deref()
.expect("segment range belongs to store");
}
}
#[must_use]
pub(crate) fn content_range_as_plain(&self, range: ContentRange) -> Option<&str> {
match self.resolve_content_range(range) {
[Content::Plain(id)] => Some(self.resolve_str(*id)),
_ => None,
}
}
#[cfg(test)]
pub(crate) fn inherits_from(&self, expected: &Arc<Self>) -> bool {
let mut base = self.base.as_ref();
while let Some(store) = base {
if Arc::ptr_eq(store, expected) {
return true;
}
base = store.base.as_ref();
}
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ranges_resolve_to_correct_sub_slices() {
let mut store = NodeStore::new();
let a = store.intern("a");
let b = store.intern("b");
let c = store.intern("c");
let first = store.push_contents(&[Content::Plain(a)]);
let second = store.push_contents(&[Content::Plain(b), Content::Plain(c)]);
assert_eq!(first, ContentRange { start: 0, len: 1 });
assert_eq!(second, ContentRange { start: 1, len: 2 });
assert_eq!(store.resolve_content_range(first), &[Content::Plain(a)]);
assert_eq!(
store.resolve_content_range(second),
&[Content::Plain(b), Content::Plain(c)]
);
let seg = store.push_segments(&[Segment::Text(a), Segment::Text(c)]);
assert_eq!(seg, SegRange { start: 0, len: 2 });
assert_eq!(
store.resolve_seg_range(seg),
&[Segment::Text(a), Segment::Text(c)]
);
}
#[test]
fn segment_ranges_resolve_in_a_layered_store() {
let mut base = NodeStore::new();
let a = base.intern("a");
let b = base.intern("b");
base.push_segments(&[Segment::Text(a), Segment::Text(b)]);
let mut layered = NodeStore::layered(Arc::new(base));
let c = layered.intern("c");
let d = layered.intern("d");
let range = layered.push_segments(&[Segment::Text(c), Segment::Text(d)]);
assert_eq!(range, SegRange { start: 2, len: 2 });
assert_eq!(
layered.resolve_seg_range(range),
&[Segment::Text(c), Segment::Text(d)],
);
}
#[test]
fn content_range_as_plain_length_one() {
let mut store = NodeStore::new();
let a = store.intern("foo");
let b = store.intern("bar");
let plain = store.push_contents(&[Content::Plain(a)]);
assert_eq!(store.content_range_as_plain(plain), Some("foo"));
let seg = store.push_segments(&[Segment::Text(a)]);
let mixed = store.push_contents(&[Content::Segments(seg)]);
assert_eq!(store.content_range_as_plain(mixed), None);
let two = store.push_contents(&[Content::Plain(a), Content::Plain(b)]);
assert_eq!(store.content_range_as_plain(two), None);
}
}