1use crate::storage::*;
9use candid::CandidType;
10use serde::{Deserialize, Serialize};
11
12#[derive(CandidType, Serialize, Deserialize, Clone, Debug, Default)]
14pub struct LogState {
15 buckets: Vec<Offset>,
17 pub size: u64,
19 pub bucket_size: usize,
21 pub max_buckets: usize,
23 pub offset: Offset,
25}
26
27pub struct Log<'a, S, T> {
29 pub state: &'a mut LogState,
30 storage: &'a mut S,
31 element: std::marker::PhantomData<T>,
32}
33
34impl LogState {
35 pub fn new(offset: Offset, bucket_size: usize, max_buckets: usize) -> Self {
37 LogState {
38 buckets: Vec::new(),
39 size: 0,
40 bucket_size,
41 max_buckets,
42 offset,
43 }
44 }
45}
46
47impl<'a, S: StorageStack, T> Log<'a, S, T> {
48 pub fn new(state: &'a mut LogState, storage: &'a mut S) -> Self {
50 Self {
51 state,
52 storage,
53 element: std::marker::PhantomData,
54 }
55 }
56
57 pub fn push(&mut self, entry: T) -> Option<()>
60 where
61 T: candid::utils::ArgumentEncoder,
62 {
63 let state = &mut self.state;
64 let mut storage = self.storage.new_with(state.offset);
65 storage.push(entry).ok()?;
66 if state.size >= state.bucket_size as u64 * state.buckets.len() as u64 {
67 if state.buckets.len() >= state.max_buckets {
68 return None;
69 }
70 state.buckets.push(state.offset);
71 }
72 state.size += 1;
73 state.offset = storage.offset();
74 Some(())
75 }
76
77 pub fn size(&self) -> u64 {
79 self.state.size
80 }
81
82 pub fn get(&self, index: u64) -> Option<T>
85 where
86 T: for<'de> candid::utils::ArgumentDecoder<'de>,
87 {
88 let state = &self.state;
89 if index >= state.size {
90 return None;
91 }
92 let i = (index / state.bucket_size as u64) as usize;
93 assert!(i < state.buckets.len());
94 let mut n;
95 let offset;
96 if i == state.buckets.len() - 1 {
97 n = state.size;
98 offset = self.state.offset;
99 } else {
100 n = ((i + 1) * state.bucket_size) as u64;
101 offset = state.buckets[i + 1];
102 }
103 let mut storage = self.storage.new_with(offset);
104 while n > index + 1 {
105 storage.seek_prev().ok()?;
106 n -= 1;
107 }
108 storage.pop().ok()
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use crate::storage::test::Stack;
116
117 #[test]
118 fn test_log_int() {
119 let mut state: LogState = LogState::new(0, 3, 0);
120 let mut stack = Stack::default();
121 let mut log: Log<Stack, (u8,)> = Log::new(&mut state, &mut stack);
122 assert_eq!(log.size(), 0);
123 assert!(log.push((0,)).is_none());
124 assert!(log.get(0).is_none());
125
126 let mut state: LogState = LogState::new(0, 4, 2);
127 let mut stack = Stack::default();
128 let mut log: Log<Stack, (u8,)> = Log::new(&mut state, &mut stack);
129 for i in 0..8 {
130 assert!(log.push((i,)).is_some());
131 assert_eq!(log.get(i as u64), Some((i,)));
132 }
133 assert_eq!(log.size(), 8);
134 for i in 0..8 {
135 assert_eq!(log.get(i as u64), Some((i,)));
136 }
137 assert_eq!(log.size(), 8);
138 assert!(log.push((0,)).is_none());
139 }
140
141 #[test]
142 fn test_log_str() {
143 let mut state: LogState = LogState::new(0, 4, 100);
144 let mut stack = Stack::default();
145 let mut log: Log<Stack, (String,)> = Log::new(&mut state, &mut stack);
146 for i in 0..108 {
147 assert!(log.push((format!("{}", i),)).is_some());
148 }
149 assert_eq!(log.size(), 108);
150 for i in 0..108 {
151 assert_eq!(log.get(i as u64), Some((format!("{}", i),)));
152 }
153 assert_eq!(log.size(), 108);
154 }
155}