All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.conqat.lib.commons.filesystem.ByteUnit Maven / Gradle / Ivy

The newest version!
package org.conqat.lib.commons.filesystem;

import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.conqat.lib.commons.collections.Pair;

/**
 * A {@code ByteUnit} represents amounts of bytes at a given unit of granularity and provides
 * utility methods to convert across units. A {@code ByteUnit} does not maintain actual size
 * information, but only helps organize and use size representations that may be maintained
 * separately across various contexts.
 * 

* This class performs computations based on the powers of 2 (e.g. 1024 bytes form 1 kibibyte) and * thus also uses the respective terms and {@link #getAbbreviation() abbreviations} (e.g. MiB * instead of MB) to avoid confusions with the SI units. The SI units are based on the powers of 10 * (e.g. 1000 bytes form 1 kilobyte) and use different abbreviations (e.g. MB, GB, etc.). */ public enum ByteUnit { /** * Byte unit representing one single byte (8 bits). */ BYTES("B"), /** * Byte unit representing one kibibyte (1024 {@link #BYTES bytes}). */ KIBIBYTES("KiB"), /** * Byte unit representing one mibibyte (1024 {@link #KIBIBYTES kibibytes}). */ MEBIBYTES("MiB"), /** * Byte unit representing one gibibyte (1024 {@link #MEBIBYTES mebibytes}). */ GIBIBYTES("GiB"), /** * Byte unit representing one tebibyte (1024 {@link #GIBIBYTES gibibytes}). */ TEBIBYTES("TiB"), /** * Byte unit representing one pebibyte (1024 {@link #TEBIBYTES tebibytes}). */ PEBIBYTES("PiB"), /** * Byte unit representing one exbibyte (1024 {@link #PEBIBYTES pebibytes}). */ EXBIBYTES("EiB"); private static final Map UNIT_FOR_ABBREVIATION = Collections.unmodifiableMap( Arrays.stream(values()).collect(Collectors.toMap(ByteUnit::getAbbreviation, Function.identity()))); private static final Pattern VALUE_WITH_ABBREVIATION = Pattern.compile("^(\\d+)\\s*" + Arrays.stream(values()).map(ByteUnit::getAbbreviation).collect(Collectors.joining("|", "(", ")")) + "$"); private final String abbreviation; ByteUnit(String abbreviation) { this.abbreviation = abbreviation; } /** * @return The IEC abbreviation. E.g. {@code MiB} for {@link #MEBIBYTES}. */ public String getAbbreviation() { return abbreviation; } @Override public String toString() { return abbreviation; } /** * Equivalent to {@link #convert(long, ByteUnit) BYTES.convert(size, this)}. * * @param size * the size * @return the converted size, or {@code Long.MIN_VALUE} if conversion would negatively overflow, or * {@code Long.MAX_VALUE} if it would positively overflow. */ public long toBytes(long size) { return BYTES.convert(size, this); } /** * Equivalent to {@link #convert(long, ByteUnit) KIBIBYTES.convert(size, this)}. * * @param size * the size * @return the converted size, or {@code Long.MIN_VALUE} if conversion would negatively overflow, or * {@code Long.MAX_VALUE} if it would positively overflow. */ public long toKibiBytes(long size) { return KIBIBYTES.convert(size, this); } /** * Equivalent to {@link #convert(long, ByteUnit) MEBIBYTES.convert(size, this)}. * * @param size * the size * @return the converted size, or {@code Long.MIN_VALUE} if conversion would negatively overflow, or * {@code Long.MAX_VALUE} if it would positively overflow. */ public long toMebiBytes(long size) { return MEBIBYTES.convert(size, this); } /** * Equivalent to {@link #convert(long, ByteUnit) GIBIBYTES.convert(size, this)}. * * @param size * the size * @return the converted size, or {@code Long.MIN_VALUE} if conversion would negatively overflow, or * {@code Long.MAX_VALUE} if it would positively overflow. */ public long toGibiBytes(long size) { return GIBIBYTES.convert(size, this); } /** * Equivalent to {@link #convert(long, ByteUnit) TEBIBYTES.convert(size, this)}. * * @param size * the size * @return the converted size, or {@code Long.MIN_VALUE} if conversion would negatively overflow, or * {@code Long.MAX_VALUE} if it would positively overflow. */ public long toTebiBytes(long size) { return TEBIBYTES.convert(size, this); } /** * Converts the given {@code sourceSize} in the {@code sourceUnit} into this unit. Conversions from * finer to coarser granularities truncate, so lose precision. For example, converting {@code 1023} * bytes to kibibytes results in {@code 0}. Conversions from coarser to finer granularities with * arguments that would numerically overflow saturate to {@code Long.MIN_VALUE} if negative or * {@code Long.MAX_VALUE} if positive. *

* For example, to convert 10 GiB to MiB, use:
* {@code ByteUnit.MIBIBYTES.convert(10L, ByteUnit.GIBIBYTES)} * * @param sourceSize * the size in the given {@code sourceUnit} * @param sourceUnit * the unit of the {@code sourceSize} argument * @return the converted size in this unit, or {@code Long.MIN_VALUE} if conversion would negatively * overflow, or {@code Long.MAX_VALUE} if it would positively overflow. */ public long convert(long sourceSize, ByteUnit sourceUnit) { return convert(sourceSize, sourceUnit, this); } /** * Returns the multiplier between this instance (as source unit) and the {@code targetUnit}. The * multiplier can be used in the way: * *

	 *     ByteUnit sourceUnit = ...;
	 *     ByteUnit targetUnit = ...;
	 *     long sourceValue = ...;
	 *     long targetValue = sourceValue * sourceUnit.getMultiplier(targetUnit);
	 * 
* * @param targetUnit * {@code ByteUnit} into which the value should be multiplied * @return The multiplier to compute values in the {@code targetUnit} from the source unit. * @throws IllegalArgumentException * Thrown then {@code targetUnit} was greater than the source unit. */ public long getMultiplier(ByteUnit targetUnit) { return getMultiplier(this, targetUnit); } /** * Returns the multiplier between the {@code sourceUnit} and the {@code targetUnit}. The multiplier * can be used in the way: * *
	 *     ByteUnit sourceUnit = ...;
	 *     ByteUnit targetUnit = ...;
	 *     long sourceValue = ...;
	 *     long targetValue = sourceValue * ByteUnit.getMultiplier(sourceUnit, targetUnit);
	 * 
* * @param sourceUnit * {@code ByteUnit} from which the value should be multiplied * @param targetUnit * {@code ByteUnit} into which the value should be multiplied * @return The multiplier to compute values in the {@code targetUnit} from the {@code sourceUnit}. * @throws IllegalArgumentException * Thrown then {@code targetUnit} was greater than the {@code sourceUnit}. */ public static long getMultiplier(ByteUnit sourceUnit, ByteUnit targetUnit) { int scaleDifference = sourceUnit.ordinal() - targetUnit.ordinal(); if (scaleDifference < 0) { throw new IllegalArgumentException( String.format("Cannot compute multiplier from %s to %s", sourceUnit.name(), targetUnit.name())); } return computeScale(scaleDifference); } private static long convert(long size, ByteUnit sourceUnit, ByteUnit targetUnit) { if (sourceUnit == targetUnit || size == 0L) { return size; } long result = size; int scaleDifference = sourceUnit.ordinal() - targetUnit.ordinal(); if (scaleDifference > 0) { result = boundedMultiply(result, computeScale(scaleDifference)); } else { result /= computeScale(scaleDifference * -1); } return result; } private static long computeScale(int steps) { if (steps > 6) { // Guard against future, where zebibyte and yobibyte may be present throw new ArithmeticException("Unable to compute conversions larger than the size of long"); } return 1L << (10 * steps); } /** * Same as {@link Math#multiplyExact(int, int)}, but instead of throwing an exception, * {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE} is returned. * * @return The result of the multiplication of {@code x} and {@code y}. * {@link Long#MAX_VALUE}/{@link Long#MIN_VALUE} if the resulting value overflowed. */ private static long boundedMultiply(long x, long y) { long r = x * y; long ax = Math.abs(x); long ay = Math.abs(y); if (((ax | ay) >>> 31 != 0)) { // Some bits greater than 2^31 that might cause overflow // Check the result using the divide operator // and check for the special case of Long.MIN_VALUE * -1 if (((y != 0) && (r / y != x)) || (x == Long.MIN_VALUE && y == -1)) { // here the ArithmeticException is thrown by Math.multiplyExact return getOverflowValue(x, y); } } return r; } private static long getOverflowValue(long x, long y) { if (x < 0 ^ y < 0) { // result should be negative as input was positive * negative return Long.MIN_VALUE; } return Long.MAX_VALUE; } /** * Parses the {@link ByteUnit} from the provided {@code abbreviation} (e.g. {@code GiB}). * * @throws IllegalArgumentException * Thrown if the provided abbreviation could not be parsed * * @see #getAbbreviation() */ public static ByteUnit fromAbbreviation(String abbreviation) { ByteUnit unit = UNIT_FOR_ABBREVIATION.get(abbreviation); if (unit == null) { throw new IllegalArgumentException(String.format("Unknown abbreviation: %s", abbreviation)); } return unit; } /** * Parses the unit measurement (e.g. {@code 3 GiB}) into the numeric value and {@link ByteUnit}. */ public static Pair parseUnitMeasurement(String value) { Matcher matcher = VALUE_WITH_ABBREVIATION.matcher(value); if (matcher.matches()) { long amount = Long.parseLong(matcher.group(1)); ByteUnit unit = ByteUnit.fromAbbreviation(matcher.group(2)); return new Pair<>(amount, unit); } else { throw new IllegalArgumentException(String .format("Provided argument does not match the expected pattern: %s", VALUE_WITH_ABBREVIATION)); } } /** * Parses the unit measurement (e.g. {@code 3 GiB}) into the numeric value of the provided * {@code targetUnit}. */ public static long parseUnitMeasurementAs(String value, ByteUnit targetUnit) { Pair valueAndUnit = parseUnitMeasurement(value); return targetUnit.convert(valueAndUnit.getFirst(), valueAndUnit.getSecond()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy