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  }