Skip to main content

fuchsia_inspect/writer/
state.rs

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