139 lines
2.7 KiB
Go
139 lines
2.7 KiB
Go
package units
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
siUnits = []string{"", "K", "M", "G", "T", "P", "E"}
|
|
)
|
|
|
|
func ToString(n int64, scale int64, suffix, baseSuffix string) string {
|
|
mn := len(siUnits)
|
|
out := make([]string, mn)
|
|
for i, m := range siUnits {
|
|
if n%scale != 0 || i == 0 && n == 0 {
|
|
s := suffix
|
|
if i == 0 {
|
|
s = baseSuffix
|
|
}
|
|
out[mn-1-i] = fmt.Sprintf("%d%s%s", n%scale, m, s)
|
|
}
|
|
n /= scale
|
|
if n == 0 {
|
|
break
|
|
}
|
|
}
|
|
return strings.Join(out, "")
|
|
}
|
|
|
|
// Below code ripped straight from http://golang.org/src/pkg/time/format.go?s=33392:33438#L1123
|
|
var errLeadingInt = errors.New("units: bad [0-9]*") // never printed
|
|
|
|
// leadingInt consumes the leading [0-9]* from s.
|
|
func leadingInt(s string) (x int64, rem string, err error) {
|
|
i := 0
|
|
for ; i < len(s); i++ {
|
|
c := s[i]
|
|
if c < '0' || c > '9' {
|
|
break
|
|
}
|
|
if x >= (1<<63-10)/10 {
|
|
// overflow
|
|
return 0, "", errLeadingInt
|
|
}
|
|
x = x*10 + int64(c) - '0'
|
|
}
|
|
return x, s[i:], nil
|
|
}
|
|
|
|
func ParseUnit(s string, unitMap map[string]float64) (int64, error) {
|
|
// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
|
|
orig := s
|
|
f := float64(0)
|
|
neg := false
|
|
|
|
// Consume [-+]?
|
|
if s != "" {
|
|
c := s[0]
|
|
if c == '-' || c == '+' {
|
|
neg = c == '-'
|
|
s = s[1:]
|
|
}
|
|
}
|
|
// Special case: if all that is left is "0", this is zero.
|
|
if s == "0" {
|
|
return 0, nil
|
|
}
|
|
if s == "" {
|
|
return 0, errors.New("units: invalid " + orig)
|
|
}
|
|
for s != "" {
|
|
g := float64(0) // this element of the sequence
|
|
|
|
var x int64
|
|
var err error
|
|
|
|
// The next character must be [0-9.]
|
|
if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) {
|
|
return 0, errors.New("units: invalid " + orig)
|
|
}
|
|
// Consume [0-9]*
|
|
pl := len(s)
|
|
x, s, err = leadingInt(s)
|
|
if err != nil {
|
|
return 0, errors.New("units: invalid " + orig)
|
|
}
|
|
g = float64(x)
|
|
pre := pl != len(s) // whether we consumed anything before a period
|
|
|
|
// Consume (\.[0-9]*)?
|
|
post := false
|
|
if s != "" && s[0] == '.' {
|
|
s = s[1:]
|
|
pl := len(s)
|
|
x, s, err = leadingInt(s)
|
|
if err != nil {
|
|
return 0, errors.New("units: invalid " + orig)
|
|
}
|
|
scale := 1.0
|
|
for n := pl - len(s); n > 0; n-- {
|
|
scale *= 10
|
|
}
|
|
g += float64(x) / scale
|
|
post = pl != len(s)
|
|
}
|
|
if !pre && !post {
|
|
// no digits (e.g. ".s" or "-.s")
|
|
return 0, errors.New("units: invalid " + orig)
|
|
}
|
|
|
|
// Consume unit.
|
|
i := 0
|
|
for ; i < len(s); i++ {
|
|
c := s[i]
|
|
if c == '.' || ('0' <= c && c <= '9') {
|
|
break
|
|
}
|
|
}
|
|
u := s[:i]
|
|
s = s[i:]
|
|
unit, ok := unitMap[u]
|
|
if !ok {
|
|
return 0, errors.New("units: unknown unit " + u + " in " + orig)
|
|
}
|
|
|
|
f += g * unit
|
|
}
|
|
|
|
if neg {
|
|
f = -f
|
|
}
|
|
if f < float64(-1<<63) || f > float64(1<<63-1) {
|
|
return 0, errors.New("units: overflow parsing unit")
|
|
}
|
|
return int64(f), nil
|
|
}
|