better tests
[pspp] / src / data / case-tmpfile.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2009, 2010, 2011 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/case-tmpfile.h"
20
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include "libpspp/assertion.h"
26 #include "libpspp/taint.h"
27 #include "libpspp/ext-array.h"
28
29 #include "gl/xalloc.h"
30
31 /* A temporary file that stores an array of cases. */
32 struct case_tmpfile
33   {
34     struct taint *taint;        /* Taint. */
35     struct caseproto *proto;    /* Format of cases in the tmpfile. */
36     size_t case_size;           /* Number of bytes per case. */
37     size_t *offsets;            /* Offset to each value. */
38     struct ext_array *ext_array; /* Temporary file. */
39   };
40
41 /* Returns the number of bytes needed to store a value with the
42    given WIDTH on disk. */
43 static size_t
44 width_to_n_bytes (int width)
45 {
46   return width == 0 ? sizeof (double) : width;
47 }
48
49 /* Returns the address of the data in VALUE (for reading or
50    writing to/from disk).  VALUE must have the given WIDTH. */
51 static void *
52 value_to_data (const union value *value_, int width)
53 {
54   union value *value = CONST_CAST (union value *, value_);
55   assert (sizeof value->f == sizeof (double));
56   if (width == 0)
57     return &value->f;
58   else
59     return value->s;
60 }
61
62 /* Creates and returns a new case_tmpfile that will store cases
63    that match case prototype PROTO.  The caller retains
64    ownership of PROTO. */
65 struct case_tmpfile *
66 case_tmpfile_create (const struct caseproto *proto)
67 {
68   struct case_tmpfile *ctf;
69   size_t n_values;
70   size_t i;
71
72   ctf = xmalloc (sizeof *ctf);
73   ctf->taint = taint_create ();
74   ctf->ext_array = ext_array_create ();
75   ctf->proto = caseproto_ref (proto);
76   ctf->case_size = 0;
77   n_values = caseproto_get_n_widths (proto);
78   ctf->offsets = xmalloc (n_values * sizeof *ctf->offsets);
79   for (i = 0; i < n_values; i++)
80     {
81       size_t width = caseproto_get_width (proto, i);
82       ctf->offsets[i] = ctf->case_size;
83       ctf->case_size += width == -1 ? 0 : width == 0 ? sizeof (double) : width;
84     }
85   return ctf;
86 }
87
88 /* Destroys case_tmpfile CTF.
89    Returns true if CTF was tainted, which is caused by an I/O
90    error on case_tmpfile access or by taint propagation to the
91    case_tmpfile. */
92 bool
93 case_tmpfile_destroy (struct case_tmpfile *ctf)
94 {
95   bool ok = true;
96   if (ctf != NULL)
97     {
98       struct taint *taint = ctf->taint;
99       ext_array_destroy (ctf->ext_array);
100       caseproto_unref (ctf->proto);
101       free (ctf->offsets);
102       free (ctf);
103       ok = taint_destroy (taint);
104     }
105   return ok;
106 }
107
108 /* Returns true if CTF is tainted, which is caused by an I/O
109    error on case_tmpfile access or by taint propagation to the
110    case_tmpfile. */
111 bool
112 case_tmpfile_error (const struct case_tmpfile *ctf)
113 {
114   return taint_is_tainted (ctf->taint);
115 }
116
117 /* Marks CTF as tainted. */
118 void
119 case_tmpfile_force_error (struct case_tmpfile *ctf)
120 {
121   taint_set_taint (ctf->taint);
122 }
123
124 /* Returns CTF's taint object. */
125 const struct taint *
126 case_tmpfile_get_taint (const struct case_tmpfile *ctf)
127 {
128   return ctf->taint;
129 }
130
131 /* Reads N_VALUES values into VALUES, from the case numbered
132    CASE_IDX starting START_VALUE values into that case.  Returns
133    true if successful, false if CTF is tainted or an I/O error
134    occurs during the operation.
135
136    The results of this function are undefined if any of the
137    values read have not been previously written to CTF. */
138 bool
139 case_tmpfile_get_values (const struct case_tmpfile *ctf,
140                          casenumber case_idx, size_t start_value,
141                          union value values[], size_t n_values)
142 {
143   off_t case_offset = (off_t) ctf->case_size * case_idx;
144   size_t i;
145
146   assert (caseproto_range_is_valid (ctf->proto, start_value, n_values));
147   for (i = start_value; i < start_value + n_values; i++)
148     {
149       int width = caseproto_get_width (ctf->proto, i);
150       if (width != -1
151           && !ext_array_read (ctf->ext_array, case_offset + ctf->offsets[i],
152                               width_to_n_bytes (width),
153                               value_to_data (&values[i], width)))
154           return false;
155     }
156   return true;
157 }
158
159 /* Reads the case numbered CASE_IDX from CTF.
160    Returns the case if successful or a null pointer if CTF is
161    tainted or an I/O error occurs during the operation.
162
163    The results of this function are undefined if the case read
164    from CTF had not previously been written. */
165 struct ccase *
166 case_tmpfile_get_case (const struct case_tmpfile *ctf, casenumber case_idx)
167 {
168   struct ccase *c = case_create (ctf->proto);
169   if (case_tmpfile_get_values (ctf, case_idx, 0, case_data_all_rw (c),
170                                caseproto_get_n_widths (ctf->proto)))
171     return c;
172   else
173     {
174       case_unref (c);
175       return NULL;
176     }
177 }
178
179 /* Writes N_VALUES values from VALUES, into the case numbered
180    CASE_IDX starting START_VALUE values into that case.
181    Returns true if successful, false if CTF is tainted or an I/O
182    error occurs during the operation. */
183 bool
184 case_tmpfile_put_values (struct case_tmpfile *ctf,
185                          casenumber case_idx, size_t start_value,
186                          const union value values[], size_t n_values)
187 {
188   off_t case_offset = (off_t) ctf->case_size * case_idx;
189   size_t i;
190
191   assert (caseproto_range_is_valid (ctf->proto, start_value, n_values));
192   for (i = start_value; i < start_value + n_values; i++)
193     {
194       int width = caseproto_get_width (ctf->proto, i);
195       if (width != -1
196           && !ext_array_write (ctf->ext_array, case_offset + ctf->offsets[i],
197                                width_to_n_bytes (width),
198                                value_to_data (values++, width)))
199           return false;
200     }
201   return true;
202 }
203
204 /* Writes C to CTF as the case numbered CASE_IDX.
205    Returns true if successful, false if CTF is tainted or an I/O
206    error occurs during the operation. */
207 bool
208 case_tmpfile_put_case (struct case_tmpfile *ctf, casenumber case_idx,
209                        struct ccase *c)
210 {
211   bool ok = case_tmpfile_put_values (ctf, case_idx, 0, case_data_all (c),
212                                      caseproto_get_n_widths (ctf->proto));
213   case_unref (c);
214   return ok;
215 }
216