Merge pull request #4395 from greatroar/parsebytes

cmd, ui: Move size parsing code and make it more robust
This commit is contained in:
Michael Eischer
2023-07-07 21:24:47 +00:00
committed by GitHub
6 changed files with 97 additions and 84 deletions

View File

@@ -3,7 +3,10 @@ package ui
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"math/bits"
"strconv"
"time"
)
@@ -56,6 +59,44 @@ func FormatSeconds(sec uint64) string {
return fmt.Sprintf("%d:%02d", min, sec)
}
// ParseBytes parses a size in bytes from s. It understands the suffixes
// B, K, M, G and T for powers of 1024.
func ParseBytes(s string) (int64, error) {
if s == "" {
return 0, errors.New("expected size, got empty string")
}
numStr := s[:len(s)-1]
var unit uint64 = 1
switch s[len(s)-1] {
case 'b', 'B':
// use initialized values, do nothing here
case 'k', 'K':
unit = 1024
case 'm', 'M':
unit = 1024 * 1024
case 'g', 'G':
unit = 1024 * 1024 * 1024
case 't', 'T':
unit = 1024 * 1024 * 1024 * 1024
default:
numStr = s
}
value, err := strconv.ParseInt(numStr, 10, 64)
if err != nil {
return 0, err
}
hi, lo := bits.Mul64(uint64(value), unit)
value = int64(lo)
if hi != 0 || value < 0 {
return 0, fmt.Errorf("ParseSize: %q: %w", numStr, strconv.ErrRange)
}
return value, nil
}
func ToJSONString(status interface{}) string {
buf := new(bytes.Buffer)
err := json.NewEncoder(buf).Encode(status)

View File

@@ -1,6 +1,10 @@
package ui
import "testing"
import (
"testing"
"github.com/restic/restic/internal/test"
)
func TestFormatBytes(t *testing.T) {
for _, c := range []struct {
@@ -36,3 +40,47 @@ func TestFormatPercent(t *testing.T) {
}
}
}
func TestParseBytes(t *testing.T) {
for _, tt := range []struct {
in string
expected int64
}{
{"1024", 1024},
{"1024b", 1024},
{"1024B", 1024},
{"1k", 1024},
{"100k", 102400},
{"100K", 102400},
{"10M", 10485760},
{"100m", 104857600},
{"20G", 21474836480},
{"10g", 10737418240},
{"2T", 2199023255552},
{"2t", 2199023255552},
{"9223372036854775807", 1<<63 - 1},
} {
actual, err := ParseBytes(tt.in)
test.OK(t, err)
test.Equals(t, tt.expected, actual)
}
}
func TestParseBytesInvalid(t *testing.T) {
for _, s := range []string{
"",
" ",
"foobar",
"zzz",
"18446744073709551615", // 1<<64-1.
"9223372036854775807k", // 1<<63-1 kiB.
"9999999999999M",
"99999999999999999999",
} {
v, err := ParseBytes(s)
if err == nil {
t.Errorf("wanted error for invalid value %q, got nil", s)
}
test.Equals(t, int64(0), v)
}
}