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 }{ { name: "EiB", size: humanize.EiByte, }, { name: "EB", size: humanize.EByte, }, { name: "PiB", size: humanize.PiByte, }, { name: "PB", size: humanize.PByte, }, { name: "TiB", size: humanize.TiByte, }, { name: "TB", size: humanize.TByte, }, { name: "GiB", size: humanize.GiByte, }, { name: "GB", size: humanize.GByte, }, { name: "MiB", size: humanize.MiByte, }, { name: "MB", size: humanize.MByte, }, { name: "KiB", size: humanize.KiByte, }, { name: "KB", size: 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(text string) (ByteSize, error) { trimmed := strings.TrimSpace(text) if trimmed == "-1" || strings.EqualFold(trimmed, "-1B") { return ByteSizeNoLimit, nil } if strings.HasPrefix(trimmed, "-") { return 0, fmt.Errorf( "util: byte size %q is negative; only -1 (no limit) is allowed", text, ) } parsed, err := humanize.ParseBytes(text) if err != nil { return 0, err } if parsed > math.MaxInt64 { return 0, fmt.Errorf("util: byte size %q is too large", text) } return ByteSize(parsed), 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 _, unit := range byteUnits { if int64(b)%unit.size == 0 { return strconv.FormatInt(int64(b)/unit.size, 10) + unit.name } } } return strconv.FormatInt(int64(b), 10) + "B" }