e5542b882e94a4b3d928078c546ef67cc02c6d5f
[pspp-builds.git] / src / data / transformations.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21
22 #include <data/transformations.h>
23
24 #include <assert.h>
25 #include <stdlib.h>
26
27 #include <libpspp/str.h>
28 #include <procedure.h>
29
30 #include "xalloc.h"
31
32 /* A single transformation. */
33 struct transformation
34   {
35     /* Offset to add to EXECUTE's return value, if it returns a
36        transformation index.  Normally 0 but set to the starting
37        index of a spliced chain after splicing. */
38     int idx_ofs;                        
39     trns_finalize_func *finalize;       /* Finalize proc. */
40     trns_proc_func *execute;            /* Executes the transformation. */
41     trns_free_func *free;               /* Garbage collector proc. */
42     void *aux;                          /* Auxiliary data. */
43   };
44
45 /* A chain of transformations. */
46 struct trns_chain 
47   {
48     struct transformation *trns;        /* Array of transformations. */
49     size_t trns_cnt;                    /* Number of transformations. */
50     size_t trns_cap;                    /* Allocated capacity. */
51     bool finalized;                     /* Finalize functions called? */
52   };
53
54 /* Allocates and returns a new transformation chain. */
55 struct trns_chain *
56 trns_chain_create (void) 
57 {
58   struct trns_chain *chain = xmalloc (sizeof *chain);
59   chain->trns = NULL;
60   chain->trns_cnt = 0;
61   chain->trns_cap = 0;
62   chain->finalized = false;
63   return chain;
64 }
65
66 /* Finalizes all the transformations in CHAIN.
67    A chain is only finalized once; afterward, calling this
68    function is a no-op.
69    Finalizers may add transformations to CHAIN, but after
70    finalization the chain's contents are fixed, so that no more
71    transformations may be added afterward. */
72 void
73 trns_chain_finalize (struct trns_chain *chain) 
74 {
75   if (!chain->finalized) 
76     {
77       size_t i;
78
79       for (i = 0; i < chain->trns_cnt; i++) 
80         {
81           struct transformation *trns = &chain->trns[i];
82           if (trns->finalize != NULL)
83             trns->finalize (trns->aux); 
84         }
85       chain->finalized = true;
86     }
87 }
88
89 /* Destroys CHAIN, finalizing it in the process if it has not
90    already been finalized. */
91 bool
92 trns_chain_destroy (struct trns_chain *chain) 
93 {
94   bool ok = true;
95
96   if (chain != NULL) 
97     {
98       size_t i;
99       
100       /* Needed to ensure that the control stack gets cleared. */
101       trns_chain_finalize (chain);
102       
103       for (i = 0; i < chain->trns_cnt; i++) 
104         {
105           struct transformation *trns = &chain->trns[i];
106           if (trns->free != NULL) 
107             ok = trns->free (trns->aux) && ok;
108         }
109       free (chain);
110     }
111   
112   return ok;
113 }
114
115 /* Returns true if CHAIN contains any transformations,
116    false otherwise. */
117 bool
118 trns_chain_is_empty (const struct trns_chain *chain) 
119 {
120   return chain->trns_cnt == 0;
121 }
122
123 /* Adds a transformation to CHAIN with finalize function
124    FINALIZE, execute function EXECUTE, free function FREE, and
125    auxiliary data AUX. */
126 void
127 trns_chain_append (struct trns_chain *chain, trns_finalize_func *finalize,
128                    trns_proc_func *execute, trns_free_func *free,
129                    void *aux) 
130 {
131   struct transformation *trns;
132
133   assert (!chain->finalized);
134
135   if (chain->trns_cnt == chain->trns_cap)
136     chain->trns = x2nrealloc (chain->trns, &chain->trns_cap,
137                               sizeof *chain->trns);
138
139   trns = &chain->trns[chain->trns_cnt++];
140   trns->idx_ofs = 0;
141   trns->finalize = finalize;
142   trns->execute = execute;
143   trns->free = free;
144   trns->aux = aux;
145 }
146
147 /* Appends the transformations in SRC to those in DST,
148    and destroys SRC.
149    Both DST and SRC must already be finalized. */
150 void
151 trns_chain_splice (struct trns_chain *dst, struct trns_chain *src) 
152 {
153   size_t i;
154   
155   assert (dst->finalized);
156   assert (src->finalized);
157
158   if (dst->trns_cnt + src->trns_cnt > dst->trns_cap) 
159     {
160       dst->trns_cap = dst->trns_cnt + src->trns_cnt;
161       dst->trns = xnrealloc (dst->trns, dst->trns_cap, sizeof *dst->trns);
162     }
163
164   for (i = 0; i < src->trns_cnt; i++) 
165     {
166       struct transformation *d = &dst->trns[i + dst->trns_cnt];
167       const struct transformation *s = &src->trns[i];
168       *d = *s;
169       d->idx_ofs += src->trns_cnt; 
170     }
171   dst->trns_cnt += src->trns_cnt;
172
173   trns_chain_destroy (src);
174 }
175
176 /* Returns the index that a transformation execution function may
177    return to "jump" to the next transformation to be added. */
178 size_t
179 trns_chain_next (struct trns_chain *chain) 
180 {
181   return chain->trns_cnt;
182 }
183
184 /* Executes the given CHAIN of transformations on C,
185    passing *CASE_NR as the case number.
186    If a transformation modifies *CASE_NR, it will affect the case
187    number passed to following transformations.
188    Returns the result code that caused the transformations to
189    terminate, or TRNS_CONTINUE if the transformations finished
190    due to "falling off the end" of the set of transformations. */
191 enum trns_result
192 trns_chain_execute (struct trns_chain *chain, struct ccase *c,
193                     const size_t *case_nr) 
194 {
195   size_t i;
196
197   assert (chain->finalized);
198   for (i = 0; i < chain->trns_cnt; )
199     {
200       struct transformation *trns = &chain->trns[i];
201       int retval = trns->execute (trns->aux, c, *case_nr);
202       if (retval == TRNS_CONTINUE)
203         i++;
204       else if (retval >= 0)
205         i = retval + trns->idx_ofs;
206       else
207         return retval; 
208     }
209
210   return TRNS_CONTINUE;
211 }