github.com/cloudwego/dynamicgo@v0.2.6-0.20240519101509-707f41b6b834/native/thrift_skip.c (about)

     1  /**
     2   * Copyright 2023 CloudWeGo Authors.
     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  #include <stdint.h>
    18  #include "test/xprintf.h"
    19  
    20  #define ETAG -1
    21  #define EEOF -2
    22  #define ESTACK -3
    23  #define MAX_STACK 1024
    24  
    25  #define T_bool 2
    26  #define T_i8 3
    27  #define T_double 4
    28  #define T_i16 6
    29  #define T_i32 8
    30  #define T_i64 10
    31  #define T_string 11
    32  #define T_struct 12
    33  #define T_map 13
    34  #define T_set 14
    35  #define T_list 15
    36  #define T_list_elem 0xfe
    37  #define T_map_pair 0xff
    38  
    39  typedef struct
    40  {
    41      uint8_t t;
    42      uint8_t k;
    43      uint8_t v;
    44      uint32_t n;
    45  } skipbuf_t;
    46  
    47  static const char WireTags[256] = {
    48      [T_bool] = 1,
    49      [T_i8] = 1,
    50      [T_double] = 1,
    51      [T_i16] = 1,
    52      [T_i32] = 1,
    53      [T_i64] = 1,
    54      [T_string] = 1,
    55      [T_struct] = 1,
    56      [T_map] = 1,
    57      [T_set] = 1,
    58      [T_list] = 1,
    59  };
    60  
    61  static const int8_t SkipSizeFixed[256] = {
    62      [T_bool] = 1,
    63      [T_i8] = 1,
    64      [T_double] = 8,
    65      [T_i16] = 2,
    66      [T_i32] = 4,
    67      [T_i64] = 8,
    68  };
    69  
    70  static inline int64_t u32be(const char *s)
    71  {
    72      return __builtin_bswap32(*(const uint32_t *)s);
    73  }
    74  
    75  static inline int64_t u16be(const char *s)
    76  {
    77      return __builtin_bswap16(*(const uint16_t *)s);
    78  }
    79  
    80  static inline char stpop(skipbuf_t *s, int64_t *p)
    81  {
    82      if (s[*p].n == 0)
    83      {
    84          (*p)--;
    85          return 1;
    86      }
    87      else
    88      {
    89          s[*p].n--;
    90          return 0;
    91      }
    92  }
    93  
    94  static inline char stadd(skipbuf_t *s, int64_t *p, uint8_t t)
    95  {
    96      if (++*p >= MAX_STACK)
    97      {
    98          return 0;
    99      }
   100      else
   101      {
   102          s[*p].t = t;
   103          s[*p].n = 0;
   104          return 1;
   105      }
   106  }
   107  
   108  static inline void mvbuf(const char **s, int64_t *n, int64_t *r, int64_t nb)
   109  {
   110      *n -= nb;
   111      *r += nb;
   112      *s += nb;
   113  }
   114  
   115  int64_t tb_skip(skipbuf_t *st, const char *s, int64_t n, uint8_t t)
   116  {
   117      int64_t nb;
   118      int64_t rv = 0;
   119      int64_t sp = 0;
   120  
   121      /* initialize the stack */
   122      st->n = 0;
   123      st->t = t;
   124  
   125      /* run until drain */
   126      while (sp >= 0)
   127      {
   128          xprintf("[T_%d] sp:%d, rv:%d, st:{t:%d, k:%d, e:%d, n:%d}\n", st[sp].t, sp, rv, st[sp].t, st[sp].k, st[sp].v, st[sp].n);
   129          // xprintf("[T_%d] sp:%d, rv:%d\n", st[sp].t, sp, rv);
   130          switch (st[sp].t)
   131          {
   132          default:
   133          {
   134              return ETAG;
   135          }
   136  
   137          /* simple fixed types */
   138          case T_bool:
   139          case T_i8:
   140          case T_double:
   141          case T_i16:
   142          case T_i32:
   143          case T_i64:
   144          {
   145              if ((nb = SkipSizeFixed[st[sp].t]) > n)
   146              {
   147                  return EEOF;
   148              }
   149              else
   150              {
   151                  stpop(st, &sp);
   152                  mvbuf(&s, &n, &rv, nb);
   153                  break;
   154              }
   155          }
   156  
   157          /* strings & binaries */
   158          case T_string:
   159          {
   160              if (n < 4)
   161              {
   162                  return EEOF;
   163              }
   164              else if ((nb = u32be(s) + 4) > n)
   165              {
   166                  return EEOF;
   167              }
   168              else
   169              {
   170                  stpop(st, &sp);
   171                  mvbuf(&s, &n, &rv, nb);
   172                  break;
   173              }
   174          }
   175  
   176          /* structs */
   177          case T_struct:
   178          {
   179              int64_t nf;
   180              uint8_t vt;
   181  
   182              /* must have at least 1 byte */
   183              if (n < 1)
   184              {
   185                  return EEOF;
   186              }
   187  
   188              /* check for end of tag */
   189              if ((vt = *s) == 0)
   190              {
   191                  stpop(st, &sp);
   192                  mvbuf(&s, &n, &rv, 1);
   193                  continue;
   194              }
   195  
   196              xprintf("[T_struct] rv:%d, ft:%d, fid:%d\n", rv + 3, vt, u16be(s + 1));
   197  
   198              /* check for tag value */
   199              if (!(WireTags[vt]))
   200              {
   201                  return ETAG;
   202              }
   203  
   204              /* fast-path for primitive fields */
   205              if ((nf = SkipSizeFixed[vt]) != 0)
   206              {
   207                  if (n < nf + 3)
   208                  {
   209                      return EEOF;
   210                  }
   211                  else
   212                  {
   213                      mvbuf(&s, &n, &rv, nf + 3);
   214                      continue;
   215                  }
   216              }
   217  
   218              /* must have more than 3 bytes (fields cannot have a size of zero), also skip the field ID cause we don't care */
   219              if (n <= 3)
   220              {
   221                  return EEOF;
   222              }
   223              else if (!stadd(st, &sp, vt))
   224              {
   225                  return ESTACK;
   226              }
   227              else
   228              {
   229                  mvbuf(&s, &n, &rv, 3);
   230                  break;
   231              }
   232          }
   233  
   234          /* maps */
   235          case T_map:
   236          {
   237              int64_t np;
   238              uint8_t kt;
   239              uint8_t vt;
   240  
   241              /* must have at least 6 bytes */
   242              if (n < 6)
   243              {
   244                  return EEOF;
   245              }
   246  
   247              /* get the element type and count */
   248              kt = s[0];
   249              vt = s[1];
   250              np = u32be(s + 2);
   251              xprintf("[T_map] header rv:%d, kt:%d, vt:%d, np:%d\n", rv + 6, kt, vt, np);
   252  
   253              /* check for tag value */
   254              if (!(WireTags[kt] && WireTags[vt]))
   255              {
   256                  return ETAG;
   257              }
   258  
   259              /* empty map */
   260              if (np == 0)
   261              {
   262                  stpop(st, &sp);
   263                  mvbuf(&s, &n, &rv, 6);
   264                  continue;
   265              }
   266  
   267              /* check for fixed key and value */
   268              int64_t nk = SkipSizeFixed[kt];
   269              int64_t nv = SkipSizeFixed[vt];
   270  
   271              /* fast path for fixed key and value */
   272              if (nk != 0 && nv != 0)
   273              {
   274                  if ((nb = np * (nk + nv) + 6) > n)
   275                  {
   276                      return EEOF;
   277                  }
   278                  else
   279                  {
   280                      stpop(st, &sp);
   281                      mvbuf(&s, &n, &rv, nb);
   282                      continue;
   283                  }
   284              }
   285  
   286              /* set to parse the map pairs */
   287              st[sp].k = kt;
   288              st[sp].v = vt;
   289              st[sp].t = T_map_pair;
   290              st[sp].n = np * 2 - 1;
   291              mvbuf(&s, &n, &rv, 6);
   292              break;
   293          }
   294  
   295          /* map pairs */
   296          case T_map_pair:
   297          {
   298              uint8_t kt = st[sp].k;
   299              uint8_t vt = st[sp].v;
   300  
   301              /* there are keys pending */
   302              if (!stpop(st, &sp))
   303              {
   304                  if ((st[sp].n & 1) == 0)
   305                  {
   306                      xprintf("[T_map] key rv:%d, i:%d\n", rv, st[sp].n / 2);
   307                      vt = kt;
   308                  }
   309                  else
   310                  {
   311                      xprintf("[T_map] elem rv:%d, i:%d\n", rv, st[sp].n / 2);
   312                  }
   313              }
   314              else
   315              {
   316                  xprintf("[T_map] elem rv:%d, i:%d\n", rv, st[sp].n / 2);
   317              }
   318              /* push the element onto stack */
   319              if (stadd(st, &sp, vt))
   320              {
   321                  break;
   322              }
   323              else
   324              {
   325                  return ESTACK;
   326              }
   327          }
   328  
   329          /* sets and lists */
   330          case T_set:
   331          case T_list:
   332          {
   333              int64_t nv;
   334              int64_t nt;
   335              uint8_t et;
   336  
   337              /* must have at least 5 bytes */
   338              if (n < 5)
   339              {
   340                  return EEOF;
   341              }
   342  
   343              /* get the element type and count */
   344              et = s[0];
   345              nv = u32be(s + 1);
   346              xprintf("[T_list] header rv:%d, et:%d, np:%d\n", rv + 5, et, nv);
   347  
   348              /* check for tag value */
   349              if (!(WireTags[et]))
   350              {
   351                  return ETAG;
   352              }
   353  
   354              /* empty sequence */
   355              if (nv == 0)
   356              {
   357                  stpop(st, &sp);
   358                  mvbuf(&s, &n, &rv, 5);
   359                  continue;
   360              }
   361  
   362              /* fast path for fixed types */
   363              if ((nt = SkipSizeFixed[et]) != 0)
   364              {
   365                  if ((nb = nv * nt + 5) > n)
   366                  {
   367                      return EEOF;
   368                  }
   369                  else
   370                  {
   371                      xprintf("[T_list] elem rv:%d, i:%d\n", rv + 5, st[sp].n);
   372                      stpop(st, &sp);
   373                      mvbuf(&s, &n, &rv, nb);
   374                      continue;
   375                  }
   376              }
   377  
   378              /* set to parse the elements */
   379              st[sp].t = T_list_elem;
   380              st[sp].v = et;
   381              st[sp].n = nv - 1;
   382              mvbuf(&s, &n, &rv, 5);
   383              break;
   384          }
   385  
   386          /* list elem */
   387          case T_list_elem:
   388          {
   389              uint8_t et = st[sp].v;
   390              xprintf("[T_list] elem rv:%d, i:%d\n", rv, st[sp].n);
   391              stpop(st, &sp);
   392              /* push the element onto stack */
   393              if (stadd(st, &sp, et))
   394              {
   395                  break;
   396              }
   397              else
   398              {
   399                  return ESTACK;
   400              }
   401          }
   402          }
   403      }
   404  
   405      /* all done */
   406      return rv;
   407  }