Coverage Report

Created: 2026-05-16 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/dcmdata/libsrc/dcostrmz.cc
Line
Count
Source
1
/*
2
 *
3
 *  Copyright (C) 2002-2010, OFFIS e.V.
4
 *  All rights reserved.  See COPYRIGHT file for details.
5
 *
6
 *  This software and supporting documentation were developed by
7
 *
8
 *    OFFIS e.V.
9
 *    R&D Division Health
10
 *    Escherweg 2
11
 *    D-26121 Oldenburg, Germany
12
 *
13
 *
14
 *  Module:  dcmdata
15
 *
16
 *  Author:  Marco Eichelberg
17
 *
18
 *  Purpose: zlib compression filter for output streams
19
 *
20
 */
21
22
#include "dcmtk/config/osconfig.h"
23
24
#ifdef WITH_ZLIB
25
26
#include "dcmtk/dcmdata/dcostrmz.h"
27
#include "dcmtk/dcmdata/dcerror.h"
28
29
0
#define DCMZLIBOUTPUTFILTER_BUFSIZE 4096
30
31
/* taken from zutil.h */
32
#if MAX_MEM_LEVEL >= 8
33
#define DEF_MEM_LEVEL 8
34
#else
35
#define DEF_MEM_LEVEL  MAX_MEM_LEVEL
36
#endif
37
38
OFGlobal<int> dcmZlibCompressionLevel(Z_DEFAULT_COMPRESSION);
39
40
// helper method to fix old-style casts warnings
41
BEGIN_EXTERN_C
42
static int OFdeflateInit(z_stream* const stream, int level)
43
0
{
44
#ifdef ZLIB_ENCODE_RFC1950_HEADER
45
  /* create deflated ZLIB format instead of deflated bitstream format
46
   * (i.e. RFC 1950 instead of RFC 1951).
47
   * THE RESULTING BITSTREAM IS NOT DICOM COMPLIANT!
48
   * Use only for testing, and use with care.
49
   */
50
  return deflateInit(stream, level);
51
#else
52
  /* windowBits is passed < 0 to suppress zlib header */
53
0
  return deflateInit2(stream, level, Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
54
0
#endif
55
0
}
56
END_EXTERN_C
57
58
DcmZLibOutputFilter::DcmZLibOutputFilter()
59
0
: DcmOutputFilter()
60
0
, current_(NULL)
61
0
, zstream_(new z_stream)
62
0
, status_(EC_MemoryExhausted)
63
0
, flushed_(OFFalse)
64
0
, inputBuf_(new unsigned char[DCMZLIBOUTPUTFILTER_BUFSIZE])
65
0
, inputBufStart_(0)
66
0
, inputBufCount_(0)
67
0
, outputBuf_(new unsigned char[DCMZLIBOUTPUTFILTER_BUFSIZE])
68
0
, outputBufStart_(0)
69
0
, outputBufCount_(0)
70
0
{
71
0
  if (zstream_ && inputBuf_ && outputBuf_)
72
0
  {
73
0
    zstream_->zalloc = Z_NULL;
74
0
    zstream_->zfree = Z_NULL;
75
0
    zstream_->opaque = Z_NULL;
76
0
    if (Z_OK == OFdeflateInit(zstream_, dcmZlibCompressionLevel.get()))
77
0
      status_ = EC_Normal;
78
0
    else
79
0
    {
80
0
      OFString etext = "ZLib Error: ";
81
0
      if (zstream_->msg) etext += zstream_->msg;
82
0
      status_ = makeOFCondition(OFM_dcmdata, 16, OF_error, etext.c_str());
83
0
    }
84
0
  }
85
0
}
86
87
DcmZLibOutputFilter::~DcmZLibOutputFilter()
88
0
{
89
0
  if (zstream_)
90
0
  {
91
0
    deflateEnd(zstream_); // discards any unprocessed input and does not flush any pending output
92
0
    delete zstream_;
93
0
  }
94
0
  delete[] inputBuf_;
95
0
  delete[] outputBuf_;
96
0
}
97
98
99
OFBool DcmZLibOutputFilter::good() const
100
0
{
101
0
  return status_.good();
102
0
}
103
104
OFCondition DcmZLibOutputFilter::status() const
105
0
{
106
0
  return status_;
107
0
}
108
109
OFBool DcmZLibOutputFilter::isFlushed() const
110
0
{
111
0
  if (status_.bad() || (current_ == NULL)) return OFTrue;
112
0
  return (inputBufCount_ == 0) && (outputBufCount_ == 0) && flushed_ && current_->isFlushed();
113
0
}
114
115
116
offile_off_t DcmZLibOutputFilter::avail() const
117
0
{
118
  // compute number of bytes available in input buffer
119
0
  if (status_.good() ) return DCMZLIBOUTPUTFILTER_BUFSIZE - inputBufCount_;
120
0
    else return 0;
121
0
}
122
123
void DcmZLibOutputFilter::flushOutputBuffer()
124
0
{
125
0
  if (outputBufCount_)
126
0
  {
127
    // flush from outputBufStart_ to end of data or end of buffer, whatever comes first
128
0
    offile_off_t numBytes = (outputBufStart_ + outputBufCount_ > DCMZLIBOUTPUTFILTER_BUFSIZE) ?
129
0
      (DCMZLIBOUTPUTFILTER_BUFSIZE - outputBufStart_) : outputBufCount_ ;
130
131
0
    offile_off_t written = current_->write(outputBuf_ + outputBufStart_, numBytes);
132
133
    // adjust counters
134
0
    outputBufCount_ -= written;
135
0
    outputBufStart_ += written;
136
137
0
    if (outputBufStart_ == DCMZLIBOUTPUTFILTER_BUFSIZE)
138
0
    {
139
      // wrapped around
140
0
      outputBufStart_ = 0;
141
142
      // now flush to end of data
143
0
      if (outputBufCount_ && written)
144
0
      {
145
0
        written = current_->write(outputBuf_, outputBufCount_);
146
147
        // adjust counters
148
0
        outputBufCount_ -= written;
149
0
        outputBufStart_ += written;
150
0
      }
151
0
    }
152
153
    // reset buffer start to make things faster
154
0
    if (outputBufCount_ == 0) outputBufStart_ = 0;
155
0
  }
156
0
}
157
158
offile_off_t DcmZLibOutputFilter::fillInputBuffer(const void *buf, offile_off_t buflen)
159
0
{
160
0
  offile_off_t result = 0;
161
0
  if (buf && buflen && inputBufCount_ < DCMZLIBOUTPUTFILTER_BUFSIZE)
162
0
  {
163
164
0
    const unsigned char *data = OFstatic_cast(const unsigned char *, buf);
165
166
    // use first part of input buffer
167
0
    if (inputBufStart_ + inputBufCount_ < DCMZLIBOUTPUTFILTER_BUFSIZE)
168
0
    {
169
0
      result = DCMZLIBOUTPUTFILTER_BUFSIZE - (inputBufStart_ + inputBufCount_);
170
0
      if (result > buflen) result = buflen;
171
172
0
      memcpy(inputBuf_ + inputBufStart_ + inputBufCount_, data, OFstatic_cast(size_t, result));
173
0
      inputBufCount_ += result;
174
0
      data += result;
175
0
      buflen -= result;
176
0
    }
177
178
    // use second part of input buffer
179
0
    if (buflen && (inputBufCount_ < DCMZLIBOUTPUTFILTER_BUFSIZE) &&
180
0
        inputBufStart_ + inputBufCount_ >= DCMZLIBOUTPUTFILTER_BUFSIZE)
181
0
    {
182
0
      offile_off_t len = DCMZLIBOUTPUTFILTER_BUFSIZE - inputBufCount_;
183
0
      if (len > buflen) len = buflen;
184
185
0
      memcpy(inputBuf_ + (inputBufStart_ + inputBufCount_ - DCMZLIBOUTPUTFILTER_BUFSIZE), data, OFstatic_cast(size_t, len));
186
187
0
      inputBufCount_ += len;
188
0
      result += len;
189
0
    }
190
0
  }
191
0
  return result;
192
0
}
193
194
void DcmZLibOutputFilter::compressInputBuffer(OFBool finalize)
195
0
{
196
0
  if (inputBufCount_ || finalize)
197
0
  {
198
    // flush from inputBufStart_ to end of data or end of buffer, whatever comes first
199
0
    offile_off_t numBytes = (inputBufStart_ + inputBufCount_ > DCMZLIBOUTPUTFILTER_BUFSIZE) ?
200
0
      (DCMZLIBOUTPUTFILTER_BUFSIZE - inputBufStart_) : inputBufCount_ ;
201
202
0
    offile_off_t written = compress(inputBuf_ + inputBufStart_, numBytes, finalize);
203
204
    // adjust counters
205
0
    inputBufCount_ -= written;
206
0
    inputBufStart_ += written;
207
208
0
    if (inputBufStart_ == DCMZLIBOUTPUTFILTER_BUFSIZE)
209
0
    {
210
      // wrapped around
211
0
      inputBufStart_ = 0;
212
213
      // now flush to end of data
214
0
      if (inputBufCount_ && written)
215
0
      {
216
0
        written = compress(inputBuf_, inputBufCount_, finalize);
217
218
        // adjust counters
219
0
        inputBufCount_ -= written;
220
0
        inputBufStart_ += written;
221
0
      }
222
0
    }
223
224
    // reset buffer start to make things faster
225
0
    if (inputBufCount_ == 0) inputBufStart_ = 0;
226
0
  }
227
0
}
228
229
offile_off_t DcmZLibOutputFilter::compress(const void *buf, offile_off_t buflen, OFBool finalize)
230
0
{
231
0
  offile_off_t result = 0;
232
0
  if (outputBufCount_ < DCMZLIBOUTPUTFILTER_BUFSIZE)
233
0
  {
234
0
    zstream_->next_in = OFstatic_cast(Bytef *, OFconst_cast(void *, buf));
235
0
    zstream_->avail_in = OFstatic_cast(uInt, buflen);
236
0
    int zstatus;
237
238
    // use first part of output buffer
239
0
    if (outputBufStart_ + outputBufCount_ < DCMZLIBOUTPUTFILTER_BUFSIZE)
240
0
    {
241
0
      zstream_->next_out = OFstatic_cast(Bytef *, outputBuf_ + outputBufStart_ + outputBufCount_);
242
0
      zstream_->avail_out = OFstatic_cast(uInt, DCMZLIBOUTPUTFILTER_BUFSIZE - (outputBufStart_ + outputBufCount_));
243
0
      zstatus = deflate(zstream_, (finalize ? Z_FINISH : 0));
244
245
0
      if (zstatus == Z_OK || zstatus == Z_BUF_ERROR) { /* everything OK */ }
246
0
      else if (zstatus == Z_STREAM_END) flushed_ = OFTrue;
247
0
      else
248
0
      {
249
0
        OFString etext = "ZLib Error: ";
250
0
        if (zstream_->msg) etext += zstream_->msg;
251
0
        status_ = makeOFCondition(OFM_dcmdata, 16, OF_error, etext.c_str());
252
0
      }
253
254
0
      outputBufCount_ = DCMZLIBOUTPUTFILTER_BUFSIZE - outputBufStart_ - OFstatic_cast(offile_off_t, zstream_->avail_out);
255
0
    }
256
257
    // use second part of output buffer
258
0
    if ((outputBufCount_ < DCMZLIBOUTPUTFILTER_BUFSIZE) &&
259
0
        outputBufStart_ + outputBufCount_ >= DCMZLIBOUTPUTFILTER_BUFSIZE)
260
0
    {
261
0
      zstream_->next_out = OFstatic_cast(Bytef *, outputBuf_ + (outputBufStart_ + outputBufCount_ - DCMZLIBOUTPUTFILTER_BUFSIZE));
262
0
      zstream_->avail_out = OFstatic_cast(uInt, DCMZLIBOUTPUTFILTER_BUFSIZE - outputBufCount_);
263
0
      zstatus = deflate(zstream_, (finalize ? Z_FINISH : 0));
264
265
0
      if (zstatus == Z_OK || zstatus == Z_BUF_ERROR) { /* everything OK */ }
266
0
      else if (zstatus == Z_STREAM_END) flushed_ = OFTrue;
267
0
      else
268
0
      {
269
0
        OFString etext = "ZLib Error: ";
270
0
        if (zstream_->msg) etext += zstream_->msg;
271
0
        status_ = makeOFCondition(OFM_dcmdata, 16, OF_error, etext.c_str());
272
0
      }
273
274
0
      outputBufCount_ =  DCMZLIBOUTPUTFILTER_BUFSIZE - OFstatic_cast(offile_off_t, zstream_->avail_out);
275
0
    }
276
277
0
    result = (buflen - OFstatic_cast(offile_off_t, zstream_->avail_in));
278
0
  }
279
0
  return result;
280
0
}
281
282
offile_off_t DcmZLibOutputFilter::write(const void *buf, offile_off_t buflen)
283
0
{
284
0
  if (status_.bad() || (current_ == NULL)) return 0;
285
286
  // flush output buffer if necessary
287
0
  if (outputBufCount_ == DCMZLIBOUTPUTFILTER_BUFSIZE) flushOutputBuffer();
288
289
  // compress pending input from input buffer
290
0
  while (status_.good() && inputBufCount_ > 0 && outputBufCount_ < DCMZLIBOUTPUTFILTER_BUFSIZE)
291
0
  {
292
0
    compressInputBuffer(OFFalse);
293
0
    if (outputBufCount_ == DCMZLIBOUTPUTFILTER_BUFSIZE) flushOutputBuffer();
294
0
  }
295
296
0
  const unsigned char *data = OFstatic_cast(const unsigned char *, buf);
297
0
  offile_off_t result = 0;
298
299
  // compress user data only if input buffer is empty
300
0
  if (inputBufCount_ == 0)
301
0
  {
302
0
    while (status_.good() && (buflen > result) && outputBufCount_ < DCMZLIBOUTPUTFILTER_BUFSIZE)
303
0
    {
304
0
      result += compress(data+result, buflen-result, OFFalse);
305
0
      if (outputBufCount_ == DCMZLIBOUTPUTFILTER_BUFSIZE) flushOutputBuffer();
306
0
    }
307
0
  }
308
309
  // finally stuff as much into the input buffer as possible
310
0
  result += fillInputBuffer(data+result, buflen-result);
311
312
  // total number of bytes consumed from input
313
0
  return result;
314
0
}
315
316
317
void DcmZLibOutputFilter::flush()
318
0
{
319
0
  if (status_.good() && current_)
320
0
  {
321
    // flush output buffer first
322
0
    if (outputBufCount_ == DCMZLIBOUTPUTFILTER_BUFSIZE) flushOutputBuffer();
323
324
    // compress pending input from input buffer
325
0
    while (status_.good() && inputBufCount_ > 0 && outputBufCount_ < DCMZLIBOUTPUTFILTER_BUFSIZE)
326
0
    {
327
0
      compressInputBuffer(OFTrue);
328
0
      if (outputBufCount_ == DCMZLIBOUTPUTFILTER_BUFSIZE) flushOutputBuffer();
329
0
    }
330
331
0
    while (status_.good() && (! flushed_) && outputBufCount_ < DCMZLIBOUTPUTFILTER_BUFSIZE)
332
0
    {
333
      // create output from compression engine until end of compressed stream
334
0
      compress(NULL, 0, OFTrue);
335
0
      if (outputBufCount_ == DCMZLIBOUTPUTFILTER_BUFSIZE) flushOutputBuffer();
336
0
    }
337
338
    // final attempt to flush output buffer
339
0
    if (outputBufCount_ > 0) flushOutputBuffer();
340
0
  }
341
0
}
342
343
344
void DcmZLibOutputFilter::append(DcmConsumer& consumer)
345
0
{
346
0
  current_ = &consumer;
347
0
}
348
349
#else  /* WITH_ZLIB */
350
351
/* make sure that the object file is not completely empty if compiled
352
 * without zlib because some linkers might fail otherwise.
353
 */
354
void dcostrmz_dummy_function()
355
{
356
  return;
357
}
358
359
#endif /* WITH_ZLIB */