github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/lib/stringlib/unpacker.go (about) 1 package stringlib 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "io" 7 "math" 8 "unsafe" 9 10 rt "github.com/arnodel/golua/runtime" 11 ) 12 13 type unpacker struct { 14 packFormatReader 15 pack []byte // The packed data 16 j int // Current index in the packed data 17 values []rt.Value // Values unpacked so far 18 intVal int64 // Last unpacked integral value (for options 'i' and 'I') 19 strVal string // Last unpacked string value 20 21 budget uint64 // Number of bytes we are allowed to write before stopping (0 means unbounded) 22 used uint64 // Number of bytes written so far 23 } 24 25 func UnpackString(format, pack string, j int, budget uint64) (vals []rt.Value, nextPos int, used uint64, err error) { 26 u := &unpacker{ 27 packFormatReader: packFormatReader{ 28 format: format, 29 byteOrder: nativeEndian, 30 maxAlignment: defaultMaxAlignement, 31 }, 32 pack: []byte(pack), 33 j: j, 34 35 budget: budget, 36 } 37 for u.hasNext() { 38 switch c := u.nextOption(); c { 39 case '<': 40 u.byteOrder = binary.LittleEndian 41 case '>': 42 u.byteOrder = binary.BigEndian 43 case '=': 44 u.byteOrder = nativeEndian 45 case '!': 46 if u.smallOptSize(defaultMaxAlignement) { 47 u.maxAlignment = u.optSize 48 } 49 case 'b': 50 var x int8 51 _ = u.align(0) && 52 u.read(1, &x) && 53 u.add(rt.IntValue(int64(x))) 54 case 'B': 55 var x uint8 56 _ = u.align(0) && 57 u.read(1, &x) && 58 u.add(rt.IntValue(int64(x))) 59 case 'h': 60 var x int16 61 _ = u.align(2) && 62 u.read(2, &x) && 63 u.add(rt.IntValue(int64(x))) 64 case 'H': 65 var x uint16 66 _ = u.align(2) && 67 u.read(2, &x) && 68 u.add(rt.IntValue(int64(x))) 69 case 'l', 'j': 70 var x int64 71 _ = u.align(8) && 72 u.read(8, &x) && 73 u.add(rt.IntValue(x)) 74 case 'L', 'J', 'T': 75 var x uint64 76 _ = u.align(8) && 77 u.read(8, &x) && 78 u.add(rt.IntValue(int64(x))) 79 case 'i': 80 _ = u.smallOptSize(8) && 81 u.align(u.optSize) && 82 u.readVarInt() && 83 u.add(rt.IntValue(u.intVal)) 84 case 'I': 85 _ = u.smallOptSize(8) && 86 u.align(u.optSize) && 87 u.readVarUint() && 88 u.add(rt.IntValue(u.intVal)) 89 case 'f': 90 var x float32 91 _ = u.align(4) && 92 u.read(4, &x) && 93 u.add(rt.FloatValue(float64(x))) 94 case 'd', 'n': 95 var x float64 96 _ = u.align(8) && 97 u.read(8, &x) && 98 u.add(rt.FloatValue(x)) 99 case 'c': 100 _ = u.align(0) && 101 u.mustGetOptSize() && 102 u.readStr(int(u.optSize)) && 103 u.add(rt.StringValue(u.strVal)) 104 case 'z': 105 if !u.align(0) { 106 u.err = errExpectedOption 107 break 108 } 109 var zi = u.j 110 for { 111 if zi >= len(u.pack) { 112 return nil, 0, u.used, errUnexpectedPackEnd 113 } 114 if u.pack[zi] == 0 { 115 break 116 } 117 zi++ 118 if !u.consumeBudget(1) { 119 return nil, 0, u.used, u.err 120 } 121 } 122 b := make([]byte, zi-u.j) 123 _ = u.read(0, b) && 124 u.add(rt.StringValue(string(b))) && 125 u.skip(1) 126 case 's': 127 _ = u.smallOptSize(8) && 128 u.align(u.optSize) && 129 u.readVarUint() && 130 u.readStr(int(u.intVal)) && 131 u.add(rt.StringValue(u.strVal)) 132 case 'x': 133 _ = u.skip(1) 134 case 'X': 135 if u.alignOnly { 136 u.err = errExpectedOption 137 } else { 138 u.alignOnly = true 139 } 140 case ' ': 141 if u.alignOnly { 142 u.err = errExpectedOption 143 } 144 default: 145 u.err = errBadFormatString(c) 146 } 147 if u.err != nil { 148 return nil, 0, u.used, u.err 149 } 150 } 151 if u.alignOnly { 152 return nil, 0, u.used, errExpectedOption 153 } 154 return u.values, u.j, u.used, nil 155 } 156 157 // Read implements io.Read 158 func (u *unpacker) Read(b []byte) (n int, err error) { 159 if u.j >= len(u.pack) { 160 return 0, io.EOF 161 } 162 n = copy(b, u.pack[u.j:]) 163 u.j += n 164 return 165 } 166 167 func (u *unpacker) align(n uint) bool { 168 if n != 0 { 169 if n > u.maxAlignment { 170 n = u.maxAlignment 171 } 172 if r := uint(u.j) % n; r != 0 { 173 if !u.skip(n - r) { 174 return false 175 } 176 } 177 } 178 if u.alignOnly { 179 u.alignOnly = false 180 return false 181 } 182 return true 183 } 184 185 func (u *unpacker) read(sz uint64, x interface{}) bool { 186 if sz > 0 && !u.consumeBudget(sz) { 187 return false 188 } 189 if err := binary.Read(u, u.byteOrder, x); err != nil { 190 if err == io.ErrUnexpectedEOF { 191 err = errUnexpectedPackEnd 192 } 193 u.err = err 194 return false 195 } 196 return true 197 } 198 199 func (u *unpacker) readStr(n int) (ok bool) { 200 if !u.consumeBudget(uint64(n)) { 201 return false 202 } 203 b := make([]byte, n) 204 ok = u.read(0, b) 205 if ok { 206 u.strVal = string(b) 207 } 208 return 209 } 210 211 func (u *unpacker) readVarUint() (ok bool) { 212 if !u.consumeBudget(8) { 213 return false 214 } 215 switch n := u.optSize; { 216 case n == 4: 217 var x uint32 218 ok = u.read(0, &x) && 219 u.setIntVal(int64(x)) 220 case n == 8: 221 var x uint64 222 ok = u.read(0, &x) && 223 u.setIntVal(int64(x)) 224 case n > 8: 225 var x uint64 226 ok = (u.byteOrder == binary.LittleEndian || u.skip0(n-8)) && 227 u.read(0, &x) && 228 u.setIntVal(int64(x)) && 229 (u.byteOrder == binary.BigEndian || u.skip0(n-8)) 230 default: 231 // n < 8 so truncated 232 var b [8]byte 233 var rn int 234 switch u.byteOrder { 235 case binary.LittleEndian: 236 rn, u.err = u.Read(b[:n]) 237 default: 238 rn, u.err = u.Read(b[8-n:]) 239 } 240 if rn < int(n) { 241 u.err = errUnexpectedPackEnd 242 } 243 if u.err != nil { 244 return false 245 } 246 r := bytes.NewReader(b[:]) 247 var x uint64 248 _ = binary.Read(r, u.byteOrder, &x) // There should be no error! 249 u.intVal = int64(x) 250 return true 251 } 252 return 253 } 254 255 func (u *unpacker) readVarInt() (ok bool) { 256 if !u.consumeBudget(8) { 257 return false 258 } 259 switch n := u.optSize; { 260 case n == 4: 261 var x int32 262 ok = u.read(0, &x) && 263 u.setIntVal(int64(x)) 264 case n == 8: 265 var x int64 266 ok = u.read(0, &x) && 267 u.setIntVal(x) 268 case n > 8: 269 var x uint64 270 var signExt uint8 271 if u.byteOrder == binary.BigEndian { 272 ok = u.readSignExt(n-8, &signExt) && u.read(0, &x) 273 } else { 274 ok = u.read(0, &x) && u.readSignExt(n-8, &signExt) 275 } 276 if !ok { 277 return 278 } 279 if signExt == 0 { 280 ok = x <= math.MaxInt64 281 if !ok { 282 u.err = errDoesNotFit 283 return 284 } 285 u.intVal = int64(x) 286 } else { 287 if ok = x > math.MaxInt64; ok { 288 xx := *(*int64)(unsafe.Pointer(&x)) 289 u.intVal = xx 290 } else { 291 u.err = errDoesNotFit 292 } 293 } 294 default: 295 // n < 8 so truncated 296 var b [8]byte 297 var rn int 298 switch u.byteOrder { 299 case binary.LittleEndian: 300 rn, u.err = u.Read(b[:n]) 301 if rn < int(n) { 302 u.err = errUnexpectedPackEnd 303 } 304 if u.err != nil { 305 return false 306 } 307 if b[n-1]&(1<<7) != 0 { 308 for i := n; i < 8; i++ { 309 b[i] = 0xff 310 } 311 } 312 default: 313 rn, u.err = u.Read(b[8-n:]) 314 if rn < int(n) { 315 u.err = errUnexpectedPackEnd 316 } 317 if u.err != nil { 318 return false 319 } 320 if b[8-n]&(1<<7) != 0 { 321 for i := uint(0); i < 8-n; i++ { 322 b[i] = 0xff 323 } 324 } 325 } 326 r := bytes.NewReader(b[:]) 327 var x int64 328 _ = binary.Read(r, u.byteOrder, &x) // There should be no error! 329 u.intVal = x 330 return true 331 } 332 return 333 } 334 335 func (u *unpacker) add(v rt.Value) bool { 336 u.values = append(u.values, v) 337 return true 338 } 339 340 func (u *unpacker) skip(n uint) (ok bool) { 341 u.j += int(n) 342 ok = u.j <= len(u.pack) 343 if !ok { 344 u.err = errUnexpectedPackEnd 345 } 346 return 347 } 348 349 func (u *unpacker) skip0(n uint) (ok bool) { 350 j := u.j 351 u.j += int(n) 352 ok = u.j <= len(u.pack) 353 if !ok { 354 u.err = errUnexpectedPackEnd 355 return 356 } 357 for j < u.j { 358 if ok = u.pack[j] == 0; !ok { 359 u.err = errDoesNotFit 360 return 361 } 362 j++ 363 } 364 return 365 } 366 367 func (u *unpacker) readSignExt(n uint, sign *uint8) (ok bool) { 368 // No need to consume budget, that's alrady been done in the calling 369 // function. 370 j := u.j 371 u.j += int(n) 372 ok = n > 0 && u.j <= len(u.pack) 373 if !ok { 374 u.err = errUnexpectedPackEnd 375 return 376 } 377 *sign = u.pack[j] 378 ok = *sign == 0 || *sign == 0xff 379 for j++; ok && j < u.j; j++ { 380 ok = u.pack[j] == *sign 381 } 382 if !ok { 383 u.err = errDoesNotFit 384 } 385 return 386 } 387 388 func (u *unpacker) setIntVal(v int64) bool { 389 u.intVal = v 390 return true 391 } 392 393 func (u *unpacker) consumeBudget(amount uint64) bool { 394 if u.budget == 0 { 395 return true 396 } 397 u.used += amount 398 if u.used > u.budget { 399 u.err = errBudgetConsumed 400 u.used = u.budget 401 return false 402 } 403 return true 404 }