/*
* Copyright 2013-present Facebook, Inc.
*
* 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.
*/
package com.facebook.buck.util.unit;
import com.facebook.buck.model.Pair;
import com.facebook.buck.util.MoreStrings;
import com.google.common.collect.ImmutableMap;
import java.math.BigDecimal;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public enum SizeUnit {
BYTES(0, "B"),
KILOBYTES(1, "KB"),
MEGABYTES(2, "MB"),
GIGABYTES(3, "GB"),
TERABYTES(4, "TB");
private final int ordinal;
private final String abbreviation;
private SizeUnit(int ordinal, String abbreviation) {
this.ordinal = ordinal;
this.abbreviation = abbreviation;
}
public int getOrdinal() {
return ordinal;
}
public String getAbbreviation() {
return abbreviation;
}
private static long multiplyByByteOrderOfMagnitude(double size, int magnitude) {
if (magnitude == 0) {
return (long) size;
} else if (magnitude > 0) {
return BigDecimal.valueOf(size).multiply(BigDecimal.valueOf(1024).pow(magnitude)).longValue();
} else {
return BigDecimal.valueOf(size)
.divide(BigDecimal.valueOf(1024).pow(-1 * magnitude))
.longValue();
}
}
private static final ImmutableMap<String, SizeUnit> SHORT_TO_CODE =
ImmutableMap.<String, SizeUnit>builder()
.put("b", BYTES)
.put("kb", KILOBYTES)
.put("kilobytes", KILOBYTES)
.put("mb", MEGABYTES)
.put("megabytes", MEGABYTES)
.put("gb", GIGABYTES)
.put("gigabytes", GIGABYTES)
.put("tb", TERABYTES)
.put("terabytes", TERABYTES)
.build();
private static final Pattern SIZE_PATTERN =
Pattern.compile(
"([\\d]+(?:\\.[\\d]+)?)\\s*" + MoreStrings.regexPatternForAny(SHORT_TO_CODE.keySet()),
Pattern.CASE_INSENSITIVE);
/** Parses a string that represents a size into the number of bytes represented by that string. */
public static long parseBytes(String input) throws NumberFormatException {
Matcher matcher = SIZE_PATTERN.matcher(input);
if (matcher.find()) {
String number = matcher.group(1);
SizeUnit sizeUnit = SHORT_TO_CODE.get(matcher.group(2).toLowerCase());
if (sizeUnit != null) {
try {
double value = Double.parseDouble(number);
return sizeUnit.toBytes(value);
} catch (NumberFormatException e) {
// If the number was so large as to overflow Long.MAX_VALUE, return LONG.MAX_VALUE.
return Long.MAX_VALUE;
}
}
}
throw new NumberFormatException(String.format("%s is not a valid file size", input));
}
public long toBytes(double size) {
return multiplyByByteOrderOfMagnitude(size, getOrdinal() - BYTES.getOrdinal());
}
public long toKilobytes(double size) {
return multiplyByByteOrderOfMagnitude(size, getOrdinal() - KILOBYTES.getOrdinal());
}
public long toMegabytes(double size) {
return multiplyByByteOrderOfMagnitude(size, getOrdinal() - MEGABYTES.getOrdinal());
}
public long toGigabytes(double size) {
return multiplyByByteOrderOfMagnitude(size, getOrdinal() - GIGABYTES.getOrdinal());
}
public long toTerabytes(double size) {
return multiplyByByteOrderOfMagnitude(size, getOrdinal() - TERABYTES.getOrdinal());
}
public static Pair<Double, SizeUnit> getHumanReadableSize(double size, SizeUnit unit) {
if (size == 0) {
return new Pair<>(0.0, unit);
}
int ordinal = unit.getOrdinal();
double resultSize = size;
if (size > 1) {
while (size > 1 && ordinal < 4) {
size = size / 1024;
if (size > 1) {
ordinal++;
resultSize = size;
}
}
} else {
while (size < 1024 && ordinal > 0) {
size = size * 1024;
if (size < 1024) {
ordinal--;
resultSize = size;
}
}
}
return new Pair<>(resultSize, SizeUnit.values()[ordinal]);
}
public static String toHumanReadableString(Pair<Double, SizeUnit> size, Locale locale) {
return String.format(locale, "%.2f %s", size.getFirst(), size.getSecond().getAbbreviation());
}
}