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