Coverage Report

Created: 2026-04-01 07:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/compression_zlib.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2022 Dirk Farin <dirk.farin@gmail.com>
4
 *
5
 * This file is part of libheif.
6
 *
7
 * libheif is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libheif is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
22
#include "compression.h"
23
24
25
#if HAVE_ZLIB
26
27
#include <zlib.h>
28
#include <cstring>
29
#include <iostream>
30
31
std::vector<uint8_t> compress(const uint8_t* input, size_t size, int windowSize)
32
0
{
33
0
  std::vector<uint8_t> output;
34
35
  // initialize compressor
36
37
0
  const int outBufferSize = 8192;
38
0
  uint8_t dst[outBufferSize];
39
40
0
  z_stream strm;
41
0
  memset(&strm, 0, sizeof(z_stream));
42
43
0
  strm.avail_in = (uInt)size;
44
0
  strm.next_in = (Bytef*)input;
45
46
0
  strm.avail_out = outBufferSize;
47
0
  strm.next_out = (Bytef*) dst;
48
49
0
  strm.zalloc = Z_NULL;
50
0
  strm.zfree = Z_NULL;
51
0
  strm.opaque = Z_NULL;
52
53
0
  int err = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowSize, 8, Z_DEFAULT_STRATEGY);
54
0
  if (err != Z_OK) {
55
0
    return {}; // TODO: return error
56
0
  }
57
58
0
  do {
59
0
    strm.next_out = dst;
60
0
    strm.avail_out = outBufferSize;
61
62
0
    err = deflate(&strm, Z_FINISH);
63
0
    if (err == Z_BUF_ERROR || err == Z_OK) {
64
      // this is the usual case when we run out of buffer space
65
      // -> do nothing
66
0
    }
67
0
    else if (err == Z_STREAM_ERROR) {
68
0
      return {}; // TODO: return error
69
0
    }
70
71
72
    // append decoded data to output
73
74
0
    output.insert(output.end(), dst, dst + outBufferSize - strm.avail_out);
75
0
  } while (err != Z_STREAM_END);
76
77
0
  deflateEnd(&strm);
78
79
0
  return output;
80
0
}
81
82
83
Result<std::vector<uint8_t>> do_inflate(const std::vector<uint8_t>& compressed_input, int windowSize)
84
3
{
85
3
  if (compressed_input.empty()) {
86
1
    return Error(heif_error_Invalid_input, heif_suberror_Decompression_invalid_data,
87
1
                 "Empty zlib compressed data.");
88
1
  }
89
90
2
  std::vector<uint8_t> output;
91
92
  // decompress data with zlib
93
94
2
  std::vector<uint8_t> dst;
95
2
  dst.resize(8192);
96
97
2
  z_stream strm;
98
2
  memset(&strm, 0, sizeof(z_stream));
99
100
2
  strm.avail_in = (int)compressed_input.size();
101
2
  strm.next_in = (Bytef*) compressed_input.data();
102
103
2
  strm.avail_out = (uInt)dst.size();
104
2
  strm.next_out = (Bytef*) dst.data();
105
106
2
  strm.zalloc = Z_NULL;
107
2
  strm.zfree = Z_NULL;
108
2
  strm.opaque = Z_NULL;
109
110
2
  int err = -1;
111
112
2
  err = inflateInit2(&strm, windowSize);
113
2
  if (err != Z_OK) {
114
0
    std::stringstream sstr;
115
0
    sstr << "Error initialising zlib inflate: " << (strm.msg ? strm.msg : "NULL") << " (" << err << ")\n";
116
0
    return Error(heif_error_Memory_allocation_error, heif_suberror_Compression_initialisation_error, sstr.str());
117
0
  }
118
119
2
  do {
120
2
    strm.avail_out = (uInt)dst.size();
121
2
    strm.next_out = (Bytef*) dst.data();
122
123
2
    err = inflate(&strm, Z_NO_FLUSH);
124
125
2
    if (err == Z_BUF_ERROR) {
126
0
      if (strm.avail_in == 0) {
127
        // All input consumed; decompression is complete even without Z_STREAM_END
128
0
        break;
129
0
      }
130
131
0
      if (dst.size() >= 256 * 1024 * 1024) { // TODO: make this a security limit
132
0
        inflateEnd(&strm);
133
0
        std::stringstream sstr;
134
0
        sstr << "Error performing zlib inflate: maximum output buffer size exceeded\n";
135
0
        return Error(heif_error_Memory_allocation_error, heif_suberror_Compression_initialisation_error, sstr.str());
136
0
      }
137
138
0
      dst.resize(dst.size() * 2);
139
0
      strm.next_out = dst.data();
140
0
      strm.avail_out = (uInt)dst.size();
141
0
      continue;
142
0
    }
143
144
2
    if (err == Z_NEED_DICT || err == Z_DATA_ERROR || err == Z_STREAM_ERROR) {
145
2
      inflateEnd(&strm);
146
2
      std::stringstream sstr;
147
2
      sstr << "Error performing zlib inflate: " << (strm.msg ? strm.msg : "NULL") << " (" << err << ")\n";
148
2
      return Error(heif_error_Invalid_input, heif_suberror_Decompression_invalid_data, sstr.str());
149
2
    }
150
151
    // append decoded data to output
152
0
    output.insert(output.end(), dst.begin(), dst.end() - strm.avail_out);
153
0
  } while (err != Z_STREAM_END);
154
155
156
0
  inflateEnd(&strm);
157
158
0
  return output;
159
2
}
160
161
std::vector<uint8_t> compress_zlib(const uint8_t* input, size_t size)
162
0
{
163
0
  return compress(input, size, 15);
164
0
}
165
166
std::vector<uint8_t> compress_deflate(const uint8_t* input, size_t size)
167
0
{
168
0
  return compress(input, size, -15);
169
0
}
170
171
172
Result<std::vector<uint8_t>> decompress_zlib(const std::vector<uint8_t>& compressed_input)
173
3
{
174
3
  return do_inflate(compressed_input, 15);
175
3
}
176
177
Result<std::vector<uint8_t>> decompress_deflate(const std::vector<uint8_t>& compressed_input)
178
0
{
179
0
  return do_inflate(compressed_input, -15);
180
0
}
181
#endif