GRASS GIS 8 Programmer's Manual  8.4.0dev(2024)-112dd97adf
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 
24 static int seg_format(int, off_t, off_t, int, int, int, int);
25 static int write_int(int, int);
26 static int write_off_t(int, off_t);
27 static int zero_fill(int, off_t);
28 static 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 
61 int 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 
99 int 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 
105 static 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 
181 static 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 
198 static 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 
215 static 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 
250 static 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 }
void G_warning(const char *,...) __attribute__((format(printf
int G_debug(int, const char *,...) __attribute__((format(printf
#define _(str)
Definition: glocale.h:10
double b
Definition: r_raster.c:39
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)
Definition: symbol/read.c:216