mirror of https://github.com/ledisdb/ledisdb.git
Merge branch 'develop'
This commit is contained in:
commit
b16691ad6c
16
README.md
16
README.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -238,14 +238,6 @@ class Ledis(object):
|
|||
"Ping the Ledis server"
|
||||
return self.execute_command('PING')
|
||||
|
||||
def select(self, db):
|
||||
"""Select a Ledis db, ``db`` is integer type"""
|
||||
try:
|
||||
db = int(db)
|
||||
except ValueError:
|
||||
db = 0
|
||||
return self.execute_command('SELECT', db)
|
||||
|
||||
def info(self, section=None):
|
||||
"""
|
||||
Return
|
||||
|
|
9
dev.sh
9
dev.sh
|
@ -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,11 @@ 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
|
||||
CGO_CFLAGS="$CGO_CFLAGS -I$LUA_DIR/include"
|
||||
CGO_LDFLAGS="$CGO_LDFLAGS -L$LUA_DIR/lib -llua"
|
||||
LD_LIBRARY_PATH=$(add_path $LD_LIBRARY_PATH $LUA_DIR/lib)
|
||||
DYLD_LIBRARY_PATH=$(add_path $DYLD_LIBRARY_PATH $LUA_DIR/lib)
|
||||
GO_BUILD_TAGS="$GO_BUILD_TAGS lua"
|
||||
fi
|
||||
|
||||
|
|
|
@ -256,6 +256,10 @@ func (l *BinLog) checkLogFileSize() bool {
|
|||
}
|
||||
|
||||
func (l *BinLog) closeLog() {
|
||||
if l.logFile == nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.lastLogIndex++
|
||||
|
||||
l.logFile.Close()
|
||||
|
@ -263,6 +267,9 @@ func (l *BinLog) closeLog() {
|
|||
}
|
||||
|
||||
func (l *BinLog) purge(n int) {
|
||||
if len(l.logNames) < n {
|
||||
n = len(l.logNames)
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
logPath := path.Join(l.path, l.logNames[i])
|
||||
os.Remove(logPath)
|
||||
|
@ -338,6 +345,9 @@ func (l *BinLog) PurgeAll() error {
|
|||
defer l.Unlock()
|
||||
|
||||
l.closeLog()
|
||||
|
||||
l.purge(len(l.logNames))
|
||||
|
||||
return l.openNewLogFile()
|
||||
}
|
||||
|
||||
|
|
|
@ -34,4 +34,16 @@ func TestBinLog(t *testing.T) {
|
|||
} else if len(fs) != 2 {
|
||||
t.Fatal(len(fs))
|
||||
}
|
||||
|
||||
if err := b.PurgeAll(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if fs, err := ioutil.ReadDir(b.LogPath()); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(fs) != 2 {
|
||||
t.Fatal(len(fs))
|
||||
} else if b.LogFilePos() != 0 {
|
||||
t.Fatal(b.LogFilePos())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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()})
|
||||
}
|
|
@ -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);
|
|
@ -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.
|
|
@ -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))
|
||||
}
|
|
@ -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)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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.
|
||||
******************************************************************************/
|
|
@ -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
|
||||
)
|
|
@ -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.
|
||||
******************************************************************************/
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
|
@ -295,10 +295,8 @@ func parseScanArgs(c *client) (key []byte, match string, count int, err error) {
|
|||
switch strings.ToLower(ledis.String(args[1])) {
|
||||
case "match":
|
||||
match = ledis.String(args[2])
|
||||
return
|
||||
case "count":
|
||||
count, err = strconv.Atoi(ledis.String(args[2]))
|
||||
return
|
||||
default:
|
||||
err = ErrCmdParams
|
||||
return
|
||||
|
@ -314,7 +312,10 @@ func parseScanArgs(c *client) (key []byte, match string, count int, err error) {
|
|||
|
||||
match = ledis.String(args[2])
|
||||
count, err = strconv.Atoi(ledis.String(args[4]))
|
||||
return
|
||||
}
|
||||
|
||||
if count <= 0 {
|
||||
err = ErrCmdParams
|
||||
}
|
||||
|
||||
return
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -4,8 +4,8 @@ package server
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/siddontang/golua/lua"
|
||||
"github.com/siddontang/ledisdb/config"
|
||||
"github.com/siddontang/ledisdb/lua"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
|
|
@ -152,6 +152,10 @@ func (db *DB) BatchPut(writes []driver.Write) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (db *DB) Compact() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver.Register(Store{})
|
||||
}
|
||||
|
|
|
@ -6,6 +6,11 @@ import (
|
|||
|
||||
type DB struct {
|
||||
driver.IDB
|
||||
name string
|
||||
}
|
||||
|
||||
func (db *DB) String() string {
|
||||
return db.name
|
||||
}
|
||||
|
||||
func (db *DB) NewIterator() *Iterator {
|
||||
|
@ -29,6 +34,10 @@ func (db *DB) NewSnapshot() (*Snapshot, error) {
|
|||
return s, nil
|
||||
}
|
||||
|
||||
func (db *DB) Compact() error {
|
||||
return db.IDB.Compact()
|
||||
}
|
||||
|
||||
func (db *DB) RangeIterator(min []byte, max []byte, rangeType uint8) *RangeLimitIterator {
|
||||
return NewRangeLimitIterator(db.NewIterator(), &Range{min, max, rangeType}, &Limit{0, -1})
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ type IDB interface {
|
|||
NewSnapshot() (ISnapshot, error)
|
||||
|
||||
Begin() (Tx, error)
|
||||
|
||||
Compact() error
|
||||
}
|
||||
|
||||
type ISnapshot interface {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/siddontang/goleveldb/leveldb/filter"
|
||||
"github.com/siddontang/goleveldb/leveldb/opt"
|
||||
"github.com/siddontang/goleveldb/leveldb/storage"
|
||||
"github.com/siddontang/goleveldb/leveldb/util"
|
||||
|
||||
"github.com/siddontang/ledisdb/config"
|
||||
"github.com/siddontang/ledisdb/store/driver"
|
||||
|
@ -180,6 +181,10 @@ func (db *DB) NewSnapshot() (driver.ISnapshot, error) {
|
|||
return s, nil
|
||||
}
|
||||
|
||||
func (db *DB) Compact() error {
|
||||
return db.db.CompactRange(util.Range{nil, nil})
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver.Register(Store{})
|
||||
driver.Register(MemStore{})
|
||||
|
|
|
@ -270,6 +270,11 @@ func (db *DB) Begin() (driver.Tx, error) {
|
|||
return nil, driver.ErrTxSupport
|
||||
}
|
||||
|
||||
func (db *DB) Compact() error {
|
||||
C.leveldb_compact_range(db.db, nil, 0, nil, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver.Register(Store{})
|
||||
}
|
||||
|
|
|
@ -270,6 +270,11 @@ func (db *DB) Begin() (driver.Tx, error) {
|
|||
return nil, driver.ErrTxSupport
|
||||
}
|
||||
|
||||
func (db *DB) Compact() error {
|
||||
C.leveldb_compact_range(db.db, nil, 0, nil, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver.Register(Store{})
|
||||
}
|
||||
|
|
|
@ -240,9 +240,6 @@ func (db MDB) Path() string {
|
|||
return db.path
|
||||
}
|
||||
|
||||
func (db MDB) Compact() {
|
||||
}
|
||||
|
||||
func (db MDB) iterator(rdonly bool) *MDBIterator {
|
||||
flags := uint(0)
|
||||
if rdonly {
|
||||
|
@ -286,6 +283,10 @@ func (db MDB) NewSnapshot() (driver.ISnapshot, error) {
|
|||
return newSnapshot(db)
|
||||
}
|
||||
|
||||
func (db MDB) Compact() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver.Register(Store{})
|
||||
}
|
||||
|
|
|
@ -296,6 +296,11 @@ func (db *DB) Begin() (driver.Tx, error) {
|
|||
return nil, driver.ErrTxSupport
|
||||
}
|
||||
|
||||
func (db *DB) Compact() error {
|
||||
C.rocksdb_compact_range(db.db, nil, 0, nil, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver.Register(Store{})
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ func Open(cfg *config.Config) (*DB, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
db := &DB{idb}
|
||||
db := &DB{idb, s.String()}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
|
|
@ -148,6 +148,9 @@ func checkIterator(it *RangeLimitIterator, cv ...int) error {
|
|||
v := make([]string, 0, len(cv))
|
||||
for ; it.Valid(); it.Next() {
|
||||
k := it.Key()
|
||||
if string(it.Value()) != "value" {
|
||||
return fmt.Errorf("invalid value")
|
||||
}
|
||||
v = append(v, string(k))
|
||||
}
|
||||
|
||||
|
@ -175,7 +178,7 @@ func testIterator(db *DB, t *testing.T) {
|
|||
|
||||
for i := 0; i < 10; i++ {
|
||||
key := []byte(fmt.Sprintf("key_%d", i))
|
||||
value := []byte("")
|
||||
value := []byte("value")
|
||||
db.Put(key, value)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/siddontang/golua/lua"
|
||||
|
||||
func main() {
|
||||
L := lua.NewState()
|
||||
L.Close()
|
||||
}
|
Loading…
Reference in New Issue