github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/stdlib/string/pack.go (about)

     1  package string
     2  
     3  import (
     4  	"bytes"
     5  	"math"
     6  	"strings"
     7  
     8  	"github.com/hirochachacha/plua/internal/limits"
     9  	"github.com/hirochachacha/plua/object"
    10  	"github.com/hirochachacha/plua/object/fnutil"
    11  )
    12  
    13  type byteOrder int
    14  
    15  const (
    16  	littleEndian byteOrder = iota
    17  	bigEndian
    18  )
    19  
    20  // pack(fmt, v1, v2, ...)
    21  func pack(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    22  	ap := fnutil.NewArgParser(th, args)
    23  
    24  	fmt, err := ap.ToGoString(0)
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  
    29  	p := &packer{
    30  		optParser: optParser{
    31  			ap:       ap,
    32  			fmt:      fmt,
    33  			maxAlign: 1,
    34  			endian:   littleEndian,
    35  		},
    36  	}
    37  
    38  	s, err := p.pack()
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	return []object.Value{object.String(s)}, nil
    44  }
    45  
    46  // packsize(fmt)
    47  func packsize(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    48  	ap := fnutil.NewArgParser(th, args)
    49  
    50  	fmt, err := ap.ToGoString(0)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	p := &packer{
    56  		optParser: optParser{
    57  			ap:       ap,
    58  			fmt:      fmt,
    59  			maxAlign: 1,
    60  			endian:   littleEndian,
    61  		},
    62  	}
    63  
    64  	for {
    65  		opt, err := p.nextkOpt()
    66  		if err != nil {
    67  			return nil, err
    68  		}
    69  
    70  		if opt == nil {
    71  			break
    72  		}
    73  
    74  		if p.off > int(limits.MaxInt)-(opt.padding+opt.size) {
    75  			return nil, p.ap.ArgError(0, "format result too large")
    76  		}
    77  
    78  		p.off += opt.padding + opt.size
    79  
    80  		switch opt.typ {
    81  		case kString, kZeroString:
    82  			return nil, ap.ArgError(0, "variable-length format")
    83  		}
    84  	}
    85  
    86  	return []object.Value{object.Integer(p.off)}, nil
    87  }
    88  
    89  type packer struct {
    90  	optParser
    91  	bytes.Buffer
    92  }
    93  
    94  func (p *packer) pack() (string, *object.RuntimeError) {
    95  	n := 1
    96  
    97  	for {
    98  		opt, err := p.nextkOpt()
    99  		if err != nil {
   100  			return "", err
   101  		}
   102  
   103  		if opt == nil {
   104  			break
   105  		}
   106  
   107  		if p.off > int(limits.MaxInt)-(opt.padding+opt.size) {
   108  			return "", p.ap.ArgError(0, "format result too large")
   109  		}
   110  
   111  		p.off += opt.padding + opt.size
   112  
   113  		for j := 0; j < opt.padding; j++ {
   114  			p.packPadding()
   115  		}
   116  
   117  		switch opt.typ {
   118  		case kInt:
   119  			err = p.packInt(n, opt)
   120  		case kUint:
   121  			err = p.packUint(n, opt)
   122  		case kFloat:
   123  			err = p.packFloat(n, opt)
   124  		case kChar:
   125  			err = p.packChar(n, opt)
   126  		case kString:
   127  			err = p.packString(n, opt)
   128  		case kZeroString:
   129  			err = p.packZeroString(n)
   130  		case kPadding:
   131  			p.packPadding()
   132  			continue
   133  		case kPaddingAlign, kNop:
   134  			continue
   135  		default:
   136  			panic("unreachable")
   137  		}
   138  		if err != nil {
   139  			return "", err
   140  		}
   141  
   142  		n++
   143  	}
   144  
   145  	return p.String(), nil
   146  }
   147  
   148  func (p *packer) packUint64(u64 uint64, opt *kOption) {
   149  	switch p.endian {
   150  	case littleEndian:
   151  		for j := 0; j < opt.size; j++ {
   152  			p.WriteByte(byte(u64 >> uint(8*j)))
   153  		}
   154  	case bigEndian:
   155  		for j := 0; j < opt.size; j++ {
   156  			p.WriteByte(byte(u64 >> uint(8*(opt.size-1-j))))
   157  		}
   158  	default:
   159  		panic("unreachable")
   160  	}
   161  }
   162  
   163  func (p *packer) packInt64(i64 int64, opt *kOption) {
   164  	switch p.endian {
   165  	case littleEndian:
   166  		for j := 0; j < opt.size; j++ {
   167  			p.WriteByte(^byte(^i64 >> uint(8*j)))
   168  		}
   169  	case bigEndian:
   170  		for j := 0; j < opt.size; j++ {
   171  			p.WriteByte(^byte(^i64 >> uint(8*(opt.size-1-j))))
   172  		}
   173  	default:
   174  		panic("unreachable")
   175  	}
   176  }
   177  
   178  func (p *packer) packUint(n int, opt *kOption) *object.RuntimeError {
   179  	i64, err := p.ap.ToGoInt64(n)
   180  	if err != nil {
   181  		return err
   182  	}
   183  
   184  	u64 := uint64(i64)
   185  
   186  	if opt.size < 8 {
   187  		lim := uint64(1 << uint((opt.size * 8)))
   188  
   189  		if u64 >= lim {
   190  			return p.ap.ArgError(n, "unsigned overflow")
   191  		}
   192  	}
   193  
   194  	p.packUint64(u64, opt)
   195  
   196  	return nil
   197  }
   198  
   199  func (p *packer) packInt(n int, opt *kOption) *object.RuntimeError {
   200  	i64, err := p.ap.ToGoInt64(n)
   201  	if err != nil {
   202  		return err
   203  	}
   204  
   205  	if opt.size < 8 {
   206  		lim := int64(1 << uint((opt.size*8)-1))
   207  
   208  		if !(-lim <= i64 && i64 < lim) {
   209  			return p.ap.ArgError(n, "integer overflow")
   210  		}
   211  	}
   212  
   213  	p.packInt64(i64, opt)
   214  
   215  	return nil
   216  }
   217  
   218  func (p *packer) packFloat(n int, opt *kOption) *object.RuntimeError {
   219  	f, err := p.ap.ToGoFloat64(n)
   220  	if err != nil {
   221  		return err
   222  	}
   223  
   224  	switch opt.size {
   225  	case 4:
   226  		p.packUint64(uint64(math.Float32bits(float32(f))), opt)
   227  	case 8:
   228  		p.packUint64(math.Float64bits(f), opt)
   229  	default:
   230  		panic("unreachable")
   231  	}
   232  
   233  	return nil
   234  }
   235  
   236  func (p *packer) packChar(n int, opt *kOption) *object.RuntimeError {
   237  	s, err := p.ap.ToGoString(n)
   238  	if err != nil {
   239  		return err
   240  	}
   241  
   242  	if opt.size < len(s) {
   243  		return p.ap.ArgError(n, "string longer than given size")
   244  	}
   245  
   246  	p.WriteString(s)
   247  
   248  	for i := len(s); i < opt.size; i++ {
   249  		p.WriteByte(0)
   250  	}
   251  
   252  	return nil
   253  }
   254  
   255  func (p *packer) packString(n int, opt *kOption) *object.RuntimeError {
   256  	s, err := p.ap.ToGoString(n)
   257  	if err != nil {
   258  		return err
   259  	}
   260  
   261  	if opt.size < 8 {
   262  		lim := uint64(1 << uint((opt.size*8)-1))
   263  
   264  		if uint64(len(s)) >= lim {
   265  			return p.ap.ArgError(n, "string length does not fit in given size")
   266  		}
   267  	}
   268  
   269  	p.packUint64(uint64(len(s)), opt)
   270  
   271  	p.WriteString(s)
   272  
   273  	p.off += len(s)
   274  
   275  	return nil
   276  }
   277  
   278  func (p *packer) packZeroString(n int) *object.RuntimeError {
   279  	s, err := p.ap.ToGoString(n)
   280  	if err != nil {
   281  		return err
   282  	}
   283  
   284  	if strings.ContainsRune(s, 0x00) {
   285  		return p.ap.ArgError(n, "string contains zeros")
   286  	}
   287  
   288  	p.WriteString(s)
   289  	p.WriteByte(0x00)
   290  
   291  	p.off += len(s) + 1
   292  
   293  	return nil
   294  }
   295  
   296  func (p *packer) packPadding() {
   297  	p.WriteByte(0x00)
   298  }