1use std::env;
15use std::fs;
16use std::path::Path;
17
18use crate::device::{Assignment, Device, FileOpenFlags, NamedMemFlags};
19use crate::{runtime::Runtime, Result};
20
21#[cfg(target_os = "macos")]
22const MCO_DATABASE_DEFAULT_MAP_ADDRESS: usize = 0x2_0000_0000;
23
24#[cfg(not(target_os = "macos"))]
25const MCO_DATABASE_DEFAULT_MAP_ADDRESS: usize = 0x2000_0000;
26
27const MAX_SHMEM_NAME_LEN: usize = 32;
28const MAX_FILE_NAME_LEN: usize = 128;
29
30pub struct DeviceContainer {
37 devs: Vec<Device>,
38}
39
40impl DeviceContainer {
41 pub fn new() -> Self {
43 let rt_info = Runtime::info_impl();
44
45 let mut ret = DeviceContainer { devs: Vec::new() };
46
47 if rt_info.disk_supported() {
48 let stem = "rstest";
49
50 remove_db_files(&stem);
52
53 ret.devs.push(new_test_mem_dev(Assignment::Database));
54 ret.devs.push(new_test_mem_dev(Assignment::Cache));
55 ret.devs.push(new_test_file_dev(
56 Assignment::Persistent,
57 Some(db_file_name(&stem)),
58 ));
59 ret.devs.push(new_test_file_dev(
60 Assignment::Log,
61 Some(log_file_name(&stem)),
62 ));
63 } else {
64 ret.devs.push(new_test_mem_dev(Assignment::Database));
65 }
66
67 ret
68 }
69
70 pub fn devices(&mut self) -> &mut Vec<Device> {
72 &mut self.devs
73 }
74}
75
76impl Drop for DeviceContainer {
77 fn drop(&mut self) {
78 for dev in &self.devs {
79 if let Some(file_name) = dev.file_name() {
80 fs::remove_file(file_name)
81 .expect(&format!("Failed to remove database file {}", file_name));
82 }
83 }
84 }
85}
86
87pub fn new_mem_dev(a: Assignment, s: usize, name: &str) -> Result<Device> {
90 let info = Runtime::info_impl();
91
92 if info.multiprocess_access_supported() {
93 let hint = if info.direct_pointers_supported() {
94 MCO_DATABASE_DEFAULT_MAP_ADDRESS
95 } else {
96 0usize
97 };
98 Device::new_mem_named(a, s, name, NamedMemFlags::new(), hint)
99 } else {
100 Device::new_mem_conv(a, s)
101 }
102}
103
104pub fn new_test_mem_dev(a: Assignment) -> Device {
113 new_mem_dev(a, 1 * 1024 * 1024, &mem_dev_name(a))
114 .expect("Failed to create the default memory device")
115}
116
117pub fn new_test_file_dev(a: Assignment, name: Option<String>) -> Device {
127 let file_name = name.unwrap_or_else(|| file_name(&db_file_name_stem(), a));
128
129 Device::new_file(a, FileOpenFlags::new(), &file_name)
130 .expect("Failed to create the default disk device")
131}
132
133fn exec_name() -> String {
134 let path = env::args_os().next().expect("Unknown executable path");
135 let name = Path::new(&path)
136 .file_name()
137 .expect("Unknown executable name");
138 name.to_str().expect("Invalid executable name").to_string()
139}
140
141fn mem_dev_name(a: Assignment) -> String {
142 let mut name = exec_name();
143 name.truncate(MAX_SHMEM_NAME_LEN);
144 match a {
145 Assignment::Database => format!("{}-db", name),
146 Assignment::Cache => format!("{}-cache", name),
147 _ => panic!("Unexpected memory device assignment"),
148 }
149}
150
151fn db_file_name_stem() -> String {
152 let mut exec = exec_name();
153 exec.truncate(MAX_FILE_NAME_LEN);
154 let mut stem = exec.clone();
155 let mut i = 1usize;
156
157 while Path::new(&db_file_name(&stem)).exists() || Path::new(&log_file_name(&stem)).exists() {
158 stem = format!("{}-{}", exec, i);
159 i += 1;
160 }
161
162 stem
163}
164
165fn file_name(stem: &str, a: Assignment) -> String {
166 match a {
167 Assignment::Persistent => db_file_name(stem),
168 Assignment::Log => log_file_name(stem),
169 _ => panic!("Unexpected file device assignment"),
170 }
171}
172
173fn db_file_name(stem: &str) -> String {
174 format!("{}.dbs", stem)
175}
176
177fn log_file_name(stem: &str) -> String {
178 format!("{}.log", stem)
179}
180
181fn remove_db_files(stem: &str) {
182 for a in &[Assignment::Persistent, Assignment::Log] {
183 let name = file_name(stem, *a);
184 if Path::new(&name).exists() {
185 fs::remove_file(&name).expect(&format!("Failed to remove {}", name));
186 }
187 }
188}