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
649impl InnerState {
650    /// Creates a new inner state that performs all operations on the heap.
651    pub fn new(
652        heap: Heap<Container>,
653        storage: Arc<<Container as BlockContainer>::ShareableData>,
654    ) -> Self {
655        Self {
656            heap,
657            storage,
658            next_unique_link_id: AtomicU64::new(0),
659            callbacks: HashMap::new(),
660            transaction_count: 0,
661            string_reference_block_indexes: HashMap::new(),
662            block_index_string_references: HashMap::new(),
663        }
664    }
665
666    #[inline]
667    fn header_mut(&mut self) -> Block<&mut Container, inspect_format::Header> {
668        self.heap.container.block_at_unchecked_mut(BlockIndex::HEADER)
669    }
670
671    fn lock_header(&mut self) {
672        if self.transaction_count == 0 {
673            self.header_mut().lock();
674        }
675        self.transaction_count += 1;
676    }
677
678    fn unlock_header(&mut self) {
679        self.transaction_count -= 1;
680        if self.transaction_count == 0 {
681            self.header_mut().unlock();
682        }
683    }
684
685    /// Allocate a NODE block with the given |name| and |parent_index|.
686    fn create_node<'a>(
687        &mut self,
688        name: impl Into<Cow<'a, str>>,
689        parent_index: BlockIndex,
690    ) -> Result<BlockIndex, Error> {
691        let pending =
692            self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
693        pending
694            .state
695            .heap
696            .container
697            .block_at_unchecked_mut::<Reserved>(pending.block_index)
698            .become_node(pending.name_block_index, parent_index);
699        Ok(pending.commit())
700    }
701
702    /// Allocate a LINK block with the given |name| and |parent_index| and keep track
703    /// of the callback that will fill it.
704    fn create_lazy_node<'a, F>(
705        &mut self,
706        name: impl Into<Cow<'a, str>>,
707        parent_index: BlockIndex,
708        disposition: LinkNodeDisposition,
709        callback: F,
710    ) -> Result<BlockIndex, Error>
711    where
712        F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
713    {
714        let name = name.into();
715        let content = self.unique_link_name(&name);
716        let link = self.allocate_link(name, &content, disposition, parent_index)?;
717        self.callbacks.insert(content, Arc::from(callback));
718        Ok(link)
719    }
720
721    /// Frees a LINK block at the given |index|.
722    fn free_lazy_node(&mut self, index: BlockIndex) -> Result<(), Error> {
723        let content_block_index =
724            self.heap.container.block_at_unchecked::<Link>(index).content_index();
725        let content_block_type = self.heap.container.block_at(content_block_index).block_type();
726        let content = self.load_key_string(content_block_index)?;
727        self.delete_value(index)?;
728        // Free the name or string reference block used for content.
729        match content_block_type {
730            Some(BlockType::StringReference) => {
731                self.release_string_reference(content_block_index)?;
732            }
733            _ => {
734                self.heap.free_block(content_block_index).expect("Failed to free block");
735            }
736        }
737
738        self.callbacks.remove(content.as_str());
739        Ok(())
740    }
741
742    fn unique_link_name(&mut self, prefix: &str) -> String {
743        let id = self.next_unique_link_id.fetch_add(1, Ordering::Relaxed);
744        format!("{prefix}-{id}")
745    }
746
747    pub(crate) fn allocate_link<'a, 'b>(
748        &mut self,
749        name: impl Into<Cow<'a, str>>,
750        content: impl Into<Cow<'b, str>>,
751        disposition: LinkNodeDisposition,
752        parent_index: BlockIndex,
753    ) -> Result<BlockIndex, Error> {
754        let pending =
755            self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
756        let content_block_index = pending.state.get_or_create_string_reference(content)?;
757        pending
758            .state
759            .heap
760            .container
761            .block_at_unchecked_mut::<StringRef>(content_block_index)
762            .increment_ref_count()?;
763        pending
764            .state
765            .heap
766            .container
767            .block_at_unchecked_mut::<Reserved>(pending.block_index)
768            .become_link(pending.name_block_index, parent_index, content_block_index, disposition);
769        Ok(pending.commit())
770    }
771
772    /// Free a *_VALUE block at the given |index|.
773    fn free_value(&mut self, index: BlockIndex) -> Result<(), Error> {
774        self.delete_value(index)?;
775        Ok(())
776    }
777
778    /// Allocate a BUFFER_VALUE block with the given |name|, |value| and |parent_index|.
779    fn create_buffer_property<'a>(
780        &mut self,
781        name: impl Into<Cow<'a, str>>,
782        value: &[u8],
783        parent_index: BlockIndex,
784    ) -> Result<BlockIndex, Error> {
785        let pending =
786            self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
787        pending
788            .state
789            .heap
790            .container
791            .block_at_unchecked_mut::<Reserved>(pending.block_index)
792            .become_property(pending.name_block_index, parent_index, PropertyFormat::Bytes);
793        pending.state.inner_set_buffer_property_value(pending.block_index, value)?;
794        Ok(pending.commit())
795    }
796
797    /// Allocate a BUFFER_VALUE block with the given |name|, |value| and |parent_index|, where
798    /// |value| is stored as a STRING_REFERENCE.
799    fn create_string<'a, 'b>(
800        &mut self,
801        name: impl Into<Cow<'a, str>>,
802        value: impl Into<Cow<'b, str>>,
803        parent_index: BlockIndex,
804    ) -> Result<BlockIndex, Error> {
805        let pending =
806            self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
807        pending
808            .state
809            .heap
810            .container
811            .block_at_unchecked_mut::<Reserved>(pending.block_index)
812            .become_property(
813                pending.name_block_index,
814                parent_index,
815                PropertyFormat::StringReference,
816            );
817
818        let value_block_index = pending.state.get_or_create_string_reference(value)?;
819        pending
820            .state
821            .heap
822            .container
823            .block_at_unchecked_mut::<StringRef>(value_block_index)
824            .increment_ref_count()?;
825
826        let mut block =
827            pending.state.heap.container.block_at_unchecked_mut::<Buffer>(pending.block_index);
828        block.set_extent_index(value_block_index);
829        block.set_total_length(0);
830
831        Ok(pending.commit())
832    }
833
834    /// Get or allocate a STRING_REFERENCE block with the given |value|.
835    /// When a new string reference is created, its reference count is set to zero.
836    fn get_or_create_string_reference<'a>(
837        &mut self,
838        value: impl Into<Cow<'a, str>>,
839    ) -> Result<BlockIndex, Error> {
840        let value = value.into();
841        match self.string_reference_block_indexes.get(&value) {
842            Some(index) => Ok(*index),
843            None => {
844                let block_index = self.heap.allocate_block(utils::block_size_for_payload(
845                    value.len() + constants::STRING_REFERENCE_TOTAL_LENGTH_BYTES,
846                ))?;
847                self.heap
848                    .container
849                    .block_at_unchecked_mut::<Reserved>(block_index)
850                    .become_string_reference();
851                self.write_string_reference_payload(block_index, &value)?;
852                let owned_value = Arc::new(value.into_owned().into());
853                self.string_reference_block_indexes.insert(Arc::clone(&owned_value), block_index);
854                self.block_index_string_references.insert(block_index, owned_value);
855                Ok(block_index)
856            }
857        }
858    }
859
860    /// Given a string, write the canonical value out, allocating as needed.
861    fn write_string_reference_payload(
862        &mut self,
863        block_index: BlockIndex,
864        value: &str,
865    ) -> Result<(), Error> {
866        let value_bytes = value.as_bytes();
867        let (head_extent, bytes_written) = {
868            let inlined = self.inline_string_reference(block_index, value.as_bytes());
869            if inlined < value.len() {
870                let (head, in_extents) = self.write_extents(&value_bytes[inlined..])?;
871                (head, inlined + in_extents)
872            } else {
873                (BlockIndex::EMPTY, inlined)
874            }
875        };
876        let mut block = self.heap.container.block_at_unchecked_mut::<StringRef>(block_index);
877        block.set_next_index(head_extent);
878        block.set_total_length(bytes_written.try_into().unwrap_or(u32::MAX));
879        Ok(())
880    }
881
882    /// Given a string, write the portion that can be inlined to the given block.
883    /// Return the number of bytes written.
884    fn inline_string_reference(&mut self, block_index: BlockIndex, value: &[u8]) -> usize {
885        self.heap.container.block_at_unchecked_mut::<StringRef>(block_index).write_inline(value)
886    }
887
888    /// Decrement the reference count on the block and free it if the count is 0.
889    /// This is the function to call if you want to give up your hold on a StringReference.
890    fn release_string_reference(&mut self, block_index: BlockIndex) -> Result<(), Error> {
891        self.heap
892            .container
893            .block_at_unchecked_mut::<StringRef>(block_index)
894            .decrement_ref_count()?;
895        self.maybe_free_string_reference(block_index)
896    }
897
898    /// Free a STRING_REFERENCE if the count is 0. This should not be
899    /// directly called outside of tests.
900    fn maybe_free_string_reference(&mut self, block_index: BlockIndex) -> Result<(), Error> {
901        let block = self.heap.container.block_at_unchecked::<StringRef>(block_index);
902        if block.reference_count() != 0 {
903            return Ok(());
904        }
905        let first_extent = block.next_extent();
906        self.heap.free_block(block_index)?;
907        let str_ref = self.block_index_string_references.remove(&block_index).expect("blk idx key");
908        self.string_reference_block_indexes.remove(&str_ref);
909
910        if first_extent == BlockIndex::EMPTY {
911            return Ok(());
912        }
913        self.free_extents(first_extent)
914    }
915
916    fn load_key_string(&self, index: BlockIndex) -> Result<String, Error> {
917        let block = self.heap.container.block_at(index);
918        match block.block_type() {
919            Some(BlockType::StringReference) => {
920                self.read_string_reference(block.cast::<StringRef>().unwrap())
921            }
922            Some(BlockType::Name) => block
923                .cast::<Name>()
924                .unwrap()
925                .contents()
926                .map(|s| s.to_string())
927                .map_err(|_| Error::NameNotUtf8),
928            _ => Err(Error::InvalidBlockTypeNumber(index, block.block_type_raw())),
929        }
930    }
931
932    /// Read a StringReference
933    fn read_string_reference(&self, block: Block<&Container, StringRef>) -> Result<String, Error> {
934        let mut content = block.inline_data()?.to_vec();
935        let mut next = block.next_extent();
936        while next != BlockIndex::EMPTY {
937            let next_block = self.heap.container.block_at_unchecked::<Extent>(next);
938            content.extend_from_slice(next_block.contents()?);
939            next = next_block.next_extent();
940        }
941
942        content.truncate(block.total_length());
943        String::from_utf8(content).ok().ok_or(Error::NameNotUtf8)
944    }
945
946    /// Free a BUFFER_VALUE block.
947    fn free_string_or_bytes_buffer_property(&mut self, index: BlockIndex) -> Result<(), Error> {
948        let (format, data_index) = {
949            let block = self.heap.container.block_at_unchecked::<Buffer>(index);
950            (block.format(), block.extent_index())
951        };
952        match format {
953            Some(PropertyFormat::String) | Some(PropertyFormat::Bytes) => {
954                self.free_extents(data_index)?;
955            }
956            Some(PropertyFormat::StringReference) => {
957                self.release_string_reference(data_index)?;
958            }
959            _ => {
960                return Err(Error::VmoFormat(FormatError::InvalidBufferFormat(
961                    self.heap.container.block_at_unchecked(index).format_raw(),
962                )));
963            }
964        }
965
966        self.delete_value(index)?;
967        Ok(())
968    }
969
970    /// Set the |value| of a String BUFFER_VALUE block.
971    fn set_string_property<'a>(
972        &mut self,
973        block_index: BlockIndex,
974        value: impl Into<Cow<'a, str>>,
975    ) -> Result<(), Error> {
976        self.inner_set_string_property_value(block_index, value)?;
977        Ok(())
978    }
979
980    /// Set the |value| of a String BUFFER_VALUE block.
981    fn set_buffer_property(&mut self, block_index: BlockIndex, value: &[u8]) -> Result<(), Error> {
982        self.inner_set_buffer_property_value(block_index, value)?;
983        Ok(())
984    }
985
986    fn check_lineage(
987        &self,
988        being_reparented: BlockIndex,
989        new_parent: BlockIndex,
990    ) -> Result<(), Error> {
991        // you cannot adopt the root node
992        if being_reparented == BlockIndex::ROOT {
993            return Err(Error::AdoptAncestor);
994        }
995
996        let mut being_checked = new_parent;
997        while being_checked != BlockIndex::ROOT {
998            if being_checked == being_reparented {
999                return Err(Error::AdoptAncestor);
1000            }
1001            // Note: all values share the parent_index in the same position, so we can just assume
1002            // we have ANY_VALUE here, so just using a Node.
1003            being_checked =
1004                self.heap.container.block_at_unchecked::<Node>(being_checked).parent_index();
1005        }
1006
1007        Ok(())
1008    }
1009
1010    fn reparent(
1011        &mut self,
1012        being_reparented: BlockIndex,
1013        new_parent: BlockIndex,
1014    ) -> Result<(), Error> {
1015        self.check_lineage(being_reparented, new_parent)?;
1016        let original_parent_idx =
1017            self.heap.container.block_at_unchecked::<Node>(being_reparented).parent_index();
1018        if original_parent_idx != BlockIndex::ROOT {
1019            let mut original_parent_block =
1020                self.heap.container.block_at_unchecked_mut::<Node>(original_parent_idx);
1021            let child_count = original_parent_block.child_count() - 1;
1022            original_parent_block.set_child_count(child_count);
1023        }
1024
1025        self.heap.container.block_at_unchecked_mut::<Node>(being_reparented).set_parent(new_parent);
1026
1027        if new_parent != BlockIndex::ROOT {
1028            let mut new_parent_block =
1029                self.heap.container.block_at_unchecked_mut::<Node>(new_parent);
1030            let child_count = new_parent_block.child_count() + 1;
1031            new_parent_block.set_child_count(child_count);
1032        }
1033
1034        Ok(())
1035    }
1036
1037    fn create_bool<'a>(
1038        &mut self,
1039        name: impl Into<Cow<'a, str>>,
1040        value: bool,
1041        parent_index: BlockIndex,
1042    ) -> Result<BlockIndex, Error> {
1043        let pending =
1044            self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
1045        pending
1046            .state
1047            .heap
1048            .container
1049            .block_at_unchecked_mut::<Reserved>(pending.block_index)
1050            .become_bool_value(value, pending.name_block_index, parent_index);
1051        Ok(pending.commit())
1052    }
1053
1054    fn set_bool(&mut self, block_index: BlockIndex, value: bool) {
1055        let mut block = self.heap.container.block_at_unchecked_mut::<Bool>(block_index);
1056        block.set(value);
1057    }
1058
1059    metric_fns!(int, i64, Int);
1060    metric_fns!(uint, u64, Uint);
1061    metric_fns!(double, f64, Double);
1062
1063    arithmetic_array_fns!(int, i64, IntValue, Int);
1064    arithmetic_array_fns!(uint, u64, UintValue, Uint);
1065    arithmetic_array_fns!(double, f64, DoubleValue, Double);
1066
1067    fn create_string_array<'a>(
1068        &mut self,
1069        name: impl Into<Cow<'a, str>>,
1070        slots: usize,
1071        parent_index: BlockIndex,
1072    ) -> Result<BlockIndex, Error> {
1073        let block_size = slots * StringRef::array_entry_type_size() + constants::MIN_ORDER_SIZE;
1074        if block_size > constants::MAX_ORDER_SIZE {
1075            return Err(Error::BlockSizeTooBig(block_size));
1076        }
1077        let pending = self.allocate_reserved_value(name, parent_index, block_size)?;
1078        pending
1079            .state
1080            .heap
1081            .container
1082            .block_at_unchecked_mut::<Reserved>(pending.block_index)
1083            .become_array_value::<StringRef>(
1084            slots,
1085            ArrayFormat::Default,
1086            pending.name_block_index,
1087            parent_index,
1088        )?;
1089        Ok(pending.commit())
1090    }
1091
1092    fn get_array_size(&self, block_index: BlockIndex) -> usize {
1093        let block = self.heap.container.block_at_unchecked::<Array<Unknown>>(block_index);
1094        block.slots()
1095    }
1096
1097    fn set_array_string_slot<'a>(
1098        &mut self,
1099        block_index: BlockIndex,
1100        slot_index: usize,
1101        value: impl Into<Cow<'a, str>>,
1102    ) -> Result<(), Error> {
1103        if self.heap.container.block_at_unchecked_mut::<Array<StringRef>>(block_index).slots()
1104            <= slot_index
1105        {
1106            return Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(slot_index)));
1107        }
1108
1109        let value = value.into();
1110        let reference_index = if !value.is_empty() {
1111            let reference_index = self.get_or_create_string_reference(value)?;
1112            self.heap
1113                .container
1114                .block_at_unchecked_mut::<StringRef>(reference_index)
1115                .increment_ref_count()?;
1116            reference_index
1117        } else {
1118            BlockIndex::EMPTY
1119        };
1120
1121        let existing_index = self
1122            .heap
1123            .container
1124            .block_at_unchecked::<Array<StringRef>>(block_index)
1125            .get_string_index_at(slot_index)
1126            .ok_or(Error::InvalidArrayIndex(slot_index))?;
1127        if existing_index != BlockIndex::EMPTY {
1128            self.release_string_reference(existing_index)?;
1129        }
1130
1131        self.heap
1132            .container
1133            .block_at_unchecked_mut::<Array<StringRef>>(block_index)
1134            .set_string_slot(slot_index, reference_index);
1135        Ok(())
1136    }
1137
1138    /// Sets all slots of the array at the given index to zero.
1139    /// Does appropriate deallocation on string references in payload.
1140    fn clear_array(
1141        &mut self,
1142        block_index: BlockIndex,
1143        start_slot_index: usize,
1144    ) -> Result<(), Error> {
1145        // TODO(https://fxbug.dev/392965471): this should be cleaner. Technically we can know
1146        // statically what kind of block we are dealing with.
1147        let block = self.heap.container.block_at_unchecked_mut::<Array<Unknown>>(block_index);
1148        match block.entry_type() {
1149            Some(value) if value.is_numeric_value() => {
1150                self.heap
1151                    .container
1152                    .block_at_unchecked_mut::<Array<Unknown>>(block_index)
1153                    .clear(start_slot_index);
1154            }
1155            Some(BlockType::StringReference) => {
1156                let array_slots = block.slots();
1157                for i in start_slot_index..array_slots {
1158                    let index = {
1159                        let mut block = self
1160                            .heap
1161                            .container
1162                            .block_at_unchecked_mut::<Array<StringRef>>(block_index);
1163                        let index =
1164                            block.get_string_index_at(i).ok_or(Error::InvalidArrayIndex(i))?;
1165                        if index == BlockIndex::EMPTY {
1166                            continue;
1167                        }
1168                        block.set_string_slot(i, BlockIndex::EMPTY);
1169                        index
1170                    };
1171                    self.release_string_reference(index)?;
1172                }
1173            }
1174
1175            _ => return Err(Error::InvalidArrayType(block_index)),
1176        }
1177
1178        Ok(())
1179    }
1180
1181    fn allocate_reserved_value<'a, 'b>(
1182        &'b mut self,
1183        name: impl Into<Cow<'a, str>>,
1184        parent_index: BlockIndex,
1185        block_size: usize,
1186    ) -> Result<PendingValueBlock<'b>, Error> {
1187        let block_index = self.heap.allocate_block(block_size)?;
1188        let name_block_index = match self.get_or_create_string_reference(name) {
1189            Ok(b_index) => {
1190                self.heap
1191                    .container
1192                    .block_at_unchecked_mut::<StringRef>(b_index)
1193                    .increment_ref_count()?;
1194                b_index
1195            }
1196            Err(err) => {
1197                self.heap.free_block(block_index)?;
1198                return Err(err);
1199            }
1200        };
1201
1202        let result = {
1203            // Safety: NodeValues and Tombstones always have child_count
1204            let mut parent_block = self.heap.container.block_at_unchecked_mut::<Node>(parent_index);
1205            let parent_block_type = parent_block.block_type();
1206            match parent_block_type {
1207                Some(BlockType::NodeValue) | Some(BlockType::Tombstone) => {
1208                    parent_block.set_child_count(parent_block.child_count() + 1);
1209                    Ok(())
1210                }
1211                Some(BlockType::Header) => Ok(()),
1212                _ => Err(Error::InvalidBlockType(parent_index, parent_block.block_type_raw())),
1213            }
1214        };
1215        match result {
1216            Ok(()) => {
1217                Ok(PendingValueBlock { state: self, block_index, name_block_index, parent_index })
1218            }
1219            Err(err) => {
1220                self.release_string_reference(name_block_index)?;
1221                self.heap.free_block(block_index)?;
1222                Err(err)
1223            }
1224        }
1225    }
1226
1227    fn delete_value(&mut self, block_index: BlockIndex) -> Result<(), Error> {
1228        // For our purposes here, we just need "ANY_VALUE". Using "node".
1229        let block = self.heap.container.block_at_unchecked::<Node>(block_index);
1230        let parent_index = block.parent_index();
1231        let name_index = block.name_index();
1232
1233        // Decrement parent child count.
1234        if parent_index != BlockIndex::ROOT {
1235            let parent = self.heap.container.block_at_mut(parent_index);
1236            match parent.block_type() {
1237                Some(BlockType::Tombstone) => {
1238                    let mut parent = parent.cast::<Tombstone>().unwrap();
1239                    let child_count = parent.child_count() - 1;
1240                    if child_count == 0 {
1241                        self.heap.free_block(parent_index).expect("Failed to free block");
1242                    } else {
1243                        parent.set_child_count(child_count);
1244                    }
1245                }
1246                Some(BlockType::NodeValue) => {
1247                    let mut parent = parent.cast::<Node>().unwrap();
1248                    let child_count = parent.child_count() - 1;
1249                    parent.set_child_count(child_count);
1250                }
1251                other => {
1252                    unreachable!(
1253                        "the parent of any value is either tombstone or node. Saw: {other:?}"
1254                    );
1255                }
1256            }
1257        }
1258
1259        // Free the name block.
1260        match self.heap.container.block_at(name_index).block_type() {
1261            Some(BlockType::StringReference) => {
1262                self.release_string_reference(name_index)?;
1263            }
1264            _ => self.heap.free_block(name_index).expect("Failed to free block"),
1265        }
1266
1267        // If the block is a NODE and has children, make it a TOMBSTONE so that
1268        // it's freed when the last of its children is freed. Otherwise, free it.
1269        let block = self.heap.container.block_at_mut(block_index);
1270        match block.cast::<Node>() {
1271            Some(block) if block.child_count() != 0 => {
1272                let _ = block.become_tombstone();
1273            }
1274            _ => {
1275                self.heap.free_block(block_index)?;
1276            }
1277        }
1278        Ok(())
1279    }
1280
1281    fn inner_set_string_property_value<'a>(
1282        &mut self,
1283        block_index: BlockIndex,
1284        value: impl Into<Cow<'a, str>>,
1285    ) -> Result<(), Error> {
1286        let old_string_ref_idx =
1287            self.heap.container.block_at_unchecked::<Buffer>(block_index).extent_index();
1288        let new_string_ref_idx = self.get_or_create_string_reference(value.into())?;
1289        self.heap
1290            .container
1291            .block_at_unchecked_mut::<StringRef>(new_string_ref_idx)
1292            .increment_ref_count()?;
1293
1294        self.heap
1295            .container
1296            .block_at_unchecked_mut::<Buffer>(block_index)
1297            .set_extent_index(new_string_ref_idx);
1298        self.release_string_reference(old_string_ref_idx)?;
1299        Ok(())
1300    }
1301
1302    fn inner_set_buffer_property_value(
1303        &mut self,
1304        block_index: BlockIndex,
1305        value: &[u8],
1306    ) -> Result<(), Error> {
1307        self.free_extents(
1308            self.heap.container.block_at_unchecked::<Buffer>(block_index).extent_index(),
1309        )?;
1310        let (result, (extent_index, written)) = match self.write_extents(value) {
1311            Ok((e, w)) => (Ok(()), (e, w)),
1312            Err(err) => (Err(err), (BlockIndex::ROOT, 0)),
1313        };
1314        let mut block = self.heap.container.block_at_unchecked_mut::<Buffer>(block_index);
1315        block.set_total_length(written.try_into().unwrap_or(u32::MAX));
1316        block.set_extent_index(extent_index);
1317        result
1318    }
1319
1320    fn free_extents(&mut self, head_extent_index: BlockIndex) -> Result<(), Error> {
1321        let mut index = head_extent_index;
1322        while index != BlockIndex::ROOT {
1323            let next_index = self.heap.container.block_at_unchecked::<Extent>(index).next_extent();
1324            self.heap.free_block(index)?;
1325            index = next_index;
1326        }
1327        Ok(())
1328    }
1329
1330    fn write_extents(&mut self, value: &[u8]) -> Result<(BlockIndex, usize), Error> {
1331        if value.is_empty() {
1332            // Invalid index
1333            return Ok((BlockIndex::ROOT, 0));
1334        }
1335        let mut offset = 0;
1336        let total_size = value.len();
1337        let head_extent_index =
1338            self.heap.allocate_block(utils::block_size_for_payload(total_size - offset))?;
1339        let mut extent_block_index = head_extent_index;
1340        while offset < total_size {
1341            let bytes_written = {
1342                let mut extent_block = self
1343                    .heap
1344                    .container
1345                    .block_at_unchecked_mut::<Reserved>(extent_block_index)
1346                    .become_extent(BlockIndex::EMPTY);
1347                extent_block.set_contents(&value[offset..])
1348            };
1349            offset += bytes_written;
1350            if offset < total_size {
1351                let Ok(block_index) =
1352                    self.heap.allocate_block(utils::block_size_for_payload(total_size - offset))
1353                else {
1354                    // If we fail to allocate, just take what was written already and bail.
1355                    return Ok((head_extent_index, offset));
1356                };
1357                self.heap
1358                    .container
1359                    .block_at_unchecked_mut::<Extent>(extent_block_index)
1360                    .set_next_index(block_index);
1361                extent_block_index = block_index;
1362            }
1363        }
1364        Ok((head_extent_index, offset))
1365    }
1366}
1367
1368#[cfg(test)]
1369mod tests {
1370    use super::*;
1371    use crate::reader::PartialNodeHierarchy;
1372    use crate::reader::snapshot::{BackingBuffer, ScannedBlock, Snapshot};
1373    use crate::writer::testing_utils::get_state;
1374    use assert_matches::assert_matches;
1375    use diagnostics_assertions::assert_data_tree;
1376    use futures::prelude::*;
1377    use inspect_format::Header;
1378
1379    #[track_caller]
1380    fn assert_all_free_or_reserved<'a>(
1381        blocks: impl Iterator<Item = Block<&'a BackingBuffer, Unknown>>,
1382    ) {
1383        let mut errors = vec![];
1384        for block in blocks {
1385            if block.block_type() != Some(BlockType::Free)
1386                && block.block_type() != Some(BlockType::Reserved)
1387            {
1388                errors.push(format!(
1389                    "block at {} is {:?}, expected {} or {}",
1390                    block.index(),
1391                    block.block_type(),
1392                    BlockType::Free,
1393                    BlockType::Reserved,
1394                ));
1395            }
1396        }
1397
1398        if !errors.is_empty() {
1399            panic!("{errors:#?}");
1400        }
1401    }
1402
1403    #[track_caller]
1404    fn assert_all_free<'a>(blocks: impl Iterator<Item = Block<&'a BackingBuffer, Unknown>>) {
1405        let mut errors = vec![];
1406        for block in blocks {
1407            if block.block_type() != Some(BlockType::Free) {
1408                errors.push(format!(
1409                    "block at {} is {:?}, expected {}",
1410                    block.index(),
1411                    block.block_type(),
1412                    BlockType::Free
1413                ));
1414            }
1415        }
1416
1417        if !errors.is_empty() {
1418            panic!("{errors:#?}");
1419        }
1420    }
1421
1422    #[fuchsia::test]
1423    fn test_create() {
1424        let state = get_state(4096);
1425        let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
1426        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1427        assert_eq!(blocks.len(), 8);
1428        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1429        assert_all_free(blocks.into_iter().skip(1));
1430    }
1431
1432    #[fuchsia::test]
1433    fn test_load_string() {
1434        let outer = get_state(4096);
1435        let mut state = outer.try_lock().expect("lock state");
1436        let block_index = state.inner_lock.get_or_create_string_reference("a value").unwrap();
1437        assert_eq!(state.load_string(block_index).unwrap(), "a value");
1438    }
1439
1440    #[fuchsia::test]
1441    fn test_check_lineage() {
1442        let core_state = get_state(4096);
1443        let mut state = core_state.try_lock().expect("lock state");
1444        let parent_index = state.create_node("", 0.into()).unwrap();
1445        let child_index = state.create_node("", parent_index).unwrap();
1446        let uncle_index = state.create_node("", 0.into()).unwrap();
1447
1448        state.inner_lock.check_lineage(parent_index, child_index).unwrap_err();
1449        state.inner_lock.check_lineage(0.into(), child_index).unwrap_err();
1450        state.inner_lock.check_lineage(child_index, uncle_index).unwrap();
1451    }
1452
1453    #[fuchsia::test]
1454    fn test_reparent() {
1455        let core_state = get_state(4096);
1456        let mut state = core_state.try_lock().expect("lock state");
1457
1458        let a_index = state.create_node("a", 0.into()).unwrap();
1459        let b_index = state.create_node("b", 0.into()).unwrap();
1460
1461        let a = state.get_block::<Node>(a_index);
1462        let b = state.get_block::<Node>(b_index);
1463        assert_eq!(*a.parent_index(), 0);
1464        assert_eq!(*b.parent_index(), 0);
1465
1466        assert_eq!(a.child_count(), 0);
1467        assert_eq!(b.child_count(), 0);
1468
1469        state.reparent(b_index, a_index).unwrap();
1470
1471        let a = state.get_block::<Node>(a_index);
1472        let b = state.get_block::<Node>(b_index);
1473        assert_eq!(*a.parent_index(), 0);
1474        assert_eq!(b.parent_index(), a.index());
1475
1476        assert_eq!(a.child_count(), 1);
1477        assert_eq!(b.child_count(), 0);
1478
1479        let c_index = state.create_node("c", a_index).unwrap();
1480
1481        let a = state.get_block::<Node>(a_index);
1482        let b = state.get_block::<Node>(b_index);
1483        let c = state.get_block::<Node>(c_index);
1484        assert_eq!(*a.parent_index(), 0);
1485        assert_eq!(b.parent_index(), a.index());
1486        assert_eq!(c.parent_index(), a.index());
1487
1488        assert_eq!(a.child_count(), 2);
1489        assert_eq!(b.child_count(), 0);
1490        assert_eq!(c.child_count(), 0);
1491
1492        state.reparent(c_index, b_index).unwrap();
1493
1494        let a = state.get_block::<Node>(a_index);
1495        let b = state.get_block::<Node>(b_index);
1496        let c = state.get_block::<Node>(c_index);
1497        assert_eq!(*a.parent_index(), 0);
1498        assert_eq!(b.parent_index(), a_index);
1499        assert_eq!(c.parent_index(), b_index);
1500
1501        assert_eq!(a.child_count(), 1);
1502        assert_eq!(b.child_count(), 1);
1503        assert_eq!(c.child_count(), 0);
1504    }
1505
1506    #[fuchsia::test]
1507    fn test_node() {
1508        let core_state = get_state(4096);
1509        let block_index = {
1510            let mut state = core_state.try_lock().expect("lock state");
1511
1512            // Create a node value and verify its fields
1513            let block_index = state.create_node("test-node", 0.into()).unwrap();
1514            let block = state.get_block::<Node>(block_index);
1515            assert_eq!(block.block_type(), Some(BlockType::NodeValue));
1516            assert_eq!(*block.index(), 2);
1517            assert_eq!(block.child_count(), 0);
1518            assert_eq!(*block.name_index(), 4);
1519            assert_eq!(*block.parent_index(), 0);
1520
1521            // Verify name block.
1522            let name_block = state.get_block::<StringRef>(block.name_index());
1523            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1524            assert_eq!(name_block.total_length(), 9);
1525            assert_eq!(name_block.order(), 1);
1526            assert_eq!(state.load_string(name_block.index()).unwrap(), "test-node");
1527            block_index
1528        };
1529
1530        // Verify blocks.
1531        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1532        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1533        assert_eq!(blocks.len(), 10);
1534        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1535        assert_eq!(blocks[1].block_type(), Some(BlockType::NodeValue));
1536        assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
1537        assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
1538        assert_all_free(blocks.into_iter().skip(4));
1539
1540        {
1541            let mut state = core_state.try_lock().expect("lock state");
1542            let child_block_index = state.create_node("child1", block_index).unwrap();
1543            assert_eq!(state.get_block::<Node>(block_index).child_count(), 1);
1544
1545            // Create a child of the child and verify child counts.
1546            let child11_block_index = state.create_node("child1-1", child_block_index).unwrap();
1547            {
1548                assert_eq!(state.get_block::<Node>(child11_block_index).child_count(), 0);
1549                assert_eq!(state.get_block::<Node>(child_block_index).child_count(), 1);
1550                assert_eq!(state.get_block::<Node>(block_index).child_count(), 1);
1551            }
1552
1553            assert!(state.free_value(child11_block_index).is_ok());
1554            {
1555                let child_block = state.get_block::<Node>(child_block_index);
1556                assert_eq!(child_block.child_count(), 0);
1557            }
1558
1559            // Add a couple more children to the block and verify count.
1560            let child_block2_index = state.create_node("child2", block_index).unwrap();
1561            let child_block3_index = state.create_node("child3", block_index).unwrap();
1562            assert_eq!(state.get_block::<Node>(block_index).child_count(), 3);
1563
1564            // Free children and verify count.
1565            assert!(state.free_value(child_block_index).is_ok());
1566            assert!(state.free_value(child_block2_index).is_ok());
1567            assert!(state.free_value(child_block3_index).is_ok());
1568            assert_eq!(state.get_block::<Node>(block_index).child_count(), 0);
1569
1570            // Free node.
1571            assert!(state.free_value(block_index).is_ok());
1572        }
1573
1574        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1575        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1576        assert_all_free(blocks.into_iter().skip(1));
1577    }
1578
1579    #[fuchsia::test]
1580    fn test_int_metric() {
1581        let core_state = get_state(4096);
1582        let block_index = {
1583            let mut state = core_state.try_lock().expect("lock state");
1584            let block_index = state.create_int_metric("test", 3, 0.into()).unwrap();
1585            let block = state.get_block::<Int>(block_index);
1586            assert_eq!(block.block_type(), Some(BlockType::IntValue));
1587            assert_eq!(*block.index(), 2);
1588            assert_eq!(block.value(), 3);
1589            assert_eq!(*block.name_index(), 3);
1590            assert_eq!(*block.parent_index(), 0);
1591
1592            let name_block = state.get_block::<StringRef>(block.name_index());
1593            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1594            assert_eq!(name_block.total_length(), 4);
1595            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1596            block_index
1597        };
1598
1599        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1600        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1601        assert_eq!(blocks.len(), 9);
1602        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1603        assert_eq!(blocks[1].block_type(), Some(BlockType::IntValue));
1604        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1605        assert_all_free(blocks.into_iter().skip(3));
1606
1607        {
1608            let mut state = core_state.try_lock().expect("lock state");
1609            assert_eq!(state.add_int_metric(block_index, 10), 13);
1610            assert_eq!(state.get_block::<Int>(block_index).value(), 13);
1611
1612            assert_eq!(state.subtract_int_metric(block_index, 5), 8);
1613            assert_eq!(state.get_block::<Int>(block_index).value(), 8);
1614
1615            state.set_int_metric(block_index, -6);
1616            assert_eq!(state.get_block::<Int>(block_index).value(), -6);
1617
1618            assert_eq!(state.subtract_int_metric(block_index, i64::MAX), i64::MIN);
1619            assert_eq!(state.get_block::<Int>(block_index).value(), i64::MIN);
1620            state.set_int_metric(block_index, i64::MAX);
1621
1622            assert_eq!(state.add_int_metric(block_index, 2), i64::MAX);
1623            assert_eq!(state.get_block::<Int>(block_index).value(), i64::MAX);
1624
1625            // Free metric.
1626            assert!(state.free_value(block_index).is_ok());
1627        }
1628
1629        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1630        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1631        assert_all_free(blocks.into_iter().skip(1));
1632    }
1633
1634    #[fuchsia::test]
1635    fn test_uint_metric() {
1636        let core_state = get_state(4096);
1637
1638        // Creates with value
1639        let block_index = {
1640            let mut state = core_state.try_lock().expect("try lock");
1641            let block_index = state.create_uint_metric("test", 3, 0.into()).unwrap();
1642            let block = state.get_block::<Uint>(block_index);
1643            assert_eq!(block.block_type(), Some(BlockType::UintValue));
1644            assert_eq!(*block.index(), 2);
1645            assert_eq!(block.value(), 3);
1646            assert_eq!(*block.name_index(), 3);
1647            assert_eq!(*block.parent_index(), 0);
1648
1649            let name_block = state.get_block::<StringRef>(block.name_index());
1650            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1651            assert_eq!(name_block.total_length(), 4);
1652            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1653            block_index
1654        };
1655
1656        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1657        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1658        assert_eq!(blocks.len(), 9);
1659        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1660        assert_eq!(blocks[1].block_type(), Some(BlockType::UintValue));
1661        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1662        assert_all_free(blocks.into_iter().skip(3));
1663
1664        {
1665            let mut state = core_state.try_lock().expect("try lock");
1666            assert_eq!(state.add_uint_metric(block_index, 10), 13);
1667            assert_eq!(state.get_block::<Uint>(block_index).value(), 13);
1668
1669            assert_eq!(state.subtract_uint_metric(block_index, 5), 8);
1670            assert_eq!(state.get_block::<Uint>(block_index).value(), 8);
1671
1672            state.set_uint_metric(block_index, 0);
1673            assert_eq!(state.get_block::<Uint>(block_index).value(), 0);
1674
1675            assert_eq!(state.subtract_uint_metric(block_index, u64::MAX), 0);
1676            assert_eq!(state.get_block::<Uint>(block_index).value(), 0);
1677
1678            state.set_uint_metric(block_index, 3);
1679            assert_eq!(state.add_uint_metric(block_index, u64::MAX), u64::MAX);
1680            assert_eq!(state.get_block::<Uint>(block_index).value(), u64::MAX);
1681
1682            // Free metric.
1683            assert!(state.free_value(block_index).is_ok());
1684        }
1685
1686        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1687        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1688        assert_all_free(blocks.into_iter().skip(1));
1689    }
1690
1691    #[fuchsia::test]
1692    fn test_double_metric() {
1693        let core_state = get_state(4096);
1694
1695        // Creates with value
1696        let block_index = {
1697            let mut state = core_state.try_lock().expect("lock state");
1698            let block_index = state.create_double_metric("test", 3.0, 0.into()).unwrap();
1699            let block = state.get_block::<Double>(block_index);
1700            assert_eq!(block.block_type(), Some(BlockType::DoubleValue));
1701            assert_eq!(*block.index(), 2);
1702            assert_eq!(block.value(), 3.0);
1703            assert_eq!(*block.name_index(), 3);
1704            assert_eq!(*block.parent_index(), 0);
1705
1706            let name_block = state.get_block::<StringRef>(block.name_index());
1707            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1708            assert_eq!(name_block.total_length(), 4);
1709            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1710            block_index
1711        };
1712
1713        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1714        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1715        assert_eq!(blocks.len(), 9);
1716        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1717        assert_eq!(blocks[1].block_type(), Some(BlockType::DoubleValue));
1718        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1719        assert_all_free(blocks.into_iter().skip(3));
1720
1721        {
1722            let mut state = core_state.try_lock().expect("lock state");
1723            assert_eq!(state.add_double_metric(block_index, 10.5), 13.5);
1724            assert_eq!(state.get_block::<Double>(block_index).value(), 13.5);
1725
1726            assert_eq!(state.subtract_double_metric(block_index, 5.1), 8.4);
1727            assert_eq!(state.get_block::<Double>(block_index).value(), 8.4);
1728
1729            state.set_double_metric(block_index, -6.0);
1730            assert_eq!(state.get_block::<Double>(block_index).value(), -6.0);
1731
1732            // Free metric.
1733            assert!(state.free_value(block_index).is_ok());
1734        }
1735
1736        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1737        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1738        assert_all_free(blocks.into_iter().skip(1));
1739    }
1740
1741    #[fuchsia::test]
1742    fn test_create_buffer_property_cleanup_on_failure() {
1743        // this implementation detail is important for the test below to be valid
1744        assert_eq!(constants::MAX_ORDER_SIZE, 2048);
1745
1746        let core_state = get_state(5121); // large enough to fit to max size blocks plus 1024
1747        let mut state = core_state.try_lock().expect("lock state");
1748        // allocate a max size block and one extent
1749        let name = (0..3000).map(|_| " ").collect::<String>();
1750        // allocate a max size property + at least one extent
1751        // the extent won't fit into the VMO, causing allocation failure when the property
1752        // is set
1753        let payload = [0u8; 4096]; // won't fit into vmo
1754
1755        // fails because the property is too big, but, allocates the name and should clean it up
1756        assert!(state.create_buffer_property(name, &payload, 0.into()).is_err());
1757
1758        drop(state);
1759
1760        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1761        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1762
1763        // if cleanup happened correctly, the name + extent and property + extent have been freed
1764        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1765        assert_all_free(blocks.into_iter().skip(1));
1766    }
1767
1768    #[fuchsia::test]
1769    fn test_string_reference_allocations() {
1770        let core_state = get_state(4096); // allocates HEADER
1771        {
1772            let mut state = core_state.try_lock().expect("lock state");
1773            let sf = "a reference-counted canonical name";
1774            assert_eq!(state.stats().allocated_blocks, 1);
1775
1776            let mut collected = vec![];
1777            for _ in 0..100 {
1778                collected.push(state.create_node(sf, 0.into()).unwrap());
1779            }
1780
1781            let acsf = Arc::new(Cow::Borrowed(sf));
1782            assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1783
1784            assert_eq!(state.stats().allocated_blocks, 102);
1785            let block = state.get_block::<Node>(collected[0]);
1786            let sf_block = state.get_block::<StringRef>(block.name_index());
1787            assert_eq!(sf_block.reference_count(), 100);
1788
1789            collected.into_iter().for_each(|b| {
1790                assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1791                assert!(state.free_value(b).is_ok())
1792            });
1793
1794            assert!(!state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1795
1796            let node_index = state.create_node(sf, 0.into()).unwrap();
1797            assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1798            assert!(state.free_value(node_index).is_ok());
1799            assert!(!state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1800        }
1801
1802        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1803        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1804        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1805        assert_all_free(blocks.into_iter().skip(1));
1806    }
1807
1808    #[fuchsia::test]
1809    fn test_string_reference_data() {
1810        let core_state = get_state(4096); // allocates HEADER
1811        let mut state = core_state.try_lock().expect("lock state");
1812
1813        // 4 bytes (4 ASCII characters in UTF-8) will fit inlined with a minimum block size
1814        let block_index = state.inner_lock.get_or_create_string_reference("abcd").unwrap();
1815        let block = state.get_block::<StringRef>(block_index);
1816        assert_eq!(block.block_type(), Some(BlockType::StringReference));
1817        assert_eq!(block.order(), 0);
1818        assert_eq!(state.stats().allocated_blocks, 2);
1819        assert_eq!(state.stats().deallocated_blocks, 0);
1820        assert_eq!(block.reference_count(), 0);
1821        assert_eq!(block.total_length(), 4);
1822        assert_eq!(*block.next_extent(), 0);
1823        assert_eq!(block.order(), 0);
1824        assert_eq!(state.load_string(block.index()).unwrap(), "abcd");
1825
1826        state.inner_lock.maybe_free_string_reference(block_index).unwrap();
1827        assert_eq!(state.stats().deallocated_blocks, 1);
1828
1829        let block_index = state.inner_lock.get_or_create_string_reference("longer").unwrap();
1830        let block = state.get_block::<StringRef>(block_index);
1831        assert_eq!(block.block_type(), Some(BlockType::StringReference));
1832        assert_eq!(block.order(), 1);
1833        assert_eq!(block.reference_count(), 0);
1834        assert_eq!(block.total_length(), 6);
1835        assert_eq!(state.stats().allocated_blocks, 3);
1836        assert_eq!(state.stats().deallocated_blocks, 1);
1837        assert_eq!(state.load_string(block.index()).unwrap(), "longer");
1838
1839        let idx = block.next_extent();
1840        assert_eq!(*idx, 0);
1841
1842        state.inner_lock.maybe_free_string_reference(block_index).unwrap();
1843        assert_eq!(state.stats().deallocated_blocks, 2);
1844
1845        let block_index = state.inner_lock.get_or_create_string_reference("longer").unwrap();
1846        let mut block = state.get_block_mut::<StringRef>(block_index);
1847        assert_eq!(block.order(), 1);
1848        block.increment_ref_count().unwrap();
1849        // not an error to try and free
1850        assert!(state.inner_lock.maybe_free_string_reference(block_index).is_ok());
1851
1852        let mut block = state.get_block_mut(block_index);
1853        block.decrement_ref_count().unwrap();
1854        state.inner_lock.maybe_free_string_reference(block_index).unwrap();
1855    }
1856
1857    #[fuchsia::test]
1858    fn test_string_reference_format_property() {
1859        let core_state = get_state(4096);
1860        let block_index = {
1861            let mut state = core_state.try_lock().expect("lock state");
1862
1863            // Creates with value
1864            let block_index =
1865                state.create_string("test", "test-property", BlockIndex::from(0)).unwrap();
1866            let block = state.get_block::<Buffer>(block_index);
1867            assert_eq!(block.block_type(), Some(BlockType::BufferValue));
1868            assert_eq!(*block.index(), 2);
1869            assert_eq!(*block.parent_index(), 0);
1870            assert_eq!(*block.name_index(), 3);
1871            assert_eq!(block.total_length(), 0);
1872            assert_eq!(block.format(), Some(PropertyFormat::StringReference));
1873
1874            let name_block = state.get_block::<StringRef>(block.name_index());
1875            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1876            assert_eq!(name_block.total_length(), 4);
1877            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1878
1879            let data_block = state.get_block::<StringRef>(block.extent_index());
1880            assert_eq!(data_block.block_type(), Some(BlockType::StringReference));
1881            assert_eq!(state.load_string(data_block.index()).unwrap(), "test-property");
1882            block_index
1883        };
1884
1885        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1886        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1887        assert_eq!(blocks.len(), 10);
1888        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1889        assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
1890        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1891        assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
1892        assert_all_free(blocks.into_iter().skip(4));
1893
1894        {
1895            let mut state = core_state.try_lock().expect("lock state");
1896            // Free property.
1897            assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
1898        }
1899        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1900        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1901        assert_all_free(blocks.into_iter().skip(1));
1902    }
1903
1904    #[fuchsia::test]
1905    fn test_string_arrays() {
1906        let core_state = get_state(4096);
1907        {
1908            let mut state = core_state.try_lock().expect("lock state");
1909            let array_index = state.create_string_array("array", 4, 0.into()).unwrap();
1910            assert_eq!(state.set_array_string_slot(array_index, 0, "0"), Ok(()));
1911            assert_eq!(state.set_array_string_slot(array_index, 1, "1"), Ok(()));
1912            assert_eq!(state.set_array_string_slot(array_index, 2, "2"), Ok(()));
1913            assert_eq!(state.set_array_string_slot(array_index, 3, "3"), Ok(()));
1914
1915            // size is 4
1916            assert_matches!(
1917                state.set_array_string_slot(array_index, 4, ""),
1918                Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(4)))
1919            );
1920            assert_matches!(
1921                state.set_array_string_slot(array_index, 5, ""),
1922                Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(5)))
1923            );
1924
1925            for i in 0..4 {
1926                let idx = state
1927                    .get_block::<Array<StringRef>>(array_index)
1928                    .get_string_index_at(i)
1929                    .unwrap();
1930                assert_eq!(i.to_string(), state.load_string(idx).unwrap());
1931            }
1932
1933            assert_eq!(
1934                state.get_block::<Array<StringRef>>(array_index).get_string_index_at(4),
1935                None
1936            );
1937            assert_eq!(
1938                state.get_block::<Array<StringRef>>(array_index).get_string_index_at(5),
1939                None
1940            );
1941
1942            state.clear_array(array_index, 0).unwrap();
1943            state.free_value(array_index).unwrap();
1944        }
1945
1946        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1947        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1948        assert_all_free(blocks.into_iter().skip(1));
1949    }
1950
1951    #[fuchsia::test]
1952    fn update_string_array_value() {
1953        let core_state = get_state(4096);
1954        {
1955            let mut state = core_state.try_lock().expect("lock state");
1956            let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
1957
1958            assert_eq!(state.set_array_string_slot(array_index, 0, "abc"), Ok(()));
1959            assert_eq!(state.set_array_string_slot(array_index, 1, "def"), Ok(()));
1960
1961            assert_eq!(state.set_array_string_slot(array_index, 0, "cba"), Ok(()));
1962            assert_eq!(state.set_array_string_slot(array_index, 1, "fed"), Ok(()));
1963
1964            let cba_index_slot =
1965                state.get_block::<Array<StringRef>>(array_index).get_string_index_at(0).unwrap();
1966            let fed_index_slot =
1967                state.get_block::<Array<StringRef>>(array_index).get_string_index_at(1).unwrap();
1968            assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap());
1969            assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
1970
1971            state.clear_array(array_index, 0).unwrap();
1972            state.free_value(array_index).unwrap();
1973        }
1974
1975        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1976        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1977        blocks[1..].iter().enumerate().for_each(|(i, b)| {
1978            assert!(b.block_type() == Some(BlockType::Free), "index is {}", i + 1);
1979        });
1980    }
1981
1982    #[fuchsia::test]
1983    fn set_string_reference_instances_multiple_times_in_array() {
1984        let core_state = get_state(4096);
1985        {
1986            let mut state = core_state.try_lock().expect("lock state");
1987            let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
1988
1989            let abc = "abc";
1990            let def = "def";
1991            let cba = "cba";
1992            let fed = "fed";
1993
1994            state.set_array_string_slot(array_index, 0, abc).unwrap();
1995            state.set_array_string_slot(array_index, 1, def).unwrap();
1996            state.set_array_string_slot(array_index, 0, abc).unwrap();
1997            state.set_array_string_slot(array_index, 1, def).unwrap();
1998
1999            let abc_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2000            let def_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2001            assert_eq!("abc".to_string(), state.load_string(abc_index_slot).unwrap(),);
2002            assert_eq!("def".to_string(), state.load_string(def_index_slot).unwrap(),);
2003
2004            state.set_array_string_slot(array_index, 0, cba).unwrap();
2005            state.set_array_string_slot(array_index, 1, fed).unwrap();
2006
2007            let cba_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2008            let fed_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2009            assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap(),);
2010            assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2011
2012            state.set_array_string_slot(array_index, 0, abc).unwrap();
2013            state.set_array_string_slot(array_index, 1, def).unwrap();
2014
2015            let abc_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2016            let def_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2017            assert_eq!("abc".to_string(), state.load_string(abc_index_slot).unwrap(),);
2018            assert_eq!("def".to_string(), state.load_string(def_index_slot).unwrap(),);
2019
2020            state.set_array_string_slot(array_index, 0, cba).unwrap();
2021            state.set_array_string_slot(array_index, 1, fed).unwrap();
2022
2023            let cba_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2024            let fed_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2025            assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap(),);
2026            assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2027
2028            state.clear_array(array_index, 0).unwrap();
2029            state.free_value(array_index).unwrap();
2030        }
2031
2032        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2033        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2034        blocks[1..].iter().enumerate().for_each(|(i, b)| {
2035            assert!(b.block_type() == Some(BlockType::Free), "index is {}", i + 1);
2036        });
2037    }
2038
2039    #[fuchsia::test]
2040    fn test_empty_value_string_arrays() {
2041        let core_state = get_state(4096);
2042        {
2043            let mut state = core_state.try_lock().expect("lock state");
2044            let array_index = state.create_string_array("array", 4, 0.into()).unwrap();
2045
2046            state.set_array_string_slot(array_index, 0, "").unwrap();
2047            state.set_array_string_slot(array_index, 1, "").unwrap();
2048            state.set_array_string_slot(array_index, 2, "").unwrap();
2049            state.set_array_string_slot(array_index, 3, "").unwrap();
2050        }
2051
2052        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2053        let state = core_state.try_lock().expect("lock state");
2054
2055        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2056        for b in blocks {
2057            if b.block_type() == Some(BlockType::StringReference)
2058                && state.load_string(b.index()).unwrap() == "array"
2059            {
2060                continue;
2061            }
2062
2063            assert_ne!(
2064                b.block_type(),
2065                Some(BlockType::StringReference),
2066                "Got unexpected StringReference, index: {}, value (wrapped in single quotes): '{:?}'",
2067                b.index(),
2068                b.block_type()
2069            );
2070        }
2071    }
2072
2073    #[fuchsia::test]
2074    fn test_bytevector_property() {
2075        let core_state = get_state(4096);
2076
2077        // Creates with value
2078        let block_index = {
2079            let mut state = core_state.try_lock().expect("lock state");
2080            let block_index =
2081                state.create_buffer_property("test", b"test-property", 0.into()).unwrap();
2082            let block = state.get_block::<Buffer>(block_index);
2083            assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2084            assert_eq!(*block.index(), 2);
2085            assert_eq!(*block.parent_index(), 0);
2086            assert_eq!(*block.name_index(), 3);
2087            assert_eq!(block.total_length(), 13);
2088            assert_eq!(*block.extent_index(), 4);
2089            assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2090
2091            let name_block = state.get_block::<StringRef>(block.name_index());
2092            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2093            assert_eq!(name_block.total_length(), 4);
2094            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2095
2096            let extent_block = state.get_block::<Extent>(4.into());
2097            assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2098            assert_eq!(*extent_block.next_extent(), 0);
2099            assert_eq!(
2100                std::str::from_utf8(extent_block.contents().unwrap()).unwrap(),
2101                "test-property\0\0\0\0\0\0\0\0\0\0\0"
2102            );
2103            block_index
2104        };
2105
2106        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2107        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2108        assert_eq!(blocks.len(), 10);
2109        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2110        assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2111        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2112        assert_eq!(blocks[3].block_type(), Some(BlockType::Extent));
2113        assert_all_free(blocks.into_iter().skip(4));
2114
2115        // Free property.
2116        {
2117            let mut state = core_state.try_lock().expect("lock state");
2118            assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2119        }
2120        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2121        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2122        assert_all_free(blocks.into_iter().skip(1));
2123    }
2124
2125    #[fuchsia::test]
2126    fn test_bool() {
2127        let core_state = get_state(4096);
2128        let block_index = {
2129            let mut state = core_state.try_lock().expect("lock state");
2130
2131            // Creates with value
2132            let block_index = state.create_bool("test", true, 0.into()).unwrap();
2133            let block = state.get_block::<Bool>(block_index);
2134            assert_eq!(block.block_type(), Some(BlockType::BoolValue));
2135            assert_eq!(*block.index(), 2);
2136            assert!(block.value());
2137            assert_eq!(*block.name_index(), 3);
2138            assert_eq!(*block.parent_index(), 0);
2139
2140            let name_block = state.get_block::<StringRef>(block.name_index());
2141            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2142            assert_eq!(name_block.total_length(), 4);
2143            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2144            block_index
2145        };
2146
2147        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2148        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2149        assert_eq!(blocks.len(), 9);
2150        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2151        assert_eq!(blocks[1].block_type(), Some(BlockType::BoolValue));
2152        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2153        assert_all_free(blocks.into_iter().skip(3));
2154
2155        // Free metric.
2156        {
2157            let mut state = core_state.try_lock().expect("lock state");
2158            assert!(state.free_value(block_index).is_ok());
2159        }
2160        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2161        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2162        assert_all_free(blocks.into_iter().skip(1));
2163    }
2164
2165    #[fuchsia::test]
2166    fn test_int_array() {
2167        let core_state = get_state(4096);
2168        let block_index = {
2169            let mut state = core_state.try_lock().expect("lock state");
2170            let block_index =
2171                state.create_int_array("test", 5, ArrayFormat::Default, 0.into()).unwrap();
2172            let block = state.get_block::<Array<Int>>(block_index);
2173            assert_eq!(block.block_type(), Some(BlockType::ArrayValue));
2174            assert_eq!(block.order(), 2);
2175            assert_eq!(*block.index(), 4);
2176            assert_eq!(*block.name_index(), 2);
2177            assert_eq!(*block.parent_index(), 0);
2178            assert_eq!(block.slots(), 5);
2179            assert_eq!(block.format(), Some(ArrayFormat::Default));
2180            assert_eq!(block.entry_type(), Some(BlockType::IntValue));
2181
2182            let name_block = state.get_block::<StringRef>(BlockIndex::from(2));
2183            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2184            assert_eq!(name_block.total_length(), 4);
2185            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2186            for i in 0..5 {
2187                state.set_array_int_slot(block_index, i, 3 * i as i64);
2188            }
2189            for i in 0..5 {
2190                assert_eq!(state.get_block::<Array<Int>>(block_index).get(i), Some(3 * i as i64));
2191            }
2192            block_index
2193        };
2194
2195        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2196        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2197        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2198        assert_eq!(blocks[1].block_type(), Some(BlockType::StringReference));
2199        assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2200        assert_eq!(blocks[3].block_type(), Some(BlockType::ArrayValue));
2201        assert_all_free(blocks.into_iter().skip(4));
2202
2203        // Free the array.
2204        {
2205            let mut state = core_state.try_lock().expect("lock state");
2206            assert!(state.free_value(block_index).is_ok());
2207        }
2208        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2209        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2210        assert_all_free(blocks.into_iter().skip(1));
2211    }
2212
2213    #[fuchsia::test]
2214    fn test_write_extent_overflow() {
2215        const SIZE: usize = constants::MAX_ORDER_SIZE * 2;
2216        const EXPECTED_WRITTEN: usize = constants::MAX_ORDER_SIZE - constants::HEADER_SIZE_BYTES;
2217        const TRIED_TO_WRITE: usize = SIZE + 1;
2218        let core_state = get_state(SIZE);
2219        let (_, written) = core_state
2220            .try_lock()
2221            .unwrap()
2222            .inner_lock
2223            .write_extents(&[4u8; TRIED_TO_WRITE])
2224            .unwrap();
2225        assert_eq!(written, EXPECTED_WRITTEN);
2226    }
2227
2228    #[fuchsia::test]
2229    fn overflow_property() {
2230        const SIZE: usize = constants::MAX_ORDER_SIZE * 2;
2231        const EXPECTED_WRITTEN: usize = constants::MAX_ORDER_SIZE - constants::HEADER_SIZE_BYTES;
2232
2233        let core_state = get_state(SIZE);
2234        let mut state = core_state.try_lock().expect("lock state");
2235
2236        let data = "X".repeat(SIZE * 2);
2237        let block_index = state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2238        let block = state.get_block::<Buffer>(block_index);
2239        assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2240        assert_eq!(*block.index(), 2);
2241        assert_eq!(*block.parent_index(), 0);
2242        assert_eq!(*block.name_index(), 3);
2243        assert_eq!(block.total_length(), EXPECTED_WRITTEN);
2244        assert_eq!(*block.extent_index(), 128);
2245        assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2246
2247        let name_block = state.get_block::<StringRef>(block.name_index());
2248        assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2249        assert_eq!(name_block.total_length(), 4);
2250        assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2251
2252        let extent_block = state.get_block::<Extent>(128.into());
2253        assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2254        assert_eq!(extent_block.order(), 7);
2255        assert_eq!(*extent_block.next_extent(), *BlockIndex::EMPTY);
2256        assert_eq!(
2257            extent_block.contents().unwrap(),
2258            data.chars().take(EXPECTED_WRITTEN).map(|c| c as u8).collect::<Vec<u8>>()
2259        );
2260    }
2261
2262    #[fuchsia::test]
2263    fn test_multi_extent_property() {
2264        let core_state = get_state(10000);
2265        let block_index = {
2266            let mut state = core_state.try_lock().expect("lock state");
2267
2268            let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2269            let data = chars.iter().cycle().take(6000).collect::<String>();
2270            let block_index =
2271                state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2272            let block = state.get_block::<Buffer>(block_index);
2273            assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2274            assert_eq!(*block.index(), 2);
2275            assert_eq!(*block.parent_index(), 0);
2276            assert_eq!(*block.name_index(), 3);
2277            assert_eq!(block.total_length(), 6000);
2278            assert_eq!(*block.extent_index(), 128);
2279            assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2280
2281            let name_block = state.get_block::<StringRef>(block.name_index());
2282            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2283            assert_eq!(name_block.total_length(), 4);
2284            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2285
2286            let extent_block = state.get_block::<Extent>(128.into());
2287            assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2288            assert_eq!(extent_block.order(), 7);
2289            assert_eq!(*extent_block.next_extent(), 256);
2290            assert_eq!(
2291                extent_block.contents().unwrap(),
2292                chars.iter().cycle().take(2040).map(|&c| c as u8).collect::<Vec<u8>>()
2293            );
2294
2295            let extent_block = state.get_block::<Extent>(256.into());
2296            assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2297            assert_eq!(extent_block.order(), 7);
2298            assert_eq!(*extent_block.next_extent(), 384);
2299            assert_eq!(
2300                extent_block.contents().unwrap(),
2301                chars.iter().cycle().skip(2040).take(2040).map(|&c| c as u8).collect::<Vec<u8>>()
2302            );
2303
2304            let extent_block = state.get_block::<Extent>(384.into());
2305            assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2306            assert_eq!(extent_block.order(), 7);
2307            assert_eq!(*extent_block.next_extent(), 0);
2308            assert_eq!(
2309                extent_block.contents().unwrap()[..1920],
2310                chars.iter().cycle().skip(4080).take(1920).map(|&c| c as u8).collect::<Vec<u8>>()[..]
2311            );
2312            assert_eq!(extent_block.contents().unwrap()[1920..], [0u8; 120][..]);
2313            block_index
2314        };
2315
2316        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2317        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2318        assert_eq!(blocks.len(), 11);
2319        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2320        assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2321        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2322        assert_eq!(blocks[8].block_type(), Some(BlockType::Extent));
2323        assert_eq!(blocks[9].block_type(), Some(BlockType::Extent));
2324        assert_eq!(blocks[10].block_type(), Some(BlockType::Extent));
2325        assert_all_free(blocks.into_iter().skip(3).take(5));
2326        // Free property.
2327        {
2328            let mut state = core_state.try_lock().expect("lock state");
2329            assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2330        }
2331        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2332        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2333        assert_all_free(blocks.into_iter().skip(1));
2334    }
2335
2336    #[fuchsia::test]
2337    fn test_freeing_string_references() {
2338        let core_state = get_state(4096);
2339        {
2340            let mut state = core_state.try_lock().expect("lock state");
2341            assert_eq!(state.stats().allocated_blocks, 1);
2342
2343            let block0_index = state.create_node("abcd123456789", 0.into()).unwrap();
2344            let block0_name_index = {
2345                let block0_name_index = state.get_block::<Node>(block0_index).name_index();
2346                let block0_name = state.get_block::<StringRef>(block0_name_index);
2347                assert_eq!(block0_name.order(), 1);
2348                block0_name_index
2349            };
2350            assert_eq!(state.stats().allocated_blocks, 3);
2351
2352            let block1_index = state.inner_lock.get_or_create_string_reference("abcd").unwrap();
2353            assert_eq!(state.stats().allocated_blocks, 4);
2354            assert_eq!(state.get_block::<StringRef>(block1_index).order(), 0);
2355
2356            // no allocation!
2357            let block2_index =
2358                state.inner_lock.get_or_create_string_reference("abcd123456789").unwrap();
2359            assert_eq!(state.get_block::<StringRef>(block2_index).order(), 1);
2360            assert_eq!(block0_name_index, block2_index);
2361            assert_eq!(state.stats().allocated_blocks, 4);
2362
2363            let block3_index = state.create_node("abcd12345678", 0.into()).unwrap();
2364            let block3 = state.get_block::<Node>(block3_index);
2365            let block3_name = state.get_block::<StringRef>(block3.name_index());
2366            assert_eq!(block3_name.order(), 1);
2367            assert_eq!(block3.order(), 0);
2368            assert_eq!(state.stats().allocated_blocks, 6);
2369
2370            let mut long_name = "".to_string();
2371            for _ in 0..3000 {
2372                long_name += " ";
2373            }
2374
2375            let block4_index = state.create_node(long_name, 0.into()).unwrap();
2376            let block4 = state.get_block::<Node>(block4_index);
2377            let block4_name = state.get_block::<StringRef>(block4.name_index());
2378            assert_eq!(block4_name.order(), 7);
2379            assert!(*block4_name.next_extent() != 0);
2380            assert_eq!(state.stats().allocated_blocks, 9);
2381
2382            assert!(state.inner_lock.maybe_free_string_reference(block1_index).is_ok());
2383            assert_eq!(state.stats().deallocated_blocks, 1);
2384            assert!(state.inner_lock.maybe_free_string_reference(block2_index).is_ok());
2385            // no deallocation because same ref as block2 is held in block0_name
2386            assert_eq!(state.stats().deallocated_blocks, 1);
2387            assert!(state.free_value(block3_index).is_ok());
2388            assert_eq!(state.stats().deallocated_blocks, 3);
2389            assert!(state.free_value(block4_index).is_ok());
2390            assert_eq!(state.stats().deallocated_blocks, 6);
2391        }
2392
2393        // Current expected layout of VMO:
2394        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2395        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2396
2397        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2398        assert_eq!(blocks[1].block_type(), Some(BlockType::NodeValue));
2399        assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2400        assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2401        assert_all_free(blocks.into_iter().skip(4));
2402    }
2403
2404    #[fuchsia::test]
2405    fn test_tombstone() {
2406        let core_state = get_state(4096);
2407        let child_block_index = {
2408            let mut state = core_state.try_lock().expect("lock state");
2409
2410            // Create a node value and verify its fields
2411            let block_index = state.create_node("root-node", 0.into()).unwrap();
2412            let block_name_as_string_ref =
2413                state.get_block::<StringRef>(state.get_block::<Node>(block_index).name_index());
2414            assert_eq!(block_name_as_string_ref.order(), 1);
2415            assert_eq!(state.stats().allocated_blocks, 3);
2416            assert_eq!(state.stats().deallocated_blocks, 0);
2417
2418            let child_block_index = state.create_node("child-node", block_index).unwrap();
2419            assert_eq!(state.stats().allocated_blocks, 5);
2420            assert_eq!(state.stats().deallocated_blocks, 0);
2421
2422            // Node still has children, so will become a tombstone.
2423            assert!(state.free_value(block_index).is_ok());
2424            assert_eq!(state.stats().allocated_blocks, 5);
2425            assert_eq!(state.stats().deallocated_blocks, 1);
2426            child_block_index
2427        };
2428
2429        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2430        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2431
2432        // Note that the way Extents get allocated means that they aren't necessarily
2433        // put in the buffer where it would seem they should based on the literal order of allocation.
2434        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2435        assert_eq!(blocks[1].block_type(), Some(BlockType::Tombstone));
2436        assert_eq!(blocks[2].block_type(), Some(BlockType::NodeValue));
2437        assert_eq!(blocks[3].block_type(), Some(BlockType::Free));
2438        assert_eq!(blocks[4].block_type(), Some(BlockType::StringReference));
2439        assert_all_free(blocks.into_iter().skip(5));
2440
2441        // Freeing the child, causes all blocks to be freed.
2442        {
2443            let mut state = core_state.try_lock().expect("lock state");
2444            assert!(state.free_value(child_block_index).is_ok());
2445        }
2446        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2447        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2448        assert_all_free(blocks.into_iter().skip(1));
2449    }
2450
2451    #[fuchsia::test]
2452    fn test_with_header_lock() {
2453        let state = get_state(4096);
2454        // Initial generation count is 0
2455        state.with_current_header(|header| {
2456            assert_eq!(header.generation_count(), 0);
2457        });
2458
2459        // Lock the state
2460        let mut lock_guard = state.try_lock().expect("lock state");
2461        assert!(lock_guard.header().is_locked());
2462        assert_eq!(lock_guard.header().generation_count(), 1);
2463        // Operations on the lock guard do not change the generation counter.
2464        let _ = lock_guard.create_node("test", 0.into()).unwrap();
2465        let _ = lock_guard.create_node("test2", 2.into()).unwrap();
2466        assert_eq!(lock_guard.header().generation_count(), 1);
2467
2468        // Dropping the guard releases the lock.
2469        drop(lock_guard);
2470        state.with_current_header(|header| {
2471            assert_eq!(header.generation_count(), 2);
2472            assert!(!header.is_locked());
2473        });
2474    }
2475
2476    #[fuchsia::test]
2477    async fn test_link() {
2478        // Initialize state and create a link block.
2479        let state = get_state(4096);
2480        let block_index = {
2481            let mut state_guard = state.try_lock().expect("lock state");
2482            let block_index = state_guard
2483                .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2484                    async move {
2485                        let inspector = Inspector::default();
2486                        inspector.root().record_uint("a", 1);
2487                        Ok(inspector)
2488                    }
2489                    .boxed()
2490                })
2491                .unwrap();
2492
2493            // Verify the callback was properly saved.
2494            assert!(state_guard.callbacks().get("link-name-0").is_some());
2495            let callback = state_guard.callbacks().get("link-name-0").unwrap();
2496            match callback().await {
2497                Ok(inspector) => {
2498                    let hierarchy =
2499                        PartialNodeHierarchy::try_from(Snapshot::try_from(&inspector).unwrap())
2500                            .unwrap();
2501                    assert_data_tree!(hierarchy, root: {
2502                        a: 1u64,
2503                    });
2504                }
2505                Err(_) => unreachable!("we never return errors in the callback"),
2506            }
2507
2508            // Verify link block.
2509            let block = state_guard.get_block::<Link>(block_index);
2510            assert_eq!(block.block_type(), Some(BlockType::LinkValue));
2511            assert_eq!(*block.index(), 2);
2512            assert_eq!(*block.parent_index(), 0);
2513            assert_eq!(*block.name_index(), 4);
2514            assert_eq!(*block.content_index(), 6);
2515            assert_eq!(block.link_node_disposition(), Some(LinkNodeDisposition::Inline));
2516
2517            // Verify link's name block.
2518            let name_block = state_guard.get_block::<StringRef>(block.name_index());
2519            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2520            assert_eq!(name_block.total_length(), 9);
2521            assert_eq!(state_guard.load_string(name_block.index()).unwrap(), "link-name");
2522
2523            // Verify link's content block.
2524            let content_block = state_guard.get_block::<StringRef>(block.content_index());
2525            assert_eq!(content_block.block_type(), Some(BlockType::StringReference));
2526            assert_eq!(content_block.total_length(), 11);
2527            assert_eq!(state_guard.load_string(content_block.index()).unwrap(), "link-name-0");
2528            block_index
2529        };
2530
2531        // Verify all the VMO blocks.
2532        let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2533        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2534        assert_eq!(blocks.len(), 10);
2535        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2536        assert_eq!(blocks[1].block_type(), Some(BlockType::LinkValue));
2537        assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2538        assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2539        assert_eq!(blocks[4].block_type(), Some(BlockType::StringReference));
2540        assert_all_free(blocks.into_iter().skip(5));
2541
2542        // Free link
2543        {
2544            let mut state_guard = state.try_lock().expect("lock state");
2545            assert!(state_guard.free_lazy_node(block_index).is_ok());
2546
2547            // Verify the callback was cleared on free link.
2548            assert!(state_guard.callbacks().get("link-name-0").is_none());
2549        }
2550        let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2551        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2552        assert_all_free(blocks.into_iter().skip(1));
2553
2554        // Verify adding another link generates a different ID regardless of the params.
2555        let mut state_guard = state.try_lock().expect("lock state");
2556        state_guard
2557            .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2558                async move { Ok(Inspector::default()) }.boxed()
2559            })
2560            .unwrap();
2561        let content_block = state_guard.get_block::<StringRef>(6.into());
2562        assert_eq!(state_guard.load_string(content_block.index()).unwrap(), "link-name-1");
2563    }
2564
2565    #[fuchsia::test]
2566    fn free_lazy_node_test() {
2567        let state = get_state(4096);
2568        let (lazy_index, _int_with_magic_name_index) = {
2569            let mut state_guard = state.try_lock().expect("lock state");
2570            let lazy_index = state_guard
2571                .create_lazy_node("lk", 0.into(), LinkNodeDisposition::Inline, || {
2572                    async move { Ok(Inspector::default()) }.boxed()
2573                })
2574                .unwrap();
2575
2576            let magic_link_name = "lk-0";
2577            let int_with_magic_name_index =
2578                state_guard.create_int_metric(magic_link_name, 0, BlockIndex::from(0)).unwrap();
2579
2580            (lazy_index, int_with_magic_name_index)
2581        };
2582
2583        let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2584        let mut blocks = snapshot.scan();
2585        assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Header));
2586
2587        let block = blocks.next().and_then(|b| b.cast::<Link>()).unwrap();
2588        assert_eq!(block.block_type(), Some(BlockType::LinkValue));
2589        assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk");
2590        assert_eq!(state.try_lock().unwrap().load_string(block.content_index()).unwrap(), "lk-0");
2591        assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2592        assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2593        let block = blocks.next().and_then(|b| b.cast::<Int>()).unwrap();
2594        assert_eq!(block.block_type(), Some(BlockType::IntValue));
2595        assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk-0");
2596        assert_all_free(blocks);
2597
2598        state.try_lock().unwrap().free_lazy_node(lazy_index).unwrap();
2599
2600        let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2601        let mut blocks = snapshot.scan();
2602
2603        assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Header));
2604        assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Free));
2605        assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2606        let block = blocks.next().and_then(|b| b.cast::<Int>()).unwrap();
2607        assert_eq!(block.block_type(), Some(BlockType::IntValue));
2608        assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk-0");
2609        assert_all_free(blocks);
2610    }
2611
2612    #[fuchsia::test]
2613    async fn stats() {
2614        // Initialize state and create a link block.
2615        let state = get_state(3 * 4096);
2616        let mut state_guard = state.try_lock().expect("lock state");
2617        let _block1 = state_guard
2618            .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2619                async move {
2620                    let inspector = Inspector::default();
2621                    inspector.root().record_uint("a", 1);
2622                    Ok(inspector)
2623                }
2624                .boxed()
2625            })
2626            .unwrap();
2627        let _block2 = state_guard.create_uint_metric("test", 3, 0.into()).unwrap();
2628        assert_eq!(
2629            state_guard.stats(),
2630            Stats {
2631                total_dynamic_children: 1,
2632                maximum_size: 3 * 4096,
2633                current_size: 4096,
2634                allocated_blocks: 6, /* HEADER, state_guard, _block1 (and content),
2635                                     // "link-name", _block2, "test" */
2636                deallocated_blocks: 0,
2637                failed_allocations: 0,
2638            }
2639        )
2640    }
2641
2642    #[fuchsia::test]
2643    fn transaction_locking() {
2644        let state = get_state(4096);
2645        // Initial generation count is 0
2646        state.with_current_header(|header| {
2647            assert_eq!(header.generation_count(), 0);
2648        });
2649
2650        // Begin a transaction
2651        state.begin_transaction();
2652        state.with_current_header(|header| {
2653            assert_eq!(header.generation_count(), 1);
2654            assert!(header.is_locked());
2655        });
2656
2657        // Operations on the lock  guard do not change the generation counter.
2658        let mut lock_guard1 = state.try_lock().expect("lock state");
2659        assert_eq!(lock_guard1.inner_lock.transaction_count, 1);
2660        assert_eq!(lock_guard1.header().generation_count(), 1);
2661        assert!(lock_guard1.header().is_locked());
2662        let _ = lock_guard1.create_node("test", 0.into());
2663        assert_eq!(lock_guard1.inner_lock.transaction_count, 1);
2664        assert_eq!(lock_guard1.header().generation_count(), 1);
2665
2666        // Dropping the guard releases the mutex lock but the header remains locked.
2667        drop(lock_guard1);
2668        state.with_current_header(|header| {
2669            assert_eq!(header.generation_count(), 1);
2670            assert!(header.is_locked());
2671        });
2672
2673        // When the transaction finishes, the header is unlocked.
2674        state.end_transaction();
2675
2676        state.with_current_header(|header| {
2677            assert_eq!(header.generation_count(), 2);
2678            assert!(!header.is_locked());
2679        });
2680
2681        // Operations under no transaction work as usual.
2682        let lock_guard2 = state.try_lock().expect("lock state");
2683        assert!(lock_guard2.header().is_locked());
2684        assert_eq!(lock_guard2.header().generation_count(), 3);
2685        assert_eq!(lock_guard2.inner_lock.transaction_count, 0);
2686    }
2687
2688    #[fuchsia::test]
2689    async fn update_header_vmo_size() {
2690        let core_state = get_state(3 * 4096);
2691        core_state.get_block(BlockIndex::HEADER, |header: &Block<_, Header>| {
2692            assert_eq!(header.vmo_size(), Ok(Some(4096)));
2693        });
2694        let block1_index = {
2695            let mut state = core_state.try_lock().expect("lock state");
2696
2697            let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2698            let data = chars.iter().cycle().take(6000).collect::<String>();
2699            let block_index =
2700                state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2701            assert_eq!(state.header().vmo_size(), Ok(Some(2 * 4096)));
2702
2703            block_index
2704        };
2705
2706        let block2_index = {
2707            let mut state = core_state.try_lock().expect("lock state");
2708
2709            let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2710            let data = chars.iter().cycle().take(3000).collect::<String>();
2711            let block_index =
2712                state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2713            assert_eq!(state.header().vmo_size(), Ok(Some(3 * 4096)));
2714
2715            block_index
2716        };
2717        // Free properties.
2718        {
2719            let mut state = core_state.try_lock().expect("lock state");
2720            assert!(state.free_string_or_bytes_buffer_property(block1_index).is_ok());
2721            assert!(state.free_string_or_bytes_buffer_property(block2_index).is_ok());
2722        }
2723        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2724        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2725        assert_all_free(blocks.into_iter().skip(1));
2726    }
2727
2728    #[fuchsia::test]
2729    fn test_buffer_property_on_overflow_set() {
2730        let core_state = get_state(4096);
2731        let block_index = {
2732            let mut state = core_state.try_lock().expect("lock state");
2733
2734            // Create string property with value.
2735            let block_index =
2736                state.create_buffer_property("test", b"test-property", 0.into()).unwrap();
2737
2738            // Fill the vmo.
2739            for _ in 10..(4096 / constants::MIN_ORDER_SIZE).try_into().unwrap() {
2740                state.inner_lock.heap.allocate_block(constants::MIN_ORDER_SIZE).unwrap();
2741            }
2742
2743            // Set the value of the string to something very large that causes an overflow.
2744            let values = [b'a'; 8096];
2745            assert!(state.set_buffer_property(block_index, &values).is_err());
2746
2747            // We now expect the length of the payload, as well as the property extent index to be
2748            // reset.
2749            let block = state.get_block::<Buffer>(block_index);
2750            assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2751            assert_eq!(*block.index(), 2);
2752            assert_eq!(*block.parent_index(), 0);
2753            assert_eq!(*block.name_index(), 3);
2754            assert_eq!(block.total_length(), 0);
2755            assert_eq!(*block.extent_index(), 0);
2756            assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2757
2758            block_index
2759        };
2760
2761        // We also expect no extents to be present.
2762        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2763        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2764        assert_eq!(blocks.len(), 251);
2765        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2766        assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2767        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2768        assert_all_free_or_reserved(blocks.into_iter().skip(3));
2769
2770        {
2771            let mut state = core_state.try_lock().expect("lock state");
2772            // Free property.
2773            assert_matches!(state.free_string_or_bytes_buffer_property(block_index), Ok(()));
2774        }
2775        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2776        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2777        assert_all_free_or_reserved(blocks.into_iter().skip(1));
2778    }
2779
2780    #[fuchsia::test]
2781    fn test_string_property_on_overflow_set() {
2782        let core_state = get_state(4096);
2783        {
2784            let mut state = core_state.try_lock().expect("lock state");
2785
2786            // Create string property with value.
2787            let block_index = state.create_string("test", "test-property", 0.into()).unwrap();
2788
2789            // Fill the vmo.
2790            for _ in 10..(4096 / constants::MIN_ORDER_SIZE).try_into().unwrap() {
2791                state.inner_lock.heap.allocate_block(constants::MIN_ORDER_SIZE).unwrap();
2792            }
2793
2794            // make a value too large to fit in the VMO, then attempt to set it into the property
2795            // in order to trigger error conditions and make sure the old value isn't deallocated
2796            let values = ["a"].into_iter().cycle().take(5000).collect::<String>();
2797            assert!(state.set_string_property(block_index, values).is_err());
2798            let block = state.get_block::<Buffer>(block_index);
2799            assert_eq!(*block.index(), 2);
2800            assert_eq!(*block.parent_index(), 0);
2801            assert_eq!(*block.name_index(), 3);
2802
2803            // expect the old value to be there
2804            assert_eq!(
2805                state.load_string(BlockIndex::from(*block.extent_index())).unwrap(),
2806                "test-property"
2807            );
2808
2809            // make sure state can still create some new values
2810            assert!(state.create_int_metric("foo", 1, 0.into()).is_ok());
2811            assert!(state.create_int_metric("bar", 1, 0.into()).is_ok());
2812        };
2813
2814        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2815        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2816        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2817        assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2818        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2819        assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2820        assert_eq!(blocks[250].block_type(), Some(BlockType::IntValue));
2821        assert_eq!(blocks[251].block_type(), Some(BlockType::StringReference));
2822        assert_eq!(blocks[252].block_type(), Some(BlockType::IntValue));
2823        assert_eq!(blocks[253].block_type(), Some(BlockType::StringReference));
2824        assert_all_free_or_reserved(blocks.into_iter().skip(4).rev().skip(4));
2825    }
2826
2827    #[fuchsia::test]
2828    fn test_allocate_link_cleanup_failure() {
2829        let core_state = get_state(4096);
2830        {
2831            let mut state = core_state.try_lock().expect("lock state");
2832
2833            // Fill the heap until almost full with nodes sharing the same name.
2834            // This ensures we have many node blocks but only one name block.
2835            let mut nodes = vec![];
2836            while let Ok(idx) = state.create_node("n", 0.into()) {
2837                // "n" will be interned and shared.
2838                nodes.push(idx);
2839            }
2840
2841            // Free one node to make space for exactly one block (the reserved block for the link).
2842            // The name "n" is still held by other nodes, so the name block is not freed.
2843            state.free_value(nodes.pop().unwrap()).unwrap();
2844
2845            // Now call `create_lazy_node`.
2846            // 1. `allocate_reserved_value("n", ...)`:
2847            //    - `allocate_block` succeeds (takes the freed slot).
2848            //    - `get_or_create_string_reference("n")` succeeds (reused).
2849            //    - Returns PendingBlock.
2850            // 2. `get_or_create_string_reference("new_content")`:
2851            //    - Tries to allocate new string ref block.
2852            //    - Fails (no space).
2853            // 3. PendingBlock drops.
2854            //    - Should cleanly free the reserved block and release "n" ref.
2855
2856            let result = state.create_lazy_node("n", 0.into(), LinkNodeDisposition::Inline, || {
2857                async move { Ok(Inspector::default()) }.boxed()
2858            });
2859
2860            assert!(result.is_err());
2861        }
2862
2863        // Verify header is intact.
2864        core_state.with_current_header(|header| {
2865            assert_eq!(header.magic_number(), constants::HEADER_MAGIC_NUMBER);
2866            assert_eq!(header.version(), constants::HEADER_VERSION_NUMBER);
2867        });
2868    }
2869}