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 pub peak_bytes_requested: usize,
337}
338
339pub struct LockedStateGuard<'a> {
340 inner_lock: MutexGuard<'a, InnerState>,
341 #[cfg(test)]
342 drop: bool,
343}
344
345#[cfg(target_os = "fuchsia")]
346impl LockedStateGuard<'_> {
347 pub fn frozen_vmo_copy(&mut self) -> Result<zx::Vmo, Error> {
349 self.inner_lock.frozen_vmo_copy()
350 }
351}
352
353impl<'a> LockedStateGuard<'a> {
354 fn new(mut inner_lock: MutexGuard<'a, InnerState>) -> Result<Self, Error> {
355 if inner_lock.transaction_count == 0 {
356 inner_lock.header_mut().lock();
357 }
358 Ok(Self {
359 inner_lock,
360 #[cfg(test)]
361 drop: true,
362 })
363 }
364
365 pub fn stats(&self) -> Stats {
367 Stats {
368 total_dynamic_children: self.inner_lock.callbacks.len(),
369 current_size: self.inner_lock.heap.current_size(),
370 maximum_size: self.inner_lock.heap.maximum_size(),
371 allocated_blocks: self.inner_lock.heap.total_allocated_blocks(),
372 deallocated_blocks: self.inner_lock.heap.total_deallocated_blocks(),
373 failed_allocations: self.inner_lock.heap.failed_allocations(),
374 peak_bytes_requested: self.inner_lock.heap.peak_bytes_requested(),
375 }
376 }
377
378 pub fn callbacks(&self) -> &HashMap<String, LazyNodeContextFnArc> {
380 &self.inner_lock.callbacks
381 }
382
383 pub fn create_node<'b>(
385 &mut self,
386 name: impl Into<Cow<'b, str>>,
387 parent_index: BlockIndex,
388 ) -> Result<BlockIndex, Error> {
389 self.inner_lock.create_node(name, parent_index)
390 }
391
392 pub fn create_lazy_node<'b, F>(
395 &mut self,
396 name: impl Into<Cow<'b, str>>,
397 parent_index: BlockIndex,
398 disposition: LinkNodeDisposition,
399 callback: F,
400 ) -> Result<BlockIndex, Error>
401 where
402 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
403 {
404 self.inner_lock.create_lazy_node(name, parent_index, disposition, callback)
405 }
406
407 pub fn free_lazy_node(&mut self, index: BlockIndex) -> Result<(), Error> {
408 self.inner_lock.free_lazy_node(index)
409 }
410
411 pub fn free_value(&mut self, index: BlockIndex) -> Result<(), Error> {
413 self.inner_lock.free_value(index)
414 }
415
416 pub fn create_buffer_property<'b>(
418 &mut self,
419 name: impl Into<Cow<'b, str>>,
420 value: &[u8],
421 parent_index: BlockIndex,
422 ) -> Result<BlockIndex, Error> {
423 self.inner_lock.create_buffer_property(name, value, parent_index)
424 }
425
426 pub fn create_string<'b, 'c>(
429 &mut self,
430 name: impl Into<Cow<'b, str>>,
431 value: impl Into<Cow<'c, str>>,
432 parent_index: BlockIndex,
433 ) -> Result<BlockIndex, Error> {
434 self.inner_lock.create_string(name, value, parent_index)
435 }
436
437 pub fn reparent(
438 &mut self,
439 being_reparented: BlockIndex,
440 new_parent: BlockIndex,
441 ) -> Result<(), Error> {
442 self.inner_lock.reparent(being_reparented, new_parent)
443 }
444
445 pub fn set_name<'b>(
446 &mut self,
447 block_index: BlockIndex,
448 name: impl Into<Cow<'b, str>>,
449 ) -> Result<(), Error> {
450 self.inner_lock.set_name(block_index, name)
451 }
452
453 pub fn free_string_or_bytes_buffer_property(&mut self, index: BlockIndex) -> Result<(), Error> {
455 self.inner_lock.free_string_or_bytes_buffer_property(index)
456 }
457
458 pub fn set_string_property<'b>(
460 &mut self,
461 block_index: BlockIndex,
462 value: impl Into<Cow<'b, str>>,
463 ) -> Result<(), Error> {
464 self.inner_lock.set_string_property(block_index, value)
465 }
466
467 pub fn set_buffer_property(
469 &mut self,
470 block_index: BlockIndex,
471 value: &[u8],
472 ) -> Result<(), Error> {
473 self.inner_lock.set_buffer_property(block_index, value)
474 }
475
476 pub fn create_bool<'b>(
477 &mut self,
478 name: impl Into<Cow<'b, str>>,
479 value: bool,
480 parent_index: BlockIndex,
481 ) -> Result<BlockIndex, Error> {
482 self.inner_lock.create_bool(name, value, parent_index)
483 }
484
485 pub fn set_bool(&mut self, block_index: BlockIndex, value: bool) {
486 self.inner_lock.set_bool(block_index, value)
487 }
488
489 locked_state_metric_fns!(int, i64);
490 locked_state_metric_fns!(uint, u64);
491 locked_state_metric_fns!(double, f64);
492
493 locked_state_array_fns!(int, i64, IntValue);
494 locked_state_array_fns!(uint, u64, UintValue);
495 locked_state_array_fns!(double, f64, DoubleValue);
496
497 pub fn clear_array(
499 &mut self,
500 block_index: BlockIndex,
501 start_slot_index: usize,
502 ) -> Result<(), Error> {
503 self.inner_lock.clear_array(block_index, start_slot_index)
504 }
505
506 pub fn create_string_array<'b>(
507 &mut self,
508 name: impl Into<Cow<'b, str>>,
509 slots: usize,
510 parent_index: BlockIndex,
511 ) -> Result<BlockIndex, Error> {
512 self.inner_lock.create_string_array(name, slots, parent_index)
513 }
514
515 pub fn get_array_size(&self, block_index: BlockIndex) -> usize {
516 self.inner_lock.get_array_size(block_index)
517 }
518
519 pub fn set_array_string_slot<'b>(
520 &mut self,
521 block_index: BlockIndex,
522 slot_index: usize,
523 value: impl Into<Cow<'b, str>>,
524 ) -> Result<(), Error> {
525 self.inner_lock.set_array_string_slot(block_index, slot_index, value)
526 }
527}
528
529impl Drop for LockedStateGuard<'_> {
530 fn drop(&mut self) {
531 #[cfg(test)]
532 {
533 if !self.drop {
534 return;
535 }
536 }
537 if self.inner_lock.transaction_count == 0 {
538 self.inner_lock.header_mut().unlock();
539 }
540 }
541}
542
543#[cfg(test)]
544impl<'a> LockedStateGuard<'a> {
545 fn without_gen_count_changes(inner_lock: MutexGuard<'a, InnerState>) -> Self {
546 Self { inner_lock, drop: false }
547 }
548
549 pub(crate) fn load_string(&self, index: BlockIndex) -> Result<String, Error> {
550 self.inner_lock.load_key_string(index)
551 }
552
553 pub(crate) fn allocate_link<'b, 'c>(
554 &mut self,
555 name: impl Into<Cow<'b, str>>,
556 content: impl Into<Cow<'c, str>>,
557 disposition: LinkNodeDisposition,
558 parent_index: BlockIndex,
559 ) -> Result<BlockIndex, Error> {
560 let mut txn = Txn::new(&mut self.inner_lock);
561 let link = txn.allocate_link(name, content, disposition, parent_index)?;
562 txn.commit();
563 Ok(link)
564 }
565
566 #[track_caller]
567 pub(crate) fn get_block<K: inspect_format::BlockKind>(
568 &self,
569 index: BlockIndex,
570 ) -> Block<&Container, K> {
571 self.inner_lock.heap.container.maybe_block_at::<K>(index).unwrap()
572 }
573
574 fn header(&self) -> Block<&Container, inspect_format::Header> {
575 self.get_block(BlockIndex::HEADER)
576 }
577
578 #[track_caller]
579 fn get_block_mut<K: inspect_format::BlockKind>(
580 &mut self,
581 index: BlockIndex,
582 ) -> Block<&mut Container, K> {
583 self.inner_lock.heap.container.maybe_block_at_mut::<K>(index).unwrap()
584 }
585}
586
587#[derive(Derivative)]
589#[derivative(Debug)]
590struct InnerState {
591 #[derivative(Debug = "ignore")]
592 heap: Heap<Container>,
593 #[allow(dead_code)] storage: Arc<<Container as BlockContainer>::ShareableData>,
595 next_unique_link_id: AtomicU64,
596 transaction_count: usize,
597
598 string_reference_block_indexes: HashMap<Arc<Cow<'static, str>>, BlockIndex>,
600 block_index_string_references: HashMap<BlockIndex, Arc<Cow<'static, str>>>,
602
603 #[derivative(Debug = "ignore")]
604 callbacks: HashMap<String, LazyNodeContextFnArc>,
605}
606
607#[cfg(target_os = "fuchsia")]
608impl InnerState {
609 fn frozen_vmo_copy(&mut self) -> Result<zx::Vmo, Error> {
610 if self.transaction_count > 0 {
611 return Err(Error::ConcurrentTransaction(self.transaction_count));
612 }
613
614 let old = self.header_mut().freeze();
615 let child = self
616 .storage
617 .create_child(
618 zx::VmoChildOptions::SNAPSHOT | zx::VmoChildOptions::NO_WRITE,
619 0,
620 self.storage.get_size().map_err(Error::GetVmoSize)?,
621 )
622 .map_err(Error::CreateChildVmo);
623 self.header_mut().thaw(old);
624 child
625 }
626}
627
628#[derive(Debug)]
629enum Undo {
630 FreeBlock(BlockIndex),
631 ReleaseStringRef(BlockIndex),
632 DecrementChildCount(BlockIndex),
633 IncrementChildCount(BlockIndex),
634 SetParent(BlockIndex, BlockIndex),
635 FreeExtentChain(BlockIndex),
636}
637
638struct Txn<'a> {
639 state: &'a mut InnerState,
640 undo: SmallVec<[Undo; 8]>,
641 to_free_on_commit: Vec<BlockIndex>,
642 committed: bool,
643}
644
645impl<'a> Txn<'a> {
646 fn new(state: &'a mut InnerState) -> Self {
647 Self { state, undo: SmallVec::new(), to_free_on_commit: Vec::new(), committed: false }
648 }
649
650 fn commit(mut self) {
651 self.committed = true;
652 for index in self.to_free_on_commit.drain(..) {
653 let block = self.state.heap.container.block_at(index);
654 match block.block_type() {
655 Some(BlockType::Tombstone) => {
656 let tombstone = block.cast_unchecked::<Tombstone>();
657 if tombstone.child_count() == 0 {
658 if let Err(e) = self.state.heap.free_block(index) {
659 log::error!("Failed to free deferred tombstone: {:?}", e);
660 }
661 } else {
662 log::error!(
663 "Deferred free: tombstone {:?} child count is not 0 ({})",
664 index,
665 tombstone.child_count()
666 );
667 }
668 }
669 Some(t) => {
670 log::error!("Deferred free: expected Tombstone at {:?}, got {:?}", index, t);
671 }
672 None => {
673 log::error!("Deferred free: invalid block at {:?}", index);
674 }
675 }
676 }
677 }
678
679 fn allocate_block(&mut self, size: usize) -> Result<BlockIndex, Error> {
680 let block_index = self.state.heap.allocate_block(size)?;
681 self.undo.push(Undo::FreeBlock(block_index));
682 Ok(block_index)
683 }
684
685 fn intern_and_ref_string<'b>(
686 &mut self,
687 v: impl Into<Cow<'b, str>>,
688 ) -> Result<BlockIndex, Error> {
689 let block_index = self.get_or_create_string_reference(v)?;
690 self.state
691 .heap
692 .container
693 .block_at_unchecked_mut::<StringRef>(block_index)
694 .increment_ref_count()?;
695 self.undo.push(Undo::ReleaseStringRef(block_index));
696 Ok(block_index)
697 }
698
699 fn increment_child_count(&mut self, parent_index: BlockIndex) -> Result<(), Error> {
700 if parent_index != BlockIndex::EMPTY {
701 let mut parent_block =
702 self.state.heap.container.block_at_unchecked_mut::<Node>(parent_index);
703 match parent_block.block_type() {
704 Some(BlockType::NodeValue) | Some(BlockType::Tombstone) => {
705 parent_block.set_child_count(parent_block.child_count() + 1);
706 self.undo.push(Undo::DecrementChildCount(parent_index));
707 Ok(())
708 }
709 Some(BlockType::Header) => Ok(()),
710 _ => Err(Error::InvalidBlockType(parent_index, parent_block.block_type_raw())),
711 }
712 } else {
713 Ok(())
714 }
715 }
716
717 fn write_extents(&mut self, value: &[u8]) -> Result<(BlockIndex, usize), Error> {
718 if value.is_empty() {
719 return Ok((BlockIndex::ROOT, 0));
721 }
722 let mut offset = 0;
723 let total_size = value.len();
724 let head_extent_index =
725 self.state.heap.allocate_block(utils::block_size_for_payload(total_size - offset))?;
726 let mut extent_block_index = head_extent_index;
727 while offset < total_size {
728 let bytes_written = {
729 let mut extent_block = self
730 .state
731 .heap
732 .container
733 .block_at_unchecked_mut::<Reserved>(extent_block_index)
734 .become_extent(BlockIndex::EMPTY);
735 extent_block.set_contents(&value[offset..])
736 };
737 offset += bytes_written;
738 if offset < total_size {
739 let Ok(block_index) = self
740 .state
741 .heap
742 .allocate_block(utils::block_size_for_payload(total_size - offset))
743 else {
744 self.undo.push(Undo::FreeExtentChain(head_extent_index));
746 return Ok((head_extent_index, offset));
747 };
748 self.state
749 .heap
750 .container
751 .block_at_unchecked_mut::<Extent>(extent_block_index)
752 .set_next_index(block_index);
753 extent_block_index = block_index;
754 }
755 }
756 self.undo.push(Undo::FreeExtentChain(head_extent_index));
757 Ok((head_extent_index, offset))
758 }
759
760 fn release_string_ref(&mut self, i: BlockIndex) -> Result<(), Error> {
761 self.state.release_string_reference(i)
762 }
763
764 fn block_mut<K: inspect_format::BlockKind>(
765 &mut self,
766 i: BlockIndex,
767 ) -> Block<&mut Container, K> {
768 self.state.heap.container.block_at_unchecked_mut::<K>(i)
769 }
770
771 fn allocate_reserved_value<'b>(
772 &mut self,
773 name: impl Into<Cow<'b, str>>,
774 parent_index: BlockIndex,
775 block_size: usize,
776 ) -> Result<(BlockIndex, BlockIndex), Error> {
777 let block_index = self.allocate_block(block_size)?;
778 let name_index = self.intern_and_ref_string(name)?;
779 self.increment_child_count(parent_index)?;
780 Ok((block_index, name_index))
781 }
782
783 fn allocate_link<'b, 'c>(
784 &mut self,
785 name: impl Into<Cow<'b, str>>,
786 content: impl Into<Cow<'c, str>>,
787 disposition: LinkNodeDisposition,
788 parent_index: BlockIndex,
789 ) -> Result<BlockIndex, Error> {
790 let (block_index, name_index) =
791 self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
792 let content_index = self.intern_and_ref_string(content)?;
793
794 self.block_mut::<Reserved>(block_index).become_link(
795 name_index,
796 parent_index,
797 content_index,
798 disposition,
799 );
800 Ok(block_index)
801 }
802
803 fn get_or_create_string_reference<'b>(
804 &mut self,
805 value: impl Into<Cow<'b, str>>,
806 ) -> Result<BlockIndex, Error> {
807 let value = value.into();
808 match self.state.string_reference_block_indexes.get(&value) {
809 Some(index) => Ok(*index),
810 None => {
811 let undo_len_before = self.undo.len();
812 let block_size = utils::block_size_for_payload(
813 value.len() + constants::STRING_REFERENCE_TOTAL_LENGTH_BYTES,
814 );
815
816 let block_index = self.allocate_block(block_size)?;
817 self.block_mut::<Reserved>(block_index).become_string_reference();
818
819 self.write_string_reference_payload(block_index, &value)?;
820
821 let owned_value = Arc::new(value.into_owned().into());
822 self.state
823 .string_reference_block_indexes
824 .insert(Arc::clone(&owned_value), block_index);
825 self.state.block_index_string_references.insert(block_index, owned_value);
826
827 self.undo.truncate(undo_len_before);
834
835 Ok(block_index)
836 }
837 }
838 }
839
840 fn write_string_reference_payload(
841 &mut self,
842 block_index: BlockIndex,
843 value: &str,
844 ) -> Result<(), Error> {
845 let value_bytes = value.as_bytes();
846 let (head_extent, bytes_written) = {
847 let inlined = self.state.inline_string_reference(block_index, value.as_bytes());
848 if inlined < value.len() {
849 let (head, in_extents) = self.write_extents(&value_bytes[inlined..])?;
850 (head, inlined + in_extents)
851 } else {
852 (BlockIndex::EMPTY, inlined)
853 }
854 };
855 let mut block = self.block_mut::<StringRef>(block_index);
856 block.set_next_index(head_extent);
857 block.set_total_length(bytes_written.try_into().unwrap_or(u32::MAX));
858 Ok(())
859 }
860
861 fn reparent(
862 &mut self,
863 being_reparented: BlockIndex,
864 new_parent: BlockIndex,
865 ) -> Result<(), Error> {
866 self.state.check_lineage(being_reparented, new_parent)?;
867 let original_parent_idx =
868 self.state.heap.container.block_at_unchecked::<Node>(being_reparented).parent_index();
869 if original_parent_idx == new_parent {
870 return Ok(());
871 }
872
873 if original_parent_idx != BlockIndex::ROOT {
874 let original_parent_block = self.state.heap.container.block_at(original_parent_idx);
875 match original_parent_block.block_type() {
876 Some(BlockType::Tombstone) => {
877 let mut parent = self.block_mut::<Tombstone>(original_parent_idx);
878 let child_count = parent.child_count() - 1;
879 parent.set_child_count(child_count);
880 self.undo.push(Undo::IncrementChildCount(original_parent_idx));
881 if child_count == 0 {
882 self.to_free_on_commit.push(original_parent_idx);
884 }
885 }
886 Some(BlockType::NodeValue) => {
887 let mut parent = self.block_mut::<Node>(original_parent_idx);
888 let child_count = parent.child_count() - 1;
889 parent.set_child_count(child_count);
890 self.undo.push(Undo::IncrementChildCount(original_parent_idx));
891 }
892 _ => {
893 return Err(Error::InvalidBlockType(
894 original_parent_idx,
895 original_parent_block.block_type_raw(),
896 ));
897 }
898 }
899 }
900
901 self.block_mut::<Node>(being_reparented).set_parent(new_parent);
902 self.undo.push(Undo::SetParent(being_reparented, original_parent_idx));
903
904 if new_parent != BlockIndex::ROOT {
905 let mut new_parent_block = self.block_mut::<Node>(new_parent);
906 let child_count = new_parent_block.child_count() + 1;
907 new_parent_block.set_child_count(child_count);
908 self.undo.push(Undo::DecrementChildCount(new_parent));
909 }
910
911 Ok(())
912 }
913
914 fn clear_array(
915 &mut self,
916 block_index: BlockIndex,
917 start_slot_index: usize,
918 ) -> Result<(), Error> {
919 let block = self.block_mut::<Array<Unknown>>(block_index);
920 match block.entry_type() {
921 Some(value) if value.is_numeric_value() => {
922 self.block_mut::<Array<Unknown>>(block_index).clear(start_slot_index);
923 }
924 Some(BlockType::StringReference) => {
925 let array_slots = block.slots();
926 for i in start_slot_index..array_slots {
927 let index = {
928 let mut block = self.block_mut::<Array<StringRef>>(block_index);
929 let index =
930 block.get_string_index_at(i).ok_or(Error::InvalidArrayIndex(i))?;
931 if index == BlockIndex::EMPTY {
932 continue;
933 }
934 block.set_string_slot(i, BlockIndex::EMPTY);
935 index
936 };
937 self.release_string_ref(index)?;
938 }
939 }
940 _ => return Err(Error::InvalidArrayType(block_index)),
941 }
942 Ok(())
943 }
944}
945
946impl Drop for Txn<'_> {
947 fn drop(&mut self) {
948 if self.committed {
949 return;
950 }
951 while let Some(u) = self.undo.pop() {
952 self.state.apply_undo(u);
953 }
954 }
955}
956
957impl InnerState {
958 pub fn new(
960 heap: Heap<Container>,
961 storage: Arc<<Container as BlockContainer>::ShareableData>,
962 ) -> Self {
963 Self {
964 heap,
965 storage,
966 next_unique_link_id: AtomicU64::new(0),
967 callbacks: HashMap::new(),
968 transaction_count: 0,
969 string_reference_block_indexes: HashMap::new(),
970 block_index_string_references: HashMap::new(),
971 }
972 }
973
974 fn apply_undo(&mut self, undo: Undo) {
975 match undo {
976 Undo::FreeBlock(i) => {
977 if let Err(e) = self.heap.free_block(i) {
978 log::error!("Undo FreeBlock({:?}) failed: {:?}", i, e);
979 }
980 }
981 Undo::ReleaseStringRef(i) => {
982 if let Err(e) = self.release_string_reference(i) {
983 log::error!("Undo ReleaseStringRef({:?}) failed: {:?}", i, e);
984 }
985 }
986 Undo::DecrementChildCount(parent_index) => {
987 if parent_index == BlockIndex::EMPTY {
988 return;
989 }
990 let parent = self.heap.container.block_at_mut(parent_index);
991 match parent.block_type() {
992 Some(BlockType::Tombstone) => {
993 let mut parent = parent.cast_unchecked::<Tombstone>();
994 let child_count = parent.child_count() - 1;
995 if child_count == 0 {
996 if let Err(e) = self.heap.free_block(parent_index) {
997 log::error!(
998 "Undo DecrementChildCount free tombstone parent({:?}) failed: {:?}",
999 parent_index,
1000 e
1001 );
1002 }
1003 } else {
1004 parent.set_child_count(child_count);
1005 }
1006 }
1007 Some(BlockType::NodeValue) => {
1008 let mut parent = parent.cast_unchecked::<Node>();
1009 let child_count = parent.child_count() - 1;
1010 parent.set_child_count(child_count);
1011 }
1012 _ => {
1013 log::error!(
1014 "Undo DecrementChildCount: invalid parent block type raw={:?} for parent={:?}",
1015 parent.block_type_raw(),
1016 parent_index
1017 );
1018 }
1019 }
1020 }
1021 Undo::IncrementChildCount(parent_index) => {
1022 if parent_index == BlockIndex::EMPTY || parent_index == BlockIndex::ROOT {
1023 return;
1024 }
1025 let parent = self.heap.container.block_at_mut(parent_index);
1026 match parent.block_type() {
1027 Some(BlockType::Tombstone) => {
1028 let mut parent = parent.cast_unchecked::<Tombstone>();
1029 parent.set_child_count(parent.child_count() + 1);
1030 }
1031 Some(BlockType::NodeValue) => {
1032 let mut parent = parent.cast_unchecked::<Node>();
1033 parent.set_child_count(parent.child_count() + 1);
1034 }
1035 _ => {
1036 log::error!(
1037 "Undo IncrementChildCount: invalid parent block type raw={:?} for parent={:?}",
1038 parent.block_type_raw(),
1039 parent_index
1040 );
1041 }
1042 }
1043 }
1044 Undo::SetParent(child_index, parent_index) => {
1045 self.heap
1046 .container
1047 .block_at_unchecked_mut::<Node>(child_index)
1048 .set_parent(parent_index);
1049 }
1050 Undo::FreeExtentChain(head) => {
1051 if let Err(e) = self.free_extents(head) {
1052 log::error!("Undo FreeExtentChain({:?}) failed: {:?}", head, e);
1053 }
1054 }
1055 }
1056 }
1057
1058 #[inline]
1059 fn header_mut(&mut self) -> Block<&mut Container, inspect_format::Header> {
1060 self.heap.container.block_at_unchecked_mut(BlockIndex::HEADER)
1061 }
1062
1063 fn lock_header(&mut self) {
1064 if self.transaction_count == 0 {
1065 self.header_mut().lock();
1066 }
1067 self.transaction_count += 1;
1068 }
1069
1070 fn unlock_header(&mut self) {
1071 self.transaction_count -= 1;
1072 if self.transaction_count == 0 {
1073 self.header_mut().unlock();
1074 }
1075 }
1076
1077 fn create_node<'a>(
1078 &mut self,
1079 name: impl Into<Cow<'a, str>>,
1080 parent_index: BlockIndex,
1081 ) -> Result<BlockIndex, Error> {
1082 let mut txn = Txn::new(self);
1083 let (block_index, name_index) =
1084 txn.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
1085 txn.block_mut::<Reserved>(block_index).become_node(name_index, parent_index);
1086 txn.commit();
1087 Ok(block_index)
1088 }
1089
1090 fn create_lazy_node<'a, F>(
1093 &mut self,
1094 name: impl Into<Cow<'a, str>>,
1095 parent_index: BlockIndex,
1096 disposition: LinkNodeDisposition,
1097 callback: F,
1098 ) -> Result<BlockIndex, Error>
1099 where
1100 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
1101 {
1102 let name = name.into();
1103 let content = self.unique_link_name(&name);
1104 let link = {
1105 let mut txn = Txn::new(self);
1106 let link = txn.allocate_link(name, &content, disposition, parent_index)?;
1107 txn.commit();
1108 link
1109 };
1110 self.callbacks.insert(content, Arc::from(callback));
1111 Ok(link)
1112 }
1113
1114 fn free_lazy_node(&mut self, index: BlockIndex) -> Result<(), Error> {
1115 let mut txn = Txn::new(self);
1116 let content_block_index =
1117 txn.state.heap.container.block_at_unchecked::<Link>(index).content_index();
1118 let content_block_type =
1119 txn.state.heap.container.block_at(content_block_index).block_type();
1120 let content = txn.state.load_key_string(content_block_index)?;
1121 txn.state.delete_value(index)?;
1122 match content_block_type {
1124 Some(BlockType::StringReference) => {
1125 txn.release_string_ref(content_block_index)?;
1126 }
1127 _ => {
1128 txn.state.heap.free_block(content_block_index).expect("Failed to free block");
1129 }
1130 }
1131
1132 txn.state.callbacks.remove(content.as_str());
1133 txn.commit();
1134 Ok(())
1135 }
1136
1137 fn unique_link_name(&mut self, prefix: &str) -> String {
1138 let id = self.next_unique_link_id.fetch_add(1, Ordering::Relaxed);
1139 format!("{prefix}-{id}")
1140 }
1141
1142 fn free_value(&mut self, index: BlockIndex) -> Result<(), Error> {
1144 self.delete_value(index)?;
1145 Ok(())
1146 }
1147
1148 fn create_buffer_property<'a>(
1150 &mut self,
1151 name: impl Into<Cow<'a, str>>,
1152 value: &[u8],
1153 parent_index: BlockIndex,
1154 ) -> Result<BlockIndex, Error> {
1155 let mut txn = Txn::new(self);
1156 let (block_index, name_index) =
1157 txn.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
1158 txn.block_mut::<Reserved>(block_index).become_property(
1159 name_index,
1160 parent_index,
1161 PropertyFormat::Bytes,
1162 );
1163
1164 let (extent_index, written) = txn.write_extents(value)?;
1165 let mut block = txn.block_mut::<Buffer>(block_index);
1166 block.set_total_length(written.try_into().unwrap_or(u32::MAX));
1167 block.set_extent_index(extent_index);
1168
1169 txn.commit();
1170 Ok(block_index)
1171 }
1172
1173 fn create_string<'a, 'b>(
1176 &mut self,
1177 name: impl Into<Cow<'a, str>>,
1178 value: impl Into<Cow<'b, str>>,
1179 parent_index: BlockIndex,
1180 ) -> Result<BlockIndex, Error> {
1181 let mut txn = Txn::new(self);
1182 let (block_index, name_index) =
1183 txn.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
1184 txn.block_mut::<Reserved>(block_index).become_property(
1185 name_index,
1186 parent_index,
1187 PropertyFormat::StringReference,
1188 );
1189
1190 let value_index = txn.intern_and_ref_string(value)?;
1191
1192 let mut block = txn.block_mut::<Buffer>(block_index);
1193 block.set_extent_index(value_index);
1194 block.set_total_length(0);
1195
1196 txn.commit();
1197 Ok(block_index)
1198 }
1199
1200 fn inline_string_reference(&mut self, block_index: BlockIndex, value: &[u8]) -> usize {
1203 self.heap.container.block_at_unchecked_mut::<StringRef>(block_index).write_inline(value)
1204 }
1205
1206 fn release_string_reference(&mut self, block_index: BlockIndex) -> Result<(), Error> {
1209 self.heap
1210 .container
1211 .block_at_unchecked_mut::<StringRef>(block_index)
1212 .decrement_ref_count()?;
1213 self.maybe_free_string_reference(block_index)
1214 }
1215
1216 fn maybe_free_string_reference(&mut self, block_index: BlockIndex) -> Result<(), Error> {
1219 let block = self.heap.container.block_at_unchecked::<StringRef>(block_index);
1220 if block.reference_count() != 0 {
1221 return Ok(());
1222 }
1223 let first_extent = block.next_extent();
1224 self.heap.free_block(block_index)?;
1225 let str_ref = self.block_index_string_references.remove(&block_index).expect("blk idx key");
1226 self.string_reference_block_indexes.remove(&str_ref);
1227
1228 if first_extent == BlockIndex::EMPTY {
1229 return Ok(());
1230 }
1231 self.free_extents(first_extent)
1232 }
1233
1234 fn load_key_string(&self, index: BlockIndex) -> Result<String, Error> {
1235 let block = self.heap.container.block_at(index);
1236 match block.block_type() {
1237 Some(BlockType::StringReference) => {
1238 self.read_string_reference(block.cast::<StringRef>().unwrap())
1239 }
1240 Some(BlockType::Name) => block
1241 .cast::<Name>()
1242 .unwrap()
1243 .contents()
1244 .map(|s| s.to_string())
1245 .map_err(|_| Error::NameNotUtf8),
1246 _ => Err(Error::InvalidBlockTypeNumber(index, block.block_type_raw())),
1247 }
1248 }
1249
1250 fn read_string_reference(&self, block: Block<&Container, StringRef>) -> Result<String, Error> {
1252 let mut content = block.inline_data()?.to_vec();
1253 let mut next = block.next_extent();
1254 while next != BlockIndex::EMPTY {
1255 let next_block = self.heap.container.block_at_unchecked::<Extent>(next);
1256 content.extend_from_slice(next_block.contents()?);
1257 next = next_block.next_extent();
1258 }
1259
1260 content.truncate(block.total_length());
1261 String::from_utf8(content).ok().ok_or(Error::NameNotUtf8)
1262 }
1263
1264 fn free_string_or_bytes_buffer_property(&mut self, index: BlockIndex) -> Result<(), Error> {
1266 let (format, data_index) = {
1267 let block = self.heap.container.block_at_unchecked::<Buffer>(index);
1268 (block.format(), block.extent_index())
1269 };
1270 match format {
1271 Some(PropertyFormat::String) | Some(PropertyFormat::Bytes) => {
1272 self.free_extents(data_index)?;
1273 }
1274 Some(PropertyFormat::StringReference) => {
1275 if data_index != BlockIndex::EMPTY {
1276 self.release_string_reference(data_index)?;
1277 }
1278 }
1279 _ => {
1280 return Err(Error::VmoFormat(FormatError::InvalidBufferFormat(
1281 self.heap.container.block_at_unchecked(index).format_raw(),
1282 )));
1283 }
1284 }
1285
1286 self.delete_value(index)?;
1287 Ok(())
1288 }
1289
1290 fn set_string_property<'a>(
1292 &mut self,
1293 block_index: BlockIndex,
1294 value: impl Into<Cow<'a, str>>,
1295 ) -> Result<(), Error> {
1296 self.inner_set_string_property_value(block_index, value)?;
1297 Ok(())
1298 }
1299
1300 fn set_buffer_property(&mut self, block_index: BlockIndex, value: &[u8]) -> Result<(), Error> {
1302 self.inner_set_buffer_property_value(block_index, value)?;
1303 Ok(())
1304 }
1305
1306 fn check_lineage(
1307 &self,
1308 being_reparented: BlockIndex,
1309 new_parent: BlockIndex,
1310 ) -> Result<(), Error> {
1311 if being_reparented == BlockIndex::ROOT {
1313 return Err(Error::AdoptAncestor);
1314 }
1315
1316 let mut being_checked = new_parent;
1317 while being_checked != BlockIndex::ROOT {
1318 if being_checked == being_reparented {
1319 return Err(Error::AdoptAncestor);
1320 }
1321 being_checked =
1324 self.heap.container.block_at_unchecked::<Node>(being_checked).parent_index();
1325 }
1326
1327 Ok(())
1328 }
1329
1330 fn reparent(
1331 &mut self,
1332 being_reparented: BlockIndex,
1333 new_parent: BlockIndex,
1334 ) -> Result<(), Error> {
1335 let mut txn = Txn::new(self);
1336 txn.reparent(being_reparented, new_parent)?;
1337 txn.commit();
1338 Ok(())
1339 }
1340
1341 fn set_name<'a>(
1342 &mut self,
1343 block_index: BlockIndex,
1344 name: impl Into<Cow<'a, str>>,
1345 ) -> Result<(), Error> {
1346 if block_index == BlockIndex::ROOT {
1347 return Err(Error::RenameRoot);
1348 }
1349
1350 let block = self.heap.container.block_at(block_index);
1351 if !block.block_type().is_some_and(|t| t.is_any_value()) {
1352 return Err(Error::InvalidBlockType(block_index, block.block_type_raw()));
1353 }
1354
1355 let old_name_index = block.cast_unchecked::<Node>().name_index();
1358 let name = name.into();
1359 if self.string_reference_block_indexes.get(&name) == Some(&old_name_index) {
1360 return Ok(());
1361 }
1362
1363 let mut txn = Txn::new(self);
1364 let new_name_index = txn.intern_and_ref_string(name)?;
1365
1366 if old_name_index != BlockIndex::EMPTY {
1367 match txn.state.heap.container.block_at(old_name_index).block_type() {
1368 Some(BlockType::StringReference) => txn.release_string_ref(old_name_index)?,
1369 _ => txn.state.heap.free_block(old_name_index)?,
1370 }
1371 }
1372
1373 txn.block_mut::<Node>(block_index).set_name(new_name_index);
1374 txn.commit();
1375 Ok(())
1376 }
1377
1378 fn create_bool<'a>(
1379 &mut self,
1380 name: impl Into<Cow<'a, str>>,
1381 value: bool,
1382 parent_index: BlockIndex,
1383 ) -> Result<BlockIndex, Error> {
1384 let mut txn = Txn::new(self);
1385 let (block_index, name_index) =
1386 txn.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
1387 txn.block_mut::<Reserved>(block_index).become_bool_value(value, name_index, parent_index);
1388 txn.commit();
1389 Ok(block_index)
1390 }
1391
1392 fn set_bool(&mut self, block_index: BlockIndex, value: bool) {
1393 let mut block = self.heap.container.block_at_unchecked_mut::<Bool>(block_index);
1394 block.set(value);
1395 }
1396
1397 metric_fns!(int, i64, Int);
1398 metric_fns!(uint, u64, Uint);
1399 metric_fns!(double, f64, Double);
1400
1401 arithmetic_array_fns!(int, i64, IntValue, Int);
1402 arithmetic_array_fns!(uint, u64, UintValue, Uint);
1403 arithmetic_array_fns!(double, f64, DoubleValue, Double);
1404
1405 fn create_string_array<'a>(
1406 &mut self,
1407 name: impl Into<Cow<'a, str>>,
1408 slots: usize,
1409 parent_index: BlockIndex,
1410 ) -> Result<BlockIndex, Error> {
1411 let block_size = slots * StringRef::array_entry_type_size() + constants::MIN_ORDER_SIZE;
1412 if block_size > constants::MAX_ORDER_SIZE {
1413 return Err(Error::BlockSizeTooBig(block_size));
1414 }
1415 let mut txn = Txn::new(self);
1416 let (block_index, name_index) =
1417 txn.allocate_reserved_value(name, parent_index, block_size)?;
1418 txn.block_mut::<Reserved>(block_index).become_array_value::<StringRef>(
1419 slots,
1420 ArrayFormat::Default,
1421 name_index,
1422 parent_index,
1423 )?;
1424 txn.commit();
1425 Ok(block_index)
1426 }
1427
1428 fn get_array_size(&self, block_index: BlockIndex) -> usize {
1429 let block = self.heap.container.block_at_unchecked::<Array<Unknown>>(block_index);
1430 block.slots()
1431 }
1432
1433 fn set_array_string_slot<'a>(
1434 &mut self,
1435 block_index: BlockIndex,
1436 slot_index: usize,
1437 value: impl Into<Cow<'a, str>>,
1438 ) -> Result<(), Error> {
1439 if self.heap.container.block_at_unchecked_mut::<Array<StringRef>>(block_index).slots()
1440 <= slot_index
1441 {
1442 return Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(slot_index)));
1443 }
1444
1445 let value = value.into();
1446
1447 let existing_index = self
1448 .heap
1449 .container
1450 .block_at_unchecked::<Array<StringRef>>(block_index)
1451 .get_string_index_at(slot_index)
1452 .ok_or(Error::InvalidArrayIndex(slot_index))?;
1453 if existing_index != BlockIndex::EMPTY
1454 && self.string_reference_block_indexes.get(&value) == Some(&existing_index)
1455 {
1456 return Ok(());
1457 }
1458
1459 let mut txn = Txn::new(self);
1460 let reference_index = if !value.is_empty() {
1461 let idx = txn.intern_and_ref_string(value)?;
1462 if existing_index != BlockIndex::EMPTY {
1463 txn.release_string_ref(existing_index)?;
1464 }
1465 idx
1466 } else {
1467 if existing_index != BlockIndex::EMPTY {
1468 txn.release_string_ref(existing_index)?;
1469 }
1470 BlockIndex::EMPTY
1471 };
1472
1473 txn.block_mut::<Array<StringRef>>(block_index).set_string_slot(slot_index, reference_index);
1474 txn.commit();
1475 Ok(())
1476 }
1477
1478 fn clear_array(
1481 &mut self,
1482 block_index: BlockIndex,
1483 start_slot_index: usize,
1484 ) -> Result<(), Error> {
1485 let mut txn = Txn::new(self);
1486 txn.clear_array(block_index, start_slot_index)?;
1487 txn.commit();
1488 Ok(())
1489 }
1490
1491 fn delete_value(&mut self, block_index: BlockIndex) -> Result<(), Error> {
1492 let block = self.heap.container.block_at_unchecked::<Node>(block_index);
1494 let parent_index = block.parent_index();
1495 let name_index = block.name_index();
1496
1497 if parent_index != BlockIndex::ROOT {
1499 let parent = self.heap.container.block_at_mut(parent_index);
1500 match parent.block_type() {
1501 Some(BlockType::Tombstone) => {
1502 let mut parent = parent.cast::<Tombstone>().unwrap();
1503 let child_count = parent.child_count() - 1;
1504 if child_count == 0 {
1505 self.heap.free_block(parent_index)?;
1506 } else {
1507 parent.set_child_count(child_count);
1508 }
1509 }
1510 Some(BlockType::NodeValue) => {
1511 let mut parent = parent.cast::<Node>().unwrap();
1512 let child_count = parent.child_count() - 1;
1513 parent.set_child_count(child_count);
1514 }
1515 _ => {
1516 return Err(Error::InvalidBlockType(parent_index, parent.block_type_raw()));
1517 }
1518 }
1519 }
1520
1521 match self.heap.container.block_at(name_index).block_type() {
1523 Some(BlockType::StringReference) => {
1524 self.release_string_reference(name_index)?;
1525 }
1526 _ => self.heap.free_block(name_index)?,
1527 }
1528
1529 let block = self.heap.container.block_at_mut(block_index);
1532 match block.cast::<Node>() {
1533 Some(block) if block.child_count() != 0 => {
1534 let _ = block.become_tombstone();
1535 }
1536 _ => {
1537 self.heap.free_block(block_index)?;
1538 }
1539 }
1540 Ok(())
1541 }
1542
1543 fn inner_set_string_property_value<'a>(
1544 &mut self,
1545 block_index: BlockIndex,
1546 value: impl Into<Cow<'a, str>>,
1547 ) -> Result<(), Error> {
1548 let format = self.heap.container.block_at_unchecked::<Buffer>(block_index).format();
1549 if format != Some(PropertyFormat::StringReference) && format != Some(PropertyFormat::String)
1550 {
1551 return Err(Error::VmoFormat(FormatError::InvalidBufferFormat(
1552 self.heap.container.block_at_unchecked(block_index).format_raw(),
1553 )));
1554 }
1555 let value = value.into();
1556 let old_string_ref_idx =
1557 self.heap.container.block_at_unchecked::<Buffer>(block_index).extent_index();
1558
1559 if old_string_ref_idx != BlockIndex::EMPTY
1560 && self.string_reference_block_indexes.get(&value) == Some(&old_string_ref_idx)
1561 {
1562 return Ok(());
1563 }
1564
1565 let mut txn = Txn::new(self);
1566 let new_string_ref_idx = txn.intern_and_ref_string(value)?;
1567
1568 if old_string_ref_idx != BlockIndex::EMPTY {
1569 txn.release_string_ref(old_string_ref_idx)?;
1570 }
1571
1572 txn.block_mut::<Buffer>(block_index).set_extent_index(new_string_ref_idx);
1573 txn.commit();
1574 Ok(())
1575 }
1576
1577 fn inner_set_buffer_property_value(
1578 &mut self,
1579 block_index: BlockIndex,
1580 value: &[u8],
1581 ) -> Result<(), Error> {
1582 let format = self.heap.container.block_at_unchecked::<Buffer>(block_index).format();
1583 if format != Some(PropertyFormat::Bytes) {
1584 return Err(Error::VmoFormat(FormatError::InvalidBufferFormat(
1585 self.heap.container.block_at_unchecked(block_index).format_raw(),
1586 )));
1587 }
1588 self.free_extents(
1589 self.heap.container.block_at_unchecked::<Buffer>(block_index).extent_index(),
1590 )?;
1591 let mut txn = Txn::new(self);
1592 let (result, (extent_index, written)) = match txn.write_extents(value) {
1593 Ok((e, w)) => (Ok(()), (e, w)),
1594 Err(err) => (Err(err), (BlockIndex::ROOT, 0)),
1595 };
1596 let mut block = txn.block_mut::<Buffer>(block_index);
1597 block.set_total_length(written.try_into().unwrap_or(u32::MAX));
1598 block.set_extent_index(extent_index);
1599 txn.commit();
1600 result
1601 }
1602
1603 fn free_extents(&mut self, head_extent_index: BlockIndex) -> Result<(), Error> {
1604 let mut index = head_extent_index;
1605 while index != BlockIndex::ROOT {
1606 let next_index = self.heap.container.block_at_unchecked::<Extent>(index).next_extent();
1607 self.heap.free_block(index)?;
1608 index = next_index;
1609 }
1610 Ok(())
1611 }
1612}
1613
1614#[cfg(test)]
1615mod tests {
1616 use super::*;
1617 use crate::reader::PartialNodeHierarchy;
1618 use crate::reader::snapshot::{BackingBuffer, ScannedBlock, Snapshot};
1619 use crate::writer::testing_utils::get_state;
1620 use assert_matches::assert_matches;
1621 use diagnostics_assertions::assert_data_tree;
1622 use futures::prelude::*;
1623 use inspect_format::Header;
1624
1625 #[fuchsia::test]
1626 fn test_safe_op_overflow_direction() {
1627 assert_eq!((-100i64).safe_add(i64::MIN), i64::MIN);
1628 assert_eq!((100i64).safe_add(i64::MAX), i64::MAX);
1629 assert_eq!((100i64).safe_sub(i64::MIN), i64::MAX);
1630 assert_eq!((-100i64).safe_sub(i64::MAX), i64::MIN);
1631 assert_eq!(0u64.safe_sub(10), 0);
1632 assert_eq!(u64::MAX.safe_add(10), u64::MAX);
1633 }
1634
1635 #[track_caller]
1636 fn assert_all_free_or_reserved<'a>(
1637 blocks: impl Iterator<Item = Block<&'a BackingBuffer, Unknown>>,
1638 ) {
1639 let mut errors = vec![];
1640 for block in blocks {
1641 if block.block_type() != Some(BlockType::Free)
1642 && block.block_type() != Some(BlockType::Reserved)
1643 {
1644 errors.push(format!(
1645 "block at {} is {:?}, expected {} or {}",
1646 block.index(),
1647 block.block_type(),
1648 BlockType::Free,
1649 BlockType::Reserved,
1650 ));
1651 }
1652 }
1653
1654 if !errors.is_empty() {
1655 panic!("{errors:#?}");
1656 }
1657 }
1658
1659 #[track_caller]
1660 fn assert_all_free<'a>(blocks: impl Iterator<Item = Block<&'a BackingBuffer, Unknown>>) {
1661 let mut errors = vec![];
1662 for block in blocks {
1663 if block.block_type() != Some(BlockType::Free) {
1664 errors.push(format!(
1665 "block at {} is {:?}, expected {}",
1666 block.index(),
1667 block.block_type(),
1668 BlockType::Free
1669 ));
1670 }
1671 }
1672
1673 if !errors.is_empty() {
1674 panic!("{errors:#?}");
1675 }
1676 }
1677
1678 #[fuchsia::test]
1679 fn test_create() {
1680 let state = get_state(4096);
1681 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
1682 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1683 assert_eq!(blocks.len(), 8);
1684 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1685 assert_all_free(blocks.into_iter().skip(1));
1686 }
1687
1688 #[fuchsia::test]
1689 fn test_load_string() {
1690 let outer = get_state(4096);
1691 let mut state = outer.try_lock().expect("lock state");
1692 let block_index = {
1693 let mut txn = Txn::new(&mut state.inner_lock);
1694 let idx = txn.get_or_create_string_reference("a value").unwrap();
1695 txn.commit();
1696 idx
1697 };
1698 assert_eq!(state.load_string(block_index).unwrap(), "a value");
1699 }
1700
1701 #[fuchsia::test]
1702 fn test_check_lineage() {
1703 let core_state = get_state(4096);
1704 let mut state = core_state.try_lock().expect("lock state");
1705 let parent_index = state.create_node("", 0.into()).unwrap();
1706 let child_index = state.create_node("", parent_index).unwrap();
1707 let uncle_index = state.create_node("", 0.into()).unwrap();
1708
1709 state.inner_lock.check_lineage(parent_index, child_index).unwrap_err();
1710 state.inner_lock.check_lineage(0.into(), child_index).unwrap_err();
1711 state.inner_lock.check_lineage(child_index, uncle_index).unwrap();
1712 }
1713
1714 #[fuchsia::test]
1715 fn test_reparent() {
1716 let core_state = get_state(4096);
1717 let mut state = core_state.try_lock().expect("lock state");
1718
1719 let a_index = state.create_node("a", 0.into()).unwrap();
1720 let b_index = state.create_node("b", 0.into()).unwrap();
1721
1722 let a = state.get_block::<Node>(a_index);
1723 let b = state.get_block::<Node>(b_index);
1724 assert_eq!(*a.parent_index(), 0);
1725 assert_eq!(*b.parent_index(), 0);
1726
1727 assert_eq!(a.child_count(), 0);
1728 assert_eq!(b.child_count(), 0);
1729
1730 state.reparent(b_index, a_index).unwrap();
1731
1732 let a = state.get_block::<Node>(a_index);
1733 let b = state.get_block::<Node>(b_index);
1734 assert_eq!(*a.parent_index(), 0);
1735 assert_eq!(b.parent_index(), a.index());
1736
1737 assert_eq!(a.child_count(), 1);
1738 assert_eq!(b.child_count(), 0);
1739
1740 let c_index = state.create_node("c", a_index).unwrap();
1741
1742 let a = state.get_block::<Node>(a_index);
1743 let b = state.get_block::<Node>(b_index);
1744 let c = state.get_block::<Node>(c_index);
1745 assert_eq!(*a.parent_index(), 0);
1746 assert_eq!(b.parent_index(), a.index());
1747 assert_eq!(c.parent_index(), a.index());
1748
1749 assert_eq!(a.child_count(), 2);
1750 assert_eq!(b.child_count(), 0);
1751 assert_eq!(c.child_count(), 0);
1752
1753 state.reparent(c_index, b_index).unwrap();
1754
1755 let a = state.get_block::<Node>(a_index);
1756 let b = state.get_block::<Node>(b_index);
1757 let c = state.get_block::<Node>(c_index);
1758 assert_eq!(*a.parent_index(), 0);
1759 assert_eq!(b.parent_index(), a_index);
1760 assert_eq!(c.parent_index(), b_index);
1761
1762 assert_eq!(a.child_count(), 1);
1763 assert_eq!(b.child_count(), 1);
1764 assert_eq!(c.child_count(), 0);
1765 }
1766
1767 #[fuchsia::test]
1768 fn test_set_name() {
1769 let core_state = get_state(4096);
1770 let mut state = core_state.try_lock().expect("lock state");
1771
1772 assert_eq!(state.set_name(BlockIndex::ROOT, "root"), Err(Error::RenameRoot));
1773
1774 let node_index = state.create_node("initial_name", BlockIndex::ROOT).unwrap();
1775 let node = state.get_block::<Node>(node_index);
1776 let initial_name_index = node.name_index();
1777 assert_eq!(state.load_string(initial_name_index).unwrap(), "initial_name");
1778
1779 assert_matches!(
1782 state.set_name(initial_name_index, "invalid"),
1783 Err(Error::InvalidBlockType(_, _))
1784 );
1785
1786 let stats_before_failure = state.stats();
1789 assert!(state.set_name(node_index, "a".repeat(8192)).is_err());
1790 let node = state.get_block::<Node>(node_index);
1791 assert_eq!(node.name_index(), initial_name_index);
1792 assert_eq!(state.load_string(initial_name_index).unwrap(), "initial_name");
1793 assert_eq!(state.get_block::<StringRef>(initial_name_index).reference_count(), 1);
1794 assert_eq!(
1795 state.stats().allocated_blocks - state.stats().deallocated_blocks,
1796 stats_before_failure.allocated_blocks - stats_before_failure.deallocated_blocks
1797 );
1798
1799 state.set_name(node_index, "new_name").unwrap();
1800 let node = state.get_block::<Node>(node_index);
1801 let new_name_index = node.name_index();
1802 assert_eq!(state.load_string(new_name_index).unwrap(), "new_name");
1803 }
1804
1805 #[fuchsia::test]
1806 fn test_node() {
1807 let core_state = get_state(4096);
1808 let block_index = {
1809 let mut state = core_state.try_lock().expect("lock state");
1810
1811 let block_index = state.create_node("test-node", 0.into()).unwrap();
1813 let block = state.get_block::<Node>(block_index);
1814 assert_eq!(block.block_type(), Some(BlockType::NodeValue));
1815 assert_eq!(*block.index(), 2);
1816 assert_eq!(block.child_count(), 0);
1817 assert_eq!(*block.name_index(), 4);
1818 assert_eq!(*block.parent_index(), 0);
1819
1820 let name_block = state.get_block::<StringRef>(block.name_index());
1822 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1823 assert_eq!(name_block.total_length(), 9);
1824 assert_eq!(name_block.order(), 1);
1825 assert_eq!(state.load_string(name_block.index()).unwrap(), "test-node");
1826 block_index
1827 };
1828
1829 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1831 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1832 assert_eq!(blocks.len(), 10);
1833 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1834 assert_eq!(blocks[1].block_type(), Some(BlockType::NodeValue));
1835 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
1836 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
1837 assert_all_free(blocks.into_iter().skip(4));
1838
1839 {
1840 let mut state = core_state.try_lock().expect("lock state");
1841 let child_block_index = state.create_node("child1", block_index).unwrap();
1842 assert_eq!(state.get_block::<Node>(block_index).child_count(), 1);
1843
1844 let child11_block_index = state.create_node("child1-1", child_block_index).unwrap();
1846 {
1847 assert_eq!(state.get_block::<Node>(child11_block_index).child_count(), 0);
1848 assert_eq!(state.get_block::<Node>(child_block_index).child_count(), 1);
1849 assert_eq!(state.get_block::<Node>(block_index).child_count(), 1);
1850 }
1851
1852 assert!(state.free_value(child11_block_index).is_ok());
1853 {
1854 let child_block = state.get_block::<Node>(child_block_index);
1855 assert_eq!(child_block.child_count(), 0);
1856 }
1857
1858 let child_block2_index = state.create_node("child2", block_index).unwrap();
1860 let child_block3_index = state.create_node("child3", block_index).unwrap();
1861 assert_eq!(state.get_block::<Node>(block_index).child_count(), 3);
1862
1863 assert!(state.free_value(child_block_index).is_ok());
1865 assert!(state.free_value(child_block2_index).is_ok());
1866 assert!(state.free_value(child_block3_index).is_ok());
1867 assert_eq!(state.get_block::<Node>(block_index).child_count(), 0);
1868
1869 assert!(state.free_value(block_index).is_ok());
1871 }
1872
1873 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1874 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1875 assert_all_free(blocks.into_iter().skip(1));
1876 }
1877
1878 #[fuchsia::test]
1879 fn test_int_metric() {
1880 let core_state = get_state(4096);
1881 let block_index = {
1882 let mut state = core_state.try_lock().expect("lock state");
1883 let block_index = state.create_int_metric("test", 3, 0.into()).unwrap();
1884 let block = state.get_block::<Int>(block_index);
1885 assert_eq!(block.block_type(), Some(BlockType::IntValue));
1886 assert_eq!(*block.index(), 2);
1887 assert_eq!(block.value(), 3);
1888 assert_eq!(*block.name_index(), 3);
1889 assert_eq!(*block.parent_index(), 0);
1890
1891 let name_block = state.get_block::<StringRef>(block.name_index());
1892 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1893 assert_eq!(name_block.total_length(), 4);
1894 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1895 block_index
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_eq!(blocks.len(), 9);
1901 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1902 assert_eq!(blocks[1].block_type(), Some(BlockType::IntValue));
1903 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1904 assert_all_free(blocks.into_iter().skip(3));
1905
1906 {
1907 let mut state = core_state.try_lock().expect("lock state");
1908 assert_eq!(state.add_int_metric(block_index, 10), 13);
1909 assert_eq!(state.get_block::<Int>(block_index).value(), 13);
1910
1911 assert_eq!(state.subtract_int_metric(block_index, 5), 8);
1912 assert_eq!(state.get_block::<Int>(block_index).value(), 8);
1913
1914 state.set_int_metric(block_index, -6);
1915 assert_eq!(state.get_block::<Int>(block_index).value(), -6);
1916
1917 assert_eq!(state.subtract_int_metric(block_index, i64::MAX), i64::MIN);
1918 assert_eq!(state.get_block::<Int>(block_index).value(), i64::MIN);
1919 state.set_int_metric(block_index, i64::MAX);
1920
1921 assert_eq!(state.add_int_metric(block_index, 2), i64::MAX);
1922 assert_eq!(state.get_block::<Int>(block_index).value(), i64::MAX);
1923
1924 assert!(state.free_value(block_index).is_ok());
1926 }
1927
1928 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1929 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1930 assert_all_free(blocks.into_iter().skip(1));
1931 }
1932
1933 #[fuchsia::test]
1934 fn test_uint_metric() {
1935 let core_state = get_state(4096);
1936
1937 let block_index = {
1939 let mut state = core_state.try_lock().expect("try lock");
1940 let block_index = state.create_uint_metric("test", 3, 0.into()).unwrap();
1941 let block = state.get_block::<Uint>(block_index);
1942 assert_eq!(block.block_type(), Some(BlockType::UintValue));
1943 assert_eq!(*block.index(), 2);
1944 assert_eq!(block.value(), 3);
1945 assert_eq!(*block.name_index(), 3);
1946 assert_eq!(*block.parent_index(), 0);
1947
1948 let name_block = state.get_block::<StringRef>(block.name_index());
1949 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1950 assert_eq!(name_block.total_length(), 4);
1951 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1952 block_index
1953 };
1954
1955 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1956 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1957 assert_eq!(blocks.len(), 9);
1958 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1959 assert_eq!(blocks[1].block_type(), Some(BlockType::UintValue));
1960 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1961 assert_all_free(blocks.into_iter().skip(3));
1962
1963 {
1964 let mut state = core_state.try_lock().expect("try lock");
1965 assert_eq!(state.add_uint_metric(block_index, 10), 13);
1966 assert_eq!(state.get_block::<Uint>(block_index).value(), 13);
1967
1968 assert_eq!(state.subtract_uint_metric(block_index, 5), 8);
1969 assert_eq!(state.get_block::<Uint>(block_index).value(), 8);
1970
1971 state.set_uint_metric(block_index, 0);
1972 assert_eq!(state.get_block::<Uint>(block_index).value(), 0);
1973
1974 assert_eq!(state.subtract_uint_metric(block_index, u64::MAX), 0);
1975 assert_eq!(state.get_block::<Uint>(block_index).value(), 0);
1976
1977 state.set_uint_metric(block_index, 3);
1978 assert_eq!(state.add_uint_metric(block_index, u64::MAX), u64::MAX);
1979 assert_eq!(state.get_block::<Uint>(block_index).value(), u64::MAX);
1980
1981 assert!(state.free_value(block_index).is_ok());
1983 }
1984
1985 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1986 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1987 assert_all_free(blocks.into_iter().skip(1));
1988 }
1989
1990 #[fuchsia::test]
1991 fn test_double_metric() {
1992 let core_state = get_state(4096);
1993
1994 let block_index = {
1996 let mut state = core_state.try_lock().expect("lock state");
1997 let block_index = state.create_double_metric("test", 3.0, 0.into()).unwrap();
1998 let block = state.get_block::<Double>(block_index);
1999 assert_eq!(block.block_type(), Some(BlockType::DoubleValue));
2000 assert_eq!(*block.index(), 2);
2001 assert_eq!(block.value(), 3.0);
2002 assert_eq!(*block.name_index(), 3);
2003 assert_eq!(*block.parent_index(), 0);
2004
2005 let name_block = state.get_block::<StringRef>(block.name_index());
2006 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2007 assert_eq!(name_block.total_length(), 4);
2008 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2009 block_index
2010 };
2011
2012 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2013 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2014 assert_eq!(blocks.len(), 9);
2015 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2016 assert_eq!(blocks[1].block_type(), Some(BlockType::DoubleValue));
2017 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2018 assert_all_free(blocks.into_iter().skip(3));
2019
2020 {
2021 let mut state = core_state.try_lock().expect("lock state");
2022 assert_eq!(state.add_double_metric(block_index, 10.5), 13.5);
2023 assert_eq!(state.get_block::<Double>(block_index).value(), 13.5);
2024
2025 assert_eq!(state.subtract_double_metric(block_index, 5.1), 8.4);
2026 assert_eq!(state.get_block::<Double>(block_index).value(), 8.4);
2027
2028 state.set_double_metric(block_index, -6.0);
2029 assert_eq!(state.get_block::<Double>(block_index).value(), -6.0);
2030
2031 assert!(state.free_value(block_index).is_ok());
2033 }
2034
2035 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2036 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2037 assert_all_free(blocks.into_iter().skip(1));
2038 }
2039
2040 #[fuchsia::test]
2041 fn test_create_buffer_property_cleanup_on_failure() {
2042 assert_eq!(constants::MAX_ORDER_SIZE, 2048);
2044
2045 let core_state = get_state(5121); let mut state = core_state.try_lock().expect("lock state");
2047 let name = (0..3000).map(|_| " ").collect::<String>();
2049 let payload = [0u8; 4096]; assert!(state.create_buffer_property(name, &payload, 0.into()).is_err());
2056
2057 drop(state);
2058
2059 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2060 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2061
2062 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2064 assert_all_free(blocks.into_iter().skip(1));
2065 }
2066
2067 #[fuchsia::test]
2068 fn test_string_reference_allocations() {
2069 let core_state = get_state(4096); {
2071 let mut state = core_state.try_lock().expect("lock state");
2072 let sf = "a reference-counted canonical name";
2073 assert_eq!(state.stats().allocated_blocks, 1);
2074
2075 let mut collected = vec![];
2076 for _ in 0..100 {
2077 collected.push(state.create_node(sf, 0.into()).unwrap());
2078 }
2079
2080 let acsf = Arc::new(Cow::Borrowed(sf));
2081 assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
2082
2083 assert_eq!(state.stats().allocated_blocks, 102);
2084 let block = state.get_block::<Node>(collected[0]);
2085 let sf_block = state.get_block::<StringRef>(block.name_index());
2086 assert_eq!(sf_block.reference_count(), 100);
2087
2088 collected.into_iter().for_each(|b| {
2089 assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
2090 assert!(state.free_value(b).is_ok())
2091 });
2092
2093 assert!(!state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
2094
2095 let node_index = state.create_node(sf, 0.into()).unwrap();
2096 assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
2097 assert!(state.free_value(node_index).is_ok());
2098 assert!(!state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
2099 }
2100
2101 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2102 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2103 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2104 assert_all_free(blocks.into_iter().skip(1));
2105 }
2106
2107 #[fuchsia::test]
2108 fn test_string_reference_data() {
2109 let core_state = get_state(4096); let mut state = core_state.try_lock().expect("lock state");
2111
2112 let block_index = {
2114 let mut txn = Txn::new(&mut state.inner_lock);
2115 let idx = txn.get_or_create_string_reference("abcd").unwrap();
2116 txn.commit();
2117 idx
2118 };
2119 let block = state.get_block::<StringRef>(block_index);
2120 assert_eq!(block.block_type(), Some(BlockType::StringReference));
2121 assert_eq!(block.order(), 0);
2122 assert_eq!(state.stats().allocated_blocks, 2);
2123 assert_eq!(state.stats().deallocated_blocks, 0);
2124 assert_eq!(block.reference_count(), 0);
2125 assert_eq!(block.total_length(), 4);
2126 assert_eq!(*block.next_extent(), 0);
2127 assert_eq!(block.order(), 0);
2128 assert_eq!(state.load_string(block.index()).unwrap(), "abcd");
2129
2130 state.inner_lock.maybe_free_string_reference(block_index).unwrap();
2131 assert_eq!(state.stats().deallocated_blocks, 1);
2132
2133 let block_index = {
2134 let mut txn = Txn::new(&mut state.inner_lock);
2135 let idx = txn.get_or_create_string_reference("longer").unwrap();
2136 txn.commit();
2137 idx
2138 };
2139 let block = state.get_block::<StringRef>(block_index);
2140 assert_eq!(block.block_type(), Some(BlockType::StringReference));
2141 assert_eq!(block.order(), 1);
2142 assert_eq!(block.reference_count(), 0);
2143 assert_eq!(block.total_length(), 6);
2144 assert_eq!(state.stats().allocated_blocks, 3);
2145 assert_eq!(state.stats().deallocated_blocks, 1);
2146 assert_eq!(state.load_string(block.index()).unwrap(), "longer");
2147
2148 let idx = block.next_extent();
2149 assert_eq!(*idx, 0);
2150
2151 state.inner_lock.maybe_free_string_reference(block_index).unwrap();
2152 assert_eq!(state.stats().deallocated_blocks, 2);
2153
2154 let block_index = {
2155 let mut txn = Txn::new(&mut state.inner_lock);
2156 let idx = txn.get_or_create_string_reference("longer").unwrap();
2157 txn.commit();
2158 idx
2159 };
2160 let mut block = state.get_block_mut::<StringRef>(block_index);
2161 assert_eq!(block.order(), 1);
2162 block.increment_ref_count().unwrap();
2163 assert!(state.inner_lock.maybe_free_string_reference(block_index).is_ok());
2165
2166 let mut block = state.get_block_mut(block_index);
2167 block.decrement_ref_count().unwrap();
2168 state.inner_lock.maybe_free_string_reference(block_index).unwrap();
2169 }
2170
2171 #[fuchsia::test]
2172 fn test_string_reference_format_property() {
2173 let core_state = get_state(4096);
2174 let block_index = {
2175 let mut state = core_state.try_lock().expect("lock state");
2176
2177 let block_index =
2179 state.create_string("test", "test-property", BlockIndex::from(0)).unwrap();
2180 let block = state.get_block::<Buffer>(block_index);
2181 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2182 assert_eq!(*block.index(), 2);
2183 assert_eq!(*block.parent_index(), 0);
2184 assert_eq!(*block.name_index(), 3);
2185 assert_eq!(block.total_length(), 0);
2186 assert_eq!(block.format(), Some(PropertyFormat::StringReference));
2187
2188 let name_block = state.get_block::<StringRef>(block.name_index());
2189 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2190 assert_eq!(name_block.total_length(), 4);
2191 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2192
2193 let data_block = state.get_block::<StringRef>(block.extent_index());
2194 assert_eq!(data_block.block_type(), Some(BlockType::StringReference));
2195 assert_eq!(state.load_string(data_block.index()).unwrap(), "test-property");
2196 block_index
2197 };
2198
2199 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2200 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2201 assert_eq!(blocks.len(), 10);
2202 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2203 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2204 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2205 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2206 assert_all_free(blocks.into_iter().skip(4));
2207
2208 {
2209 let mut state = core_state.try_lock().expect("lock state");
2210 assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2212 }
2213 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2214 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2215 assert_all_free(blocks.into_iter().skip(1));
2216 }
2217
2218 #[fuchsia::test]
2219 fn test_string_arrays() {
2220 let core_state = get_state(4096);
2221 {
2222 let mut state = core_state.try_lock().expect("lock state");
2223 let array_index = state.create_string_array("array", 4, 0.into()).unwrap();
2224 assert_eq!(state.set_array_string_slot(array_index, 0, "0"), Ok(()));
2225 assert_eq!(state.set_array_string_slot(array_index, 1, "1"), Ok(()));
2226 assert_eq!(state.set_array_string_slot(array_index, 2, "2"), Ok(()));
2227 assert_eq!(state.set_array_string_slot(array_index, 3, "3"), Ok(()));
2228
2229 assert_matches!(
2231 state.set_array_string_slot(array_index, 4, ""),
2232 Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(4)))
2233 );
2234 assert_matches!(
2235 state.set_array_string_slot(array_index, 5, ""),
2236 Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(5)))
2237 );
2238
2239 for i in 0..4 {
2240 let idx = state
2241 .get_block::<Array<StringRef>>(array_index)
2242 .get_string_index_at(i)
2243 .unwrap();
2244 assert_eq!(i.to_string(), state.load_string(idx).unwrap());
2245 }
2246
2247 assert_eq!(
2248 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(4),
2249 None
2250 );
2251 assert_eq!(
2252 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(5),
2253 None
2254 );
2255
2256 state.clear_array(array_index, 0).unwrap();
2257 state.free_value(array_index).unwrap();
2258 }
2259
2260 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2261 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2262 assert_all_free(blocks.into_iter().skip(1));
2263 }
2264
2265 #[fuchsia::test]
2266 fn update_string_array_value() {
2267 let core_state = get_state(4096);
2268 {
2269 let mut state = core_state.try_lock().expect("lock state");
2270 let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
2271
2272 assert_eq!(state.set_array_string_slot(array_index, 0, "abc"), Ok(()));
2273 assert_eq!(state.set_array_string_slot(array_index, 1, "def"), Ok(()));
2274
2275 assert_eq!(state.set_array_string_slot(array_index, 0, "cba"), Ok(()));
2276 assert_eq!(state.set_array_string_slot(array_index, 1, "fed"), Ok(()));
2277
2278 let cba_index_slot =
2279 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(0).unwrap();
2280 let fed_index_slot =
2281 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(1).unwrap();
2282 assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap());
2283 assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2284
2285 state.clear_array(array_index, 0).unwrap();
2286 state.free_value(array_index).unwrap();
2287 }
2288
2289 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2290 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2291 blocks[1..].iter().enumerate().for_each(|(i, b)| {
2292 assert!(b.block_type() == Some(BlockType::Free), "index is {}", i + 1);
2293 });
2294 }
2295
2296 #[fuchsia::test]
2297 fn set_string_reference_instances_multiple_times_in_array() {
2298 let core_state = get_state(4096);
2299 {
2300 let mut state = core_state.try_lock().expect("lock state");
2301 let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
2302
2303 let abc = "abc";
2304 let def = "def";
2305 let cba = "cba";
2306 let fed = "fed";
2307
2308 state.set_array_string_slot(array_index, 0, abc).unwrap();
2309 state.set_array_string_slot(array_index, 1, def).unwrap();
2310 state.set_array_string_slot(array_index, 0, abc).unwrap();
2311 state.set_array_string_slot(array_index, 1, def).unwrap();
2312
2313 let abc_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2314 let def_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2315 assert_eq!("abc".to_string(), state.load_string(abc_index_slot).unwrap(),);
2316 assert_eq!("def".to_string(), state.load_string(def_index_slot).unwrap(),);
2317
2318 state.set_array_string_slot(array_index, 0, cba).unwrap();
2319 state.set_array_string_slot(array_index, 1, fed).unwrap();
2320
2321 let cba_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2322 let fed_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2323 assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap(),);
2324 assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2325
2326 state.set_array_string_slot(array_index, 0, abc).unwrap();
2327 state.set_array_string_slot(array_index, 1, def).unwrap();
2328
2329 let abc_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2330 let def_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2331 assert_eq!("abc".to_string(), state.load_string(abc_index_slot).unwrap(),);
2332 assert_eq!("def".to_string(), state.load_string(def_index_slot).unwrap(),);
2333
2334 state.set_array_string_slot(array_index, 0, cba).unwrap();
2335 state.set_array_string_slot(array_index, 1, fed).unwrap();
2336
2337 let cba_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2338 let fed_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2339 assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap(),);
2340 assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2341
2342 state.clear_array(array_index, 0).unwrap();
2343 state.free_value(array_index).unwrap();
2344 }
2345
2346 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2347 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2348 blocks[1..].iter().enumerate().for_each(|(i, b)| {
2349 assert!(b.block_type() == Some(BlockType::Free), "index is {}", i + 1);
2350 });
2351 }
2352
2353 #[fuchsia::test]
2354 fn test_empty_value_string_arrays() {
2355 let core_state = get_state(4096);
2356 {
2357 let mut state = core_state.try_lock().expect("lock state");
2358 let array_index = state.create_string_array("array", 4, 0.into()).unwrap();
2359
2360 state.set_array_string_slot(array_index, 0, "").unwrap();
2361 state.set_array_string_slot(array_index, 1, "").unwrap();
2362 state.set_array_string_slot(array_index, 2, "").unwrap();
2363 state.set_array_string_slot(array_index, 3, "").unwrap();
2364 }
2365
2366 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2367 let state = core_state.try_lock().expect("lock state");
2368
2369 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2370 for b in blocks {
2371 if b.block_type() == Some(BlockType::StringReference)
2372 && state.load_string(b.index()).unwrap() == "array"
2373 {
2374 continue;
2375 }
2376
2377 assert_ne!(
2378 b.block_type(),
2379 Some(BlockType::StringReference),
2380 "Got unexpected StringReference, index: {}, value (wrapped in single quotes): '{:?}'",
2381 b.index(),
2382 b.block_type()
2383 );
2384 }
2385 }
2386
2387 #[fuchsia::test]
2388 fn test_bytevector_property() {
2389 let core_state = get_state(4096);
2390
2391 let block_index = {
2393 let mut state = core_state.try_lock().expect("lock state");
2394 let block_index =
2395 state.create_buffer_property("test", b"test-property", 0.into()).unwrap();
2396 let block = state.get_block::<Buffer>(block_index);
2397 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2398 assert_eq!(*block.index(), 2);
2399 assert_eq!(*block.parent_index(), 0);
2400 assert_eq!(*block.name_index(), 3);
2401 assert_eq!(block.total_length(), 13);
2402 assert_eq!(*block.extent_index(), 4);
2403 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2404
2405 let name_block = state.get_block::<StringRef>(block.name_index());
2406 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2407 assert_eq!(name_block.total_length(), 4);
2408 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2409
2410 let extent_block = state.get_block::<Extent>(4.into());
2411 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2412 assert_eq!(*extent_block.next_extent(), 0);
2413 assert_eq!(
2414 std::str::from_utf8(extent_block.contents().unwrap()).unwrap(),
2415 "test-property\0\0\0\0\0\0\0\0\0\0\0"
2416 );
2417 block_index
2418 };
2419
2420 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2421 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2422 assert_eq!(blocks.len(), 10);
2423 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2424 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2425 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2426 assert_eq!(blocks[3].block_type(), Some(BlockType::Extent));
2427 assert_all_free(blocks.into_iter().skip(4));
2428
2429 {
2431 let mut state = core_state.try_lock().expect("lock state");
2432 assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2433 }
2434 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2435 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2436 assert_all_free(blocks.into_iter().skip(1));
2437 }
2438
2439 #[fuchsia::test]
2440 fn test_bool() {
2441 let core_state = get_state(4096);
2442 let block_index = {
2443 let mut state = core_state.try_lock().expect("lock state");
2444
2445 let block_index = state.create_bool("test", true, 0.into()).unwrap();
2447 let block = state.get_block::<Bool>(block_index);
2448 assert_eq!(block.block_type(), Some(BlockType::BoolValue));
2449 assert_eq!(*block.index(), 2);
2450 assert!(block.value());
2451 assert_eq!(*block.name_index(), 3);
2452 assert_eq!(*block.parent_index(), 0);
2453
2454 let name_block = state.get_block::<StringRef>(block.name_index());
2455 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2456 assert_eq!(name_block.total_length(), 4);
2457 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2458 block_index
2459 };
2460
2461 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2462 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2463 assert_eq!(blocks.len(), 9);
2464 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2465 assert_eq!(blocks[1].block_type(), Some(BlockType::BoolValue));
2466 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2467 assert_all_free(blocks.into_iter().skip(3));
2468
2469 {
2471 let mut state = core_state.try_lock().expect("lock state");
2472 assert!(state.free_value(block_index).is_ok());
2473 }
2474 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2475 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2476 assert_all_free(blocks.into_iter().skip(1));
2477 }
2478
2479 #[fuchsia::test]
2480 fn test_int_array() {
2481 let core_state = get_state(4096);
2482 let block_index = {
2483 let mut state = core_state.try_lock().expect("lock state");
2484 let block_index =
2485 state.create_int_array("test", 5, ArrayFormat::Default, 0.into()).unwrap();
2486 let block = state.get_block::<Array<Int>>(block_index);
2487 assert_eq!(block.block_type(), Some(BlockType::ArrayValue));
2488 assert_eq!(block.order(), 2);
2489 assert_eq!(*block.index(), 4);
2490 assert_eq!(*block.name_index(), 2);
2491 assert_eq!(*block.parent_index(), 0);
2492 assert_eq!(block.slots(), 5);
2493 assert_eq!(block.format(), Some(ArrayFormat::Default));
2494 assert_eq!(block.entry_type(), Some(BlockType::IntValue));
2495
2496 let name_block = state.get_block::<StringRef>(BlockIndex::from(2));
2497 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2498 assert_eq!(name_block.total_length(), 4);
2499 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2500 for i in 0..5 {
2501 state.set_array_int_slot(block_index, i, 3 * i as i64);
2502 }
2503 for i in 0..5 {
2504 assert_eq!(state.get_block::<Array<Int>>(block_index).get(i), Some(3 * i as i64));
2505 }
2506 block_index
2507 };
2508
2509 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2510 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2511 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2512 assert_eq!(blocks[1].block_type(), Some(BlockType::StringReference));
2513 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2514 assert_eq!(blocks[3].block_type(), Some(BlockType::ArrayValue));
2515 assert_all_free(blocks.into_iter().skip(4));
2516
2517 {
2519 let mut state = core_state.try_lock().expect("lock state");
2520 assert!(state.free_value(block_index).is_ok());
2521 }
2522 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2523 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2524 assert_all_free(blocks.into_iter().skip(1));
2525 }
2526
2527 #[fuchsia::test]
2528 fn test_write_extent_overflow() {
2529 const SIZE: usize = constants::MAX_ORDER_SIZE * 2;
2530 const EXPECTED_WRITTEN: usize = constants::MAX_ORDER_SIZE - constants::HEADER_SIZE_BYTES;
2531 const TRIED_TO_WRITE: usize = SIZE + 1;
2532 let core_state = get_state(SIZE);
2533 let mut state = core_state.try_lock().unwrap();
2534 let (_, written) = {
2535 let mut txn = Txn::new(&mut state.inner_lock);
2536 let res = txn.write_extents(&[4u8; TRIED_TO_WRITE]).unwrap();
2537 txn.commit();
2538 res
2539 };
2540 assert_eq!(written, EXPECTED_WRITTEN);
2541 }
2542
2543 #[fuchsia::test]
2544 fn overflow_property() {
2545 const SIZE: usize = constants::MAX_ORDER_SIZE * 2;
2546 const EXPECTED_WRITTEN: usize = constants::MAX_ORDER_SIZE - constants::HEADER_SIZE_BYTES;
2547
2548 let core_state = get_state(SIZE);
2549 let mut state = core_state.try_lock().expect("lock state");
2550
2551 let data = "X".repeat(SIZE * 2);
2552 let block_index = state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2553 let block = state.get_block::<Buffer>(block_index);
2554 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2555 assert_eq!(*block.index(), 2);
2556 assert_eq!(*block.parent_index(), 0);
2557 assert_eq!(*block.name_index(), 3);
2558 assert_eq!(block.total_length(), EXPECTED_WRITTEN);
2559 assert_eq!(*block.extent_index(), 128);
2560 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2561
2562 let name_block = state.get_block::<StringRef>(block.name_index());
2563 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2564 assert_eq!(name_block.total_length(), 4);
2565 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2566
2567 let extent_block = state.get_block::<Extent>(128.into());
2568 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2569 assert_eq!(extent_block.order(), 7);
2570 assert_eq!(*extent_block.next_extent(), *BlockIndex::EMPTY);
2571 assert_eq!(
2572 extent_block.contents().unwrap(),
2573 data.chars().take(EXPECTED_WRITTEN).map(|c| c as u8).collect::<Vec<u8>>()
2574 );
2575 }
2576
2577 #[fuchsia::test]
2578 fn test_multi_extent_property() {
2579 let core_state = get_state(10000);
2580 let block_index = {
2581 let mut state = core_state.try_lock().expect("lock state");
2582
2583 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2584 let data = chars.iter().cycle().take(6000).collect::<String>();
2585 let block_index =
2586 state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2587 let block = state.get_block::<Buffer>(block_index);
2588 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2589 assert_eq!(*block.index(), 2);
2590 assert_eq!(*block.parent_index(), 0);
2591 assert_eq!(*block.name_index(), 3);
2592 assert_eq!(block.total_length(), 6000);
2593 assert_eq!(*block.extent_index(), 128);
2594 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2595
2596 let name_block = state.get_block::<StringRef>(block.name_index());
2597 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2598 assert_eq!(name_block.total_length(), 4);
2599 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2600
2601 let extent_block = state.get_block::<Extent>(128.into());
2602 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2603 assert_eq!(extent_block.order(), 7);
2604 assert_eq!(*extent_block.next_extent(), 256);
2605 assert_eq!(
2606 extent_block.contents().unwrap(),
2607 chars.iter().cycle().take(2040).map(|&c| c as u8).collect::<Vec<u8>>()
2608 );
2609
2610 let extent_block = state.get_block::<Extent>(256.into());
2611 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2612 assert_eq!(extent_block.order(), 7);
2613 assert_eq!(*extent_block.next_extent(), 384);
2614 assert_eq!(
2615 extent_block.contents().unwrap(),
2616 chars.iter().cycle().skip(2040).take(2040).map(|&c| c as u8).collect::<Vec<u8>>()
2617 );
2618
2619 let extent_block = state.get_block::<Extent>(384.into());
2620 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2621 assert_eq!(extent_block.order(), 7);
2622 assert_eq!(*extent_block.next_extent(), 0);
2623 assert_eq!(
2624 extent_block.contents().unwrap()[..1920],
2625 chars.iter().cycle().skip(4080).take(1920).map(|&c| c as u8).collect::<Vec<u8>>()[..]
2626 );
2627 assert_eq!(extent_block.contents().unwrap()[1920..], [0u8; 120][..]);
2628 block_index
2629 };
2630
2631 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2632 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2633 assert_eq!(blocks.len(), 11);
2634 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2635 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2636 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2637 assert_eq!(blocks[8].block_type(), Some(BlockType::Extent));
2638 assert_eq!(blocks[9].block_type(), Some(BlockType::Extent));
2639 assert_eq!(blocks[10].block_type(), Some(BlockType::Extent));
2640 assert_all_free(blocks.into_iter().skip(3).take(5));
2641 {
2643 let mut state = core_state.try_lock().expect("lock state");
2644 assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2645 }
2646 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2647 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2648 assert_all_free(blocks.into_iter().skip(1));
2649 }
2650
2651 #[fuchsia::test]
2652 fn test_freeing_string_references() {
2653 let core_state = get_state(4096);
2654 {
2655 let mut state = core_state.try_lock().expect("lock state");
2656 assert_eq!(state.stats().allocated_blocks, 1);
2657
2658 let block0_index = state.create_node("abcd123456789", 0.into()).unwrap();
2659 let block0_name_index = {
2660 let block0_name_index = state.get_block::<Node>(block0_index).name_index();
2661 let block0_name = state.get_block::<StringRef>(block0_name_index);
2662 assert_eq!(block0_name.order(), 1);
2663 block0_name_index
2664 };
2665 assert_eq!(state.stats().allocated_blocks, 3);
2666
2667 let block1_index = {
2668 let mut txn = Txn::new(&mut state.inner_lock);
2669 let idx = txn.get_or_create_string_reference("abcd").unwrap();
2670 txn.commit();
2671 idx
2672 };
2673 assert_eq!(state.stats().allocated_blocks, 4);
2674 assert_eq!(state.get_block::<StringRef>(block1_index).order(), 0);
2675
2676 let block2_index = {
2677 let mut txn = Txn::new(&mut state.inner_lock);
2678 let idx = txn.get_or_create_string_reference("abcd123456789").unwrap();
2679 txn.commit();
2680 idx
2681 };
2682 assert_eq!(state.get_block::<StringRef>(block2_index).order(), 1);
2683 assert_eq!(block0_name_index, block2_index);
2684 assert_eq!(state.stats().allocated_blocks, 4);
2685
2686 let block3_index = state.create_node("abcd12345678", 0.into()).unwrap();
2687 let block3 = state.get_block::<Node>(block3_index);
2688 let block3_name = state.get_block::<StringRef>(block3.name_index());
2689 assert_eq!(block3_name.order(), 1);
2690 assert_eq!(block3.order(), 0);
2691 assert_eq!(state.stats().allocated_blocks, 6);
2692
2693 let mut long_name = "".to_string();
2694 for _ in 0..3000 {
2695 long_name += " ";
2696 }
2697
2698 let block4_index = state.create_node(long_name, 0.into()).unwrap();
2699 let block4 = state.get_block::<Node>(block4_index);
2700 let block4_name = state.get_block::<StringRef>(block4.name_index());
2701 assert_eq!(block4_name.order(), 7);
2702 assert!(*block4_name.next_extent() != 0);
2703 assert_eq!(state.stats().allocated_blocks, 9);
2704
2705 assert!(state.inner_lock.maybe_free_string_reference(block1_index).is_ok());
2706 assert_eq!(state.stats().deallocated_blocks, 1);
2707 assert!(state.inner_lock.maybe_free_string_reference(block2_index).is_ok());
2708 assert_eq!(state.stats().deallocated_blocks, 1);
2710 assert!(state.free_value(block3_index).is_ok());
2711 assert_eq!(state.stats().deallocated_blocks, 3);
2712 assert!(state.free_value(block4_index).is_ok());
2713 assert_eq!(state.stats().deallocated_blocks, 6);
2714 }
2715
2716 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2718 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2719
2720 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2721 assert_eq!(blocks[1].block_type(), Some(BlockType::NodeValue));
2722 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2723 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2724 assert_all_free(blocks.into_iter().skip(4));
2725 }
2726
2727 #[fuchsia::test]
2728 fn test_tombstone() {
2729 let core_state = get_state(4096);
2730 let child_block_index = {
2731 let mut state = core_state.try_lock().expect("lock state");
2732
2733 let block_index = state.create_node("root-node", 0.into()).unwrap();
2735 let block_name_as_string_ref =
2736 state.get_block::<StringRef>(state.get_block::<Node>(block_index).name_index());
2737 assert_eq!(block_name_as_string_ref.order(), 1);
2738 assert_eq!(state.stats().allocated_blocks, 3);
2739 assert_eq!(state.stats().deallocated_blocks, 0);
2740
2741 let child_block_index = state.create_node("child-node", block_index).unwrap();
2742 assert_eq!(state.stats().allocated_blocks, 5);
2743 assert_eq!(state.stats().deallocated_blocks, 0);
2744
2745 assert!(state.free_value(block_index).is_ok());
2747 assert_eq!(state.stats().allocated_blocks, 5);
2748 assert_eq!(state.stats().deallocated_blocks, 1);
2749 child_block_index
2750 };
2751
2752 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2753 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2754
2755 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2758 assert_eq!(blocks[1].block_type(), Some(BlockType::Tombstone));
2759 assert_eq!(blocks[2].block_type(), Some(BlockType::NodeValue));
2760 assert_eq!(blocks[3].block_type(), Some(BlockType::Free));
2761 assert_eq!(blocks[4].block_type(), Some(BlockType::StringReference));
2762 assert_all_free(blocks.into_iter().skip(5));
2763
2764 {
2766 let mut state = core_state.try_lock().expect("lock state");
2767 assert!(state.free_value(child_block_index).is_ok());
2768 }
2769 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2770 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2771 assert_all_free(blocks.into_iter().skip(1));
2772 }
2773
2774 #[fuchsia::test]
2775 fn test_with_header_lock() {
2776 let state = get_state(4096);
2777 state.with_current_header(|header| {
2779 assert_eq!(header.generation_count(), 0);
2780 });
2781
2782 let mut lock_guard = state.try_lock().expect("lock state");
2784 assert!(lock_guard.header().is_locked());
2785 assert_eq!(lock_guard.header().generation_count(), 1);
2786 let _ = lock_guard.create_node("test", 0.into()).unwrap();
2788 let _ = lock_guard.create_node("test2", 2.into()).unwrap();
2789 assert_eq!(lock_guard.header().generation_count(), 1);
2790
2791 drop(lock_guard);
2793 state.with_current_header(|header| {
2794 assert_eq!(header.generation_count(), 2);
2795 assert!(!header.is_locked());
2796 });
2797 }
2798
2799 #[fuchsia::test]
2800 async fn test_link() {
2801 let state = get_state(4096);
2803 let block_index = {
2804 let mut state_guard = state.try_lock().expect("lock state");
2805 let block_index = state_guard
2806 .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2807 async move {
2808 let inspector = Inspector::default();
2809 inspector.root().record_uint("a", 1);
2810 Ok(inspector)
2811 }
2812 .boxed()
2813 })
2814 .unwrap();
2815
2816 assert!(state_guard.callbacks().get("link-name-0").is_some());
2818 let callback = state_guard.callbacks().get("link-name-0").unwrap();
2819 match callback().await {
2820 Ok(inspector) => {
2821 let hierarchy =
2822 PartialNodeHierarchy::try_from(Snapshot::try_from(&inspector).unwrap())
2823 .unwrap();
2824 assert_data_tree!(hierarchy, root: {
2825 a: 1u64,
2826 });
2827 }
2828 Err(_) => unreachable!("we never return errors in the callback"),
2829 }
2830
2831 let block = state_guard.get_block::<Link>(block_index);
2833 assert_eq!(block.block_type(), Some(BlockType::LinkValue));
2834 assert_eq!(*block.index(), 2);
2835 assert_eq!(*block.parent_index(), 0);
2836 assert_eq!(*block.name_index(), 4);
2837 assert_eq!(*block.content_index(), 6);
2838 assert_eq!(block.link_node_disposition(), Some(LinkNodeDisposition::Inline));
2839
2840 let name_block = state_guard.get_block::<StringRef>(block.name_index());
2842 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2843 assert_eq!(name_block.total_length(), 9);
2844 assert_eq!(state_guard.load_string(name_block.index()).unwrap(), "link-name");
2845
2846 let content_block = state_guard.get_block::<StringRef>(block.content_index());
2848 assert_eq!(content_block.block_type(), Some(BlockType::StringReference));
2849 assert_eq!(content_block.total_length(), 11);
2850 assert_eq!(state_guard.load_string(content_block.index()).unwrap(), "link-name-0");
2851 block_index
2852 };
2853
2854 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2856 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2857 assert_eq!(blocks.len(), 10);
2858 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2859 assert_eq!(blocks[1].block_type(), Some(BlockType::LinkValue));
2860 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2861 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2862 assert_eq!(blocks[4].block_type(), Some(BlockType::StringReference));
2863 assert_all_free(blocks.into_iter().skip(5));
2864
2865 {
2867 let mut state_guard = state.try_lock().expect("lock state");
2868 assert!(state_guard.free_lazy_node(block_index).is_ok());
2869
2870 assert!(state_guard.callbacks().get("link-name-0").is_none());
2872 }
2873 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2874 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2875 assert_all_free(blocks.into_iter().skip(1));
2876
2877 let mut state_guard = state.try_lock().expect("lock state");
2879 state_guard
2880 .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2881 async move { Ok(Inspector::default()) }.boxed()
2882 })
2883 .unwrap();
2884 let content_block = state_guard.get_block::<StringRef>(6.into());
2885 assert_eq!(state_guard.load_string(content_block.index()).unwrap(), "link-name-1");
2886 }
2887
2888 #[fuchsia::test]
2889 fn free_lazy_node_test() {
2890 let state = get_state(4096);
2891 let (lazy_index, _int_with_magic_name_index) = {
2892 let mut state_guard = state.try_lock().expect("lock state");
2893 let lazy_index = state_guard
2894 .create_lazy_node("lk", 0.into(), LinkNodeDisposition::Inline, || {
2895 async move { Ok(Inspector::default()) }.boxed()
2896 })
2897 .unwrap();
2898
2899 let magic_link_name = "lk-0";
2900 let int_with_magic_name_index =
2901 state_guard.create_int_metric(magic_link_name, 0, BlockIndex::from(0)).unwrap();
2902
2903 (lazy_index, int_with_magic_name_index)
2904 };
2905
2906 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2907 let mut blocks = snapshot.scan();
2908 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Header));
2909
2910 let block = blocks.next().and_then(|b| b.cast::<Link>()).unwrap();
2911 assert_eq!(block.block_type(), Some(BlockType::LinkValue));
2912 assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk");
2913 assert_eq!(state.try_lock().unwrap().load_string(block.content_index()).unwrap(), "lk-0");
2914 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2915 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2916 let block = blocks.next().and_then(|b| b.cast::<Int>()).unwrap();
2917 assert_eq!(block.block_type(), Some(BlockType::IntValue));
2918 assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk-0");
2919 assert_all_free(blocks);
2920
2921 state.try_lock().unwrap().free_lazy_node(lazy_index).unwrap();
2922
2923 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2924 let mut blocks = snapshot.scan();
2925
2926 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Header));
2927 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Free));
2928 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2929 let block = blocks.next().and_then(|b| b.cast::<Int>()).unwrap();
2930 assert_eq!(block.block_type(), Some(BlockType::IntValue));
2931 assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk-0");
2932 assert_all_free(blocks);
2933 }
2934
2935 #[fuchsia::test]
2936 async fn stats() {
2937 let state = get_state(3 * 4096);
2939 let mut state_guard = state.try_lock().expect("lock state");
2940 let _block1 = state_guard
2941 .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2942 async move {
2943 let inspector = Inspector::default();
2944 inspector.root().record_uint("a", 1);
2945 Ok(inspector)
2946 }
2947 .boxed()
2948 })
2949 .unwrap();
2950 let _block2 = state_guard.create_uint_metric("test", 3, 0.into()).unwrap();
2951 assert_eq!(
2952 state_guard.stats(),
2953 Stats {
2954 total_dynamic_children: 1,
2955 maximum_size: 3 * 4096,
2956 current_size: 4096,
2957 allocated_blocks: 6, deallocated_blocks: 0,
2960 failed_allocations: 0,
2961 peak_bytes_requested: 144,
2962 }
2963 )
2964 }
2965
2966 #[fuchsia::test]
2967 fn transaction_locking() {
2968 let state = get_state(4096);
2969 state.with_current_header(|header| {
2971 assert_eq!(header.generation_count(), 0);
2972 });
2973
2974 state.begin_transaction();
2976 state.with_current_header(|header| {
2977 assert_eq!(header.generation_count(), 1);
2978 assert!(header.is_locked());
2979 });
2980
2981 let mut lock_guard1 = state.try_lock().expect("lock state");
2983 assert_eq!(lock_guard1.inner_lock.transaction_count, 1);
2984 assert_eq!(lock_guard1.header().generation_count(), 1);
2985 assert!(lock_guard1.header().is_locked());
2986 let _ = lock_guard1.create_node("test", 0.into());
2987 assert_eq!(lock_guard1.inner_lock.transaction_count, 1);
2988 assert_eq!(lock_guard1.header().generation_count(), 1);
2989
2990 drop(lock_guard1);
2992 state.with_current_header(|header| {
2993 assert_eq!(header.generation_count(), 1);
2994 assert!(header.is_locked());
2995 });
2996
2997 state.end_transaction();
2999
3000 state.with_current_header(|header| {
3001 assert_eq!(header.generation_count(), 2);
3002 assert!(!header.is_locked());
3003 });
3004
3005 let lock_guard2 = state.try_lock().expect("lock state");
3007 assert!(lock_guard2.header().is_locked());
3008 assert_eq!(lock_guard2.header().generation_count(), 3);
3009 assert_eq!(lock_guard2.inner_lock.transaction_count, 0);
3010 }
3011
3012 #[fuchsia::test]
3013 async fn update_header_vmo_size() {
3014 let core_state = get_state(3 * 4096);
3015 core_state.get_block(BlockIndex::HEADER, |header: &Block<_, Header>| {
3016 assert_eq!(header.vmo_size(), Ok(Some(4096)));
3017 });
3018 let block1_index = {
3019 let mut state = core_state.try_lock().expect("lock state");
3020
3021 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
3022 let data = chars.iter().cycle().take(6000).collect::<String>();
3023 let block_index =
3024 state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
3025 assert_eq!(state.header().vmo_size(), Ok(Some(2 * 4096)));
3026
3027 block_index
3028 };
3029
3030 let block2_index = {
3031 let mut state = core_state.try_lock().expect("lock state");
3032
3033 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
3034 let data = chars.iter().cycle().take(3000).collect::<String>();
3035 let block_index =
3036 state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
3037 assert_eq!(state.header().vmo_size(), Ok(Some(3 * 4096)));
3038
3039 block_index
3040 };
3041 {
3043 let mut state = core_state.try_lock().expect("lock state");
3044 assert!(state.free_string_or_bytes_buffer_property(block1_index).is_ok());
3045 assert!(state.free_string_or_bytes_buffer_property(block2_index).is_ok());
3046 }
3047 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
3048 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
3049 assert_all_free(blocks.into_iter().skip(1));
3050 }
3051
3052 #[fuchsia::test]
3053 fn test_buffer_property_on_overflow_set() {
3054 let core_state = get_state(4096);
3055 let block_index = {
3056 let mut state = core_state.try_lock().expect("lock state");
3057
3058 let block_index =
3060 state.create_buffer_property("test", b"test-property", 0.into()).unwrap();
3061
3062 for _ in 10..(4096 / constants::MIN_ORDER_SIZE).try_into().unwrap() {
3064 state.inner_lock.heap.allocate_block(constants::MIN_ORDER_SIZE).unwrap();
3065 }
3066
3067 let values = [b'a'; 8096];
3069 assert!(state.set_buffer_property(block_index, &values).is_err());
3070
3071 let block = state.get_block::<Buffer>(block_index);
3074 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
3075 assert_eq!(*block.index(), 2);
3076 assert_eq!(*block.parent_index(), 0);
3077 assert_eq!(*block.name_index(), 3);
3078 assert_eq!(block.total_length(), 0);
3079 assert_eq!(*block.extent_index(), 0);
3080 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
3081
3082 block_index
3083 };
3084
3085 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
3087 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
3088 assert_eq!(blocks.len(), 251);
3089 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
3090 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
3091 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
3092 assert_all_free_or_reserved(blocks.into_iter().skip(3));
3093
3094 {
3095 let mut state = core_state.try_lock().expect("lock state");
3096 assert_matches!(state.free_string_or_bytes_buffer_property(block_index), Ok(()));
3098 }
3099 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
3100 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
3101 assert_all_free_or_reserved(blocks.into_iter().skip(1));
3102 }
3103
3104 #[fuchsia::test]
3105 fn test_string_property_on_overflow_set() {
3106 let core_state = get_state(4096);
3107 {
3108 let mut state = core_state.try_lock().expect("lock state");
3109
3110 let block_index = state.create_string("test", "test-property", 0.into()).unwrap();
3112
3113 for _ in 10..(4096 / constants::MIN_ORDER_SIZE).try_into().unwrap() {
3115 state.inner_lock.heap.allocate_block(constants::MIN_ORDER_SIZE).unwrap();
3116 }
3117
3118 let values = ["a"].into_iter().cycle().take(5000).collect::<String>();
3121 assert!(state.set_string_property(block_index, values).is_err());
3122 let block = state.get_block::<Buffer>(block_index);
3123 assert_eq!(*block.index(), 2);
3124 assert_eq!(*block.parent_index(), 0);
3125 assert_eq!(*block.name_index(), 3);
3126
3127 assert_eq!(
3129 state.load_string(BlockIndex::from(*block.extent_index())).unwrap(),
3130 "test-property"
3131 );
3132
3133 assert!(state.create_int_metric("foo", 1, 0.into()).is_ok());
3135 assert!(state.create_int_metric("bar", 1, 0.into()).is_ok());
3136 };
3137
3138 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
3139 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
3140 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
3141 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
3142 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
3143 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
3144 assert_eq!(blocks[250].block_type(), Some(BlockType::IntValue));
3145 assert_eq!(blocks[251].block_type(), Some(BlockType::StringReference));
3146 assert_eq!(blocks[252].block_type(), Some(BlockType::IntValue));
3147 assert_eq!(blocks[253].block_type(), Some(BlockType::StringReference));
3148 assert_all_free_or_reserved(blocks.into_iter().skip(4).rev().skip(4));
3149 }
3150
3151 #[fuchsia::test]
3152 fn test_reparent_tombstone_leak() {
3153 use inspect_format::Free;
3154
3155 let core_state = get_state(4096);
3156 let mut state = core_state.try_lock().expect("lock state");
3157
3158 let parent_index = state.create_node("parent", 0.into()).unwrap();
3159 let child_index = state.create_node("child", parent_index).unwrap();
3160 let new_parent_index = state.create_node("new_parent", 0.into()).unwrap();
3161
3162 assert_eq!(state.get_block::<Node>(parent_index).child_count(), 1);
3164
3165 state.free_value(parent_index).unwrap();
3167 assert_eq!(
3168 state.get_block::<Tombstone>(parent_index).block_type(),
3169 Some(BlockType::Tombstone)
3170 );
3171
3172 state.reparent(child_index, new_parent_index).unwrap();
3176
3177 let parent_block = state.get_block::<Free>(parent_index);
3180 assert_eq!(parent_block.block_type(), Some(BlockType::Free));
3181 }
3182
3183 #[fuchsia::test]
3184 fn test_allocate_reserved_value_overflow_leak() {
3185 use inspect_format::HeaderFields;
3186 use inspect_format::constants::MAX_REFERENCE_COUNT;
3187
3188 let core_state = get_state(4096);
3189 let mut state = core_state.try_lock().expect("lock state");
3190
3191 let parent_index = state.create_node("foo", 0.into()).unwrap();
3193 let node_block = state.get_block::<Node>(parent_index);
3194 let name_index = node_block.name_index();
3195
3196 {
3198 let mut name_block = state.get_block_mut::<StringRef>(name_index);
3199 HeaderFields::set_string_reference_count(&mut name_block, MAX_REFERENCE_COUNT);
3200 assert_eq!(MAX_REFERENCE_COUNT, HeaderFields::string_reference_count(&name_block));
3201 }
3202
3203 let stats_before = state.stats();
3205
3206 let result = state.create_node("foo", parent_index);
3209 assert!(result.is_ok());
3210 {
3211 let name_block = state.get_block_mut::<StringRef>(name_index);
3212 assert_eq!(MAX_REFERENCE_COUNT, HeaderFields::string_reference_count(&name_block));
3213 }
3214
3215 let stats_after = state.stats();
3217
3218 let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3219 let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3220 assert_eq!(active_after, active_before + 1);
3222 }
3223
3224 #[fuchsia::test]
3225 fn test_set_array_string_slot_release_failure_leak() {
3226 use inspect_format::HeaderFields;
3227
3228 let core_state = get_state(4096);
3229 let mut state = core_state.try_lock().expect("lock state");
3230
3231 let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
3232 state.set_array_string_slot(array_index, 0, "foo").unwrap();
3233
3234 let foo_index =
3235 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(0).unwrap();
3236
3237 {
3239 let mut foo_block = state.get_block_mut::<StringRef>(foo_index);
3240 HeaderFields::set_string_reference_count(&mut foo_block, 0);
3241 }
3242
3243 let stats_before = state.stats();
3244 let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3245
3246 let result = state.set_array_string_slot(array_index, 0, "bar");
3250 assert!(result.is_err());
3251
3252 let stats_after = state.stats();
3253 let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3254
3255 assert_eq!(active_after, active_before);
3257 }
3258
3259 #[fuchsia::test]
3260 fn test_allocate_link_cleanup_failure() {
3261 let core_state = get_state(4096);
3262 {
3263 let mut state = core_state.try_lock().expect("lock state");
3264
3265 let mut nodes = vec![];
3268 while let Ok(idx) = state.create_node("n", 0.into()) {
3269 nodes.push(idx);
3271 }
3272
3273 state.free_value(nodes.pop().unwrap()).unwrap();
3276
3277 let result = state.create_lazy_node("n", 0.into(), LinkNodeDisposition::Inline, || {
3289 async move { Ok(Inspector::default()) }.boxed()
3290 });
3291
3292 assert!(result.is_err());
3293 }
3294
3295 core_state.with_current_header(|header| {
3297 assert_eq!(header.magic_number(), constants::HEADER_MAGIC_NUMBER);
3298 assert_eq!(header.version(), constants::HEADER_VERSION_NUMBER);
3299 });
3300 }
3301
3302 #[fuchsia::test]
3303 fn test_get_or_create_string_reference_payload_failure_leak() {
3304 let core_state = get_state(4096);
3305 let mut state = core_state.try_lock().expect("lock state");
3306
3307 let mut allocated_blocks = vec![];
3310 for size in &[32, 64, 128, 256, 512, 1024] {
3311 allocated_blocks.push(state.inner_lock.heap.allocate_block(*size).unwrap());
3312 }
3313
3314 let stats_before = state.stats();
3315
3316 let result = {
3324 let mut txn = Txn::new(&mut state.inner_lock);
3325 txn.get_or_create_string_reference("a".repeat(2040))
3326 };
3327 assert!(result.is_err());
3328
3329 let stats_after = state.stats();
3331 let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3332 let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3333 assert_eq!(active_after, active_before);
3334
3335 for block in allocated_blocks {
3337 state.inner_lock.heap.free_block(block).unwrap();
3338 }
3339 }
3340
3341 #[fuchsia::test]
3342 fn test_reparent_to_self_tombstone() {
3343 use inspect_format::Free;
3344
3345 let core_state = get_state(4096);
3346 let mut state = core_state.try_lock().expect("lock state");
3347
3348 let parent_index = state.create_node("parent", 0.into()).unwrap();
3349 let child_index = state.create_node("child", parent_index).unwrap();
3350
3351 state.free_value(parent_index).unwrap();
3353 assert_eq!(
3354 state.get_block::<Tombstone>(parent_index).block_type(),
3355 Some(BlockType::Tombstone)
3356 );
3357
3358 state.reparent(child_index, parent_index).unwrap();
3360
3361 let parent_block = state.get_block::<Tombstone>(parent_index);
3363 assert_eq!(parent_block.block_type(), Some(BlockType::Tombstone));
3364
3365 state.free_value(child_index).unwrap();
3367
3368 let parent_block = state.get_block::<Free>(parent_index);
3370 assert_eq!(parent_block.block_type(), Some(BlockType::Free));
3371 }
3372
3373 #[fuchsia::test]
3374 fn test_uncommitted_txn_drop_no_panic() {
3375 let core_state = get_state(4096);
3376 let mut state = core_state.try_lock().expect("lock state");
3377
3378 let stats_before = state.stats();
3379 {
3380 let mut txn = Txn::new(&mut state.inner_lock);
3381 let _block_index = txn.allocate_block(16).unwrap();
3382 }
3384 let stats_after = state.stats();
3385 let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3386 let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3387 assert_eq!(active_after, active_before);
3388 }
3389
3390 #[fuchsia::test]
3391 fn test_txn_rollback() {
3392 let core_state = get_state(4096);
3393 let mut state = core_state.try_lock().expect("lock state");
3394
3395 let parent_index = state.create_node("parent", 0.into()).unwrap();
3397 let parent_node = state.get_block::<Node>(parent_index);
3398 assert_eq!(parent_node.child_count(), 0);
3399
3400 let stats_before = state.stats();
3401
3402 {
3404 let mut txn = Txn::new(&mut state.inner_lock);
3405 let (child_index, name_index) = txn
3406 .allocate_reserved_value("child", parent_index, constants::MIN_ORDER_SIZE)
3407 .unwrap();
3408
3409 txn.block_mut::<Reserved>(child_index).become_node(name_index, parent_index);
3410
3411 let parent_node = txn.state.heap.container.block_at_unchecked::<Node>(parent_index);
3413 assert_eq!(parent_node.child_count(), 1);
3414
3415 }
3417
3418 let parent_node = state.get_block::<Node>(parent_index);
3420 assert_eq!(parent_node.child_count(), 0);
3421
3422 let stats_after = state.stats();
3424 let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3425 let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3426 assert_eq!(active_after, active_before);
3427 }
3428}