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