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