Skip to content

Commit

Permalink
refactor: replace number inspector with bitmath
Browse files Browse the repository at this point in the history
  • Loading branch information
shellcromancer committed Aug 9, 2023
1 parent 46b0b8d commit 387b4ca
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 155 deletions.
26 changes: 13 additions & 13 deletions build/config/substation.libsonnet
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
defaults: {
inspector: {
bitmath: {
options: { type: null, value: null },
},
content: {
options: { type: null },
},
Expand All @@ -16,9 +19,6 @@
length: {
options: { type: null, value: null, measurement: 'byte' },
},
number: {
options: { type: null, value: null },
},
regexp: {
options: { expression: null },
},
Expand Down Expand Up @@ -212,6 +212,16 @@
},
inspector: {
settings: { key: null, negate: null },
bitmath(options=$.defaults.inspector.bitmath.options,
settings=$.interfaces.inspector.settings): {
local opt = std.mergePatch($.defaults.inspector.bitmath.options, options),

assert $.helpers.inspector.validate(settings) : 'invalid inspector settings',
local s = std.mergePatch($.interfaces.inspector.settings, settings),

type: 'bitmath',
settings: std.mergePatch({ options: opt }, s),
},
condition(options=null,
settings=$.interfaces.inspector.settings): {
assert options != null : 'invalid inspector options',
Expand Down Expand Up @@ -279,16 +289,6 @@
type: 'length',
settings: std.mergePatch({ options: opt }, s),
},
number(options=$.defaults.inspector.number.options,
settings=$.interfaces.inspector.settings): {
local opt = std.mergePatch($.defaults.inspector.number.options, options),

assert $.helpers.inspector.validate(settings) : 'invalid inspector settings',
local s = std.mergePatch($.interfaces.inspector.settings, settings),

type: 'number',
settings: std.mergePatch({ options: opt }, s),
},
random: {
type: 'random',
},
Expand Down
103 changes: 103 additions & 0 deletions condition/bitmath.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package condition

import (
"context"
"fmt"
"strconv"

"golang.org/x/exp/slices"

"github.com/brexhq/substation/config"
"github.com/brexhq/substation/internal/errors"
)

// bitmath evaluates data using bitwith math operations.
//
// This inspector supports the data and object handling patterns.
type inspBitmath struct {
condition
Options inspBitmathOptions `json:"options"`
}

type inspBitmathOptions struct {
// Type is the string evaluation Type used during inspection.
//
// Must be one of:
//
// - and
//
// - or
//
// - not
//
// - xor
Type string `json:"type"`
// Value is the length that is used for comparison during inspection.
Value int64 `json:"value"`
}

// Creates a new bitmath inspector.
func newInspBitmath(_ context.Context, cfg config.Config) (c inspBitmath, err error) {
if err = config.Decode(cfg.Settings, &c); err != nil {
return inspBitmath{}, err
}

// validate option.type
if !slices.Contains(
[]string{
"and",
"or",
"not",
"xor",
},
c.Options.Type) {
return inspBitmath{}, fmt.Errorf("condition: bitmath: type %q: %v", c.Options.Type, errors.ErrInvalidOption)
}

return c, nil
}

func (c inspBitmath) String() string {
return toString(c)
}

// Inspect evaluates encapsulated data with the bitmath inspector.
func (c inspBitmath) Inspect(ctx context.Context, capsule config.Capsule) (output bool, err error) {
var check int64
if c.Key == "" {
check, err = strconv.ParseInt(string(capsule.Data()), 10, 64)
if err != nil {
return false, fmt.Errorf("condition: bitmath: invalid data processing value: %v", err)
}
} else {
check = capsule.Get(c.Key).Int()
}

var matched bool
switch c.Options.Type {
case "and":
if check&c.Options.Value != 0 {
matched = true
}
case "or":
if check|c.Options.Value != 0 {
matched = true
}
case "not":
if ^check != 0 {
matched = true
}
case "xor":
if check^c.Options.Value != 0 {
matched = true
}
default:
return false, fmt.Errorf("condition: bitmath: type %s: %v", c.Options.Type, errors.ErrInvalidOption)
}

if c.Negate {
return !matched, nil
}

return matched, nil
}
89 changes: 52 additions & 37 deletions condition/number_test.go → condition/bitmath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,53 +7,68 @@ import (
"github.com/brexhq/substation/config"
)

var _ Inspector = inspNumber{}
var _ Inspector = inspBitmath{}

var numberTests = []struct {
var bitmathTests = []struct {
name string
cfg config.Config
test []byte
expected bool
}{
{
"pass equals",
"pass xor",
config.Config{
Type: "number",
Type: "bitmath",
Settings: map[string]interface{}{
"key": "foo",
"options": map[string]interface{}{
"type": "equals",
"type": "xor",
"value": 3,
},
},
},
[]byte(`{"foo":"0"}`),
true,
},
{
"fail xor",
config.Config{
Type: "bitmath",
Settings: map[string]interface{}{
"key": "foo",
"options": map[string]interface{}{
"type": "xor",
"value": 42,
},
},
},
[]byte(`{"foo":"42"}`),
true,
false,
},
{
"!fail equals",
"!fail xor",
config.Config{
Type: "number",
Type: "bitmath",
Settings: map[string]interface{}{
"key": "foo",
"negate": true,
"options": map[string]interface{}{
"type": "equals",
"type": "xor",
"value": 42,
},
},
},
[]byte(`{"foo":"42"}`),
false,
true,
},
{
"pass greater_than",
"pass or",
config.Config{
Type: "number",
Type: "bitmath",
Settings: map[string]interface{}{
"key": "foo",
"options": map[string]interface{}{
"type": "greater_than",
"type": "or",
"value": -1,
},
},
Expand All @@ -62,58 +77,58 @@ var numberTests = []struct {
true,
},
{
"!pass greater_than",
"!pass or",
config.Config{
Type: "number",
Type: "bitmath",
Settings: map[string]interface{}{
"key": "foo",
"negate": true,
"options": map[string]interface{}{
"type": "greater_than",
"type": "or",
"value": 1,
},
},
},
[]byte(`{"foo":"0"}`),
true,
false,
},
{
"pass less_than",
"pass and",
config.Config{
Type: "number",
Type: "bitmath",
Settings: map[string]interface{}{
"key": "foo",
"options": map[string]interface{}{
"type": "less_than",
"value": 50,
"type": "and",
"value": 0x0001,
},
},
},
[]byte(`{"foo":42}`),
[]byte(`{"foo":"570506001"}`),
true,
},
{
"pass bitwise_and",
"fail and",
config.Config{
Type: "number",
Type: "bitmath",
Settings: map[string]interface{}{
"key": "foo",
"options": map[string]interface{}{
"type": "bitwise_and",
"value": 0x0001,
"type": "and",
"value": 0x0002,
},
},
},
[]byte(`{"foo":"570506001"}`),
true,
false,
},
{
"pass data",
config.Config{
Type: "number",
Type: "bitmath",
Settings: map[string]interface{}{
"options": map[string]interface{}{
"type": "equals",
"type": "or",
"value": 1,
},
},
Expand All @@ -123,15 +138,15 @@ var numberTests = []struct {
},
}

func TestNumber(t *testing.T) {
func TestBitmath(t *testing.T) {
ctx := context.TODO()
capsule := config.NewCapsule()

for _, test := range numberTests {
for _, test := range bitmathTests {
t.Run(test.name, func(t *testing.T) {
capsule.SetData(test.test)

insp, err := newInspNumber(ctx, test.cfg)
insp, err := newInspBitmath(ctx, test.cfg)
if err != nil {
t.Fatal(err)
}
Expand All @@ -148,25 +163,25 @@ func TestNumber(t *testing.T) {
}
}

func benchmarkNumberByte(b *testing.B, inspector inspNumber, capsule config.Capsule) {
func benchmarkBitmathByte(b *testing.B, inspector inspBitmath, capsule config.Capsule) {
ctx := context.TODO()
for i := 0; i < b.N; i++ {
_, _ = inspector.Inspect(ctx, capsule)
}
}

func BenchmarkNumberByte(b *testing.B) {
func BenchmarkBitmathByte(b *testing.B) {
capsule := config.NewCapsule()
for _, test := range numberTests {
insp, err := newInspNumber(context.TODO(), test.cfg)
for _, test := range bitmathTests {
insp, err := newInspBitmath(context.TODO(), test.cfg)
if err != nil {
b.Fatal(err)
}

b.Run(test.name,
func(b *testing.B) {
capsule.SetData(test.test)
benchmarkNumberByte(b, insp, capsule)
benchmarkBitmathByte(b, insp, capsule)
},
)
}
Expand Down
4 changes: 2 additions & 2 deletions condition/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type Inspector interface {
// NewInspector returns a configured Inspector from an Inspector configuration.
func NewInspector(ctx context.Context, cfg config.Config) (Inspector, error) {
switch cfg.Type {
case "bitmath":
return newInspBitmath(ctx, cfg)
case "condition":
return newInspCondition(ctx, cfg)
case "content":
Expand All @@ -58,8 +60,6 @@ func NewInspector(ctx context.Context, cfg config.Config) (Inspector, error) {
return newInspJSONValid(ctx, cfg)
case "length":
return newInspLength(ctx, cfg)
case "number":
return newInspNumber(ctx, cfg)
case "random":
return newInspRandom(ctx, cfg)
case "regexp":
Expand Down
Loading

0 comments on commit 387b4ca

Please sign in to comment.