1use std::{
5 marker::PhantomData,
6 ops::{Deref, DerefMut},
7 sync::Mutex,
8};
9
10use allocative::Allocative;
11use linera_base::visit_allocative_simple;
12use serde::{de::DeserializeOwned, Serialize};
13
14use crate::{
15 batch::Batch,
16 common::from_bytes_option,
17 context::Context,
18 views::{ClonableView, HashableView, Hasher, ReplaceContext, View, ViewError, MIN_VIEW_TAG},
19};
20
21#[derive(Debug, Allocative)]
23#[allocative(bound = "C, O, W: Allocative")]
24pub struct WrappedHashableContainerView<C, W, O> {
25 #[allocative(skip)]
27 _phantom: PhantomData<C>,
28 #[allocative(visit = visit_allocative_simple)]
30 stored_hash: Option<O>,
31 #[allocative(visit = visit_allocative_simple)]
33 hash: Mutex<Option<O>>,
34 inner: W,
36}
37
38#[repr(u8)]
40enum KeyTag {
41 Inner = MIN_VIEW_TAG,
43 Hash,
45}
46
47impl<C, W, O, C2> ReplaceContext<C2> for WrappedHashableContainerView<C, W, O>
48where
49 W: HashableView<Hasher: Hasher<Output = O>, Context = C> + ReplaceContext<C2>,
50 <W as ReplaceContext<C2>>::Target: HashableView<Hasher: Hasher<Output = O>>,
51 O: Serialize + DeserializeOwned + Send + Sync + Copy + PartialEq,
52 C: Context,
53 C2: Context,
54{
55 type Target = WrappedHashableContainerView<C2, <W as ReplaceContext<C2>>::Target, O>;
56
57 async fn with_context(
58 &mut self,
59 ctx: impl FnOnce(&Self::Context) -> C2 + Clone,
60 ) -> Self::Target {
61 let hash = *self.hash.lock().unwrap();
62 WrappedHashableContainerView {
63 _phantom: PhantomData,
64 stored_hash: self.stored_hash,
65 hash: Mutex::new(hash),
66 inner: self.inner.with_context(ctx).await,
67 }
68 }
69}
70
71impl<W: HashableView, O> View for WrappedHashableContainerView<W::Context, W, O>
72where
73 W: HashableView<Hasher: Hasher<Output = O>>,
74 O: Serialize + DeserializeOwned + Send + Sync + Copy + PartialEq,
75{
76 const NUM_INIT_KEYS: usize = 1 + W::NUM_INIT_KEYS;
77
78 type Context = W::Context;
79
80 fn context(&self) -> &Self::Context {
81 self.inner.context()
82 }
83
84 fn pre_load(context: &Self::Context) -> Result<Vec<Vec<u8>>, ViewError> {
85 let mut v = vec![context.base_key().base_tag(KeyTag::Hash as u8)];
86 let base_key = context.base_key().base_tag(KeyTag::Inner as u8);
87 let context = context.clone_with_base_key(base_key);
88 v.extend(W::pre_load(&context)?);
89 Ok(v)
90 }
91
92 fn post_load(context: Self::Context, values: &[Option<Vec<u8>>]) -> Result<Self, ViewError> {
93 let hash = from_bytes_option(values.first().ok_or(ViewError::PostLoadValuesError)?)?;
94 let base_key = context.base_key().base_tag(KeyTag::Inner as u8);
95 let context = context.clone_with_base_key(base_key);
96 let inner = W::post_load(
97 context,
98 values.get(1..).ok_or(ViewError::PostLoadValuesError)?,
99 )?;
100 Ok(Self {
101 _phantom: PhantomData,
102 stored_hash: hash,
103 hash: Mutex::new(hash),
104 inner,
105 })
106 }
107
108 fn rollback(&mut self) {
109 self.inner.rollback();
110 *self.hash.get_mut().unwrap() = self.stored_hash;
111 }
112
113 async fn has_pending_changes(&self) -> bool {
114 if self.inner.has_pending_changes().await {
115 return true;
116 }
117 let hash = self.hash.lock().unwrap();
118 self.stored_hash != *hash
119 }
120
121 fn pre_save(&self, batch: &mut Batch) -> Result<bool, ViewError> {
122 let delete_view = self.inner.pre_save(batch)?;
123 let hash = *self.hash.lock().unwrap();
124 if delete_view {
125 let mut key_prefix = self.inner.context().base_key().bytes.clone();
126 key_prefix.pop();
127 batch.delete_key_prefix(key_prefix);
128 } else if self.stored_hash != hash {
129 let mut key = self.inner.context().base_key().bytes.clone();
130 let tag = key.last_mut().unwrap();
131 *tag = KeyTag::Hash as u8;
132 match hash {
133 None => batch.delete_key(key),
134 Some(hash) => batch.put_key_value(key, &hash)?,
135 }
136 }
137 Ok(delete_view)
138 }
139
140 fn post_save(&mut self) {
141 self.inner.post_save();
142 let hash = *self.hash.get_mut().unwrap();
143 self.stored_hash = hash;
144 }
145
146 fn clear(&mut self) {
147 self.inner.clear();
148 *self.hash.get_mut().unwrap() = None;
149 }
150}
151
152impl<W, O> ClonableView for WrappedHashableContainerView<W::Context, W, O>
153where
154 W: HashableView + ClonableView,
155 O: Serialize + DeserializeOwned + Send + Sync + Copy + PartialEq,
156 W::Hasher: Hasher<Output = O>,
157{
158 fn clone_unchecked(&mut self) -> Result<Self, ViewError> {
159 Ok(WrappedHashableContainerView {
160 _phantom: PhantomData,
161 stored_hash: self.stored_hash,
162 hash: Mutex::new(*self.hash.get_mut().unwrap()),
163 inner: self.inner.clone_unchecked()?,
164 })
165 }
166}
167
168impl<W, O> HashableView for WrappedHashableContainerView<W::Context, W, O>
169where
170 W: HashableView,
171 O: Serialize + DeserializeOwned + Send + Sync + Copy + PartialEq,
172 W::Hasher: Hasher<Output = O>,
173{
174 type Hasher = W::Hasher;
175
176 async fn hash_mut(&mut self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
177 let hash = *self.hash.get_mut().unwrap();
178 match hash {
179 Some(hash) => Ok(hash),
180 None => {
181 let new_hash = self.inner.hash_mut().await?;
182 let hash = self.hash.get_mut().unwrap();
183 *hash = Some(new_hash);
184 Ok(new_hash)
185 }
186 }
187 }
188
189 async fn hash(&self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
190 let hash = *self.hash.lock().unwrap();
191 match hash {
192 Some(hash) => Ok(hash),
193 None => {
194 let new_hash = self.inner.hash().await?;
195 let mut hash = self.hash.lock().unwrap();
196 *hash = Some(new_hash);
197 Ok(new_hash)
198 }
199 }
200 }
201}
202
203impl<C, W, O> Deref for WrappedHashableContainerView<C, W, O> {
204 type Target = W;
205
206 fn deref(&self) -> &W {
207 &self.inner
208 }
209}
210
211impl<C, W, O> DerefMut for WrappedHashableContainerView<C, W, O> {
212 fn deref_mut(&mut self) -> &mut W {
213 *self.hash.get_mut().unwrap() = None;
214 &mut self.inner
215 }
216}
217
218impl<C, W, O> WrappedHashableContainerView<C, W, O> {
219 pub(crate) fn inner_mut_preserve_hash(&mut self) -> &mut W {
223 &mut self.inner
224 }
225}
226
227#[cfg(with_graphql)]
228mod graphql {
229 use std::borrow::Cow;
230
231 use super::WrappedHashableContainerView;
232 use crate::context::Context;
233
234 impl<C, W, O> async_graphql::OutputType for WrappedHashableContainerView<C, W, O>
235 where
236 C: Context,
237 W: async_graphql::OutputType + Send + Sync,
238 O: Send + Sync,
239 {
240 fn type_name() -> Cow<'static, str> {
241 W::type_name()
242 }
243
244 fn qualified_type_name() -> String {
245 W::qualified_type_name()
246 }
247
248 fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
249 W::create_type_info(registry)
250 }
251
252 async fn resolve(
253 &self,
254 ctx: &async_graphql::ContextSelectionSet<'_>,
255 field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
256 ) -> async_graphql::ServerResult<async_graphql::Value> {
257 (**self).resolve(ctx, field).await
258 }
259 }
260}