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