userdata
This commit is contained in:
@@ -600,6 +600,84 @@ static int luaArch_unicode_wtrunc(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int luaArch_userdata_free(lua_State *L) {
|
||||
luaArch *arch = luaArch_from(L);
|
||||
size_t user = (size_t)lua_touserdata(L, 1);
|
||||
nn_freeUserdata(arch->computer, user);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luaArch_userdata_methods(lua_State *L) {
|
||||
luaArch *arch = luaArch_from(L);
|
||||
size_t user = (size_t)lua_touserdata(L, 1);
|
||||
if(!nn_isUserdataValid(arch->computer, user)) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "no such userdata");
|
||||
return 2;
|
||||
}
|
||||
lua_createtable(L, 0, 0);
|
||||
size_t idx = 0;
|
||||
while(true) {
|
||||
nn_Method m;
|
||||
if(nn_getUserdataMethod(arch->computer, user, idx, &m)) break;
|
||||
idx++;
|
||||
if(m.name == NULL) continue;
|
||||
lua_pushstring(L, m.name);
|
||||
lua_createtable(L, 0, 4);
|
||||
|
||||
// the method data
|
||||
{
|
||||
lua_pushstring(L, "doc");
|
||||
if(m.doc == NULL) lua_pushnil(L); else lua_pushstring(L, m.doc);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "direct");
|
||||
lua_pushboolean(L, (m.flags & NN_DIRECT) != 0);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "getter");
|
||||
lua_pushboolean(L, (m.flags & NN_GETTER) != 0);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "setter");
|
||||
lua_pushboolean(L, (m.flags & NN_SETTER) != 0);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int luaArch_userdata_invoke(lua_State *L) {
|
||||
luaArch *arch = luaArch_from(L);
|
||||
size_t user = (size_t)lua_touserdata(L, 1);
|
||||
if(!nn_isUserdataValid(arch->computer, user)) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "no such userdata");
|
||||
return 2;
|
||||
}
|
||||
const char *method = luaL_checkstring(L, 2);
|
||||
size_t argc = lua_gettop(L);
|
||||
|
||||
nn_clearstack(arch->computer);
|
||||
for(size_t i = 3; i <= argc; i++) {
|
||||
luaArch_luaToNN(arch, L, i);
|
||||
}
|
||||
nn_Exit err = nn_invokeUserdata(arch->computer, user, method);
|
||||
if(err != NN_OK) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, nn_getError(arch->computer));
|
||||
return 2;
|
||||
}
|
||||
size_t retc = nn_getstacksize(arch->computer);
|
||||
for(size_t i = 0; i < retc; i++) {
|
||||
luaArch_nnToLua(arch, L, i);
|
||||
}
|
||||
nn_clearstack(arch->computer);
|
||||
return retc;
|
||||
}
|
||||
|
||||
static void luaArch_loadEnv(lua_State *L) {
|
||||
lua_createtable(L, 0, 10);
|
||||
int computer = lua_gettop(L);
|
||||
@@ -644,6 +722,7 @@ static void luaArch_loadEnv(lua_State *L) {
|
||||
lua_pushcfunction(L, luaArch_computer_getDeviceInfo);
|
||||
lua_setfield(L, computer, "getDeviceInfo");
|
||||
lua_setglobal(L, "computer");
|
||||
|
||||
lua_createtable(L, 0, 10);
|
||||
int component = lua_gettop(L);
|
||||
lua_pushcfunction(L, luaArch_component_list);
|
||||
@@ -663,6 +742,7 @@ static void luaArch_loadEnv(lua_State *L) {
|
||||
lua_pushcfunction(L, luaArch_component_fields);
|
||||
lua_setfield(L, component, "fields");
|
||||
lua_setglobal(L, "component");
|
||||
|
||||
lua_createtable(L, 0, 10);
|
||||
int unicode = lua_gettop(L);
|
||||
lua_pushcfunction(L, luaArch_unicode_char);
|
||||
@@ -676,6 +756,16 @@ static void luaArch_loadEnv(lua_State *L) {
|
||||
lua_pushcfunction(L, luaArch_unicode_wtrunc);
|
||||
lua_setfield(L, unicode, "wtrunc");
|
||||
lua_setglobal(L, "unicode");
|
||||
|
||||
lua_createtable(L, 0, 10);
|
||||
int userdata = lua_gettop(L);
|
||||
lua_pushcfunction(L, luaArch_userdata_free);
|
||||
lua_setfield(L, userdata, "free");
|
||||
lua_pushcfunction(L, luaArch_userdata_methods);
|
||||
lua_setfield(L, userdata, "methods");
|
||||
lua_pushcfunction(L, luaArch_userdata_invoke);
|
||||
lua_setfield(L, userdata, "invoke");
|
||||
lua_setglobal(L, "userdata");
|
||||
}
|
||||
|
||||
static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) {
|
||||
|
||||
@@ -70,6 +70,44 @@ end
|
||||
|
||||
local syncedMethodStats
|
||||
|
||||
local function sandboxValue(val)
|
||||
if type(val) == "table" then
|
||||
local nt = {}
|
||||
for k, v in pairs(val) do
|
||||
local sk, sv = sandboxValue(k), sandboxValue(v)
|
||||
if sk ~= nil then nt[sk] = sv end
|
||||
end
|
||||
return nt
|
||||
end
|
||||
if type(val) == "userdata" then
|
||||
-- Light userdata shall never escape our shi!
|
||||
-- This matches how OC wraps it
|
||||
-- TODO: just make our own shi with fields and stuff
|
||||
-- like a component proxy, because this sucks
|
||||
local userMeta = {
|
||||
__gc = function()
|
||||
userdata.free(val)
|
||||
end,
|
||||
}
|
||||
|
||||
local wrapped = {type = "userdata"}
|
||||
for name, m in pairs(userdata.methods(val)) do
|
||||
wrapped[name] = {
|
||||
name = name,
|
||||
proxy = wrapped,
|
||||
}
|
||||
setmetatable(wrapped[name], {
|
||||
__tostring = function() return m.doc or "function" end,
|
||||
__call = function(_, ...)
|
||||
return userdata.invoke(val, name, ...)
|
||||
end,
|
||||
})
|
||||
end
|
||||
return setmetatable(wrapped, userMeta)
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
||||
local function realInvoke(address, method, ...)
|
||||
local t = {pcall(cinvoke, address, method, ...)}
|
||||
if not _SYNCED and not os.getenv("NN_FAST") then
|
||||
@@ -83,6 +121,8 @@ local function realInvoke(address, method, ...)
|
||||
print("got", table.unpack(t))
|
||||
end
|
||||
|
||||
for i=1,#t do t[i] = sandboxValue(t[i]) end
|
||||
|
||||
if t[1] then
|
||||
return table.unpack(t, 2)
|
||||
end
|
||||
@@ -477,6 +517,9 @@ sandbox = {
|
||||
},
|
||||
|
||||
utf8 = utf8,
|
||||
|
||||
-- unsafe, don't care
|
||||
userdata = userdata,
|
||||
}
|
||||
sandbox._G = sandbox
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
|
||||
return NN_OK;
|
||||
case NN_COMP_DROP:
|
||||
return NN_OK;
|
||||
case NN_COMP_SIGNAL:
|
||||
case NN_COMP_USERDATA:
|
||||
return NN_OK;
|
||||
}
|
||||
return NN_OK;
|
||||
|
||||
391
src/neonucleus.c
391
src/neonucleus.c
@@ -1064,6 +1064,11 @@ nn_Exit nn_resizeDeviceInfoArray(nn_Context *ctx, nn_DeviceInfoArray *arr, size_
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
typedef struct nn_Userdata {
|
||||
void *state;
|
||||
char *compAddress;
|
||||
} nn_Userdata;
|
||||
|
||||
typedef struct nn_Computer {
|
||||
nn_ComputerState state;
|
||||
nn_Universe *universe;
|
||||
@@ -1092,6 +1097,7 @@ typedef struct nn_Computer {
|
||||
nn_Architecture archs[NN_MAX_ARCHITECTURES];
|
||||
nn_Signal signals[NN_MAX_SIGNALS];
|
||||
char *users[NN_MAX_USERS];
|
||||
nn_Userdata uservals[NN_MAX_USERDATA];
|
||||
} nn_Computer;
|
||||
|
||||
nn_Universe *nn_createUniverse(nn_Context *ctx, void *userdata) {
|
||||
@@ -1220,6 +1226,7 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char
|
||||
c->idleTimestamp = 0;
|
||||
// set to empty string
|
||||
c->errorBuffer[0] = '\0';
|
||||
for(size_t i = 0; i < NN_MAX_USERDATA; i++) c->uservals[i].state = NULL;
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -1430,8 +1437,9 @@ void nn_destroyComputer(nn_Computer *computer) {
|
||||
nn_strfree(ctx, computer->users[i]);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < NN_MAX_USERDATA; i++) nn_freeUserdata(computer, i);
|
||||
|
||||
for(nn_ComponentEntry *c = nn_hashIterate(&computer->components, NULL); c != NULL; c = nn_hashIterate(&computer->components, c)) {
|
||||
nn_signalComponent(c->comp, computer, NN_CSIGUNMOUNTED);
|
||||
nn_dropComponent(c->comp);
|
||||
}
|
||||
|
||||
@@ -2061,7 +2069,6 @@ nn_Exit nn_mountComponent(nn_Computer *c, nn_Component *comp, int slot, bool sil
|
||||
};
|
||||
if(!nn_hashPut(&c->components, &ent)) return NN_ELIMIT;
|
||||
nn_retainComponent(comp);
|
||||
nn_signalComponent(comp, c, NN_CSIGMOUNTED);
|
||||
if(c->state == NN_RUNNING && !silent) {
|
||||
return nn_pushComponentAdded(c, comp->address, comp->type);
|
||||
}
|
||||
@@ -2078,7 +2085,6 @@ nn_Exit nn_unmountComponent(nn_Computer *c, const char *address, bool silent) {
|
||||
if(c->state == NN_RUNNING && !silent) {
|
||||
e = nn_pushComponentRemoved(c, address, comp->type);
|
||||
}
|
||||
nn_signalComponent(comp, c, NN_CSIGUNMOUNTED);
|
||||
nn_dropComponent(comp);
|
||||
return e;
|
||||
}
|
||||
@@ -2184,16 +2190,250 @@ nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
nn_Exit nn_signalComponent(nn_Component *component, nn_Computer *computer, const char *signal) {
|
||||
nn_ComponentRequest req;
|
||||
req.ctx = &component->universe->ctx;
|
||||
req.computer = computer;
|
||||
req.state = component->state;
|
||||
req.classState = component->classState;
|
||||
req.compAddress = component->address;
|
||||
req.action = NN_COMP_SIGNAL;
|
||||
req.signal = signal;
|
||||
return component->handler(&req);
|
||||
int nn_allocUserdata(nn_Computer *computer, void *state, const char *compAddress) {
|
||||
for(size_t i = 0; i < NN_MAX_USERDATA; i++) {
|
||||
if(nn_isUserdataValid(computer, i)) continue;
|
||||
char *comp = nn_strdup(nn_getComputerContext(computer), compAddress);
|
||||
if(comp == NULL) return -1;
|
||||
computer->uservals[i].state = state;
|
||||
computer->uservals[i].compAddress = comp;
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Frees a userdata index.
|
||||
void nn_freeUserdata(nn_Computer *computer, size_t userdata) {
|
||||
if(!nn_isUserdataValid(computer, userdata)) return;
|
||||
nn_Context *ctx = nn_getComputerContext(computer);
|
||||
nn_Userdata *user = computer->uservals + userdata;
|
||||
|
||||
nn_UserdataRequest ureq = {
|
||||
.state = user->state,
|
||||
.action = NN_USER_DROP,
|
||||
};
|
||||
|
||||
nn_Component *c = nn_getComponent(computer, user->compAddress);
|
||||
// really, we should *panic*, as this is a BAD state
|
||||
if(c == NULL) return;
|
||||
|
||||
nn_ComponentRequest creq = {
|
||||
.ctx = ctx,
|
||||
.computer = computer,
|
||||
.state = c->state,
|
||||
.classState = c->classState,
|
||||
.compAddress = c->address,
|
||||
.action = NN_COMP_USERDATA,
|
||||
.methodIdx = 0,
|
||||
.user = &ureq,
|
||||
};
|
||||
|
||||
// errors in here are catastrophic
|
||||
c->handler(&creq);
|
||||
|
||||
user->state = NULL;
|
||||
nn_strfree(ctx, user->compAddress);
|
||||
}
|
||||
|
||||
// Returns whether the userdata index is valid
|
||||
bool nn_isUserdataValid(nn_Computer *computer, size_t userdata) {
|
||||
if(userdata >= NN_MAX_USERDATA) return false;
|
||||
return computer->uservals[userdata].state != NULL;
|
||||
}
|
||||
|
||||
// If compAddress is correct and userdata is valid, returns the state pointer.
|
||||
// If not, returns NULL, to prevent UB.
|
||||
void *nn_unwrapUserdata(nn_Computer *computer, size_t userdata, const char *compAddress) {
|
||||
if(!nn_isUserdataValid(computer, userdata)) return NULL;
|
||||
nn_Userdata user = computer->uservals[userdata];
|
||||
if(nn_strcmp(user.compAddress, compAddress) != 0) return NULL;
|
||||
return user.state;
|
||||
}
|
||||
|
||||
// gets the component address which manages this userdata
|
||||
const char *nn_getUserdataComponent(nn_Computer *computer, size_t userdata) {
|
||||
if(!nn_isUserdataValid(computer, userdata)) return NULL;
|
||||
return computer->uservals[userdata].compAddress;
|
||||
}
|
||||
|
||||
// Gets information about a method of this userdata, by index.
|
||||
// If idx is out of bounds, this returns true, which means to stop iteration.
|
||||
// If method->name is NULL, the method should be skipped.
|
||||
bool nn_getUserdataMethod(nn_Computer *computer, size_t userdata, size_t idx, nn_Method *method) {
|
||||
if(!nn_isUserdataValid(computer, userdata)) return true;
|
||||
nn_Context *ctx = nn_getComputerContext(computer);
|
||||
nn_Userdata *user = computer->uservals + userdata;
|
||||
|
||||
nn_UserdataRequest ureq = {
|
||||
.state = user->state,
|
||||
.action = NN_USER_GETMETHOD,
|
||||
.getmethod.method = method,
|
||||
.getmethod.idx = idx,
|
||||
};
|
||||
|
||||
nn_Component *c = nn_getComponent(computer, user->compAddress);
|
||||
// really, we should *panic*, as this is a BAD state
|
||||
if(c == NULL) return true;
|
||||
|
||||
nn_ComponentRequest creq = {
|
||||
.ctx = ctx,
|
||||
.computer = computer,
|
||||
.state = c->state,
|
||||
.classState = c->classState,
|
||||
.compAddress = c->address,
|
||||
.action = NN_COMP_USERDATA,
|
||||
.methodIdx = 0,
|
||||
.user = &ureq,
|
||||
};
|
||||
|
||||
// errors in here are catastrophic
|
||||
if(c->handler(&creq)) return true;
|
||||
return ureq.getmethod.method == NULL;
|
||||
}
|
||||
|
||||
// Invokes a method on some userdata, same semantics as nn_invokeComponent
|
||||
nn_Exit nn_invokeUserdata(nn_Computer *computer, size_t userdata, const char *method) {
|
||||
if(!nn_isUserdataValid(computer, userdata)) return true;
|
||||
nn_Context *ctx = nn_getComputerContext(computer);
|
||||
nn_Userdata *user = computer->uservals + userdata;
|
||||
|
||||
nn_UserdataRequest ureq = {
|
||||
.state = user->state,
|
||||
.action = NN_USER_INVOKE,
|
||||
.invoke.method = method,
|
||||
.invoke.returnCount = 0,
|
||||
};
|
||||
|
||||
nn_Component *c = nn_getComponent(computer, user->compAddress);
|
||||
// really, we should *panic*, as this is a BAD state
|
||||
if(c == NULL) return true;
|
||||
|
||||
nn_ComponentRequest creq = {
|
||||
.ctx = ctx,
|
||||
.computer = computer,
|
||||
.state = c->state,
|
||||
.classState = c->classState,
|
||||
.compAddress = c->address,
|
||||
.action = NN_COMP_USERDATA,
|
||||
.methodIdx = 0,
|
||||
.user = &ureq,
|
||||
};
|
||||
|
||||
// prepare stack for call
|
||||
while(nn_getstacksize(computer) > 0) {
|
||||
if(!nn_isnull(computer, nn_getstacksize(computer) - 1)) break;
|
||||
nn_pop(computer);
|
||||
}
|
||||
|
||||
// errors in here are catastrophic
|
||||
nn_Exit e = c->handler(&creq);
|
||||
if(e) {
|
||||
if(e != NN_EBADCALL) nn_setErrorFromExit(computer, e);
|
||||
nn_clearstack(computer);
|
||||
return e;
|
||||
}
|
||||
|
||||
size_t endOfTrim = computer->stackSize - ureq.invoke.returnCount;
|
||||
for(size_t i = 0; i < endOfTrim; i++) {
|
||||
nn_dropValue(computer->callstack[i]);
|
||||
}
|
||||
for(size_t i = endOfTrim; i < computer->stackSize; i++) {
|
||||
computer->callstack[i - endOfTrim] = computer->callstack[i];
|
||||
}
|
||||
computer->stackSize = ureq.invoke.returnCount;
|
||||
|
||||
if(nn_getEnergy(computer) <= 0) {
|
||||
nn_setError(computer, "out of energy");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
// Serializes the userdata into a buffer and pushes it as a string.
|
||||
// Make sure to keep track of its index and component address!
|
||||
nn_Exit nn_serializeUserdata(nn_Computer *computer, size_t userdata) {
|
||||
if(!nn_isUserdataValid(computer, userdata)) return true;
|
||||
nn_Context *ctx = nn_getComputerContext(computer);
|
||||
nn_Userdata *user = computer->uservals + userdata;
|
||||
|
||||
nn_UserdataRequest ureq = {
|
||||
.state = user->state,
|
||||
.action = NN_USER_SERIALIZE,
|
||||
};
|
||||
|
||||
nn_Component *c = nn_getComponent(computer, user->compAddress);
|
||||
// really, we should *panic*, as this is a BAD state
|
||||
if(c == NULL) return true;
|
||||
|
||||
nn_ComponentRequest creq = {
|
||||
.ctx = ctx,
|
||||
.computer = computer,
|
||||
.state = c->state,
|
||||
.classState = c->classState,
|
||||
.compAddress = c->address,
|
||||
.action = NN_COMP_USERDATA,
|
||||
.methodIdx = 0,
|
||||
.user = &ureq,
|
||||
};
|
||||
|
||||
// errors in here are catastrophic
|
||||
return c->handler(&creq);
|
||||
}
|
||||
|
||||
// Deserializes userdata at a particular index.
|
||||
// NOTE: if the component does not exist, or the userdata index is already taken, this errors.
|
||||
nn_Exit nn_deserializeUserdata(nn_Computer *computer, size_t userdata, const char *compAddress, const char *buf, size_t len) {
|
||||
if(userdata >= NN_MAX_USERDATA) {
|
||||
nn_setError(computer, "invalid slot");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
|
||||
if(nn_isUserdataValid(computer, userdata)) {
|
||||
nn_setError(computer, "slot taken");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
|
||||
nn_Component *c = nn_getComponent(computer, compAddress);
|
||||
if(c == NULL) {
|
||||
nn_setError(computer, "no such component");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
|
||||
nn_Context *ctx = nn_getComputerContext(computer);
|
||||
nn_Userdata *user = computer->uservals + userdata;
|
||||
|
||||
nn_UserdataRequest ureq = {
|
||||
.state = user->state,
|
||||
.action = NN_USER_DESERIALIZE,
|
||||
.deserialize.data = buf,
|
||||
.deserialize.len = len,
|
||||
};
|
||||
|
||||
nn_ComponentRequest creq = {
|
||||
.ctx = ctx,
|
||||
.computer = computer,
|
||||
.state = c->state,
|
||||
.classState = c->classState,
|
||||
.compAddress = c->address,
|
||||
.action = NN_COMP_USERDATA,
|
||||
.methodIdx = 0,
|
||||
.user = &ureq,
|
||||
};
|
||||
|
||||
char *compAddr = nn_strdup(ctx, compAddress);
|
||||
if(compAddr == NULL) return NN_ENOMEM;
|
||||
|
||||
// errors in here are catastrophic
|
||||
nn_Exit e = c->handler(&creq);
|
||||
if(e) {
|
||||
nn_strfree(ctx, compAddr);
|
||||
return e;
|
||||
}
|
||||
|
||||
user->state = ureq.state;
|
||||
user->compAddress = compAddr;
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
static void nn_retainValue(nn_Value val) {
|
||||
@@ -3906,7 +4146,7 @@ typedef enum nn_CompNum {
|
||||
|
||||
static nn_Exit nn_computerHandler(nn_ComponentRequest *req) {
|
||||
if(req->action == NN_COMP_DROP) return NN_OK;
|
||||
if(req->action == NN_COMP_SIGNAL) return NN_OK;
|
||||
if(req->action == NN_COMP_USERDATA) return NN_OK;
|
||||
nn_Computer *src = req->computer;
|
||||
if(src) nn_setError(src, "computer: not implemented yet");
|
||||
return NN_EBADCALL;
|
||||
@@ -3964,7 +4204,7 @@ typedef struct nn_EEState {
|
||||
} nn_EEState;
|
||||
|
||||
static nn_Exit nn_eepromHandler(nn_ComponentRequest *req) {
|
||||
if(req->action == NN_COMP_SIGNAL) return NN_OK;
|
||||
if(req->action == NN_COMP_USERDATA) return NN_OK;
|
||||
if(req->action == NN_COMP_CHECKMETHOD) return NN_OK;
|
||||
nn_EEState *state = req->classState;
|
||||
nn_EEPROMRequest ereq;
|
||||
@@ -4230,7 +4470,7 @@ static nn_Exit nn_fsPathCheck(nn_Computer *C, char buf[NN_MAX_PATH], const char
|
||||
}
|
||||
|
||||
static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
|
||||
if(req->action == NN_COMP_SIGNAL) return NN_OK;
|
||||
if(req->action == NN_COMP_USERDATA) return NN_OK;
|
||||
if(req->action == NN_COMP_CHECKMETHOD) return NN_OK;
|
||||
nn_Context *ctx = req->ctx;
|
||||
nn_FSState *state = req->classState;
|
||||
@@ -4650,7 +4890,7 @@ static nn_Exit nn_drvHandler(nn_ComponentRequest *request) {
|
||||
dreq.drv = &state->drive;
|
||||
nn_Exit e;
|
||||
|
||||
if(request->action == NN_COMP_SIGNAL) return NN_OK;
|
||||
if(request->action == NN_COMP_USERDATA) return NN_OK;
|
||||
if(request->action == NN_COMP_CHECKMETHOD) return NN_OK;
|
||||
|
||||
if(request->action == NN_COMP_DROP) {
|
||||
@@ -4850,7 +5090,7 @@ static nn_Exit nn_flashHandler(nn_ComponentRequest *request) {
|
||||
freq.flash = &state->flash;
|
||||
nn_Exit e;
|
||||
|
||||
if(request->action == NN_COMP_SIGNAL) return NN_OK;
|
||||
if(request->action == NN_COMP_USERDATA) return NN_OK;
|
||||
if(request->action == NN_COMP_CHECKMETHOD) return NN_OK;
|
||||
|
||||
if(request->action == NN_COMP_DROP) {
|
||||
@@ -5069,7 +5309,7 @@ typedef struct nn_ScreenClassState {
|
||||
} nn_ScreenClassState;
|
||||
|
||||
static nn_Exit nn_screenHandler(nn_ComponentRequest *req) {
|
||||
if(req->action == NN_COMP_SIGNAL) return NN_OK;
|
||||
if(req->action == NN_COMP_USERDATA) return NN_OK;
|
||||
nn_Context *ctx = req->ctx;
|
||||
nn_ScreenClassState *cls = req->classState;
|
||||
nn_Computer *C = req->computer;
|
||||
@@ -5335,7 +5575,7 @@ typedef struct nn_GPUClassState {
|
||||
|
||||
static nn_Exit nn_gpuHandler(nn_ComponentRequest *req) {
|
||||
if(req->action == NN_COMP_CHECKMETHOD) return NN_OK;
|
||||
if(req->action == NN_COMP_SIGNAL) return NN_OK;
|
||||
if(req->action == NN_COMP_USERDATA) return NN_OK;
|
||||
nn_Context *ctx = req->ctx;
|
||||
nn_GPUClassState *cls = req->classState;
|
||||
nn_Computer *C = req->computer;
|
||||
@@ -6125,15 +6365,22 @@ nn_DataCard nn_defaultDataCards[3] = {
|
||||
.assymetricCost = 10.0,
|
||||
},
|
||||
};
|
||||
|
||||
typedef struct nn_DataKey {
|
||||
bool isPublic;
|
||||
unsigned short bitlen;
|
||||
size_t bytelen;
|
||||
char bytes[];
|
||||
} nn_DataKey;
|
||||
|
||||
static nn_Exit nn_dataHandler(nn_ComponentRequest *req) {
|
||||
if(req->action == NN_COMP_SIGNAL) return NN_OK;
|
||||
nn_Context *ctx = req->ctx;
|
||||
nn_DataState *state = req->classState;
|
||||
nn_Computer *C = req->computer;
|
||||
|
||||
nn_DataCard dataCard = state->dataCard;
|
||||
nn_DataNum method = req->methodIdx;
|
||||
|
||||
|
||||
if(req->action == NN_COMP_CHECKMETHOD) {
|
||||
if(method == NN_DATANUM_SHA256 || method == NN_DATANUM_MD5) {
|
||||
req->methodEnabled = dataCard.canHash;
|
||||
@@ -6149,6 +6396,84 @@ static nn_Exit nn_dataHandler(nn_ComponentRequest *req) {
|
||||
}
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
if(req->action == NN_COMP_USERDATA) {
|
||||
nn_UserdataRequest *user = req->user;
|
||||
nn_DataKey *key = user->state;
|
||||
|
||||
if(user->action == NN_USER_DROP) {
|
||||
nn_free(ctx, key, sizeof(*key) + key->bytelen);
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
if(user->action == NN_USER_SERIALIZE) {
|
||||
// its assumed bytelen is not ridiculous
|
||||
size_t serlen = 2 + key->bytelen;
|
||||
NN_VLA(unsigned char, ser, serlen);
|
||||
unsigned short bitsAndPub = (key->bitlen * 2) + (key->isPublic ? 1 : 0);
|
||||
ser[0] = (bitsAndPub >> 0) & 0xFF;
|
||||
ser[1] = (bitsAndPub >> 8) & 0xFF;
|
||||
nn_memcpy(ser + 2, key->bytes, key->bytelen);
|
||||
return nn_pushlstring(C, (const char *)ser, serlen);
|
||||
}
|
||||
|
||||
if(user->action == NN_USER_DESERIALIZE) {
|
||||
size_t serlen = user->deserialize.len;
|
||||
const unsigned char *ser = (const unsigned char *)user->deserialize.data;
|
||||
size_t bytelen = serlen - 2;
|
||||
unsigned short bitsAndPub = ((unsigned short)ser[0]) + (((unsigned short)ser[1]) << 8);
|
||||
key = nn_alloc(ctx, sizeof(*key) + bytelen);
|
||||
if(key == NULL) return NN_ENOMEM;
|
||||
key->isPublic = (bitsAndPub&1) != 0;
|
||||
key->bitlen = bitsAndPub/2;
|
||||
key->bytelen = bytelen;
|
||||
nn_memcpy(key->bytes, ser + 2, key->bytelen);
|
||||
user->state = key;
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
if(user->action == NN_USER_GETMETHOD) {
|
||||
size_t idx = user->getmethod.idx;
|
||||
nn_Method *m = user->getmethod.method;
|
||||
if(idx == 0) {
|
||||
m->name = "serialize";
|
||||
m->doc = "function(): string - Serializes the key to its array of bytes";
|
||||
m->flags = NN_DIRECT;
|
||||
} else if(idx == 1) {
|
||||
m->name = "keyType";
|
||||
m->doc = "function(): string - Gets the type of the key (ec-public or ec-private)";
|
||||
m->flags = NN_DIRECT;
|
||||
} else if(idx == 2) {
|
||||
m->name = "isPublic";
|
||||
m->doc = "function(): boolean - Returns whether this key is public or not";
|
||||
m->flags = NN_DIRECT;
|
||||
} else {
|
||||
user->getmethod.method = NULL;
|
||||
}
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
if(user->action == NN_USER_INVOKE) {
|
||||
const char *method = user->invoke.method;
|
||||
if(nn_strcmp(method, "isPublic") == 0) {
|
||||
user->invoke.returnCount = 1;
|
||||
return nn_pushbool(C, key->isPublic);
|
||||
}
|
||||
if(nn_strcmp(method, "keyType") == 0) {
|
||||
user->invoke.returnCount = 1;
|
||||
return nn_pushstring(C, key->isPublic ? "ec-public" : "ec-private");
|
||||
}
|
||||
if(nn_strcmp(method, "serialize") == 0) {
|
||||
user->invoke.returnCount = 1;
|
||||
return nn_pushlstring(C, key->bytes, key->bytelen);
|
||||
}
|
||||
|
||||
nn_setError(C, "no such method");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
nn_DataCardRequest dreq;
|
||||
dreq.ctx = ctx;
|
||||
@@ -6332,6 +6657,26 @@ static nn_Exit nn_dataHandler(nn_ComponentRequest *req) {
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
if(method == NN_DATANUM_GENKEYPAIR) {
|
||||
e = nn_defaultinteger(C, 0, 256);
|
||||
if(e) return e;
|
||||
if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL;
|
||||
size_t bitLen = nn_tointeger(C, 0);
|
||||
if(bitLen != 256 && bitLen != 384) {
|
||||
nn_setError(C, "invalid bit length (only 256-bit and 384-bit are supported)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
// TODO: this is test code, no OOM check. Implement the correct version and make sure it handles OOMs correctly
|
||||
size_t keylen = bitLen / 8;
|
||||
nn_DataKey *key = nn_alloc(ctx, sizeof(*key) + keylen);
|
||||
key->isPublic = true;
|
||||
key->bitlen = bitLen;
|
||||
key->bytelen = keylen;
|
||||
nn_memset(key->bytes, 'A', key->bytelen);
|
||||
req->returnCount = 1;
|
||||
return nn_pushuserdata(C, nn_allocUserdata(C, key, req->compAddress));
|
||||
}
|
||||
|
||||
if(C) nn_setError(C, "data: not implemented yet");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
@@ -6415,7 +6760,7 @@ typedef struct nn_ModemState {
|
||||
} nn_ModemState;
|
||||
|
||||
static nn_Exit nn_modemHandler(nn_ComponentRequest *req) {
|
||||
if(req->action == NN_COMP_SIGNAL) return NN_OK;
|
||||
if(req->action == NN_COMP_USERDATA) return NN_OK;
|
||||
nn_Context *ctx = req->ctx;
|
||||
nn_ModemState *state = req->classState;
|
||||
nn_Computer *C = req->computer;
|
||||
|
||||
@@ -659,13 +659,6 @@ typedef struct nn_Method {
|
||||
nn_MethodFlags flags;
|
||||
} nn_Method;
|
||||
|
||||
// component signals
|
||||
|
||||
// mounted
|
||||
#define NN_CSIGMOUNTED "mounted"
|
||||
// unmounted
|
||||
#define NN_CSIGUNMOUNTED "unmounted"
|
||||
|
||||
typedef enum nn_ComponentAction {
|
||||
// component dropped
|
||||
NN_COMP_DROP,
|
||||
@@ -674,10 +667,38 @@ typedef enum nn_ComponentAction {
|
||||
// checking if component method is enabled
|
||||
// (may be locked by tier)
|
||||
NN_COMP_CHECKMETHOD,
|
||||
// signal sent to the machine
|
||||
NN_COMP_SIGNAL,
|
||||
// userdata request
|
||||
NN_COMP_USERDATA,
|
||||
} nn_ComponentAction;
|
||||
|
||||
typedef enum nn_UserdataAction {
|
||||
NN_USER_DROP,
|
||||
NN_USER_SERIALIZE,
|
||||
NN_USER_DESERIALIZE,
|
||||
NN_USER_GETMETHOD,
|
||||
NN_USER_INVOKE,
|
||||
} nn_UserdataAction;
|
||||
|
||||
typedef struct nn_UserdataRequest {
|
||||
void *state;
|
||||
nn_UserdataAction action;
|
||||
union {
|
||||
struct {
|
||||
const char *data;
|
||||
size_t len;
|
||||
} deserialize;
|
||||
struct {
|
||||
size_t idx;
|
||||
// set to NULL if idx is out of bounds
|
||||
nn_Method *method;
|
||||
} getmethod;
|
||||
struct {
|
||||
const char *method;
|
||||
size_t returnCount;
|
||||
} invoke;
|
||||
};
|
||||
} nn_UserdataRequest;
|
||||
|
||||
typedef struct nn_ComponentRequest {
|
||||
nn_Context *ctx;
|
||||
nn_Computer *computer;
|
||||
@@ -692,8 +713,7 @@ typedef struct nn_ComponentRequest {
|
||||
size_t returnCount;
|
||||
// method enabled
|
||||
bool methodEnabled;
|
||||
// signal invocation
|
||||
const char *signal;
|
||||
nn_UserdataRequest *user;
|
||||
};
|
||||
} nn_ComponentRequest;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user