github.com/cloudwego/frugal@v0.1.15/internal/binary/encoder/encoder.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 encoder 18 19 import ( 20 `fmt` 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 `github.com/cloudwego/frugal/iov` 28 ) 29 30 type Encoder func ( 31 buf unsafe.Pointer, 32 len int, 33 mem iov.BufferWriter, 34 p unsafe.Pointer, 35 rs *RuntimeState, 36 st int, 37 ) (int, error) 38 39 var ( 40 HitCount uint64 = 0 41 MissCount uint64 = 0 42 TypeCount uint64 = 0 43 ) 44 45 var ( 46 programCache = utils.CreateProgramCache() 47 ) 48 49 func encode(vt *rt.GoType, buf unsafe.Pointer, len int, mem iov.BufferWriter, p unsafe.Pointer, rs *RuntimeState, st int) (int, error) { 50 if enc, err := resolve(vt); err != nil { 51 return -1, err 52 } else { 53 return enc(buf, len, mem, p, rs, st) 54 } 55 } 56 57 func resolve(vt *rt.GoType) (Encoder, error) { 58 var err error 59 var val interface{} 60 61 /* fast-path: type is cached */ 62 if val = programCache.Get(vt); val != nil { 63 atomic.AddUint64(&HitCount, 1) 64 return val.(Encoder), nil 65 } 66 67 /* record the cache miss, and compile the type */ 68 atomic.AddUint64(&MissCount, 1) 69 val, err = programCache.Compute(vt, compile) 70 71 /* check for errors */ 72 if err != nil { 73 return nil, err 74 } 75 76 /* record the successful compilation */ 77 atomic.AddUint64(&TypeCount, 1) 78 return val.(Encoder), nil 79 } 80 81 func compile(vt *rt.GoType) (interface{}, error) { 82 if pp, err := CreateCompiler().CompileAndFree(vt.Pack()); err != nil { 83 return nil, err 84 } else { 85 return Link(Translate(pp)), nil 86 } 87 } 88 89 func mkcompile(opts opts.Options) func(*rt.GoType) (interface{}, error) { 90 return func(vt *rt.GoType) (interface{}, error) { 91 if pp, err := CreateCompiler().Apply(opts).CompileAndFree(vt.Pack()); err != nil { 92 return nil, err 93 } else { 94 return Link(Translate(pp)), nil 95 } 96 } 97 } 98 99 func Pretouch(vt *rt.GoType, opts opts.Options) error { 100 if programCache.Get(vt) != nil { 101 return nil 102 } else if _, err := programCache.Compute(vt, mkcompile(opts)); err != nil { 103 return err 104 } else { 105 atomic.AddUint64(&TypeCount, 1) 106 return nil 107 } 108 } 109 110 func EncodedSize(val interface{}) int { 111 if ret, err := EncodeObject(nil, nil, val); err != nil { 112 panic(fmt.Errorf("frugal: cannot measure encoded size: %w", err)) 113 } else { 114 return ret 115 } 116 } 117 118 func EncodeObject(buf []byte, mem iov.BufferWriter, val interface{}) (ret int, err error) { 119 rst := newRuntimeState() 120 efv := rt.UnpackEface(val) 121 out := (*rt.GoSlice)(unsafe.Pointer(&buf)) 122 123 /* check for indirect types */ 124 if efv.Type.IsIndirect() { 125 ret, err = encode(efv.Type, out.Ptr, out.Len, mem, efv.Value, rst, 0) 126 } else { 127 /* avoid an extra mallocgc which is expensive for small objects */ 128 rst.Val = efv.Value 129 ret, err = encode(efv.Type, out.Ptr, out.Len, mem, unsafe.Pointer(&rst.Val), rst, 0) 130 /* remove reference to avoid leak since rst will be reused */ 131 rst.Val = nil 132 } 133 134 /* return the state into pool */ 135 freeRuntimeState(rst) 136 return 137 }