datafusion_common/display/
human_readable.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Helpers for rendering sizes, counts, and durations in human readable form.
19
20/// Common data size units
21pub mod units {
22    pub const TB: u64 = 1 << 40;
23    pub const GB: u64 = 1 << 30;
24    pub const MB: u64 = 1 << 20;
25    pub const KB: u64 = 1 << 10;
26}
27
28/// Present size in human-readable form
29pub fn human_readable_size(size: usize) -> String {
30    use units::*;
31
32    let size = size as u64;
33    let (value, unit) = {
34        if size >= 2 * TB {
35            (size as f64 / TB as f64, "TB")
36        } else if size >= 2 * GB {
37            (size as f64 / GB as f64, "GB")
38        } else if size >= 2 * MB {
39            (size as f64 / MB as f64, "MB")
40        } else if size >= 2 * KB {
41            (size as f64 / KB as f64, "KB")
42        } else {
43            (size as f64, "B")
44        }
45    };
46    format!("{value:.1} {unit}")
47}
48
49/// Present count in human-readable form with K, M, B, T suffixes
50pub fn human_readable_count(count: usize) -> String {
51    let count = count as u64;
52    let (value, unit) = {
53        if count >= 1_000_000_000_000 {
54            (count as f64 / 1_000_000_000_000.0, " T")
55        } else if count >= 1_000_000_000 {
56            (count as f64 / 1_000_000_000.0, " B")
57        } else if count >= 1_000_000 {
58            (count as f64 / 1_000_000.0, " M")
59        } else if count >= 1_000 {
60            (count as f64 / 1_000.0, " K")
61        } else {
62            return count.to_string();
63        }
64    };
65
66    // Format with appropriate precision
67    // For values >= 100, show 1 decimal place (e.g., 123.4 K)
68    // For values < 100, show 2 decimal places (e.g., 10.12 K)
69    if value >= 100.0 {
70        format!("{value:.1}{unit}")
71    } else {
72        format!("{value:.2}{unit}")
73    }
74}
75
76/// Present duration in human-readable form with 2 decimal places
77pub fn human_readable_duration(nanos: u64) -> String {
78    const NANOS_PER_SEC: f64 = 1_000_000_000.0;
79    const NANOS_PER_MILLI: f64 = 1_000_000.0;
80    const NANOS_PER_MICRO: f64 = 1_000.0;
81
82    let nanos_f64 = nanos as f64;
83
84    if nanos >= 1_000_000_000 {
85        // >= 1 second: show in seconds
86        format!("{:.2}s", nanos_f64 / NANOS_PER_SEC)
87    } else if nanos >= 1_000_000 {
88        // >= 1 millisecond: show in milliseconds
89        format!("{:.2}ms", nanos_f64 / NANOS_PER_MILLI)
90    } else if nanos >= 1_000 {
91        // >= 1 microsecond: show in microseconds
92        format!("{:.2}µs", nanos_f64 / NANOS_PER_MICRO)
93    } else {
94        // < 1 microsecond: show in nanoseconds
95        format!("{nanos}ns")
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn test_human_readable_count() {
105        assert_eq!(human_readable_count(0), "0");
106        assert_eq!(human_readable_count(1), "1");
107        assert_eq!(human_readable_count(999), "999");
108        assert_eq!(human_readable_count(1_000), "1.00 K");
109        assert_eq!(human_readable_count(10_100), "10.10 K");
110        assert_eq!(human_readable_count(1_532), "1.53 K");
111        assert_eq!(human_readable_count(99_999), "100.00 K");
112        assert_eq!(human_readable_count(1_000_000), "1.00 M");
113        assert_eq!(human_readable_count(1_532_000), "1.53 M");
114        assert_eq!(human_readable_count(99_000_000), "99.00 M");
115        assert_eq!(human_readable_count(123_456_789), "123.5 M");
116        assert_eq!(human_readable_count(1_000_000_000), "1.00 B");
117        assert_eq!(human_readable_count(1_532_000_000), "1.53 B");
118        assert_eq!(human_readable_count(999_999_999_999), "1000.0 B");
119        assert_eq!(human_readable_count(1_000_000_000_000), "1.00 T");
120        assert_eq!(human_readable_count(42_000_000_000_000), "42.00 T");
121    }
122
123    #[test]
124    fn test_human_readable_duration() {
125        assert_eq!(human_readable_duration(0), "0ns");
126        assert_eq!(human_readable_duration(1), "1ns");
127        assert_eq!(human_readable_duration(999), "999ns");
128        assert_eq!(human_readable_duration(1_000), "1.00µs");
129        assert_eq!(human_readable_duration(1_234), "1.23µs");
130        assert_eq!(human_readable_duration(999_999), "1000.00µs");
131        assert_eq!(human_readable_duration(1_000_000), "1.00ms");
132        assert_eq!(human_readable_duration(11_295_377), "11.30ms");
133        assert_eq!(human_readable_duration(1_234_567), "1.23ms");
134        assert_eq!(human_readable_duration(999_999_999), "1000.00ms");
135        assert_eq!(human_readable_duration(1_000_000_000), "1.00s");
136        assert_eq!(human_readable_duration(1_234_567_890), "1.23s");
137        assert_eq!(human_readable_duration(42_000_000_000), "42.00s");
138    }
139}