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 }