1use crate::writer::Inspector;
6use crate::writer::error::Error;
7use crate::writer::heap::Heap;
8use derivative::Derivative;
9use fuchsia_sync::{Mutex, MutexGuard};
10use futures::future::BoxFuture;
11use inspect_format::{
12 Array, ArrayFormat, ArraySlotKind, Block, BlockAccessorExt, BlockAccessorMutExt,
13 BlockContainer, BlockIndex, BlockType, Bool, Buffer, Container, Double, Error as FormatError,
14 Extent, Int, Link, LinkNodeDisposition, Name, Node, PropertyFormat, Reserved, StringRef,
15 Tombstone, Uint, Unknown, constants, utils,
16};
17use smallvec::SmallVec;
18use std::borrow::Cow;
19use std::collections::HashMap;
20use std::sync::Arc;
21use std::sync::atomic::{AtomicU64, Ordering};
22
23pub type LazyNodeContextFnArc =
25 Arc<dyn Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send>;
26
27trait SafeOp {
28 fn safe_sub(&self, other: Self) -> Self;
29 fn safe_add(&self, other: Self) -> Self;
30}
31
32impl SafeOp for u64 {
33 fn safe_sub(&self, other: u64) -> u64 {
34 self.checked_sub(other).unwrap_or(0)
35 }
36 fn safe_add(&self, other: u64) -> u64 {
37 self.checked_add(other).unwrap_or(u64::MAX)
38 }
39}
40
41impl SafeOp for i64 {
42 fn safe_sub(&self, other: i64) -> i64 {
43 self.checked_sub(other).unwrap_or(i64::MIN)
44 }
45 fn safe_add(&self, other: i64) -> i64 {
46 self.checked_add(other).unwrap_or(i64::MAX)
47 }
48}
49
50impl SafeOp for f64 {
51 fn safe_sub(&self, other: f64) -> f64 {
52 self - other
53 }
54 fn safe_add(&self, other: f64) -> f64 {
55 self + other
56 }
57}
58
59macro_rules! locked_state_metric_fns {
60 ($name:ident, $type:ident) => {
61 paste::paste! {
62 pub fn [<create_ $name _metric>]<'b>(
63 &mut self,
64 name: impl Into<Cow<'b, str>>,
65 value: $type,
66 parent_index: BlockIndex,
67 ) -> Result<BlockIndex, Error> {
68 self.inner_lock.[<create_ $name _metric>](name, value, parent_index)
69 }
70
71 pub fn [<set_ $name _metric>](&mut self, block_index: BlockIndex, value: $type) {
72 self.inner_lock.[<set_ $name _metric>](block_index, value);
73 }
74
75 pub fn [<add_ $name _metric>](&mut self, block_index: BlockIndex, value: $type) -> $type {
76 self.inner_lock.[<add_ $name _metric>](block_index, value)
77 }
78
79 pub fn [<subtract_ $name _metric>](&mut self, block_index: BlockIndex, value: $type) -> $type {
80 self.inner_lock.[<subtract_ $name _metric>](block_index, value)
81 }
82 }
83 };
84}
85
86macro_rules! metric_fns {
88 ($name:ident, $type:ident, $marker:ident) => {
89 paste::paste! {
90 fn [<create_ $name _metric>]<'a>(
91 &mut self,
92 name: impl Into<Cow<'a, str>>,
93 value: $type,
94 parent_index: BlockIndex,
95 ) -> Result<BlockIndex, Error> {
96 let mut txn = Txn::new(self);
97 let (block_index, name_index) = txn.allocate_reserved_value(
98 name, parent_index, constants::MIN_ORDER_SIZE)?;
99 txn.block_mut::<Reserved>(block_index)
100 .[<become_ $name _value>](value, name_index, parent_index);
101 txn.commit();
102 Ok(block_index)
103 }
104
105 fn [<set_ $name _metric>](&mut self, block_index: BlockIndex, value: $type) {
106 let mut block = self.heap.container.block_at_unchecked_mut::<$marker>(block_index);
107 block.set(value);
108 }
109
110 fn [<add_ $name _metric>](&mut self, block_index: BlockIndex, value: $type) -> $type {
111 let mut block = self.heap.container.block_at_unchecked_mut::<$marker>(block_index);
112 let current_value = block.value();
113 let new_value = current_value.safe_add(value);
114 block.set(new_value);
115 new_value
116 }
117
118 fn [<subtract_ $name _metric>](&mut self, block_index: BlockIndex, value: $type) -> $type {
119 let mut block = self.heap.container.block_at_unchecked_mut::<$marker>(block_index);
120 let current_value = block.value();
121 let new_value = current_value.safe_sub(value);
122 block.set(new_value);
123 new_value
124 }
125 }
126 };
127}
128macro_rules! locked_state_array_fns {
129 ($name:ident, $type:ident, $value:ident) => {
130 paste::paste! {
131 pub fn [<create_ $name _array>]<'b>(
132 &mut self,
133 name: impl Into<Cow<'b, str>>,
134 slots: usize,
135 array_format: ArrayFormat,
136 parent_index: BlockIndex,
137 ) -> Result<BlockIndex, Error> {
138 self.inner_lock.[<create_ $name _array>](name, slots, array_format, parent_index)
139 }
140
141 pub fn [<set_array_ $name _slot>](
142 &mut self, block_index: BlockIndex, slot_index: usize, value: $type
143 ) {
144 self.inner_lock.[<set_array_ $name _slot>](block_index, slot_index, value);
145 }
146
147 pub fn [<add_array_ $name _slot>](
148 &mut self, block_index: BlockIndex, slot_index: usize, value: $type
149 ) -> Option<$type> {
150 self.inner_lock.[<add_array_ $name _slot>](block_index, slot_index, value)
151 }
152
153 pub fn [<subtract_array_ $name _slot>](
154 &mut self, block_index: BlockIndex, slot_index: usize, value: $type
155 ) -> Option<$type> {
156 self.inner_lock.[<subtract_array_ $name _slot>](block_index, slot_index, value)
157 }
158 }
159 };
160}
161
162macro_rules! arithmetic_array_fns {
163 ($name:ident, $type:ident, $value:ident, $marker:ident) => {
164 paste::paste! {
165 pub fn [<create_ $name _array>]<'a>(
166 &mut self,
167 name: impl Into<Cow<'a, str>>,
168 slots: usize,
169 array_format: ArrayFormat,
170 parent_index: BlockIndex,
171 ) -> Result<BlockIndex, Error> {
172 let block_size =
173 slots as usize * std::mem::size_of::<$type>() + constants::MIN_ORDER_SIZE;
174 if block_size > constants::MAX_ORDER_SIZE {
175 return Err(Error::BlockSizeTooBig(block_size))
176 }
177 let mut txn = Txn::new(self);
178 let (block_index, name_index) = txn.allocate_reserved_value(
179 name, parent_index, block_size)?;
180 txn.block_mut::<Reserved>(block_index)
181 .become_array_value::<$marker>(
182 slots, array_format, name_index, parent_index
183 )?;
184 txn.commit();
185 Ok(block_index)
186 }
187
188 pub fn [<set_array_ $name _slot>](
189 &mut self, block_index: BlockIndex, slot_index: usize, value: $type
190 ) {
191 let mut block = self.heap.container
192 .block_at_unchecked_mut::<Array<$marker>>(block_index);
193 block.set(slot_index, value);
194 }
195
196 pub fn [<add_array_ $name _slot>](
197 &mut self, block_index: BlockIndex, slot_index: usize, value: $type
198 ) -> Option<$type> {
199 let mut block = self.heap.container
200 .block_at_unchecked_mut::<Array<$marker>>(block_index);
201 let previous_value = block.get(slot_index)?;
202 let new_value = previous_value.safe_add(value);
203 block.set(slot_index, new_value);
204 Some(new_value)
205 }
206
207 pub fn [<subtract_array_ $name _slot>](
208 &mut self, block_index: BlockIndex, slot_index: usize, value: $type
209 ) -> Option<$type> {
210 let mut block = self.heap.container
211 .block_at_unchecked_mut::<Array<$marker>>(block_index);
212 let previous_value = block.get(slot_index)?;
213 let new_value = previous_value.safe_sub(value);
214 block.set(slot_index, new_value);
215 Some(new_value)
216 }
217 }
218 };
219}
220
221#[derive(Clone, Debug)]
225pub struct State {
226 inner: Arc<Mutex<InnerState>>,
230}
231
232impl PartialEq for State {
233 fn eq(&self, other: &Self) -> bool {
234 Arc::ptr_eq(&self.inner, &other.inner)
235 }
236}
237
238impl State {
239 pub fn create(
242 heap: Heap<Container>,
243 storage: Arc<<Container as BlockContainer>::ShareableData>,
244 ) -> Result<Self, Error> {
245 let inner = Arc::new(Mutex::new(InnerState::new(heap, storage)));
246 Ok(Self { inner })
247 }
248
249 pub fn try_lock(&self) -> Result<LockedStateGuard<'_>, Error> {
252 let inner_lock = self.inner.lock();
253 LockedStateGuard::new(inner_lock)
254 }
255
256 pub fn begin_transaction(&self) {
259 self.inner.lock().lock_header();
260 }
261
262 pub fn end_transaction(&self) {
265 self.inner.lock().unlock_header();
266 }
267
268 pub fn copy_vmo_bytes(&self) -> Option<Vec<u8>> {
270 let state = self.inner.lock();
271 if state.transaction_count > 0 {
272 return None;
273 }
274
275 Some(state.heap.bytes())
276 }
277}
278
279#[cfg(test)]
280impl State {
281 pub(crate) fn with_current_header<F, R>(&self, callback: F) -> R
282 where
283 F: FnOnce(&Block<&Container, inspect_format::Header>) -> R,
284 {
285 let lock_guard = LockedStateGuard::without_gen_count_changes(self.inner.lock());
288 let block = lock_guard.header();
289 callback(&block)
290 }
291
292 #[track_caller]
293 pub(crate) fn get_block<F, K>(&self, index: BlockIndex, callback: F)
294 where
295 K: inspect_format::BlockKind,
296 F: FnOnce(&Block<&Container, K>),
297 {
298 let state_lock = self.try_lock().unwrap();
299 callback(&state_lock.get_block::<K>(index))
300 }
301
302 #[track_caller]
303 pub(crate) fn get_block_mut<F, K>(&self, index: BlockIndex, callback: F)
304 where
305 K: inspect_format::BlockKind,
306 F: FnOnce(&mut Block<&mut Container, K>),
307 {
308 let mut state_lock = self.try_lock().unwrap();
309 callback(&mut state_lock.get_block_mut::<K>(index))
310 }
311}
312
313#[derive(Debug, Eq, PartialEq)]
315pub struct Stats {
316 pub total_dynamic_children: usize,
318
319 pub maximum_size: usize,
321
322 pub current_size: usize,
324
325 pub allocated_blocks: usize,
328
329 pub deallocated_blocks: usize,
331
332 pub failed_allocations: usize,
334}
335
336pub struct LockedStateGuard<'a> {
337 inner_lock: MutexGuard<'a, InnerState>,
338 #[cfg(test)]
339 drop: bool,
340}
341
342#[cfg(target_os = "fuchsia")]
343impl LockedStateGuard<'_> {
344 pub fn frozen_vmo_copy(&mut self) -> Result<zx::Vmo, Error> {
346 self.inner_lock.frozen_vmo_copy()
347 }
348}
349
350impl<'a> LockedStateGuard<'a> {
351 fn new(mut inner_lock: MutexGuard<'a, InnerState>) -> Result<Self, Error> {
352 if inner_lock.transaction_count == 0 {
353 inner_lock.header_mut().lock();
354 }
355 Ok(Self {
356 inner_lock,
357 #[cfg(test)]
358 drop: true,
359 })
360 }
361
362 pub fn stats(&self) -> Stats {
364 Stats {
365 total_dynamic_children: self.inner_lock.callbacks.len(),
366 current_size: self.inner_lock.heap.current_size(),
367 maximum_size: self.inner_lock.heap.maximum_size(),
368 allocated_blocks: self.inner_lock.heap.total_allocated_blocks(),
369 deallocated_blocks: self.inner_lock.heap.total_deallocated_blocks(),
370 failed_allocations: self.inner_lock.heap.failed_allocations(),
371 }
372 }
373
374 pub fn callbacks(&self) -> &HashMap<String, LazyNodeContextFnArc> {
376 &self.inner_lock.callbacks
377 }
378
379 pub fn create_node<'b>(
381 &mut self,
382 name: impl Into<Cow<'b, str>>,
383 parent_index: BlockIndex,
384 ) -> Result<BlockIndex, Error> {
385 self.inner_lock.create_node(name, parent_index)
386 }
387
388 pub fn create_lazy_node<'b, F>(
391 &mut self,
392 name: impl Into<Cow<'b, str>>,
393 parent_index: BlockIndex,
394 disposition: LinkNodeDisposition,
395 callback: F,
396 ) -> Result<BlockIndex, Error>
397 where
398 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
399 {
400 self.inner_lock.create_lazy_node(name, parent_index, disposition, callback)
401 }
402
403 pub fn free_lazy_node(&mut self, index: BlockIndex) -> Result<(), Error> {
404 self.inner_lock.free_lazy_node(index)
405 }
406
407 pub fn free_value(&mut self, index: BlockIndex) -> Result<(), Error> {
409 self.inner_lock.free_value(index)
410 }
411
412 pub fn create_buffer_property<'b>(
414 &mut self,
415 name: impl Into<Cow<'b, str>>,
416 value: &[u8],
417 parent_index: BlockIndex,
418 ) -> Result<BlockIndex, Error> {
419 self.inner_lock.create_buffer_property(name, value, parent_index)
420 }
421
422 pub fn create_string<'b, 'c>(
425 &mut self,
426 name: impl Into<Cow<'b, str>>,
427 value: impl Into<Cow<'c, str>>,
428 parent_index: BlockIndex,
429 ) -> Result<BlockIndex, Error> {
430 self.inner_lock.create_string(name, value, parent_index)
431 }
432
433 pub fn reparent(
434 &mut self,
435 being_reparented: BlockIndex,
436 new_parent: BlockIndex,
437 ) -> Result<(), Error> {
438 self.inner_lock.reparent(being_reparented, new_parent)
439 }
440
441 pub fn free_string_or_bytes_buffer_property(&mut self, index: BlockIndex) -> Result<(), Error> {
443 self.inner_lock.free_string_or_bytes_buffer_property(index)
444 }
445
446 pub fn set_string_property<'b>(
448 &mut self,
449 block_index: BlockIndex,
450 value: impl Into<Cow<'b, str>>,
451 ) -> Result<(), Error> {
452 self.inner_lock.set_string_property(block_index, value)
453 }
454
455 pub fn set_buffer_property(
457 &mut self,
458 block_index: BlockIndex,
459 value: &[u8],
460 ) -> Result<(), Error> {
461 self.inner_lock.set_buffer_property(block_index, value)
462 }
463
464 pub fn create_bool<'b>(
465 &mut self,
466 name: impl Into<Cow<'b, str>>,
467 value: bool,
468 parent_index: BlockIndex,
469 ) -> Result<BlockIndex, Error> {
470 self.inner_lock.create_bool(name, value, parent_index)
471 }
472
473 pub fn set_bool(&mut self, block_index: BlockIndex, value: bool) {
474 self.inner_lock.set_bool(block_index, value)
475 }
476
477 locked_state_metric_fns!(int, i64);
478 locked_state_metric_fns!(uint, u64);
479 locked_state_metric_fns!(double, f64);
480
481 locked_state_array_fns!(int, i64, IntValue);
482 locked_state_array_fns!(uint, u64, UintValue);
483 locked_state_array_fns!(double, f64, DoubleValue);
484
485 pub fn clear_array(
487 &mut self,
488 block_index: BlockIndex,
489 start_slot_index: usize,
490 ) -> Result<(), Error> {
491 self.inner_lock.clear_array(block_index, start_slot_index)
492 }
493
494 pub fn create_string_array<'b>(
495 &mut self,
496 name: impl Into<Cow<'b, str>>,
497 slots: usize,
498 parent_index: BlockIndex,
499 ) -> Result<BlockIndex, Error> {
500 self.inner_lock.create_string_array(name, slots, parent_index)
501 }
502
503 pub fn get_array_size(&self, block_index: BlockIndex) -> usize {
504 self.inner_lock.get_array_size(block_index)
505 }
506
507 pub fn set_array_string_slot<'b>(
508 &mut self,
509 block_index: BlockIndex,
510 slot_index: usize,
511 value: impl Into<Cow<'b, str>>,
512 ) -> Result<(), Error> {
513 self.inner_lock.set_array_string_slot(block_index, slot_index, value)
514 }
515}
516
517impl Drop for LockedStateGuard<'_> {
518 fn drop(&mut self) {
519 #[cfg(test)]
520 {
521 if !self.drop {
522 return;
523 }
524 }
525 if self.inner_lock.transaction_count == 0 {
526 self.inner_lock.header_mut().unlock();
527 }
528 }
529}
530
531#[cfg(test)]
532impl<'a> LockedStateGuard<'a> {
533 fn without_gen_count_changes(inner_lock: MutexGuard<'a, InnerState>) -> Self {
534 Self { inner_lock, drop: false }
535 }
536
537 pub(crate) fn load_string(&self, index: BlockIndex) -> Result<String, Error> {
538 self.inner_lock.load_key_string(index)
539 }
540
541 pub(crate) fn allocate_link<'b, 'c>(
542 &mut self,
543 name: impl Into<Cow<'b, str>>,
544 content: impl Into<Cow<'c, str>>,
545 disposition: LinkNodeDisposition,
546 parent_index: BlockIndex,
547 ) -> Result<BlockIndex, Error> {
548 let mut txn = Txn::new(&mut self.inner_lock);
549 let link = txn.allocate_link(name, content, disposition, parent_index)?;
550 txn.commit();
551 Ok(link)
552 }
553
554 #[track_caller]
555 pub(crate) fn get_block<K: inspect_format::BlockKind>(
556 &self,
557 index: BlockIndex,
558 ) -> Block<&Container, K> {
559 self.inner_lock.heap.container.maybe_block_at::<K>(index).unwrap()
560 }
561
562 fn header(&self) -> Block<&Container, inspect_format::Header> {
563 self.get_block(BlockIndex::HEADER)
564 }
565
566 #[track_caller]
567 fn get_block_mut<K: inspect_format::BlockKind>(
568 &mut self,
569 index: BlockIndex,
570 ) -> Block<&mut Container, K> {
571 self.inner_lock.heap.container.maybe_block_at_mut::<K>(index).unwrap()
572 }
573}
574
575#[derive(Derivative)]
577#[derivative(Debug)]
578struct InnerState {
579 #[derivative(Debug = "ignore")]
580 heap: Heap<Container>,
581 #[allow(dead_code)] storage: Arc<<Container as BlockContainer>::ShareableData>,
583 next_unique_link_id: AtomicU64,
584 transaction_count: usize,
585
586 string_reference_block_indexes: HashMap<Arc<Cow<'static, str>>, BlockIndex>,
588 block_index_string_references: HashMap<BlockIndex, Arc<Cow<'static, str>>>,
590
591 #[derivative(Debug = "ignore")]
592 callbacks: HashMap<String, LazyNodeContextFnArc>,
593}
594
595#[cfg(target_os = "fuchsia")]
596impl InnerState {
597 fn frozen_vmo_copy(&mut self) -> Result<zx::Vmo, Error> {
598 if self.transaction_count > 0 {
599 return Err(Error::ConcurrentTransaction(self.transaction_count));
600 }
601
602 let old = self.header_mut().freeze();
603 let child = self
604 .storage
605 .create_child(
606 zx::VmoChildOptions::SNAPSHOT | zx::VmoChildOptions::NO_WRITE,
607 0,
608 self.storage.get_size().map_err(Error::GetVmoSize)?,
609 )
610 .map_err(Error::CreateChildVmo);
611 self.header_mut().thaw(old);
612 child
613 }
614}
615
616#[derive(Debug)]
617enum Undo {
618 FreeBlock(BlockIndex),
619 ReleaseStringRef(BlockIndex),
620 DecrementChildCount(BlockIndex),
621 IncrementChildCount(BlockIndex),
622 SetParent(BlockIndex, BlockIndex),
623 FreeExtentChain(BlockIndex),
624}
625
626struct Txn<'a> {
627 state: &'a mut InnerState,
628 undo: SmallVec<[Undo; 8]>,
629 to_free_on_commit: Vec<BlockIndex>,
630 committed: bool,
631}
632
633impl<'a> Txn<'a> {
634 fn new(state: &'a mut InnerState) -> Self {
635 Self { state, undo: SmallVec::new(), to_free_on_commit: Vec::new(), committed: false }
636 }
637
638 fn commit(mut self) {
639 self.committed = true;
640 for index in self.to_free_on_commit.drain(..) {
641 let block = self.state.heap.container.block_at(index);
642 match block.block_type() {
643 Some(BlockType::Tombstone) => {
644 let tombstone = block.cast_unchecked::<Tombstone>();
645 if tombstone.child_count() == 0 {
646 if let Err(e) = self.state.heap.free_block(index) {
647 log::error!("Failed to free deferred tombstone: {:?}", e);
648 }
649 } else {
650 log::error!(
651 "Deferred free: tombstone {:?} child count is not 0 ({})",
652 index,
653 tombstone.child_count()
654 );
655 }
656 }
657 Some(t) => {
658 log::error!("Deferred free: expected Tombstone at {:?}, got {:?}", index, t);
659 }
660 None => {
661 log::error!("Deferred free: invalid block at {:?}", index);
662 }
663 }
664 }
665 }
666
667 fn allocate_block(&mut self, size: usize) -> Result<BlockIndex, Error> {
668 let block_index = self.state.heap.allocate_block(size)?;
669 self.undo.push(Undo::FreeBlock(block_index));
670 Ok(block_index)
671 }
672
673 fn intern_and_ref_string<'b>(
674 &mut self,
675 v: impl Into<Cow<'b, str>>,
676 ) -> Result<BlockIndex, Error> {
677 let block_index = self.get_or_create_string_reference(v)?;
678 self.state
679 .heap
680 .container
681 .block_at_unchecked_mut::<StringRef>(block_index)
682 .increment_ref_count()?;
683 self.undo.push(Undo::ReleaseStringRef(block_index));
684 Ok(block_index)
685 }
686
687 fn increment_child_count(&mut self, parent_index: BlockIndex) -> Result<(), Error> {
688 if parent_index != BlockIndex::EMPTY {
689 let mut parent_block =
690 self.state.heap.container.block_at_unchecked_mut::<Node>(parent_index);
691 match parent_block.block_type() {
692 Some(BlockType::NodeValue) | Some(BlockType::Tombstone) => {
693 parent_block.set_child_count(parent_block.child_count() + 1);
694 self.undo.push(Undo::DecrementChildCount(parent_index));
695 Ok(())
696 }
697 Some(BlockType::Header) => Ok(()),
698 _ => Err(Error::InvalidBlockType(parent_index, parent_block.block_type_raw())),
699 }
700 } else {
701 Ok(())
702 }
703 }
704
705 fn write_extents(&mut self, value: &[u8]) -> Result<(BlockIndex, usize), Error> {
706 if value.is_empty() {
707 return Ok((BlockIndex::ROOT, 0));
709 }
710 let mut offset = 0;
711 let total_size = value.len();
712 let head_extent_index =
713 self.state.heap.allocate_block(utils::block_size_for_payload(total_size - offset))?;
714 let mut extent_block_index = head_extent_index;
715 while offset < total_size {
716 let bytes_written = {
717 let mut extent_block = self
718 .state
719 .heap
720 .container
721 .block_at_unchecked_mut::<Reserved>(extent_block_index)
722 .become_extent(BlockIndex::EMPTY);
723 extent_block.set_contents(&value[offset..])
724 };
725 offset += bytes_written;
726 if offset < total_size {
727 let Ok(block_index) = self
728 .state
729 .heap
730 .allocate_block(utils::block_size_for_payload(total_size - offset))
731 else {
732 self.undo.push(Undo::FreeExtentChain(head_extent_index));
734 return Ok((head_extent_index, offset));
735 };
736 self.state
737 .heap
738 .container
739 .block_at_unchecked_mut::<Extent>(extent_block_index)
740 .set_next_index(block_index);
741 extent_block_index = block_index;
742 }
743 }
744 self.undo.push(Undo::FreeExtentChain(head_extent_index));
745 Ok((head_extent_index, offset))
746 }
747
748 fn release_string_ref(&mut self, i: BlockIndex) -> Result<(), Error> {
749 self.state.release_string_reference(i)
750 }
751
752 fn block_mut<K: inspect_format::BlockKind>(
753 &mut self,
754 i: BlockIndex,
755 ) -> Block<&mut Container, K> {
756 self.state.heap.container.block_at_unchecked_mut::<K>(i)
757 }
758
759 fn allocate_reserved_value<'b>(
760 &mut self,
761 name: impl Into<Cow<'b, str>>,
762 parent_index: BlockIndex,
763 block_size: usize,
764 ) -> Result<(BlockIndex, BlockIndex), Error> {
765 let block_index = self.allocate_block(block_size)?;
766 let name_index = self.intern_and_ref_string(name)?;
767 self.increment_child_count(parent_index)?;
768 Ok((block_index, name_index))
769 }
770
771 fn allocate_link<'b, 'c>(
772 &mut self,
773 name: impl Into<Cow<'b, str>>,
774 content: impl Into<Cow<'c, str>>,
775 disposition: LinkNodeDisposition,
776 parent_index: BlockIndex,
777 ) -> Result<BlockIndex, Error> {
778 let (block_index, name_index) =
779 self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
780 let content_index = self.intern_and_ref_string(content)?;
781
782 self.block_mut::<Reserved>(block_index).become_link(
783 name_index,
784 parent_index,
785 content_index,
786 disposition,
787 );
788 Ok(block_index)
789 }
790
791 fn get_or_create_string_reference<'b>(
792 &mut self,
793 value: impl Into<Cow<'b, str>>,
794 ) -> Result<BlockIndex, Error> {
795 let value = value.into();
796 match self.state.string_reference_block_indexes.get(&value) {
797 Some(index) => Ok(*index),
798 None => {
799 let undo_len_before = self.undo.len();
800 let block_size = utils::block_size_for_payload(
801 value.len() + constants::STRING_REFERENCE_TOTAL_LENGTH_BYTES,
802 );
803
804 let block_index = self.allocate_block(block_size)?;
805 self.block_mut::<Reserved>(block_index).become_string_reference();
806
807 self.write_string_reference_payload(block_index, &value)?;
808
809 let owned_value = Arc::new(value.into_owned().into());
810 self.state
811 .string_reference_block_indexes
812 .insert(Arc::clone(&owned_value), block_index);
813 self.state.block_index_string_references.insert(block_index, owned_value);
814
815 self.undo.truncate(undo_len_before);
822
823 Ok(block_index)
824 }
825 }
826 }
827
828 fn write_string_reference_payload(
829 &mut self,
830 block_index: BlockIndex,
831 value: &str,
832 ) -> Result<(), Error> {
833 let value_bytes = value.as_bytes();
834 let (head_extent, bytes_written) = {
835 let inlined = self.state.inline_string_reference(block_index, value.as_bytes());
836 if inlined < value.len() {
837 let (head, in_extents) = self.write_extents(&value_bytes[inlined..])?;
838 (head, inlined + in_extents)
839 } else {
840 (BlockIndex::EMPTY, inlined)
841 }
842 };
843 let mut block = self.block_mut::<StringRef>(block_index);
844 block.set_next_index(head_extent);
845 block.set_total_length(bytes_written.try_into().unwrap_or(u32::MAX));
846 Ok(())
847 }
848
849 fn reparent(
850 &mut self,
851 being_reparented: BlockIndex,
852 new_parent: BlockIndex,
853 ) -> Result<(), Error> {
854 self.state.check_lineage(being_reparented, new_parent)?;
855 let original_parent_idx =
856 self.state.heap.container.block_at_unchecked::<Node>(being_reparented).parent_index();
857 if original_parent_idx == new_parent {
858 return Ok(());
859 }
860
861 if original_parent_idx != BlockIndex::ROOT {
862 let original_parent_block = self.state.heap.container.block_at(original_parent_idx);
863 match original_parent_block.block_type() {
864 Some(BlockType::Tombstone) => {
865 let mut parent = self.block_mut::<Tombstone>(original_parent_idx);
866 let child_count = parent.child_count() - 1;
867 parent.set_child_count(child_count);
868 self.undo.push(Undo::IncrementChildCount(original_parent_idx));
869 if child_count == 0 {
870 self.to_free_on_commit.push(original_parent_idx);
872 }
873 }
874 Some(BlockType::NodeValue) => {
875 let mut parent = self.block_mut::<Node>(original_parent_idx);
876 let child_count = parent.child_count() - 1;
877 parent.set_child_count(child_count);
878 self.undo.push(Undo::IncrementChildCount(original_parent_idx));
879 }
880 _ => {
881 return Err(Error::InvalidBlockType(
882 original_parent_idx,
883 original_parent_block.block_type_raw(),
884 ));
885 }
886 }
887 }
888
889 self.block_mut::<Node>(being_reparented).set_parent(new_parent);
890 self.undo.push(Undo::SetParent(being_reparented, original_parent_idx));
891
892 if new_parent != BlockIndex::ROOT {
893 let mut new_parent_block = self.block_mut::<Node>(new_parent);
894 let child_count = new_parent_block.child_count() + 1;
895 new_parent_block.set_child_count(child_count);
896 self.undo.push(Undo::DecrementChildCount(new_parent));
897 }
898
899 Ok(())
900 }
901
902 fn clear_array(
903 &mut self,
904 block_index: BlockIndex,
905 start_slot_index: usize,
906 ) -> Result<(), Error> {
907 let block = self.block_mut::<Array<Unknown>>(block_index);
908 match block.entry_type() {
909 Some(value) if value.is_numeric_value() => {
910 self.block_mut::<Array<Unknown>>(block_index).clear(start_slot_index);
911 }
912 Some(BlockType::StringReference) => {
913 let array_slots = block.slots();
914 for i in start_slot_index..array_slots {
915 let index = {
916 let mut block = self.block_mut::<Array<StringRef>>(block_index);
917 let index =
918 block.get_string_index_at(i).ok_or(Error::InvalidArrayIndex(i))?;
919 if index == BlockIndex::EMPTY {
920 continue;
921 }
922 block.set_string_slot(i, BlockIndex::EMPTY);
923 index
924 };
925 self.release_string_ref(index)?;
926 }
927 }
928 _ => return Err(Error::InvalidArrayType(block_index)),
929 }
930 Ok(())
931 }
932}
933
934impl Drop for Txn<'_> {
935 fn drop(&mut self) {
936 if self.committed {
937 return;
938 }
939 while let Some(u) = self.undo.pop() {
940 self.state.apply_undo(u);
941 }
942 }
943}
944
945impl InnerState {
946 pub fn new(
948 heap: Heap<Container>,
949 storage: Arc<<Container as BlockContainer>::ShareableData>,
950 ) -> Self {
951 Self {
952 heap,
953 storage,
954 next_unique_link_id: AtomicU64::new(0),
955 callbacks: HashMap::new(),
956 transaction_count: 0,
957 string_reference_block_indexes: HashMap::new(),
958 block_index_string_references: HashMap::new(),
959 }
960 }
961
962 fn apply_undo(&mut self, undo: Undo) {
963 match undo {
964 Undo::FreeBlock(i) => {
965 if let Err(e) = self.heap.free_block(i) {
966 log::error!("Undo FreeBlock({:?}) failed: {:?}", i, e);
967 }
968 }
969 Undo::ReleaseStringRef(i) => {
970 if let Err(e) = self.release_string_reference(i) {
971 log::error!("Undo ReleaseStringRef({:?}) failed: {:?}", i, e);
972 }
973 }
974 Undo::DecrementChildCount(parent_index) => {
975 if parent_index == BlockIndex::EMPTY {
976 return;
977 }
978 let parent = self.heap.container.block_at_mut(parent_index);
979 match parent.block_type() {
980 Some(BlockType::Tombstone) => {
981 let mut parent = parent.cast_unchecked::<Tombstone>();
982 let child_count = parent.child_count() - 1;
983 if child_count == 0 {
984 if let Err(e) = self.heap.free_block(parent_index) {
985 log::error!(
986 "Undo DecrementChildCount free tombstone parent({:?}) failed: {:?}",
987 parent_index,
988 e
989 );
990 }
991 } else {
992 parent.set_child_count(child_count);
993 }
994 }
995 Some(BlockType::NodeValue) => {
996 let mut parent = parent.cast_unchecked::<Node>();
997 let child_count = parent.child_count() - 1;
998 parent.set_child_count(child_count);
999 }
1000 _ => {
1001 log::error!(
1002 "Undo DecrementChildCount: invalid parent block type raw={:?} for parent={:?}",
1003 parent.block_type_raw(),
1004 parent_index
1005 );
1006 }
1007 }
1008 }
1009 Undo::IncrementChildCount(parent_index) => {
1010 if parent_index == BlockIndex::EMPTY || parent_index == BlockIndex::ROOT {
1011 return;
1012 }
1013 let parent = self.heap.container.block_at_mut(parent_index);
1014 match parent.block_type() {
1015 Some(BlockType::Tombstone) => {
1016 let mut parent = parent.cast_unchecked::<Tombstone>();
1017 parent.set_child_count(parent.child_count() + 1);
1018 }
1019 Some(BlockType::NodeValue) => {
1020 let mut parent = parent.cast_unchecked::<Node>();
1021 parent.set_child_count(parent.child_count() + 1);
1022 }
1023 _ => {
1024 log::error!(
1025 "Undo IncrementChildCount: invalid parent block type raw={:?} for parent={:?}",
1026 parent.block_type_raw(),
1027 parent_index
1028 );
1029 }
1030 }
1031 }
1032 Undo::SetParent(child_index, parent_index) => {
1033 self.heap
1034 .container
1035 .block_at_unchecked_mut::<Node>(child_index)
1036 .set_parent(parent_index);
1037 }
1038 Undo::FreeExtentChain(head) => {
1039 if let Err(e) = self.free_extents(head) {
1040 log::error!("Undo FreeExtentChain({:?}) failed: {:?}", head, e);
1041 }
1042 }
1043 }
1044 }
1045
1046 #[inline]
1047 fn header_mut(&mut self) -> Block<&mut Container, inspect_format::Header> {
1048 self.heap.container.block_at_unchecked_mut(BlockIndex::HEADER)
1049 }
1050
1051 fn lock_header(&mut self) {
1052 if self.transaction_count == 0 {
1053 self.header_mut().lock();
1054 }
1055 self.transaction_count += 1;
1056 }
1057
1058 fn unlock_header(&mut self) {
1059 self.transaction_count -= 1;
1060 if self.transaction_count == 0 {
1061 self.header_mut().unlock();
1062 }
1063 }
1064
1065 fn create_node<'a>(
1066 &mut self,
1067 name: impl Into<Cow<'a, str>>,
1068 parent_index: BlockIndex,
1069 ) -> Result<BlockIndex, Error> {
1070 let mut txn = Txn::new(self);
1071 let (block_index, name_index) =
1072 txn.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
1073 txn.block_mut::<Reserved>(block_index).become_node(name_index, parent_index);
1074 txn.commit();
1075 Ok(block_index)
1076 }
1077
1078 fn create_lazy_node<'a, F>(
1081 &mut self,
1082 name: impl Into<Cow<'a, str>>,
1083 parent_index: BlockIndex,
1084 disposition: LinkNodeDisposition,
1085 callback: F,
1086 ) -> Result<BlockIndex, Error>
1087 where
1088 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
1089 {
1090 let name = name.into();
1091 let content = self.unique_link_name(&name);
1092 let link = {
1093 let mut txn = Txn::new(self);
1094 let link = txn.allocate_link(name, &content, disposition, parent_index)?;
1095 txn.commit();
1096 link
1097 };
1098 self.callbacks.insert(content, Arc::from(callback));
1099 Ok(link)
1100 }
1101
1102 fn free_lazy_node(&mut self, index: BlockIndex) -> Result<(), Error> {
1103 let mut txn = Txn::new(self);
1104 let content_block_index =
1105 txn.state.heap.container.block_at_unchecked::<Link>(index).content_index();
1106 let content_block_type =
1107 txn.state.heap.container.block_at(content_block_index).block_type();
1108 let content = txn.state.load_key_string(content_block_index)?;
1109 txn.state.delete_value(index)?;
1110 match content_block_type {
1112 Some(BlockType::StringReference) => {
1113 txn.release_string_ref(content_block_index)?;
1114 }
1115 _ => {
1116 txn.state.heap.free_block(content_block_index).expect("Failed to free block");
1117 }
1118 }
1119
1120 txn.state.callbacks.remove(content.as_str());
1121 txn.commit();
1122 Ok(())
1123 }
1124
1125 fn unique_link_name(&mut self, prefix: &str) -> String {
1126 let id = self.next_unique_link_id.fetch_add(1, Ordering::Relaxed);
1127 format!("{prefix}-{id}")
1128 }
1129
1130 fn free_value(&mut self, index: BlockIndex) -> Result<(), Error> {
1132 self.delete_value(index)?;
1133 Ok(())
1134 }
1135
1136 fn create_buffer_property<'a>(
1138 &mut self,
1139 name: impl Into<Cow<'a, str>>,
1140 value: &[u8],
1141 parent_index: BlockIndex,
1142 ) -> Result<BlockIndex, Error> {
1143 let mut txn = Txn::new(self);
1144 let (block_index, name_index) =
1145 txn.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
1146 txn.block_mut::<Reserved>(block_index).become_property(
1147 name_index,
1148 parent_index,
1149 PropertyFormat::Bytes,
1150 );
1151
1152 let (extent_index, written) = txn.write_extents(value)?;
1153 let mut block = txn.block_mut::<Buffer>(block_index);
1154 block.set_total_length(written.try_into().unwrap_or(u32::MAX));
1155 block.set_extent_index(extent_index);
1156
1157 txn.commit();
1158 Ok(block_index)
1159 }
1160
1161 fn create_string<'a, 'b>(
1164 &mut self,
1165 name: impl Into<Cow<'a, str>>,
1166 value: impl Into<Cow<'b, str>>,
1167 parent_index: BlockIndex,
1168 ) -> Result<BlockIndex, Error> {
1169 let mut txn = Txn::new(self);
1170 let (block_index, name_index) =
1171 txn.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
1172 txn.block_mut::<Reserved>(block_index).become_property(
1173 name_index,
1174 parent_index,
1175 PropertyFormat::StringReference,
1176 );
1177
1178 let value_index = txn.intern_and_ref_string(value)?;
1179
1180 let mut block = txn.block_mut::<Buffer>(block_index);
1181 block.set_extent_index(value_index);
1182 block.set_total_length(0);
1183
1184 txn.commit();
1185 Ok(block_index)
1186 }
1187
1188 fn inline_string_reference(&mut self, block_index: BlockIndex, value: &[u8]) -> usize {
1191 self.heap.container.block_at_unchecked_mut::<StringRef>(block_index).write_inline(value)
1192 }
1193
1194 fn release_string_reference(&mut self, block_index: BlockIndex) -> Result<(), Error> {
1197 self.heap
1198 .container
1199 .block_at_unchecked_mut::<StringRef>(block_index)
1200 .decrement_ref_count()?;
1201 self.maybe_free_string_reference(block_index)
1202 }
1203
1204 fn maybe_free_string_reference(&mut self, block_index: BlockIndex) -> Result<(), Error> {
1207 let block = self.heap.container.block_at_unchecked::<StringRef>(block_index);
1208 if block.reference_count() != 0 {
1209 return Ok(());
1210 }
1211 let first_extent = block.next_extent();
1212 self.heap.free_block(block_index)?;
1213 let str_ref = self.block_index_string_references.remove(&block_index).expect("blk idx key");
1214 self.string_reference_block_indexes.remove(&str_ref);
1215
1216 if first_extent == BlockIndex::EMPTY {
1217 return Ok(());
1218 }
1219 self.free_extents(first_extent)
1220 }
1221
1222 fn load_key_string(&self, index: BlockIndex) -> Result<String, Error> {
1223 let block = self.heap.container.block_at(index);
1224 match block.block_type() {
1225 Some(BlockType::StringReference) => {
1226 self.read_string_reference(block.cast::<StringRef>().unwrap())
1227 }
1228 Some(BlockType::Name) => block
1229 .cast::<Name>()
1230 .unwrap()
1231 .contents()
1232 .map(|s| s.to_string())
1233 .map_err(|_| Error::NameNotUtf8),
1234 _ => Err(Error::InvalidBlockTypeNumber(index, block.block_type_raw())),
1235 }
1236 }
1237
1238 fn read_string_reference(&self, block: Block<&Container, StringRef>) -> Result<String, Error> {
1240 let mut content = block.inline_data()?.to_vec();
1241 let mut next = block.next_extent();
1242 while next != BlockIndex::EMPTY {
1243 let next_block = self.heap.container.block_at_unchecked::<Extent>(next);
1244 content.extend_from_slice(next_block.contents()?);
1245 next = next_block.next_extent();
1246 }
1247
1248 content.truncate(block.total_length());
1249 String::from_utf8(content).ok().ok_or(Error::NameNotUtf8)
1250 }
1251
1252 fn free_string_or_bytes_buffer_property(&mut self, index: BlockIndex) -> Result<(), Error> {
1254 let (format, data_index) = {
1255 let block = self.heap.container.block_at_unchecked::<Buffer>(index);
1256 (block.format(), block.extent_index())
1257 };
1258 match format {
1259 Some(PropertyFormat::String) | Some(PropertyFormat::Bytes) => {
1260 self.free_extents(data_index)?;
1261 }
1262 Some(PropertyFormat::StringReference) => {
1263 self.release_string_reference(data_index)?;
1264 }
1265 _ => {
1266 return Err(Error::VmoFormat(FormatError::InvalidBufferFormat(
1267 self.heap.container.block_at_unchecked(index).format_raw(),
1268 )));
1269 }
1270 }
1271
1272 self.delete_value(index)?;
1273 Ok(())
1274 }
1275
1276 fn set_string_property<'a>(
1278 &mut self,
1279 block_index: BlockIndex,
1280 value: impl Into<Cow<'a, str>>,
1281 ) -> Result<(), Error> {
1282 self.inner_set_string_property_value(block_index, value)?;
1283 Ok(())
1284 }
1285
1286 fn set_buffer_property(&mut self, block_index: BlockIndex, value: &[u8]) -> Result<(), Error> {
1288 self.inner_set_buffer_property_value(block_index, value)?;
1289 Ok(())
1290 }
1291
1292 fn check_lineage(
1293 &self,
1294 being_reparented: BlockIndex,
1295 new_parent: BlockIndex,
1296 ) -> Result<(), Error> {
1297 if being_reparented == BlockIndex::ROOT {
1299 return Err(Error::AdoptAncestor);
1300 }
1301
1302 let mut being_checked = new_parent;
1303 while being_checked != BlockIndex::ROOT {
1304 if being_checked == being_reparented {
1305 return Err(Error::AdoptAncestor);
1306 }
1307 being_checked =
1310 self.heap.container.block_at_unchecked::<Node>(being_checked).parent_index();
1311 }
1312
1313 Ok(())
1314 }
1315
1316 fn reparent(
1317 &mut self,
1318 being_reparented: BlockIndex,
1319 new_parent: BlockIndex,
1320 ) -> Result<(), Error> {
1321 let mut txn = Txn::new(self);
1322 txn.reparent(being_reparented, new_parent)?;
1323 txn.commit();
1324 Ok(())
1325 }
1326
1327 fn create_bool<'a>(
1328 &mut self,
1329 name: impl Into<Cow<'a, str>>,
1330 value: bool,
1331 parent_index: BlockIndex,
1332 ) -> Result<BlockIndex, Error> {
1333 let mut txn = Txn::new(self);
1334 let (block_index, name_index) =
1335 txn.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
1336 txn.block_mut::<Reserved>(block_index).become_bool_value(value, name_index, parent_index);
1337 txn.commit();
1338 Ok(block_index)
1339 }
1340
1341 fn set_bool(&mut self, block_index: BlockIndex, value: bool) {
1342 let mut block = self.heap.container.block_at_unchecked_mut::<Bool>(block_index);
1343 block.set(value);
1344 }
1345
1346 metric_fns!(int, i64, Int);
1347 metric_fns!(uint, u64, Uint);
1348 metric_fns!(double, f64, Double);
1349
1350 arithmetic_array_fns!(int, i64, IntValue, Int);
1351 arithmetic_array_fns!(uint, u64, UintValue, Uint);
1352 arithmetic_array_fns!(double, f64, DoubleValue, Double);
1353
1354 fn create_string_array<'a>(
1355 &mut self,
1356 name: impl Into<Cow<'a, str>>,
1357 slots: usize,
1358 parent_index: BlockIndex,
1359 ) -> Result<BlockIndex, Error> {
1360 let block_size = slots * StringRef::array_entry_type_size() + constants::MIN_ORDER_SIZE;
1361 if block_size > constants::MAX_ORDER_SIZE {
1362 return Err(Error::BlockSizeTooBig(block_size));
1363 }
1364 let mut txn = Txn::new(self);
1365 let (block_index, name_index) =
1366 txn.allocate_reserved_value(name, parent_index, block_size)?;
1367 txn.block_mut::<Reserved>(block_index).become_array_value::<StringRef>(
1368 slots,
1369 ArrayFormat::Default,
1370 name_index,
1371 parent_index,
1372 )?;
1373 txn.commit();
1374 Ok(block_index)
1375 }
1376
1377 fn get_array_size(&self, block_index: BlockIndex) -> usize {
1378 let block = self.heap.container.block_at_unchecked::<Array<Unknown>>(block_index);
1379 block.slots()
1380 }
1381
1382 fn set_array_string_slot<'a>(
1383 &mut self,
1384 block_index: BlockIndex,
1385 slot_index: usize,
1386 value: impl Into<Cow<'a, str>>,
1387 ) -> Result<(), Error> {
1388 if self.heap.container.block_at_unchecked_mut::<Array<StringRef>>(block_index).slots()
1389 <= slot_index
1390 {
1391 return Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(slot_index)));
1392 }
1393
1394 let value = value.into();
1395
1396 let existing_index = self
1397 .heap
1398 .container
1399 .block_at_unchecked::<Array<StringRef>>(block_index)
1400 .get_string_index_at(slot_index)
1401 .ok_or(Error::InvalidArrayIndex(slot_index))?;
1402 if existing_index != BlockIndex::EMPTY
1403 && self.string_reference_block_indexes.get(&value) == Some(&existing_index)
1404 {
1405 return Ok(());
1406 }
1407
1408 let mut txn = Txn::new(self);
1409 let reference_index = if !value.is_empty() {
1410 let idx = txn.intern_and_ref_string(value)?;
1411 if existing_index != BlockIndex::EMPTY {
1412 txn.release_string_ref(existing_index)?;
1413 }
1414 idx
1415 } else {
1416 if existing_index != BlockIndex::EMPTY {
1417 txn.release_string_ref(existing_index)?;
1418 }
1419 BlockIndex::EMPTY
1420 };
1421
1422 txn.block_mut::<Array<StringRef>>(block_index).set_string_slot(slot_index, reference_index);
1423 txn.commit();
1424 Ok(())
1425 }
1426
1427 fn clear_array(
1430 &mut self,
1431 block_index: BlockIndex,
1432 start_slot_index: usize,
1433 ) -> Result<(), Error> {
1434 let mut txn = Txn::new(self);
1435 txn.clear_array(block_index, start_slot_index)?;
1436 txn.commit();
1437 Ok(())
1438 }
1439
1440 fn delete_value(&mut self, block_index: BlockIndex) -> Result<(), Error> {
1441 let block = self.heap.container.block_at_unchecked::<Node>(block_index);
1443 let parent_index = block.parent_index();
1444 let name_index = block.name_index();
1445
1446 if parent_index != BlockIndex::ROOT {
1448 let parent = self.heap.container.block_at_mut(parent_index);
1449 match parent.block_type() {
1450 Some(BlockType::Tombstone) => {
1451 let mut parent = parent.cast::<Tombstone>().unwrap();
1452 let child_count = parent.child_count() - 1;
1453 if child_count == 0 {
1454 self.heap.free_block(parent_index)?;
1455 } else {
1456 parent.set_child_count(child_count);
1457 }
1458 }
1459 Some(BlockType::NodeValue) => {
1460 let mut parent = parent.cast::<Node>().unwrap();
1461 let child_count = parent.child_count() - 1;
1462 parent.set_child_count(child_count);
1463 }
1464 _ => {
1465 return Err(Error::InvalidBlockType(parent_index, parent.block_type_raw()));
1466 }
1467 }
1468 }
1469
1470 match self.heap.container.block_at(name_index).block_type() {
1472 Some(BlockType::StringReference) => {
1473 self.release_string_reference(name_index)?;
1474 }
1475 _ => self.heap.free_block(name_index)?,
1476 }
1477
1478 let block = self.heap.container.block_at_mut(block_index);
1481 match block.cast::<Node>() {
1482 Some(block) if block.child_count() != 0 => {
1483 let _ = block.become_tombstone();
1484 }
1485 _ => {
1486 self.heap.free_block(block_index)?;
1487 }
1488 }
1489 Ok(())
1490 }
1491
1492 fn inner_set_string_property_value<'a>(
1493 &mut self,
1494 block_index: BlockIndex,
1495 value: impl Into<Cow<'a, str>>,
1496 ) -> Result<(), Error> {
1497 let value = value.into();
1498 let old_string_ref_idx =
1499 self.heap.container.block_at_unchecked::<Buffer>(block_index).extent_index();
1500
1501 if old_string_ref_idx != BlockIndex::EMPTY
1502 && self.string_reference_block_indexes.get(&value) == Some(&old_string_ref_idx)
1503 {
1504 return Ok(());
1505 }
1506
1507 let mut txn = Txn::new(self);
1508 let new_string_ref_idx = txn.intern_and_ref_string(value)?;
1509
1510 if old_string_ref_idx != BlockIndex::EMPTY {
1511 txn.release_string_ref(old_string_ref_idx)?;
1512 }
1513
1514 txn.block_mut::<Buffer>(block_index).set_extent_index(new_string_ref_idx);
1515 txn.commit();
1516 Ok(())
1517 }
1518
1519 fn inner_set_buffer_property_value(
1520 &mut self,
1521 block_index: BlockIndex,
1522 value: &[u8],
1523 ) -> Result<(), Error> {
1524 self.free_extents(
1525 self.heap.container.block_at_unchecked::<Buffer>(block_index).extent_index(),
1526 )?;
1527 let mut txn = Txn::new(self);
1528 let (result, (extent_index, written)) = match txn.write_extents(value) {
1529 Ok((e, w)) => (Ok(()), (e, w)),
1530 Err(err) => (Err(err), (BlockIndex::ROOT, 0)),
1531 };
1532 let mut block = txn.block_mut::<Buffer>(block_index);
1533 block.set_total_length(written.try_into().unwrap_or(u32::MAX));
1534 block.set_extent_index(extent_index);
1535 txn.commit();
1536 result
1537 }
1538
1539 fn free_extents(&mut self, head_extent_index: BlockIndex) -> Result<(), Error> {
1540 let mut index = head_extent_index;
1541 while index != BlockIndex::ROOT {
1542 let next_index = self.heap.container.block_at_unchecked::<Extent>(index).next_extent();
1543 self.heap.free_block(index)?;
1544 index = next_index;
1545 }
1546 Ok(())
1547 }
1548}
1549
1550#[cfg(test)]
1551mod tests {
1552 use super::*;
1553 use crate::reader::PartialNodeHierarchy;
1554 use crate::reader::snapshot::{BackingBuffer, ScannedBlock, Snapshot};
1555 use crate::writer::testing_utils::get_state;
1556 use assert_matches::assert_matches;
1557 use diagnostics_assertions::assert_data_tree;
1558 use futures::prelude::*;
1559 use inspect_format::Header;
1560
1561 #[track_caller]
1562 fn assert_all_free_or_reserved<'a>(
1563 blocks: impl Iterator<Item = Block<&'a BackingBuffer, Unknown>>,
1564 ) {
1565 let mut errors = vec![];
1566 for block in blocks {
1567 if block.block_type() != Some(BlockType::Free)
1568 && block.block_type() != Some(BlockType::Reserved)
1569 {
1570 errors.push(format!(
1571 "block at {} is {:?}, expected {} or {}",
1572 block.index(),
1573 block.block_type(),
1574 BlockType::Free,
1575 BlockType::Reserved,
1576 ));
1577 }
1578 }
1579
1580 if !errors.is_empty() {
1581 panic!("{errors:#?}");
1582 }
1583 }
1584
1585 #[track_caller]
1586 fn assert_all_free<'a>(blocks: impl Iterator<Item = Block<&'a BackingBuffer, Unknown>>) {
1587 let mut errors = vec![];
1588 for block in blocks {
1589 if block.block_type() != Some(BlockType::Free) {
1590 errors.push(format!(
1591 "block at {} is {:?}, expected {}",
1592 block.index(),
1593 block.block_type(),
1594 BlockType::Free
1595 ));
1596 }
1597 }
1598
1599 if !errors.is_empty() {
1600 panic!("{errors:#?}");
1601 }
1602 }
1603
1604 #[fuchsia::test]
1605 fn test_create() {
1606 let state = get_state(4096);
1607 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
1608 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1609 assert_eq!(blocks.len(), 8);
1610 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1611 assert_all_free(blocks.into_iter().skip(1));
1612 }
1613
1614 #[fuchsia::test]
1615 fn test_load_string() {
1616 let outer = get_state(4096);
1617 let mut state = outer.try_lock().expect("lock state");
1618 let block_index = {
1619 let mut txn = Txn::new(&mut state.inner_lock);
1620 let idx = txn.get_or_create_string_reference("a value").unwrap();
1621 txn.commit();
1622 idx
1623 };
1624 assert_eq!(state.load_string(block_index).unwrap(), "a value");
1625 }
1626
1627 #[fuchsia::test]
1628 fn test_check_lineage() {
1629 let core_state = get_state(4096);
1630 let mut state = core_state.try_lock().expect("lock state");
1631 let parent_index = state.create_node("", 0.into()).unwrap();
1632 let child_index = state.create_node("", parent_index).unwrap();
1633 let uncle_index = state.create_node("", 0.into()).unwrap();
1634
1635 state.inner_lock.check_lineage(parent_index, child_index).unwrap_err();
1636 state.inner_lock.check_lineage(0.into(), child_index).unwrap_err();
1637 state.inner_lock.check_lineage(child_index, uncle_index).unwrap();
1638 }
1639
1640 #[fuchsia::test]
1641 fn test_reparent() {
1642 let core_state = get_state(4096);
1643 let mut state = core_state.try_lock().expect("lock state");
1644
1645 let a_index = state.create_node("a", 0.into()).unwrap();
1646 let b_index = state.create_node("b", 0.into()).unwrap();
1647
1648 let a = state.get_block::<Node>(a_index);
1649 let b = state.get_block::<Node>(b_index);
1650 assert_eq!(*a.parent_index(), 0);
1651 assert_eq!(*b.parent_index(), 0);
1652
1653 assert_eq!(a.child_count(), 0);
1654 assert_eq!(b.child_count(), 0);
1655
1656 state.reparent(b_index, a_index).unwrap();
1657
1658 let a = state.get_block::<Node>(a_index);
1659 let b = state.get_block::<Node>(b_index);
1660 assert_eq!(*a.parent_index(), 0);
1661 assert_eq!(b.parent_index(), a.index());
1662
1663 assert_eq!(a.child_count(), 1);
1664 assert_eq!(b.child_count(), 0);
1665
1666 let c_index = state.create_node("c", a_index).unwrap();
1667
1668 let a = state.get_block::<Node>(a_index);
1669 let b = state.get_block::<Node>(b_index);
1670 let c = state.get_block::<Node>(c_index);
1671 assert_eq!(*a.parent_index(), 0);
1672 assert_eq!(b.parent_index(), a.index());
1673 assert_eq!(c.parent_index(), a.index());
1674
1675 assert_eq!(a.child_count(), 2);
1676 assert_eq!(b.child_count(), 0);
1677 assert_eq!(c.child_count(), 0);
1678
1679 state.reparent(c_index, b_index).unwrap();
1680
1681 let a = state.get_block::<Node>(a_index);
1682 let b = state.get_block::<Node>(b_index);
1683 let c = state.get_block::<Node>(c_index);
1684 assert_eq!(*a.parent_index(), 0);
1685 assert_eq!(b.parent_index(), a_index);
1686 assert_eq!(c.parent_index(), b_index);
1687
1688 assert_eq!(a.child_count(), 1);
1689 assert_eq!(b.child_count(), 1);
1690 assert_eq!(c.child_count(), 0);
1691 }
1692
1693 #[fuchsia::test]
1694 fn test_node() {
1695 let core_state = get_state(4096);
1696 let block_index = {
1697 let mut state = core_state.try_lock().expect("lock state");
1698
1699 let block_index = state.create_node("test-node", 0.into()).unwrap();
1701 let block = state.get_block::<Node>(block_index);
1702 assert_eq!(block.block_type(), Some(BlockType::NodeValue));
1703 assert_eq!(*block.index(), 2);
1704 assert_eq!(block.child_count(), 0);
1705 assert_eq!(*block.name_index(), 4);
1706 assert_eq!(*block.parent_index(), 0);
1707
1708 let name_block = state.get_block::<StringRef>(block.name_index());
1710 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1711 assert_eq!(name_block.total_length(), 9);
1712 assert_eq!(name_block.order(), 1);
1713 assert_eq!(state.load_string(name_block.index()).unwrap(), "test-node");
1714 block_index
1715 };
1716
1717 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1719 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1720 assert_eq!(blocks.len(), 10);
1721 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1722 assert_eq!(blocks[1].block_type(), Some(BlockType::NodeValue));
1723 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
1724 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
1725 assert_all_free(blocks.into_iter().skip(4));
1726
1727 {
1728 let mut state = core_state.try_lock().expect("lock state");
1729 let child_block_index = state.create_node("child1", block_index).unwrap();
1730 assert_eq!(state.get_block::<Node>(block_index).child_count(), 1);
1731
1732 let child11_block_index = state.create_node("child1-1", child_block_index).unwrap();
1734 {
1735 assert_eq!(state.get_block::<Node>(child11_block_index).child_count(), 0);
1736 assert_eq!(state.get_block::<Node>(child_block_index).child_count(), 1);
1737 assert_eq!(state.get_block::<Node>(block_index).child_count(), 1);
1738 }
1739
1740 assert!(state.free_value(child11_block_index).is_ok());
1741 {
1742 let child_block = state.get_block::<Node>(child_block_index);
1743 assert_eq!(child_block.child_count(), 0);
1744 }
1745
1746 let child_block2_index = state.create_node("child2", block_index).unwrap();
1748 let child_block3_index = state.create_node("child3", block_index).unwrap();
1749 assert_eq!(state.get_block::<Node>(block_index).child_count(), 3);
1750
1751 assert!(state.free_value(child_block_index).is_ok());
1753 assert!(state.free_value(child_block2_index).is_ok());
1754 assert!(state.free_value(child_block3_index).is_ok());
1755 assert_eq!(state.get_block::<Node>(block_index).child_count(), 0);
1756
1757 assert!(state.free_value(block_index).is_ok());
1759 }
1760
1761 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1762 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1763 assert_all_free(blocks.into_iter().skip(1));
1764 }
1765
1766 #[fuchsia::test]
1767 fn test_int_metric() {
1768 let core_state = get_state(4096);
1769 let block_index = {
1770 let mut state = core_state.try_lock().expect("lock state");
1771 let block_index = state.create_int_metric("test", 3, 0.into()).unwrap();
1772 let block = state.get_block::<Int>(block_index);
1773 assert_eq!(block.block_type(), Some(BlockType::IntValue));
1774 assert_eq!(*block.index(), 2);
1775 assert_eq!(block.value(), 3);
1776 assert_eq!(*block.name_index(), 3);
1777 assert_eq!(*block.parent_index(), 0);
1778
1779 let name_block = state.get_block::<StringRef>(block.name_index());
1780 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1781 assert_eq!(name_block.total_length(), 4);
1782 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1783 block_index
1784 };
1785
1786 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1787 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1788 assert_eq!(blocks.len(), 9);
1789 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1790 assert_eq!(blocks[1].block_type(), Some(BlockType::IntValue));
1791 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1792 assert_all_free(blocks.into_iter().skip(3));
1793
1794 {
1795 let mut state = core_state.try_lock().expect("lock state");
1796 assert_eq!(state.add_int_metric(block_index, 10), 13);
1797 assert_eq!(state.get_block::<Int>(block_index).value(), 13);
1798
1799 assert_eq!(state.subtract_int_metric(block_index, 5), 8);
1800 assert_eq!(state.get_block::<Int>(block_index).value(), 8);
1801
1802 state.set_int_metric(block_index, -6);
1803 assert_eq!(state.get_block::<Int>(block_index).value(), -6);
1804
1805 assert_eq!(state.subtract_int_metric(block_index, i64::MAX), i64::MIN);
1806 assert_eq!(state.get_block::<Int>(block_index).value(), i64::MIN);
1807 state.set_int_metric(block_index, i64::MAX);
1808
1809 assert_eq!(state.add_int_metric(block_index, 2), i64::MAX);
1810 assert_eq!(state.get_block::<Int>(block_index).value(), i64::MAX);
1811
1812 assert!(state.free_value(block_index).is_ok());
1814 }
1815
1816 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1817 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1818 assert_all_free(blocks.into_iter().skip(1));
1819 }
1820
1821 #[fuchsia::test]
1822 fn test_uint_metric() {
1823 let core_state = get_state(4096);
1824
1825 let block_index = {
1827 let mut state = core_state.try_lock().expect("try lock");
1828 let block_index = state.create_uint_metric("test", 3, 0.into()).unwrap();
1829 let block = state.get_block::<Uint>(block_index);
1830 assert_eq!(block.block_type(), Some(BlockType::UintValue));
1831 assert_eq!(*block.index(), 2);
1832 assert_eq!(block.value(), 3);
1833 assert_eq!(*block.name_index(), 3);
1834 assert_eq!(*block.parent_index(), 0);
1835
1836 let name_block = state.get_block::<StringRef>(block.name_index());
1837 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1838 assert_eq!(name_block.total_length(), 4);
1839 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1840 block_index
1841 };
1842
1843 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1844 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1845 assert_eq!(blocks.len(), 9);
1846 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1847 assert_eq!(blocks[1].block_type(), Some(BlockType::UintValue));
1848 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1849 assert_all_free(blocks.into_iter().skip(3));
1850
1851 {
1852 let mut state = core_state.try_lock().expect("try lock");
1853 assert_eq!(state.add_uint_metric(block_index, 10), 13);
1854 assert_eq!(state.get_block::<Uint>(block_index).value(), 13);
1855
1856 assert_eq!(state.subtract_uint_metric(block_index, 5), 8);
1857 assert_eq!(state.get_block::<Uint>(block_index).value(), 8);
1858
1859 state.set_uint_metric(block_index, 0);
1860 assert_eq!(state.get_block::<Uint>(block_index).value(), 0);
1861
1862 assert_eq!(state.subtract_uint_metric(block_index, u64::MAX), 0);
1863 assert_eq!(state.get_block::<Uint>(block_index).value(), 0);
1864
1865 state.set_uint_metric(block_index, 3);
1866 assert_eq!(state.add_uint_metric(block_index, u64::MAX), u64::MAX);
1867 assert_eq!(state.get_block::<Uint>(block_index).value(), u64::MAX);
1868
1869 assert!(state.free_value(block_index).is_ok());
1871 }
1872
1873 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1874 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1875 assert_all_free(blocks.into_iter().skip(1));
1876 }
1877
1878 #[fuchsia::test]
1879 fn test_double_metric() {
1880 let core_state = get_state(4096);
1881
1882 let block_index = {
1884 let mut state = core_state.try_lock().expect("lock state");
1885 let block_index = state.create_double_metric("test", 3.0, 0.into()).unwrap();
1886 let block = state.get_block::<Double>(block_index);
1887 assert_eq!(block.block_type(), Some(BlockType::DoubleValue));
1888 assert_eq!(*block.index(), 2);
1889 assert_eq!(block.value(), 3.0);
1890 assert_eq!(*block.name_index(), 3);
1891 assert_eq!(*block.parent_index(), 0);
1892
1893 let name_block = state.get_block::<StringRef>(block.name_index());
1894 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1895 assert_eq!(name_block.total_length(), 4);
1896 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1897 block_index
1898 };
1899
1900 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1901 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1902 assert_eq!(blocks.len(), 9);
1903 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1904 assert_eq!(blocks[1].block_type(), Some(BlockType::DoubleValue));
1905 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1906 assert_all_free(blocks.into_iter().skip(3));
1907
1908 {
1909 let mut state = core_state.try_lock().expect("lock state");
1910 assert_eq!(state.add_double_metric(block_index, 10.5), 13.5);
1911 assert_eq!(state.get_block::<Double>(block_index).value(), 13.5);
1912
1913 assert_eq!(state.subtract_double_metric(block_index, 5.1), 8.4);
1914 assert_eq!(state.get_block::<Double>(block_index).value(), 8.4);
1915
1916 state.set_double_metric(block_index, -6.0);
1917 assert_eq!(state.get_block::<Double>(block_index).value(), -6.0);
1918
1919 assert!(state.free_value(block_index).is_ok());
1921 }
1922
1923 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1924 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1925 assert_all_free(blocks.into_iter().skip(1));
1926 }
1927
1928 #[fuchsia::test]
1929 fn test_create_buffer_property_cleanup_on_failure() {
1930 assert_eq!(constants::MAX_ORDER_SIZE, 2048);
1932
1933 let core_state = get_state(5121); let mut state = core_state.try_lock().expect("lock state");
1935 let name = (0..3000).map(|_| " ").collect::<String>();
1937 let payload = [0u8; 4096]; assert!(state.create_buffer_property(name, &payload, 0.into()).is_err());
1944
1945 drop(state);
1946
1947 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1948 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1949
1950 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1952 assert_all_free(blocks.into_iter().skip(1));
1953 }
1954
1955 #[fuchsia::test]
1956 fn test_string_reference_allocations() {
1957 let core_state = get_state(4096); {
1959 let mut state = core_state.try_lock().expect("lock state");
1960 let sf = "a reference-counted canonical name";
1961 assert_eq!(state.stats().allocated_blocks, 1);
1962
1963 let mut collected = vec![];
1964 for _ in 0..100 {
1965 collected.push(state.create_node(sf, 0.into()).unwrap());
1966 }
1967
1968 let acsf = Arc::new(Cow::Borrowed(sf));
1969 assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1970
1971 assert_eq!(state.stats().allocated_blocks, 102);
1972 let block = state.get_block::<Node>(collected[0]);
1973 let sf_block = state.get_block::<StringRef>(block.name_index());
1974 assert_eq!(sf_block.reference_count(), 100);
1975
1976 collected.into_iter().for_each(|b| {
1977 assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1978 assert!(state.free_value(b).is_ok())
1979 });
1980
1981 assert!(!state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1982
1983 let node_index = state.create_node(sf, 0.into()).unwrap();
1984 assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1985 assert!(state.free_value(node_index).is_ok());
1986 assert!(!state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1987 }
1988
1989 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1990 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1991 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1992 assert_all_free(blocks.into_iter().skip(1));
1993 }
1994
1995 #[fuchsia::test]
1996 fn test_string_reference_data() {
1997 let core_state = get_state(4096); let mut state = core_state.try_lock().expect("lock state");
1999
2000 let block_index = {
2002 let mut txn = Txn::new(&mut state.inner_lock);
2003 let idx = txn.get_or_create_string_reference("abcd").unwrap();
2004 txn.commit();
2005 idx
2006 };
2007 let block = state.get_block::<StringRef>(block_index);
2008 assert_eq!(block.block_type(), Some(BlockType::StringReference));
2009 assert_eq!(block.order(), 0);
2010 assert_eq!(state.stats().allocated_blocks, 2);
2011 assert_eq!(state.stats().deallocated_blocks, 0);
2012 assert_eq!(block.reference_count(), 0);
2013 assert_eq!(block.total_length(), 4);
2014 assert_eq!(*block.next_extent(), 0);
2015 assert_eq!(block.order(), 0);
2016 assert_eq!(state.load_string(block.index()).unwrap(), "abcd");
2017
2018 state.inner_lock.maybe_free_string_reference(block_index).unwrap();
2019 assert_eq!(state.stats().deallocated_blocks, 1);
2020
2021 let block_index = {
2022 let mut txn = Txn::new(&mut state.inner_lock);
2023 let idx = txn.get_or_create_string_reference("longer").unwrap();
2024 txn.commit();
2025 idx
2026 };
2027 let block = state.get_block::<StringRef>(block_index);
2028 assert_eq!(block.block_type(), Some(BlockType::StringReference));
2029 assert_eq!(block.order(), 1);
2030 assert_eq!(block.reference_count(), 0);
2031 assert_eq!(block.total_length(), 6);
2032 assert_eq!(state.stats().allocated_blocks, 3);
2033 assert_eq!(state.stats().deallocated_blocks, 1);
2034 assert_eq!(state.load_string(block.index()).unwrap(), "longer");
2035
2036 let idx = block.next_extent();
2037 assert_eq!(*idx, 0);
2038
2039 state.inner_lock.maybe_free_string_reference(block_index).unwrap();
2040 assert_eq!(state.stats().deallocated_blocks, 2);
2041
2042 let block_index = {
2043 let mut txn = Txn::new(&mut state.inner_lock);
2044 let idx = txn.get_or_create_string_reference("longer").unwrap();
2045 txn.commit();
2046 idx
2047 };
2048 let mut block = state.get_block_mut::<StringRef>(block_index);
2049 assert_eq!(block.order(), 1);
2050 block.increment_ref_count().unwrap();
2051 assert!(state.inner_lock.maybe_free_string_reference(block_index).is_ok());
2053
2054 let mut block = state.get_block_mut(block_index);
2055 block.decrement_ref_count().unwrap();
2056 state.inner_lock.maybe_free_string_reference(block_index).unwrap();
2057 }
2058
2059 #[fuchsia::test]
2060 fn test_string_reference_format_property() {
2061 let core_state = get_state(4096);
2062 let block_index = {
2063 let mut state = core_state.try_lock().expect("lock state");
2064
2065 let block_index =
2067 state.create_string("test", "test-property", BlockIndex::from(0)).unwrap();
2068 let block = state.get_block::<Buffer>(block_index);
2069 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2070 assert_eq!(*block.index(), 2);
2071 assert_eq!(*block.parent_index(), 0);
2072 assert_eq!(*block.name_index(), 3);
2073 assert_eq!(block.total_length(), 0);
2074 assert_eq!(block.format(), Some(PropertyFormat::StringReference));
2075
2076 let name_block = state.get_block::<StringRef>(block.name_index());
2077 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2078 assert_eq!(name_block.total_length(), 4);
2079 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2080
2081 let data_block = state.get_block::<StringRef>(block.extent_index());
2082 assert_eq!(data_block.block_type(), Some(BlockType::StringReference));
2083 assert_eq!(state.load_string(data_block.index()).unwrap(), "test-property");
2084 block_index
2085 };
2086
2087 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2088 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2089 assert_eq!(blocks.len(), 10);
2090 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2091 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2092 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2093 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2094 assert_all_free(blocks.into_iter().skip(4));
2095
2096 {
2097 let mut state = core_state.try_lock().expect("lock state");
2098 assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2100 }
2101 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2102 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2103 assert_all_free(blocks.into_iter().skip(1));
2104 }
2105
2106 #[fuchsia::test]
2107 fn test_string_arrays() {
2108 let core_state = get_state(4096);
2109 {
2110 let mut state = core_state.try_lock().expect("lock state");
2111 let array_index = state.create_string_array("array", 4, 0.into()).unwrap();
2112 assert_eq!(state.set_array_string_slot(array_index, 0, "0"), Ok(()));
2113 assert_eq!(state.set_array_string_slot(array_index, 1, "1"), Ok(()));
2114 assert_eq!(state.set_array_string_slot(array_index, 2, "2"), Ok(()));
2115 assert_eq!(state.set_array_string_slot(array_index, 3, "3"), Ok(()));
2116
2117 assert_matches!(
2119 state.set_array_string_slot(array_index, 4, ""),
2120 Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(4)))
2121 );
2122 assert_matches!(
2123 state.set_array_string_slot(array_index, 5, ""),
2124 Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(5)))
2125 );
2126
2127 for i in 0..4 {
2128 let idx = state
2129 .get_block::<Array<StringRef>>(array_index)
2130 .get_string_index_at(i)
2131 .unwrap();
2132 assert_eq!(i.to_string(), state.load_string(idx).unwrap());
2133 }
2134
2135 assert_eq!(
2136 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(4),
2137 None
2138 );
2139 assert_eq!(
2140 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(5),
2141 None
2142 );
2143
2144 state.clear_array(array_index, 0).unwrap();
2145 state.free_value(array_index).unwrap();
2146 }
2147
2148 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2149 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2150 assert_all_free(blocks.into_iter().skip(1));
2151 }
2152
2153 #[fuchsia::test]
2154 fn update_string_array_value() {
2155 let core_state = get_state(4096);
2156 {
2157 let mut state = core_state.try_lock().expect("lock state");
2158 let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
2159
2160 assert_eq!(state.set_array_string_slot(array_index, 0, "abc"), Ok(()));
2161 assert_eq!(state.set_array_string_slot(array_index, 1, "def"), Ok(()));
2162
2163 assert_eq!(state.set_array_string_slot(array_index, 0, "cba"), Ok(()));
2164 assert_eq!(state.set_array_string_slot(array_index, 1, "fed"), Ok(()));
2165
2166 let cba_index_slot =
2167 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(0).unwrap();
2168 let fed_index_slot =
2169 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(1).unwrap();
2170 assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap());
2171 assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2172
2173 state.clear_array(array_index, 0).unwrap();
2174 state.free_value(array_index).unwrap();
2175 }
2176
2177 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2178 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2179 blocks[1..].iter().enumerate().for_each(|(i, b)| {
2180 assert!(b.block_type() == Some(BlockType::Free), "index is {}", i + 1);
2181 });
2182 }
2183
2184 #[fuchsia::test]
2185 fn set_string_reference_instances_multiple_times_in_array() {
2186 let core_state = get_state(4096);
2187 {
2188 let mut state = core_state.try_lock().expect("lock state");
2189 let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
2190
2191 let abc = "abc";
2192 let def = "def";
2193 let cba = "cba";
2194 let fed = "fed";
2195
2196 state.set_array_string_slot(array_index, 0, abc).unwrap();
2197 state.set_array_string_slot(array_index, 1, def).unwrap();
2198 state.set_array_string_slot(array_index, 0, abc).unwrap();
2199 state.set_array_string_slot(array_index, 1, def).unwrap();
2200
2201 let abc_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2202 let def_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2203 assert_eq!("abc".to_string(), state.load_string(abc_index_slot).unwrap(),);
2204 assert_eq!("def".to_string(), state.load_string(def_index_slot).unwrap(),);
2205
2206 state.set_array_string_slot(array_index, 0, cba).unwrap();
2207 state.set_array_string_slot(array_index, 1, fed).unwrap();
2208
2209 let cba_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2210 let fed_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2211 assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap(),);
2212 assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2213
2214 state.set_array_string_slot(array_index, 0, abc).unwrap();
2215 state.set_array_string_slot(array_index, 1, def).unwrap();
2216
2217 let abc_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2218 let def_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2219 assert_eq!("abc".to_string(), state.load_string(abc_index_slot).unwrap(),);
2220 assert_eq!("def".to_string(), state.load_string(def_index_slot).unwrap(),);
2221
2222 state.set_array_string_slot(array_index, 0, cba).unwrap();
2223 state.set_array_string_slot(array_index, 1, fed).unwrap();
2224
2225 let cba_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2226 let fed_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2227 assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap(),);
2228 assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2229
2230 state.clear_array(array_index, 0).unwrap();
2231 state.free_value(array_index).unwrap();
2232 }
2233
2234 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2235 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2236 blocks[1..].iter().enumerate().for_each(|(i, b)| {
2237 assert!(b.block_type() == Some(BlockType::Free), "index is {}", i + 1);
2238 });
2239 }
2240
2241 #[fuchsia::test]
2242 fn test_empty_value_string_arrays() {
2243 let core_state = get_state(4096);
2244 {
2245 let mut state = core_state.try_lock().expect("lock state");
2246 let array_index = state.create_string_array("array", 4, 0.into()).unwrap();
2247
2248 state.set_array_string_slot(array_index, 0, "").unwrap();
2249 state.set_array_string_slot(array_index, 1, "").unwrap();
2250 state.set_array_string_slot(array_index, 2, "").unwrap();
2251 state.set_array_string_slot(array_index, 3, "").unwrap();
2252 }
2253
2254 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2255 let state = core_state.try_lock().expect("lock state");
2256
2257 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2258 for b in blocks {
2259 if b.block_type() == Some(BlockType::StringReference)
2260 && state.load_string(b.index()).unwrap() == "array"
2261 {
2262 continue;
2263 }
2264
2265 assert_ne!(
2266 b.block_type(),
2267 Some(BlockType::StringReference),
2268 "Got unexpected StringReference, index: {}, value (wrapped in single quotes): '{:?}'",
2269 b.index(),
2270 b.block_type()
2271 );
2272 }
2273 }
2274
2275 #[fuchsia::test]
2276 fn test_bytevector_property() {
2277 let core_state = get_state(4096);
2278
2279 let block_index = {
2281 let mut state = core_state.try_lock().expect("lock state");
2282 let block_index =
2283 state.create_buffer_property("test", b"test-property", 0.into()).unwrap();
2284 let block = state.get_block::<Buffer>(block_index);
2285 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2286 assert_eq!(*block.index(), 2);
2287 assert_eq!(*block.parent_index(), 0);
2288 assert_eq!(*block.name_index(), 3);
2289 assert_eq!(block.total_length(), 13);
2290 assert_eq!(*block.extent_index(), 4);
2291 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2292
2293 let name_block = state.get_block::<StringRef>(block.name_index());
2294 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2295 assert_eq!(name_block.total_length(), 4);
2296 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2297
2298 let extent_block = state.get_block::<Extent>(4.into());
2299 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2300 assert_eq!(*extent_block.next_extent(), 0);
2301 assert_eq!(
2302 std::str::from_utf8(extent_block.contents().unwrap()).unwrap(),
2303 "test-property\0\0\0\0\0\0\0\0\0\0\0"
2304 );
2305 block_index
2306 };
2307
2308 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2309 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2310 assert_eq!(blocks.len(), 10);
2311 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2312 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2313 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2314 assert_eq!(blocks[3].block_type(), Some(BlockType::Extent));
2315 assert_all_free(blocks.into_iter().skip(4));
2316
2317 {
2319 let mut state = core_state.try_lock().expect("lock state");
2320 assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2321 }
2322 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2323 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2324 assert_all_free(blocks.into_iter().skip(1));
2325 }
2326
2327 #[fuchsia::test]
2328 fn test_bool() {
2329 let core_state = get_state(4096);
2330 let block_index = {
2331 let mut state = core_state.try_lock().expect("lock state");
2332
2333 let block_index = state.create_bool("test", true, 0.into()).unwrap();
2335 let block = state.get_block::<Bool>(block_index);
2336 assert_eq!(block.block_type(), Some(BlockType::BoolValue));
2337 assert_eq!(*block.index(), 2);
2338 assert!(block.value());
2339 assert_eq!(*block.name_index(), 3);
2340 assert_eq!(*block.parent_index(), 0);
2341
2342 let name_block = state.get_block::<StringRef>(block.name_index());
2343 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2344 assert_eq!(name_block.total_length(), 4);
2345 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2346 block_index
2347 };
2348
2349 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2350 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2351 assert_eq!(blocks.len(), 9);
2352 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2353 assert_eq!(blocks[1].block_type(), Some(BlockType::BoolValue));
2354 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2355 assert_all_free(blocks.into_iter().skip(3));
2356
2357 {
2359 let mut state = core_state.try_lock().expect("lock state");
2360 assert!(state.free_value(block_index).is_ok());
2361 }
2362 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2363 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2364 assert_all_free(blocks.into_iter().skip(1));
2365 }
2366
2367 #[fuchsia::test]
2368 fn test_int_array() {
2369 let core_state = get_state(4096);
2370 let block_index = {
2371 let mut state = core_state.try_lock().expect("lock state");
2372 let block_index =
2373 state.create_int_array("test", 5, ArrayFormat::Default, 0.into()).unwrap();
2374 let block = state.get_block::<Array<Int>>(block_index);
2375 assert_eq!(block.block_type(), Some(BlockType::ArrayValue));
2376 assert_eq!(block.order(), 2);
2377 assert_eq!(*block.index(), 4);
2378 assert_eq!(*block.name_index(), 2);
2379 assert_eq!(*block.parent_index(), 0);
2380 assert_eq!(block.slots(), 5);
2381 assert_eq!(block.format(), Some(ArrayFormat::Default));
2382 assert_eq!(block.entry_type(), Some(BlockType::IntValue));
2383
2384 let name_block = state.get_block::<StringRef>(BlockIndex::from(2));
2385 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2386 assert_eq!(name_block.total_length(), 4);
2387 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2388 for i in 0..5 {
2389 state.set_array_int_slot(block_index, i, 3 * i as i64);
2390 }
2391 for i in 0..5 {
2392 assert_eq!(state.get_block::<Array<Int>>(block_index).get(i), Some(3 * i as i64));
2393 }
2394 block_index
2395 };
2396
2397 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2398 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2399 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2400 assert_eq!(blocks[1].block_type(), Some(BlockType::StringReference));
2401 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2402 assert_eq!(blocks[3].block_type(), Some(BlockType::ArrayValue));
2403 assert_all_free(blocks.into_iter().skip(4));
2404
2405 {
2407 let mut state = core_state.try_lock().expect("lock state");
2408 assert!(state.free_value(block_index).is_ok());
2409 }
2410 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2411 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2412 assert_all_free(blocks.into_iter().skip(1));
2413 }
2414
2415 #[fuchsia::test]
2416 fn test_write_extent_overflow() {
2417 const SIZE: usize = constants::MAX_ORDER_SIZE * 2;
2418 const EXPECTED_WRITTEN: usize = constants::MAX_ORDER_SIZE - constants::HEADER_SIZE_BYTES;
2419 const TRIED_TO_WRITE: usize = SIZE + 1;
2420 let core_state = get_state(SIZE);
2421 let mut state = core_state.try_lock().unwrap();
2422 let (_, written) = {
2423 let mut txn = Txn::new(&mut state.inner_lock);
2424 let res = txn.write_extents(&[4u8; TRIED_TO_WRITE]).unwrap();
2425 txn.commit();
2426 res
2427 };
2428 assert_eq!(written, EXPECTED_WRITTEN);
2429 }
2430
2431 #[fuchsia::test]
2432 fn overflow_property() {
2433 const SIZE: usize = constants::MAX_ORDER_SIZE * 2;
2434 const EXPECTED_WRITTEN: usize = constants::MAX_ORDER_SIZE - constants::HEADER_SIZE_BYTES;
2435
2436 let core_state = get_state(SIZE);
2437 let mut state = core_state.try_lock().expect("lock state");
2438
2439 let data = "X".repeat(SIZE * 2);
2440 let block_index = state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2441 let block = state.get_block::<Buffer>(block_index);
2442 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2443 assert_eq!(*block.index(), 2);
2444 assert_eq!(*block.parent_index(), 0);
2445 assert_eq!(*block.name_index(), 3);
2446 assert_eq!(block.total_length(), EXPECTED_WRITTEN);
2447 assert_eq!(*block.extent_index(), 128);
2448 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2449
2450 let name_block = state.get_block::<StringRef>(block.name_index());
2451 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2452 assert_eq!(name_block.total_length(), 4);
2453 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2454
2455 let extent_block = state.get_block::<Extent>(128.into());
2456 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2457 assert_eq!(extent_block.order(), 7);
2458 assert_eq!(*extent_block.next_extent(), *BlockIndex::EMPTY);
2459 assert_eq!(
2460 extent_block.contents().unwrap(),
2461 data.chars().take(EXPECTED_WRITTEN).map(|c| c as u8).collect::<Vec<u8>>()
2462 );
2463 }
2464
2465 #[fuchsia::test]
2466 fn test_multi_extent_property() {
2467 let core_state = get_state(10000);
2468 let block_index = {
2469 let mut state = core_state.try_lock().expect("lock state");
2470
2471 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2472 let data = chars.iter().cycle().take(6000).collect::<String>();
2473 let block_index =
2474 state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2475 let block = state.get_block::<Buffer>(block_index);
2476 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2477 assert_eq!(*block.index(), 2);
2478 assert_eq!(*block.parent_index(), 0);
2479 assert_eq!(*block.name_index(), 3);
2480 assert_eq!(block.total_length(), 6000);
2481 assert_eq!(*block.extent_index(), 128);
2482 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2483
2484 let name_block = state.get_block::<StringRef>(block.name_index());
2485 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2486 assert_eq!(name_block.total_length(), 4);
2487 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2488
2489 let extent_block = state.get_block::<Extent>(128.into());
2490 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2491 assert_eq!(extent_block.order(), 7);
2492 assert_eq!(*extent_block.next_extent(), 256);
2493 assert_eq!(
2494 extent_block.contents().unwrap(),
2495 chars.iter().cycle().take(2040).map(|&c| c as u8).collect::<Vec<u8>>()
2496 );
2497
2498 let extent_block = state.get_block::<Extent>(256.into());
2499 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2500 assert_eq!(extent_block.order(), 7);
2501 assert_eq!(*extent_block.next_extent(), 384);
2502 assert_eq!(
2503 extent_block.contents().unwrap(),
2504 chars.iter().cycle().skip(2040).take(2040).map(|&c| c as u8).collect::<Vec<u8>>()
2505 );
2506
2507 let extent_block = state.get_block::<Extent>(384.into());
2508 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2509 assert_eq!(extent_block.order(), 7);
2510 assert_eq!(*extent_block.next_extent(), 0);
2511 assert_eq!(
2512 extent_block.contents().unwrap()[..1920],
2513 chars.iter().cycle().skip(4080).take(1920).map(|&c| c as u8).collect::<Vec<u8>>()[..]
2514 );
2515 assert_eq!(extent_block.contents().unwrap()[1920..], [0u8; 120][..]);
2516 block_index
2517 };
2518
2519 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2520 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2521 assert_eq!(blocks.len(), 11);
2522 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2523 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2524 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2525 assert_eq!(blocks[8].block_type(), Some(BlockType::Extent));
2526 assert_eq!(blocks[9].block_type(), Some(BlockType::Extent));
2527 assert_eq!(blocks[10].block_type(), Some(BlockType::Extent));
2528 assert_all_free(blocks.into_iter().skip(3).take(5));
2529 {
2531 let mut state = core_state.try_lock().expect("lock state");
2532 assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2533 }
2534 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2535 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2536 assert_all_free(blocks.into_iter().skip(1));
2537 }
2538
2539 #[fuchsia::test]
2540 fn test_freeing_string_references() {
2541 let core_state = get_state(4096);
2542 {
2543 let mut state = core_state.try_lock().expect("lock state");
2544 assert_eq!(state.stats().allocated_blocks, 1);
2545
2546 let block0_index = state.create_node("abcd123456789", 0.into()).unwrap();
2547 let block0_name_index = {
2548 let block0_name_index = state.get_block::<Node>(block0_index).name_index();
2549 let block0_name = state.get_block::<StringRef>(block0_name_index);
2550 assert_eq!(block0_name.order(), 1);
2551 block0_name_index
2552 };
2553 assert_eq!(state.stats().allocated_blocks, 3);
2554
2555 let block1_index = {
2556 let mut txn = Txn::new(&mut state.inner_lock);
2557 let idx = txn.get_or_create_string_reference("abcd").unwrap();
2558 txn.commit();
2559 idx
2560 };
2561 assert_eq!(state.stats().allocated_blocks, 4);
2562 assert_eq!(state.get_block::<StringRef>(block1_index).order(), 0);
2563
2564 let block2_index = {
2565 let mut txn = Txn::new(&mut state.inner_lock);
2566 let idx = txn.get_or_create_string_reference("abcd123456789").unwrap();
2567 txn.commit();
2568 idx
2569 };
2570 assert_eq!(state.get_block::<StringRef>(block2_index).order(), 1);
2571 assert_eq!(block0_name_index, block2_index);
2572 assert_eq!(state.stats().allocated_blocks, 4);
2573
2574 let block3_index = state.create_node("abcd12345678", 0.into()).unwrap();
2575 let block3 = state.get_block::<Node>(block3_index);
2576 let block3_name = state.get_block::<StringRef>(block3.name_index());
2577 assert_eq!(block3_name.order(), 1);
2578 assert_eq!(block3.order(), 0);
2579 assert_eq!(state.stats().allocated_blocks, 6);
2580
2581 let mut long_name = "".to_string();
2582 for _ in 0..3000 {
2583 long_name += " ";
2584 }
2585
2586 let block4_index = state.create_node(long_name, 0.into()).unwrap();
2587 let block4 = state.get_block::<Node>(block4_index);
2588 let block4_name = state.get_block::<StringRef>(block4.name_index());
2589 assert_eq!(block4_name.order(), 7);
2590 assert!(*block4_name.next_extent() != 0);
2591 assert_eq!(state.stats().allocated_blocks, 9);
2592
2593 assert!(state.inner_lock.maybe_free_string_reference(block1_index).is_ok());
2594 assert_eq!(state.stats().deallocated_blocks, 1);
2595 assert!(state.inner_lock.maybe_free_string_reference(block2_index).is_ok());
2596 assert_eq!(state.stats().deallocated_blocks, 1);
2598 assert!(state.free_value(block3_index).is_ok());
2599 assert_eq!(state.stats().deallocated_blocks, 3);
2600 assert!(state.free_value(block4_index).is_ok());
2601 assert_eq!(state.stats().deallocated_blocks, 6);
2602 }
2603
2604 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2606 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2607
2608 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2609 assert_eq!(blocks[1].block_type(), Some(BlockType::NodeValue));
2610 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2611 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2612 assert_all_free(blocks.into_iter().skip(4));
2613 }
2614
2615 #[fuchsia::test]
2616 fn test_tombstone() {
2617 let core_state = get_state(4096);
2618 let child_block_index = {
2619 let mut state = core_state.try_lock().expect("lock state");
2620
2621 let block_index = state.create_node("root-node", 0.into()).unwrap();
2623 let block_name_as_string_ref =
2624 state.get_block::<StringRef>(state.get_block::<Node>(block_index).name_index());
2625 assert_eq!(block_name_as_string_ref.order(), 1);
2626 assert_eq!(state.stats().allocated_blocks, 3);
2627 assert_eq!(state.stats().deallocated_blocks, 0);
2628
2629 let child_block_index = state.create_node("child-node", block_index).unwrap();
2630 assert_eq!(state.stats().allocated_blocks, 5);
2631 assert_eq!(state.stats().deallocated_blocks, 0);
2632
2633 assert!(state.free_value(block_index).is_ok());
2635 assert_eq!(state.stats().allocated_blocks, 5);
2636 assert_eq!(state.stats().deallocated_blocks, 1);
2637 child_block_index
2638 };
2639
2640 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2641 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2642
2643 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2646 assert_eq!(blocks[1].block_type(), Some(BlockType::Tombstone));
2647 assert_eq!(blocks[2].block_type(), Some(BlockType::NodeValue));
2648 assert_eq!(blocks[3].block_type(), Some(BlockType::Free));
2649 assert_eq!(blocks[4].block_type(), Some(BlockType::StringReference));
2650 assert_all_free(blocks.into_iter().skip(5));
2651
2652 {
2654 let mut state = core_state.try_lock().expect("lock state");
2655 assert!(state.free_value(child_block_index).is_ok());
2656 }
2657 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2658 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2659 assert_all_free(blocks.into_iter().skip(1));
2660 }
2661
2662 #[fuchsia::test]
2663 fn test_with_header_lock() {
2664 let state = get_state(4096);
2665 state.with_current_header(|header| {
2667 assert_eq!(header.generation_count(), 0);
2668 });
2669
2670 let mut lock_guard = state.try_lock().expect("lock state");
2672 assert!(lock_guard.header().is_locked());
2673 assert_eq!(lock_guard.header().generation_count(), 1);
2674 let _ = lock_guard.create_node("test", 0.into()).unwrap();
2676 let _ = lock_guard.create_node("test2", 2.into()).unwrap();
2677 assert_eq!(lock_guard.header().generation_count(), 1);
2678
2679 drop(lock_guard);
2681 state.with_current_header(|header| {
2682 assert_eq!(header.generation_count(), 2);
2683 assert!(!header.is_locked());
2684 });
2685 }
2686
2687 #[fuchsia::test]
2688 async fn test_link() {
2689 let state = get_state(4096);
2691 let block_index = {
2692 let mut state_guard = state.try_lock().expect("lock state");
2693 let block_index = state_guard
2694 .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2695 async move {
2696 let inspector = Inspector::default();
2697 inspector.root().record_uint("a", 1);
2698 Ok(inspector)
2699 }
2700 .boxed()
2701 })
2702 .unwrap();
2703
2704 assert!(state_guard.callbacks().get("link-name-0").is_some());
2706 let callback = state_guard.callbacks().get("link-name-0").unwrap();
2707 match callback().await {
2708 Ok(inspector) => {
2709 let hierarchy =
2710 PartialNodeHierarchy::try_from(Snapshot::try_from(&inspector).unwrap())
2711 .unwrap();
2712 assert_data_tree!(hierarchy, root: {
2713 a: 1u64,
2714 });
2715 }
2716 Err(_) => unreachable!("we never return errors in the callback"),
2717 }
2718
2719 let block = state_guard.get_block::<Link>(block_index);
2721 assert_eq!(block.block_type(), Some(BlockType::LinkValue));
2722 assert_eq!(*block.index(), 2);
2723 assert_eq!(*block.parent_index(), 0);
2724 assert_eq!(*block.name_index(), 4);
2725 assert_eq!(*block.content_index(), 6);
2726 assert_eq!(block.link_node_disposition(), Some(LinkNodeDisposition::Inline));
2727
2728 let name_block = state_guard.get_block::<StringRef>(block.name_index());
2730 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2731 assert_eq!(name_block.total_length(), 9);
2732 assert_eq!(state_guard.load_string(name_block.index()).unwrap(), "link-name");
2733
2734 let content_block = state_guard.get_block::<StringRef>(block.content_index());
2736 assert_eq!(content_block.block_type(), Some(BlockType::StringReference));
2737 assert_eq!(content_block.total_length(), 11);
2738 assert_eq!(state_guard.load_string(content_block.index()).unwrap(), "link-name-0");
2739 block_index
2740 };
2741
2742 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2744 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2745 assert_eq!(blocks.len(), 10);
2746 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2747 assert_eq!(blocks[1].block_type(), Some(BlockType::LinkValue));
2748 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2749 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2750 assert_eq!(blocks[4].block_type(), Some(BlockType::StringReference));
2751 assert_all_free(blocks.into_iter().skip(5));
2752
2753 {
2755 let mut state_guard = state.try_lock().expect("lock state");
2756 assert!(state_guard.free_lazy_node(block_index).is_ok());
2757
2758 assert!(state_guard.callbacks().get("link-name-0").is_none());
2760 }
2761 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2762 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2763 assert_all_free(blocks.into_iter().skip(1));
2764
2765 let mut state_guard = state.try_lock().expect("lock state");
2767 state_guard
2768 .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2769 async move { Ok(Inspector::default()) }.boxed()
2770 })
2771 .unwrap();
2772 let content_block = state_guard.get_block::<StringRef>(6.into());
2773 assert_eq!(state_guard.load_string(content_block.index()).unwrap(), "link-name-1");
2774 }
2775
2776 #[fuchsia::test]
2777 fn free_lazy_node_test() {
2778 let state = get_state(4096);
2779 let (lazy_index, _int_with_magic_name_index) = {
2780 let mut state_guard = state.try_lock().expect("lock state");
2781 let lazy_index = state_guard
2782 .create_lazy_node("lk", 0.into(), LinkNodeDisposition::Inline, || {
2783 async move { Ok(Inspector::default()) }.boxed()
2784 })
2785 .unwrap();
2786
2787 let magic_link_name = "lk-0";
2788 let int_with_magic_name_index =
2789 state_guard.create_int_metric(magic_link_name, 0, BlockIndex::from(0)).unwrap();
2790
2791 (lazy_index, int_with_magic_name_index)
2792 };
2793
2794 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2795 let mut blocks = snapshot.scan();
2796 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Header));
2797
2798 let block = blocks.next().and_then(|b| b.cast::<Link>()).unwrap();
2799 assert_eq!(block.block_type(), Some(BlockType::LinkValue));
2800 assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk");
2801 assert_eq!(state.try_lock().unwrap().load_string(block.content_index()).unwrap(), "lk-0");
2802 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2803 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2804 let block = blocks.next().and_then(|b| b.cast::<Int>()).unwrap();
2805 assert_eq!(block.block_type(), Some(BlockType::IntValue));
2806 assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk-0");
2807 assert_all_free(blocks);
2808
2809 state.try_lock().unwrap().free_lazy_node(lazy_index).unwrap();
2810
2811 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2812 let mut blocks = snapshot.scan();
2813
2814 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Header));
2815 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Free));
2816 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2817 let block = blocks.next().and_then(|b| b.cast::<Int>()).unwrap();
2818 assert_eq!(block.block_type(), Some(BlockType::IntValue));
2819 assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk-0");
2820 assert_all_free(blocks);
2821 }
2822
2823 #[fuchsia::test]
2824 async fn stats() {
2825 let state = get_state(3 * 4096);
2827 let mut state_guard = state.try_lock().expect("lock state");
2828 let _block1 = state_guard
2829 .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2830 async move {
2831 let inspector = Inspector::default();
2832 inspector.root().record_uint("a", 1);
2833 Ok(inspector)
2834 }
2835 .boxed()
2836 })
2837 .unwrap();
2838 let _block2 = state_guard.create_uint_metric("test", 3, 0.into()).unwrap();
2839 assert_eq!(
2840 state_guard.stats(),
2841 Stats {
2842 total_dynamic_children: 1,
2843 maximum_size: 3 * 4096,
2844 current_size: 4096,
2845 allocated_blocks: 6, deallocated_blocks: 0,
2848 failed_allocations: 0,
2849 }
2850 )
2851 }
2852
2853 #[fuchsia::test]
2854 fn transaction_locking() {
2855 let state = get_state(4096);
2856 state.with_current_header(|header| {
2858 assert_eq!(header.generation_count(), 0);
2859 });
2860
2861 state.begin_transaction();
2863 state.with_current_header(|header| {
2864 assert_eq!(header.generation_count(), 1);
2865 assert!(header.is_locked());
2866 });
2867
2868 let mut lock_guard1 = state.try_lock().expect("lock state");
2870 assert_eq!(lock_guard1.inner_lock.transaction_count, 1);
2871 assert_eq!(lock_guard1.header().generation_count(), 1);
2872 assert!(lock_guard1.header().is_locked());
2873 let _ = lock_guard1.create_node("test", 0.into());
2874 assert_eq!(lock_guard1.inner_lock.transaction_count, 1);
2875 assert_eq!(lock_guard1.header().generation_count(), 1);
2876
2877 drop(lock_guard1);
2879 state.with_current_header(|header| {
2880 assert_eq!(header.generation_count(), 1);
2881 assert!(header.is_locked());
2882 });
2883
2884 state.end_transaction();
2886
2887 state.with_current_header(|header| {
2888 assert_eq!(header.generation_count(), 2);
2889 assert!(!header.is_locked());
2890 });
2891
2892 let lock_guard2 = state.try_lock().expect("lock state");
2894 assert!(lock_guard2.header().is_locked());
2895 assert_eq!(lock_guard2.header().generation_count(), 3);
2896 assert_eq!(lock_guard2.inner_lock.transaction_count, 0);
2897 }
2898
2899 #[fuchsia::test]
2900 async fn update_header_vmo_size() {
2901 let core_state = get_state(3 * 4096);
2902 core_state.get_block(BlockIndex::HEADER, |header: &Block<_, Header>| {
2903 assert_eq!(header.vmo_size(), Ok(Some(4096)));
2904 });
2905 let block1_index = {
2906 let mut state = core_state.try_lock().expect("lock state");
2907
2908 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2909 let data = chars.iter().cycle().take(6000).collect::<String>();
2910 let block_index =
2911 state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2912 assert_eq!(state.header().vmo_size(), Ok(Some(2 * 4096)));
2913
2914 block_index
2915 };
2916
2917 let block2_index = {
2918 let mut state = core_state.try_lock().expect("lock state");
2919
2920 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2921 let data = chars.iter().cycle().take(3000).collect::<String>();
2922 let block_index =
2923 state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2924 assert_eq!(state.header().vmo_size(), Ok(Some(3 * 4096)));
2925
2926 block_index
2927 };
2928 {
2930 let mut state = core_state.try_lock().expect("lock state");
2931 assert!(state.free_string_or_bytes_buffer_property(block1_index).is_ok());
2932 assert!(state.free_string_or_bytes_buffer_property(block2_index).is_ok());
2933 }
2934 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2935 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2936 assert_all_free(blocks.into_iter().skip(1));
2937 }
2938
2939 #[fuchsia::test]
2940 fn test_buffer_property_on_overflow_set() {
2941 let core_state = get_state(4096);
2942 let block_index = {
2943 let mut state = core_state.try_lock().expect("lock state");
2944
2945 let block_index =
2947 state.create_buffer_property("test", b"test-property", 0.into()).unwrap();
2948
2949 for _ in 10..(4096 / constants::MIN_ORDER_SIZE).try_into().unwrap() {
2951 state.inner_lock.heap.allocate_block(constants::MIN_ORDER_SIZE).unwrap();
2952 }
2953
2954 let values = [b'a'; 8096];
2956 assert!(state.set_buffer_property(block_index, &values).is_err());
2957
2958 let block = state.get_block::<Buffer>(block_index);
2961 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2962 assert_eq!(*block.index(), 2);
2963 assert_eq!(*block.parent_index(), 0);
2964 assert_eq!(*block.name_index(), 3);
2965 assert_eq!(block.total_length(), 0);
2966 assert_eq!(*block.extent_index(), 0);
2967 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2968
2969 block_index
2970 };
2971
2972 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2974 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2975 assert_eq!(blocks.len(), 251);
2976 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2977 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2978 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2979 assert_all_free_or_reserved(blocks.into_iter().skip(3));
2980
2981 {
2982 let mut state = core_state.try_lock().expect("lock state");
2983 assert_matches!(state.free_string_or_bytes_buffer_property(block_index), Ok(()));
2985 }
2986 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2987 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2988 assert_all_free_or_reserved(blocks.into_iter().skip(1));
2989 }
2990
2991 #[fuchsia::test]
2992 fn test_string_property_on_overflow_set() {
2993 let core_state = get_state(4096);
2994 {
2995 let mut state = core_state.try_lock().expect("lock state");
2996
2997 let block_index = state.create_string("test", "test-property", 0.into()).unwrap();
2999
3000 for _ in 10..(4096 / constants::MIN_ORDER_SIZE).try_into().unwrap() {
3002 state.inner_lock.heap.allocate_block(constants::MIN_ORDER_SIZE).unwrap();
3003 }
3004
3005 let values = ["a"].into_iter().cycle().take(5000).collect::<String>();
3008 assert!(state.set_string_property(block_index, values).is_err());
3009 let block = state.get_block::<Buffer>(block_index);
3010 assert_eq!(*block.index(), 2);
3011 assert_eq!(*block.parent_index(), 0);
3012 assert_eq!(*block.name_index(), 3);
3013
3014 assert_eq!(
3016 state.load_string(BlockIndex::from(*block.extent_index())).unwrap(),
3017 "test-property"
3018 );
3019
3020 assert!(state.create_int_metric("foo", 1, 0.into()).is_ok());
3022 assert!(state.create_int_metric("bar", 1, 0.into()).is_ok());
3023 };
3024
3025 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
3026 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
3027 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
3028 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
3029 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
3030 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
3031 assert_eq!(blocks[250].block_type(), Some(BlockType::IntValue));
3032 assert_eq!(blocks[251].block_type(), Some(BlockType::StringReference));
3033 assert_eq!(blocks[252].block_type(), Some(BlockType::IntValue));
3034 assert_eq!(blocks[253].block_type(), Some(BlockType::StringReference));
3035 assert_all_free_or_reserved(blocks.into_iter().skip(4).rev().skip(4));
3036 }
3037
3038 #[fuchsia::test]
3039 fn test_reparent_tombstone_leak() {
3040 use inspect_format::Free;
3041
3042 let core_state = get_state(4096);
3043 let mut state = core_state.try_lock().expect("lock state");
3044
3045 let parent_index = state.create_node("parent", 0.into()).unwrap();
3046 let child_index = state.create_node("child", parent_index).unwrap();
3047 let new_parent_index = state.create_node("new_parent", 0.into()).unwrap();
3048
3049 assert_eq!(state.get_block::<Node>(parent_index).child_count(), 1);
3051
3052 state.free_value(parent_index).unwrap();
3054 assert_eq!(
3055 state.get_block::<Tombstone>(parent_index).block_type(),
3056 Some(BlockType::Tombstone)
3057 );
3058
3059 state.reparent(child_index, new_parent_index).unwrap();
3063
3064 let parent_block = state.get_block::<Free>(parent_index);
3067 assert_eq!(parent_block.block_type(), Some(BlockType::Free));
3068 }
3069
3070 #[fuchsia::test]
3071 fn test_allocate_reserved_value_overflow_leak() {
3072 use inspect_format::HeaderFields;
3073 use inspect_format::constants::MAX_REFERENCE_COUNT;
3074
3075 let core_state = get_state(4096);
3076 let mut state = core_state.try_lock().expect("lock state");
3077
3078 let parent_index = state.create_node("foo", 0.into()).unwrap();
3080 let node_block = state.get_block::<Node>(parent_index);
3081 let name_index = node_block.name_index();
3082
3083 {
3085 let mut name_block = state.get_block_mut::<StringRef>(name_index);
3086 HeaderFields::set_string_reference_count(&mut name_block, MAX_REFERENCE_COUNT);
3087 assert_eq!(MAX_REFERENCE_COUNT, HeaderFields::string_reference_count(&name_block));
3088 }
3089
3090 let stats_before = state.stats();
3092
3093 let result = state.create_node("foo", parent_index);
3096 assert!(result.is_ok());
3097 {
3098 let name_block = state.get_block_mut::<StringRef>(name_index);
3099 assert_eq!(MAX_REFERENCE_COUNT, HeaderFields::string_reference_count(&name_block));
3100 }
3101
3102 let stats_after = state.stats();
3104
3105 let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3106 let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3107 assert_eq!(active_after, active_before + 1);
3109 }
3110
3111 #[fuchsia::test]
3112 fn test_set_array_string_slot_release_failure_leak() {
3113 use inspect_format::HeaderFields;
3114
3115 let core_state = get_state(4096);
3116 let mut state = core_state.try_lock().expect("lock state");
3117
3118 let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
3119 state.set_array_string_slot(array_index, 0, "foo").unwrap();
3120
3121 let foo_index =
3122 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(0).unwrap();
3123
3124 {
3126 let mut foo_block = state.get_block_mut::<StringRef>(foo_index);
3127 HeaderFields::set_string_reference_count(&mut foo_block, 0);
3128 }
3129
3130 let stats_before = state.stats();
3131 let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3132
3133 let result = state.set_array_string_slot(array_index, 0, "bar");
3137 assert!(result.is_err());
3138
3139 let stats_after = state.stats();
3140 let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3141
3142 assert_eq!(active_after, active_before);
3144 }
3145
3146 #[fuchsia::test]
3147 fn test_allocate_link_cleanup_failure() {
3148 let core_state = get_state(4096);
3149 {
3150 let mut state = core_state.try_lock().expect("lock state");
3151
3152 let mut nodes = vec![];
3155 while let Ok(idx) = state.create_node("n", 0.into()) {
3156 nodes.push(idx);
3158 }
3159
3160 state.free_value(nodes.pop().unwrap()).unwrap();
3163
3164 let result = state.create_lazy_node("n", 0.into(), LinkNodeDisposition::Inline, || {
3176 async move { Ok(Inspector::default()) }.boxed()
3177 });
3178
3179 assert!(result.is_err());
3180 }
3181
3182 core_state.with_current_header(|header| {
3184 assert_eq!(header.magic_number(), constants::HEADER_MAGIC_NUMBER);
3185 assert_eq!(header.version(), constants::HEADER_VERSION_NUMBER);
3186 });
3187 }
3188
3189 #[fuchsia::test]
3190 fn test_get_or_create_string_reference_payload_failure_leak() {
3191 let core_state = get_state(4096);
3192 let mut state = core_state.try_lock().expect("lock state");
3193
3194 let mut allocated_blocks = vec![];
3197 for size in &[32, 64, 128, 256, 512, 1024] {
3198 allocated_blocks.push(state.inner_lock.heap.allocate_block(*size).unwrap());
3199 }
3200
3201 let stats_before = state.stats();
3202
3203 let result = {
3211 let mut txn = Txn::new(&mut state.inner_lock);
3212 txn.get_or_create_string_reference("a".repeat(2040))
3213 };
3214 assert!(result.is_err());
3215
3216 let stats_after = state.stats();
3218 let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3219 let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3220 assert_eq!(active_after, active_before);
3221
3222 for block in allocated_blocks {
3224 state.inner_lock.heap.free_block(block).unwrap();
3225 }
3226 }
3227
3228 #[fuchsia::test]
3229 fn test_reparent_to_self_tombstone() {
3230 use inspect_format::Free;
3231
3232 let core_state = get_state(4096);
3233 let mut state = core_state.try_lock().expect("lock state");
3234
3235 let parent_index = state.create_node("parent", 0.into()).unwrap();
3236 let child_index = state.create_node("child", parent_index).unwrap();
3237
3238 state.free_value(parent_index).unwrap();
3240 assert_eq!(
3241 state.get_block::<Tombstone>(parent_index).block_type(),
3242 Some(BlockType::Tombstone)
3243 );
3244
3245 state.reparent(child_index, parent_index).unwrap();
3247
3248 let parent_block = state.get_block::<Tombstone>(parent_index);
3250 assert_eq!(parent_block.block_type(), Some(BlockType::Tombstone));
3251
3252 state.free_value(child_index).unwrap();
3254
3255 let parent_block = state.get_block::<Free>(parent_index);
3257 assert_eq!(parent_block.block_type(), Some(BlockType::Free));
3258 }
3259
3260 #[fuchsia::test]
3261 fn test_uncommitted_txn_drop_no_panic() {
3262 let core_state = get_state(4096);
3263 let mut state = core_state.try_lock().expect("lock state");
3264
3265 let stats_before = state.stats();
3266 {
3267 let mut txn = Txn::new(&mut state.inner_lock);
3268 let _block_index = txn.allocate_block(16).unwrap();
3269 }
3271 let stats_after = state.stats();
3272 let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3273 let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3274 assert_eq!(active_after, active_before);
3275 }
3276
3277 #[fuchsia::test]
3278 fn test_txn_rollback() {
3279 let core_state = get_state(4096);
3280 let mut state = core_state.try_lock().expect("lock state");
3281
3282 let parent_index = state.create_node("parent", 0.into()).unwrap();
3284 let parent_node = state.get_block::<Node>(parent_index);
3285 assert_eq!(parent_node.child_count(), 0);
3286
3287 let stats_before = state.stats();
3288
3289 {
3291 let mut txn = Txn::new(&mut state.inner_lock);
3292 let (child_index, name_index) = txn
3293 .allocate_reserved_value("child", parent_index, constants::MIN_ORDER_SIZE)
3294 .unwrap();
3295
3296 txn.block_mut::<Reserved>(child_index).become_node(name_index, parent_index);
3297
3298 let parent_node = txn.state.heap.container.block_at_unchecked::<Node>(parent_index);
3300 assert_eq!(parent_node.child_count(), 1);
3301
3302 }
3304
3305 let parent_node = state.get_block::<Node>(parent_index);
3307 assert_eq!(parent_node.child_count(), 0);
3308
3309 let stats_after = state.stats();
3311 let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3312 let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3313 assert_eq!(active_after, active_before);
3314 }
3315}