This commit is contained in:
2026-05-07 22:48:55 +03:00
parent 2ccd3a84b5
commit 9b16a7b8e2
6 changed files with 533 additions and 48 deletions

13
TODO.md
View File

@@ -2,21 +2,8 @@
- make `computer` component use callbacks - make `computer` component use callbacks
- finish tmpfs (rework the whole thing) - finish tmpfs (rework the whole thing)
- userdata support
- get rid of component signals (useless) - get rid of component signals (useless)
## Userdata system
The idea is that the computer stores userdata as structs which have a pointer (the state) and a bound-component address.
When a component is removed, all of its associated userdata is instantly dropped.
For serialization, the userdata pushes a string storing the encoded byte buffer.
For deserialization, the userdata pops a string and decodes it.
To get component methods, requests are sent to get method info by index, and a NULL name means method is not allowed for that
particular userdata. Methods can obviously be invoked just like component data.
Serialization pushes the string on the stack, deserialization gets the buffer as ptr + len and sets the state pointer.
They are handled by components, thus if it is not mounted at deserialization time, the deserialization fails.
# To re-evaluate # To re-evaluate
- Exposing the internal non-resizing hashmap implementation. - Exposing the internal non-resizing hashmap implementation.

View File

@@ -600,6 +600,84 @@ static int luaArch_unicode_wtrunc(lua_State *L) {
return 1; 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) { static void luaArch_loadEnv(lua_State *L) {
lua_createtable(L, 0, 10); lua_createtable(L, 0, 10);
int computer = lua_gettop(L); int computer = lua_gettop(L);
@@ -644,6 +722,7 @@ static void luaArch_loadEnv(lua_State *L) {
lua_pushcfunction(L, luaArch_computer_getDeviceInfo); lua_pushcfunction(L, luaArch_computer_getDeviceInfo);
lua_setfield(L, computer, "getDeviceInfo"); lua_setfield(L, computer, "getDeviceInfo");
lua_setglobal(L, "computer"); lua_setglobal(L, "computer");
lua_createtable(L, 0, 10); lua_createtable(L, 0, 10);
int component = lua_gettop(L); int component = lua_gettop(L);
lua_pushcfunction(L, luaArch_component_list); lua_pushcfunction(L, luaArch_component_list);
@@ -663,6 +742,7 @@ static void luaArch_loadEnv(lua_State *L) {
lua_pushcfunction(L, luaArch_component_fields); lua_pushcfunction(L, luaArch_component_fields);
lua_setfield(L, component, "fields"); lua_setfield(L, component, "fields");
lua_setglobal(L, "component"); lua_setglobal(L, "component");
lua_createtable(L, 0, 10); lua_createtable(L, 0, 10);
int unicode = lua_gettop(L); int unicode = lua_gettop(L);
lua_pushcfunction(L, luaArch_unicode_char); lua_pushcfunction(L, luaArch_unicode_char);
@@ -676,6 +756,16 @@ static void luaArch_loadEnv(lua_State *L) {
lua_pushcfunction(L, luaArch_unicode_wtrunc); lua_pushcfunction(L, luaArch_unicode_wtrunc);
lua_setfield(L, unicode, "wtrunc"); lua_setfield(L, unicode, "wtrunc");
lua_setglobal(L, "unicode"); 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) { static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) {

View File

@@ -70,6 +70,44 @@ end
local syncedMethodStats 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 function realInvoke(address, method, ...)
local t = {pcall(cinvoke, address, method, ...)} local t = {pcall(cinvoke, address, method, ...)}
if not _SYNCED and not os.getenv("NN_FAST") then if not _SYNCED and not os.getenv("NN_FAST") then
@@ -83,6 +121,8 @@ local function realInvoke(address, method, ...)
print("got", table.unpack(t)) print("got", table.unpack(t))
end end
for i=1,#t do t[i] = sandboxValue(t[i]) end
if t[1] then if t[1] then
return table.unpack(t, 2) return table.unpack(t, 2)
end end
@@ -477,6 +517,9 @@ sandbox = {
}, },
utf8 = utf8, utf8 = utf8,
-- unsafe, don't care
userdata = userdata,
} }
sandbox._G = sandbox sandbox._G = sandbox

View File

@@ -42,7 +42,7 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
return NN_OK; return NN_OK;
case NN_COMP_DROP: case NN_COMP_DROP:
return NN_OK; return NN_OK;
case NN_COMP_SIGNAL: case NN_COMP_USERDATA:
return NN_OK; return NN_OK;
} }
return NN_OK; return NN_OK;

View File

@@ -1064,6 +1064,11 @@ nn_Exit nn_resizeDeviceInfoArray(nn_Context *ctx, nn_DeviceInfoArray *arr, size_
return NN_OK; return NN_OK;
} }
typedef struct nn_Userdata {
void *state;
char *compAddress;
} nn_Userdata;
typedef struct nn_Computer { typedef struct nn_Computer {
nn_ComputerState state; nn_ComputerState state;
nn_Universe *universe; nn_Universe *universe;
@@ -1092,6 +1097,7 @@ typedef struct nn_Computer {
nn_Architecture archs[NN_MAX_ARCHITECTURES]; nn_Architecture archs[NN_MAX_ARCHITECTURES];
nn_Signal signals[NN_MAX_SIGNALS]; nn_Signal signals[NN_MAX_SIGNALS];
char *users[NN_MAX_USERS]; char *users[NN_MAX_USERS];
nn_Userdata uservals[NN_MAX_USERDATA];
} nn_Computer; } nn_Computer;
nn_Universe *nn_createUniverse(nn_Context *ctx, void *userdata) { 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; c->idleTimestamp = 0;
// set to empty string // set to empty string
c->errorBuffer[0] = '\0'; c->errorBuffer[0] = '\0';
for(size_t i = 0; i < NN_MAX_USERDATA; i++) c->uservals[i].state = NULL;
return c; return c;
} }
@@ -1430,8 +1437,9 @@ void nn_destroyComputer(nn_Computer *computer) {
nn_strfree(ctx, computer->users[i]); 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)) { 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); 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; if(!nn_hashPut(&c->components, &ent)) return NN_ELIMIT;
nn_retainComponent(comp); nn_retainComponent(comp);
nn_signalComponent(comp, c, NN_CSIGMOUNTED);
if(c->state == NN_RUNNING && !silent) { if(c->state == NN_RUNNING && !silent) {
return nn_pushComponentAdded(c, comp->address, comp->type); 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) { if(c->state == NN_RUNNING && !silent) {
e = nn_pushComponentRemoved(c, address, comp->type); e = nn_pushComponentRemoved(c, address, comp->type);
} }
nn_signalComponent(comp, c, NN_CSIGUNMOUNTED);
nn_dropComponent(comp); nn_dropComponent(comp);
return e; return e;
} }
@@ -2184,16 +2190,250 @@ nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const
return NN_OK; return NN_OK;
} }
nn_Exit nn_signalComponent(nn_Component *component, nn_Computer *computer, const char *signal) { int nn_allocUserdata(nn_Computer *computer, void *state, const char *compAddress) {
nn_ComponentRequest req; for(size_t i = 0; i < NN_MAX_USERDATA; i++) {
req.ctx = &component->universe->ctx; if(nn_isUserdataValid(computer, i)) continue;
req.computer = computer; char *comp = nn_strdup(nn_getComputerContext(computer), compAddress);
req.state = component->state; if(comp == NULL) return -1;
req.classState = component->classState; computer->uservals[i].state = state;
req.compAddress = component->address; computer->uservals[i].compAddress = comp;
req.action = NN_COMP_SIGNAL; return i;
req.signal = signal; }
return component->handler(&req); 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) { static void nn_retainValue(nn_Value val) {
@@ -3906,7 +4146,7 @@ typedef enum nn_CompNum {
static nn_Exit nn_computerHandler(nn_ComponentRequest *req) { static nn_Exit nn_computerHandler(nn_ComponentRequest *req) {
if(req->action == NN_COMP_DROP) return NN_OK; 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; nn_Computer *src = req->computer;
if(src) nn_setError(src, "computer: not implemented yet"); if(src) nn_setError(src, "computer: not implemented yet");
return NN_EBADCALL; return NN_EBADCALL;
@@ -3964,7 +4204,7 @@ typedef struct nn_EEState {
} nn_EEState; } nn_EEState;
static nn_Exit nn_eepromHandler(nn_ComponentRequest *req) { 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; if(req->action == NN_COMP_CHECKMETHOD) return NN_OK;
nn_EEState *state = req->classState; nn_EEState *state = req->classState;
nn_EEPROMRequest ereq; 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) { 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; if(req->action == NN_COMP_CHECKMETHOD) return NN_OK;
nn_Context *ctx = req->ctx; nn_Context *ctx = req->ctx;
nn_FSState *state = req->classState; nn_FSState *state = req->classState;
@@ -4650,7 +4890,7 @@ static nn_Exit nn_drvHandler(nn_ComponentRequest *request) {
dreq.drv = &state->drive; dreq.drv = &state->drive;
nn_Exit e; 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_CHECKMETHOD) return NN_OK;
if(request->action == NN_COMP_DROP) { if(request->action == NN_COMP_DROP) {
@@ -4850,7 +5090,7 @@ static nn_Exit nn_flashHandler(nn_ComponentRequest *request) {
freq.flash = &state->flash; freq.flash = &state->flash;
nn_Exit e; 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_CHECKMETHOD) return NN_OK;
if(request->action == NN_COMP_DROP) { if(request->action == NN_COMP_DROP) {
@@ -5069,7 +5309,7 @@ typedef struct nn_ScreenClassState {
} nn_ScreenClassState; } nn_ScreenClassState;
static nn_Exit nn_screenHandler(nn_ComponentRequest *req) { 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_Context *ctx = req->ctx;
nn_ScreenClassState *cls = req->classState; nn_ScreenClassState *cls = req->classState;
nn_Computer *C = req->computer; nn_Computer *C = req->computer;
@@ -5335,7 +5575,7 @@ typedef struct nn_GPUClassState {
static nn_Exit nn_gpuHandler(nn_ComponentRequest *req) { static nn_Exit nn_gpuHandler(nn_ComponentRequest *req) {
if(req->action == NN_COMP_CHECKMETHOD) return NN_OK; 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_Context *ctx = req->ctx;
nn_GPUClassState *cls = req->classState; nn_GPUClassState *cls = req->classState;
nn_Computer *C = req->computer; nn_Computer *C = req->computer;
@@ -6125,8 +6365,15 @@ nn_DataCard nn_defaultDataCards[3] = {
.assymetricCost = 10.0, .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) { static nn_Exit nn_dataHandler(nn_ComponentRequest *req) {
if(req->action == NN_COMP_SIGNAL) return NN_OK;
nn_Context *ctx = req->ctx; nn_Context *ctx = req->ctx;
nn_DataState *state = req->classState; nn_DataState *state = req->classState;
nn_Computer *C = req->computer; nn_Computer *C = req->computer;
@@ -6150,6 +6397,84 @@ static nn_Exit nn_dataHandler(nn_ComponentRequest *req) {
return NN_OK; 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; nn_DataCardRequest dreq;
dreq.ctx = ctx; dreq.ctx = ctx;
dreq.computer = C; dreq.computer = C;
@@ -6332,6 +6657,26 @@ static nn_Exit nn_dataHandler(nn_ComponentRequest *req) {
return NN_OK; 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"); if(C) nn_setError(C, "data: not implemented yet");
return NN_EBADCALL; return NN_EBADCALL;
} }
@@ -6415,7 +6760,7 @@ typedef struct nn_ModemState {
} nn_ModemState; } nn_ModemState;
static nn_Exit nn_modemHandler(nn_ComponentRequest *req) { 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_Context *ctx = req->ctx;
nn_ModemState *state = req->classState; nn_ModemState *state = req->classState;
nn_Computer *C = req->computer; nn_Computer *C = req->computer;

View File

@@ -659,13 +659,6 @@ typedef struct nn_Method {
nn_MethodFlags flags; nn_MethodFlags flags;
} nn_Method; } nn_Method;
// component signals
// mounted
#define NN_CSIGMOUNTED "mounted"
// unmounted
#define NN_CSIGUNMOUNTED "unmounted"
typedef enum nn_ComponentAction { typedef enum nn_ComponentAction {
// component dropped // component dropped
NN_COMP_DROP, NN_COMP_DROP,
@@ -674,10 +667,38 @@ typedef enum nn_ComponentAction {
// checking if component method is enabled // checking if component method is enabled
// (may be locked by tier) // (may be locked by tier)
NN_COMP_CHECKMETHOD, NN_COMP_CHECKMETHOD,
// signal sent to the machine // userdata request
NN_COMP_SIGNAL, NN_COMP_USERDATA,
} nn_ComponentAction; } 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 { typedef struct nn_ComponentRequest {
nn_Context *ctx; nn_Context *ctx;
nn_Computer *computer; nn_Computer *computer;
@@ -692,8 +713,7 @@ typedef struct nn_ComponentRequest {
size_t returnCount; size_t returnCount;
// method enabled // method enabled
bool methodEnabled; bool methodEnabled;
// signal invocation nn_UserdataRequest *user;
const char *signal;
}; };
} nn_ComponentRequest; } nn_ComponentRequest;