grovedb_merk/test_utils/
temp_merk.rs

1// MIT LICENSE
2//
3// Copyright (c) 2021 Dash Core Group
4//
5// Permission is hereby granted, free of charge, to any
6// person obtaining a copy of this software and associated
7// documentation files (the "Software"), to deal in the
8// Software without restriction, including without
9// limitation the rights to use, copy, modify, merge,
10// publish, distribute, sublicense, and/or sell copies of
11// the Software, and to permit persons to whom the Software
12// is furnished to do so, subject to the following
13// conditions:
14//
15// The above copyright notice and this permission notice
16// shall be included in all copies or substantial portions
17// of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
20// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
21// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
22// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
23// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
26// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27// DEALINGS IN THE SOFTWARE.
28
29//! Temp merk test utils
30
31#[cfg(feature = "full")]
32use std::ops::{Deref, DerefMut};
33
34use grovedb_path::SubtreePath;
35#[cfg(feature = "full")]
36use grovedb_storage::{rocksdb_storage::test_utils::TempStorage, Storage};
37use grovedb_storage::{
38    rocksdb_storage::{PrefixedRocksDbTransactionContext, RocksDbStorage},
39    StorageBatch,
40};
41use grovedb_version::version::GroveVersion;
42
43#[cfg(feature = "full")]
44use crate::Merk;
45use crate::{tree::kv::ValueDefinedCostType, TreeType};
46
47#[cfg(feature = "full")]
48/// Wraps a Merk instance and deletes it from disk it once it goes out of scope.
49pub struct TempMerk {
50    storage: &'static TempStorage,
51    batch: &'static StorageBatch,
52    merk: Merk<PrefixedRocksDbTransactionContext<'static>>,
53    tx: &'static <RocksDbStorage as Storage<'static>>::Transaction,
54}
55
56#[cfg(feature = "full")]
57impl TempMerk {
58    /// Opens a `TempMerk` at the given file path, creating a new one if it
59    /// does not exist.
60    pub fn new(grove_version: &GroveVersion) -> Self {
61        let storage = Box::leak(Box::new(TempStorage::new()));
62        let batch = Box::leak(Box::new(StorageBatch::new()));
63        let tx = Box::leak(Box::new(storage.start_transaction()));
64
65        let context = storage
66            .get_transactional_storage_context(SubtreePath::empty(), Some(batch), tx)
67            .unwrap();
68
69        let merk = Merk::open_base(
70            context,
71            TreeType::NormalTree,
72            None::<fn(&[u8], &GroveVersion) -> Option<ValueDefinedCostType>>,
73            grove_version,
74        )
75        .unwrap()
76        .unwrap();
77        TempMerk {
78            storage,
79            merk,
80            batch,
81            tx,
82        }
83    }
84
85    /// Commits pending batch operations.
86    pub fn commit(&mut self, grove_version: &GroveVersion) {
87        let batch: Box<StorageBatch> =
88            unsafe { Box::from_raw(self.batch as *const _ as *mut StorageBatch) };
89        let tx: Box<<RocksDbStorage as Storage<'static>>::Transaction> = unsafe {
90            Box::from_raw(
91                self.tx as *const _ as *mut <RocksDbStorage as Storage<'static>>::Transaction,
92            )
93        };
94        self.storage
95            .commit_multi_context_batch(*batch, Some(self.tx))
96            .unwrap()
97            .expect("unable to commit batch");
98        self.storage
99            .commit_transaction(*tx)
100            .unwrap()
101            .expect("unable to commit transaction");
102        self.batch = Box::leak(Box::new(StorageBatch::new()));
103        self.tx = Box::leak(Box::new(self.storage.start_transaction()));
104        let context = self
105            .storage
106            .get_transactional_storage_context(SubtreePath::empty(), Some(self.batch), self.tx)
107            .unwrap();
108        self.merk = Merk::open_base(
109            context,
110            TreeType::NormalTree,
111            None::<fn(&[u8], &GroveVersion) -> Option<ValueDefinedCostType>>,
112            grove_version,
113        )
114        .unwrap()
115        .unwrap();
116    }
117}
118
119#[cfg(feature = "full")]
120impl Drop for TempMerk {
121    fn drop(&mut self) {
122        unsafe {
123            let batch = Box::from_raw(self.batch as *const _ as *mut StorageBatch);
124
125            let tx: Box<<RocksDbStorage as Storage<'static>>::Transaction> = Box::from_raw(
126                self.tx as *const _ as *mut <RocksDbStorage as Storage<'static>>::Transaction,
127            );
128
129            let _ = self.storage.commit_multi_context_batch(*batch, Some(&tx));
130            let _ = self.storage.commit_transaction(*tx).unwrap();
131            drop(Box::from_raw(self.storage as *const _ as *mut TempStorage));
132        }
133    }
134}
135
136#[cfg(feature = "full")]
137impl Default for TempMerk {
138    fn default() -> Self {
139        Self::new(GroveVersion::latest())
140    }
141}
142
143#[cfg(feature = "full")]
144impl Deref for TempMerk {
145    type Target = Merk<PrefixedRocksDbTransactionContext<'static>>;
146
147    fn deref(&self) -> &Merk<PrefixedRocksDbTransactionContext<'static>> {
148        &self.merk
149    }
150}
151
152#[cfg(feature = "full")]
153impl DerefMut for TempMerk {
154    fn deref_mut(&mut self) -> &mut Merk<PrefixedRocksDbTransactionContext<'static>> {
155        &mut self.merk
156    }
157}