Skip to main content

paper_cache/
policy.rs

1/*
2 * Copyright (c) Kia Shakiba
3 *
4 * This source code is licensed under the GNU AGPLv3 license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8use std::{
9	fmt::{self, Display},
10	str::FromStr,
11};
12
13use serde::{
14	Deserialize,
15	de::{self, Deserializer, Visitor},
16};
17
18use crate::error::CacheError;
19
20#[derive(PartialEq, Clone, Copy, Debug)]
21pub enum PaperPolicy {
22	Auto,
23	Lfu,
24	Fifo,
25	Clock,
26	Sieve,
27	Lru,
28	Mru,
29	TwoQ(f64, f64),
30	Arc,
31	SThreeFifo(f64),
32}
33
34impl PaperPolicy {
35	pub fn is_auto(&self) -> bool {
36		matches!(self, PaperPolicy::Auto)
37	}
38}
39
40impl Display for PaperPolicy {
41	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42		match self {
43			PaperPolicy::Auto => write!(f, "auto"),
44			PaperPolicy::Lfu => write!(f, "lfu"),
45			PaperPolicy::Fifo => write!(f, "fifo"),
46			PaperPolicy::Clock => write!(f, "clock"),
47			PaperPolicy::Sieve => write!(f, "sieve"),
48			PaperPolicy::Lru => write!(f, "lru"),
49			PaperPolicy::Mru => write!(f, "mru"),
50			PaperPolicy::TwoQ(k_in, k_out) => write!(f, "2q-{k_in}-{k_out}"),
51			PaperPolicy::Arc => write!(f, "arc"),
52			PaperPolicy::SThreeFifo(ratio) => write!(f, "s3-fifo-{ratio}"),
53		}
54	}
55}
56
57impl FromStr for PaperPolicy {
58	type Err = CacheError;
59
60	fn from_str(value: &str) -> Result<Self, Self::Err> {
61		let policy = match value {
62			"auto" => PaperPolicy::Auto,
63			"lfu" => PaperPolicy::Lfu,
64			"fifo" => PaperPolicy::Fifo,
65			"clock" => PaperPolicy::Clock,
66			"sieve" => PaperPolicy::Sieve,
67			"lru" => PaperPolicy::Lru,
68			"mru" => PaperPolicy::Mru,
69			value if value.starts_with("2q-") => parse_two_q(value)?,
70			"arc" => PaperPolicy::Arc,
71			value if value.starts_with("s3-fifo-") => parse_s_three_fifo(value)?,
72
73			_ => return Err(CacheError::InvalidPolicy),
74		};
75
76		Ok(policy)
77	}
78}
79
80impl<'a> Deserialize<'a> for PaperPolicy {
81	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
82	where
83		D: Deserializer<'a>,
84	{
85		deserializer.deserialize_str(PaperPolicyVisitor)
86	}
87}
88
89struct PaperPolicyVisitor;
90
91impl Visitor<'_> for PaperPolicyVisitor {
92	type Value = PaperPolicy;
93
94	fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
95		formatter.write_str("a PaperPolicy config")
96	}
97
98	fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
99	where
100		E: de::Error,
101	{
102		PaperPolicy::from_str(value).map_err(|err| E::custom(err.to_string()))
103	}
104}
105
106fn parse_two_q(value: &str) -> Result<PaperPolicy, CacheError> {
107	// skip the "2q-"
108	let tokens = value[3..].split('-').collect::<Vec<&str>>();
109
110	if tokens.len() != 2 {
111		return Err(CacheError::InvalidPolicy);
112	}
113
114	let Ok(k_in) = tokens[0].parse::<f64>() else {
115		return Err(CacheError::InvalidPolicy);
116	};
117
118	let Ok(k_out) = tokens[1].parse::<f64>() else {
119		return Err(CacheError::InvalidPolicy);
120	};
121
122	if k_in + k_out > 1.0 || !(0.0..=1.0).contains(&k_in) || !(0.0..=1.0).contains(&k_out) {
123		return Err(CacheError::InvalidPolicy);
124	}
125
126	Ok(PaperPolicy::TwoQ(k_in, k_out))
127}
128
129fn parse_s_three_fifo(value: &str) -> Result<PaperPolicy, CacheError> {
130	// skip the "s3-fifo-"
131	let tokens = value[8..].split('-').collect::<Vec<&str>>();
132
133	if tokens.len() != 1 {
134		return Err(CacheError::InvalidPolicy);
135	}
136
137	let Ok(ratio) = tokens[0].parse::<f64>() else {
138		return Err(CacheError::InvalidPolicy);
139	};
140
141	if !(0.0..=1.0).contains(&ratio) {
142		return Err(CacheError::InvalidPolicy);
143	}
144
145	Ok(PaperPolicy::SThreeFifo(ratio))
146}