minFreeSpace argument allows size like 10GiB

This commit is contained in:
bingoohuang
2021-04-26 18:48:34 +08:00
parent d861cbd81b
commit 31f1cdeac2
6 changed files with 227 additions and 27 deletions

View File

@@ -5,8 +5,13 @@ import (
"crypto/md5"
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"io"
"math"
"strconv"
"strings"
"unicode"
)
// BytesToHumanReadable returns the converted human readable representation of the bytes.
@@ -161,3 +166,105 @@ func NewBytesReader(b []byte) *BytesReader {
Reader: bytes.NewReader(b),
}
}
// EmptyTo returns to if s is empty.
func EmptyTo(s, to string) string {
if s == "" {
return to
}
return s
}
var ErrMinFreeSpaceBadValue = errors.New("minFreeSpace is invalid")
// ParseMinFreeSpace parses min free space expression s as percentage like 1,10 or human readable size like 10G
func ParseMinFreeSpace(s string) (float32, error) {
if value, e := strconv.ParseFloat(s, 32); e == nil {
if value < 0 || value > 100 {
return 0, ErrMinFreeSpaceBadValue
}
return float32(value), nil
} else if directSize, e2 := ParseBytes(s); e2 == nil {
if directSize <= 100 {
return 0, ErrMinFreeSpaceBadValue
}
return float32(directSize), nil
}
return 0, ErrMinFreeSpaceBadValue
}
// ParseBytes parses a string representation of bytes into the number
// of bytes it represents.
//
// See Also: Bytes, IBytes.
//
// ParseBytes("42MB") -> 42000000, nil
// ParseBytes("42 MB") -> 42000000, nil
// ParseBytes("42 mib") -> 44040192, nil
func ParseBytes(s string) (uint64, error) {
lastDigit := 0
hasComma := false
for _, r := range s {
if !(unicode.IsDigit(r) || r == '.' || r == ',') {
break
}
if r == ',' {
hasComma = true
}
lastDigit++
}
num := s[:lastDigit]
if hasComma {
num = strings.Replace(num, ",", "", -1)
}
f, err := strconv.ParseFloat(num, 64)
if err != nil {
return 0, err
}
extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
if m, ok := bytesSizeTable[extra]; ok {
f *= float64(m)
if f >= math.MaxUint64 {
return 0, fmt.Errorf("too large: %v", s)
}
return uint64(f), nil
}
return 0, fmt.Errorf("unhandled size name: %v", extra)
}
var bytesSizeTable = map[string]uint64{
"b": Byte, "kib": KiByte, "kb": KByte, "mib": MiByte, "mb": MByte, "gib": GiByte, "gb": GByte,
"tib": TiByte, "tb": TByte, "pib": PiByte, "pb": PByte, "eib": EiByte, "eb": EByte,
// Without suffix
"": Byte, "ki": KiByte, "k": KByte, "mi": MiByte, "m": MByte, "gi": GiByte, "g": GByte,
"ti": TiByte, "t": TByte, "pi": PiByte, "p": PByte, "ei": EiByte, "e": EByte,
}
// IEC Sizes.
// kibis of bits
const (
Byte = 1 << (iota * 10)
KiByte
MiByte
GiByte
TiByte
PiByte
EiByte
)
// SI Sizes.
const (
IByte = 1
KByte = IByte * 1000
MByte = KByte * 1000
GByte = MByte * 1000
TByte = GByte * 1000
PByte = TByte * 1000
EByte = PByte * 1000
)

85
weed/util/bytes_test.go Normal file
View File

@@ -0,0 +1,85 @@
package util
import "testing"
func TestParseMinFreeSpace(t *testing.T) {
tests := []struct {
in string
ok bool
value float32
}{
{in: "42", ok: true, value: 42},
{in: "-1", ok: false, value: 0},
{in: "101", ok: false, value: 0},
{in: "100B", ok: false, value: 0},
{in: "100Ki", ok: true, value: 100 * 1024},
{in: "100GiB", ok: true, value: 100 * 1024 * 1024 * 1024},
{in: "42M", ok: true, value: 42 * 1000 * 1000},
}
for _, p := range tests {
got, err := ParseMinFreeSpace(p.in)
if p.ok != (err == nil) {
t.Errorf("failed to test %v", p.in)
}
if p.ok && err == nil && got != p.value {
t.Errorf("failed to test %v", p.in)
}
}
}
func TestByteParsing(t *testing.T) {
tests := []struct {
in string
exp uint64
}{
{"42", 42},
{"42MB", 42000000},
{"42MiB", 44040192},
{"42mb", 42000000},
{"42mib", 44040192},
{"42MIB", 44040192},
{"42 MB", 42000000},
{"42 MiB", 44040192},
{"42 mb", 42000000},
{"42 mib", 44040192},
{"42 MIB", 44040192},
{"42.5MB", 42500000},
{"42.5MiB", 44564480},
{"42.5 MB", 42500000},
{"42.5 MiB", 44564480},
// No need to say B
{"42M", 42000000},
{"42Mi", 44040192},
{"42m", 42000000},
{"42mi", 44040192},
{"42MI", 44040192},
{"42 M", 42000000},
{"42 Mi", 44040192},
{"42 m", 42000000},
{"42 mi", 44040192},
{"42 MI", 44040192},
{"42.5M", 42500000},
{"42.5Mi", 44564480},
{"42.5 M", 42500000},
{"42.5 Mi", 44564480},
// Bug #42
{"1,005.03 MB", 1005030000},
// Large testing, breaks when too much larger than
// this.
{"12.5 EB", uint64(12.5 * float64(EByte))},
{"12.5 E", uint64(12.5 * float64(EByte))},
{"12.5 EiB", uint64(12.5 * float64(EiByte))},
}
for _, p := range tests {
got, err := ParseBytes(p.in)
if err != nil {
t.Errorf("Couldn't parse %v: %v", p.in, err)
}
if got != p.exp {
t.Errorf("Expected %v for %v, got %v",
p.exp, p.in, got)
}
}
}