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")