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  }