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; |