Skip to content

Commit

Permalink
chore: various additions
Browse files Browse the repository at this point in the history
- Rework type registration - its now done recursively and eagerly to avoid bugs in prod.
- Disallow arrays of complex types, since we don't know to properly work with them.
- Disallow interfaces since it is unknown how to unmarshal them.
- Return an error if type is not empty, but has only private fields.
- Disallow custom encoding/decoding of **T and anything above where T kind is Struct.

Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
  • Loading branch information
DmitriyMV committed Aug 17, 2022
1 parent 3e56913 commit 647e9da
Show file tree
Hide file tree
Showing 5 changed files with 440 additions and 316 deletions.
94 changes: 2 additions & 92 deletions marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func fieldByIndex(structVal reflect.Value, data FieldData) reflect.Value {
return result
}

//nolint:cyclop,gocyclo
//nolint:cyclop
func (m *marshaller) encodeValue(num protowire.Number, val reflect.Value) {
if m.tryEncodePredefined(num, val) {
return
Expand Down Expand Up @@ -219,7 +219,7 @@ func (m *marshaller) encodeValue(num protowire.Number, val reflect.Value) {
}

// If the pointer is to a struct
if deref(val.Type()).Kind() == reflect.Struct {
if indirect(val.Type()).Kind() == reflect.Struct {
b, ok := tryEncodeFunc(val)
if ok {
putTag(m, num, protowire.BytesType)
Expand All @@ -231,28 +231,6 @@ func (m *marshaller) encodeValue(num protowire.Number, val reflect.Value) {

m.encodeValue(num, val.Elem())

case reflect.Interface:
// Abstract interface field.
if val.IsNil() {
return
}

// If the object support self-encoding, use that.
if enc, ok := val.Interface().(encoding.BinaryMarshaler); ok {
putTag(m, num, protowire.BytesType)

bytes, err := enc.MarshalBinary()
if err != nil {
panic(err)
}

putBytes(m, bytes)

return
}

m.encodeValue(num, val.Elem())

case reflect.Map:
m.encodeMap(num, val)

Expand Down Expand Up @@ -420,12 +398,7 @@ func (m *marshaller) encodeSlice(key protowire.Number, val reflect.Value) {
putBytes(m, result.Bytes())
}

//nolint:gocyclo,cyclop
func (m *marshaller) sliceReflect(key protowire.Number, val reflect.Value) {
if !isSliceOrArray(val) {
panic("passed value is not slice or array")
}

sliceLen := val.Len()
elem := val.Type().Elem()
result := marshaller{}
Expand Down Expand Up @@ -467,10 +440,6 @@ func (m *marshaller) sliceReflect(key protowire.Number, val reflect.Value) {
}

case reflect.Pointer:
if !isSlicePtrElemSupported(elem) {
panic(fmt.Errorf("unsupported type: '%s'", val.String()))
}

for i := 0; i < sliceLen; i++ {
m.encodeValue(key, val.Index(i))
}
Expand All @@ -481,13 +450,6 @@ func (m *marshaller) sliceReflect(key protowire.Number, val reflect.Value) {
panic(fmt.Errorf("unsupported type %s", val.Type().String()))

default: // Write each element as a separate key,value pair
if elem.Kind() == reflect.Slice || elem.Kind() == reflect.Array {
subSlice := elem.Elem()
if subSlice.Kind() != reflect.Uint8 {
panic("unsupported type: error no support for 2-dimensional array except for [][]byte")
}
}

for i := 0; i < sliceLen; i++ {
m.encodeValue(key, val.Index(i))
}
Expand All @@ -499,56 +461,10 @@ func (m *marshaller) sliceReflect(key protowire.Number, val reflect.Value) {
putBytes(m, result.buf)
}

func isSlicePtrElemSupported(elem reflect.Type) bool {
elem = deref(elem)

switch elem.Kind() { //nolint:exhaustive
case reflect.Int8, reflect.Int16, reflect.Uint8, reflect.Uint16, reflect.Bool,
reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
return false

case reflect.Slice, reflect.Array:
if elem.Elem().Kind() == reflect.Uint8 {
return true
}

return false

default:
return true
}
}

func (m *marshaller) encodeMap(key protowire.Number, mpval reflect.Value) {
first := true

for _, mkey := range mpval.MapKeys() {
mval := mpval.MapIndex(mkey)

if first {
// map key can only be a primitive type or a string
switch mkey.Kind() { //nolint:exhaustive
case reflect.Struct, reflect.Array, reflect.Interface, reflect.Pointer:
panic(errors.New("unsupported type: map key cannot be struct, array, interface or pointer"))
}

unwrapVal := deref(mval.Type())

switch unwrapVal.Kind() { //nolint:exhaustive
case reflect.Slice, reflect.Array:
if mval.Type().Elem() == typeByte {
break
}

fallthrough
case reflect.Interface:
panic(errors.New("unsupported type: map value cannot be non byte slice, array or interface"))
}

first = false
}

if kind := mval.Kind(); kind == reflect.Pointer && mval.IsNil() {
panic("error: map has nil element")
}
Expand Down Expand Up @@ -589,9 +505,3 @@ func putString(m *marshaller, s string) {
func putBytes(m *marshaller, b []byte) {
m.buf = protowire.AppendBytes(m.buf, b)
}

func isSliceOrArray(val reflect.Value) bool {
kind := val.Kind()

return kind == reflect.Slice || kind == reflect.Array
}
Loading

0 comments on commit 647e9da

Please sign in to comment.