github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/lib/stringlib/stringlib.go (about) 1 package stringlib 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 8 "github.com/arnodel/golua/lib/packagelib" 9 "github.com/arnodel/golua/luastrings" 10 rt "github.com/arnodel/golua/runtime" 11 ) 12 13 // LibLoader specifies how to load the string lib 14 var LibLoader = packagelib.Loader{ 15 Load: load, 16 Name: "string", 17 } 18 19 func load(r *rt.Runtime) (rt.Value, func()) { 20 pkg := rt.NewTable() 21 pkgVal := rt.TableValue(pkg) 22 23 rt.SolemnlyDeclareCompliance( 24 rt.ComplyCpuSafe|rt.ComplyMemSafe|rt.ComplyTimeSafe|rt.ComplyIoSafe, 25 26 r.SetEnvGoFunc(pkg, "byte", bytef, 3, false), 27 r.SetEnvGoFunc(pkg, "char", char, 0, true), 28 r.SetEnvGoFunc(pkg, "dump", dump, 2, false), 29 r.SetEnvGoFunc(pkg, "find", find, 4, false), 30 r.SetEnvGoFunc(pkg, "gmatch", gmatch, 3, false), 31 r.SetEnvGoFunc(pkg, "gsub", gsub, 4, false), 32 r.SetEnvGoFunc(pkg, "len", lenf, 1, false), 33 r.SetEnvGoFunc(pkg, "lower", lower, 1, false), 34 r.SetEnvGoFunc(pkg, "match", match, 3, false), 35 r.SetEnvGoFunc(pkg, "upper", upper, 1, false), 36 r.SetEnvGoFunc(pkg, "rep", rep, 3, false), 37 r.SetEnvGoFunc(pkg, "reverse", reverse, 1, false), 38 r.SetEnvGoFunc(pkg, "sub", sub, 3, false), 39 r.SetEnvGoFunc(pkg, "format", format, 1, true), 40 r.SetEnvGoFunc(pkg, "pack", pack, 1, true), 41 r.SetEnvGoFunc(pkg, "packsize", packsize, 1, false), 42 r.SetEnvGoFunc(pkg, "unpack", unpack, 3, false), 43 ) 44 45 stringMeta := rt.NewTable() 46 r.SetEnv(stringMeta, "__index", pkgVal) 47 48 rt.SolemnlyDeclareCompliance( 49 rt.ComplyCpuSafe|rt.ComplyMemSafe|rt.ComplyTimeSafe|rt.ComplyIoSafe, 50 51 r.SetEnvGoFunc(stringMeta, "__add", string__add, 2, false), 52 r.SetEnvGoFunc(stringMeta, "__sub", string__sub, 2, false), 53 r.SetEnvGoFunc(stringMeta, "__mul", string__mul, 2, false), 54 r.SetEnvGoFunc(stringMeta, "__div", string__div, 2, false), 55 r.SetEnvGoFunc(stringMeta, "__idiv", string__idiv, 2, false), 56 r.SetEnvGoFunc(stringMeta, "__mod", string__mod, 2, false), 57 r.SetEnvGoFunc(stringMeta, "__pow", string__pow, 2, false), 58 r.SetEnvGoFunc(stringMeta, "__unm", string__unm, 1, false), 59 ) 60 r.SetStringMeta(stringMeta) 61 62 return pkgVal, nil 63 } 64 65 func maxpos(i, j int) int { 66 if i > j { 67 return i 68 } 69 return j 70 } 71 72 func minpos(i, j int) int { 73 if i < j { 74 return i 75 } 76 return j 77 } 78 79 func bytef(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 80 if err := c.Check1Arg(); err != nil { 81 return nil, err 82 } 83 s, err := c.StringArg(0) 84 if err != nil { 85 return nil, err 86 } 87 i, j := 1, 1 88 if c.NArgs() >= 2 { 89 ii, err := c.IntArg(1) 90 if err != nil { 91 return nil, err 92 } 93 i = luastrings.StringNormPos(s, int(ii)) 94 j = i 95 } 96 if c.NArgs() >= 3 { 97 jj, err := c.IntArg(2) 98 if err != nil { 99 return nil, err 100 } 101 j = luastrings.StringNormPos(s, int(jj)) 102 } 103 next := c.Next() 104 i = maxpos(1, i) 105 j = minpos(len(s), j) 106 for i <= j { 107 t.Push1(next, rt.IntValue(int64(s[i-1]))) 108 i++ 109 } 110 return next, nil 111 } 112 113 func char(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 114 vals := c.Etc() 115 buf := make([]byte, len(vals)) 116 for i, v := range vals { 117 x, ok := rt.ToInt(v) 118 if !ok { 119 return nil, errors.New("arguments must be integers") 120 } 121 if x < 0 || x > 255 { 122 return nil, fmt.Errorf("#%d out of range", i+1) 123 } 124 buf[i] = byte(x) 125 } 126 t.RequireBytes(len(buf)) 127 return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil 128 } 129 130 func lenf(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 131 if err := c.Check1Arg(); err != nil { 132 return nil, err 133 } 134 s, err := c.StringArg(0) 135 if err != nil { 136 return nil, err 137 } 138 return c.PushingNext1(t.Runtime, rt.IntValue(int64(len(s)))), nil 139 } 140 141 func lower(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 142 if err := c.Check1Arg(); err != nil { 143 return nil, err 144 } 145 s, err := c.StringArg(0) 146 if err != nil { 147 return nil, err 148 } 149 t.RequireBytes(len(s)) 150 s = strings.ToLower(string(s)) 151 return c.PushingNext1(t.Runtime, rt.StringValue(s)), nil 152 } 153 154 func upper(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 155 if err := c.Check1Arg(); err != nil { 156 return nil, err 157 } 158 s, err := c.StringArg(0) 159 if err != nil { 160 return nil, err 161 } 162 t.RequireBytes(len(s)) 163 s = strings.ToUpper(string(s)) 164 return c.PushingNext1(t.Runtime, rt.StringValue(s)), nil 165 } 166 167 func rep(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 168 if err := c.CheckNArgs(2); err != nil { 169 return nil, err 170 } 171 ls, err := c.StringArg(0) 172 if err != nil { 173 return nil, err 174 } 175 ln, err := c.IntArg(1) 176 if err != nil { 177 return nil, err 178 } 179 n := int(ln) 180 if n < 0 { 181 return nil, errors.New("#2 out of range") 182 } 183 var sep []byte 184 if c.NArgs() >= 3 { 185 lsep, err := c.StringArg(2) 186 if err != nil { 187 return nil, err 188 } 189 sep = []byte(lsep) 190 } 191 if n == 0 { 192 return c.PushingNext1(t.Runtime, rt.StringValue("")), nil 193 } 194 if n == 1 { 195 return c.PushingNext1(t.Runtime, rt.StringValue(ls)), nil 196 } 197 if sep == nil { 198 if len(ls)*n/n != len(ls) { 199 // Overflow 200 return nil, errors.New("rep causes overflow") 201 } 202 t.RequireBytes(n * len(ls)) 203 return c.PushingNext1(t.Runtime, rt.StringValue(strings.Repeat(string(ls), n))), nil 204 } 205 s := []byte(ls) 206 builder := strings.Builder{} 207 sz1 := n * len(s) 208 sz2 := (n - 1) * len(sep) 209 sz := sz1 + sz2 210 if sz1/n != len(s) || sz2/(n-1) != len(sep) || sz < 0 { 211 return nil, errors.New("rep causes overflow") 212 } 213 t.RequireBytes(n*len(s) + (n-1)*len(sep)) 214 builder.Grow(sz) 215 builder.Write(s) 216 for { 217 n-- 218 if n == 0 { 219 break 220 } 221 builder.Write(sep) 222 builder.Write(s) 223 } 224 return c.PushingNext1(t.Runtime, rt.StringValue(builder.String())), nil 225 } 226 227 func reverse(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 228 if err := c.Check1Arg(); err != nil { 229 return nil, err 230 } 231 s, err := c.StringArg(0) 232 if err != nil { 233 return nil, err 234 } 235 t.RequireBytes(len(s)) 236 sb := []byte(s) 237 l := len(s) - 1 238 for i := 0; 2*i <= l; i++ { 239 sb[i], sb[l-i] = sb[l-i], sb[i] 240 } 241 return c.PushingNext1(t.Runtime, rt.StringValue(string(sb))), nil 242 } 243 244 func sub(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 245 if err := c.CheckNArgs(2); err != nil { 246 return nil, err 247 } 248 s, err := c.StringArg(0) 249 if err != nil { 250 return nil, err 251 } 252 ii, err := c.IntArg(1) 253 if err != nil { 254 return nil, err 255 } 256 i := luastrings.StringNormPos(s, int(ii)) 257 j := len(s) 258 if c.NArgs() >= 3 { 259 jj, err := c.IntArg(2) 260 if err != nil { 261 return nil, err 262 } 263 j = luastrings.StringNormPos(s, int(jj)) 264 } 265 var slice string 266 i = maxpos(1, i) 267 j = minpos(len(s), j) 268 if i <= len(s) && i <= j { 269 t.RequireBytes(j - i + 1) 270 slice = s[i-1 : j] 271 } 272 return c.PushingNext1(t.Runtime, rt.StringValue(slice)), nil 273 }