|
@@ -2,7 +2,6 @@ package units
|
|
|
|
|
|
import (
|
|
|
"fmt"
|
|
|
- "regexp"
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
)
|
|
@@ -26,16 +25,17 @@ const (
|
|
|
PiB = 1024 * TiB
|
|
|
)
|
|
|
|
|
|
-type unitMap map[string]int64
|
|
|
+type unitMap map[byte]int64
|
|
|
|
|
|
var (
|
|
|
- decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
|
|
|
- binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB}
|
|
|
- sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[iI]?[bB]?$`)
|
|
|
+ decimalMap = unitMap{'k': KB, 'm': MB, 'g': GB, 't': TB, 'p': PB}
|
|
|
+ binaryMap = unitMap{'k': KiB, 'm': MiB, 'g': GiB, 't': TiB, 'p': PiB}
|
|
|
)
|
|
|
|
|
|
-var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
|
|
-var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
|
|
|
+var (
|
|
|
+ decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
|
|
+ binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
|
|
|
+)
|
|
|
|
|
|
func getSizeAndUnit(size float64, base float64, _map []string) (float64, string) {
|
|
|
i := 0
|
|
@@ -89,20 +89,66 @@ func RAMInBytes(size string) (int64, error) {
|
|
|
|
|
|
// Parses the human-readable size string into the amount it represents.
|
|
|
func parseSize(sizeStr string, uMap unitMap) (int64, error) {
|
|
|
- matches := sizeRegex.FindStringSubmatch(sizeStr)
|
|
|
- if len(matches) != 4 {
|
|
|
+ // TODO: rewrite to use strings.Cut if there's a space
|
|
|
+ // once Go < 1.18 is deprecated.
|
|
|
+ sep := strings.LastIndexAny(sizeStr, "01234567890. ")
|
|
|
+ if sep == -1 {
|
|
|
+ // There should be at least a digit.
|
|
|
return -1, fmt.Errorf("invalid size: '%s'", sizeStr)
|
|
|
}
|
|
|
+ var num, sfx string
|
|
|
+ if sizeStr[sep] != ' ' {
|
|
|
+ num = sizeStr[:sep+1]
|
|
|
+ sfx = sizeStr[sep+1:]
|
|
|
+ } else {
|
|
|
+ // Omit the space separator.
|
|
|
+ num = sizeStr[:sep]
|
|
|
+ sfx = sizeStr[sep+1:]
|
|
|
+ }
|
|
|
|
|
|
- size, err := strconv.ParseFloat(matches[1], 64)
|
|
|
+ size, err := strconv.ParseFloat(num, 64)
|
|
|
if err != nil {
|
|
|
return -1, err
|
|
|
}
|
|
|
+ // Backward compatibility: reject negative sizes.
|
|
|
+ if size < 0 {
|
|
|
+ return -1, fmt.Errorf("invalid size: '%s'", sizeStr)
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(sfx) == 0 {
|
|
|
+ return int64(size), nil
|
|
|
+ }
|
|
|
|
|
|
- unitPrefix := strings.ToLower(matches[3])
|
|
|
- if mul, ok := uMap[unitPrefix]; ok {
|
|
|
+ // Process the suffix.
|
|
|
+
|
|
|
+ if len(sfx) > 3 { // Too long.
|
|
|
+ goto badSuffix
|
|
|
+ }
|
|
|
+ sfx = strings.ToLower(sfx)
|
|
|
+ // Trivial case: b suffix.
|
|
|
+ if sfx[0] == 'b' {
|
|
|
+ if len(sfx) > 1 { // no extra characters allowed after b.
|
|
|
+ goto badSuffix
|
|
|
+ }
|
|
|
+ return int64(size), nil
|
|
|
+ }
|
|
|
+ // A suffix from the map.
|
|
|
+ if mul, ok := uMap[sfx[0]]; ok {
|
|
|
size *= float64(mul)
|
|
|
+ } else {
|
|
|
+ goto badSuffix
|
|
|
+ }
|
|
|
+
|
|
|
+ // The suffix may have extra "b" or "ib" (e.g. KiB or MB).
|
|
|
+ switch {
|
|
|
+ case len(sfx) == 2 && sfx[1] != 'b':
|
|
|
+ goto badSuffix
|
|
|
+ case len(sfx) == 3 && sfx[1:] != "ib":
|
|
|
+ goto badSuffix
|
|
|
}
|
|
|
|
|
|
return int64(size), nil
|
|
|
+
|
|
|
+badSuffix:
|
|
|
+ return -1, fmt.Errorf("invalid suffix: '%s'", sfx)
|
|
|
}
|