From cbb5f2111287dd77fb8e9b1ec787f7aed396b8eb Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Fri, 12 Oct 2018 15:25:34 -0700 Subject: [PATCH] New parser and comparison --- helper/funcs.go | 5 + plugins/shared/structs/attribute.go | 300 +++++++++++- plugins/shared/structs/attribute_test.go | 595 ++++++++++++++++++++++- plugins/shared/structs/units.go | 19 +- plugins/shared/structs/util.go | 17 +- 5 files changed, 886 insertions(+), 50 deletions(-) diff --git a/helper/funcs.go b/helper/funcs.go index 4d8f53b1a..083ab865b 100644 --- a/helper/funcs.go +++ b/helper/funcs.go @@ -77,6 +77,11 @@ func TimeToPtr(t time.Duration) *time.Duration { return &t } +// Float64ToPtr returns the pointer to an float64 +func Float64ToPtr(f float64) *float64 { + return &f +} + func IntMin(a, b int) int { if a < b { return a diff --git a/plugins/shared/structs/attribute.go b/plugins/shared/structs/attribute.go index 38a6909fa..a426bc422 100644 --- a/plugins/shared/structs/attribute.go +++ b/plugins/shared/structs/attribute.go @@ -2,8 +2,18 @@ package structs import ( "fmt" - "regexp" + "math/big" "strconv" + "strings" + "unicode" + + "github.com/hashicorp/nomad/helper" +) + +const ( + // floatPrecision is the precision used before rounding. It is set to a high + // number to give a high chance of correctly returning equality. + floatPrecision = uint(256) ) // BaseUnit is a unique base unit. All units that share the same base unit @@ -27,7 +37,7 @@ type Unit struct { Base BaseUnit // Multiplier is the multiplier over the base unit (KiB multiplier is 1024) - Multiplier uint64 + Multiplier int64 // InverseMultiplier specifies that the multiplier is an inverse so: // Base / Multiplier. For example a mW is a W/1000. @@ -47,83 +57,323 @@ func (u *Unit) Comparable(o *Unit) bool { // specifying units type Attribute struct { // Float is the float value for the attribute - Float float64 + Float *float64 // Int is the int value for the attribute - Int int64 + Int *int64 // String is the string value for the attribute - String string + String *string // Bool is the bool value for the attribute - Bool bool + Bool *bool // Unit is the optional unit for the set int or float value Unit string } +// GoString returns a string representation of the attribute +func (a *Attribute) GoString() string { + if a == nil { + return "nil attribute" + } + + var b strings.Builder + if a.Float != nil { + b.WriteString(fmt.Sprintf("%v", *a.Float)) + } else if a.Int != nil { + b.WriteString(fmt.Sprintf("%v", *a.Int)) + } else if a.Bool != nil { + b.WriteString(fmt.Sprintf("%v", *a.Bool)) + } else if a.String != nil { + b.WriteString(*a.String) + } + + if a.Unit != "" { + b.WriteString(a.Unit) + } + + return b.String() +} + // Validate checks if the attribute is valid func (a *Attribute) Validate() error { if a.Unit != "" { if _, ok := UnitIndex[a.Unit]; !ok { return fmt.Errorf("unrecognized unit %q", a.Unit) } + + // Check only int/float set + if a.String != nil || a.Bool != nil { + return fmt.Errorf("unit can not be specified on a boolean or string attribute") + } + } + + // Assert only one of the attributes is set + set := 0 + if a.Float != nil { + set++ + } + if a.Int != nil { + set++ + } + if a.String != nil { + set++ + } + if a.Bool != nil { + set++ + } + + if set == 0 { + return fmt.Errorf("no attribute value set") + } else if set > 1 { + return fmt.Errorf("only one attribute value may be set") } return nil } -var ( - // numericWithUnits matches only if it is a integer or float ending with - // units. It has two capture groups, one for the numeric value and one for - // the unit value - numericWithUnits = regexp.MustCompile(`^([-]?(?:[0-9]+|[0-9]+\.[0-9]+|\.[0-9]+))\s*([a-zA-Z]+\/?[a-zA-z]+|[a-zA-Z])$`) -) +// Compare compares two attributes. If the returned boolean value is false, it +// means the values are not comparable, either because they are of different +// types (bool versus int) or the units are incompatible for comparison. +// The returned int will be 0 if a==b, -1 if a < b, and +1 if a > b for all +// values but bool. For bool it will be 0 if a==b or 1 if a!=b. +func (a *Attribute) Compare(b *Attribute) (int, bool) { + if !a.Comparable(b) { + return 0, false + } + return a.comparitor()(b) +} + +// comparitor returns the comparitor function for the attribute +func (a *Attribute) comparitor() compareFn { + if a.Bool != nil { + return a.boolComparitor + } + if a.String != nil { + return a.stringComparitor + } + if a.Int != nil || a.Float != nil { + return a.numberComparitor + } + + return nullComparitor +} + +// boolComparitor compares two boolean attributes +func (a *Attribute) boolComparitor(b *Attribute) (int, bool) { + if *a.Bool == *b.Bool { + return 0, true + } + + return 1, true +} + +// stringComparitor compares two string attributes +func (a *Attribute) stringComparitor(b *Attribute) (int, bool) { + return strings.Compare(*a.String, *b.String), true +} + +// numberComparitor compares two number attributes, having either Int or Float +// set. +func (a *Attribute) numberComparitor(b *Attribute) (int, bool) { + // If they are both integers we do perfect precision comparisons + if a.Int != nil && b.Int != nil { + return a.intComparitor(b) + } + + // Push both into the float space + af := a.getBigFloat() + bf := b.getBigFloat() + if af == nil || bf == nil { + return 0, false + } + + return af.Cmp(bf), true +} + +// intComparitor compares two integer attributes. +func (a *Attribute) intComparitor(b *Attribute) (int, bool) { + ai := a.getInt() + bi := b.getInt() + + if ai == bi { + return 0, true + } else if ai < bi { + return -1, true + } else { + return 1, true + } +} + +// nullComparitor always returns false and is used when no comparison function +// is possible +func nullComparitor(*Attribute) (int, bool) { + return 0, false +} + +// compareFn is used to compare two attributes. It returns -1, 0, 1 for ordering +// and a boolean for if the comparison is possible. +type compareFn func(b *Attribute) (int, bool) + +// getBigFloat returns a big.Float representation of the attribute, converting +// the value to the base unit if a unit is specified. +func (a *Attribute) getBigFloat() *big.Float { + f := new(big.Float) + f.SetPrec(floatPrecision) + if a.Int != nil { + f.SetInt64(*a.Int) + } else if a.Float != nil { + f.SetFloat64(*a.Float) + } else { + return nil + } + + // Get the unit + u := a.getTypedUnit() + + // If there is no unit just return the float + if u == nil { + return f + } + + // Convert to the base unit + multiplier := new(big.Float) + multiplier.SetPrec(floatPrecision) + multiplier.SetInt64(u.Multiplier) + if u.InverseMultiplier { + base := big.NewFloat(1.0) + base.SetPrec(floatPrecision) + multiplier = multiplier.Quo(base, multiplier) + } + + f.Mul(f, multiplier) + return f +} + +// getInt returns an int representation of the attribute, converting +// the value to the base unit if a unit is specified. +func (a *Attribute) getInt() int64 { + if a.Int == nil { + return 0 + } + + i := *a.Int + + // Get the unit + u := a.getTypedUnit() + + // If there is no unit just return the int + if u == nil { + return i + } + + if u.InverseMultiplier { + i /= u.Multiplier + } else { + i *= u.Multiplier + } + + return i +} + +// Comparable returns whether they are comparable +func (a *Attribute) Comparable(b *Attribute) bool { + if a == nil || b == nil { + return false + } + + // First use the units to decide if comparison is possible + aUnit := a.getTypedUnit() + bUnit := b.getTypedUnit() + if aUnit != nil && bUnit != nil { + return aUnit.Comparable(bUnit) + } else if aUnit != nil && bUnit == nil { + return false + } else if aUnit == nil && bUnit != nil { + return false + } + + if a.String != nil { + if b.String != nil { + return true + } + return false + } + if a.Bool != nil { + if b.Bool != nil { + return true + } + return false + } + + return true +} + +// getTypedUnit returns the Unit for the attribute or nil if no unit exists. +func (a *Attribute) getTypedUnit() *Unit { + return UnitIndex[a.Unit] +} + +// ParseAttribute takes a string and parses it into an attribute, pulling out +// units if they are specified as a suffix on a number func ParseAttribute(input string) *Attribute { + ll := len(input) + if ll == 0 { + return &Attribute{String: helper.StringToPtr(input)} + } + // Try to parse as a bool b, err := strconv.ParseBool(input) if err == nil { - return &Attribute{Bool: b} + return &Attribute{Bool: helper.BoolToPtr(b)} } - // Try to parse as a number. - // Check if the string is a number ending with potential units - if matches := numericWithUnits.FindStringSubmatch(input); len(matches) == 3 { - numeric := matches[1] - unit := matches[2] + if unicode.IsLetter(rune(input[ll-1])) { + // Try suffix matching + var unit string + for _, u := range lengthSortedUnits { + if strings.HasSuffix(input, u) { + unit = u + break + } + } // Check if we know about the unit. If we don't we can only treat this // as a string - if _, ok := UnitIndex[unit]; !ok { - return &Attribute{String: input} + if len(unit) == 0 { + return &Attribute{String: helper.StringToPtr(input)} } + // Grab the numeric + numeric := strings.TrimSpace(strings.TrimSuffix(input, unit)) + // Try to parse as an int i, err := strconv.ParseInt(numeric, 10, 64) if err == nil { - return &Attribute{Int: i, Unit: unit} + return &Attribute{Int: helper.Int64ToPtr(i), Unit: unit} } // Try to parse as a float f, err := strconv.ParseFloat(numeric, 64) if err == nil { - return &Attribute{Float: f, Unit: unit} + return &Attribute{Float: helper.Float64ToPtr(f), Unit: unit} } } // Try to parse as an int i, err := strconv.ParseInt(input, 10, 64) if err == nil { - return &Attribute{Int: i} + return &Attribute{Int: helper.Int64ToPtr(i)} } // Try to parse as a float f, err := strconv.ParseFloat(input, 64) if err == nil { - return &Attribute{Float: f} + return &Attribute{Float: helper.Float64ToPtr(f)} } - return &Attribute{String: input} + return &Attribute{String: helper.StringToPtr(input)} } diff --git a/plugins/shared/structs/attribute_test.go b/plugins/shared/structs/attribute_test.go index 68342c215..fa9197206 100644 --- a/plugins/shared/structs/attribute_test.go +++ b/plugins/shared/structs/attribute_test.go @@ -1,11 +1,546 @@ package structs import ( + "fmt" "testing" + "github.com/hashicorp/nomad/helper" "github.com/stretchr/testify/require" ) +func TestAttribute_Validate(t *testing.T) { + cases := []struct { + Input *Attribute + Fail bool + }{ + { + Input: &Attribute{ + Bool: helper.BoolToPtr(true), + }, + }, + { + Input: &Attribute{ + String: helper.StringToPtr("foo"), + }, + }, + { + Input: &Attribute{ + Int: helper.Int64ToPtr(123), + }, + }, + { + Input: &Attribute{ + Float: helper.Float64ToPtr(123.2), + }, + }, + { + Input: &Attribute{ + Bool: helper.BoolToPtr(true), + Unit: "MB", + }, + Fail: true, + }, + { + Input: &Attribute{ + String: helper.StringToPtr("foo"), + Unit: "MB", + }, + Fail: true, + }, + { + Input: &Attribute{ + Int: helper.Int64ToPtr(123), + Unit: "lolNO", + }, + Fail: true, + }, + { + Input: &Attribute{ + Float: helper.Float64ToPtr(123.2), + Unit: "lolNO", + }, + Fail: true, + }, + { + Input: &Attribute{ + Int: helper.Int64ToPtr(123), + Float: helper.Float64ToPtr(123.2), + Unit: "mW", + }, + Fail: true, + }, + } + + for _, c := range cases { + t.Run(c.Input.GoString(), func(t *testing.T) { + if err := c.Input.Validate(); err != nil && !c.Fail { + require.NoError(t, err) + } + }) + } +} + +type compareTestCase struct { + A *Attribute + B *Attribute + Expected int + NotComparable bool +} + +func TestAttribute_Compare_Bool(t *testing.T) { + cases := []*compareTestCase{ + { + A: &Attribute{ + Bool: helper.BoolToPtr(true), + }, + B: &Attribute{ + Bool: helper.BoolToPtr(true), + }, + Expected: 0, + }, + { + A: &Attribute{ + Bool: helper.BoolToPtr(true), + }, + B: &Attribute{ + Bool: helper.BoolToPtr(false), + }, + Expected: 1, + }, + { + A: &Attribute{ + Bool: helper.BoolToPtr(true), + }, + B: &Attribute{ + String: helper.StringToPtr("foo"), + }, + NotComparable: true, + }, + { + A: &Attribute{ + Bool: helper.BoolToPtr(true), + }, + B: &Attribute{ + Int: helper.Int64ToPtr(123), + }, + NotComparable: true, + }, + { + A: &Attribute{ + Bool: helper.BoolToPtr(true), + }, + B: &Attribute{ + Float: helper.Float64ToPtr(123.2), + }, + NotComparable: true, + }, + } + testComparison(t, cases) +} + +func TestAttribute_Compare_String(t *testing.T) { + cases := []*compareTestCase{ + { + A: &Attribute{ + String: helper.StringToPtr("a"), + }, + B: &Attribute{ + String: helper.StringToPtr("b"), + }, + Expected: -1, + }, + { + A: &Attribute{ + String: helper.StringToPtr("hello"), + }, + B: &Attribute{ + String: helper.StringToPtr("hello"), + }, + Expected: 0, + }, + { + A: &Attribute{ + String: helper.StringToPtr("b"), + }, + B: &Attribute{ + String: helper.StringToPtr("a"), + }, + Expected: 1, + }, + { + A: &Attribute{ + String: helper.StringToPtr("hello"), + }, + B: &Attribute{ + Bool: helper.BoolToPtr(true), + }, + NotComparable: true, + }, + { + A: &Attribute{ + String: helper.StringToPtr("hello"), + }, + B: &Attribute{ + Int: helper.Int64ToPtr(123), + }, + NotComparable: true, + }, + { + A: &Attribute{ + String: helper.StringToPtr("hello"), + }, + B: &Attribute{ + Float: helper.Float64ToPtr(123.2), + }, + NotComparable: true, + }, + } + testComparison(t, cases) +} + +func TestAttribute_Compare_Float(t *testing.T) { + cases := []*compareTestCase{ + { + A: &Attribute{ + Float: helper.Float64ToPtr(101.5), + }, + B: &Attribute{ + Float: helper.Float64ToPtr(100001.5), + }, + Expected: -1, + }, + { + A: &Attribute{ + Float: helper.Float64ToPtr(100001.5), + }, + B: &Attribute{ + Float: helper.Float64ToPtr(100001.5), + }, + Expected: 0, + }, + { + A: &Attribute{ + Float: helper.Float64ToPtr(999999999.5), + }, + B: &Attribute{ + Float: helper.Float64ToPtr(101.5), + }, + Expected: 1, + }, + { + A: &Attribute{ + Float: helper.Float64ToPtr(101.5), + }, + B: &Attribute{ + Bool: helper.BoolToPtr(true), + }, + NotComparable: true, + }, + { + A: &Attribute{ + Float: helper.Float64ToPtr(101.5), + }, + B: &Attribute{ + String: helper.StringToPtr("hello"), + }, + NotComparable: true, + }, + } + testComparison(t, cases) +} + +func TestAttribute_Compare_Int(t *testing.T) { + cases := []*compareTestCase{ + { + A: &Attribute{ + Int: helper.Int64ToPtr(3), + }, + B: &Attribute{ + Int: helper.Int64ToPtr(10), + }, + Expected: -1, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(10), + }, + B: &Attribute{ + Int: helper.Int64ToPtr(10), + }, + Expected: 0, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(100), + }, + B: &Attribute{ + Int: helper.Int64ToPtr(10), + }, + Expected: 1, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(10), + }, + B: &Attribute{ + Bool: helper.BoolToPtr(true), + }, + NotComparable: true, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(10), + }, + B: &Attribute{ + String: helper.StringToPtr("hello"), + }, + NotComparable: true, + }, + } + testComparison(t, cases) +} + +func TestAttribute_Compare_Int_With_Units(t *testing.T) { + cases := []*compareTestCase{ + { + A: &Attribute{ + Int: helper.Int64ToPtr(3), + Unit: "MB", + }, + B: &Attribute{ + Int: helper.Int64ToPtr(10), + Unit: "MB", + }, + Expected: -1, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(10), + Unit: "MB", + }, + B: &Attribute{ + Int: helper.Int64ToPtr(10), + Unit: "MB", + }, + Expected: 0, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(100), + Unit: "MB", + }, + B: &Attribute{ + Int: helper.Int64ToPtr(10), + Unit: "MB", + }, + Expected: 1, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(3), + Unit: "GB", + }, + B: &Attribute{ + Int: helper.Int64ToPtr(3), + Unit: "MB", + }, + Expected: 1, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(1), + Unit: "GiB", + }, + B: &Attribute{ + Int: helper.Int64ToPtr(1024), + Unit: "MiB", + }, + Expected: 0, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(1), + Unit: "GiB", + }, + B: &Attribute{ + Int: helper.Int64ToPtr(1025), + Unit: "MiB", + }, + Expected: -1, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(1000), + Unit: "mW", + }, + B: &Attribute{ + Int: helper.Int64ToPtr(1), + Unit: "W", + }, + Expected: 0, + }, + } + testComparison(t, cases) +} + +func TestAttribute_Compare_Float_With_Units(t *testing.T) { + cases := []*compareTestCase{ + { + A: &Attribute{ + Float: helper.Float64ToPtr(3.0), + Unit: "MB", + }, + B: &Attribute{ + Float: helper.Float64ToPtr(10.0), + Unit: "MB", + }, + Expected: -1, + }, + { + A: &Attribute{ + Float: helper.Float64ToPtr(10.0), + Unit: "MB", + }, + B: &Attribute{ + Float: helper.Float64ToPtr(10.0), + Unit: "MB", + }, + Expected: 0, + }, + { + A: &Attribute{ + Float: helper.Float64ToPtr(100.0), + Unit: "MB", + }, + B: &Attribute{ + Float: helper.Float64ToPtr(10.0), + Unit: "MB", + }, + Expected: 1, + }, + { + A: &Attribute{ + Float: helper.Float64ToPtr(3.0), + Unit: "GB", + }, + B: &Attribute{ + Float: helper.Float64ToPtr(3.0), + Unit: "MB", + }, + Expected: 1, + }, + { + A: &Attribute{ + Float: helper.Float64ToPtr(1.0), + Unit: "GiB", + }, + B: &Attribute{ + Float: helper.Float64ToPtr(1024.0), + Unit: "MiB", + }, + Expected: 0, + }, + { + A: &Attribute{ + Float: helper.Float64ToPtr(1.0), + Unit: "GiB", + }, + B: &Attribute{ + Float: helper.Float64ToPtr(1025.0), + Unit: "MiB", + }, + Expected: -1, + }, + { + A: &Attribute{ + Float: helper.Float64ToPtr(1000.0), + Unit: "mW", + }, + B: &Attribute{ + Float: helper.Float64ToPtr(1.0), + Unit: "W", + }, + Expected: 0, + }, + { + A: &Attribute{ + Float: helper.Float64ToPtr(1.5), + Unit: "GiB", + }, + B: &Attribute{ + Float: helper.Float64ToPtr(1400.0), + Unit: "MiB", + }, + Expected: 1, + }, + } + testComparison(t, cases) +} + +func TestAttribute_Compare_IntToFloat(t *testing.T) { + cases := []*compareTestCase{ + { + A: &Attribute{ + Int: helper.Int64ToPtr(3), + }, + B: &Attribute{ + Float: helper.Float64ToPtr(10.0), + }, + Expected: -1, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(10), + }, + B: &Attribute{ + Float: helper.Float64ToPtr(10.0), + }, + Expected: 0, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(10), + }, + B: &Attribute{ + Float: helper.Float64ToPtr(10.1), + }, + Expected: -1, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(100), + }, + B: &Attribute{ + Float: helper.Float64ToPtr(10.0), + }, + Expected: 1, + }, + { + A: &Attribute{ + Int: helper.Int64ToPtr(100), + }, + B: &Attribute{ + Float: helper.Float64ToPtr(100.00001), + }, + Expected: -1, + }, + } + testComparison(t, cases) +} + +func testComparison(t *testing.T, cases []*compareTestCase) { + for _, c := range cases { + t.Run(fmt.Sprintf("%#v vs %#v", c.A, c.B), func(t *testing.T) { + v, ok := c.A.Compare(c.B) + if !ok && !c.NotComparable { + t.Fatal("should be comparable") + } else if ok { + require.Equal(t, c.Expected, v) + } + }) + } +} + func TestAttribute_ParseAndValidate(t *testing.T) { cases := []struct { Input string @@ -14,102 +549,102 @@ func TestAttribute_ParseAndValidate(t *testing.T) { { Input: "true", Expected: &Attribute{ - Bool: true, + Bool: helper.BoolToPtr(true), }, }, { Input: "false", Expected: &Attribute{ - Bool: false, + Bool: helper.BoolToPtr(false), }, }, { Input: "100", Expected: &Attribute{ - Int: 100, + Int: helper.Int64ToPtr(100), }, }, { Input: "-100", Expected: &Attribute{ - Int: -100, + Int: helper.Int64ToPtr(-100), }, }, { Input: "-1.0", Expected: &Attribute{ - Float: -1.0, + Float: helper.Float64ToPtr(-1.0), }, }, { Input: "-100.25", Expected: &Attribute{ - Float: -100.25, + Float: helper.Float64ToPtr(-100.25), }, }, { Input: "1.01", Expected: &Attribute{ - Float: 1.01, + Float: helper.Float64ToPtr(1.01), }, }, { Input: "100.25", Expected: &Attribute{ - Float: 100.25, + Float: helper.Float64ToPtr(100.25), }, }, { Input: "foobar", Expected: &Attribute{ - String: "foobar", + String: helper.StringToPtr("foobar"), }, }, { Input: "foo123bar", Expected: &Attribute{ - String: "foo123bar", + String: helper.StringToPtr("foo123bar"), }, }, { Input: "100MB", Expected: &Attribute{ - Int: 100, + Int: helper.Int64ToPtr(100), Unit: "MB", }, }, { Input: "-100MHz", Expected: &Attribute{ - Int: -100, + Int: helper.Int64ToPtr(-100), Unit: "MHz", }, }, { Input: "-1.0MB/s", Expected: &Attribute{ - Float: -1.0, + Float: helper.Float64ToPtr(-1.0), Unit: "MB/s", }, }, { Input: "-100.25GiB/s", Expected: &Attribute{ - Float: -100.25, + Float: helper.Float64ToPtr(-100.25), Unit: "GiB/s", }, }, { Input: "1.01TB", Expected: &Attribute{ - Float: 1.01, + Float: helper.Float64ToPtr(1.01), Unit: "TB", }, }, { Input: "100.25mW", Expected: &Attribute{ - Float: 100.25, + Float: helper.Float64ToPtr(100.25), Unit: "mW", }, }, @@ -123,3 +658,31 @@ func TestAttribute_ParseAndValidate(t *testing.T) { }) } } + +func BenchmarkParse(b *testing.B) { + cases := []string{ + "true", + "false", + "100", + "-100", + "-1.0", + "-100.25", + "1.01", + "100.25", + "foobar", + "foo123bar", + "100MB", + "-100MHz", + "-1.0MB/s", + "-100.25GiB/s", + "1.01TB", + "100.25mW", + } + + // run the Fib function b.N times + for n := 0; n < b.N; n++ { + for _, c := range cases { + ParseAttribute(c) + } + } +} diff --git a/plugins/shared/structs/units.go b/plugins/shared/structs/units.go index 86296433a..f77c0dd40 100644 --- a/plugins/shared/structs/units.go +++ b/plugins/shared/structs/units.go @@ -1,7 +1,17 @@ -package structs. +package structs + +import "sort" var ( - UnitIndex = make(map[string]*Unit, len(binarySIBytes)+len(decimalSIBytes)+len(binarySIByteRates)+len(decimalSIByteRates)+len(watts)+len(hertz)) + // numUnits is the number of known units + numUnits = len(binarySIBytes) + len(decimalSIBytes) + len(binarySIByteRates) + len(decimalSIByteRates) + len(watts) + len(hertz) + + // UnitIndex is a map of unit name to unit + UnitIndex = make(map[string]*Unit, numUnits) + + // lengthSortedUnits is a list of unit names sorted by length with longest + // first + lengthSortedUnits = make([]string, 0, numUnits) binarySIBytes = []*Unit{ &Unit{ @@ -193,6 +203,11 @@ func init() { for _, units := range [][]*Unit{binarySIBytes, decimalSIBytes, binarySIByteRates, decimalSIByteRates, watts, hertz} { for _, unit := range units { UnitIndex[unit.Name] = unit + lengthSortedUnits = append(lengthSortedUnits, unit.Name) } } + + sort.Slice(lengthSortedUnits, func(i, j int) bool { + return len(lengthSortedUnits[i]) >= len(lengthSortedUnits[j]) + }) } diff --git a/plugins/shared/structs/util.go b/plugins/shared/structs/util.go index 1f2ec7e23..dec6b9ad0 100644 --- a/plugins/shared/structs/util.go +++ b/plugins/shared/structs/util.go @@ -1,6 +1,9 @@ package structs -import "github.com/hashicorp/nomad/plugins/shared/structs/proto" +import ( + "github.com/hashicorp/nomad/helper" + "github.com/hashicorp/nomad/plugins/shared/structs/proto" +) func ConvertProtoAttribute(in *proto.Attribute) *Attribute { out := &Attribute{ @@ -9,21 +12,21 @@ func ConvertProtoAttribute(in *proto.Attribute) *Attribute { switch in.Value.(type) { case *proto.Attribute_BoolVal: - out.Bool = in.GetBoolVal() + out.Bool = helper.BoolToPtr(in.GetBoolVal()) case *proto.Attribute_FloatVal: - out.Float = in.GetFloatVal() + out.Float = helper.Float64ToPtr(in.GetFloatVal()) case *proto.Attribute_IntVal: - out.Int = in.GetIntVal() + out.Int = helper.Int64ToPtr(in.GetIntVal()) case *proto.Attribute_StringVal: - out.String = in.GetStringVal() + out.String = helper.StringToPtr(in.GetStringVal()) default: } return out } -func Pow(a, b uint64) uint64 { - var p uint64 = 1 +func Pow(a, b int64) int64 { + var p int64 = 1 for b > 0 { if b&1 != 0 { p *= a