Skip to content

Commit

Permalink
packet/channel/field: add structure
Browse files Browse the repository at this point in the history
  • Loading branch information
johnlettman committed Jul 18, 2024
1 parent f81239a commit 7778aca
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 0 deletions.
78 changes: 78 additions & 0 deletions packet/channel/field/structure.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package field

import (
"fmt"
"github.com/johnlettman/oyster/util"
"math/bits"
"strings"
)

// Structure represents a data structure with its type, offset, mask, and shift.
type Structure struct {
Type Type
Offset int
ValueMask uint64
Shift int
}

// String returns the string representation of a Structure value.
func (s Structure) String() string {
sb := new(strings.Builder)
sb.WriteString(fmt.Sprintf("{Type: %s, Offset: %d", s.Type.String(), s.Offset))

if s.ValueMask != 0 {
sb.WriteString(fmt.Sprintf(", ValueMask: 0x%.16X", s.ValueMask))
}

if s.Shift != 0 {
sb.WriteString(fmt.Sprintf(", Shift: %d", s.Shift))
}

sb.WriteRune('}')
return sb.String()
}

// GoString returns the Go syntax representation of a Structure value.
func (s Structure) GoString() string {
sb := new(strings.Builder)
sb.WriteString(fmt.Sprintf("channel.Structure{Type: %s, Offset: %d", s.Type.GoString(), s.Offset))

if s.ValueMask != 0 {
sb.WriteString(fmt.Sprintf(", ValueMask: 0x%.16X", s.ValueMask))
}

if s.Shift != 0 {
sb.WriteString(fmt.Sprintf(", Shift: %d", s.Shift))
}

sb.WriteRune('}')
return sb.String()
}

// Mask returns the masked value based on the Type, ValueMask, and Shift fields of the Structure.
// - If the ValueMask is zero, it uses the mask derived from the Type field.
// - If the Shift field is greater than zero, it right-shifts the mask by the Shift amount.
// - If the Shift field is less than zero, it left-shifts the mask by the absolute value of the Shift amount.
//
// Finally, it performs a bitwise AND operation between the shifted mask and the type-specific mask.
func (s Structure) Mask() uint64 {
typeMask := s.Type.Mask()

mask := s.ValueMask
if mask == 0 {
mask = typeMask
}

if s.Shift > 0 {
mask >>= uint64(s.Shift)
} else if s.Shift < 0 {
mask <<= uint64(util.Abs(s.Shift))
}

return mask & typeMask
}

// MaskBits returns the number of bits set to 1 in the masked value obtained from the Mask method of the Structure.
func (s Structure) MaskBits() int {
return bits.OnesCount64(s.Mask())
}
156 changes: 156 additions & 0 deletions packet/channel/field/structure_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package field

import (
"fmt"
"github.com/stretchr/testify/assert"
"math"
"testing"
)

func TestStructure_StringInterfaces(t *testing.T) {
assert.Implements(t, (*fmt.Stringer)(nil), new(Structure))
assert.Implements(t, (*fmt.GoStringer)(nil), new(Structure))
}

func TestStructure_String(t *testing.T) {
type TestCase struct {
name string
s Structure
want string
}

cases := []TestCase{
{
name: "full",
s: Structure{Type: TypeUint8, Offset: 1, ValueMask: 0xFF, Shift: 32},
want: "{Type: uint8, Offset: 1, ValueMask: 0x00000000000000FF, Shift: 32}",
},
{
name: "no Shift",
s: Structure{Type: TypeUint8, Offset: 1, ValueMask: 0xFF, Shift: 0},
want: "{Type: uint8, Offset: 1, ValueMask: 0x00000000000000FF}",
},
{
name: "no ValueMask",
s: Structure{Type: TypeUint8, Offset: 1, Shift: 32},
want: "{Type: uint8, Offset: 1, Shift: 32}",
},
{
name: "no Shift no ValueMask",
s: Structure{Type: TypeUint8, Offset: 1},
want: "{Type: uint8, Offset: 1}",
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
got := c.s.String()
assert.Equal(t, c.want, got, "it should return the correct representation")
})

}
}

func TestStructure_GoString(t *testing.T) {
type TestCase struct {
name string
s Structure
want string
}

cases := []TestCase{
{
name: "full",
s: Structure{Type: TypeUint8, Offset: 1, ValueMask: 0xFF, Shift: 32},
want: "channel.Structure{Type: TypeUint8, Offset: 1, ValueMask: 0x00000000000000FF, Shift: 32}",
},
{
name: "no Shift",
s: Structure{Type: TypeUint8, Offset: 1, ValueMask: 0xFF, Shift: 0},
want: "channel.Structure{Type: TypeUint8, Offset: 1, ValueMask: 0x00000000000000FF}",
},
{
name: "no ValueMask",
s: Structure{Type: TypeUint8, Offset: 1, Shift: 32},
want: "channel.Structure{Type: TypeUint8, Offset: 1, Shift: 32}",
},
{
name: "no Shift no ValueMask",
s: Structure{Type: TypeUint8, Offset: 1},
want: "channel.Structure{Type: TypeUint8, Offset: 1}",
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
got := c.s.GoString()
assert.Equal(t, c.want, got, "it should return the correct representation")
})
}
}

func TestStructure_Mask(t *testing.T) {
type TestCase struct {
name string
s Structure
want uint64
}

cases := []TestCase{
{
name: "normal",
s: Structure{Type: TypeUint8, Offset: 1, ValueMask: 0xFF, Shift: 0},
want: 0xFF,
},
{
name: "ValueMask greater than type",
s: Structure{Type: TypeUint8, Offset: 1, ValueMask: 0xFFFFFFFF, Shift: 0},
want: 0xFF,
},
{
name: "positive shift",
s: Structure{Type: TypeUint8, Offset: 1, ValueMask: 0xF0, Shift: 4},
want: 0x0F,
},
{
name: "negative shift",
s: Structure{Type: TypeUint8, Offset: 1, ValueMask: 0x0F, Shift: -4},
want: 0xF0,
},
{
name: "no ValueMask",
s: Structure{Type: TypeUint8, Offset: 1, ValueMask: 0, Shift: 0},
want: math.MaxUint8,
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
got := c.s.Mask()
assert.Equal(t, c.want, got, "it should return the correct mask")
})
}
}

func TestStructure_MaskBits(t *testing.T) {
type TestCase struct {
name string
s Structure
want int
}

cases := []TestCase{
{
name: "normal count",
s: Structure{Type: TypeUint8, Offset: 1, ValueMask: 0b00001111, Shift: 0},
want: 4,
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
got := c.s.MaskBits()
assert.Equal(t, c.want, got, "it should return the correct mask bit count")
})
}
}

0 comments on commit 7778aca

Please sign in to comment.