[#903] config: support floating-point sizes

Rounding is done using the suffix boundary (i.e. floating point value
with `gb` suffix will return size which is an integer number of
megabytes).

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2021-11-11 14:49:27 +03:00 committed by LeL
parent e7fd980951
commit aba09bb853
4 changed files with 31 additions and 5 deletions

View file

@ -1,6 +1,7 @@
package config
import (
"math/bits"
"strings"
"time"
"unicode"
@ -164,12 +165,28 @@ func SizeInBytesSafe(c *Config, name string) uint64 {
// with minor corrections (allow to use both `k` and `kb` forms.
// Seems like viper allows to convert sizes but corresponding parser in `cast` package
// is missing.
func safeMul(a, b uint64) uint64 {
c := a * b
if a > 1 && b > 1 && c/b != a {
// safeMul returns size*multiplier, rounding down to the
// multiplier/1024 number of bytes.
// Returns 0 if overflow is detected.
func safeMul(size float64, multiplier uint64) uint64 {
n := uint64(size)
f := uint64((size - float64(n)) * 1024)
if f != 0 && multiplier != 1 {
s := n<<10 + f
if s < n {
return 0
}
return c
n = s
multiplier >>= 10
}
hi, lo := bits.Mul64(n, multiplier)
if hi != 0 {
return 0
}
return lo
}
// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes
@ -203,6 +220,6 @@ func parseSizeInBytes(sizeStr string) uint64 {
}
}
size := cast.ToUint64(sizeStr)
size := cast.ToFloat64(sizeStr)
return safeMul(size, multiplier)
}

View file

@ -130,6 +130,9 @@ func TestSizeInBytes(t *testing.T) {
require.EqualValues(t, 12*mb, config.SizeInBytesSafe(c, "size_mb"))
require.EqualValues(t, 4*gb, config.SizeInBytesSafe(c, "size_gb"))
require.EqualValues(t, 5*tb, config.SizeInBytesSafe(c, "size_tb"))
require.EqualValues(t, 12, config.SizeInBytesSafe(c, "size_i_am_not_very_clever"))
require.EqualValues(t, tb/2, config.SizeInBytesSafe(c, "size_float"))
require.EqualValues(t, uint64(14*gb+(gb*123/1000/mb*mb)), config.SizeInBytesSafe(c, "size_float_big"))
require.EqualValues(t, 2048, config.SizeInBytesSafe(c, "size_bytes"))
require.EqualValues(t, 123456, config.SizeInBytesSafe(c, "size_bytes_no_suffix"))
})

View file

@ -54,6 +54,9 @@
"size_mb": "12m",
"size_gb": "4g",
"size_tb": "5 TB",
"size_float": ".5t",
"size_float_big": "14.123 gb",
"size_i_am_not_very_clever": "12.12345678",
"size_bytes": "2048b",
"size_bytes_no_suffix": 123456
},

View file

@ -47,6 +47,9 @@ sizes:
size_mb: 12m
size_gb: 4g
size_tb: 5 TB
size_float: .5t
size_float_big: 14.123 gb
size_i_am_not_very_clever: 12.12345678
size_bytes: 2048b
size_bytes_no_suffix: 123456