use std::env;
use std::fs;
use std::path::Path;
use crate::device::{Assignment, Device, FileOpenFlags, NamedMemFlags};
use crate::{runtime::Runtime, Result};
#[cfg(target_os = "macos")]
const MCO_DATABASE_DEFAULT_MAP_ADDRESS: usize = 0x2_0000_0000;
#[cfg(not(target_os = "macos"))]
const MCO_DATABASE_DEFAULT_MAP_ADDRESS: usize = 0x2000_0000;
const MAX_SHMEM_NAME_LEN: usize = 32;
const MAX_FILE_NAME_LEN: usize = 128;
pub struct DeviceContainer {
devs: Vec<Device>,
}
impl DeviceContainer {
pub fn new() -> Self {
let rt_info = Runtime::info_impl();
let mut ret = DeviceContainer { devs: Vec::new() };
if rt_info.disk_supported() {
let stem = "rstest";
remove_db_files(&stem);
ret.devs.push(new_test_mem_dev(Assignment::Database));
ret.devs.push(new_test_mem_dev(Assignment::Cache));
ret.devs.push(new_test_file_dev(
Assignment::Persistent,
Some(db_file_name(&stem)),
));
ret.devs.push(new_test_file_dev(
Assignment::Log,
Some(log_file_name(&stem)),
));
} else {
ret.devs.push(new_test_mem_dev(Assignment::Database));
}
ret
}
pub fn devices(&mut self) -> &mut Vec<Device> {
&mut self.devs
}
}
impl Drop for DeviceContainer {
fn drop(&mut self) {
for dev in &self.devs {
if let Some(file_name) = dev.file_name() {
fs::remove_file(file_name)
.expect(&format!("Failed to remove database file {}", file_name));
}
}
}
}
pub fn new_mem_dev(a: Assignment, s: usize, name: &str) -> Result<Device> {
let info = Runtime::info_impl();
if info.multiprocess_access_supported() {
let hint = if info.direct_pointers_supported() {
MCO_DATABASE_DEFAULT_MAP_ADDRESS
} else {
0usize
};
Device::new_mem_named(a, s, name, NamedMemFlags::new(), hint)
} else {
Device::new_mem_conv(a, s)
}
}
pub fn new_test_mem_dev(a: Assignment) -> Device {
new_mem_dev(a, 1 * 1024 * 1024, &mem_dev_name(a))
.expect("Failed to create the default memory device")
}
pub fn new_test_file_dev(a: Assignment, name: Option<String>) -> Device {
let file_name = name.unwrap_or_else(|| file_name(&db_file_name_stem(), a));
Device::new_file(a, FileOpenFlags::new(), &file_name)
.expect("Failed to create the default disk device")
}
fn exec_name() -> String {
let path = env::args_os().next().expect("Unknown executable path");
let name = Path::new(&path)
.file_name()
.expect("Unknown executable name");
name.to_str().expect("Invalid executable name").to_string()
}
fn mem_dev_name(a: Assignment) -> String {
let mut name = exec_name();
name.truncate(MAX_SHMEM_NAME_LEN);
match a {
Assignment::Database => format!("{}-db", name),
Assignment::Cache => format!("{}-cache", name),
_ => panic!("Unexpected memory device assignment"),
}
}
fn db_file_name_stem() -> String {
let mut exec = exec_name();
exec.truncate(MAX_FILE_NAME_LEN);
let mut stem = exec.clone();
let mut i = 1usize;
while Path::new(&db_file_name(&stem)).exists() || Path::new(&log_file_name(&stem)).exists() {
stem = format!("{}-{}", exec, i);
i += 1;
}
stem
}
fn file_name(stem: &str, a: Assignment) -> String {
match a {
Assignment::Persistent => db_file_name(stem),
Assignment::Log => log_file_name(stem),
_ => panic!("Unexpected file device assignment"),
}
}
fn db_file_name(stem: &str) -> String {
format!("{}.dbs", stem)
}
fn log_file_name(stem: &str) -> String {
format!("{}.log", stem)
}
fn remove_db_files(stem: &str) {
for a in &[Assignment::Persistent, Assignment::Log] {
let name = file_name(stem, *a);
if Path::new(&name).exists() {
fs::remove_file(&name).expect(&format!("Failed to remove {}", name));
}
}
}