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