github.com/binbinly/pkg@v0.0.11-0.20240321014439-f4fbf666eb0f/codec/codec.go (about) 1 package codec 2 3 import ( 4 "encoding" 5 "errors" 6 "reflect" 7 "strings" 8 ) 9 10 var ( 11 // ErrNotAPointer . 12 ErrNotAPointer = errors.New("v argument must be a pointer") 13 ) 14 15 // Codec defines the interface gRPC uses to encode and decode messages. Note 16 // that implementations of this interface must be thread safe; a Codec's 17 // methods can be called from concurrent goroutines. 18 type Codec interface { 19 // Marshal returns the wire format of v. 20 Marshal(v any) ([]byte, error) 21 // Unmarshal parses the wire format into v. 22 Unmarshal(data []byte, v any) error 23 // Name returns the name of the Codec implementation. The returned string 24 // will be used as part of content type in transmission. The result must be 25 // static; the result cannot change between calls. 26 Name() string 27 } 28 29 var registeredCodecs = make(map[string]Codec) 30 31 // RegisterCodec registers the provided Codec for use with all transport clients and 32 // servers. 33 // 34 // The Codec will be stored and looked up by result of its Name() method, which 35 // should match the content-subtype of the encoding handled by the Codec. This 36 // is case-insensitive, and is stored and looked up as lowercase. If the 37 // result of calling Name() is an empty string, RegisterCodec will panic. See 38 // Content-Type on 39 // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for 40 // more details. 41 // 42 // NOTE: this function must only be called during initialization time (i.e. in 43 // an init() function), and is not thread-safe. If multiple Compressors are 44 // registered with the same name, the one registered last will take effect. 45 func RegisterCodec(codec Codec) { 46 if codec == nil { 47 panic("cannot register a nil Codec") 48 } 49 if codec.Name() == "" { 50 panic("cannot register Codec with empty string result for Name()") 51 } 52 contentSubtype := strings.ToLower(codec.Name()) 53 registeredCodecs[contentSubtype] = codec 54 } 55 56 // GetCodec gets a registered Codec by content-subtype, or nil if no Codec is 57 // registered for the content-subtype. 58 // 59 // The content-subtype is expected to be lowercase. 60 func GetCodec(contentSubtype string) Codec { 61 return registeredCodecs[contentSubtype] 62 } 63 64 // Encoding 编码接口定义 65 type Encoding interface { 66 Marshal(v any) ([]byte, error) 67 Unmarshal(data []byte, v any) error 68 } 69 70 // Marshal encode data 71 func Marshal(e Encoding, v any) (data []byte, err error) { 72 if !isPointer(v) { 73 return data, ErrNotAPointer 74 } 75 bm, ok := v.(encoding.BinaryMarshaler) 76 if ok && e == nil { 77 data, err = bm.MarshalBinary() 78 return 79 } 80 81 data, err = e.Marshal(v) 82 if err == nil { 83 return 84 } 85 if ok { 86 data, err = bm.MarshalBinary() 87 } 88 89 return 90 } 91 92 // Unmarshal decode data 93 func Unmarshal(e Encoding, data []byte, v any) (err error) { 94 if !isPointer(v) { 95 return ErrNotAPointer 96 } 97 bm, ok := v.(encoding.BinaryUnmarshaler) 98 if ok && e == nil { 99 err = bm.UnmarshalBinary(data) 100 return err 101 } 102 err = e.Unmarshal(data, v) 103 if err == nil { 104 return 105 } 106 if ok { 107 return bm.UnmarshalBinary(data) 108 } 109 return 110 } 111 112 func isPointer(data any) bool { 113 switch reflect.ValueOf(data).Kind() { 114 case reflect.Ptr, reflect.Interface: 115 return true 116 default: 117 return false 118 } 119 }