GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/nrrd/encodingGzip.c Lines: 1 102 1.0 %
Date: 2017-05-26 Branches: 0 69 0.0 %

Line Branch Exec Source
1
/*
2
  Teem: Tools to process and visualize scientific data and images             .
3
  Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
4
  Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
5
  Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
6
7
  This library is free software; you can redistribute it and/or
8
  modify it under the terms of the GNU Lesser General Public License
9
  (LGPL) as published by the Free Software Foundation; either
10
  version 2.1 of the License, or (at your option) any later version.
11
  The terms of redistributing and/or modifying this software also
12
  include exceptions to the LGPL that facilitate static linking.
13
14
  This library is distributed in the hope that it will be useful,
15
  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
  Lesser General Public License for more details.
18
19
  You should have received a copy of the GNU Lesser General Public License
20
  along with this library; if not, write to Free Software Foundation, Inc.,
21
  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22
*/
23
24
#include "nrrd.h"
25
#include "privateNrrd.h"
26
27
static int
28
_nrrdEncodingGzip_available(void) {
29
30
#if TEEM_ZLIB
31
10
  return AIR_TRUE;
32
#else
33
  return AIR_FALSE;
34
#endif
35
}
36
37
#if TEEM_ZLIB
38
/*
39
** Maximum size that allow zlib to try to read or write at once.
40
** The real limit is UINT_MAX, but a smaller value here permits
41
** exercising the multi-chunk capability of the code below.
42
*/
43
static unsigned int
44
_nrrdZlibMaxChunk = UINT_MAX;
45
#endif
46
47
/*
48
** nio->byteSkip < 0 functionality contributed by Katharina Quintus
49
*/
50
static int
51
_nrrdEncodingGzip_read(FILE *file, void *_data, size_t elNum,
52
                       Nrrd *nrrd, NrrdIoState *nio) {
53
  static const char me[]="_nrrdEncodingGzip_read";
54
#if TEEM_ZLIB
55
  size_t sizeData, sizeRed;
56
  int error;
57
  long int bi;
58
  unsigned int didread, sizeChunk, maxChunk;
59
  char *data;
60
  gzFile gzfin;
61
  airPtrPtrUnion appu;
62
63
  sizeData = nrrdElementSize(nrrd)*elNum;
64
  /* Create the gzFile for reading in the gzipped data. */
65
  if ((gzfin = _nrrdGzOpen(file, "rb")) == Z_NULL) {
66
    /* there was a problem */
67
    biffAddf(NRRD, "%s: error opening gzFile", me);
68
    return 1;
69
  }
70
71
  /* keeps track of how many bytes have been successfully read in */
72
  sizeRed = 0;
73
74
  /* zlib can only handle data sizes up to UINT_MAX ==> if there's more than
75
     UINT_MAX bytes to read in, we read in in chunks. However, we wrap a value
76
     _nrrdZlibMaxChunk around UINT_MAX for testing purposes.  Given how
77
     sizeChunk is used below, we also cap chunk size at _nrrdZlibMaxChunk/2 to
78
     prevent overflow. */
79
  maxChunk = _nrrdZlibMaxChunk/2;
80
  sizeChunk = AIR_CAST(unsigned int, AIR_MIN(sizeData, maxChunk));
81
82
  if (nio->byteSkip < 0) {
83
    /* We don't know the size of the size to skip before the data, so
84
       decompress the data first into a temporary memory buffer.  Then
85
       the byteskipping is then just memcpy-ing the appropriate region
86
       of memory from "buff" into the given "_data" pointer */
87
    char *buff;
88
    airArray *buffArr;
89
    long backwards;
90
91
    /* setting the airArray increment to twice the chunk size means that for
92
       headers that are small compared to the data, the airArray never
93
       actually has to reallocate.  The unit is 1 because we are managing
94
       the reading in terms of bytes (sizeof(char)==1 by definition) */
95
    buff = NULL;
96
    appu.c = &buff;
97
    buffArr = airArrayNew(appu.v, NULL, 1, 2*sizeChunk);
98
    airArrayLenSet(buffArr, sizeChunk);
99
    if (!( buffArr && buffArr->data )) {
100
      biffAddf(NRRD, "%s: couldn't initialize airArray\n", me);
101
      return 1;
102
    }
103
104
    /* we keep reading in chunks as long as there hasn't been an error,
105
       and we haven't hit EOF (EOF signified by read == 0).  Unlike the
106
       code below (for positive byteskip), we are obligated to read until
107
       the bitter end, and can't update sizeChunk to encompass only the
108
       required data. */
109
    while (!(error = _nrrdGzRead(gzfin, buff + sizeRed,
110
                                 sizeChunk, &didread))
111
           && didread > 0) {
112
      sizeRed += didread;
113
      if (didread >= sizeChunk) {
114
        /* we were able to read as much data as we requested, maybe there is
115
           more, so we need to make our temp buffer bigger */
116
        unsigned int newlen = buffArr->len + sizeChunk;
117
        if (newlen < buffArr->len) {
118
          biffAddf(NRRD, "%s: array size will exceed uint capacity", me);
119
          return 1;
120
        }
121
        airArrayLenSet(buffArr, newlen);
122
        if (!buffArr->data) {
123
          biffAddf(NRRD, "%s: couldn't re-allocate data buffer", me);
124
          return 1;
125
        }
126
      }
127
    }
128
    if (error) {
129
      biffAddf(NRRD, "%s: error reading from gzFile", me);
130
      return 1;
131
    }
132
    /* backwards is (positive) number of bytes AFTER data that we ignore */
133
    backwards = -nio->byteSkip - 1;
134
    if (sizeRed < sizeData + AIR_CAST(size_t, backwards)) {
135
      char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
136
      biffAddf(NRRD, "%s: expected %s bytes but received only %s", me,
137
               airSprintSize_t(stmp1, sizeData + AIR_CAST(size_t, backwards)),
138
               airSprintSize_t(stmp2, sizeRed));
139
      return 1;
140
    }
141
    /* also handles nio->byteSkip == -N-1 signifying extra N bytes at end */
142
    memcpy(_data, buff + sizeRed - sizeData - backwards, sizeData);
143
    airArrayNuke(buffArr);
144
  } else {
145
    /* no negative byteskip: after byteskipping, we can read directly
146
       into given data buffer */
147
    if (nio->byteSkip > 0) {
148
      for (bi=0; bi<nio->byteSkip; bi++) {
149
        unsigned char b;
150
        /* Check to see if a single byte was able to be read. */
151
        if (_nrrdGzRead(gzfin, &b, 1, &didread) != 0 || didread != 1) {
152
          biffAddf(NRRD, "%s: hit an error skipping byte %ld of %ld",
153
                   me, bi, nio->byteSkip);
154
          return 1;
155
        }
156
      }
157
    }
158
    /* Pointer to chunks as we read them. */
159
    data = AIR_CAST(char *, _data);
160
    while (!(error = _nrrdGzRead(gzfin, data, sizeChunk, &didread))
161
           && didread > 0) {
162
      /* Increment the data pointer to the next available chunk. */
163
      data += didread;
164
      sizeRed += didread;
165
      /* We only want to read as much data as we need, so we need to check
166
         to make sure that we don't request data that might be there but that
167
         we don't want.  This will reduce sizeChunk when we get to the last
168
         block (which may be smaller than the original sizeChunk). */
169
      if (sizeData >= sizeRed
170
          && sizeData - sizeRed < sizeChunk) {
171
        sizeChunk = AIR_CAST(unsigned int, sizeData - sizeRed);
172
      }
173
    }
174
    if (error) {
175
      biffAddf(NRRD, "%s: error reading from gzFile", me);
176
      return 1;
177
    }
178
    /* Check to see if we got out as much as we thought we should. */
179
    if (sizeRed != sizeData) {
180
      char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
181
      biffAddf(NRRD, "%s: expected %s bytes but received %s", me,
182
               airSprintSize_t(stmp1, sizeData),
183
               airSprintSize_t(stmp2, sizeRed));
184
      return 1;
185
    }
186
  }
187
188
  /* Close the gzFile.  Since _nrrdGzClose does not close the FILE* we
189
     will not encounter problems when dataFile is closed later. */
190
  if (_nrrdGzClose(gzfin)) {
191
    biffAddf(NRRD, "%s: error closing gzFile", me);
192
    return 1;
193
  }
194
195
  return 0;
196
#else
197
  AIR_UNUSED(file);
198
  AIR_UNUSED(_data);
199
  AIR_UNUSED(elNum);
200
  AIR_UNUSED(nrrd);
201
  AIR_UNUSED(nio);
202
  biffAddf(NRRD, "%s: sorry, this nrrd not compiled with gzip enabled", me);
203
  return 1;
204
#endif
205
}
206
207
static int
208
_nrrdEncodingGzip_write(FILE *file, const void *_data, size_t elNum,
209
                        const Nrrd *nrrd, NrrdIoState *nio) {
210
  static const char me[]="_nrrdEncodingGzip_write";
211
#if TEEM_ZLIB
212
  size_t sizeData, sizeWrit;
213
  int fmt_i=0, error;
214
  const char *data;
215
  char fmt[4];
216
  gzFile gzfout;
217
  unsigned int wrote, sizeChunk;
218
219
  sizeData = nrrdElementSize(nrrd)*elNum;
220
221
  /* Set format string based on the NrrdIoState parameters. */
222
  fmt[fmt_i++] = 'w';
223
  if (0 <= nio->zlibLevel && nio->zlibLevel <= 9)
224
    fmt[fmt_i++] = AIR_CAST(char, '0' + nio->zlibLevel);
225
  switch (nio->zlibStrategy) {
226
  case nrrdZlibStrategyHuffman:
227
    fmt[fmt_i++] = 'h';
228
    break;
229
  case nrrdZlibStrategyFiltered:
230
    fmt[fmt_i++] = 'f';
231
    break;
232
  case nrrdZlibStrategyDefault:
233
  default:
234
    break;
235
  }
236
  fmt[fmt_i] = 0;
237
238
  /* Create the gzFile for writing in the gzipped data. */
239
  if ((gzfout = _nrrdGzOpen(file, fmt)) == Z_NULL) {
240
    /* there was a problem */
241
    biffAddf(NRRD, "%s: error opening gzFile", me);
242
    return 1;
243
  }
244
245
  /* zlib can only handle data sizes up to UINT_MAX ==> if there's more than
246
     UINT_MAX bytes to write out, we write out in chunks.  As above, we wrap
247
     _nrrdZlibMaxChunk around UINT_MAX for testing purposes. */
248
  sizeChunk = AIR_CAST(unsigned int, AIR_MIN(sizeData, _nrrdZlibMaxChunk));
249
250
  /* keeps track of what how much has been successfully written */
251
  sizeWrit = 0;
252
  /* Pointer to the chunks as we write them. */
253
  data = AIR_CAST(const char *, _data);
254
255
  /* Ok, now we can begin writing. */
256
  while ((error = _nrrdGzWrite(gzfout, AIR_CVOIDP(data),
257
                               sizeChunk, &wrote)) == 0
258
         && wrote > 0) {
259
    /* Increment the data pointer to the next available spot. */
260
    data += wrote;
261
    sizeWrit += wrote;
262
    /* We only want to write as much data as we need, so we need to check
263
       to make sure that we don't write more data than is there.  This
264
       will reduce sizeChunk when we get to the last block (which may
265
       be smaller than the original sizeChunk).
266
    */
267
    if (sizeData >= sizeWrit
268
        && sizeData - sizeWrit < sizeChunk)
269
      sizeChunk = AIR_CAST(unsigned int, sizeData - sizeWrit);
270
  }
271
272
  if (error) {
273
    biffAddf(NRRD, "%s: error writing to gzFile", me);
274
    return 1;
275
  }
276
277
  /* Check to see if we wrote out as much as we thought we should. */
278
  if (sizeWrit != sizeData) {
279
    char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
280
    biffAddf(NRRD, "%s: expected to write %s bytes, but only wrote %s", me,
281
             airSprintSize_t(stmp1, sizeData),
282
             airSprintSize_t(stmp2, sizeWrit));
283
    return 1;
284
  }
285
286
  /* Close the gzFile.  Since _nrrdGzClose does not close the FILE* we
287
     will not encounter problems when dataFile is closed later. */
288
  if (_nrrdGzClose(gzfout)) {
289
    biffAddf(NRRD, "%s: error closing gzFile", me);
290
    return 1;
291
  }
292
293
  return 0;
294
#else
295
  AIR_UNUSED(file);
296
  AIR_UNUSED(_data);
297
  AIR_UNUSED(elNum);
298
  AIR_UNUSED(nrrd);
299
  AIR_UNUSED(nio);
300
  biffAddf(NRRD, "%s: sorry, this nrrd not compiled with zlib "
301
           "(needed for gzip) enabled", me);
302
  return 1;
303
#endif
304
}
305
306
const NrrdEncoding
307
_nrrdEncodingGzip = {
308
  "gzip",      /* name */
309
  "raw.gz",    /* suffix */
310
  AIR_TRUE,    /* endianMatters */
311
  AIR_TRUE,   /* isCompression */
312
  _nrrdEncodingGzip_available,
313
  _nrrdEncodingGzip_read,
314
  _nrrdEncodingGzip_write
315
};
316
317
const NrrdEncoding *const
318
nrrdEncodingGzip = &_nrrdEncodingGzip;