summaryrefslogtreecommitdiffstats
path: root/src/H5Zdeflate.c
blob: b7a372b645aaf91add5ac30398a3840f05cadc2d (plain)
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
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the COPYING file, which can be found at the root of the source code       *
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "H5Zmodule.h" /* This source code file is part of the H5Z module */

#include "H5private.h"   /* Generic Functions			*/
#include "H5Eprivate.h"  /* Error handling		  	*/
#include "H5MMprivate.h" /* Memory management			*/
#include "H5Zpkg.h"      /* Data filters				*/

#ifdef H5_HAVE_FILTER_DEFLATE

#if defined(H5_HAVE_ZLIB_H) && !defined(H5_ZLIB_HEADER)
#define H5_ZLIB_HEADER "zlib.h"
#endif
#if defined(H5_ZLIB_HEADER)
#include H5_ZLIB_HEADER /* "zlib.h" */
#endif

/* Local function prototypes */
static size_t H5Z__filter_deflate(unsigned flags, size_t cd_nelmts, const unsigned cd_values[], size_t nbytes,
                                  size_t *buf_size, void **buf);

/* This message derives from H5Z */
const H5Z_class2_t H5Z_DEFLATE[1] = {{
    H5Z_CLASS_T_VERS,    /* H5Z_class_t version */
    H5Z_FILTER_DEFLATE,  /* Filter id number		*/
    1,                   /* encoder_present flag (set to true) */
    1,                   /* decoder_present flag (set to true) */
    "deflate",           /* Filter name for debugging	*/
    NULL,                /* The "can apply" callback     */
    NULL,                /* The "set local" callback     */
    H5Z__filter_deflate, /* The actual filter function	*/
}};

#define H5Z_DEFLATE_SIZE_ADJUST(s) (HDceil(((double)(s)) * 1.001) + 12)

/*-------------------------------------------------------------------------
 * Function:	H5Z__filter_deflate
 *
 * Purpose:	Implement an I/O filter around the 'deflate' algorithm in
 *              libz
 *
 * Return:	Success: Size of buffer filtered
 *		Failure: 0
 *
 *-------------------------------------------------------------------------
 */
static size_t
H5Z__filter_deflate(unsigned flags, size_t cd_nelmts, const unsigned cd_values[], size_t nbytes,
                    size_t *buf_size, void **buf)
{
    void  *outbuf = NULL; /* Pointer to new buffer */
    int    status;        /* Status from zlib operation */
    size_t ret_value = 0; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Sanity check */
    assert(*buf_size > 0);
    assert(buf);
    assert(*buf);

    /* Check arguments */
    if (cd_nelmts != 1 || cd_values[0] > 9)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, 0, "invalid deflate aggression level");

    if (flags & H5Z_FLAG_REVERSE) {
        /* Input; uncompress */
        z_stream z_strm;             /* zlib parameters */
        size_t   nalloc = *buf_size; /* Number of bytes for output (compressed) buffer */

        /* Allocate space for the compressed buffer */
        if (NULL == (outbuf = H5MM_malloc(nalloc)))
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, 0, "memory allocation failed for deflate uncompression");

        /* Set the uncompression parameters */
        memset(&z_strm, 0, sizeof(z_strm));
        z_strm.next_in = (Bytef *)*buf;
        H5_CHECKED_ASSIGN(z_strm.avail_in, unsigned, nbytes, size_t);
        z_strm.next_out = (Bytef *)outbuf;
        H5_CHECKED_ASSIGN(z_strm.avail_out, unsigned, nalloc, size_t);

        /* Initialize the uncompression routines */
        if (Z_OK != inflateInit(&z_strm))
            HGOTO_ERROR(H5E_PLINE, H5E_CANTINIT, 0, "inflateInit() failed");

        /* Loop to uncompress the buffer */
        do {
            /* Uncompress some data */
            status = inflate(&z_strm, Z_SYNC_FLUSH);

            /* Check if we are done uncompressing data */
            if (Z_STREAM_END == status)
                break; /*done*/

            /* Check for error */
            if (Z_OK != status) {
                (void)inflateEnd(&z_strm);
                HGOTO_ERROR(H5E_PLINE, H5E_CANTINIT, 0, "inflate() failed");
            }
            else {
                /* If we're not done and just ran out of buffer space, get more */
                if (0 == z_strm.avail_out) {
                    void *new_outbuf; /* Pointer to new output buffer */

                    /* Allocate a buffer twice as big */
                    nalloc *= 2;
                    if (NULL == (new_outbuf = H5MM_realloc(outbuf, nalloc))) {
                        (void)inflateEnd(&z_strm);
                        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, 0,
                                    "memory allocation failed for deflate uncompression");
                    } /* end if */
                    outbuf = new_outbuf;

                    /* Update pointers to buffer for next set of uncompressed data */
                    z_strm.next_out  = (unsigned char *)outbuf + z_strm.total_out;
                    z_strm.avail_out = (uInt)(nalloc - z_strm.total_out);
                } /* end if */
            }     /* end else */
        } while (status == Z_OK);

        /* Free the input buffer */
        H5MM_xfree(*buf);

        /* Set return values */
        *buf      = outbuf;
        outbuf    = NULL;
        *buf_size = nalloc;
        ret_value = z_strm.total_out;

        /* Finish uncompressing the stream */
        (void)inflateEnd(&z_strm);
    } /* end if */
    else {
        /*
         * Output; compress but fail if the result would be larger than the
         * input.  The library doesn't provide in-place compression, so we
         * must allocate a separate buffer for the result.
         */
        const Bytef *z_src = (const Bytef *)(*buf);
        Bytef       *z_dst; /*destination buffer		*/
        uLongf       z_dst_nbytes = (uLongf)H5Z_DEFLATE_SIZE_ADJUST(nbytes);
        uLong        z_src_nbytes = (uLong)nbytes;
        int          aggression; /* Compression aggression setting */

        /* Set the compression aggression level */
        H5_CHECKED_ASSIGN(aggression, int, cd_values[0], unsigned);

        /* Allocate output (compressed) buffer */
        if (NULL == (outbuf = H5MM_malloc(z_dst_nbytes)))
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, 0, "unable to allocate deflate destination buffer");
        z_dst = (Bytef *)outbuf;

        /* Perform compression from the source to the destination buffer */
        status = compress2(z_dst, &z_dst_nbytes, z_src, z_src_nbytes, aggression);

        /* Check for various zlib errors */
        if (Z_BUF_ERROR == status)
            HGOTO_ERROR(H5E_PLINE, H5E_CANTINIT, 0, "overflow");
        else if (Z_MEM_ERROR == status)
            HGOTO_ERROR(H5E_PLINE, H5E_CANTINIT, 0, "deflate memory error");
        else if (Z_OK != status)
            HGOTO_ERROR(H5E_PLINE, H5E_CANTINIT, 0, "other deflate error");
        /* Successfully uncompressed the buffer */
        else {
            /* Free the input buffer */
            H5MM_xfree(*buf);

            /* Set return values */
            *buf      = outbuf;
            outbuf    = NULL;
            *buf_size = nbytes;
            ret_value = z_dst_nbytes;
        } /* end else */
    }     /* end else */

done:
    if (outbuf)
        H5MM_xfree(outbuf);
    FUNC_LEAVE_NOAPI(ret_value)
}
#endif /* H5_HAVE_FILTER_DEFLATE */