6 * Maximum amount of creatable lists.
7 * Lists can be given endless amount of entities, only restricted by engine limitations.
9 const int IL_MAX = 128;
12 void IL_INIT(entity this);
14 void IL_DTOR(entity this);
20 * NULL cannot be present
21 * elements can only be present once
22 * a maximum of `IL_MAX` lists can exist at one time
23 * freed entities must be removed from the list
25 CLASS(IntrusiveList, Object)
26 ATTRIB(IntrusiveList, il_head, entity);
27 ATTRIB(IntrusiveList, il_tail, entity);
28 ATTRIB(IntrusiveList, il_nextfld, .entity, nil);
29 ATTRIB(IntrusiveList, il_prevfld, .entity, nil);
30 INIT(IntrusiveList) { IL_INIT(this); }
31 DESTRUCTOR(IntrusiveList) { IL_DTOR(this); }
32 ENDCLASS(IntrusiveList)
39 #define IL_NEW() NEW(IntrusiveList)
41 #define IL_EMPTY(this) (this.il_head == NULL)
43 #define IL_FIRST(this) (this.il_head)
44 #define IL_LAST(this) (this.il_tail)
45 #define IL_PEEK(this) (this.il_tail)
48 bool IL_CONTAINS(IntrusiveList this, entity it)
50 assert(this, return false);
51 return it.(this.il_nextfld) || this.il_head == it || this.il_tail == it;
58 entity IL_PUSH(IntrusiveList this, entity it)
60 assert(this, return NULL);
61 assert(it, return NULL);
62 .entity il_next = this.il_nextfld;
63 .entity il_prev = this.il_prevfld;
64 assert(!IL_CONTAINS(this, it), return NULL);
66 entity tail = it.(il_prev) = this.il_tail;
67 tail ? (tail.(il_next) = it) : this.il_head = it;
69 it.il_lists |= this.il_listmask;
77 entity IL_UNSHIFT(IntrusiveList this, entity it)
79 assert(this, return NULL);
80 assert(it, return NULL);
81 .entity il_next = this.il_nextfld;
82 .entity il_prev = this.il_prevfld;
83 assert(!IL_CONTAINS(this, it), return NULL);
85 entity head = it.(il_next) = this.il_head;
86 head ? (head.(il_prev) = it) : this.il_tail = it;
88 it.il_lists |= this.il_listmask;
96 entity IL_POP(IntrusiveList this)
98 assert(this, return NULL);
99 .entity il_next = this.il_nextfld;
100 .entity il_prev = this.il_prevfld;
102 if (!this.il_tail) return NULL;
103 entity it = this.il_tail;
104 entity prev = it.(il_prev);
105 if (prev) (this.il_tail = prev).(il_next) = NULL;
106 else this.il_head = this.il_tail = NULL;
114 entity IL_SHIFT(IntrusiveList this)
116 assert(this, return NULL);
117 .entity il_next = this.il_nextfld;
118 .entity il_prev = this.il_prevfld;
120 if (!this.il_head) return NULL;
121 entity it = this.il_head;
122 entity next = it.(il_next);
123 if (next) (this.il_head = next).(il_prev) = NULL;
124 else this.il_head = this.il_tail = NULL;
129 * Remove any element, anywhere in the list
132 void IL_REMOVE(IntrusiveList this, entity it)
134 assert(this, return);
135 .entity il_next = this.il_nextfld;
136 .entity il_prev = this.il_prevfld;
137 //assert(!IL_CONTAINS(this, it), return);
138 entity next = it.(il_next);
139 entity prev = it.(il_prev);
140 entity ohead = this.il_head;
141 entity otail = this.il_tail;
142 next ? next.(il_prev) = prev : this.il_tail = prev;
143 prev ? prev.(il_next) = next : this.il_head = next;
144 LOG_DEBUGF("remove %i (%i :: %i), head: %i -> %i, tail: %i -> %i", it, it.(il_prev), it.(il_next), ohead, this.il_head, otail, this.il_tail);
145 it.(il_next) = it.(il_prev) = NULL;
149 * Remove all elements
151 #define IL_CLEAR(this) \
153 IntrusiveList __il = this; \
155 .entity il_prev = __il.il_prevfld; \
156 IL_EACH(__il, true, it.(il_next) = it.(il_prev) = NULL); \
157 __il.il_head = __il.il_tail = NULL; \
163 #define IL_DELETE(this) \
169 #define IL_EACH(this, cond, body) \
171 IntrusiveList _il = this; \
173 .entity il_next = _il.il_nextfld; \
175 for (entity _next, _it = _il.il_head; _it; (_it = _next, ++i)) \
177 const noref entity it = _it; \
178 _next = it.(il_next); \
179 if (cond) { LAMBDA(body) } \
184 IntrusiveList il_links[IL_MAX];
185 .entity il_links_flds[IL_MAX * 2];
188 #define IL_FLOOR(n) ((n) | 0)
189 #define IL_CEIL(n) IL_FLOOR((n) + 0.5)
191 #define IL_LISTS_PER_BIT IL_CEIL(IL_MAX / (3 * 24))
194 void IL_INIT(IntrusiveList this)
196 .entity nextfld, prevfld;
197 for (int i = il_links_ptr; i < il_links_ptr + IL_MAX; ++i) {
199 if (idx >= IL_MAX) idx -= IL_MAX;
202 if (!il_links[idx]) {
203 il_links[idx] = this;
204 nextfld = il_links_flds[idx + 0];
205 prevfld = il_links_flds[idx + 1];
207 int bit = IL_FLOOR(id / IL_LISTS_PER_BIT);
208 if (bit < (1 * 24)) this.il_listmask = '1 0 0' * (1 << (bit - (0 * 24)));
209 else if (bit < (2 * 24)) this.il_listmask = '0 1 0' * (1 << (bit - (1 * 24)));
210 else if (bit < (3 * 24)) this.il_listmask = '0 0 1' * (1 << (bit - (2 * 24)));
212 il_links_ptr = id + 1;
213 if (il_links_ptr >= IL_MAX) il_links_ptr -= IL_MAX;
214 this.il_nextfld = nextfld;
215 this.il_prevfld = prevfld;
219 LOG_WARN("IntrusiveList overflow");
223 void IL_DTOR(IntrusiveList this)
226 il_links[this.il_id] = NULL;
233 // incompatible with CSQC, remove() clears entities
234 for (int i = 0; i < IL_MAX; ++i) {
235 IntrusiveList list = il_links[i];
237 .entity nextfld = list.il_nextfld;
238 for (entity next, it = list.il_head; it; it = next) {
249 // called when an entity is deleted with delete() / remove()
250 // or when a player disconnects
251 void ONREMOVE(entity this)
253 // remove 'this' from any intrusive lists it is on
254 vector lists = this.il_lists;
256 for (int i = 0; i < IL_MAX; ++i) {
257 IntrusiveList list = il_links[i];
258 if ((lists & list.il_listmask) && IL_CONTAINS(list, this)) {
259 IL_REMOVE(list, this);