github.com/cloudwego/frugal@v0.1.15/internal/binary/decoder/decoder.go (about) 1 /* 2 * Copyright 2022 ByteDance Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package decoder 18 19 import ( 20 `reflect` 21 `sync/atomic` 22 `unsafe` 23 24 `github.com/cloudwego/frugal/internal/opts` 25 `github.com/cloudwego/frugal/internal/rt` 26 `github.com/cloudwego/frugal/internal/utils` 27 ) 28 29 type Decoder func ( 30 buf unsafe.Pointer, 31 nb int, 32 i int, 33 p unsafe.Pointer, 34 rs *RuntimeState, 35 st int, 36 ) (int, error) 37 38 var ( 39 HitCount uint64 = 0 40 MissCount uint64 = 0 41 TypeCount uint64 = 0 42 ) 43 44 var ( 45 programCache = utils.CreateProgramCache() 46 ) 47 48 func decode(vt *rt.GoType, buf unsafe.Pointer, nb int, i int, p unsafe.Pointer, rs *RuntimeState, st int) (int, error) { 49 if dec, err := resolve(vt); err != nil { 50 return 0, err 51 } else { 52 return dec(buf, nb, i, p, rs, st) 53 } 54 } 55 56 func resolve(vt *rt.GoType) (Decoder, error) { 57 var err error 58 var val interface{} 59 60 /* fast-path: type is cached */ 61 if val = programCache.Get(vt); val != nil { 62 atomic.AddUint64(&HitCount, 1) 63 return val.(Decoder), nil 64 } 65 66 /* record the cache miss, and compile the type */ 67 atomic.AddUint64(&MissCount, 1) 68 val, err = programCache.Compute(vt, compile) 69 70 /* check for errors */ 71 if err != nil { 72 return nil, err 73 } 74 75 /* record the successful compilation */ 76 atomic.AddUint64(&TypeCount, 1) 77 return val.(Decoder), nil 78 } 79 80 func compile(vt *rt.GoType) (interface{}, error) { 81 if pp, err := CreateCompiler().CompileAndFree(vt.Pack()); err != nil { 82 return nil, err 83 } else { 84 return Link(Translate(pp)), nil 85 } 86 } 87 88 func mkcompile(ty map[reflect.Type]struct{}, opts opts.Options) func(*rt.GoType) (interface{}, error) { 89 return func(vt *rt.GoType) (interface{}, error) { 90 cc := CreateCompiler() 91 pp, err := cc.Apply(opts).Compile(vt.Pack()) 92 93 /* add all the deferred types */ 94 for t := range cc.d { 95 ty[t] = struct{}{} 96 } 97 98 /* translate and link the program */ 99 if err != nil { 100 return nil, err 101 } else { 102 return Link(Translate(pp)), nil 103 } 104 } 105 } 106 107 type DecodeError struct { 108 vt *rt.GoType 109 } 110 111 func (self DecodeError) Error() string { 112 if self.vt == nil { 113 return "frugal: unmarshal to nil interface" 114 } else if self.vt.Kind() == reflect.Ptr { 115 return "frugal: unmarshal to nil " + self.vt.String() 116 } else { 117 return "frugal: unmarshal to non-pointer " + self.vt.String() 118 } 119 } 120 121 func Pretouch(vt *rt.GoType, opts opts.Options) (map[reflect.Type]struct{}, error) { 122 var err error 123 var ret map[reflect.Type]struct{} 124 125 /* check for cached types */ 126 if programCache.Get(vt) != nil { 127 return nil, nil 128 } 129 130 /* compile & load the type */ 131 ret = make(map[reflect.Type]struct{}) 132 _, err = programCache.Compute(vt, mkcompile(ret, opts)) 133 134 /* check for errors */ 135 if err != nil { 136 return nil, err 137 } 138 139 /* add the type count */ 140 atomic.AddUint64(&TypeCount, 1) 141 return ret, nil 142 } 143 144 func DecodeObject(buf []byte, val interface{}) (ret int, err error) { 145 vv := rt.UnpackEface(val) 146 vt := vv.Type 147 148 /* check for nil interface */ 149 if vt == nil || vv.Value == nil || vt.Kind() != reflect.Ptr { 150 return 0, DecodeError { vt } 151 } 152 153 /* create a new runtime state */ 154 et := rt.PtrElem(vt) 155 st := newRuntimeState() 156 sl := (*rt.GoSlice)(unsafe.Pointer(&buf)) 157 158 /* call the encoder, and return the runtime state into pool */ 159 ret, err = decode(et, sl.Ptr, sl.Len, 0, vv.Value, st, 0) 160 freeRuntimeState(st) 161 return 162 }