Type/Pool System¶
The type system provides pool-based memory allocation, property accessors, constructors/destructors, and reference validation on top of KSP's array-based data model.
Defining a Type¶
1. Define Members¶
define MyType.MEMBERS := field1, field2, field3
define NodeEnv.MEMBERS := ...
node_id, ...
input_type, ...
flag.disable_paths_on_release, ...
sentVoiceCompose member lists from multiple sources:
define Voice.MEMBERS := Voice.BASE_MEMBERS, Voice.ADD_MEMBERS2. Provide Initial Values¶
declare NodeEnv.INIT[] := (...
-1, ... {node_id}
0, ... {input_type}
TRUE, ... {flag.disable_paths_on_release}
-1 ... {sentVoice}
)3. Wrap in def() Macro¶
macro Entry.def()
define Entry.MEMBERS := prev, next, data
declare Entry.INIT[] := (-1, -1, 0)
end macroCreating Pools¶
type.Create(#type#) / type.CreateCustomPool(#type#, #pool#)¶
type.Create(Entry) // Default pool: 524,288 elements
type.CreateCustomPool(NodeEnv, 1000000) // Custom pool sizeThis generates:
| Generated Item | Description |
|---|---|
#type#.field.* |
Const enumeration of field indices |
#type#.BLOCK |
Elements per object |
#type#.POOL_SIZE |
Pool size (aligned to block) |
#type#.pool[] |
Data array |
#type#.is_alloc[] |
Allocation status |
#type#.count |
Live object count |
#type#.#field# |
Property for each field |
Memory Layout¶
Pool: [obj0.f0][obj0.f1][obj0.f2][obj1.f0][obj1.f1][obj1.f2]...
|<----- BLOCK ------>| |<----- BLOCK ------>|
Object data starts at ref * BLOCK. Pool size is aligned: #pool# - (#pool# mod BLOCK).
Pool Functions¶
Generate with type.Functions(MyType).
Allocation¶
| Function | Description |
|---|---|
Type.new() -> ref |
Allocate, initialize, call constructor. Returns -1 if pool full |
Type.new.ICB() -> ref |
ICB variant for on init context |
Type.copy(src) -> ref |
Allocate, copy fields, call copy constructor |
Type.copy.ICB(src) -> ref |
ICB variant |
declare my_ref := MyType.new()
if my_ref # -1
MyType[my_ref].value := 42
end if
declare clone := Voice.copy(original)Deallocation¶
| Function | Description |
|---|---|
Type.delete(ref) |
Call destructor, release to pool |
Type.delete.ICB(ref) |
ICB variant |
Type.clear_pool() |
Delete all allocated objects |
Utilities¶
| Function | Description |
|---|---|
Type.init(ref) |
Reset fields to defaults |
Type.clear_mem(ref) |
Set all fields to TYPE.MEM_CLEAR |
Type.empty_fields(ref) |
Set all fields to -1 |
Type.copy_a_to_b(a, b) |
Copy all fields between objects |
Type.print(ref) |
Debug output |
Type.repr(ref) -> str |
String representation |
Property Access¶
Type[ref].field¶
declare note := Voice[vo_ref].note // getter
Voice[vo_ref].vel := 127 // setterCompiles to: Voice.pool[vo_ref * Voice.BLOCK + Voice.field.note]
Type[ref].access[index]¶
Dynamic field access for iteration:
for i := 0 to MyType.field.SIZE - 1
MyType[ref].access[i] := 0
end forField Enumeration¶
define Voice.MEMBERS := note, vel, pan, volume
// Generates: Voice.field.note=0, Voice.field.vel=1, Voice.field.pan=2, Voice.field.volume=3
// Voice.field.SIZE = 4Field indices for reset lists and conditional logic:
declare Voice.RESET_ON_NEW[] := (Voice.field.input, Voice.field.runtime, Voice.field.auto_release)Reference Validation¶
check_ref(#type#, #ref#)¶
define check_ref(#type#, #ref#) := (#ref# > -1 and #type#.is_alloc[#ref#] > 0)
if check_ref(Voice, vo_ref)
Voice[vo_ref].note := 60
end ifAlways validate after wait statements (objects may be deleted during waits):
ivls.wait_ms(100)
if check_ref(Voice, self)
Voice[self].volume := new_volume
end ifConstructor/Destructor Patterns¶
macro Voice.Constructor(#ref#)
declare ref := #ref#
__RUN_CB__(VoiceConstructor)
end macro
macro Voice.Destructor(#ref#)
declare ref := #ref#
__RUN_CB__(VoiceDestructor)
end macro
macro Entry.Destructor(ref)
declare prev := Entry[ref].prev
declare next := Entry[ref].next
if check_ref(Entry, prev)
Entry[prev].next := next
end if
if check_ref(Entry, next)
Entry[next].prev := prev
end if
end macroLocal Variable Helpers¶
| Macro | Description |
|---|---|
type.Locals(T, ref) |
Declare prefixed locals from object |
type.FillLocals(T, ref) |
Fill prefixed locals from object |
type.FillObj(T, ref) |
Write prefixed locals back to object |
type.RawLocals(T, ref) |
Declare unprefixed locals from object |
Complete Example¶
macro Entry.def()
define Entry.MEMBERS := prev, next, data
declare Entry.INIT[] := (-1, -1, 0)
end macro
macro Entry.Destructor(ref)
declare prev := Entry[ref].prev
declare next := Entry[ref].next
if check_ref(Entry, prev)
Entry[prev].next := next
end if
if check_ref(Entry, next)
Entry[next].prev := prev
end if
end macro
node Stl.Lists:
cb Init:
type.Create(Entry)
cb Functions:
type.Functions(Entry)
function list_append(head_ref, data) -> new_entry
new_entry := Entry.new()
Entry[new_entry].data := data
if check_ref(Entry, head_ref)
Entry[new_entry].prev := head_ref
Entry[head_ref].next := new_entry
end if
end function
end node