blob: f33bb95d527a3098726a8d49357e814e108a3a7b [file] [log] [blame]
// Package bytefmt contains helper methods and constants for converting to and from a human-readable byte format.
//
// bytefmt.ByteSize(100.5*bytefmt.MEGABYTE) // "100.5M"
// bytefmt.ByteSize(uint64(1024)) // "1K"
//
package bytefmt
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
)
const (
BYTE = 1.0 << (10 * iota)
KILOBYTE
MEGABYTE
GIGABYTE
TERABYTE
)
var (
bytesPattern = regexp.MustCompile(`(?i)^(-?\d+(?:\.\d+)?)([KMGT]i?B?|B)$`)
invalidByteQuantityError = errors.New("byte quantity must be a positive integer with a unit of measurement like M, MB, MiB, G, GiB, or GB")
)
// ByteSize returns a human-readable byte string of the form 10M, 12.5K, and so forth. The following units are available:
// T: Terabyte
// G: Gigabyte
// M: Megabyte
// K: Kilobyte
// B: Byte
// The unit that results in the smallest number greater than or equal to 1 is always chosen.
func ByteSize(bytes uint64) string {
unit := ""
value := float32(bytes)
switch {
case bytes >= TERABYTE:
unit = "T"
value = value / TERABYTE
case bytes >= GIGABYTE:
unit = "G"
value = value / GIGABYTE
case bytes >= MEGABYTE:
unit = "M"
value = value / MEGABYTE
case bytes >= KILOBYTE:
unit = "K"
value = value / KILOBYTE
case bytes >= BYTE:
unit = "B"
case bytes == 0:
return "0"
}
stringValue := fmt.Sprintf("%.1f", value)
stringValue = strings.TrimSuffix(stringValue, ".0")
return fmt.Sprintf("%s%s", stringValue, unit)
}
// ToMegabytes parses a string formatted by ByteSize as megabytes.
func ToMegabytes(s string) (uint64, error) {
bytes, err := ToBytes(s)
if err != nil {
return 0, err
}
return bytes / MEGABYTE, nil
}
// ToBytes parses a string formatted by ByteSize as bytes. Note binary-prefixed and SI prefixed units both mean a base-2 units
// KB = K = KiB = 1024
// MB = M = MiB = 1024 * K
// GB = G = GiB = 1024 * M
// TB = T = TiB = 1024 * G
func ToBytes(s string) (uint64, error) {
parts := bytesPattern.FindStringSubmatch(strings.TrimSpace(s))
if len(parts) < 3 {
return 0, invalidByteQuantityError
}
value, err := strconv.ParseFloat(parts[1], 64)
if err != nil || value <= 0 {
return 0, invalidByteQuantityError
}
var bytes uint64
unit := strings.ToUpper(parts[2])
switch unit[:1] {
case "T":
bytes = uint64(value * TERABYTE)
case "G":
bytes = uint64(value * GIGABYTE)
case "M":
bytes = uint64(value * MEGABYTE)
case "K":
bytes = uint64(value * KILOBYTE)
case "B":
bytes = uint64(value * BYTE)
}
return bytes, nil
}