-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f81239a
commit 7778aca
Showing
2 changed files
with
234 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
}) | ||
} | ||
} |