github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/lib/stringlib/packing.go (about) 1 package stringlib 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "fmt" 7 "unsafe" 8 9 "github.com/arnodel/golua/luastrings" 10 rt "github.com/arnodel/golua/runtime" 11 ) 12 13 /* 14 Notes: 15 16 <: sets little endian 17 >: sets big endian 18 19 =: sets native endian 20 21 ![n]: sets maximum alignment to n (default is native alignment) 22 between 1 and 16 23 Does not need to be a power of 2 but if it's not it seems things break? 24 25 b: a signed byte (char) 26 int8 27 28 B: an unsigned byte (char) 29 uint8 30 31 h: a signed short (native size) 32 int16 33 34 H: an unsigned short (native size) 35 uint16 36 37 l: a signed long (native size) 38 int64 39 40 L: an unsigned long (native size) 41 uint64 42 43 j: a lua_Integer 44 int64 45 46 J: a lua_Unsigned 47 uint64 48 49 T: a size_t (native size) 50 uint64 51 52 i[n]: a signed int with n bytes (default is native size) 53 default int32 54 n between 1 and 16 errors if an overflow 55 56 I[n]: an unsigned int with n bytes (default is native size) 57 default uint32 58 n between 1 and 16 59 f: a float (native size) 60 float32 61 d: a double (native size) 62 float64 63 64 n: a lua_Number 65 float64 66 67 cn: a fixed-sized string with n bytes 68 Not aligned 69 70 z: a zero-terminated string 71 Not aligned 72 73 s[n]: a string preceded by its length coded as an unsigned integer with n bytes (default is a size_t) 74 Aligned like I[n] 75 76 x: one byte of padding 77 That is one zero byte 78 79 Xop: an empty item that aligns according to option op (which is otherwise ignored) 80 That is, add padding for alignment but do not add a value 81 82 ' ': (empty space) ignored 83 */ 84 85 func pack(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 86 if err := c.Check1Arg(); err != nil { 87 return nil, err 88 } 89 format, err := c.StringArg(0) 90 if err != nil { 91 return nil, err 92 } 93 res, used, perr := PackValues(string(format), c.Etc(), t.LinearUnused(10)) 94 t.LinearRequire(10, used) 95 if perr != nil { 96 return nil, perr 97 } 98 return c.PushingNext1(t.Runtime, rt.StringValue(res)), nil 99 } 100 101 func unpack(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 102 if err := c.CheckNArgs(2); err != nil { 103 return nil, err 104 } 105 var ( 106 format, pack string 107 n int64 = 1 108 err error 109 ) 110 format, err = c.StringArg(0) 111 if err == nil { 112 pack, err = c.StringArg(1) 113 } 114 if err == nil && c.NArgs() >= 3 { 115 n, err = c.IntArg(2) 116 } 117 i := luastrings.StringNormPos(pack, int(n)) - 1 118 if i < 0 || i > len(pack) { 119 err = errors.New("#3 out of string") 120 } 121 if err != nil { 122 return nil, err 123 } 124 vals, m, used, uerr := UnpackString(string(format), string(pack), i, t.LinearUnused(10)) 125 t.LinearRequire(10, used) 126 if uerr != nil { 127 return nil, uerr 128 } 129 next := c.Next() 130 t.Push(next, vals...) 131 t.Push1(next, rt.IntValue(int64(m+1))) 132 return next, nil 133 } 134 135 func packsize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 136 if err := c.Check1Arg(); err != nil { 137 return nil, err 138 } 139 format, err := c.StringArg(0) 140 if err != nil { 141 return nil, err 142 } 143 size, serr := PackSize(string(format)) 144 if serr != nil { 145 return nil, serr 146 } 147 return c.PushingNext1(t.Runtime, rt.IntValue(int64(size))), nil 148 } 149 150 func isLittleEndian() bool { 151 var i int32 = 0x01020304 152 u := unsafe.Pointer(&i) 153 pb := (*byte)(u) 154 b := *pb 155 return (b == 0x04) 156 } 157 158 var nativeEndian binary.ByteOrder 159 160 const defaultMaxAlignement uint = 1 161 162 func init() { 163 if isLittleEndian() { 164 nativeEndian = binary.LittleEndian 165 } else { 166 nativeEndian = binary.BigEndian 167 } 168 } 169 170 var ( 171 errBadOptionArg = errors.New("arg out of limits [1,16]") 172 errMissingSize = errors.New("missing size") 173 errBadType = errors.New("bad value type") // TODO: better error 174 errOutOfBounds = errors.New("overflow") // TODO: better error 175 errExpectedOption = errors.New("invalid next option after 'X'") 176 errBadAlignment = errors.New("alignment not power of 2") 177 errUnexpectedPackEnd = errors.New("packed string too short: unexpected end") 178 errDoesNotFit = errors.New("does not fit into Lua integer") 179 errStringLongerThanFormat = errors.New("string longer than format spec") 180 errStringDoesNotFit = errors.New("string does not fit") 181 errVariableLength = errors.New("variable-length format") // For packsize only 182 errOverflow = errors.New("invalid format: option size overflow") 183 errStringContainsZeros = errors.New("string contains zeros") 184 185 errBudgetConsumed = errors.New("Packing memory budget consumed") 186 ) 187 188 func errBadFormatString(c byte) error { 189 return fmt.Errorf("invalid format option %q", c) 190 }