4.1 KiB
4.1 KiB
Coding style
NN and NCL have a very specific API and code style. This is meant to be consistent everywhere to keep the API and codebase easy to learn, navigate, use and maintain.
Separation of files
src/neonucleus.hfor the header to NeoNucleus, the engine. This provides the systems everything interfaces with.src/neonucleus.c, the implementation of stuff defined in its header file.src/ncomplib.hfor the header to the NeoNucleus Component Library, providing complete implementations for various components.src/ncomplib.cfor the implementation of stuff defined in its header file.src/main.cfor the test emulator used purely to try out the engine and see if it works.
The rationale for single C-file libraries is that they are easy to download and compile. This can simplify the process of updating and compiling into projects. The reason for using this over shared libraries is that NN makes no ABI stability guarantees. It also makes it super easy to vendor the library, meaning you can trivially pin to a specific version, and even a trivial build system can work. The reason for not going full STB single-file is that LSPs can struggle to give diagnostics for the implementation side.
General coding rules
- For actual source code, aim for 80 cols max on 4-space indentation. This isn't a hard rule, but breaking it should be avoided.
gotoshould be used for trivial cases, such as the classicgoto fail;pattern, but also to loop (goto retry;) or to skip (goto found;)- While commenting is fine, comments should not be needed most of the time. Avoid writing code which needs to be explained through comments, aim to ensure the code is readable.
- Prefer fixed capacities. This is useful to reduce the effect of memory hogging. If the fixed capacity is small enough to not use much memory, prefer pre-allocating the maximum capacity to simplify the code and reduce places that can OOM.
Component classes/implementations
- NN should provide component classes. These are component instances with a
void *state specified to them, which stores its data inclassState. Their job is to define the methods and its docs and validate the methods to convert them into requests specific to the component type, and send those requests to a handler (a simple function pointer). They should not have their own locks, they should not store their own state. - NCL should provide component implementations. These do not have custom state or handlers, as they are complete. Prefer functions which set internal state over many parameters in the constructors. NCL should handle the locking, as all components must be thread-safe, in which case you should avoid wrapping entire handlers in the locks, and instead prefer locking as late as possible and unlocking as early as possible. This is to give threads exclusive ownership over the potentially shared component for as little time as possible.
- Assume the component user is experimenting, a hacker, or has an RCE in their code. Make sure everything is validated, from internal state to arguments. Do not trust the user, we do not have the JVM to save us here.
- Assume anything that can error will error at least once and ensure recovery with an exit code and error message, never panic or forcefully crash. Assume memory allocation will eventually fail. Assume the filesystem will eventually break. Do not abort/exit/panic.
- Use an enum to keep track of method indexes and method count.
- Avoid separate functions for each method handler. Inline them into the single handler that handles dispatch. This not only net-shrinks the codebase,
but also means that navigating by searching for
== NN_<COMPONENT>_<REQUEST>)(in NCL) or== NN_<COMPONENT>NUM_<METHOD>)(in NN) will show the dispatch logic and implementation. - Properties which are always equal to a constant from the config, or simply has no dependency on state, must not be requests, and instead handled by the component class directly.
- All loops inside methods must be bound by something controlled by either the config or a compile-time constant, to prevent denial of service attacks on the worker thread.