57ff8e4a7d6225e3b56c9b71c0760e6e9570f635
[pspp] / src / libpspp / ext-array.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 /* An interface to an array of octets that is stored on disk as a temporary
18    file. */
19
20 #include <config.h>
21
22 #include "libpspp/ext-array.h"
23
24 #include <errno.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #include "libpspp/assertion.h"
29 #include "libpspp/cast.h"
30 #include "libpspp/temp-file.h"
31
32 #include "gl/error.h"
33 #include "gl/unlocked-io.h"
34 #include "gl/xalloc.h"
35
36 #include "gettext.h"
37 #define _(msgid) gettext (msgid)
38
39 struct ext_array
40   {
41     FILE *file;                 /* Underlying file. */
42
43     /* Current byte offset in file.  We track this manually,
44        instead of using ftello, because in glibc ftello flushes
45        the stream buffer, making the common case of sequential
46        access to cases unreasonably slow. */
47     off_t position;
48   };
49
50 /* Creates and returns a new external array. */
51 struct ext_array *
52 ext_array_create (void)
53 {
54   struct ext_array *ea = xmalloc (sizeof *ea);
55   ea->file = create_temp_file ();
56   if (ea->file == NULL)
57     error (0, errno, _("failed to create temporary file"));
58   ea->position = 0;
59   return ea;
60 }
61
62 /* Closes and destroys external array EA.  Returns true if I/O on EA always
63    succeeded, false if an I/O error occurred at some point. */
64 bool
65 ext_array_destroy (struct ext_array *ea)
66 {
67   bool ok = true;
68   if (ea != NULL)
69     {
70       ok = !ext_array_error (ea);
71       if (ea->file != NULL)
72         close_temp_file (ea->file);
73       free (ea);
74     }
75   return ok;
76 }
77
78 /* Seeks EA's underlying file to the start of `union value'
79    VALUE_IDX within case CASE_IDX.
80    Returns true if the seek is successful and EA is not
81    otherwise tainted, false otherwise. */
82 static bool
83 do_seek (const struct ext_array *ea_, off_t offset)
84 {
85   struct ext_array *ea = CONST_CAST (struct ext_array *, ea_);
86
87   if (!ext_array_error (ea))
88     {
89       if (ea->position == offset)
90         return true;
91       else if (fseeko (ea->file, offset, SEEK_SET) == 0)
92         {
93           ea->position = offset;
94           return true;
95         }
96       else
97         error (0, errno, _("seeking in temporary file"));
98     }
99
100   return false;
101 }
102
103 /* Reads BYTES bytes from EA's underlying file into BUFFER.
104    EA must not be tainted upon entry into this function.
105    Returns true if successful, false upon an I/O error (in which
106    case EA is marked tainted). */
107 static bool
108 do_read (const struct ext_array *ea_, void *buffer, size_t bytes)
109 {
110   struct ext_array *ea = CONST_CAST (struct ext_array *, ea_);
111
112   assert (!ext_array_error (ea));
113   if (bytes > 0 && fread (buffer, bytes, 1, ea->file) != 1)
114     {
115       if (ferror (ea->file))
116         error (0, errno, _("reading temporary file"));
117       else if (feof (ea->file))
118         error (0, 0, _("unexpected end of file reading temporary file"));
119       else
120         NOT_REACHED ();
121       return false;
122     }
123   ea->position += bytes;
124   return true;
125 }
126
127 /* Writes BYTES bytes from BUFFER into EA's underlying file.
128    EA must not be tainted upon entry into this function.
129    Returns true if successful, false upon an I/O error (in which
130    case EA is marked tainted). */
131 static bool
132 do_write (struct ext_array *ea, const void *buffer, size_t bytes)
133 {
134   assert (!ext_array_error (ea));
135   if (bytes > 0 && fwrite (buffer, bytes, 1, ea->file) != 1)
136     {
137       error (0, errno, _("writing to temporary file"));
138       return false;
139     }
140   ea->position += bytes;
141   return true;
142 }
143
144 /* Reads N bytes from EA at byte offset OFFSET into DATA.
145    Returns true if successful, false on failure.  */
146 bool
147 ext_array_read (const struct ext_array *ea, off_t offset, size_t n, void *data)
148 {
149   return do_seek (ea, offset) && do_read (ea, data, n);
150 }
151
152 /* Writes the N bytes in DATA to EA at byte offset OFFSET.
153    Returns true if successful, false on failure.  */
154 bool
155 ext_array_write (struct ext_array *ea, off_t offset, size_t n,
156                  const void *data)
157 {
158   return do_seek (ea, offset) && do_write (ea, data, n);
159 }
160
161 /* Returns true if an error has occurred in I/O on EA,
162    false if no error has been detected. */
163 bool
164 ext_array_error (const struct ext_array *ea)
165 {
166   return ea->file == NULL || ferror (ea->file) || feof (ea->file);
167 }