1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007, 2009, 2011 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"
33 #include "gl/xalloc.h"
35 /* A queue of cases. */
39 struct caseproto
*proto
; /* Prototype of cases in window. */
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
*, const struct caseproto
*);
52 void (*destroy
) (void *aux
);
53 void (*push_head
) (void *aux
, struct ccase
*);
54 void (*pop_tail
) (void *aux
, casenumber n
);
55 struct ccase
*(*get_case
) (void *aux
, casenumber ofs
);
56 casenumber (*get_n_cases
) (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
, const struct caseproto
*proto
,
67 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
, proto
);
74 cw
->proto
= caseproto_ref (proto
);
75 cw
->max_in_core_cases
= max_in_core_cases
;
80 /* Creates and returns a new casewindow for cases that take the
81 form specified by PROTO. 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 The caller retains its reference to PROTO. */
87 casewindow_create (const struct caseproto
*proto
, casenumber max_in_core_cases
)
89 return do_casewindow_create (taint_create (), proto
, max_in_core_cases
);
92 /* Destroys casewindow CW.
93 Returns true if CW was tainted, which is caused by an I/O
94 error or by taint propagation to the casewindow. */
96 casewindow_destroy (struct casewindow
*cw
)
101 cw
->class->destroy (cw
->aux
);
102 ok
= taint_destroy (cw
->taint
);
103 caseproto_unref (cw
->proto
);
109 /* Swaps the contents of casewindows A and B. */
111 casewindow_swap (struct casewindow
*a
, struct casewindow
*b
)
113 struct casewindow tmp
= *a
;
118 /* Dumps the contents of casewindow OLD to disk. */
120 casewindow_to_disk (struct casewindow
*old
)
122 struct casewindow
*new;
123 new = do_casewindow_create (taint_clone (old
->taint
), old
->proto
, 0);
124 while (casewindow_get_n_cases (old
) > 0 && !casewindow_error (new))
126 struct ccase
*c
= casewindow_get_case (old
, 0);
129 casewindow_pop_tail (old
, 1);
130 casewindow_push_head (new, c
);
132 casewindow_swap (old
, new);
133 casewindow_destroy (new);
136 /* Pushes case C at the head of casewindow CW.
137 Case C becomes owned by the casewindow. */
139 casewindow_push_head (struct casewindow
*cw
, struct ccase
*c
)
141 if (!casewindow_error (cw
))
143 cw
->class->push_head (cw
->aux
, c
);
144 if (!casewindow_error (cw
))
146 casenumber n_cases
= cw
->class->get_n_cases (cw
->aux
);
147 if (n_cases
> cw
->max_in_core_cases
148 && cw
->class == &casewindow_memory_class
)
149 casewindow_to_disk (cw
);
156 /* Deletes N_CASES cases at the tail of casewindow CW. */
158 casewindow_pop_tail (struct casewindow
*cw
, casenumber n_cases
)
160 if (!casewindow_error (cw
))
161 cw
->class->pop_tail (cw
->aux
, n_cases
);
164 /* Returns the case that is CASE_IDX cases away from CW's tail
165 into C, or a null pointer on an I/O error or if CW is
166 otherwise tainted. The caller must call case_unref() on the
167 returned case when it is no longer needed. */
169 casewindow_get_case (const struct casewindow
*cw_
, casenumber case_idx
)
171 struct casewindow
*cw
= CONST_CAST (struct casewindow
*, cw_
);
173 assert (case_idx
>= 0 && case_idx
< casewindow_get_n_cases (cw
));
174 if (casewindow_error (cw
))
176 return cw
->class->get_case (cw
->aux
, case_idx
);
179 /* Returns the number of cases in casewindow CW. */
181 casewindow_get_n_cases (const struct casewindow
*cw
)
183 return cw
->class->get_n_cases (cw
->aux
);
186 /* Returns the case prototype for the cases in casewindow CW.
187 The caller must not unref the returned prototype. */
188 const struct caseproto
*
189 casewindow_get_proto (const struct casewindow
*cw
)
194 /* Returns true if casewindow CW is tainted.
195 A casewindow is tainted by an I/O error or by taint
196 propagation to the casewindow. */
198 casewindow_error (const struct casewindow
*cw
)
200 return taint_is_tainted (cw
->taint
);
203 /* Marks casewindow CW tainted. */
205 casewindow_force_error (struct casewindow
*cw
)
207 taint_set_taint (cw
->taint
);
210 /* Returns casewindow CW's taint object. */
212 casewindow_get_taint (const struct casewindow
*cw
)
217 /* In-memory casewindow data. */
218 struct casewindow_memory
221 struct ccase
**cases
;
225 casewindow_memory_create (struct taint
*taint UNUSED
,
226 const struct caseproto
*proto UNUSED
)
228 struct casewindow_memory
*cwm
= xmalloc (sizeof *cwm
);
229 *cwm
= (struct casewindow_memory
) { .deque
= DEQUE_EMPTY_INITIALIZER
};
234 casewindow_memory_destroy (void *cwm_
)
236 struct casewindow_memory
*cwm
= cwm_
;
237 while (!deque_is_empty (&cwm
->deque
))
238 case_unref (cwm
->cases
[deque_pop_front (&cwm
->deque
)]);
244 casewindow_memory_push_head (void *cwm_
, struct ccase
*c
)
246 struct casewindow_memory
*cwm
= cwm_
;
247 if (deque_is_full (&cwm
->deque
))
248 cwm
->cases
= deque_expand (&cwm
->deque
, cwm
->cases
, sizeof *cwm
->cases
);
249 cwm
->cases
[deque_push_back (&cwm
->deque
)] = c
;
253 casewindow_memory_pop_tail (void *cwm_
, casenumber n_cases
)
255 struct casewindow_memory
*cwm
= cwm_
;
256 assert (deque_count (&cwm
->deque
) >= n_cases
);
257 while (n_cases
-- > 0)
258 case_unref (cwm
->cases
[deque_pop_front (&cwm
->deque
)]);
261 static struct ccase
*
262 casewindow_memory_get_case (void *cwm_
, casenumber ofs
)
264 struct casewindow_memory
*cwm
= cwm_
;
265 return case_ref (cwm
->cases
[deque_front (&cwm
->deque
, ofs
)]);
269 casewindow_memory_get_n_cases (const void *cwm_
)
271 const struct casewindow_memory
*cwm
= cwm_
;
272 return deque_count (&cwm
->deque
);
275 static const struct casewindow_class casewindow_memory_class
=
277 casewindow_memory_create
,
278 casewindow_memory_destroy
,
279 casewindow_memory_push_head
,
280 casewindow_memory_pop_tail
,
281 casewindow_memory_get_case
,
282 casewindow_memory_get_n_cases
,
285 /* On-disk casewindow data. */
286 struct casewindow_file
288 struct case_tmpfile
*file
;
289 casenumber head
, tail
;
293 casewindow_file_create (struct taint
*taint
, const struct caseproto
*proto
)
295 struct casewindow_file
*cwf
= xmalloc (sizeof *cwf
);
296 cwf
->file
= case_tmpfile_create (proto
);
297 cwf
->head
= cwf
->tail
= 0;
298 taint_propagate (case_tmpfile_get_taint (cwf
->file
), taint
);
303 casewindow_file_destroy (void *cwf_
)
305 struct casewindow_file
*cwf
= cwf_
;
306 case_tmpfile_destroy (cwf
->file
);
311 casewindow_file_push_head (void *cwf_
, struct ccase
*c
)
313 struct casewindow_file
*cwf
= cwf_
;
314 if (case_tmpfile_put_case (cwf
->file
, cwf
->head
, c
))
319 casewindow_file_pop_tail (void *cwf_
, casenumber n
)
321 struct casewindow_file
*cwf
= cwf_
;
322 assert (n
<= cwf
->head
- cwf
->tail
);
324 if (cwf
->head
== cwf
->tail
)
325 cwf
->head
= cwf
->tail
= 0;
328 static struct ccase
*
329 casewindow_file_get_case (void *cwf_
, casenumber ofs
)
331 struct casewindow_file
*cwf
= cwf_
;
332 return case_tmpfile_get_case (cwf
->file
, cwf
->tail
+ ofs
);
336 casewindow_file_get_n_cases (const void *cwf_
)
338 const struct casewindow_file
*cwf
= cwf_
;
339 return cwf
->head
- cwf
->tail
;
342 static const struct casewindow_class casewindow_file_class
=
344 casewindow_file_create
,
345 casewindow_file_destroy
,
346 casewindow_file_push_head
,
347 casewindow_file_pop_tail
,
348 casewindow_file_get_case
,
349 casewindow_file_get_n_cases
,