46241c8fc2bab393571233def90bb16fb1a261ba
[pspp] / src / data / transformations.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009, 2011, 2013, 2015 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 #include "data/control-stack.h" /* XXX layering violation */
26
27 #include "gl/xalloc.h"
28
29 /* A single transformation. */
30 struct transformation
31   {
32     /* Offset to add to EXECUTE's return value, if it returns a
33        transformation index.  Normally 0 but set to the starting
34        index of a spliced chain after splicing. */
35     int idx_ofs;
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 *
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
65 trns_chain_finalize (struct trns_chain *chain)
66 {
67   while (!chain->finalized)
68     {
69       ctl_stack_clear ();       /* XXX layering violation */
70       chain->finalized = true;
71     }
72 }
73
74 /* Destroys CHAIN, finalizing it in the process if it has not
75    already been finalized. */
76 bool
77 trns_chain_destroy (struct trns_chain *chain)
78 {
79   bool ok = true;
80
81   if (chain != NULL)
82     {
83       size_t i;
84
85       /* Needed to ensure that the control stack gets cleared. */
86       trns_chain_finalize (chain);
87
88       for (i = 0; i < chain->trns_cnt; i++)
89         {
90           struct transformation *trns = &chain->trns[i];
91           if (trns->free != NULL)
92             ok = trns->free (trns->aux) && ok;
93         }
94       free (chain->trns);
95       free (chain);
96     }
97
98   return ok;
99 }
100
101 /* Returns true if CHAIN contains any transformations,
102    false otherwise. */
103 bool
104 trns_chain_is_empty (const struct trns_chain *chain)
105 {
106   return chain->trns_cnt == 0;
107 }
108
109 /* Adds a transformation to CHAIN with execute function EXECUTE, free function
110    FREE, and auxiliary data AUX. */
111 void
112 trns_chain_append (struct trns_chain *chain, trns_proc_func *execute,
113                    trns_free_func *free, void *aux)
114 {
115   struct transformation *trns;
116
117   chain->finalized = false;
118
119   if (chain->trns_cnt == chain->trns_cap)
120     chain->trns = x2nrealloc (chain->trns, &chain->trns_cap,
121                               sizeof *chain->trns);
122
123   trns = &chain->trns[chain->trns_cnt++];
124   trns->idx_ofs = 0;
125   trns->execute = execute;
126   trns->free = free;
127   trns->aux = aux;
128 }
129
130 /* Appends the transformations in SRC to those in DST,
131    and destroys SRC.
132    Both DST and SRC must already be finalized. */
133 void
134 trns_chain_splice (struct trns_chain *dst, struct trns_chain *src)
135 {
136   size_t i;
137
138   assert (dst->finalized);
139   assert (src->finalized);
140
141   if (dst->trns_cnt + src->trns_cnt > dst->trns_cap)
142     {
143       dst->trns_cap = dst->trns_cnt + src->trns_cnt;
144       dst->trns = xnrealloc (dst->trns, dst->trns_cap, sizeof *dst->trns);
145     }
146
147   for (i = 0; i < src->trns_cnt; i++)
148     {
149       struct transformation *d = &dst->trns[i + dst->trns_cnt];
150       const struct transformation *s = &src->trns[i];
151       *d = *s;
152       d->idx_ofs += src->trns_cnt;
153     }
154   dst->trns_cnt += src->trns_cnt;
155
156   src->trns_cnt = 0;
157   trns_chain_destroy (src);
158 }
159
160 /* Returns the index that a transformation execution function may
161    return to "jump" to the next transformation to be added. */
162 size_t
163 trns_chain_next (struct trns_chain *chain)
164 {
165   return chain->trns_cnt;
166 }
167
168 /* Executes the given CHAIN of transformations on *C,
169    passing CASE_NR as the case number.
170    *C may be replaced by a new case.
171    Returns the result code that caused the transformations to
172    terminate, or TRNS_CONTINUE if the transformations finished
173    due to "falling off the end" of the set of transformations. */
174 enum trns_result
175 trns_chain_execute (const struct trns_chain *chain, enum trns_result start,
176                     struct ccase **c, casenumber case_nr)
177 {
178   int i;
179
180   assert (chain->finalized);
181   for (i = start < 0 ? 0 : start; i < chain->trns_cnt; )
182     {
183       struct transformation *trns = &chain->trns[i];
184       int retval;
185
186       retval = trns->execute (trns->aux, c, case_nr);
187       switch (retval)
188         {
189         case TRNS_CONTINUE:
190           i++;
191           break;
192
193         case TRNS_END_CASE:
194           return i + 1;
195
196         case TRNS_DROP_CASE:
197         case TRNS_ERROR:
198         case TRNS_END_FILE:
199           return retval;
200
201         default:
202           i += retval;
203           assert (i <= chain->trns_cnt);
204           break;
205         }
206     }
207
208   return TRNS_CONTINUE;
209 }