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