1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007, 2009 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* This casewindow implementation in terms of an class interface
18 is undoubtedly a form of over-abstraction. However, it works
19 and the extra abstraction seems to be harmless. */
23 #include <data/casewindow.h>
27 #include <data/case-tmpfile.h>
28 #include <libpspp/assertion.h>
29 #include <libpspp/compiler.h>
30 #include <libpspp/deque.h>
31 #include <libpspp/taint.h>
35 /* A queue of cases. */
39 size_t value_cnt; /* Number of values per case. */
40 casenumber max_in_core_cases; /* Max cases before dumping to disk. */
41 struct taint *taint; /* Taint status. */
43 /* Implementation data. */
44 const struct casewindow_class *class;
48 /* Implementation of a casewindow. */
49 struct casewindow_class
51 void *(*create) (struct taint *, size_t value_cnt);
52 void (*destroy) (void *aux);
53 void (*push_head) (void *aux, struct ccase *);
54 void (*pop_tail) (void *aux, casenumber cnt);
55 struct ccase *(*get_case) (void *aux, casenumber ofs);
56 casenumber (*get_case_cnt) (const void *aux);
60 static const struct casewindow_class casewindow_memory_class;
61 static const struct casewindow_class casewindow_file_class;
63 /* Creates and returns a new casewindow using the given
65 static struct casewindow *
66 do_casewindow_create (struct taint *taint,
67 size_t value_cnt, casenumber max_in_core_cases)
69 struct casewindow *cw = xmalloc (sizeof *cw);
70 cw->class = (max_in_core_cases
71 ? &casewindow_memory_class
72 : &casewindow_file_class);
73 cw->aux = cw->class->create (taint, value_cnt);
74 cw->value_cnt = value_cnt;
75 cw->max_in_core_cases = max_in_core_cases;
80 /* Creates and returns a new casewindow for cases with VALUE_CNT
81 values each. If the casewindow holds more than
82 MAX_IN_CORE_CASES cases at any time, its cases will be dumped
83 to disk; otherwise, its cases will be held in memory. */
85 casewindow_create (size_t value_cnt, casenumber max_in_core_cases)
87 return do_casewindow_create (taint_create (), value_cnt, max_in_core_cases);
90 /* Destroys casewindow CW.
91 Returns true if CW was tainted, which is caused by an I/O
92 error or by taint propagation to the casewindow. */
94 casewindow_destroy (struct casewindow *cw)
99 cw->class->destroy (cw->aux);
100 ok = taint_destroy (cw->taint);
106 /* Swaps the contents of casewindows A and B. */
108 casewindow_swap (struct casewindow *a, struct casewindow *b)
110 struct casewindow tmp = *a;
115 /* Dumps the contents of casewindow OLD to disk. */
117 casewindow_to_disk (struct casewindow *old)
119 struct casewindow *new;
120 new = do_casewindow_create (taint_clone (old->taint), old->value_cnt, 0);
121 while (casewindow_get_case_cnt (old) > 0 && !casewindow_error (new))
123 struct ccase *c = casewindow_get_case (old, 0);
126 casewindow_pop_tail (old, 1);
127 casewindow_push_head (new, c);
129 casewindow_swap (old, new);
130 casewindow_destroy (new);
133 /* Pushes case C at the head of casewindow CW.
134 Case C becomes owned by the casewindow. */
136 casewindow_push_head (struct casewindow *cw, struct ccase *c)
138 if (!casewindow_error (cw))
140 cw->class->push_head (cw->aux, c);
141 if (!casewindow_error (cw))
143 casenumber case_cnt = cw->class->get_case_cnt (cw->aux);
144 if (case_cnt > cw->max_in_core_cases
145 && cw->class == &casewindow_memory_class)
146 casewindow_to_disk (cw);
153 /* Deletes CASE_CNT cases at the tail of casewindow CW. */
155 casewindow_pop_tail (struct casewindow *cw, casenumber case_cnt)
157 if (!casewindow_error (cw))
158 cw->class->pop_tail (cw->aux, case_cnt);
161 /* Returns the case that is CASE_IDX cases away from CW's tail
162 into C, or a null pointer on an I/O error or if CW is
163 otherwise tainted. The caller must call case_unref() on the
164 returned case when it is no longer needed. */
166 casewindow_get_case (const struct casewindow *cw_, casenumber case_idx)
168 struct casewindow *cw = (struct casewindow *) cw_;
170 assert (case_idx >= 0 && case_idx < casewindow_get_case_cnt (cw));
171 if (casewindow_error (cw))
173 return cw->class->get_case (cw->aux, case_idx);
176 /* Returns the number of cases in casewindow CW. */
178 casewindow_get_case_cnt (const struct casewindow *cw)
180 return cw->class->get_case_cnt (cw->aux);
183 /* Returns the number of values per case in casewindow CW. */
185 casewindow_get_value_cnt (const struct casewindow *cw)
187 return cw->value_cnt;
190 /* Returns true if casewindow CW is tainted.
191 A casewindow is tainted by an I/O error or by taint
192 propagation to the casewindow. */
194 casewindow_error (const struct casewindow *cw)
196 return taint_is_tainted (cw->taint);
199 /* Marks casewindow CW tainted. */
201 casewindow_force_error (struct casewindow *cw)
203 taint_set_taint (cw->taint);
206 /* Returns casewindow CW's taint object. */
208 casewindow_get_taint (const struct casewindow *cw)
213 /* In-memory casewindow data. */
214 struct casewindow_memory
217 struct ccase **cases;
221 casewindow_memory_create (struct taint *taint UNUSED, size_t value_cnt UNUSED)
223 struct casewindow_memory *cwm = xmalloc (sizeof *cwm);
224 cwm->cases = deque_init (&cwm->deque, 4, sizeof *cwm->cases);
229 casewindow_memory_destroy (void *cwm_)
231 struct casewindow_memory *cwm = cwm_;
232 while (!deque_is_empty (&cwm->deque))
233 case_unref (cwm->cases[deque_pop_front (&cwm->deque)]);
239 casewindow_memory_push_head (void *cwm_, struct ccase *c)
241 struct casewindow_memory *cwm = cwm_;
242 if (deque_is_full (&cwm->deque))
243 cwm->cases = deque_expand (&cwm->deque, cwm->cases, sizeof *cwm->cases);
244 cwm->cases[deque_push_back (&cwm->deque)] = c;
248 casewindow_memory_pop_tail (void *cwm_, casenumber case_cnt)
250 struct casewindow_memory *cwm = cwm_;
251 assert (deque_count (&cwm->deque) >= case_cnt);
252 while (case_cnt-- > 0)
253 case_unref (cwm->cases[deque_pop_front (&cwm->deque)]);
256 static struct ccase *
257 casewindow_memory_get_case (void *cwm_, casenumber ofs)
259 struct casewindow_memory *cwm = cwm_;
260 return case_ref (cwm->cases[deque_front (&cwm->deque, ofs)]);
264 casewindow_memory_get_case_cnt (const void *cwm_)
266 const struct casewindow_memory *cwm = cwm_;
267 return deque_count (&cwm->deque);
270 static const struct casewindow_class casewindow_memory_class =
272 casewindow_memory_create,
273 casewindow_memory_destroy,
274 casewindow_memory_push_head,
275 casewindow_memory_pop_tail,
276 casewindow_memory_get_case,
277 casewindow_memory_get_case_cnt,
280 /* On-disk casewindow data. */
281 struct casewindow_file
283 struct case_tmpfile *file;
284 casenumber head, tail;
288 casewindow_file_create (struct taint *taint, size_t value_cnt)
290 struct casewindow_file *cwf = xmalloc (sizeof *cwf);
291 cwf->file = case_tmpfile_create (value_cnt);
292 cwf->head = cwf->tail = 0;
293 taint_propagate (case_tmpfile_get_taint (cwf->file), taint);
298 casewindow_file_destroy (void *cwf_)
300 struct casewindow_file *cwf = cwf_;
301 case_tmpfile_destroy (cwf->file);
306 casewindow_file_push_head (void *cwf_, struct ccase *c)
308 struct casewindow_file *cwf = cwf_;
309 if (case_tmpfile_put_case (cwf->file, cwf->head, c))
314 casewindow_file_pop_tail (void *cwf_, casenumber cnt)
316 struct casewindow_file *cwf = cwf_;
317 assert (cnt <= cwf->head - cwf->tail);
319 if (cwf->head == cwf->tail)
320 cwf->head = cwf->tail = 0;
323 static struct ccase *
324 casewindow_file_get_case (void *cwf_, casenumber ofs)
326 struct casewindow_file *cwf = cwf_;
327 return case_tmpfile_get_case (cwf->file, cwf->tail + ofs);
331 casewindow_file_get_case_cnt (const void *cwf_)
333 const struct casewindow_file *cwf = cwf_;
334 return cwf->head - cwf->tail;
337 static const struct casewindow_class casewindow_file_class =
339 casewindow_file_create,
340 casewindow_file_destroy,
341 casewindow_file_push_head,
342 casewindow_file_pop_tail,
343 casewindow_file_get_case,
344 casewindow_file_get_case_cnt,