linera_views/views/
hashable_wrapper.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{
5    marker::PhantomData,
6    ops::{Deref, DerefMut},
7    sync::Mutex,
8};
9
10use serde::{de::DeserializeOwned, Serialize};
11
12use crate::{
13    batch::Batch,
14    common::from_bytes_option,
15    context::Context,
16    store::ReadableKeyValueStore as _,
17    views::{ClonableView, HashableView, Hasher, View, ViewError, MIN_VIEW_TAG},
18};
19
20/// A hash for `ContainerView` and storing of the hash for memoization purposes
21#[derive(Debug)]
22pub struct WrappedHashableContainerView<C, W, O> {
23    _phantom: PhantomData<C>,
24    stored_hash: Option<O>,
25    hash: Mutex<Option<O>>,
26    inner: W,
27}
28
29/// Key tags to create the sub-keys of a `MapView` on top of the base key.
30#[repr(u8)]
31enum KeyTag {
32    /// Prefix for the indices of the view.
33    Inner = MIN_VIEW_TAG,
34    /// Prefix for the hash.
35    Hash,
36}
37
38impl<W: HashableView, O> View for WrappedHashableContainerView<W::Context, W, O>
39where
40    W: HashableView<Hasher: Hasher<Output = O>>,
41    O: Serialize + DeserializeOwned + Send + Sync + Copy + PartialEq,
42{
43    const NUM_INIT_KEYS: usize = 1 + W::NUM_INIT_KEYS;
44
45    type Context = W::Context;
46
47    fn context(&self) -> &Self::Context {
48        self.inner.context()
49    }
50
51    fn pre_load(context: &Self::Context) -> Result<Vec<Vec<u8>>, ViewError> {
52        let mut v = vec![context.base_key().base_tag(KeyTag::Hash as u8)];
53        let base_key = context.base_key().base_tag(KeyTag::Inner as u8);
54        let context = context.clone_with_base_key(base_key);
55        v.extend(W::pre_load(&context)?);
56        Ok(v)
57    }
58
59    fn post_load(context: Self::Context, values: &[Option<Vec<u8>>]) -> Result<Self, ViewError> {
60        let hash = from_bytes_option(values.first().ok_or(ViewError::PostLoadValuesError)?)?;
61        let base_key = context.base_key().base_tag(KeyTag::Inner as u8);
62        let context = context.clone_with_base_key(base_key);
63        let inner = W::post_load(
64            context,
65            values.get(1..).ok_or(ViewError::PostLoadValuesError)?,
66        )?;
67        Ok(Self {
68            _phantom: PhantomData,
69            stored_hash: hash,
70            hash: Mutex::new(hash),
71            inner,
72        })
73    }
74
75    async fn load(context: Self::Context) -> Result<Self, ViewError> {
76        let keys = Self::pre_load(&context)?;
77        let values = context.store().read_multi_values_bytes(keys).await?;
78        Self::post_load(context, &values)
79    }
80
81    fn rollback(&mut self) {
82        self.inner.rollback();
83        *self.hash.get_mut().unwrap() = self.stored_hash;
84    }
85
86    async fn has_pending_changes(&self) -> bool {
87        if self.inner.has_pending_changes().await {
88            return true;
89        }
90        let hash = self.hash.lock().unwrap();
91        self.stored_hash != *hash
92    }
93
94    fn flush(&mut self, batch: &mut Batch) -> Result<bool, ViewError> {
95        let delete_view = self.inner.flush(batch)?;
96        let hash = self.hash.get_mut().unwrap();
97        if delete_view {
98            let mut key_prefix = self.inner.context().base_key().bytes.clone();
99            key_prefix.pop();
100            batch.delete_key_prefix(key_prefix);
101            self.stored_hash = None;
102            *hash = None;
103        } else if self.stored_hash != *hash {
104            let mut key = self.inner.context().base_key().bytes.clone();
105            let tag = key.last_mut().unwrap();
106            *tag = KeyTag::Hash as u8;
107            match hash {
108                None => batch.delete_key(key),
109                Some(hash) => batch.put_key_value(key, hash)?,
110            }
111            self.stored_hash = *hash;
112        }
113        Ok(delete_view)
114    }
115
116    fn clear(&mut self) {
117        self.inner.clear();
118        *self.hash.get_mut().unwrap() = None;
119    }
120}
121
122impl<W, O> ClonableView for WrappedHashableContainerView<W::Context, W, O>
123where
124    W: HashableView + ClonableView,
125    O: Serialize + DeserializeOwned + Send + Sync + Copy + PartialEq,
126    W::Hasher: Hasher<Output = O>,
127{
128    fn clone_unchecked(&mut self) -> Result<Self, ViewError> {
129        Ok(WrappedHashableContainerView {
130            _phantom: PhantomData,
131            stored_hash: self.stored_hash,
132            hash: Mutex::new(*self.hash.get_mut().unwrap()),
133            inner: self.inner.clone_unchecked()?,
134        })
135    }
136}
137
138impl<W, O> HashableView for WrappedHashableContainerView<W::Context, W, O>
139where
140    W: HashableView,
141    O: Serialize + DeserializeOwned + Send + Sync + Copy + PartialEq,
142    W::Hasher: Hasher<Output = O>,
143{
144    type Hasher = W::Hasher;
145
146    async fn hash_mut(&mut self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
147        let hash = *self.hash.get_mut().unwrap();
148        match hash {
149            Some(hash) => Ok(hash),
150            None => {
151                let new_hash = self.inner.hash_mut().await?;
152                let hash = self.hash.get_mut().unwrap();
153                *hash = Some(new_hash);
154                Ok(new_hash)
155            }
156        }
157    }
158
159    async fn hash(&self) -> Result<<Self::Hasher as Hasher>::Output, ViewError> {
160        let hash = *self.hash.lock().unwrap();
161        match hash {
162            Some(hash) => Ok(hash),
163            None => {
164                let new_hash = self.inner.hash().await?;
165                let mut hash = self.hash.lock().unwrap();
166                *hash = Some(new_hash);
167                Ok(new_hash)
168            }
169        }
170    }
171}
172
173impl<C, W, O> Deref for WrappedHashableContainerView<C, W, O> {
174    type Target = W;
175
176    fn deref(&self) -> &W {
177        &self.inner
178    }
179}
180
181impl<C, W, O> DerefMut for WrappedHashableContainerView<C, W, O> {
182    fn deref_mut(&mut self) -> &mut W {
183        *self.hash.get_mut().unwrap() = None;
184        &mut self.inner
185    }
186}
187
188#[cfg(with_graphql)]
189mod graphql {
190    use std::borrow::Cow;
191
192    use super::WrappedHashableContainerView;
193    use crate::context::Context;
194
195    impl<C, W, O> async_graphql::OutputType for WrappedHashableContainerView<C, W, O>
196    where
197        C: Context,
198        W: async_graphql::OutputType + Send + Sync,
199        O: Send + Sync,
200    {
201        fn type_name() -> Cow<'static, str> {
202            W::type_name()
203        }
204
205        fn qualified_type_name() -> String {
206            W::qualified_type_name()
207        }
208
209        fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
210            W::create_type_info(registry)
211        }
212
213        async fn resolve(
214            &self,
215            ctx: &async_graphql::ContextSelectionSet<'_>,
216            field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
217        ) -> async_graphql::ServerResult<async_graphql::Value> {
218            (**self).resolve(ctx, field).await
219        }
220    }
221}