package util import ( "fmt" "math" "strconv" "strings" "github.com/dustin/go-humanize" ) // ByteSize is a size in bytes, configured as a string such as "32KiB", "1MB" // or "1.5 GB" (see ParseByteSize). KB, MB, ... are powers of 1000 and KiB, // MiB, ... powers of 1024; a bare number is bytes. ByteSizeNoLimit (-1) means // no limit; no other size is negative. type ByteSize int64 // ByteSizeNoLimit is the size that means no limit, written "-1". A setting // for which no limit is unsafe rejects it. const ByteSizeNoLimit ByteSize = -1 // byteUnits are the units String may use, largest first. var byteUnits = []struct { name string size int64 }{ {"EiB", humanize.EiByte}, {"EB", humanize.EByte}, {"PiB", humanize.PiByte}, {"PB", humanize.PByte}, {"TiB", humanize.TiByte}, {"TB", humanize.TByte}, {"GiB", humanize.GiByte}, {"GB", humanize.GByte}, {"MiB", humanize.MiByte}, {"MB", humanize.MByte}, {"KiB", humanize.KiByte}, {"KB", humanize.KByte}, } // ParseByteSize parses a size such as "32KiB", "1 MB" or "4096". Unit names // are case-insensitive. "-1" (or "-1B") is ByteSizeNoLimit; any other // negative size is an error. func ParseByteSize(s string) (ByteSize, error) { trimmed := strings.TrimSpace(s) if trimmed == "-1" || strings.EqualFold(trimmed, "-1B") { return ByteSizeNoLimit, nil } if strings.HasPrefix(trimmed, "-") { return 0, fmt.Errorf("byte size %q is negative; only -1 (no limit) is allowed", s) } n, err := humanize.ParseBytes(s) if err != nil { return 0, err } if n > math.MaxInt64 { return 0, fmt.Errorf("byte size %q is too large", s) } return ByteSize(n), nil } // String formats b exactly, in the largest unit that divides it (e.g. "32KiB", // "1MB"), or in bytes ("1500B"), so it parses back to the same value. // ByteSizeNoLimit is "-1". func (b ByteSize) String() string { if b == ByteSizeNoLimit { return "-1" } if b != 0 { for _, u := range byteUnits { if int64(b)%u.size == 0 { return strconv.FormatInt(int64(b)/u.size, 10) + u.name } } } return strconv.FormatInt(int64(b), 10) + "B" }