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 }