Introduction
The script_state plugin owns a single, globally living (running
across the whole script's life) byte buffer, and exposes its address and
size to the script. State based on the buffer can be shared between the main script thread and callbacks by raw pointer — a pattern that predates the language's global / callback global variables and that is safe only when the two sides provably never touch the buffer at the same time. The plugin
works hand-in-hand with the blob plugin's RefFromPointer function: with
the returned address and size, you can construct a ref-blob view onto
the buffer.
Why it exists
DominScript's global variables live in two worlds — global on the main thread, callback global on the dispatcher thread — and the two worlds cannot name each other's variables; state crosses between them by a call (worker.Call), not by a read. This plugin is the older, lower-level alternative: one raw byte buffer that both sides map by pointer. There is no lock on it, so it is correct exactly when you guarantee the ordering: one side waits inside a call while the other touches the buffer, or the callback only ever runs while the main loop is blocked in a plugin call. Two valid patterns, then — the language's own (callback global + worker.Call, where the runtime provides the ordering) is the default; this one is for when the guarantee is yours. The OpenGL demo showed the second until it moved to the first. When in doubt, use map, queue or a Call. The
script_state plugin solves this with a plugin-scoped storage: the main
thread writes the state into the buffer, and the callback reads/writes
the same memory through a ref-blob (using GetPointer + GetSize). Both
sides see the same bytes.
Typical pattern with the blob plugin
Usually you describe the state with a struct and allocate the struct's bytes: `script_state.Allocate(structsize(AppState));`. Then in both the main thread and the callback you use `ref blob &S = blob.RefFromPointer(script_state.GetPointer(), script_state.GetSize()); S assign AppState;` for typed access. The plugin owns the buffer; the ref-blob is just a view onto it — the caller must therefore make sure no ref-blob is still in use before Free is called.
Loading the plugin
plugin "builtin:print"; plugin "../plugins/script_state/ScriptStatePlugin"; plugin "../plugins/blob/BlobPlugin";
A typical flow — allocating and accessing script state
struct AppState packet { i32 width; i32 height; double angle; } i32 main() { script_state.Allocate(structsize(AppState)); { ref blob &S = blob.RefFromPointer(script_state.GetPointer(), script_state.GetSize()); S assign AppState; S.width = 1280; S.height = 720; S.angle = 0.0; } // ... in callbacks the same ref blob can be recreated ... script_state.Free(); return(0); }
What to know about every function (the basics)
The plugin owns one global buffer; at most one allocation exists at a time. A new Allocate call DROPS the old buffer (its contents are lost) and allocates a new one.
Allocate fills the buffer with ZERO bytes: every byte is 0 initially (via calloc). Typed structures thus start in a zeroed state.
Allocate(0) frees: it does not fail; it just drops the buffer and returns 0 (GetPointer and GetSize also become 0).
A negative size raises a runtime error.
GetPointer is an integer: the raw memory address of the buffer (converted to int64). It does NOT return a pointer type; usage goes through blob.RefFromPointer.
GetSize is the currently allocated size in bytes — 0 when unallocated.
An error (stopping the script) is caused by a wrong argument count or type, a negative size, and memory exhaustion.
How to read the signatures
size is the desired buffer size in bytes. GetPointer returns an integer memory address (as int64), which blob.RefFromPointer converts back into a ref-blob. The type after the -> arrow is the return type.
Lifecycle
Allocating and freeing the internal buffer. Only one buffer exists at a time; every new Allocate drops the existing one.
script_state.Allocate
script_state.Allocate(size) -> intAllocates a zero-filled byte buffer of the given size. If a buffer already exists, it is dropped and a new one is allocated.
| Parameter | Type | Description |
|---|---|---|
| size | int | The desired buffer size in bytes (non-negative). 0 frees the existing buffer. |
The actual buffer size in bytes (size on a successful allocation, 0 after Allocate(0)). A failed allocation or a negative size raises a runtime error.
printf("%d\n", script_state.Allocate(64)); printf("%d\n", script_state.Allocate(128)); printf("%d\n", script_state.Allocate(0));
64 128 0
For initializing the script state: with the structsize(...) operator you can size the allocation exactly to fit the state struct. Reallocation (with a different size) drops the old contents — if you need to keep them, save them into another blob first.
script_state.Free
script_state.Free() -> intFrees the internal buffer. Safe to call even when unallocated.
This function takes no arguments.
Always 1.
script_state.Allocate(32); printf("%d\n", script_state.Free());
1
At the end of the script, or when replacing the script state. Before calling, make sure no ref-blob still points to the buffer — after Free the memory address is invalid.
Inspection
Querying the buffer's address and current size. With the returned values you can construct the ref-blob via blob.RefFromPointer.
script_state.GetPointer
script_state.GetPointer() -> intReturns the raw memory address of the internal buffer as an integer (int64).
This function takes no arguments.
The buffer's address; 0 when unallocated.
script_state.Allocate(64); printf("%d\n", (script_state.GetPointer() != 0)); script_state.Free(); printf("%d\n", script_state.GetPointer());
1 0
The first argument of blob.RefFromPointer. It is not meant for pointer arithmetic on its own — only as input to the blob plugin's ref-blob constructor.
script_state.GetSize
script_state.GetSize() -> intReturns the current size of the internal buffer in bytes.
This function takes no arguments.
The size in bytes; 0 when unallocated.
script_state.Allocate(128); printf("%d\n", script_state.GetSize()); script_state.Free(); printf("%d\n", script_state.GetSize());
128 0
The second argument of blob.RefFromPointer. Also useful on its own to check whether the script state is allocated (GetSize > 0).
Practical notes
What the script_state plugin is good for
Central script state: data the main thread and callbacks both need to access (animation angles, counters, configuration).
Sharing a small state block between main and callbacks when their access is provably serialized (the callback runs only while main waits inside a plugin call).
Combined with the blob plugin's RefFromPointer, any struct can be overlaid on the buffer — you get a typed state model.
Practical for resetting: Allocate(0) or Free frees the state in a single call.
Lifecycle rules
The plugin is designed for one buffer: a new Allocate drops the old one. If you need several independent state areas, it is more practical to organize the whole script state into ONE struct and allocate it with a single Allocate. After Free, GetPointer/GetSize return 0 and the previous address is invalid — any ref-blob still pointing to it becomes unusable.
Memory management
The plugin owns the buffer: Free or a new Allocate releases it. The script has only a view onto the memory (via blob.RefFromPointer), and the view owns nothing. On plugin shutdown the remaining buffer is freed automatically. The buffer starts at zero in every byte — due to calloc, there is no need for an explicit memset.
When this is NOT the right tool
If you need local, function-scoped state (not to be shared with callbacks), declaring the variable or blob in the function is simpler. If you want several parallel script-state areas, this plugin gives only one — organize a struct for several areas, or use your own blob.* allocations. If the data is only short-lived (does not outlive the function call), there is no need for this plugin.
Error handling
A wrong argument count or type, a negative size, and memory exhaustion are reported by the runtime as a runtime error, and the script stops. On a successful call, Allocate returns the actual size and Free always 1. GetPointer/GetSize called in the unallocated state return 0 — not an error, but a neutral state indicator.