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
649impl InnerState {
650 pub fn new(
652 heap: Heap<Container>,
653 storage: Arc<<Container as BlockContainer>::ShareableData>,
654 ) -> Self {
655 Self {
656 heap,
657 storage,
658 next_unique_link_id: AtomicU64::new(0),
659 callbacks: HashMap::new(),
660 transaction_count: 0,
661 string_reference_block_indexes: HashMap::new(),
662 block_index_string_references: HashMap::new(),
663 }
664 }
665
666 #[inline]
667 fn header_mut(&mut self) -> Block<&mut Container, inspect_format::Header> {
668 self.heap.container.block_at_unchecked_mut(BlockIndex::HEADER)
669 }
670
671 fn lock_header(&mut self) {
672 if self.transaction_count == 0 {
673 self.header_mut().lock();
674 }
675 self.transaction_count += 1;
676 }
677
678 fn unlock_header(&mut self) {
679 self.transaction_count -= 1;
680 if self.transaction_count == 0 {
681 self.header_mut().unlock();
682 }
683 }
684
685 fn create_node<'a>(
687 &mut self,
688 name: impl Into<Cow<'a, str>>,
689 parent_index: BlockIndex,
690 ) -> Result<BlockIndex, Error> {
691 let pending =
692 self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
693 pending
694 .state
695 .heap
696 .container
697 .block_at_unchecked_mut::<Reserved>(pending.block_index)
698 .become_node(pending.name_block_index, parent_index);
699 Ok(pending.commit())
700 }
701
702 fn create_lazy_node<'a, F>(
705 &mut self,
706 name: impl Into<Cow<'a, str>>,
707 parent_index: BlockIndex,
708 disposition: LinkNodeDisposition,
709 callback: F,
710 ) -> Result<BlockIndex, Error>
711 where
712 F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
713 {
714 let name = name.into();
715 let content = self.unique_link_name(&name);
716 let link = self.allocate_link(name, &content, disposition, parent_index)?;
717 self.callbacks.insert(content, Arc::from(callback));
718 Ok(link)
719 }
720
721 fn free_lazy_node(&mut self, index: BlockIndex) -> Result<(), Error> {
723 let content_block_index =
724 self.heap.container.block_at_unchecked::<Link>(index).content_index();
725 let content_block_type = self.heap.container.block_at(content_block_index).block_type();
726 let content = self.load_key_string(content_block_index)?;
727 self.delete_value(index)?;
728 match content_block_type {
730 Some(BlockType::StringReference) => {
731 self.release_string_reference(content_block_index)?;
732 }
733 _ => {
734 self.heap.free_block(content_block_index).expect("Failed to free block");
735 }
736 }
737
738 self.callbacks.remove(content.as_str());
739 Ok(())
740 }
741
742 fn unique_link_name(&mut self, prefix: &str) -> String {
743 let id = self.next_unique_link_id.fetch_add(1, Ordering::Relaxed);
744 format!("{prefix}-{id}")
745 }
746
747 pub(crate) fn allocate_link<'a, 'b>(
748 &mut self,
749 name: impl Into<Cow<'a, str>>,
750 content: impl Into<Cow<'b, str>>,
751 disposition: LinkNodeDisposition,
752 parent_index: BlockIndex,
753 ) -> Result<BlockIndex, Error> {
754 let pending =
755 self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
756 let content_block_index = pending.state.get_or_create_string_reference(content)?;
757 pending
758 .state
759 .heap
760 .container
761 .block_at_unchecked_mut::<StringRef>(content_block_index)
762 .increment_ref_count()?;
763 pending
764 .state
765 .heap
766 .container
767 .block_at_unchecked_mut::<Reserved>(pending.block_index)
768 .become_link(pending.name_block_index, parent_index, content_block_index, disposition);
769 Ok(pending.commit())
770 }
771
772 fn free_value(&mut self, index: BlockIndex) -> Result<(), Error> {
774 self.delete_value(index)?;
775 Ok(())
776 }
777
778 fn create_buffer_property<'a>(
780 &mut self,
781 name: impl Into<Cow<'a, str>>,
782 value: &[u8],
783 parent_index: BlockIndex,
784 ) -> Result<BlockIndex, Error> {
785 let pending =
786 self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
787 pending
788 .state
789 .heap
790 .container
791 .block_at_unchecked_mut::<Reserved>(pending.block_index)
792 .become_property(pending.name_block_index, parent_index, PropertyFormat::Bytes);
793 pending.state.inner_set_buffer_property_value(pending.block_index, value)?;
794 Ok(pending.commit())
795 }
796
797 fn create_string<'a, 'b>(
800 &mut self,
801 name: impl Into<Cow<'a, str>>,
802 value: impl Into<Cow<'b, str>>,
803 parent_index: BlockIndex,
804 ) -> Result<BlockIndex, Error> {
805 let pending =
806 self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
807 pending
808 .state
809 .heap
810 .container
811 .block_at_unchecked_mut::<Reserved>(pending.block_index)
812 .become_property(
813 pending.name_block_index,
814 parent_index,
815 PropertyFormat::StringReference,
816 );
817
818 let value_block_index = pending.state.get_or_create_string_reference(value)?;
819 pending
820 .state
821 .heap
822 .container
823 .block_at_unchecked_mut::<StringRef>(value_block_index)
824 .increment_ref_count()?;
825
826 let mut block =
827 pending.state.heap.container.block_at_unchecked_mut::<Buffer>(pending.block_index);
828 block.set_extent_index(value_block_index);
829 block.set_total_length(0);
830
831 Ok(pending.commit())
832 }
833
834 fn get_or_create_string_reference<'a>(
837 &mut self,
838 value: impl Into<Cow<'a, str>>,
839 ) -> Result<BlockIndex, Error> {
840 let value = value.into();
841 match self.string_reference_block_indexes.get(&value) {
842 Some(index) => Ok(*index),
843 None => {
844 let block_index = self.heap.allocate_block(utils::block_size_for_payload(
845 value.len() + constants::STRING_REFERENCE_TOTAL_LENGTH_BYTES,
846 ))?;
847 self.heap
848 .container
849 .block_at_unchecked_mut::<Reserved>(block_index)
850 .become_string_reference();
851 self.write_string_reference_payload(block_index, &value)?;
852 let owned_value = Arc::new(value.into_owned().into());
853 self.string_reference_block_indexes.insert(Arc::clone(&owned_value), block_index);
854 self.block_index_string_references.insert(block_index, owned_value);
855 Ok(block_index)
856 }
857 }
858 }
859
860 fn write_string_reference_payload(
862 &mut self,
863 block_index: BlockIndex,
864 value: &str,
865 ) -> Result<(), Error> {
866 let value_bytes = value.as_bytes();
867 let (head_extent, bytes_written) = {
868 let inlined = self.inline_string_reference(block_index, value.as_bytes());
869 if inlined < value.len() {
870 let (head, in_extents) = self.write_extents(&value_bytes[inlined..])?;
871 (head, inlined + in_extents)
872 } else {
873 (BlockIndex::EMPTY, inlined)
874 }
875 };
876 let mut block = self.heap.container.block_at_unchecked_mut::<StringRef>(block_index);
877 block.set_next_index(head_extent);
878 block.set_total_length(bytes_written.try_into().unwrap_or(u32::MAX));
879 Ok(())
880 }
881
882 fn inline_string_reference(&mut self, block_index: BlockIndex, value: &[u8]) -> usize {
885 self.heap.container.block_at_unchecked_mut::<StringRef>(block_index).write_inline(value)
886 }
887
888 fn release_string_reference(&mut self, block_index: BlockIndex) -> Result<(), Error> {
891 self.heap
892 .container
893 .block_at_unchecked_mut::<StringRef>(block_index)
894 .decrement_ref_count()?;
895 self.maybe_free_string_reference(block_index)
896 }
897
898 fn maybe_free_string_reference(&mut self, block_index: BlockIndex) -> Result<(), Error> {
901 let block = self.heap.container.block_at_unchecked::<StringRef>(block_index);
902 if block.reference_count() != 0 {
903 return Ok(());
904 }
905 let first_extent = block.next_extent();
906 self.heap.free_block(block_index)?;
907 let str_ref = self.block_index_string_references.remove(&block_index).expect("blk idx key");
908 self.string_reference_block_indexes.remove(&str_ref);
909
910 if first_extent == BlockIndex::EMPTY {
911 return Ok(());
912 }
913 self.free_extents(first_extent)
914 }
915
916 fn load_key_string(&self, index: BlockIndex) -> Result<String, Error> {
917 let block = self.heap.container.block_at(index);
918 match block.block_type() {
919 Some(BlockType::StringReference) => {
920 self.read_string_reference(block.cast::<StringRef>().unwrap())
921 }
922 Some(BlockType::Name) => block
923 .cast::<Name>()
924 .unwrap()
925 .contents()
926 .map(|s| s.to_string())
927 .map_err(|_| Error::NameNotUtf8),
928 _ => Err(Error::InvalidBlockTypeNumber(index, block.block_type_raw())),
929 }
930 }
931
932 fn read_string_reference(&self, block: Block<&Container, StringRef>) -> Result<String, Error> {
934 let mut content = block.inline_data()?.to_vec();
935 let mut next = block.next_extent();
936 while next != BlockIndex::EMPTY {
937 let next_block = self.heap.container.block_at_unchecked::<Extent>(next);
938 content.extend_from_slice(next_block.contents()?);
939 next = next_block.next_extent();
940 }
941
942 content.truncate(block.total_length());
943 String::from_utf8(content).ok().ok_or(Error::NameNotUtf8)
944 }
945
946 fn free_string_or_bytes_buffer_property(&mut self, index: BlockIndex) -> Result<(), Error> {
948 let (format, data_index) = {
949 let block = self.heap.container.block_at_unchecked::<Buffer>(index);
950 (block.format(), block.extent_index())
951 };
952 match format {
953 Some(PropertyFormat::String) | Some(PropertyFormat::Bytes) => {
954 self.free_extents(data_index)?;
955 }
956 Some(PropertyFormat::StringReference) => {
957 self.release_string_reference(data_index)?;
958 }
959 _ => {
960 return Err(Error::VmoFormat(FormatError::InvalidBufferFormat(
961 self.heap.container.block_at_unchecked(index).format_raw(),
962 )));
963 }
964 }
965
966 self.delete_value(index)?;
967 Ok(())
968 }
969
970 fn set_string_property<'a>(
972 &mut self,
973 block_index: BlockIndex,
974 value: impl Into<Cow<'a, str>>,
975 ) -> Result<(), Error> {
976 self.inner_set_string_property_value(block_index, value)?;
977 Ok(())
978 }
979
980 fn set_buffer_property(&mut self, block_index: BlockIndex, value: &[u8]) -> Result<(), Error> {
982 self.inner_set_buffer_property_value(block_index, value)?;
983 Ok(())
984 }
985
986 fn check_lineage(
987 &self,
988 being_reparented: BlockIndex,
989 new_parent: BlockIndex,
990 ) -> Result<(), Error> {
991 if being_reparented == BlockIndex::ROOT {
993 return Err(Error::AdoptAncestor);
994 }
995
996 let mut being_checked = new_parent;
997 while being_checked != BlockIndex::ROOT {
998 if being_checked == being_reparented {
999 return Err(Error::AdoptAncestor);
1000 }
1001 being_checked =
1004 self.heap.container.block_at_unchecked::<Node>(being_checked).parent_index();
1005 }
1006
1007 Ok(())
1008 }
1009
1010 fn reparent(
1011 &mut self,
1012 being_reparented: BlockIndex,
1013 new_parent: BlockIndex,
1014 ) -> Result<(), Error> {
1015 self.check_lineage(being_reparented, new_parent)?;
1016 let original_parent_idx =
1017 self.heap.container.block_at_unchecked::<Node>(being_reparented).parent_index();
1018 if original_parent_idx != BlockIndex::ROOT {
1019 let mut original_parent_block =
1020 self.heap.container.block_at_unchecked_mut::<Node>(original_parent_idx);
1021 let child_count = original_parent_block.child_count() - 1;
1022 original_parent_block.set_child_count(child_count);
1023 }
1024
1025 self.heap.container.block_at_unchecked_mut::<Node>(being_reparented).set_parent(new_parent);
1026
1027 if new_parent != BlockIndex::ROOT {
1028 let mut new_parent_block =
1029 self.heap.container.block_at_unchecked_mut::<Node>(new_parent);
1030 let child_count = new_parent_block.child_count() + 1;
1031 new_parent_block.set_child_count(child_count);
1032 }
1033
1034 Ok(())
1035 }
1036
1037 fn create_bool<'a>(
1038 &mut self,
1039 name: impl Into<Cow<'a, str>>,
1040 value: bool,
1041 parent_index: BlockIndex,
1042 ) -> Result<BlockIndex, Error> {
1043 let pending =
1044 self.allocate_reserved_value(name, parent_index, constants::MIN_ORDER_SIZE)?;
1045 pending
1046 .state
1047 .heap
1048 .container
1049 .block_at_unchecked_mut::<Reserved>(pending.block_index)
1050 .become_bool_value(value, pending.name_block_index, parent_index);
1051 Ok(pending.commit())
1052 }
1053
1054 fn set_bool(&mut self, block_index: BlockIndex, value: bool) {
1055 let mut block = self.heap.container.block_at_unchecked_mut::<Bool>(block_index);
1056 block.set(value);
1057 }
1058
1059 metric_fns!(int, i64, Int);
1060 metric_fns!(uint, u64, Uint);
1061 metric_fns!(double, f64, Double);
1062
1063 arithmetic_array_fns!(int, i64, IntValue, Int);
1064 arithmetic_array_fns!(uint, u64, UintValue, Uint);
1065 arithmetic_array_fns!(double, f64, DoubleValue, Double);
1066
1067 fn create_string_array<'a>(
1068 &mut self,
1069 name: impl Into<Cow<'a, str>>,
1070 slots: usize,
1071 parent_index: BlockIndex,
1072 ) -> Result<BlockIndex, Error> {
1073 let block_size = slots * StringRef::array_entry_type_size() + constants::MIN_ORDER_SIZE;
1074 if block_size > constants::MAX_ORDER_SIZE {
1075 return Err(Error::BlockSizeTooBig(block_size));
1076 }
1077 let pending = self.allocate_reserved_value(name, parent_index, block_size)?;
1078 pending
1079 .state
1080 .heap
1081 .container
1082 .block_at_unchecked_mut::<Reserved>(pending.block_index)
1083 .become_array_value::<StringRef>(
1084 slots,
1085 ArrayFormat::Default,
1086 pending.name_block_index,
1087 parent_index,
1088 )?;
1089 Ok(pending.commit())
1090 }
1091
1092 fn get_array_size(&self, block_index: BlockIndex) -> usize {
1093 let block = self.heap.container.block_at_unchecked::<Array<Unknown>>(block_index);
1094 block.slots()
1095 }
1096
1097 fn set_array_string_slot<'a>(
1098 &mut self,
1099 block_index: BlockIndex,
1100 slot_index: usize,
1101 value: impl Into<Cow<'a, str>>,
1102 ) -> Result<(), Error> {
1103 if self.heap.container.block_at_unchecked_mut::<Array<StringRef>>(block_index).slots()
1104 <= slot_index
1105 {
1106 return Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(slot_index)));
1107 }
1108
1109 let value = value.into();
1110 let reference_index = if !value.is_empty() {
1111 let reference_index = self.get_or_create_string_reference(value)?;
1112 self.heap
1113 .container
1114 .block_at_unchecked_mut::<StringRef>(reference_index)
1115 .increment_ref_count()?;
1116 reference_index
1117 } else {
1118 BlockIndex::EMPTY
1119 };
1120
1121 let existing_index = self
1122 .heap
1123 .container
1124 .block_at_unchecked::<Array<StringRef>>(block_index)
1125 .get_string_index_at(slot_index)
1126 .ok_or(Error::InvalidArrayIndex(slot_index))?;
1127 if existing_index != BlockIndex::EMPTY {
1128 self.release_string_reference(existing_index)?;
1129 }
1130
1131 self.heap
1132 .container
1133 .block_at_unchecked_mut::<Array<StringRef>>(block_index)
1134 .set_string_slot(slot_index, reference_index);
1135 Ok(())
1136 }
1137
1138 fn clear_array(
1141 &mut self,
1142 block_index: BlockIndex,
1143 start_slot_index: usize,
1144 ) -> Result<(), Error> {
1145 let block = self.heap.container.block_at_unchecked_mut::<Array<Unknown>>(block_index);
1148 match block.entry_type() {
1149 Some(value) if value.is_numeric_value() => {
1150 self.heap
1151 .container
1152 .block_at_unchecked_mut::<Array<Unknown>>(block_index)
1153 .clear(start_slot_index);
1154 }
1155 Some(BlockType::StringReference) => {
1156 let array_slots = block.slots();
1157 for i in start_slot_index..array_slots {
1158 let index = {
1159 let mut block = self
1160 .heap
1161 .container
1162 .block_at_unchecked_mut::<Array<StringRef>>(block_index);
1163 let index =
1164 block.get_string_index_at(i).ok_or(Error::InvalidArrayIndex(i))?;
1165 if index == BlockIndex::EMPTY {
1166 continue;
1167 }
1168 block.set_string_slot(i, BlockIndex::EMPTY);
1169 index
1170 };
1171 self.release_string_reference(index)?;
1172 }
1173 }
1174
1175 _ => return Err(Error::InvalidArrayType(block_index)),
1176 }
1177
1178 Ok(())
1179 }
1180
1181 fn allocate_reserved_value<'a, 'b>(
1182 &'b mut self,
1183 name: impl Into<Cow<'a, str>>,
1184 parent_index: BlockIndex,
1185 block_size: usize,
1186 ) -> Result<PendingValueBlock<'b>, Error> {
1187 let block_index = self.heap.allocate_block(block_size)?;
1188 let name_block_index = match self.get_or_create_string_reference(name) {
1189 Ok(b_index) => {
1190 self.heap
1191 .container
1192 .block_at_unchecked_mut::<StringRef>(b_index)
1193 .increment_ref_count()?;
1194 b_index
1195 }
1196 Err(err) => {
1197 self.heap.free_block(block_index)?;
1198 return Err(err);
1199 }
1200 };
1201
1202 let result = {
1203 let mut parent_block = self.heap.container.block_at_unchecked_mut::<Node>(parent_index);
1205 let parent_block_type = parent_block.block_type();
1206 match parent_block_type {
1207 Some(BlockType::NodeValue) | Some(BlockType::Tombstone) => {
1208 parent_block.set_child_count(parent_block.child_count() + 1);
1209 Ok(())
1210 }
1211 Some(BlockType::Header) => Ok(()),
1212 _ => Err(Error::InvalidBlockType(parent_index, parent_block.block_type_raw())),
1213 }
1214 };
1215 match result {
1216 Ok(()) => {
1217 Ok(PendingValueBlock { state: self, block_index, name_block_index, parent_index })
1218 }
1219 Err(err) => {
1220 self.release_string_reference(name_block_index)?;
1221 self.heap.free_block(block_index)?;
1222 Err(err)
1223 }
1224 }
1225 }
1226
1227 fn delete_value(&mut self, block_index: BlockIndex) -> Result<(), Error> {
1228 let block = self.heap.container.block_at_unchecked::<Node>(block_index);
1230 let parent_index = block.parent_index();
1231 let name_index = block.name_index();
1232
1233 if parent_index != BlockIndex::ROOT {
1235 let parent = self.heap.container.block_at_mut(parent_index);
1236 match parent.block_type() {
1237 Some(BlockType::Tombstone) => {
1238 let mut parent = parent.cast::<Tombstone>().unwrap();
1239 let child_count = parent.child_count() - 1;
1240 if child_count == 0 {
1241 self.heap.free_block(parent_index).expect("Failed to free block");
1242 } else {
1243 parent.set_child_count(child_count);
1244 }
1245 }
1246 Some(BlockType::NodeValue) => {
1247 let mut parent = parent.cast::<Node>().unwrap();
1248 let child_count = parent.child_count() - 1;
1249 parent.set_child_count(child_count);
1250 }
1251 other => {
1252 unreachable!(
1253 "the parent of any value is either tombstone or node. Saw: {other:?}"
1254 );
1255 }
1256 }
1257 }
1258
1259 match self.heap.container.block_at(name_index).block_type() {
1261 Some(BlockType::StringReference) => {
1262 self.release_string_reference(name_index)?;
1263 }
1264 _ => self.heap.free_block(name_index).expect("Failed to free block"),
1265 }
1266
1267 let block = self.heap.container.block_at_mut(block_index);
1270 match block.cast::<Node>() {
1271 Some(block) if block.child_count() != 0 => {
1272 let _ = block.become_tombstone();
1273 }
1274 _ => {
1275 self.heap.free_block(block_index)?;
1276 }
1277 }
1278 Ok(())
1279 }
1280
1281 fn inner_set_string_property_value<'a>(
1282 &mut self,
1283 block_index: BlockIndex,
1284 value: impl Into<Cow<'a, str>>,
1285 ) -> Result<(), Error> {
1286 let old_string_ref_idx =
1287 self.heap.container.block_at_unchecked::<Buffer>(block_index).extent_index();
1288 let new_string_ref_idx = self.get_or_create_string_reference(value.into())?;
1289 self.heap
1290 .container
1291 .block_at_unchecked_mut::<StringRef>(new_string_ref_idx)
1292 .increment_ref_count()?;
1293
1294 self.heap
1295 .container
1296 .block_at_unchecked_mut::<Buffer>(block_index)
1297 .set_extent_index(new_string_ref_idx);
1298 self.release_string_reference(old_string_ref_idx)?;
1299 Ok(())
1300 }
1301
1302 fn inner_set_buffer_property_value(
1303 &mut self,
1304 block_index: BlockIndex,
1305 value: &[u8],
1306 ) -> Result<(), Error> {
1307 self.free_extents(
1308 self.heap.container.block_at_unchecked::<Buffer>(block_index).extent_index(),
1309 )?;
1310 let (result, (extent_index, written)) = match self.write_extents(value) {
1311 Ok((e, w)) => (Ok(()), (e, w)),
1312 Err(err) => (Err(err), (BlockIndex::ROOT, 0)),
1313 };
1314 let mut block = self.heap.container.block_at_unchecked_mut::<Buffer>(block_index);
1315 block.set_total_length(written.try_into().unwrap_or(u32::MAX));
1316 block.set_extent_index(extent_index);
1317 result
1318 }
1319
1320 fn free_extents(&mut self, head_extent_index: BlockIndex) -> Result<(), Error> {
1321 let mut index = head_extent_index;
1322 while index != BlockIndex::ROOT {
1323 let next_index = self.heap.container.block_at_unchecked::<Extent>(index).next_extent();
1324 self.heap.free_block(index)?;
1325 index = next_index;
1326 }
1327 Ok(())
1328 }
1329
1330 fn write_extents(&mut self, value: &[u8]) -> Result<(BlockIndex, usize), Error> {
1331 if value.is_empty() {
1332 return Ok((BlockIndex::ROOT, 0));
1334 }
1335 let mut offset = 0;
1336 let total_size = value.len();
1337 let head_extent_index =
1338 self.heap.allocate_block(utils::block_size_for_payload(total_size - offset))?;
1339 let mut extent_block_index = head_extent_index;
1340 while offset < total_size {
1341 let bytes_written = {
1342 let mut extent_block = self
1343 .heap
1344 .container
1345 .block_at_unchecked_mut::<Reserved>(extent_block_index)
1346 .become_extent(BlockIndex::EMPTY);
1347 extent_block.set_contents(&value[offset..])
1348 };
1349 offset += bytes_written;
1350 if offset < total_size {
1351 let Ok(block_index) =
1352 self.heap.allocate_block(utils::block_size_for_payload(total_size - offset))
1353 else {
1354 return Ok((head_extent_index, offset));
1356 };
1357 self.heap
1358 .container
1359 .block_at_unchecked_mut::<Extent>(extent_block_index)
1360 .set_next_index(block_index);
1361 extent_block_index = block_index;
1362 }
1363 }
1364 Ok((head_extent_index, offset))
1365 }
1366}
1367
1368#[cfg(test)]
1369mod tests {
1370 use super::*;
1371 use crate::reader::PartialNodeHierarchy;
1372 use crate::reader::snapshot::{BackingBuffer, ScannedBlock, Snapshot};
1373 use crate::writer::testing_utils::get_state;
1374 use assert_matches::assert_matches;
1375 use diagnostics_assertions::assert_data_tree;
1376 use futures::prelude::*;
1377 use inspect_format::Header;
1378
1379 #[track_caller]
1380 fn assert_all_free_or_reserved<'a>(
1381 blocks: impl Iterator<Item = Block<&'a BackingBuffer, Unknown>>,
1382 ) {
1383 let mut errors = vec![];
1384 for block in blocks {
1385 if block.block_type() != Some(BlockType::Free)
1386 && block.block_type() != Some(BlockType::Reserved)
1387 {
1388 errors.push(format!(
1389 "block at {} is {:?}, expected {} or {}",
1390 block.index(),
1391 block.block_type(),
1392 BlockType::Free,
1393 BlockType::Reserved,
1394 ));
1395 }
1396 }
1397
1398 if !errors.is_empty() {
1399 panic!("{errors:#?}");
1400 }
1401 }
1402
1403 #[track_caller]
1404 fn assert_all_free<'a>(blocks: impl Iterator<Item = Block<&'a BackingBuffer, Unknown>>) {
1405 let mut errors = vec![];
1406 for block in blocks {
1407 if block.block_type() != Some(BlockType::Free) {
1408 errors.push(format!(
1409 "block at {} is {:?}, expected {}",
1410 block.index(),
1411 block.block_type(),
1412 BlockType::Free
1413 ));
1414 }
1415 }
1416
1417 if !errors.is_empty() {
1418 panic!("{errors:#?}");
1419 }
1420 }
1421
1422 #[fuchsia::test]
1423 fn test_create() {
1424 let state = get_state(4096);
1425 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
1426 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1427 assert_eq!(blocks.len(), 8);
1428 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1429 assert_all_free(blocks.into_iter().skip(1));
1430 }
1431
1432 #[fuchsia::test]
1433 fn test_load_string() {
1434 let outer = get_state(4096);
1435 let mut state = outer.try_lock().expect("lock state");
1436 let block_index = state.inner_lock.get_or_create_string_reference("a value").unwrap();
1437 assert_eq!(state.load_string(block_index).unwrap(), "a value");
1438 }
1439
1440 #[fuchsia::test]
1441 fn test_check_lineage() {
1442 let core_state = get_state(4096);
1443 let mut state = core_state.try_lock().expect("lock state");
1444 let parent_index = state.create_node("", 0.into()).unwrap();
1445 let child_index = state.create_node("", parent_index).unwrap();
1446 let uncle_index = state.create_node("", 0.into()).unwrap();
1447
1448 state.inner_lock.check_lineage(parent_index, child_index).unwrap_err();
1449 state.inner_lock.check_lineage(0.into(), child_index).unwrap_err();
1450 state.inner_lock.check_lineage(child_index, uncle_index).unwrap();
1451 }
1452
1453 #[fuchsia::test]
1454 fn test_reparent() {
1455 let core_state = get_state(4096);
1456 let mut state = core_state.try_lock().expect("lock state");
1457
1458 let a_index = state.create_node("a", 0.into()).unwrap();
1459 let b_index = state.create_node("b", 0.into()).unwrap();
1460
1461 let a = state.get_block::<Node>(a_index);
1462 let b = state.get_block::<Node>(b_index);
1463 assert_eq!(*a.parent_index(), 0);
1464 assert_eq!(*b.parent_index(), 0);
1465
1466 assert_eq!(a.child_count(), 0);
1467 assert_eq!(b.child_count(), 0);
1468
1469 state.reparent(b_index, a_index).unwrap();
1470
1471 let a = state.get_block::<Node>(a_index);
1472 let b = state.get_block::<Node>(b_index);
1473 assert_eq!(*a.parent_index(), 0);
1474 assert_eq!(b.parent_index(), a.index());
1475
1476 assert_eq!(a.child_count(), 1);
1477 assert_eq!(b.child_count(), 0);
1478
1479 let c_index = state.create_node("c", a_index).unwrap();
1480
1481 let a = state.get_block::<Node>(a_index);
1482 let b = state.get_block::<Node>(b_index);
1483 let c = state.get_block::<Node>(c_index);
1484 assert_eq!(*a.parent_index(), 0);
1485 assert_eq!(b.parent_index(), a.index());
1486 assert_eq!(c.parent_index(), a.index());
1487
1488 assert_eq!(a.child_count(), 2);
1489 assert_eq!(b.child_count(), 0);
1490 assert_eq!(c.child_count(), 0);
1491
1492 state.reparent(c_index, b_index).unwrap();
1493
1494 let a = state.get_block::<Node>(a_index);
1495 let b = state.get_block::<Node>(b_index);
1496 let c = state.get_block::<Node>(c_index);
1497 assert_eq!(*a.parent_index(), 0);
1498 assert_eq!(b.parent_index(), a_index);
1499 assert_eq!(c.parent_index(), b_index);
1500
1501 assert_eq!(a.child_count(), 1);
1502 assert_eq!(b.child_count(), 1);
1503 assert_eq!(c.child_count(), 0);
1504 }
1505
1506 #[fuchsia::test]
1507 fn test_node() {
1508 let core_state = get_state(4096);
1509 let block_index = {
1510 let mut state = core_state.try_lock().expect("lock state");
1511
1512 let block_index = state.create_node("test-node", 0.into()).unwrap();
1514 let block = state.get_block::<Node>(block_index);
1515 assert_eq!(block.block_type(), Some(BlockType::NodeValue));
1516 assert_eq!(*block.index(), 2);
1517 assert_eq!(block.child_count(), 0);
1518 assert_eq!(*block.name_index(), 4);
1519 assert_eq!(*block.parent_index(), 0);
1520
1521 let name_block = state.get_block::<StringRef>(block.name_index());
1523 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1524 assert_eq!(name_block.total_length(), 9);
1525 assert_eq!(name_block.order(), 1);
1526 assert_eq!(state.load_string(name_block.index()).unwrap(), "test-node");
1527 block_index
1528 };
1529
1530 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1532 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1533 assert_eq!(blocks.len(), 10);
1534 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1535 assert_eq!(blocks[1].block_type(), Some(BlockType::NodeValue));
1536 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
1537 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
1538 assert_all_free(blocks.into_iter().skip(4));
1539
1540 {
1541 let mut state = core_state.try_lock().expect("lock state");
1542 let child_block_index = state.create_node("child1", block_index).unwrap();
1543 assert_eq!(state.get_block::<Node>(block_index).child_count(), 1);
1544
1545 let child11_block_index = state.create_node("child1-1", child_block_index).unwrap();
1547 {
1548 assert_eq!(state.get_block::<Node>(child11_block_index).child_count(), 0);
1549 assert_eq!(state.get_block::<Node>(child_block_index).child_count(), 1);
1550 assert_eq!(state.get_block::<Node>(block_index).child_count(), 1);
1551 }
1552
1553 assert!(state.free_value(child11_block_index).is_ok());
1554 {
1555 let child_block = state.get_block::<Node>(child_block_index);
1556 assert_eq!(child_block.child_count(), 0);
1557 }
1558
1559 let child_block2_index = state.create_node("child2", block_index).unwrap();
1561 let child_block3_index = state.create_node("child3", block_index).unwrap();
1562 assert_eq!(state.get_block::<Node>(block_index).child_count(), 3);
1563
1564 assert!(state.free_value(child_block_index).is_ok());
1566 assert!(state.free_value(child_block2_index).is_ok());
1567 assert!(state.free_value(child_block3_index).is_ok());
1568 assert_eq!(state.get_block::<Node>(block_index).child_count(), 0);
1569
1570 assert!(state.free_value(block_index).is_ok());
1572 }
1573
1574 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1575 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1576 assert_all_free(blocks.into_iter().skip(1));
1577 }
1578
1579 #[fuchsia::test]
1580 fn test_int_metric() {
1581 let core_state = get_state(4096);
1582 let block_index = {
1583 let mut state = core_state.try_lock().expect("lock state");
1584 let block_index = state.create_int_metric("test", 3, 0.into()).unwrap();
1585 let block = state.get_block::<Int>(block_index);
1586 assert_eq!(block.block_type(), Some(BlockType::IntValue));
1587 assert_eq!(*block.index(), 2);
1588 assert_eq!(block.value(), 3);
1589 assert_eq!(*block.name_index(), 3);
1590 assert_eq!(*block.parent_index(), 0);
1591
1592 let name_block = state.get_block::<StringRef>(block.name_index());
1593 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1594 assert_eq!(name_block.total_length(), 4);
1595 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1596 block_index
1597 };
1598
1599 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1600 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1601 assert_eq!(blocks.len(), 9);
1602 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1603 assert_eq!(blocks[1].block_type(), Some(BlockType::IntValue));
1604 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1605 assert_all_free(blocks.into_iter().skip(3));
1606
1607 {
1608 let mut state = core_state.try_lock().expect("lock state");
1609 assert_eq!(state.add_int_metric(block_index, 10), 13);
1610 assert_eq!(state.get_block::<Int>(block_index).value(), 13);
1611
1612 assert_eq!(state.subtract_int_metric(block_index, 5), 8);
1613 assert_eq!(state.get_block::<Int>(block_index).value(), 8);
1614
1615 state.set_int_metric(block_index, -6);
1616 assert_eq!(state.get_block::<Int>(block_index).value(), -6);
1617
1618 assert_eq!(state.subtract_int_metric(block_index, i64::MAX), i64::MIN);
1619 assert_eq!(state.get_block::<Int>(block_index).value(), i64::MIN);
1620 state.set_int_metric(block_index, i64::MAX);
1621
1622 assert_eq!(state.add_int_metric(block_index, 2), i64::MAX);
1623 assert_eq!(state.get_block::<Int>(block_index).value(), i64::MAX);
1624
1625 assert!(state.free_value(block_index).is_ok());
1627 }
1628
1629 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1630 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1631 assert_all_free(blocks.into_iter().skip(1));
1632 }
1633
1634 #[fuchsia::test]
1635 fn test_uint_metric() {
1636 let core_state = get_state(4096);
1637
1638 let block_index = {
1640 let mut state = core_state.try_lock().expect("try lock");
1641 let block_index = state.create_uint_metric("test", 3, 0.into()).unwrap();
1642 let block = state.get_block::<Uint>(block_index);
1643 assert_eq!(block.block_type(), Some(BlockType::UintValue));
1644 assert_eq!(*block.index(), 2);
1645 assert_eq!(block.value(), 3);
1646 assert_eq!(*block.name_index(), 3);
1647 assert_eq!(*block.parent_index(), 0);
1648
1649 let name_block = state.get_block::<StringRef>(block.name_index());
1650 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1651 assert_eq!(name_block.total_length(), 4);
1652 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1653 block_index
1654 };
1655
1656 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1657 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1658 assert_eq!(blocks.len(), 9);
1659 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1660 assert_eq!(blocks[1].block_type(), Some(BlockType::UintValue));
1661 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1662 assert_all_free(blocks.into_iter().skip(3));
1663
1664 {
1665 let mut state = core_state.try_lock().expect("try lock");
1666 assert_eq!(state.add_uint_metric(block_index, 10), 13);
1667 assert_eq!(state.get_block::<Uint>(block_index).value(), 13);
1668
1669 assert_eq!(state.subtract_uint_metric(block_index, 5), 8);
1670 assert_eq!(state.get_block::<Uint>(block_index).value(), 8);
1671
1672 state.set_uint_metric(block_index, 0);
1673 assert_eq!(state.get_block::<Uint>(block_index).value(), 0);
1674
1675 assert_eq!(state.subtract_uint_metric(block_index, u64::MAX), 0);
1676 assert_eq!(state.get_block::<Uint>(block_index).value(), 0);
1677
1678 state.set_uint_metric(block_index, 3);
1679 assert_eq!(state.add_uint_metric(block_index, u64::MAX), u64::MAX);
1680 assert_eq!(state.get_block::<Uint>(block_index).value(), u64::MAX);
1681
1682 assert!(state.free_value(block_index).is_ok());
1684 }
1685
1686 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1687 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1688 assert_all_free(blocks.into_iter().skip(1));
1689 }
1690
1691 #[fuchsia::test]
1692 fn test_double_metric() {
1693 let core_state = get_state(4096);
1694
1695 let block_index = {
1697 let mut state = core_state.try_lock().expect("lock state");
1698 let block_index = state.create_double_metric("test", 3.0, 0.into()).unwrap();
1699 let block = state.get_block::<Double>(block_index);
1700 assert_eq!(block.block_type(), Some(BlockType::DoubleValue));
1701 assert_eq!(*block.index(), 2);
1702 assert_eq!(block.value(), 3.0);
1703 assert_eq!(*block.name_index(), 3);
1704 assert_eq!(*block.parent_index(), 0);
1705
1706 let name_block = state.get_block::<StringRef>(block.name_index());
1707 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1708 assert_eq!(name_block.total_length(), 4);
1709 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1710 block_index
1711 };
1712
1713 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1714 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1715 assert_eq!(blocks.len(), 9);
1716 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1717 assert_eq!(blocks[1].block_type(), Some(BlockType::DoubleValue));
1718 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1719 assert_all_free(blocks.into_iter().skip(3));
1720
1721 {
1722 let mut state = core_state.try_lock().expect("lock state");
1723 assert_eq!(state.add_double_metric(block_index, 10.5), 13.5);
1724 assert_eq!(state.get_block::<Double>(block_index).value(), 13.5);
1725
1726 assert_eq!(state.subtract_double_metric(block_index, 5.1), 8.4);
1727 assert_eq!(state.get_block::<Double>(block_index).value(), 8.4);
1728
1729 state.set_double_metric(block_index, -6.0);
1730 assert_eq!(state.get_block::<Double>(block_index).value(), -6.0);
1731
1732 assert!(state.free_value(block_index).is_ok());
1734 }
1735
1736 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1737 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1738 assert_all_free(blocks.into_iter().skip(1));
1739 }
1740
1741 #[fuchsia::test]
1742 fn test_create_buffer_property_cleanup_on_failure() {
1743 assert_eq!(constants::MAX_ORDER_SIZE, 2048);
1745
1746 let core_state = get_state(5121); let mut state = core_state.try_lock().expect("lock state");
1748 let name = (0..3000).map(|_| " ").collect::<String>();
1750 let payload = [0u8; 4096]; assert!(state.create_buffer_property(name, &payload, 0.into()).is_err());
1757
1758 drop(state);
1759
1760 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1761 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1762
1763 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1765 assert_all_free(blocks.into_iter().skip(1));
1766 }
1767
1768 #[fuchsia::test]
1769 fn test_string_reference_allocations() {
1770 let core_state = get_state(4096); {
1772 let mut state = core_state.try_lock().expect("lock state");
1773 let sf = "a reference-counted canonical name";
1774 assert_eq!(state.stats().allocated_blocks, 1);
1775
1776 let mut collected = vec![];
1777 for _ in 0..100 {
1778 collected.push(state.create_node(sf, 0.into()).unwrap());
1779 }
1780
1781 let acsf = Arc::new(Cow::Borrowed(sf));
1782 assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1783
1784 assert_eq!(state.stats().allocated_blocks, 102);
1785 let block = state.get_block::<Node>(collected[0]);
1786 let sf_block = state.get_block::<StringRef>(block.name_index());
1787 assert_eq!(sf_block.reference_count(), 100);
1788
1789 collected.into_iter().for_each(|b| {
1790 assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1791 assert!(state.free_value(b).is_ok())
1792 });
1793
1794 assert!(!state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1795
1796 let node_index = state.create_node(sf, 0.into()).unwrap();
1797 assert!(state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1798 assert!(state.free_value(node_index).is_ok());
1799 assert!(!state.inner_lock.string_reference_block_indexes.contains_key(&acsf));
1800 }
1801
1802 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1803 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1804 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1805 assert_all_free(blocks.into_iter().skip(1));
1806 }
1807
1808 #[fuchsia::test]
1809 fn test_string_reference_data() {
1810 let core_state = get_state(4096); let mut state = core_state.try_lock().expect("lock state");
1812
1813 let block_index = state.inner_lock.get_or_create_string_reference("abcd").unwrap();
1815 let block = state.get_block::<StringRef>(block_index);
1816 assert_eq!(block.block_type(), Some(BlockType::StringReference));
1817 assert_eq!(block.order(), 0);
1818 assert_eq!(state.stats().allocated_blocks, 2);
1819 assert_eq!(state.stats().deallocated_blocks, 0);
1820 assert_eq!(block.reference_count(), 0);
1821 assert_eq!(block.total_length(), 4);
1822 assert_eq!(*block.next_extent(), 0);
1823 assert_eq!(block.order(), 0);
1824 assert_eq!(state.load_string(block.index()).unwrap(), "abcd");
1825
1826 state.inner_lock.maybe_free_string_reference(block_index).unwrap();
1827 assert_eq!(state.stats().deallocated_blocks, 1);
1828
1829 let block_index = state.inner_lock.get_or_create_string_reference("longer").unwrap();
1830 let block = state.get_block::<StringRef>(block_index);
1831 assert_eq!(block.block_type(), Some(BlockType::StringReference));
1832 assert_eq!(block.order(), 1);
1833 assert_eq!(block.reference_count(), 0);
1834 assert_eq!(block.total_length(), 6);
1835 assert_eq!(state.stats().allocated_blocks, 3);
1836 assert_eq!(state.stats().deallocated_blocks, 1);
1837 assert_eq!(state.load_string(block.index()).unwrap(), "longer");
1838
1839 let idx = block.next_extent();
1840 assert_eq!(*idx, 0);
1841
1842 state.inner_lock.maybe_free_string_reference(block_index).unwrap();
1843 assert_eq!(state.stats().deallocated_blocks, 2);
1844
1845 let block_index = state.inner_lock.get_or_create_string_reference("longer").unwrap();
1846 let mut block = state.get_block_mut::<StringRef>(block_index);
1847 assert_eq!(block.order(), 1);
1848 block.increment_ref_count().unwrap();
1849 assert!(state.inner_lock.maybe_free_string_reference(block_index).is_ok());
1851
1852 let mut block = state.get_block_mut(block_index);
1853 block.decrement_ref_count().unwrap();
1854 state.inner_lock.maybe_free_string_reference(block_index).unwrap();
1855 }
1856
1857 #[fuchsia::test]
1858 fn test_string_reference_format_property() {
1859 let core_state = get_state(4096);
1860 let block_index = {
1861 let mut state = core_state.try_lock().expect("lock state");
1862
1863 let block_index =
1865 state.create_string("test", "test-property", BlockIndex::from(0)).unwrap();
1866 let block = state.get_block::<Buffer>(block_index);
1867 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
1868 assert_eq!(*block.index(), 2);
1869 assert_eq!(*block.parent_index(), 0);
1870 assert_eq!(*block.name_index(), 3);
1871 assert_eq!(block.total_length(), 0);
1872 assert_eq!(block.format(), Some(PropertyFormat::StringReference));
1873
1874 let name_block = state.get_block::<StringRef>(block.name_index());
1875 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
1876 assert_eq!(name_block.total_length(), 4);
1877 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
1878
1879 let data_block = state.get_block::<StringRef>(block.extent_index());
1880 assert_eq!(data_block.block_type(), Some(BlockType::StringReference));
1881 assert_eq!(state.load_string(data_block.index()).unwrap(), "test-property");
1882 block_index
1883 };
1884
1885 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1886 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1887 assert_eq!(blocks.len(), 10);
1888 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
1889 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
1890 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
1891 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
1892 assert_all_free(blocks.into_iter().skip(4));
1893
1894 {
1895 let mut state = core_state.try_lock().expect("lock state");
1896 assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
1898 }
1899 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1900 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1901 assert_all_free(blocks.into_iter().skip(1));
1902 }
1903
1904 #[fuchsia::test]
1905 fn test_string_arrays() {
1906 let core_state = get_state(4096);
1907 {
1908 let mut state = core_state.try_lock().expect("lock state");
1909 let array_index = state.create_string_array("array", 4, 0.into()).unwrap();
1910 assert_eq!(state.set_array_string_slot(array_index, 0, "0"), Ok(()));
1911 assert_eq!(state.set_array_string_slot(array_index, 1, "1"), Ok(()));
1912 assert_eq!(state.set_array_string_slot(array_index, 2, "2"), Ok(()));
1913 assert_eq!(state.set_array_string_slot(array_index, 3, "3"), Ok(()));
1914
1915 assert_matches!(
1917 state.set_array_string_slot(array_index, 4, ""),
1918 Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(4)))
1919 );
1920 assert_matches!(
1921 state.set_array_string_slot(array_index, 5, ""),
1922 Err(Error::VmoFormat(FormatError::ArrayIndexOutOfBounds(5)))
1923 );
1924
1925 for i in 0..4 {
1926 let idx = state
1927 .get_block::<Array<StringRef>>(array_index)
1928 .get_string_index_at(i)
1929 .unwrap();
1930 assert_eq!(i.to_string(), state.load_string(idx).unwrap());
1931 }
1932
1933 assert_eq!(
1934 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(4),
1935 None
1936 );
1937 assert_eq!(
1938 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(5),
1939 None
1940 );
1941
1942 state.clear_array(array_index, 0).unwrap();
1943 state.free_value(array_index).unwrap();
1944 }
1945
1946 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1947 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1948 assert_all_free(blocks.into_iter().skip(1));
1949 }
1950
1951 #[fuchsia::test]
1952 fn update_string_array_value() {
1953 let core_state = get_state(4096);
1954 {
1955 let mut state = core_state.try_lock().expect("lock state");
1956 let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
1957
1958 assert_eq!(state.set_array_string_slot(array_index, 0, "abc"), Ok(()));
1959 assert_eq!(state.set_array_string_slot(array_index, 1, "def"), Ok(()));
1960
1961 assert_eq!(state.set_array_string_slot(array_index, 0, "cba"), Ok(()));
1962 assert_eq!(state.set_array_string_slot(array_index, 1, "fed"), Ok(()));
1963
1964 let cba_index_slot =
1965 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(0).unwrap();
1966 let fed_index_slot =
1967 state.get_block::<Array<StringRef>>(array_index).get_string_index_at(1).unwrap();
1968 assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap());
1969 assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
1970
1971 state.clear_array(array_index, 0).unwrap();
1972 state.free_value(array_index).unwrap();
1973 }
1974
1975 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
1976 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
1977 blocks[1..].iter().enumerate().for_each(|(i, b)| {
1978 assert!(b.block_type() == Some(BlockType::Free), "index is {}", i + 1);
1979 });
1980 }
1981
1982 #[fuchsia::test]
1983 fn set_string_reference_instances_multiple_times_in_array() {
1984 let core_state = get_state(4096);
1985 {
1986 let mut state = core_state.try_lock().expect("lock state");
1987 let array_index = state.create_string_array("array", 2, 0.into()).unwrap();
1988
1989 let abc = "abc";
1990 let def = "def";
1991 let cba = "cba";
1992 let fed = "fed";
1993
1994 state.set_array_string_slot(array_index, 0, abc).unwrap();
1995 state.set_array_string_slot(array_index, 1, def).unwrap();
1996 state.set_array_string_slot(array_index, 0, abc).unwrap();
1997 state.set_array_string_slot(array_index, 1, def).unwrap();
1998
1999 let abc_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2000 let def_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2001 assert_eq!("abc".to_string(), state.load_string(abc_index_slot).unwrap(),);
2002 assert_eq!("def".to_string(), state.load_string(def_index_slot).unwrap(),);
2003
2004 state.set_array_string_slot(array_index, 0, cba).unwrap();
2005 state.set_array_string_slot(array_index, 1, fed).unwrap();
2006
2007 let cba_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2008 let fed_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2009 assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap(),);
2010 assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2011
2012 state.set_array_string_slot(array_index, 0, abc).unwrap();
2013 state.set_array_string_slot(array_index, 1, def).unwrap();
2014
2015 let abc_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2016 let def_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2017 assert_eq!("abc".to_string(), state.load_string(abc_index_slot).unwrap(),);
2018 assert_eq!("def".to_string(), state.load_string(def_index_slot).unwrap(),);
2019
2020 state.set_array_string_slot(array_index, 0, cba).unwrap();
2021 state.set_array_string_slot(array_index, 1, fed).unwrap();
2022
2023 let cba_index_slot = state.get_block(array_index).get_string_index_at(0).unwrap();
2024 let fed_index_slot = state.get_block(array_index).get_string_index_at(1).unwrap();
2025 assert_eq!("cba".to_string(), state.load_string(cba_index_slot).unwrap(),);
2026 assert_eq!("fed".to_string(), state.load_string(fed_index_slot).unwrap(),);
2027
2028 state.clear_array(array_index, 0).unwrap();
2029 state.free_value(array_index).unwrap();
2030 }
2031
2032 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2033 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2034 blocks[1..].iter().enumerate().for_each(|(i, b)| {
2035 assert!(b.block_type() == Some(BlockType::Free), "index is {}", i + 1);
2036 });
2037 }
2038
2039 #[fuchsia::test]
2040 fn test_empty_value_string_arrays() {
2041 let core_state = get_state(4096);
2042 {
2043 let mut state = core_state.try_lock().expect("lock state");
2044 let array_index = state.create_string_array("array", 4, 0.into()).unwrap();
2045
2046 state.set_array_string_slot(array_index, 0, "").unwrap();
2047 state.set_array_string_slot(array_index, 1, "").unwrap();
2048 state.set_array_string_slot(array_index, 2, "").unwrap();
2049 state.set_array_string_slot(array_index, 3, "").unwrap();
2050 }
2051
2052 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2053 let state = core_state.try_lock().expect("lock state");
2054
2055 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2056 for b in blocks {
2057 if b.block_type() == Some(BlockType::StringReference)
2058 && state.load_string(b.index()).unwrap() == "array"
2059 {
2060 continue;
2061 }
2062
2063 assert_ne!(
2064 b.block_type(),
2065 Some(BlockType::StringReference),
2066 "Got unexpected StringReference, index: {}, value (wrapped in single quotes): '{:?}'",
2067 b.index(),
2068 b.block_type()
2069 );
2070 }
2071 }
2072
2073 #[fuchsia::test]
2074 fn test_bytevector_property() {
2075 let core_state = get_state(4096);
2076
2077 let block_index = {
2079 let mut state = core_state.try_lock().expect("lock state");
2080 let block_index =
2081 state.create_buffer_property("test", b"test-property", 0.into()).unwrap();
2082 let block = state.get_block::<Buffer>(block_index);
2083 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2084 assert_eq!(*block.index(), 2);
2085 assert_eq!(*block.parent_index(), 0);
2086 assert_eq!(*block.name_index(), 3);
2087 assert_eq!(block.total_length(), 13);
2088 assert_eq!(*block.extent_index(), 4);
2089 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2090
2091 let name_block = state.get_block::<StringRef>(block.name_index());
2092 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2093 assert_eq!(name_block.total_length(), 4);
2094 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2095
2096 let extent_block = state.get_block::<Extent>(4.into());
2097 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2098 assert_eq!(*extent_block.next_extent(), 0);
2099 assert_eq!(
2100 std::str::from_utf8(extent_block.contents().unwrap()).unwrap(),
2101 "test-property\0\0\0\0\0\0\0\0\0\0\0"
2102 );
2103 block_index
2104 };
2105
2106 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2107 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2108 assert_eq!(blocks.len(), 10);
2109 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2110 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2111 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2112 assert_eq!(blocks[3].block_type(), Some(BlockType::Extent));
2113 assert_all_free(blocks.into_iter().skip(4));
2114
2115 {
2117 let mut state = core_state.try_lock().expect("lock state");
2118 assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2119 }
2120 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2121 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2122 assert_all_free(blocks.into_iter().skip(1));
2123 }
2124
2125 #[fuchsia::test]
2126 fn test_bool() {
2127 let core_state = get_state(4096);
2128 let block_index = {
2129 let mut state = core_state.try_lock().expect("lock state");
2130
2131 let block_index = state.create_bool("test", true, 0.into()).unwrap();
2133 let block = state.get_block::<Bool>(block_index);
2134 assert_eq!(block.block_type(), Some(BlockType::BoolValue));
2135 assert_eq!(*block.index(), 2);
2136 assert!(block.value());
2137 assert_eq!(*block.name_index(), 3);
2138 assert_eq!(*block.parent_index(), 0);
2139
2140 let name_block = state.get_block::<StringRef>(block.name_index());
2141 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2142 assert_eq!(name_block.total_length(), 4);
2143 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2144 block_index
2145 };
2146
2147 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2148 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2149 assert_eq!(blocks.len(), 9);
2150 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2151 assert_eq!(blocks[1].block_type(), Some(BlockType::BoolValue));
2152 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2153 assert_all_free(blocks.into_iter().skip(3));
2154
2155 {
2157 let mut state = core_state.try_lock().expect("lock state");
2158 assert!(state.free_value(block_index).is_ok());
2159 }
2160 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2161 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2162 assert_all_free(blocks.into_iter().skip(1));
2163 }
2164
2165 #[fuchsia::test]
2166 fn test_int_array() {
2167 let core_state = get_state(4096);
2168 let block_index = {
2169 let mut state = core_state.try_lock().expect("lock state");
2170 let block_index =
2171 state.create_int_array("test", 5, ArrayFormat::Default, 0.into()).unwrap();
2172 let block = state.get_block::<Array<Int>>(block_index);
2173 assert_eq!(block.block_type(), Some(BlockType::ArrayValue));
2174 assert_eq!(block.order(), 2);
2175 assert_eq!(*block.index(), 4);
2176 assert_eq!(*block.name_index(), 2);
2177 assert_eq!(*block.parent_index(), 0);
2178 assert_eq!(block.slots(), 5);
2179 assert_eq!(block.format(), Some(ArrayFormat::Default));
2180 assert_eq!(block.entry_type(), Some(BlockType::IntValue));
2181
2182 let name_block = state.get_block::<StringRef>(BlockIndex::from(2));
2183 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2184 assert_eq!(name_block.total_length(), 4);
2185 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2186 for i in 0..5 {
2187 state.set_array_int_slot(block_index, i, 3 * i as i64);
2188 }
2189 for i in 0..5 {
2190 assert_eq!(state.get_block::<Array<Int>>(block_index).get(i), Some(3 * i as i64));
2191 }
2192 block_index
2193 };
2194
2195 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2196 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2197 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2198 assert_eq!(blocks[1].block_type(), Some(BlockType::StringReference));
2199 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2200 assert_eq!(blocks[3].block_type(), Some(BlockType::ArrayValue));
2201 assert_all_free(blocks.into_iter().skip(4));
2202
2203 {
2205 let mut state = core_state.try_lock().expect("lock state");
2206 assert!(state.free_value(block_index).is_ok());
2207 }
2208 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2209 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2210 assert_all_free(blocks.into_iter().skip(1));
2211 }
2212
2213 #[fuchsia::test]
2214 fn test_write_extent_overflow() {
2215 const SIZE: usize = constants::MAX_ORDER_SIZE * 2;
2216 const EXPECTED_WRITTEN: usize = constants::MAX_ORDER_SIZE - constants::HEADER_SIZE_BYTES;
2217 const TRIED_TO_WRITE: usize = SIZE + 1;
2218 let core_state = get_state(SIZE);
2219 let (_, written) = core_state
2220 .try_lock()
2221 .unwrap()
2222 .inner_lock
2223 .write_extents(&[4u8; TRIED_TO_WRITE])
2224 .unwrap();
2225 assert_eq!(written, EXPECTED_WRITTEN);
2226 }
2227
2228 #[fuchsia::test]
2229 fn overflow_property() {
2230 const SIZE: usize = constants::MAX_ORDER_SIZE * 2;
2231 const EXPECTED_WRITTEN: usize = constants::MAX_ORDER_SIZE - constants::HEADER_SIZE_BYTES;
2232
2233 let core_state = get_state(SIZE);
2234 let mut state = core_state.try_lock().expect("lock state");
2235
2236 let data = "X".repeat(SIZE * 2);
2237 let block_index = state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2238 let block = state.get_block::<Buffer>(block_index);
2239 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2240 assert_eq!(*block.index(), 2);
2241 assert_eq!(*block.parent_index(), 0);
2242 assert_eq!(*block.name_index(), 3);
2243 assert_eq!(block.total_length(), EXPECTED_WRITTEN);
2244 assert_eq!(*block.extent_index(), 128);
2245 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2246
2247 let name_block = state.get_block::<StringRef>(block.name_index());
2248 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2249 assert_eq!(name_block.total_length(), 4);
2250 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2251
2252 let extent_block = state.get_block::<Extent>(128.into());
2253 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2254 assert_eq!(extent_block.order(), 7);
2255 assert_eq!(*extent_block.next_extent(), *BlockIndex::EMPTY);
2256 assert_eq!(
2257 extent_block.contents().unwrap(),
2258 data.chars().take(EXPECTED_WRITTEN).map(|c| c as u8).collect::<Vec<u8>>()
2259 );
2260 }
2261
2262 #[fuchsia::test]
2263 fn test_multi_extent_property() {
2264 let core_state = get_state(10000);
2265 let block_index = {
2266 let mut state = core_state.try_lock().expect("lock state");
2267
2268 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2269 let data = chars.iter().cycle().take(6000).collect::<String>();
2270 let block_index =
2271 state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2272 let block = state.get_block::<Buffer>(block_index);
2273 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2274 assert_eq!(*block.index(), 2);
2275 assert_eq!(*block.parent_index(), 0);
2276 assert_eq!(*block.name_index(), 3);
2277 assert_eq!(block.total_length(), 6000);
2278 assert_eq!(*block.extent_index(), 128);
2279 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2280
2281 let name_block = state.get_block::<StringRef>(block.name_index());
2282 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2283 assert_eq!(name_block.total_length(), 4);
2284 assert_eq!(state.load_string(name_block.index()).unwrap(), "test");
2285
2286 let extent_block = state.get_block::<Extent>(128.into());
2287 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2288 assert_eq!(extent_block.order(), 7);
2289 assert_eq!(*extent_block.next_extent(), 256);
2290 assert_eq!(
2291 extent_block.contents().unwrap(),
2292 chars.iter().cycle().take(2040).map(|&c| c as u8).collect::<Vec<u8>>()
2293 );
2294
2295 let extent_block = state.get_block::<Extent>(256.into());
2296 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2297 assert_eq!(extent_block.order(), 7);
2298 assert_eq!(*extent_block.next_extent(), 384);
2299 assert_eq!(
2300 extent_block.contents().unwrap(),
2301 chars.iter().cycle().skip(2040).take(2040).map(|&c| c as u8).collect::<Vec<u8>>()
2302 );
2303
2304 let extent_block = state.get_block::<Extent>(384.into());
2305 assert_eq!(extent_block.block_type(), Some(BlockType::Extent));
2306 assert_eq!(extent_block.order(), 7);
2307 assert_eq!(*extent_block.next_extent(), 0);
2308 assert_eq!(
2309 extent_block.contents().unwrap()[..1920],
2310 chars.iter().cycle().skip(4080).take(1920).map(|&c| c as u8).collect::<Vec<u8>>()[..]
2311 );
2312 assert_eq!(extent_block.contents().unwrap()[1920..], [0u8; 120][..]);
2313 block_index
2314 };
2315
2316 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2317 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2318 assert_eq!(blocks.len(), 11);
2319 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2320 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2321 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2322 assert_eq!(blocks[8].block_type(), Some(BlockType::Extent));
2323 assert_eq!(blocks[9].block_type(), Some(BlockType::Extent));
2324 assert_eq!(blocks[10].block_type(), Some(BlockType::Extent));
2325 assert_all_free(blocks.into_iter().skip(3).take(5));
2326 {
2328 let mut state = core_state.try_lock().expect("lock state");
2329 assert!(state.free_string_or_bytes_buffer_property(block_index).is_ok());
2330 }
2331 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2332 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2333 assert_all_free(blocks.into_iter().skip(1));
2334 }
2335
2336 #[fuchsia::test]
2337 fn test_freeing_string_references() {
2338 let core_state = get_state(4096);
2339 {
2340 let mut state = core_state.try_lock().expect("lock state");
2341 assert_eq!(state.stats().allocated_blocks, 1);
2342
2343 let block0_index = state.create_node("abcd123456789", 0.into()).unwrap();
2344 let block0_name_index = {
2345 let block0_name_index = state.get_block::<Node>(block0_index).name_index();
2346 let block0_name = state.get_block::<StringRef>(block0_name_index);
2347 assert_eq!(block0_name.order(), 1);
2348 block0_name_index
2349 };
2350 assert_eq!(state.stats().allocated_blocks, 3);
2351
2352 let block1_index = state.inner_lock.get_or_create_string_reference("abcd").unwrap();
2353 assert_eq!(state.stats().allocated_blocks, 4);
2354 assert_eq!(state.get_block::<StringRef>(block1_index).order(), 0);
2355
2356 let block2_index =
2358 state.inner_lock.get_or_create_string_reference("abcd123456789").unwrap();
2359 assert_eq!(state.get_block::<StringRef>(block2_index).order(), 1);
2360 assert_eq!(block0_name_index, block2_index);
2361 assert_eq!(state.stats().allocated_blocks, 4);
2362
2363 let block3_index = state.create_node("abcd12345678", 0.into()).unwrap();
2364 let block3 = state.get_block::<Node>(block3_index);
2365 let block3_name = state.get_block::<StringRef>(block3.name_index());
2366 assert_eq!(block3_name.order(), 1);
2367 assert_eq!(block3.order(), 0);
2368 assert_eq!(state.stats().allocated_blocks, 6);
2369
2370 let mut long_name = "".to_string();
2371 for _ in 0..3000 {
2372 long_name += " ";
2373 }
2374
2375 let block4_index = state.create_node(long_name, 0.into()).unwrap();
2376 let block4 = state.get_block::<Node>(block4_index);
2377 let block4_name = state.get_block::<StringRef>(block4.name_index());
2378 assert_eq!(block4_name.order(), 7);
2379 assert!(*block4_name.next_extent() != 0);
2380 assert_eq!(state.stats().allocated_blocks, 9);
2381
2382 assert!(state.inner_lock.maybe_free_string_reference(block1_index).is_ok());
2383 assert_eq!(state.stats().deallocated_blocks, 1);
2384 assert!(state.inner_lock.maybe_free_string_reference(block2_index).is_ok());
2385 assert_eq!(state.stats().deallocated_blocks, 1);
2387 assert!(state.free_value(block3_index).is_ok());
2388 assert_eq!(state.stats().deallocated_blocks, 3);
2389 assert!(state.free_value(block4_index).is_ok());
2390 assert_eq!(state.stats().deallocated_blocks, 6);
2391 }
2392
2393 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2395 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2396
2397 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2398 assert_eq!(blocks[1].block_type(), Some(BlockType::NodeValue));
2399 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2400 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2401 assert_all_free(blocks.into_iter().skip(4));
2402 }
2403
2404 #[fuchsia::test]
2405 fn test_tombstone() {
2406 let core_state = get_state(4096);
2407 let child_block_index = {
2408 let mut state = core_state.try_lock().expect("lock state");
2409
2410 let block_index = state.create_node("root-node", 0.into()).unwrap();
2412 let block_name_as_string_ref =
2413 state.get_block::<StringRef>(state.get_block::<Node>(block_index).name_index());
2414 assert_eq!(block_name_as_string_ref.order(), 1);
2415 assert_eq!(state.stats().allocated_blocks, 3);
2416 assert_eq!(state.stats().deallocated_blocks, 0);
2417
2418 let child_block_index = state.create_node("child-node", block_index).unwrap();
2419 assert_eq!(state.stats().allocated_blocks, 5);
2420 assert_eq!(state.stats().deallocated_blocks, 0);
2421
2422 assert!(state.free_value(block_index).is_ok());
2424 assert_eq!(state.stats().allocated_blocks, 5);
2425 assert_eq!(state.stats().deallocated_blocks, 1);
2426 child_block_index
2427 };
2428
2429 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2430 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2431
2432 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2435 assert_eq!(blocks[1].block_type(), Some(BlockType::Tombstone));
2436 assert_eq!(blocks[2].block_type(), Some(BlockType::NodeValue));
2437 assert_eq!(blocks[3].block_type(), Some(BlockType::Free));
2438 assert_eq!(blocks[4].block_type(), Some(BlockType::StringReference));
2439 assert_all_free(blocks.into_iter().skip(5));
2440
2441 {
2443 let mut state = core_state.try_lock().expect("lock state");
2444 assert!(state.free_value(child_block_index).is_ok());
2445 }
2446 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2447 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2448 assert_all_free(blocks.into_iter().skip(1));
2449 }
2450
2451 #[fuchsia::test]
2452 fn test_with_header_lock() {
2453 let state = get_state(4096);
2454 state.with_current_header(|header| {
2456 assert_eq!(header.generation_count(), 0);
2457 });
2458
2459 let mut lock_guard = state.try_lock().expect("lock state");
2461 assert!(lock_guard.header().is_locked());
2462 assert_eq!(lock_guard.header().generation_count(), 1);
2463 let _ = lock_guard.create_node("test", 0.into()).unwrap();
2465 let _ = lock_guard.create_node("test2", 2.into()).unwrap();
2466 assert_eq!(lock_guard.header().generation_count(), 1);
2467
2468 drop(lock_guard);
2470 state.with_current_header(|header| {
2471 assert_eq!(header.generation_count(), 2);
2472 assert!(!header.is_locked());
2473 });
2474 }
2475
2476 #[fuchsia::test]
2477 async fn test_link() {
2478 let state = get_state(4096);
2480 let block_index = {
2481 let mut state_guard = state.try_lock().expect("lock state");
2482 let block_index = state_guard
2483 .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2484 async move {
2485 let inspector = Inspector::default();
2486 inspector.root().record_uint("a", 1);
2487 Ok(inspector)
2488 }
2489 .boxed()
2490 })
2491 .unwrap();
2492
2493 assert!(state_guard.callbacks().get("link-name-0").is_some());
2495 let callback = state_guard.callbacks().get("link-name-0").unwrap();
2496 match callback().await {
2497 Ok(inspector) => {
2498 let hierarchy =
2499 PartialNodeHierarchy::try_from(Snapshot::try_from(&inspector).unwrap())
2500 .unwrap();
2501 assert_data_tree!(hierarchy, root: {
2502 a: 1u64,
2503 });
2504 }
2505 Err(_) => unreachable!("we never return errors in the callback"),
2506 }
2507
2508 let block = state_guard.get_block::<Link>(block_index);
2510 assert_eq!(block.block_type(), Some(BlockType::LinkValue));
2511 assert_eq!(*block.index(), 2);
2512 assert_eq!(*block.parent_index(), 0);
2513 assert_eq!(*block.name_index(), 4);
2514 assert_eq!(*block.content_index(), 6);
2515 assert_eq!(block.link_node_disposition(), Some(LinkNodeDisposition::Inline));
2516
2517 let name_block = state_guard.get_block::<StringRef>(block.name_index());
2519 assert_eq!(name_block.block_type(), Some(BlockType::StringReference));
2520 assert_eq!(name_block.total_length(), 9);
2521 assert_eq!(state_guard.load_string(name_block.index()).unwrap(), "link-name");
2522
2523 let content_block = state_guard.get_block::<StringRef>(block.content_index());
2525 assert_eq!(content_block.block_type(), Some(BlockType::StringReference));
2526 assert_eq!(content_block.total_length(), 11);
2527 assert_eq!(state_guard.load_string(content_block.index()).unwrap(), "link-name-0");
2528 block_index
2529 };
2530
2531 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2533 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2534 assert_eq!(blocks.len(), 10);
2535 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2536 assert_eq!(blocks[1].block_type(), Some(BlockType::LinkValue));
2537 assert_eq!(blocks[2].block_type(), Some(BlockType::Free));
2538 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2539 assert_eq!(blocks[4].block_type(), Some(BlockType::StringReference));
2540 assert_all_free(blocks.into_iter().skip(5));
2541
2542 {
2544 let mut state_guard = state.try_lock().expect("lock state");
2545 assert!(state_guard.free_lazy_node(block_index).is_ok());
2546
2547 assert!(state_guard.callbacks().get("link-name-0").is_none());
2549 }
2550 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2551 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2552 assert_all_free(blocks.into_iter().skip(1));
2553
2554 let mut state_guard = state.try_lock().expect("lock state");
2556 state_guard
2557 .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2558 async move { Ok(Inspector::default()) }.boxed()
2559 })
2560 .unwrap();
2561 let content_block = state_guard.get_block::<StringRef>(6.into());
2562 assert_eq!(state_guard.load_string(content_block.index()).unwrap(), "link-name-1");
2563 }
2564
2565 #[fuchsia::test]
2566 fn free_lazy_node_test() {
2567 let state = get_state(4096);
2568 let (lazy_index, _int_with_magic_name_index) = {
2569 let mut state_guard = state.try_lock().expect("lock state");
2570 let lazy_index = state_guard
2571 .create_lazy_node("lk", 0.into(), LinkNodeDisposition::Inline, || {
2572 async move { Ok(Inspector::default()) }.boxed()
2573 })
2574 .unwrap();
2575
2576 let magic_link_name = "lk-0";
2577 let int_with_magic_name_index =
2578 state_guard.create_int_metric(magic_link_name, 0, BlockIndex::from(0)).unwrap();
2579
2580 (lazy_index, int_with_magic_name_index)
2581 };
2582
2583 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2584 let mut blocks = snapshot.scan();
2585 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Header));
2586
2587 let block = blocks.next().and_then(|b| b.cast::<Link>()).unwrap();
2588 assert_eq!(block.block_type(), Some(BlockType::LinkValue));
2589 assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk");
2590 assert_eq!(state.try_lock().unwrap().load_string(block.content_index()).unwrap(), "lk-0");
2591 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2592 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2593 let block = blocks.next().and_then(|b| b.cast::<Int>()).unwrap();
2594 assert_eq!(block.block_type(), Some(BlockType::IntValue));
2595 assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk-0");
2596 assert_all_free(blocks);
2597
2598 state.try_lock().unwrap().free_lazy_node(lazy_index).unwrap();
2599
2600 let snapshot = Snapshot::try_from(state.copy_vmo_bytes().unwrap()).unwrap();
2601 let mut blocks = snapshot.scan();
2602
2603 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Header));
2604 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::Free));
2605 assert_eq!(blocks.next().unwrap().block_type(), Some(BlockType::StringReference));
2606 let block = blocks.next().and_then(|b| b.cast::<Int>()).unwrap();
2607 assert_eq!(block.block_type(), Some(BlockType::IntValue));
2608 assert_eq!(state.try_lock().unwrap().load_string(block.name_index()).unwrap(), "lk-0");
2609 assert_all_free(blocks);
2610 }
2611
2612 #[fuchsia::test]
2613 async fn stats() {
2614 let state = get_state(3 * 4096);
2616 let mut state_guard = state.try_lock().expect("lock state");
2617 let _block1 = state_guard
2618 .create_lazy_node("link-name", 0.into(), LinkNodeDisposition::Inline, || {
2619 async move {
2620 let inspector = Inspector::default();
2621 inspector.root().record_uint("a", 1);
2622 Ok(inspector)
2623 }
2624 .boxed()
2625 })
2626 .unwrap();
2627 let _block2 = state_guard.create_uint_metric("test", 3, 0.into()).unwrap();
2628 assert_eq!(
2629 state_guard.stats(),
2630 Stats {
2631 total_dynamic_children: 1,
2632 maximum_size: 3 * 4096,
2633 current_size: 4096,
2634 allocated_blocks: 6, deallocated_blocks: 0,
2637 failed_allocations: 0,
2638 }
2639 )
2640 }
2641
2642 #[fuchsia::test]
2643 fn transaction_locking() {
2644 let state = get_state(4096);
2645 state.with_current_header(|header| {
2647 assert_eq!(header.generation_count(), 0);
2648 });
2649
2650 state.begin_transaction();
2652 state.with_current_header(|header| {
2653 assert_eq!(header.generation_count(), 1);
2654 assert!(header.is_locked());
2655 });
2656
2657 let mut lock_guard1 = state.try_lock().expect("lock state");
2659 assert_eq!(lock_guard1.inner_lock.transaction_count, 1);
2660 assert_eq!(lock_guard1.header().generation_count(), 1);
2661 assert!(lock_guard1.header().is_locked());
2662 let _ = lock_guard1.create_node("test", 0.into());
2663 assert_eq!(lock_guard1.inner_lock.transaction_count, 1);
2664 assert_eq!(lock_guard1.header().generation_count(), 1);
2665
2666 drop(lock_guard1);
2668 state.with_current_header(|header| {
2669 assert_eq!(header.generation_count(), 1);
2670 assert!(header.is_locked());
2671 });
2672
2673 state.end_transaction();
2675
2676 state.with_current_header(|header| {
2677 assert_eq!(header.generation_count(), 2);
2678 assert!(!header.is_locked());
2679 });
2680
2681 let lock_guard2 = state.try_lock().expect("lock state");
2683 assert!(lock_guard2.header().is_locked());
2684 assert_eq!(lock_guard2.header().generation_count(), 3);
2685 assert_eq!(lock_guard2.inner_lock.transaction_count, 0);
2686 }
2687
2688 #[fuchsia::test]
2689 async fn update_header_vmo_size() {
2690 let core_state = get_state(3 * 4096);
2691 core_state.get_block(BlockIndex::HEADER, |header: &Block<_, Header>| {
2692 assert_eq!(header.vmo_size(), Ok(Some(4096)));
2693 });
2694 let block1_index = {
2695 let mut state = core_state.try_lock().expect("lock state");
2696
2697 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2698 let data = chars.iter().cycle().take(6000).collect::<String>();
2699 let block_index =
2700 state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2701 assert_eq!(state.header().vmo_size(), Ok(Some(2 * 4096)));
2702
2703 block_index
2704 };
2705
2706 let block2_index = {
2707 let mut state = core_state.try_lock().expect("lock state");
2708
2709 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
2710 let data = chars.iter().cycle().take(3000).collect::<String>();
2711 let block_index =
2712 state.create_buffer_property("test", data.as_bytes(), 0.into()).unwrap();
2713 assert_eq!(state.header().vmo_size(), Ok(Some(3 * 4096)));
2714
2715 block_index
2716 };
2717 {
2719 let mut state = core_state.try_lock().expect("lock state");
2720 assert!(state.free_string_or_bytes_buffer_property(block1_index).is_ok());
2721 assert!(state.free_string_or_bytes_buffer_property(block2_index).is_ok());
2722 }
2723 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2724 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2725 assert_all_free(blocks.into_iter().skip(1));
2726 }
2727
2728 #[fuchsia::test]
2729 fn test_buffer_property_on_overflow_set() {
2730 let core_state = get_state(4096);
2731 let block_index = {
2732 let mut state = core_state.try_lock().expect("lock state");
2733
2734 let block_index =
2736 state.create_buffer_property("test", b"test-property", 0.into()).unwrap();
2737
2738 for _ in 10..(4096 / constants::MIN_ORDER_SIZE).try_into().unwrap() {
2740 state.inner_lock.heap.allocate_block(constants::MIN_ORDER_SIZE).unwrap();
2741 }
2742
2743 let values = [b'a'; 8096];
2745 assert!(state.set_buffer_property(block_index, &values).is_err());
2746
2747 let block = state.get_block::<Buffer>(block_index);
2750 assert_eq!(block.block_type(), Some(BlockType::BufferValue));
2751 assert_eq!(*block.index(), 2);
2752 assert_eq!(*block.parent_index(), 0);
2753 assert_eq!(*block.name_index(), 3);
2754 assert_eq!(block.total_length(), 0);
2755 assert_eq!(*block.extent_index(), 0);
2756 assert_eq!(block.format(), Some(PropertyFormat::Bytes));
2757
2758 block_index
2759 };
2760
2761 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2763 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2764 assert_eq!(blocks.len(), 251);
2765 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2766 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2767 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2768 assert_all_free_or_reserved(blocks.into_iter().skip(3));
2769
2770 {
2771 let mut state = core_state.try_lock().expect("lock state");
2772 assert_matches!(state.free_string_or_bytes_buffer_property(block_index), Ok(()));
2774 }
2775 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2776 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2777 assert_all_free_or_reserved(blocks.into_iter().skip(1));
2778 }
2779
2780 #[fuchsia::test]
2781 fn test_string_property_on_overflow_set() {
2782 let core_state = get_state(4096);
2783 {
2784 let mut state = core_state.try_lock().expect("lock state");
2785
2786 let block_index = state.create_string("test", "test-property", 0.into()).unwrap();
2788
2789 for _ in 10..(4096 / constants::MIN_ORDER_SIZE).try_into().unwrap() {
2791 state.inner_lock.heap.allocate_block(constants::MIN_ORDER_SIZE).unwrap();
2792 }
2793
2794 let values = ["a"].into_iter().cycle().take(5000).collect::<String>();
2797 assert!(state.set_string_property(block_index, values).is_err());
2798 let block = state.get_block::<Buffer>(block_index);
2799 assert_eq!(*block.index(), 2);
2800 assert_eq!(*block.parent_index(), 0);
2801 assert_eq!(*block.name_index(), 3);
2802
2803 assert_eq!(
2805 state.load_string(BlockIndex::from(*block.extent_index())).unwrap(),
2806 "test-property"
2807 );
2808
2809 assert!(state.create_int_metric("foo", 1, 0.into()).is_ok());
2811 assert!(state.create_int_metric("bar", 1, 0.into()).is_ok());
2812 };
2813
2814 let snapshot = Snapshot::try_from(core_state.copy_vmo_bytes().unwrap()).unwrap();
2815 let blocks: Vec<ScannedBlock<'_, Unknown>> = snapshot.scan().collect();
2816 assert_eq!(blocks[0].block_type(), Some(BlockType::Header));
2817 assert_eq!(blocks[1].block_type(), Some(BlockType::BufferValue));
2818 assert_eq!(blocks[2].block_type(), Some(BlockType::StringReference));
2819 assert_eq!(blocks[3].block_type(), Some(BlockType::StringReference));
2820 assert_eq!(blocks[250].block_type(), Some(BlockType::IntValue));
2821 assert_eq!(blocks[251].block_type(), Some(BlockType::StringReference));
2822 assert_eq!(blocks[252].block_type(), Some(BlockType::IntValue));
2823 assert_eq!(blocks[253].block_type(), Some(BlockType::StringReference));
2824 assert_all_free_or_reserved(blocks.into_iter().skip(4).rev().skip(4));
2825 }
2826
2827 #[fuchsia::test]
2828 fn test_allocate_link_cleanup_failure() {
2829 let core_state = get_state(4096);
2830 {
2831 let mut state = core_state.try_lock().expect("lock state");
2832
2833 let mut nodes = vec![];
2836 while let Ok(idx) = state.create_node("n", 0.into()) {
2837 nodes.push(idx);
2839 }
2840
2841 state.free_value(nodes.pop().unwrap()).unwrap();
2844
2845 let result = state.create_lazy_node("n", 0.into(), LinkNodeDisposition::Inline, || {
2857 async move { Ok(Inspector::default()) }.boxed()
2858 });
2859
2860 assert!(result.is_err());
2861 }
2862
2863 core_state.with_current_header(|header| {
2865 assert_eq!(header.magic_number(), constants::HEADER_MAGIC_NUMBER);
2866 assert_eq!(header.version(), constants::HEADER_VERSION_NUMBER);
2867 });
2868 }
2869}