Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Try to compile in supernovae mode when requested to
[simgrid.git] / src / gras / DataDesc / cbps.c
1 /* $Id$ */
2
3 /* cbps - persistant states for callbacks                                   */
4
5 /* Copyright (c) 2003 Olivier Aumage.                                       */
6 /* Copyright (c) 2003, 2004 Martin Quinson.                                 */
7 /* All rights reserved.                                                     */
8
9 /* This program is free software; you can redistribute it and/or modify it
10  * under the terms of the license (GNU LGPL) which comes with this package. */
11
12 #include "xbt/ex.h"
13 #include "gras/DataDesc/datadesc_private.h"
14 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(gras_ddt_cbps, gras_ddt,
15                                 "callback persistant state");
16
17 typedef struct {
18   gras_datadesc_type_t type;
19   void *data;
20 } s_gras_cbps_elm_t, *gras_cbps_elm_t;
21
22 typedef struct s_gras_cbps {
23   xbt_dynar_t lints;            /* simple stack of long integers (easy interface) */
24
25   xbt_dict_t space;             /* varname x dynar of gras_cbps_elm_t */
26   xbt_dynar_t frames;           /* of dynar of names defined within this frame
27                                    (and to pop when we leave it) */
28   xbt_dynar_t globals;
29 } s_gras_cbps_t;
30
31 gras_cbps_t gras_cbps_new(void)
32 {
33   gras_cbps_t res;
34
35   res = xbt_new(s_gras_cbps_t, 1);
36
37   res->lints = xbt_dynar_new(sizeof(int), NULL);
38   res->space = xbt_dict_new();
39   /* no leak, the content is freed manually on block_end */
40   res->frames = xbt_dynar_new(sizeof(xbt_dynar_t), NULL);
41   res->globals = xbt_dynar_new(sizeof(char *), NULL);
42
43   gras_cbps_block_begin(res);
44
45   return res;
46 }
47
48 void gras_cbps_free(gras_cbps_t * state)
49 {
50
51   xbt_dynar_free(&((*state)->lints));
52
53   gras_cbps_block_end(*state);
54   xbt_dict_free(&((*state)->space));
55   xbt_dynar_free(&((*state)->frames));
56   xbt_dynar_free(&((*state)->globals));
57
58   free(*state);
59   *state = NULL;
60 }
61
62 void gras_cbps_reset(gras_cbps_t state)
63 {
64
65   xbt_dynar_reset(state->lints);
66
67   xbt_dict_reset(state->space);
68
69   xbt_dynar_reset(state->frames);
70   xbt_dynar_reset(state->globals);
71 }
72
73 /** \brief Declare a new element in the PS, and give it a value.
74  *
75  * If an element of that
76  * name already exists, it is masked by the one given here, and will be
77  * seeable again only after a pop to remove the value this push adds.
78  */
79 void
80 gras_cbps_v_push(gras_cbps_t ps,
81                  const char *name, void *data, gras_datadesc_type_t ddt)
82 {
83
84   xbt_dynar_t varstack = NULL, frame;
85   gras_cbps_elm_t var;
86   char *varname = (char *) xbt_strdup(name);
87   xbt_ex_t e;
88
89   DEBUG2("push(%s,%p)", name, (void *) data);
90
91   TRY {
92     varstack = xbt_dict_get(ps->space, name);
93   } CATCH(e) {
94     if (e.category != mismatch_error)
95       RETHROW;
96
97     DEBUG1("Create a new variable stack for '%s' into the space", name);
98     varstack = xbt_dynar_new(sizeof(gras_cbps_elm_t *), NULL);
99     xbt_dict_set(ps->space, varname, (void **) varstack, NULL);
100     xbt_ex_free(e);
101     /* leaking, you think? only if you do not close all the openned blocks ;) */
102   }
103
104   var = xbt_new0(s_gras_cbps_elm_t, 1);
105   var->type = ddt;
106   var->data = data;
107
108   xbt_dynar_push(varstack, &var);
109
110   xbt_dynar_pop(ps->frames, &frame);
111   DEBUG4("Push %s (%p @%p) into frame %p", varname, (void *) varname,
112          (void *) &varname, (void *) frame);
113   xbt_dynar_push(frame, &varname);
114   xbt_dynar_push(ps->frames, &frame);
115 }
116
117 /** \brief Retrieve an element from the PS, and remove it from the PS.
118  *
119  * If it's not present in the current block, it will fail (throwing not_found)
120  * and not search in upper blocks since this denotes a programmation error.
121  */
122 void
123 gras_cbps_v_pop(gras_cbps_t ps,
124                 const char *name, gras_datadesc_type_t * ddt, void **res)
125 {
126   xbt_dynar_t varstack, frame;
127   gras_cbps_elm_t var = NULL;
128   void *data = NULL;
129   xbt_ex_t e;
130
131   DEBUG1("pop(%s)", name);
132   TRY {
133     varstack = xbt_dict_get(ps->space, name);
134   } CATCH(e) {
135     if (e.category != mismatch_error)
136       RETHROW;
137
138     xbt_ex_free(e);
139     THROW1(not_found_error, 1, "Asked to pop the non-existant %s", name);
140   }
141   xbt_dynar_pop(varstack, &var);
142
143   if (!xbt_dynar_length(varstack)) {
144     DEBUG1("Last incarnation of %s poped. Kill it", name);
145     xbt_dict_remove(ps->space, name);
146     xbt_dynar_free(&varstack);
147   }
148
149   if (ddt)
150     *ddt = var->type;
151   data = var->data;
152
153   free(var);
154
155   xbt_dynar_pop(ps->frames, &frame);
156   {
157     int l = xbt_dynar_length(frame);
158
159     while (l--) {
160       char *_name = NULL;
161
162       _name = xbt_dynar_get_as(frame, l, char *);
163       if (!strcmp(name, _name)) {
164         xbt_dynar_remove_at(frame, l, &_name);
165         free(_name);
166         break;
167       }
168     }
169   }
170   xbt_dynar_push(ps->frames, &frame);
171
172   *res = data;
173 }
174
175 /** \brief Change the value of an element in the PS.
176  *
177  * If it's not present in the current block, look in the upper ones.
178  * If it's not present in any of them, modify in the globals
179  * If not present there neither, the code may segfault (Oli?).
180  *
181  * Once a reference to an element of that name is found somewhere in the PS,
182  *   its value is changed.
183  */
184 void
185 gras_cbps_v_set(gras_cbps_t ps,
186                 const char *name, void *data, gras_datadesc_type_t ddt)
187 {
188
189   xbt_dynar_t dynar = NULL;
190   gras_cbps_elm_t elm = NULL;
191
192   DEBUG1("set(%s)", name);
193   dynar = xbt_dict_get_or_null(ps->space, name);
194
195   if (dynar == NULL) {
196     dynar = xbt_dynar_new(sizeof(gras_cbps_elm_t), NULL);
197     xbt_dict_set(ps->space, name, (void **) dynar, NULL);
198
199     elm = xbt_new0(s_gras_cbps_elm_t, 1);
200     xbt_dynar_push(ps->globals, &name);
201   } else {
202     xbt_dynar_pop(dynar, &elm);
203   }
204
205   elm->type = ddt;
206   elm->data = data;
207
208   xbt_dynar_push(dynar, &elm);
209
210 }
211
212 /** \brief Get the value of an element in the PS without modifying it.
213  *
214  * (note that you get the content of the data struct and not a copy to it)
215  * If it's not present in the current block, look in the upper ones.
216  * If it's not present in any of them, look in the globals
217  * If not present there neither, the code may segfault (Oli?).
218  */
219 void *gras_cbps_v_get(gras_cbps_t ps, const char *name,
220                       /* OUT */ gras_datadesc_type_t * ddt)
221 {
222
223   xbt_dynar_t dynar = NULL;
224   gras_cbps_elm_t elm = NULL;
225
226   DEBUG1("get(%s)", name);
227   dynar = xbt_dict_get(ps->space, name);
228   xbt_dynar_pop(dynar, &elm);
229   xbt_dynar_push(dynar, &elm);
230
231   if (ddt) {
232     *ddt = elm->type;
233   }
234
235   return elm->data;
236
237 }
238
239 /** \brief Begins a new block.
240  *
241  * Blocks are usefull to remove a whole set of declarations you don't even know
242  *
243  * E.g., they constitute an elegent solution to recursive data structures.
244  *
245  * push/pop may be used in some cases for that, but if your recursive data
246  * struct contains other structs needing themselves callbacks, you have to
247  * use block_{begin,end} to do the trick.
248  */
249
250 void gras_cbps_block_begin(gras_cbps_t ps)
251 {
252
253   xbt_dynar_t dynar = NULL;
254
255   DEBUG0(">>> Block begin");
256   dynar = xbt_dynar_new(sizeof(char *), NULL);
257   xbt_dynar_push(ps->frames, &dynar);
258 }
259
260 /** \brief End the current block, and go back to the upper one. */
261 void gras_cbps_block_end(gras_cbps_t ps)
262 {
263
264   xbt_dynar_t frame = NULL;
265   unsigned int cursor = 0;
266   char *name = NULL;
267
268   xbt_assert0(xbt_dynar_length(ps->frames),
269               "More block_end than block_begin");
270   xbt_dynar_pop(ps->frames, &frame);
271
272   xbt_dynar_foreach(frame, cursor, name) {
273
274     xbt_dynar_t varstack = NULL;
275     gras_cbps_elm_t var = NULL;
276
277     DEBUG2("Get ride of %s (%p)", name, (void *) name);
278     varstack = xbt_dict_get(ps->space, name);
279     xbt_dynar_pop(varstack, &var);
280
281     if (!xbt_dynar_length(varstack)) {
282       xbt_dict_remove(ps->space, name);
283       xbt_dynar_free_container(&varstack);      /*already empty, save a test ;) */
284     }
285
286     if (var->data)
287       free(var->data);
288     free(var);
289     free(name);
290   }
291   xbt_dynar_free_container(&frame);     /* we just emptied it */
292   DEBUG0("<<< Block end");
293 }
294
295
296 /** \brief Push a new integer value into the cbps. */
297 void gras_cbps_i_push(gras_cbps_t ps, int val)
298 {
299   DEBUG1("push %d as a size", val);
300   xbt_dynar_push_as(ps->lints, int, val);
301 }
302
303 /** \brief Pop the lastly pushed integer value from the cbps. */
304 int gras_cbps_i_pop(gras_cbps_t ps)
305 {
306   int ret;
307
308   xbt_assert0(xbt_dynar_length(ps->lints) > 0,
309               "gras_cbps_i_pop: no value to pop");
310   ret = xbt_dynar_pop_as(ps->lints, int);
311   DEBUG1("pop %d as a size", ret);
312   return ret;
313 }
314
315 /** \brief Generic cb returning the lastly pushed value
316  *
317  * Used by \ref gras_datadesc_ref_pop_arr
318  */
319 int gras_datadesc_cb_pop(gras_datadesc_type_t ignored, gras_cbps_t vars,
320                          void *data)
321 {
322   return gras_cbps_i_pop(vars);
323 }
324
325 /* ************************* */
326 /* **** PUSHy callbacks **** */
327 /* ************************* */
328
329 /** \brief Cb to push an integer. Must be attached to the field you want to push */
330 void gras_datadesc_cb_push_int(gras_datadesc_type_t ignored, gras_cbps_t vars,
331                                void *data)
332 {
333   int *i = (int *) data;
334   gras_cbps_i_push(vars, (int) *i);
335 }
336
337 /** \brief Cb to push an unsigned integer. Must be attached to the field you want to push */
338 void gras_datadesc_cb_push_uint(gras_datadesc_type_t ignored,
339                                 gras_cbps_t vars, void *data)
340 {
341   unsigned int *i = (unsigned int *) data;
342   gras_cbps_i_push(vars, (int) *i);
343 }
344
345 /** \brief Cb to push an long integer. Must be attached to the field you want to push
346  */
347 void gras_datadesc_cb_push_lint(gras_datadesc_type_t ignored,
348                                 gras_cbps_t vars, void *data)
349 {
350   long int *i = (long int *) data;
351   gras_cbps_i_push(vars, (int) *i);
352 }
353
354 /** \brief Cb to push an unsigned long integer. Must be attached to the field you want to push
355  */
356 void gras_datadesc_cb_push_ulint(gras_datadesc_type_t ignored,
357                                  gras_cbps_t vars, void *data)
358 {
359   unsigned long int *i = (unsigned long int *) data;
360   gras_cbps_i_push(vars, (int) *i);
361 }
362
363 /* ************************************ */
364 /* **** PUSHy multiplier callbacks **** */
365 /* ************************************ */
366 /** \brief Cb to push an integer as multiplier. Must be attached to the field you want to push */
367 void gras_datadesc_cb_push_int_mult(gras_datadesc_type_t ignored,
368                                     gras_cbps_t vars, void *data)
369 {
370   int old = *(int *) data;
371   int new = gras_cbps_i_pop(vars);
372   DEBUG2("push %d x %d as a size", old, new);
373   gras_cbps_i_push(vars, old * new);
374 }
375
376 /** \brief Cb to push an unsigned integer as multiplier. Must be attached to the field you want to push */
377 void gras_datadesc_cb_push_uint_mult(gras_datadesc_type_t ignored,
378                                      gras_cbps_t vars, void *data)
379 {
380   unsigned int old = *(unsigned int *) data;
381   unsigned int new = gras_cbps_i_pop(vars);
382
383   DEBUG2("push %d x %d as a size", old, new);
384   gras_cbps_i_push(vars, (int) (old * new));
385 }
386
387 /** \brief Cb to push an long integer as multiplier. Must be attached to the field you want to push
388  */
389 void gras_datadesc_cb_push_lint_mult(gras_datadesc_type_t ignored,
390                                      gras_cbps_t vars, void *data)
391 {
392   long int i = *(long int *) data;
393   i *= gras_cbps_i_pop(vars);
394   gras_cbps_i_push(vars, (int) i);
395 }
396
397 /** \brief Cb to push an unsigned long integer as multiplier. Must be attached to the field you want to push
398  */
399 void gras_datadesc_cb_push_ulint_mult(gras_datadesc_type_t ignored,
400                                       gras_cbps_t vars, void *data)
401 {
402   unsigned long int old = *(unsigned long int *) data;
403   unsigned long int new = gras_cbps_i_pop(vars);
404
405   DEBUG2("push %ld x %ld as a size", old, new);
406   gras_cbps_i_push(vars, (int) (old * new));
407 }