zrepl/internal/util/datasizeunit/datasizeunit.go
2024-10-18 19:21:17 +02:00

87 lines
1.5 KiB
Go

package datasizeunit
import (
"errors"
"fmt"
"math"
"regexp"
"strconv"
"strings"
)
type Bits struct {
bits float64
}
func (b Bits) ToBits() float64 { return b.bits }
func (b Bits) ToBytes() float64 { return b.bits / 8 }
func FromBytesInt64(i int64) Bits { return Bits{float64(i) * 8} }
var datarateRegex = regexp.MustCompile(`^([-0-9\.]*)\s*(bit|(|K|Ki|M|Mi|G|Gi|T|Ti)([bB]))$`)
func (r *Bits) UnmarshalYAML(u func(interface{}, bool) error) (_ error) {
var s string
err := u(&s, false)
if err != nil {
return err
}
genericErr := func(err error) error {
var buf strings.Builder
fmt.Fprintf(&buf, "cannot parse %q using regex %s", s, datarateRegex)
if err != nil {
fmt.Fprintf(&buf, ": %s", err)
}
return errors.New(buf.String())
}
match := datarateRegex.FindStringSubmatch(s)
if match == nil {
return genericErr(nil)
}
bps, err := strconv.ParseFloat(match[1], 64)
if err != nil {
return genericErr(err)
}
if match[2] == "bit" {
if math.Round(bps) != bps {
return genericErr(fmt.Errorf("unit bit must be an integer value"))
}
r.bits = bps
return nil
}
factorMap := map[string]uint64{
"": 1,
"K": 1e3,
"M": 1e6,
"G": 1e9,
"T": 1e12,
"Ki": 1 << 10,
"Mi": 1 << 20,
"Gi": 1 << 30,
"Ti": 1 << 40,
}
factor, ok := factorMap[match[3]]
if !ok {
panic(match)
}
baseUnitFactorMap := map[string]uint64{
"b": 1,
"B": 8,
}
baseUnitFactor, ok := baseUnitFactorMap[match[4]]
if !ok {
panic(match)
}
r.bits = bps * float64(factor) * float64(baseUnitFactor)
return nil
}