File: format.c

package info (click to toggle)
grass 8.4.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 277,040 kB
  • sloc: ansic: 460,798; python: 227,732; cpp: 42,026; sh: 11,262; makefile: 7,007; xml: 3,637; sql: 968; lex: 520; javascript: 484; yacc: 450; asm: 387; perl: 157; sed: 25; objc: 6; ruby: 4
file content (283 lines) | stat: -rw-r--r-- 8,578 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/**
 * \file lib/segment/format.c
 *
 * \brief Segment formatting routines.
 *
 * This program is free software under the GNU General Public License
 * (>=v2). Read the file COPYING that comes with GRASS for details.
 *
 * \author GRASS GIS Development Team
 *
 * \date 2005-2018
 */

#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <limits.h>
#include <grass/gis.h>
#include <grass/glocale.h>
#include "local_proto.h"

static int seg_format(int, off_t, off_t, int, int, int, int);
static int write_int(int, int);
static int write_off_t(int, off_t);
static int zero_fill(int, off_t);
static int seek_only(int, off_t);

/* fd must be open for write */

/**
 * \brief Format a segment file.
 *
 * The segmentation routines require a disk file to be used for paging
 * segments in and out of memory. This routine formats the file open for
 * write on file descriptor <b>fd</b> for use as a segment file.
 *
 * A segment file must be formatted before it can be processed by other
 * segment routines. The configuration parameters <b>nrows</b>,
 * <b>ncols</b>, <b>srows</b>, <b>scols</b>, and <b>len</b> are written
 * to the beginning of the segment file which is then filled with zeros.
 *
 * The corresponding nonsegmented data matrix, which is to be
 * transferred to the segment file, is <b>nrows</b> by <b>ncols</b>. The
 * segment file is to be formed of segments which are <b>srows</b> by
 * <b>scols</b>. The data items have length <b>len</b> bytes. For
 * example, if the <em>data type is int</em>, <em>len is sizeof(int)</em>.
 *
 * \param[in] fd file descriptor
 * \param[in] nrows number of non-segmented rows
 * \param[in] ncols number of non-segmented columns
 * \param[in] srows segment rows
 * \param[in] scols segment columns
 * \param[in] len length of data type
 * \return 1 of successful
 * \return -1 if unable to seek or write <b>fd</b>
 * \return -3 if illegal parameters are passed
 */

int Segment_format(int fd, off_t nrows, off_t ncols, int srows, int scols,
                   int len)
{
    return seg_format(fd, nrows, ncols, srows, scols, len, 1);
}

/**
 * \brief Format a segment file.
 *
 * The segmentation routines require a disk file to be used for paging
 * segments in and out of memory. This routine formats the file open for
 * write on file descriptor <b>fd</b> for use as a segment file.
 *
 * A segment file must be formatted before it can be processed by other
 * segment routines. The configuration parameters <b>nrows</b>,
 * <b>ncols</b>, <b>srows</b>, <b>scols</b>, and <b>len</b> are written
 * to the beginning of the segment file which is then filled with zeros.
 *
 * The corresponding nonsegmented data matrix, which is to be
 * transferred to the segment file, is <b>nrows</b> by <b>ncols</b>. The
 * segment file is to be formed of segments which are <b>srows</b> by
 * <b>scols</b>. The data items have length <b>len</b> bytes. For
 * example, if the <em>data type is int</em>, <em>len is sizeof(int)</em>.
 *
 * <b>Note:</b> This version of the function does <b>not</b> fill in the
 * initialized data structures with zeros.
 *
 * \param[in] fd file descriptor
 * \param[in] nrows number of non-segmented rows
 * \param[in] ncols number of non-segmented columns
 * \param[in] srows segment rows
 * \param[in] scols segment columns
 * \param[in] len length of data type
 * \return 1 of successful
 * \return -1 if unable to seek or write <b>fd</b>
 * \return -3 if illegal parameters are passed
 */

int Segment_format_nofill(int fd, off_t nrows, off_t ncols, int srows,
                          int scols, int len)
{
    return seg_format(fd, nrows, ncols, srows, scols, len, 0);
}

static int seg_format(int fd, off_t nrows, off_t ncols, int srows, int scols,
                      int len, int fill)
{
    off_t nbytes;
    int spr, size;

    if (nrows <= 0 || ncols <= 0 || len <= 0 || srows <= 0 || scols <= 0) {
        G_warning("Segment_format(fd,%" PRId64 ",%" PRId64
                  ",%d,%d,%d): illegal value(s)",
                  nrows, ncols, srows, scols, len);
        return -3;
    }

    spr = ncols / scols;
    if (ncols % scols)
        spr++;

    size = srows * scols * len;

    if (sizeof(off_t) == 4 && sizeof(double) >= 8) {
        double d_size;
        off_t o_size;

        /* calculate total number of segments */
        d_size = (double)spr * ((nrows + srows - 1) / srows);
        /* multiply with segment size */
        d_size *= size;

        /* add header */
        d_size += 2 * sizeof(off_t) + 3 * sizeof(int);

        o_size = (off_t)d_size;

        /* this test assumes that all off_t values can be exactly
         * represented as double if sizeof(off_t) = 4 and sizeof(double) >= 8 */
        if ((double)o_size != d_size) {
            G_warning(_("Segment format: file size too large"));
            G_warning(_("Please recompile with Large File Support (LFS)"));
            return -1;
        }
    }

    if (lseek(fd, 0L, SEEK_SET) == (off_t)-1) {
        int err = errno;

        G_warning("Segment_format(): Unable to seek (%s)", strerror(err));
        return -1;
    }

    if (!write_off_t(fd, nrows) || !write_off_t(fd, ncols) ||
        !write_int(fd, srows) || !write_int(fd, scols) || !write_int(fd, len))
        return -1;

    /* calculate total number of segments */
    nbytes = spr * ((nrows + srows - 1) / srows);
    nbytes *= size;

    if (!fill) {
        /* only seek and write a zero byte to the end */
        if (seek_only(fd, nbytes) < 0)
            return -1;
        return 1;
    }

    /* fill segment file with zeros */
    /* NOTE: this could be done faster using lseek() by seeking
     * ahead nbytes and then writing a single byte of 0,
     * provided lseek() on all version of UNIX will create a file
     * with holes that read as zeros.
     */
    if (zero_fill(fd, nbytes) < 0)
        return -1;

    return 1;
}

static int write_int(int fd, int n)
{
    errno = 0;
    if (write(fd, &n, sizeof(int)) != sizeof(int)) {
        int err = errno;

        if (err)
            G_warning("Segment format: Unable to write (%s)", strerror(err));
        else
            G_warning(
                "Segment format: Unable to write (insufficient disk space?)");
        return 0;
    }

    return 1;
}

static int write_off_t(int fd, off_t n)
{
    errno = 0;
    if (write(fd, &n, sizeof(off_t)) != sizeof(off_t)) {
        int err = errno;

        if (err)
            G_warning("Segment format: Unable to write (%s)", strerror(err));
        else
            G_warning(
                "Segment format: Unable to write (insufficient disk space?)");
        return 0;
    }

    return 1;
}

static int zero_fill(int fd, off_t nbytes)
{
#ifndef USE_LSEEK
    char buf[16384];
    register char *b;
    register int n;

    /* zero buf */
    n = nbytes > (int)sizeof(buf) ? (int)sizeof(buf) : nbytes;
    b = buf;
    while (n-- > 0)
        *b++ = 0;

    while (nbytes > 0) {
        n = nbytes > (int)sizeof(buf) ? (int)sizeof(buf) : nbytes;
        errno = 0;
        if (write(fd, buf, n) != n) {
            int err = errno;

            if (err)
                G_warning("segment zero_fill(): Unable to write (%s)",
                          strerror(err));
            else
                G_warning("segment zero_fill(): Unable to write (insufficient "
                          "disk space?)");
            return -1;
        }
        nbytes -= n;
    }
    return 1;
#else
    return seek_only(fd, nbytes);
#endif
}

static int seek_only(int fd, off_t nbytes)
{
    /* Using lseek (faster upon initialization).
       NOTE: This version doesn't allocate disk storage for the file; storage
       will be allocated dynamically as blocks are actually written. This could
       result in seek_only() succeeding but a subsequent call to write() failing
       with ENOSPC ("No space left on device").
     */

    static const char buf[10];

    G_debug(3, "Using new segmentation code...");
    errno = 0;
    if (lseek(fd, nbytes - 1, SEEK_CUR) < 0) {
        int err = errno;

        G_warning("segment zero_fill(): Unable to seek (%s)", strerror(err));
        return -1;
    }
    errno = 0;
    if (write(fd, buf, 1) != 1) {
        int err = errno;

        if (err)
            G_warning("segment zero_fill(): Unable to write (%s)",
                      strerror(err));
        else
            G_warning("segment zero_fill(): Unable to write (insufficient disk "
                      "space?)");
        return -1;
    }

    return 1;
}