treewide: Replace <name>_cnt by n_<name>s and <name>_cap by allocated_<name>.
[pspp] / src / data / transformations.c
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 n_trns;                      /* Number of transformations. */
46     size_t allocated_trns;              /* Allocated capacity. */
47     bool finalized;                     /* Finalize functions called? */
48   };
49
50 /* Allocates and returns a new transformation chain. */
51 struct trns_chain *
52 trns_chain_create (void)
53 {
54   struct trns_chain *chain = xmalloc (sizeof *chain);
55   chain->trns = NULL;
56   chain->n_trns = 0;
57   chain->allocated_trns = 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
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->n_trns; 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
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->n_trns; 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
114 trns_chain_is_empty (const struct trns_chain *chain)
115 {
116   return chain->n_trns == 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
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->n_trns == chain->allocated_trns)
132     chain->trns = x2nrealloc (chain->trns, &chain->allocated_trns,
133                               sizeof *chain->trns);
134
135   trns = &chain->trns[chain->n_trns++];
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
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->n_trns + src->n_trns > dst->allocated_trns)
155     {
156       dst->allocated_trns = dst->n_trns + src->n_trns;
157       dst->trns = xnrealloc (dst->trns, dst->allocated_trns, sizeof *dst->trns);
158     }
159
160   for (i = 0; i < src->n_trns; i++)
161     {
162       struct transformation *d = &dst->trns[i + dst->n_trns];
163       const struct transformation *s = &src->trns[i];
164       *d = *s;
165       d->idx_ofs += src->n_trns;
166     }
167   dst->n_trns += src->n_trns;
168
169   src->n_trns = 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
176 trns_chain_next (struct trns_chain *chain)
177 {
178   return chain->n_trns;
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
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->n_trns;)
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 }