forked from TrueCloudLab/policy-engine
[#1] chain: Add binary marshal/unmarshal
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
parent
06cbfe8691
commit
58386edf58
6 changed files with 1112 additions and 3 deletions
2
go.mod
2
go.mod
|
@ -4,6 +4,7 @@ go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.18.1-0.20231129062201-a1b61d394958
|
git.frostfs.info/TrueCloudLab/frostfs-contract v0.18.1-0.20231129062201-a1b61d394958
|
||||||
|
github.com/google/uuid v1.3.0
|
||||||
github.com/nspcc-dev/neo-go v0.103.0
|
github.com/nspcc-dev/neo-go v0.103.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
|
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
|
||||||
|
@ -12,7 +13,6 @@ require (
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
|
||||||
github.com/hashicorp/golang-lru v0.6.0 // indirect
|
github.com/hashicorp/golang-lru v0.6.0 // indirect
|
||||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 // indirect
|
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 // indirect
|
||||||
|
|
|
@ -45,7 +45,7 @@ func (id *ID) UnmarshalJSON(data []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Chain) Bytes() []byte {
|
func (c *Chain) Bytes() []byte {
|
||||||
data, err := json.Marshal(c)
|
data, err := c.MarshalBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ func (c *Chain) Bytes() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Chain) DecodeBytes(b []byte) error {
|
func (c *Chain) DecodeBytes(b []byte) error {
|
||||||
return json.Unmarshal(b, c)
|
return c.UnmarshalBinary(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Rule struct {
|
type Rule struct {
|
||||||
|
|
257
pkg/chain/marshal.go
Normal file
257
pkg/chain/marshal.go
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
package chain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/marshal"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ChainMarshalVersion uint8 = 0 // increase if breaking change
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ encoding.BinaryMarshaler = (*Chain)(nil)
|
||||||
|
_ encoding.BinaryUnmarshaler = (*Chain)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Chain) MarshalBinary() ([]byte, error) {
|
||||||
|
s := marshal.UInt8Size // Marshaller version
|
||||||
|
s += marshal.UInt8Size // Chain version
|
||||||
|
s += marshal.StringSize(string(c.ID))
|
||||||
|
s += marshal.SliceSize(c.Rules, ruleSize)
|
||||||
|
s += marshal.UInt8Size // MatchType
|
||||||
|
|
||||||
|
buf := make([]byte, s)
|
||||||
|
var offset int
|
||||||
|
var err error
|
||||||
|
offset, err = marshal.UInt8Marshal(buf, offset, marshal.Version)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
offset, err = marshal.UInt8Marshal(buf, offset, ChainMarshalVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
offset, err = marshal.StringMarshal(buf, offset, string(c.ID))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
offset, err = marshal.SliceMarshal(buf, offset, c.Rules, marshalRule)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
offset, err = marshal.UInt8Marshal(buf, offset, uint8(c.MatchType))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := marshal.VerifyMarshal(buf, offset); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Chain) UnmarshalBinary(data []byte) error {
|
||||||
|
var offset int
|
||||||
|
|
||||||
|
marshallerVersion, offset, err := marshal.UInt8Unmarshal(data, offset)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if marshallerVersion != marshal.Version {
|
||||||
|
return fmt.Errorf("unsupported marshaller version %d", marshallerVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
chainVersion, offset, err := marshal.UInt8Unmarshal(data, offset)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if chainVersion != ChainMarshalVersion {
|
||||||
|
return fmt.Errorf("unsupported chain version %d", chainVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
idStr, offset, err := marshal.StringUnmarshal(data, offset)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.ID = ID(idStr)
|
||||||
|
|
||||||
|
c.Rules, offset, err = marshal.SliceUnmarshal(data, offset, unmarshalRule)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
matchTypeV, offset, err := marshal.UInt8Unmarshal(data, offset)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.MatchType = MatchType(matchTypeV)
|
||||||
|
|
||||||
|
return marshal.VerifyUnmarshal(data, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ruleSize(r Rule) int {
|
||||||
|
s := marshal.ByteSize // Status
|
||||||
|
s += actionsSize(r.Actions)
|
||||||
|
s += resourcesSize(r.Resources)
|
||||||
|
s += marshal.BoolSize // Any
|
||||||
|
s += marshal.SliceSize(r.Condition, conditionSize)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalRule(buf []byte, offset int, r Rule) (int, error) {
|
||||||
|
offset, err := marshal.ByteMarshal(buf, offset, byte(r.Status))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
offset, err = marshalActions(buf, offset, r.Actions)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
offset, err = marshalResources(buf, offset, r.Resources)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
offset, err = marshal.BoolMarshal(buf, offset, r.Any)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return marshal.SliceMarshal(buf, offset, r.Condition, marshalCondition)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalRule(buf []byte, offset int) (Rule, int, error) {
|
||||||
|
var r Rule
|
||||||
|
statusV, offset, err := marshal.ByteUnmarshal(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return Rule{}, 0, err
|
||||||
|
}
|
||||||
|
r.Status = Status(statusV)
|
||||||
|
|
||||||
|
r.Actions, offset, err = unmarshalActions(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return Rule{}, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Resources, offset, err = unmarshalResources(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return Rule{}, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Any, offset, err = marshal.BoolUnmarshal(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return Rule{}, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Condition, offset, err = marshal.SliceUnmarshal(buf, offset, unmarshalCondition)
|
||||||
|
if err != nil {
|
||||||
|
return Rule{}, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func actionsSize(a Actions) int {
|
||||||
|
return marshal.BoolSize + // Inverted
|
||||||
|
marshal.SliceSize(a.Names, marshal.StringSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalActions(buf []byte, offset int, a Actions) (int, error) {
|
||||||
|
offset, err := marshal.BoolMarshal(buf, offset, a.Inverted)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return marshal.SliceMarshal(buf, offset, a.Names, marshal.StringMarshal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalActions(buf []byte, offset int) (Actions, int, error) {
|
||||||
|
var a Actions
|
||||||
|
var err error
|
||||||
|
a.Inverted, offset, err = marshal.BoolUnmarshal(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return Actions{}, 0, err
|
||||||
|
}
|
||||||
|
a.Names, offset, err = marshal.SliceUnmarshal(buf, offset, marshal.StringUnmarshal)
|
||||||
|
if err != nil {
|
||||||
|
return Actions{}, 0, err
|
||||||
|
}
|
||||||
|
return a, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcesSize(r Resources) int {
|
||||||
|
return marshal.BoolSize + // Inverted
|
||||||
|
marshal.SliceSize(r.Names, marshal.StringSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalResources(buf []byte, offset int, r Resources) (int, error) {
|
||||||
|
offset, err := marshal.BoolMarshal(buf, offset, r.Inverted)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return marshal.SliceMarshal(buf, offset, r.Names, marshal.StringMarshal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalResources(buf []byte, offset int) (Resources, int, error) {
|
||||||
|
var r Resources
|
||||||
|
var err error
|
||||||
|
r.Inverted, offset, err = marshal.BoolUnmarshal(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return Resources{}, 0, err
|
||||||
|
}
|
||||||
|
r.Names, offset, err = marshal.SliceUnmarshal(buf, offset, marshal.StringUnmarshal)
|
||||||
|
if err != nil {
|
||||||
|
return Resources{}, 0, err
|
||||||
|
}
|
||||||
|
return r, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func conditionSize(c Condition) int {
|
||||||
|
return marshal.ByteSize + // Op
|
||||||
|
marshal.ByteSize + // Object
|
||||||
|
marshal.StringSize(c.Key) +
|
||||||
|
marshal.StringSize(c.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalCondition(buf []byte, offset int, c Condition) (int, error) {
|
||||||
|
offset, err := marshal.ByteMarshal(buf, offset, byte(c.Op))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
offset, err = marshal.ByteMarshal(buf, offset, byte(c.Object))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
offset, err = marshal.StringMarshal(buf, offset, c.Key)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return marshal.StringMarshal(buf, offset, c.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalCondition(buf []byte, offset int) (Condition, int, error) {
|
||||||
|
var c Condition
|
||||||
|
opV, offset, err := marshal.ByteUnmarshal(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return Condition{}, 0, err
|
||||||
|
}
|
||||||
|
c.Op = ConditionType(opV)
|
||||||
|
|
||||||
|
obV, offset, err := marshal.ByteUnmarshal(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return Condition{}, 0, err
|
||||||
|
}
|
||||||
|
c.Object = ObjectType(obV)
|
||||||
|
|
||||||
|
c.Key, offset, err = marshal.StringUnmarshal(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return Condition{}, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Value, offset, err = marshal.StringUnmarshal(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return Condition{}, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, offset, nil
|
||||||
|
}
|
272
pkg/chain/marshal_test.go
Normal file
272
pkg/chain/marshal_test.go
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
package chain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestChainMarshalling(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
for _, id := range generateTestIDs() {
|
||||||
|
for _, rules := range generateTestRules() {
|
||||||
|
for _, matchType := range generateTestMatchTypes() {
|
||||||
|
performMarshalTest(t, id, rules, matchType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidChainData(t *testing.T) {
|
||||||
|
var ch Chain
|
||||||
|
require.Error(t, ch.UnmarshalBinary(nil))
|
||||||
|
require.Error(t, ch.UnmarshalBinary([]byte{}))
|
||||||
|
require.Error(t, ch.UnmarshalBinary([]byte{1, 2, 3}))
|
||||||
|
require.Error(t, ch.UnmarshalBinary([]byte("\x00\x00:aws:iam::namespace:group/so\x82\x82\x82\x82\x82\x82u\x82")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzUnmarshal(f *testing.F) {
|
||||||
|
for _, id := range generateTestIDs() {
|
||||||
|
for _, rules := range generateTestRules() {
|
||||||
|
for _, matchType := range generateTestMatchTypes() {
|
||||||
|
|
||||||
|
chain := Chain{
|
||||||
|
ID: id,
|
||||||
|
Rules: rules,
|
||||||
|
MatchType: matchType,
|
||||||
|
}
|
||||||
|
data, err := chain.MarshalBinary()
|
||||||
|
require.NoError(f, err)
|
||||||
|
f.Add(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
|
var ch Chain
|
||||||
|
require.NotPanics(t, func() {
|
||||||
|
_ = ch.UnmarshalBinary(data)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func performMarshalTest(t *testing.T, id ID, r []Rule, mt MatchType) {
|
||||||
|
chain := Chain{
|
||||||
|
ID: id,
|
||||||
|
Rules: r,
|
||||||
|
MatchType: mt,
|
||||||
|
}
|
||||||
|
data, err := chain.MarshalBinary()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var unmarshalledChain Chain
|
||||||
|
require.NoError(t, unmarshalledChain.UnmarshalBinary(data))
|
||||||
|
|
||||||
|
require.Equal(t, chain, unmarshalledChain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTestIDs() []ID {
|
||||||
|
return []ID{
|
||||||
|
ID(""),
|
||||||
|
ID(uuid.New().String()),
|
||||||
|
ID("*::/"),
|
||||||
|
ID("avada kedavra"),
|
||||||
|
ID("arn:aws:iam::namespace:group/some_group"),
|
||||||
|
ID("$Object:homomorphicHash"),
|
||||||
|
ID("native:container/ns/9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTestRules() [][]Rule {
|
||||||
|
result := [][]Rule{
|
||||||
|
nil,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, st := range generateTestStatuses() {
|
||||||
|
for _, act := range generateTestActions() {
|
||||||
|
for _, res := range generateTestResources() {
|
||||||
|
for _, cond := range generateTestConditions() {
|
||||||
|
result[2] = append(result[2], Rule{
|
||||||
|
Status: st,
|
||||||
|
Actions: act,
|
||||||
|
Resources: res,
|
||||||
|
Condition: cond,
|
||||||
|
Any: true,
|
||||||
|
})
|
||||||
|
result[2] = append(result[2], Rule{
|
||||||
|
Status: st,
|
||||||
|
Actions: act,
|
||||||
|
Resources: res,
|
||||||
|
Condition: cond,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTestStatuses() []Status {
|
||||||
|
return []Status{
|
||||||
|
Allow,
|
||||||
|
NoRuleFound,
|
||||||
|
AccessDenied,
|
||||||
|
QuotaLimitReached,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTestActions() []Actions {
|
||||||
|
return []Actions{
|
||||||
|
{
|
||||||
|
Inverted: true,
|
||||||
|
Names: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Names: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Inverted: true,
|
||||||
|
Names: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Names: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Inverted: true,
|
||||||
|
Names: []string{native.MethodPutObject},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Names: []string{native.MethodPutObject},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Inverted: true,
|
||||||
|
Names: []string{native.MethodPutObject, native.MethodDeleteContainer, native.MethodDeleteObject},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Names: []string{native.MethodPutObject, native.MethodDeleteContainer, native.MethodDeleteObject},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTestResources() []Resources {
|
||||||
|
return []Resources{
|
||||||
|
{
|
||||||
|
Inverted: true,
|
||||||
|
Names: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Names: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Inverted: true,
|
||||||
|
Names: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Names: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Inverted: true,
|
||||||
|
Names: []string{native.ResourceFormatAllObjects},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Names: []string{native.ResourceFormatAllObjects},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Inverted: true,
|
||||||
|
Names: []string{
|
||||||
|
native.ResourceFormatAllObjects,
|
||||||
|
fmt.Sprintf(native.ResourceFormatRootContainer, "9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Names: []string{
|
||||||
|
native.ResourceFormatAllObjects,
|
||||||
|
fmt.Sprintf(native.ResourceFormatRootContainer, "9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTestConditions() [][]Condition {
|
||||||
|
result := [][]Condition{
|
||||||
|
nil,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ct := range generateTestConditionTypes() {
|
||||||
|
for _, ot := range generateObjectTypes() {
|
||||||
|
result[2] = append(result[2], Condition{
|
||||||
|
Op: ct,
|
||||||
|
Object: ot,
|
||||||
|
Key: "",
|
||||||
|
Value: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
result[2] = append(result[2], Condition{
|
||||||
|
Op: ct,
|
||||||
|
Object: ot,
|
||||||
|
Key: "key",
|
||||||
|
Value: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
result[2] = append(result[2], Condition{
|
||||||
|
Op: ct,
|
||||||
|
Object: ot,
|
||||||
|
Key: "",
|
||||||
|
Value: "value",
|
||||||
|
})
|
||||||
|
|
||||||
|
result[2] = append(result[2], Condition{
|
||||||
|
Op: ct,
|
||||||
|
Object: ot,
|
||||||
|
Key: "key",
|
||||||
|
Value: "value",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTestConditionTypes() []ConditionType {
|
||||||
|
return []ConditionType{
|
||||||
|
CondStringEquals,
|
||||||
|
CondStringNotEquals,
|
||||||
|
CondStringEqualsIgnoreCase,
|
||||||
|
CondStringNotEqualsIgnoreCase,
|
||||||
|
CondStringLike,
|
||||||
|
CondStringNotLike,
|
||||||
|
CondStringLessThan,
|
||||||
|
CondStringLessThanEquals,
|
||||||
|
CondStringGreaterThan,
|
||||||
|
CondStringGreaterThanEquals,
|
||||||
|
CondNumericEquals,
|
||||||
|
CondNumericNotEquals,
|
||||||
|
CondNumericLessThan,
|
||||||
|
CondNumericLessThanEquals,
|
||||||
|
CondNumericGreaterThan,
|
||||||
|
CondNumericGreaterThanEquals,
|
||||||
|
CondSliceContains,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateObjectTypes() []ObjectType {
|
||||||
|
return []ObjectType{
|
||||||
|
ObjectResource,
|
||||||
|
ObjectRequest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTestMatchTypes() []MatchType {
|
||||||
|
return []MatchType{
|
||||||
|
MatchTypeDenyPriority,
|
||||||
|
MatchTypeFirstMatch,
|
||||||
|
}
|
||||||
|
}
|
267
pkg/marshal/marshal.go
Normal file
267
pkg/marshal/marshal.go
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
package marshal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Version byte = 0 // increase if breaking change
|
||||||
|
|
||||||
|
ByteSize int = 1
|
||||||
|
UInt8Size int = ByteSize
|
||||||
|
BoolSize int = ByteSize
|
||||||
|
|
||||||
|
nilSlice int64 = -1
|
||||||
|
nilSliceSize int = 1
|
||||||
|
|
||||||
|
byteTrue uint8 = 1
|
||||||
|
byteFalse uint8 = 0
|
||||||
|
|
||||||
|
// maxSliceLen taken from https://github.com/neo-project/neo/blob/38218bbee5bbe8b33cd8f9453465a19381c9a547/src/Neo/IO/Helper.cs#L77
|
||||||
|
maxSliceLen = 0x1000000
|
||||||
|
)
|
||||||
|
|
||||||
|
type MarshallerError struct {
|
||||||
|
errMsg string
|
||||||
|
offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MarshallerError) Error() string {
|
||||||
|
if e == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if e.offset < 0 {
|
||||||
|
return e.errMsg
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s (offset: %d)", e.errMsg, e.offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func errBufTooSmall(t string, marshal bool, offset int) error {
|
||||||
|
action := "unmarshal"
|
||||||
|
if marshal {
|
||||||
|
action = "marshal"
|
||||||
|
}
|
||||||
|
return &MarshallerError{
|
||||||
|
errMsg: fmt.Sprintf("not enough bytes left to %s value of type '%s'", action, t),
|
||||||
|
offset: offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func VerifyMarshal(buf []byte, lastOffset int) error {
|
||||||
|
if len(buf) != lastOffset {
|
||||||
|
return &MarshallerError{
|
||||||
|
errMsg: "actual data size differs from expected",
|
||||||
|
offset: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func VerifyUnmarshal(buf []byte, lastOffset int) error {
|
||||||
|
if len(buf) != lastOffset {
|
||||||
|
return &MarshallerError{
|
||||||
|
errMsg: "unmarshalled bytes left",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceSize[T any](slice []T, sizeOf func(T) int) int {
|
||||||
|
if slice == nil {
|
||||||
|
return nilSliceSize
|
||||||
|
}
|
||||||
|
s := Int64Size(int64(len(slice)))
|
||||||
|
for _, v := range slice {
|
||||||
|
s += sizeOf(v)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceMarshal[T any](buf []byte, offset int, slice []T, marshalT func([]byte, int, T) (int, error)) (int, error) {
|
||||||
|
if slice == nil {
|
||||||
|
return Int64Marshal(buf, offset, nilSlice)
|
||||||
|
}
|
||||||
|
if len(slice) > maxSliceLen {
|
||||||
|
return 0, &MarshallerError{
|
||||||
|
errMsg: fmt.Sprintf("slice size if too big: '%d'", len(slice)),
|
||||||
|
offset: offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset, err := Int64Marshal(buf, offset, int64(len(slice)))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
for _, v := range slice {
|
||||||
|
offset, err = marshalT(buf, offset, v)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceUnmarshal[T any](buf []byte, offset int, unmarshalT func(buf []byte, offset int) (T, int, error)) ([]T, int, error) {
|
||||||
|
size, offset, err := Int64Unmarshal(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if size == nilSlice {
|
||||||
|
return nil, offset, nil
|
||||||
|
}
|
||||||
|
if size > maxSliceLen {
|
||||||
|
return nil, 0, &MarshallerError{
|
||||||
|
errMsg: fmt.Sprintf("slice size if too big: '%d'", size),
|
||||||
|
offset: offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if size < 0 {
|
||||||
|
return nil, 0, &MarshallerError{
|
||||||
|
errMsg: fmt.Sprintf("invalid slice size: '%d'", size),
|
||||||
|
offset: offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result := make([]T, size)
|
||||||
|
for idx := 0; idx < len(result); idx++ {
|
||||||
|
result[idx], offset, err = unmarshalT(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Int64Size(v int64) int {
|
||||||
|
// https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=92;drc=dac9b9ddbd5160c5f4552410f5f8281bd5eed38c
|
||||||
|
// and
|
||||||
|
// https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=41;drc=dac9b9ddbd5160c5f4552410f5f8281bd5eed38c
|
||||||
|
ux := uint64(v) << 1
|
||||||
|
if v < 0 {
|
||||||
|
ux = ^ux
|
||||||
|
}
|
||||||
|
s := 0
|
||||||
|
for ux >= 0x80 {
|
||||||
|
s++
|
||||||
|
ux >>= 7
|
||||||
|
}
|
||||||
|
return s + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func Int64Marshal(buf []byte, offset int, v int64) (int, error) {
|
||||||
|
if len(buf)-offset < Int64Size(v) {
|
||||||
|
return 0, errBufTooSmall("int64", true, offset)
|
||||||
|
}
|
||||||
|
return offset + binary.PutVarint(buf[offset:], v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Int64Unmarshal(buf []byte, offset int) (int64, int, error) {
|
||||||
|
v, read := binary.Varint(buf[offset:])
|
||||||
|
if read == 0 {
|
||||||
|
return 0, 0, errBufTooSmall("int64", false, offset)
|
||||||
|
}
|
||||||
|
if read < 0 {
|
||||||
|
return 0, 0, &MarshallerError{
|
||||||
|
errMsg: "int64 unmarshal overflow",
|
||||||
|
offset: offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v, offset + read, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func StringSize(s string) int {
|
||||||
|
return Int64Size(int64(len(s))) + len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func StringMarshal(buf []byte, offset int, s string) (int, error) {
|
||||||
|
if len(s) > maxSliceLen {
|
||||||
|
return 0, &MarshallerError{
|
||||||
|
errMsg: fmt.Sprintf("string is too long: '%d'", len(s)),
|
||||||
|
offset: offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(buf)-offset < Int64Size(int64(len(s)))+len(s) {
|
||||||
|
return 0, errBufTooSmall("string", true, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
offset, err := Int64Marshal(buf, offset, int64(len(s)))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
return offset, nil
|
||||||
|
}
|
||||||
|
return offset + copy(buf[offset:], s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func StringUnmarshal(buf []byte, offset int) (string, int, error) {
|
||||||
|
size, offset, err := Int64Unmarshal(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
if size == 0 {
|
||||||
|
return "", offset, nil
|
||||||
|
}
|
||||||
|
if size > maxSliceLen {
|
||||||
|
return "", 0, &MarshallerError{
|
||||||
|
errMsg: fmt.Sprintf("string is too long: '%d'", size),
|
||||||
|
offset: offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if size < 0 {
|
||||||
|
return "", 0, &MarshallerError{
|
||||||
|
errMsg: fmt.Sprintf("invalid string size: '%d'", size),
|
||||||
|
offset: offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(buf)-offset < int(size) {
|
||||||
|
return "", 0, errBufTooSmall("string", false, offset)
|
||||||
|
}
|
||||||
|
return string(buf[offset : offset+int(size)]), offset + int(size), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UInt8Marshal(buf []byte, offset int, value uint8) (int, error) {
|
||||||
|
if len(buf)-offset < 1 {
|
||||||
|
return 0, errBufTooSmall("uint8", true, offset)
|
||||||
|
}
|
||||||
|
buf[offset] = value
|
||||||
|
return offset + 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UInt8Unmarshal(buf []byte, offset int) (uint8, int, error) {
|
||||||
|
if len(buf)-offset < 1 {
|
||||||
|
return 0, 0, errBufTooSmall("uint8", false, offset)
|
||||||
|
}
|
||||||
|
return buf[offset], offset + 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ByteMarshal(buf []byte, offset int, value byte) (int, error) {
|
||||||
|
return UInt8Marshal(buf, offset, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ByteUnmarshal(buf []byte, offset int) (byte, int, error) {
|
||||||
|
return UInt8Unmarshal(buf, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BoolMarshal(buf []byte, offset int, value bool) (int, error) {
|
||||||
|
if value {
|
||||||
|
return UInt8Marshal(buf, offset, byteTrue)
|
||||||
|
}
|
||||||
|
return UInt8Marshal(buf, offset, byteFalse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BoolUnmarshal(buf []byte, offset int) (bool, int, error) {
|
||||||
|
v, offset, err := UInt8Unmarshal(buf, offset)
|
||||||
|
if err != nil {
|
||||||
|
return false, 0, err
|
||||||
|
}
|
||||||
|
if v == byteTrue {
|
||||||
|
return true, offset, nil
|
||||||
|
}
|
||||||
|
if v == byteFalse {
|
||||||
|
return false, offset, nil
|
||||||
|
}
|
||||||
|
return false, 0, &MarshallerError{
|
||||||
|
errMsg: fmt.Sprintf("invalid marshalled value for bool: %d", v),
|
||||||
|
offset: offset - BoolSize,
|
||||||
|
}
|
||||||
|
}
|
313
pkg/marshal/marshal_test.go
Normal file
313
pkg/marshal/marshal_test.go
Normal file
|
@ -0,0 +1,313 @@
|
||||||
|
package marshal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMarshalling(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
t.Run("slice", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
t.Run("nil slice", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var int64s []int64
|
||||||
|
expectedSize := SliceSize(int64s, Int64Size)
|
||||||
|
require.Equal(t, 1, expectedSize)
|
||||||
|
buf := make([]byte, expectedSize)
|
||||||
|
offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyMarshal(buf, offset))
|
||||||
|
|
||||||
|
result, offset, err := SliceUnmarshal(buf, 0, Int64Unmarshal)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||||
|
require.Nil(t, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("empty slice", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
int64s := make([]int64, 0)
|
||||||
|
expectedSize := SliceSize(int64s, Int64Size)
|
||||||
|
require.Equal(t, 1, expectedSize)
|
||||||
|
buf := make([]byte, expectedSize)
|
||||||
|
offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyMarshal(buf, offset))
|
||||||
|
|
||||||
|
result, offset, err := SliceUnmarshal(buf, 0, Int64Unmarshal)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||||
|
require.NotNil(t, result)
|
||||||
|
require.Len(t, result, 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("non empty slice", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
int64s := make([]int64, 100)
|
||||||
|
for i := range int64s {
|
||||||
|
int64s[i] = int64(i)
|
||||||
|
}
|
||||||
|
expectedSize := SliceSize(int64s, Int64Size)
|
||||||
|
buf := make([]byte, expectedSize)
|
||||||
|
offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyMarshal(buf, offset))
|
||||||
|
|
||||||
|
result, offset, err := SliceUnmarshal(buf, 0, Int64Unmarshal)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||||
|
require.Equal(t, int64s, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("corrupted slice size", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
int64s := make([]int64, 100)
|
||||||
|
for i := range int64s {
|
||||||
|
int64s[i] = int64(i)
|
||||||
|
}
|
||||||
|
expectedSize := SliceSize(int64s, Int64Size)
|
||||||
|
buf := make([]byte, expectedSize)
|
||||||
|
offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyMarshal(buf, offset))
|
||||||
|
|
||||||
|
for i := 0; i < binary.MaxVarintLen64; i++ {
|
||||||
|
buf[i] = 129
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = SliceUnmarshal(buf, 0, Int64Unmarshal)
|
||||||
|
var mErr *MarshallerError
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
|
||||||
|
for i := 0; i < binary.MaxVarintLen64; i++ {
|
||||||
|
buf[i] = 127
|
||||||
|
}
|
||||||
|
_, _, err = SliceUnmarshal(buf, 0, Int64Unmarshal)
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("corrupted slice item", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
int64s := make([]int64, 100)
|
||||||
|
for i := range int64s {
|
||||||
|
int64s[i] = int64(i)
|
||||||
|
}
|
||||||
|
expectedSize := SliceSize(int64s, Int64Size)
|
||||||
|
buf := make([]byte, expectedSize)
|
||||||
|
offset, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyMarshal(buf, offset))
|
||||||
|
|
||||||
|
for i := 2; i < binary.MaxVarintLen64+2; i++ {
|
||||||
|
buf[i] = 129
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = SliceUnmarshal(buf, 0, Int64Unmarshal)
|
||||||
|
var mErr *MarshallerError
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("small buffer", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
int64s := make([]int64, 100)
|
||||||
|
for i := range int64s {
|
||||||
|
int64s[i] = int64(i)
|
||||||
|
}
|
||||||
|
buf := make([]byte, 1)
|
||||||
|
_, err := SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||||
|
var mErr *MarshallerError
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
|
||||||
|
buf = make([]byte, 10)
|
||||||
|
_, err = SliceMarshal(buf, 0, int64s, Int64Marshal)
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("int64", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("success", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
require.Equal(t, 1, Int64Size(0))
|
||||||
|
require.Equal(t, binary.MaxVarintLen64, Int64Size(math.MaxInt64))
|
||||||
|
require.Equal(t, binary.MaxVarintLen64, Int64Size(math.MinInt64))
|
||||||
|
|
||||||
|
for _, v := range []int64{0, math.MinInt64, math.MaxInt64} {
|
||||||
|
size := Int64Size(v)
|
||||||
|
buf := make([]byte, size)
|
||||||
|
offset, err := Int64Marshal(buf, 0, v)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyMarshal(buf, offset))
|
||||||
|
|
||||||
|
uv, offset, err := Int64Unmarshal(buf, 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||||
|
require.Equal(t, v, uv)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("invalid buffer", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var mErr *MarshallerError
|
||||||
|
|
||||||
|
_, err := Int64Marshal([]byte{}, 0, 100500)
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
|
||||||
|
_, _, err = Int64Unmarshal(nil, 0)
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("overflow", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var mErr *MarshallerError
|
||||||
|
|
||||||
|
var v int64 = math.MaxInt64
|
||||||
|
buf := make([]byte, Int64Size(v))
|
||||||
|
_, err := Int64Marshal(buf, 0, v)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
buf[9] = 2
|
||||||
|
|
||||||
|
_, _, err = Int64Unmarshal(buf, 0)
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("string", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("success", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
for _, v := range []string{
|
||||||
|
"", "arn:aws:iam::namespace:group/some_group", "$Object:homomorphicHash",
|
||||||
|
"native:container/ns/9LPLUFZpEmfidG4n44vi2cjXKXSqWT492tCvLJiJ8W1J",
|
||||||
|
} {
|
||||||
|
size := StringSize(v)
|
||||||
|
buf := make([]byte, size)
|
||||||
|
offset, err := StringMarshal(buf, 0, v)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyMarshal(buf, offset))
|
||||||
|
|
||||||
|
uv, offset, err := StringUnmarshal(buf, 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||||
|
require.Equal(t, v, uv)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("invalid buffer", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
str := "avada kedavra"
|
||||||
|
|
||||||
|
var mErr *MarshallerError
|
||||||
|
_, err := StringMarshal(nil, 0, str)
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
|
||||||
|
_, _, err = StringUnmarshal(nil, 0)
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
|
||||||
|
buf := make([]byte, StringSize(str))
|
||||||
|
offset, err := StringMarshal(buf, 0, str)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyMarshal(buf, offset))
|
||||||
|
buf = buf[:len(buf)-1]
|
||||||
|
_, _, err = StringUnmarshal(buf, 0)
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("uint8, byte", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for _, v := range []byte{0, 8, 16, 32, 64, 128, 255} {
|
||||||
|
buf := make([]byte, ByteSize)
|
||||||
|
offset, err := ByteMarshal(buf, 0, v)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyMarshal(buf, offset))
|
||||||
|
|
||||||
|
ub, offset, err := ByteUnmarshal(buf, 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||||
|
require.Equal(t, v, ub)
|
||||||
|
|
||||||
|
buf = make([]byte, UInt8Size)
|
||||||
|
offset, err = UInt8Marshal(buf, 0, v)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyMarshal(buf, offset))
|
||||||
|
|
||||||
|
uu, offset, err := UInt8Unmarshal(buf, 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||||
|
require.Equal(t, v, uu)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("bool", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("success", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
for _, v := range []bool{false, true} {
|
||||||
|
buf := make([]byte, BoolSize)
|
||||||
|
offset, err := BoolMarshal(buf, 0, v)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyMarshal(buf, offset))
|
||||||
|
|
||||||
|
ub, offset, err := BoolUnmarshal(buf, 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyUnmarshal(buf, offset))
|
||||||
|
require.Equal(t, v, ub)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("invalid value", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
buf := make([]byte, BoolSize)
|
||||||
|
offset, err := BoolMarshal(buf, 0, true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, VerifyMarshal(buf, offset))
|
||||||
|
|
||||||
|
buf[0] = 2
|
||||||
|
|
||||||
|
_, _, err = BoolUnmarshal(buf, 0)
|
||||||
|
var mErr *MarshallerError
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("invalid buffer", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
var mErr *MarshallerError
|
||||||
|
|
||||||
|
_, err := BoolMarshal(nil, 0, true)
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
|
||||||
|
buf := append(make([]byte, BoolSize), 100)
|
||||||
|
offset, err := BoolMarshal(buf, 0, true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.ErrorAs(t, VerifyMarshal(buf, offset), &mErr)
|
||||||
|
|
||||||
|
v, offset, err := BoolUnmarshal(buf, 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, v)
|
||||||
|
require.ErrorAs(t, VerifyUnmarshal(buf, offset), &mErr)
|
||||||
|
|
||||||
|
_, _, err = BoolUnmarshal(nil, 0)
|
||||||
|
require.ErrorAs(t, err, &mErr)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue