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