github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/lib/base/load.go (about)

     1  package base
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  
     7  	rt "github.com/arnodel/golua/runtime"
     8  )
     9  
    10  const maxChunkNameLen = 59
    11  
    12  func load(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
    13  	if err := c.Check1Arg(); err != nil {
    14  		return nil, err
    15  	}
    16  	var (
    17  		chunk     []byte
    18  		chunkName = "chunk"
    19  		chunkMode = "bt"
    20  		chunkEnv  = rt.TableValue(t.GlobalEnv())
    21  		next      = c.Next()
    22  	)
    23  
    24  	switch nargs := c.NArgs(); {
    25  	case nargs >= 4:
    26  		chunkEnv = c.Arg(3)
    27  		fallthrough
    28  	case nargs >= 3:
    29  		if !c.Arg(2).IsNil() {
    30  			mode, err := c.StringArg(2)
    31  			if err != nil {
    32  				return nil, err
    33  			}
    34  			chunkMode = string(mode)
    35  		}
    36  		fallthrough
    37  	case nargs >= 2:
    38  		if !c.Arg(1).IsNil() {
    39  			name, err := c.StringArg(1)
    40  			if err != nil {
    41  				return nil, err
    42  			}
    43  			chunkName = name
    44  			if len(name) > maxChunkNameLen {
    45  				chunkName = chunkName[:maxChunkNameLen]
    46  			}
    47  		}
    48  		fallthrough
    49  	case nargs >= 1:
    50  		switch x := c.Arg(0); x.Type() {
    51  		case rt.StringType:
    52  			xs := x.AsString()
    53  			// Use CPU as well as memory, but not much
    54  			t.LinearRequire(10, uint64(len(xs)))
    55  			chunk = []byte(xs)
    56  		case rt.FunctionType:
    57  			var buf bytes.Buffer
    58  			for {
    59  				bit, err := rt.Call1(t, x)
    60  				if err != nil {
    61  					t.Push1(next, rt.NilValue)
    62  					t.Push1(next, rt.StringValue(err.Error()))
    63  					t.ReleaseBytes(buf.Len())
    64  					return next, nil
    65  				}
    66  				if bit.IsNil() {
    67  					break
    68  				}
    69  				bitString, ok := bit.TryString()
    70  				if !ok {
    71  					t.Push(next, rt.NilValue, rt.StringValue("reader must return a string"))
    72  					t.ReleaseBytes(buf.Len())
    73  					return next, nil
    74  				}
    75  				if len(bitString) == 0 {
    76  					break
    77  				}
    78  				// When bitString is small, cpu required may be 0 but thats' ok
    79  				// because cpu was used when calling the function.
    80  				t.LinearRequire(10, uint64(len(bitString)))
    81  				buf.WriteString(bitString)
    82  			}
    83  			chunk = buf.Bytes()
    84  		default:
    85  			return nil, errors.New("#1 must be a string or function")
    86  		}
    87  	}
    88  	// The chunk is no longer used once we leave this function.
    89  	defer t.ReleaseBytes(len(chunk))
    90  
    91  	clos, err := t.LoadFromSourceOrCode(chunkName, chunk, chunkMode, chunkEnv, false)
    92  	if err != nil {
    93  		t.Push(next, rt.NilValue, rt.StringValue(err.Error()))
    94  	} else {
    95  		t.Push1(next, rt.FunctionValue(clos))
    96  	}
    97  	return next, nil
    98  }