1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
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.
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.
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
21 #include <data/transformations.h>
26 #include <libpspp/str.h>
30 /* A single transformation. */
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. */
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. */
43 /* A chain of transformations. */
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? */
52 /* Allocates and returns a new transformation chain. */
54 trns_chain_create (void)
56 struct trns_chain *chain = xmalloc (sizeof *chain);
60 chain->finalized = false;
64 /* Finalizes all the transformations in CHAIN.
65 A chain is only finalized once; afterward, calling this
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. */
71 trns_chain_finalize (struct trns_chain *chain)
73 if (!chain->finalized)
77 for (i = 0; i < chain->trns_cnt; i++)
79 struct transformation *trns = &chain->trns[i];
80 if (trns->finalize != NULL)
81 trns->finalize (trns->aux);
83 chain->finalized = true;
87 /* Destroys CHAIN, finalizing it in the process if it has not
88 already been finalized. */
90 trns_chain_destroy (struct trns_chain *chain)
98 /* Needed to ensure that the control stack gets cleared. */
99 trns_chain_finalize (chain);
101 for (i = 0; i < chain->trns_cnt; i++)
103 struct transformation *trns = &chain->trns[i];
104 if (trns->free != NULL)
105 ok = trns->free (trns->aux) && ok;
114 /* Returns true if CHAIN contains any transformations,
117 trns_chain_is_empty (const struct trns_chain *chain)
119 return chain->trns_cnt == 0;
122 /* Adds a transformation to CHAIN with finalize function
123 FINALIZE, execute function EXECUTE, free function FREE, and
124 auxiliary data AUX. */
126 trns_chain_append (struct trns_chain *chain, trns_finalize_func *finalize,
127 trns_proc_func *execute, trns_free_func *free,
130 struct transformation *trns;
132 assert (!chain->finalized);
134 if (chain->trns_cnt == chain->trns_cap)
135 chain->trns = x2nrealloc (chain->trns, &chain->trns_cap,
136 sizeof *chain->trns);
138 trns = &chain->trns[chain->trns_cnt++];
140 trns->finalize = finalize;
141 trns->execute = execute;
146 /* Appends the transformations in SRC to those in DST,
148 Both DST and SRC must already be finalized. */
150 trns_chain_splice (struct trns_chain *dst, struct trns_chain *src)
154 assert (dst->finalized);
155 assert (src->finalized);
157 if (dst->trns_cnt + src->trns_cnt > dst->trns_cap)
159 dst->trns_cap = dst->trns_cnt + src->trns_cnt;
160 dst->trns = xnrealloc (dst->trns, dst->trns_cap, sizeof *dst->trns);
163 for (i = 0; i < src->trns_cnt; i++)
165 struct transformation *d = &dst->trns[i + dst->trns_cnt];
166 const struct transformation *s = &src->trns[i];
168 d->idx_ofs += src->trns_cnt;
170 dst->trns_cnt += src->trns_cnt;
172 trns_chain_destroy (src);
175 /* Returns the index that a transformation execution function may
176 return to "jump" to the next transformation to be added. */
178 trns_chain_next (struct trns_chain *chain)
180 return chain->trns_cnt;
183 /* Executes the given CHAIN of transformations on C,
184 passing *CASE_NR as the case number.
185 If a transformation modifies *CASE_NR, it will affect the case
186 number passed to following transformations.
187 Returns the result code that caused the transformations to
188 terminate, or TRNS_CONTINUE if the transformations finished
189 due to "falling off the end" of the set of transformations. */
191 trns_chain_execute (struct trns_chain *chain, enum trns_result start,
192 struct ccase *c, const size_t *case_nr)
196 assert (chain->finalized);
197 for (i = start < 0 ? 0 : start; i < chain->trns_cnt; )
199 struct transformation *trns = &chain->trns[i];
200 int retval = trns->execute (trns->aux, c, *case_nr);
201 if (retval == TRNS_CONTINUE)
203 else if (retval >= 0)
204 i = retval + trns->idx_ofs;
206 return retval == TRNS_END_CASE ? i + 1 : retval;
209 return TRNS_CONTINUE;