This commit is contained in:
2026-05-07 00:13:52 +03:00
parent fa0910d730
commit 2ccd3a84b5
4 changed files with 68 additions and 33 deletions

12
TODO.md
View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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));
}

View File

@@ -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;
};
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);