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  }