/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2008 Sun Microsystems, Inc.
*/
package org.opends.server.admin;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This enumeration defines various memory size units.
*/
public enum SizeUnit {
/**
* A byte unit.
*/
BYTES(1L, "b", "bytes"),
/**
* A gibi-byte unit.
*/
GIBI_BYTES((long) 1024 * 1024 * 1024, "gib", "gibibytes"),
/**
* A giga-byte unit.
*/
GIGA_BYTES((long) 1000 * 1000 * 1000, "gb", "gigabytes"),
/**
* A kibi-byte unit.
*/
KIBI_BYTES(1024L, "kib", "kibibytes"),
/**
* A kilo-byte unit.
*/
KILO_BYTES(1000L, "kb", "kilobytes"),
/**
* A mebi-byte unit.
*/
MEBI_BYTES((long) 1024 * 1024, "mib", "mebibytes"),
/**
* A mega-byte unit.
*/
MEGA_BYTES((long) 1000 * 1000, "mb", "megabytes"),
/**
* A tebi-byte unit.
*/
TEBI_BYTES((long) 1024 * 1024 * 1024 * 1024, "tib", "tebibytes"),
/**
* A tera-byte unit.
*/
TERA_BYTES((long) 1000 * 1000 * 1000 * 1000, "tb", "terabytes");
// A lookup table for resolving a unit from its name.
private static final Map<String, SizeUnit> nameToUnit;
static {
nameToUnit = new HashMap<String, SizeUnit>();
for (SizeUnit unit : SizeUnit.values()) {
nameToUnit.put(unit.shortName, unit);
nameToUnit.put(unit.longName, unit);
}
}
/**
* Gets the best-fit unit for the specified number of bytes. The
* returned unit will be able to represent the number of bytes using
* a decimal number comprising of an integer part which is greater
* than zero. Bigger units are chosen in preference to smaller units
* and binary units are only returned if they are an exact fit. If
* the number of bytes is zero then the {@link #BYTES} unit is
* always returned. For example:
*
* <pre>
* getBestFitUnit(0) // BYTES
* getBestFitUnit(999) // BYTES
* getBestFitUnit(1000) // KILO_BYTES
* getBestFitUnit(1024) // KIBI_BYTES
* getBestFitUnit(1025) // KILO_BYTES
* getBestFitUnit(999999) // KILO_BYTES
* getBestFitUnit(1000000) // MEGA_BYTES
* </pre>
*
* @param bytes
* The number of bytes.
* @return Returns the best fit unit.
* @throws IllegalArgumentException
* If <code>bytes</code> is negative.
* @see #getBestFitUnitExact(long)
*/
public static SizeUnit getBestFitUnit(long bytes)
throws IllegalArgumentException {
if (bytes < 0) {
throw new IllegalArgumentException("negative number of bytes: " + bytes);
} else if (bytes == 0) {
// Always use bytes for zero values.
return BYTES;
} else {
// Determine best fit: prefer non-binary units unless binary
// fits exactly.
SizeUnit[] nonBinary = new SizeUnit[] { TERA_BYTES, GIGA_BYTES,
MEGA_BYTES, KILO_BYTES };
SizeUnit[] binary = new SizeUnit[] { TEBI_BYTES, GIBI_BYTES, MEBI_BYTES,
KIBI_BYTES };
for (int i = 0; i < nonBinary.length; i++) {
if ((bytes % binary[i].getSize()) == 0) {
return binary[i];
} else if ((bytes / nonBinary[i].getSize()) > 0) {
return nonBinary[i];
}
}
return BYTES;
}
}
/**
* Gets the best-fit unit for the specified number of bytes which
* can represent the provided value using an integral value. Bigger
* units are chosen in preference to smaller units. If the number of
* bytes is zero then the {@link #BYTES} unit is always returned.
* For example:
*
* <pre>
* getBestFitUnitExact(0) // BYTES
* getBestFitUnitExact(999) // BYTES
* getBestFitUnitExact(1000) // KILO_BYTES
* getBestFitUnitExact(1024) // KIBI_BYTES
* getBestFitUnitExact(1025) // BYTES
* getBestFitUnitExact(999999) // BYTES
* getBestFitUnitExact(1000000) // MEGA_BYTES
* </pre>
*
* @param bytes
* The number of bytes.
* @return Returns the best fit unit can represent the provided
* value using an integral value.
* @throws IllegalArgumentException
* If <code>bytes</code> is negative.
* @see #getBestFitUnit(long)
*/
public static SizeUnit getBestFitUnitExact(long bytes)
throws IllegalArgumentException {
if (bytes < 0) {
throw new IllegalArgumentException("negative number of bytes: " + bytes);
} else if (bytes == 0) {
// Always use bytes for zero values.
return BYTES;
} else {
// Determine best fit.
SizeUnit[] units = new SizeUnit[] { TEBI_BYTES, TERA_BYTES, GIBI_BYTES,
GIGA_BYTES, MEBI_BYTES, MEGA_BYTES, KIBI_BYTES, KILO_BYTES };
for (SizeUnit unit : units) {
if ((bytes % unit.getSize()) == 0) {
return unit;
}
}
return BYTES;
}
}
/**
* Get the unit corresponding to the provided unit name.
*
* @param s
* The name of the unit. Can be the abbreviated or long
* name and can contain white space and mixed case
* characters.
* @return Returns the unit corresponding to the provided unit name.
* @throws IllegalArgumentException
* If the provided name did not correspond to a known
* memory size unit.
*/
public static SizeUnit getUnit(String s) throws IllegalArgumentException {
SizeUnit unit = nameToUnit.get(s.trim().toLowerCase());
if (unit == null) {
throw new IllegalArgumentException("Illegal memory size unit \"" + s
+ "\"");
}
return unit;
}
/**
* Parse the provided size string and return its equivalent size in
* bytes. The size string must specify the unit e.g. "10kb".
*
* @param s
* The size string to be parsed.
* @return Returns the parsed duration in bytes.
* @throws NumberFormatException
* If the provided size string could not be parsed.
*/
public static long parseValue(String s) throws NumberFormatException {
return parseValue(s, null);
}
/**
* Parse the provided size string and return its equivalent size in
* bytes.
*
* @param s
* The size string to be parsed.
* @param defaultUnit
* The default unit to use if there is no unit specified in
* the size string, or <code>null</code> if the string
* must always contain a unit.
* @return Returns the parsed size in bytes.
* @throws NumberFormatException
* If the provided size string could not be parsed.
*/
public static long parseValue(String s, SizeUnit defaultUnit)
throws NumberFormatException {
// Value must be a floating point number followed by a unit.
Pattern p = Pattern.compile("^\\s*(\\d+(\\.\\d+)?)\\s*(\\w+)?\\s*$");
Matcher m = p.matcher(s);
if (!m.matches()) {
throw new NumberFormatException("Invalid size value \"" + s + "\"");
}
// Group 1 is the float.
double d;
try {
d = Double.valueOf(m.group(1));
} catch (NumberFormatException e) {
throw new NumberFormatException("Invalid size value \"" + s + "\"");
}
// Group 3 is the unit.
String unitString = m.group(3);
SizeUnit unit;
if (unitString == null) {
if (defaultUnit == null) {
throw new NumberFormatException("Invalid size value \"" + s + "\"");
} else {
unit = defaultUnit;
}
} else {
try {
unit = getUnit(unitString);
} catch (IllegalArgumentException e) {
throw new NumberFormatException("Invalid size value \"" + s + "\"");
}
}
return unit.toBytes(d);
}
// The long name of the unit.
private final String longName;
// The abbreviation of the unit.
private final String shortName;
// The size of the unit in bytes.
private final long sz;
// Private constructor.
private SizeUnit(long sz, String shortName, String longName) {
this.sz = sz;
this.shortName = shortName;
this.longName = longName;
}
/**
* Converts the specified size in bytes to this unit.
*
* @param amount
* The size in bytes.
* @return Returns size in this unit.
*/
public double fromBytes(long amount) {
return ((double) amount / sz);
}
/**
* Get the long name of this unit.
*
* @return Returns the long name of this unit.
*/
public String getLongName() {
return longName;
}
/**
* Get the abbreviated name of this unit.
*
* @return Returns the abbreviated name of this unit.
*/
public String getShortName() {
return shortName;
}
/**
* Get the number of bytes that this unit represents.
*
* @return Returns the number of bytes that this unit represents.
*/
public long getSize() {
return sz;
}
/**
* Converts the specified size in this unit to bytes.
*
* @param amount
* The size as a quantity of this unit.
* @return Returns the number of bytes that the size represents.
*
* @throws NumberFormatException
* If the provided size exceeded long.MAX_VALUE.
*/
public long toBytes(double amount) throws NumberFormatException {
double value = sz * amount;
if (value > Long.MAX_VALUE)
{
throw new NumberFormatException
("number too big (exceeded long.MAX_VALUE");
}
return (long) (value);
}
/**
* {@inheritDoc}
* <p>
* This implementation returns the abbreviated name of this size
* unit.
*/
@Override
public String toString() {
return shortName;
}
}