mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2025-10-21 06:47:24 +08:00
minFreeSpace argument allows size like 10GiB
This commit is contained in:
@@ -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
85
weed/util/bytes_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user