Skip to content

Commit

Permalink
support bits list
Browse files Browse the repository at this point in the history
  • Loading branch information
dhubler committed Jan 26, 2024
1 parent a9b9212 commit e483b3e
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 37 deletions.
75 changes: 49 additions & 26 deletions node/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ func NewValue(typ *meta.Type, v interface{}) (val.Value, error) {
return toUnionList(typ, v)
case val.FmtLeafRef, val.FmtLeafRefList:
return NewValue(typ.Resolve(), v)
case val.FmtBitsList:
return toBitsList(typ.Bits(), v)
case val.FmtBits:
return toBits(typ.Bits(), v)
}
Expand Down Expand Up @@ -198,47 +200,68 @@ func toUnionList(typ *meta.Type, v interface{}) (val.Value, error) {
return nil, fmt.Errorf("could not coerce %v into UnionList", v)
}

func toBitsListHandler[V uint64 | int | uint | int64 | string | []string | float64](bitDefintions []*meta.Bit, vList []V) (val.BitsList, error) {
result := make([]val.Bits, len(vList))
var err error
for i, v := range vList {
if result[i], err = toBits(bitDefintions, v); err != nil {
return nil, err
}
}
return result, nil
}

func toBitsList(bitDefintions []*meta.Bit, v interface{}) (val.BitsList, error) {
switch x := v.(type) {
case []string: // treat string as list of bit identifiers separated by space
return toBitsListHandler(bitDefintions, x)
case [][]string:
return toBitsListHandler(bitDefintions, x)
case []uint64:
return toBitsListHandler(bitDefintions, x)
case []int:
return toBitsListHandler(bitDefintions, x)
case []float64: // default type for decimals from JSON parser
return toBitsListHandler(bitDefintions, x)
}
return nil, fmt.Errorf("could not coerce %v into BitList", v)
}

func toBitsValueHandler[V int | uint | int64 | float64](bitDefintions []*meta.Bit, v V) (val.Bits, error) {
return toBits(bitDefintions, uint64(v))
}

func toBits(bitDefintions []*meta.Bit, v interface{}) (val.Bits, error) {
result := val.Bits{}
switch x := v.(type) {
case string: // treat string as list of bit identifiers separated by space
for _, strBit := range strings.Split(x, " ") {
for _, bitDef := range bitDefintions {
if strBit == bitDef.Ident() {
result.StringList = append(result.StringList, strBit)
result.Decimal = result.Decimal | (1 << bitDef.Position)
}
}
}
return result, nil
case float64: // default type for decimals from JSON parser
intVal := uint64(x)
// TODO: add verify that value has less or eq bits than definition
for _, bitDef := range bitDefintions {
if intVal&(1<<bitDef.Position) != 0 {
result.Decimal = result.Decimal | (1 << bitDef.Position)
result.StringList = append(result.StringList, bitDef.Ident())
}
}
return result, nil
case []string: // each string is bit identifier
case []string: // labels only
for _, strBit := range x {
for _, bitDef := range bitDefintions {
if strBit == bitDef.Ident() {
result.StringList = append(result.StringList, strBit)
result.Decimal = result.Decimal | (1 << bitDef.Position)
result.Labels = append(result.Labels, strBit)
result.Positions = result.Positions | (1 << bitDef.Position)
}
}
}
return result, nil
case int:
case uint64: // positions only
for _, bitDef := range bitDefintions {
if x&(1<<bitDef.Position) != 0 {
result.Decimal = result.Decimal | (1 << bitDef.Position)
result.StringList = append(result.StringList, bitDef.Ident())
result.Positions = result.Positions | (1 << bitDef.Position)
result.Labels = append(result.Labels, bitDef.Ident())
}
}
return result, nil
case string: // treat string as list of bit identifiers separated by space
return toBits(bitDefintions, strings.Split(x, " "))
case int:
return toBitsValueHandler(bitDefintions, x)
case uint:
return toBitsValueHandler(bitDefintions, x)
case float64:
return toBitsValueHandler(bitDefintions, x)
case int64:
return toBitsValueHandler(bitDefintions, x)
default:
return result, fmt.Errorf("could not coerce %v (of type %T) into UnionList", v, v)
}
Expand Down
41 changes: 41 additions & 0 deletions node/value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/freeconf/yang/fc"
"github.com/freeconf/yang/meta"
"github.com/freeconf/yang/val"
)

func TestCoerseValue(t *testing.T) {
Expand Down Expand Up @@ -103,17 +104,57 @@ func TestToBits(t *testing.T) {

// cast from int (non empty)
v, err = NewValue(dt, 0b11)
fc.AssertEqual(t, nil, err)
fc.AssertEqual(t, uint64(0b11), v.Value())
fc.AssertEqual(t, "b0 b1", v.String())

// cast from string
v, err = NewValue(dt, "b1")
fc.AssertEqual(t, nil, err)
fc.AssertEqual(t, uint64(0b10), v.Value())
fc.AssertEqual(t, "b1", v.String())

// cast from []string (wrong order)
v, err = NewValue(dt, []string{"b1", "b0"})
fc.AssertEqual(t, nil, err)
fc.AssertEqual(t, uint64(0b11), v.Value())
// side effect: keeping order so input and output data are equal
fc.AssertEqual(t, "b1 b0", v.String())
}

func TestBitList(t *testing.T) {
b := &meta.Builder{}
m := b.Module("x", nil)
l := b.LeafList(m, "l")
dt := b.Type(l, "bits")
b0 := b.Bit(dt, "b0")
b.Position(b0, 0)
b1 := b.Bit(dt, "b1")
b.Position(b1, 1)
fc.RequireEqual(t, nil, meta.Compile(m))

tests := []struct {
positions []uint64
expectedLabels [][]string
toString string
}{
{
positions: []uint64{0, 0},
expectedLabels: [][]string{nil, nil},
toString: ",",
},
{
positions: []uint64{0b10, 0b11},
expectedLabels: [][]string{{"b1"}, {"b0", "b1"}},
toString: "b1,b0 b1",
},
}
for _, test := range tests {
v, err := NewValue(dt, test.positions)
fc.AssertEqual(t, nil, err)
b := v.(val.BitsList)
fc.AssertEqual(t, test.positions, b.Positions())
fc.AssertEqual(t, test.toString, b.String())
fc.AssertEqual(t, test.expectedLabels, b.Labels())
}
}
11 changes: 10 additions & 1 deletion nodeutil/json_wtr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
)

func TestJsonWriterLeafs(t *testing.T) {
fc.DebugLog(true)
tests := []struct {
Yang string
Val val.Value
Expand Down Expand Up @@ -50,6 +49,16 @@ func TestJsonWriterLeafs(t *testing.T) {
expected: `"x":6`,
enumAsId: true,
},
{
Yang: `leaf-list x { type bits { bit one; bit two; }}`,
Val: val.BitsList(
[]val.Bits{
{Positions: 0b01, Labels: []string{"one"}},
{Positions: 0b11, Labels: []string{"one", "two"}},
},
),
expected: `"x":["one","one two"]`,
},
}
for _, test := range tests {
m, err := parser.LoadModuleFromString(nil, fmt.Sprintf(`module m { namespace ""; %s }`, test.Yang))
Expand Down
4 changes: 2 additions & 2 deletions nodeutil/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -582,9 +582,9 @@ func (self Reflect) WriteFieldWithFieldName(fieldName string, m meta.Leafable, p
if fieldVal.Type().Elem().Kind() != reflect.String {
return fmt.Errorf("cannot assign bits value to type %T, only '[]string' or 'int' are accepted for bits representation", fieldVal.Interface())
}
fieldVal.Set(reflect.ValueOf(b.StringList))
fieldVal.Set(reflect.ValueOf(b.Labels))
case reflect.Int:
fieldVal.SetInt(int64(b.Decimal))
fieldVal.SetInt(int64(b.Positions))
default:
return fmt.Errorf("cannot convert bits value to fieldvalue '%v'. Please use 'int' or '[]string' for bits field definition", fieldVal.Kind())
}
Expand Down
40 changes: 32 additions & 8 deletions val/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -866,26 +866,26 @@ func (NotEmptyType) Value() interface{} {
//////////////////////////

type Bits struct {
Decimal uint64
StringList []string
Positions uint64
Labels []string
}

func (Bits) Format() Format {
return FmtBits
}

func (b Bits) String() string {
return strings.Join(b.StringList, " ")
return strings.Join(b.Labels, " ")
}

func (b Bits) Value() interface{} {
return b.Decimal
return b.Positions
}

type BitsList [][]byte
type BitsList []Bits

func (BitsList) Format() Format {
return FmtEmpty
return FmtBitsList
}

func (b BitsList) String() string {
Expand All @@ -894,11 +894,35 @@ func (b BitsList) String() string {
if i != 0 {
s += ","
}
s += string(x)
s += x.String()
}
return s
}

func (b BitsList) Value() interface{} {
return [][]byte(b)
return b.Positions()
}

func (e BitsList) Positions() []uint64 {
l := make([]uint64, len(e))
for i := range e {
l[i] = e[i].Positions
}
return l
}

func (e BitsList) Labels() [][]string {
l := make([][]string, len(e))
for i := range e {
l[i] = e[i].Labels
}
return l
}

func (x BitsList) Len() int {
return len(x)
}

func (x BitsList) Item(i int) Value {
return x[i]
}

0 comments on commit e483b3e

Please sign in to comment.