use golua directly

This commit is contained in:
siddontang 2014-09-13 14:11:05 +08:00
parent a1096fd1d8
commit 6aec5d4952
20 changed files with 4914 additions and 26 deletions

View File

@ -48,8 +48,8 @@ Create a workspace and checkout ledisdb source
LedisDB use the modified LevelDB for better performance, see [here](https://github.com/siddontang/ledisdb/wiki/leveldb-source-modification).
+ Set ```LEVELDB_DIR``` and ```SNAPPY_DIR``` to the actual install path in dev.sh.
+ ```make```
+ Set `LEVELDB_DIR` and `SNAPPY_DIR` to the actual install path in dev.sh.
+ `make clean && make`
## RocksDB support
@ -57,8 +57,8 @@ Create a workspace and checkout ledisdb source
LedisDB has not supplied a simple script to install, maybe later.
+ Set ```ROCKSDB_DIR``` and ```SNAPPY_DIR``` to the actual install path in `dev.sh`.
+ ```make```
+ Set `ROCKSDB_DIR` and `SNAPPY_DIR` to the actual install path in `dev.sh`.
+ `make clean && make`
**Because RocksDB API may change sometimes, LedisDB may not build successfully. Now LedisDB supports RocksDB version 3.5 or newest master branch. **
@ -70,7 +70,7 @@ Create a workspace and checkout ledisdb source
LedisDB has not supplied a simple script to install, maybe later.
+ Set `HYPERLEVELDB` and `SNAPPY_DIR` to the actual install path in `dev.sh`.
+ `make`
+ `make clean && make`
## Choose store database
@ -91,9 +91,9 @@ Choosing a store database to use is very simple, you have two ways:
## Lua support
+ You must install lua by yourself first and be sure that `golua` can find and link it.
+ `go get -u github.com/siddontang/golua/lua`
+ `make`
+ Compile and install lua
+ Set `LUA_DIR` to the actual path in `dev.sh`
+ `make clean && make`
## Configuration

View File

@ -15,5 +15,3 @@ go get github.com/ugorji/go/codec
go get github.com/BurntSushi/toml
go get github.com/siddontang/go-bson/bson
go get github.com/siddontang/golua/lua

5
dev.sh
View File

@ -14,6 +14,7 @@ SNAPPY_DIR=/usr/local/snappy
LEVELDB_DIR=/usr/local/leveldb
ROCKSDB_DIR=/usr/local/rocksdb
HYPERLEVELDB_DIR=/usr/local/hyperleveldb
LUA_DIR=/usr/local/lua
function add_path()
{
@ -75,9 +76,7 @@ if [ -f $HYPERLEVELDB_DIR/include/hyperleveldb/c.h ]; then
fi
#check lua
CHECK_LUA_FILE="$LEDISTOP/tools/check_lua.go"
go run $CHECK_LUA_FILE >> /dev/null 2>&1
if [ "$?" = 0 ]; then
if [ -f $LUA_DIR/include/lua.h ]; then
GO_BUILD_TAGS="$GO_BUILD_TAGS lua"
fi

422
lua/c-golua.c Normal file
View File

@ -0,0 +1,422 @@
// +build lua
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdint.h>
#include <stdio.h>
#include "_cgo_export.h"
#define MT_GOFUNCTION "GoLua.GoFunction"
#define MT_GOINTERFACE "GoLua.GoInterface"
#define GOLUA_DEFAULT_MSGHANDLER "golua_default_msghandler"
static const char GoStateRegistryKey = 'k'; //golua registry key
static const char PanicFIDRegistryKey = 'k';
/* taken from lua5.2 source */
void *testudata(lua_State *L, int ud, const char *tname)
{
void *p = lua_touserdata(L, ud);
if (p != NULL)
{ /* value is a userdata? */
if (lua_getmetatable(L, ud))
{ /* does it have a metatable? */
luaL_getmetatable(L, tname); /* get correct metatable */
if (!lua_rawequal(L, -1, -2)) /* not the same? */
p = NULL; /* value is a userdata with wrong metatable */
lua_pop(L, 2); /* remove both metatables */
return p;
}
}
return NULL; /* value is not a userdata with a metatable */
}
int clua_isgofunction(lua_State *L, int n)
{
return testudata(L, n, MT_GOFUNCTION) != NULL;
}
int clua_isgostruct(lua_State *L, int n)
{
return testudata(L, n, MT_GOINTERFACE) != NULL;
}
unsigned int* clua_checkgosomething(lua_State* L, int index, const char *desired_metatable)
{
if (desired_metatable != NULL)
{
return testudata(L, index, desired_metatable);
}
else
{
unsigned int *sid = testudata(L, index, MT_GOFUNCTION);
if (sid != NULL) return sid;
return testudata(L, index, MT_GOINTERFACE);
}
}
GoInterface* clua_getgostate(lua_State* L)
{
//get gostate from registry entry
lua_pushlightuserdata(L,(void*)&GoStateRegistryKey);
lua_gettable(L, LUA_REGISTRYINDEX);
GoInterface* gip = lua_touserdata(L,-1);
lua_pop(L,1);
return gip;
}
//wrapper for callgofunction
int callback_function(lua_State* L)
{
int r;
unsigned int *fid = clua_checkgosomething(L, 1, MT_GOFUNCTION);
GoInterface* gi = clua_getgostate(L);
//remove the go function from the stack (to present same behavior as lua_CFunctions)
lua_remove(L,1);
return golua_callgofunction(*gi, fid!=NULL ? *fid : -1);
}
//wrapper for gchook
int gchook_wrapper(lua_State* L)
{
//printf("Garbage collection wrapper\n");
unsigned int* fid = clua_checkgosomething(L, -1, NULL);
GoInterface* gi = clua_getgostate(L);
if (fid != NULL)
return golua_gchook(*gi,*fid);
return 0;
}
unsigned int clua_togofunction(lua_State* L, int index)
{
unsigned int *r = clua_checkgosomething(L, index, MT_GOFUNCTION);
return (r != NULL) ? *r : -1;
}
unsigned int clua_togostruct(lua_State *L, int index)
{
unsigned int *r = clua_checkgosomething(L, index, MT_GOINTERFACE);
return (r != NULL) ? *r : -1;
}
void clua_pushgofunction(lua_State* L, unsigned int fid)
{
unsigned int* fidptr = (unsigned int *)lua_newuserdata(L, sizeof(unsigned int));
*fidptr = fid;
luaL_getmetatable(L, MT_GOFUNCTION);
lua_setmetatable(L, -2);
}
static int callback_c (lua_State* L)
{
int fid = clua_togofunction(L,lua_upvalueindex(1));
GoInterface *gi = clua_getgostate(L);
return golua_callgofunction(*gi,fid);
}
void clua_pushcallback(lua_State* L)
{
lua_pushcclosure(L,callback_c,1);
}
void clua_pushgostruct(lua_State* L, unsigned int iid)
{
unsigned int* iidptr = (unsigned int *)lua_newuserdata(L, sizeof(unsigned int));
*iidptr = iid;
luaL_getmetatable(L, MT_GOINTERFACE);
lua_setmetatable(L,-2);
}
int default_panicf(lua_State *L)
{
const char *s = lua_tostring(L, -1);
printf("Lua unprotected panic: %s\n", s);
abort();
}
void clua_setgostate(lua_State* L, GoInterface gi)
{
lua_atpanic(L, default_panicf);
lua_pushlightuserdata(L,(void*)&GoStateRegistryKey);
GoInterface* gip = (GoInterface*)lua_newuserdata(L,sizeof(GoInterface));
//copy interface value to userdata
gip->v = gi.v;
gip->t = gi.t;
//set into registry table
lua_settable(L, LUA_REGISTRYINDEX);
}
/* called when lua code attempts to access a field of a published go object */
int interface_index_callback(lua_State *L)
{
unsigned int *iid = clua_checkgosomething(L, 1, MT_GOINTERFACE);
if (iid == NULL)
{
lua_pushnil(L);
return 1;
}
char *field_name = (char *)lua_tostring(L, 2);
if (field_name == NULL)
{
lua_pushnil(L);
return 1;
}
GoInterface* gi = clua_getgostate(L);
int r = golua_interface_index_callback(*gi, *iid, field_name);
if (r < 0)
{
lua_error(L);
return 0;
}
else
{
return r;
}
}
/* called when lua code attempts to set a field of a published go object */
int interface_newindex_callback(lua_State *L)
{
unsigned int *iid = clua_checkgosomething(L, 1, MT_GOINTERFACE);
if (iid == NULL)
{
lua_pushnil(L);
return 1;
}
char *field_name = (char *)lua_tostring(L, 2);
if (field_name == NULL)
{
lua_pushnil(L);
return 1;
}
GoInterface* gi = clua_getgostate(L);
int r = golua_interface_newindex_callback(*gi, *iid, field_name);
if (r < 0)
{
lua_error(L);
return 0;
}
else
{
return r;
}
}
int panic_msghandler(lua_State *L)
{
GoInterface* gi = clua_getgostate(L);
go_panic_msghandler(*gi, (char *)lua_tolstring(L, -1, NULL));
return 0;
}
void clua_hide_pcall(lua_State *L)
{
lua_getglobal(L, "pcall");
lua_setglobal(L, "unsafe_pcall");
lua_pushnil(L);
lua_setglobal(L, "pcall");
lua_getglobal(L, "xpcall");
lua_setglobal(L, "unsafe_xpcall");
lua_pushnil(L);
lua_setglobal(L, "xpcall");
}
void clua_initstate(lua_State* L)
{
/* create the GoLua.GoFunction metatable */
luaL_newmetatable(L, MT_GOFUNCTION);
// gofunction_metatable[__call] = &callback_function
lua_pushliteral(L,"__call");
lua_pushcfunction(L,&callback_function);
lua_settable(L,-3);
// gofunction_metatable[__gc] = &gchook_wrapper
lua_pushliteral(L,"__gc");
lua_pushcfunction(L,&gchook_wrapper);
lua_settable(L,-3);
lua_pop(L,1);
luaL_newmetatable(L, MT_GOINTERFACE);
// gointerface_metatable[__gc] = &gchook_wrapper
lua_pushliteral(L, "__gc");
lua_pushcfunction(L, &gchook_wrapper);
lua_settable(L, -3);
// gointerface_metatable[__index] = &interface_index_callback
lua_pushliteral(L, "__index");
lua_pushcfunction(L, &interface_index_callback);
lua_settable(L, -3);
// gointerface_metatable[__newindex] = &interface_newindex_callback
lua_pushliteral(L, "__newindex");
lua_pushcfunction(L, &interface_newindex_callback);
lua_settable(L, -3);
lua_register(L, GOLUA_DEFAULT_MSGHANDLER, &panic_msghandler);
lua_pop(L, 1);
}
int callback_panicf(lua_State* L)
{
lua_pushlightuserdata(L,(void*)&PanicFIDRegistryKey);
lua_gettable(L,LUA_REGISTRYINDEX);
unsigned int fid = lua_tointeger(L,-1);
lua_pop(L,1);
GoInterface* gi = clua_getgostate(L);
return golua_callpanicfunction(*gi,fid);
}
//TODO: currently setting garbage when panicf set to null
GoInterface clua_atpanic(lua_State* L, unsigned int panicf_id)
{
//get old panicfid
unsigned int old_id;
lua_pushlightuserdata(L, (void*)&PanicFIDRegistryKey);
lua_gettable(L,LUA_REGISTRYINDEX);
if(lua_isnil(L, -1) == 0)
old_id = lua_tointeger(L,-1);
lua_pop(L, 1);
//set registry key for function id of go panic function
lua_pushlightuserdata(L, (void*)&PanicFIDRegistryKey);
//push id value
lua_pushinteger(L, panicf_id);
//set into registry table
lua_settable(L, LUA_REGISTRYINDEX);
//now set the panic function
lua_CFunction pf = lua_atpanic(L,&callback_panicf);
//make a GoInterface with a wrapped C panicf or the original go panicf
if(pf == &callback_panicf)
{
return golua_idtointerface(old_id);
}
else
{
//TODO: technically UB, function ptr -> non function ptr
return golua_cfunctiontointerface((GoUintptr *)pf);
}
}
int clua_callluacfunc(lua_State* L, lua_CFunction f)
{
return f(L);
}
void* allocwrapper(void* ud, void *ptr, size_t osize, size_t nsize)
{
return (void*)golua_callallocf((GoUintptr)ud,(GoUintptr)ptr,osize,nsize);
}
lua_State* clua_newstate(void* goallocf)
{
return lua_newstate(&allocwrapper,goallocf);
}
void clua_setallocf(lua_State* L, void* goallocf)
{
lua_setallocf(L,&allocwrapper,goallocf);
}
void clua_openbase(lua_State* L)
{
lua_pushcfunction(L,&luaopen_base);
lua_pushstring(L,"");
lua_call(L, 1, 0);
clua_hide_pcall(L);
}
void clua_openio(lua_State* L)
{
lua_pushcfunction(L,&luaopen_io);
lua_pushstring(L,"io");
lua_call(L, 1, 0);
}
void clua_openmath(lua_State* L)
{
lua_pushcfunction(L,&luaopen_math);
lua_pushstring(L,"math");
lua_call(L, 1, 0);
}
void clua_openpackage(lua_State* L)
{
lua_pushcfunction(L,&luaopen_package);
lua_pushstring(L,"package");
lua_call(L, 1, 0);
}
void clua_openstring(lua_State* L)
{
lua_pushcfunction(L,&luaopen_string);
lua_pushstring(L,"string");
lua_call(L, 1, 0);
}
void clua_opentable(lua_State* L)
{
lua_pushcfunction(L,&luaopen_table);
lua_pushstring(L,"table");
lua_call(L, 1, 0);
}
void clua_openos(lua_State* L)
{
lua_pushcfunction(L,&luaopen_os);
lua_pushstring(L,"os");
lua_call(L, 1, 0);
}
void clua_hook_function(lua_State *L, lua_Debug *ar)
{
lua_checkstack(L, 2);
lua_pushstring(L, "Lua execution quantum exceeded");
lua_error(L);
}
void clua_setexecutionlimit(lua_State* L, int n)
{
lua_sethook(L, &clua_hook_function, LUA_MASKCOUNT, n);
}
LUALIB_API int (luaopen_cjson) (lua_State *L);
LUALIB_API int (luaopen_struct) (lua_State *L);
LUALIB_API int (luaopen_cmsgpack) (lua_State *L);
void clua_opencjson(lua_State* L)
{
lua_pushcfunction(L,&luaopen_cjson);
lua_pushstring(L,"cjson");
lua_call(L, 1, 0);
}
void clua_openstruct(lua_State* L)
{
lua_pushcfunction(L,&luaopen_struct);
lua_pushstring(L,"struct");
lua_call(L, 1, 0);
}
void clua_opencmsgpack(lua_State* L)
{
lua_pushcfunction(L,&luaopen_cmsgpack);
lua_pushstring(L,"cmsgpack");
lua_call(L, 1, 0);
}

222
lua/golua.go Normal file
View File

@ -0,0 +1,222 @@
// +build lua
package lua
/*
#include <lua.h>
#include <lualib.h>
#include <stdlib.h>
*/
import "C"
import (
"reflect"
"unsafe"
)
// Type of allocation functions to use with NewStateAlloc
type Alloc func(ptr unsafe.Pointer, osize uint, nsize uint) unsafe.Pointer
// This is the type of go function that can be registered as lua functions
type LuaGoFunction func(L *State) int
// Wrapper to keep cgo from complaining about incomplete ptr type
//export State
type State struct {
// Wrapped lua_State object
s *C.lua_State
// Registry of go object that have been pushed to Lua VM
registry []interface{}
// Freelist for funcs indices, to allow for freeing
freeIndices []uint
}
//export golua_callgofunction
func golua_callgofunction(L interface{}, fid uint) int {
L1 := L.(*State)
if fid < 0 {
panic(&LuaError{0, "Requested execution of an unknown function", L1.StackTrace()})
}
f := L1.registry[fid].(LuaGoFunction)
return f(L1)
}
//export golua_interface_newindex_callback
func golua_interface_newindex_callback(Li interface{}, iid uint, field_name_cstr *C.char) int {
L := Li.(*State)
iface := L.registry[iid]
ifacevalue := reflect.ValueOf(iface).Elem()
field_name := C.GoString(field_name_cstr)
fval := ifacevalue.FieldByName(field_name)
if fval.Kind() == reflect.Ptr {
fval = fval.Elem()
}
luatype := LuaValType(C.lua_type(L.s, 3))
switch fval.Kind() {
case reflect.Bool:
if luatype == LUA_TBOOLEAN {
fval.SetBool(int(C.lua_toboolean(L.s, 3)) != 0)
return 1
} else {
L.PushString("Wrong assignment to field " + field_name)
return -1
}
case reflect.Int:
fallthrough
case reflect.Int8:
fallthrough
case reflect.Int16:
fallthrough
case reflect.Int32:
fallthrough
case reflect.Int64:
if luatype == LUA_TNUMBER {
fval.SetInt(int64(C.lua_tointeger(L.s, 3)))
return 1
} else {
L.PushString("Wrong assignment to field " + field_name)
return -1
}
case reflect.Uint:
fallthrough
case reflect.Uint8:
fallthrough
case reflect.Uint16:
fallthrough
case reflect.Uint32:
fallthrough
case reflect.Uint64:
if luatype == LUA_TNUMBER {
fval.SetUint(uint64(C.lua_tointeger(L.s, 3)))
return 1
} else {
L.PushString("Wrong assignment to field " + field_name)
return -1
}
case reflect.String:
if luatype == LUA_TSTRING {
fval.SetString(C.GoString(C.lua_tolstring(L.s, 3, nil)))
return 1
} else {
L.PushString("Wrong assignment to field " + field_name)
return -1
}
case reflect.Float32:
fallthrough
case reflect.Float64:
if luatype == LUA_TNUMBER {
fval.SetFloat(float64(C.lua_tonumber(L.s, 3)))
return 1
} else {
L.PushString("Wrong assignment to field " + field_name)
return -1
}
}
L.PushString("Unsupported type of field " + field_name + ": " + fval.Type().String())
return -1
}
//export golua_interface_index_callback
func golua_interface_index_callback(Li interface{}, iid uint, field_name *C.char) int {
L := Li.(*State)
iface := L.registry[iid]
ifacevalue := reflect.ValueOf(iface).Elem()
fval := ifacevalue.FieldByName(C.GoString(field_name))
if fval.Kind() == reflect.Ptr {
fval = fval.Elem()
}
switch fval.Kind() {
case reflect.Bool:
L.PushBoolean(fval.Bool())
return 1
case reflect.Int:
fallthrough
case reflect.Int8:
fallthrough
case reflect.Int16:
fallthrough
case reflect.Int32:
fallthrough
case reflect.Int64:
L.PushInteger(fval.Int())
return 1
case reflect.Uint:
fallthrough
case reflect.Uint8:
fallthrough
case reflect.Uint16:
fallthrough
case reflect.Uint32:
fallthrough
case reflect.Uint64:
L.PushInteger(int64(fval.Uint()))
return 1
case reflect.String:
L.PushString(fval.String())
return 1
case reflect.Float32:
fallthrough
case reflect.Float64:
L.PushNumber(fval.Float())
return 1
}
L.PushString("Unsupported type of field: " + fval.Type().String())
return -1
}
//export golua_gchook
func golua_gchook(L interface{}, id uint) int {
L1 := L.(*State)
L1.unregister(id)
return 0
}
//export golua_callpanicfunction
func golua_callpanicfunction(L interface{}, id uint) int {
L1 := L.(*State)
f := L1.registry[id].(LuaGoFunction)
return f(L1)
}
//export golua_idtointerface
func golua_idtointerface(id uint) interface{} {
return id
}
//export golua_cfunctiontointerface
func golua_cfunctiontointerface(f *uintptr) interface{} {
return f
}
//export golua_callallocf
func golua_callallocf(fp uintptr, ptr uintptr, osize uint, nsize uint) uintptr {
return uintptr((*((*Alloc)(unsafe.Pointer(fp))))(unsafe.Pointer(ptr), osize, nsize))
}
//export go_panic_msghandler
func go_panic_msghandler(Li interface{}, z *C.char) {
L := Li.(*State)
s := C.GoString(z)
panic(&LuaError{LUA_ERRERR, s, L.StackTrace()})
}

39
lua/golua.h Normal file
View File

@ -0,0 +1,39 @@
// +build lua
#include <stdint.h>
typedef struct { void *t; void *v; } GoInterface;
#define GOLUA_DEFAULT_MSGHANDLER "golua_default_msghandler"
/* function to setup metatables, etc */
void clua_initstate(lua_State* L);
void clua_hide_pcall(lua_State *L);
unsigned int clua_togofunction(lua_State* L, int index);
unsigned int clua_togostruct(lua_State *L, int index);
void clua_pushcallback(lua_State* L);
void clua_pushgofunction(lua_State* L, unsigned int fid);
void clua_pushgostruct(lua_State *L, unsigned int fid);
void clua_setgostate(lua_State* L, GoInterface gostate);
GoInterface* clua_getgostate(lua_State* L);
GoInterface clua_atpanic(lua_State* L, unsigned int panicf_id);
int clua_callluacfunc(lua_State* L, lua_CFunction f);
lua_State* clua_newstate(void* goallocf);
void clua_setallocf(lua_State* L, void* goallocf);
void clua_openbase(lua_State* L);
void clua_openio(lua_State* L);
void clua_openmath(lua_State* L);
void clua_openpackage(lua_State* L);
void clua_openstring(lua_State* L);
void clua_opentable(lua_State* L);
void clua_openos(lua_State* L);
void clua_setexecutionlimit(lua_State* L, int n);
int clua_isgofunction(lua_State *L, int n);
int clua_isgostruct(lua_State *L, int n);
void clua_opencjson(lua_State* L);
void clua_openstruct(lua_State* L);
void clua_opencmsgpack(lua_State* L);

21
lua/golua_license Normal file
View File

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2010
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

216
lua/lauxlib.go Normal file
View File

@ -0,0 +1,216 @@
// +build lua
package lua
//#include <lua.h>
//#include <lauxlib.h>
//#include <lualib.h>
//#include <stdlib.h>
//#include "golua.h"
import "C"
import "unsafe"
type LuaError struct {
code int
message string
stackTrace []LuaStackEntry
}
func (err *LuaError) Error() string {
return err.message
}
func (err *LuaError) Code() int {
return err.code
}
func (err *LuaError) StackTrace() []LuaStackEntry {
return err.stackTrace
}
// luaL_argcheck
func (L *State) ArgCheck(cond bool, narg int, extramsg string) {
if cond {
Cextramsg := C.CString(extramsg)
defer C.free(unsafe.Pointer(Cextramsg))
C.luaL_argerror(L.s, C.int(narg), Cextramsg)
}
}
// luaL_argerror
func (L *State) ArgError(narg int, extramsg string) int {
Cextramsg := C.CString(extramsg)
defer C.free(unsafe.Pointer(Cextramsg))
return int(C.luaL_argerror(L.s, C.int(narg), Cextramsg))
}
// luaL_callmeta
func (L *State) CallMeta(obj int, e string) int {
Ce := C.CString(e)
defer C.free(unsafe.Pointer(Ce))
return int(C.luaL_callmeta(L.s, C.int(obj), Ce))
}
// luaL_checkany
func (L *State) CheckAny(narg int) {
C.luaL_checkany(L.s, C.int(narg))
}
// luaL_checkinteger
func (L *State) CheckInteger(narg int) int {
return int(C.luaL_checkinteger(L.s, C.int(narg)))
}
// luaL_checknumber
func (L *State) CheckNumber(narg int) float64 {
return float64(C.luaL_checknumber(L.s, C.int(narg)))
}
// luaL_checkstring
func (L *State) CheckString(narg int) string {
var length C.size_t
return C.GoString(C.luaL_checklstring(L.s, C.int(narg), &length))
}
// luaL_checkoption
//
// BUG(everyone_involved): not implemented
func (L *State) CheckOption(narg int, def string, lst []string) int {
//TODO: complication: lst conversion to const char* lst[] from string slice
return 0
}
// luaL_checktype
func (L *State) CheckType(narg int, t LuaValType) {
C.luaL_checktype(L.s, C.int(narg), C.int(t))
}
// luaL_checkudata
func (L *State) CheckUdata(narg int, tname string) unsafe.Pointer {
Ctname := C.CString(tname)
defer C.free(unsafe.Pointer(Ctname))
return unsafe.Pointer(C.luaL_checkudata(L.s, C.int(narg), Ctname))
}
// Executes file, returns nil for no errors or the lua error string on failure
func (L *State) DoFile(filename string) error {
if r := L.LoadFile(filename); r != 0 {
return &LuaError{r, L.ToString(-1), L.StackTrace()}
}
return L.Call(0, LUA_MULTRET)
}
// Executes the string, returns nil for no errors or the lua error string on failure
func (L *State) DoString(str string) error {
if r := L.LoadString(str); r != 0 {
return &LuaError{r, L.ToString(-1), L.StackTrace()}
}
return L.Call(0, LUA_MULTRET)
}
// Like DoString but panics on error
func (L *State) MustDoString(str string) {
if err := L.DoString(str); err != nil {
panic(err)
}
}
// luaL_getmetafield
func (L *State) GetMetaField(obj int, e string) bool {
Ce := C.CString(e)
defer C.free(unsafe.Pointer(Ce))
return C.luaL_getmetafield(L.s, C.int(obj), Ce) != 0
}
// luaL_getmetatable
func (L *State) LGetMetaTable(tname string) {
Ctname := C.CString(tname)
defer C.free(unsafe.Pointer(Ctname))
C.lua_getfield(L.s, LUA_REGISTRYINDEX, Ctname)
}
// luaL_gsub
func (L *State) GSub(s string, p string, r string) string {
Cs := C.CString(s)
Cp := C.CString(p)
Cr := C.CString(r)
defer func() {
C.free(unsafe.Pointer(Cs))
C.free(unsafe.Pointer(Cp))
C.free(unsafe.Pointer(Cr))
}()
return C.GoString(C.luaL_gsub(L.s, Cs, Cp, Cr))
}
// luaL_loadfile
func (L *State) LoadFile(filename string) int {
Cfilename := C.CString(filename)
defer C.free(unsafe.Pointer(Cfilename))
return int(C.luaL_loadfile(L.s, Cfilename))
}
// luaL_loadstring
func (L *State) LoadString(s string) int {
Cs := C.CString(s)
defer C.free(unsafe.Pointer(Cs))
return int(C.luaL_loadstring(L.s, Cs))
}
// luaL_newmetatable
func (L *State) NewMetaTable(tname string) bool {
Ctname := C.CString(tname)
defer C.free(unsafe.Pointer(Ctname))
return C.luaL_newmetatable(L.s, Ctname) != 0
}
// luaL_newstate
func NewState() *State {
ls := (C.luaL_newstate())
L := newState(ls)
return L
}
// luaL_openlibs
func (L *State) OpenLibs() {
C.luaL_openlibs(L.s)
C.clua_hide_pcall(L.s)
}
// luaL_optinteger
func (L *State) OptInteger(narg int, d int) int {
return int(C.luaL_optinteger(L.s, C.int(narg), C.lua_Integer(d)))
}
// luaL_optnumber
func (L *State) OptNumber(narg int, d float64) float64 {
return float64(C.luaL_optnumber(L.s, C.int(narg), C.lua_Number(d)))
}
// luaL_optstring
func (L *State) OptString(narg int, d string) string {
var length C.size_t
Cd := C.CString(d)
defer C.free(unsafe.Pointer(Cd))
return C.GoString(C.luaL_optlstring(L.s, C.int(narg), Cd, &length))
}
// luaL_ref
func (L *State) Ref(t int) int {
return int(C.luaL_ref(L.s, C.int(t)))
}
// luaL_typename
func (L *State) LTypename(index int) string {
return C.GoString(C.lua_typename(L.s, C.lua_type(L.s, C.int(index))))
}
// luaL_unref
func (L *State) Unref(t int, ref int) {
C.luaL_unref(L.s, C.int(t), C.int(ref))
}
// luaL_where
func (L *State) Where(lvl int) {
C.luaL_where(L.s, C.int(lvl))
}

670
lua/lua.go Normal file
View File

@ -0,0 +1,670 @@
// +build lua
// This package provides access to the excellent lua language interpreter from go code.
//
// Access to most of the functions in lua.h and lauxlib.h is provided as well as additional convenience functions to publish Go objects and functions to lua code.
//
// The documentation of this package is no substitute for the official lua documentation and in many instances methods are described only with the name of their C equivalent
package lua
/*
#cgo LDFLAGS: -llua
#cgo linux LDFLAGS: -lm -ldl
#include <lua.h>
#include <stdlib.h>
#include "golua.h"
*/
import "C"
import "unsafe"
import "fmt"
type LuaStackEntry struct {
Name string
Source string
ShortSource string
CurrentLine int
}
func newState(L *C.lua_State) *State {
var newstatei interface{}
newstate := &State{L, make([]interface{}, 0, 8), make([]uint, 0, 8)}
newstatei = newstate
ns1 := unsafe.Pointer(&newstatei)
ns2 := (*C.GoInterface)(ns1)
C.clua_setgostate(L, *ns2) //hacky....
C.clua_initstate(L)
return newstate
}
func (L *State) addFreeIndex(i uint) {
freelen := len(L.freeIndices)
//reallocate if necessary
if freelen+1 > cap(L.freeIndices) {
newSlice := make([]uint, freelen, cap(L.freeIndices)*2)
copy(newSlice, L.freeIndices)
L.freeIndices = newSlice
}
//reslice
L.freeIndices = L.freeIndices[0 : freelen+1]
L.freeIndices[freelen] = i
}
func (L *State) getFreeIndex() (index uint, ok bool) {
freelen := len(L.freeIndices)
//if there exist entries in the freelist
if freelen > 0 {
i := L.freeIndices[freelen-1] //get index
//fmt.Printf("Free indices before: %v\n", L.freeIndices)
L.freeIndices = L.freeIndices[0 : freelen-1] //'pop' index from list
//fmt.Printf("Free indices after: %v\n", L.freeIndices)
return i, true
}
return 0, false
}
//returns the registered function id
func (L *State) register(f interface{}) uint {
//fmt.Printf("Registering %v\n")
index, ok := L.getFreeIndex()
//fmt.Printf("\tfreeindex: index = %v, ok = %v\n", index, ok)
//if not ok, then we need to add new index by extending the slice
if !ok {
index = uint(len(L.registry))
//reallocate backing array if necessary
if index+1 > uint(cap(L.registry)) {
newSlice := make([]interface{}, index, cap(L.registry)*2)
copy(newSlice, L.registry)
L.registry = newSlice
}
//reslice
L.registry = L.registry[0 : index+1]
}
//fmt.Printf("\tregistering %d %v\n", index, f)
L.registry[index] = f
return index
}
func (L *State) unregister(fid uint) {
//fmt.Printf("Unregistering %d (len: %d, value: %v)\n", fid, len(L.registry), L.registry[fid])
if (fid < uint(len(L.registry))) && (L.registry[fid] != nil) {
L.registry[fid] = nil
L.addFreeIndex(fid)
}
}
// Like lua_pushcfunction pushes onto the stack a go function as user data
func (L *State) PushGoFunction(f LuaGoFunction) {
fid := L.register(f)
C.clua_pushgofunction(L.s, C.uint(fid))
}
// Sets a metamethod to execute a go function
//
// The code:
//
// L.LGetMetaTable(tableName)
// L.SetMetaMethod(methodName, function)
//
// is the logical equivalent of:
//
// L.LGetMetaTable(tableName)
// L.PushGoFunction(function)
// L.SetField(-2, methodName)
//
// except this wouldn't work because pushing a go function results in user data not a cfunction
func (L *State) SetMetaMethod(methodName string, f LuaGoFunction) {
L.PushGoFunction(f) // leaves Go function userdata on stack
C.clua_pushcallback(L.s) // wraps the userdata object with a closure making it into a function
L.SetField(-2, methodName)
}
// Pushes a Go struct onto the stack as user data.
//
// The user data will be rigged so that lua code can access and change to public members of simple types directly
func (L *State) PushGoStruct(iface interface{}) {
iid := L.register(iface)
C.clua_pushgostruct(L.s, C.uint(iid))
}
// Push a pointer onto the stack as user data.
//
// This function doesn't save a reference to the interface, it is the responsibility of the caller of this function to insure that the interface outlasts the lifetime of the lua object that this function creates.
func (L *State) PushLightUserdata(ud *interface{}) {
//push
C.lua_pushlightuserdata(L.s, unsafe.Pointer(ud))
}
// Creates a new user data object of specified size and returns it
func (L *State) NewUserdata(size uintptr) unsafe.Pointer {
return unsafe.Pointer(C.lua_newuserdata(L.s, C.size_t(size)))
}
// Sets the AtPanic function, returns the old one
//
// BUG(everyone_involved): passing nil causes serious problems
func (L *State) AtPanic(panicf LuaGoFunction) (oldpanicf LuaGoFunction) {
fid := uint(0)
if panicf != nil {
fid = L.register(panicf)
}
oldres := interface{}(C.clua_atpanic(L.s, C.uint(fid)))
switch i := oldres.(type) {
case C.uint:
f := L.registry[uint(i)].(LuaGoFunction)
//free registry entry
L.unregister(uint(i))
return f
case C.lua_CFunction:
return func(L1 *State) int {
return int(C.clua_callluacfunc(L1.s, i))
}
}
//generally we only get here if the panicf got set to something like nil
//potentially dangerous because we may silently fail
return nil
}
func (L *State) pcall(nargs, nresults, errfunc int) int {
return int(C.lua_pcall(L.s, C.int(nargs), C.int(nresults), C.int(errfunc)))
}
func (L *State) callEx(nargs, nresults int, catch bool) (err error) {
if catch {
defer func() {
if err2 := recover(); err2 != nil {
if _, ok := err2.(error); ok {
err = err2.(error)
}
return
}
}()
}
L.GetGlobal(C.GOLUA_DEFAULT_MSGHANDLER)
// We must record where we put the error handler in the stack otherwise it will be impossible to remove after the pcall when nresults == LUA_MULTRET
erridx := L.GetTop() - nargs - 1
L.Insert(erridx)
r := L.pcall(nargs, nresults, erridx)
L.Remove(erridx)
if r != 0 {
err = &LuaError{r, L.ToString(-1), L.StackTrace()}
if !catch {
panic(err)
}
}
return
}
// lua_call
func (L *State) Call(nargs, nresults int) (err error) {
return L.callEx(nargs, nresults, true)
}
// Like lua_call but panics on errors
func (L *State) MustCall(nargs, nresults int) {
L.callEx(nargs, nresults, false)
}
// lua_checkstack
func (L *State) CheckStack(extra int) bool {
return C.lua_checkstack(L.s, C.int(extra)) != 0
}
// lua_close
func (L *State) Close() {
C.lua_close(L.s)
}
// lua_concat
func (L *State) Concat(n int) {
C.lua_concat(L.s, C.int(n))
}
// lua_createtable
func (L *State) CreateTable(narr int, nrec int) {
C.lua_createtable(L.s, C.int(narr), C.int(nrec))
}
// lua_equal
func (L *State) Equal(index1, index2 int) bool {
return C.lua_equal(L.s, C.int(index1), C.int(index2)) == 1
}
// lua_gc
func (L *State) GC(what, data int) int { return int(C.lua_gc(L.s, C.int(what), C.int(data))) }
// lua_getfenv
func (L *State) GetfEnv(index int) { C.lua_getfenv(L.s, C.int(index)) }
// lua_getfield
func (L *State) GetField(index int, k string) {
Ck := C.CString(k)
defer C.free(unsafe.Pointer(Ck))
C.lua_getfield(L.s, C.int(index), Ck)
}
// Pushes on the stack the value of a global variable (lua_getglobal)
func (L *State) GetGlobal(name string) { L.GetField(LUA_GLOBALSINDEX, name) }
// lua_getmetatable
func (L *State) GetMetaTable(index int) bool {
return C.lua_getmetatable(L.s, C.int(index)) != 0
}
// lua_gettable
func (L *State) GetTable(index int) { C.lua_gettable(L.s, C.int(index)) }
// lua_gettop
func (L *State) GetTop() int { return int(C.lua_gettop(L.s)) }
// lua_insert
func (L *State) Insert(index int) { C.lua_insert(L.s, C.int(index)) }
// Returns true if lua_type == LUA_TBOOLEAN
func (L *State) IsBoolean(index int) bool {
return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TBOOLEAN
}
// Returns true if the value at index is a LuaGoFunction
func (L *State) IsGoFunction(index int) bool {
return C.clua_isgofunction(L.s, C.int(index)) != 0
}
// Returns true if the value at index is user data pushed with PushGoStruct
func (L *State) IsGoStruct(index int) bool {
return C.clua_isgostruct(L.s, C.int(index)) != 0
}
// Returns true if the value at index is user data pushed with PushGoFunction
func (L *State) IsFunction(index int) bool {
return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TFUNCTION
}
// Returns true if the value at index is light user data
func (L *State) IsLightUserdata(index int) bool {
return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TLIGHTUSERDATA
}
// lua_isnil
func (L *State) IsNil(index int) bool { return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TNIL }
// lua_isnone
func (L *State) IsNone(index int) bool { return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TNONE }
// lua_isnoneornil
func (L *State) IsNoneOrNil(index int) bool { return int(C.lua_type(L.s, C.int(index))) <= 0 }
// lua_isnumber
func (L *State) IsNumber(index int) bool { return C.lua_isnumber(L.s, C.int(index)) == 1 }
// lua_isstring
func (L *State) IsString(index int) bool { return C.lua_isstring(L.s, C.int(index)) == 1 }
// lua_istable
func (L *State) IsTable(index int) bool {
return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TTABLE
}
// lua_isthread
func (L *State) IsThread(index int) bool {
return LuaValType(C.lua_type(L.s, C.int(index))) == LUA_TTHREAD
}
// lua_isuserdata
func (L *State) IsUserdata(index int) bool { return C.lua_isuserdata(L.s, C.int(index)) == 1 }
// lua_lessthan
func (L *State) LessThan(index1, index2 int) bool {
return C.lua_lessthan(L.s, C.int(index1), C.int(index2)) == 1
}
// Creates a new lua interpreter state with the given allocation function
func NewStateAlloc(f Alloc) *State {
ls := C.clua_newstate(unsafe.Pointer(&f))
return newState(ls)
}
// lua_newtable
func (L *State) NewTable() {
C.lua_createtable(L.s, 0, 0)
}
// lua_newthread
func (L *State) NewThread() *State {
//TODO: call newState with result from C.lua_newthread and return it
//TODO: should have same lists as parent
// but may complicate gc
s := C.lua_newthread(L.s)
return &State{s, nil, nil}
}
// lua_next
func (L *State) Next(index int) int {
return int(C.lua_next(L.s, C.int(index)))
}
// lua_objlen
func (L *State) ObjLen(index int) uint {
return uint(C.lua_objlen(L.s, C.int(index)))
}
// lua_pop
func (L *State) Pop(n int) {
//Why is this implemented this way? I don't get it...
//C.lua_pop(L.s, C.int(n));
C.lua_settop(L.s, C.int(-n-1))
}
// lua_pushboolean
func (L *State) PushBoolean(b bool) {
var bint int
if b {
bint = 1
} else {
bint = 0
}
C.lua_pushboolean(L.s, C.int(bint))
}
// lua_pushstring
func (L *State) PushString(str string) {
Cstr := C.CString(str)
defer C.free(unsafe.Pointer(Cstr))
C.lua_pushlstring(L.s, Cstr, C.size_t(len(str)))
}
// lua_pushinteger
func (L *State) PushInteger(n int64) {
C.lua_pushinteger(L.s, C.lua_Integer(n))
}
// lua_pushnil
func (L *State) PushNil() {
C.lua_pushnil(L.s)
}
// lua_pushnumber
func (L *State) PushNumber(n float64) {
C.lua_pushnumber(L.s, C.lua_Number(n))
}
// lua_pushthread
func (L *State) PushThread() (isMain bool) {
return C.lua_pushthread(L.s) != 0
}
// lua_pushvalue
func (L *State) PushValue(index int) {
C.lua_pushvalue(L.s, C.int(index))
}
// lua_rawequal
func (L *State) RawEqual(index1 int, index2 int) bool {
return C.lua_rawequal(L.s, C.int(index1), C.int(index2)) != 0
}
// lua_rawget
func (L *State) RawGet(index int) {
C.lua_rawget(L.s, C.int(index))
}
// lua_rawgeti
func (L *State) RawGeti(index int, n int) {
C.lua_rawgeti(L.s, C.int(index), C.int(n))
}
// lua_rawset
func (L *State) RawSet(index int) {
C.lua_rawset(L.s, C.int(index))
}
// lua_rawseti
func (L *State) RawSeti(index int, n int) {
C.lua_rawseti(L.s, C.int(index), C.int(n))
}
// Registers a Go function as a global variable
func (L *State) Register(name string, f LuaGoFunction) {
L.PushGoFunction(f)
L.SetGlobal(name)
}
// lua_remove
func (L *State) Remove(index int) {
C.lua_remove(L.s, C.int(index))
}
// lua_replace
func (L *State) Replace(index int) {
C.lua_replace(L.s, C.int(index))
}
// lua_resume
func (L *State) Resume(narg int) int {
return int(C.lua_resume(L.s, C.int(narg)))
}
// lua_setallocf
func (L *State) SetAllocf(f Alloc) {
C.clua_setallocf(L.s, unsafe.Pointer(&f))
}
// lua_setfenv
func (L *State) SetfEnv(index int) {
C.lua_setfenv(L.s, C.int(index))
}
// lua_setfield
func (L *State) SetField(index int, k string) {
Ck := C.CString(k)
defer C.free(unsafe.Pointer(Ck))
C.lua_setfield(L.s, C.int(index), Ck)
}
// lua_setglobal
func (L *State) SetGlobal(name string) {
Cname := C.CString(name)
defer C.free(unsafe.Pointer(Cname))
C.lua_setfield(L.s, C.int(LUA_GLOBALSINDEX), Cname)
}
// lua_setmetatable
func (L *State) SetMetaTable(index int) {
C.lua_setmetatable(L.s, C.int(index))
}
// lua_settable
func (L *State) SetTable(index int) {
C.lua_settable(L.s, C.int(index))
}
// lua_settop
func (L *State) SetTop(index int) {
C.lua_settop(L.s, C.int(index))
}
// lua_status
func (L *State) Status() int {
return int(C.lua_status(L.s))
}
// lua_toboolean
func (L *State) ToBoolean(index int) bool {
return C.lua_toboolean(L.s, C.int(index)) != 0
}
// Returns the value at index as a Go function (it must be something pushed with PushGoFunction)
func (L *State) ToGoFunction(index int) (f LuaGoFunction) {
if !L.IsGoFunction(index) {
return nil
}
fid := C.clua_togofunction(L.s, C.int(index))
if fid < 0 {
return nil
}
return L.registry[fid].(LuaGoFunction)
}
// Returns the value at index as a Go Struct (it must be something pushed with PushGoStruct)
func (L *State) ToGoStruct(index int) (f interface{}) {
if !L.IsGoStruct(index) {
return nil
}
fid := C.clua_togostruct(L.s, C.int(index))
if fid < 0 {
return nil
}
return L.registry[fid]
}
// lua_tostring
func (L *State) ToString(index int) string {
var size C.size_t
r := C.lua_tolstring(L.s, C.int(index), &size)
return C.GoStringN(r, C.int(size))
}
// lua_tointeger
func (L *State) ToInteger(index int) int {
return int(C.lua_tointeger(L.s, C.int(index)))
}
// lua_tonumber
func (L *State) ToNumber(index int) float64 {
return float64(C.lua_tonumber(L.s, C.int(index)))
}
// lua_topointer
func (L *State) ToPointer(index int) uintptr {
return uintptr(C.lua_topointer(L.s, C.int(index)))
}
// lua_tothread
func (L *State) ToThread(index int) *State {
//TODO: find a way to link lua_State* to existing *State, return that
return &State{}
}
// lua_touserdata
func (L *State) ToUserdata(index int) unsafe.Pointer {
return unsafe.Pointer(C.lua_touserdata(L.s, C.int(index)))
}
// lua_type
func (L *State) Type(index int) LuaValType {
return LuaValType(C.lua_type(L.s, C.int(index)))
}
// lua_typename
func (L *State) Typename(tp int) string {
return C.GoString(C.lua_typename(L.s, C.int(tp)))
}
// lua_xmove
func XMove(from *State, to *State, n int) {
C.lua_xmove(from.s, to.s, C.int(n))
}
// lua_yield
func (L *State) Yield(nresults int) int {
return int(C.lua_yield(L.s, C.int(nresults)))
}
// Restricted library opens
// Calls luaopen_base
func (L *State) OpenBase() {
C.clua_openbase(L.s)
}
// Calls luaopen_io
func (L *State) OpenIO() {
C.clua_openio(L.s)
}
// Calls luaopen_math
func (L *State) OpenMath() {
C.clua_openmath(L.s)
}
// Calls luaopen_package
func (L *State) OpenPackage() {
C.clua_openpackage(L.s)
}
// Calls luaopen_string
func (L *State) OpenString() {
C.clua_openstring(L.s)
}
// Calls luaopen_table
func (L *State) OpenTable() {
C.clua_opentable(L.s)
}
// Calls luaopen_os
func (L *State) OpenOS() {
C.clua_openos(L.s)
}
// Sets the maximum number of operations to execute at instrNumber, after this the execution ends
func (L *State) SetExecutionLimit(instrNumber int) {
C.clua_setexecutionlimit(L.s, C.int(instrNumber))
}
// Returns the current stack trace
func (L *State) StackTrace() []LuaStackEntry {
r := []LuaStackEntry{}
var d C.lua_Debug
Sln := C.CString("Sln")
defer C.free(unsafe.Pointer(Sln))
for depth := 0; C.lua_getstack(L.s, C.int(depth), &d) > 0; depth++ {
C.lua_getinfo(L.s, Sln, &d)
ssb := make([]byte, C.LUA_IDSIZE)
for i := 0; i < C.LUA_IDSIZE; i++ {
ssb[i] = byte(d.short_src[i])
if ssb[i] == 0 {
ssb = ssb[:i]
break
}
}
ss := string(ssb)
r = append(r, LuaStackEntry{C.GoString(d.name), C.GoString(d.source), ss, int(d.currentline)})
}
return r
}
func (L *State) RaiseError(msg string) {
st := L.StackTrace()
prefix := ""
if len(st) >= 1 {
prefix = fmt.Sprintf("%s:%d: ", st[1].ShortSource, st[1].CurrentLine)
}
panic(&LuaError{0, prefix + msg, st})
}
func (L *State) NewError(msg string) *LuaError {
return &LuaError{0, msg, L.StackTrace()}
}
// Calls luaopen_cjson
func (L *State) OpenCJson() {
C.clua_opencjson(L.s)
}
// Calls luaopen_struct
func (L *State) OpenStruct() {
C.clua_openstruct(L.s)
}
// Calls luaopen_cmsgpack
func (L *State) OpenCMsgpack() {
C.clua_opencmsgpack(L.s)
}

1301
lua/lua_cjson.c Normal file

File diff suppressed because it is too large Load Diff

731
lua/lua_cmsgpack.c Normal file
View File

@ -0,0 +1,731 @@
// +build lua
#include <math.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <lua.h>
#include <lauxlib.h>
#define LUACMSGPACK_VERSION "lua-cmsgpack 0.3.0"
#define LUACMSGPACK_COPYRIGHT "Copyright (C) 2012, Salvatore Sanfilippo"
#define LUACMSGPACK_DESCRIPTION "MessagePack C implementation for Lua"
#define LUACMSGPACK_MAX_NESTING 16 /* Max tables nesting. */
/* ==============================================================================
* MessagePack implementation and bindings for Lua 5.1.
* Copyright(C) 2012 Salvatore Sanfilippo <antirez@gmail.com>
*
* http://github.com/antirez/lua-cmsgpack
*
* For MessagePack specification check the following web site:
* http://wiki.msgpack.org/display/MSGPACK/Format+specification
*
* See Copyright Notice at the end of this file.
*
* CHANGELOG:
* 19-Feb-2012 (ver 0.1.0): Initial release.
* 20-Feb-2012 (ver 0.2.0): Tables encoding improved.
* 20-Feb-2012 (ver 0.2.1): Minor bug fixing.
* 20-Feb-2012 (ver 0.3.0): Module renamed lua-cmsgpack (was lua-msgpack).
* ============================================================================ */
/* --------------------------- Endian conversion --------------------------------
* We use it only for floats and doubles, all the other conversions are performed
* in an endian independent fashion. So the only thing we need is a function
* that swaps a binary string if the arch is little endian (and left it untouched
* otherwise). */
/* Reverse memory bytes if arch is little endian. Given the conceptual
* simplicity of the Lua build system we prefer to check for endianess at runtime.
* The performance difference should be acceptable. */
static void memrevifle(void *ptr, size_t len) {
unsigned char *p = ptr, *e = p+len-1, aux;
int test = 1;
unsigned char *testp = (unsigned char*) &test;
if (testp[0] == 0) return; /* Big endian, nothign to do. */
len /= 2;
while(len--) {
aux = *p;
*p = *e;
*e = aux;
p++;
e--;
}
}
/* ----------------------------- String buffer ----------------------------------
* This is a simple implementation of string buffers. The only opereation
* supported is creating empty buffers and appending bytes to it.
* The string buffer uses 2x preallocation on every realloc for O(N) append
* behavior. */
typedef struct mp_buf {
unsigned char *b;
size_t len, free;
} mp_buf;
static mp_buf *mp_buf_new(void) {
mp_buf *buf = malloc(sizeof(*buf));
buf->b = NULL;
buf->len = buf->free = 0;
return buf;
}
void mp_buf_append(mp_buf *buf, const unsigned char *s, size_t len) {
if (buf->free < len) {
size_t newlen = buf->len+len;
buf->b = realloc(buf->b,newlen*2);
buf->free = newlen;
}
memcpy(buf->b+buf->len,s,len);
buf->len += len;
buf->free -= len;
}
void mp_buf_free(mp_buf *buf) {
free(buf->b);
free(buf);
}
/* ------------------------------ String cursor ----------------------------------
* This simple data structure is used for parsing. Basically you create a cursor
* using a string pointer and a length, then it is possible to access the
* current string position with cursor->p, check the remaining length
* in cursor->left, and finally consume more string using
* mp_cur_consume(cursor,len), to advance 'p' and subtract 'left'.
* An additional field cursor->error is set to zero on initialization and can
* be used to report errors. */
#define MP_CUR_ERROR_NONE 0
#define MP_CUR_ERROR_EOF 1 /* Not enough data to complete the opereation. */
#define MP_CUR_ERROR_BADFMT 2 /* Bad data format */
typedef struct mp_cur {
const unsigned char *p;
size_t left;
int err;
} mp_cur;
static mp_cur *mp_cur_new(const unsigned char *s, size_t len) {
mp_cur *cursor = malloc(sizeof(*cursor));
cursor->p = s;
cursor->left = len;
cursor->err = MP_CUR_ERROR_NONE;
return cursor;
}
static void mp_cur_free(mp_cur *cursor) {
free(cursor);
}
#define mp_cur_consume(_c,_len) do { _c->p += _len; _c->left -= _len; } while(0)
/* When there is not enough room we set an error in the cursor and return, this
* is very common across the code so we have a macro to make the code look
* a bit simpler. */
#define mp_cur_need(_c,_len) do { \
if (_c->left < _len) { \
_c->err = MP_CUR_ERROR_EOF; \
return; \
} \
} while(0)
/* --------------------------- Low level MP encoding -------------------------- */
static void mp_encode_bytes(mp_buf *buf, const unsigned char *s, size_t len) {
unsigned char hdr[5];
int hdrlen;
if (len < 32) {
hdr[0] = 0xa0 | (len&0xff); /* fix raw */
hdrlen = 1;
} else if (len <= 0xffff) {
hdr[0] = 0xda;
hdr[1] = (len&0xff00)>>8;
hdr[2] = len&0xff;
hdrlen = 3;
} else {
hdr[0] = 0xdb;
hdr[1] = (len&0xff000000)>>24;
hdr[2] = (len&0xff0000)>>16;
hdr[3] = (len&0xff00)>>8;
hdr[4] = len&0xff;
hdrlen = 5;
}
mp_buf_append(buf,hdr,hdrlen);
mp_buf_append(buf,s,len);
}
/* we assume IEEE 754 internal format for single and double precision floats. */
static void mp_encode_double(mp_buf *buf, double d) {
unsigned char b[9];
float f = d;
assert(sizeof(f) == 4 && sizeof(d) == 8);
if (d == (double)f) {
b[0] = 0xca; /* float IEEE 754 */
memcpy(b+1,&f,4);
memrevifle(b+1,4);
mp_buf_append(buf,b,5);
} else if (sizeof(d) == 8) {
b[0] = 0xcb; /* double IEEE 754 */
memcpy(b+1,&d,8);
memrevifle(b+1,8);
mp_buf_append(buf,b,9);
}
}
static void mp_encode_int(mp_buf *buf, int64_t n) {
unsigned char b[9];
int enclen;
if (n >= 0) {
if (n <= 127) {
b[0] = n & 0x7f; /* positive fixnum */
enclen = 1;
} else if (n <= 0xff) {
b[0] = 0xcc; /* uint 8 */
b[1] = n & 0xff;
enclen = 2;
} else if (n <= 0xffff) {
b[0] = 0xcd; /* uint 16 */
b[1] = (n & 0xff00) >> 8;
b[2] = n & 0xff;
enclen = 3;
} else if (n <= 0xffffffffLL) {
b[0] = 0xce; /* uint 32 */
b[1] = (n & 0xff000000) >> 24;
b[2] = (n & 0xff0000) >> 16;
b[3] = (n & 0xff00) >> 8;
b[4] = n & 0xff;
enclen = 5;
} else {
b[0] = 0xcf; /* uint 64 */
b[1] = (n & 0xff00000000000000LL) >> 56;
b[2] = (n & 0xff000000000000LL) >> 48;
b[3] = (n & 0xff0000000000LL) >> 40;
b[4] = (n & 0xff00000000LL) >> 32;
b[5] = (n & 0xff000000) >> 24;
b[6] = (n & 0xff0000) >> 16;
b[7] = (n & 0xff00) >> 8;
b[8] = n & 0xff;
enclen = 9;
}
} else {
if (n >= -32) {
b[0] = ((char)n); /* negative fixnum */
enclen = 1;
} else if (n >= -128) {
b[0] = 0xd0; /* int 8 */
b[1] = n & 0xff;
enclen = 2;
} else if (n >= -32768) {
b[0] = 0xd1; /* int 16 */
b[1] = (n & 0xff00) >> 8;
b[2] = n & 0xff;
enclen = 3;
} else if (n >= -2147483648LL) {
b[0] = 0xd2; /* int 32 */
b[1] = (n & 0xff000000) >> 24;
b[2] = (n & 0xff0000) >> 16;
b[3] = (n & 0xff00) >> 8;
b[4] = n & 0xff;
enclen = 5;
} else {
b[0] = 0xd3; /* int 64 */
b[1] = (n & 0xff00000000000000LL) >> 56;
b[2] = (n & 0xff000000000000LL) >> 48;
b[3] = (n & 0xff0000000000LL) >> 40;
b[4] = (n & 0xff00000000LL) >> 32;
b[5] = (n & 0xff000000) >> 24;
b[6] = (n & 0xff0000) >> 16;
b[7] = (n & 0xff00) >> 8;
b[8] = n & 0xff;
enclen = 9;
}
}
mp_buf_append(buf,b,enclen);
}
static void mp_encode_array(mp_buf *buf, int64_t n) {
unsigned char b[5];
int enclen;
if (n <= 15) {
b[0] = 0x90 | (n & 0xf); /* fix array */
enclen = 1;
} else if (n <= 65535) {
b[0] = 0xdc; /* array 16 */
b[1] = (n & 0xff00) >> 8;
b[2] = n & 0xff;
enclen = 3;
} else {
b[0] = 0xdd; /* array 32 */
b[1] = (n & 0xff000000) >> 24;
b[2] = (n & 0xff0000) >> 16;
b[3] = (n & 0xff00) >> 8;
b[4] = n & 0xff;
enclen = 5;
}
mp_buf_append(buf,b,enclen);
}
static void mp_encode_map(mp_buf *buf, int64_t n) {
unsigned char b[5];
int enclen;
if (n <= 15) {
b[0] = 0x80 | (n & 0xf); /* fix map */
enclen = 1;
} else if (n <= 65535) {
b[0] = 0xde; /* map 16 */
b[1] = (n & 0xff00) >> 8;
b[2] = n & 0xff;
enclen = 3;
} else {
b[0] = 0xdf; /* map 32 */
b[1] = (n & 0xff000000) >> 24;
b[2] = (n & 0xff0000) >> 16;
b[3] = (n & 0xff00) >> 8;
b[4] = n & 0xff;
enclen = 5;
}
mp_buf_append(buf,b,enclen);
}
/* ----------------------------- Lua types encoding --------------------------- */
static void mp_encode_lua_string(lua_State *L, mp_buf *buf) {
size_t len;
const char *s;
s = lua_tolstring(L,-1,&len);
mp_encode_bytes(buf,(const unsigned char*)s,len);
}
static void mp_encode_lua_bool(lua_State *L, mp_buf *buf) {
unsigned char b = lua_toboolean(L,-1) ? 0xc3 : 0xc2;
mp_buf_append(buf,&b,1);
}
static void mp_encode_lua_number(lua_State *L, mp_buf *buf) {
lua_Number n = lua_tonumber(L,-1);
if (floor(n) != n) {
mp_encode_double(buf,(double)n);
} else {
mp_encode_int(buf,(int64_t)n);
}
}
static void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level);
/* Convert a lua table into a message pack list. */
static void mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) {
size_t len = lua_objlen(L,-1), j;
mp_encode_array(buf,len);
for (j = 1; j <= len; j++) {
lua_pushnumber(L,j);
lua_gettable(L,-2);
mp_encode_lua_type(L,buf,level+1);
}
}
/* Convert a lua table into a message pack key-value map. */
static void mp_encode_lua_table_as_map(lua_State *L, mp_buf *buf, int level) {
size_t len = 0;
/* First step: count keys into table. No other way to do it with the
* Lua API, we need to iterate a first time. Note that an alternative
* would be to do a single run, and then hack the buffer to insert the
* map opcodes for message pack. Too hachish for this lib. */
lua_pushnil(L);
while(lua_next(L,-2)) {
lua_pop(L,1); /* remove value, keep key for next iteration. */
len++;
}
/* Step two: actually encoding of the map. */
mp_encode_map(buf,len);
lua_pushnil(L);
while(lua_next(L,-2)) {
/* Stack: ... key value */
lua_pushvalue(L,-2); /* Stack: ... key value key */
mp_encode_lua_type(L,buf,level+1); /* encode key */
mp_encode_lua_type(L,buf,level+1); /* encode val */
}
}
/* Returns true if the Lua table on top of the stack is exclusively composed
* of keys from numerical keys from 1 up to N, with N being the total number
* of elements, without any hole in the middle. */
static int table_is_an_array(lua_State *L) {
long count = 0, max = 0, idx = 0;
lua_Number n;
lua_pushnil(L);
while(lua_next(L,-2)) {
/* Stack: ... key value */
lua_pop(L,1); /* Stack: ... key */
if (lua_type(L,-1) != LUA_TNUMBER) goto not_array;
n = lua_tonumber(L,-1);
idx = n;
if (idx != n || idx < 1) goto not_array;
count++;
max = idx;
}
/* We have the total number of elements in "count". Also we have
* the max index encountered in "idx". We can't reach this code
* if there are indexes <= 0. If you also note that there can not be
* repeated keys into a table, you have that if idx==count you are sure
* that there are all the keys form 1 to count (both included). */
return idx == count;
not_array:
lua_pop(L,1);
return 0;
}
/* If the length operator returns non-zero, that is, there is at least
* an object at key '1', we serialize to message pack list. Otherwise
* we use a map. */
static void mp_encode_lua_table(lua_State *L, mp_buf *buf, int level) {
if (table_is_an_array(L))
mp_encode_lua_table_as_array(L,buf,level);
else
mp_encode_lua_table_as_map(L,buf,level);
}
static void mp_encode_lua_null(lua_State *L, mp_buf *buf) {
unsigned char b[1];
b[0] = 0xc0;
mp_buf_append(buf,b,1);
}
static void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level) {
int t = lua_type(L,-1);
/* Limit the encoding of nested tables to a specfiied maximum depth, so that
* we survive when called against circular references in tables. */
if (t == LUA_TTABLE && level == LUACMSGPACK_MAX_NESTING) t = LUA_TNIL;
switch(t) {
case LUA_TSTRING: mp_encode_lua_string(L,buf); break;
case LUA_TBOOLEAN: mp_encode_lua_bool(L,buf); break;
case LUA_TNUMBER: mp_encode_lua_number(L,buf); break;
case LUA_TTABLE: mp_encode_lua_table(L,buf,level); break;
default: mp_encode_lua_null(L,buf); break;
}
lua_pop(L,1);
}
static int mp_pack(lua_State *L) {
mp_buf *buf = mp_buf_new();
mp_encode_lua_type(L,buf,0);
lua_pushlstring(L,(char*)buf->b,buf->len);
mp_buf_free(buf);
return 1;
}
/* --------------------------------- Decoding --------------------------------- */
void mp_decode_to_lua_type(lua_State *L, mp_cur *c);
void mp_decode_to_lua_array(lua_State *L, mp_cur *c, size_t len) {
int index = 1;
lua_newtable(L);
while(len--) {
lua_pushnumber(L,index++);
mp_decode_to_lua_type(L,c);
if (c->err) return;
lua_settable(L,-3);
}
}
void mp_decode_to_lua_hash(lua_State *L, mp_cur *c, size_t len) {
lua_newtable(L);
while(len--) {
mp_decode_to_lua_type(L,c); /* key */
if (c->err) return;
mp_decode_to_lua_type(L,c); /* value */
if (c->err) return;
lua_settable(L,-3);
}
}
/* Decode a Message Pack raw object pointed by the string cursor 'c' to
* a Lua type, that is left as the only result on the stack. */
void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
mp_cur_need(c,1);
switch(c->p[0]) {
case 0xcc: /* uint 8 */
mp_cur_need(c,2);
lua_pushnumber(L,c->p[1]);
mp_cur_consume(c,2);
break;
case 0xd0: /* int 8 */
mp_cur_need(c,2);
lua_pushnumber(L,(char)c->p[1]);
mp_cur_consume(c,2);
break;
case 0xcd: /* uint 16 */
mp_cur_need(c,3);
lua_pushnumber(L,
(c->p[1] << 8) |
c->p[2]);
mp_cur_consume(c,3);
break;
case 0xd1: /* int 16 */
mp_cur_need(c,3);
lua_pushnumber(L,(int16_t)
(c->p[1] << 8) |
c->p[2]);
mp_cur_consume(c,3);
break;
case 0xce: /* uint 32 */
mp_cur_need(c,5);
lua_pushnumber(L,
((uint32_t)c->p[1] << 24) |
((uint32_t)c->p[2] << 16) |
((uint32_t)c->p[3] << 8) |
(uint32_t)c->p[4]);
mp_cur_consume(c,5);
break;
case 0xd2: /* int 32 */
mp_cur_need(c,5);
lua_pushnumber(L,
((int32_t)c->p[1] << 24) |
((int32_t)c->p[2] << 16) |
((int32_t)c->p[3] << 8) |
(int32_t)c->p[4]);
mp_cur_consume(c,5);
break;
case 0xcf: /* uint 64 */
mp_cur_need(c,9);
lua_pushnumber(L,
((uint64_t)c->p[1] << 56) |
((uint64_t)c->p[2] << 48) |
((uint64_t)c->p[3] << 40) |
((uint64_t)c->p[4] << 32) |
((uint64_t)c->p[5] << 24) |
((uint64_t)c->p[6] << 16) |
((uint64_t)c->p[7] << 8) |
(uint64_t)c->p[8]);
mp_cur_consume(c,9);
break;
case 0xd3: /* int 64 */
mp_cur_need(c,9);
lua_pushnumber(L,
((int64_t)c->p[1] << 56) |
((int64_t)c->p[2] << 48) |
((int64_t)c->p[3] << 40) |
((int64_t)c->p[4] << 32) |
((int64_t)c->p[5] << 24) |
((int64_t)c->p[6] << 16) |
((int64_t)c->p[7] << 8) |
(int64_t)c->p[8]);
mp_cur_consume(c,9);
break;
case 0xc0: /* nil */
lua_pushnil(L);
mp_cur_consume(c,1);
break;
case 0xc3: /* true */
lua_pushboolean(L,1);
mp_cur_consume(c,1);
break;
case 0xc2: /* false */
lua_pushboolean(L,0);
mp_cur_consume(c,1);
break;
case 0xca: /* float */
mp_cur_need(c,5);
assert(sizeof(float) == 4);
{
float f;
memcpy(&f,c->p+1,4);
memrevifle(&f,4);
lua_pushnumber(L,f);
mp_cur_consume(c,5);
}
break;
case 0xcb: /* double */
mp_cur_need(c,9);
assert(sizeof(double) == 8);
{
double d;
memcpy(&d,c->p+1,8);
memrevifle(&d,8);
lua_pushnumber(L,d);
mp_cur_consume(c,9);
}
break;
case 0xda: /* raw 16 */
mp_cur_need(c,3);
{
size_t l = (c->p[1] << 8) | c->p[2];
mp_cur_need(c,3+l);
lua_pushlstring(L,(char*)c->p+3,l);
mp_cur_consume(c,3+l);
}
break;
case 0xdb: /* raw 32 */
mp_cur_need(c,5);
{
size_t l = (c->p[1] << 24) |
(c->p[2] << 16) |
(c->p[3] << 8) |
c->p[4];
mp_cur_need(c,5+l);
lua_pushlstring(L,(char*)c->p+5,l);
mp_cur_consume(c,5+l);
}
break;
case 0xdc: /* array 16 */
mp_cur_need(c,3);
{
size_t l = (c->p[1] << 8) | c->p[2];
mp_cur_consume(c,3);
mp_decode_to_lua_array(L,c,l);
}
break;
case 0xdd: /* array 32 */
mp_cur_need(c,5);
{
size_t l = (c->p[1] << 24) |
(c->p[2] << 16) |
(c->p[3] << 8) |
c->p[4];
mp_cur_consume(c,5);
mp_decode_to_lua_array(L,c,l);
}
break;
case 0xde: /* map 16 */
mp_cur_need(c,3);
{
size_t l = (c->p[1] << 8) | c->p[2];
mp_cur_consume(c,3);
mp_decode_to_lua_hash(L,c,l);
}
break;
case 0xdf: /* map 32 */
mp_cur_need(c,5);
{
size_t l = (c->p[1] << 24) |
(c->p[2] << 16) |
(c->p[3] << 8) |
c->p[4];
mp_cur_consume(c,5);
mp_decode_to_lua_hash(L,c,l);
}
break;
default: /* types that can't be idenitified by first byte value. */
if ((c->p[0] & 0x80) == 0) { /* positive fixnum */
lua_pushnumber(L,c->p[0]);
mp_cur_consume(c,1);
} else if ((c->p[0] & 0xe0) == 0xe0) { /* negative fixnum */
lua_pushnumber(L,(signed char)c->p[0]);
mp_cur_consume(c,1);
} else if ((c->p[0] & 0xe0) == 0xa0) { /* fix raw */
size_t l = c->p[0] & 0x1f;
mp_cur_need(c,1+l);
lua_pushlstring(L,(char*)c->p+1,l);
mp_cur_consume(c,1+l);
} else if ((c->p[0] & 0xf0) == 0x90) { /* fix map */
size_t l = c->p[0] & 0xf;
mp_cur_consume(c,1);
mp_decode_to_lua_array(L,c,l);
} else if ((c->p[0] & 0xf0) == 0x80) { /* fix map */
size_t l = c->p[0] & 0xf;
mp_cur_consume(c,1);
mp_decode_to_lua_hash(L,c,l);
} else {
c->err = MP_CUR_ERROR_BADFMT;
}
}
}
static int mp_unpack(lua_State *L) {
size_t len;
const unsigned char *s;
mp_cur *c;
if (!lua_isstring(L,-1)) {
lua_pushstring(L,"MessagePack decoding needs a string as input.");
lua_error(L);
}
s = (const unsigned char*) lua_tolstring(L,-1,&len);
c = mp_cur_new(s,len);
mp_decode_to_lua_type(L,c);
if (c->err == MP_CUR_ERROR_EOF) {
mp_cur_free(c);
lua_pushstring(L,"Missing bytes in input.");
lua_error(L);
} else if (c->err == MP_CUR_ERROR_BADFMT) {
mp_cur_free(c);
lua_pushstring(L,"Bad data format in input.");
lua_error(L);
} else if (c->left != 0) {
mp_cur_free(c);
lua_pushstring(L,"Extra bytes in input.");
lua_error(L);
}
mp_cur_free(c);
return 1;
}
/* ---------------------------------------------------------------------------- */
static const struct luaL_reg thislib[] = {
{"pack", mp_pack},
{"unpack", mp_unpack},
{NULL, NULL}
};
LUALIB_API int luaopen_cmsgpack (lua_State *L) {
luaL_register(L, "cmsgpack", thislib);
lua_pushliteral(L, LUACMSGPACK_VERSION);
lua_setfield(L, -2, "_VERSION");
lua_pushliteral(L, LUACMSGPACK_COPYRIGHT);
lua_setfield(L, -2, "_COPYRIGHT");
lua_pushliteral(L, LUACMSGPACK_DESCRIPTION);
lua_setfield(L, -2, "_DESCRIPTION");
return 1;
}
/******************************************************************************
* Copyright (C) 2012 Salvatore Sanfilippo. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/

73
lua/lua_defs.go Normal file
View File

@ -0,0 +1,73 @@
// +build lua
package lua
/*
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
*/
import "C"
type LuaValType int
const (
LUA_TNIL = LuaValType(C.LUA_TNIL)
LUA_TNUMBER = LuaValType(C.LUA_TNUMBER)
LUA_TBOOLEAN = LuaValType(C.LUA_TBOOLEAN)
LUA_TSTRING = LuaValType(C.LUA_TSTRING)
LUA_TTABLE = LuaValType(C.LUA_TTABLE)
LUA_TFUNCTION = LuaValType(C.LUA_TFUNCTION)
LUA_TUSERDATA = LuaValType(C.LUA_TUSERDATA)
LUA_TTHREAD = LuaValType(C.LUA_TTHREAD)
LUA_TLIGHTUSERDATA = LuaValType(C.LUA_TLIGHTUSERDATA)
)
const (
LUA_VERSION = C.LUA_VERSION
LUA_RELEASE = C.LUA_RELEASE
LUA_VERSION_NUM = C.LUA_VERSION_NUM
LUA_COPYRIGHT = C.LUA_COPYRIGHT
LUA_AUTHORS = C.LUA_AUTHORS
LUA_MULTRET = C.LUA_MULTRET
LUA_REGISTRYINDEX = C.LUA_REGISTRYINDEX
LUA_ENVIRONINDEX = C.LUA_ENVIRONINDEX
LUA_GLOBALSINDEX = C.LUA_GLOBALSINDEX
LUA_YIELD = C.LUA_YIELD
LUA_ERRRUN = C.LUA_ERRRUN
LUA_ERRSYNTAX = C.LUA_ERRSYNTAX
LUA_ERRMEM = C.LUA_ERRMEM
LUA_ERRERR = C.LUA_ERRERR
LUA_TNONE = C.LUA_TNONE
LUA_MINSTACK = C.LUA_MINSTACK
LUA_GCSTOP = C.LUA_GCSTOP
LUA_GCRESTART = C.LUA_GCRESTART
LUA_GCCOLLECT = C.LUA_GCCOLLECT
LUA_GCCOUNT = C.LUA_GCCOUNT
LUA_GCCOUNTB = C.LUA_GCCOUNTB
LUA_GCSTEP = C.LUA_GCSTEP
LUA_GCSETPAUSE = C.LUA_GCSETPAUSE
LUA_GCSETSTEPMUL = C.LUA_GCSETSTEPMUL
LUA_HOOKCALL = C.LUA_HOOKCALL
LUA_HOOKRET = C.LUA_HOOKRET
LUA_HOOKLINE = C.LUA_HOOKLINE
LUA_HOOKCOUNT = C.LUA_HOOKCOUNT
LUA_HOOKTAILRET = C.LUA_HOOKTAILRET
LUA_MASKCALL = C.LUA_MASKCALL
LUA_MASKRET = C.LUA_MASKRET
LUA_MASKLINE = C.LUA_MASKLINE
LUA_MASKCOUNT = C.LUA_MASKCOUNT
LUA_ERRFILE = C.LUA_ERRFILE
LUA_NOREF = C.LUA_NOREF
LUA_REFNIL = C.LUA_REFNIL
LUA_FILEHANDLE = C.LUA_FILEHANDLE
LUA_COLIBNAME = C.LUA_COLIBNAME
LUA_TABLIBNAME = C.LUA_TABLIBNAME
LUA_IOLIBNAME = C.LUA_IOLIBNAME
LUA_OSLIBNAME = C.LUA_OSLIBNAME
LUA_STRLIBNAME = C.LUA_STRLIBNAME
LUA_MATHLIBNAME = C.LUA_MATHLIBNAME
LUA_DBLIBNAME = C.LUA_DBLIBNAME
LUA_LOADLIBNAME = C.LUA_LOADLIBNAME
)

423
lua/lua_struct.c Normal file
View File

@ -0,0 +1,423 @@
// +build lua
/*
** {======================================================
** Library for packing/unpacking structures.
** $Id: struct.c,v 1.4 2012/07/04 18:54:29 roberto Exp $
** See Copyright Notice at the end of this file
** =======================================================
*/
/*
** Valid formats:
** > - big endian
** < - little endian
** ![num] - alignment
** x - pading
** b/B - signed/unsigned byte
** h/H - signed/unsigned short
** l/L - signed/unsigned long
** T - size_t
** i/In - signed/unsigned integer with size `n' (default is size of int)
** cn - sequence of `n' chars (from/to a string); when packing, n==0 means
the whole string; when unpacking, n==0 means use the previous
read number as the string length
** s - zero-terminated string
** f - float
** d - double
** ' ' - ignored
*/
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#if (LUA_VERSION_NUM >= 502)
#define luaL_register(L,n,f) luaL_newlib(L,f)
#endif
/* basic integer type */
#if !defined(STRUCT_INT)
#define STRUCT_INT long
#endif
typedef STRUCT_INT Inttype;
/* corresponding unsigned version */
typedef unsigned STRUCT_INT Uinttype;
/* maximum size (in bytes) for integral types */
#define MAXINTSIZE 32
/* is 'x' a power of 2? */
#define isp2(x) ((x) > 0 && ((x) & ((x) - 1)) == 0)
/* dummy structure to get alignment requirements */
struct cD {
char c;
double d;
};
#define PADDING (sizeof(struct cD) - sizeof(double))
#define MAXALIGN (PADDING > sizeof(int) ? PADDING : sizeof(int))
/* endian options */
#define BIG 0
#define LITTLE 1
static union {
int dummy;
char endian;
} const native = {1};
typedef struct Header {
int endian;
int align;
} Header;
static int getnum (const char **fmt, int df) {
if (!isdigit(**fmt)) /* no number? */
return df; /* return default value */
else {
int a = 0;
do {
a = a*10 + *((*fmt)++) - '0';
} while (isdigit(**fmt));
return a;
}
}
#define defaultoptions(h) ((h)->endian = native.endian, (h)->align = 1)
static size_t optsize (lua_State *L, char opt, const char **fmt) {
switch (opt) {
case 'B': case 'b': return sizeof(char);
case 'H': case 'h': return sizeof(short);
case 'L': case 'l': return sizeof(long);
case 'T': return sizeof(size_t);
case 'f': return sizeof(float);
case 'd': return sizeof(double);
case 'x': return 1;
case 'c': return getnum(fmt, 1);
case 'i': case 'I': {
int sz = getnum(fmt, sizeof(int));
if (sz > MAXINTSIZE)
luaL_error(L, "integral size %d is larger than limit of %d",
sz, MAXINTSIZE);
return sz;
}
default: return 0; /* other cases do not need alignment */
}
}
/*
** return number of bytes needed to align an element of size 'size'
** at current position 'len'
*/
static int gettoalign (size_t len, Header *h, int opt, size_t size) {
if (size == 0 || opt == 'c') return 0;
if (size > (size_t)h->align)
size = h->align; /* respect max. alignment */
return (size - (len & (size - 1))) & (size - 1);
}
/*
** options to control endianess and alignment
*/
static void controloptions (lua_State *L, int opt, const char **fmt,
Header *h) {
switch (opt) {
case ' ': return; /* ignore white spaces */
case '>': h->endian = BIG; return;
case '<': h->endian = LITTLE; return;
case '!': {
int a = getnum(fmt, MAXALIGN);
if (!isp2(a))
luaL_error(L, "alignment %d is not a power of 2", a);
h->align = a;
return;
}
default: {
const char *msg = lua_pushfstring(L, "invalid format option '%c'", opt);
luaL_argerror(L, 1, msg);
}
}
}
static void putinteger (lua_State *L, luaL_Buffer *b, int arg, int endian,
int size) {
lua_Number n = luaL_checknumber(L, arg);
Uinttype value;
char buff[MAXINTSIZE];
if (n < 0)
value = (Uinttype)(Inttype)n;
else
value = (Uinttype)n;
if (endian == LITTLE) {
int i;
for (i = 0; i < size; i++) {
buff[i] = (value & 0xff);
value >>= 8;
}
}
else {
int i;
for (i = size - 1; i >= 0; i--) {
buff[i] = (value & 0xff);
value >>= 8;
}
}
luaL_addlstring(b, buff, size);
}
static void correctbytes (char *b, int size, int endian) {
if (endian != native.endian) {
int i = 0;
while (i < --size) {
char temp = b[i];
b[i++] = b[size];
b[size] = temp;
}
}
}
static int b_pack (lua_State *L) {
luaL_Buffer b;
const char *fmt = luaL_checkstring(L, 1);
Header h;
int arg = 2;
size_t totalsize = 0;
defaultoptions(&h);
lua_pushnil(L); /* mark to separate arguments from string buffer */
luaL_buffinit(L, &b);
while (*fmt != '\0') {
int opt = *fmt++;
size_t size = optsize(L, opt, &fmt);
int toalign = gettoalign(totalsize, &h, opt, size);
totalsize += toalign;
while (toalign-- > 0) luaL_addchar(&b, '\0');
switch (opt) {
case 'b': case 'B': case 'h': case 'H':
case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */
putinteger(L, &b, arg++, h.endian, size);
break;
}
case 'x': {
luaL_addchar(&b, '\0');
break;
}
case 'f': {
float f = (float)luaL_checknumber(L, arg++);
correctbytes((char *)&f, size, h.endian);
luaL_addlstring(&b, (char *)&f, size);
break;
}
case 'd': {
double d = luaL_checknumber(L, arg++);
correctbytes((char *)&d, size, h.endian);
luaL_addlstring(&b, (char *)&d, size);
break;
}
case 'c': case 's': {
size_t l;
const char *s = luaL_checklstring(L, arg++, &l);
if (size == 0) size = l;
luaL_argcheck(L, l >= (size_t)size, arg, "string too short");
luaL_addlstring(&b, s, size);
if (opt == 's') {
luaL_addchar(&b, '\0'); /* add zero at the end */
size++;
}
break;
}
default: controloptions(L, opt, &fmt, &h);
}
totalsize += size;
}
luaL_pushresult(&b);
return 1;
}
static lua_Number getinteger (const char *buff, int endian,
int issigned, int size) {
Uinttype l = 0;
int i;
if (endian == BIG) {
for (i = 0; i < size; i++) {
l <<= 8;
l |= (Uinttype)(unsigned char)buff[i];
}
}
else {
for (i = size - 1; i >= 0; i--) {
l <<= 8;
l |= (Uinttype)(unsigned char)buff[i];
}
}
if (!issigned)
return (lua_Number)l;
else { /* signed format */
Uinttype mask = (Uinttype)(~((Uinttype)0)) << (size*8 - 1);
if (l & mask) /* negative value? */
l |= mask; /* signal extension */
return (lua_Number)(Inttype)l;
}
}
static int b_unpack (lua_State *L) {
Header h;
const char *fmt = luaL_checkstring(L, 1);
size_t ld;
const char *data = luaL_checklstring(L, 2, &ld);
size_t pos = luaL_optinteger(L, 3, 1) - 1;
defaultoptions(&h);
lua_settop(L, 2);
while (*fmt) {
int opt = *fmt++;
size_t size = optsize(L, opt, &fmt);
pos += gettoalign(pos, &h, opt, size);
luaL_argcheck(L, pos+size <= ld, 2, "data string too short");
luaL_checkstack(L, 1, "too many results");
switch (opt) {
case 'b': case 'B': case 'h': case 'H':
case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */
int issigned = islower(opt);
lua_Number res = getinteger(data+pos, h.endian, issigned, size);
lua_pushnumber(L, res);
break;
}
case 'x': {
break;
}
case 'f': {
float f;
memcpy(&f, data+pos, size);
correctbytes((char *)&f, sizeof(f), h.endian);
lua_pushnumber(L, f);
break;
}
case 'd': {
double d;
memcpy(&d, data+pos, size);
correctbytes((char *)&d, sizeof(d), h.endian);
lua_pushnumber(L, d);
break;
}
case 'c': {
if (size == 0) {
if (!lua_isnumber(L, -1))
luaL_error(L, "format `c0' needs a previous size");
size = lua_tonumber(L, -1);
lua_pop(L, 1);
luaL_argcheck(L, pos+size <= ld, 2, "data string too short");
}
lua_pushlstring(L, data+pos, size);
break;
}
case 's': {
const char *e = (const char *)memchr(data+pos, '\0', ld - pos);
if (e == NULL)
luaL_error(L, "unfinished string in data");
size = (e - (data+pos)) + 1;
lua_pushlstring(L, data+pos, size - 1);
break;
}
default: controloptions(L, opt, &fmt, &h);
}
pos += size;
}
lua_pushinteger(L, pos + 1);
return lua_gettop(L) - 2;
}
static int b_size (lua_State *L) {
Header h;
const char *fmt = luaL_checkstring(L, 1);
size_t pos = 0;
defaultoptions(&h);
while (*fmt) {
int opt = *fmt++;
size_t size = optsize(L, opt, &fmt);
pos += gettoalign(pos, &h, opt, size);
if (opt == 's')
luaL_argerror(L, 1, "option 's' has no fixed size");
else if (opt == 'c' && size == 0)
luaL_argerror(L, 1, "option 'c0' has no fixed size");
if (!isalnum(opt))
controloptions(L, opt, &fmt, &h);
pos += size;
}
lua_pushinteger(L, pos);
return 1;
}
/* }====================================================== */
static const struct luaL_Reg thislib[] = {
{"pack", b_pack},
{"unpack", b_unpack},
{"size", b_size},
{NULL, NULL}
};
LUALIB_API int luaopen_struct (lua_State *L);
LUALIB_API int luaopen_struct (lua_State *L) {
luaL_register(L, "struct", thislib);
return 1;
}
/******************************************************************************
* Copyright (C) 2010-2012 Lua.org, PUC-Rio. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/

386
lua/lua_test.go Normal file
View File

@ -0,0 +1,386 @@
// +build lua
package lua
import (
"testing"
"unsafe"
)
type TestStruct struct {
IntField int
StringField string
FloatField float64
}
func TestGoStruct(t *testing.T) {
L := NewState()
L.OpenLibs()
defer L.Close()
ts := &TestStruct{10, "test", 2.3}
L.CheckStack(1)
L.PushGoStruct(ts)
L.SetGlobal("t")
L.GetGlobal("t")
if !L.IsGoStruct(-1) {
t.Fatal("Not go struct")
}
tsr := L.ToGoStruct(-1).(*TestStruct)
if tsr != ts {
t.Fatal("Retrieved something different from what we inserted")
}
L.Pop(1)
L.PushString("This is not a struct")
if L.ToGoStruct(-1) != nil {
t.Fatal("Non-GoStruct value attempted to convert into GoStruct should result in nil")
}
L.Pop(1)
}
func TestCheckStringSuccess(t *testing.T) {
L := NewState()
L.OpenLibs()
defer L.Close()
Test := func(L *State) int {
L.PushString("this is a test")
L.CheckString(-1)
return 0
}
L.Register("test", Test)
err := L.DoString("test()")
if err != nil {
t.Fatalf("DoString did return an error: %v\n", err.Error())
}
}
func TestCheckStringFail(t *testing.T) {
L := NewState()
L.OpenLibs()
defer L.Close()
Test := func(L *State) int {
L.CheckString(-1)
return 0
}
L.Register("test", Test)
err := L.DoString("test();")
if err == nil {
t.Fatal("DoString did not return an error\n")
}
}
func TestPCallHidden(t *testing.T) {
L := NewState()
L.OpenLibs()
defer L.Close()
err := L.DoString("pcall(print, \"ciao\")")
if err == nil {
t.Fatal("Can use pcall\n")
}
err = L.DoString("unsafe_pcall(print, \"ciao\")")
if err != nil {
t.Fatal("Can not use unsafe_pcall\n")
}
}
func TestCall(t *testing.T) {
L := NewState()
L.OpenLibs()
defer L.Close()
test := func(L *State) int {
arg1 := L.ToString(1)
arg2 := L.ToString(2)
arg3 := L.ToString(3)
if arg1 != "Argument1" {
t.Fatal("Got wrong argument (1)")
}
if arg2 != "Argument2" {
t.Fatal("Got wrong argument (2)")
}
if arg3 != "Argument3" {
t.Fatal("Got wrong argument (3)")
}
L.PushString("Return1")
L.PushString("Return2")
return 2
}
L.Register("test", test)
L.PushString("Dummy")
L.GetGlobal("test")
L.PushString("Argument1")
L.PushString("Argument2")
L.PushString("Argument3")
err := L.Call(3, 2)
if err != nil {
t.Fatalf("Error executing call: %v\n", err)
}
dummy := L.ToString(1)
ret1 := L.ToString(2)
ret2 := L.ToString(3)
if dummy != "Dummy" {
t.Fatal("The stack was disturbed")
}
if ret1 != "Return1" {
t.Fatalf("Wrong return value (1) got: <%s>", ret1)
}
if ret2 != "Return2" {
t.Fatalf("Wrong return value (2) got: <%s>", ret2)
}
}
// equivalent to basic.go
func TestLikeBasic(t *testing.T) {
L := NewState()
defer L.Close()
L.OpenLibs()
testCalled := 0
test := func(L *State) int {
testCalled++
return 0
}
test2Arg := -1
test2Argfrombottom := -1
test2 := func(L *State) int {
test2Arg = L.CheckInteger(-1)
test2Argfrombottom = L.CheckInteger(1)
return 0
}
L.GetField(LUA_GLOBALSINDEX, "print")
L.PushString("Hello World!")
if err := L.Call(1, 0); err != nil {
t.Fatalf("Call to print returned error")
}
L.PushGoFunction(test)
L.PushGoFunction(test)
L.PushGoFunction(test)
L.PushGoFunction(test2)
L.PushInteger(42)
if err := L.Call(1, 0); err != nil {
t.Fatalf("Call to print returned error")
}
if (test2Arg != 42) || (test2Argfrombottom != 42) {
t.Fatalf("Call to test2 didn't work")
}
if err := L.Call(0, 0); err != nil {
t.Fatalf("Call to print returned error")
}
if err := L.Call(0, 0); err != nil {
t.Fatalf("Call to print returned error")
}
if err := L.Call(0, 0); err != nil {
t.Fatalf("Call to print returned error")
}
if testCalled != 3 {
t.Fatalf("Test function not called the correct number of times: %d\n", testCalled)
}
// this will fail as we didn't register test2 function
if err := L.DoString("test2(42)"); err == nil {
t.Fatal("No error when calling unregistered function")
}
}
// equivalent to quickstart.go
func TestLikeQuickstart(t *testing.T) {
adder := func(L *State) int {
a := L.ToInteger(1)
b := L.ToInteger(2)
L.PushInteger(int64(a + b))
return 1
}
L := NewState()
defer L.Close()
L.OpenLibs()
L.Register("adder", adder)
if err := L.DoString("return adder(2, 2)"); err != nil {
t.Fatalf("Error during call to adder: %v\n", err)
}
if r := L.ToInteger(1); r != 4 {
t.Fatalf("Wrong return value from adder (was: %d)\n", r)
}
}
// equivalent to userdata.go
func TestLikeUserdata(t *testing.T) {
type Userdata struct {
a, b int
}
userDataProper := func(L *State) {
rawptr := L.NewUserdata(uintptr(unsafe.Sizeof(Userdata{})))
var ptr *Userdata
ptr = (*Userdata)(rawptr)
ptr.a = 2
ptr.b = 3
rawptr2 := L.ToUserdata(-1)
ptr2 := (*Userdata)(rawptr2)
if ptr != ptr2 {
t.Fatalf("Failed to create userdata\n")
}
}
testCalled := 0
test := func(L *State) int {
testCalled++
return 0
}
goDefinedFunctions := func(L *State) {
// example_function is registered inside Lua VM
L.Register("test", test)
// This code demonstrates checking that a value on the stack is a go function
L.CheckStack(1)
L.GetGlobal("test")
if !L.IsGoFunction(-1) {
t.Fatalf("IsGoFunction failed to recognize a Go function object")
}
L.Pop(1)
// We call example_function from inside Lua VM
testCalled = 0
if err := L.DoString("test()"); err != nil {
t.Fatalf("Error executing test function: %v\n", err)
}
if testCalled != 1 {
t.Fatalf("It appears the test function wasn't actually called\n")
}
}
type TestObject struct {
AField int
}
goDefinedObjects := func(L *State) {
z := &TestObject{42}
L.PushGoStruct(z)
L.SetGlobal("z")
// This code demonstrates checking that a value on the stack is a go object
L.CheckStack(1)
L.GetGlobal("z")
if !L.IsGoStruct(-1) {
t.Fatal("IsGoStruct failed to recognize a Go struct\n")
}
L.Pop(1)
// This code demonstrates access and assignment to a field of a go object
if err := L.DoString("return z.AField"); err != nil {
t.Fatal("Couldn't execute code")
}
before := L.ToInteger(-1)
L.Pop(1)
if before != 42 {
t.Fatalf("Wrong value of z.AField before change (%d)\n", before)
}
if err := L.DoString("z.AField = 10;"); err != nil {
t.Fatal("Couldn't execute code")
}
if err := L.DoString("return z.AField"); err != nil {
t.Fatal("Couldn't execute code")
}
after := L.ToInteger(-1)
L.Pop(1)
if after != 10 {
t.Fatalf("Wrong value of z.AField after change (%d)\n", after)
}
}
L := NewState()
defer L.Close()
L.OpenLibs()
userDataProper(L)
goDefinedFunctions(L)
goDefinedObjects(L)
}
func TestStackTrace(t *testing.T) {
L := NewState()
defer L.Close()
L.OpenLibs()
err := L.DoFile("../example/calls.lua")
if err == nil {
t.Fatal("No error returned from the execution of calls.lua")
}
le := err.(*LuaError)
if le.Code() != LUA_ERRERR {
t.Fatalf("Wrong kind of error encountered running calls.lua: %v (%d %d)\n", le, le.Code(), LUA_ERRERR)
}
if len(le.StackTrace()) != 6 {
t.Fatalf("Wrong size of stack trace (%v)\n", le.StackTrace())
}
}
func TestConv(t *testing.T) {
L := NewState()
defer L.Close()
L.OpenLibs()
L.PushString("10")
n := L.ToNumber(-1)
if n != 10 {
t.Fatalf("Wrong conversion (str -> int)")
}
if L.Type(-1) != LUA_TSTRING {
t.Fatalf("Wrong type (str)")
}
L.Pop(1)
L.PushInteger(10)
s := L.ToString(-1)
if s != "10" {
t.Fatalf("Wrong conversion (int -> str)")
}
L.Pop(1)
L.PushString("a\000test")
s = L.ToString(-1)
if s != "a\000test" {
t.Fatalf("Wrong conversion (str -> str): <%s>", s)
}
}

253
lua/strbuf.c Normal file
View File

@ -0,0 +1,253 @@
// +build lua
/* strbuf - string buffer routines
*
* Copyright (c) 2010-2011 Mark Pulford <mark@kyne.com.au>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "strbuf.h"
void die(const char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
vfprintf(stderr, fmt, arg);
va_end(arg);
fprintf(stderr, "\n");
exit(-1);
}
void strbuf_init(strbuf_t *s, int len)
{
int size;
if (len <= 0)
size = STRBUF_DEFAULT_SIZE;
else
size = len + 1; /* \0 terminator */
s->buf = NULL;
s->size = size;
s->length = 0;
s->increment = STRBUF_DEFAULT_INCREMENT;
s->dynamic = 0;
s->reallocs = 0;
s->debug = 0;
s->buf = malloc(size);
if (!s->buf)
die("Out of memory");
strbuf_ensure_null(s);
}
strbuf_t *strbuf_new(int len)
{
strbuf_t *s;
s = malloc(sizeof(strbuf_t));
if (!s)
die("Out of memory");
strbuf_init(s, len);
/* Dynamic strbuf allocation / deallocation */
s->dynamic = 1;
return s;
}
void strbuf_set_increment(strbuf_t *s, int increment)
{
/* Increment > 0: Linear buffer growth rate
* Increment < -1: Exponential buffer growth rate */
if (increment == 0 || increment == -1)
die("BUG: Invalid string increment");
s->increment = increment;
}
static inline void debug_stats(strbuf_t *s)
{
if (s->debug) {
fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n",
(long)s, s->reallocs, s->length, s->size);
}
}
/* If strbuf_t has not been dynamically allocated, strbuf_free() can
* be called any number of times strbuf_init() */
void strbuf_free(strbuf_t *s)
{
debug_stats(s);
if (s->buf) {
free(s->buf);
s->buf = NULL;
}
if (s->dynamic)
free(s);
}
char *strbuf_free_to_string(strbuf_t *s, int *len)
{
char *buf;
debug_stats(s);
strbuf_ensure_null(s);
buf = s->buf;
if (len)
*len = s->length;
if (s->dynamic)
free(s);
return buf;
}
static int calculate_new_size(strbuf_t *s, int len)
{
int reqsize, newsize;
if (len <= 0)
die("BUG: Invalid strbuf length requested");
/* Ensure there is room for optional NULL termination */
reqsize = len + 1;
/* If the user has requested to shrink the buffer, do it exactly */
if (s->size > reqsize)
return reqsize;
newsize = s->size;
if (s->increment < 0) {
/* Exponential sizing */
while (newsize < reqsize)
newsize *= -s->increment;
} else {
/* Linear sizing */
newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
}
return newsize;
}
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
* optional termination). */
void strbuf_resize(strbuf_t *s, int len)
{
int newsize;
newsize = calculate_new_size(s, len);
if (s->debug > 1) {
fprintf(stderr, "strbuf(%lx) resize: %d => %d\n",
(long)s, s->size, newsize);
}
s->size = newsize;
s->buf = realloc(s->buf, s->size);
if (!s->buf)
die("Out of memory");
s->reallocs++;
}
void strbuf_append_string(strbuf_t *s, const char *str)
{
int space, i;
space = strbuf_empty_length(s);
for (i = 0; str[i]; i++) {
if (space < 1) {
strbuf_resize(s, s->length + 1);
space = strbuf_empty_length(s);
}
s->buf[s->length] = str[i];
s->length++;
space--;
}
}
/* strbuf_append_fmt() should only be used when an upper bound
* is known for the output string. */
void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
{
va_list arg;
int fmt_len;
strbuf_ensure_empty_length(s, len);
va_start(arg, fmt);
fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
va_end(arg);
if (fmt_len < 0)
die("BUG: Unable to convert number"); /* This should never happen.. */
s->length += fmt_len;
}
/* strbuf_append_fmt_retry() can be used when the there is no known
* upper bound for the output string. */
void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
{
va_list arg;
int fmt_len, try;
int empty_len;
/* If the first attempt to append fails, resize the buffer appropriately
* and try again */
for (try = 0; ; try++) {
va_start(arg, fmt);
/* Append the new formatted string */
/* fmt_len is the length of the string required, excluding the
* trailing NULL */
empty_len = strbuf_empty_length(s);
/* Add 1 since there is also space to store the terminating NULL. */
fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
va_end(arg);
if (fmt_len <= empty_len)
break; /* SUCCESS */
if (try > 0)
die("BUG: length of formatted string changed");
strbuf_resize(s, s->length + fmt_len);
}
s->length += fmt_len;
}
/* vi:ai et sw=4 ts=4:
*/

144
lua/strbuf.h Normal file
View File

@ -0,0 +1,144 @@
// +build lua
/* strbuf - String buffer routines
*
* Copyright (c) 2010-2011 Mark Pulford <mark@kyne.com.au>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdarg.h>
/* Size: Total bytes allocated to *buf
* Length: String length, excluding optional NULL terminator.
* Increment: Allocation increments when resizing the string buffer.
* Dynamic: True if created via strbuf_new()
*/
typedef struct {
char *buf;
int size;
int length;
int increment;
int dynamic;
int reallocs;
int debug;
} strbuf_t;
#ifndef STRBUF_DEFAULT_SIZE
#define STRBUF_DEFAULT_SIZE 1023
#endif
#ifndef STRBUF_DEFAULT_INCREMENT
#define STRBUF_DEFAULT_INCREMENT -2
#endif
/* Initialise */
extern strbuf_t *strbuf_new(int len);
extern void strbuf_init(strbuf_t *s, int len);
extern void strbuf_set_increment(strbuf_t *s, int increment);
/* Release */
extern void strbuf_free(strbuf_t *s);
extern char *strbuf_free_to_string(strbuf_t *s, int *len);
/* Management */
extern void strbuf_resize(strbuf_t *s, int len);
static int strbuf_empty_length(strbuf_t *s);
static int strbuf_length(strbuf_t *s);
static char *strbuf_string(strbuf_t *s, int *len);
static void strbuf_ensure_empty_length(strbuf_t *s, int len);
/* Update */
extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);
extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...);
static void strbuf_append_mem(strbuf_t *s, const char *c, int len);
extern void strbuf_append_string(strbuf_t *s, const char *str);
static void strbuf_append_char(strbuf_t *s, const char c);
static void strbuf_ensure_null(strbuf_t *s);
/* Reset string for before use */
static inline void strbuf_reset(strbuf_t *s)
{
s->length = 0;
}
static inline int strbuf_allocated(strbuf_t *s)
{
return s->buf != NULL;
}
/* Return bytes remaining in the string buffer
* Ensure there is space for a NULL terminator. */
static inline int strbuf_empty_length(strbuf_t *s)
{
return s->size - s->length - 1;
}
static inline void strbuf_ensure_empty_length(strbuf_t *s, int len)
{
if (len > strbuf_empty_length(s))
strbuf_resize(s, s->length + len);
}
static inline int strbuf_length(strbuf_t *s)
{
return s->length;
}
static inline void strbuf_append_char(strbuf_t *s, const char c)
{
strbuf_ensure_empty_length(s, 1);
s->buf[s->length++] = c;
}
static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
{
s->buf[s->length++] = c;
}
static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len)
{
strbuf_ensure_empty_length(s, len);
memcpy(s->buf + s->length, c, len);
s->length += len;
}
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len)
{
memcpy(s->buf + s->length, c, len);
s->length += len;
}
static inline void strbuf_ensure_null(strbuf_t *s)
{
s->buf[s->length] = 0;
}
static inline char *strbuf_string(strbuf_t *s, int *len)
{
if (len)
*len = s->length;
return s->buf;
}
/* vi:ai et sw=4 ts=4:
*/

View File

@ -6,8 +6,8 @@ import (
"crypto/sha1"
"encoding/hex"
"fmt"
"github.com/siddontang/golua/lua"
"github.com/siddontang/ledisdb/ledis"
"github.com/siddontang/ledisdb/lua"
"strconv"
"strings"
)

View File

@ -5,8 +5,8 @@ package server
import (
"encoding/hex"
"fmt"
"github.com/siddontang/golua/lua"
"github.com/siddontang/ledisdb/ledis"
"github.com/siddontang/ledisdb/lua"
"io"
"sync"
)

View File

@ -4,8 +4,8 @@ package server
import (
"fmt"
"github.com/siddontang/golua/lua"
"github.com/siddontang/ledisdb/config"
"github.com/siddontang/ledisdb/lua"
"testing"
)

View File

@ -1,10 +0,0 @@
// +build ignore
package main
import "github.com/siddontang/golua/lua"
func main() {
L := lua.NewState()
L.Close()
}