package codec import ( "encoding/binary" "fmt" "strings" "github.com/cosmos/cosmos-sdk/codec/types" "github.com/gogo/protobuf/jsonpb" "github.com/gogo/protobuf/proto" ) // ProtoCodec defines a codec that utilizes Protobuf for both binary and JSON // encoding. type ProtoCodec struct { anyUnpacker types.AnyUnpacker } var _ Marshaler = &ProtoCodec{} // NewProtoCodec returns a reference to a new ProtoCodec func NewProtoCodec(anyUnpacker types.AnyUnpacker) *ProtoCodec { return &ProtoCodec{anyUnpacker: anyUnpacker} } // MarshalBinaryBare implements BinaryMarshaler.MarshalBinaryBare method. func (pc *ProtoCodec) MarshalBinaryBare(o ProtoMarshaler) ([]byte, error) { return o.Marshal() } // MustMarshalBinaryBare implements BinaryMarshaler.MustMarshalBinaryBare method. func (pc *ProtoCodec) MustMarshalBinaryBare(o ProtoMarshaler) []byte { bz, err := pc.MarshalBinaryBare(o) if err != nil { panic(err) } return bz } // MarshalBinaryLengthPrefixed implements BinaryMarshaler.MarshalBinaryLengthPrefixed method. func (pc *ProtoCodec) MarshalBinaryLengthPrefixed(o ProtoMarshaler) ([]byte, error) { bz, err := pc.MarshalBinaryBare(o) if err != nil { return nil, err } var sizeBuf [binary.MaxVarintLen64]byte n := binary.PutUvarint(sizeBuf[:], uint64(o.Size())) return append(sizeBuf[:n], bz...), nil } // MustMarshalBinaryLengthPrefixed implements BinaryMarshaler.MustMarshalBinaryLengthPrefixed method. func (pc *ProtoCodec) MustMarshalBinaryLengthPrefixed(o ProtoMarshaler) []byte { bz, err := pc.MarshalBinaryLengthPrefixed(o) if err != nil { panic(err) } return bz } // UnmarshalBinaryBare implements BinaryMarshaler.UnmarshalBinaryBare method. func (pc *ProtoCodec) UnmarshalBinaryBare(bz []byte, ptr ProtoMarshaler) error { err := ptr.Unmarshal(bz) if err != nil { return err } err = types.UnpackInterfaces(ptr, pc.anyUnpacker) if err != nil { return err } return nil } // MustUnmarshalBinaryBare implements BinaryMarshaler.MustUnmarshalBinaryBare method. func (pc *ProtoCodec) MustUnmarshalBinaryBare(bz []byte, ptr ProtoMarshaler) { if err := pc.UnmarshalBinaryBare(bz, ptr); err != nil { panic(err) } } // UnmarshalBinaryLengthPrefixed implements BinaryMarshaler.UnmarshalBinaryLengthPrefixed method. func (pc *ProtoCodec) UnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshaler) error { size, n := binary.Uvarint(bz) if n < 0 { return fmt.Errorf("invalid number of bytes read from length-prefixed encoding: %d", n) } if size > uint64(len(bz)-n) { return fmt.Errorf("not enough bytes to read; want: %v, got: %v", size, len(bz)-n) } else if size < uint64(len(bz)-n) { return fmt.Errorf("too many bytes to read; want: %v, got: %v", size, len(bz)-n) } bz = bz[n:] return pc.UnmarshalBinaryBare(bz, ptr) } // MustUnmarshalBinaryLengthPrefixed implements BinaryMarshaler.MustUnmarshalBinaryLengthPrefixed method. func (pc *ProtoCodec) MustUnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshaler) { if err := pc.UnmarshalBinaryLengthPrefixed(bz, ptr); err != nil { panic(err) } } // MarshalJSON implements JSONMarshaler.MarshalJSON method, // it marshals to JSON using proto codec. func (pc *ProtoCodec) MarshalJSON(o proto.Message) ([]byte, error) { m, ok := o.(ProtoMarshaler) if !ok { return nil, fmt.Errorf("cannot protobuf JSON encode unsupported type: %T", o) } return ProtoMarshalJSON(m) } // MustMarshalJSON implements JSONMarshaler.MustMarshalJSON method, // it executes MarshalJSON except it panics upon failure. func (pc *ProtoCodec) MustMarshalJSON(o proto.Message) []byte { bz, err := pc.MarshalJSON(o) if err != nil { panic(err) } return bz } // UnmarshalJSON implements JSONMarshaler.UnmarshalJSON method, // it unmarshals from JSON using proto codec. func (pc *ProtoCodec) UnmarshalJSON(bz []byte, ptr proto.Message) error { m, ok := ptr.(ProtoMarshaler) if !ok { return fmt.Errorf("cannot protobuf JSON decode unsupported type: %T", ptr) } err := jsonpb.Unmarshal(strings.NewReader(string(bz)), m) if err != nil { return err } return types.UnpackInterfaces(ptr, pc.anyUnpacker) } // MustUnmarshalJSON implements JSONMarshaler.MustUnmarshalJSON method, // it executes UnmarshalJSON except it panics upon failure. func (pc *ProtoCodec) MustUnmarshalJSON(bz []byte, ptr proto.Message) { if err := pc.UnmarshalJSON(bz, ptr); err != nil { panic(err) } } // UnpackAny implements AnyUnpacker.UnpackAny method, // it unpacks the value in any to the interface pointer passed in as // iface. func (pc *ProtoCodec) UnpackAny(any *types.Any, iface interface{}) error { return pc.anyUnpacker.UnpackAny(any, iface) }