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 }