#![allow(unsafe_code)]
use rustc_hash::FxHashMap;
use std::sync::Arc;
pub(crate) const SEGMENT_SEPARATOR: char = '\n';
#[derive(Clone, Debug)]
pub struct SegmentsTree {
is_root: bool,
nodes: FxHashMap<String, Self>,
fields: FxHashMap<String, Arc<[u8]>>,
}
impl Default for SegmentsTree {
fn default() -> Self {
Self::new()
}
}
impl SegmentsTree {
pub fn new() -> Self {
Self {
is_root: true,
nodes: FxHashMap::default(),
fields: FxHashMap::default(),
}
}
fn new_node() -> Self {
Self {
is_root: false,
nodes: FxHashMap::default(),
fields: FxHashMap::default(),
}
}
pub fn add(&mut self, path: &str) {
let segments: Vec<&str> = path.split(SEGMENT_SEPARATOR).collect();
if segments.len() == 1 {
self.fields
.insert(path.to_string(), Arc::from(path.as_bytes()));
return;
}
let mut node = self;
for (i, segment) in segments.iter().enumerate() {
if i == segments.len() - 1 {
node.fields
.insert((*segment).to_string(), Arc::from(path.as_bytes()));
} else {
node = node
.nodes
.entry((*segment).to_string())
.or_insert_with(Self::new_node);
}
}
}
#[inline]
pub fn is_root(&self) -> bool {
self.is_root
}
#[inline]
pub fn is_segment_used(&self, segment: &[u8]) -> bool {
let s = unsafe { std::str::from_utf8_unchecked(segment) };
self.fields.contains_key(s) || self.nodes.contains_key(s)
}
#[inline]
pub fn get(&self, segment: &[u8]) -> Option<&Self> {
let s = unsafe { std::str::from_utf8_unchecked(segment) };
self.nodes.get(s)
}
#[inline]
pub fn path_for_segment(&self, segment: &[u8]) -> Option<&[u8]> {
let s = unsafe { std::str::from_utf8_unchecked(segment) };
self.fields.get(s).map(|v| v.as_ref())
}
#[inline]
pub fn path_arc_for_segment(&self, segment: &[u8]) -> Option<Arc<[u8]>> {
let s = unsafe { std::str::from_utf8_unchecked(segment) };
self.fields.get(s).cloned()
}
#[inline]
pub fn nodes_count(&self) -> usize {
self.nodes.len()
}
#[inline]
pub fn fields_count(&self) -> usize {
self.fields.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_single_field() {
let mut tree = SegmentsTree::new();
tree.add("status");
assert!(tree.is_segment_used(b"status"));
assert!(!tree.is_segment_used(b"other"));
assert_eq!(tree.fields_count(), 1);
assert_eq!(tree.nodes_count(), 0);
}
#[test]
fn test_nested_path() {
let mut tree = SegmentsTree::new();
tree.add("context\nuser\nid");
assert!(tree.is_segment_used(b"context"));
assert_eq!(tree.nodes_count(), 1);
assert_eq!(tree.fields_count(), 0);
let context = tree.get(b"context").unwrap();
assert!(context.is_segment_used(b"user"));
assert_eq!(context.nodes_count(), 1);
assert_eq!(context.fields_count(), 0);
let user = context.get(b"user").unwrap();
assert!(user.is_segment_used(b"id"));
assert_eq!(user.fields_count(), 1);
assert_eq!(user.nodes_count(), 0);
assert_eq!(
user.path_for_segment(b"id"),
Some(b"context\nuser\nid".as_slice())
);
}
#[test]
fn test_multiple_paths() {
let mut tree = SegmentsTree::new();
tree.add("a\nb");
tree.add("a\nc");
tree.add("d");
assert!(tree.is_segment_used(b"a"));
assert!(tree.is_segment_used(b"d"));
assert!(!tree.is_segment_used(b"x"));
let a = tree.get(b"a").unwrap();
assert!(a.is_segment_used(b"b"));
assert!(a.is_segment_used(b"c"));
assert_eq!(a.fields_count(), 2);
}
#[test]
fn test_is_root() {
let tree = SegmentsTree::new();
assert!(tree.is_root());
let mut tree = SegmentsTree::new();
tree.add("a\nb");
let child = tree.get(b"a").unwrap();
assert!(!child.is_root());
}
}