diff --git a/docs/release-notes.html b/docs/release-notes.html
index 15c4b5e94..639ad7098 100644
--- a/docs/release-notes.html
+++ b/docs/release-notes.html
@@ -38,6 +38,22 @@
Version 6.0.11
+
+ Added new BinarySizeUnit and DecimalSizeUnit enums that can be used when working
+ with data sizes in a variety of units, including bytes, kilobytes, megabytes,
+ gigabytes, terabytes, petabytes, exabytes, zettabytes, and yottabytes. These
+ enums provide methods for determining the number of bytes in a specified number
+ of instances of the given unit, determining the number of instances of a unit
+ represented by a given number of bytes, and generating a human-readable string
+ representation of a given number of bytes using the unit deemed most appropriate.
+ The BinarySizeUnit enum assumes that each unit is 1024 times larger than the next
+ smaller unit (e.g., so one kilobyte is 1024 bytes, one megabyte is 1024
+ kilobytes, etc.), while the DecimalSizeUnit enum assumes that each unit is 1000
+ times larger than the next smaller unit (e.g., so one kilobyte is 1000 bytes, one
+ megabyte is 1000 kilobytes, etc.).
+
+
+
Updated client-side support for the LDIF export administrative task in the Ping
Identity Directory Server to allow requesting that the server invoke one or more
diff --git a/messages/unboundid-ldapsdk-util.properties b/messages/unboundid-ldapsdk-util.properties
index 14f691efe..224f55ded 100644
--- a/messages/unboundid-ldapsdk-util.properties
+++ b/messages/unboundid-ldapsdk-util.properties
@@ -1253,3 +1253,30 @@ ERR_GET_NON_FIPS_BC_CLASS_LOADER_NO_JARS_FOUND=Unable to create a class \
ERR_PROXY_SF_CANNOT_CREATE_UNCONNECTED_SOCKET=Unable to create an unconnected \
socket for communication through a proxy server when an SSLSocketFactory \
has been configured.
+INFO_SIZE_UNIT_BYTES_SINGULAR=byte
+INFO_SIZE_UNIT_BYTES_PLURAL=bytes
+INFO_SIZE_UNIT_BYTES_ABBREVIATION=B
+INFO_SIZE_UNIT_KILOBYTES_SINGULAR=kilobyte
+INFO_SIZE_UNIT_KILOBYTES_PLURAL=kilobytes
+INFO_SIZE_UNIT_KILOBYTES_ABBREVIATION=KB
+INFO_SIZE_UNIT_MEGABYTES_SINGULAR=megabyte
+INFO_SIZE_UNIT_MEGABYTES_PLURAL=megabytes
+INFO_SIZE_UNIT_MEGABYTES_ABBREVIATION=MB
+INFO_SIZE_UNIT_GIGABYTES_SINGULAR=gigabyte
+INFO_SIZE_UNIT_GIGABYTES_PLURAL=gigabytes
+INFO_SIZE_UNIT_GIGABYTES_ABBREVIATION=GB
+INFO_SIZE_UNIT_TERABYTES_SINGULAR=terabyte
+INFO_SIZE_UNIT_TERABYTES_PLURAL=terabytes
+INFO_SIZE_UNIT_TERABYTES_ABBREVIATION=TB
+INFO_SIZE_UNIT_PETABYTES_SINGULAR=petabyte
+INFO_SIZE_UNIT_PETABYTES_PLURAL=petabytes
+INFO_SIZE_UNIT_PETABYTES_ABBREVIATION=PB
+INFO_SIZE_UNIT_EXABYTES_SINGULAR=exabyte
+INFO_SIZE_UNIT_EXABYTES_PLURAL=exabytes
+INFO_SIZE_UNIT_EXABYTES_ABBREVIATION=EB
+INFO_SIZE_UNIT_ZETTABYTES_SINGULAR=zettabyte
+INFO_SIZE_UNIT_ZETTABYTES_PLURAL=zettabytes
+INFO_SIZE_UNIT_ZETTABYTES_ABBREVIATION=ZB
+INFO_SIZE_UNIT_YOTTABYTES_SINGULAR=yottabyte
+INFO_SIZE_UNIT_YOTTABYTES_PLURAL=yottabytes
+INFO_SIZE_UNIT_YOTTABYTES_ABBREVIATION=YB
diff --git a/src/com/unboundid/util/BinarySizeUnit.java b/src/com/unboundid/util/BinarySizeUnit.java
new file mode 100644
index 000000000..e9f051b61
--- /dev/null
+++ b/src/com/unboundid/util/BinarySizeUnit.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright 2023 Ping Identity Corporation
+ * All Rights Reserved.
+ */
+/*
+ * Copyright 2023 Ping Identity Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (C) 2023 Ping Identity Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (GPLv2 only)
+ * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+package com.unboundid.util;
+
+
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+
+import static com.unboundid.util.UtilityMessages.*;
+
+
+
+/**
+ * This enum defines a set of size units that can be used to represent data
+ * sizes in varying units (bytes, kilobytes, megabytes, gigabytes, etc.). This
+ * class uses binary-based values rather than decimal-based values, so each
+ * unit is 1024 times larger than the previous (for example, one kilobyte is
+ * interpreted as 1024 bytes rather than 1000 bytes).
+ */
+public enum BinarySizeUnit
+{
+ /**
+ * The size unit that represents bytes.
+ */
+ BYTES(BigInteger.valueOf(1L),
+ INFO_SIZE_UNIT_BYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_BYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_BYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents kilobytes. Each kilobyte is 1024 bytes.
+ */
+ KILOBYTES(BigInteger.valueOf(1_024L),
+ INFO_SIZE_UNIT_KILOBYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_KILOBYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_KILOBYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents megabytes. Each megabyte is 1024 kilobytes.
+ */
+ MEGABYTES(BigInteger.valueOf(1_048_576L),
+ INFO_SIZE_UNIT_MEGABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_MEGABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_MEGABYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents gigabytes. Each gigabyte is 1024 megabytes.
+ */
+ GIGABYTES(BigInteger.valueOf(1_073_741_824L),
+ INFO_SIZE_UNIT_GIGABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_GIGABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_GIGABYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents terabytes. Each terabyte is 1024 gigabytes.
+ */
+ TERABYTES(BigInteger.valueOf(1_099_511_627_776L),
+ INFO_SIZE_UNIT_TERABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_TERABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_TERABYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents petabyte. Each petabyte is 1024 terabytes.
+ */
+ PETABYTES(BigInteger.valueOf(1_125_899_906_842_624L),
+ INFO_SIZE_UNIT_PETABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_PETABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_PETABYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents exabytes. Each exabyte is 1024 petabytes.
+ */
+ EXABYTES(new BigInteger("1152921504606846976"),
+ INFO_SIZE_UNIT_EXABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_EXABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_EXABYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents zettabytes. Each zettabyte is 1024 exabytes.
+ */
+ ZETTABYTES(new BigInteger("1180591620717411303424"),
+ INFO_SIZE_UNIT_ZETTABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_ZETTABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_ZETTABYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents yottabytes. Each yottabyte is 1024
+ * zettabytes.
+ */
+ YOTTABYTES(new BigInteger("1208925819614629174706176"),
+ INFO_SIZE_UNIT_YOTTABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_YOTTABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_YOTTABYTES_ABBREVIATION.get());
+
+
+
+ // The number of bytes per single instance of this unit.
+ @NotNull private final BigInteger numBytesPerUnit;
+
+ // The abbreviation for the unit.
+ @NotNull private final String abbreviation;
+
+ // The plural name for the unit.
+ @NotNull private final String pluralName;
+
+ // The singular name for the unit.
+ @NotNull private final String singularName;
+
+
+
+ /**
+ * Creates a new size unit with the provided information.
+ *
+ * @param numBytesPerUnit The number of bytes per single instance of this
+ * size unit. It must not be {@code null}.
+ * @param singularName The name for a single instance of this size unit.
+ * It must not be {@code null}.
+ * @param pluralName The name for multiple instances of this size unit.
+ * It must not be {@code null}.
+ * @param abbreviation The abbreviation for multiple instances of this
+ * size unit. It must not be {@code null}.
+ */
+ BinarySizeUnit(@NotNull final BigInteger numBytesPerUnit,
+ @NotNull final String singularName,
+ @NotNull final String pluralName,
+ @NotNull final String abbreviation)
+ {
+ this.numBytesPerUnit = numBytesPerUnit;
+ this.singularName = singularName;
+ this.pluralName = pluralName;
+ this.abbreviation = abbreviation;
+ }
+
+
+
+ /**
+ * Retrieves the number of bytes per single instance of this size unit.
+ *
+ * @return The number of bytes per single instance of this size unit.
+ */
+ @NotNull()
+ public BigInteger getNumBytesPerUnit()
+ {
+ return numBytesPerUnit;
+ }
+
+
+
+ /**
+ * Retrieves the singular name for this size unit.
+ *
+ * @return The singular name for this size unit.
+ */
+ @NotNull()
+ public String getSingularName()
+ {
+ return singularName;
+ }
+
+
+
+ /**
+ * Retrieves the plural name for this size unit.
+ *
+ * @return The plural name for this size unit.
+ */
+ @NotNull()
+ public String getPluralName()
+ {
+ return pluralName;
+ }
+
+
+
+ /**
+ * Retrieves the abbreviation for this size unit.
+ *
+ * @return The abbreviation for this size unit.
+ */
+ @NotNull()
+ public String getAbbreviation()
+ {
+ return abbreviation;
+ }
+
+
+
+ /**
+ * Retrieves the number of bytes in the specified number of instances of this
+ * size unit.
+ *
+ * @param value The number of instances of this unit to convert to bytes.
+ *
+ * @return The number of bytes in the specified number of instances of this
+ * size unit.
+ */
+ @NotNull()
+ public BigInteger toBytes(final long value)
+ {
+ return toBytes(BigInteger.valueOf(value));
+ }
+
+
+
+ /**
+ * Retrieves the number of bytes in the specified number of instances of this
+ * size unit.
+ *
+ * @param value The number of instances of this unit to convert to bytes.
+ * It must not be {@code null}.
+ *
+ * @return The number of bytes in the specified number of instances of this
+ * size unit.
+ */
+ @NotNull()
+ public BigInteger toBytes(@NotNull final BigInteger value)
+ {
+ return numBytesPerUnit.multiply(value);
+ }
+
+
+
+ /**
+ * Retrieves the number of bytes in the specified number of instances of this
+ * size unit, rounded to the nearest integer.
+ *
+ * @param value The number of instances of this unit to convert to bytes.
+ *
+ * @return The number of bytes in the specified number of instances of this
+ * size unit.
+ */
+ @NotNull()
+ public BigInteger toBytes(final double value)
+ {
+ return toBytes(BigDecimal.valueOf(value));
+ }
+
+
+
+ /**
+ * Retrieves the number of bytes in the specified number of instances of this
+ * size unit, rounded to the nearest integer.
+ *
+ * @param value The number of instances of this unit to convert to bytes.
+ * It must not be {@code null}.
+ *
+ * @return The number of bytes in the specified number of instances of this
+ * size unit.
+ */
+ @NotNull()
+ public BigInteger toBytes(@NotNull final BigDecimal value)
+ {
+ final BigDecimal numBytesPerUnitAsBigDecimal =
+ new BigDecimal(numBytesPerUnit);
+ final BigDecimal numBytesBigDecimal =
+ numBytesPerUnitAsBigDecimal.multiply(value);
+ final BigDecimal roundedBigDecimal =
+ numBytesBigDecimal.setScale(0, RoundingMode.HALF_UP);
+ return roundedBigDecimal.toBigInteger();
+ }
+
+
+
+ /**
+ * Retrieves the number of instances of this unit represented by the
+ * specified number of bytes.
+ *
+ * @param numBytes The number of bytes to use to make the determination.
+ *
+ * @return The number of instances of this unit represented by the specified
+ * number of bytes.
+ */
+ @NotNull()
+ public BigDecimal fromBytes(final long numBytes)
+ {
+ return fromBytes(BigInteger.valueOf(numBytes));
+ }
+
+
+
+ /**
+ * Retrieves the number of instances of this unit represented by the
+ * specified number of bytes.
+ *
+ * @param numBytes The number of bytes to use to make the determination. It
+ * must not be {@code null}.
+ *
+ * @return The number of instances of this unit represented by the specified
+ * number of bytes.
+ */
+ @NotNull()
+ public BigDecimal fromBytes(@NotNull final BigInteger numBytes)
+ {
+ final BigDecimal numBytesPerUnitAsBigDecimal =
+ new BigDecimal(numBytesPerUnit);
+ final BigDecimal numBytesAsBigDecimal = new BigDecimal(numBytes);
+ return numBytesAsBigDecimal.divide(numBytesPerUnitAsBigDecimal);
+ }
+
+
+
+ /**
+ * Retrieves a string that represents a human-readable representation of the
+ * specified number of bytes. The string representation will be constructed
+ * in accordance with the following rules:
+ *
+ * -
+ * The string representation will use the abbreviation for the unit (e.g.,
+ * "b" instead of "bytes", "KB" instead of kilobytes, etc.)
+ *
+ * -
+ * If the provided value represents an exact multiple of the number of
+ * bytes for a given unit, then the string representation will be an
+ * integer followed by the abbreviation for the unit (e.g., a value of
+ * 123 will result in a string representation of "123b", a value of
+ * 524880 will result in a string representation of "5MB", a value of
+ * 7516192768 will result in a string representation of "7GB", etc.).
+ *
+ * -
+ * If the provided value does not represent an exact multiple of the
+ * number of bytes for the given unit, then the string representation will
+ * use a floating-point number with two digits behind the decimal point.
+ * It will select the unit so that when possible, there will be between
+ * 1 and 3 digits before the decimal point (e.g., a value of 12345 will
+ * result in a string representation of "12.06KB", a value of
+ * 9876543210 will result in a string representation of "9.20GB", etc.).
+ *
+ *
+ *
+ * @param numBytes The number of bytes to represent as a human-readable
+ * size. It must be greater than or equal to zero.
+ *
+ * @return A string that represents a human-readable representation of the
+ * specified number of bytes.
+ */
+ @NotNull()
+ public static String bytesToHumanReadableSize(final long numBytes)
+ {
+ return bytesToHumanReadableSize(BigInteger.valueOf(numBytes));
+ }
+
+
+
+ /**
+ * Retrieves a string that represents a human-readable representation of the
+ * specified number of bytes. The string representation will be constructed
+ * in accordance with the following rules:
+ *
+ * -
+ * The string representation will use the abbreviation for the unit (e.g.,
+ * "B" instead of "bytes", "KB" instead of kilobytes, etc.)
+ *
+ * -
+ * The string representation
+ * The string representation will use the abbreviation for the unit (e.g.,
+ * "B" instead of "bytes", "KB" instead of kilobytes, etc.)
+ *
+ * -
+ * If the provided value represents an exact multiple of the number of
+ * bytes for the selected unit, then the string representation will be an
+ * integer followed by the abbreviation for the unit (e.g., a value of
+ * 123 will result in a string representation of "123B", a value of
+ * 524880 will result in a string representation of "5MB", a value of
+ * 7516192768 will result in a string representation of "7GB", etc.).
+ *
+ * -
+ * If the provided value does not represent an exact multiple of the
+ * number of bytes for the selected unit, then the string representation
+ * will use a floating-point number with two digits behind the decimal
+ * point (e.g., a value of 12345 will result in a string representation of
+ * "12.06KB", a value of 9876543210 will result in a string representation
+ * of "9.20GB", etc.).
+ *
+ *
+ *
+ * @param numBytes The number of bytes to represent as a human-readable
+ * size. It must not be {@code null}, and it must represent
+ * a value that is greater than or equal to zero.
+ *
+ * @return A string that represents a human-readable representation of the
+ * specified number of bytes.
+ */
+ @NotNull()
+ public static String bytesToHumanReadableSize(
+ @NotNull final BigInteger numBytes)
+ {
+ Validator.ensureTrue((numBytes.compareTo(BigInteger.ZERO) >= 0),
+ "BinarySizeUnits.bytesToHumanReadableSize.numBytes must be greater " +
+ "than or equal to zero.");
+
+
+ // Find the smallest unit whose numBytesPerUnit is greater than or equal
+ // to the given value.
+ BinarySizeUnit selectedUnit = null;
+ final BinarySizeUnit[] values = values();
+ for (int i=(values.length - 1); i >= 0; i--)
+ {
+ final BinarySizeUnit unit = values[i];
+ if (numBytes.compareTo(unit.numBytesPerUnit) >= 0)
+ {
+ selectedUnit = unit;
+ break;
+ }
+ }
+
+
+ // Check to see if we ended up without a selected unit (which should only
+ // happen if the provided unit was zero). In that case, we'll default to
+ // a unit of bytes.
+ if (selectedUnit == null)
+ {
+ return numBytes + BYTES.abbreviation;
+ }
+
+
+ // Check to see if the provided value is an exact multiple of the number of
+ // bytes per instance of the selected unit. If so, then represent the value
+ // as an integer followed by the unit abbreviation.
+ if (numBytes.remainder(selectedUnit.numBytesPerUnit).equals(
+ BigInteger.ZERO))
+ {
+ return numBytes.divide(selectedUnit.numBytesPerUnit) +
+ selectedUnit.abbreviation;
+ }
+
+
+ // Compute the number of instances of the given unit needed to represent
+ // the provided value.
+ final BigDecimal numBytesAsBigDecimal = new BigDecimal(numBytes);
+ final BigDecimal numBytesPerUnitAsBigDecimal =
+ new BigDecimal(selectedUnit.numBytesPerUnit);
+ final BigDecimal numUnitsPerValueAsBigDecimal =
+ numBytesAsBigDecimal.divide(numBytesPerUnitAsBigDecimal, 2,
+ RoundingMode.HALF_UP);
+ return numUnitsPerValueAsBigDecimal.toString() + selectedUnit.abbreviation;
+ }
+
+
+
+ /**
+ * Retrieves the binary size unit value that has the given name as either its
+ * singular name, plural name, or abbreviation, in a case-insensitive manner.
+ *
+ *
+ * @param name The name for which to retrieve the binary size unit value.
+ * It must not be {@code null}.
+ *
+ * @return The binary size unit value for the given name, or {@code null} if
+ * no value has a singular name, plural name, or abbreviation that
+ * matches the provided name in a case-insensitive manner.
+ */
+ @Nullable()
+ public static BinarySizeUnit forName(@NotNull final String name)
+ {
+ for (final BinarySizeUnit unit : values())
+ {
+ if (name.equalsIgnoreCase(unit.name()) ||
+ name.equalsIgnoreCase(unit.singularName) ||
+ name.equalsIgnoreCase(unit.pluralName) ||
+ name.equalsIgnoreCase(unit.abbreviation))
+ {
+ return unit;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/com/unboundid/util/DecimalSizeUnit.java b/src/com/unboundid/util/DecimalSizeUnit.java
new file mode 100644
index 000000000..656d1e762
--- /dev/null
+++ b/src/com/unboundid/util/DecimalSizeUnit.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright 2023 Ping Identity Corporation
+ * All Rights Reserved.
+ */
+/*
+ * Copyright 2023 Ping Identity Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (C) 2023 Ping Identity Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (GPLv2 only)
+ * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+package com.unboundid.util;
+
+
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+
+import static com.unboundid.util.UtilityMessages.*;
+
+
+
+/**
+ * This enum defines a set of size units that can be used to represent data
+ * sizes in varying units (bytes, kilobytes, megabytes, gigabytes, etc.). This
+ * class uses decimal-based values rather than binary-based values, so each
+ * unit is 1000 times larger than the previous (for example, one kilobyte is
+ * interpreted as 1000 bytes rather than 1024 bytes).
+ */
+public enum DecimalSizeUnit
+{
+ /**
+ * The size unit that represents bytes.
+ */
+ BYTES(BigInteger.valueOf(1L),
+ INFO_SIZE_UNIT_BYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_BYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_BYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents kilobytes. Each kilobyte is 1024 bytes.
+ */
+ KILOBYTES(BigInteger.valueOf(1_000L),
+ INFO_SIZE_UNIT_KILOBYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_KILOBYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_KILOBYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents megabytes. Each megabyte is 1024 kilobytes.
+ */
+ MEGABYTES(BigInteger.valueOf(1_000_000L),
+ INFO_SIZE_UNIT_MEGABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_MEGABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_MEGABYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents gigabytes. Each gigabyte is 1024 megabytes.
+ */
+ GIGABYTES(BigInteger.valueOf(1_000_000_000L),
+ INFO_SIZE_UNIT_GIGABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_GIGABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_GIGABYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents terabytes. Each terabyte is 1024 gigabytes.
+ */
+ TERABYTES(BigInteger.valueOf(1_000_000_000_000L),
+ INFO_SIZE_UNIT_TERABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_TERABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_TERABYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents petabyte. Each petabyte is 1024 terabytes.
+ */
+ PETABYTES(BigInteger.valueOf(1_000_000_000_000_000L),
+ INFO_SIZE_UNIT_PETABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_PETABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_PETABYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents exabytes. Each exabyte is 1024 petabytes.
+ */
+ EXABYTES(new BigInteger("1000000000000000000"),
+ INFO_SIZE_UNIT_EXABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_EXABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_EXABYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents zettabytes. Each zettabyte is 1024 exabytes.
+ */
+ ZETTABYTES(new BigInteger("1000000000000000000000"),
+ INFO_SIZE_UNIT_ZETTABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_ZETTABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_ZETTABYTES_ABBREVIATION.get()),
+
+
+
+ /**
+ * The size unit that represents yottabytes. Each yottabyte is 1024
+ * zettabytes.
+ */
+ YOTTABYTES(new BigInteger("1000000000000000000000000"),
+ INFO_SIZE_UNIT_YOTTABYTES_SINGULAR.get(),
+ INFO_SIZE_UNIT_YOTTABYTES_PLURAL.get(),
+ INFO_SIZE_UNIT_YOTTABYTES_ABBREVIATION.get());
+
+
+
+ // The number of bytes per single instance of this unit.
+ @NotNull private final BigInteger numBytesPerUnit;
+
+ // The abbreviation for the unit.
+ @NotNull private final String abbreviation;
+
+ // The plural name for the unit.
+ @NotNull private final String pluralName;
+
+ // The singular name for the unit.
+ @NotNull private final String singularName;
+
+
+
+ /**
+ * Creates a new size unit with the provided information.
+ *
+ * @param numBytesPerUnit The number of bytes per single instance of this
+ * size unit. It must not be {@code null}.
+ * @param singularName The name for a single instance of this size unit.
+ * It must not be {@code null}.
+ * @param pluralName The name for multiple instances of this size unit.
+ * It must not be {@code null}.
+ * @param abbreviation The abbreviation for multiple instances of this
+ * size unit. It must not be {@code null}.
+ */
+ DecimalSizeUnit(@NotNull final BigInteger numBytesPerUnit,
+ @NotNull final String singularName,
+ @NotNull final String pluralName,
+ @NotNull final String abbreviation)
+ {
+ this.numBytesPerUnit = numBytesPerUnit;
+ this.singularName = singularName;
+ this.pluralName = pluralName;
+ this.abbreviation = abbreviation;
+ }
+
+
+
+ /**
+ * Retrieves the number of bytes per single instance of this size unit.
+ *
+ * @return The number of bytes per single instance of this size unit.
+ */
+ @NotNull()
+ public BigInteger getNumBytesPerUnit()
+ {
+ return numBytesPerUnit;
+ }
+
+
+
+ /**
+ * Retrieves the singular name for this size unit.
+ *
+ * @return The singular name for this size unit.
+ */
+ @NotNull()
+ public String getSingularName()
+ {
+ return singularName;
+ }
+
+
+
+ /**
+ * Retrieves the plural name for this size unit.
+ *
+ * @return The plural name for this size unit.
+ */
+ @NotNull()
+ public String getPluralName()
+ {
+ return pluralName;
+ }
+
+
+
+ /**
+ * Retrieves the abbreviation for this size unit.
+ *
+ * @return The abbreviation for this size unit.
+ */
+ @NotNull()
+ public String getAbbreviation()
+ {
+ return abbreviation;
+ }
+
+
+
+ /**
+ * Retrieves the number of bytes in the specified number of instances of this
+ * size unit.
+ *
+ * @param value The number of instances of this unit to convert to bytes.
+ *
+ * @return The number of bytes in the specified number of instances of this
+ * size unit.
+ */
+ @NotNull()
+ public BigInteger toBytes(final long value)
+ {
+ return toBytes(BigInteger.valueOf(value));
+ }
+
+
+
+ /**
+ * Retrieves the number of bytes in the specified number of instances of this
+ * size unit.
+ *
+ * @param value The number of instances of this unit to convert to bytes.
+ * It must not be {@code null}.
+ *
+ * @return The number of bytes in the specified number of instances of this
+ * size unit.
+ */
+ @NotNull()
+ public BigInteger toBytes(@NotNull final BigInteger value)
+ {
+ return numBytesPerUnit.multiply(value);
+ }
+
+
+
+ /**
+ * Retrieves the number of bytes in the specified number of instances of this
+ * size unit, rounded to the nearest integer.
+ *
+ * @param value The number of instances of this unit to convert to bytes.
+ *
+ * @return The number of bytes in the specified number of instances of this
+ * size unit.
+ */
+ @NotNull()
+ public BigInteger toBytes(final double value)
+ {
+ return toBytes(BigDecimal.valueOf(value));
+ }
+
+
+
+ /**
+ * Retrieves the number of bytes in the specified number of instances of this
+ * size unit, rounded to the nearest integer.
+ *
+ * @param value The number of instances of this unit to convert to bytes.
+ * It must not be {@code null}.
+ *
+ * @return The number of bytes in the specified number of instances of this
+ * size unit.
+ */
+ @NotNull()
+ public BigInteger toBytes(@NotNull final BigDecimal value)
+ {
+ final BigDecimal numBytesPerUnitAsBigDecimal =
+ new BigDecimal(numBytesPerUnit);
+ final BigDecimal numBytesBigDecimal =
+ numBytesPerUnitAsBigDecimal.multiply(value);
+ final BigDecimal roundedBigDecimal =
+ numBytesBigDecimal.setScale(0, RoundingMode.HALF_UP);
+ return roundedBigDecimal.toBigInteger();
+ }
+
+
+
+ /**
+ * Retrieves the number of instances of this unit represented by the
+ * specified number of bytes.
+ *
+ * @param numBytes The number of bytes to use to make the determination.
+ *
+ * @return The number of instances of this unit represented by the specified
+ * number of bytes.
+ */
+ @NotNull()
+ public BigDecimal fromBytes(final long numBytes)
+ {
+ return fromBytes(BigInteger.valueOf(numBytes));
+ }
+
+
+
+ /**
+ * Retrieves the number of instances of this unit represented by the
+ * specified number of bytes.
+ *
+ * @param numBytes The number of bytes to use to make the determination. It
+ * must not be {@code null}.
+ *
+ * @return The number of instances of this unit represented by the specified
+ * number of bytes.
+ */
+ @NotNull()
+ public BigDecimal fromBytes(@NotNull final BigInteger numBytes)
+ {
+ final BigDecimal numBytesPerUnitAsBigDecimal =
+ new BigDecimal(numBytesPerUnit);
+ final BigDecimal numBytesAsBigDecimal = new BigDecimal(numBytes);
+ return numBytesAsBigDecimal.divide(numBytesPerUnitAsBigDecimal);
+ }
+
+
+
+ /**
+ * Retrieves a string that represents a human-readable representation of the
+ * specified number of bytes. The string representation will be constructed
+ * in accordance with the following rules:
+ *
+ * -
+ * The string representation will use the abbreviation for the unit (e.g.,
+ * "b" instead of "bytes", "KB" instead of kilobytes, etc.)
+ *
+ * -
+ * If the provided value represents an exact multiple of the number of
+ * bytes for a given unit, then the string representation will be an
+ * integer followed by the abbreviation for the unit (e.g., a value of
+ * 123 will result in a string representation of "123b", a value of
+ * 524880 will result in a string representation of "5MB", a value of
+ * 7516192768 will result in a string representation of "7GB", etc.).
+ *
+ * -
+ * If the provided value does not represent an exact multiple of the
+ * number of bytes for the given unit, then the string representation will
+ * use a floating-point number with two digits behind the decimal point.
+ * It will select the unit so that when possible, there will be between
+ * 1 and 3 digits before the decimal point (e.g., a value of 12345 will
+ * result in a string representation of "12.06KB", a value of
+ * 9876543210 will result in a string representation of "9.20GB", etc.).
+ *
+ *
+ *
+ * @param numBytes The number of bytes to represent as a human-readable
+ * size. It must be greater than or equal to zero.
+ *
+ * @return A string that represents a human-readable representation of the
+ * specified number of bytes.
+ */
+ @NotNull()
+ public static String bytesToHumanReadableSize(final long numBytes)
+ {
+ return bytesToHumanReadableSize(BigInteger.valueOf(numBytes));
+ }
+
+
+
+ /**
+ * Retrieves a string that represents a human-readable representation of the
+ * specified number of bytes. The string representation will be constructed
+ * in accordance with the following rules:
+ *
+ * -
+ * The string representation will use the abbreviation for the unit (e.g.,
+ * "B" instead of "bytes", "KB" instead of kilobytes, etc.)
+ *
+ * -
+ * The string representation
+ * The string representation will use the abbreviation for the unit (e.g.,
+ * "B" instead of "bytes", "KB" instead of kilobytes, etc.)
+ *
+ * -
+ * If the provided value represents an exact multiple of the number of
+ * bytes for the selected unit, then the string representation will be an
+ * integer followed by the abbreviation for the unit (e.g., a value of
+ * 123 will result in a string representation of "123B", a value of
+ * 524880 will result in a string representation of "5MB", a value of
+ * 7516192768 will result in a string representation of "7GB", etc.).
+ *
+ * -
+ * If the provided value does not represent an exact multiple of the
+ * number of bytes for the selected unit, then the string representation
+ * will use a floating-point number with two digits behind the decimal
+ * point (e.g., a value of 12345 will result in a string representation of
+ * "12.06KB", a value of 9876543210 will result in a string representation
+ * of "9.20GB", etc.).
+ *
+ *
+ *
+ * @param numBytes The number of bytes to represent as a human-readable
+ * size. It must not be {@code null}, and it must represent
+ * a value that is greater than or equal to zero.
+ *
+ * @return A string that represents a human-readable representation of the
+ * specified number of bytes.
+ */
+ @NotNull()
+ public static String bytesToHumanReadableSize(
+ @NotNull final BigInteger numBytes)
+ {
+ Validator.ensureTrue((numBytes.compareTo(BigInteger.ZERO) >= 0),
+ "DecimalSizeUnits.bytesToHumanReadableSize.numBytes must be greater " +
+ "than or equal to zero.");
+
+
+ // Find the smallest unit whose numBytesPerUnit is greater than or equal
+ // to the given value.
+ DecimalSizeUnit selectedUnit = null;
+ final DecimalSizeUnit[] values = values();
+ for (int i=(values.length - 1); i >= 0; i--)
+ {
+ final DecimalSizeUnit unit = values[i];
+ if (numBytes.compareTo(unit.numBytesPerUnit) >= 0)
+ {
+ selectedUnit = unit;
+ break;
+ }
+ }
+
+
+ // Check to see if we ended up without a selected unit (which should only
+ // happen if the provided unit was zero). In that case, we'll default to
+ // a unit of bytes.
+ if (selectedUnit == null)
+ {
+ return numBytes + BYTES.abbreviation;
+ }
+
+
+ // Check to see if the provided value is an exact multiple of the number of
+ // bytes per instance of the selected unit. If so, then represent the value
+ // as an integer followed by the unit abbreviation.
+ if (numBytes.remainder(selectedUnit.numBytesPerUnit).equals(
+ BigInteger.ZERO))
+ {
+ return numBytes.divide(selectedUnit.numBytesPerUnit) +
+ selectedUnit.abbreviation;
+ }
+
+
+ // Compute the number of instances of the given unit needed to represent
+ // the provided value.
+ final BigDecimal numBytesAsBigDecimal = new BigDecimal(numBytes);
+ final BigDecimal numBytesPerUnitAsBigDecimal =
+ new BigDecimal(selectedUnit.numBytesPerUnit);
+ final BigDecimal numUnitsPerValueAsBigDecimal =
+ numBytesAsBigDecimal.divide(numBytesPerUnitAsBigDecimal, 2,
+ RoundingMode.HALF_UP);
+ return numUnitsPerValueAsBigDecimal.toString() + selectedUnit.abbreviation;
+ }
+
+
+
+ /**
+ * Retrieves the decimal size unit value that has the given name as either its
+ * singular name, plural name, or abbreviation, in a case-insensitive manner.
+ *
+ *
+ * @param name The name for which to retrieve the decimal size unit value.
+ * It must not be {@code null}.
+ *
+ * @return The decimal size unit value for the given name, or {@code null} if
+ * no value has a singular name, plural name, or abbreviation that
+ * matches the provided name in a case-insensitive manner.
+ */
+ @Nullable()
+ public static DecimalSizeUnit forName(@NotNull final String name)
+ {
+ for (final DecimalSizeUnit unit : values())
+ {
+ if (name.equalsIgnoreCase(unit.name()) ||
+ name.equalsIgnoreCase(unit.singularName) ||
+ name.equalsIgnoreCase(unit.pluralName) ||
+ name.equalsIgnoreCase(unit.abbreviation))
+ {
+ return unit;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/tests/unit/src/com/unboundid/util/BinarySizeUnitTestCase.java b/tests/unit/src/com/unboundid/util/BinarySizeUnitTestCase.java
new file mode 100644
index 000000000..9bcd0b254
--- /dev/null
+++ b/tests/unit/src/com/unboundid/util/BinarySizeUnitTestCase.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2023 Ping Identity Corporation
+ * All Rights Reserved.
+ */
+/*
+ * Copyright 2023 Ping Identity Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (C) 2023 Ping Identity Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (GPLv2 only)
+ * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+package com.unboundid.util;
+
+
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import com.unboundid.ldap.sdk.LDAPSDKTestCase;
+
+
+
+/**
+ * This class provides a set of test cases for the {@code BinarySizeUnit} enum.
+ */
+public final class BinarySizeUnitTestCase
+ extends LDAPSDKTestCase
+{
+ /**
+ * Provides basic test coverage for the various enum methods.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testBasicEnumMethods()
+ throws Exception
+ {
+ for (final BinarySizeUnit unit : BinarySizeUnit.values())
+ {
+ assertNotNull(unit.getSingularName());
+ assertNotNull(unit.getPluralName());
+ assertNotNull(unit.getAbbreviation());
+ assertNotNull(unit.getNumBytesPerUnit());
+
+ assertEquals(BinarySizeUnit.forName(unit.name()), unit);
+ assertEquals(BinarySizeUnit.forName(unit.getSingularName()), unit);
+ assertEquals(BinarySizeUnit.forName(unit.getPluralName()), unit);
+ assertEquals(BinarySizeUnit.forName(unit.getAbbreviation()), unit);
+
+ assertEquals(BinarySizeUnit.forName(
+ StaticUtils.toLowerCase(unit.name())), unit);
+ assertEquals(BinarySizeUnit.forName(
+ StaticUtils.toLowerCase(unit.getSingularName())), unit);
+ assertEquals(BinarySizeUnit.forName(
+ StaticUtils.toLowerCase(unit.getPluralName())), unit);
+ assertEquals(BinarySizeUnit.forName(
+ StaticUtils.toLowerCase(unit.getAbbreviation())), unit);
+
+ assertEquals(BinarySizeUnit.forName(
+ StaticUtils.toUpperCase(unit.name())), unit);
+ assertEquals(BinarySizeUnit.forName(
+ StaticUtils.toUpperCase(unit.getSingularName())), unit);
+ assertEquals(BinarySizeUnit.forName(
+ StaticUtils.toUpperCase(unit.getPluralName())), unit);
+ assertEquals(BinarySizeUnit.forName(
+ StaticUtils.toUpperCase(unit.getAbbreviation())), unit);
+
+ for (int i=1; i <= 999; i++)
+ {
+ final BigInteger numBytes = unit.getNumBytesPerUnit().multiply(
+ BigInteger.valueOf(i));
+
+ assertEquals(BinarySizeUnit.bytesToHumanReadableSize(numBytes),
+ i + unit.getAbbreviation());
+
+ assertEquals(unit.toBytes(i), numBytes);
+ assertEquals(unit.toBytes(i * 1.0d), numBytes);
+
+ assertEquals(unit.fromBytes(numBytes), BigDecimal.valueOf(i));
+ if (canRepresentAsLong(numBytes))
+ {
+ assertEquals(unit.fromBytes(numBytes.longValue()),
+ BigDecimal.valueOf(i));
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Ensures that the {@code forName} method returns {@code null} for an
+ * invalid or unrecognized name.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testForNameInvalid()
+ throws Exception
+ {
+ assertNull(BinarySizeUnit.forName("invalid"));
+ }
+
+
+
+ /**
+ * Ensures that the {@code bytesToHumanReadableSize} method yields the
+ * expected output for the given value.
+ *
+ * @param numBytes
+ * The number of bytes to provide as an argument to the
+ * {@code bytesToHumanReadableSize} method.
+ * @param expectedStringRepresentation
+ * The expected string representation for the provided value.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "BytesToHumanReadableSizeTestData")
+ public void testBytesToHumanReadableSize(final BigInteger numBytes,
+ final String expectedStringRepresentation)
+ throws Exception
+ {
+ assertEquals(BinarySizeUnit.bytesToHumanReadableSize(numBytes),
+ expectedStringRepresentation);
+
+ if (canRepresentAsLong(numBytes))
+ {
+ assertEquals(
+ BinarySizeUnit.bytesToHumanReadableSize(numBytes.longValue()),
+ expectedStringRepresentation);
+ }
+ }
+
+
+
+ /**
+ * Retrieves data that may be used for testing the
+ * {@code bytesToHumanReadableDuration} method.
+ *
+ * @return Data that may be used for testing the
+ * {@code bytesToHumanReadableDuration} method.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "BytesToHumanReadableSizeTestData")
+ public Object[][] getBytesToHumanReadableSizeTestData()
+ throws Exception
+ {
+ return new Object[][]
+ {
+ new Object[]
+ {
+ BigInteger.valueOf(0L),
+ "0B"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1L),
+ "1B"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(12L),
+ "12B"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(123L),
+ "123B"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1024L),
+ "1KB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1234L),
+ "1.21KB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(2048L),
+ "2KB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(12345L),
+ "12.06KB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(123456L),
+ "120.56KB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1048576L),
+ "1MB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1234567L),
+ "1.18MB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(6291456L),
+ "6MB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(12345678L),
+ "11.77MB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(123456789L),
+ "117.74MB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1073741824L),
+ "1GB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1234567890L),
+ "1.15GB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(12345678901L),
+ "11.50GB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(56908316672L),
+ "53GB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(123456789012L),
+ "114.98GB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1099511627776L),
+ "1TB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1234567890123L),
+ "1.12TB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(12345678901234L),
+ "11.23TB"
+ },
+ };
+ }
+
+
+
+ /**
+ * Indicates whether the provided {@code BigInteger} value can be represented
+ * as a {@code long}.
+ *
+ * @param value The value for which to make the determination.
+ *
+ * @return {@code true} if the provided value can be represented as a
+ * {@code long}, or {@code false} if not.
+ */
+ private static boolean canRepresentAsLong(final BigInteger value)
+ {
+ return value.equals(BigInteger.valueOf(value.longValue()));
+ }
+}
diff --git a/tests/unit/src/com/unboundid/util/DecimalSizeUnitTestCase.java b/tests/unit/src/com/unboundid/util/DecimalSizeUnitTestCase.java
new file mode 100644
index 000000000..8a827792c
--- /dev/null
+++ b/tests/unit/src/com/unboundid/util/DecimalSizeUnitTestCase.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2023 Ping Identity Corporation
+ * All Rights Reserved.
+ */
+/*
+ * Copyright 2023 Ping Identity Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (C) 2023 Ping Identity Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (GPLv2 only)
+ * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+package com.unboundid.util;
+
+
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import com.unboundid.ldap.sdk.LDAPSDKTestCase;
+
+
+
+/**
+ * This class provides a set of test cases for the {@code DecimalSizeUnit} enum.
+ */
+public final class DecimalSizeUnitTestCase
+ extends LDAPSDKTestCase
+{
+ /**
+ * Provides basic test coverage for the various enum methods.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testBasicEnumMethods()
+ throws Exception
+ {
+ for (final DecimalSizeUnit unit : DecimalSizeUnit.values())
+ {
+ assertNotNull(unit.getSingularName());
+ assertNotNull(unit.getPluralName());
+ assertNotNull(unit.getAbbreviation());
+ assertNotNull(unit.getNumBytesPerUnit());
+
+ assertEquals(DecimalSizeUnit.forName(unit.name()), unit);
+ assertEquals(DecimalSizeUnit.forName(unit.getSingularName()), unit);
+ assertEquals(DecimalSizeUnit.forName(unit.getPluralName()), unit);
+ assertEquals(DecimalSizeUnit.forName(unit.getAbbreviation()), unit);
+
+ assertEquals(DecimalSizeUnit.forName(
+ StaticUtils.toLowerCase(unit.name())), unit);
+ assertEquals(DecimalSizeUnit.forName(
+ StaticUtils.toLowerCase(unit.getSingularName())), unit);
+ assertEquals(DecimalSizeUnit.forName(
+ StaticUtils.toLowerCase(unit.getPluralName())), unit);
+ assertEquals(DecimalSizeUnit.forName(
+ StaticUtils.toLowerCase(unit.getAbbreviation())), unit);
+
+ assertEquals(DecimalSizeUnit.forName(
+ StaticUtils.toUpperCase(unit.name())), unit);
+ assertEquals(DecimalSizeUnit.forName(
+ StaticUtils.toUpperCase(unit.getSingularName())), unit);
+ assertEquals(DecimalSizeUnit.forName(
+ StaticUtils.toUpperCase(unit.getPluralName())), unit);
+ assertEquals(DecimalSizeUnit.forName(
+ StaticUtils.toUpperCase(unit.getAbbreviation())), unit);
+
+ for (int i=1; i <= 999; i++)
+ {
+ final BigInteger numBytes = unit.getNumBytesPerUnit().multiply(
+ BigInteger.valueOf(i));
+
+ assertEquals(DecimalSizeUnit.bytesToHumanReadableSize(numBytes),
+ i + unit.getAbbreviation());
+
+ assertEquals(unit.toBytes(i), numBytes);
+ assertEquals(unit.toBytes(i * 1.0d), numBytes);
+
+ assertEquals(unit.fromBytes(numBytes), BigDecimal.valueOf(i));
+ if (canRepresentAsLong(numBytes))
+ {
+ assertEquals(unit.fromBytes(numBytes.longValue()),
+ BigDecimal.valueOf(i));
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Ensures that the {@code forName} method returns {@code null} for an
+ * invalid or unrecognized name.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testForNameInvalid()
+ throws Exception
+ {
+ assertNull(DecimalSizeUnit.forName("invalid"));
+ }
+
+
+
+ /**
+ * Ensures that the {@code bytesToHumanReadableSize} method yields the
+ * expected output for the given value.
+ *
+ * @param numBytes
+ * The number of bytes to provide as an argument to the
+ * {@code bytesToHumanReadableSize} method.
+ * @param expectedStringRepresentation
+ * The expected string representation for the provided value.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "BytesToHumanReadableSizeTestData")
+ public void testBytesToHumanReadableSize(final BigInteger numBytes,
+ final String expectedStringRepresentation)
+ throws Exception
+ {
+ assertEquals(DecimalSizeUnit.bytesToHumanReadableSize(numBytes),
+ expectedStringRepresentation);
+
+ if (canRepresentAsLong(numBytes))
+ {
+ assertEquals(
+ DecimalSizeUnit.bytesToHumanReadableSize(numBytes.longValue()),
+ expectedStringRepresentation);
+ }
+ }
+
+
+
+ /**
+ * Retrieves data that may be used for testing the
+ * {@code bytesToHumanReadableDuration} method.
+ *
+ * @return Data that may be used for testing the
+ * {@code bytesToHumanReadableDuration} method.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "BytesToHumanReadableSizeTestData")
+ public Object[][] getBytesToHumanReadableSizeTestData()
+ throws Exception
+ {
+ return new Object[][]
+ {
+ new Object[]
+ {
+ BigInteger.valueOf(0L),
+ "0B"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1L),
+ "1B"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(12L),
+ "12B"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(123L),
+ "123B"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1000L),
+ "1KB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1234L),
+ "1.23KB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(2000L),
+ "2KB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(12345L),
+ "12.35KB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(123456L),
+ "123.46KB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1000000L),
+ "1MB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1234567L),
+ "1.23MB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(6000000L),
+ "6MB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(12345678L),
+ "12.35MB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(123456789L),
+ "123.46MB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1000000000L),
+ "1GB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1234567890L),
+ "1.23GB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(10000000000L),
+ "10GB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(53000000000L),
+ "53GB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(123456789012L),
+ "123.46GB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1000000000000L),
+ "1TB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(1234567890123L),
+ "1.23TB"
+ },
+ new Object[]
+ {
+ BigInteger.valueOf(12345678901234L),
+ "12.35TB"
+ },
+ };
+ }
+
+
+
+ /**
+ * Indicates whether the provided {@code BigInteger} value can be represented
+ * as a {@code long}.
+ *
+ * @param value The value for which to make the determination.
+ *
+ * @return {@code true} if the provided value can be represented as a
+ * {@code long}, or {@code false} if not.
+ */
+ private static boolean canRepresentAsLong(final BigInteger value)
+ {
+ return value.equals(BigInteger.valueOf(value.longValue()));
+ }
+}