github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/lib/stringlib/packer.go (about)

     1  package stringlib
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"math"
     7  	"strings"
     8  
     9  	rt "github.com/arnodel/golua/runtime"
    10  )
    11  
    12  type packer struct {
    13  	packFormatReader
    14  	values   []rt.Value   // Lua values to be packed
    15  	j        int          // Current index in the values above
    16  	val      rt.Value     // Current value
    17  	intVal   int64        // Current integral value (if applicable)
    18  	floatVal float64      // Current floating point value (if applicable)
    19  	strVal   string       // Current string value (if applicable)
    20  	w        bytes.Buffer // Where the output is written
    21  
    22  	budget uint64 // Number of bytes we are allowed to write before stopping (0 means unbounded)
    23  	used   uint64 // Number of bytes written so far
    24  }
    25  
    26  func PackValues(format string, values []rt.Value, budget uint64) (string, uint64, error) {
    27  	p := &packer{
    28  		packFormatReader: packFormatReader{
    29  			format:       format,
    30  			byteOrder:    nativeEndian,
    31  			maxAlignment: defaultMaxAlignement,
    32  		},
    33  		values: values,
    34  		budget: budget,
    35  	}
    36  	for p.hasNext() {
    37  		switch c := p.nextOption(); c {
    38  		case '<':
    39  			p.byteOrder = binary.LittleEndian
    40  		case '>':
    41  			p.byteOrder = binary.BigEndian
    42  		case '=':
    43  			p.byteOrder = nativeEndian
    44  		case '!':
    45  			if p.smallOptSize(defaultMaxAlignement) {
    46  				p.maxAlignment = p.optSize
    47  			}
    48  		case 'b':
    49  			_ = p.align(0) &&
    50  				p.nextIntValue() &&
    51  				p.checkBounds(math.MinInt8, math.MaxInt8) &&
    52  				p.write(1, int8(p.intVal))
    53  		case 'B':
    54  			_ = p.align(0) &&
    55  				p.nextIntValue() &&
    56  				p.checkBounds(0, math.MaxUint8) &&
    57  				p.write(1, uint8(p.intVal))
    58  		case 'h':
    59  			_ = p.align(2) &&
    60  				p.nextIntValue() &&
    61  				p.checkBounds(math.MinInt16, math.MaxInt16) &&
    62  				p.write(2, int16(p.intVal))
    63  		case 'H':
    64  			_ = p.align(2) &&
    65  				p.nextIntValue() &&
    66  				p.checkBounds(0, math.MaxUint16) &&
    67  				p.write(2, uint16(p.intVal))
    68  		case 'l', 'j':
    69  			_ = p.align(8) &&
    70  				p.nextIntValue() &&
    71  				p.write(8, p.intVal)
    72  		case 'L', 'J', 'T':
    73  			_ = p.align(8) &&
    74  				p.nextIntValue() &&
    75  				p.checkBounds(0, math.MaxInt64) &&
    76  				p.write(8, uint64(p.intVal))
    77  		case 'i':
    78  			_ = p.smallOptSize(8) &&
    79  				p.align(p.optSize) &&
    80  				p.nextIntValue() &&
    81  				p.packInt()
    82  		case 'I':
    83  			_ = p.smallOptSize(8) &&
    84  				p.align(p.optSize) &&
    85  				p.nextIntValue() &&
    86  				p.packUint()
    87  		case 'f':
    88  			_ = p.align(4) &&
    89  				p.nextFloatValue() &&
    90  				p.checkFloatSize(math.MaxFloat32) &&
    91  				p.write(4, float32(p.floatVal))
    92  		case 'd', 'n':
    93  			_ = p.align(8) &&
    94  				p.nextFloatValue() &&
    95  				p.write(8, p.floatVal)
    96  		case 'c':
    97  			_ = p.align(0) &&
    98  				p.mustGetOptSize() &&
    99  				p.nextStringValue() &&
   100  				p.writeStr(p.optSize)
   101  		case 'z':
   102  			if p.align(0) && p.nextStringValue() {
   103  				if strings.IndexByte(p.strVal, 0) >= 0 {
   104  					p.err = errStringContainsZeros
   105  				} else {
   106  
   107  					_ = p.writeStr(0) &&
   108  						p.writeByte(0)
   109  				}
   110  			}
   111  		case 's':
   112  			_ = p.smallOptSize(8) &&
   113  				p.align(p.optSize) &&
   114  				p.nextStringValue() &&
   115  				p.packUint() &&
   116  				p.writeStr(0)
   117  			if p.err == errOutOfBounds {
   118  				p.err = errStringDoesNotFit
   119  			}
   120  		case 'x':
   121  			_ = p.align(0) &&
   122  				p.writeByte(0)
   123  		case 'X':
   124  			p.alignOnly = true
   125  		case ' ':
   126  			// ignored
   127  		default:
   128  			p.err = errBadFormatString(c)
   129  		}
   130  		if p.err != nil {
   131  			return "", p.used, p.err
   132  		}
   133  	}
   134  	if p.alignOnly {
   135  		return "", p.used, errExpectedOption
   136  	}
   137  	return p.w.String(), p.used, nil
   138  }
   139  
   140  func (p *packer) nextValue() bool {
   141  	if len(p.values) > p.j {
   142  		p.val = p.values[p.j]
   143  		p.j++
   144  		return true
   145  	}
   146  	p.err = errNotEnoughValues
   147  	return false
   148  }
   149  
   150  func (p *packer) nextIntValue() bool {
   151  	if !p.nextValue() {
   152  		return false
   153  	}
   154  	n, ok := rt.ToInt(p.val)
   155  	if !ok {
   156  		p.err = errBadType
   157  		return false
   158  	}
   159  	p.intVal = int64(n)
   160  	return true
   161  }
   162  
   163  func (p *packer) nextFloatValue() bool {
   164  	if !p.nextValue() {
   165  		return false
   166  	}
   167  	f, ok := rt.ToFloat(p.val)
   168  	if !ok {
   169  		p.err = errBadType
   170  		return false
   171  	}
   172  	p.floatVal = float64(f)
   173  	return true
   174  }
   175  
   176  func (p *packer) nextStringValue() bool {
   177  	if !p.nextValue() {
   178  		return false
   179  	}
   180  	s, ok := p.val.ToString()
   181  	if !ok {
   182  		p.err = errBadType
   183  		return false
   184  	}
   185  	p.strVal = string(s)
   186  	p.intVal = int64(len(s))
   187  	return true
   188  }
   189  
   190  func (p *packer) checkBounds(min, max int64) bool {
   191  	ok := p.intVal >= min && p.intVal <= max
   192  	if !ok {
   193  		p.err = errOutOfBounds
   194  	}
   195  	return ok
   196  }
   197  
   198  func (p *packer) checkFloatSize(max float64) bool {
   199  	ok := (p.floatVal >= -max && p.floatVal <= max) || math.IsInf(p.floatVal, 0)
   200  	if !ok {
   201  		p.err = errOutOfBounds
   202  	}
   203  	return ok
   204  }
   205  
   206  func (p *packer) writeByte(b byte) bool {
   207  	p.w.WriteByte(b)
   208  	return true
   209  }
   210  
   211  func (p *packer) write(amount uint64, x interface{}) bool {
   212  	if !p.consumeBudget(amount) {
   213  		return false
   214  	}
   215  	p.err = binary.Write(&p.w, p.byteOrder, x)
   216  	return p.err == nil
   217  }
   218  
   219  func (p *packer) consumeBudget(amount uint64) bool {
   220  	if p.budget == 0 {
   221  		return true
   222  	}
   223  	p.used += amount
   224  	if p.used > p.budget {
   225  		p.err = errBudgetConsumed
   226  		p.used = p.budget
   227  		return false
   228  	}
   229  	return true
   230  }
   231  
   232  func (p *packer) writeStr(maxLen uint) bool {
   233  	diff := 0
   234  	if maxLen > 0 {
   235  		diff = int(maxLen) - len(p.strVal)
   236  	}
   237  	if diff < 0 {
   238  		p.err = errStringLongerThanFormat
   239  		return false
   240  	}
   241  	if !p.consumeBudget(uint64(len(p.strVal))) {
   242  		return false
   243  	}
   244  	p.w.Write([]byte(p.strVal))
   245  	if diff > 0 {
   246  		return p.fill(uint(diff), 0)
   247  	}
   248  	return true
   249  }
   250  
   251  func (p *packer) align(n uint) bool {
   252  	if n != 0 {
   253  		if n > p.maxAlignment {
   254  			n = p.maxAlignment
   255  		}
   256  		if (n-1)&n != 0 { // (n-1)&n == 0 iff n is a power of 2 (or 0)
   257  			p.err = errBadAlignment
   258  			return false
   259  		}
   260  		if r := uint(p.w.Len()) % n; r != 0 {
   261  			if !p.fill(n-r, 0) {
   262  				return false
   263  			}
   264  		}
   265  	}
   266  	if p.alignOnly {
   267  		p.alignOnly = false
   268  		return false
   269  	}
   270  	return true
   271  }
   272  
   273  func (p *packer) fill(n uint, c byte) bool {
   274  	if !p.consumeBudget(uint64(n)) {
   275  		return false
   276  	}
   277  	for ; n > 0; n-- {
   278  		p.w.WriteByte(c)
   279  	}
   280  	return true
   281  }
   282  
   283  func (p *packer) packInt() bool {
   284  	switch n := p.optSize; {
   285  	case n == 4:
   286  		// It's an int32
   287  		return p.checkBounds(math.MinInt32, math.MaxInt32) && p.write(4, int32(p.intVal))
   288  	case n == 8:
   289  		// It's an int64
   290  		return p.write(8, p.intVal)
   291  	case n >= 8:
   292  		// Pad to make up the length
   293  		var fill byte
   294  		if p.intVal < 0 {
   295  			fill = 255
   296  		}
   297  		if p.byteOrder == binary.BigEndian {
   298  			if !p.fill(n-8, fill) {
   299  				return false
   300  			}
   301  		}
   302  		if !p.write(8, p.intVal) {
   303  			return false
   304  		}
   305  		if p.byteOrder == binary.LittleEndian {
   306  			if !p.fill(n-8, fill) {
   307  				return false
   308  			}
   309  		}
   310  	default:
   311  		// n < 8 so truncate
   312  		max := int64(1) << (n<<3 - 1)
   313  		if !p.checkBounds(-max, max-1) {
   314  			return false
   315  		}
   316  		var ww bytes.Buffer
   317  		if err := binary.Write(&ww, p.byteOrder, p.intVal); err != nil {
   318  			p.err = err
   319  			return false
   320  		}
   321  		switch p.byteOrder {
   322  		case binary.LittleEndian:
   323  			p.w.Write(ww.Bytes()[:n])
   324  		default:
   325  			p.w.Write(ww.Bytes()[8-n:])
   326  		}
   327  	}
   328  	return true
   329  }
   330  
   331  func (p *packer) packUint() bool {
   332  	switch n := p.optSize; {
   333  	case n == 4:
   334  		// It's an uint32
   335  		return p.checkBounds(0, math.MaxUint32) && p.write(4, uint32(p.intVal))
   336  	case n == 8:
   337  		// It's an uint64
   338  		return p.checkBounds(0, math.MaxInt64) && p.write(8, uint64(p.intVal))
   339  	case n > 8:
   340  		// Pad to make up the length
   341  		if p.byteOrder == binary.BigEndian {
   342  			if !p.fill(n-8, 0) {
   343  				return false
   344  			}
   345  		}
   346  		if !p.write(8, uint64(p.intVal)) {
   347  			return false
   348  		}
   349  		if p.byteOrder == binary.LittleEndian {
   350  			if !p.fill(n-8, 0) {
   351  				return false
   352  			}
   353  		}
   354  	default:
   355  		// n < 8 so truncate
   356  		max := int64(1) << (n << 3)
   357  		if !p.checkBounds(0, max-1) {
   358  			return false
   359  		}
   360  		var ww bytes.Buffer
   361  		if err := binary.Write(&ww, p.byteOrder, uint64(p.intVal)); err != nil {
   362  			p.err = err
   363  			return false
   364  		}
   365  		switch p.byteOrder {
   366  		case binary.LittleEndian:
   367  			p.w.Write(ww.Bytes()[:n])
   368  		default:
   369  			p.w.Write(ww.Bytes()[8-n:])
   370  		}
   371  	}
   372  	return true
   373  }