GRASS GIS 8 Programmer's Manual 8.3.2(2024)-exported
Loading...
Searching...
No Matches
segment/format.c
Go to the documentation of this file.
1/**
2 * \file lib/segment/format.c
3 *
4 * \brief Segment formatting routines.
5 *
6 * This program is free software under the GNU General Public License
7 * (>=v2). Read the file COPYING that comes with GRASS for details.
8 *
9 * \author GRASS GIS Development Team
10 *
11 * \date 2005-2018
12 */
13
14#include <inttypes.h>
15#include <stdio.h>
16#include <string.h>
17#include <errno.h>
18#include <unistd.h>
19#include <limits.h>
20#include <grass/gis.h>
21#include <grass/glocale.h>
22#include "local_proto.h"
23
24static int seg_format(int, off_t, off_t, int, int, int, int);
25static int write_int(int, int);
26static int write_off_t(int, off_t);
27static int zero_fill(int, off_t);
28static int seek_only(int, off_t);
29
30/* fd must be open for write */
31
32/**
33 * \brief Format a segment file.
34 *
35 * The segmentation routines require a disk file to be used for paging
36 * segments in and out of memory. This routine formats the file open for
37 * write on file descriptor <b>fd</b> for use as a segment file.
38 *
39 * A segment file must be formatted before it can be processed by other
40 * segment routines. The configuration parameters <b>nrows</b>,
41 * <b>ncols</b>, <b>srows</b>, <b>scols</b>, and <b>len</b> are written
42 * to the beginning of the segment file which is then filled with zeros.
43 *
44 * The corresponding nonsegmented data matrix, which is to be
45 * transferred to the segment file, is <b>nrows</b> by <b>ncols</b>. The
46 * segment file is to be formed of segments which are <b>srows</b> by
47 * <b>scols</b>. The data items have length <b>len</b> bytes. For
48 * example, if the <em>data type is int</em>, <em>len is sizeof(int)</em>.
49 *
50 * \param[in] fd file descriptor
51 * \param[in] nrows number of non-segmented rows
52 * \param[in] ncols number of non-segmented columns
53 * \param[in] srows segment rows
54 * \param[in] scols segment columns
55 * \param[in] len length of data type
56 * \return 1 of successful
57 * \return -1 if unable to seek or write <b>fd</b>
58 * \return -3 if illegal parameters are passed
59 */
60
61int Segment_format(int fd, off_t nrows, off_t ncols, int srows, int scols,
62 int len)
63{
64 return seg_format(fd, nrows, ncols, srows, scols, len, 1);
65}
66
67/**
68 * \brief Format a segment file.
69 *
70 * The segmentation routines require a disk file to be used for paging
71 * segments in and out of memory. This routine formats the file open for
72 * write on file descriptor <b>fd</b> for use as a segment file.
73 *
74 * A segment file must be formatted before it can be processed by other
75 * segment routines. The configuration parameters <b>nrows</b>,
76 * <b>ncols</b>, <b>srows</b>, <b>scols</b>, and <b>len</b> are written
77 * to the beginning of the segment file which is then filled with zeros.
78 *
79 * The corresponding nonsegmented data matrix, which is to be
80 * transferred to the segment file, is <b>nrows</b> by <b>ncols</b>. The
81 * segment file is to be formed of segments which are <b>srows</b> by
82 * <b>scols</b>. The data items have length <b>len</b> bytes. For
83 * example, if the <em>data type is int</em>, <em>len is sizeof(int)</em>.
84 *
85 * <b>Note:</b> This version of the function does <b>not</b> fill in the
86 * initialized data structures with zeros.
87 *
88 * \param[in] fd file descriptor
89 * \param[in] nrows number of non-segmented rows
90 * \param[in] ncols number of non-segmented columns
91 * \param[in] srows segment rows
92 * \param[in] scols segment columns
93 * \param[in] len length of data type
94 * \return 1 of successful
95 * \return -1 if unable to seek or write <b>fd</b>
96 * \return -3 if illegal parameters are passed
97 */
98
99int Segment_format_nofill(int fd, off_t nrows, off_t ncols, int srows,
100 int scols, int len)
101{
102 return seg_format(fd, nrows, ncols, srows, scols, len, 0);
103}
104
105static int seg_format(int fd, off_t nrows, off_t ncols, int srows, int scols,
106 int len, int fill)
107{
108 off_t nbytes;
109 int spr, size;
110
111 if (nrows <= 0 || ncols <= 0 || len <= 0 || srows <= 0 || scols <= 0) {
112 G_warning("Segment_format(fd,%" PRId64 ",%" PRId64
113 ",%d,%d,%d): illegal value(s)",
114 nrows, ncols, srows, scols, len);
115 return -3;
116 }
117
118 spr = ncols / scols;
119 if (ncols % scols)
120 spr++;
121
122 size = srows * scols * len;
123
124 if (sizeof(off_t) == 4 && sizeof(double) >= 8) {
125 double d_size;
126 off_t o_size;
127
128 /* calculate total number of segments */
129 d_size = (double)spr * ((nrows + srows - 1) / srows);
130 /* multiply with segment size */
131 d_size *= size;
132
133 /* add header */
134 d_size += 2 * sizeof(off_t) + 3 * sizeof(int);
135
136 o_size = (off_t)d_size;
137
138 /* this test assumes that all off_t values can be exactly
139 * represented as double if sizeof(off_t) = 4 and sizeof(double) >= 8 */
140 if ((double)o_size != d_size) {
141 G_warning(_("Segment format: file size too large"));
142 G_warning(_("Please recompile with Large File Support (LFS)"));
143 return -1;
144 }
145 }
146
147 if (lseek(fd, 0L, SEEK_SET) == (off_t)-1) {
148 int err = errno;
149
150 G_warning("Segment_format(): Unable to seek (%s)", strerror(err));
151 return -1;
152 }
153
154 if (!write_off_t(fd, nrows) || !write_off_t(fd, ncols) ||
155 !write_int(fd, srows) || !write_int(fd, scols) || !write_int(fd, len))
156 return -1;
157
158 /* calculate total number of segments */
159 nbytes = spr * ((nrows + srows - 1) / srows);
160 nbytes *= size;
161
162 if (!fill) {
163 /* only seek and write a zero byte to the end */
164 if (seek_only(fd, nbytes) < 0)
165 return -1;
166 return 1;
167 }
168
169 /* fill segment file with zeros */
170 /* NOTE: this could be done faster using lseek() by seeking
171 * ahead nbytes and then writing a single byte of 0,
172 * provided lseek() on all version of UNIX will create a file
173 * with holes that read as zeros.
174 */
175 if (zero_fill(fd, nbytes) < 0)
176 return -1;
177
178 return 1;
179}
180
181static int write_int(int fd, int n)
182{
183 errno = 0;
184 if (write(fd, &n, sizeof(int)) != sizeof(int)) {
185 int err = errno;
186
187 if (err)
188 G_warning("Segment format: Unable to write (%s)", strerror(err));
189 else
190 G_warning(
191 "Segment format: Unable to write (insufficient disk space?)");
192 return 0;
193 }
194
195 return 1;
196}
197
198static int write_off_t(int fd, off_t n)
199{
200 errno = 0;
201 if (write(fd, &n, sizeof(off_t)) != sizeof(off_t)) {
202 int err = errno;
203
204 if (err)
205 G_warning("Segment format: Unable to write (%s)", strerror(err));
206 else
207 G_warning(
208 "Segment format: Unable to write (insufficient disk space?)");
209 return 0;
210 }
211
212 return 1;
213}
214
215static int zero_fill(int fd, off_t nbytes)
216{
217#ifndef USE_LSEEK
218 char buf[16384];
219 register char *b;
220 register int n;
221
222 /* zero buf */
223 n = nbytes > (int)sizeof(buf) ? (int)sizeof(buf) : nbytes;
224 b = buf;
225 while (n-- > 0)
226 *b++ = 0;
227
228 while (nbytes > 0) {
229 n = nbytes > (int)sizeof(buf) ? (int)sizeof(buf) : nbytes;
230 errno = 0;
231 if (write(fd, buf, n) != n) {
232 int err = errno;
233
234 if (err)
235 G_warning("segment zero_fill(): Unable to write (%s)",
236 strerror(err));
237 else
238 G_warning("segment zero_fill(): Unable to write (insufficient "
239 "disk space?)");
240 return -1;
241 }
242 nbytes -= n;
243 }
244 return 1;
245#else
246 return seek_only(fd, nbytes);
247#endif
248}
249
250static int seek_only(int fd, off_t nbytes)
251{
252 /* Using lseek (faster upon initialization).
253 NOTE: This version doesn't allocate disk storage for the file; storage
254 will be allocated dynamically as blocks are actually written. This could
255 result in seek_only() succeeding but a subsequent call to write() failing
256 with ENOSPC ("No space left on device").
257 */
258
259 static const char buf[10];
260
261 G_debug(3, "Using new segmentation code...");
262 errno = 0;
263 if (lseek(fd, nbytes - 1, SEEK_CUR) < 0) {
264 int err = errno;
265
266 G_warning("segment zero_fill(): Unable to seek (%s)", strerror(err));
267 return -1;
268 }
269 errno = 0;
270 if (write(fd, buf, 1) != 1) {
271 int err = errno;
272
273 if (err)
274 G_warning("segment zero_fill(): Unable to write (%s)",
275 strerror(err));
276 else
277 G_warning("segment zero_fill(): Unable to write (insufficient disk "
278 "space?)");
279 return -1;
280 }
281
282 return 1;
283}
int G_debug(int level, const char *msg,...)
Print debugging message.
Definition debug.c:66
double b
void G_warning(const char *msg,...)
Print a warning message to stderr.
Definition gis/error.c:203
int Segment_format_nofill(int fd, off_t nrows, off_t ncols, int srows, int scols, int len)
Format a segment file.
int Segment_format(int fd, off_t nrows, off_t ncols, int srows, int scols, int len)
Format a segment file.
SYMBOL * err(FILE *fp, SYMBOL *s, char *msg)