Line | Count | Source (jump to first uncovered line) |
1 | | /** @file inflate.c |
2 | | * @brief Functions to inflate data/tags |
3 | | * @ingroup MAT |
4 | | */ |
5 | | /* |
6 | | * Copyright (c) 2015-2024, The matio contributors |
7 | | * Copyright (c) 2005-2014, Christopher C. Hulbert |
8 | | * All rights reserved. |
9 | | * |
10 | | * Redistribution and use in source and binary forms, with or without |
11 | | * modification, are permitted provided that the following conditions are met: |
12 | | * |
13 | | * 1. Redistributions of source code must retain the above copyright notice, this |
14 | | * list of conditions and the following disclaimer. |
15 | | * |
16 | | * 2. Redistributions in binary form must reproduce the above copyright notice, |
17 | | * this list of conditions and the following disclaimer in the documentation |
18 | | * and/or other materials provided with the distribution. |
19 | | * |
20 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
21 | | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 | | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
23 | | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
24 | | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
25 | | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
26 | | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
27 | | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
28 | | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 | | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | | */ |
31 | | |
32 | | #include "matio_private.h" |
33 | | #include <stdlib.h> |
34 | | #include <string.h> |
35 | | #include <limits.h> |
36 | | |
37 | | #if HAVE_ZLIB |
38 | | |
39 | | /** @cond mat_devman */ |
40 | | |
41 | | /** @brief Inflate the data until @c nBytes of uncompressed data has been |
42 | | * inflated |
43 | | * |
44 | | * @ingroup mat_internal |
45 | | * @param mat Pointer to the MAT file |
46 | | * @param z zlib compression stream |
47 | | * @param nBytes Number of uncompressed bytes to skip |
48 | | * @param[out] bytesread Number of bytes read from the file |
49 | | * @retval 0 on success |
50 | | |
51 | | */ |
52 | | int |
53 | | InflateSkip(mat_t *mat, z_streamp z, int nBytes, size_t *bytesread) |
54 | 37 | { |
55 | 37 | mat_uint8_t comp_buf[READ_BLOCK_SIZE], uncomp_buf[READ_BLOCK_SIZE]; |
56 | 37 | int n, err = MATIO_E_NO_ERROR, cnt = 0; |
57 | | |
58 | 37 | if ( nBytes < 1 ) |
59 | 0 | return MATIO_E_NO_ERROR; |
60 | | |
61 | 37 | n = nBytes < READ_BLOCK_SIZE ? nBytes : READ_BLOCK_SIZE; |
62 | 37 | if ( !z->avail_in ) { |
63 | 34 | size_t nbytes = fread(comp_buf, 1, n, (FILE *)mat->fp); |
64 | 34 | if ( 0 == nbytes ) { |
65 | 0 | return err; |
66 | 0 | } |
67 | 34 | if ( NULL != bytesread ) { |
68 | 16 | *bytesread += nbytes; |
69 | 16 | } |
70 | 34 | z->avail_in = (uInt)nbytes; |
71 | 34 | z->next_in = comp_buf; |
72 | 34 | } |
73 | 37 | z->avail_out = n; |
74 | 37 | z->next_out = uncomp_buf; |
75 | 37 | err = inflate(z, Z_FULL_FLUSH); |
76 | 37 | if ( err == Z_STREAM_END ) { |
77 | 0 | return MATIO_E_NO_ERROR; |
78 | 37 | } else if ( err != Z_OK ) { |
79 | 6 | Mat_Critical("InflateSkip: inflate returned %s", |
80 | 6 | zError(err == Z_NEED_DICT ? Z_DATA_ERROR : err)); |
81 | 6 | return MATIO_E_FILE_FORMAT_VIOLATION; |
82 | 31 | } else { |
83 | 31 | err = MATIO_E_NO_ERROR; |
84 | 31 | } |
85 | 31 | if ( !z->avail_out ) { |
86 | 31 | cnt += n; |
87 | 31 | n = nBytes - cnt; |
88 | 31 | if ( n > READ_BLOCK_SIZE ) { |
89 | 5 | n = READ_BLOCK_SIZE; |
90 | 5 | } |
91 | 31 | z->avail_out = n; |
92 | 31 | z->next_out = uncomp_buf; |
93 | 31 | } |
94 | 1.57k | while ( cnt < nBytes ) { |
95 | 1.54k | if ( !z->avail_in ) { |
96 | 175 | size_t nbytes = fread(comp_buf, 1, n, (FILE *)mat->fp); |
97 | 175 | if ( 0 == nbytes ) { |
98 | 5 | break; |
99 | 5 | } |
100 | 170 | if ( NULL != bytesread ) { |
101 | 170 | *bytesread += nbytes; |
102 | 170 | } |
103 | 170 | z->avail_in = (uInt)nbytes; |
104 | 170 | z->next_in = comp_buf; |
105 | 170 | } |
106 | 1.54k | err = inflate(z, Z_FULL_FLUSH); |
107 | 1.54k | if ( err == Z_STREAM_END ) { |
108 | 0 | err = MATIO_E_NO_ERROR; |
109 | 0 | break; |
110 | 1.54k | } else if ( err != Z_OK ) { |
111 | 0 | const char *errMsg = zError(err == Z_NEED_DICT ? Z_DATA_ERROR : err); |
112 | 0 | err = MATIO_E_FILE_FORMAT_VIOLATION; |
113 | 0 | Mat_Critical("InflateSkip: inflate returned %s", errMsg); |
114 | 0 | break; |
115 | 1.54k | } else { |
116 | 1.54k | err = MATIO_E_NO_ERROR; |
117 | 1.54k | } |
118 | 1.54k | if ( !z->avail_out ) { |
119 | 1.36k | cnt += n; |
120 | 1.36k | n = nBytes - cnt; |
121 | 1.36k | if ( n > READ_BLOCK_SIZE ) { |
122 | 1.36k | n = READ_BLOCK_SIZE; |
123 | 1.36k | } |
124 | 1.36k | z->avail_out = n; |
125 | 1.36k | z->next_out = uncomp_buf; |
126 | 1.36k | } |
127 | 1.54k | } |
128 | | |
129 | 31 | if ( z->avail_in ) { |
130 | 26 | const mat_off_t offset = -(mat_off_t)z->avail_in; |
131 | 26 | (void)fseeko((FILE *)mat->fp, offset, SEEK_CUR); |
132 | 26 | if ( NULL != bytesread ) { |
133 | 8 | *bytesread -= z->avail_in; |
134 | 8 | } |
135 | 26 | z->avail_in = 0; |
136 | 26 | } |
137 | | |
138 | 31 | return err; |
139 | 37 | } |
140 | | |
141 | | /** @brief Inflate the data until @c len elements of compressed data with data |
142 | | * type @c data_type has been inflated |
143 | | * |
144 | | * @ingroup mat_internal |
145 | | * @param mat Pointer to the MAT file |
146 | | * @param z zlib compression stream |
147 | | * @param data_type Data type (matio_types enumerations) |
148 | | * @param len Number of elements of datatype @c data_type to skip |
149 | | * @retval 0 on success |
150 | | |
151 | | */ |
152 | | int |
153 | | InflateSkipData(mat_t *mat, z_streamp z, enum matio_types data_type, int len) |
154 | 0 | { |
155 | 0 | if ( mat == NULL || z == NULL || len < 1 ) |
156 | 0 | return MATIO_E_BAD_ARGUMENT; |
157 | | |
158 | 0 | switch ( data_type ) { |
159 | 0 | case MAT_T_UTF8: |
160 | 0 | case MAT_T_UTF16: |
161 | 0 | case MAT_T_UTF32: |
162 | 0 | return MATIO_E_OPERATION_NOT_SUPPORTED; |
163 | 0 | default: |
164 | 0 | break; |
165 | 0 | } |
166 | | |
167 | 0 | return InflateSkip(mat, z, (unsigned int)Mat_SizeOf(data_type) * len, NULL); |
168 | 0 | } |
169 | | |
170 | | /** @brief Inflates the dimensions tag and the dimensions data |
171 | | * |
172 | | * @c buf must hold at least (8+4*rank) bytes where rank is the number of |
173 | | * dimensions. If the end of the dimensions data is not aligned on an 8-byte |
174 | | * boundary, this function eats up those bytes and stores then in @c buf. |
175 | | * @ingroup mat_internal |
176 | | * @param mat Pointer to the MAT file |
177 | | * @param z zlib compression stream |
178 | | * @param buf Pointer to store the dimensions flag and data |
179 | | * @param nBytes Size of buf in bytes |
180 | | * @param dims Output buffer to be allocated if (8+4*rank) > nBytes |
181 | | * @param[out] bytesread Number of bytes read from the file |
182 | | * @retval 0 on success |
183 | | |
184 | | */ |
185 | | int |
186 | | InflateRankDims(mat_t *mat, z_streamp z, void *buf, size_t nBytes, mat_uint32_t **dims, |
187 | | size_t *bytesread) |
188 | 29 | { |
189 | 29 | mat_uint32_t tag[2]; |
190 | 29 | mat_uint32_t rank, i; |
191 | 29 | int err; |
192 | 29 | size_t nbytes = 0; |
193 | | |
194 | 29 | if ( buf == NULL ) |
195 | 0 | return MATIO_E_BAD_ARGUMENT; |
196 | | |
197 | 29 | err = Inflate(mat, z, buf, 8, bytesread); |
198 | 29 | if ( err ) { |
199 | 0 | return err; |
200 | 0 | } |
201 | 29 | tag[0] = *(mat_uint32_t *)buf; |
202 | 29 | tag[1] = *((mat_uint32_t *)buf + 1); |
203 | 29 | if ( mat->byteswap ) { |
204 | 0 | Mat_uint32Swap(tag); |
205 | 0 | Mat_uint32Swap(tag + 1); |
206 | 0 | } |
207 | 29 | if ( (tag[0] & 0x0000ffff) != MAT_T_INT32 ) { |
208 | 0 | Mat_Critical("InflateRankDims: Reading dimensions expected type MAT_T_INT32"); |
209 | 0 | return MATIO_E_FILE_FORMAT_VIOLATION; |
210 | 0 | } |
211 | 29 | rank = tag[1]; |
212 | 29 | if ( rank % 8 != 0 ) |
213 | 0 | i = 8 - (rank % 8); |
214 | 29 | else |
215 | 29 | i = 0; |
216 | | |
217 | 29 | if ( rank > INT_MAX - i - 2 ) { |
218 | 0 | Mat_Critical("InflateRankDims: Reading dimensions expected rank in integer range"); |
219 | 0 | return MATIO_E_FILE_FORMAT_VIOLATION; |
220 | 0 | } |
221 | 29 | rank += i; |
222 | | |
223 | 29 | err = Mul(&nbytes, rank + 2, sizeof(mat_uint32_t)); |
224 | 29 | if ( err ) { |
225 | 0 | Mat_Critical("Integer multiplication overflow"); |
226 | 0 | return err; |
227 | 0 | } |
228 | | |
229 | 29 | if ( nbytes <= nBytes ) { |
230 | 29 | err = Inflate(mat, z, (mat_uint32_t *)buf + 2, rank, bytesread); |
231 | 29 | } else { |
232 | | /* Cannot use too small buf, but can allocate output buffer dims */ |
233 | 0 | *dims = (mat_uint32_t *)calloc(rank, sizeof(mat_uint32_t)); |
234 | 0 | if ( NULL != *dims ) { |
235 | 0 | err = Inflate(mat, z, *dims, rank, bytesread); |
236 | 0 | } else { |
237 | 0 | *((mat_uint32_t *)buf + 1) = 0; |
238 | 0 | Mat_Critical("Error allocating memory for dims"); |
239 | 0 | return MATIO_E_OUT_OF_MEMORY; |
240 | 0 | } |
241 | 0 | } |
242 | | |
243 | 29 | return err; |
244 | 29 | } |
245 | | |
246 | | /** @brief Inflates the data |
247 | | * |
248 | | * buf must hold at least @c nBytes bytes |
249 | | * @ingroup mat_internal |
250 | | * @param mat Pointer to the MAT file |
251 | | * @param z zlib compression stream |
252 | | * @param buf Pointer to store the uncompressed data |
253 | | * @param nBytes Number of uncompressed bytes to inflate |
254 | | * @param[out] bytesread Number of bytes read from the file |
255 | | * @retval 0 on success |
256 | | |
257 | | */ |
258 | | int |
259 | | Inflate(mat_t *mat, z_streamp z, void *buf, unsigned int nBytes, size_t *bytesread) |
260 | 219 | { |
261 | 219 | mat_uint8_t comp_buf[4]; |
262 | 219 | int err = MATIO_E_NO_ERROR; |
263 | | |
264 | 219 | if ( buf == NULL ) |
265 | 0 | return MATIO_E_BAD_ARGUMENT; |
266 | | |
267 | 219 | if ( !z->avail_in ) { |
268 | 216 | size_t nbytes = fread(comp_buf, 1, 1, (FILE *)mat->fp); |
269 | 216 | if ( 0 == nbytes ) { |
270 | 40 | return err; |
271 | 40 | } |
272 | 176 | if ( NULL != bytesread ) { |
273 | 145 | *bytesread += nbytes; |
274 | 145 | } |
275 | 176 | z->avail_in = (uInt)nbytes; |
276 | 176 | z->next_in = comp_buf; |
277 | 176 | } |
278 | 179 | z->avail_out = nBytes; |
279 | 179 | z->next_out = ZLIB_BYTE_PTR(buf); |
280 | 179 | err = inflate(z, Z_NO_FLUSH); |
281 | 179 | if ( err != Z_OK ) { |
282 | 5 | Mat_Critical("Inflate: inflate returned %s", |
283 | 5 | zError(err == Z_NEED_DICT ? Z_DATA_ERROR : err)); |
284 | 5 | return MATIO_E_FILE_FORMAT_VIOLATION; |
285 | 174 | } else { |
286 | 174 | err = MATIO_E_NO_ERROR; |
287 | 174 | } |
288 | 1.31k | while ( z->avail_out && !z->avail_in ) { |
289 | 1.14k | size_t nbytes = fread(comp_buf, 1, 1, (FILE *)mat->fp); |
290 | 1.14k | if ( 0 == nbytes ) { |
291 | 0 | break; |
292 | 0 | } |
293 | 1.14k | if ( NULL != bytesread ) { |
294 | 1.12k | *bytesread += nbytes; |
295 | 1.12k | } |
296 | 1.14k | z->avail_in = (uInt)nbytes; |
297 | 1.14k | z->next_in = comp_buf; |
298 | 1.14k | err = inflate(z, Z_NO_FLUSH); |
299 | 1.14k | if ( err != Z_OK ) { |
300 | 0 | Mat_Critical("Inflate: inflate returned %s", |
301 | 0 | zError(err == Z_NEED_DICT ? Z_DATA_ERROR : err)); |
302 | 0 | return MATIO_E_FILE_FORMAT_VIOLATION; |
303 | 1.14k | } else { |
304 | 1.14k | err = MATIO_E_NO_ERROR; |
305 | 1.14k | } |
306 | 1.14k | } |
307 | | |
308 | 174 | if ( z->avail_in ) { |
309 | 36 | const mat_off_t offset = -(mat_off_t)z->avail_in; |
310 | 36 | (void)fseeko((FILE *)mat->fp, offset, SEEK_CUR); |
311 | 36 | if ( NULL != bytesread ) { |
312 | 32 | *bytesread -= z->avail_in; |
313 | 32 | } |
314 | 36 | z->avail_in = 0; |
315 | 36 | } |
316 | | |
317 | 174 | if ( z->avail_out && feof((FILE *)mat->fp) ) { |
318 | 0 | Mat_Warning( |
319 | 0 | "Unexpected end-of-file: " |
320 | 0 | "Processed %u bytes, expected %u bytes", |
321 | 0 | nBytes - z->avail_out, nBytes); |
322 | 0 | memset(buf, 0, nBytes); |
323 | 0 | } |
324 | | |
325 | 174 | return err; |
326 | 174 | } |
327 | | |
328 | | /** @brief Inflates the data in blocks |
329 | | * |
330 | | * buf must hold at least @c nBytes bytes |
331 | | * @ingroup mat_internal |
332 | | * @param mat Pointer to the MAT file |
333 | | * @param z zlib compression stream |
334 | | * @param buf Pointer to store the uncompressed data |
335 | | * @param nBytes Number of uncompressed bytes to inflate |
336 | | * @retval 0 on success |
337 | | |
338 | | */ |
339 | | int |
340 | | InflateData(mat_t *mat, z_streamp z, void *buf, unsigned int nBytes) |
341 | 12 | { |
342 | 12 | mat_uint8_t comp_buf[READ_BLOCK_SIZE]; |
343 | 12 | int err = MATIO_E_NO_ERROR; |
344 | 12 | unsigned int n; |
345 | 12 | size_t bytesread = 0; |
346 | | |
347 | 12 | if ( buf == NULL ) |
348 | 0 | return MATIO_E_BAD_ARGUMENT; |
349 | 12 | if ( nBytes == 0 ) { |
350 | 0 | return MATIO_E_NO_ERROR; |
351 | 0 | } |
352 | | |
353 | 12 | n = nBytes < READ_BLOCK_SIZE ? nBytes : READ_BLOCK_SIZE; |
354 | 12 | if ( !z->avail_in ) { |
355 | 12 | size_t nbytes = fread(comp_buf, 1, n, (FILE *)mat->fp); |
356 | 12 | if ( 0 == nbytes ) { |
357 | 0 | return err; |
358 | 0 | } |
359 | 12 | bytesread += nbytes; |
360 | 12 | z->avail_in = (uInt)nbytes; |
361 | 12 | z->next_in = comp_buf; |
362 | 12 | } |
363 | 12 | z->avail_out = nBytes; |
364 | 12 | z->next_out = ZLIB_BYTE_PTR(buf); |
365 | 12 | err = inflate(z, Z_FULL_FLUSH); |
366 | 12 | if ( err == Z_STREAM_END ) { |
367 | 0 | return MATIO_E_NO_ERROR; |
368 | 12 | } else if ( err != Z_OK ) { |
369 | 3 | Mat_Critical("InflateData: inflate returned %s", |
370 | 3 | zError(err == Z_NEED_DICT ? Z_DATA_ERROR : err)); |
371 | 3 | return MATIO_E_FAIL_TO_IDENTIFY; |
372 | 9 | } else { |
373 | 9 | err = MATIO_E_NO_ERROR; |
374 | 9 | } |
375 | 9 | while ( z->avail_out && !z->avail_in ) { |
376 | 0 | size_t nbytes; |
377 | 0 | if ( nBytes > READ_BLOCK_SIZE + bytesread ) { |
378 | 0 | nbytes = fread(comp_buf, 1, READ_BLOCK_SIZE, (FILE *)mat->fp); |
379 | 0 | } else if ( nBytes < 1 + bytesread ) { /* Read a byte at a time */ |
380 | 0 | nbytes = fread(comp_buf, 1, 1, (FILE *)mat->fp); |
381 | 0 | } else { |
382 | 0 | nbytes = fread(comp_buf, 1, nBytes - bytesread, (FILE *)mat->fp); |
383 | 0 | } |
384 | 0 | if ( 0 == nbytes ) { |
385 | 0 | break; |
386 | 0 | } |
387 | 0 | bytesread += nbytes; |
388 | 0 | z->avail_in = (uInt)nbytes; |
389 | 0 | z->next_in = comp_buf; |
390 | 0 | err = inflate(z, Z_FULL_FLUSH); |
391 | 0 | if ( err == Z_STREAM_END ) { |
392 | 0 | err = MATIO_E_NO_ERROR; |
393 | 0 | break; |
394 | 0 | } else if ( err != Z_OK ) { |
395 | 0 | const char *errMsg = zError(err == Z_NEED_DICT ? Z_DATA_ERROR : err); |
396 | 0 | err = MATIO_E_FAIL_TO_IDENTIFY; |
397 | 0 | Mat_Critical("InflateData: inflate returned %s", errMsg); |
398 | 0 | break; |
399 | 0 | } else { |
400 | 0 | err = MATIO_E_NO_ERROR; |
401 | 0 | } |
402 | 0 | } |
403 | | |
404 | 9 | if ( z->avail_in ) { |
405 | 9 | const mat_off_t offset = -(mat_off_t)z->avail_in; |
406 | 9 | (void)fseeko((FILE *)mat->fp, offset, SEEK_CUR); |
407 | | /* bytesread -= z->avail_in; */ |
408 | 9 | z->avail_in = 0; |
409 | 9 | } |
410 | | |
411 | 9 | if ( z->avail_out && feof((FILE *)mat->fp) ) { |
412 | 0 | Mat_Warning("InflateData: Read beyond EOF error: Processed %u bytes, expected %u bytes", |
413 | 0 | nBytes - z->avail_out, nBytes); |
414 | 0 | memset(buf, 0, nBytes); |
415 | 0 | } |
416 | | |
417 | 9 | return err; |
418 | 12 | } |
419 | | |
420 | | /** @endcond */ |
421 | | |
422 | | #endif |