Logo AND Algorithmique Numérique Distribuée

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