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