Coverage Report

Created: 2024-06-18 06:29

/src/matio/src/inflate.c
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