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

     1  package string
     2  
     3  import (
     4  	"fmt"
     5  	"unsafe"
     6  
     7  	"github.com/hirochachacha/plua/internal/limits"
     8  	"github.com/hirochachacha/plua/object"
     9  	"github.com/hirochachacha/plua/object/fnutil"
    10  )
    11  
    12  type kOption struct {
    13  	typ     kType
    14  	size    int
    15  	padding int
    16  }
    17  
    18  type kType uint
    19  
    20  const (
    21  	kInt kType = iota
    22  	kUint
    23  	kFloat
    24  	kChar       // fixed-length string
    25  	kString     // length + string
    26  	kZeroString // string + '\x00'
    27  	kPadding
    28  	kPaddingAlign
    29  	kNop
    30  )
    31  
    32  const maxIntSize = 16
    33  const nativeAlign = int(unsafe.Offsetof(dummy{}.i))
    34  
    35  type dummy struct {
    36  	b byte
    37  	i int64
    38  }
    39  
    40  func isDigit(b byte) bool {
    41  	return '0' <= b && b <= '9'
    42  }
    43  
    44  type optParser struct {
    45  	ap       *fnutil.ArgParser
    46  	fmt      string
    47  	maxAlign int
    48  	endian   byteOrder
    49  	off      int
    50  }
    51  
    52  func (p *optParser) nextkOpt() (opt *kOption, err *object.RuntimeError) {
    53  	var typ kType
    54  	var size int
    55  
    56  next:
    57  	if len(p.fmt) == 0 {
    58  		return nil, nil
    59  	}
    60  
    61  	switch op := p.fmt[0]; op {
    62  	case 'b':
    63  		typ = kInt
    64  		size = 1
    65  		p.fmt = p.fmt[1:]
    66  	case 'B':
    67  		typ = kUint
    68  		size = 1
    69  		p.fmt = p.fmt[1:]
    70  	case 'h':
    71  		typ = kInt
    72  		size = 2
    73  		p.fmt = p.fmt[1:]
    74  	case 'H':
    75  		typ = kUint
    76  		size = 2
    77  		p.fmt = p.fmt[1:]
    78  	case 'l':
    79  		typ = kInt
    80  		size = 8
    81  		p.fmt = p.fmt[1:]
    82  	case 'L':
    83  		typ = kUint
    84  		size = 8
    85  		p.fmt = p.fmt[1:]
    86  	case 'j':
    87  		typ = kInt
    88  		size = 8
    89  		p.fmt = p.fmt[1:]
    90  	case 'J':
    91  		typ = kUint
    92  		size = 8
    93  		p.fmt = p.fmt[1:]
    94  	case 'T':
    95  		typ = kUint
    96  		size = 8
    97  		p.fmt = p.fmt[1:]
    98  	case 'f':
    99  		typ = kFloat
   100  		size = 4
   101  		p.fmt = p.fmt[1:]
   102  	case 'd':
   103  		typ = kFloat
   104  		size = 8
   105  		p.fmt = p.fmt[1:]
   106  	case 'n':
   107  		typ = kFloat
   108  		size = 8
   109  		p.fmt = p.fmt[1:]
   110  	case 'i':
   111  		typ = kInt
   112  		size = limits.IntSize
   113  
   114  		i := 1
   115  
   116  		if i < len(p.fmt) && isDigit(p.fmt[i]) {
   117  			size = int(p.fmt[i] - '0')
   118  
   119  			i++
   120  
   121  			for i < len(p.fmt) && isDigit(p.fmt[i]) {
   122  				if (int(limits.MaxInt)-int(p.fmt[i]-'0'))/10 < size {
   123  					return nil, object.NewRuntimeError("size is too large")
   124  				}
   125  
   126  				size = size*10 + int(p.fmt[i]-'0')
   127  				i++
   128  			}
   129  
   130  			if size == 0 || size > maxIntSize {
   131  				return nil, object.NewRuntimeError(fmt.Sprintf("integral size (%d) out of limits [1,%d]", size, maxIntSize))
   132  			}
   133  		}
   134  
   135  		p.fmt = p.fmt[i:]
   136  	case 'I':
   137  		typ = kUint
   138  		size = limits.IntSize
   139  
   140  		i := 1
   141  
   142  		if i < len(p.fmt) && isDigit(p.fmt[i]) {
   143  			size = int(p.fmt[i] - '0')
   144  
   145  			i++
   146  
   147  			for i < len(p.fmt) && isDigit(p.fmt[i]) {
   148  				if (int(limits.MaxInt)-int(p.fmt[i]-'0'))/10 < size {
   149  					return nil, object.NewRuntimeError("size is too large")
   150  				}
   151  
   152  				size = size*10 + int(p.fmt[i]-'0')
   153  				i++
   154  			}
   155  
   156  			if size == 0 || size > maxIntSize {
   157  				return nil, object.NewRuntimeError(fmt.Sprintf("integral size (%d) out of limits [1,%d]", size, maxIntSize))
   158  			}
   159  		}
   160  
   161  		p.fmt = p.fmt[i:]
   162  	case 's':
   163  		typ = kString
   164  		size = 8
   165  
   166  		i := 1
   167  
   168  		if i < len(p.fmt) && isDigit(p.fmt[i]) {
   169  			size = int(p.fmt[i] - '0')
   170  
   171  			i++
   172  
   173  			for i < len(p.fmt) && isDigit(p.fmt[i]) {
   174  				if (int(limits.MaxInt)-int(p.fmt[i]-'0'))/10 < size {
   175  					return nil, object.NewRuntimeError("size is too large")
   176  				}
   177  
   178  				size = size*10 + int(p.fmt[i]-'0')
   179  				i++
   180  			}
   181  
   182  			if size == 0 || size > maxIntSize {
   183  				return nil, object.NewRuntimeError(fmt.Sprintf("integral size (%d) out of limits [1,%d]", size, maxIntSize))
   184  			}
   185  		}
   186  
   187  		p.fmt = p.fmt[i:]
   188  	case 'c':
   189  		typ = kChar
   190  		size = 0
   191  
   192  		i := 1
   193  
   194  		if i < len(p.fmt) && isDigit(p.fmt[i]) {
   195  			size = int(p.fmt[i] - '0')
   196  
   197  			i++
   198  
   199  			for i < len(p.fmt) && isDigit(p.fmt[i]) {
   200  				if (int(limits.MaxInt)-int(p.fmt[i]-'0'))/10 < size {
   201  					return nil, object.NewRuntimeError("size is too large")
   202  				}
   203  
   204  				size = size*10 + int(p.fmt[i]-'0')
   205  				i++
   206  			}
   207  		} else {
   208  			return nil, object.NewRuntimeError("missing size for format option 'c'")
   209  		}
   210  
   211  		p.fmt = p.fmt[i:]
   212  	case 'z':
   213  		typ = kZeroString
   214  		size = 0
   215  		p.fmt = p.fmt[1:]
   216  	case 'x':
   217  		typ = kPadding
   218  		size = 1
   219  		p.fmt = p.fmt[1:]
   220  	case 'X':
   221  		typ = kPaddingAlign
   222  		size = 0
   223  		p.fmt = p.fmt[1:]
   224  
   225  		opt, err := p.nextkOpt()
   226  		if err != nil {
   227  			return nil, err
   228  		}
   229  
   230  		if opt == nil || opt.typ == kPaddingAlign || opt.typ == kChar || opt.size == 0 {
   231  			return nil, p.ap.ArgError(0, "invalid next option for option 'X'")
   232  		}
   233  
   234  		opt.typ = kPaddingAlign
   235  		opt.size = 0
   236  
   237  		return opt, nil
   238  	case ' ':
   239  		p.fmt = p.fmt[1:]
   240  		goto next
   241  	case '<':
   242  		p.endian = littleEndian
   243  		p.fmt = p.fmt[1:]
   244  		goto next
   245  	case '>':
   246  		p.endian = bigEndian
   247  		p.fmt = p.fmt[1:]
   248  		goto next
   249  	case '=':
   250  		p.endian = littleEndian
   251  		p.fmt = p.fmt[1:]
   252  		goto next
   253  	case '!':
   254  		p.maxAlign = nativeAlign
   255  
   256  		i := 1
   257  
   258  		if i < len(p.fmt) && isDigit(p.fmt[i]) {
   259  			p.maxAlign = int(p.fmt[i] - '0')
   260  
   261  			i++
   262  
   263  			for i < len(p.fmt) && isDigit(p.fmt[i]) {
   264  				if (int(limits.MaxInt)-int(p.fmt[i]-'0'))/10 < p.maxAlign {
   265  					return nil, object.NewRuntimeError("size is too large")
   266  				}
   267  
   268  				p.maxAlign = p.maxAlign*10 + int(p.fmt[i]-'0')
   269  				i++
   270  			}
   271  
   272  			if p.maxAlign == 0 || p.maxAlign > maxIntSize {
   273  				return nil, object.NewRuntimeError(fmt.Sprintf("integral size (%d) out of limits [1,%d]", p.maxAlign, maxIntSize))
   274  			}
   275  		}
   276  
   277  		p.fmt = p.fmt[i:]
   278  
   279  		goto next
   280  	default:
   281  		return nil, p.ap.ArgError(0, "invalid format option '"+string(op)+"'")
   282  	}
   283  
   284  	opt = &kOption{
   285  		typ:  typ,
   286  		size: size,
   287  	}
   288  
   289  	align := size
   290  
   291  	if align > 1 && typ != kChar {
   292  		if align > p.maxAlign {
   293  			align = p.maxAlign
   294  		}
   295  
   296  		if align&(align-1) != 0 { // is not power of two?
   297  			return nil, p.ap.ArgError(0, "format ask for alignment not power of 2")
   298  		}
   299  
   300  		opt.padding = (align - p.off&(align-1)) & (align - 1) // (align - total % align) % align
   301  	}
   302  
   303  	// if p.total > int(limits.MaxInt)-(opt.padding+opt.size) {
   304  	// return nil, p.ap.ArgError(0, "format result too large")
   305  	// }
   306  
   307  	// p.total += opt.padding + opt.size
   308  
   309  	return opt, nil
   310  }