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 std::borrow::Cow;
18use std::collections::HashMap;
19use std::sync::Arc;
20use std::sync::atomic::{AtomicU64, Ordering};
21
22pub type LazyNodeContextFnArc =
24 Arc<dyn Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send>;
25
26trait SafeOp {
27 fn safe_sub(&self, other: Self) -> Self;
28 fn safe_add(&self, other: Self) -> Self;
29}
30
31impl SafeOp for u64 {
32 fn safe_sub(&self, other: u64) -> u64 {
33 self.checked_sub(other).unwrap_or(0)
34 }
35 fn safe_add(&self, other: u64) -> u64 {
36 self.checked_add(other).unwrap_or(u64::MAX)
37 }
38}
39
40impl SafeOp for i64 {
41 fn safe_sub(&self, other: i64) -> i64 {
42 self.checked_sub(other).unwrap_or(i64::MIN)
43 }
44 fn safe_add(&self, other: i64) -> i64 {
45 self.checked_add(other).unwrap_or(i64::MAX)
46 }
47}
48
49impl SafeOp for f64 {
50 fn safe_sub(&self, other: f64) -> f64 {
51 self - other
52 }
53 fn safe_add(&self, other: f64) -> f64 {
54 self + other
55 }
56}
57
58macro_rules! locked_state_metric_fns {
59 ($name:ident, $type:ident) => {
60 paste::paste! {
61 pub fn [<create_ $name _metric>]<'b>(
62 &mut self,
63 name: impl Into<Cow<'b, str>>,
64 value: $type,
65 parent_index: BlockIndex,
66 ) -> Result<BlockIndex, Error> {
67 self.inner_lock.[<create_ $name _metric>](name, value, parent_index)
68 }
69
70 pub fn [<set_ $name _metric>](&mut self, block_index: BlockIndex, value: $type) {
71 self.inner_lock.[<set_ $name _metric>](block_index, value);
72 }
73
74 pub fn [<add_ $name _metric>](&mut self, block_index: BlockIndex, value: $type) -> $type {
75 self.inner_lock.[<add_ $name _metric>](block_index, value)
76 }
77
78 pub fn [<subtract_ $name _metric>](&mut self, block_index: BlockIndex, value: $type) -> $type {
79 self.inner_lock.[<subtract_ $name _metric>](block_index, value)
80 }
81 }
82 };
83}
84
85macro_rules! metric_fns {
87 ($name:ident, $type:ident, $marker:ident) => {
88 paste::paste! {
89 fn [<create_ $name _metric>]<'a>(
90 &mut self,
91 name: impl Into<Cow<'a, str>>,
92 value: $type,
93 parent_index: BlockIndex,
94 ) -> Result<BlockIndex, Error> {
95 let pending = self.allocate_reserved_value(
96 name, parent_index, constants::MIN_ORDER_SIZE)?;
97 pending
98 .state
99 .heap
100 .container
101 .block_at_unchecked_mut::<Reserved>(pending.block_index)
102 .[<become_ $name _value>](value, pending.name_block_index, parent_index);
103 Ok(pending.commit())
104 }
105
106 fn [<set_ $name _metric>](&mut self, block_index: BlockIndex, value: $type) {
107 let mut block = self.heap.container.block_at_unchecked_mut::<$marker>(block_index);
108 block.set(value);
109 }
110
111 fn [<add_ $name _metric>](&mut self, block_index: BlockIndex, value: $type) -> $type {
112 let mut block = self.heap.container.block_at_unchecked_mut::<$marker>(block_index);
113 let current_value = block.value();
114 let new_value = current_value.safe_add(value);
115 block.set(new_value);
116 new_value
117 }
118
119 fn [<subtract_ $name _metric>](&mut self, block_index: BlockIndex, value: $type) -> $type {
120 let mut block = self.heap.container.block_at_unchecked_mut::<$marker>(block_index);
121 let current_value = block.value();
122 let new_value = current_value.safe_sub(value);
123 block.set(new_value);
124 new_value
125 }
126 }
127 };
128}
129macro_rules! locked_state_array_fns {
130 ($name:ident, $type:ident, $value:ident) => {
131 paste::paste! {
132 pub fn [<create_ $name _array>]<'b>(
133 &mut self,
134 name: impl Into<Cow<'b, str>>,
135 slots: usize,
136 array_format: ArrayFormat,
137 parent_index: BlockIndex,
138 ) -> Result<BlockIndex, Error> {
139 self.inner_lock.[<create_ $name _array>](name, slots, array_format, parent_index)
140 }
141
142 pub fn [<set_array_ $name _slot>](
143 &mut self, block_index: BlockIndex, slot_index: usize, value: $type
144 ) {
145 self.inner_lock.[<set_array_ $name _slot>](block_index, slot_index, value);
146 }
147
148 pub fn [<add_array_ $name _slot>](
149 &mut self, block_index: BlockIndex, slot_index: usize, value: $type
150 ) -> Option<$type> {
151 self.inner_lock.[<add_array_ $name _slot>](block_index, slot_index, value)
152 }
153
154 pub fn [<subtract_array_ $name _slot>](
155 &mut self, block_index: BlockIndex, slot_index: usize, value: $type
156 ) -> Option<$type> {
157 self.inner_lock.[<subtract_array_ $name _slot>](block_index, slot_index, value)
158 }
159 }
160 };
161}
162
163macro_rules! arithmetic_array_fns {
164 ($name:ident, $type:ident, $value:ident, $marker:ident) => {
165 paste::paste! {
166 pub fn [<create_ $name _array>]<'a>(
167 &mut self,
168 name: impl Into<Cow<'a, str>>,
169 slots: usize,
170 array_format: ArrayFormat,
171 parent_index: BlockIndex,
172 ) -> Result<BlockIndex, Error> {
173 let block_size =
174 slots as usize * std::mem::size_of::<$type>() + constants::MIN_ORDER_SIZE;
175 if block_size > constants::MAX_ORDER_SIZE {
176 return Err(Error::BlockSizeTooBig(block_size))
177 }
178 let pending = self.allocate_reserved_value(
179 name, parent_index, block_size)?;
180 pending
181 .state
182 .heap
183 .container
184 .block_at_unchecked_mut::<Reserved>(pending.block_index)
185 .become_array_value::<$marker>(
186 slots, array_format, pending.name_block_index, parent_index
187 )?;
188 Ok(pending.commit())
189 }
190
191 pub fn [<set_array_ $name _slot>](
192 &mut self, block_index: BlockIndex, slot_index: usize, value: $type
193 ) {
194 let mut block = self.heap.container
195 .block_at_unchecked_mut::<Array<$marker>>(block_index);
196 block.set(slot_index, value);
197 }
198
199 pub fn [<add_array_ $name _slot>](
200 &mut self, block_index: BlockIndex, slot_index: usize, value: $type
201 ) -> Option<$type> {
202 let mut block = self.heap.container
203 .block_at_unchecked_mut::<Array<$marker>>(block_index);
204 let previous_value = block.get(slot_index)?;
205 let new_value = previous_value.safe_add(value);
206 block.set(slot_index, new_value);
207 Some(new_value)
208 }
209
210 pub fn [<subtract_array_ $name _slot>](
211 &mut self, block_index: BlockIndex, slot_index: usize, value: $type
212 ) -> Option<$type> {
213 let mut block = self.heap.container
214 .block_at_unchecked_mut::<Array<$marker>>(block_index);
215 let previous_value = block.get(slot_index)?;
216 let new_value = previous_value.safe_sub(value);
217 block.set(slot_index, new_value);
218 Some(new_value)
219 }
220 }
221 };
222}
223
224#[derive(Clone, Debug)]
228pub struct State {
229 inner: Arc<Mutex<InnerState>>,
233}
234
235impl PartialEq for State {
236 fn eq(&self, other: &Self) -> bool {
237 Arc::ptr_eq(&self.inner, &other.inner)
238 }
239}
240
241impl State {
242 pub fn create(
245 heap: Heap<Container>,
246 storage: Arc<<Container as BlockContainer>::ShareableData>,
247 ) -> Result<Self, Error> {
248 let inner = Arc::new(Mutex::new(InnerState::new(heap, storage)));
249 Ok(Self { inner })
250 }
251
252 pub fn try_lock(&self) -> Result<LockedStateGuard<'_>, Error> {
255 let inner_lock = self.inner.lock();
256 LockedStateGuard::new(inner_lock)
257 }
258
259 pub fn begin_transaction(&self) {
262 self.inner.lock().lock_header();
263 }
264
265 pub fn end_transaction(&self) {
268 self.inner.lock().unlock_header();
269 }
270
271 pub fn copy_vmo_bytes(&self) -> Option<Vec<u8>> {
273 let state = self.inner.lock();
274 if state.transaction_count > 0 {
275 return None;
276 }
277
278 Some(state.heap.bytes())
279 }
280}
281
282#[cfg(test)]
283impl State {
284 pub(crate) fn with_current_header<F, R>(&self, callback: F) -> R
285 where
286 F: FnOnce(&Block<&Container, inspect_format::Header>) -> R,
287 {
288 let lock_guard = LockedStateGuard::without_gen_count_changes(self.inner.lock());
291 let block = lock_guard.header();
292 callback(&block)
293 }
294
295 #[track_caller]
296 pub(crate) fn get_block<F, K>(&self, index: BlockIndex, callback: F)
297 where
298 K: inspect_format::BlockKind,
299 F: FnOnce(&Block<&Container, K>),
300 {
301 let state_lock = self.try_lock().unwrap();
302 callback(&state_lock.get_block::<K>(index))
303 }
304
305 #[track_caller]
306 pub(crate) fn get_block_mut<F, K>(&self, index: BlockIndex, callback: F)
307 where
308 K: inspect_format::BlockKind,
309 F: FnOnce(&mut Block<&mut Container, K>),
310 {
311 let mut state_lock = self.try_lock().unwrap();
312 callback(&mut state_lock.get_block_mut::<K>(index))
313 }
314}
315
316#[derive(Debug, Eq, PartialEq)]
318pub struct Stats {
319 pub total_dynamic_children: usize,
321
322 pub maximum_size: usize,
324
325 pub current_size: usize,
327
328 pub allocated_blocks: usize,
331
332 pub deallocated_blocks: usize,
334
335 pub failed_allocations: 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 }
375 }
376
377 pub fn callbacks(&self) -> &HashMap<String, LazyNodeContextFnArc> {
379 &self.inner_lock.callbacks
380 }
381
382 pub fn create_node<'b>(
384 &mut self,
385 name: impl Into<Cow<'b, str>>,
386 parent_index: BlockIndex,
387 ) -> Result<BlockIndex, Error> {
388 self.inner_lock.create_node(name, parent_index)
389 }
390
391 pub fn create_lazy_node<'b, F>(
394 &mut self,
395 name: impl Into<Cow<'b, str>>,
396 parent_index: BlockIndex,
397 disposition: LinkNodeDisposition,
398 callback: F,
399 ) -> Result<BlockIndex, Error>
400 where
401 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
402 {
403 self.inner_lock.create_lazy_node(name, parent_index, disposition, callback)
404 }
405
406 pub fn free_lazy_node(&mut self, index: BlockIndex) -> Result<(), Error> {
407 self.inner_lock.free_lazy_node(index)
408 }
409
410 pub fn free_value(&mut self, index: BlockIndex) -> Result<(), Error> {
412 self.inner_lock.free_value(index)
413 }
414
415 pub fn create_buffer_property<'b>(
417 &mut self,
418 name: impl Into<Cow<'b, str>>,
419 value: &[u8],
420 parent_index: BlockIndex,
421 ) -> Result<BlockIndex, Error> {
422 self.inner_lock.create_buffer_property(name, value, parent_index)
423 }
424
425 pub fn create_string<'b, 'c>(
428 &mut self,
429 name: impl Into<Cow<'b, str>>,
430 value: impl Into<Cow<'c, str>>,
431 parent_index: BlockIndex,
432 ) -> Result<BlockIndex, Error> {
433 self.inner_lock.create_string(name, value, parent_index)
434 }
435
436 pub fn reparent(
437 &mut self,
438 being_reparented: BlockIndex,
439 new_parent: BlockIndex,
440 ) -> Result<(), Error> {
441 self.inner_lock.reparent(being_reparented, new_parent)
442 }
443
444 pub fn free_string_or_bytes_buffer_property(&mut self, index: BlockIndex) -> Result<(), Error> {
446 self.inner_lock.free_string_or_bytes_buffer_property(index)
447 }
448
449 pub fn set_string_property<'b>(
451 &mut self,
452 block_index: BlockIndex,
453 value: impl Into<Cow<'b, str>>,
454 ) -> Result<(), Error> {
455 self.inner_lock.set_string_property(block_index, value)
456 }
457
458 pub fn set_buffer_property(
460 &mut self,
461 block_index: BlockIndex,
462 value: &[u8],
463 ) -> Result<(), Error> {
464 self.inner_lock.set_buffer_property(block_index, value)
465 }
466
467 pub fn create_bool<'b>(
468 &mut self,
469 name: impl Into<Cow<'b, str>>,
470 value: bool,
471 parent_index: BlockIndex,
472 ) -> Result<BlockIndex, Error> {
473 self.inner_lock.create_bool(name, value, parent_index)
474 }
475
476 pub fn set_bool(&mut self, block_index: BlockIndex, value: bool) {
477 self.inner_lock.set_bool(block_index, value)
478 }
479
480 locked_state_metric_fns!(int, i64);
481 locked_state_metric_fns!(uint, u64);
482 locked_state_metric_fns!(double, f64);
483
484 locked_state_array_fns!(int, i64, IntValue);
485 locked_state_array_fns!(uint, u64, UintValue);
486 locked_state_array_fns!(double, f64, DoubleValue);
487
488 pub fn clear_array(
490 &mut self,
491 block_index: BlockIndex,
492 start_slot_index: usize,
493 ) -> Result<(), Error> {
494 self.inner_lock.clear_array(block_index, start_slot_index)
495 }
496
497 pub fn create_string_array<'b>(
498 &mut self,
499 name: impl Into<Cow<'b, str>>,
500 slots: usize,
501 parent_index: BlockIndex,
502 ) -> Result<BlockIndex, Error> {
503 self.inner_lock.create_string_array(name, slots, parent_index)
504 }
505
506 pub fn get_array_size(&self, block_index: BlockIndex) -> usize {
507 self.inner_lock.get_array_size(block_index)
508 }
509
510 pub fn set_array_string_slot<'b>(
511 &mut self,
512 block_index: BlockIndex,
513 slot_index: usize,
514 value: impl Into<Cow<'b, str>>,
515 ) -> Result<(), Error> {
516 self.inner_lock.set_array_string_slot(block_index, slot_index, value)
517 }
518}
519
520impl Drop for LockedStateGuard<'_> {
521 fn drop(&mut self) {
522 #[cfg(test)]
523 {
524 if !self.drop {
525 return;
526 }
527 }
528 if self.inner_lock.transaction_count == 0 {
529 self.inner_lock.header_mut().unlock();
530 }
531 }
532}
533
534#[cfg(test)]
535impl<'a> LockedStateGuard<'a> {
536 fn without_gen_count_changes(inner_lock: MutexGuard<'a, InnerState>) -> Self {
537 Self { inner_lock, drop: false }
538 }
539
540 pub(crate) fn load_string(&self, index: BlockIndex) -> Result<String, Error> {
541 self.inner_lock.load_key_string(index)
542 }
543
544 pub(crate) fn allocate_link<'b, 'c>(
545 &mut self,
546 name: impl Into<Cow<'b, str>>,
547 content: impl Into<Cow<'c, str>>,
548 disposition: LinkNodeDisposition,
549 parent_index: BlockIndex,
550 ) -> Result<BlockIndex, Error> {
551 self.inner_lock.allocate_link(name, content, disposition, parent_index)
552 }
553
554 #[track_caller]
555 pub(crate) fn get_block<K: inspect_format::BlockKind>(
556 &self,
557 index: BlockIndex,
558 ) -> Block<&Container, K> {
559 self.inner_lock.heap.container.maybe_block_at::<K>(index).unwrap()
560 }
561
562 fn header(&self) -> Block<&Container, inspect_format::Header> {
563 self.get_block(BlockIndex::HEADER)
564 }
565
566 #[track_caller]
567 fn get_block_mut<K: inspect_format::BlockKind>(
568 &mut self,
569 index: BlockIndex,
570 ) -> Block<&mut Container, K> {
571 self.inner_lock.heap.container.maybe_block_at_mut::<K>(index).unwrap()
572 }
573}
574
575#[derive(Derivative)]
577#[derivative(Debug)]
578struct InnerState {
579 #[derivative(Debug = "ignore")]
580 heap: Heap<Container>,
581 #[allow(dead_code)] storage: Arc<<Container as BlockContainer>::ShareableData>,
583 next_unique_link_id: AtomicU64,
584 transaction_count: usize,
585
586 string_reference_block_indexes: HashMap<Arc<Cow<'static, str>>, BlockIndex>,
588 block_index_string_references: HashMap<BlockIndex, Arc<Cow<'static, str>>>,
590
591 #[derivative(Debug = "ignore")]
592 callbacks: HashMap<String, LazyNodeContextFnArc>,
593}
594
595#[cfg(target_os = "fuchsia")]
596impl InnerState {
597 fn frozen_vmo_copy(&mut self) -> Result<zx::Vmo, Error> {
598 if self.transaction_count > 0 {
599 return Err(Error::ConcurrentTransaction(self.transaction_count));
600 }
601
602 let old = self.header_mut().freeze();
603 let child = self
604 .storage
605 .create_child(
606 zx::VmoChildOptions::SNAPSHOT | zx::VmoChildOptions::NO_WRITE,
607 0,
608 self.storage.get_size().map_err(Error::GetVmoSize)?,
609 )
610 .map_err(Error::CreateChildVmo);
611 self.header_mut().thaw(old);
612 child
613 }
614}
615
616struct PendingValueBlock<'a> {
617 state: &'a mut InnerState,
618 block_index: BlockIndex,
619 name_block_index: BlockIndex,
620 parent_index: BlockIndex,
621}
622
623impl Drop for PendingValueBlock<'_> {
624 fn drop(&mut self) {
625 if self.state.heap.container.block_at(self.block_index).block_type()
626 == Some(BlockType::Reserved)
627 {
628 self.state
629 .heap
630 .container
631 .block_at_unchecked_mut::<Reserved>(self.block_index)
632 .become_node(self.name_block_index, self.parent_index);
633 }
634
635 if let Err(e) = self.state.delete_value(self.block_index) {
636 panic!("failed to free pending block: {:?}", e);
637 }
638 }
639}
640
641impl PendingValueBlock<'_> {
642 fn commit(self) -> BlockIndex {
643 let index = self.block_index;
644 std::mem::forget(self);
645 index
646 }
647}
648
649struct PendingStringReference<'a> {
650 state: &'a mut InnerState,
651 block_index: BlockIndex,
652}
653
654impl Drop for PendingStringReference<'_> {
655 fn drop(&mut self) {
656 if let Err(e) = self.state.maybe_free_string_reference(self.block_index) {
657 log::error!("failed to maybe free pending string reference: {:?}", e);
658 }
659 }
660}
661
662impl<'a> PendingStringReference<'a> {
663 fn new<'b>(state: &'a mut InnerState, value: impl Into<Cow<'b, str>>) -> Result<Self, Error> {
664 let block_index = state.get_or_create_string_reference(value)?;
665 Ok(Self { state, block_index })
666 }
667
668 fn commit(self) -> Result<BlockIndex, Error> {
669 self.state
670 .heap
671 .container
672 .block_at_unchecked_mut::<StringRef>(self.block_index)
673 .increment_ref_count()?;
674 let index = self.block_index;
675 std::mem::forget(self);
676 Ok(index)
677 }
678}
679
680struct PendingBlock<'a> {
681 state: &'a mut InnerState,
682 block_index: BlockIndex,
683}
684
685impl Drop for PendingBlock<'_> {
686 fn drop(&mut self) {
687 let _ = self.state.heap.free_block(self.block_index);
688 }
689}
690
691impl PendingBlock<'_> {
692 fn commit(self) -> BlockIndex {
693 let index = self.block_index;
694 std::mem::forget(self);
695 index
696 }
697}
698
699impl InnerState {
700 pub fn new(
702 heap: Heap<Container>,
703 storage: Arc<<Container as BlockContainer>::ShareableData>,
704 ) -> Self {
705 Self {
706 heap,
707 storage,
708 next_unique_link_id: AtomicU64::new(0),
709 callbacks: HashMap::new(),
710 transaction_count: 0,
711 string_reference_block_indexes: HashMap::new(),
712 block_index_string_references: HashMap::new(),
713 }
714 }
715
716 #[inline]
717 fn header_mut(&mut self) -> Block<&mut Container, inspect_format::Header> {
718 self.heap.container.block_at_unchecked_mut(BlockIndex::HEADER)
719 }
720
721 fn lock_header(&mut self) {
722 if self.transaction_count == 0 {
723 self.header_mut().lock();
724 }
725 self.transaction_count += 1;
726 }
727
728 fn unlock_header(&mut self) {
729 self.transaction_count -= 1;
730 if self.transaction_count == 0 {
731 self.header_mut().unlock();
732 }
733 }
734
735 fn create_node<'a>(
737 &mut self,
738 name: impl Into<Cow<'a, str>>,
739 parent_index: BlockIndex,
740 ) -> Result<BlockIndex, Error> {
741 let pending =
742 self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
743 pending
744 .state
745 .heap
746 .container
747 .block_at_unchecked_mut::<Reserved>(pending.block_index)
748 .become_node(pending.name_block_index, parent_index);
749 Ok(pending.commit())
750 }
751
752 fn create_lazy_node<'a, F>(
755 &mut self,
756 name: impl Into<Cow<'a, str>>,
757 parent_index: BlockIndex,
758 disposition: LinkNodeDisposition,
759 callback: F,
760 ) -> Result<BlockIndex, Error>
761 where
762 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
763 {
764 let name = name.into();
765 let content = self.unique_link_name(&name);
766 let link = self.allocate_link(name, &content, disposition, parent_index)?;
767 self.callbacks.insert(content, Arc::from(callback));
768 Ok(link)
769 }
770
771 fn free_lazy_node(&mut self, index: BlockIndex) -> Result<(), Error> {
773 let content_block_index =
774 self.heap.container.block_at_unchecked::<Link>(index).content_index();
775 let content_block_type = self.heap.container.block_at(content_block_index).block_type();
776 let content = self.load_key_string(content_block_index)?;
777 self.delete_value(index)?;
778 match content_block_type {
780 Some(BlockType::StringReference) => {
781 self.release_string_reference(content_block_index)?;
782 }
783 _ => {
784 self.heap.free_block(content_block_index).expect("Failed to free block");
785 }
786 }
787
788 self.callbacks.remove(content.as_str());
789 Ok(())
790 }
791
792 fn unique_link_name(&mut self, prefix: &str) -> String {
793 let id = self.next_unique_link_id.fetch_add(1, Ordering::Relaxed);
794 format!("{prefix}-{id}")
795 }
796
797 pub(crate) fn allocate_link<'a, 'b>(
798 &mut self,
799 name: impl Into<Cow<'a, str>>,
800 content: impl Into<Cow<'b, str>>,
801 disposition: LinkNodeDisposition,
802 parent_index: BlockIndex,
803 ) -> Result<BlockIndex, Error> {
804 let pending =
805 self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
806 let content_index = PendingStringReference::new(pending.state, content)?.commit()?;
807
808 pending
809 .state
810 .heap
811 .container
812 .block_at_unchecked_mut::<Reserved>(pending.block_index)
813 .become_link(pending.name_block_index, parent_index, content_index, disposition);
814 Ok(pending.commit())
815 }
816
817 fn free_value(&mut self, index: BlockIndex) -> Result<(), Error> {
819 self.delete_value(index)?;
820 Ok(())
821 }
822
823 fn create_buffer_property<'a>(
825 &mut self,
826 name: impl Into<Cow<'a, str>>,
827 value: &[u8],
828 parent_index: BlockIndex,
829 ) -> Result<BlockIndex, Error> {
830 let pending =
831 self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
832 pending
833 .state
834 .heap
835 .container
836 .block_at_unchecked_mut::<Reserved>(pending.block_index)
837 .become_property(pending.name_block_index, parent_index, PropertyFormat::Bytes);
838 pending.state.inner_set_buffer_property_value(pending.block_index, value)?;
839 Ok(pending.commit())
840 }
841
842 fn create_string<'a, 'b>(
845 &mut self,
846 name: impl Into<Cow<'a, str>>,
847 value: impl Into<Cow<'b, str>>,
848 parent_index: BlockIndex,
849 ) -> Result<BlockIndex, Error> {
850 let pending =
851 self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
852 pending
853 .state
854 .heap
855 .container
856 .block_at_unchecked_mut::<Reserved>(pending.block_index)
857 .become_property(
858 pending.name_block_index,
859 parent_index,
860 PropertyFormat::StringReference,
861 );
862
863 let value_index = PendingStringReference::new(pending.state, value)?.commit()?;
864
865 let mut block =
866 pending.state.heap.container.block_at_unchecked_mut::<Buffer>(pending.block_index);
867 block.set_extent_index(value_index);
868 block.set_total_length(0);
869
870 Ok(pending.commit())
871 }
872
873 fn get_or_create_string_reference<'a>(
876 &mut self,
877 value: impl Into<Cow<'a, str>>,
878 ) -> Result<BlockIndex, Error> {
879 let value = value.into();
880 match self.string_reference_block_indexes.get(&value) {
881 Some(index) => Ok(*index),
882 None => {
883 let block_index = self.heap.allocate_block(utils::block_size_for_payload(
884 value.len() + constants::STRING_REFERENCE_TOTAL_LENGTH_BYTES,
885 ))?;
886 self.heap
887 .container
888 .block_at_unchecked_mut::<Reserved>(block_index)
889 .become_string_reference();
890 let res = self.write_string_reference_payload(block_index, &value);
891 if let Err(err) = res {
892 let _ = self.heap.free_block(block_index);
893 return Err(err);
894 }
895 let owned_value = Arc::new(value.into_owned().into());
896 self.string_reference_block_indexes.insert(Arc::clone(&owned_value), block_index);
897 self.block_index_string_references.insert(block_index, owned_value);
898 Ok(block_index)
899 }
900 }
901 }
902
903 fn write_string_reference_payload(
905 &mut self,
906 block_index: BlockIndex,
907 value: &str,
908 ) -> Result<(), Error> {
909 let value_bytes = value.as_bytes();
910 let (head_extent, bytes_written) = {
911 let inlined = self.inline_string_reference(block_index, value.as_bytes());
912 if inlined < value.len() {
913 let (head, in_extents) = self.write_extents(&value_bytes[inlined..])?;
914 (head, inlined + in_extents)
915 } else {
916 (BlockIndex::EMPTY, inlined)
917 }
918 };
919 let mut block = self.heap.container.block_at_unchecked_mut::<StringRef>(block_index);
920 block.set_next_index(head_extent);
921 block.set_total_length(bytes_written.try_into().unwrap_or(u32::MAX));
922 Ok(())
923 }
924
925 fn inline_string_reference(&mut self, block_index: BlockIndex, value: &[u8]) -> usize {
928 self.heap.container.block_at_unchecked_mut::<StringRef>(block_index).write_inline(value)
929 }
930
931 fn release_string_reference(&mut self, block_index: BlockIndex) -> Result<(), Error> {
934 self.heap
935 .container
936 .block_at_unchecked_mut::<StringRef>(block_index)
937 .decrement_ref_count()?;
938 self.maybe_free_string_reference(block_index)
939 }
940
941 fn maybe_free_string_reference(&mut self, block_index: BlockIndex) -> Result<(), Error> {
944 let block = self.heap.container.block_at_unchecked::<StringRef>(block_index);
945 if block.reference_count() != 0 {
946 return Ok(());
947 }
948 let first_extent = block.next_extent();
949 self.heap.free_block(block_index)?;
950 let str_ref = self.block_index_string_references.remove(&block_index).expect("blk idx key");
951 self.string_reference_block_indexes.remove(&str_ref);
952
953 if first_extent == BlockIndex::EMPTY {
954 return Ok(());
955 }
956 self.free_extents(first_extent)
957 }
958
959 fn load_key_string(&self, index: BlockIndex) -> Result<String, Error> {
960 let block = self.heap.container.block_at(index);
961 match block.block_type() {
962 Some(BlockType::StringReference) => {
963 self.read_string_reference(block.cast::<StringRef>().unwrap())
964 }
965 Some(BlockType::Name) => block
966 .cast::<Name>()
967 .unwrap()
968 .contents()
969 .map(|s| s.to_string())
970 .map_err(|_| Error::NameNotUtf8),
971 _ => Err(Error::InvalidBlockTypeNumber(index, block.block_type_raw())),
972 }
973 }
974
975 fn read_string_reference(&self, block: Block<&Container, StringRef>) -> Result<String, Error> {
977 let mut content = block.inline_data()?.to_vec();
978 let mut next = block.next_extent();
979 while next != BlockIndex::EMPTY {
980 let next_block = self.heap.container.block_at_unchecked::<Extent>(next);
981 content.extend_from_slice(next_block.contents()?);
982 next = next_block.next_extent();
983 }
984
985 content.truncate(block.total_length());
986 String::from_utf8(content).ok().ok_or(Error::NameNotUtf8)
987 }
988
989 fn free_string_or_bytes_buffer_property(&mut self, index: BlockIndex) -> Result<(), Error> {
991 let (format, data_index) = {
992 let block = self.heap.container.block_at_unchecked::<Buffer>(index);
993 (block.format(), block.extent_index())
994 };
995 match format {
996 Some(PropertyFormat::String) | Some(PropertyFormat::Bytes) => {
997 self.free_extents(data_index)?;
998 }
999 Some(PropertyFormat::StringReference) => {
1000 self.release_string_reference(data_index)?;
1001 }
1002 _ => {
1003 return Err(Error::VmoFormat(FormatError::InvalidBufferFormat(
1004 self.heap.container.block_at_unchecked(index).format_raw(),
1005 )));
1006 }
1007 }
1008
1009 self.delete_value(index)?;
1010 Ok(())
1011 }
1012
1013 fn set_string_property<'a>(
1015 &mut self,
1016 block_index: BlockIndex,
1017 value: impl Into<Cow<'a, str>>,
1018 ) -> Result<(), Error> {
1019 self.inner_set_string_property_value(block_index, value)?;
1020 Ok(())
1021 }
1022
1023 fn set_buffer_property(&mut self, block_index: BlockIndex, value: &[u8]) -> Result<(), Error> {
1025 self.inner_set_buffer_property_value(block_index, value)?;
1026 Ok(())
1027 }
1028
1029 fn check_lineage(
1030 &self,
1031 being_reparented: BlockIndex,
1032 new_parent: BlockIndex,
1033 ) -> Result<(), Error> {
1034 if being_reparented == BlockIndex::ROOT {
1036 return Err(Error::AdoptAncestor);
1037 }
1038
1039 let mut being_checked = new_parent;
1040 while being_checked != BlockIndex::ROOT {
1041 if being_checked == being_reparented {
1042 return Err(Error::AdoptAncestor);
1043 }
1044 being_checked =
1047 self.heap.container.block_at_unchecked::<Node>(being_checked).parent_index();
1048 }
1049
1050 Ok(())
1051 }
1052
1053 fn reparent(
1054 &mut self,
1055 being_reparented: BlockIndex,
1056 new_parent: BlockIndex,
1057 ) -> Result<(), Error> {
1058 self.check_lineage(being_reparented, new_parent)?;
1059 let original_parent_idx =
1060 self.heap.container.block_at_unchecked::<Node>(being_reparented).parent_index();
1061 if original_parent_idx == new_parent {
1062 return Ok(());
1063 }
1064 if original_parent_idx != BlockIndex::ROOT {
1065 let original_parent_block = self.heap.container.block_at_mut(original_parent_idx);
1066 match original_parent_block.block_type() {
1067 Some(BlockType::Tombstone) => {
1068 let mut parent = original_parent_block.cast::<Tombstone>().unwrap();
1069 let child_count = parent.child_count() - 1;
1070 if child_count == 0 {
1071 self.heap.free_block(original_parent_idx).expect("Failed to free block");
1072 } else {
1073 parent.set_child_count(child_count);
1074 }
1075 }
1076 Some(BlockType::NodeValue) => {
1077 let mut parent = original_parent_block.cast::<Node>().unwrap();
1078 let child_count = parent.child_count() - 1;
1079 parent.set_child_count(child_count);
1080 }
1081 _ => {
1082 return Err(Error::InvalidBlockType(
1083 original_parent_idx,
1084 original_parent_block.block_type_raw(),
1085 ));
1086 }
1087 }
1088 }
1089
1090 self.heap.container.block_at_unchecked_mut::<Node>(being_reparented).set_parent(new_parent);
1091
1092 if new_parent != BlockIndex::ROOT {
1093 let mut new_parent_block =
1094 self.heap.container.block_at_unchecked_mut::<Node>(new_parent);
1095 let child_count = new_parent_block.child_count() + 1;
1096 new_parent_block.set_child_count(child_count);
1097 }
1098
1099 Ok(())
1100 }
1101
1102 fn create_bool<'a>(
1103 &mut self,
1104 name: impl Into<Cow<'a, str>>,
1105 value: bool,
1106 parent_index: BlockIndex,
1107 ) -> Result<BlockIndex, Error> {
1108 let pending =
1109 self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
1110 pending
1111 .state
1112 .heap
1113 .container
1114 .block_at_unchecked_mut::<Reserved>(pending.block_index)
1115 .become_bool_value(value, pending.name_block_index, parent_index);
1116 Ok(pending.commit())
1117 }
1118
1119 fn set_bool(&mut self, block_index: BlockIndex, value: bool) {
1120 let mut block = self.heap.container.block_at_unchecked_mut::<Bool>(block_index);
1121 block.set(value);
1122 }
1123
1124 metric_fns!(int, i64, Int);
1125 metric_fns!(uint, u64, Uint);
1126 metric_fns!(double, f64, Double);
1127
1128 arithmetic_array_fns!(int, i64, IntValue, Int);
1129 arithmetic_array_fns!(uint, u64, UintValue, Uint);
1130 arithmetic_array_fns!(double, f64, DoubleValue, Double);
1131
1132 fn create_string_array<'a>(
1133 &mut self,
1134 name: impl Into<Cow<'a, str>>,
1135 slots: usize,
1136 parent_index: BlockIndex,
1137 ) -> Result<BlockIndex, Error> {
1138 let block_size = slots * StringRef::array_entry_type_size() + constants::MIN_ORDER_SIZE;
1139 if block_size > constants::MAX_ORDER_SIZE {
1140 return Err(Error::BlockSizeTooBig(block_size));
1141 }
1142 let pending = self.allocate_reserved_value(name, parent_index, block_size)?;
1143 pending
1144 .state
1145 .heap
1146 .container
1147 .block_at_unchecked_mut::<Reserved>(pending.block_index)
1148 .become_array_value::<StringRef>(
1149 slots,
1150 ArrayFormat::Default,
1151 pending.name_block_index,
1152 parent_index,
1153 )?;
1154 Ok(pending.commit())
1155 }
1156
1157 fn get_array_size(&self, block_index: BlockIndex) -> usize {
1158 let block = self.heap.container.block_at_unchecked::<Array<Unknown>>(block_index);
1159 block.slots()
1160 }
1161
1162 fn set_array_string_slot<'a>(
1163 &mut self,
1164 block_index: BlockIndex,
1165 slot_index: usize,
1166 value: impl Into<Cow<'a, str>>,
1167 ) -> Result<(), Error> {
1168 if self.heap.container.block_at_unchecked_mut::<Array<StringRef>>(block_index).slots()
1169 <= slot_index
1170 {
1171 return Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(slot_index)));
1172 }
1173
1174 let value = value.into();
1175
1176 let existing_index = self
1177 .heap
1178 .container
1179 .block_at_unchecked::<Array<StringRef>>(block_index)
1180 .get_string_index_at(slot_index)
1181 .ok_or(Error::InvalidArrayIndex(slot_index))?;
1182
1183 let reference_index = if !value.is_empty() {
1184 PendingStringReference::new(self, value)?.commit()?
1185 } else {
1186 BlockIndex::EMPTY
1187 };
1188
1189 if existing_index != BlockIndex::EMPTY {
1190 match self.release_string_reference(existing_index) {
1191 Ok(()) => {}
1192 Err(err) => {
1193 if reference_index != BlockIndex::EMPTY {
1194 let _ = self.release_string_reference(reference_index);
1195 }
1196 return Err(err);
1197 }
1198 }
1199 }
1200
1201 self.heap
1202 .container
1203 .block_at_unchecked_mut::<Array<StringRef>>(block_index)
1204 .set_string_slot(slot_index, reference_index);
1205 Ok(())
1206 }
1207
1208 fn clear_array(
1211 &mut self,
1212 block_index: BlockIndex,
1213 start_slot_index: usize,
1214 ) -> Result<(), Error> {
1215 let block = self.heap.container.block_at_unchecked_mut::<Array<Unknown>>(block_index);
1218 match block.entry_type() {
1219 Some(value) if value.is_numeric_value() => {
1220 self.heap
1221 .container
1222 .block_at_unchecked_mut::<Array<Unknown>>(block_index)
1223 .clear(start_slot_index);
1224 }
1225 Some(BlockType::StringReference) => {
1226 let array_slots = block.slots();
1227 for i in start_slot_index..array_slots {
1228 let index = {
1229 let mut block = self
1230 .heap
1231 .container
1232 .block_at_unchecked_mut::<Array<StringRef>>(block_index);
1233 let index =
1234 block.get_string_index_at(i).ok_or(Error::InvalidArrayIndex(i))?;
1235 if index == BlockIndex::EMPTY {
1236 continue;
1237 }
1238 block.set_string_slot(i, BlockIndex::EMPTY);
1239 index
1240 };
1241 self.release_string_reference(index)?;
1242 }
1243 }
1244
1245 _ => return Err(Error::InvalidArrayType(block_index)),
1246 }
1247
1248 Ok(())
1249 }
1250
1251 fn allocate_reserved_value<'a, 'b>(
1252 &'b mut self,
1253 name: impl Into<Cow<'a, str>>,
1254 parent_index: BlockIndex,
1255 block_size: usize,
1256 ) -> Result<PendingValueBlock<'b>, Error> {
1257 let block_index = self.heap.allocate_block(block_size)?;
1258 let pending_block = PendingBlock { state: self, block_index };
1259
1260 let pending_name = PendingStringReference::new(pending_block.state, name)?;
1261
1262 let result = {
1263 let mut parent_block =
1264 pending_name.state.heap.container.block_at_unchecked_mut::<Node>(parent_index);
1265 let parent_block_type = parent_block.block_type();
1266 match parent_block_type {
1267 Some(BlockType::NodeValue) | Some(BlockType::Tombstone) => {
1268 parent_block.set_child_count(parent_block.child_count() + 1);
1269 Ok(())
1270 }
1271 Some(BlockType::Header) => Ok(()),
1272 _ => Err(Error::InvalidBlockType(parent_index, parent_block.block_type_raw())),
1273 }
1274 };
1275
1276 result?;
1277
1278 let name_index = pending_name.commit()?;
1279 let block_index = pending_block.commit();
1280
1281 Ok(PendingValueBlock {
1282 state: self,
1283 block_index,
1284 name_block_index: name_index,
1285 parent_index,
1286 })
1287 }
1288
1289 fn delete_value(&mut self, block_index: BlockIndex) -> Result<(), Error> {
1290 let block = self.heap.container.block_at_unchecked::<Node>(block_index);
1292 let parent_index = block.parent_index();
1293 let name_index = block.name_index();
1294
1295 if parent_index != BlockIndex::ROOT {
1297 let parent = self.heap.container.block_at_mut(parent_index);
1298 match parent.block_type() {
1299 Some(BlockType::Tombstone) => {
1300 let mut parent = parent.cast::<Tombstone>().unwrap();
1301 let child_count = parent.child_count() - 1;
1302 if child_count == 0 {
1303 self.heap.free_block(parent_index)?;
1304 } else {
1305 parent.set_child_count(child_count);
1306 }
1307 }
1308 Some(BlockType::NodeValue) => {
1309 let mut parent = parent.cast::<Node>().unwrap();
1310 let child_count = parent.child_count() - 1;
1311 parent.set_child_count(child_count);
1312 }
1313 _ => {
1314 return Err(Error::InvalidBlockType(parent_index, parent.block_type_raw()));
1315 }
1316 }
1317 }
1318
1319 match self.heap.container.block_at(name_index).block_type() {
1321 Some(BlockType::StringReference) => {
1322 self.release_string_reference(name_index)?;
1323 }
1324 _ => self.heap.free_block(name_index)?,
1325 }
1326
1327 let block = self.heap.container.block_at_mut(block_index);
1330 match block.cast::<Node>() {
1331 Some(block) if block.child_count() != 0 => {
1332 let _ = block.become_tombstone();
1333 }
1334 _ => {
1335 self.heap.free_block(block_index)?;
1336 }
1337 }
1338 Ok(())
1339 }
1340
1341 fn inner_set_string_property_value<'a>(
1342 &mut self,
1343 block_index: BlockIndex,
1344 value: impl Into<Cow<'a, str>>,
1345 ) -> Result<(), Error> {
1346 let old_string_ref_idx =
1347 self.heap.container.block_at_unchecked::<Buffer>(block_index).extent_index();
1348 let new_string_ref_idx = PendingStringReference::new(self, value.into())?.commit()?;
1349
1350 self.heap
1351 .container
1352 .block_at_unchecked_mut::<Buffer>(block_index)
1353 .set_extent_index(new_string_ref_idx);
1354 self.release_string_reference(old_string_ref_idx)?;
1355 Ok(())
1356 }
1357
1358 fn inner_set_buffer_property_value(
1359 &mut self,
1360 block_index: BlockIndex,
1361 value: &[u8],
1362 ) -> Result<(), Error> {
1363 self.free_extents(
1364 self.heap.container.block_at_unchecked::<Buffer>(block_index).extent_index(),
1365 )?;
1366 let (result, (extent_index, written)) = match self.write_extents(value) {
1367 Ok((e, w)) => (Ok(()), (e, w)),
1368 Err(err) => (Err(err), (BlockIndex::ROOT, 0)),
1369 };
1370 let mut block = self.heap.container.block_at_unchecked_mut::<Buffer>(block_index);
1371 block.set_total_length(written.try_into().unwrap_or(u32::MAX));
1372 block.set_extent_index(extent_index);
1373 result
1374 }
1375
1376 fn free_extents(&mut self, head_extent_index: BlockIndex) -> Result<(), Error> {
1377 let mut index = head_extent_index;
1378 while index != BlockIndex::ROOT {
1379 let next_index = self.heap.container.block_at_unchecked::<Extent>(index).next_extent();
1380 self.heap.free_block(index)?;
1381 index = next_index;
1382 }
1383 Ok(())
1384 }
1385
1386 fn write_extents(&mut self, value: &[u8]) -> Result<(BlockIndex, usize), Error> {
1387 if value.is_empty() {
1388 return Ok((BlockIndex::ROOT, 0));
1390 }
1391 let mut offset = 0;
1392 let total_size = value.len();
1393 let head_extent_index =
1394 self.heap.allocate_block(utils::block_size_for_payload(total_size - offset))?;
1395 let mut extent_block_index = head_extent_index;
1396 while offset < total_size {
1397 let bytes_written = {
1398 let mut extent_block = self
1399 .heap
1400 .container
1401 .block_at_unchecked_mut::<Reserved>(extent_block_index)
1402 .become_extent(BlockIndex::EMPTY);
1403 extent_block.set_contents(&value[offset..])
1404 };
1405 offset += bytes_written;
1406 if offset < total_size {
1407 let Ok(block_index) =
1408 self.heap.allocate_block(utils::block_size_for_payload(total_size - offset))
1409 else {
1410 return Ok((head_extent_index, offset));
1412 };
1413 self.heap
1414 .container
1415 .block_at_unchecked_mut::<Extent>(extent_block_index)
1416 .set_next_index(block_index);
1417 extent_block_index = block_index;
1418 }
1419 }
1420 Ok((head_extent_index, offset))
1421 }
1422}
1423
1424#[cfg(test)]
1425mod tests {
1426 use super::*;
1427 use crate::reader::PartialNodeHierarchy;
1428 use crate::reader::snapshot::{BackingBuffer, ScannedBlock, Snapshot};
1429 use crate::writer::testing_utils::get_state;
1430 use assert_matches::assert_matches;
1431 use diagnostics_assertions::assert_data_tree;
1432 use futures::prelude::*;
1433 use inspect_format::Header;
1434
1435 #[track_caller]
1436 fn assert_all_free_or_reserved<'a>(
1437 blocks: impl Iterator<Item = Block<&'a BackingBuffer, Unknown>>,
1438 ) {
1439 let mut errors = vec![];
1440 for block in blocks {
1441 if block.block_type() != Some(BlockType::Free)
1442 && block.block_type() != Some(BlockType::Reserved)
1443 {
1444 errors.push(format!(
1445 "block at {} is {:?}, expected {} or {}",
1446 block.index(),
1447 block.block_type(),
1448 BlockType::Free,
1449 BlockType::Reserved,
1450 ));
1451 }
1452 }
1453
1454 if !errors.is_empty() {
1455 panic!("{errors:#?}");
1456 }
1457 }
1458
1459 #[track_caller]
1460 fn assert_all_free<'a>(blocks: impl Iterator<Item = Block<&'a BackingBuffer, Unknown>>) {
1461 let mut errors = vec![];
1462 for block in blocks {
1463 if block.block_type() != Some(BlockType::Free) {
1464 errors.push(format!(
1465 "block at {} is {:?}, expected {}",
1466 block.index(),
1467 block.block_type(),
1468 BlockType::Free
1469 ));
1470 }
1471 }
1472
1473 if !errors.is_empty() {
1474 panic!("{errors:#?}");
1475 }
1476 }
1477
1478 #[fuchsia::test]
1479 fn test_create() {
1480 let state = get_state(4096);
1481 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
1482 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1483 assert_eq!(blocks.len(), 8);
1484 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1485 assert_all_free(blocks.into_iter().skip(1));
1486 }
1487
1488 #[fuchsia::test]
1489 fn test_load_string() {
1490 let outer = get_state(4096);
1491 let mut state = outer.try_lock().expect("lock state");
1492 let block_index = state.inner_lock.get_or_create_string_reference("a value").unwrap();
1493 assert_eq!(state.load_string(block_index).unwrap(), "a value");
1494 }
1495
1496 #[fuchsia::test]
1497 fn test_check_lineage() {
1498 let core_state = get_state(4096);
1499 let mut state = core_state.try_lock().expect("lock state");
1500 let parent_index = state.create_node("", 0.into()).unwrap();
1501 let child_index = state.create_node("", parent_index).unwrap();
1502 let uncle_index = state.create_node("", 0.into()).unwrap();
1503
1504 state.inner_lock.check_lineage(parent_index, child_index).unwrap_err();
1505 state.inner_lock.check_lineage(0.into(), child_index).unwrap_err();
1506 state.inner_lock.check_lineage(child_index, uncle_index).unwrap();
1507 }
1508
1509 #[fuchsia::test]
1510 fn test_reparent() {
1511 let core_state = get_state(4096);
1512 let mut state = core_state.try_lock().expect("lock state");
1513
1514 let a_index = state.create_node("a", 0.into()).unwrap();
1515 let b_index = state.create_node("b", 0.into()).unwrap();
1516
1517 let a = state.get_block::<Node>(a_index);
1518 let b = state.get_block::<Node>(b_index);
1519 assert_eq!(*a.parent_index(), 0);
1520 assert_eq!(*b.parent_index(), 0);
1521
1522 assert_eq!(a.child_count(), 0);
1523 assert_eq!(b.child_count(), 0);
1524
1525 state.reparent(b_index, a_index).unwrap();
1526
1527 let a = state.get_block::<Node>(a_index);
1528 let b = state.get_block::<Node>(b_index);
1529 assert_eq!(*a.parent_index(), 0);
1530 assert_eq!(b.parent_index(), a.index());
1531
1532 assert_eq!(a.child_count(), 1);
1533 assert_eq!(b.child_count(), 0);
1534
1535 let c_index = state.create_node("c", a_index).unwrap();
1536
1537 let a = state.get_block::<Node>(a_index);
1538 let b = state.get_block::<Node>(b_index);
1539 let c = state.get_block::<Node>(c_index);
1540 assert_eq!(*a.parent_index(), 0);
1541 assert_eq!(b.parent_index(), a.index());
1542 assert_eq!(c.parent_index(), a.index());
1543
1544 assert_eq!(a.child_count(), 2);
1545 assert_eq!(b.child_count(), 0);
1546 assert_eq!(c.child_count(), 0);
1547
1548 state.reparent(c_index, b_index).unwrap();
1549
1550 let a = state.get_block::<Node>(a_index);
1551 let b = state.get_block::<Node>(b_index);
1552 let c = state.get_block::<Node>(c_index);
1553 assert_eq!(*a.parent_index(), 0);
1554 assert_eq!(b.parent_index(), a_index);
1555 assert_eq!(c.parent_index(), b_index);
1556
1557 assert_eq!(a.child_count(), 1);
1558 assert_eq!(b.child_count(), 1);
1559 assert_eq!(c.child_count(), 0);
1560 }
1561
1562 #[fuchsia::test]
1563 fn test_node() {
1564 let core_state = get_state(4096);
1565 let block_index = {
1566 let mut state = core_state.try_lock().expect("lock state");
1567
1568 let block_index = state.create_node("test-node", 0.into()).unwrap();
1570 let block = state.get_block::<Node>(block_index);
1571 assert_eq!(block.block_type(), Some(BlockType::NodeValue));
1572 assert_eq!(*block.index(), 2);
1573 assert_eq!(block.child_count(), 0);
1574 assert_eq!(*block.name_index(), 4);
1575 assert_eq!(*block.parent_index(), 0);
1576
1577 let name_block = state.get_block::<StringRef>(block.name_index());
1579 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1580 assert_eq!(name_block.total_length(), 9);
1581 assert_eq!(name_block.order(), 1);
1582 assert_eq!(state.load_string(name_block.index()).unwrap(), "test-node");
1583 block_index
1584 };
1585
1586 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1588 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1589 assert_eq!(blocks.len(), 10);
1590 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1591 assert_eq!(blocks[1].block_type(), Some(BlockType::NodeValue));
1592 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
1593 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
1594 assert_all_free(blocks.into_iter().skip(4));
1595
1596 {
1597 let mut state = core_state.try_lock().expect("lock state");
1598 let child_block_index = state.create_node("child1", block_index).unwrap();
1599 assert_eq!(state.get_block::<Node>(block_index).child_count(), 1);
1600
1601 let child11_block_index = state.create_node("child1-1", child_block_index).unwrap();
1603 {
1604 assert_eq!(state.get_block::<Node>(child11_block_index).child_count(), 0);
1605 assert_eq!(state.get_block::<Node>(child_block_index).child_count(), 1);
1606 assert_eq!(state.get_block::<Node>(block_index).child_count(), 1);
1607 }
1608
1609 assert!(state.free_value(child11_block_index).is_ok());
1610 {
1611 let child_block = state.get_block::<Node>(child_block_index);
1612 assert_eq!(child_block.child_count(), 0);
1613 }
1614
1615 let child_block2_index = state.create_node("child2", block_index).unwrap();
1617 let child_block3_index = state.create_node("child3", block_index).unwrap();
1618 assert_eq!(state.get_block::<Node>(block_index).child_count(), 3);
1619
1620 assert!(state.free_value(child_block_index).is_ok());
1622 assert!(state.free_value(child_block2_index).is_ok());
1623 assert!(state.free_value(child_block3_index).is_ok());
1624 assert_eq!(state.get_block::<Node>(block_index).child_count(), 0);
1625
1626 assert!(state.free_value(block_index).is_ok());
1628 }
1629
1630 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1631 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1632 assert_all_free(blocks.into_iter().skip(1));
1633 }
1634
1635 #[fuchsia::test]
1636 fn test_int_metric() {
1637 let core_state = get_state(4096);
1638 let block_index = {
1639 let mut state = core_state.try_lock().expect("lock state");
1640 let block_index = state.create_int_metric("test", 3, 0.into()).unwrap();
1641 let block = state.get_block::<Int>(block_index);
1642 assert_eq!(block.block_type(), Some(BlockType::IntValue));
1643 assert_eq!(*block.index(), 2);
1644 assert_eq!(block.value(), 3);
1645 assert_eq!(*block.name_index(), 3);
1646 assert_eq!(*block.parent_index(), 0);
1647
1648 let name_block = state.get_block::<StringRef>(block.name_index());
1649 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1650 assert_eq!(name_block.total_length(), 4);
1651 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1652 block_index
1653 };
1654
1655 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1656 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1657 assert_eq!(blocks.len(), 9);
1658 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1659 assert_eq!(blocks[1].block_type(), Some(BlockType::IntValue));
1660 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1661 assert_all_free(blocks.into_iter().skip(3));
1662
1663 {
1664 let mut state = core_state.try_lock().expect("lock state");
1665 assert_eq!(state.add_int_metric(block_index, 10), 13);
1666 assert_eq!(state.get_block::<Int>(block_index).value(), 13);
1667
1668 assert_eq!(state.subtract_int_metric(block_index, 5), 8);
1669 assert_eq!(state.get_block::<Int>(block_index).value(), 8);
1670
1671 state.set_int_metric(block_index, -6);
1672 assert_eq!(state.get_block::<Int>(block_index).value(), -6);
1673
1674 assert_eq!(state.subtract_int_metric(block_index, i64::MAX), i64::MIN);
1675 assert_eq!(state.get_block::<Int>(block_index).value(), i64::MIN);
1676 state.set_int_metric(block_index, i64::MAX);
1677
1678 assert_eq!(state.add_int_metric(block_index, 2), i64::MAX);
1679 assert_eq!(state.get_block::<Int>(block_index).value(), i64::MAX);
1680
1681 assert!(state.free_value(block_index).is_ok());
1683 }
1684
1685 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1686 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1687 assert_all_free(blocks.into_iter().skip(1));
1688 }
1689
1690 #[fuchsia::test]
1691 fn test_uint_metric() {
1692 let core_state = get_state(4096);
1693
1694 let block_index = {
1696 let mut state = core_state.try_lock().expect("try lock");
1697 let block_index = state.create_uint_metric("test", 3, 0.into()).unwrap();
1698 let block = state.get_block::<Uint>(block_index);
1699 assert_eq!(block.block_type(), Some(BlockType::UintValue));
1700 assert_eq!(*block.index(), 2);
1701 assert_eq!(block.value(), 3);
1702 assert_eq!(*block.name_index(), 3);
1703 assert_eq!(*block.parent_index(), 0);
1704
1705 let name_block = state.get_block::<StringRef>(block.name_index());
1706 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1707 assert_eq!(name_block.total_length(), 4);
1708 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1709 block_index
1710 };
1711
1712 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1713 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1714 assert_eq!(blocks.len(), 9);
1715 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1716 assert_eq!(blocks[1].block_type(), Some(BlockType::UintValue));
1717 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1718 assert_all_free(blocks.into_iter().skip(3));
1719
1720 {
1721 let mut state = core_state.try_lock().expect("try lock");
1722 assert_eq!(state.add_uint_metric(block_index, 10), 13);
1723 assert_eq!(state.get_block::<Uint>(block_index).value(), 13);
1724
1725 assert_eq!(state.subtract_uint_metric(block_index, 5), 8);
1726 assert_eq!(state.get_block::<Uint>(block_index).value(), 8);
1727
1728 state.set_uint_metric(block_index, 0);
1729 assert_eq!(state.get_block::<Uint>(block_index).value(), 0);
1730
1731 assert_eq!(state.subtract_uint_metric(block_index, u64::MAX), 0);
1732 assert_eq!(state.get_block::<Uint>(block_index).value(), 0);
1733
1734 state.set_uint_metric(block_index, 3);
1735 assert_eq!(state.add_uint_metric(block_index, u64::MAX), u64::MAX);
1736 assert_eq!(state.get_block::<Uint>(block_index).value(), u64::MAX);
1737
1738 assert!(state.free_value(block_index).is_ok());
1740 }
1741
1742 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1743 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1744 assert_all_free(blocks.into_iter().skip(1));
1745 }
1746
1747 #[fuchsia::test]
1748 fn test_double_metric() {
1749 let core_state = get_state(4096);
1750
1751 let block_index = {
1753 let mut state = core_state.try_lock().expect("lock state");
1754 let block_index = state.create_double_metric("test", 3.0, 0.into()).unwrap();
1755 let block = state.get_block::<Double>(block_index);
1756 assert_eq!(block.block_type(), Some(BlockType::DoubleValue));
1757 assert_eq!(*block.index(), 2);
1758 assert_eq!(block.value(), 3.0);
1759 assert_eq!(*block.name_index(), 3);
1760 assert_eq!(*block.parent_index(), 0);
1761
1762 let name_block = state.get_block::<StringRef>(block.name_index());
1763 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1764 assert_eq!(name_block.total_length(), 4);
1765 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1766 block_index
1767 };
1768
1769 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1770 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1771 assert_eq!(blocks.len(), 9);
1772 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1773 assert_eq!(blocks[1].block_type(), Some(BlockType::DoubleValue));
1774 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1775 assert_all_free(blocks.into_iter().skip(3));
1776
1777 {
1778 let mut state = core_state.try_lock().expect("lock state");
1779 assert_eq!(state.add_double_metric(block_index, 10.5), 13.5);
1780 assert_eq!(state.get_block::<Double>(block_index).value(), 13.5);
1781
1782 assert_eq!(state.subtract_double_metric(block_index, 5.1), 8.4);
1783 assert_eq!(state.get_block::<Double>(block_index).value(), 8.4);
1784
1785 state.set_double_metric(block_index, -6.0);
1786 assert_eq!(state.get_block::<Double>(block_index).value(), -6.0);
1787
1788 assert!(state.free_value(block_index).is_ok());
1790 }
1791
1792 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1793 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1794 assert_all_free(blocks.into_iter().skip(1));
1795 }
1796
1797 #[fuchsia::test]
1798 fn test_create_buffer_property_cleanup_on_failure() {
1799 assert_eq!(constants::MAX_ORDER_SIZE, 2048);
1801
1802 let core_state = get_state(5121); let mut state = core_state.try_lock().expect("lock state");
1804 let name = (0..3000).map(|_| " ").collect::<String>();
1806 let payload = [0u8; 4096]; assert!(state.create_buffer_property(name, &payload, 0.into()).is_err());
1813
1814 drop(state);
1815
1816 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1817 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1818
1819 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1821 assert_all_free(blocks.into_iter().skip(1));
1822 }
1823
1824 #[fuchsia::test]
1825 fn test_string_reference_allocations() {
1826 let core_state = get_state(4096); {
1828 let mut state = core_state.try_lock().expect("lock state");
1829 let sf = "a reference-counted canonical name";
1830 assert_eq!(state.stats().allocated_blocks, 1);
1831
1832 let mut collected = vec![];
1833 for _ in 0..100 {
1834 collected.push(state.create_node(sf, 0.into()).unwrap());
1835 }
1836
1837 let acsf = Arc::new(Cow::Borrowed(sf));
1838 assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1839
1840 assert_eq!(state.stats().allocated_blocks, 102);
1841 let block = state.get_block::<Node>(collected[0]);
1842 let sf_block = state.get_block::<StringRef>(block.name_index());
1843 assert_eq!(sf_block.reference_count(), 100);
1844
1845 collected.into_iter().for_each(|b| {
1846 assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1847 assert!(state.free_value(b).is_ok())
1848 });
1849
1850 assert!(!state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1851
1852 let node_index = state.create_node(sf, 0.into()).unwrap();
1853 assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1854 assert!(state.free_value(node_index).is_ok());
1855 assert!(!state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1856 }
1857
1858 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1859 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1860 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1861 assert_all_free(blocks.into_iter().skip(1));
1862 }
1863
1864 #[fuchsia::test]
1865 fn test_string_reference_data() {
1866 let core_state = get_state(4096); let mut state = core_state.try_lock().expect("lock state");
1868
1869 let block_index = state.inner_lock.get_or_create_string_reference("abcd").unwrap();
1871 let block = state.get_block::<StringRef>(block_index);
1872 assert_eq!(block.block_type(), Some(BlockType::StringReference));
1873 assert_eq!(block.order(), 0);
1874 assert_eq!(state.stats().allocated_blocks, 2);
1875 assert_eq!(state.stats().deallocated_blocks, 0);
1876 assert_eq!(block.reference_count(), 0);
1877 assert_eq!(block.total_length(), 4);
1878 assert_eq!(*block.next_extent(), 0);
1879 assert_eq!(block.order(), 0);
1880 assert_eq!(state.load_string(block.index()).unwrap(), "abcd");
1881
1882 state.inner_lock.maybe_free_string_reference(block_index).unwrap();
1883 assert_eq!(state.stats().deallocated_blocks, 1);
1884
1885 let block_index = state.inner_lock.get_or_create_string_reference("longer").unwrap();
1886 let block = state.get_block::<StringRef>(block_index);
1887 assert_eq!(block.block_type(), Some(BlockType::StringReference));
1888 assert_eq!(block.order(), 1);
1889 assert_eq!(block.reference_count(), 0);
1890 assert_eq!(block.total_length(), 6);
1891 assert_eq!(state.stats().allocated_blocks, 3);
1892 assert_eq!(state.stats().deallocated_blocks, 1);
1893 assert_eq!(state.load_string(block.index()).unwrap(), "longer");
1894
1895 let idx = block.next_extent();
1896 assert_eq!(*idx, 0);
1897
1898 state.inner_lock.maybe_free_string_reference(block_index).unwrap();
1899 assert_eq!(state.stats().deallocated_blocks, 2);
1900
1901 let block_index = state.inner_lock.get_or_create_string_reference("longer").unwrap();
1902 let mut block = state.get_block_mut::<StringRef>(block_index);
1903 assert_eq!(block.order(), 1);
1904 block.increment_ref_count().unwrap();
1905 assert!(state.inner_lock.maybe_free_string_reference(block_index).is_ok());
1907
1908 let mut block = state.get_block_mut(block_index);
1909 block.decrement_ref_count().unwrap();
1910 state.inner_lock.maybe_free_string_reference(block_index).unwrap();
1911 }
1912
1913 #[fuchsia::test]
1914 fn test_string_reference_format_property() {
1915 let core_state = get_state(4096);
1916 let block_index = {
1917 let mut state = core_state.try_lock().expect("lock state");
1918
1919 let block_index =
1921 state.create_string("test", "test-property", BlockIndex::from(0)).unwrap();
1922 let block = state.get_block::<Buffer>(block_index);
1923 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
1924 assert_eq!(*block.index(), 2);
1925 assert_eq!(*block.parent_index(), 0);
1926 assert_eq!(*block.name_index(), 3);
1927 assert_eq!(block.total_length(), 0);
1928 assert_eq!(block.format(), Some(PropertyFormat::StringReference));
1929
1930 let name_block = state.get_block::<StringRef>(block.name_index());
1931 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1932 assert_eq!(name_block.total_length(), 4);
1933 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1934
1935 let data_block = state.get_block::<StringRef>(block.extent_index());
1936 assert_eq!(data_block.block_type(), Some(BlockType::StringReference));
1937 assert_eq!(state.load_string(data_block.index()).unwrap(), "test-property");
1938 block_index
1939 };
1940
1941 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1942 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1943 assert_eq!(blocks.len(), 10);
1944 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1945 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
1946 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1947 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
1948 assert_all_free(blocks.into_iter().skip(4));
1949
1950 {
1951 let mut state = core_state.try_lock().expect("lock state");
1952 assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
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_all_free(blocks.into_iter().skip(1));
1958 }
1959
1960 #[fuchsia::test]
1961 fn test_string_arrays() {
1962 let core_state = get_state(4096);
1963 {
1964 let mut state = core_state.try_lock().expect("lock state");
1965 let array_index = state.create_string_array("array", 4, 0.into()).unwrap();
1966 assert_eq!(state.set_array_string_slot(array_index, 0, "0"), Ok(()));
1967 assert_eq!(state.set_array_string_slot(array_index, 1, "1"), Ok(()));
1968 assert_eq!(state.set_array_string_slot(array_index, 2, "2"), Ok(()));
1969 assert_eq!(state.set_array_string_slot(array_index, 3, "3"), Ok(()));
1970
1971 assert_matches!(
1973 state.set_array_string_slot(array_index, 4, ""),
1974 Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(4)))
1975 );
1976 assert_matches!(
1977 state.set_array_string_slot(array_index, 5, ""),
1978 Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(5)))
1979 );
1980
1981 for i in 0..4 {
1982 let idx = state
1983 .get_block::<Array<StringRef>>(array_index)
1984 .get_string_index_at(i)
1985 .unwrap();
1986 assert_eq!(i.to_string(), state.load_string(idx).unwrap());
1987 }
1988
1989 assert_eq!(
1990 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(4),
1991 None
1992 );
1993 assert_eq!(
1994 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(5),
1995 None
1996 );
1997
1998 state.clear_array(array_index, 0).unwrap();
1999 state.free_value(array_index).unwrap();
2000 }
2001
2002 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2003 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2004 assert_all_free(blocks.into_iter().skip(1));
2005 }
2006
2007 #[fuchsia::test]
2008 fn update_string_array_value() {
2009 let core_state = get_state(4096);
2010 {
2011 let mut state = core_state.try_lock().expect("lock state");
2012 let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
2013
2014 assert_eq!(state.set_array_string_slot(array_index, 0, "abc"), Ok(()));
2015 assert_eq!(state.set_array_string_slot(array_index, 1, "def"), Ok(()));
2016
2017 assert_eq!(state.set_array_string_slot(array_index, 0, "cba"), Ok(()));
2018 assert_eq!(state.set_array_string_slot(array_index, 1, "fed"), Ok(()));
2019
2020 let cba_index_slot =
2021 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(0).unwrap();
2022 let fed_index_slot =
2023 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(1).unwrap();
2024 assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap());
2025 assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2026
2027 state.clear_array(array_index, 0).unwrap();
2028 state.free_value(array_index).unwrap();
2029 }
2030
2031 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2032 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2033 blocks[1..].iter().enumerate().for_each(|(i, b)| {
2034 assert!(b.block_type() == Some(BlockType::Free), "index is {}", i + 1);
2035 });
2036 }
2037
2038 #[fuchsia::test]
2039 fn set_string_reference_instances_multiple_times_in_array() {
2040 let core_state = get_state(4096);
2041 {
2042 let mut state = core_state.try_lock().expect("lock state");
2043 let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
2044
2045 let abc = "abc";
2046 let def = "def";
2047 let cba = "cba";
2048 let fed = "fed";
2049
2050 state.set_array_string_slot(array_index, 0, abc).unwrap();
2051 state.set_array_string_slot(array_index, 1, def).unwrap();
2052 state.set_array_string_slot(array_index, 0, abc).unwrap();
2053 state.set_array_string_slot(array_index, 1, def).unwrap();
2054
2055 let abc_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2056 let def_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2057 assert_eq!("abc".to_string(), state.load_string(abc_index_slot).unwrap(),);
2058 assert_eq!("def".to_string(), state.load_string(def_index_slot).unwrap(),);
2059
2060 state.set_array_string_slot(array_index, 0, cba).unwrap();
2061 state.set_array_string_slot(array_index, 1, fed).unwrap();
2062
2063 let cba_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2064 let fed_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2065 assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap(),);
2066 assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2067
2068 state.set_array_string_slot(array_index, 0, abc).unwrap();
2069 state.set_array_string_slot(array_index, 1, def).unwrap();
2070
2071 let abc_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2072 let def_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2073 assert_eq!("abc".to_string(), state.load_string(abc_index_slot).unwrap(),);
2074 assert_eq!("def".to_string(), state.load_string(def_index_slot).unwrap(),);
2075
2076 state.set_array_string_slot(array_index, 0, cba).unwrap();
2077 state.set_array_string_slot(array_index, 1, fed).unwrap();
2078
2079 let cba_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2080 let fed_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2081 assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap(),);
2082 assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2083
2084 state.clear_array(array_index, 0).unwrap();
2085 state.free_value(array_index).unwrap();
2086 }
2087
2088 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2089 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2090 blocks[1..].iter().enumerate().for_each(|(i, b)| {
2091 assert!(b.block_type() == Some(BlockType::Free), "index is {}", i + 1);
2092 });
2093 }
2094
2095 #[fuchsia::test]
2096 fn test_empty_value_string_arrays() {
2097 let core_state = get_state(4096);
2098 {
2099 let mut state = core_state.try_lock().expect("lock state");
2100 let array_index = state.create_string_array("array", 4, 0.into()).unwrap();
2101
2102 state.set_array_string_slot(array_index, 0, "").unwrap();
2103 state.set_array_string_slot(array_index, 1, "").unwrap();
2104 state.set_array_string_slot(array_index, 2, "").unwrap();
2105 state.set_array_string_slot(array_index, 3, "").unwrap();
2106 }
2107
2108 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2109 let state = core_state.try_lock().expect("lock state");
2110
2111 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2112 for b in blocks {
2113 if b.block_type() == Some(BlockType::StringReference)
2114 && state.load_string(b.index()).unwrap() == "array"
2115 {
2116 continue;
2117 }
2118
2119 assert_ne!(
2120 b.block_type(),
2121 Some(BlockType::StringReference),
2122 "Got unexpected StringReference, index: {}, value (wrapped in single quotes): '{:?}'",
2123 b.index(),
2124 b.block_type()
2125 );
2126 }
2127 }
2128
2129 #[fuchsia::test]
2130 fn test_bytevector_property() {
2131 let core_state = get_state(4096);
2132
2133 let block_index = {
2135 let mut state = core_state.try_lock().expect("lock state");
2136 let block_index =
2137 state.create_buffer_property("test", b"test-property", 0.into()).unwrap();
2138 let block = state.get_block::<Buffer>(block_index);
2139 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2140 assert_eq!(*block.index(), 2);
2141 assert_eq!(*block.parent_index(), 0);
2142 assert_eq!(*block.name_index(), 3);
2143 assert_eq!(block.total_length(), 13);
2144 assert_eq!(*block.extent_index(), 4);
2145 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2146
2147 let name_block = state.get_block::<StringRef>(block.name_index());
2148 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2149 assert_eq!(name_block.total_length(), 4);
2150 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2151
2152 let extent_block = state.get_block::<Extent>(4.into());
2153 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2154 assert_eq!(*extent_block.next_extent(), 0);
2155 assert_eq!(
2156 std::str::from_utf8(extent_block.contents().unwrap()).unwrap(),
2157 "test-property\0\0\0\0\0\0\0\0\0\0\0"
2158 );
2159 block_index
2160 };
2161
2162 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2163 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2164 assert_eq!(blocks.len(), 10);
2165 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2166 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2167 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2168 assert_eq!(blocks[3].block_type(), Some(BlockType::Extent));
2169 assert_all_free(blocks.into_iter().skip(4));
2170
2171 {
2173 let mut state = core_state.try_lock().expect("lock state");
2174 assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2175 }
2176 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2177 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2178 assert_all_free(blocks.into_iter().skip(1));
2179 }
2180
2181 #[fuchsia::test]
2182 fn test_bool() {
2183 let core_state = get_state(4096);
2184 let block_index = {
2185 let mut state = core_state.try_lock().expect("lock state");
2186
2187 let block_index = state.create_bool("test", true, 0.into()).unwrap();
2189 let block = state.get_block::<Bool>(block_index);
2190 assert_eq!(block.block_type(), Some(BlockType::BoolValue));
2191 assert_eq!(*block.index(), 2);
2192 assert!(block.value());
2193 assert_eq!(*block.name_index(), 3);
2194 assert_eq!(*block.parent_index(), 0);
2195
2196 let name_block = state.get_block::<StringRef>(block.name_index());
2197 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2198 assert_eq!(name_block.total_length(), 4);
2199 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2200 block_index
2201 };
2202
2203 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2204 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2205 assert_eq!(blocks.len(), 9);
2206 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2207 assert_eq!(blocks[1].block_type(), Some(BlockType::BoolValue));
2208 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2209 assert_all_free(blocks.into_iter().skip(3));
2210
2211 {
2213 let mut state = core_state.try_lock().expect("lock state");
2214 assert!(state.free_value(block_index).is_ok());
2215 }
2216 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2217 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2218 assert_all_free(blocks.into_iter().skip(1));
2219 }
2220
2221 #[fuchsia::test]
2222 fn test_int_array() {
2223 let core_state = get_state(4096);
2224 let block_index = {
2225 let mut state = core_state.try_lock().expect("lock state");
2226 let block_index =
2227 state.create_int_array("test", 5, ArrayFormat::Default, 0.into()).unwrap();
2228 let block = state.get_block::<Array<Int>>(block_index);
2229 assert_eq!(block.block_type(), Some(BlockType::ArrayValue));
2230 assert_eq!(block.order(), 2);
2231 assert_eq!(*block.index(), 4);
2232 assert_eq!(*block.name_index(), 2);
2233 assert_eq!(*block.parent_index(), 0);
2234 assert_eq!(block.slots(), 5);
2235 assert_eq!(block.format(), Some(ArrayFormat::Default));
2236 assert_eq!(block.entry_type(), Some(BlockType::IntValue));
2237
2238 let name_block = state.get_block::<StringRef>(BlockIndex::from(2));
2239 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2240 assert_eq!(name_block.total_length(), 4);
2241 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2242 for i in 0..5 {
2243 state.set_array_int_slot(block_index, i, 3 * i as i64);
2244 }
2245 for i in 0..5 {
2246 assert_eq!(state.get_block::<Array<Int>>(block_index).get(i), Some(3 * i as i64));
2247 }
2248 block_index
2249 };
2250
2251 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2252 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2253 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2254 assert_eq!(blocks[1].block_type(), Some(BlockType::StringReference));
2255 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2256 assert_eq!(blocks[3].block_type(), Some(BlockType::ArrayValue));
2257 assert_all_free(blocks.into_iter().skip(4));
2258
2259 {
2261 let mut state = core_state.try_lock().expect("lock state");
2262 assert!(state.free_value(block_index).is_ok());
2263 }
2264 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2265 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2266 assert_all_free(blocks.into_iter().skip(1));
2267 }
2268
2269 #[fuchsia::test]
2270 fn test_write_extent_overflow() {
2271 const SIZE: usize = constants::MAX_ORDER_SIZE * 2;
2272 const EXPECTED_WRITTEN: usize = constants::MAX_ORDER_SIZE - constants::HEADER_SIZE_BYTES;
2273 const TRIED_TO_WRITE: usize = SIZE + 1;
2274 let core_state = get_state(SIZE);
2275 let (_, written) = core_state
2276 .try_lock()
2277 .unwrap()
2278 .inner_lock
2279 .write_extents(&[4u8; TRIED_TO_WRITE])
2280 .unwrap();
2281 assert_eq!(written, EXPECTED_WRITTEN);
2282 }
2283
2284 #[fuchsia::test]
2285 fn overflow_property() {
2286 const SIZE: usize = constants::MAX_ORDER_SIZE * 2;
2287 const EXPECTED_WRITTEN: usize = constants::MAX_ORDER_SIZE - constants::HEADER_SIZE_BYTES;
2288
2289 let core_state = get_state(SIZE);
2290 let mut state = core_state.try_lock().expect("lock state");
2291
2292 let data = "X".repeat(SIZE * 2);
2293 let block_index = state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2294 let block = state.get_block::<Buffer>(block_index);
2295 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2296 assert_eq!(*block.index(), 2);
2297 assert_eq!(*block.parent_index(), 0);
2298 assert_eq!(*block.name_index(), 3);
2299 assert_eq!(block.total_length(), EXPECTED_WRITTEN);
2300 assert_eq!(*block.extent_index(), 128);
2301 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2302
2303 let name_block = state.get_block::<StringRef>(block.name_index());
2304 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2305 assert_eq!(name_block.total_length(), 4);
2306 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2307
2308 let extent_block = state.get_block::<Extent>(128.into());
2309 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2310 assert_eq!(extent_block.order(), 7);
2311 assert_eq!(*extent_block.next_extent(), *BlockIndex::EMPTY);
2312 assert_eq!(
2313 extent_block.contents().unwrap(),
2314 data.chars().take(EXPECTED_WRITTEN).map(|c| c as u8).collect::<Vec<u8>>()
2315 );
2316 }
2317
2318 #[fuchsia::test]
2319 fn test_multi_extent_property() {
2320 let core_state = get_state(10000);
2321 let block_index = {
2322 let mut state = core_state.try_lock().expect("lock state");
2323
2324 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2325 let data = chars.iter().cycle().take(6000).collect::<String>();
2326 let block_index =
2327 state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2328 let block = state.get_block::<Buffer>(block_index);
2329 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2330 assert_eq!(*block.index(), 2);
2331 assert_eq!(*block.parent_index(), 0);
2332 assert_eq!(*block.name_index(), 3);
2333 assert_eq!(block.total_length(), 6000);
2334 assert_eq!(*block.extent_index(), 128);
2335 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2336
2337 let name_block = state.get_block::<StringRef>(block.name_index());
2338 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2339 assert_eq!(name_block.total_length(), 4);
2340 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2341
2342 let extent_block = state.get_block::<Extent>(128.into());
2343 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2344 assert_eq!(extent_block.order(), 7);
2345 assert_eq!(*extent_block.next_extent(), 256);
2346 assert_eq!(
2347 extent_block.contents().unwrap(),
2348 chars.iter().cycle().take(2040).map(|&c| c as u8).collect::<Vec<u8>>()
2349 );
2350
2351 let extent_block = state.get_block::<Extent>(256.into());
2352 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2353 assert_eq!(extent_block.order(), 7);
2354 assert_eq!(*extent_block.next_extent(), 384);
2355 assert_eq!(
2356 extent_block.contents().unwrap(),
2357 chars.iter().cycle().skip(2040).take(2040).map(|&c| c as u8).collect::<Vec<u8>>()
2358 );
2359
2360 let extent_block = state.get_block::<Extent>(384.into());
2361 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2362 assert_eq!(extent_block.order(), 7);
2363 assert_eq!(*extent_block.next_extent(), 0);
2364 assert_eq!(
2365 extent_block.contents().unwrap()[..1920],
2366 chars.iter().cycle().skip(4080).take(1920).map(|&c| c as u8).collect::<Vec<u8>>()[..]
2367 );
2368 assert_eq!(extent_block.contents().unwrap()[1920..], [0u8; 120][..]);
2369 block_index
2370 };
2371
2372 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2373 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2374 assert_eq!(blocks.len(), 11);
2375 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2376 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2377 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2378 assert_eq!(blocks[8].block_type(), Some(BlockType::Extent));
2379 assert_eq!(blocks[9].block_type(), Some(BlockType::Extent));
2380 assert_eq!(blocks[10].block_type(), Some(BlockType::Extent));
2381 assert_all_free(blocks.into_iter().skip(3).take(5));
2382 {
2384 let mut state = core_state.try_lock().expect("lock state");
2385 assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2386 }
2387 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2388 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2389 assert_all_free(blocks.into_iter().skip(1));
2390 }
2391
2392 #[fuchsia::test]
2393 fn test_freeing_string_references() {
2394 let core_state = get_state(4096);
2395 {
2396 let mut state = core_state.try_lock().expect("lock state");
2397 assert_eq!(state.stats().allocated_blocks, 1);
2398
2399 let block0_index = state.create_node("abcd123456789", 0.into()).unwrap();
2400 let block0_name_index = {
2401 let block0_name_index = state.get_block::<Node>(block0_index).name_index();
2402 let block0_name = state.get_block::<StringRef>(block0_name_index);
2403 assert_eq!(block0_name.order(), 1);
2404 block0_name_index
2405 };
2406 assert_eq!(state.stats().allocated_blocks, 3);
2407
2408 let block1_index = state.inner_lock.get_or_create_string_reference("abcd").unwrap();
2409 assert_eq!(state.stats().allocated_blocks, 4);
2410 assert_eq!(state.get_block::<StringRef>(block1_index).order(), 0);
2411
2412 let block2_index =
2414 state.inner_lock.get_or_create_string_reference("abcd123456789").unwrap();
2415 assert_eq!(state.get_block::<StringRef>(block2_index).order(), 1);
2416 assert_eq!(block0_name_index, block2_index);
2417 assert_eq!(state.stats().allocated_blocks, 4);
2418
2419 let block3_index = state.create_node("abcd12345678", 0.into()).unwrap();
2420 let block3 = state.get_block::<Node>(block3_index);
2421 let block3_name = state.get_block::<StringRef>(block3.name_index());
2422 assert_eq!(block3_name.order(), 1);
2423 assert_eq!(block3.order(), 0);
2424 assert_eq!(state.stats().allocated_blocks, 6);
2425
2426 let mut long_name = "".to_string();
2427 for _ in 0..3000 {
2428 long_name += " ";
2429 }
2430
2431 let block4_index = state.create_node(long_name, 0.into()).unwrap();
2432 let block4 = state.get_block::<Node>(block4_index);
2433 let block4_name = state.get_block::<StringRef>(block4.name_index());
2434 assert_eq!(block4_name.order(), 7);
2435 assert!(*block4_name.next_extent() != 0);
2436 assert_eq!(state.stats().allocated_blocks, 9);
2437
2438 assert!(state.inner_lock.maybe_free_string_reference(block1_index).is_ok());
2439 assert_eq!(state.stats().deallocated_blocks, 1);
2440 assert!(state.inner_lock.maybe_free_string_reference(block2_index).is_ok());
2441 assert_eq!(state.stats().deallocated_blocks, 1);
2443 assert!(state.free_value(block3_index).is_ok());
2444 assert_eq!(state.stats().deallocated_blocks, 3);
2445 assert!(state.free_value(block4_index).is_ok());
2446 assert_eq!(state.stats().deallocated_blocks, 6);
2447 }
2448
2449 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2451 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2452
2453 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2454 assert_eq!(blocks[1].block_type(), Some(BlockType::NodeValue));
2455 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2456 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2457 assert_all_free(blocks.into_iter().skip(4));
2458 }
2459
2460 #[fuchsia::test]
2461 fn test_tombstone() {
2462 let core_state = get_state(4096);
2463 let child_block_index = {
2464 let mut state = core_state.try_lock().expect("lock state");
2465
2466 let block_index = state.create_node("root-node", 0.into()).unwrap();
2468 let block_name_as_string_ref =
2469 state.get_block::<StringRef>(state.get_block::<Node>(block_index).name_index());
2470 assert_eq!(block_name_as_string_ref.order(), 1);
2471 assert_eq!(state.stats().allocated_blocks, 3);
2472 assert_eq!(state.stats().deallocated_blocks, 0);
2473
2474 let child_block_index = state.create_node("child-node", block_index).unwrap();
2475 assert_eq!(state.stats().allocated_blocks, 5);
2476 assert_eq!(state.stats().deallocated_blocks, 0);
2477
2478 assert!(state.free_value(block_index).is_ok());
2480 assert_eq!(state.stats().allocated_blocks, 5);
2481 assert_eq!(state.stats().deallocated_blocks, 1);
2482 child_block_index
2483 };
2484
2485 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2486 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2487
2488 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2491 assert_eq!(blocks[1].block_type(), Some(BlockType::Tombstone));
2492 assert_eq!(blocks[2].block_type(), Some(BlockType::NodeValue));
2493 assert_eq!(blocks[3].block_type(), Some(BlockType::Free));
2494 assert_eq!(blocks[4].block_type(), Some(BlockType::StringReference));
2495 assert_all_free(blocks.into_iter().skip(5));
2496
2497 {
2499 let mut state = core_state.try_lock().expect("lock state");
2500 assert!(state.free_value(child_block_index).is_ok());
2501 }
2502 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2503 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2504 assert_all_free(blocks.into_iter().skip(1));
2505 }
2506
2507 #[fuchsia::test]
2508 fn test_with_header_lock() {
2509 let state = get_state(4096);
2510 state.with_current_header(|header| {
2512 assert_eq!(header.generation_count(), 0);
2513 });
2514
2515 let mut lock_guard = state.try_lock().expect("lock state");
2517 assert!(lock_guard.header().is_locked());
2518 assert_eq!(lock_guard.header().generation_count(), 1);
2519 let _ = lock_guard.create_node("test", 0.into()).unwrap();
2521 let _ = lock_guard.create_node("test2", 2.into()).unwrap();
2522 assert_eq!(lock_guard.header().generation_count(), 1);
2523
2524 drop(lock_guard);
2526 state.with_current_header(|header| {
2527 assert_eq!(header.generation_count(), 2);
2528 assert!(!header.is_locked());
2529 });
2530 }
2531
2532 #[fuchsia::test]
2533 async fn test_link() {
2534 let state = get_state(4096);
2536 let block_index = {
2537 let mut state_guard = state.try_lock().expect("lock state");
2538 let block_index = state_guard
2539 .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2540 async move {
2541 let inspector = Inspector::default();
2542 inspector.root().record_uint("a", 1);
2543 Ok(inspector)
2544 }
2545 .boxed()
2546 })
2547 .unwrap();
2548
2549 assert!(state_guard.callbacks().get("link-name-0").is_some());
2551 let callback = state_guard.callbacks().get("link-name-0").unwrap();
2552 match callback().await {
2553 Ok(inspector) => {
2554 let hierarchy =
2555 PartialNodeHierarchy::try_from(Snapshot::try_from(&inspector).unwrap())
2556 .unwrap();
2557 assert_data_tree!(hierarchy, root: {
2558 a: 1u64,
2559 });
2560 }
2561 Err(_) => unreachable!("we never return errors in the callback"),
2562 }
2563
2564 let block = state_guard.get_block::<Link>(block_index);
2566 assert_eq!(block.block_type(), Some(BlockType::LinkValue));
2567 assert_eq!(*block.index(), 2);
2568 assert_eq!(*block.parent_index(), 0);
2569 assert_eq!(*block.name_index(), 4);
2570 assert_eq!(*block.content_index(), 6);
2571 assert_eq!(block.link_node_disposition(), Some(LinkNodeDisposition::Inline));
2572
2573 let name_block = state_guard.get_block::<StringRef>(block.name_index());
2575 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2576 assert_eq!(name_block.total_length(), 9);
2577 assert_eq!(state_guard.load_string(name_block.index()).unwrap(), "link-name");
2578
2579 let content_block = state_guard.get_block::<StringRef>(block.content_index());
2581 assert_eq!(content_block.block_type(), Some(BlockType::StringReference));
2582 assert_eq!(content_block.total_length(), 11);
2583 assert_eq!(state_guard.load_string(content_block.index()).unwrap(), "link-name-0");
2584 block_index
2585 };
2586
2587 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2589 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2590 assert_eq!(blocks.len(), 10);
2591 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2592 assert_eq!(blocks[1].block_type(), Some(BlockType::LinkValue));
2593 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2594 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2595 assert_eq!(blocks[4].block_type(), Some(BlockType::StringReference));
2596 assert_all_free(blocks.into_iter().skip(5));
2597
2598 {
2600 let mut state_guard = state.try_lock().expect("lock state");
2601 assert!(state_guard.free_lazy_node(block_index).is_ok());
2602
2603 assert!(state_guard.callbacks().get("link-name-0").is_none());
2605 }
2606 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2607 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2608 assert_all_free(blocks.into_iter().skip(1));
2609
2610 let mut state_guard = state.try_lock().expect("lock state");
2612 state_guard
2613 .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2614 async move { Ok(Inspector::default()) }.boxed()
2615 })
2616 .unwrap();
2617 let content_block = state_guard.get_block::<StringRef>(6.into());
2618 assert_eq!(state_guard.load_string(content_block.index()).unwrap(), "link-name-1");
2619 }
2620
2621 #[fuchsia::test]
2622 fn free_lazy_node_test() {
2623 let state = get_state(4096);
2624 let (lazy_index, _int_with_magic_name_index) = {
2625 let mut state_guard = state.try_lock().expect("lock state");
2626 let lazy_index = state_guard
2627 .create_lazy_node("lk", 0.into(), LinkNodeDisposition::Inline, || {
2628 async move { Ok(Inspector::default()) }.boxed()
2629 })
2630 .unwrap();
2631
2632 let magic_link_name = "lk-0";
2633 let int_with_magic_name_index =
2634 state_guard.create_int_metric(magic_link_name, 0, BlockIndex::from(0)).unwrap();
2635
2636 (lazy_index, int_with_magic_name_index)
2637 };
2638
2639 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2640 let mut blocks = snapshot.scan();
2641 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Header));
2642
2643 let block = blocks.next().and_then(|b| b.cast::<Link>()).unwrap();
2644 assert_eq!(block.block_type(), Some(BlockType::LinkValue));
2645 assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk");
2646 assert_eq!(state.try_lock().unwrap().load_string(block.content_index()).unwrap(), "lk-0");
2647 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2648 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2649 let block = blocks.next().and_then(|b| b.cast::<Int>()).unwrap();
2650 assert_eq!(block.block_type(), Some(BlockType::IntValue));
2651 assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk-0");
2652 assert_all_free(blocks);
2653
2654 state.try_lock().unwrap().free_lazy_node(lazy_index).unwrap();
2655
2656 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2657 let mut blocks = snapshot.scan();
2658
2659 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Header));
2660 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Free));
2661 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2662 let block = blocks.next().and_then(|b| b.cast::<Int>()).unwrap();
2663 assert_eq!(block.block_type(), Some(BlockType::IntValue));
2664 assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk-0");
2665 assert_all_free(blocks);
2666 }
2667
2668 #[fuchsia::test]
2669 async fn stats() {
2670 let state = get_state(3 * 4096);
2672 let mut state_guard = state.try_lock().expect("lock state");
2673 let _block1 = state_guard
2674 .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2675 async move {
2676 let inspector = Inspector::default();
2677 inspector.root().record_uint("a", 1);
2678 Ok(inspector)
2679 }
2680 .boxed()
2681 })
2682 .unwrap();
2683 let _block2 = state_guard.create_uint_metric("test", 3, 0.into()).unwrap();
2684 assert_eq!(
2685 state_guard.stats(),
2686 Stats {
2687 total_dynamic_children: 1,
2688 maximum_size: 3 * 4096,
2689 current_size: 4096,
2690 allocated_blocks: 6, deallocated_blocks: 0,
2693 failed_allocations: 0,
2694 }
2695 )
2696 }
2697
2698 #[fuchsia::test]
2699 fn transaction_locking() {
2700 let state = get_state(4096);
2701 state.with_current_header(|header| {
2703 assert_eq!(header.generation_count(), 0);
2704 });
2705
2706 state.begin_transaction();
2708 state.with_current_header(|header| {
2709 assert_eq!(header.generation_count(), 1);
2710 assert!(header.is_locked());
2711 });
2712
2713 let mut lock_guard1 = state.try_lock().expect("lock state");
2715 assert_eq!(lock_guard1.inner_lock.transaction_count, 1);
2716 assert_eq!(lock_guard1.header().generation_count(), 1);
2717 assert!(lock_guard1.header().is_locked());
2718 let _ = lock_guard1.create_node("test", 0.into());
2719 assert_eq!(lock_guard1.inner_lock.transaction_count, 1);
2720 assert_eq!(lock_guard1.header().generation_count(), 1);
2721
2722 drop(lock_guard1);
2724 state.with_current_header(|header| {
2725 assert_eq!(header.generation_count(), 1);
2726 assert!(header.is_locked());
2727 });
2728
2729 state.end_transaction();
2731
2732 state.with_current_header(|header| {
2733 assert_eq!(header.generation_count(), 2);
2734 assert!(!header.is_locked());
2735 });
2736
2737 let lock_guard2 = state.try_lock().expect("lock state");
2739 assert!(lock_guard2.header().is_locked());
2740 assert_eq!(lock_guard2.header().generation_count(), 3);
2741 assert_eq!(lock_guard2.inner_lock.transaction_count, 0);
2742 }
2743
2744 #[fuchsia::test]
2745 async fn update_header_vmo_size() {
2746 let core_state = get_state(3 * 4096);
2747 core_state.get_block(BlockIndex::HEADER, |header: &Block<_, Header>| {
2748 assert_eq!(header.vmo_size(), Ok(Some(4096)));
2749 });
2750 let block1_index = {
2751 let mut state = core_state.try_lock().expect("lock state");
2752
2753 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2754 let data = chars.iter().cycle().take(6000).collect::<String>();
2755 let block_index =
2756 state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2757 assert_eq!(state.header().vmo_size(), Ok(Some(2 * 4096)));
2758
2759 block_index
2760 };
2761
2762 let block2_index = {
2763 let mut state = core_state.try_lock().expect("lock state");
2764
2765 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2766 let data = chars.iter().cycle().take(3000).collect::<String>();
2767 let block_index =
2768 state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2769 assert_eq!(state.header().vmo_size(), Ok(Some(3 * 4096)));
2770
2771 block_index
2772 };
2773 {
2775 let mut state = core_state.try_lock().expect("lock state");
2776 assert!(state.free_string_or_bytes_buffer_property(block1_index).is_ok());
2777 assert!(state.free_string_or_bytes_buffer_property(block2_index).is_ok());
2778 }
2779 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2780 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2781 assert_all_free(blocks.into_iter().skip(1));
2782 }
2783
2784 #[fuchsia::test]
2785 fn test_buffer_property_on_overflow_set() {
2786 let core_state = get_state(4096);
2787 let block_index = {
2788 let mut state = core_state.try_lock().expect("lock state");
2789
2790 let block_index =
2792 state.create_buffer_property("test", b"test-property", 0.into()).unwrap();
2793
2794 for _ in 10..(4096 / constants::MIN_ORDER_SIZE).try_into().unwrap() {
2796 state.inner_lock.heap.allocate_block(constants::MIN_ORDER_SIZE).unwrap();
2797 }
2798
2799 let values = [b'a'; 8096];
2801 assert!(state.set_buffer_property(block_index, &values).is_err());
2802
2803 let block = state.get_block::<Buffer>(block_index);
2806 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2807 assert_eq!(*block.index(), 2);
2808 assert_eq!(*block.parent_index(), 0);
2809 assert_eq!(*block.name_index(), 3);
2810 assert_eq!(block.total_length(), 0);
2811 assert_eq!(*block.extent_index(), 0);
2812 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2813
2814 block_index
2815 };
2816
2817 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2819 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2820 assert_eq!(blocks.len(), 251);
2821 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2822 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2823 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2824 assert_all_free_or_reserved(blocks.into_iter().skip(3));
2825
2826 {
2827 let mut state = core_state.try_lock().expect("lock state");
2828 assert_matches!(state.free_string_or_bytes_buffer_property(block_index), Ok(()));
2830 }
2831 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2832 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2833 assert_all_free_or_reserved(blocks.into_iter().skip(1));
2834 }
2835
2836 #[fuchsia::test]
2837 fn test_string_property_on_overflow_set() {
2838 let core_state = get_state(4096);
2839 {
2840 let mut state = core_state.try_lock().expect("lock state");
2841
2842 let block_index = state.create_string("test", "test-property", 0.into()).unwrap();
2844
2845 for _ in 10..(4096 / constants::MIN_ORDER_SIZE).try_into().unwrap() {
2847 state.inner_lock.heap.allocate_block(constants::MIN_ORDER_SIZE).unwrap();
2848 }
2849
2850 let values = ["a"].into_iter().cycle().take(5000).collect::<String>();
2853 assert!(state.set_string_property(block_index, values).is_err());
2854 let block = state.get_block::<Buffer>(block_index);
2855 assert_eq!(*block.index(), 2);
2856 assert_eq!(*block.parent_index(), 0);
2857 assert_eq!(*block.name_index(), 3);
2858
2859 assert_eq!(
2861 state.load_string(BlockIndex::from(*block.extent_index())).unwrap(),
2862 "test-property"
2863 );
2864
2865 assert!(state.create_int_metric("foo", 1, 0.into()).is_ok());
2867 assert!(state.create_int_metric("bar", 1, 0.into()).is_ok());
2868 };
2869
2870 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2871 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2872 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2873 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2874 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2875 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2876 assert_eq!(blocks[250].block_type(), Some(BlockType::IntValue));
2877 assert_eq!(blocks[251].block_type(), Some(BlockType::StringReference));
2878 assert_eq!(blocks[252].block_type(), Some(BlockType::IntValue));
2879 assert_eq!(blocks[253].block_type(), Some(BlockType::StringReference));
2880 assert_all_free_or_reserved(blocks.into_iter().skip(4).rev().skip(4));
2881 }
2882
2883 #[fuchsia::test]
2884 fn test_reparent_tombstone_leak() {
2885 use inspect_format::Free;
2886
2887 let core_state = get_state(4096);
2888 let mut state = core_state.try_lock().expect("lock state");
2889
2890 let parent_index = state.create_node("parent", 0.into()).unwrap();
2891 let child_index = state.create_node("child", parent_index).unwrap();
2892 let new_parent_index = state.create_node("new_parent", 0.into()).unwrap();
2893
2894 assert_eq!(state.get_block::<Node>(parent_index).child_count(), 1);
2896
2897 state.free_value(parent_index).unwrap();
2899 assert_eq!(
2900 state.get_block::<Tombstone>(parent_index).block_type(),
2901 Some(BlockType::Tombstone)
2902 );
2903
2904 state.reparent(child_index, new_parent_index).unwrap();
2908
2909 let parent_block = state.get_block::<Free>(parent_index);
2912 assert_eq!(parent_block.block_type(), Some(BlockType::Free));
2913 }
2914
2915 #[fuchsia::test]
2916 fn test_allocate_reserved_value_overflow_leak() {
2917 use inspect_format::HeaderFields;
2918 use inspect_format::constants::MAX_REFERENCE_COUNT;
2919
2920 let core_state = get_state(4096);
2921 let mut state = core_state.try_lock().expect("lock state");
2922
2923 let parent_index = state.create_node("foo", 0.into()).unwrap();
2925 let node_block = state.get_block::<Node>(parent_index);
2926 let name_index = node_block.name_index();
2927
2928 {
2930 let mut name_block = state.get_block_mut::<StringRef>(name_index);
2931 HeaderFields::set_string_reference_count(&mut name_block, MAX_REFERENCE_COUNT);
2932 }
2933
2934 let stats_before = state.stats();
2936
2937 let result = state.create_node("foo", parent_index);
2941 assert!(result.is_err());
2942
2943 let stats_after = state.stats();
2945
2946 let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
2947 let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
2948 assert_eq!(active_after, active_before);
2949 }
2950
2951 #[fuchsia::test]
2952 fn test_set_array_string_slot_release_failure_leak() {
2953 use inspect_format::HeaderFields;
2954
2955 let core_state = get_state(4096);
2956 let mut state = core_state.try_lock().expect("lock state");
2957
2958 let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
2959 state.set_array_string_slot(array_index, 0, "foo").unwrap();
2960
2961 let foo_index =
2962 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(0).unwrap();
2963
2964 {
2966 let mut foo_block = state.get_block_mut::<StringRef>(foo_index);
2967 HeaderFields::set_string_reference_count(&mut foo_block, 0);
2968 }
2969
2970 let stats_before = state.stats();
2971 let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
2972
2973 let result = state.set_array_string_slot(array_index, 0, "bar");
2977 assert!(result.is_err());
2978
2979 let stats_after = state.stats();
2980 let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
2981
2982 assert_eq!(active_after, active_before);
2984 }
2985
2986 #[fuchsia::test]
2987 fn test_allocate_link_cleanup_failure() {
2988 let core_state = get_state(4096);
2989 {
2990 let mut state = core_state.try_lock().expect("lock state");
2991
2992 let mut nodes = vec![];
2995 while let Ok(idx) = state.create_node("n", 0.into()) {
2996 nodes.push(idx);
2998 }
2999
3000 state.free_value(nodes.pop().unwrap()).unwrap();
3003
3004 let result = state.create_lazy_node("n", 0.into(), LinkNodeDisposition::Inline, || {
3016 async move { Ok(Inspector::default()) }.boxed()
3017 });
3018
3019 assert!(result.is_err());
3020 }
3021
3022 core_state.with_current_header(|header| {
3024 assert_eq!(header.magic_number(), constants::HEADER_MAGIC_NUMBER);
3025 assert_eq!(header.version(), constants::HEADER_VERSION_NUMBER);
3026 });
3027 }
3028
3029 #[fuchsia::test]
3030 fn test_get_or_create_string_reference_payload_failure_leak() {
3031 let core_state = get_state(4096);
3032 let mut state = core_state.try_lock().expect("lock state");
3033
3034 let mut allocated_blocks = vec![];
3037 for size in &[32, 64, 128, 256, 512, 1024] {
3038 allocated_blocks.push(state.inner_lock.heap.allocate_block(*size).unwrap());
3039 }
3040
3041 let stats_before = state.stats();
3042
3043 let result = state.inner_lock.get_or_create_string_reference("a".repeat(2040));
3051 assert!(result.is_err());
3052
3053 let stats_after = state.stats();
3055 let active_before = stats_before.allocated_blocks - stats_before.deallocated_blocks;
3056 let active_after = stats_after.allocated_blocks - stats_after.deallocated_blocks;
3057 assert_eq!(active_after, active_before);
3058
3059 for block in allocated_blocks {
3061 state.inner_lock.heap.free_block(block).unwrap();
3062 }
3063 }
3064
3065 #[fuchsia::test]
3066 fn test_reparent_to_self_tombstone() {
3067 use inspect_format::Free;
3068
3069 let core_state = get_state(4096);
3070 let mut state = core_state.try_lock().expect("lock state");
3071
3072 let parent_index = state.create_node("parent", 0.into()).unwrap();
3073 let child_index = state.create_node("child", parent_index).unwrap();
3074
3075 state.free_value(parent_index).unwrap();
3077 assert_eq!(
3078 state.get_block::<Tombstone>(parent_index).block_type(),
3079 Some(BlockType::Tombstone)
3080 );
3081
3082 state.reparent(child_index, parent_index).unwrap();
3084
3085 let parent_block = state.get_block::<Tombstone>(parent_index);
3087 assert_eq!(parent_block.block_type(), Some(BlockType::Tombstone));
3088
3089 state.free_value(child_index).unwrap();
3091
3092 let parent_block = state.get_block::<Free>(parent_index);
3094 assert_eq!(parent_block.block_type(), Some(BlockType::Free));
3095 }
3096}