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 }