github.com/cloudwego/frugal@v0.1.15/native/skipping.c (about) 1 #include <stdint.h> 2 3 #define ETAG -1 4 #define EEOF -2 5 #define ESTACK -3 6 #define MAX_STACK 1024 7 8 #define T_bool 2 9 #define T_i8 3 10 #define T_double 4 11 #define T_i16 6 12 #define T_i32 8 13 #define T_i64 10 14 #define T_string 11 15 #define T_struct 12 16 #define T_map 13 17 #define T_set 14 18 #define T_list 15 19 #define T_list_elem 0xfe 20 #define T_map_pair 0xff 21 22 typedef struct { 23 uint8_t t; 24 uint8_t k; 25 uint8_t v; 26 uint32_t n; 27 } skipbuf_t; 28 29 static const char WireTags[256] = { 30 [T_bool ] = 1, 31 [T_i8 ] = 1, 32 [T_double] = 1, 33 [T_i16 ] = 1, 34 [T_i32 ] = 1, 35 [T_i64 ] = 1, 36 [T_string] = 1, 37 [T_struct] = 1, 38 [T_map ] = 1, 39 [T_set ] = 1, 40 [T_list ] = 1, 41 }; 42 43 static const int8_t SkipSizeFixed[256] = { 44 [T_bool ] = 1, 45 [T_i8 ] = 1, 46 [T_double] = 8, 47 [T_i16 ] = 2, 48 [T_i32 ] = 4, 49 [T_i64 ] = 8, 50 }; 51 52 static inline int64_t u32be(const char *s) { 53 return __builtin_bswap32(*(const uint32_t *)s); 54 } 55 56 static inline char stpop(skipbuf_t *s, int64_t *p) { 57 if (s[*p].n == 0) { 58 (*p)--; 59 return 1; 60 } else { 61 s[*p].n--; 62 return 0; 63 } 64 } 65 66 static inline char stadd(skipbuf_t *s, int64_t *p, uint8_t t) { 67 if (++*p >= MAX_STACK) { 68 return 0; 69 } else { 70 s[*p].t = t; 71 s[*p].n = 0; 72 return 1; 73 } 74 } 75 76 static inline void mvbuf(const char **s, int64_t *n, int64_t *r, int64_t nb) { 77 *n -= nb; 78 *r += nb; 79 *s += nb; 80 } 81 82 int64_t do_skip(skipbuf_t *st, const char *s, int64_t n, uint8_t t) { 83 int64_t nb; 84 int64_t rv = 0; 85 int64_t sp = 0; 86 87 /* initialize the stack */ 88 st->n = 0; 89 st->t = t; 90 91 /* run until drain */ 92 while (sp >= 0) { 93 switch (st[sp].t) { 94 default: { 95 return ETAG; 96 } 97 98 /* simple fixed types */ 99 case T_bool : 100 case T_i8 : 101 case T_double : 102 case T_i16 : 103 case T_i32 : 104 case T_i64 : { 105 if ((nb = SkipSizeFixed[st[sp].t]) > n) { 106 return EEOF; 107 } else { 108 stpop(st, &sp); 109 mvbuf(&s, &n, &rv, nb); 110 break; 111 } 112 } 113 114 /* strings & binaries */ 115 case T_string: { 116 if (n < 4) { 117 return EEOF; 118 } else if ((nb = u32be(s) + 4) > n) { 119 return EEOF; 120 } else { 121 stpop(st, &sp); 122 mvbuf(&s, &n, &rv, nb); 123 break; 124 } 125 } 126 127 /* structs */ 128 case T_struct: { 129 int64_t nf; 130 uint8_t vt; 131 132 /* must have at least 1 byte */ 133 if (n < 1) { 134 return EEOF; 135 } 136 137 /* check for end of tag */ 138 if ((vt = *s) == 0) { 139 stpop(st, &sp); 140 mvbuf(&s, &n, &rv, 1); 141 continue; 142 } 143 144 /* check for tag value */ 145 if (!(WireTags[vt])) { 146 return ETAG; 147 } 148 149 /* fast-path for primitive fields */ 150 if ((nf = SkipSizeFixed[vt]) != 0) { 151 if (n < nf + 3) { 152 return EEOF; 153 } else { 154 mvbuf(&s, &n, &rv, nf + 3); 155 continue; 156 } 157 } 158 159 /* must have more than 3 bytes (fields cannot have a size of zero), 160 * also skip the field ID because we don't care */ 161 if (n <= 3) { 162 return EEOF; 163 } else if (!stadd(st, &sp, vt)) { 164 return ESTACK; 165 } else { 166 mvbuf(&s, &n, &rv, 3); 167 break; 168 } 169 } 170 171 /* maps */ 172 case T_map: { 173 int64_t np; 174 uint8_t kt; 175 uint8_t vt; 176 177 /* must have at least 6 bytes */ 178 if (n < 6) { 179 return EEOF; 180 } 181 182 /* get the element type and count */ 183 kt = s[0]; 184 vt = s[1]; 185 np = u32be(s + 2); 186 187 /* check for tag value */ 188 if (!(WireTags[kt] && WireTags[vt])) { 189 return ETAG; 190 } 191 192 /* empty map */ 193 if (np == 0) { 194 stpop(st, &sp); 195 mvbuf(&s, &n, &rv, 6); 196 continue; 197 } 198 199 /* check for fixed key and value */ 200 int64_t nk = SkipSizeFixed[kt]; 201 int64_t nv = SkipSizeFixed[vt]; 202 203 /* fast path for fixed key and value */ 204 if (nk != 0 && nv != 0) { 205 if ((nb = np * (nk + nv) + 6) > n) { 206 return EEOF; 207 } else { 208 stpop(st, &sp); 209 mvbuf(&s, &n, &rv, nb); 210 continue; 211 } 212 } 213 214 /* set to parse the map pairs */ 215 st[sp].k = kt; 216 st[sp].v = vt; 217 st[sp].t = T_map_pair; 218 st[sp].n = np * 2 - 1; 219 mvbuf(&s, &n, &rv, 6); 220 break; 221 } 222 223 /* map pairs */ 224 case T_map_pair: { 225 uint8_t kt = st[sp].k; 226 uint8_t vt = st[sp].v; 227 228 /* there are keys pending */ 229 if (!stpop(st, &sp) && (st[sp].n & 1) == 0) { 230 vt = kt; 231 } 232 233 /* push the element onto stack */ 234 if (stadd(st, &sp, vt)) { 235 break; 236 } else { 237 return ESTACK; 238 } 239 } 240 241 /* sets and lists */ 242 case T_set : 243 case T_list : { 244 int64_t nv; 245 int64_t nt; 246 uint8_t et; 247 248 /* must have at least 5 bytes */ 249 if (n < 5) { 250 return EEOF; 251 } 252 253 /* get the element type and count */ 254 et = s[0]; 255 nv = u32be(s + 1); 256 257 /* check for tag value */ 258 if (!(WireTags[et])) { 259 return ETAG; 260 } 261 262 /* empty sequence */ 263 if (nv == 0) { 264 stpop(st, &sp); 265 mvbuf(&s, &n, &rv, 5); 266 continue; 267 } 268 269 /* fast path for fixed types */ 270 if ((nt = SkipSizeFixed[et]) != 0) { 271 if ((nb = nv * nt + 5) > n) { 272 return EEOF; 273 } else { 274 stpop(st, &sp); 275 mvbuf(&s, &n, &rv, nb); 276 continue; 277 } 278 } 279 280 /* set to parse the elements */ 281 st[sp].t = T_list_elem; 282 st[sp].v = et; 283 st[sp].n = nv - 1; 284 mvbuf(&s, &n, &rv, 5); 285 break; 286 } 287 288 /* list elements */ 289 case T_list_elem: { 290 uint8_t et = st[sp].v; 291 stpop(st, &sp); 292 293 /* push the element onto stack */ 294 if (stadd(st, &sp, et)) { 295 break; 296 } else { 297 return ESTACK; 298 } 299 } 300 } 301 } 302 303 /* all done */ 304 return rv; 305 }