View Javadoc
1   /*
2    * Copyright 2016-2023 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.util;
6   
7   import java.math.BigInteger;
8   import java.util.Locale;
9   import java.util.concurrent.TimeUnit;
10  
11  import oshi.annotation.concurrent.ThreadSafe;
12  
13  /**
14   * Formatting utility for appending units or converting between number types.
15   */
16  @ThreadSafe
17  public final class FormatUtil {
18      /**
19       * Binary prefixes, used in IEC Standard for naming bytes.
20       * (https://en.wikipedia.org/wiki/International_Electrotechnical_Commission)
21       *
22       * Should be used for most representations of bytes
23       */
24      private static final long KIBI = 1L << 10;
25      private static final long MEBI = 1L << 20;
26      private static final long GIBI = 1L << 30;
27      private static final long TEBI = 1L << 40;
28      private static final long PEBI = 1L << 50;
29      private static final long EXBI = 1L << 60;
30  
31      /**
32       * Decimal prefixes, used for Hz and other metric units and for bytes by hard drive manufacturers
33       */
34      private static final long KILO = 1_000L;
35      private static final long MEGA = 1_000_000L;
36      private static final long GIGA = 1_000_000_000L;
37      private static final long TERA = 1_000_000_000_000L;
38      private static final long PETA = 1_000_000_000_000_000L;
39      private static final long EXA = 1_000_000_000_000_000_000L;
40  
41      /*
42       * Two's complement reference: 2^64.
43       */
44      private static final BigInteger TWOS_COMPLEMENT_REF = BigInteger.ONE.shiftLeft(64);
45  
46      /** Constant <code>HEX_ERROR="0x%08X"</code> */
47      public static final String HEX_ERROR = "0x%08X";
48  
49      private FormatUtil() {
50      }
51  
52      /**
53       * Format bytes into a rounded string representation using IEC standard (matches Mac/Linux). For hard drive
54       * capacities, use @link {@link #formatBytesDecimal(long)}. For Windows displays for KB, MB and GB, in JEDEC units,
55       * edit the returned string to remove the 'i' to display the (incorrect) JEDEC units.
56       *
57       * @param bytes Bytes.
58       * @return Rounded string representation of the byte size.
59       */
60      public static String formatBytes(long bytes) {
61          if (bytes == 1L) { // bytes
62              return String.format(Locale.ROOT, "%d byte", bytes);
63          } else if (bytes < KIBI) { // bytes
64              return String.format(Locale.ROOT, "%d bytes", bytes);
65          } else if (bytes < MEBI) { // KiB
66              return formatUnits(bytes, KIBI, "KiB");
67          } else if (bytes < GIBI) { // MiB
68              return formatUnits(bytes, MEBI, "MiB");
69          } else if (bytes < TEBI) { // GiB
70              return formatUnits(bytes, GIBI, "GiB");
71          } else if (bytes < PEBI) { // TiB
72              return formatUnits(bytes, TEBI, "TiB");
73          } else if (bytes < EXBI) { // PiB
74              return formatUnits(bytes, PEBI, "PiB");
75          } else { // EiB
76              return formatUnits(bytes, EXBI, "EiB");
77          }
78      }
79  
80      /**
81       * Format units as exact integer or fractional decimal based on the prefix, appending the appropriate units
82       *
83       * @param value  The value to format
84       * @param prefix The divisor of the unit multiplier
85       * @param unit   A string representing the units
86       * @return A string with the value
87       */
88      private static String formatUnits(long value, long prefix, String unit) {
89          if (value % prefix == 0) {
90              return String.format(Locale.ROOT, "%d %s", value / prefix, unit);
91          }
92          return String.format(Locale.ROOT, "%.1f %s", (double) value / prefix, unit);
93      }
94  
95      /**
96       * Format bytes into a rounded string representation using decimal SI units. These are used by hard drive
97       * manufacturers for capacity. Most other storage should use {@link #formatBytes(long)}.
98       *
99       * @param bytes Bytes.
100      * @return Rounded string representation of the byte size.
101      */
102     public static String formatBytesDecimal(long bytes) {
103         if (bytes == 1L) { // bytes
104             return String.format(Locale.ROOT, "%d byte", bytes);
105         } else if (bytes < KILO) { // bytes
106             return String.format(Locale.ROOT, "%d bytes", bytes);
107         } else {
108             return formatValue(bytes, "B");
109         }
110     }
111 
112     /**
113      * Format hertz into a string to a rounded string representation.
114      *
115      * @param hertz Hertz.
116      * @return Rounded string representation of the hertz size.
117      */
118     public static String formatHertz(long hertz) {
119         return formatValue(hertz, "Hz");
120     }
121 
122     /**
123      * Format arbitrary units into a string to a rounded string representation.
124      *
125      * @param value The value
126      * @param unit  Units to append metric prefix to
127      * @return Rounded string representation of the value with metric prefix to extension
128      */
129     public static String formatValue(long value, String unit) {
130         if (value < KILO) {
131             return String.format(Locale.ROOT, "%d %s", value, unit).trim();
132         } else if (value < MEGA) { // K
133             return formatUnits(value, KILO, "K" + unit);
134         } else if (value < GIGA) { // M
135             return formatUnits(value, MEGA, "M" + unit);
136         } else if (value < TERA) { // G
137             return formatUnits(value, GIGA, "G" + unit);
138         } else if (value < PETA) { // T
139             return formatUnits(value, TERA, "T" + unit);
140         } else if (value < EXA) { // P
141             return formatUnits(value, PETA, "P" + unit);
142         } else { // E
143             return formatUnits(value, EXA, "E" + unit);
144         }
145     }
146 
147     /**
148      * Formats an elapsed time in seconds as days, hh:mm:ss.
149      *
150      * @param secs Elapsed seconds
151      * @return A string representation of elapsed time
152      */
153     public static String formatElapsedSecs(long secs) {
154         long eTime = secs;
155         final long days = TimeUnit.SECONDS.toDays(eTime);
156         eTime -= TimeUnit.DAYS.toSeconds(days);
157         final long hr = TimeUnit.SECONDS.toHours(eTime);
158         eTime -= TimeUnit.HOURS.toSeconds(hr);
159         final long min = TimeUnit.SECONDS.toMinutes(eTime);
160         eTime -= TimeUnit.MINUTES.toSeconds(min);
161         final long sec = eTime;
162         return String.format(Locale.ROOT, "%d days, %02d:%02d:%02d", days, hr, min, sec);
163     }
164 
165     /**
166      * Convert unsigned int to signed long.
167      *
168      * @param x Signed int representing an unsigned integer
169      * @return long value of x unsigned
170      */
171     public static long getUnsignedInt(int x) {
172         return x & 0x0000_0000_ffff_ffffL;
173     }
174 
175     /**
176      * Represent a 32 bit value as if it were an unsigned integer.
177      *
178      * This is a Java 7 implementation of Java 8's Integer.toUnsignedString.
179      *
180      * @param i a 32 bit value
181      * @return the string representation of the unsigned integer
182      */
183     public static String toUnsignedString(int i) {
184         if (i >= 0) {
185             return Integer.toString(i);
186         }
187         return Long.toString(getUnsignedInt(i));
188     }
189 
190     /**
191      * Represent a 64 bit value as if it were an unsigned long.
192      *
193      * This is a Java 7 implementation of Java 8's Long.toUnsignedString.
194      *
195      * @param l a 64 bit value
196      * @return the string representation of the unsigned long
197      */
198     public static String toUnsignedString(long l) {
199         if (l >= 0) {
200             return Long.toString(l);
201         }
202         return BigInteger.valueOf(l).add(TWOS_COMPLEMENT_REF).toString();
203     }
204 
205     /**
206      * Translate an integer error code to its hex notation
207      *
208      * @param errorCode The error code
209      * @return A string representing the error as 0x....
210      */
211     public static String formatError(int errorCode) {
212         return String.format(Locale.ROOT, HEX_ERROR, errorCode);
213     }
214 
215     /**
216      * Rounds a floating point number to the nearest integer
217      *
218      * @param x the floating point number
219      * @return the integer
220      */
221     public static int roundToInt(double x) {
222         return (int) Math.round(x);
223     }
224 }