mirror of
https://github.com/restic/restic.git
synced 2025-08-23 14:57:37 +00:00
cmd, ui: Move size parsing code and make it more robust
This commit is contained in:
@@ -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)
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user