• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 24015923070

06 Apr 2026 02:11AM UTC coverage: 89.455% (+0.001%) from 89.454%
24015923070

Pull #5521

github

web-flow
Merge 91d8c1a3f into 417709dd7
Pull Request #5521: Rollup of small fixes

105887 of 118369 relevant lines covered (89.46%)

11524933.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.22
/src/lib/compression/compress_utils.cpp
1
/*
2
* Compression Utils
3
* (C) 2014,2016 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include <botan/internal/compress_utils.h>
9

10
#include <botan/exceptn.h>
11
#include <botan/mem_ops.h>
12
#include <botan/internal/fmt.h>
13
#include <botan/internal/int_utils.h>
14
#include <cstdlib>
15

16
namespace Botan {
17

18
Compression_Error::Compression_Error(const char* func_name, ErrorType type, int rc) :
×
19
      Exception(fmt("Compression API {} failed with return code {}", func_name, rc)), m_type(type), m_rc(rc) {}
×
20

21
void* Compression_Alloc_Info::do_malloc(size_t n, size_t size) {
1,263✔
22
   // Precheck for integer overflow in the multiplication
23
   // before passing to calloc, which may or may not check.
24
   if(!checked_mul(n, size)) {
2,526✔
25
      return nullptr;
26
   }
27

28
   void* ptr = std::calloc(n, size);  // NOLINT(*-no-malloc,*-owning-memory,*-const-correctness)
1,263✔
29

30
   /*
31
   * Return null rather than throwing here as we are being called by a
32
   * C library and it may not be possible for an exception to unwind
33
   * the call stack from here. The compression library is expecting a
34
   * function written in C and a null return on error, which it will
35
   * send upwards to the compression wrappers.
36
   */
37

38
   if(ptr != nullptr) {
1,263✔
39
      m_current_allocs[ptr] = n * size;
1,263✔
40
   }
41

42
   return ptr;
1,263✔
43
}
44

45
void Compression_Alloc_Info::do_free(void* ptr) {
1,343✔
46
   if(ptr != nullptr) {
1,343✔
47
      auto i = m_current_allocs.find(ptr);
1,263✔
48

49
      if(i == m_current_allocs.end()) {
1,263✔
50
         throw Internal_Error("Compression_Alloc_Info::free got pointer not allocated by us");
×
51
      }
52

53
      secure_scrub_memory(ptr, i->second);
1,263✔
54
      std::free(ptr);  // NOLINT(*-no-malloc,*-owning-memory)
1,263✔
55
      m_current_allocs.erase(i);
1,263✔
56
   }
57
}
1,343✔
58

59
void Stream_Compression::clear() {
133✔
60
   m_stream.reset();
×
61
}
×
62

63
void Stream_Compression::start(size_t level) {
133✔
64
   m_stream = make_stream(level);
133✔
65
}
133✔
66

67
void Stream_Compression::process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags) {
296✔
68
   BOTAN_ASSERT(m_stream, "Initialized");
296✔
69
   BOTAN_ASSERT(buf.size() >= offset, "Offset is sane");
296✔
70

71
   // bzip doesn't like being called with no input and BZ_RUN
72
   if(buf.size() == offset && flags == m_stream->run_flag()) {
296✔
73
      return;
74
   }
75

76
   if(m_buffer.size() < buf.size() + offset) {
276✔
77
      m_buffer.resize(buf.size() + offset);
113✔
78
   }
79

80
   // If the output buffer has zero length, .data() might return nullptr. This would
81
   // make some compression algorithms (notably those provided by zlib) fail.
82
   // Any small positive value works fine, but we choose 32 as it is the smallest power
83
   // of two that is large enough to hold all the headers and trailers of the common
84
   // formats, preventing further resizings to make room for output data.
85
   if(m_buffer.empty()) {
276✔
86
      m_buffer.resize(32);
70✔
87
   }
88

89
   m_stream->next_in(buf.data() + offset, buf.size() - offset);
276✔
90
   m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset);
276✔
91

92
   while(true) {
381✔
93
      const bool stream_end = m_stream->run(flags);
381✔
94

95
      if(stream_end) {
381✔
96
         BOTAN_ASSERT(m_stream->avail_in() == 0, "After stream is done, no input remains to be processed");
143✔
97
         m_buffer.resize(m_buffer.size() - m_stream->avail_out());
143✔
98
         break;
143✔
99
      } else if(m_stream->avail_out() == 0) {
238✔
100
         const size_t added = 8 + m_buffer.size();
105✔
101
         m_buffer.resize(m_buffer.size() + added);
105✔
102
         m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added);
105✔
103
      } else if(m_stream->avail_in() == 0) {
133✔
104
         m_buffer.resize(m_buffer.size() - m_stream->avail_out());
133✔
105
         break;
133✔
106
      }
107
   }
108

109
   copy_mem(m_buffer.data(), buf.data(), offset);
276✔
110
   buf.swap(m_buffer);
276✔
111
}
112

113
void Stream_Compression::update(secure_vector<uint8_t>& buf, size_t offset, bool flush) {
163✔
114
   BOTAN_ASSERT(m_stream, "Initialized");
163✔
115
   process(buf, offset, flush ? m_stream->flush_flag() : m_stream->run_flag());
163✔
116
}
163✔
117

118
void Stream_Compression::finish(secure_vector<uint8_t>& buf, size_t offset) {
133✔
119
   BOTAN_ASSERT(m_stream, "Initialized");
133✔
120
   process(buf, offset, m_stream->finish_flag());
133✔
121
   clear();
133✔
122
}
133✔
123

124
void Stream_Decompression::clear() {
118✔
125
   m_stream.reset();
×
126
}
×
127

128
void Stream_Decompression::start() {
123✔
129
   m_stream = make_stream();
128✔
130
}
123✔
131

132
void Stream_Decompression::process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags) {
413✔
133
   BOTAN_ASSERT(m_stream, "Initialized");
413✔
134
   BOTAN_ASSERT(buf.size() >= offset, "Offset is sane");
413✔
135

136
   if(m_buffer.size() < buf.size() + offset) {
413✔
137
      m_buffer.resize(buf.size() + offset);
38✔
138
   }
139

140
   m_stream->next_in(buf.data() + offset, buf.size() - offset);
413✔
141
   m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset);
413✔
142

143
   while(true) {
576✔
144
      const bool stream_end = m_stream->run(flags);
576✔
145

146
      if(stream_end) {
576✔
147
         if(m_stream->avail_in() == 0) {
123✔
148
            // all data consumed
149
            m_buffer.resize(m_buffer.size() - m_stream->avail_out());
118✔
150
            clear();
118✔
151
            break;
152
         }
153

154
         // More data follows: try to process as a following stream
155
         // Remove stream1's unused output space so stream2's output
156
         // is placed immediately after stream1's data with no gap.
157
         m_buffer.resize(m_buffer.size() - m_stream->avail_out());
5✔
158
         const size_t read = (buf.size() - offset) - m_stream->avail_in();
5✔
159
         start();
5✔
160
         m_stream->next_in(buf.data() + offset + read, buf.size() - offset - read);
5✔
161
      }
162

163
      if(m_stream->avail_out() == 0) {
458✔
164
         const size_t added = 8 + m_buffer.size();
163✔
165
         m_buffer.resize(m_buffer.size() + added);
163✔
166
         m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added);
163✔
167
      } else if(m_stream->avail_in() == 0) {
295✔
168
         m_buffer.resize(m_buffer.size() - m_stream->avail_out());
295✔
169
         break;
295✔
170
      }
171
   }
172

173
   copy_mem(m_buffer.data(), buf.data(), offset);
413✔
174
   buf.swap(m_buffer);
413✔
175
}
413✔
176

177
void Stream_Decompression::update(secure_vector<uint8_t>& buf, size_t offset) {
413✔
178
   if(!m_stream) {
413✔
179
      if(buf.size() == offset) {
5✔
180
         return;
181
      }
182
      // Previous stream ended cleanly; re-initialize for a concatenated stream
183
      start();
5✔
184
   }
185
   process(buf, offset, m_stream->run_flag());
413✔
186
}
187

188
void Stream_Decompression::finish(secure_vector<uint8_t>& buf, size_t offset) {
113✔
189
   if(!m_stream) {
113✔
190
      if(buf.size() == offset) {
113✔
191
         return;
192
      }
193
      // Previous stream ended cleanly; re-initialize for a concatenated stream
194
      start();
×
195
   }
196

197
   process(buf, offset, m_stream->finish_flag());
×
198

199
   if(m_stream) {
×
200
      throw Invalid_State(fmt("{} finished but not at stream end", name()));
×
201
   }
202
}
203

204
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc