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