revmc_runtime/runtime/
storage.rs1use crate::{OptimizationLevel, eyre};
4use alloy_primitives::B256;
5use dashmap::DashMap;
6use revm_primitives::hardfork::SpecId;
7use std::{fs, path::PathBuf};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
11pub struct RuntimeCacheKey {
12 pub code_hash: B256,
14 pub spec_id: SpecId,
16}
17
18#[derive(Clone, Debug, PartialEq, Eq, Hash)]
22pub struct ArtifactKey {
23 pub runtime: RuntimeCacheKey,
25 pub backend: BackendSelection,
27 pub opt_level: OptimizationLevel,
29}
30
31#[derive(Clone, Debug)]
33pub struct StoredArtifact {
34 pub manifest: ArtifactManifest,
36 pub dylib_path: PathBuf,
38}
39
40#[derive(Clone, Debug)]
42pub struct ArtifactManifest {
43 pub artifact_key: ArtifactKey,
45 pub symbol_name: String,
47 pub bytecode_len: usize,
49 pub artifact_len: usize,
51 pub created_at_unix_secs: u64,
53 pub content_hash: [u8; 32],
55}
56
57#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
59pub enum BackendSelection {
60 #[default]
62 Auto,
63 Llvm,
65}
66
67pub trait ArtifactStore: Send + Sync + 'static {
72 fn load_all(&self) -> eyre::Result<Vec<(ArtifactKey, StoredArtifact)>>;
74
75 fn load(&self, key: &ArtifactKey) -> eyre::Result<Option<StoredArtifact>>;
77
78 fn store(
81 &self,
82 key: &ArtifactKey,
83 manifest: &ArtifactManifest,
84 dylib_bytes: &[u8],
85 ) -> eyre::Result<()>;
86
87 fn delete(&self, key: &ArtifactKey) -> eyre::Result<()>;
89
90 fn clear(&self) -> eyre::Result<()>;
92}
93
94#[derive(Debug)]
96pub struct RuntimeArtifactStore {
97 dir: tempfile::TempDir,
98 artifacts: DashMap<ArtifactKey, StoredArtifact>,
99}
100
101impl RuntimeArtifactStore {
102 pub fn new() -> eyre::Result<Self> {
104 Ok(Self { dir: tempfile::tempdir()?, artifacts: DashMap::default() })
105 }
106
107 pub fn len(&self) -> usize {
109 self.artifacts.len()
110 }
111
112 pub fn is_empty(&self) -> bool {
114 self.len() == 0
115 }
116
117 fn artifact_path(&self, key: &ArtifactKey) -> PathBuf {
118 self.dir.path().join(format!(
119 "{:x}_{:?}_{:?}_{:?}.so",
120 key.runtime.code_hash, key.runtime.spec_id, key.backend, key.opt_level,
121 ))
122 }
123}
124
125impl ArtifactStore for RuntimeArtifactStore {
126 fn load_all(&self) -> eyre::Result<Vec<(ArtifactKey, StoredArtifact)>> {
127 Ok(self
128 .artifacts
129 .iter()
130 .map(|entry| (entry.key().clone(), entry.value().clone()))
131 .collect())
132 }
133
134 fn load(&self, key: &ArtifactKey) -> eyre::Result<Option<StoredArtifact>> {
135 Ok(self.artifacts.get(key).map(|entry| entry.value().clone()))
136 }
137
138 fn store(
139 &self,
140 key: &ArtifactKey,
141 manifest: &ArtifactManifest,
142 dylib_bytes: &[u8],
143 ) -> eyre::Result<()> {
144 let path = self.artifact_path(key);
145 fs::write(&path, dylib_bytes)?;
146 self.artifacts
147 .insert(key.clone(), StoredArtifact { manifest: manifest.clone(), dylib_path: path });
148 Ok(())
149 }
150
151 fn delete(&self, key: &ArtifactKey) -> eyre::Result<()> {
152 if let Some((_, artifact)) = self.artifacts.remove(key) {
153 let _ = fs::remove_file(artifact.dylib_path);
154 }
155 Ok(())
156 }
157
158 fn clear(&self) -> eyre::Result<()> {
159 let paths = self.artifacts.iter().map(|entry| entry.dylib_path.clone()).collect::<Vec<_>>();
160 self.artifacts.clear();
161 for path in paths {
162 let _ = fs::remove_file(path);
163 }
164 Ok(())
165 }
166}