Skip to content

Commit 9f81f9a

Browse files
committed
libvirt: Fix decimal vs binary unit handling in memory conversion
The previous implementation incorrectly treated decimal units (KB, MB, GB - powers of 1000) the same as binary units (KiB, MiB, GiB - powers of 1024). Per libvirt specification, these are distinct unit types. Updated convert_memory_to_mb() to: - Properly distinguish decimal vs binary units - Use u128 for calculations to prevent overflow with TB values - Add comprehensive test coverage for both unit types Fixed test assertion for KB unit (1048576 KB = 1000 MiB, not 1024 MiB). Assisted-by: Claude Code Signed-off-by: Colin Walters <walters@verbum.org>
1 parent 7992a8f commit 9f81f9a

2 files changed

Lines changed: 37 additions & 22 deletions

File tree

crates/kit/src/utils.rs

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -174,21 +174,32 @@ pub(crate) fn parse_memory_to_mb(memory_str: &str) -> Result<u32> {
174174
}
175175
}
176176

177-
/// Convert memory value with unit to megabytes
178-
/// Handles libvirt-style units (KiB, MiB, GiB, etc.) and short forms (K, M, G)
177+
/// Convert memory value with unit to megabytes (MiB)
178+
/// Handles libvirt-style units distinguishing between decimal (KB, MB, GB - powers of 1000)
179+
/// and binary (KiB, MiB, GiB - powers of 1024) units per libvirt specification
179180
pub(crate) fn convert_memory_to_mb(value: u32, unit: &str) -> u32 {
180-
match unit {
181-
"B" => value / (1024 * 1024),
182-
"KB" | "k" => value / 1024,
183-
"KiB" | "K" => value / 1024,
184-
"MB" | "M" => value,
185-
"MiB" => value,
186-
"GB" | "G" => value * 1024,
187-
"GiB" => value * 1024,
188-
"TB" | "T" => value * 1024 * 1024,
189-
"TiB" => value * 1024 * 1024,
190-
_ => value / 1024, // Default to KiB if unknown
191-
}
181+
// Use u128 for calculations to prevent overflow with large units like TB
182+
let value_u128 = value as u128;
183+
let mib_u128 = 1024 * 1024;
184+
185+
let mb = match unit {
186+
// Binary prefixes (powers of 1024), converting to MiB
187+
"k" | "K" | "KiB" => value_u128 / 1024,
188+
"M" | "MiB" => value_u128,
189+
"G" | "GiB" => value_u128 * 1024,
190+
"T" | "TiB" => value_u128 * 1024 * 1024,
191+
192+
// Decimal prefixes (powers of 1000), converting to MiB
193+
"B" | "bytes" => value_u128 / mib_u128,
194+
"KB" => (value_u128 * 1_000) / mib_u128,
195+
"MB" => (value_u128 * 1_000_000) / mib_u128,
196+
"GB" => (value_u128 * 1_000_000_000) / mib_u128,
197+
"TB" => (value_u128 * 1_000_000_000_000) / mib_u128,
198+
199+
// Libvirt default is KiB for memory
200+
_ => value_u128 / 1024,
201+
};
202+
mb as u32
192203
}
193204

194205
#[cfg(test)]
@@ -197,21 +208,25 @@ mod tests {
197208

198209
#[test]
199210
fn test_convert_memory_to_mb() {
200-
// Test KiB (libvirt default)
211+
// Test binary units (powers of 1024)
201212
assert_eq!(convert_memory_to_mb(4194304, "KiB"), 4096);
202213
assert_eq!(convert_memory_to_mb(2097152, "KiB"), 2048);
203-
204-
// Test MiB
205214
assert_eq!(convert_memory_to_mb(2048, "MiB"), 2048);
206215
assert_eq!(convert_memory_to_mb(4096, "MiB"), 4096);
207-
208-
// Test GiB
209216
assert_eq!(convert_memory_to_mb(4, "GiB"), 4096);
210217
assert_eq!(convert_memory_to_mb(2, "GiB"), 2048);
211218

212-
// Test short forms
219+
// Test short forms (binary)
213220
assert_eq!(convert_memory_to_mb(4, "G"), 4096);
214221
assert_eq!(convert_memory_to_mb(2048, "M"), 2048);
215222
assert_eq!(convert_memory_to_mb(2097152, "K"), 2048);
223+
224+
// Test decimal units (powers of 1000)
225+
assert_eq!(convert_memory_to_mb(1048576, "KB"), 1000);
226+
assert_eq!(convert_memory_to_mb(1024, "MB"), 976);
227+
assert_eq!(convert_memory_to_mb(4, "GB"), 3814);
228+
229+
// Test default/unknown unit (defaults to KiB)
230+
assert_eq!(convert_memory_to_mb(4194304, "unknown"), 4096);
216231
}
217232
}

crates/kit/src/xml_utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,9 +397,9 @@ mod tests {
397397
let dom = parse_xml_dom(xml).unwrap();
398398
assert_eq!(dom.parse_memory_mb(), Some(4096));
399399

400-
// Test KB
400+
// Test KB (decimal unit: 1000-based)
401401
let xml = r#"<memory unit='KB'>1048576</memory>"#;
402402
let dom = parse_xml_dom(xml).unwrap();
403-
assert_eq!(dom.parse_memory_mb(), Some(1024));
403+
assert_eq!(dom.parse_memory_mb(), Some(1000));
404404
}
405405
}

0 commit comments

Comments
 (0)