Line data Source code
1 : /* Copyright 2017 noseglasses <shinynoseglasses@gmail.com>
2 : *
3 : * This program is free software: you can redistribute it and/or modify
4 : * it under the terms of the GNU Lesser General Public License as published by
5 : * the Free Software Foundation, either version 3 of the License, or
6 : * (at your option) any later version.
7 : *
8 : * This program is distributed in the hope that it will be useful,
9 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 : * GNU Lesser General Public License for more details.
12 : *
13 : * You should have received a copy of the GNU Lesser General Public License
14 : * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 : */
16 :
17 : #include "detail/ppg_active_tokens_detail.h"
18 : #include "detail/ppg_token_detail.h"
19 : #include "detail/ppg_context_detail.h"
20 : #include "detail/ppg_malloc_detail.h"
21 :
22 : #include "ppg_debug.h"
23 :
24 : #include <assert.h>
25 : #include <string.h>
26 :
27 50 : void ppg_active_tokens_resize(PPG_Active_Tokens *active_tokens,
28 : PPG_Count new_size)
29 : {
30 50 : PPG_ASSERT(active_tokens);
31 :
32 50 : if(new_size <= active_tokens->max_tokens) { return; }
33 :
34 50 : PPG_Token__ **new_tokens
35 50 : = (PPG_Token__**)PPG_MALLOC(sizeof(PPG_Token__*)*new_size);
36 :
37 50 : active_tokens->max_tokens = new_size;
38 :
39 50 : if(active_tokens->tokens && (active_tokens->n_tokens > 0)) {
40 0 : memcpy(new_tokens, active_tokens->tokens,
41 0 : sizeof(PPG_Token__ *)*active_tokens->n_tokens);
42 : }
43 :
44 50 : active_tokens->tokens = new_tokens;
45 : }
46 :
47 41 : void ppg_active_tokens_init(PPG_Active_Tokens *active_tokens)
48 : {
49 41 : active_tokens->tokens = NULL;
50 41 : active_tokens->n_tokens = 0;
51 41 : active_tokens->max_tokens = 0;
52 :
53 41 : ppg_active_tokens_resize(active_tokens, PPG_MAX_ACTIVE_TOKENS);
54 :
55 10496 : for(size_t i = 0; i < PPG_MAX_ACTIVE_TOKENS; ++i) {
56 10455 : active_tokens->tokens[i] = NULL;
57 : }
58 41 : }
59 :
60 9 : void ppg_active_tokens_restore(PPG_Active_Tokens *active_tokens)
61 : {
62 9 : PPG_Count saved_size = active_tokens->max_tokens;
63 :
64 9 : active_tokens->tokens = NULL;
65 9 : active_tokens->max_tokens = 0; // This forces resize
66 :
67 9 : ppg_active_tokens_resize(active_tokens, saved_size);
68 9 : }
69 :
70 41 : void ppg_active_tokens_free(PPG_Active_Tokens *active_tokens)
71 : {
72 41 : if(!active_tokens->tokens) { return; }
73 :
74 41 : free(active_tokens->tokens);
75 :
76 41 : active_tokens->tokens = NULL;
77 : }
78 :
79 235 : static void ppg_active_tokens_add(PPG_Token__ *token)
80 : {
81 235 : PPG_GAT.tokens[PPG_GAT.n_tokens] = token;
82 :
83 235 : PPG_ASSERT(PPG_GAT.n_tokens < PPG_MAX_ACTIVE_TOKENS);
84 :
85 235 : ++PPG_GAT.n_tokens;
86 235 : }
87 :
88 235 : static void ppg_active_tokens_remove(PPG_Count id)
89 : {
90 565 : for(PPG_Count i = id; i < PPG_GAT.n_tokens - 1; ++i) {
91 330 : PPG_GAT.tokens[i] = PPG_GAT.tokens[i + 1];
92 : }
93 :
94 235 : --PPG_GAT.n_tokens;
95 235 : }
96 :
97 241 : static void ppg_active_tokens_search_remove(PPG_Token__ *token)
98 : {
99 253 : for(PPG_Count i = 0; i < PPG_GAT.n_tokens; ++i) {
100 247 : if(token == PPG_GAT.tokens[i]) {
101 235 : ppg_active_tokens_remove(i);
102 235 : break;
103 : }
104 : }
105 241 : }
106 :
107 337 : static void ppg_active_tokens_on_deactivation(PPG_Token__ *consumer,
108 : PPG_Count state,
109 : bool changed
110 : )
111 : {
112 : // The event deactivates an input
113 :
114 337 : if(consumer) {
115 :
116 337 : if(consumer->misc.flags & PPG_Token_Flags_Pedantic) {
117 :
118 0 : if( (state
119 : == PPG_Token_Matches)
120 0 : && (changed)) {
121 :
122 : // PPG_LOG(" Cs act & deact\n");
123 :
124 0 : if(consumer->misc.action_state == PPG_Action_Enabled) {
125 :
126 : // PPG_LOG(" T.a.a.\n");
127 :
128 0 : consumer->action.callback.func(true /* signal deactivation */,
129 : consumer->action.callback.user_data);
130 :
131 0 : consumer->misc.action_state = PPG_Action_Activation_Triggered;
132 :
133 0 : consumer->action.callback.func(false /* signal deactivation */,
134 : consumer->action.callback.user_data);
135 :
136 0 : consumer->misc.action_state = PPG_Action_Deactivation_Triggered;
137 : }
138 :
139 0 : ppg_active_tokens_search_remove(consumer);
140 : }
141 :
142 0 : return;
143 : }
144 :
145 : // It there is a consumer listed, this means that the event was
146 : // consumed by one of the tokens of the matching branch
147 :
148 337 : if(changed) {
149 :
150 : // Token state just changed...
151 :
152 260 : if(state
153 : == PPG_Token_Deactivation_In_Progress) {
154 :
155 : // PPG_LOG(" C. d.\n");
156 :
157 : // It turned from state "matching" to state
158 : // "deactivation in progress"
159 :
160 25 : if(consumer->misc.action_flags & PPG_Action_Deactivate_On_Token_Unmatch) {
161 :
162 0 : if(consumer->misc.action_state == PPG_Action_Enabled) {
163 :
164 : // PPG_LOG(" T.e.a.\n");
165 0 : consumer->action.callback.func(false /* signal deactivation */,
166 : consumer->action.callback.user_data);
167 :
168 0 : consumer->misc.action_state = PPG_Action_Deactivation_Triggered;
169 : }
170 : }
171 : }
172 235 : else if(state
173 : == PPG_Token_Finalized) {
174 :
175 : // PPG_LOG(" Causes finalization of 0x%" PRIXPTR "\n", (uintptr_t)consumer);
176 : // PPG_LOG("Fin\n");
177 : // The token just became initialized, i.e. all related inputs were deactivated
178 : // again.
179 :
180 235 : if( ((consumer->misc.action_flags & PPG_Action_Deactivate_On_Token_Unmatch) == 0)
181 0 : || (consumer->misc.action_state != PPG_Action_Deactivation_Triggered)) {
182 :
183 235 : if(consumer->misc.action_state == PPG_Action_Enabled) {
184 : // PPG_LOG(" T.a.a.\n");
185 2 : consumer->action.callback.func(true /* signal deactivation */,
186 : consumer->action.callback.user_data);
187 :
188 2 : consumer->misc.action_state = PPG_Action_Activation_Triggered;
189 : }
190 :
191 235 : if(consumer->misc.action_state == PPG_Action_Activation_Triggered) {
192 : // PPG_LOG(" T.d.a.\n");
193 85 : consumer->action.callback.func(false /* signal deactivation */,
194 : consumer->action.callback.user_data);
195 :
196 85 : consumer->misc.action_state = PPG_Action_Deactivation_Triggered;
197 : }
198 : }
199 :
200 : // Remove it from the active set as no further events will affect it
201 : //
202 235 : ppg_active_tokens_search_remove(consumer);
203 : }
204 : }
205 : }
206 : }
207 :
208 890 : bool ppg_active_tokens_check_consumption(
209 : PPG_Event *event)
210 : {
211 890 : if(PPG_GAT.n_tokens == 0) {
212 575 : return false;
213 : }
214 :
215 : // No consumer (token) is listed for the deactivation event.
216 : // This means that the input deactivation that
217 : // is represented by the token is related to an input from the
218 : // active set that has been registered in one of the previous pattern matching rounds
219 : // and whose inputs have not all been deactivated yet.
220 :
221 315 : PPG_Token__ *consumer = NULL;
222 315 : bool event_consumed = false;
223 : PPG_Count i;
224 315 : bool state_changed = false;
225 : PPG_Count new_state;
226 :
227 : // Thus, we first have to find it in the active token set.
228 : //
229 654 : for(i = 0; i < PPG_GAT.n_tokens; ++i) {
230 :
231 327 : consumer = PPG_GAT.tokens[i];
232 :
233 : // PPG_LOG(" consumer: 0x%" PRIXPTR "\n", (uintptr_t)consumer);
234 : // PPG_LOG(" consumer action state: %d\n", consumer->misc.action_state);
235 :
236 327 : PPG_Count old_state = consumer->misc.state;
237 :
238 327 : event_consumed = consumer
239 327 : ->vtable->match_event(
240 : consumer,
241 : event,
242 : true /*modify only if consuming*/
243 : );
244 :
245 327 : PPG_PRINT_TOKEN(consumer)
246 :
247 327 : if(!event_consumed) {
248 12 : continue;
249 : }
250 :
251 315 : new_state = consumer->misc.state;
252 :
253 315 : if(old_state != new_state) {
254 254 : state_changed = true;
255 : }
256 :
257 315 : break;
258 : }
259 :
260 315 : if(!event_consumed) {
261 0 : return false;
262 : }
263 :
264 : // PPG_LOG(" consumed\n");
265 : // PPG_LOG(" new_state: %d\n", new_state);
266 : // PPG_LOG(" state_changed: %d\n", state_changed);
267 :
268 : // The token just became initialized, i.e. all related inputs were deactivated
269 : // again.
270 :
271 315 : if((event->flags & PPG_Event_Active) == 0) {
272 :
273 311 : ppg_active_tokens_on_deactivation(consumer,
274 : new_state,
275 : state_changed);
276 :
277 : }
278 :
279 : // One of the tokens from the active set consumed our deactivation event
280 :
281 : // Mark it, so the user can recognize it as consumed during event
282 : // buffer itearation.
283 : //
284 315 : event->flags |= PPG_Event_Considered;
285 :
286 315 : return true;
287 : }
288 :
289 469 : static void ppg_active_tokens_update_aux(
290 : PPG_Event_Queue_Entry *eqe,
291 : void *user_data)
292 : {
293 : PPG_UNUSED(user_data);
294 :
295 : // PPG_LOG("Event: Input 0x%d, active: %d\n",
296 : // eqe->event.input,
297 : // eqe->event.flags & PPG_Event_Active);
298 :
299 : // Check if the event has already been considered
300 : // This is the case for deactivation events that
301 : // have been consumed by unfinished tokens of a previous pattern match.
302 : //
303 469 : if(eqe->event.flags & PPG_Event_Considered) {
304 0 : return;
305 : }
306 :
307 469 : if(eqe->consumer) {
308 : // PPG_LOG(" consumer: 0x%" PRIXPTR "\n", (uintptr_t)eqe->consumer);
309 : // PPG_LOG(" consumer action state: %d\n", eqe->consumer->misc.action_state);
310 359 : PPG_PRINT_TOKEN(eqe->consumer)
311 : }
312 :
313 469 : if(eqe->event.flags & PPG_Event_Active) {
314 :
315 : // The event activates an input
316 :
317 333 : if( (eqe->token_state.state == PPG_Token_Matches)
318 235 : && eqe->token_state.changed
319 : ) {
320 : // PPG_LOG(" Causes token match\n");
321 :
322 : // The last event led to a match
323 :
324 : // Add the token to the active set
325 : //
326 235 : PPG_ASSERT(eqe->consumer);
327 235 : ppg_active_tokens_add(eqe->consumer);
328 :
329 : // In pedantic tokens mode, tokens can only trigger actions
330 : // when all their related inputs became inactive
331 : //
332 235 : if((eqe->consumer->misc.flags & PPG_Token_Flags_Pedantic) == 0) {
333 :
334 : // As the token just matched. Lets check if
335 : // we are allowed to trigger the respective action.
336 : //
337 235 : if(eqe->consumer->misc.action_state == PPG_Action_Enabled) {
338 :
339 : // PPG_LOG(" Triggering activation action\n");
340 :
341 166 : eqe->consumer->action.callback.func(true /* signal activation */,
342 83 : eqe->consumer->action.callback.user_data);
343 :
344 83 : eqe->consumer->misc.action_state = PPG_Action_Activation_Triggered;
345 : }
346 : }
347 : }
348 :
349 333 : eqe->event.flags |= PPG_Event_Considered;
350 :
351 333 : if(eqe->consumer->misc.flags & PPG_Token_Flags_Done) {
352 6 : ppg_active_tokens_search_remove(eqe->consumer);
353 : }
354 : }
355 : else {
356 :
357 : // The event deactivates an input
358 :
359 136 : if(eqe->consumer) {
360 52 : ppg_active_tokens_on_deactivation(eqe->consumer,
361 26 : eqe->token_state.state,
362 26 : eqe->token_state.changed);
363 :
364 26 : eqe->event.flags |= PPG_Event_Considered;
365 : }
366 : else {
367 :
368 : // PPG_LOG(" Not part of current match branch\n");
369 :
370 110 : ppg_active_tokens_check_consumption(&eqe->event);
371 : }
372 : }
373 : }
374 :
375 102 : void ppg_active_tokens_update(void)
376 : {
377 : // PPG_LOG("************************\n")
378 : // PPG_LOG("Activating active tokens\n")
379 102 : ppg_event_buffer_iterate2(
380 : (PPG_Event_Processor_Visitor)ppg_active_tokens_update_aux,
381 : NULL);
382 102 : }
|