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