internal/common: introduce function to convert data sizes

This function will be used to parse filesystem sizes specified as
string.
This commit is contained in:
Martin Sehnoutka 2021-10-18 13:00:36 +02:00 committed by Ondřej Budai
parent 2d13fa3a87
commit 7d6dadb598
2 changed files with 92 additions and 1 deletions

View file

@ -1,8 +1,12 @@
package common
import (
"fmt"
"regexp"
"runtime"
"sort"
"strconv"
"strings"
)
var RuntimeGOARCH = runtime.GOARCH
@ -36,3 +40,51 @@ func IsStringInSortedSlice(slice []string, s string) bool {
}
return false
}
// DataSizeToUint64 converts a size specified as a string in KB/KiB/MB/etc. to
// a number of bytes represented by uint64.
func DataSizeToUint64(size string) (uint64, error) {
// Pre-process the input
size = strings.TrimSpace(size)
// Get the number from the string
plain_number := regexp.MustCompile(`[[:digit:]]+`)
number_as_str := plain_number.FindString(size)
if number_as_str == "" {
return 0, fmt.Errorf("the size string doesn't contain any number: %s", size)
}
// Parse the number into integer
return_size, err := strconv.ParseUint(number_as_str, 10, 64)
if err != nil {
return 0, fmt.Errorf("failed to parse size as integer: %s", number_as_str)
}
// List of all supported units (from kB to TB and KiB to TiB)
supported_units := []struct {
re *regexp.Regexp
multiple uint64
}{
{regexp.MustCompile(`^\s*[[:digit:]]+\s*kB$`), 1000},
{regexp.MustCompile(`^\s*[[:digit:]]+\s*KiB$`), 1024},
{regexp.MustCompile(`^\s*[[:digit:]]+\s*MB$`), 1000 * 1000},
{regexp.MustCompile(`^\s*[[:digit:]]+\s*MiB$`), 1024 * 1024},
{regexp.MustCompile(`^\s*[[:digit:]]+\s*GB$`), 1000 * 1000 * 1000},
{regexp.MustCompile(`^\s*[[:digit:]]+\s*GiB$`), 1024 * 1024 * 1024},
{regexp.MustCompile(`^\s*[[:digit:]]+\s*TB$`), 1000 * 1000 * 1000 * 1000},
{regexp.MustCompile(`^\s*[[:digit:]]+\s*TiB$`), 1024 * 1024 * 1024 * 1024},
{regexp.MustCompile(`^\s*[[:digit:]]+$`), 1},
}
for _, unit := range supported_units {
if unit.re.MatchString(size) {
return_size *= unit.multiple
return return_size, nil
}
}
// In case the strign didn't match any of the above regexes, return nil
// even if a number was found. This is to prevent users from submitting
// unknown units.
return 0, fmt.Errorf("unknown data size units in string: %s", size)
}

View file

@ -2,8 +2,10 @@ package common
import (
"errors"
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCurrentArchAMD64(t *testing.T) {
@ -52,3 +54,40 @@ func TestIsStringInSortedSlice(t *testing.T) {
assert.False(t, IsStringInSortedSlice([]string{"bart", "lisa", "marge"}, ""))
assert.False(t, IsStringInSortedSlice([]string{}, "homer"))
}
func TestDataSizeToUint64(t *testing.T) {
cases := []struct {
input string
success bool
output uint64
}{
{"123", true, 123},
{"123 kB", true, 123000},
{"123 KiB", true, 123 * 1024},
{"123 MB", true, 123 * 1000 * 1000},
{"123 MiB", true, 123 * 1024 * 1024},
{"123 GB", true, 123 * 1000 * 1000 * 1000},
{"123 GiB", true, 123 * 1024 * 1024 * 1024},
{"123 TB", true, 123 * 1000 * 1000 * 1000 * 1000},
{"123 TiB", true, 123 * 1024 * 1024 * 1024 * 1024},
{"123kB", true, 123000},
{"123KiB", true, 123 * 1024},
{" 123 ", true, 123},
{" 123kB ", true, 123000},
{" 123KiB ", true, 123 * 1024},
{"123 KB", false, 0},
{"123 mb", false, 0},
{"123 PB", false, 0},
{"123 PiB", false, 0},
}
for _, c := range cases {
result, err := DataSizeToUint64(c.input)
if c.success {
require.Nil(t, err)
assert.EqualValues(t, c.output, result)
} else {
assert.NotNil(t, err)
}
}
}