blob: dc496e5ba1974362329b5302aeac3c95e481ebed [file] [log] [blame]
package humanize
import (
"fmt"
"math/big"
"strings"
"unicode"
)
var (
bigIECExp = big.NewInt(1024)
BigByte = big.NewInt(1)
BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp)
BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp)
BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp)
BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp)
BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp)
BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp)
BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp)
BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp)
)
var (
bigSIExp = big.NewInt(1000)
BigSIByte = big.NewInt(1)
BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp)
BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp)
BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp)
BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp)
BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp)
BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp)
BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp)
BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp)
)
var bigBytesSizeTable = map[string]*big.Int{
"b": BigByte,
"kib": BigKiByte,
"kb": BigKByte,
"mib": BigMiByte,
"mb": BigMByte,
"gib": BigGiByte,
"gb": BigGByte,
"tib": BigTiByte,
"tb": BigTByte,
"pib": BigPiByte,
"pb": BigPByte,
"eib": BigEiByte,
"eb": BigEByte,
"zib": BigZiByte,
"zb": BigZByte,
"yib": BigYiByte,
"yb": BigYByte,
// Without suffix
"": BigByte,
"ki": BigKiByte,
"k": BigKByte,
"mi": BigMiByte,
"m": BigMByte,
"gi": BigGiByte,
"g": BigGByte,
"ti": BigTiByte,
"t": BigTByte,
"pi": BigPiByte,
"p": BigPByte,
"ei": BigEiByte,
"e": BigEByte,
"z": BigZByte,
"zi": BigZiByte,
"y": BigYByte,
"yi": BigYiByte,
}
func oom(n, b *big.Int, maxmag int) (float64, int) {
mag := 0
m := &big.Int{}
for n.Cmp(b) >= 0 {
n.DivMod(n, b, m)
mag++
if mag == maxmag {
break
}
}
return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag
}
var ten = big.NewInt(10)
func humanateBigBytes(s, base *big.Int, sizes []string) string {
if s.Cmp(ten) < 0 {
return fmt.Sprintf("%dB", s)
}
c := (&big.Int{}).Set(s)
val, mag := oom(c, base, len(sizes)-1)
suffix := sizes[mag]
f := "%.0f"
if val < 10 {
f = "%.1f"
}
return fmt.Sprintf(f+"%s", val, suffix)
}
// BigBytes produces a human readable representation of an SI size.
// BigBytes(82854982) -> 83MB
func BigBytes(s *big.Int) string {
sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
return humanateBigBytes(s, bigSIExp, sizes)
}
// BigIBytes produces a human readable representation of an IEC size.
// BigIBytes(82854982) -> 79MiB
func BigIBytes(s *big.Int) string {
sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
return humanateBigBytes(s, bigIECExp, sizes)
}
// ParseBigBytes parses a string representation of bytes into the number
// of bytes it represents.
// ParseBigBytes("42MB") -> 42000000, nil
// ParseBigBytes("42mib") -> 44040192, nil
func ParseBigBytes(s string) (*big.Int, error) {
lastDigit := 0
for _, r := range s {
if !(unicode.IsDigit(r) || r == '.') {
break
}
lastDigit++
}
val := &big.Rat{}
_, err := fmt.Sscanf(s[:lastDigit], "%f", val)
if err != nil {
return nil, err
}
extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
if m, ok := bigBytesSizeTable[extra]; ok {
mv := (&big.Rat{}).SetInt(m)
val.Mul(val, mv)
rv := &big.Int{}
rv.Div(val.Num(), val.Denom())
return rv, nil
}
return nil, fmt.Errorf("Unhandled size name: %v", extra)
}