From cb3bd57fb3bac138a5426960e413ddd2176c4616 Mon Sep 17 00:00:00 2001 From: Alexander Pickering Date: Mon, 13 Jul 2020 16:16:46 -0400 Subject: Inital Commit --- Makefile | 34 ++++++++ lua-nng-dev-1.rockspec | 45 ++++++++++ spec/startup_spec.lua | 83 ++++++++++++++++++ src/lua-nng.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 390 insertions(+) create mode 100644 Makefile create mode 100644 lua-nng-dev-1.rockspec create mode 100644 spec/startup_spec.lua create mode 100644 src/lua-nng.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d82ee92 --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +CC?=gcc +CAFLAGS+=-I$(NNG_INCDIR) -I$(LUA_INCDIR) $(CFLAGS) +LADFLAGS+=$(LIBFLAG) $(LDFLAGS) -L$(NNG_LIBDIR) -L$(LUA_LIBDIR) +LD=gcc +LIBS=-lnng -llua53 + +ifeq ($(OS), Windows_NT) + LDFLAGS+=-mwindows + LIBS+=-lws2_32 +else +endif + + +src_files=$(shell find src/*.c) +obj_files=$(src_files:src/%.c=build/%.o) +target=bin/nng.$(LIB_EXTENSION) + +all: $(target) + +$(target) : $(obj_files) + $(LD) $(LADFLAGS) -o $@ $^ $(LIBS) + +$(obj_files): build/%.o : src/%.c + $(CC) $(CAFLAGS) -c -o $@ $< + +install: $(target) + $(CP) $(target) $(INST_LIBDIR) + +test: + busted --cpath=./bin/?$(SHARE_EXT) + +clean: + rm -rf build/* + rm -rf bin/* diff --git a/lua-nng-dev-1.rockspec b/lua-nng-dev-1.rockspec new file mode 100644 index 0000000..5cf013f --- /dev/null +++ b/lua-nng-dev-1.rockspec @@ -0,0 +1,45 @@ +package = "lua-nng" +rockspec_format="3.0" +version = "dev-1" +source = { + url = "*** please add URL for source tarball, zip or repository here ***" +} +description = { + summary = "A simple binding for Nanomessage Next Generation", + homepage = "https://cogarr.net/source/cgit.cgi/lua-nng/about", + license = "BSD/2 Clause", + maintainer = "Alexander Pickering ", + labels = {"network","nanomessage","nng"} +} +external_dependencies = { + NNG = { + library = "nng", + header = "nng/nng.h" + } +} +build = { + type = "make", + build_variables = { + CFLAGS="$(CFLAGS)", + LUA_INCDIR="$(LUA_INCDIR)", + NNG_INCDIR="$(NNG_INCDIR)", + NNG_LIBDIR="$(NNG_LIBDIR)", + LUA_LIBDIR="$(LUA_LIBDIR)", + FIND="$(FIND)", + LIB_EXTENSION="$(LIB_EXTENSION)", + LIBFLAG="$(LIBFLAG)", + }, + install_variables = { + INST_PREFIX="$(PREFIX)", + INST_LIBDIR="$(LIBDIR)", + NNG_LIBDIR="$(NNG_LIBDIR)", + LUA_LIBDIR="$(LUA_LIBDIR)", + LIB_EXTENSION="$(LIB_EXTENSION)", + CP="$(CP)", + }, + install = { + bin = { + "bin/nng.dll" + } + } +} diff --git a/spec/startup_spec.lua b/spec/startup_spec.lua new file mode 100644 index 0000000..45fa996 --- /dev/null +++ b/spec/startup_spec.lua @@ -0,0 +1,83 @@ +--[[ +test startup of nanomsg api +]] + +describe("nng",function() + local nng + it("should be included with require()",function() + nng = require("nng") + end) + it("should be able to create sockets",function() + local socket = assert(nng.pair1_open()) + end) + it("should be able to extablish a connection over inter-process communication",function() + local s1 = assert(nng.pair1_open()) + local s2 = assert(nng.pair1_open()) + assert(s1:listen("ipc:///tmp/pair.ipc")) + assert(s2:dial("ipc://tmp/pair.ipc")) + + assert(s2:send("hello")) + local rec = assert(s1:recv()) + assert(rec == "hello","Failed to receive hello, received:" .. rec) + end) + it("should be able to use a bus socket to distribute information",function() + local b = {} + for i = 1,10 do + local s = assert(nng.bus0_open()) + b[i] = s + end + for i = 1,10 do + local ipcaddr = string.format("ipc:///tmp/bus_%d.ipc",i) + assert(b[i]:listen(ipcaddr)) + end + for i = 1,10 do + for j = 1,10 do + if i ~= j then + local addr = string.format("ipc:///tmp/bus_%d.ipc",j) + assert(b[i]:dial(addr)) + end + end + end + assert(b[1]:send("Hello")) + for i = 2,10 do + local msg = assert(b[i]:recv()) + assert(msg == "Hello") + end + end) + it("should be able to use a survey socket to gather information",function() + math.randomseed(os.time()) + local s = assert(nng.surveyor0_open()) + assert(s:listen("ipc:///tmp/survey.ipc")) + local b = {} + for i = 1,100 do + local r = assert(nng.respondent0_open()) + assert(r:dial("ipc:///tmp/survey.ipc")) + b[i] = r + end + assert(s:send("Hello")) + for i = 1,100 do + local survey = assert(b[i]:recv()) + assert(survey == "Hello") + assert(b[i]:send(string.format("%f",math.random()))) + end + local responses = {} + while true do + local succ, msg = s:recv(nng.NNG_FLAG_NONBLOCK) + if succ then + table.insert(responses,tonumber(succ)) + elseif msg == "Try again" then + os.execute("sleep 1") + elseif msg == "Incorrect state" then + break + end + end + local avg = 0 + for _,v in pairs(responses) do + avg = avg + v + end + avg = avg / #responses + --avg should be about 0.5 + assert(avg > 0.4) + assert(avg < 0.6) + end) +end) diff --git a/src/lua-nng.c b/src/lua-nng.c new file mode 100644 index 0000000..71e57e8 --- /dev/null +++ b/src/lua-nng.c @@ -0,0 +1,228 @@ +#include +#include +#include + +#define NNG_STATIC_LIB + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OPEN(name)\ + int lnng_ ## name ## _open(lua_State *L){\ + nng_socket *s = (nng_socket*)lua_newuserdata(L,sizeof(nng_socket));\ + int err = nng_ ## name ## _open(s);\ + if(err == 0){\ + luaL_setmetatable(L,"nng.socket");\ + return 1;\ + }else{\ + lua_pushboolean(L,0);\ + lua_pushstring(L,nng_strerror(err));\ + return 2;\ + }\ + } + +OPEN(bus0); +OPEN(pair1); +OPEN(pub0); +OPEN(sub0); +OPEN(pull0); +OPEN(push0); +OPEN(req0); +OPEN(rep0); +OPEN(surveyor0); +OPEN(respondent0); + +nng_socket* tosocket(lua_State *L, int offset){ + luaL_checkudata(L,offset,"nng.socket"); + return (nng_socket*)lua_touserdata(L,offset); +} + +//socket:listen(url[, flags]) :: listener +int lnng_listen(lua_State *L){ + int argc = lua_gettop(L); + int flags = 0; + nng_socket *sock = tosocket(L,1); + const char *url = luaL_checkstring(L,2); + if(argc >= 3){ + flags = luaL_checkinteger(L,3); + } + lua_pop(L,argc); + nng_listener *lp = (nng_listener*)lua_newuserdata(L,sizeof(nng_listener)); + int err = nng_listen(*sock, url, lp, flags); + if(err == 0){ + luaL_setmetatable(L,"nng.listener"); + return 1; + }else{ + lua_pushboolean(L,0); + lua_pushstring(L,nng_strerror(err)); + return 2; + } +} + +//socket:dial(url[, flags]) :: dialer +int lnng_dial(lua_State *L){ + int argc = lua_gettop(L); + int flags = 0; + nng_socket *sock = tosocket(L,1); + const char *url = luaL_checkstring(L,2); + if(argc >= 3){ + flags = luaL_checkinteger(L,3); + } + lua_pop(L,argc); + nng_dialer *dp = (nng_dialer*)lua_newuserdata(L,sizeof(nng_dialer)); + int err = nng_dial(*sock, url, dp, flags); + if(err == 0){ + luaL_setmetatable(L,"nng.dialer"); + return 1; + }else{ + lua_pushboolean(L,0); + lua_pushstring(L,nng_strerror(err)); + return 2; + } +} + +//socket:send("data"[, flags]) +int lnng_send(lua_State *L){ + int argc = lua_gettop(L); + int flags = 0; + nng_socket *sock = tosocket(L,1); + size_t datasize; + const char *data = luaL_checklstring(L,2,&datasize); + if(argc >= 3){ + flags = luaL_checkinteger(L,3); + } + lua_pop(L,argc); + int err = nng_send(*sock, (void*)data, datasize, flags); + if(err == 0){ + lua_pushboolean(L,1); + return 1; + }else{ + lua_pushboolean(L,0); + lua_pushstring(L,nng_strerror(err)); + return 2; + } +} + +//socket:recv([flags]) +int lnng_recv(lua_State *L){ + int argc = lua_gettop(L); + int flags = NNG_FLAG_ALLOC; //don't support zero copy + nng_socket *sock = tosocket(L,1); + if(argc >= 2){ + flags += luaL_checkinteger(L,2); + } + char *data = NULL; + size_t datasize; + int err = nng_recv(*sock, &data, &datasize, flags); + if(err == 0){ + lua_pushlstring(L,data,datasize); + nng_free(data,datasize); + return 1; + }else{ + lua_pushboolean(L,0); + lua_pushstring(L,nng_strerror(err)); + return 2; + } +} + +//socket:close() +int lnng_socket_close(lua_State *L){ + nng_socket *sock = tosocket(L,1); + int err = nng_close(*sock); + lua_pop(L,1); + return 0; +} + +//close(ud_dialer) +int lnng_dialer_close(lua_State *L){ + nng_dialer *dp = (nng_dialer*)lua_touserdata(L,1); + nng_dialer_close(*dp); + lua_pop(L,1); + return 0; +} + +//close(ud_listener) +int lnng_listener_close(lua_State *L){ + nng_listener *lp = (nng_listener*)lua_touserdata(L,1); + int err = nng_listener_close(*lp); + lua_pop(L,1); + return 0; +} + +static const struct luaL_Reg nng_dialer_m[] = { + + {NULL, NULL} +}; + +static const struct luaL_Reg nng_listener_m[] = { + + {NULL, NULL} +}; + +static const struct luaL_Reg nng_socket_m[] = { + {"dial", lnng_dial}, + {"listen", lnng_listen}, + {"send", lnng_send}, + {"recv", lnng_recv}, + {NULL, NULL} +}; + +static const struct luaL_Reg nng_f[] = { + {"bus0_open", lnng_bus0_open}, + {"pair1_open", lnng_pair1_open}, + {"pub0_open", lnng_pub0_open}, + {"sub0_open", lnng_sub0_open}, + {"pull0_open", lnng_pull0_open}, + {"push0_open", lnng_push0_open}, + {"req0_open", lnng_req0_open}, + {"rep0_open", lnng_rep0_open}, + {"surveyor0_open",lnng_surveyor0_open}, + {"respondent0_open",lnng_respondent0_open}, + {NULL, NULL} +}; + + +#define flag(name) lua_pushnumber(L,name); lua_setfield(L,-2,#name); +int luaopen_nng(lua_State *L){ + luaL_newmetatable(L,"nng.socket"); + luaL_newlib(L,nng_socket_m); + lua_setfield(L,-2,"__index"); + lua_pushcfunction(L,lnng_socket_close); + lua_setfield(L,-2,"__gc"); + lua_pop(L,1); + + luaL_newmetatable(L,"nng.dialer"); + luaL_newlib(L,nng_dialer_m); + lua_setfield(L,-2,"__index"); + lua_pushcfunction(L,lnng_dialer_close); + lua_setfield(L,-2,"__gc"); + lua_pop(L,1); + + luaL_newmetatable(L,"nng.listener"); + luaL_newlib(L,nng_listener_m); + lua_setfield(L,-2,"__index"); + lua_pushcfunction(L,lnng_listener_close); + lua_setfield(L,-2,"__gc"); + lua_pop(L,1); + + luaL_newlib(L,nng_f); + flag(NNG_FLAG_NONBLOCK); + flag(NNG_FLAG_ALLOC); + return 1; +} -- cgit v1.2.3-70-g09d2