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