mirror of https://github.com/zhufuyi/sponge
120 lines
3.1 KiB
Go
120 lines
3.1 KiB
Go
package encoding
|
|
|
|
import (
|
|
"encoding"
|
|
"errors"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
// ErrNotAPointer .
|
|
ErrNotAPointer = errors.New("v argument must be a pointer")
|
|
)
|
|
|
|
// Codec defines the interface gRPC uses to encode and decode messages. Note
|
|
// that implementations of this interface must be thread safe; a Codec's
|
|
// methods can be called from concurrent goroutines.
|
|
type Codec interface {
|
|
// Marshal returns the wire format of v.
|
|
Marshal(v interface{}) ([]byte, error)
|
|
// Unmarshal parses the wire format into v.
|
|
Unmarshal(data []byte, v interface{}) error
|
|
// Name returns the name of the Codec implementation. The returned string
|
|
// will be used as part of content type in transmission. The result must be
|
|
// static; the result cannot change between calls.
|
|
Name() string
|
|
}
|
|
|
|
var registeredCodecs = make(map[string]Codec)
|
|
|
|
// RegisterCodec registers the provided Codec for use with all transport clients and
|
|
// servers.
|
|
//
|
|
// The Codec will be stored and looked up by result of its Name() method, which
|
|
// should match the content-subtype of the encoding handled by the Codec. This
|
|
// is case-insensitive, and is stored and looked up as lowercase. If the
|
|
// result of calling Name() is an empty string, RegisterCodec will panic. See
|
|
// Content-Type on
|
|
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
|
|
// more details.
|
|
//
|
|
// NOTE: this function must only be called during initialization time (i.e. in
|
|
// an init() function), and is not thread-safe. If multiple Compressors are
|
|
// registered with the same name, the one registered last will take effect.
|
|
func RegisterCodec(codec Codec) {
|
|
if codec == nil {
|
|
panic("cannot register a nil Codec")
|
|
}
|
|
if codec.Name() == "" {
|
|
panic("cannot register Codec with empty string result for Name()")
|
|
}
|
|
contentSubtype := strings.ToLower(codec.Name())
|
|
registeredCodecs[contentSubtype] = codec
|
|
}
|
|
|
|
// GetCodec gets a registered Codec by content-subtype, or nil if no Codec is
|
|
// registered for the content-subtype.
|
|
//
|
|
// The content-subtype is expected to be lowercase.
|
|
func GetCodec(contentSubtype string) Codec {
|
|
return registeredCodecs[contentSubtype]
|
|
}
|
|
|
|
// Encoding 编码接口定义
|
|
type Encoding interface {
|
|
Marshal(v interface{}) ([]byte, error)
|
|
Unmarshal(data []byte, v interface{}) error
|
|
}
|
|
|
|
// Marshal encode data
|
|
func Marshal(e Encoding, v interface{}) (data []byte, err error) {
|
|
if !isPointer(v) {
|
|
return data, ErrNotAPointer
|
|
}
|
|
bm, ok := v.(encoding.BinaryMarshaler)
|
|
if ok && e == nil {
|
|
data, err = bm.MarshalBinary()
|
|
return
|
|
}
|
|
|
|
data, err = e.Marshal(v)
|
|
if err == nil {
|
|
return
|
|
}
|
|
if ok {
|
|
data, err = bm.MarshalBinary()
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Unmarshal decode data
|
|
func Unmarshal(e Encoding, data []byte, v interface{}) (err error) {
|
|
if !isPointer(v) {
|
|
return ErrNotAPointer
|
|
}
|
|
bm, ok := v.(encoding.BinaryUnmarshaler)
|
|
if ok && e == nil {
|
|
err = bm.UnmarshalBinary(data)
|
|
return err
|
|
}
|
|
err = e.Unmarshal(data, v)
|
|
if err == nil {
|
|
return
|
|
}
|
|
if ok {
|
|
return bm.UnmarshalBinary(data)
|
|
}
|
|
return
|
|
}
|
|
|
|
func isPointer(data interface{}) bool {
|
|
switch reflect.ValueOf(data).Kind() {
|
|
case reflect.Ptr, reflect.Interface:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|