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.saturating_sub(other)
35    }
36    fn safe_add(&self, other: u64) -> u64 {
37        self.saturating_add(other)
38    }
39}
40
41impl SafeOp for i64 {
42    fn safe_sub(&self, other: i64) -> i64 {
43        self.saturating_sub(other)
44    }
45    fn safe_add(&self, other: i64) -> i64 {
46        self.saturating_add(other)
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                if data_index != BlockIndex::EMPTY {
1264                    self.release_string_reference(data_index)?;
1265                }
1266            }
1267            _ => {
1268                return Err(Error::VmoFormat(FormatError::InvalidBufferFormat(
1269                    self.heap.container.block_at_unchecked(index).format_raw(),
1270                )));
1271            }
1272        }
1273
1274        self.delete_value(index)?;
1275        Ok(())
1276    }
1277
1278    /// Set the |value| of a String BUFFER_VALUE block.
1279    fn set_string_property<'a>(
1280        &mut self,
1281        block_index: BlockIndex,
1282        value: impl Into<Cow<'a, str>>,
1283    ) -> Result<(), Error> {
1284        self.inner_set_string_property_value(block_index, value)?;
1285        Ok(())
1286    }
1287
1288    /// Set the |value| of a String BUFFER_VALUE block.
1289    fn set_buffer_property(&mut self, block_index: BlockIndex, value: &[u8]) -> Result<(), Error> {
1290        self.inner_set_buffer_property_value(block_index, value)?;
1291        Ok(())
1292    }
1293
1294    fn check_lineage(
1295        &self,
1296        being_reparented: BlockIndex,
1297        new_parent: BlockIndex,
1298    ) -> Result<(), Error> {
1299        // you cannot adopt the root node
1300        if being_reparented == BlockIndex::ROOT {
1301            return Err(Error::AdoptAncestor);
1302        }
1303
1304        let mut being_checked = new_parent;
1305        while being_checked != BlockIndex::ROOT {
1306            if being_checked == being_reparented {
1307                return Err(Error::AdoptAncestor);
1308            }
1309            // Note: all values share the parent_index in the same position, so we can just assume
1310            // we have ANY_VALUE here, so just using a Node.
1311            being_checked =
1312                self.heap.container.block_at_unchecked::<Node>(being_checked).parent_index();
1313        }
1314
1315        Ok(())
1316    }
1317
1318    fn reparent(
1319        &mut self,
1320        being_reparented: BlockIndex,
1321        new_parent: BlockIndex,
1322    ) -> Result<(), Error> {
1323        let mut txn = Txn::new(self);
1324        txn.reparent(being_reparented, new_parent)?;
1325        txn.commit();
1326        Ok(())
1327    }
1328
1329    fn create_bool<'a>(
1330        &mut self,
1331        name: impl Into<Cow<'a, str>>,
1332        value: bool,
1333        parent_index: BlockIndex,
1334    ) -> Result<BlockIndex, Error> {
1335        let mut txn = Txn::new(self);
1336        let (block_index, name_index) =
1337            txn.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
1338        txn.block_mut::<Reserved>(block_index).become_bool_value(value, name_index, parent_index);
1339        txn.commit();
1340        Ok(block_index)
1341    }
1342
1343    fn set_bool(&mut self, block_index: BlockIndex, value: bool) {
1344        let mut block = self.heap.container.block_at_unchecked_mut::<Bool>(block_index);
1345        block.set(value);
1346    }
1347
1348    metric_fns!(int, i64, Int);
1349    metric_fns!(uint, u64, Uint);
1350    metric_fns!(double, f64, Double);
1351
1352    arithmetic_array_fns!(int, i64, IntValue, Int);
1353    arithmetic_array_fns!(uint, u64, UintValue, Uint);
1354    arithmetic_array_fns!(double, f64, DoubleValue, Double);
1355
1356    fn create_string_array<'a>(
1357        &mut self,
1358        name: impl Into<Cow<'a, str>>,
1359        slots: usize,
1360        parent_index: BlockIndex,
1361    ) -> Result<BlockIndex, Error> {
1362        let block_size = slots * StringRef::array_entry_type_size() + constants::MIN_ORDER_SIZE;
1363        if block_size > constants::MAX_ORDER_SIZE {
1364            return Err(Error::BlockSizeTooBig(block_size));
1365        }
1366        let mut txn = Txn::new(self);
1367        let (block_index, name_index) =
1368            txn.allocate_reserved_value(name, parent_index, block_size)?;
1369        txn.block_mut::<Reserved>(block_index).become_array_value::<StringRef>(
1370            slots,
1371            ArrayFormat::Default,
1372            name_index,
1373            parent_index,
1374        )?;
1375        txn.commit();
1376        Ok(block_index)
1377    }
1378
1379    fn get_array_size(&self, block_index: BlockIndex) -> usize {
1380        let block = self.heap.container.block_at_unchecked::<Array<Unknown>>(block_index);
1381        block.slots()
1382    }
1383
1384    fn set_array_string_slot<'a>(
1385        &mut self,
1386        block_index: BlockIndex,
1387        slot_index: usize,
1388        value: impl Into<Cow<'a, str>>,
1389    ) -> Result<(), Error> {
1390        if self.heap.container.block_at_unchecked_mut::<Array<StringRef>>(block_index).slots()
1391            <= slot_index
1392        {
1393            return Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(slot_index)));
1394        }
1395
1396        let value = value.into();
1397
1398        let existing_index = self
1399            .heap
1400            .container
1401            .block_at_unchecked::<Array<StringRef>>(block_index)
1402            .get_string_index_at(slot_index)
1403            .ok_or(Error::InvalidArrayIndex(slot_index))?;
1404        if existing_index != BlockIndex::EMPTY
1405            && self.string_reference_block_indexes.get(&value) == Some(&existing_index)
1406        {
1407            return Ok(());
1408        }
1409
1410        let mut txn = Txn::new(self);
1411        let reference_index = if !value.is_empty() {
1412            let idx = txn.intern_and_ref_string(value)?;
1413            if existing_index != BlockIndex::EMPTY {
1414                txn.release_string_ref(existing_index)?;
1415            }
1416            idx
1417        } else {
1418            if existing_index != BlockIndex::EMPTY {
1419                txn.release_string_ref(existing_index)?;
1420            }
1421            BlockIndex::EMPTY
1422        };
1423
1424        txn.block_mut::<Array<StringRef>>(block_index).set_string_slot(slot_index, reference_index);
1425        txn.commit();
1426        Ok(())
1427    }
1428
1429    /// Sets all slots of the array at the given index to zero.
1430    /// Does appropriate deallocation on string references in payload.
1431    fn clear_array(
1432        &mut self,
1433        block_index: BlockIndex,
1434        start_slot_index: usize,
1435    ) -> Result<(), Error> {
1436        let mut txn = Txn::new(self);
1437        txn.clear_array(block_index, start_slot_index)?;
1438        txn.commit();
1439        Ok(())
1440    }
1441
1442    fn delete_value(&mut self, block_index: BlockIndex) -> Result<(), Error> {
1443        // For our purposes here, we just need "ANY_VALUE". Using "node".
1444        let block = self.heap.container.block_at_unchecked::<Node>(block_index);
1445        let parent_index = block.parent_index();
1446        let name_index = block.name_index();
1447
1448        // Decrement parent child count.
1449        if parent_index != BlockIndex::ROOT {
1450            let parent = self.heap.container.block_at_mut(parent_index);
1451            match parent.block_type() {
1452                Some(BlockType::Tombstone) => {
1453                    let mut parent = parent.cast::<Tombstone>().unwrap();
1454                    let child_count = parent.child_count() - 1;
1455                    if child_count == 0 {
1456                        self.heap.free_block(parent_index)?;
1457                    } else {
1458                        parent.set_child_count(child_count);
1459                    }
1460                }
1461                Some(BlockType::NodeValue) => {
1462                    let mut parent = parent.cast::<Node>().unwrap();
1463                    let child_count = parent.child_count() - 1;
1464                    parent.set_child_count(child_count);
1465                }
1466                _ => {
1467                    return Err(Error::InvalidBlockType(parent_index, parent.block_type_raw()));
1468                }
1469            }
1470        }
1471
1472        // Free the name block.
1473        match self.heap.container.block_at(name_index).block_type() {
1474            Some(BlockType::StringReference) => {
1475                self.release_string_reference(name_index)?;
1476            }
1477            _ => self.heap.free_block(name_index)?,
1478        }
1479
1480        // If the block is a NODE and has children, make it a TOMBSTONE so that
1481        // it's freed when the last of its children is freed. Otherwise, free it.
1482        let block = self.heap.container.block_at_mut(block_index);
1483        match block.cast::<Node>() {
1484            Some(block) if block.child_count() != 0 => {
1485                let _ = block.become_tombstone();
1486            }
1487            _ => {
1488                self.heap.free_block(block_index)?;
1489            }
1490        }
1491        Ok(())
1492    }
1493
1494    fn inner_set_string_property_value<'a>(
1495        &mut self,
1496        block_index: BlockIndex,
1497        value: impl Into<Cow<'a, str>>,
1498    ) -> Result<(), Error> {
1499        let format = self.heap.container.block_at_unchecked::<Buffer>(block_index).format();
1500        if format != Some(PropertyFormat::StringReference) && format != Some(PropertyFormat::String)
1501        {
1502            return Err(Error::VmoFormat(FormatError::InvalidBufferFormat(
1503                self.heap.container.block_at_unchecked(block_index).format_raw(),
1504            )));
1505        }
1506        let value = value.into();
1507        let old_string_ref_idx =
1508            self.heap.container.block_at_unchecked::<Buffer>(block_index).extent_index();
1509
1510        if old_string_ref_idx != BlockIndex::EMPTY
1511            && self.string_reference_block_indexes.get(&value) == Some(&old_string_ref_idx)
1512        {
1513            return Ok(());
1514        }
1515
1516        let mut txn = Txn::new(self);
1517        let new_string_ref_idx = txn.intern_and_ref_string(value)?;
1518
1519        if old_string_ref_idx != BlockIndex::EMPTY {
1520            txn.release_string_ref(old_string_ref_idx)?;
1521        }
1522
1523        txn.block_mut::<Buffer>(block_index).set_extent_index(new_string_ref_idx);
1524        txn.commit();
1525        Ok(())
1526    }
1527
1528    fn inner_set_buffer_property_value(
1529        &mut self,
1530        block_index: BlockIndex,
1531        value: &[u8],
1532    ) -> Result<(), Error> {
1533        let format = self.heap.container.block_at_unchecked::<Buffer>(block_index).format();
1534        if format != Some(PropertyFormat::Bytes) {
1535            return Err(Error::VmoFormat(FormatError::InvalidBufferFormat(
1536                self.heap.container.block_at_unchecked(block_index).format_raw(),
1537            )));
1538        }
1539        self.free_extents(
1540            self.heap.container.block_at_unchecked::<Buffer>(block_index).extent_index(),
1541        )?;
1542        let mut txn = Txn::new(self);
1543        let (result, (extent_index, written)) = match txn.write_extents(value) {
1544            Ok((e, w)) => (Ok(()), (e, w)),
1545            Err(err) => (Err(err), (BlockIndex::ROOT, 0)),
1546        };
1547        let mut block = txn.block_mut::<Buffer>(block_index);
1548        block.set_total_length(written.try_into().unwrap_or(u32::MAX));
1549        block.set_extent_index(extent_index);
1550        txn.commit();
1551        result
1552    }
1553
1554    fn free_extents(&mut self, head_extent_index: BlockIndex) -> Result<(), Error> {
1555        let mut index = head_extent_index;
1556        while index != BlockIndex::ROOT {
1557            let next_index = self.heap.container.block_at_unchecked::<Extent>(index).next_extent();
1558            self.heap.free_block(index)?;
1559            index = next_index;
1560        }
1561        Ok(())
1562    }
1563}
1564
1565#[cfg(test)]
1566mod tests {
1567    use super::*;
1568    use crate::reader::PartialNodeHierarchy;
1569    use crate::reader::snapshot::{BackingBuffer, ScannedBlock, Snapshot};
1570    use crate::writer::testing_utils::get_state;
1571    use assert_matches::assert_matches;
1572    use diagnostics_assertions::assert_data_tree;
1573    use futures::prelude::*;
1574    use inspect_format::Header;
1575
1576    #[fuchsia::test]
1577    fn test_safe_op_overflow_direction() {
1578        assert_eq!((-100i64).safe_add(i64::MIN), i64::MIN);
1579        assert_eq!((100i64).safe_add(i64::MAX), i64::MAX);
1580        assert_eq!((100i64).safe_sub(i64::MIN), i64::MAX);
1581        assert_eq!((-100i64).safe_sub(i64::MAX), i64::MIN);
1582        assert_eq!(0u64.safe_sub(10), 0);
1583        assert_eq!(u64::MAX.safe_add(10), u64::MAX);
1584    }
1585
1586    #[track_caller]
1587    fn assert_all_free_or_reserved<'a>(
1588        blocks: impl Iterator<Item = Block<&'a BackingBuffer, Unknown>>,
1589    ) {
1590        let mut errors = vec![];
1591        for block in blocks {
1592            if block.block_type() != Some(BlockType::Free)
1593                && block.block_type() != Some(BlockType::Reserved)
1594            {
1595                errors.push(format!(
1596                    "block at {} is {:?}, expected {} or {}",
1597                    block.index(),
1598                    block.block_type(),
1599                    BlockType::Free,
1600                    BlockType::Reserved,
1601                ));
1602            }
1603        }
1604
1605        if !errors.is_empty() {
1606            panic!("{errors:#?}");
1607        }
1608    }
1609
1610    #[track_caller]
1611    fn assert_all_free<'a>(blocks: impl Iterator<Item = Block<&'a BackingBuffer, Unknown>>) {
1612        let mut errors = vec![];
1613        for block in blocks {
1614            if block.block_type() != Some(BlockType::Free) {
1615                errors.push(format!(
1616                    "block at {} is {:?}, expected {}",
1617                    block.index(),
1618                    block.block_type(),
1619                    BlockType::Free
1620                ));
1621            }
1622        }
1623
1624        if !errors.is_empty() {
1625            panic!("{errors:#?}");
1626        }
1627    }
1628
1629    #[fuchsia::test]
1630    fn test_create() {
1631        let state = get_state(4096);
1632        let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
1633        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1634        assert_eq!(blocks.len(), 8);
1635        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1636        assert_all_free(blocks.into_iter().skip(1));
1637    }
1638
1639    #[fuchsia::test]
1640    fn test_load_string() {
1641        let outer = get_state(4096);
1642        let mut state = outer.try_lock().expect("lock state");
1643        let block_index = {
1644            let mut txn = Txn::new(&mut state.inner_lock);
1645            let idx = txn.get_or_create_string_reference("a value").unwrap();
1646            txn.commit();
1647            idx
1648        };
1649        assert_eq!(state.load_string(block_index).unwrap(), "a value");
1650    }
1651
1652    #[fuchsia::test]
1653    fn test_check_lineage() {
1654        let core_state = get_state(4096);
1655        let mut state = core_state.try_lock().expect("lock state");
1656        let parent_index = state.create_node("", 0.into()).unwrap();
1657        let child_index = state.create_node("", parent_index).unwrap();
1658        let uncle_index = state.create_node("", 0.into()).unwrap();
1659
1660        state.inner_lock.check_lineage(parent_index, child_index).unwrap_err();
1661        state.inner_lock.check_lineage(0.into(), child_index).unwrap_err();
1662        state.inner_lock.check_lineage(child_index, uncle_index).unwrap();
1663    }
1664
1665    #[fuchsia::test]
1666    fn test_reparent() {
1667        let core_state = get_state(4096);
1668        let mut state = core_state.try_lock().expect("lock state");
1669
1670        let a_index = state.create_node("a", 0.into()).unwrap();
1671        let b_index = state.create_node("b", 0.into()).unwrap();
1672
1673        let a = state.get_block::<Node>(a_index);
1674        let b = state.get_block::<Node>(b_index);
1675        assert_eq!(*a.parent_index(), 0);
1676        assert_eq!(*b.parent_index(), 0);
1677
1678        assert_eq!(a.child_count(), 0);
1679        assert_eq!(b.child_count(), 0);
1680
1681        state.reparent(b_index, a_index).unwrap();
1682
1683        let a = state.get_block::<Node>(a_index);
1684        let b = state.get_block::<Node>(b_index);
1685        assert_eq!(*a.parent_index(), 0);
1686        assert_eq!(b.parent_index(), a.index());
1687
1688        assert_eq!(a.child_count(), 1);
1689        assert_eq!(b.child_count(), 0);
1690
1691        let c_index = state.create_node("c", a_index).unwrap();
1692
1693        let a = state.get_block::<Node>(a_index);
1694        let b = state.get_block::<Node>(b_index);
1695        let c = state.get_block::<Node>(c_index);
1696        assert_eq!(*a.parent_index(), 0);
1697        assert_eq!(b.parent_index(), a.index());
1698        assert_eq!(c.parent_index(), a.index());
1699
1700        assert_eq!(a.child_count(), 2);
1701        assert_eq!(b.child_count(), 0);
1702        assert_eq!(c.child_count(), 0);
1703
1704        state.reparent(c_index, b_index).unwrap();
1705
1706        let a = state.get_block::<Node>(a_index);
1707        let b = state.get_block::<Node>(b_index);
1708        let c = state.get_block::<Node>(c_index);
1709        assert_eq!(*a.parent_index(), 0);
1710        assert_eq!(b.parent_index(), a_index);
1711        assert_eq!(c.parent_index(), b_index);
1712
1713        assert_eq!(a.child_count(), 1);
1714        assert_eq!(b.child_count(), 1);
1715        assert_eq!(c.child_count(), 0);
1716    }
1717
1718    #[fuchsia::test]
1719    fn test_node() {
1720        let core_state = get_state(4096);
1721        let block_index = {
1722            let mut state = core_state.try_lock().expect("lock state");
1723
1724            // Create a node value and verify its fields
1725            let block_index = state.create_node("test-node", 0.into()).unwrap();
1726            let block = state.get_block::<Node>(block_index);
1727            assert_eq!(block.block_type(), Some(BlockType::NodeValue));
1728            assert_eq!(*block.index(), 2);
1729            assert_eq!(block.child_count(), 0);
1730            assert_eq!(*block.name_index(), 4);
1731            assert_eq!(*block.parent_index(), 0);
1732
1733            // Verify name block.
1734            let name_block = state.get_block::<StringRef>(block.name_index());
1735            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1736            assert_eq!(name_block.total_length(), 9);
1737            assert_eq!(name_block.order(), 1);
1738            assert_eq!(state.load_string(name_block.index()).unwrap(), "test-node");
1739            block_index
1740        };
1741
1742        // Verify blocks.
1743        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1744        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1745        assert_eq!(blocks.len(), 10);
1746        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1747        assert_eq!(blocks[1].block_type(), Some(BlockType::NodeValue));
1748        assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
1749        assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
1750        assert_all_free(blocks.into_iter().skip(4));
1751
1752        {
1753            let mut state = core_state.try_lock().expect("lock state");
1754            let child_block_index = state.create_node("child1", block_index).unwrap();
1755            assert_eq!(state.get_block::<Node>(block_index).child_count(), 1);
1756
1757            // Create a child of the child and verify child counts.
1758            let child11_block_index = state.create_node("child1-1", child_block_index).unwrap();
1759            {
1760                assert_eq!(state.get_block::<Node>(child11_block_index).child_count(), 0);
1761                assert_eq!(state.get_block::<Node>(child_block_index).child_count(), 1);
1762                assert_eq!(state.get_block::<Node>(block_index).child_count(), 1);
1763            }
1764
1765            assert!(state.free_value(child11_block_index).is_ok());
1766            {
1767                let child_block = state.get_block::<Node>(child_block_index);
1768                assert_eq!(child_block.child_count(), 0);
1769            }
1770
1771            // Add a couple more children to the block and verify count.
1772            let child_block2_index = state.create_node("child2", block_index).unwrap();
1773            let child_block3_index = state.create_node("child3", block_index).unwrap();
1774            assert_eq!(state.get_block::<Node>(block_index).child_count(), 3);
1775
1776            // Free children and verify count.
1777            assert!(state.free_value(child_block_index).is_ok());
1778            assert!(state.free_value(child_block2_index).is_ok());
1779            assert!(state.free_value(child_block3_index).is_ok());
1780            assert_eq!(state.get_block::<Node>(block_index).child_count(), 0);
1781
1782            // Free node.
1783            assert!(state.free_value(block_index).is_ok());
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_all_free(blocks.into_iter().skip(1));
1789    }
1790
1791    #[fuchsia::test]
1792    fn test_int_metric() {
1793        let core_state = get_state(4096);
1794        let block_index = {
1795            let mut state = core_state.try_lock().expect("lock state");
1796            let block_index = state.create_int_metric("test", 3, 0.into()).unwrap();
1797            let block = state.get_block::<Int>(block_index);
1798            assert_eq!(block.block_type(), Some(BlockType::IntValue));
1799            assert_eq!(*block.index(), 2);
1800            assert_eq!(block.value(), 3);
1801            assert_eq!(*block.name_index(), 3);
1802            assert_eq!(*block.parent_index(), 0);
1803
1804            let name_block = state.get_block::<StringRef>(block.name_index());
1805            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1806            assert_eq!(name_block.total_length(), 4);
1807            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1808            block_index
1809        };
1810
1811        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1812        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1813        assert_eq!(blocks.len(), 9);
1814        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1815        assert_eq!(blocks[1].block_type(), Some(BlockType::IntValue));
1816        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1817        assert_all_free(blocks.into_iter().skip(3));
1818
1819        {
1820            let mut state = core_state.try_lock().expect("lock state");
1821            assert_eq!(state.add_int_metric(block_index, 10), 13);
1822            assert_eq!(state.get_block::<Int>(block_index).value(), 13);
1823
1824            assert_eq!(state.subtract_int_metric(block_index, 5), 8);
1825            assert_eq!(state.get_block::<Int>(block_index).value(), 8);
1826
1827            state.set_int_metric(block_index, -6);
1828            assert_eq!(state.get_block::<Int>(block_index).value(), -6);
1829
1830            assert_eq!(state.subtract_int_metric(block_index, i64::MAX), i64::MIN);
1831            assert_eq!(state.get_block::<Int>(block_index).value(), i64::MIN);
1832            state.set_int_metric(block_index, i64::MAX);
1833
1834            assert_eq!(state.add_int_metric(block_index, 2), i64::MAX);
1835            assert_eq!(state.get_block::<Int>(block_index).value(), i64::MAX);
1836
1837            // Free metric.
1838            assert!(state.free_value(block_index).is_ok());
1839        }
1840
1841        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1842        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1843        assert_all_free(blocks.into_iter().skip(1));
1844    }
1845
1846    #[fuchsia::test]
1847    fn test_uint_metric() {
1848        let core_state = get_state(4096);
1849
1850        // Creates with value
1851        let block_index = {
1852            let mut state = core_state.try_lock().expect("try lock");
1853            let block_index = state.create_uint_metric("test", 3, 0.into()).unwrap();
1854            let block = state.get_block::<Uint>(block_index);
1855            assert_eq!(block.block_type(), Some(BlockType::UintValue));
1856            assert_eq!(*block.index(), 2);
1857            assert_eq!(block.value(), 3);
1858            assert_eq!(*block.name_index(), 3);
1859            assert_eq!(*block.parent_index(), 0);
1860
1861            let name_block = state.get_block::<StringRef>(block.name_index());
1862            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1863            assert_eq!(name_block.total_length(), 4);
1864            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1865            block_index
1866        };
1867
1868        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1869        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1870        assert_eq!(blocks.len(), 9);
1871        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1872        assert_eq!(blocks[1].block_type(), Some(BlockType::UintValue));
1873        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1874        assert_all_free(blocks.into_iter().skip(3));
1875
1876        {
1877            let mut state = core_state.try_lock().expect("try lock");
1878            assert_eq!(state.add_uint_metric(block_index, 10), 13);
1879            assert_eq!(state.get_block::<Uint>(block_index).value(), 13);
1880
1881            assert_eq!(state.subtract_uint_metric(block_index, 5), 8);
1882            assert_eq!(state.get_block::<Uint>(block_index).value(), 8);
1883
1884            state.set_uint_metric(block_index, 0);
1885            assert_eq!(state.get_block::<Uint>(block_index).value(), 0);
1886
1887            assert_eq!(state.subtract_uint_metric(block_index, u64::MAX), 0);
1888            assert_eq!(state.get_block::<Uint>(block_index).value(), 0);
1889
1890            state.set_uint_metric(block_index, 3);
1891            assert_eq!(state.add_uint_metric(block_index, u64::MAX), u64::MAX);
1892            assert_eq!(state.get_block::<Uint>(block_index).value(), u64::MAX);
1893
1894            // Free metric.
1895            assert!(state.free_value(block_index).is_ok());
1896        }
1897
1898        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1899        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1900        assert_all_free(blocks.into_iter().skip(1));
1901    }
1902
1903    #[fuchsia::test]
1904    fn test_double_metric() {
1905        let core_state = get_state(4096);
1906
1907        // Creates with value
1908        let block_index = {
1909            let mut state = core_state.try_lock().expect("lock state");
1910            let block_index = state.create_double_metric("test", 3.0, 0.into()).unwrap();
1911            let block = state.get_block::<Double>(block_index);
1912            assert_eq!(block.block_type(), Some(BlockType::DoubleValue));
1913            assert_eq!(*block.index(), 2);
1914            assert_eq!(block.value(), 3.0);
1915            assert_eq!(*block.name_index(), 3);
1916            assert_eq!(*block.parent_index(), 0);
1917
1918            let name_block = state.get_block::<StringRef>(block.name_index());
1919            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1920            assert_eq!(name_block.total_length(), 4);
1921            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1922            block_index
1923        };
1924
1925        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1926        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1927        assert_eq!(blocks.len(), 9);
1928        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1929        assert_eq!(blocks[1].block_type(), Some(BlockType::DoubleValue));
1930        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1931        assert_all_free(blocks.into_iter().skip(3));
1932
1933        {
1934            let mut state = core_state.try_lock().expect("lock state");
1935            assert_eq!(state.add_double_metric(block_index, 10.5), 13.5);
1936            assert_eq!(state.get_block::<Double>(block_index).value(), 13.5);
1937
1938            assert_eq!(state.subtract_double_metric(block_index, 5.1), 8.4);
1939            assert_eq!(state.get_block::<Double>(block_index).value(), 8.4);
1940
1941            state.set_double_metric(block_index, -6.0);
1942            assert_eq!(state.get_block::<Double>(block_index).value(), -6.0);
1943
1944            // Free metric.
1945            assert!(state.free_value(block_index).is_ok());
1946        }
1947
1948        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1949        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1950        assert_all_free(blocks.into_iter().skip(1));
1951    }
1952
1953    #[fuchsia::test]
1954    fn test_create_buffer_property_cleanup_on_failure() {
1955        // this implementation detail is important for the test below to be valid
1956        assert_eq!(constants::MAX_ORDER_SIZE, 2048);
1957
1958        let core_state = get_state(5121); // large enough to fit to max size blocks plus 1024
1959        let mut state = core_state.try_lock().expect("lock state");
1960        // allocate a max size block and one extent
1961        let name = (0..3000).map(|_| " ").collect::<String>();
1962        // allocate a max size property + at least one extent
1963        // the extent won't fit into the VMO, causing allocation failure when the property
1964        // is set
1965        let payload = [0u8; 4096]; // won't fit into vmo
1966
1967        // fails because the property is too big, but, allocates the name and should clean it up
1968        assert!(state.create_buffer_property(name, &payload, 0.into()).is_err());
1969
1970        drop(state);
1971
1972        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1973        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1974
1975        // if cleanup happened correctly, the name + extent and property + extent have been freed
1976        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1977        assert_all_free(blocks.into_iter().skip(1));
1978    }
1979
1980    #[fuchsia::test]
1981    fn test_string_reference_allocations() {
1982        let core_state = get_state(4096); // allocates HEADER
1983        {
1984            let mut state = core_state.try_lock().expect("lock state");
1985            let sf = "a reference-counted canonical name";
1986            assert_eq!(state.stats().allocated_blocks, 1);
1987
1988            let mut collected = vec![];
1989            for _ in 0..100 {
1990                collected.push(state.create_node(sf, 0.into()).unwrap());
1991            }
1992
1993            let acsf = Arc::new(Cow::Borrowed(sf));
1994            assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1995
1996            assert_eq!(state.stats().allocated_blocks, 102);
1997            let block = state.get_block::<Node>(collected[0]);
1998            let sf_block = state.get_block::<StringRef>(block.name_index());
1999            assert_eq!(sf_block.reference_count(), 100);
2000
2001            collected.into_iter().for_each(|b| {
2002                assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
2003                assert!(state.free_value(b).is_ok())
2004            });
2005
2006            assert!(!state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
2007
2008            let node_index = state.create_node(sf, 0.into()).unwrap();
2009            assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
2010            assert!(state.free_value(node_index).is_ok());
2011            assert!(!state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
2012        }
2013
2014        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2015        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2016        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2017        assert_all_free(blocks.into_iter().skip(1));
2018    }
2019
2020    #[fuchsia::test]
2021    fn test_string_reference_data() {
2022        let core_state = get_state(4096); // allocates HEADER
2023        let mut state = core_state.try_lock().expect("lock state");
2024
2025        // 4 bytes (4 ASCII characters in UTF-8) will fit inlined with a minimum block size
2026        let block_index = {
2027            let mut txn = Txn::new(&mut state.inner_lock);
2028            let idx = txn.get_or_create_string_reference("abcd").unwrap();
2029            txn.commit();
2030            idx
2031        };
2032        let block = state.get_block::<StringRef>(block_index);
2033        assert_eq!(block.block_type(), Some(BlockType::StringReference));
2034        assert_eq!(block.order(), 0);
2035        assert_eq!(state.stats().allocated_blocks, 2);
2036        assert_eq!(state.stats().deallocated_blocks, 0);
2037        assert_eq!(block.reference_count(), 0);
2038        assert_eq!(block.total_length(), 4);
2039        assert_eq!(*block.next_extent(), 0);
2040        assert_eq!(block.order(), 0);
2041        assert_eq!(state.load_string(block.index()).unwrap(), "abcd");
2042
2043        state.inner_lock.maybe_free_string_reference(block_index).unwrap();
2044        assert_eq!(state.stats().deallocated_blocks, 1);
2045
2046        let block_index = {
2047            let mut txn = Txn::new(&mut state.inner_lock);
2048            let idx = txn.get_or_create_string_reference("longer").unwrap();
2049            txn.commit();
2050            idx
2051        };
2052        let block = state.get_block::<StringRef>(block_index);
2053        assert_eq!(block.block_type(), Some(BlockType::StringReference));
2054        assert_eq!(block.order(), 1);
2055        assert_eq!(block.reference_count(), 0);
2056        assert_eq!(block.total_length(), 6);
2057        assert_eq!(state.stats().allocated_blocks, 3);
2058        assert_eq!(state.stats().deallocated_blocks, 1);
2059        assert_eq!(state.load_string(block.index()).unwrap(), "longer");
2060
2061        let idx = block.next_extent();
2062        assert_eq!(*idx, 0);
2063
2064        state.inner_lock.maybe_free_string_reference(block_index).unwrap();
2065        assert_eq!(state.stats().deallocated_blocks, 2);
2066
2067        let block_index = {
2068            let mut txn = Txn::new(&mut state.inner_lock);
2069            let idx = txn.get_or_create_string_reference("longer").unwrap();
2070            txn.commit();
2071            idx
2072        };
2073        let mut block = state.get_block_mut::<StringRef>(block_index);
2074        assert_eq!(block.order(), 1);
2075        block.increment_ref_count().unwrap();
2076        // not an error to try and free
2077        assert!(state.inner_lock.maybe_free_string_reference(block_index).is_ok());
2078
2079        let mut block = state.get_block_mut(block_index);
2080        block.decrement_ref_count().unwrap();
2081        state.inner_lock.maybe_free_string_reference(block_index).unwrap();
2082    }
2083
2084    #[fuchsia::test]
2085    fn test_string_reference_format_property() {
2086        let core_state = get_state(4096);
2087        let block_index = {
2088            let mut state = core_state.try_lock().expect("lock state");
2089
2090            // Creates with value
2091            let block_index =
2092                state.create_string("test", "test-property", BlockIndex::from(0)).unwrap();
2093            let block = state.get_block::<Buffer>(block_index);
2094            assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2095            assert_eq!(*block.index(), 2);
2096            assert_eq!(*block.parent_index(), 0);
2097            assert_eq!(*block.name_index(), 3);
2098            assert_eq!(block.total_length(), 0);
2099            assert_eq!(block.format(), Some(PropertyFormat::StringReference));
2100
2101            let name_block = state.get_block::<StringRef>(block.name_index());
2102            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2103            assert_eq!(name_block.total_length(), 4);
2104            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2105
2106            let data_block = state.get_block::<StringRef>(block.extent_index());
2107            assert_eq!(data_block.block_type(), Some(BlockType::StringReference));
2108            assert_eq!(state.load_string(data_block.index()).unwrap(), "test-property");
2109            block_index
2110        };
2111
2112        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2113        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2114        assert_eq!(blocks.len(), 10);
2115        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2116        assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2117        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2118        assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2119        assert_all_free(blocks.into_iter().skip(4));
2120
2121        {
2122            let mut state = core_state.try_lock().expect("lock state");
2123            // Free property.
2124            assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2125        }
2126        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2127        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2128        assert_all_free(blocks.into_iter().skip(1));
2129    }
2130
2131    #[fuchsia::test]
2132    fn test_string_arrays() {
2133        let core_state = get_state(4096);
2134        {
2135            let mut state = core_state.try_lock().expect("lock state");
2136            let array_index = state.create_string_array("array", 4, 0.into()).unwrap();
2137            assert_eq!(state.set_array_string_slot(array_index, 0, "0"), Ok(()));
2138            assert_eq!(state.set_array_string_slot(array_index, 1, "1"), Ok(()));
2139            assert_eq!(state.set_array_string_slot(array_index, 2, "2"), Ok(()));
2140            assert_eq!(state.set_array_string_slot(array_index, 3, "3"), Ok(()));
2141
2142            // size is 4
2143            assert_matches!(
2144                state.set_array_string_slot(array_index, 4, ""),
2145                Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(4)))
2146            );
2147            assert_matches!(
2148                state.set_array_string_slot(array_index, 5, ""),
2149                Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(5)))
2150            );
2151
2152            for i in 0..4 {
2153                let idx = state
2154                    .get_block::<Array<StringRef>>(array_index)
2155                    .get_string_index_at(i)
2156                    .unwrap();
2157                assert_eq!(i.to_string(), state.load_string(idx).unwrap());
2158            }
2159
2160            assert_eq!(
2161                state.get_block::<Array<StringRef>>(array_index).get_string_index_at(4),
2162                None
2163            );
2164            assert_eq!(
2165                state.get_block::<Array<StringRef>>(array_index).get_string_index_at(5),
2166                None
2167            );
2168
2169            state.clear_array(array_index, 0).unwrap();
2170            state.free_value(array_index).unwrap();
2171        }
2172
2173        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2174        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2175        assert_all_free(blocks.into_iter().skip(1));
2176    }
2177
2178    #[fuchsia::test]
2179    fn update_string_array_value() {
2180        let core_state = get_state(4096);
2181        {
2182            let mut state = core_state.try_lock().expect("lock state");
2183            let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
2184
2185            assert_eq!(state.set_array_string_slot(array_index, 0, "abc"), Ok(()));
2186            assert_eq!(state.set_array_string_slot(array_index, 1, "def"), Ok(()));
2187
2188            assert_eq!(state.set_array_string_slot(array_index, 0, "cba"), Ok(()));
2189            assert_eq!(state.set_array_string_slot(array_index, 1, "fed"), Ok(()));
2190
2191            let cba_index_slot =
2192                state.get_block::<Array<StringRef>>(array_index).get_string_index_at(0).unwrap();
2193            let fed_index_slot =
2194                state.get_block::<Array<StringRef>>(array_index).get_string_index_at(1).unwrap();
2195            assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap());
2196            assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2197
2198            state.clear_array(array_index, 0).unwrap();
2199            state.free_value(array_index).unwrap();
2200        }
2201
2202        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2203        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2204        blocks[1..].iter().enumerate().for_each(|(i, b)| {
2205            assert!(b.block_type() == Some(BlockType::Free), "index is {}", i + 1);
2206        });
2207    }
2208
2209    #[fuchsia::test]
2210    fn set_string_reference_instances_multiple_times_in_array() {
2211        let core_state = get_state(4096);
2212        {
2213            let mut state = core_state.try_lock().expect("lock state");
2214            let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
2215
2216            let abc = "abc";
2217            let def = "def";
2218            let cba = "cba";
2219            let fed = "fed";
2220
2221            state.set_array_string_slot(array_index, 0, abc).unwrap();
2222            state.set_array_string_slot(array_index, 1, def).unwrap();
2223            state.set_array_string_slot(array_index, 0, abc).unwrap();
2224            state.set_array_string_slot(array_index, 1, def).unwrap();
2225
2226            let abc_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2227            let def_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2228            assert_eq!("abc".to_string(), state.load_string(abc_index_slot).unwrap(),);
2229            assert_eq!("def".to_string(), state.load_string(def_index_slot).unwrap(),);
2230
2231            state.set_array_string_slot(array_index, 0, cba).unwrap();
2232            state.set_array_string_slot(array_index, 1, fed).unwrap();
2233
2234            let cba_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2235            let fed_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2236            assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap(),);
2237            assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2238
2239            state.set_array_string_slot(array_index, 0, abc).unwrap();
2240            state.set_array_string_slot(array_index, 1, def).unwrap();
2241
2242            let abc_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2243            let def_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2244            assert_eq!("abc".to_string(), state.load_string(abc_index_slot).unwrap(),);
2245            assert_eq!("def".to_string(), state.load_string(def_index_slot).unwrap(),);
2246
2247            state.set_array_string_slot(array_index, 0, cba).unwrap();
2248            state.set_array_string_slot(array_index, 1, fed).unwrap();
2249
2250            let cba_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2251            let fed_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2252            assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap(),);
2253            assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2254
2255            state.clear_array(array_index, 0).unwrap();
2256            state.free_value(array_index).unwrap();
2257        }
2258
2259        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2260        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2261        blocks[1..].iter().enumerate().for_each(|(i, b)| {
2262            assert!(b.block_type() == Some(BlockType::Free), "index is {}", i + 1);
2263        });
2264    }
2265
2266    #[fuchsia::test]
2267    fn test_empty_value_string_arrays() {
2268        let core_state = get_state(4096);
2269        {
2270            let mut state = core_state.try_lock().expect("lock state");
2271            let array_index = state.create_string_array("array", 4, 0.into()).unwrap();
2272
2273            state.set_array_string_slot(array_index, 0, "").unwrap();
2274            state.set_array_string_slot(array_index, 1, "").unwrap();
2275            state.set_array_string_slot(array_index, 2, "").unwrap();
2276            state.set_array_string_slot(array_index, 3, "").unwrap();
2277        }
2278
2279        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2280        let state = core_state.try_lock().expect("lock state");
2281
2282        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2283        for b in blocks {
2284            if b.block_type() == Some(BlockType::StringReference)
2285                && state.load_string(b.index()).unwrap() == "array"
2286            {
2287                continue;
2288            }
2289
2290            assert_ne!(
2291                b.block_type(),
2292                Some(BlockType::StringReference),
2293                "Got unexpected StringReference, index: {}, value (wrapped in single quotes): '{:?}'",
2294                b.index(),
2295                b.block_type()
2296            );
2297        }
2298    }
2299
2300    #[fuchsia::test]
2301    fn test_bytevector_property() {
2302        let core_state = get_state(4096);
2303
2304        // Creates with value
2305        let block_index = {
2306            let mut state = core_state.try_lock().expect("lock state");
2307            let block_index =
2308                state.create_buffer_property("test", b"test-property", 0.into()).unwrap();
2309            let block = state.get_block::<Buffer>(block_index);
2310            assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2311            assert_eq!(*block.index(), 2);
2312            assert_eq!(*block.parent_index(), 0);
2313            assert_eq!(*block.name_index(), 3);
2314            assert_eq!(block.total_length(), 13);
2315            assert_eq!(*block.extent_index(), 4);
2316            assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2317
2318            let name_block = state.get_block::<StringRef>(block.name_index());
2319            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2320            assert_eq!(name_block.total_length(), 4);
2321            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2322
2323            let extent_block = state.get_block::<Extent>(4.into());
2324            assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2325            assert_eq!(*extent_block.next_extent(), 0);
2326            assert_eq!(
2327                std::str::from_utf8(extent_block.contents().unwrap()).unwrap(),
2328                "test-property\0\0\0\0\0\0\0\0\0\0\0"
2329            );
2330            block_index
2331        };
2332
2333        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2334        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2335        assert_eq!(blocks.len(), 10);
2336        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2337        assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2338        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2339        assert_eq!(blocks[3].block_type(), Some(BlockType::Extent));
2340        assert_all_free(blocks.into_iter().skip(4));
2341
2342        // Free property.
2343        {
2344            let mut state = core_state.try_lock().expect("lock state");
2345            assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2346        }
2347        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2348        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2349        assert_all_free(blocks.into_iter().skip(1));
2350    }
2351
2352    #[fuchsia::test]
2353    fn test_bool() {
2354        let core_state = get_state(4096);
2355        let block_index = {
2356            let mut state = core_state.try_lock().expect("lock state");
2357
2358            // Creates with value
2359            let block_index = state.create_bool("test", true, 0.into()).unwrap();
2360            let block = state.get_block::<Bool>(block_index);
2361            assert_eq!(block.block_type(), Some(BlockType::BoolValue));
2362            assert_eq!(*block.index(), 2);
2363            assert!(block.value());
2364            assert_eq!(*block.name_index(), 3);
2365            assert_eq!(*block.parent_index(), 0);
2366
2367            let name_block = state.get_block::<StringRef>(block.name_index());
2368            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2369            assert_eq!(name_block.total_length(), 4);
2370            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2371            block_index
2372        };
2373
2374        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2375        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2376        assert_eq!(blocks.len(), 9);
2377        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2378        assert_eq!(blocks[1].block_type(), Some(BlockType::BoolValue));
2379        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2380        assert_all_free(blocks.into_iter().skip(3));
2381
2382        // Free metric.
2383        {
2384            let mut state = core_state.try_lock().expect("lock state");
2385            assert!(state.free_value(block_index).is_ok());
2386        }
2387        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2388        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2389        assert_all_free(blocks.into_iter().skip(1));
2390    }
2391
2392    #[fuchsia::test]
2393    fn test_int_array() {
2394        let core_state = get_state(4096);
2395        let block_index = {
2396            let mut state = core_state.try_lock().expect("lock state");
2397            let block_index =
2398                state.create_int_array("test", 5, ArrayFormat::Default, 0.into()).unwrap();
2399            let block = state.get_block::<Array<Int>>(block_index);
2400            assert_eq!(block.block_type(), Some(BlockType::ArrayValue));
2401            assert_eq!(block.order(), 2);
2402            assert_eq!(*block.index(), 4);
2403            assert_eq!(*block.name_index(), 2);
2404            assert_eq!(*block.parent_index(), 0);
2405            assert_eq!(block.slots(), 5);
2406            assert_eq!(block.format(), Some(ArrayFormat::Default));
2407            assert_eq!(block.entry_type(), Some(BlockType::IntValue));
2408
2409            let name_block = state.get_block::<StringRef>(BlockIndex::from(2));
2410            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2411            assert_eq!(name_block.total_length(), 4);
2412            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2413            for i in 0..5 {
2414                state.set_array_int_slot(block_index, i, 3 * i as i64);
2415            }
2416            for i in 0..5 {
2417                assert_eq!(state.get_block::<Array<Int>>(block_index).get(i), Some(3 * i as i64));
2418            }
2419            block_index
2420        };
2421
2422        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2423        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2424        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2425        assert_eq!(blocks[1].block_type(), Some(BlockType::StringReference));
2426        assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2427        assert_eq!(blocks[3].block_type(), Some(BlockType::ArrayValue));
2428        assert_all_free(blocks.into_iter().skip(4));
2429
2430        // Free the array.
2431        {
2432            let mut state = core_state.try_lock().expect("lock state");
2433            assert!(state.free_value(block_index).is_ok());
2434        }
2435        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2436        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2437        assert_all_free(blocks.into_iter().skip(1));
2438    }
2439
2440    #[fuchsia::test]
2441    fn test_write_extent_overflow() {
2442        const SIZE: usize = constants::MAX_ORDER_SIZE * 2;
2443        const EXPECTED_WRITTEN: usize = constants::MAX_ORDER_SIZE - constants::HEADER_SIZE_BYTES;
2444        const TRIED_TO_WRITE: usize = SIZE + 1;
2445        let core_state = get_state(SIZE);
2446        let mut state = core_state.try_lock().unwrap();
2447        let (_, written) = {
2448            let mut txn = Txn::new(&mut state.inner_lock);
2449            let res = txn.write_extents(&[4u8; TRIED_TO_WRITE]).unwrap();
2450            txn.commit();
2451            res
2452        };
2453        assert_eq!(written, EXPECTED_WRITTEN);
2454    }
2455
2456    #[fuchsia::test]
2457    fn overflow_property() {
2458        const SIZE: usize = constants::MAX_ORDER_SIZE * 2;
2459        const EXPECTED_WRITTEN: usize = constants::MAX_ORDER_SIZE - constants::HEADER_SIZE_BYTES;
2460
2461        let core_state = get_state(SIZE);
2462        let mut state = core_state.try_lock().expect("lock state");
2463
2464        let data = "X".repeat(SIZE * 2);
2465        let block_index = state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2466        let block = state.get_block::<Buffer>(block_index);
2467        assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2468        assert_eq!(*block.index(), 2);
2469        assert_eq!(*block.parent_index(), 0);
2470        assert_eq!(*block.name_index(), 3);
2471        assert_eq!(block.total_length(), EXPECTED_WRITTEN);
2472        assert_eq!(*block.extent_index(), 128);
2473        assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2474
2475        let name_block = state.get_block::<StringRef>(block.name_index());
2476        assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2477        assert_eq!(name_block.total_length(), 4);
2478        assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2479
2480        let extent_block = state.get_block::<Extent>(128.into());
2481        assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2482        assert_eq!(extent_block.order(), 7);
2483        assert_eq!(*extent_block.next_extent(), *BlockIndex::EMPTY);
2484        assert_eq!(
2485            extent_block.contents().unwrap(),
2486            data.chars().take(EXPECTED_WRITTEN).map(|c| c as u8).collect::<Vec<u8>>()
2487        );
2488    }
2489
2490    #[fuchsia::test]
2491    fn test_multi_extent_property() {
2492        let core_state = get_state(10000);
2493        let block_index = {
2494            let mut state = core_state.try_lock().expect("lock state");
2495
2496            let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2497            let data = chars.iter().cycle().take(6000).collect::<String>();
2498            let block_index =
2499                state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2500            let block = state.get_block::<Buffer>(block_index);
2501            assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2502            assert_eq!(*block.index(), 2);
2503            assert_eq!(*block.parent_index(), 0);
2504            assert_eq!(*block.name_index(), 3);
2505            assert_eq!(block.total_length(), 6000);
2506            assert_eq!(*block.extent_index(), 128);
2507            assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2508
2509            let name_block = state.get_block::<StringRef>(block.name_index());
2510            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2511            assert_eq!(name_block.total_length(), 4);
2512            assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2513
2514            let extent_block = state.get_block::<Extent>(128.into());
2515            assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2516            assert_eq!(extent_block.order(), 7);
2517            assert_eq!(*extent_block.next_extent(), 256);
2518            assert_eq!(
2519                extent_block.contents().unwrap(),
2520                chars.iter().cycle().take(2040).map(|&c| c as u8).collect::<Vec<u8>>()
2521            );
2522
2523            let extent_block = state.get_block::<Extent>(256.into());
2524            assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2525            assert_eq!(extent_block.order(), 7);
2526            assert_eq!(*extent_block.next_extent(), 384);
2527            assert_eq!(
2528                extent_block.contents().unwrap(),
2529                chars.iter().cycle().skip(2040).take(2040).map(|&c| c as u8).collect::<Vec<u8>>()
2530            );
2531
2532            let extent_block = state.get_block::<Extent>(384.into());
2533            assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2534            assert_eq!(extent_block.order(), 7);
2535            assert_eq!(*extent_block.next_extent(), 0);
2536            assert_eq!(
2537                extent_block.contents().unwrap()[..1920],
2538                chars.iter().cycle().skip(4080).take(1920).map(|&c| c as u8).collect::<Vec<u8>>()[..]
2539            );
2540            assert_eq!(extent_block.contents().unwrap()[1920..], [0u8; 120][..]);
2541            block_index
2542        };
2543
2544        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2545        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2546        assert_eq!(blocks.len(), 11);
2547        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2548        assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2549        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2550        assert_eq!(blocks[8].block_type(), Some(BlockType::Extent));
2551        assert_eq!(blocks[9].block_type(), Some(BlockType::Extent));
2552        assert_eq!(blocks[10].block_type(), Some(BlockType::Extent));
2553        assert_all_free(blocks.into_iter().skip(3).take(5));
2554        // Free property.
2555        {
2556            let mut state = core_state.try_lock().expect("lock state");
2557            assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2558        }
2559        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2560        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2561        assert_all_free(blocks.into_iter().skip(1));
2562    }
2563
2564    #[fuchsia::test]
2565    fn test_freeing_string_references() {
2566        let core_state = get_state(4096);
2567        {
2568            let mut state = core_state.try_lock().expect("lock state");
2569            assert_eq!(state.stats().allocated_blocks, 1);
2570
2571            let block0_index = state.create_node("abcd123456789", 0.into()).unwrap();
2572            let block0_name_index = {
2573                let block0_name_index = state.get_block::<Node>(block0_index).name_index();
2574                let block0_name = state.get_block::<StringRef>(block0_name_index);
2575                assert_eq!(block0_name.order(), 1);
2576                block0_name_index
2577            };
2578            assert_eq!(state.stats().allocated_blocks, 3);
2579
2580            let block1_index = {
2581                let mut txn = Txn::new(&mut state.inner_lock);
2582                let idx = txn.get_or_create_string_reference("abcd").unwrap();
2583                txn.commit();
2584                idx
2585            };
2586            assert_eq!(state.stats().allocated_blocks, 4);
2587            assert_eq!(state.get_block::<StringRef>(block1_index).order(), 0);
2588
2589            let block2_index = {
2590                let mut txn = Txn::new(&mut state.inner_lock);
2591                let idx = txn.get_or_create_string_reference("abcd123456789").unwrap();
2592                txn.commit();
2593                idx
2594            };
2595            assert_eq!(state.get_block::<StringRef>(block2_index).order(), 1);
2596            assert_eq!(block0_name_index, block2_index);
2597            assert_eq!(state.stats().allocated_blocks, 4);
2598
2599            let block3_index = state.create_node("abcd12345678", 0.into()).unwrap();
2600            let block3 = state.get_block::<Node>(block3_index);
2601            let block3_name = state.get_block::<StringRef>(block3.name_index());
2602            assert_eq!(block3_name.order(), 1);
2603            assert_eq!(block3.order(), 0);
2604            assert_eq!(state.stats().allocated_blocks, 6);
2605
2606            let mut long_name = "".to_string();
2607            for _ in 0..3000 {
2608                long_name += " ";
2609            }
2610
2611            let block4_index = state.create_node(long_name, 0.into()).unwrap();
2612            let block4 = state.get_block::<Node>(block4_index);
2613            let block4_name = state.get_block::<StringRef>(block4.name_index());
2614            assert_eq!(block4_name.order(), 7);
2615            assert!(*block4_name.next_extent() != 0);
2616            assert_eq!(state.stats().allocated_blocks, 9);
2617
2618            assert!(state.inner_lock.maybe_free_string_reference(block1_index).is_ok());
2619            assert_eq!(state.stats().deallocated_blocks, 1);
2620            assert!(state.inner_lock.maybe_free_string_reference(block2_index).is_ok());
2621            // no deallocation because same ref as block2 is held in block0_name
2622            assert_eq!(state.stats().deallocated_blocks, 1);
2623            assert!(state.free_value(block3_index).is_ok());
2624            assert_eq!(state.stats().deallocated_blocks, 3);
2625            assert!(state.free_value(block4_index).is_ok());
2626            assert_eq!(state.stats().deallocated_blocks, 6);
2627        }
2628
2629        // Current expected layout of VMO:
2630        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2631        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2632
2633        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2634        assert_eq!(blocks[1].block_type(), Some(BlockType::NodeValue));
2635        assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2636        assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2637        assert_all_free(blocks.into_iter().skip(4));
2638    }
2639
2640    #[fuchsia::test]
2641    fn test_tombstone() {
2642        let core_state = get_state(4096);
2643        let child_block_index = {
2644            let mut state = core_state.try_lock().expect("lock state");
2645
2646            // Create a node value and verify its fields
2647            let block_index = state.create_node("root-node", 0.into()).unwrap();
2648            let block_name_as_string_ref =
2649                state.get_block::<StringRef>(state.get_block::<Node>(block_index).name_index());
2650            assert_eq!(block_name_as_string_ref.order(), 1);
2651            assert_eq!(state.stats().allocated_blocks, 3);
2652            assert_eq!(state.stats().deallocated_blocks, 0);
2653
2654            let child_block_index = state.create_node("child-node", block_index).unwrap();
2655            assert_eq!(state.stats().allocated_blocks, 5);
2656            assert_eq!(state.stats().deallocated_blocks, 0);
2657
2658            // Node still has children, so will become a tombstone.
2659            assert!(state.free_value(block_index).is_ok());
2660            assert_eq!(state.stats().allocated_blocks, 5);
2661            assert_eq!(state.stats().deallocated_blocks, 1);
2662            child_block_index
2663        };
2664
2665        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2666        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2667
2668        // Note that the way Extents get allocated means that they aren't necessarily
2669        // put in the buffer where it would seem they should based on the literal order of allocation.
2670        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2671        assert_eq!(blocks[1].block_type(), Some(BlockType::Tombstone));
2672        assert_eq!(blocks[2].block_type(), Some(BlockType::NodeValue));
2673        assert_eq!(blocks[3].block_type(), Some(BlockType::Free));
2674        assert_eq!(blocks[4].block_type(), Some(BlockType::StringReference));
2675        assert_all_free(blocks.into_iter().skip(5));
2676
2677        // Freeing the child, causes all blocks to be freed.
2678        {
2679            let mut state = core_state.try_lock().expect("lock state");
2680            assert!(state.free_value(child_block_index).is_ok());
2681        }
2682        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2683        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2684        assert_all_free(blocks.into_iter().skip(1));
2685    }
2686
2687    #[fuchsia::test]
2688    fn test_with_header_lock() {
2689        let state = get_state(4096);
2690        // Initial generation count is 0
2691        state.with_current_header(|header| {
2692            assert_eq!(header.generation_count(), 0);
2693        });
2694
2695        // Lock the state
2696        let mut lock_guard = state.try_lock().expect("lock state");
2697        assert!(lock_guard.header().is_locked());
2698        assert_eq!(lock_guard.header().generation_count(), 1);
2699        // Operations on the lock guard do not change the generation counter.
2700        let _ = lock_guard.create_node("test", 0.into()).unwrap();
2701        let _ = lock_guard.create_node("test2", 2.into()).unwrap();
2702        assert_eq!(lock_guard.header().generation_count(), 1);
2703
2704        // Dropping the guard releases the lock.
2705        drop(lock_guard);
2706        state.with_current_header(|header| {
2707            assert_eq!(header.generation_count(), 2);
2708            assert!(!header.is_locked());
2709        });
2710    }
2711
2712    #[fuchsia::test]
2713    async fn test_link() {
2714        // Initialize state and create a link block.
2715        let state = get_state(4096);
2716        let block_index = {
2717            let mut state_guard = state.try_lock().expect("lock state");
2718            let block_index = state_guard
2719                .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2720                    async move {
2721                        let inspector = Inspector::default();
2722                        inspector.root().record_uint("a", 1);
2723                        Ok(inspector)
2724                    }
2725                    .boxed()
2726                })
2727                .unwrap();
2728
2729            // Verify the callback was properly saved.
2730            assert!(state_guard.callbacks().get("link-name-0").is_some());
2731            let callback = state_guard.callbacks().get("link-name-0").unwrap();
2732            match callback().await {
2733                Ok(inspector) => {
2734                    let hierarchy =
2735                        PartialNodeHierarchy::try_from(Snapshot::try_from(&inspector).unwrap())
2736                            .unwrap();
2737                    assert_data_tree!(hierarchy, root: {
2738                        a: 1u64,
2739                    });
2740                }
2741                Err(_) => unreachable!("we never return errors in the callback"),
2742            }
2743
2744            // Verify link block.
2745            let block = state_guard.get_block::<Link>(block_index);
2746            assert_eq!(block.block_type(), Some(BlockType::LinkValue));
2747            assert_eq!(*block.index(), 2);
2748            assert_eq!(*block.parent_index(), 0);
2749            assert_eq!(*block.name_index(), 4);
2750            assert_eq!(*block.content_index(), 6);
2751            assert_eq!(block.link_node_disposition(), Some(LinkNodeDisposition::Inline));
2752
2753            // Verify link's name block.
2754            let name_block = state_guard.get_block::<StringRef>(block.name_index());
2755            assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2756            assert_eq!(name_block.total_length(), 9);
2757            assert_eq!(state_guard.load_string(name_block.index()).unwrap(), "link-name");
2758
2759            // Verify link's content block.
2760            let content_block = state_guard.get_block::<StringRef>(block.content_index());
2761            assert_eq!(content_block.block_type(), Some(BlockType::StringReference));
2762            assert_eq!(content_block.total_length(), 11);
2763            assert_eq!(state_guard.load_string(content_block.index()).unwrap(), "link-name-0");
2764            block_index
2765        };
2766
2767        // Verify all the VMO blocks.
2768        let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2769        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2770        assert_eq!(blocks.len(), 10);
2771        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2772        assert_eq!(blocks[1].block_type(), Some(BlockType::LinkValue));
2773        assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2774        assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2775        assert_eq!(blocks[4].block_type(), Some(BlockType::StringReference));
2776        assert_all_free(blocks.into_iter().skip(5));
2777
2778        // Free link
2779        {
2780            let mut state_guard = state.try_lock().expect("lock state");
2781            assert!(state_guard.free_lazy_node(block_index).is_ok());
2782
2783            // Verify the callback was cleared on free link.
2784            assert!(state_guard.callbacks().get("link-name-0").is_none());
2785        }
2786        let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2787        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2788        assert_all_free(blocks.into_iter().skip(1));
2789
2790        // Verify adding another link generates a different ID regardless of the params.
2791        let mut state_guard = state.try_lock().expect("lock state");
2792        state_guard
2793            .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2794                async move { Ok(Inspector::default()) }.boxed()
2795            })
2796            .unwrap();
2797        let content_block = state_guard.get_block::<StringRef>(6.into());
2798        assert_eq!(state_guard.load_string(content_block.index()).unwrap(), "link-name-1");
2799    }
2800
2801    #[fuchsia::test]
2802    fn free_lazy_node_test() {
2803        let state = get_state(4096);
2804        let (lazy_index, _int_with_magic_name_index) = {
2805            let mut state_guard = state.try_lock().expect("lock state");
2806            let lazy_index = state_guard
2807                .create_lazy_node("lk", 0.into(), LinkNodeDisposition::Inline, || {
2808                    async move { Ok(Inspector::default()) }.boxed()
2809                })
2810                .unwrap();
2811
2812            let magic_link_name = "lk-0";
2813            let int_with_magic_name_index =
2814                state_guard.create_int_metric(magic_link_name, 0, BlockIndex::from(0)).unwrap();
2815
2816            (lazy_index, int_with_magic_name_index)
2817        };
2818
2819        let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2820        let mut blocks = snapshot.scan();
2821        assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Header));
2822
2823        let block = blocks.next().and_then(|b| b.cast::<Link>()).unwrap();
2824        assert_eq!(block.block_type(), Some(BlockType::LinkValue));
2825        assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk");
2826        assert_eq!(state.try_lock().unwrap().load_string(block.content_index()).unwrap(), "lk-0");
2827        assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2828        assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2829        let block = blocks.next().and_then(|b| b.cast::<Int>()).unwrap();
2830        assert_eq!(block.block_type(), Some(BlockType::IntValue));
2831        assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk-0");
2832        assert_all_free(blocks);
2833
2834        state.try_lock().unwrap().free_lazy_node(lazy_index).unwrap();
2835
2836        let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2837        let mut blocks = snapshot.scan();
2838
2839        assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Header));
2840        assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Free));
2841        assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2842        let block = blocks.next().and_then(|b| b.cast::<Int>()).unwrap();
2843        assert_eq!(block.block_type(), Some(BlockType::IntValue));
2844        assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk-0");
2845        assert_all_free(blocks);
2846    }
2847
2848    #[fuchsia::test]
2849    async fn stats() {
2850        // Initialize state and create a link block.
2851        let state = get_state(3 * 4096);
2852        let mut state_guard = state.try_lock().expect("lock state");
2853        let _block1 = state_guard
2854            .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2855                async move {
2856                    let inspector = Inspector::default();
2857                    inspector.root().record_uint("a", 1);
2858                    Ok(inspector)
2859                }
2860                .boxed()
2861            })
2862            .unwrap();
2863        let _block2 = state_guard.create_uint_metric("test", 3, 0.into()).unwrap();
2864        assert_eq!(
2865            state_guard.stats(),
2866            Stats {
2867                total_dynamic_children: 1,
2868                maximum_size: 3 * 4096,
2869                current_size: 4096,
2870                allocated_blocks: 6, /* HEADER, state_guard, _block1 (and content),
2871                                     // "link-name", _block2, "test" */
2872                deallocated_blocks: 0,
2873                failed_allocations: 0,
2874            }
2875        )
2876    }
2877
2878    #[fuchsia::test]
2879    fn transaction_locking() {
2880        let state = get_state(4096);
2881        // Initial generation count is 0
2882        state.with_current_header(|header| {
2883            assert_eq!(header.generation_count(), 0);
2884        });
2885
2886        // Begin a transaction
2887        state.begin_transaction();
2888        state.with_current_header(|header| {
2889            assert_eq!(header.generation_count(), 1);
2890            assert!(header.is_locked());
2891        });
2892
2893        // Operations on the lock  guard do not change the generation counter.
2894        let mut lock_guard1 = state.try_lock().expect("lock state");
2895        assert_eq!(lock_guard1.inner_lock.transaction_count, 1);
2896        assert_eq!(lock_guard1.header().generation_count(), 1);
2897        assert!(lock_guard1.header().is_locked());
2898        let _ = lock_guard1.create_node("test", 0.into());
2899        assert_eq!(lock_guard1.inner_lock.transaction_count, 1);
2900        assert_eq!(lock_guard1.header().generation_count(), 1);
2901
2902        // Dropping the guard releases the mutex lock but the header remains locked.
2903        drop(lock_guard1);
2904        state.with_current_header(|header| {
2905            assert_eq!(header.generation_count(), 1);
2906            assert!(header.is_locked());
2907        });
2908
2909        // When the transaction finishes, the header is unlocked.
2910        state.end_transaction();
2911
2912        state.with_current_header(|header| {
2913            assert_eq!(header.generation_count(), 2);
2914            assert!(!header.is_locked());
2915        });
2916
2917        // Operations under no transaction work as usual.
2918        let lock_guard2 = state.try_lock().expect("lock state");
2919        assert!(lock_guard2.header().is_locked());
2920        assert_eq!(lock_guard2.header().generation_count(), 3);
2921        assert_eq!(lock_guard2.inner_lock.transaction_count, 0);
2922    }
2923
2924    #[fuchsia::test]
2925    async fn update_header_vmo_size() {
2926        let core_state = get_state(3 * 4096);
2927        core_state.get_block(BlockIndex::HEADER, |header: &Block<_, Header>| {
2928            assert_eq!(header.vmo_size(), Ok(Some(4096)));
2929        });
2930        let block1_index = {
2931            let mut state = core_state.try_lock().expect("lock state");
2932
2933            let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2934            let data = chars.iter().cycle().take(6000).collect::<String>();
2935            let block_index =
2936                state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2937            assert_eq!(state.header().vmo_size(), Ok(Some(2 * 4096)));
2938
2939            block_index
2940        };
2941
2942        let block2_index = {
2943            let mut state = core_state.try_lock().expect("lock state");
2944
2945            let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2946            let data = chars.iter().cycle().take(3000).collect::<String>();
2947            let block_index =
2948                state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2949            assert_eq!(state.header().vmo_size(), Ok(Some(3 * 4096)));
2950
2951            block_index
2952        };
2953        // Free properties.
2954        {
2955            let mut state = core_state.try_lock().expect("lock state");
2956            assert!(state.free_string_or_bytes_buffer_property(block1_index).is_ok());
2957            assert!(state.free_string_or_bytes_buffer_property(block2_index).is_ok());
2958        }
2959        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2960        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2961        assert_all_free(blocks.into_iter().skip(1));
2962    }
2963
2964    #[fuchsia::test]
2965    fn test_buffer_property_on_overflow_set() {
2966        let core_state = get_state(4096);
2967        let block_index = {
2968            let mut state = core_state.try_lock().expect("lock state");
2969
2970            // Create string property with value.
2971            let block_index =
2972                state.create_buffer_property("test", b"test-property", 0.into()).unwrap();
2973
2974            // Fill the vmo.
2975            for _ in 10..(4096 / constants::MIN_ORDER_SIZE).try_into().unwrap() {
2976                state.inner_lock.heap.allocate_block(constants::MIN_ORDER_SIZE).unwrap();
2977            }
2978
2979            // Set the value of the string to something very large that causes an overflow.
2980            let values = [b'a'; 8096];
2981            assert!(state.set_buffer_property(block_index, &values).is_err());
2982
2983            // We now expect the length of the payload, as well as the property extent index to be
2984            // reset.
2985            let block = state.get_block::<Buffer>(block_index);
2986            assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2987            assert_eq!(*block.index(), 2);
2988            assert_eq!(*block.parent_index(), 0);
2989            assert_eq!(*block.name_index(), 3);
2990            assert_eq!(block.total_length(), 0);
2991            assert_eq!(*block.extent_index(), 0);
2992            assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2993
2994            block_index
2995        };
2996
2997        // We also expect no extents to be present.
2998        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2999        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
3000        assert_eq!(blocks.len(), 251);
3001        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
3002        assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
3003        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
3004        assert_all_free_or_reserved(blocks.into_iter().skip(3));
3005
3006        {
3007            let mut state = core_state.try_lock().expect("lock state");
3008            // Free property.
3009            assert_matches!(state.free_string_or_bytes_buffer_property(block_index), Ok(()));
3010        }
3011        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
3012        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
3013        assert_all_free_or_reserved(blocks.into_iter().skip(1));
3014    }
3015
3016    #[fuchsia::test]
3017    fn test_string_property_on_overflow_set() {
3018        let core_state = get_state(4096);
3019        {
3020            let mut state = core_state.try_lock().expect("lock state");
3021
3022            // Create string property with value.
3023            let block_index = state.create_string("test", "test-property", 0.into()).unwrap();
3024
3025            // Fill the vmo.
3026            for _ in 10..(4096 / constants::MIN_ORDER_SIZE).try_into().unwrap() {
3027                state.inner_lock.heap.allocate_block(constants::MIN_ORDER_SIZE).unwrap();
3028            }
3029
3030            // make a value too large to fit in the VMO, then attempt to set it into the property
3031            // in order to trigger error conditions and make sure the old value isn't deallocated
3032            let values = ["a"].into_iter().cycle().take(5000).collect::<String>();
3033            assert!(state.set_string_property(block_index, values).is_err());
3034            let block = state.get_block::<Buffer>(block_index);
3035            assert_eq!(*block.index(), 2);
3036            assert_eq!(*block.parent_index(), 0);
3037            assert_eq!(*block.name_index(), 3);
3038
3039            // expect the old value to be there
3040            assert_eq!(
3041                state.load_string(BlockIndex::from(*block.extent_index())).unwrap(),
3042                "test-property"
3043            );
3044
3045            // make sure state can still create some new values
3046            assert!(state.create_int_metric("foo", 1, 0.into()).is_ok());
3047            assert!(state.create_int_metric("bar", 1, 0.into()).is_ok());
3048        };
3049
3050        let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
3051        let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
3052        assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
3053        assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
3054        assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
3055        assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
3056        assert_eq!(blocks[250].block_type(), Some(BlockType::IntValue));
3057        assert_eq!(blocks[251].block_type(), Some(BlockType::StringReference));
3058        assert_eq!(blocks[252].block_type(), Some(BlockType::IntValue));
3059        assert_eq!(blocks[253].block_type(), Some(BlockType::StringReference));
3060        assert_all_free_or_reserved(blocks.into_iter().skip(4).rev().skip(4));
3061    }
3062
3063    #[fuchsia::test]
3064    fn test_reparent_tombstone_leak() {
3065        use inspect_format::Free;
3066
3067        let core_state = get_state(4096);
3068        let mut state = core_state.try_lock().expect("lock state");
3069
3070        let parent_index = state.create_node("parent", 0.into()).unwrap();
3071        let child_index = state.create_node("child", parent_index).unwrap();
3072        let new_parent_index = state.create_node("new_parent", 0.into()).unwrap();
3073
3074        // Verify parent child count is 1
3075        assert_eq!(state.get_block::<Node>(parent_index).child_count(), 1);
3076
3077        // Free parent. It has a child, so it must become a Tombstone.
3078        state.free_value(parent_index).unwrap();
3079        assert_eq!(
3080            state.get_block::<Tombstone>(parent_index).block_type(),
3081            Some(BlockType::Tombstone)
3082        );
3083
3084        // Reparent child to new_parent.
3085        // This decrements parent (Tombstone) child count to 0.
3086        // The Tombstone parent should be freed.
3087        state.reparent(child_index, new_parent_index).unwrap();
3088
3089        // Verify parent is now Free.
3090        // Currently this will panic because parent is still a Tombstone (leaked).
3091        let parent_block = state.get_block::<Free>(parent_index);
3092        assert_eq!(parent_block.block_type(), Some(BlockType::Free));
3093    }
3094
3095    #[fuchsia::test]
3096    fn test_allocate_reserved_value_overflow_leak() {
3097        use inspect_format::HeaderFields;
3098        use inspect_format::constants::MAX_REFERENCE_COUNT;
3099
3100        let core_state = get_state(4096);
3101        let mut state = core_state.try_lock().expect("lock state");
3102
3103        // 1. Create a node "foo" to allocate the string reference "foo".
3104        let parent_index = state.create_node("foo", 0.into()).unwrap();
3105        let node_block = state.get_block::<Node>(parent_index);
3106        let name_index = node_block.name_index();
3107
3108        // 2. Manually set its ref count to MAX_REFERENCE_COUNT.
3109        {
3110            let mut name_block = state.get_block_mut::<StringRef>(name_index);
3111            HeaderFields::set_string_reference_count(&mut name_block, MAX_REFERENCE_COUNT);
3112            assert_eq!(MAX_REFERENCE_COUNT, HeaderFields::string_reference_count(&name_block));
3113        }
3114
3115        // Record stats before the failing allocation
3116        let stats_before = state.stats();
3117
3118        // 3. Try to create another node with the same name "foo". The ref is saturated,
3119        // so this should succeed.
3120        let result = state.create_node("foo", parent_index);
3121        assert!(result.is_ok());
3122        {
3123            let name_block = state.get_block_mut::<StringRef>(name_index);
3124            assert_eq!(MAX_REFERENCE_COUNT, HeaderFields::string_reference_count(&name_block));
3125        }
3126
3127        // 4. Verify that no block was leaked.
3128        let stats_after = state.stats();
3129
3130        let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3131        let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3132        // Add 1 because the new node block
3133        assert_eq!(active_after, active_before + 1);
3134    }
3135
3136    #[fuchsia::test]
3137    fn test_set_array_string_slot_release_failure_leak() {
3138        use inspect_format::HeaderFields;
3139
3140        let core_state = get_state(4096);
3141        let mut state = core_state.try_lock().expect("lock state");
3142
3143        let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
3144        state.set_array_string_slot(array_index, 0, "foo").unwrap();
3145
3146        let foo_index =
3147            state.get_block::<Array<StringRef>>(array_index).get_string_index_at(0).unwrap();
3148
3149        // Manually set "foo" ref count to 0 to force release_string_reference to fail.
3150        {
3151            let mut foo_block = state.get_block_mut::<StringRef>(foo_index);
3152            HeaderFields::set_string_reference_count(&mut foo_block, 0);
3153        }
3154
3155        let stats_before = state.stats();
3156        let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3157
3158        // Try to set slot 0 to "bar".
3159        // This will allocate "bar", then fail to release "foo".
3160        // It should fail and not leak "bar".
3161        let result = state.set_array_string_slot(array_index, 0, "bar");
3162        assert!(result.is_err());
3163
3164        let stats_after = state.stats();
3165        let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3166
3167        // Currently this should fail because "bar" is leaked.
3168        assert_eq!(active_after, active_before);
3169    }
3170
3171    #[fuchsia::test]
3172    fn test_allocate_link_cleanup_failure() {
3173        let core_state = get_state(4096);
3174        {
3175            let mut state = core_state.try_lock().expect("lock state");
3176
3177            // Fill the heap until almost full with nodes sharing the same name.
3178            // This ensures we have many node blocks but only one name block.
3179            let mut nodes = vec![];
3180            while let Ok(idx) = state.create_node("n", 0.into()) {
3181                // "n" will be interned and shared.
3182                nodes.push(idx);
3183            }
3184
3185            // Free one node to make space for exactly one block (the reserved block for the link).
3186            // The name "n" is still held by other nodes, so the name block is not freed.
3187            state.free_value(nodes.pop().unwrap()).unwrap();
3188
3189            // Now call `create_lazy_node`.
3190            // 1. `allocate_reserved_value("n", ...)`:
3191            //    - `allocate_block` succeeds (takes the freed slot).
3192            //    - `get_or_create_string_reference("n")` succeeds (reused).
3193            //    - Returns Pending<Node>.
3194            // 2. `get_or_create_string_reference("new_content")`:
3195            //    - Tries to allocate new string ref block.
3196            //    - Fails (no space).
3197            // 3. Pending<Node> drops.
3198            //    - Should cleanly free the reserved block and release "n" ref.
3199
3200            let result = state.create_lazy_node("n", 0.into(), LinkNodeDisposition::Inline, || {
3201                async move { Ok(Inspector::default()) }.boxed()
3202            });
3203
3204            assert!(result.is_err());
3205        }
3206
3207        // Verify header is intact.
3208        core_state.with_current_header(|header| {
3209            assert_eq!(header.magic_number(), constants::HEADER_MAGIC_NUMBER);
3210            assert_eq!(header.version(), constants::HEADER_VERSION_NUMBER);
3211        });
3212    }
3213
3214    #[fuchsia::test]
3215    fn test_get_or_create_string_reference_payload_failure_leak() {
3216        let core_state = get_state(4096);
3217        let mut state = core_state.try_lock().expect("lock state");
3218
3219        // Allocate blocks of various sizes to leave exactly one 2048-byte block free.
3220        // Free lists initially have one of each: 32, 64, 128, 256, 512, 1024, 2048.
3221        let mut allocated_blocks = vec![];
3222        for size in &[32, 64, 128, 256, 512, 1024] {
3223            allocated_blocks.push(state.inner_lock.heap.allocate_block(*size).unwrap());
3224        }
3225
3226        let stats_before = state.stats();
3227
3228        // Try to create a string reference for a string that is too large to inline.
3229        // A string of size 2040 needs:
3230        // - StringReference block: 2048 bytes (allocated size for 2040 + 4 + 8 = 2052 -> clamped to 2048)
3231        // - Extent block: 16 bytes (allocated size for 4 + 8 = 12 -> 16 bytes)
3232        // The StringReference allocation will succeed (taking the last 2048 bytes).
3233        // The Extent allocation will fail (0 bytes free).
3234        // This should fail and return Err.
3235        let result = {
3236            let mut txn = Txn::new(&mut state.inner_lock);
3237            txn.get_or_create_string_reference("a".repeat(2040))
3238        };
3239        assert!(result.is_err());
3240
3241        // Verify that the StringReference block was NOT leaked.
3242        let stats_after = state.stats();
3243        let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3244        let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3245        assert_eq!(active_after, active_before);
3246
3247        // Clean up remaining blocks.
3248        for block in allocated_blocks {
3249            state.inner_lock.heap.free_block(block).unwrap();
3250        }
3251    }
3252
3253    #[fuchsia::test]
3254    fn test_reparent_to_self_tombstone() {
3255        use inspect_format::Free;
3256
3257        let core_state = get_state(4096);
3258        let mut state = core_state.try_lock().expect("lock state");
3259
3260        let parent_index = state.create_node("parent", 0.into()).unwrap();
3261        let child_index = state.create_node("child", parent_index).unwrap();
3262
3263        // Free parent. It has a child, so it must become a Tombstone.
3264        state.free_value(parent_index).unwrap();
3265        assert_eq!(
3266            state.get_block::<Tombstone>(parent_index).block_type(),
3267            Some(BlockType::Tombstone)
3268        );
3269
3270        // Reparent child to parent (itself).
3271        state.reparent(child_index, parent_index).unwrap();
3272
3273        // Verify parent is still Tombstone (since it was a no-op).
3274        let parent_block = state.get_block::<Tombstone>(parent_index);
3275        assert_eq!(parent_block.block_type(), Some(BlockType::Tombstone));
3276
3277        // Verify that trying to free the child now succeeds.
3278        state.free_value(child_index).unwrap();
3279
3280        // Verify parent is now Free (freed when child count became 0).
3281        let parent_block = state.get_block::<Free>(parent_index);
3282        assert_eq!(parent_block.block_type(), Some(BlockType::Free));
3283    }
3284
3285    #[fuchsia::test]
3286    fn test_uncommitted_txn_drop_no_panic() {
3287        let core_state = get_state(4096);
3288        let mut state = core_state.try_lock().expect("lock state");
3289
3290        let stats_before = state.stats();
3291        {
3292            let mut txn = Txn::new(&mut state.inner_lock);
3293            let _block_index = txn.allocate_block(16).unwrap();
3294            // drops without commit
3295        }
3296        let stats_after = state.stats();
3297        let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3298        let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3299        assert_eq!(active_after, active_before);
3300    }
3301
3302    #[fuchsia::test]
3303    fn test_txn_rollback() {
3304        let core_state = get_state(4096);
3305        let mut state = core_state.try_lock().expect("lock state");
3306
3307        // 1. Create a parent node.
3308        let parent_index = state.create_node("parent", 0.into()).unwrap();
3309        let parent_node = state.get_block::<Node>(parent_index);
3310        assert_eq!(parent_node.child_count(), 0);
3311
3312        let stats_before = state.stats();
3313
3314        // 2. Open a transaction and allocate a child node.
3315        {
3316            let mut txn = Txn::new(&mut state.inner_lock);
3317            let (child_index, name_index) = txn
3318                .allocate_reserved_value("child", parent_index, constants::MIN_ORDER_SIZE)
3319                .unwrap();
3320
3321            txn.block_mut::<Reserved>(child_index).become_node(name_index, parent_index);
3322
3323            // Verify child count was incremented
3324            let parent_node = txn.state.heap.container.block_at_unchecked::<Node>(parent_index);
3325            assert_eq!(parent_node.child_count(), 1);
3326
3327            // Drop txn without committing.
3328        }
3329
3330        // 3. Verify that the parent node child count is back to 0.
3331        let parent_node = state.get_block::<Node>(parent_index);
3332        assert_eq!(parent_node.child_count(), 0);
3333
3334        // 4. Verify no block leak.
3335        let stats_after = state.stats();
3336        let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3337        let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3338        assert_eq!(active_after, active_before);
3339    }
3340}