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