1extern crate libc;
2
3use std::ptr::null_mut;
4use std::ptr::NonNull;
5use std::sync::{Arc, RwLock};
6use std::thread;
7
8use ffi::*;
9use libc::c_void;
10
11struct Wrapper<T>(NonNull<T>);
12unsafe impl<T> std::marker::Send for Wrapper<T> {}
13
14pub enum RxAction {
15 Changed,
16 NotChanged,
17}
18
19#[derive(Debug)]
20pub struct Trash {
21 cache: Option<RxCache>,
22 dataset: Option<RxDataset>,
23}
24
25impl Trash {
26 pub fn empty(&mut self) {
27 self.cache = None;
28 self.dataset = None;
29 }
30}
31
32impl Default for Trash {
33 fn default() -> Self {
34 Trash {
35 cache: None,
36 dataset: None,
37 }
38 }
39}
40
41#[derive(Debug)]
42pub struct RxCache {
43 cache: *mut randomx_cache,
44}
45
46impl Drop for RxCache {
47 fn drop(&mut self) {
48 unsafe {
49 randomx_release_cache(self.cache);
50 }
51 }
52}
53
54#[derive(Debug)]
55pub struct RxDataset {
56 dataset: *mut randomx_dataset,
57}
58
59impl Drop for RxDataset {
60 fn drop(&mut self) {
61 unsafe {
62 randomx_release_dataset(self.dataset);
63 }
64 }
65}
66
67#[derive(Debug)]
68pub struct RxState {
69 pub seed: [u8; 32],
70 pub hard_aes: bool,
71 pub full_mem: bool,
72 pub large_pages: bool,
73 pub jit_compiler: bool,
74 cache: Option<RxCache>,
75 dataset: Option<RxDataset>,
76 vms: Vec<Arc<RwLock<RxVM>>>,
77 trash: Trash,
78}
79
80#[derive(Debug, PartialEq)]
81pub struct RxVM {
82 pub vm: *mut randomx_vm,
83}
84
85impl Drop for RxVM {
86 fn drop(&mut self) {
87 unsafe {
88 randomx_destroy_vm(self.vm);
89 }
90 }
91}
92
93unsafe impl Sync for RxState {}
94unsafe impl Send for RxState {}
95
96impl RxState {
97 pub fn new() -> Self {
98 RxState {
99 seed: [0; 32],
100 hard_aes: false,
101 full_mem: false,
102 large_pages: false,
103 jit_compiler: false,
104 cache: None,
105 dataset: None,
106 vms: vec![],
107 trash: Trash::default(),
108 }
109 }
110
111 pub fn is_initialized(&self) -> bool {
112 self.cache.is_some()
113 }
114
115 pub fn get_flags(&self) -> randomx_flags {
116 let mut flags = randomx_flags_RANDOMX_FLAG_DEFAULT;
117
118 if self.jit_compiler {
119 flags |= randomx_flags_RANDOMX_FLAG_JIT;
120 }
121
122 if self.hard_aes {
123 flags |= randomx_flags_RANDOMX_FLAG_HARD_AES
124 }
125
126 if self.full_mem {
127 flags |= randomx_flags_RANDOMX_FLAG_FULL_MEM;
128 }
129
130 if self.large_pages {
131 flags |= randomx_flags_RANDOMX_FLAG_LARGE_PAGES;
132 }
133
134 flags
135 }
136
137 pub fn init_cache(&mut self, seed: &[u8]) -> Result<RxAction, &str> {
138 if self.cache.is_some() && self.is_same_seed(seed) {
139 return Ok(RxAction::NotChanged);
140 }
141
142 let flags = self.get_flags();
143 let cache_ptr = unsafe { randomx_alloc_cache(flags) };
144
145 if cache_ptr.is_null() {
146 return Err("cache not allocated");
147 }
148
149 unsafe {
150 randomx_init_cache(cache_ptr, seed.as_ptr() as *const c_void, seed.len());
151 }
152
153 self.trash.cache = self.cache.take();
154 self.cache = Some(RxCache { cache: cache_ptr });
155 self.seed.copy_from_slice(seed);
156
157 Ok(RxAction::Changed)
158 }
159
160 pub fn is_same_seed(&self, seed: &[u8]) -> bool {
161 &self.seed == seed
162 }
163
164 pub fn init_dataset(&mut self, threads_count: u8) -> Result<(), &str> {
165 let cache = self.cache.as_ref().ok_or("cache is not initialized")?;
166
167 let dataset_ptr = unsafe { randomx_alloc_dataset(self.get_flags()) };
170
171 if dataset_ptr.is_null() {
176 return Err("it's not possible initialize a dataset");
177 }
178
179 let mut threads = Vec::new();
180 let mut start: u64 = 0;
181 let count: u64 = unsafe { randomx_dataset_item_count() } as u64;
182 let perth: u64 = count / threads_count as u64;
183 let remainder: u64 = count % threads_count as u64;
184
185 for i in 0..threads_count {
186 let cache = Wrapper(NonNull::new(cache.cache).unwrap());
187 let dataset = Wrapper(NonNull::new(dataset_ptr).unwrap());
188 let count = perth
189 + if i == (threads_count - 1) {
190 remainder
191 } else {
192 0
193 };
194 threads.push(thread::spawn(move || {
195 let d = dataset.0.as_ptr();
196 let c = cache.0.as_ptr();
197 unsafe {
198 randomx_init_dataset(d, c, start.into(), count.into());
199 }
200 }));
201 start += count;
202 }
203
204 for th in threads {
205 th.join().map_err(|_| "failed to join threads")?;
206 }
207
208 self.trash.dataset = self.dataset.take();
209 self.dataset = Some(RxDataset {
210 dataset: dataset_ptr,
211 });
212
213 Ok(())
214 }
215
216 pub fn create_vm(&mut self) -> Result<Arc<RwLock<RxVM>>, &str> {
217 let cache = self.cache.as_ref().ok_or("cache is not initialized")?;
218
219 let dataset = self
220 .dataset
221 .as_ref()
222 .map(|d| d.dataset)
223 .unwrap_or(null_mut());
224
225 let flags = self.get_flags()
226 | if !dataset.is_null() {
227 randomx_flags_RANDOMX_FLAG_FULL_MEM
228 } else {
229 0
230 };
231
232 let mut vm = unsafe { randomx_create_vm(flags, cache.cache, dataset) };
233
234 if vm.is_null() {
235 vm = unsafe {
236 randomx_create_vm(randomx_flags_RANDOMX_FLAG_DEFAULT, cache.cache, dataset)
237 };
238 }
239
240 if !vm.is_null() {
241 self.vms.push(Arc::new(RwLock::new(RxVM { vm })));
242 Ok(self.vms.last().unwrap().clone())
243 } else {
244 Err("unable to create RxVM")
245 }
246 }
247
248 pub fn get_or_create_vm(&mut self) -> Result<Arc<RwLock<RxVM>>, &str> {
249 if self.vms.len() == 0 {
250 self.create_vm()
251 } else {
252 Ok(self.vms.last().unwrap().clone())
253 }
254 }
255
256 pub fn update_vms(&mut self) {
257 for vm in &self.vms {
258 let vm_lock = vm.write().unwrap();
259 unsafe {
260 self.cache
261 .as_ref()
262 .map(|x| randomx_vm_set_cache(vm_lock.vm, x.cache));
263 self.dataset
264 .as_ref()
265 .map(|x| randomx_vm_set_dataset(vm_lock.vm, x.dataset));
266 }
267 }
268
269 if self.cache.is_some() {
270 self.trash.empty();
271 }
272 }
273}