pkg/bytequantity/bytequantity.go (108 lines of code) (raw):
// 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 bytequantity
import (
"fmt"
"math"
"regexp"
"strconv"
"strings"
)
const (
/// Examples: 1mb, 1 gb, 1.0tb, 1mib, 2g, 2.001 t.
byteQuantityRegex = `^([0-9]+\.?[0-9]{0,3})[ ]?(mi?b?|gi?b?|ti?b?)?$`
mib = "MiB"
gib = "GiB"
tib = "TiB"
gbConvert = 1 << 10
tbConvert = gbConvert << 10
maxGiB = math.MaxUint64 / gbConvert
maxTiB = math.MaxUint64 / tbConvert
)
// ByteQuantity is a data type representing a byte quantity.
type ByteQuantity struct {
Quantity uint64
}
// ParseToByteQuantity parses a string representation of a byte quantity to a ByteQuantity type.
// A unit can be appended such as 16 GiB. If no unit is appended, GiB is assumed.
func ParseToByteQuantity(byteQuantityStr string) (ByteQuantity, error) {
bqRegexp := regexp.MustCompile(byteQuantityRegex)
matches := bqRegexp.FindStringSubmatch(strings.ToLower(byteQuantityStr))
if len(matches) < 2 {
return ByteQuantity{}, fmt.Errorf("%s is not a valid byte quantity", byteQuantityStr)
}
quantityStr := matches[1]
unit := gib
if len(matches) > 2 && matches[2] != "" {
unit = matches[2]
}
quantity := uint64(0)
switch strings.ToLower(string(unit[0])) {
// mib
case "m":
inputDecSplit := strings.Split(quantityStr, ".")
if len(inputDecSplit) == 2 {
d, err := strconv.Atoi(inputDecSplit[1])
if err != nil {
return ByteQuantity{}, err
}
if d != 0 {
return ByteQuantity{}, fmt.Errorf("cannot accept floating point MB value, only integers are accepted")
}
}
// need error here so that this quantity doesn't bind in the local scope
var err error
quantity, err = strconv.ParseUint(inputDecSplit[0], 10, 64)
if err != nil {
return ByteQuantity{}, err
}
// gib
case "g":
quantityDec, err := strconv.ParseFloat(quantityStr, 64)
if err != nil {
return ByteQuantity{}, err
}
if quantityDec > maxGiB {
return ByteQuantity{}, fmt.Errorf("error GiB value is too large")
}
quantity = uint64(quantityDec * gbConvert)
// tib
case "t":
quantityDec, err := strconv.ParseFloat(quantityStr, 64)
if err != nil {
return ByteQuantity{}, err
}
if quantityDec > maxTiB {
return ByteQuantity{}, fmt.Errorf("error TiB value is too large")
}
quantity = uint64(quantityDec * tbConvert)
default:
return ByteQuantity{}, fmt.Errorf("error unit %s is not supported", unit)
}
return ByteQuantity{
Quantity: quantity,
}, nil
}
// FromTiB returns a byte quantity of the passed in tebibytes quantity.
func FromTiB(tib uint64) ByteQuantity {
return ByteQuantity{
Quantity: tib * tbConvert,
}
}
// FromGiB returns a byte quantity of the passed in gibibytes quantity.
func FromGiB(gib uint64) ByteQuantity {
return ByteQuantity{
Quantity: gib * gbConvert,
}
}
// FromMiB returns a byte quantity of the passed in mebibytes quantity.
func FromMiB(mib uint64) ByteQuantity {
return ByteQuantity{
Quantity: mib,
}
}
// StringMiB returns a byte quantity in a mebibytes string representation.
func (bq ByteQuantity) StringMiB() string {
return fmt.Sprintf("%.0f %s", bq.MiB(), mib)
}
// StringGiB returns a byte quantity in a gibibytes string representation.
func (bq ByteQuantity) StringGiB() string {
return fmt.Sprintf("%.3f %s", bq.GiB(), gib)
}
// StringTiB returns a byte quantity in a tebibytes string representation.
func (bq ByteQuantity) StringTiB() string {
return fmt.Sprintf("%.3f %s", bq.TiB(), tib)
}
// MiB returns a byte quantity in mebibytes.
func (bq ByteQuantity) MiB() float64 {
return float64(bq.Quantity)
}
// GiB returns a byte quantity in gibibytes.
func (bq ByteQuantity) GiB() float64 {
return float64(bq.Quantity) * 1 / gbConvert
}
// TiB returns a byte quantity in tebibytes.
func (bq ByteQuantity) TiB() float64 {
return float64(bq.Quantity) * 1 / tbConvert
}