refactor: move bump pointer before Node

This commit is contained in:
Zixuan Chen 2022-08-13 00:56:17 +08:00
parent 9e66e2dc68
commit 3b091d8891
8 changed files with 123 additions and 118 deletions

View file

@ -1,7 +1,4 @@
use std::{
fmt::Display,
ops::{Deref, DerefMut},
};
use std::ops::{Deref, DerefMut};
use rle::{
rle_tree::{
@ -74,7 +71,7 @@ impl RleTreeTrait<CustomString> for StringTreeTrait {
}
fn update_cache_internal(node: &mut rle::rle_tree::node::InternalNode<'_, CustomString, Self>) {
node.cache = node.children().iter().map(Node::len).sum();
node.cache = node.children().iter().map(|x| Node::len(x)).sum();
}
fn find_pos_internal(
@ -86,13 +83,13 @@ impl RleTreeTrait<CustomString> for StringTreeTrait {
last_cache = match child {
Node::Internal(x) => {
if index <= x.cache {
return (i, index, get_pos(index, child));
return (i, index, get_pos(index, *child));
}
x.cache
}
Node::Leaf(x) => {
if index <= x.cache {
return (i, index, get_pos(index, child));
return (i, index, get_pos(index, *child));
}
x.cache
}

View file

@ -40,7 +40,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> RleTreeRaw<'a, T, A> {
#[inline]
fn new(bump: &'a Bump) -> Self {
Self {
node: Node::Internal(bump.alloc(InternalNode::new(bump, None))),
node: Node::Internal(InternalNode::new(bump, None)),
_pin: PhantomPinned,
_a: PhantomData,
}

View file

@ -14,14 +14,14 @@ pub(crate) mod node_trait;
#[derive(Debug, EnumAsInner)]
pub enum Node<'a, T: Rle, A: RleTreeTrait<T>> {
Internal(&'a mut InternalNode<'a, T, A>),
Leaf(&'a mut LeafNode<'a, T, A>),
Internal(InternalNode<'a, T, A>),
Leaf(LeafNode<'a, T, A>),
}
pub struct InternalNode<'a, T: Rle, A: RleTreeTrait<T>> {
bump: &'a Bump,
parent: Option<NonNull<InternalNode<'a, T, A>>>,
pub(super) children: BumpVec<'a, Node<'a, T, A>>,
pub(super) children: BumpVec<'a, &'a mut Node<'a, T, A>>,
pub cache: A::InternalCache,
_pin: PhantomPinned,
_a: PhantomData<A>,
@ -47,12 +47,12 @@ pub(crate) enum Either {
impl<'a, T: Rle, A: RleTreeTrait<T>> Node<'a, T, A> {
#[inline]
fn _new_internal(bump: &'a Bump, parent: Option<NonNull<InternalNode<'a, T, A>>>) -> Self {
Self::Internal(bump.alloc(InternalNode::new(bump, parent)))
Self::Internal(InternalNode::new(bump, parent))
}
#[inline]
fn new_leaf(bump: &'a Bump, parent: NonNull<InternalNode<'a, T, A>>) -> Self {
Self::Leaf(bump.alloc(LeafNode::new(bump, parent)))
fn new_leaf(bump: &'a Bump, parent: NonNull<InternalNode<'a, T, A>>) -> &'a mut Self {
bump.alloc(Self::Leaf(LeafNode::new(bump, parent)))
}
#[inline]
@ -96,8 +96,8 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> Node<'a, T, A> {
.children
.iter()
.position(|child| match (child, self) {
(Node::Internal(a), Node::Internal(b)) => std::ptr::eq(&**a, &**b),
(Node::Leaf(a), Node::Leaf(b)) => std::ptr::eq(&**a, &**b),
(Node::Internal(a), Node::Internal(b)) => std::ptr::eq(&*a, &*b),
(Node::Leaf(a), Node::Leaf(b)) => std::ptr::eq(&*a, &*b),
_ => false,
});
@ -136,8 +136,8 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> Node<'a, T, A> {
match sibling {
Node::Internal(sibling) => {
let self_node = self.as_internal_mut().unwrap();
let ptr = NonNull::new(&mut **sibling).unwrap();
for mut child in self_node.children.drain(..) {
let ptr = NonNull::new(&mut *sibling).unwrap();
for child in self_node.children.drain(..) {
child.set_parent(ptr);
sibling.children.push(child);
}
@ -153,10 +153,10 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> Node<'a, T, A> {
match sibling {
Node::Internal(sibling) => {
let self_node = self.as_internal_mut().unwrap();
let ptr = NonNull::new(&mut **sibling).unwrap();
let ptr = NonNull::new(&mut *sibling).unwrap();
sibling.children.splice(
0..0,
self_node.children.drain(0..).rev().map(|mut x| {
self_node.children.drain(0..).rev().map(|x| {
x.set_parent(ptr);
x
}),
@ -180,12 +180,11 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> Node<'a, T, A> {
match sibling {
Node::Internal(sibling) => {
let self_node = self.as_internal_mut().unwrap();
let self_ptr = NonNull::new(&mut **self_node).unwrap();
let sibling_drain =
sibling.children.drain(A::MIN_CHILDREN_NUM..).map(|mut x| {
x.set_parent(self_ptr);
x
});
let self_ptr = NonNull::new(&mut *self_node).unwrap();
let sibling_drain = sibling.children.drain(A::MIN_CHILDREN_NUM..).map(|x| {
x.set_parent(self_ptr);
x
});
self_node.children.splice(0..0, sibling_drain);
}
Node::Leaf(sibling) => {
@ -198,7 +197,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> Node<'a, T, A> {
match sibling {
Node::Internal(sibling) => {
let self_node = self.as_internal_mut().unwrap();
let self_ptr = NonNull::new(&mut **self_node).unwrap();
let self_ptr = NonNull::new(&mut *self_node).unwrap();
let end = self_node.children.len();
let sibling_len = sibling.children.len();
self_node.children.splice(
@ -206,7 +205,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> Node<'a, T, A> {
sibling
.children
.drain(0..sibling_len - A::MIN_CHILDREN_NUM)
.map(|mut x| {
.map(|x| {
x.set_parent(self_ptr);
x
}),

View file

@ -1,4 +1,7 @@
use std::fmt::{Debug, Error, Formatter};
use std::{
collections::HashSet,
fmt::{Debug, Error, Formatter},
};
use crate::{rle_tree::tree_trait::Position, HasLength};
@ -17,22 +20,25 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
}
#[inline]
fn _split(&mut self) -> &'a mut Self {
let ans = self.bump.alloc(Self::new(self.bump, self.parent));
let ans_ptr = NonNull::new(&mut *ans).unwrap();
for mut child in self
fn _split(&mut self) -> &'a mut Node<'a, T, A> {
let mut ans = self
.bump
.alloc(Node::Internal(Self::new(self.bump, self.parent)));
let inner_ptr = NonNull::new(&mut *ans.as_internal_mut().unwrap()).unwrap();
let inner = ans.as_internal_mut().unwrap();
for child in self
.children
.drain(self.children.len() - A::MIN_CHILDREN_NUM..self.children.len())
{
child.set_parent(ans_ptr);
ans.children.push(child);
child.set_parent(inner_ptr);
inner.children.push(child);
}
ans
}
#[inline]
pub fn children(&self) -> &[Node<'a, T, A>] {
pub fn children(&self) -> &[&'a mut Node<'a, T, A>] {
&self.children
}
@ -90,7 +96,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
to: Option<A::Int>,
visited: &mut Vec<(usize, NonNull<Node<'a, T, A>>)>,
depth: usize,
) -> Result<(), &'a mut Self> {
) -> Result<(), &'a mut Node<'a, T, A>> {
let (direct_delete_start, to_del_start_offset) =
from.map_or((0, None), |x| self._delete_start(x));
let (direct_delete_end, to_del_end_offset) =
@ -103,21 +109,19 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
if direct_delete_start - 1 == direct_delete_end {
visited.push((
depth,
NonNull::new(&mut self.children[direct_delete_end]).unwrap(),
NonNull::new(&mut *self.children[direct_delete_end]).unwrap(),
));
match &mut self.children[direct_delete_end] {
Node::Internal(node) => {
if let Err(new) =
node._delete(Some(del_from), Some(del_to), visited, depth + 1)
{
result = self
._insert_with_split(direct_delete_end + 1, Node::Internal(new));
result = self._insert_with_split(direct_delete_end + 1, new);
}
}
Node::Leaf(node) => {
if let Err(new) = node.delete(Some(del_from), Some(del_to)) {
result =
self._insert_with_split(direct_delete_end + 1, Node::Leaf(new));
result = self._insert_with_split(direct_delete_end + 1, new);
}
}
}
@ -129,7 +133,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
if let Some(del_from) = to_del_start_offset {
visited.push((
depth,
NonNull::new(&mut self.children[direct_delete_start - 1]).unwrap(),
NonNull::new(&mut *self.children[direct_delete_start - 1]).unwrap(),
));
match &mut self.children[direct_delete_start - 1] {
Node::Internal(node) => {
@ -148,19 +152,19 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
if let Some(del_to) = to_del_end_offset {
visited.push((
depth,
NonNull::new(&mut self.children[direct_delete_end]).unwrap(),
NonNull::new(&mut *self.children[direct_delete_end]).unwrap(),
));
match &mut self.children[direct_delete_end] {
Node::Internal(node) => {
if let Err(new) = node._delete(None, Some(del_to), visited, depth + 1) {
debug_assert!(result.is_ok());
result = self._insert_with_split(direct_delete_end + 1, new.into());
result = self._insert_with_split(direct_delete_end + 1, new);
}
}
Node::Leaf(node) => {
if let Err(new) = node.delete(None, Some(del_to)) {
debug_assert!(result.is_ok());
result = self._insert_with_split(direct_delete_end + 1, new.into());
result = self._insert_with_split(direct_delete_end + 1, new);
}
}
}
@ -174,7 +178,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
A::update_cache_internal(self);
if let Err(new) = &mut result {
A::update_cache_internal(new);
A::update_cache_internal(new.as_internal_mut().unwrap());
}
result
@ -198,7 +202,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
}
}
pub fn insert(&mut self, index: A::Int, value: T) -> Result<(), &'a mut Self> {
pub fn insert(&mut self, index: A::Int, value: T) -> Result<(), &'a mut Node<'a, T, A>> {
match self._insert(index, value) {
Ok(_) => {
A::update_cache_internal(self);
@ -206,7 +210,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
}
Err(new) => {
A::update_cache_internal(self);
A::update_cache_internal(new);
A::update_cache_internal(new.as_internal_mut().unwrap());
if self.is_root() {
self._create_level(new);
Ok(())
@ -218,23 +222,26 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
}
/// root node function. assume self and new's caches are up-to-date
fn _create_level(&mut self, mut new: &'a mut InternalNode<'a, T, A>) {
fn _create_level(&mut self, new: &'a mut Node<'a, T, A>) {
debug_assert!(self.is_root());
let mut left = self.bump.alloc(InternalNode::new(self.bump, None));
std::mem::swap(&mut *left, self);
let left_ptr = (&mut *left).into();
for child in left.children.iter_mut() {
let left = self
.bump
.alloc(Node::Internal(InternalNode::new(self.bump, None)));
let left_inner = left.as_internal_mut().unwrap();
std::mem::swap(left_inner, self);
let left_ptr = left_inner.into();
for child in left_inner.children.iter_mut() {
child.set_parent(left_ptr);
}
left.parent = Some(NonNull::new(self).unwrap());
new.parent = Some(NonNull::new(self).unwrap());
self.children.push(left.into());
self.children.push(new.into());
left_inner.parent = Some(NonNull::new(self).unwrap());
new.as_internal_mut().unwrap().parent = Some(NonNull::new(self).unwrap());
self.children.push(left);
self.children.push(new);
A::update_cache_internal(self);
}
fn _insert(&mut self, index: A::Int, value: T) -> Result<(), &'a mut Self> {
fn _insert(&mut self, index: A::Int, value: T) -> Result<(), &'a mut Node<'a, T, A>> {
if self.children.is_empty() {
debug_assert!(self.is_root());
let ptr = NonNull::new(self as *mut _).unwrap();
@ -244,25 +251,11 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
let (child_index, relative_idx, _) = A::find_pos_internal(self, index);
let child = &mut self.children[child_index];
let new = match child {
Node::Internal(child) => {
if let Err(new) = child.insert(relative_idx, value) {
let new = Node::Internal(new);
Some(new)
} else {
None
}
}
Node::Leaf(child) => {
if let Err(new) = child.insert(relative_idx, value) {
let new = Node::Leaf(new);
Some(new)
} else {
None
}
}
Node::Internal(child) => child.insert(relative_idx, value),
Node::Leaf(child) => child.insert(relative_idx, value),
};
if let Some(new) = new {
if let Err(new) = new {
if let Err(value) = self._insert_with_split(child_index + 1, new) {
return Err(value);
}
@ -284,14 +277,14 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
#[inline]
pub(crate) fn delete(&mut self, start: Option<A::Int>, end: Option<A::Int>) {
debug_assert!(self.is_root());
let mut visited = Vec::new();
match self._delete(start, end, &mut visited, 1) {
let mut zipper = Vec::new();
match self._delete(start, end, &mut zipper, 1) {
Ok(_) => {
A::update_cache_internal(self);
}
Err(new) => {
A::update_cache_internal(self);
A::update_cache_internal(new);
A::update_cache_internal(new.as_internal_mut().unwrap());
self._create_level(new);
}
};
@ -299,11 +292,17 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
let removed = self._root_shrink_level_if_only_1_child();
// visit in depth order, top to down (depth 0..inf)
visited.sort();
for (_, mut node) in visited.into_iter() {
zipper.sort();
let mut visited_set: HashSet<NonNull<_>> = HashSet::default();
for (_, mut node) in zipper.into_iter() {
if visited_set.contains(&node) {
continue;
}
visited_set.insert(node);
let node = unsafe { node.as_mut() };
if let Some(node) = node.as_internal() {
let ptr = &**node as *const InternalNode<'a, T, A>;
let ptr = node as *const InternalNode<'a, T, A>;
if removed.contains(&ptr) {
continue;
}
@ -349,7 +348,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
while self.children.len() == 1 && self.children[0].as_internal().is_some() {
let mut child = self.children.pop().unwrap();
let child_ptr = child.as_internal_mut().unwrap();
std::mem::swap(&mut **child_ptr, self);
std::mem::swap(&mut *child_ptr, self);
self.parent = None;
let ptr = self.into();
// TODO: extract reset parent?
@ -359,7 +358,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
child_ptr.parent = None;
child_ptr.children.clear();
ans.push(&**child_ptr as *const _);
ans.push(&*child_ptr as *const _);
}
ans
@ -373,16 +372,19 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
fn _insert_with_split(
&mut self,
child_index: usize,
mut new: Node<'a, T, A>,
) -> Result<(), &'a mut Self> {
new: &'a mut Node<'a, T, A>,
) -> Result<(), &'a mut Node<'a, T, A>> {
if self.children.len() == A::MAX_CHILDREN_NUM {
let ans = self._split();
if child_index < self.children.len() {
new.set_parent(self.into());
self.children.insert(child_index, new);
} else {
new.set_parent((&mut *ans).into());
ans.children.insert(child_index - self.children.len(), new);
new.set_parent((&mut *ans.as_internal_mut().unwrap()).into());
ans.as_internal_mut()
.unwrap()
.children
.insert(child_index - self.children.len(), new);
}
Err(ans)

View file

@ -19,22 +19,25 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> LeafNode<'a, T, A> {
}
#[inline]
fn _split(&mut self) -> &'a mut Self {
let mut ans = self.bump.alloc(Self::new(self.bump, self.parent));
fn _split(&mut self) -> &'a mut Node<'a, T, A> {
let mut ans = self
.bump
.alloc(Node::Leaf(Self::new(self.bump, self.parent)));
let mut inner = ans.as_leaf_mut().unwrap();
for child in self
.children
.drain(self.children.len() - A::MIN_CHILDREN_NUM..self.children.len())
{
ans.children.push(child);
inner.children.push(child);
}
ans.next = self.next;
ans.prev = Some(NonNull::new(self).unwrap());
self.next = Some(NonNull::new(&mut *ans).unwrap());
inner.next = self.next;
inner.prev = Some(NonNull::new(self).unwrap());
self.next = Some(NonNull::new(&mut *inner).unwrap());
ans
}
pub fn push_child(&mut self, value: T) -> Result<(), &'a mut Self> {
pub fn push_child(&mut self, value: T) -> Result<(), &'a mut Node<'a, T, A>> {
if !self.children.is_empty() {
let last = self.children.last_mut().unwrap();
if last.is_mergable(&value, &()) {
@ -46,9 +49,10 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> LeafNode<'a, T, A> {
if self.children.len() == A::MAX_CHILDREN_NUM {
let ans = self._split();
ans.push_child(value).unwrap();
let inner = ans.as_leaf_mut().unwrap();
inner.push_child(value).unwrap();
A::update_cache_leaf(self);
A::update_cache_leaf(ans);
A::update_cache_leaf(inner);
return Err(ans);
}
@ -80,7 +84,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> LeafNode<'a, T, A> {
}
}
pub fn insert(&mut self, raw_index: A::Int, value: T) -> Result<(), &'a mut Self> {
pub fn insert(&mut self, raw_index: A::Int, value: T) -> Result<(), &'a mut Node<'a, T, A>> {
match self._insert(raw_index, value) {
Ok(_) => {
A::update_cache_leaf(self);
@ -88,13 +92,13 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> LeafNode<'a, T, A> {
}
Err(new) => {
A::update_cache_leaf(self);
A::update_cache_leaf(new);
A::update_cache_leaf(new.as_leaf_mut().unwrap());
Err(new)
}
}
}
fn _insert(&mut self, raw_index: A::Int, value: T) -> Result<(), &'a mut Self> {
fn _insert(&mut self, raw_index: A::Int, value: T) -> Result<(), &'a mut Node<'a, T, A>> {
if self.children.is_empty() {
self.children.push(self.bump.alloc(value));
return Ok(());
@ -132,19 +136,20 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> LeafNode<'a, T, A> {
self.children[index] = self.bump.alloc(a);
if self.children.len() >= A::MAX_CHILDREN_NUM - 1 {
let ans = self._split();
let node = self._split();
let leaf = node.as_leaf_mut().unwrap();
if index < self.children.len() {
self.children.insert(index + 1, self.bump.alloc(value));
self.children.insert(index + 2, self.bump.alloc(b));
ans.children.insert(0, self.children.pop().unwrap());
leaf.children.insert(0, self.children.pop().unwrap());
} else {
ans.children
leaf.children
.insert(index - self.children.len() + 1, self.bump.alloc(value));
ans.children
leaf.children
.insert(index - self.children.len() + 2, self.bump.alloc(b));
}
return Err(ans);
return Err(node);
}
self.children.insert(index + 1, self.bump.alloc(b));
@ -175,7 +180,7 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> LeafNode<'a, T, A> {
&mut self,
start: Option<A::Int>,
end: Option<A::Int>,
) -> Result<(), &'a mut Self> {
) -> Result<(), &'a mut Node<'a, T, A>> {
let (del_start, del_relative_from) = start.map_or((0, None), |x| self._delete_start(x));
let (del_end, del_relative_to) =
end.map_or((self.children.len(), None), |x| self._delete_end(x));
@ -215,19 +220,21 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> LeafNode<'a, T, A> {
A::update_cache_leaf(self);
if let Err(new) = &mut result {
A::update_cache_leaf(new);
A::update_cache_leaf(new.as_leaf_mut().unwrap());
}
result
}
fn _insert_with_split(&mut self, index: usize, value: T) -> Result<(), &'a mut Self> {
fn _insert_with_split(&mut self, index: usize, value: T) -> Result<(), &'a mut Node<'a, T, A>> {
if self.children.len() == A::MAX_CHILDREN_NUM {
let ans = self._split();
if index <= self.children.len() {
self.children.insert(index, self.bump.alloc(value));
} else {
ans.children
ans.as_leaf_mut()
.unwrap()
.children
.insert(index - self.children.len(), self.bump.alloc(value));
}

View file

@ -1,14 +1,14 @@
use crate::{rle_tree::tree_trait::RleTreeTrait, Rle};
use super::{InternalNode, LeafNode, Node};
impl<'a, T: Rle, A: RleTreeTrait<T>> From<&'a mut InternalNode<'a, T, A>> for Node<'a, T, A> {
fn from(node: &'a mut InternalNode<'a, T, A>) -> Self {
impl<'a, T: Rle, A: RleTreeTrait<T>> From<InternalNode<'a, T, A>> for Node<'a, T, A> {
fn from(node: InternalNode<'a, T, A>) -> Self {
Node::Internal(node)
}
}
impl<'a, T: Rle, A: RleTreeTrait<T>> From<&'a mut LeafNode<'a, T, A>> for Node<'a, T, A> {
fn from(node: &'a mut LeafNode<'a, T, A>) -> Self {
impl<'a, T: Rle, A: RleTreeTrait<T>> From<LeafNode<'a, T, A>> for Node<'a, T, A> {
fn from(node: LeafNode<'a, T, A>) -> Self {
Node::Leaf(node)
}
}

View file

@ -42,13 +42,13 @@ impl RleTreeTrait<Range<usize>> for RangeTreeTrait {
last_cache = match child {
Node::Internal(x) => {
if index <= x.cache {
return (i, index, get_pos(index, child));
return (i, index, get_pos(index, *child));
}
x.cache
}
Node::Leaf(x) => {
if index <= x.cache {
return (i, index, get_pos(index, child));
return (i, index, get_pos(index, *child));
}
x.cache
}

View file

@ -75,7 +75,7 @@ impl RleTreeTrait<CustomString> for StringTreeTrait {
fn update_cache_internal(
node: &mut crate::rle_tree::node::InternalNode<'_, CustomString, Self>,
) {
node.cache = node.children.iter().map(Node::len).sum();
node.cache = node.children.iter().map(|x| Node::len(x)).sum();
}
fn find_pos_internal(
@ -87,13 +87,13 @@ impl RleTreeTrait<CustomString> for StringTreeTrait {
last_cache = match child {
Node::Internal(x) => {
if index <= x.cache {
return (i, index, get_pos(index, child));
return (i, index, get_pos(index, *child));
}
x.cache
}
Node::Leaf(x) => {
if index <= x.cache {
return (i, index, get_pos(index, child));
return (i, index, get_pos(index, *child));
}
x.cache
}