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}