001/*
002 * Copyright 2023-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2023-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2023-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.util;
037
038
039
040import java.math.BigDecimal;
041import java.math.BigInteger;
042import java.math.RoundingMode;
043
044import static com.unboundid.util.UtilityMessages.*;
045
046
047
048/**
049 * This enum defines a set of size units that can be used to represent data
050 * sizes in varying units (bytes, kilobytes, megabytes, gigabytes, etc.).  This
051 * class uses decimal-based values rather than binary-based values, so each
052 * unit is 1000 times larger than the previous (for example, one kilobyte is
053 * interpreted as 1000 bytes rather than 1024 bytes).
054 */
055public enum DecimalSizeUnit
056{
057  /**
058   * The size unit that represents bytes.
059   */
060  BYTES(BigInteger.valueOf(1L),
061       INFO_SIZE_UNIT_BYTES_SINGULAR.get(),
062       INFO_SIZE_UNIT_BYTES_PLURAL.get(),
063       INFO_SIZE_UNIT_BYTES_ABBREVIATION.get()),
064
065
066
067  /**
068   * The size unit that represents kilobytes.  Each kilobyte is 1024 bytes.
069   */
070  KILOBYTES(BigInteger.valueOf(1_000L),
071       INFO_SIZE_UNIT_KILOBYTES_SINGULAR.get(),
072       INFO_SIZE_UNIT_KILOBYTES_PLURAL.get(),
073       INFO_SIZE_UNIT_KILOBYTES_ABBREVIATION.get()),
074
075
076
077  /**
078   * The size unit that represents megabytes.  Each megabyte is 1024 kilobytes.
079   */
080  MEGABYTES(BigInteger.valueOf(1_000_000L),
081       INFO_SIZE_UNIT_MEGABYTES_SINGULAR.get(),
082       INFO_SIZE_UNIT_MEGABYTES_PLURAL.get(),
083       INFO_SIZE_UNIT_MEGABYTES_ABBREVIATION.get()),
084
085
086
087  /**
088   * The size unit that represents gigabytes.  Each gigabyte is 1024 megabytes.
089   */
090  GIGABYTES(BigInteger.valueOf(1_000_000_000L),
091       INFO_SIZE_UNIT_GIGABYTES_SINGULAR.get(),
092       INFO_SIZE_UNIT_GIGABYTES_PLURAL.get(),
093       INFO_SIZE_UNIT_GIGABYTES_ABBREVIATION.get()),
094
095
096
097  /**
098   * The size unit that represents terabytes.  Each terabyte is 1024 gigabytes.
099   */
100  TERABYTES(BigInteger.valueOf(1_000_000_000_000L),
101       INFO_SIZE_UNIT_TERABYTES_SINGULAR.get(),
102       INFO_SIZE_UNIT_TERABYTES_PLURAL.get(),
103       INFO_SIZE_UNIT_TERABYTES_ABBREVIATION.get()),
104
105
106
107  /**
108   * The size unit that represents petabyte.  Each petabyte is 1024 terabytes.
109   */
110  PETABYTES(BigInteger.valueOf(1_000_000_000_000_000L),
111       INFO_SIZE_UNIT_PETABYTES_SINGULAR.get(),
112       INFO_SIZE_UNIT_PETABYTES_PLURAL.get(),
113       INFO_SIZE_UNIT_PETABYTES_ABBREVIATION.get()),
114
115
116
117  /**
118   * The size unit that represents exabytes.  Each exabyte is 1024 petabytes.
119   */
120  EXABYTES(new BigInteger("1000000000000000000"),
121       INFO_SIZE_UNIT_EXABYTES_SINGULAR.get(),
122       INFO_SIZE_UNIT_EXABYTES_PLURAL.get(),
123       INFO_SIZE_UNIT_EXABYTES_ABBREVIATION.get()),
124
125
126
127  /**
128   * The size unit that represents zettabytes.  Each zettabyte is 1024 exabytes.
129   */
130  ZETTABYTES(new BigInteger("1000000000000000000000"),
131       INFO_SIZE_UNIT_ZETTABYTES_SINGULAR.get(),
132       INFO_SIZE_UNIT_ZETTABYTES_PLURAL.get(),
133       INFO_SIZE_UNIT_ZETTABYTES_ABBREVIATION.get()),
134
135
136
137  /**
138   * The size unit that represents yottabytes.  Each yottabyte is 1024
139   * zettabytes.
140   */
141  YOTTABYTES(new BigInteger("1000000000000000000000000"),
142       INFO_SIZE_UNIT_YOTTABYTES_SINGULAR.get(),
143       INFO_SIZE_UNIT_YOTTABYTES_PLURAL.get(),
144       INFO_SIZE_UNIT_YOTTABYTES_ABBREVIATION.get());
145
146
147
148  // The number of bytes per single instance of this unit.
149  @NotNull private final BigInteger numBytesPerUnit;
150
151  // The abbreviation for the unit.
152  @NotNull private final String abbreviation;
153
154  // The plural name for the unit.
155  @NotNull private final String pluralName;
156
157  // The singular name for the unit.
158  @NotNull private final String singularName;
159
160
161
162  /**
163   * Creates a new size unit with the provided information.
164   *
165   * @param  numBytesPerUnit  The number of bytes per single instance of this
166   *                          size unit.  It must not be {@code null}.
167   * @param  singularName     The name for a single instance of this size unit.
168   *                          It must not be {@code null}.
169   * @param  pluralName       The name for multiple instances of this size unit.
170   *                          It must not be {@code null}.
171   * @param  abbreviation     The abbreviation for multiple instances of this
172   *                          size unit.  It must not be {@code null}.
173   */
174  DecimalSizeUnit(@NotNull final BigInteger numBytesPerUnit,
175                  @NotNull final String singularName,
176                  @NotNull final String pluralName,
177                  @NotNull final String abbreviation)
178  {
179    this.numBytesPerUnit = numBytesPerUnit;
180    this.singularName = singularName;
181    this.pluralName = pluralName;
182    this.abbreviation = abbreviation;
183  }
184
185
186
187  /**
188   * Retrieves the number of bytes per single instance of this size unit.
189   *
190   * @return  The number of bytes per single instance of this size unit.
191   */
192  @NotNull()
193  public BigInteger getNumBytesPerUnit()
194  {
195    return numBytesPerUnit;
196  }
197
198
199
200  /**
201   * Retrieves the singular name for this size unit.
202   *
203   * @return  The singular name for this size unit.
204   */
205  @NotNull()
206  public String getSingularName()
207  {
208    return singularName;
209  }
210
211
212
213  /**
214   * Retrieves the plural name for this size unit.
215   *
216   * @return  The plural name for this size unit.
217   */
218  @NotNull()
219  public String getPluralName()
220  {
221    return pluralName;
222  }
223
224
225
226  /**
227   * Retrieves the abbreviation for this size unit.
228   *
229   * @return  The abbreviation for this size unit.
230   */
231  @NotNull()
232  public String getAbbreviation()
233  {
234    return abbreviation;
235  }
236
237
238
239  /**
240   * Retrieves the number of bytes in the specified number of instances of this
241   * size unit.
242   *
243   * @param  value  The number of instances of this unit to convert to bytes.
244   *
245   * @return  The number of bytes in the specified number of instances of this
246   *          size unit.
247   */
248  @NotNull()
249  public BigInteger toBytes(final long value)
250  {
251    return toBytes(BigInteger.valueOf(value));
252  }
253
254
255
256  /**
257   * Retrieves the number of bytes in the specified number of instances of this
258   * size unit.
259   *
260   * @param  value  The number of instances of this unit to convert to bytes.
261   *                It must not be {@code null}.
262   *
263   * @return  The number of bytes in the specified number of instances of this
264   *          size unit.
265   */
266  @NotNull()
267  public BigInteger toBytes(@NotNull final BigInteger value)
268  {
269    return numBytesPerUnit.multiply(value);
270  }
271
272
273
274  /**
275   * Retrieves the number of bytes in the specified number of instances of this
276   * size unit, rounded to the nearest integer.
277   *
278   * @param  value  The number of instances of this unit to convert to bytes.
279   *
280   * @return  The number of bytes in the specified number of instances of this
281   *          size unit.
282   */
283  @NotNull()
284  public BigInteger toBytes(final double value)
285  {
286    return toBytes(BigDecimal.valueOf(value));
287  }
288
289
290
291  /**
292   * Retrieves the number of bytes in the specified number of instances of this
293   * size unit, rounded to the nearest integer.
294   *
295   * @param  value  The number of instances of this unit to convert to bytes.
296   *                It must not be {@code null}.
297   *
298   * @return  The number of bytes in the specified number of instances of this
299   *          size unit.
300   */
301  @NotNull()
302  public BigInteger toBytes(@NotNull final BigDecimal value)
303  {
304    final BigDecimal numBytesPerUnitAsBigDecimal =
305         new BigDecimal(numBytesPerUnit);
306    final BigDecimal numBytesBigDecimal =
307         numBytesPerUnitAsBigDecimal.multiply(value);
308    final BigDecimal roundedBigDecimal =
309         numBytesBigDecimal.setScale(0, RoundingMode.HALF_UP);
310    return roundedBigDecimal.toBigInteger();
311  }
312
313
314
315  /**
316   * Retrieves the number of instances of this unit represented by the
317   * specified number of bytes.
318   *
319   * @param  numBytes  The number of bytes to use to make the determination.
320   *
321   * @return  The number of instances of this unit represented by the specified
322   *          number of bytes.
323   */
324  @NotNull()
325  public BigDecimal fromBytes(final long numBytes)
326  {
327    return fromBytes(BigInteger.valueOf(numBytes));
328  }
329
330
331
332  /**
333   * Retrieves the number of instances of this unit represented by the
334   * specified number of bytes.
335   *
336   * @param  numBytes  The number of bytes to use to make the determination.  It
337   *                   must not be {@code null}.
338   *
339   * @return  The number of instances of this unit represented by the specified
340   *          number of bytes.
341   */
342  @NotNull()
343  public BigDecimal fromBytes(@NotNull final BigInteger numBytes)
344  {
345    final BigDecimal numBytesPerUnitAsBigDecimal =
346         new BigDecimal(numBytesPerUnit);
347    final BigDecimal numBytesAsBigDecimal = new BigDecimal(numBytes);
348    return numBytesAsBigDecimal.divide(numBytesPerUnitAsBigDecimal);
349  }
350
351
352
353  /**
354   * Retrieves a string that represents a human-readable representation of the
355   * specified number of bytes.  The string representation will be constructed
356   * in accordance with the following rules:
357   * <UL>
358   *   <LI>
359   *     The string representation will use the abbreviation for the unit (e.g.,
360   *     "b" instead of "bytes", "KB" instead of kilobytes, etc.)
361   *   </LI>
362   *   <LI>
363   *     If the provided value represents an exact multiple of the number of
364   *     bytes for a given unit, then the string representation will be an
365   *     integer followed by the abbreviation for the unit (e.g., a value of
366   *     123 will result in a string representation of "123b", a value of
367   *     524880 will result in a string representation of "5MB", a value of
368   *     7516192768 will result in a string representation of "7GB", etc.).
369   *   </LI>
370   *   <LI>
371   *     If the provided value does not represent an exact multiple of the
372   *     number of bytes for the given unit, then the string representation will
373   *     use a floating-point number with two digits behind the decimal point.
374   *     It will select the unit so that when possible, there will be between
375   *     1 and 3 digits before the decimal point (e.g., a value of 12345 will
376   *     result in a string representation of "12.06KB", a value of
377   *     9876543210 will result in a string representation of "9.20GB", etc.).
378   *   </LI>
379   * </UL>
380   *
381   * @param  numBytes  The number of bytes to represent as a human-readable
382   *                   size.  It must be greater than or equal to zero.
383   *
384   * @return  A string that represents a human-readable representation of the
385   *          specified number of bytes.
386   */
387  @NotNull()
388  public static String bytesToHumanReadableSize(final long numBytes)
389  {
390    return bytesToHumanReadableSize(BigInteger.valueOf(numBytes));
391  }
392
393
394
395  /**
396   * Retrieves a string that represents a human-readable representation of the
397   * specified number of bytes.  The string representation will be constructed
398   * in accordance with the following rules:
399   * <UL>
400   *   <LI>
401   *     The string representation will use the abbreviation for the unit (e.g.,
402   *     "B" instead of "bytes", "KB" instead of kilobytes, etc.)
403   *   </LI>
404   *   <LI>
405   *     The string representation
406   *     The string representation will use the abbreviation for the unit (e.g.,
407   *     "B" instead of "bytes", "KB" instead of kilobytes, etc.)
408   *   </LI>
409   *   <LI>
410   *     If the provided value represents an exact multiple of the number of
411   *     bytes for the selected unit, then the string representation will be an
412   *     integer followed by the abbreviation for the unit (e.g., a value of
413   *     123 will result in a string representation of "123B", a value of
414   *     524880 will result in a string representation of "5MB", a value of
415   *     7516192768 will result in a string representation of "7GB", etc.).
416   *   </LI>
417   *   <LI>
418   *     If the provided value does not represent an exact multiple of the
419   *     number of bytes for the selected unit, then the string representation
420   *     will use a floating-point number with two digits behind the decimal
421   *     point (e.g., a value of 12345 will result in a string representation of
422   *     "12.06KB", a value of 9876543210 will result in a string representation
423   *     of "9.20GB", etc.).
424   *   </LI>
425   * </UL>
426   *
427   * @param  numBytes  The number of bytes to represent as a human-readable
428   *                   size.  It must not be {@code null}, and it must represent
429   *                   a value that is greater than or equal to zero.
430   *
431   * @return  A string that represents a human-readable representation of the
432   *          specified number of bytes.
433   */
434  @NotNull()
435  public static String bytesToHumanReadableSize(
436              @NotNull final BigInteger numBytes)
437  {
438    Validator.ensureTrue((numBytes.compareTo(BigInteger.ZERO) >= 0),
439         "DecimalSizeUnits.bytesToHumanReadableSize.numBytes must be greater " +
440              "than or equal to zero.");
441
442
443    // Find the smallest unit whose numBytesPerUnit is greater than or equal
444    // to the given value.
445    DecimalSizeUnit selectedUnit = null;
446    final DecimalSizeUnit[] values = values();
447    for (int i=(values.length - 1); i >= 0; i--)
448    {
449      final DecimalSizeUnit unit = values[i];
450      if (numBytes.compareTo(unit.numBytesPerUnit) >= 0)
451      {
452        selectedUnit = unit;
453        break;
454      }
455    }
456
457
458    // Check to see if we ended up without a selected unit (which should only
459    // happen if the provided unit was zero).  In that case, we'll default to
460    // a unit of bytes.
461    if (selectedUnit == null)
462    {
463      return numBytes + BYTES.abbreviation;
464    }
465
466
467    // Check to see if the provided value is an exact multiple of the number of
468    // bytes per instance of the selected unit.  If so, then represent the value
469    // as an integer followed by the unit abbreviation.
470    if (numBytes.remainder(selectedUnit.numBytesPerUnit).equals(
471         BigInteger.ZERO))
472    {
473      return numBytes.divide(selectedUnit.numBytesPerUnit) +
474           selectedUnit.abbreviation;
475    }
476
477
478    // Compute the number of instances of the given unit needed to represent
479    // the provided value.
480    final BigDecimal numBytesAsBigDecimal = new BigDecimal(numBytes);
481    final BigDecimal numBytesPerUnitAsBigDecimal =
482         new BigDecimal(selectedUnit.numBytesPerUnit);
483    final BigDecimal numUnitsPerValueAsBigDecimal =
484         numBytesAsBigDecimal.divide(numBytesPerUnitAsBigDecimal, 2,
485              RoundingMode.HALF_UP);
486    return numUnitsPerValueAsBigDecimal.toString() + selectedUnit.abbreviation;
487  }
488
489
490
491  /**
492   * Retrieves the decimal size unit value that has the given name as either its
493   * singular name, plural name, or abbreviation, in a case-insensitive manner.
494   *
495   *
496   * @param  name  The name for which to retrieve the decimal size unit value.
497   *               It must not be {@code null}.
498   *
499   * @return  The decimal size unit value for the given name, or {@code null} if
500   *          no value has a singular name, plural name, or abbreviation that
501   *          matches the provided name in a case-insensitive manner.
502   */
503  @Nullable()
504  public static DecimalSizeUnit forName(@NotNull final String name)
505  {
506    for (final DecimalSizeUnit unit : values())
507    {
508      if (name.equalsIgnoreCase(unit.name()) ||
509           name.equalsIgnoreCase(unit.singularName) ||
510           name.equalsIgnoreCase(unit.pluralName) ||
511           name.equalsIgnoreCase(unit.abbreviation))
512      {
513        return unit;
514      }
515    }
516
517    return null;
518  }
519}