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