Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions src/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,9 @@ fn container_opener_byte(doc: &Document, cur: Cursor) -> Option<u8> {
/// subsequent visits.
fn walk_children(doc: &Document, cur: Cursor, seg: &PathSeg) -> Result<Cursor, qjson_err> {
let is_obj = matches!(seg, PathSeg::Key(_));
let mut cache = doc.skip.borrow_mut();
let (slot_n, was_cached) = cache.get_or_insert(cur.idx_start);

if was_cached {
let cache = doc.skip.borrow();
if let Some(slot_n) = cache.get(cur.idx_start) {
// Fast path: iterate cached (start, end) pairs. No brace counting.
// Rc::clone is O(1) — avoids O(n) Vec clone of previous implementation.
let slot = cache.slot(slot_n);
Expand All @@ -77,6 +76,7 @@ fn walk_children(doc: &Document, cur: Cursor, seg: &PathSeg) -> Result<Cursor, q
drop(cache);
return resolve_in_known_children(doc, &starts, &ends, is_obj, seg);
}
drop(cache);

// Slow path: walk all children, populate cache fully, record match if any.
let mut starts: Vec<u32> = Vec::new();
Expand All @@ -96,9 +96,7 @@ fn walk_children(doc: &Document, cur: Cursor, seg: &PathSeg) -> Result<Cursor, q
p += 1;
}
if p == closer_byte_pos {
let slot = cache.slot_mut(slot_n);
slot.child_starts = starts.into();
slot.child_ends = ends.into();
doc.skip.borrow_mut().insert(cur.idx_start, starts, ends);
return Err(qjson_err::QJSON_NOT_FOUND);
}
}
Expand Down Expand Up @@ -141,9 +139,7 @@ fn walk_children(doc: &Document, cur: Cursor, seg: &PathSeg) -> Result<Cursor, q
}
}

let slot = cache.slot_mut(slot_n);
slot.child_starts = starts.into();
slot.child_ends = ends.into();
doc.skip.borrow_mut().insert(cur.idx_start, starts, ends);

match result {
Some(c) => Ok(c),
Expand Down
51 changes: 34 additions & 17 deletions src/skip_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,29 +35,46 @@ impl SkipCache {
}
}

/// Get an existing slot for this opener idx, or allocate a new (empty) one.
/// Returns (slot_number, was_already_populated).
pub(crate) fn get_or_insert(&mut self, opener_idx: u32) -> (u32, bool) {
if let Some(&slot) = self.by_opener.get(&opener_idx) {
return (slot, true);
}
let new = self.slots.len() as u32;
self.slots.push(SkipSlot {
child_starts: Rc::clone(&self.empty_rc),
child_ends: Rc::clone(&self.empty_rc),
});
self.by_opener.insert(opener_idx, new);
(new, false)
}

pub(crate) fn slot_mut(&mut self, n: u32) -> &mut SkipSlot {
&mut self.slots[n as usize]
pub(crate) fn get(&self, opener_idx: u32) -> Option<u32> {
self.by_opener.get(&opener_idx).copied()
}

pub(crate) fn slot(&self, n: u32) -> &SkipSlot {
&self.slots[n as usize]
}

pub(crate) fn insert(&mut self, opener_idx: u32, child_starts: Vec<u32>, child_ends: Vec<u32>) {
debug_assert_eq!(child_starts.len(), child_ends.len());

let child_starts = if child_starts.is_empty() {
Rc::clone(&self.empty_rc)
} else {
child_starts.into()
};
let child_ends = if child_ends.is_empty() {
Rc::clone(&self.empty_rc)
} else {
child_ends.into()
};

let slot_n = match self.by_opener.get(&opener_idx).copied() {
Some(slot) => slot,
None => {
let slot = self.slots.len() as u32;
self.slots.push(SkipSlot {
child_starts: Rc::clone(&self.empty_rc),
child_ends: Rc::clone(&self.empty_rc),
});
self.by_opener.insert(opener_idx, slot);
slot
}
};

let slot = &mut self.slots[slot_n as usize];
slot.child_starts = child_starts;
slot.child_ends = child_ends;
}

#[cfg(test)]
pub(crate) fn len(&self) -> usize { self.by_opener.len() }
}
Loading
Loading