Skip to main content

fuchsia_inspect/writer/
state.rs

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