1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! This module implement size spec parsing for mmd.
16
17 /// Error from [parse_size_spec].
18 #[derive(Debug, thiserror::Error)]
19 pub enum ParsingError {
20 /// Size spec was not specified
21 #[error("Size spec is empty")]
22 EmptySpec,
23 /// Specified percentage is out of range
24 #[error("Percentage out of range: {0} (expected to be less than {1})")]
25 PercentageOutOfRange(u64, u64),
26 /// Parsing int error
27 #[error("Size spec is not a valid integer: {0}")]
28 ParsingInt(#[from] std::num::ParseIntError),
29 }
30
31 /// Parse zram size that can be specified by a percentage or an absolute value.
parse_size_spec( spec: &str, block_size: u64, block_count: u64, max_percentage_allowed: u64, ) -> Result<u64, ParsingError>32 pub fn parse_size_spec(
33 spec: &str,
34 block_size: u64,
35 block_count: u64,
36 max_percentage_allowed: u64,
37 ) -> Result<u64, ParsingError> {
38 if spec.is_empty() {
39 return Err(ParsingError::EmptySpec);
40 }
41
42 if let Some(percentage_str) = spec.strip_suffix('%') {
43 let percentage = percentage_str.parse::<u64>()?;
44
45 if percentage > max_percentage_allowed {
46 return Err(ParsingError::PercentageOutOfRange(percentage, max_percentage_allowed));
47 }
48 return Ok(block_count * percentage / 100 * block_size);
49 }
50
51 let size = spec.parse::<u64>()?;
52 Ok(size)
53 }
54
55 #[cfg(test)]
56 mod tests {
57 use super::*;
58 const DEFAULT_BLOCK_SIZE: u64 = 4096;
59 const DEFAULT_BLOCK_COUNT: u64 = 998875;
60 const MAX_PERCENTAGE_ALLOWED: u64 = 500;
61
62 #[test]
parse_size_spec_invalid()63 fn parse_size_spec_invalid() {
64 assert!(parse_size_spec(
65 "",
66 DEFAULT_BLOCK_SIZE,
67 DEFAULT_BLOCK_COUNT,
68 MAX_PERCENTAGE_ALLOWED
69 )
70 .is_err());
71 assert!(parse_size_spec(
72 "not_int%",
73 DEFAULT_BLOCK_SIZE,
74 DEFAULT_BLOCK_COUNT,
75 MAX_PERCENTAGE_ALLOWED
76 )
77 .is_err());
78 assert!(parse_size_spec(
79 "not_int",
80 DEFAULT_BLOCK_SIZE,
81 DEFAULT_BLOCK_COUNT,
82 MAX_PERCENTAGE_ALLOWED
83 )
84 .is_err());
85 }
86
87 #[test]
parse_size_spec_percentage_out_of_range()88 fn parse_size_spec_percentage_out_of_range() {
89 assert!(parse_size_spec("201%", DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT, 200).is_err());
90 }
91
92 #[test]
parse_size_spec_percentage()93 fn parse_size_spec_percentage() {
94 assert_eq!(parse_size_spec("0%", 4096, 5, MAX_PERCENTAGE_ALLOWED).unwrap(), 0);
95 assert_eq!(parse_size_spec("33%", 4096, 5, MAX_PERCENTAGE_ALLOWED).unwrap(), 4096);
96 assert_eq!(parse_size_spec("50%", 4096, 5, MAX_PERCENTAGE_ALLOWED).unwrap(), 8192);
97 assert_eq!(parse_size_spec("90%", 4096, 5, MAX_PERCENTAGE_ALLOWED).unwrap(), 16384);
98 assert_eq!(parse_size_spec("100%", 4096, 5, MAX_PERCENTAGE_ALLOWED).unwrap(), 20480);
99 assert_eq!(parse_size_spec("200%", 4096, 5, MAX_PERCENTAGE_ALLOWED).unwrap(), 40960);
100 assert_eq!(
101 parse_size_spec("100%", 4096, 3995500, MAX_PERCENTAGE_ALLOWED).unwrap(),
102 16365568000
103 );
104 }
105
106 #[test]
parse_size_spec_bytes()107 fn parse_size_spec_bytes() {
108 assert_eq!(
109 parse_size_spec(
110 "1234567",
111 DEFAULT_BLOCK_SIZE,
112 DEFAULT_BLOCK_COUNT,
113 MAX_PERCENTAGE_ALLOWED
114 )
115 .unwrap(),
116 1234567
117 );
118 }
119 }
120