use super::garnet_server_options::OptionsError;
pub const DEFAULT_RESP_VERSION: u8 = 2;
pub const MIN_PAGE_SIZE_BYTES: i64 = 512;
#[derive(Debug, Clone)]
pub struct ServerOptions {
pub log_memory_size: String,
pub page_size: String,
pub pub_sub_page_size: String,
pub segment_size: String,
pub object_log_segment_size: String,
pub index_memory_size: String,
pub min_page_size_bytes: i64,
}
impl Default for ServerOptions {
fn default() -> Self {
Self {
log_memory_size: "16g".into(),
page_size: "16m".into(),
pub_sub_page_size: "4k".into(),
segment_size: "1g".into(),
object_log_segment_size: "1g".into(),
index_memory_size: "128m".into(),
min_page_size_bytes: MIN_PAGE_SIZE_BYTES,
}
}
}
impl ServerOptions {
pub fn new() -> Self {
Self::default()
}
pub fn memory_size_bits(&self) -> i32 {
let size = parse_size(&self.log_memory_size).0;
log2_exact(previous_power_of_2(size.max(1)))
}
pub fn validated_page_size_bits(
&self,
value: &str,
_prop_name: &str,
) -> Result<i32, OptionsError> {
let size = parse_size(value).0;
let adjusted = previous_power_of_2(size);
if adjusted < self.min_page_size_bytes {
return Err(OptionsError::PageSizeTooSmall(
value.to_string(),
adjusted,
self.min_page_size_bytes,
));
}
Ok(log2_exact(adjusted))
}
pub fn page_size_bits(&self) -> Result<i32, OptionsError> {
self.validated_page_size_bits(&self.page_size, "PageSize")
}
pub fn pub_sub_page_size_bytes(&self) -> i64 {
let size = parse_size(&self.pub_sub_page_size).0;
previous_power_of_2(size)
}
pub fn segment_size_bits(&self, is_obj: bool) -> i32 {
let value = if is_obj {
&self.object_log_segment_size
} else {
&self.segment_size
};
let size = parse_size(value).0;
log2_exact(previous_power_of_2(size.max(1)))
}
pub fn index_size_cachelines(&self, name: &str, index_size: &str) -> Result<i32, OptionsError> {
let size = parse_size(index_size).0;
let adjusted = previous_power_of_2(size);
if !(64..=(1i64 << 37)).contains(&adjusted) {
return Err(OptionsError::OutOfRange(name.to_string(), adjusted));
}
Ok((adjusted / 64) as i32)
}
pub fn parse_size(value: &str) -> (i64, usize) {
parse_size(value)
}
pub fn try_parse_size(value: &str) -> Option<i64> {
try_parse_size(value)
}
pub fn pretty_size(value: i64) -> String {
pretty_size(value)
}
pub fn previous_power_of_2(v: i64) -> i64 {
previous_power_of_2(v)
}
pub fn next_power_of_2(v: i64) -> i64 {
next_power_of_2(v)
}
pub fn pub_sub_page_size_bits(&self) -> u32 {
self.pub_sub_page_size_bytes().max(2).ilog2()
}
}
pub fn parse_size(value: &str) -> (i64, usize) {
parse_size_bytes(value.as_bytes())
}
pub fn parse_size_bytes(value: &[u8]) -> (i64, usize) {
const SUFFIX_EXP: [(u8, u32); 5] = [(b'k', 1), (b'm', 2), (b'g', 3), (b't', 4), (b'p', 5)];
let mut result: i64 = 0;
let mut bytes_read = 0usize;
for (i, &c) in value.iter().enumerate() {
if c.is_ascii_digit() {
result = result.wrapping_mul(10).wrapping_add(i64::from(c - b'0'));
bytes_read += 1;
} else if let Some((_, exp)) = SUFFIX_EXP.iter().find(|(s, _)| s.eq_ignore_ascii_case(&c)) {
result = result.wrapping_mul(1024_i64.pow(*exp));
bytes_read += 1;
if i + 1 < value.len() && value[i + 1].eq_ignore_ascii_case(&b'b') {
bytes_read += 1;
}
return (result, bytes_read);
}
}
(result, bytes_read)
}
pub fn try_parse_size(value: &str) -> Option<i64> {
try_parse_size_bytes(value.as_bytes())
}
pub fn try_parse_size_bytes(value: &[u8]) -> Option<i64> {
let (size, chars_read) = parse_size_bytes(value);
(chars_read == value.len()).then_some(size)
}
#[must_use]
pub fn previous_power_of_2(v: i64) -> i64 {
let mut v = v;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;
v - (v >> 1)
}
#[must_use]
pub fn next_power_of_2(v: i64) -> i64 {
let mut v = v;
v = v.wrapping_sub(1);
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;
v.wrapping_add(1)
}
#[must_use]
pub fn pretty_size(value: i64) -> String {
const SUFFIX: [char; 5] = ['k', 'm', 'g', 't', 'p'];
fn round12(v: f64) -> f64 {
let scaled = v * 1e12;
let bumped = if scaled >= 0.0 {
scaled + 0.5
} else {
scaled - 0.5
};
bumped.floor() / 1e12
}
let mut v = value as f64;
let mut exp: i32 = 0;
while v - v.floor() > 0.0 {
if exp >= 18 {
break;
}
exp += 3;
v *= 1024.0;
v = round12(v);
}
while v.floor().to_string().len() > 3 {
if exp <= -18 {
break;
}
exp -= 3;
v /= 1024.0;
v = round12(v);
}
if exp > 0 {
let c = SUFFIX[(exp / 3 - 1) as usize];
format!("{v}{c}")
} else if exp < 0 {
let idx = (-exp / 3 - 1) as usize;
if idx < SUFFIX.len() {
let c = SUFFIX[idx];
format!("{v}{c}")
} else {
format!("{v}")
}
} else {
format!("{v}")
}
}
fn log2_exact(v: i64) -> i32 {
63 - v.leading_zeros() as i32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_size_specs_with_suffix_and_b() {
assert_eq!(ServerOptions::parse_size("1024"), (1024, 4));
assert_eq!(ServerOptions::parse_size("4k"), (4 * 1024, 2));
assert_eq!(ServerOptions::parse_size("4kb"), (4 * 1024, 3));
assert_eq!(ServerOptions::parse_size("16m"), (16 * 1024 * 1024, 3));
assert_eq!(
ServerOptions::parse_size("2g"),
(2i64 * 1024 * 1024 * 1024, 2)
);
assert_eq!(ServerOptions::parse_size("1t"), (1i64 << 40, 2));
assert_eq!(ServerOptions::parse_size(""), (0, 0));
assert_eq!(ServerOptions::try_parse_size("16m"), Some(16 * 1024 * 1024));
assert_eq!(
ServerOptions::try_parse_size("16mb"),
Some(16 * 1024 * 1024)
);
assert_eq!(ServerOptions::try_parse_size("16mx"), None);
}
#[test]
fn powers_of_two() {
assert_eq!(ServerOptions::previous_power_of_2(1000), 512);
assert_eq!(ServerOptions::previous_power_of_2(1024), 1024);
assert_eq!(ServerOptions::next_power_of_2(1000), 1024);
assert_eq!(ServerOptions::next_power_of_2(1024), 1024);
}
#[test]
fn memory_and_page_size_bits() {
let mut options = ServerOptions::new();
assert_eq!(options.memory_size_bits(), 34); assert_eq!(options.page_size_bits().expect("默认页合法"), 24); options.log_memory_size = "10g".into();
assert_eq!(options.memory_size_bits(), 33);
}
#[test]
fn validated_page_size_enforces_minimum() {
let options = ServerOptions::new();
assert!(options.validated_page_size_bits("4k", "PageSize").is_ok());
let too_small = options.validated_page_size_bits("64", "PageSize");
assert!(too_small.is_err());
let mut relaxed = ServerOptions::new();
relaxed.min_page_size_bytes = 16;
assert!(relaxed.validated_page_size_bits("64", "PageSize").is_ok());
}
#[test]
fn pub_sub_page_size_bytes() {
let mut options = ServerOptions::new();
assert_eq!(options.pub_sub_page_size_bytes(), 4096);
options.pub_sub_page_size = "3k".into();
assert_eq!(options.pub_sub_page_size_bytes(), 2048); }
#[test]
fn segment_size_bits_selects_log() {
let mut options = ServerOptions::new();
assert_eq!(options.segment_size_bits(false), 30); assert_eq!(options.segment_size_bits(true), 30); options.object_log_segment_size = "512m".into();
assert_eq!(options.segment_size_bits(true), 29);
}
#[test]
fn index_size_cachelines_bounds() {
let options = ServerOptions::new();
assert_eq!(
options
.index_size_cachelines("IndexMemorySize", "128m")
.expect("合法索引"),
128 * 1024 * 1024 / 64
);
assert!(options.index_size_cachelines("idx", "32").is_err());
assert!(options.index_size_cachelines("idx", "64g").is_ok());
}
#[test]
fn pretty_size_switches_suffix() {
assert_eq!(ServerOptions::pretty_size(1024), "1k");
assert_eq!(ServerOptions::pretty_size(1536), "1.5k");
assert_eq!(ServerOptions::pretty_size(500), "500");
assert_eq!(ServerOptions::pretty_size(4 * 1024 * 1024 * 1024), "4g");
assert_eq!(ServerOptions::pretty_size(i64::MAX), "8");
}
}