fuchsia_inspect_contrib/nodes/
lru_cache.rs1use fuchsia_inspect::Node;
6use fuchsia_inspect_derive::Unit;
7use lru::LruCache;
8use std::hash::Hash;
9use std::num::NonZeroUsize;
10
11use crate::nodes::NodeTimeExt;
12
13pub struct LruCacheNode<T: Unit + Eq + Hash> {
16 node: Node,
17 items: LruCache<T, CacheItem<T>>,
18}
19
20impl<T: Unit + Eq + Hash> LruCacheNode<T> {
21 pub fn new(node: Node, capacity: usize) -> Self {
22 Self {
23 node,
24 items: LruCache::new(NonZeroUsize::new(capacity).unwrap_or(NonZeroUsize::MIN)),
25 }
26 }
27
28 pub fn insert(&mut self, item: T) -> usize {
36 match self.items.get_mut(&item) {
37 Some(entry) => entry.index,
38 None => {
39 let index = if self.items.len() < self.items.cap().get() {
40 self.items.len()
41 } else {
42 self.items.pop_lru().map(|entry| entry.1.index).unwrap_or(0)
43 };
44 let child = self.node.create_child(index.to_string());
45 NodeTimeExt::<zx::BootTimeline>::record_time(&child, "@time");
46 let data = item.inspect_create(&child, "data");
47 self.items.put(item, CacheItem { index, _node: child, _data: data });
48 index
49 }
50 }
51 }
52}
53struct CacheItem<T: Unit> {
54 index: usize,
55 _node: Node,
56 _data: <T as Unit>::Data,
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use diagnostics_assertions::{AnyNumericProperty, assert_data_tree};
63 use fuchsia_inspect::Inspector;
64
65 #[fuchsia::test]
66 async fn test_insert() {
67 let inspector = Inspector::default();
68 let cache_node = inspector.root().create_child("cache");
69 let mut cache_node = LruCacheNode::new(cache_node, 3);
70 assert_eq!(cache_node.insert(111), 0);
72 assert_eq!(cache_node.insert(222), 1);
73 assert_eq!(cache_node.insert(333), 2);
74 assert_data_tree!(inspector, root: {
75 cache: {
76 "0": { "@time": AnyNumericProperty, "data": 111i64},
77 "1": { "@time": AnyNumericProperty, "data": 222i64},
78 "2": { "@time": AnyNumericProperty, "data": 333i64},
79 }
80 });
81
82 assert_eq!(cache_node.insert(222), 1);
84 assert_eq!(cache_node.insert(111), 0);
85 assert_eq!(cache_node.insert(333), 2);
86 assert_data_tree!(inspector, root: {
87 cache: {
88 "0": { "@time": AnyNumericProperty, "data": 111i64},
89 "1": { "@time": AnyNumericProperty, "data": 222i64},
90 "2": { "@time": AnyNumericProperty, "data": 333i64},
91 }
92 });
93
94 assert_eq!(cache_node.insert(444), 1);
96 assert_data_tree!(inspector, root: {
97 cache: {
98 "0": { "@time": AnyNumericProperty, "data": 111i64},
99 "1": { "@time": AnyNumericProperty, "data": 444i64},
100 "2": { "@time": AnyNumericProperty, "data": 333i64},
101 }
102 });
103
104 assert_eq!(cache_node.insert(222), 0);
106 assert_data_tree!(inspector, root: {
107 cache: {
108 "0": { "@time": AnyNumericProperty, "data": 222i64},
109 "1": { "@time": AnyNumericProperty, "data": 444i64},
110 "2": { "@time": AnyNumericProperty, "data": 333i64},
111 }
112 });
113 }
114
115 #[derive(PartialEq, Eq, Hash, Unit)]
116 struct Item {
117 num: u64,
118 string: String,
119 }
120
121 #[fuchsia::test]
122 async fn test_insert_custom_struct() {
123 let inspector = Inspector::default();
124 let cache_node = inspector.root().create_child("cache");
125 let mut cache_node = LruCacheNode::new(cache_node, 3);
126 assert_eq!(cache_node.insert(Item { num: 1337u64, string: "42".to_string() }), 0);
127 assert_eq!(cache_node.insert(Item { num: 1337u64, string: "43".to_string() }), 1);
128 assert_data_tree!(inspector, root: {
129 cache: {
130 "0": {
131 "@time": AnyNumericProperty,
132 "data": { num: 1337u64, string: "42".to_string() }
133 },
134 "1": {
135 "@time": AnyNumericProperty,
136 "data": { num: 1337u64, string: "43".to_string() }
137 },
138 }
139 });
140 }
141}