Coverage Report

Created: 2026-05-23 06:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/Common/Compression.cpp
Line
Count
Source
1
/*
2
Open Asset Import Library (assimp)
3
----------------------------------------------------------------------
4
5
Copyright (c) 2006-2026, assimp team
6
7
All rights reserved.
8
9
Redistribution and use of this software in source and binary forms,
10
with or without modification, are permitted provided that the
11
following conditions are met:
12
13
* Redistributions of source code must retain the above
14
  copyright notice, this list of conditions and the
15
  following disclaimer.
16
17
* Redistributions in binary form must reproduce the above
18
  copyright notice, this list of conditions and the
19
  following disclaimer in the documentation and/or other
20
  materials provided with the distribution.
21
22
* Neither the name of the assimp team, nor the names of its
23
  contributors may be used to endorse or promote products
24
  derived from this software without specific prior
25
  written permission of the assimp team.
26
27
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39
----------------------------------------------------------------------
40
*/
41
42
#include "Compression.h"
43
#include <assimp/ai_assert.h>
44
#include <assimp/Exceptional.h>
45
46
namespace Assimp {
47
48
struct Compression::impl {
49
    bool mOpen;
50
    z_stream mZSstream;
51
    FlushMode mFlushMode;
52
53
    impl() :
54
5.55k
            mOpen(false),
55
5.55k
            mZSstream(),
56
5.55k
            mFlushMode(Compression::FlushMode::NoFlush) {
57
        // empty
58
5.55k
    }
59
};
60
61
Compression::Compression() :
62
5.55k
        mImpl(new impl) {
63
    // empty
64
5.55k
}
65
66
5.55k
Compression::~Compression() {
67
5.55k
    ai_assert(mImpl != nullptr);
68
69
5.55k
    if (mImpl->mOpen) {
70
1.61k
        close();
71
1.61k
    }
72
73
5.55k
    delete mImpl;
74
5.55k
}
75
76
5.55k
bool Compression::open(Format format, FlushMode flush, int windowBits) {
77
5.55k
    ai_assert(mImpl != nullptr);
78
79
5.55k
    if (mImpl->mOpen) {
80
0
        return false;
81
0
    }
82
83
    // build a zlib stream
84
5.55k
    mImpl->mZSstream.opaque = Z_NULL;
85
5.55k
    mImpl->mZSstream.zalloc = Z_NULL;
86
5.55k
    mImpl->mZSstream.zfree = Z_NULL;
87
5.55k
    mImpl->mFlushMode = flush;
88
5.55k
    if (format == Format::Binary) {
89
5.55k
        mImpl->mZSstream.data_type = Z_BINARY;
90
5.55k
    } else {
91
0
        mImpl->mZSstream.data_type = Z_ASCII;
92
0
    }
93
94
    // raw decompression without a zlib or gzip header
95
5.55k
    if (windowBits == 0) {
96
5.55k
        inflateInit(&mImpl->mZSstream);
97
5.55k
    } else {
98
0
        inflateInit2(&mImpl->mZSstream, windowBits);
99
0
    }
100
5.55k
    mImpl->mOpen = true;
101
102
5.55k
    return mImpl->mOpen;
103
5.55k
}
104
105
5.55k
static int getFlushMode(Compression::FlushMode flush) {
106
5.55k
    int z_flush = 0;
107
5.55k
    switch (flush) {
108
0
        case Compression::FlushMode::NoFlush:
109
0
            z_flush = Z_NO_FLUSH;
110
0
            break;
111
0
        case Compression::FlushMode::Block:
112
0
            z_flush = Z_BLOCK;
113
0
            break;
114
0
        case Compression::FlushMode::Tree:
115
0
            z_flush = Z_TREES;
116
0
            break;
117
0
        case Compression::FlushMode::SyncFlush:
118
0
            z_flush = Z_SYNC_FLUSH;
119
0
            break;
120
5.55k
        case Compression::FlushMode::Finish:
121
5.55k
            z_flush = Z_FINISH;
122
5.55k
            break;
123
0
        default:
124
0
            ai_assert(false);
125
0
            break;
126
5.55k
    }
127
128
5.55k
    return z_flush;
129
5.55k
}
130
131
static constexpr size_t MYBLOCK = 32786;
132
133
5.55k
size_t Compression::decompress(const void *data, size_t in, std::vector<char> &uncompressed) {
134
5.55k
    ai_assert(mImpl != nullptr);
135
5.55k
    if (data == nullptr || in == 0) {
136
0
        return 0l;
137
0
    }
138
139
5.55k
    mImpl->mZSstream.next_in = (Bytef*)(data);
140
5.55k
    mImpl->mZSstream.avail_in = (uInt)in;
141
142
5.55k
    int ret = 0;
143
5.55k
    size_t total = 0l;
144
5.55k
    const int flushMode = getFlushMode(mImpl->mFlushMode);
145
5.55k
    if (flushMode == Z_FINISH) {
146
5.55k
        mImpl->mZSstream.avail_out = static_cast<uInt>(uncompressed.size());
147
5.55k
        mImpl->mZSstream.next_out = reinterpret_cast<Bytef *>(&*uncompressed.begin());
148
5.55k
        ret = inflate(&mImpl->mZSstream, Z_FINISH);
149
150
5.55k
        if (ret != Z_STREAM_END && ret != Z_OK) {
151
1.61k
            throw DeadlyImportError("Compression", "Failure decompressing this file using gzip.");
152
1.61k
        }
153
3.93k
        total = mImpl->mZSstream.avail_out;
154
3.93k
    } else {
155
0
        do {
156
0
            Bytef block[MYBLOCK] = {};
157
0
            mImpl->mZSstream.avail_out = MYBLOCK;
158
0
            mImpl->mZSstream.next_out = block;
159
160
0
            ret = inflate(&mImpl->mZSstream, flushMode);
161
162
0
            if (ret != Z_STREAM_END && ret != Z_OK) {
163
0
                throw DeadlyImportError("Compression", "Failure decompressing this file using gzip.");
164
0
            }
165
0
            const size_t have = MYBLOCK - mImpl->mZSstream.avail_out;
166
0
            total += have;
167
0
            uncompressed.resize(total);
168
0
            ::memcpy(uncompressed.data() + total - have, block, have);
169
0
        } while (ret != Z_STREAM_END);
170
0
    }
171
172
3.93k
    return total;
173
5.55k
}
174
175
0
size_t Compression::decompressBlock(const void *data, size_t in, char *out, size_t availableOut) {
176
0
    ai_assert(mImpl != nullptr);
177
0
    if (data == nullptr || in == 0 || out == nullptr || availableOut == 0) {
178
0
        return 0l;
179
0
    }
180
181
    // push data to the stream
182
0
    mImpl->mZSstream.next_in = (Bytef *)data;
183
0
    mImpl->mZSstream.avail_in = (uInt)in;
184
0
    mImpl->mZSstream.next_out = (Bytef *)out;
185
0
    mImpl->mZSstream.avail_out = (uInt)availableOut;
186
187
    // and decompress the data ....
188
0
    int ret = ::inflate(&mImpl->mZSstream, Z_SYNC_FLUSH);
189
0
    if (ret != Z_OK && ret != Z_STREAM_END) {
190
0
        throw DeadlyImportError("X: Failed to decompress MSZIP-compressed data");
191
0
    }
192
193
0
    ::inflateReset(&mImpl->mZSstream);
194
0
    ::inflateSetDictionary(&mImpl->mZSstream, (const Bytef *)out, (uInt)availableOut - mImpl->mZSstream.avail_out);
195
196
0
    return availableOut - (size_t)mImpl->mZSstream.avail_out;
197
0
}
198
199
0
bool Compression::isOpen() const {
200
0
    ai_assert(mImpl != nullptr);
201
202
0
    return mImpl->mOpen;
203
0
}
204
205
5.55k
bool Compression::close() {
206
5.55k
    ai_assert(mImpl != nullptr);
207
208
5.55k
    if (!mImpl->mOpen) {
209
0
        return false;
210
0
    }
211
212
5.55k
    inflateEnd(&mImpl->mZSstream);
213
5.55k
    mImpl->mOpen = false;
214
215
5.55k
    return true;
216
5.55k
}
217
218
} // namespace Assimp