github.com/cloudwego/frugal@v0.1.15/internal/binary/decoder/skipping_emu.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 `math/bits` 21 `unsafe` 22 23 `github.com/cloudwego/frugal/internal/atm/hir` 24 `github.com/cloudwego/frugal/internal/binary/defs` 25 ) 26 27 type ( 28 _skipbuf_t [defs.StackSize]SkipItem 29 ) 30 31 var _SkipSizeFixed = [256]int { 32 defs.T_bool : 1, 33 defs.T_i8 : 1, 34 defs.T_double : 8, 35 defs.T_i16 : 2, 36 defs.T_i32 : 4, 37 defs.T_i64 : 8, 38 } 39 40 const ( 41 _T_list_elem defs.Tag = 0xfe 42 _T_map_pair defs.Tag = 0xff 43 ) 44 45 func u32be(s unsafe.Pointer) int { 46 return int(bits.ReverseBytes32(*(*uint32)(s))) 47 } 48 49 func stpop(s *_skipbuf_t, p *int) bool { 50 if s[*p].N == 0 { 51 *p-- 52 return true 53 } else { 54 s[*p].N-- 55 return false 56 } 57 } 58 59 func stadd(s *_skipbuf_t, p *int, t defs.Tag) bool { 60 if *p++; *p >= defs.StackSize { 61 return false 62 } else { 63 s[*p].T, s[*p].N = t, 0 64 return true 65 } 66 } 67 68 func mvbuf(s *unsafe.Pointer, n *int, r *int, nb int) { 69 *n = *n - nb 70 *r = *r + nb 71 *s = unsafe.Pointer(uintptr(*s) + uintptr(nb)) 72 } 73 74 func do_skip(st *_skipbuf_t, s unsafe.Pointer, n int, t defs.Tag) (rv int) { 75 sp := 0 76 st[0].T = t 77 78 /* run until drain */ 79 for sp >= 0 { 80 switch st[sp].T { 81 default: { 82 return ETAG 83 } 84 85 /* simple fixed types */ 86 case defs.T_bool : fallthrough 87 case defs.T_i8 : fallthrough 88 case defs.T_double : fallthrough 89 case defs.T_i16 : fallthrough 90 case defs.T_i32 : fallthrough 91 case defs.T_i64 : { 92 if nb := _SkipSizeFixed[st[sp].T]; n < nb { 93 return EEOF 94 } else { 95 stpop(st, &sp) 96 mvbuf(&s, &n, &rv, nb) 97 } 98 } 99 100 /* strings & binaries */ 101 case defs.T_string: { 102 if n < 4 { 103 return EEOF 104 } else if nb := u32be(s) + 4; n < nb { 105 return EEOF 106 } else { 107 stpop(st, &sp) 108 mvbuf(&s, &n, &rv, nb) 109 } 110 } 111 112 /* structs */ 113 case defs.T_struct: { 114 var nb int 115 var vt defs.Tag 116 117 /* must have at least 1 byte */ 118 if n < 1 { 119 return EEOF 120 } 121 122 /* check for end of tag */ 123 if vt = *(*defs.Tag)(s); vt == 0 { 124 stpop(st, &sp) 125 mvbuf(&s, &n, &rv, 1) 126 continue 127 } 128 129 /* check for tag value */ 130 if !vt.IsWireTag() { 131 return ETAG 132 } 133 134 /* fast-path for primitive fields */ 135 if nb = _SkipSizeFixed[vt]; nb != 0 { 136 if n < nb + 3 { 137 return EEOF 138 } else { 139 mvbuf(&s, &n, &rv, nb + 3) 140 continue 141 } 142 } 143 144 /* must have more than 3 bytes (fields cannot have a size of zero), also skip the field ID cause we don't care */ 145 if n <= 3 { 146 return EEOF 147 } else if !stadd(st, &sp, vt) { 148 return ESTACK 149 } else { 150 mvbuf(&s, &n, &rv, 3) 151 } 152 } 153 154 /* maps */ 155 case defs.T_map: { 156 var np int 157 var kt defs.Tag 158 var vt defs.Tag 159 160 /* must have at least 6 bytes */ 161 if n < 6 { 162 return EEOF 163 } 164 165 /* get the element type and count */ 166 kt = (*[2]defs.Tag)(s)[0] 167 vt = (*[2]defs.Tag)(s)[1] 168 np = u32be(unsafe.Pointer(uintptr(s) + 2)) 169 170 /* check for tag value */ 171 if !kt.IsWireTag() || !vt.IsWireTag() { 172 return ETAG 173 } 174 175 /* empty map */ 176 if np == 0 { 177 stpop(st, &sp) 178 mvbuf(&s, &n, &rv, 6) 179 continue 180 } 181 182 /* fast path for fixed key and value */ 183 if nk, nv := _SkipSizeFixed[kt], _SkipSizeFixed[vt]; nk != 0 && nv != 0 { 184 if nb := np * (nk + nv) + 6; n < nb { 185 return EEOF 186 } else { 187 stpop(st, &sp) 188 mvbuf(&s, &n, &rv, nb) 189 continue 190 } 191 } 192 193 /* set to parse the map pairs */ 194 st[sp].K = kt 195 st[sp].V = vt 196 st[sp].T = _T_map_pair 197 st[sp].N = uint32(np) * 2 - 1 198 mvbuf(&s, &n, &rv, 6) 199 } 200 201 /* map pairs */ 202 case _T_map_pair: { 203 if vt := st[sp].V; stpop(st, &sp) || st[sp].N & 1 != 0 { 204 if !stadd(st, &sp, vt) { 205 return ESTACK 206 } 207 } else { 208 if !stadd(st, &sp, st[sp].K) { 209 return ESTACK 210 } 211 } 212 } 213 214 /* sets and lists */ 215 case defs.T_set : fallthrough 216 case defs.T_list : { 217 var nv int 218 var et defs.Tag 219 220 /* must have at least 5 bytes */ 221 if n < 5 { 222 return EEOF 223 } 224 225 /* get the element type and count */ 226 et = *(*defs.Tag)(s) 227 nv = u32be(unsafe.Pointer(uintptr(s) + 1)) 228 229 /* check for tag value */ 230 if !et.IsWireTag() { 231 return ETAG 232 } 233 234 /* empty sequence */ 235 if nv == 0 { 236 stpop(st, &sp) 237 mvbuf(&s, &n, &rv, 5) 238 continue 239 } 240 241 /* fast path for fixed types */ 242 if nt := _SkipSizeFixed[et]; nt != 0 { 243 if nb := nv * nt + 5; n < nb { 244 return EEOF 245 } else { 246 stpop(st, &sp) 247 mvbuf(&s, &n, &rv, nb) 248 continue 249 } 250 } 251 252 /* set to parse the elements */ 253 st[sp].T = _T_list_elem 254 st[sp].V = et 255 st[sp].N = uint32(nv) - 1 256 mvbuf(&s, &n, &rv, 5) 257 } 258 259 /* list elem */ 260 case _T_list_elem: { 261 et := st[sp].V 262 stpop(st, &sp) 263 264 /* push the element onto stack */ 265 if !stadd(st, &sp, et) { 266 return ESTACK 267 } 268 } 269 } 270 } 271 272 /* all done */ 273 return 274 } 275 276 func emu_ccall_skip(ctx hir.CallContext) { 277 if !ctx.Verify("**ii", "i") { 278 panic("invalid skip call") 279 } else { 280 ctx.Ru(0, uint64(do_skip((*_skipbuf_t)(ctx.Ap(0)), ctx.Ap(1), int(ctx.Au(2)), defs.Tag(ctx.Au(3))))) 281 } 282 }