mirror of
https://github.com/ClusterCockpit/cc-units.git
synced 2025-04-29 16:01:42 +02:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e48863630b | ||
|
dcee847b92 | ||
|
b1f37c852b | ||
1dda091bf7 | |||
|
7890cf25d4 | ||
|
ddcfc07745 |
@ -1,9 +1,8 @@
|
||||
# ccUnits - A unit system for ClusterCockpit
|
||||
# cc-units - A unit system for ClusterCockpit
|
||||
|
||||
When working with metrics, the problem comes up that they may use different unit name but have the same unit in fact. There are a lot of real world examples like 'kB' and 'Kbyte'. In CC Metric Collector, the Collectors read data from different sources which may use different units or the programmer specifies a unit for a metric by hand. The ccUnits system is not comparable with the SI unit system. If you are looking for a package for the SI units, see [here](https://pkg.go.dev/github.com/gurre/si).
|
||||
When working with metrics, the problem comes up that they may use different unit name but have the same unit in fact. There are a lot of real world examples like 'kB' and 'Kbyte'. In [cc-metric-collector](https://github.com/ClusterCockpit/cc-metric-collector), the collectors read data from different sources which may use different units or the programmer specifies a unit for a metric by hand. The cc-units system is not comparable with the SI unit system. If you are looking for a package for the SI units, see [here](https://pkg.go.dev/github.com/gurre/si).
|
||||
|
||||
In order to enable unit comparison and conversion, the ccUnits package provides some helpers:
|
||||
There are basically two important functions:
|
||||
```go
|
||||
NewUnit(unit string) Unit // create a new unit from some string like 'GHz', 'Mbyte' or 'kevents/s'
|
||||
func GetUnitUnitFactor(in Unit, out Unit) (func(value float64) float64, error) // Get conversion function between two units
|
||||
|
244
ccUnitMeasure.go
244
ccUnitMeasure.go
@ -12,7 +12,7 @@ const (
|
||||
TemperatureC
|
||||
TemperatureF
|
||||
Rotation
|
||||
Hertz
|
||||
Frequency
|
||||
Time
|
||||
Watt
|
||||
Joule
|
||||
@ -22,171 +22,113 @@ const (
|
||||
Events
|
||||
)
|
||||
|
||||
type MeasureData struct {
|
||||
Long string
|
||||
Short string
|
||||
Regex string
|
||||
}
|
||||
|
||||
// Different names and regex used for input and output
|
||||
var InvalidMeasureLong string = "Invalid"
|
||||
var InvalidMeasureShort string = "inval"
|
||||
var MeasuresMap map[Measure]MeasureData = map[Measure]MeasureData{
|
||||
Bytes: {
|
||||
Long: "byte",
|
||||
Short: "B",
|
||||
Regex: "^([bB][yY]?[tT]?[eE]?[sS]?)",
|
||||
},
|
||||
Flops: {
|
||||
Long: "Flops",
|
||||
Short: "Flops",
|
||||
Regex: "^([fF][lL]?[oO]?[pP]?[sS]?)",
|
||||
},
|
||||
Percentage: {
|
||||
Long: "Percent",
|
||||
Short: "%",
|
||||
Regex: "^(%|[pP]ercent)",
|
||||
},
|
||||
TemperatureC: {
|
||||
Long: "DegreeC",
|
||||
Short: "degC",
|
||||
Regex: "^(deg[Cc]|°[cC])",
|
||||
},
|
||||
TemperatureF: {
|
||||
Long: "DegreeF",
|
||||
Short: "degF",
|
||||
Regex: "^(deg[fF]|°[fF])",
|
||||
},
|
||||
Rotation: {
|
||||
Long: "RPM",
|
||||
Short: "RPM",
|
||||
Regex: "^([rR][pP][mM])",
|
||||
},
|
||||
Frequency: {
|
||||
Long: "Hertz",
|
||||
Short: "Hz",
|
||||
Regex: "^([hH][eE]?[rR]?[tT]?[zZ])",
|
||||
},
|
||||
Time: {
|
||||
Long: "Seconds",
|
||||
Short: "s",
|
||||
Regex: "^([sS][eE]?[cC]?[oO]?[nN]?[dD]?[sS]?)",
|
||||
},
|
||||
Cycles: {
|
||||
Long: "Cycles",
|
||||
Short: "cyc",
|
||||
Regex: "^([cC][yY][cC]?[lL]?[eE]?[sS]?)",
|
||||
},
|
||||
Watt: {
|
||||
Long: "Watts",
|
||||
Short: "W",
|
||||
Regex: "^([wW][aA]?[tT]?[tT]?[sS]?)",
|
||||
},
|
||||
Joule: {
|
||||
Long: "Joules",
|
||||
Short: "J",
|
||||
Regex: "^([jJ][oO]?[uU]?[lL]?[eE]?[sS]?)",
|
||||
},
|
||||
Requests: {
|
||||
Long: "Requests",
|
||||
Short: "requests",
|
||||
Regex: "^([rR][eE][qQ][uU]?[eE]?[sS]?[tT]?[sS]?)",
|
||||
},
|
||||
Packets: {
|
||||
Long: "Packets",
|
||||
Short: "packets",
|
||||
Regex: "^([pP][aA]?[cC]?[kK][eE]?[tT][sS]?)",
|
||||
},
|
||||
Events: {
|
||||
Long: "Events",
|
||||
Short: "events",
|
||||
Regex: "^([eE][vV]?[eE]?[nN][tT][sS]?)",
|
||||
},
|
||||
}
|
||||
|
||||
// String returns the long string for the measure like 'Percent' or 'Seconds'
|
||||
func (m *Measure) String() string {
|
||||
switch *m {
|
||||
case Bytes:
|
||||
return "byte"
|
||||
case Flops:
|
||||
return "Flops"
|
||||
case Percentage:
|
||||
return "Percent"
|
||||
case TemperatureC:
|
||||
return "DegreeC"
|
||||
case TemperatureF:
|
||||
return "DegreeF"
|
||||
case Rotation:
|
||||
return "RPM"
|
||||
case Hertz:
|
||||
return "Hertz"
|
||||
case Time:
|
||||
return "Seconds"
|
||||
case Watt:
|
||||
return "Watts"
|
||||
case Joule:
|
||||
return "Joules"
|
||||
case Cycles:
|
||||
return "Cycles"
|
||||
case Requests:
|
||||
return "Requests"
|
||||
case Packets:
|
||||
return "Packets"
|
||||
case Events:
|
||||
return "Events"
|
||||
case InvalidMeasure:
|
||||
return "Invalid"
|
||||
default:
|
||||
return "Unknown"
|
||||
if data, ok := MeasuresMap[*m]; ok {
|
||||
return data.Long
|
||||
}
|
||||
return InvalidMeasureLong
|
||||
}
|
||||
|
||||
// Short returns the short string for the measure like 'B' (Bytes), 's' (Time) or 'W' (Watt). Is is recommened to use Short() over String().
|
||||
func (m *Measure) Short() string {
|
||||
switch *m {
|
||||
case Bytes:
|
||||
return "B"
|
||||
case Flops:
|
||||
return "Flops"
|
||||
case Percentage:
|
||||
return "Percent"
|
||||
case TemperatureC:
|
||||
return "degC"
|
||||
case TemperatureF:
|
||||
return "degF"
|
||||
case Rotation:
|
||||
return "RPM"
|
||||
case Hertz:
|
||||
return "Hz"
|
||||
case Time:
|
||||
return "s"
|
||||
case Watt:
|
||||
return "W"
|
||||
case Joule:
|
||||
return "J"
|
||||
case Cycles:
|
||||
return "cyc"
|
||||
case Requests:
|
||||
return "requests"
|
||||
case Packets:
|
||||
return "packets"
|
||||
case Events:
|
||||
return "events"
|
||||
case InvalidMeasure:
|
||||
return "Invalid"
|
||||
default:
|
||||
return "Unknown"
|
||||
if data, ok := MeasuresMap[*m]; ok {
|
||||
return data.Short
|
||||
}
|
||||
return InvalidMeasureShort
|
||||
}
|
||||
|
||||
const bytesRegexStr = `^([bB][yY]?[tT]?[eE]?[sS]?)`
|
||||
const flopsRegexStr = `^([fF][lL]?[oO]?[pP]?[sS]?)`
|
||||
const percentRegexStr = `^(%|[pP]ercent)`
|
||||
const degreeCRegexStr = `^(deg[Cc]|°[cC])`
|
||||
const degreeFRegexStr = `^(deg[fF]|°[fF])`
|
||||
const rpmRegexStr = `^([rR][pP][mM])`
|
||||
const hertzRegexStr = `^([hH][eE]?[rR]?[tT]?[zZ])`
|
||||
const timeRegexStr = `^([sS][eE]?[cC]?[oO]?[nN]?[dD]?[sS]?)`
|
||||
const wattRegexStr = `^([wW][aA]?[tT]?[tT]?[sS]?)`
|
||||
const jouleRegexStr = `^([jJ][oO]?[uU]?[lL]?[eE]?[sS]?)`
|
||||
const cyclesRegexStr = `^([cC][yY][cC]?[lL]?[eE]?[sS]?)`
|
||||
const requestsRegexStr = `^([rR][eE][qQ][uU]?[eE]?[sS]?[tT]?[sS]?)`
|
||||
const packetsRegexStr = `^([pP][aA]?[cC]?[kK][eE]?[tT][sS]?)`
|
||||
const eventsRegexStr = `^([eE][vV]?[eE]?[nN][tT][sS]?)`
|
||||
|
||||
var bytesRegex = regexp.MustCompile(bytesRegexStr)
|
||||
var flopsRegex = regexp.MustCompile(flopsRegexStr)
|
||||
var percentRegex = regexp.MustCompile(percentRegexStr)
|
||||
var degreeCRegex = regexp.MustCompile(degreeCRegexStr)
|
||||
var degreeFRegex = regexp.MustCompile(degreeFRegexStr)
|
||||
var rpmRegex = regexp.MustCompile(rpmRegexStr)
|
||||
var hertzRegex = regexp.MustCompile(hertzRegexStr)
|
||||
var timeRegex = regexp.MustCompile(timeRegexStr)
|
||||
var wattRegex = regexp.MustCompile(wattRegexStr)
|
||||
var jouleRegex = regexp.MustCompile(jouleRegexStr)
|
||||
var cyclesRegex = regexp.MustCompile(cyclesRegexStr)
|
||||
var requestsRegex = regexp.MustCompile(requestsRegexStr)
|
||||
var packetsRegex = regexp.MustCompile(packetsRegexStr)
|
||||
var eventsRegex = regexp.MustCompile(eventsRegexStr)
|
||||
|
||||
// NewMeasure creates a new measure out of a string representing a measure like 'Bytes', 'Flops' and 'precent'.
|
||||
// It uses regular expressions for matching.
|
||||
func NewMeasure(unit string) Measure {
|
||||
var match []string
|
||||
match = bytesRegex.FindStringSubmatch(unit)
|
||||
for m, data := range MeasuresMap {
|
||||
regex := regexp.MustCompile(data.Regex)
|
||||
match := regex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Bytes
|
||||
return m
|
||||
}
|
||||
match = flopsRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Flops
|
||||
}
|
||||
match = percentRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Percentage
|
||||
}
|
||||
match = degreeCRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return TemperatureC
|
||||
}
|
||||
match = degreeFRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return TemperatureF
|
||||
}
|
||||
match = rpmRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Rotation
|
||||
}
|
||||
match = hertzRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Hertz
|
||||
}
|
||||
match = timeRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Time
|
||||
}
|
||||
match = cyclesRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Cycles
|
||||
}
|
||||
match = wattRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Watt
|
||||
}
|
||||
match = jouleRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Joule
|
||||
}
|
||||
match = requestsRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Requests
|
||||
}
|
||||
match = packetsRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Packets
|
||||
}
|
||||
match = eventsRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Events
|
||||
}
|
||||
return InvalidMeasure
|
||||
}
|
||||
|
264
ccUnitPrefix.go
264
ccUnitPrefix.go
@ -1,6 +1,8 @@
|
||||
package ccunits
|
||||
|
||||
import "regexp"
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type Prefix float64
|
||||
|
||||
@ -27,148 +29,146 @@ const (
|
||||
Zebi = 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
|
||||
Yobi = 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
|
||||
)
|
||||
const prefixRegexStr = `^([kKmMgGtTpP]?[i]?)(.*)`
|
||||
const PrefixUnitSplitRegexStr = `^([kKmMgGtTpPeEzZyY]?[i]?)(.*)`
|
||||
|
||||
var prefixRegex = regexp.MustCompile(prefixRegexStr)
|
||||
var prefixUnitSplitRegex = regexp.MustCompile(PrefixUnitSplitRegexStr)
|
||||
|
||||
type PrefixData struct {
|
||||
Long string
|
||||
Short string
|
||||
Regex string
|
||||
}
|
||||
|
||||
// Different names and regex used for input and output
|
||||
var InvalidPrefixLong string = "Invalid"
|
||||
var InvalidPrefixShort string = "inval"
|
||||
var PrefixDataMap map[Prefix]PrefixData = map[Prefix]PrefixData{
|
||||
Base: {
|
||||
Long: "",
|
||||
Short: "",
|
||||
Regex: "^$",
|
||||
},
|
||||
Kilo: {
|
||||
Long: "Kilo",
|
||||
Short: "K",
|
||||
Regex: "^[kK]$",
|
||||
},
|
||||
Mega: {
|
||||
Long: "Mega",
|
||||
Short: "M",
|
||||
Regex: "^[M]$",
|
||||
},
|
||||
Giga: {
|
||||
Long: "Giga",
|
||||
Short: "G",
|
||||
Regex: "^[gG]$",
|
||||
},
|
||||
Tera: {
|
||||
Long: "Tera",
|
||||
Short: "T",
|
||||
Regex: "^[tT]$",
|
||||
},
|
||||
Peta: {
|
||||
Long: "Peta",
|
||||
Short: "P",
|
||||
Regex: "^[pP]$",
|
||||
},
|
||||
Exa: {
|
||||
Long: "Exa",
|
||||
Short: "E",
|
||||
Regex: "^[eE]$",
|
||||
},
|
||||
Zetta: {
|
||||
Long: "Zetta",
|
||||
Short: "Z",
|
||||
Regex: "^[zZ]$",
|
||||
},
|
||||
Yotta: {
|
||||
Long: "Yotta",
|
||||
Short: "Y",
|
||||
Regex: "^[yY]$",
|
||||
},
|
||||
Milli: {
|
||||
Long: "Milli",
|
||||
Short: "m",
|
||||
Regex: "^[m]$",
|
||||
},
|
||||
Micro: {
|
||||
Long: "Micro",
|
||||
Short: "u",
|
||||
Regex: "^[u]$",
|
||||
},
|
||||
Nano: {
|
||||
Long: "Nano",
|
||||
Short: "n",
|
||||
Regex: "^[n]$",
|
||||
},
|
||||
Kibi: {
|
||||
Long: "Kibi",
|
||||
Short: "Ki",
|
||||
Regex: "^[kK][i]$",
|
||||
},
|
||||
Mebi: {
|
||||
Long: "Mebi",
|
||||
Short: "Mi",
|
||||
Regex: "^[M][i]$",
|
||||
},
|
||||
Gibi: {
|
||||
Long: "Gibi",
|
||||
Short: "Gi",
|
||||
Regex: "^[gG][i]$",
|
||||
},
|
||||
Tebi: {
|
||||
Long: "Tebi",
|
||||
Short: "Ti",
|
||||
Regex: "^[tT][i]$",
|
||||
},
|
||||
Pebi: {
|
||||
Long: "Pebi",
|
||||
Short: "Pi",
|
||||
Regex: "^[pP][i]$",
|
||||
},
|
||||
Exbi: {
|
||||
Long: "Exbi",
|
||||
Short: "Ei",
|
||||
Regex: "^[eE][i]$",
|
||||
},
|
||||
Zebi: {
|
||||
Long: "Zebi",
|
||||
Short: "Zi",
|
||||
Regex: "^[zZ][i]$",
|
||||
},
|
||||
Yobi: {
|
||||
Long: "Yobi",
|
||||
Short: "Yi",
|
||||
Regex: "^[yY][i]$",
|
||||
},
|
||||
}
|
||||
|
||||
// String returns the long string for the prefix like 'Kilo' or 'Mega'
|
||||
func (s *Prefix) String() string {
|
||||
switch *s {
|
||||
case InvalidPrefix:
|
||||
return "Inval"
|
||||
case Base:
|
||||
return ""
|
||||
case Kilo:
|
||||
return "Kilo"
|
||||
case Mega:
|
||||
return "Mega"
|
||||
case Giga:
|
||||
return "Giga"
|
||||
case Tera:
|
||||
return "Tera"
|
||||
case Peta:
|
||||
return "Peta"
|
||||
case Exa:
|
||||
return "Exa"
|
||||
case Zetta:
|
||||
return "Zetta"
|
||||
case Yotta:
|
||||
return "Yotta"
|
||||
case Milli:
|
||||
return "Milli"
|
||||
case Micro:
|
||||
return "Micro"
|
||||
case Nano:
|
||||
return "Nano"
|
||||
case Kibi:
|
||||
return "Kibi"
|
||||
case Mebi:
|
||||
return "Mebi"
|
||||
case Gibi:
|
||||
return "Gibi"
|
||||
case Tebi:
|
||||
return "Tebi"
|
||||
default:
|
||||
return "Unkn"
|
||||
func (p *Prefix) String() string {
|
||||
if data, ok := PrefixDataMap[*p]; ok {
|
||||
return data.Long
|
||||
}
|
||||
return InvalidMeasureLong
|
||||
}
|
||||
|
||||
// Prefix returns the short string for the prefix like 'K', 'M' or 'G'. Is is recommened to use Prefix() over String().
|
||||
func (s *Prefix) Prefix() string {
|
||||
switch *s {
|
||||
case InvalidPrefix:
|
||||
return "<inval>"
|
||||
case Base:
|
||||
return ""
|
||||
case Kilo:
|
||||
return "K"
|
||||
case Mega:
|
||||
return "M"
|
||||
case Giga:
|
||||
return "G"
|
||||
case Tera:
|
||||
return "T"
|
||||
case Peta:
|
||||
return "P"
|
||||
case Exa:
|
||||
return "E"
|
||||
case Zetta:
|
||||
return "Z"
|
||||
case Yotta:
|
||||
return "Y"
|
||||
case Milli:
|
||||
return "m"
|
||||
case Micro:
|
||||
return "u"
|
||||
case Nano:
|
||||
return "n"
|
||||
case Kibi:
|
||||
return "Ki"
|
||||
case Mebi:
|
||||
return "Mi"
|
||||
case Gibi:
|
||||
return "Gi"
|
||||
case Tebi:
|
||||
return "Ti"
|
||||
default:
|
||||
return "<unkn>"
|
||||
func (p *Prefix) Prefix() string {
|
||||
if data, ok := PrefixDataMap[*p]; ok {
|
||||
return data.Short
|
||||
}
|
||||
return InvalidMeasureShort
|
||||
}
|
||||
|
||||
// NewPrefix creates a new prefix out of a string representing a unit like 'k', 'K', 'M' or 'G'.
|
||||
func NewPrefix(prefix string) Prefix {
|
||||
switch prefix {
|
||||
case "k":
|
||||
return Kilo
|
||||
case "K":
|
||||
return Kilo
|
||||
case "m":
|
||||
return Milli
|
||||
case "M":
|
||||
return Mega
|
||||
case "g":
|
||||
return Giga
|
||||
case "G":
|
||||
return Giga
|
||||
case "t":
|
||||
return Tera
|
||||
case "T":
|
||||
return Tera
|
||||
case "p":
|
||||
return Peta
|
||||
case "P":
|
||||
return Peta
|
||||
case "e":
|
||||
return Exa
|
||||
case "E":
|
||||
return Exa
|
||||
case "z":
|
||||
return Zetta
|
||||
case "Z":
|
||||
return Zetta
|
||||
case "y":
|
||||
return Yotta
|
||||
case "Y":
|
||||
return Yotta
|
||||
case "u":
|
||||
return Micro
|
||||
case "n":
|
||||
return Nano
|
||||
case "ki":
|
||||
return Kibi
|
||||
case "Ki":
|
||||
return Kibi
|
||||
case "Mi":
|
||||
return Mebi
|
||||
case "gi":
|
||||
return Gibi
|
||||
case "Gi":
|
||||
return Gibi
|
||||
case "Ti":
|
||||
return Tebi
|
||||
case "":
|
||||
return Base
|
||||
default:
|
||||
return InvalidPrefix
|
||||
for p, data := range PrefixDataMap {
|
||||
regex := regexp.MustCompile(data.Regex)
|
||||
match := regex.FindStringSubmatch(prefix)
|
||||
if match != nil {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return InvalidPrefix
|
||||
}
|
||||
|
31
ccUnits.go
31
ccUnits.go
@ -1,3 +1,4 @@
|
||||
// Unit system for cluster monitoring metrics like bytes, flops and events
|
||||
package ccunits
|
||||
|
||||
import (
|
||||
@ -16,10 +17,10 @@ type Unit interface {
|
||||
String() string
|
||||
Short() string
|
||||
AddUnitDenominator(div Measure)
|
||||
getPrefix() Prefix
|
||||
getMeasure() Measure
|
||||
getUnitDenominator() Measure
|
||||
setPrefix(p Prefix)
|
||||
GetPrefix() Prefix
|
||||
GetMeasure() Measure
|
||||
GetUnitDenominator() Measure
|
||||
SetPrefix(p Prefix)
|
||||
}
|
||||
|
||||
var INVALID_UNIT = NewUnit("foobar")
|
||||
@ -54,19 +55,19 @@ func (u *unit) AddUnitDenominator(div Measure) {
|
||||
u.divMeasure = div
|
||||
}
|
||||
|
||||
func (u *unit) getPrefix() Prefix {
|
||||
func (u *unit) GetPrefix() Prefix {
|
||||
return u.prefix
|
||||
}
|
||||
|
||||
func (u *unit) setPrefix(p Prefix) {
|
||||
func (u *unit) SetPrefix(p Prefix) {
|
||||
u.prefix = p
|
||||
}
|
||||
|
||||
func (u *unit) getMeasure() Measure {
|
||||
func (u *unit) GetMeasure() Measure {
|
||||
return u.measure
|
||||
}
|
||||
|
||||
func (u *unit) getUnitDenominator() Measure {
|
||||
func (u *unit) GetUnitDenominator() Measure {
|
||||
return u.divMeasure
|
||||
}
|
||||
|
||||
@ -161,8 +162,8 @@ func GetPrefixStringPrefixStringFactor(in string, out string) func(value interfa
|
||||
func GetUnitPrefixFactor(in Unit, out Prefix) (func(value interface{}) interface{}, Unit) {
|
||||
outUnit := NewUnit(in.Short())
|
||||
if outUnit.Valid() {
|
||||
outUnit.setPrefix(out)
|
||||
conv := GetPrefixPrefixFactor(in.getPrefix(), out)
|
||||
outUnit.SetPrefix(out)
|
||||
conv := GetPrefixPrefixFactor(in.GetPrefix(), out)
|
||||
return conv, outUnit
|
||||
}
|
||||
return nil, INVALID_UNIT
|
||||
@ -186,14 +187,14 @@ func GetUnitStringPrefixStringFactor(in string, out string) (func(value interfac
|
||||
// It is basically a wrapper for GetPrefixPrefixFactor with some special cases for temperature
|
||||
// conversion between Fahrenheit and Celsius.
|
||||
func GetUnitUnitFactor(in Unit, out Unit) (func(value interface{}) interface{}, error) {
|
||||
if in.getMeasure() == TemperatureC && out.getMeasure() == TemperatureF {
|
||||
if in.GetMeasure() == TemperatureC && out.GetMeasure() == TemperatureF {
|
||||
return convertTempC2TempF, nil
|
||||
} else if in.getMeasure() == TemperatureF && out.getMeasure() == TemperatureC {
|
||||
} else if in.GetMeasure() == TemperatureF && out.GetMeasure() == TemperatureC {
|
||||
return convertTempF2TempC, nil
|
||||
} else if in.getMeasure() != out.getMeasure() || in.getUnitDenominator() != out.getUnitDenominator() {
|
||||
} else if in.GetMeasure() != out.GetMeasure() || in.GetUnitDenominator() != out.GetUnitDenominator() {
|
||||
return func(value interface{}) interface{} { return 1.0 }, fmt.Errorf("invalid measures in in and out Unit")
|
||||
}
|
||||
return GetPrefixPrefixFactor(in.getPrefix(), out.getPrefix()), nil
|
||||
return GetPrefixPrefixFactor(in.GetPrefix(), out.GetPrefix()), nil
|
||||
}
|
||||
|
||||
// NewUnit creates a new unit out of a string representing a unit like 'Mbyte/s' or 'GHz'.
|
||||
@ -204,7 +205,7 @@ func NewUnit(unitStr string) Unit {
|
||||
measure: InvalidMeasure,
|
||||
divMeasure: InvalidMeasure,
|
||||
}
|
||||
matches := prefixRegex.FindStringSubmatch(unitStr)
|
||||
matches := prefixUnitSplitRegex.FindStringSubmatch(unitStr)
|
||||
if len(matches) > 2 {
|
||||
pre := NewPrefix(matches[1])
|
||||
measures := strings.Split(matches[2], "/")
|
||||
|
@ -2,6 +2,7 @@ package ccunits
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -66,7 +67,7 @@ func TestUnitsExact(t *testing.T) {
|
||||
{"F/B", NewUnit("flops/Bytes")},
|
||||
}
|
||||
compareUnitExact := func(in, out Unit) bool {
|
||||
if in.getMeasure() == out.getMeasure() && in.getUnitDenominator() == out.getUnitDenominator() && in.getPrefix() == out.getPrefix() {
|
||||
if in.GetMeasure() == out.GetMeasure() && in.GetUnitDenominator() == out.GetUnitDenominator() && in.GetPrefix() == out.GetPrefix() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -75,6 +76,8 @@ func TestUnitsExact(t *testing.T) {
|
||||
u := NewUnit(c.in)
|
||||
if (!u.Valid()) || (!compareUnitExact(u, c.want)) {
|
||||
t.Errorf("func NewUnit(%q) == %q, want %q", c.in, u.String(), c.want.String())
|
||||
} else {
|
||||
t.Logf("NewUnit(%q) == %q", c.in, u.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -96,8 +99,8 @@ func TestUnitUnitConversion(t *testing.T) {
|
||||
{"mb", NewUnit("MBytes"), 1.0},
|
||||
}
|
||||
compareUnitWithPrefix := func(in, out Unit, factor float64) bool {
|
||||
if in.getMeasure() == out.getMeasure() && in.getUnitDenominator() == out.getUnitDenominator() {
|
||||
if f := GetPrefixPrefixFactor(in.getPrefix(), out.getPrefix()); f(1.0) == factor {
|
||||
if in.GetMeasure() == out.GetMeasure() && in.GetUnitDenominator() == out.GetUnitDenominator() {
|
||||
if f := GetPrefixPrefixFactor(in.GetPrefix(), out.GetPrefix()); f(1.0) == factor {
|
||||
return true
|
||||
} else {
|
||||
fmt.Println(f(1.0))
|
||||
@ -108,7 +111,9 @@ func TestUnitUnitConversion(t *testing.T) {
|
||||
for _, c := range testCases {
|
||||
u := NewUnit(c.in)
|
||||
if (!u.Valid()) || (!compareUnitWithPrefix(u, c.want, c.prefixFactor)) {
|
||||
t.Errorf("GetPrefixPrefixFactor(%q, %q) invalid, want %q with factor %f", c.in, u.String(), c.want.String(), c.prefixFactor)
|
||||
t.Errorf("GetPrefixPrefixFactor(%q, %q) invalid, want %q with factor %g", c.in, u.String(), c.want.String(), c.prefixFactor)
|
||||
} else {
|
||||
t.Logf("GetPrefixPrefixFactor(%q, %q) = %g", c.in, c.want.String(), c.prefixFactor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,9 +144,10 @@ func TestUnitPrefixConversion(t *testing.T) {
|
||||
u := NewUnit(c.in)
|
||||
p := NewPrefix(c.want)
|
||||
if (!u.Valid()) || (!compareUnitPrefix(u, p, c.prefixFactor, c.wantUnit)) {
|
||||
t.Errorf("GetUnitPrefixFactor(%q, %q) invalid, want %q with factor %f", c.in, p.Prefix(), c.wantUnit.String(), c.prefixFactor)
|
||||
t.Errorf("GetUnitPrefixFactor(%q, %q) invalid, want %q with factor %g", c.in, p.Prefix(), c.wantUnit.String(), c.prefixFactor)
|
||||
} else {
|
||||
t.Logf("GetUnitPrefixFactor(%q, %q) = %g", c.in, c.wantUnit.String(), c.prefixFactor)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,24 +163,39 @@ func TestPrefixPrefixConversion(t *testing.T) {
|
||||
{"", "M", 1e-6},
|
||||
{"", "m", 1e3},
|
||||
{"m", "n", 1e6},
|
||||
//{"", "n", 1e9} does not work because of IEEE rounding problems
|
||||
}
|
||||
comparePrefixPrefix := func(in Prefix, out Prefix, factor float64) bool {
|
||||
if in != InvalidPrefix && out != InvalidPrefix {
|
||||
conv := GetPrefixPrefixFactor(in, out)
|
||||
value := conv(1.0)
|
||||
fmt.Println("1.0 -> ", value, ", want ", factor)
|
||||
if value == factor {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
//{"", "n", 1e9}, //does not work because of IEEE rounding problems
|
||||
}
|
||||
for _, c := range testCases {
|
||||
i := NewPrefix(c.in)
|
||||
o := NewPrefix(c.want)
|
||||
if !comparePrefixPrefix(i, o, c.prefixFactor) {
|
||||
t.Errorf("GetPrefixPrefixFactor(%q, %q) invalid, want %q with factor %f", c.in, c.want, o.Prefix(), c.prefixFactor)
|
||||
if i != InvalidPrefix && o != InvalidPrefix {
|
||||
conv := GetPrefixPrefixFactor(i, o)
|
||||
value := conv(1.0)
|
||||
if value != c.prefixFactor {
|
||||
t.Errorf("GetPrefixPrefixFactor(%q, %q) invalid, want %q with factor %g but got %g", c.in, c.want, o.Prefix(), c.prefixFactor, value)
|
||||
} else {
|
||||
t.Logf("GetPrefixPrefixFactor(%q, %q) = %g", c.in, c.want, c.prefixFactor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMeasureRegex(t *testing.T) {
|
||||
for _, data := range MeasuresMap {
|
||||
_, err := regexp.Compile(data.Regex)
|
||||
if err != nil {
|
||||
t.Errorf("failed to compile regex '%s': %s", data.Regex, err.Error())
|
||||
}
|
||||
t.Logf("succussfully compiled regex '%s' for measure %s", data.Regex, data.Long)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrefixRegex(t *testing.T) {
|
||||
for _, data := range PrefixDataMap {
|
||||
_, err := regexp.Compile(data.Regex)
|
||||
if err != nil {
|
||||
t.Errorf("failed to compile regex '%s': %s", data.Regex, err.Error())
|
||||
}
|
||||
t.Logf("succussfully compiled regex '%s' for prefix %s", data.Regex, data.Long)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user