106 lines
2.5 KiB
Go
106 lines
2.5 KiB
Go
|
// 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
|
||
|
KILOBYTE = 1024 * BYTE
|
||
|
MEGABYTE = 1024 * KILOBYTE
|
||
|
GIGABYTE = 1024 * MEGABYTE
|
||
|
TERABYTE = 1024 * GIGABYTE
|
||
|
)
|
||
|
|
||
|
var bytesPattern *regexp.Regexp = regexp.MustCompile(`(?i)^(-?\d+(?:\.\d+)?)([KMGT]i?B?|B)$`)
|
||
|
|
||
|
var 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
|
||
|
}
|