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

     1  package string
     2  
     3  import (
     4  	"fmt"
     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  // unpack(fmt, s, [, pos])
    14  func unpack(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    15  	ap := fnutil.NewArgParser(th, args)
    16  
    17  	fmt, err := ap.ToGoString(0)
    18  	if err != nil {
    19  		return nil, err
    20  	}
    21  
    22  	s, err := ap.ToGoString(1)
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  
    27  	pos, err := ap.OptGoInt(2, 1)
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  
    32  	if pos < 0 {
    33  		pos = len(s) + 1 + pos
    34  	}
    35  
    36  	if pos <= 0 || len(s) < pos-1 {
    37  		return nil, ap.ArgError(2, "initial position out of string")
    38  	}
    39  
    40  	u := &unpacker{
    41  		optParser: optParser{
    42  			ap:       ap,
    43  			fmt:      fmt,
    44  			maxAlign: 1,
    45  			endian:   littleEndian,
    46  			off:      pos - 1,
    47  		},
    48  		s: s,
    49  	}
    50  
    51  	return u.unpack()
    52  }
    53  
    54  type unpacker struct {
    55  	optParser
    56  	s string
    57  }
    58  
    59  func (u *unpacker) unpack() (rets []object.Value, err *object.RuntimeError) {
    60  	for {
    61  		opt, err := u.nextkOpt()
    62  		if err != nil {
    63  			return nil, err
    64  		}
    65  
    66  		if opt == nil {
    67  			break
    68  		}
    69  
    70  		err = u.skipPadding(opt.padding)
    71  		if err != nil {
    72  			return nil, err
    73  		}
    74  
    75  		u.off += opt.padding
    76  
    77  		switch opt.typ {
    78  		case kInt:
    79  			ret, err := u.unpackInt(opt)
    80  			if err != nil {
    81  				return nil, err
    82  			}
    83  			rets = append(rets, ret)
    84  		case kUint:
    85  			ret, err := u.unpackUint(opt)
    86  			if err != nil {
    87  				return nil, err
    88  			}
    89  			rets = append(rets, ret)
    90  		case kFloat:
    91  			ret, err := u.unpackFloat(opt)
    92  			if err != nil {
    93  				return nil, err
    94  			}
    95  			rets = append(rets, ret)
    96  		case kChar:
    97  			ret, err := u.unpackChar(opt)
    98  			if err != nil {
    99  				return nil, err
   100  			}
   101  			rets = append(rets, ret)
   102  		case kString:
   103  			ret, err := u.unpackString(opt)
   104  			if err != nil {
   105  				return nil, err
   106  			}
   107  			rets = append(rets, ret)
   108  		case kZeroString:
   109  			ret, err := u.unpackZeroString()
   110  			if err != nil {
   111  				return nil, err
   112  			}
   113  			rets = append(rets, ret)
   114  		case kPadding:
   115  			err = u.skipPadding(opt.size)
   116  			if err != nil {
   117  				return nil, err
   118  			}
   119  		case kPaddingAlign, kNop:
   120  		default:
   121  			panic("unreachable")
   122  		}
   123  
   124  		u.off += opt.size
   125  	}
   126  
   127  	rets = append(rets, object.Integer(u.off+1))
   128  
   129  	return rets, nil
   130  }
   131  
   132  func (u *unpacker) unpackUint64(opt *kOption) (uint64, *object.RuntimeError) {
   133  	if len(u.s)-u.off < opt.size {
   134  		return 0, u.ap.ArgError(0, "data string is too short")
   135  	}
   136  
   137  	s := u.s[u.off : u.off+opt.size]
   138  
   139  	var u64 uint64
   140  
   141  	switch u.endian {
   142  	case littleEndian:
   143  		if len(s) > 8 {
   144  			for i := 0; i < 8; i++ {
   145  				u64 |= uint64(s[i]) << uint(8*i)
   146  			}
   147  			for i := 8; i < len(s); i++ {
   148  				if s[i] != 0 {
   149  					return 0, object.NewRuntimeError(fmt.Sprintf("%d-byte integer does not fit into Lua Integer", len(s)))
   150  				}
   151  			}
   152  		} else {
   153  			for i := 0; i < len(s); i++ {
   154  				u64 |= uint64(s[i]) << uint(8*i)
   155  			}
   156  		}
   157  	case bigEndian:
   158  		if len(s) > 8 {
   159  			for i := 0; i < len(s)-8; i++ {
   160  				if s[i] != 0 {
   161  					return 0, object.NewRuntimeError(fmt.Sprintf("%d-byte integer does not fit into Lua Integer", len(s)))
   162  				}
   163  			}
   164  			for i := len(s) - 8; i < len(s); i++ {
   165  				u64 |= uint64(s[i]) << uint(8*(len(s)-1-i))
   166  			}
   167  		} else {
   168  			for i := 0; i < len(s); i++ {
   169  				u64 |= uint64(s[i]) << uint(8*(len(s)-1-i))
   170  			}
   171  		}
   172  	default:
   173  		panic("unreachable")
   174  	}
   175  
   176  	return u64, nil
   177  }
   178  
   179  func (u *unpacker) unpackInt64(opt *kOption) (int64, *object.RuntimeError) {
   180  	if len(u.s)-u.off < opt.size {
   181  		return 0, u.ap.ArgError(0, "data string is too short")
   182  	}
   183  
   184  	s := u.s[u.off : u.off+opt.size]
   185  
   186  	var u64 uint64
   187  
   188  	switch u.endian {
   189  	case littleEndian:
   190  		if s[len(s)-1]&0x80 != 0 {
   191  			if len(s) > 8 {
   192  				for i := 0; i < 8; i++ {
   193  					u64 |= uint64(s[i]) << uint(8*i)
   194  				}
   195  				for i := 8; i < len(s); i++ {
   196  					if s[i] != 0xff {
   197  						return 0, object.NewRuntimeError(fmt.Sprintf("%d-byte integer does not fit into Lua Integer", len(s)))
   198  					}
   199  				}
   200  				u64 = (u64 - (1<<64 - 1)) - 1
   201  			} else {
   202  				for i := 0; i < len(s); i++ {
   203  					u64 |= uint64(s[i]) << uint(8*i)
   204  				}
   205  				u64 = u64 - 1<<uint(len(s)*8)
   206  			}
   207  		} else {
   208  			if len(s) > 8 {
   209  				for i := 0; i < 8; i++ {
   210  					u64 |= uint64(s[i]) << uint(8*i)
   211  				}
   212  				for i := 8; i < len(s); i++ {
   213  					if s[i] != 0 {
   214  						return 0, object.NewRuntimeError(fmt.Sprintf("%d-byte integer does not fit into Lua Integer", len(s)))
   215  					}
   216  				}
   217  			} else {
   218  				for i := 0; i < len(s); i++ {
   219  					u64 |= uint64(s[i]) << uint(8*i)
   220  				}
   221  			}
   222  		}
   223  	case bigEndian:
   224  		if s[0]&0x80 != 0 {
   225  			if len(s) > 8 {
   226  				for i := 0; i < len(s)-8; i++ {
   227  					if s[i] != 0xff {
   228  						return 0, object.NewRuntimeError(fmt.Sprintf("%d-byte integer does not fit into Lua Integer", len(s)))
   229  					}
   230  				}
   231  				for i := len(s) - 8; i < len(s); i++ {
   232  					u64 |= uint64(s[i]) << uint(8*(len(s)-1-i))
   233  				}
   234  				u64 = (u64 - (1<<64 - 1)) - 1
   235  			} else {
   236  				for i := 0; i < len(s); i++ {
   237  					u64 |= uint64(s[i]) << uint(8*(len(s)-1-i))
   238  				}
   239  				u64 = u64 - 1<<uint(len(s)*8)
   240  			}
   241  		} else {
   242  			if len(s) > 8 {
   243  				for i := 0; i < len(s)-8; i++ {
   244  					if s[i] != 0 {
   245  						return 0, object.NewRuntimeError(fmt.Sprintf("%d-byte integer does not fit into Lua Integer", len(s)))
   246  					}
   247  				}
   248  				for i := len(s) - 8; i < len(s); i++ {
   249  					u64 |= uint64(s[i]) << uint(8*(len(s)-1-i))
   250  				}
   251  			} else {
   252  				for i := 0; i < len(s); i++ {
   253  					u64 |= uint64(s[i]) << uint(8*(len(s)-1-i))
   254  				}
   255  			}
   256  		}
   257  	default:
   258  		panic("unreachable")
   259  	}
   260  
   261  	return int64(u64), nil
   262  }
   263  
   264  func (u *unpacker) unpackUint(opt *kOption) (object.Value, *object.RuntimeError) {
   265  	u64, err := u.unpackUint64(opt)
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	return object.Integer(u64), nil
   271  }
   272  
   273  func (u *unpacker) unpackInt(opt *kOption) (object.Value, *object.RuntimeError) {
   274  	i64, err := u.unpackInt64(opt)
   275  	if err != nil {
   276  		return nil, err
   277  	}
   278  
   279  	return object.Integer(i64), nil
   280  }
   281  
   282  func (u *unpacker) unpackFloat(opt *kOption) (object.Value, *object.RuntimeError) {
   283  	u64, err := u.unpackUint64(opt)
   284  	if err != nil {
   285  		return nil, err
   286  	}
   287  
   288  	switch opt.size {
   289  	case 4:
   290  		return object.Number(math.Float32frombits(uint32(u64))), nil
   291  	case 8:
   292  		return object.Number(math.Float64frombits(u64)), nil
   293  	default:
   294  		panic("unreachable")
   295  	}
   296  }
   297  
   298  func (u *unpacker) unpackChar(opt *kOption) (object.Value, *object.RuntimeError) {
   299  	if len(u.s)-u.off < opt.size {
   300  		return nil, u.ap.ArgError(0, "data string is too short")
   301  	}
   302  
   303  	val := object.String(u.s[u.off : u.off+opt.size])
   304  
   305  	return val, nil
   306  }
   307  
   308  func (u *unpacker) unpackString(opt *kOption) (object.Value, *object.RuntimeError) {
   309  	u64, err := u.unpackUint64(opt)
   310  	if err != nil {
   311  		return nil, err
   312  	}
   313  
   314  	if u64 > uint64(limits.MaxInt) {
   315  		return nil, u.ap.ArgError(0, "integer overflow")
   316  	}
   317  
   318  	slen := int(u64)
   319  
   320  	if len(u.s)-u.off-opt.size < slen {
   321  		return nil, u.ap.ArgError(0, "data string is too short")
   322  	}
   323  
   324  	val := object.String(u.s[u.off+opt.size : u.off+opt.size+slen])
   325  
   326  	u.off += slen
   327  
   328  	return val, nil
   329  }
   330  
   331  func (u *unpacker) unpackZeroString() (object.Value, *object.RuntimeError) {
   332  	slen := strings.IndexByte(u.s[u.off:], 0x00)
   333  	if slen == -1 {
   334  		return nil, u.ap.ArgError(0, "data string does not contains zero")
   335  	}
   336  
   337  	val := object.String(u.s[u.off : u.off+slen])
   338  
   339  	u.off += slen + 1
   340  
   341  	return val, nil
   342  }
   343  
   344  func (u *unpacker) skipPadding(padding int) *object.RuntimeError {
   345  	if len(u.s)-padding < u.off {
   346  		return u.ap.ArgError(0, "data string is too short")
   347  	}
   348  
   349  	return nil
   350  }