ferron_common/util/
sizify.rs

1// Sizify function taken from SVR.JS and rewritten from JavaScript to Rust
2// SVR.JS is licensed under MIT, so below is the copyright notice:
3//
4// Copyright (c) 2018-2025 SVR.JS
5// Portions of this file are derived from SVR.JS (https://git.svrjs.org/svrjs/svrjs).
6//
7// Permission is hereby granted, free of charge, to any person obtaining a copy
8// of this software and associated documentation files (the "Software"), to deal
9// in the Software without restriction, including without limitation the rights
10// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11// copies of the Software, and to permit persons to whom the Software is
12// furnished to do so, subject to the following conditions:
13//
14// The above copyright notice and this permission notice shall be included in all
15// copies or substantial portions of the Software.
16//
17
18/// Converts the file size into a human-readable one
19pub fn sizify(bytes: u64, add_i: bool) -> String {
20  if bytes == 0 {
21    return "0".to_string();
22  }
23
24  let prefixes = ["", "K", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"];
25  let prefix_index = (bytes.ilog2() as usize / 10).min(prefixes.len() - 1);
26  let prefix_index_translated = 2_u64.pow(10 * prefix_index as u32);
27  let decimal_points = (2 - (bytes / prefix_index_translated).checked_ilog10().unwrap_or(0) as i32).max(0);
28
29  let size = ((bytes as f64 / prefix_index_translated as f64) * 10_f64.powi(decimal_points)).ceil()
30    / 10_f64.powi(decimal_points);
31
32  let mut result = String::new();
33  result.push_str(&size.to_string()); // Size
34  result.push_str(prefixes[prefix_index]); // Prefix
35  if prefix_index > 0 && add_i {
36    result.push('i'); // "i" suffix
37  }
38
39  result
40}
41
42#[cfg(test)]
43mod tests {
44  use super::*;
45
46  #[test]
47  fn test_sizify_zero_bytes() {
48    assert_eq!(sizify(0, false), "0");
49  }
50
51  #[test]
52  fn test_sizify_small_values() {
53    assert_eq!(sizify(1000, false), "1000");
54    assert_eq!(sizify(1024, false), "1K");
55  }
56
57  #[test]
58  fn test_sizify_larger_values() {
59    assert_eq!(sizify(1048576, false), "1M");
60    assert_eq!(sizify(1073741824, false), "1G");
61    assert_eq!(sizify(1099511627776, false), "1T");
62    assert_eq!(sizify(1125899906842624, false), "1P");
63    assert_eq!(sizify(1152921504606846976, false), "1E");
64  }
65
66  #[test]
67  fn test_sizify_add_i_suffix() {
68    assert_eq!(sizify(1024, true), "1Ki");
69    assert_eq!(sizify(1048576, true), "1Mi");
70    assert_eq!(sizify(1073741824, true), "1Gi");
71  }
72
73  #[test]
74  fn test_sizify_no_i_suffix() {
75    assert_eq!(sizify(1024, false), "1K");
76    assert_eq!(sizify(1048576, false), "1M");
77    assert_eq!(sizify(1073741824, false), "1G");
78  }
79
80  #[test]
81  fn test_sizify_decimal_points() {
82    assert_eq!(sizify(1500, false), "1.47K");
83    assert_eq!(sizify(1500000, false), "1.44M");
84    assert_eq!(sizify(1500000000, false), "1.4G");
85  }
86
87  #[test]
88  fn test_sizify_edge_cases() {
89    assert_eq!(sizify(1, false), "1");
90    assert_eq!(sizify(1023, false), "1023");
91    assert_eq!(sizify(1025, false), "1.01K");
92  }
93}