// 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 }