176 lines
6.0 KiB
Go
176 lines
6.0 KiB
Go
package codec
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
var ErrEncoding = errors.New("collections: encoding error")
|
|
|
|
// KeyCodec defines a generic interface which is implemented
|
|
// by types that are capable of encoding and decoding collections keys.
|
|
type KeyCodec[T any] interface {
|
|
// Encode writes the key bytes into the buffer. Returns the number of
|
|
// bytes written. The implementer must expect the buffer to be at least
|
|
// of length equal to Size(K) for all encodings.
|
|
// It must also return the number of written bytes which must be
|
|
// equal to Size(K) for all encodings not involving varints.
|
|
// In case of encodings involving varints then the returned
|
|
// number of written bytes is allowed to be smaller than Size(K).
|
|
Encode(buffer []byte, key T) (int, error)
|
|
// Decode reads from the provided bytes buffer to decode
|
|
// the key T. Returns the number of bytes read, the type T
|
|
// or an error in case of decoding failure.
|
|
Decode(buffer []byte) (int, T, error)
|
|
// Size returns the buffer size need to encode key T in binary format.
|
|
// The returned value must match what is computed by Encode for all
|
|
// encodings except the ones involving varints. Varints are expected
|
|
// to return the maximum varint bytes buffer length, at the risk of
|
|
// over-estimating in order to pick the most performant path.
|
|
Size(key T) int
|
|
// EncodeJSON encodes the value as JSON.
|
|
EncodeJSON(value T) ([]byte, error)
|
|
// DecodeJSON decodes the provided JSON bytes into an instance of T.
|
|
DecodeJSON(b []byte) (T, error)
|
|
// Stringify returns a string representation of T.
|
|
Stringify(key T) string
|
|
// KeyType returns a string identifier for the type of the key.
|
|
KeyType() string
|
|
|
|
// MULTIPART keys
|
|
|
|
// EncodeNonTerminal writes the key bytes into the buffer.
|
|
// EncodeNonTerminal is used in multipart keys like Pair
|
|
// when the part of the key being encoded is not the last one,
|
|
// and there needs to be a way to distinguish after how many bytes
|
|
// the first part of the key is finished. The buffer is expected to be
|
|
// at least as big as SizeNonTerminal(key) returns. It returns
|
|
// the amount of bytes written.
|
|
EncodeNonTerminal(buffer []byte, key T) (int, error)
|
|
// DecodeNonTerminal reads the buffer provided and returns
|
|
// the key T. DecodeNonTerminal is used in multipart keys
|
|
// like Pair when the part of the key being decoded is not the
|
|
// last one. It returns the amount of bytes read.
|
|
DecodeNonTerminal(buffer []byte) (int, T, error)
|
|
// SizeNonTerminal returns the maximum size of the key K when used in
|
|
// multipart keys like Pair.
|
|
SizeNonTerminal(key T) int
|
|
}
|
|
|
|
// ValueCodec defines a generic interface which is implemented
|
|
// by types that are capable of encoding and decoding collection values.
|
|
type ValueCodec[T any] interface {
|
|
// Encode encodes the value T into binary format.
|
|
Encode(value T) ([]byte, error)
|
|
// Decode returns the type T given its binary representation.
|
|
Decode(b []byte) (T, error)
|
|
// EncodeJSON encodes the value as JSON.
|
|
EncodeJSON(value T) ([]byte, error)
|
|
// DecodeJSON decodes the provided JSON bytes into an instance of T.
|
|
DecodeJSON(b []byte) (T, error)
|
|
// Stringify returns a string representation of T.
|
|
Stringify(value T) string
|
|
// ValueType returns the identifier for the type.
|
|
ValueType() string
|
|
}
|
|
|
|
// NewUntypedValueCodec returns an UntypedValueCodec for the provided ValueCodec.
|
|
func NewUntypedValueCodec[V any](v ValueCodec[V]) UntypedValueCodec {
|
|
typeName := fmt.Sprintf("%T", *new(V))
|
|
checkType := func(value interface{}) (v V, err error) {
|
|
concrete, ok := value.(V)
|
|
if !ok {
|
|
return v, fmt.Errorf("%w: expected value of type %s, got %T", ErrEncoding, typeName, value)
|
|
}
|
|
return concrete, nil
|
|
}
|
|
return UntypedValueCodec{
|
|
Decode: func(b []byte) (interface{}, error) { return v.Decode(b) },
|
|
Encode: func(value interface{}) ([]byte, error) {
|
|
concrete, err := checkType(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return v.Encode(concrete)
|
|
},
|
|
DecodeJSON: func(b []byte) (interface{}, error) {
|
|
return v.DecodeJSON(b)
|
|
},
|
|
EncodeJSON: func(value interface{}) ([]byte, error) {
|
|
concrete, err := checkType(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return v.EncodeJSON(concrete)
|
|
},
|
|
Stringify: func(value interface{}) (string, error) {
|
|
concrete, err := checkType(value)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return v.Stringify(concrete), nil
|
|
},
|
|
ValueType: func() string { return v.ValueType() },
|
|
}
|
|
}
|
|
|
|
// UntypedValueCodec wraps a ValueCodec to expose an untyped API for encoding and decoding values.
|
|
type UntypedValueCodec struct {
|
|
Decode func(b []byte) (interface{}, error)
|
|
Encode func(value interface{}) ([]byte, error)
|
|
DecodeJSON func(b []byte) (interface{}, error)
|
|
EncodeJSON func(value interface{}) ([]byte, error)
|
|
Stringify func(value interface{}) (string, error)
|
|
ValueType func() string
|
|
}
|
|
|
|
// KeyToValueCodec converts a KeyCodec into a ValueCodec.
|
|
func KeyToValueCodec[K any](keyCodec KeyCodec[K]) NameableValueCodec[K] {
|
|
return keyToValueCodec[K]{kc: keyCodec}
|
|
}
|
|
|
|
// keyToValueCodec is a ValueCodec that wraps a KeyCodec to make it behave like a ValueCodec.
|
|
type keyToValueCodec[K any] struct {
|
|
kc KeyCodec[K]
|
|
}
|
|
|
|
func (k keyToValueCodec[K]) EncodeJSON(value K) ([]byte, error) {
|
|
return k.kc.EncodeJSON(value)
|
|
}
|
|
|
|
func (k keyToValueCodec[K]) DecodeJSON(b []byte) (K, error) {
|
|
return k.kc.DecodeJSON(b)
|
|
}
|
|
|
|
func (k keyToValueCodec[K]) Encode(value K) ([]byte, error) {
|
|
buf := make([]byte, k.kc.Size(value))
|
|
_, err := k.kc.Encode(buf, value)
|
|
return buf, err
|
|
}
|
|
|
|
func (k keyToValueCodec[K]) Decode(b []byte) (K, error) {
|
|
r, key, err := k.kc.Decode(b)
|
|
if err != nil {
|
|
var key K
|
|
return key, err
|
|
}
|
|
|
|
if r != len(b) {
|
|
var key K
|
|
return key, fmt.Errorf("%w: was supposed to fully consume the key '%x', consumed %d out of %d", ErrEncoding, b, r, len(b))
|
|
}
|
|
return key, nil
|
|
}
|
|
|
|
func (k keyToValueCodec[K]) Stringify(value K) string {
|
|
return k.kc.Stringify(value)
|
|
}
|
|
|
|
func (k keyToValueCodec[K]) ValueType() string {
|
|
return k.kc.KeyType()
|
|
}
|
|
|
|
func (k keyToValueCodec[K]) WithName(name string) ValueCodec[K] {
|
|
return NamedValueCodec[K]{ValueCodec: k, Name: name}
|
|
}
|