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