1use allocative::Allocative;
5#[cfg(with_metrics)]
6use linera_base::prometheus_util::MeasureLatency as _;
7use serde::{de::DeserializeOwned, Serialize};
8
9use crate::{
10 batch::Batch,
11 common::{from_bytes_option_or_default, HasherOutput},
12 context::Context,
13 hashable_wrapper::WrappedHashableContainerView,
14 views::{ClonableView, HashableView, Hasher, ReplaceContext, View},
15 ViewError,
16};
17
18#[cfg(with_metrics)]
19mod metrics {
20 use std::sync::LazyLock;
21
22 use linera_base::prometheus_util::{exponential_bucket_latencies, register_histogram_vec};
23 use prometheus::HistogramVec;
24
25 pub static REGISTER_VIEW_HASH_RUNTIME: LazyLock<HistogramVec> = LazyLock::new(|| {
27 register_histogram_vec(
28 "register_view_hash_runtime",
29 "RegisterView hash runtime",
30 &[],
31 exponential_bucket_latencies(5.0),
32 )
33 });
34}
35
36#[derive(Debug, Allocative)]
38#[allocative(bound = "C, T: Allocative")]
39pub struct RegisterView<C, T> {
40 delete_storage_first: bool,
42 #[allocative(skip)]
44 context: C,
45 stored_value: Box<T>,
47 update: Option<Box<T>>,
49}
50
51impl<C, T, C2> ReplaceContext<C2> for RegisterView<C, T>
52where
53 C: Context,
54 C2: Context,
55 T: Default + Send + Sync + Serialize + DeserializeOwned + Clone,
56{
57 type Target = RegisterView<C2, T>;
58
59 async fn with_context(
60 &mut self,
61 ctx: impl FnOnce(&Self::Context) -> C2 + Clone,
62 ) -> Self::Target {
63 RegisterView {
64 delete_storage_first: self.delete_storage_first,
65 context: ctx(&self.context),
66 stored_value: self.stored_value.clone(),
67 update: self.update.clone(),
68 }
69 }
70}
71
72impl<C, T> View for RegisterView<C, T>
73where
74 C: Context,
75 T: Default + Send + Sync + Serialize + DeserializeOwned,
76{
77 const NUM_INIT_KEYS: usize = 1;
78
79 type Context = C;
80
81 fn context(&self) -> C {
82 self.context.clone()
83 }
84
85 fn pre_load(context: &C) -> Result<Vec<Vec<u8>>, ViewError> {
86 Ok(vec![context.base_key().bytes.clone()])
87 }
88
89 fn post_load(context: C, values: &[Option<Vec<u8>>]) -> Result<Self, ViewError> {
90 let value =
91 from_bytes_option_or_default(values.first().ok_or(ViewError::PostLoadValuesError)?)?;
92 let stored_value = Box::new(value);
93 Ok(Self {
94 delete_storage_first: false,
95 context,
96 stored_value,
97 update: None,
98 })
99 }
100
101 fn rollback(&mut self) {
102 self.delete_storage_first = false;
103 self.update = None;
104 }
105
106 async fn has_pending_changes(&self) -> bool {
107 if self.delete_storage_first {
108 return true;
109 }
110 self.update.is_some()
111 }
112
113 fn pre_save(&self, batch: &mut Batch) -> Result<bool, ViewError> {
114 let mut delete_view = false;
115 if self.delete_storage_first {
116 batch.delete_key(self.context.base_key().bytes.clone());
117 delete_view = true;
118 } else if let Some(value) = &self.update {
119 let key = self.context.base_key().bytes.clone();
120 batch.put_key_value(key, value)?;
121 }
122 Ok(delete_view)
123 }
124
125 fn post_save(&mut self) {
126 if self.delete_storage_first {
127 self.stored_value = Box::default();
128 } else if let Some(value) = self.update.take() {
129 self.stored_value = value;
130 }
131 self.delete_storage_first = false;
132 self.update = None;
133 }
134
135 fn clear(&mut self) {
136 self.delete_storage_first = true;
137 self.update = Some(Box::default());
138 }
139}
140
141impl<C, T> ClonableView for RegisterView<C, T>
142where
143 C: Context,
144 T: Clone + Default + Send + Sync + Serialize + DeserializeOwned,
145{
146 fn clone_unchecked(&mut self) -> Result<Self, ViewError> {
147 Ok(RegisterView {
148 delete_storage_first: self.delete_storage_first,
149 context: self.context.clone(),
150 stored_value: self.stored_value.clone(),
151 update: self.update.clone(),
152 })
153 }
154}
155
156impl<C, T> RegisterView<C, T>
157where
158 C: Context,
159{
160 pub fn get(&self) -> &T {
173 match &self.update {
174 None => &self.stored_value,
175 Some(value) => value,
176 }
177 }
178
179 pub fn set(&mut self, value: T) {
193 self.delete_storage_first = false;
194 self.update = Some(Box::new(value));
195 }
196
197 pub fn extra(&self) -> &C::Extra {
199 self.context.extra()
200 }
201}
202
203impl<C, T> RegisterView<C, T>
204where
205 C: Context,
206 T: Clone + Serialize,
207{
208 pub fn get_mut(&mut self) -> &mut T {
221 self.delete_storage_first = false;
222 match &mut self.update {
223 Some(value) => value,
224 update => {
225 *update = Some(self.stored_value.clone());
226 update.as_mut().unwrap()
227 }
228 }
229 }
230
231 fn compute_hash(&self) -> Result<<sha3::Sha3_256 as Hasher>::Output, ViewError> {
232 #[cfg(with_metrics)]
233 let _hash_latency = metrics::REGISTER_VIEW_HASH_RUNTIME.measure_latency();
234 let mut hasher = sha3::Sha3_256::default();
235 hasher.update_with_bcs_bytes(self.get())?;
236 Ok(hasher.finalize())
237 }
238}
239
240impl<C, T> HashableView for RegisterView<C, T>
241where
242 C: Context,
243 T: Clone + Default + Send + Sync + Serialize + DeserializeOwned,
244{
245 type Hasher = sha3::Sha3_256;
246
247 async fn hash_mut(&mut self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
248 self.compute_hash()
249 }
250
251 async fn hash(&self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
252 self.compute_hash()
253 }
254}
255
256pub type HashedRegisterView<C, T> =
258 WrappedHashableContainerView<C, RegisterView<C, T>, HasherOutput>;
259
260#[cfg(with_graphql)]
261mod graphql {
262 use std::borrow::Cow;
263
264 use super::RegisterView;
265 use crate::context::Context;
266
267 impl<C, T> async_graphql::OutputType for RegisterView<C, T>
268 where
269 C: Context,
270 T: async_graphql::OutputType + Send + Sync,
271 {
272 fn type_name() -> Cow<'static, str> {
273 T::type_name()
274 }
275
276 fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
277 T::create_type_info(registry)
278 }
279
280 async fn resolve(
281 &self,
282 ctx: &async_graphql::ContextSelectionSet<'_>,
283 field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
284 ) -> async_graphql::ServerResult<async_graphql::Value> {
285 self.get().resolve(ctx, field).await
286 }
287 }
288}