diff --git a/TODO.md b/TODO.md index ba5c37b..10c95f7 100644 --- a/TODO.md +++ b/TODO.md @@ -5,6 +5,18 @@ - userdata support - 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 - Exposing the internal non-resizing hashmap implementation. diff --git a/src/luaarch.c b/src/luaarch.c index 07111ba..33b9cac 100644 --- a/src/luaarch.c +++ b/src/luaarch.c @@ -73,6 +73,9 @@ static nn_Exit luaArch_luaToNN(luaArch *arch, lua_State *L, int luaIdx) { if(lua_isboolean(L, luaIdx)) { return nn_pushbool(C, lua_toboolean(L, luaIdx)); } + if(lua_isuserdata(L, luaIdx)) { + return nn_pushuserdata(C, (size_t)lua_touserdata(L, luaIdx)); + } luaL_error(L, "bad Lua value: %s", luaL_typename(L, luaIdx)); return NN_EBADSTATE; } @@ -112,6 +115,10 @@ static void luaArch_nnToLua(luaArch *arch, lua_State *L, size_t nnIdx) { nn_popn(C, len * 2); return; } + if(nn_isuserdata(C, nnIdx)) { + lua_pushlightuserdata(L, (void *)nn_touserdata(C, nnIdx)); + return; + } luaL_error(L, "bad NN value: %s", nn_typenameof(C, nnIdx)); } @@ -734,8 +741,11 @@ static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) { return NN_OK; } return NN_OK; - default: - break; + case NN_ARCH_SERIALIZE: + return nn_pushstring(computer, "lua stuff"); + case NN_ARCH_DESERIALIZE: + // do nothing + return NN_OK; } return NN_OK; } diff --git a/src/neonucleus.c b/src/neonucleus.c index b55b800..30c4fd7 100644 --- a/src/neonucleus.c +++ b/src/neonucleus.c @@ -1645,7 +1645,7 @@ nn_Exit nn_deserializeComputer(nn_Computer *computer, const char *buf, size_t bu return computer->arch.handler(&req); } -nn_Exit nn_serializeComputer(nn_Computer *computer, char **buf, size_t *buflen) { +nn_Exit nn_serializeComputer(nn_Computer *computer) { nn_ArchitectureRequest req; req.computer = computer; req.action = NN_ARCH_SERIALIZE; @@ -1655,24 +1655,9 @@ nn_Exit nn_serializeComputer(nn_Computer *computer, char **buf, size_t *buflen) nn_Exit e = computer->arch.handler(&req); if(e) return e; - *buf = req.memOut; - *buflen = req.memLen; - return NN_OK; } -nn_Exit nn_freeSerializedComputer(nn_Computer *computer, char *buf, size_t buflen) { - nn_ArchitectureRequest req; - req.computer = computer; - req.action = NN_ARCH_DROPSERIALIZED; - req.globalState = computer->arch.state; - req.localState = computer->archState; - req.memOut = buf; - req.memLen = buflen; - - return computer->arch.handler(&req); -} - void nn_setError(nn_Computer *computer, const char *s) { nn_setLError(computer, s, nn_strlen(s)); } diff --git a/src/neonucleus.h b/src/neonucleus.h index fc34100..b1cf22a 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -350,12 +350,10 @@ typedef enum nn_ArchitectureAction { NN_ARCH_TICK, // get the free memory NN_ARCH_FREEMEM, - // deserialize from an encoded state + // deserialize from an encoded state, passed into request. NN_ARCH_DESERIALIZE, - // serialize to an encoded state + // serialize to an encoded state, pushed it as a string NN_ARCH_SERIALIZE, - // drop the encoded buffer - NN_ARCH_DROPSERIALIZED, } nn_ArchitectureAction; typedef struct nn_ArchitectureRequest { @@ -373,12 +371,9 @@ typedef struct nn_ArchitectureRequest { bool synchronized; // in the case of NN_ARCH_FREEMEM, the free memory size_t freeMemory; - // in the case of NN_ARCH_DESERIALIZE, NN_ARCH_SERIALIZE and NN_ARCH_DROPSERIALIZED, the buffer. + // in the case of NN_ARCH_DESERIALIZE, the buffer. struct { - union { - char *memOut; - const char *memIn; - }; + const char *memIn; size_t memLen; }; }; @@ -540,14 +535,13 @@ double nn_getUptime(nn_Computer *computer); // Deserialize an encoded computer state. // Encoding depends on architecture. +// Will push it as a string and then call the architecture, which will decode it and pop it. nn_Exit nn_deserializeComputer(nn_Computer *computer, const char *buf, size_t buflen); // Serialize the computer state. // Encoding depends on architecture. -nn_Exit nn_serializeComputer(nn_Computer *computer, char **buf, size_t *buflen); - -// Free the serialized buffer. -nn_Exit nn_freeSerializedComputer(nn_Computer *computer, char *buf, size_t buflen); +// Pushes the output, if successful, as a string on the stack. +nn_Exit nn_serializeComputer(nn_Computer *computer); // address is copied. // It can be NULL if you wish to have no tmp address. @@ -771,8 +765,8 @@ void nn_getComponents(nn_Computer *c, const char **components); // invoke the component method. // Everything on-stack is taken as an argument. // Will pop off trailing nulls. -// Every remaining is what the component returned. -// In the case of +// Every remaining stack value is what the component returned. +// In the case of errors, the contents of the stack is undefined nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const char *method); // send a signal to a component. @@ -780,6 +774,40 @@ nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const // assumes one is specified. nn_Exit nn_signalComponent(nn_Component *component, nn_Computer *computer, const char *signal); +// Userdata!!!! + +// Allocates a userdata index. Returns -1 on failure or if there are too many. +int nn_allocUserdata(nn_Computer *computer, void *state, const char *compAddress); + +// Frees a userdata index. +void nn_freeUserdata(nn_Computer *computer, size_t userdata); + +// Returns whether the userdata index is valid +bool nn_isUserdataValid(nn_Computer *computer, size_t userdata); + +// 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); + +// gets the component address which manages this userdata +const char *nn_getUserdataComponent(nn_Computer *computer, size_t userdata); + +// 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); + +// Invokes a method on some userdata, same semantics as nn_invokeComponent +nn_Exit nn_invokeUserdata(nn_Computer *computer, size_t userdata, const char *method); + +// 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); + +// 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); + // Sets the call budget. // The default is 1,000. void nn_setCallBudget(nn_Computer *computer, size_t budget);