github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/lib/utf8lib/utf8lib.go (about) 1 package utf8lib 2 3 import ( 4 "errors" 5 "fmt" 6 "math" 7 "unicode/utf8" 8 9 "github.com/arnodel/golua/lib/packagelib" 10 "github.com/arnodel/golua/luastrings" 11 rt "github.com/arnodel/golua/runtime" 12 ) 13 14 // LibLoader can load the utf8 lib. 15 var LibLoader = packagelib.Loader{ 16 Load: load, 17 Name: "utf8", 18 } 19 20 func load(r *rt.Runtime) (rt.Value, func()) { 21 pkg := rt.NewTable() 22 r.SetEnv(pkg, "charpattern", rt.StringValue("[\x00-\x7F\xC2-\xFD][\x80-\xBF]*")) 23 24 rt.SolemnlyDeclareCompliance( 25 rt.ComplyCpuSafe|rt.ComplyMemSafe|rt.ComplyTimeSafe|rt.ComplyIoSafe, 26 27 r.SetEnvGoFunc(pkg, "char", char, 0, true), 28 r.SetEnvGoFunc(pkg, "codes", codes, 2, false), 29 r.SetEnvGoFunc(pkg, "codepoint", codepoint, 4, false), 30 r.SetEnvGoFunc(pkg, "len", lenf, 4, false), 31 r.SetEnvGoFunc(pkg, "offset", offset, 3, false), 32 ) 33 34 return rt.TableValue(pkg), nil 35 } 36 37 func char(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 38 runes := c.Etc() 39 maxLen := len(runes) * luastrings.UTFMax 40 t.RequireBytes(maxLen) 41 buf := make([]byte, maxLen) 42 cur := buf 43 bufLen := 0 44 t.RequireCPU(uint64(len(runes))) 45 for i, r := range runes { 46 n, ok := rt.ToInt(r) 47 if !ok { 48 return nil, fmt.Errorf("#%d should be an integer", i+1) 49 } 50 if n < 0 || n > math.MaxInt32 { 51 return nil, fmt.Errorf("#%d value out of range", i+1) 52 } 53 sz := luastrings.UTF8EncodeInt32(cur, int32(n)) 54 cur = cur[sz:] 55 bufLen += sz 56 } 57 t.ReleaseBytes(maxLen - bufLen) 58 return c.PushingNext1(t.Runtime, rt.StringValue(string(buf[:bufLen]))), nil 59 } 60 61 func codes(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 62 var ( 63 s string 64 lax bool 65 err error 66 ) 67 if err := c.Check1Arg(); err != nil { 68 return nil, err 69 } 70 s, err = c.StringArg(0) 71 if err != nil { 72 return nil, err 73 } 74 if c.NArgs() >= 2 { 75 lax, err = c.BoolArg(1) 76 if err != nil { 77 return nil, err 78 } 79 } 80 decode := luastrings.GetDecodeRuneInString(lax) 81 var p int64 82 var iterF = func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 83 t.RequireCPU(1) 84 next := c.Next() 85 r, n := decode(s[p:]) 86 if r == utf8.RuneError { 87 switch n { 88 case 0: 89 return next, nil 90 case 1: 91 return nil, errInvalidCode 92 } 93 // If n > 1, then it is a successful decode in lax mode. 94 } 95 t.Push1(next, rt.IntValue(p+1)) 96 t.Push1(next, rt.IntValue(int64(r))) 97 p += int64(n) 98 return next, nil 99 } 100 var iter = rt.NewGoFunction(iterF, "codesiterator", 0, false) 101 iter.SolemnlyDeclareCompliance(rt.ComplyCpuSafe | rt.ComplyMemSafe | rt.ComplyTimeSafe | rt.ComplyIoSafe) 102 return c.PushingNext1(t.Runtime, rt.FunctionValue(iter)), nil 103 } 104 105 func codepoint(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 106 if err := c.Check1Arg(); err != nil { 107 return nil, err 108 } 109 var ii int64 = 1 110 s, err := c.StringArg(0) 111 if err == nil && c.NArgs() >= 2 { 112 ii, err = c.IntArg(1) 113 } 114 jj := ii 115 if err == nil && c.NArgs() >= 3 { 116 jj, err = c.IntArg(2) 117 } 118 lax := false 119 if err == nil && c.NArgs() >= 4 { 120 lax, err = c.BoolArg(3) 121 } 122 if err != nil { 123 return nil, err 124 } 125 decode := luastrings.GetDecodeRuneInString(lax) 126 next := c.Next() 127 i := luastrings.StringNormPos(s, int(ii)) 128 if i < 1 { 129 return nil, errPosOutOfRange 130 } 131 j := luastrings.StringNormPos(s, int(jj)) 132 if j > len(s) { 133 return nil, errPosOutOfRange 134 } 135 for k := i - 1; k < j; { 136 t.RequireCPU(1) 137 r, sz := decode(s[k:]) 138 if r == utf8.RuneError && sz <= 1 { 139 return nil, errInvalidCode 140 } 141 t.Push1(next, rt.IntValue(int64(r))) 142 k += sz 143 } 144 return next, nil 145 } 146 147 func lenf(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 148 if err := c.Check1Arg(); err != nil { 149 return nil, err 150 } 151 var ii int64 = 1 152 s, err := c.StringArg(0) 153 if err == nil && c.NArgs() >= 2 { 154 ii, err = c.IntArg(1) 155 } 156 var jj int64 = -1 157 if err == nil && c.NArgs() >= 3 { 158 jj, err = c.IntArg(2) 159 } 160 var lax = false 161 if err == nil && c.NArgs() >= 4 { 162 lax, err = c.BoolArg(3) 163 } 164 if err != nil { 165 return nil, err 166 } 167 var ( 168 decode = luastrings.GetDecodeRuneInString(lax) 169 next = c.Next() 170 i = luastrings.StringNormPos(s, int(ii)) 171 j = luastrings.StringNormPos(s, int(jj)) 172 slen int64 173 ) 174 if i <= 0 || i > len(s)+1 || j > len(s) { 175 return nil, errPosOutOfRange 176 } 177 for k := i - 1; k < j; { 178 t.RequireCPU(1) 179 r, sz := decode(s[k:]) 180 if r == utf8.RuneError && sz <= 1 { 181 t.Push1(next, rt.NilValue) 182 t.Push1(next, rt.IntValue(int64(k+1))) 183 return next, nil 184 } 185 k += sz 186 slen++ 187 } 188 t.Push1(next, rt.IntValue(slen)) 189 return next, nil 190 } 191 192 func offset(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 193 if err := c.CheckNArgs(2); err != nil { 194 return nil, err 195 } 196 var nn int64 197 ss, err := c.StringArg(0) 198 if err == nil { 199 nn, err = c.IntArg(1) 200 } 201 var ii int64 = 1 202 if nn < 0 { 203 ii = int64(len(ss) + 1) 204 } 205 if err == nil && c.NArgs() >= 3 { 206 ii, err = c.IntArg(2) 207 } 208 if err != nil { 209 return nil, err 210 } 211 i := luastrings.StringNormPos(ss, int(ii)) - 1 212 s := string(ss) 213 if i < 0 || i > len(s) { 214 return nil, errPosOutOfRange 215 } 216 if nn == 0 { 217 // Special case: locate the starting position of the current 218 // code point. 219 for i >= 0 && i < len(s) && !utf8.RuneStart(s[i]) { 220 t.RequireCPU(1) 221 i-- 222 } 223 } else { 224 if i < len(s) && !utf8.RuneStart(s[i]) { 225 return nil, errors.New("initial position is a continuation byte") 226 } 227 if nn > 0 { 228 nn-- 229 // Go forward 230 for nn > 0 { 231 t.RequireCPU(1) 232 i++ 233 if i >= len(s) { 234 nn-- 235 break 236 } 237 if utf8.RuneStart(s[i]) { 238 nn-- 239 } 240 } 241 } else { 242 // Go backward 243 for nn < 0 && i > 0 { 244 t.RequireCPU(1) 245 i-- 246 if utf8.RuneStart(s[i]) { 247 nn++ 248 } 249 } 250 } 251 } 252 if nn == 0 { 253 return c.PushingNext1(t.Runtime, rt.IntValue(int64(i+1))), nil 254 } 255 return c.PushingNext1(t.Runtime, rt.NilValue), nil 256 } 257 258 var errInvalidCode = errors.New("invalid UTF-8 code") 259 var errPosOutOfRange = errors.New("position out of range")