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 }