1use 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
23pub 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
86macro_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#[derive(Clone, Debug)]
225pub struct State {
226 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 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 pub fn try_lock(&self) -> Result<LockedStateGuard<'_>, Error> {
252 let inner_lock = self.inner.lock();
253 LockedStateGuard::new(inner_lock)
254 }
255
256 pub fn begin_transaction(&self) {
259 self.inner.lock().lock_header();
260 }
261
262 pub fn end_transaction(&self) {
265 self.inner.lock().unlock_header();
266 }
267
268 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 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#[derive(Debug, Eq, PartialEq)]
315pub struct Stats {
316 pub total_dynamic_children: usize,
318
319 pub maximum_size: usize,
321
322 pub current_size: usize,
324
325 pub allocated_blocks: usize,
328
329 pub deallocated_blocks: usize,
331
332 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 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 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 pub fn callbacks(&self) -> &HashMap<String, LazyNodeContextFnArc> {
376 &self.inner_lock.callbacks
377 }
378
379 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 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 pub fn free_value(&mut self, index: BlockIndex) -> Result<(), Error> {
409 self.inner_lock.free_value(index)
410 }
411
412 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 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 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 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 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 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#[derive(Derivative)]
577#[derivative(Debug)]
578struct InnerState {
579 #[derivative(Debug = "ignore")]
580 heap: Heap<Container>,
581 #[allow(dead_code)] storage: Arc<<Container as BlockContainer>::ShareableData>,
583 next_unique_link_id: AtomicU64,
584 transaction_count: usize,
585
586 string_reference_block_indexes: HashMap<Arc<Cow<'static, str>>, BlockIndex>,
588 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 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 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 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 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 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 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 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 fn free_value(&mut self, index: BlockIndex) -> Result<(), Error> {
1132 self.delete_value(index)?;
1133 Ok(())
1134 }
1135
1136 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 assert_eq!(constants::MAX_ORDER_SIZE, 2048);
1957
1958 let core_state = get_state(5121); let mut state = core_state.try_lock().expect("lock state");
1960 let name = (0..3000).map(|_| " ").collect::<String>();
1962 let payload = [0u8; 4096]; 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 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); {
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); let mut state = core_state.try_lock().expect("lock state");
2024
2025 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 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 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 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 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 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 {
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 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 {
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 {
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 {
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 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 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 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 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 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 {
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 state.with_current_header(|header| {
2692 assert_eq!(header.generation_count(), 0);
2693 });
2694
2695 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 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 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 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 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 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 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 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 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 {
2780 let mut state_guard = state.try_lock().expect("lock state");
2781 assert!(state_guard.free_lazy_node(block_index).is_ok());
2782
2783 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 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 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, 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 state.with_current_header(|header| {
2883 assert_eq!(header.generation_count(), 0);
2884 });
2885
2886 state.begin_transaction();
2888 state.with_current_header(|header| {
2889 assert_eq!(header.generation_count(), 1);
2890 assert!(header.is_locked());
2891 });
2892
2893 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 drop(lock_guard1);
2904 state.with_current_header(|header| {
2905 assert_eq!(header.generation_count(), 1);
2906 assert!(header.is_locked());
2907 });
2908
2909 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 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 {
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 let block_index =
2972 state.create_buffer_property("test", b"test-property", 0.into()).unwrap();
2973
2974 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 let values = [b'a'; 8096];
2981 assert!(state.set_buffer_property(block_index, &values).is_err());
2982
2983 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 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 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 let block_index = state.create_string("test", "test-property", 0.into()).unwrap();
3024
3025 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 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 assert_eq!(
3041 state.load_string(BlockIndex::from(*block.extent_index())).unwrap(),
3042 "test-property"
3043 );
3044
3045 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 assert_eq!(state.get_block::<Node>(parent_index).child_count(), 1);
3076
3077 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 state.reparent(child_index, new_parent_index).unwrap();
3088
3089 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 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 {
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 let stats_before = state.stats();
3117
3118 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 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 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 {
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 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 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 let mut nodes = vec![];
3180 while let Ok(idx) = state.create_node("n", 0.into()) {
3181 nodes.push(idx);
3183 }
3184
3185 state.free_value(nodes.pop().unwrap()).unwrap();
3188
3189 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 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 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 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 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 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 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 state.reparent(child_index, parent_index).unwrap();
3272
3273 let parent_block = state.get_block::<Tombstone>(parent_index);
3275 assert_eq!(parent_block.block_type(), Some(BlockType::Tombstone));
3276
3277 state.free_value(child_index).unwrap();
3279
3280 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 }
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 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 {
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 let parent_node = txn.state.heap.container.block_at_unchecked::<Node>(parent_index);
3325 assert_eq!(parent_node.child_count(), 1);
3326
3327 }
3329
3330 let parent_node = state.get_block::<Node>(parent_index);
3332 assert_eq!(parent_node.child_count(), 0);
3333
3334 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}