Blender V4.5
filereader_zstd.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cstring>
11#include <zstd.h>
12
13#include "BLI_fileops.hh"
14#include "BLI_filereader.h"
15
16#ifdef __BIG_ENDIAN__
17# include "BLI_endian_switch.h"
18#endif
19
20#include "MEM_guardedalloc.h"
21
22struct ZstdReader {
24
26
27 ZSTD_DCtx *ctx;
28 ZSTD_inBuffer in_buf;
30
31 struct {
35
39};
40
41static bool zstd_read_u32(FileReader *base, uint32_t *val)
42{
43 if (base->read(base, val, sizeof(uint32_t)) != sizeof(uint32_t)) {
44 return false;
45 }
46#ifdef __BIG_ENDIAN__
48#endif
49 return true;
50}
51
53{
54 FileReader *base = zstd->base;
55
56 /* The seek table frame is at the end of the file, so seek there
57 * and verify that there is enough data. */
58 if (base->seek(base, -4, SEEK_END) < 13) {
59 return false;
60 }
61 uint32_t magic;
62 if (!zstd_read_u32(base, &magic) || magic != 0x8F92EAB1) {
63 return false;
64 }
65
66 uint8_t flags;
67 if (base->seek(base, -5, SEEK_END) < 0 || base->read(base, &flags, 1) != 1) {
68 return false;
69 }
70 /* Bit 7 indicates check-sums. Bits 5 and 6 must be zero. */
71 bool has_checksums = (flags & 0x80);
72 if (flags & 0x60) {
73 return false;
74 }
75
76 uint32_t frames_num;
77 if (base->seek(base, -9, SEEK_END) < 0 || !zstd_read_u32(base, &frames_num)) {
78 return false;
79 }
80
81 /* Each frame has either 2 or 3 uint32_t, and after that we have
82 * frames_num, flags and magic for another 9 bytes. */
83 uint32_t expected_frame_length = frames_num * (has_checksums ? 12 : 8) + 9;
84 /* The frame starts with another magic number and its length, but these
85 * two fields are not included when counting length. */
86 off64_t frame_start_ofs = 8 + expected_frame_length;
87 /* Sanity check: Before the start of the seek table frame,
88 * there must be frames_num frames, each of which at least 8 bytes long. */
89 off64_t seek_frame_start = base->seek(base, -frame_start_ofs, SEEK_END);
90 if (seek_frame_start < frames_num * 8) {
91 return false;
92 }
93
94 if (!zstd_read_u32(base, &magic) || magic != 0x184D2A5E) {
95 return false;
96 }
97
98 uint32_t frame_length;
99 if (!zstd_read_u32(base, &frame_length) || frame_length != expected_frame_length) {
100 return false;
101 }
102
103 zstd->seek.frames_num = frames_num;
104 zstd->seek.compressed_ofs = MEM_malloc_arrayN<size_t>(frames_num + 1, __func__);
105 zstd->seek.uncompressed_ofs = MEM_malloc_arrayN<size_t>(frames_num + 1, __func__);
106
107 size_t compressed_ofs = 0;
108 size_t uncompressed_ofs = 0;
109 for (int i = 0; i < frames_num; i++) {
110 uint32_t compressed_size, uncompressed_size;
111 if (!zstd_read_u32(base, &compressed_size) || !zstd_read_u32(base, &uncompressed_size)) {
112 break;
113 }
114 if (has_checksums && base->seek(base, 4, SEEK_CUR) < 0) {
115 break;
116 }
117 zstd->seek.compressed_ofs[i] = compressed_ofs;
118 zstd->seek.uncompressed_ofs[i] = uncompressed_ofs;
119 compressed_ofs += compressed_size;
120 uncompressed_ofs += uncompressed_size;
121 }
122 zstd->seek.compressed_ofs[frames_num] = compressed_ofs;
123 zstd->seek.uncompressed_ofs[frames_num] = uncompressed_ofs;
124
125 /* Seek to the end of the previous frame for the following #BHead frame detection. */
126 if (seek_frame_start != compressed_ofs || base->seek(base, seek_frame_start, SEEK_SET) < 0) {
129 memset(&zstd->seek, 0, sizeof(zstd->seek));
130 return false;
131 }
132
133 zstd->seek.cached_frame = -1;
134
135 return true;
136}
137
138/* Find out which frame contains the given position in the uncompressed stream.
139 * Basically just bisection. */
140static int zstd_frame_from_pos(ZstdReader *zstd, size_t pos)
141{
142 int low = 0, high = zstd->seek.frames_num;
143
144 if (pos >= zstd->seek.uncompressed_ofs[zstd->seek.frames_num]) {
145 return -1;
146 }
147
148 while (low + 1 < high) {
149 int mid = low + ((high - low) >> 1);
150 if (zstd->seek.uncompressed_ofs[mid] <= pos) {
151 low = mid;
152 }
153 else {
154 high = mid;
155 }
156 }
157
158 return low;
159}
160
161/* Ensure that the currently loaded frame is the correct one. */
162static const char *zstd_ensure_cache(ZstdReader *zstd, int frame)
163{
164 if (zstd->seek.cached_frame == frame) {
165 /* Cached frame matches, so just return it. */
166 return zstd->seek.cached_content;
167 }
168
169 /* Cached frame doesn't match, so discard it and cache the wanted one instead. */
171
172 size_t compressed_size = zstd->seek.compressed_ofs[frame + 1] - zstd->seek.compressed_ofs[frame];
173 size_t uncompressed_size = zstd->seek.uncompressed_ofs[frame + 1] -
174 zstd->seek.uncompressed_ofs[frame];
175
176 char *uncompressed_data = MEM_malloc_arrayN<char>(uncompressed_size, __func__);
177 char *compressed_data = MEM_malloc_arrayN<char>(compressed_size, __func__);
178 if (zstd->base->seek(zstd->base, zstd->seek.compressed_ofs[frame], SEEK_SET) < 0 ||
179 zstd->base->read(zstd->base, compressed_data, compressed_size) < compressed_size)
180 {
181 MEM_freeN(compressed_data);
182 MEM_freeN(uncompressed_data);
183 return nullptr;
184 }
185
186 size_t res = ZSTD_decompressDCtx(
187 zstd->ctx, uncompressed_data, uncompressed_size, compressed_data, compressed_size);
188 MEM_freeN(compressed_data);
189 if (ZSTD_isError(res) || res < uncompressed_size) {
190 MEM_freeN(uncompressed_data);
191 return nullptr;
192 }
193
194 zstd->seek.cached_frame = frame;
195 zstd->seek.cached_content = uncompressed_data;
196 return uncompressed_data;
197}
198
199static int64_t zstd_read_seekable(FileReader *reader, void *buffer, size_t size)
200{
201 ZstdReader *zstd = (ZstdReader *)reader;
202
203 size_t end_offset = zstd->reader.offset + size, read_len = 0;
204 while (zstd->reader.offset < end_offset) {
205 int frame = zstd_frame_from_pos(zstd, zstd->reader.offset);
206 if (frame < 0) {
207 /* EOF is reached, so return as much as we can. */
208 break;
209 }
210
211 const char *framedata = zstd_ensure_cache(zstd, frame);
212 if (framedata == nullptr) {
213 /* Error while reading the frame, so return as much as we can. */
214 break;
215 }
216
217 size_t frame_end_offset = std::min(zstd->seek.uncompressed_ofs[frame + 1], end_offset);
218 size_t frame_read_len = frame_end_offset - zstd->reader.offset;
219
220 size_t offset_in_frame = zstd->reader.offset - zstd->seek.uncompressed_ofs[frame];
221 memcpy((char *)buffer + read_len, framedata + offset_in_frame, frame_read_len);
222 read_len += frame_read_len;
223 zstd->reader.offset = frame_end_offset;
224 }
225
226 return read_len;
227}
228
229static off64_t zstd_seek(FileReader *reader, off64_t offset, int whence)
230{
231 ZstdReader *zstd = (ZstdReader *)reader;
232 off64_t new_pos;
233 if (whence == SEEK_SET) {
234 new_pos = offset;
235 }
236 else if (whence == SEEK_END) {
237 new_pos = zstd->seek.uncompressed_ofs[zstd->seek.frames_num] + offset;
238 }
239 else {
240 new_pos = zstd->reader.offset + offset;
241 }
242
243 if (new_pos < 0 || new_pos > zstd->seek.uncompressed_ofs[zstd->seek.frames_num]) {
244 return -1;
245 }
246 zstd->reader.offset = new_pos;
247 return zstd->reader.offset;
248}
249
250static int64_t zstd_read(FileReader *reader, void *buffer, size_t size)
251{
252 ZstdReader *zstd = (ZstdReader *)reader;
253 ZSTD_outBuffer output = {buffer, size, 0};
254
255 while (output.pos < output.size) {
256 if (zstd->in_buf.pos == zstd->in_buf.size) {
257 /* Ran out of buffered input data, read some more. */
258 zstd->in_buf.pos = 0;
259 int64_t readsize = zstd->base->read(
260 zstd->base, (char *)zstd->in_buf.src, zstd->in_buf_max_size);
261
262 if (readsize > 0) {
263 /* We got some data, so mark the buffer as refilled. */
264 zstd->in_buf.size = readsize;
265 }
266 else {
267 /* The underlying file is EOF, so return as much as we can. */
268 break;
269 }
270 }
271
272 if (ZSTD_isError(ZSTD_decompressStream(zstd->ctx, &output, &zstd->in_buf))) {
273 break;
274 }
275 }
276
277 zstd->reader.offset += output.pos;
278 return output.pos;
279}
280
281static void zstd_close(FileReader *reader)
282{
283 ZstdReader *zstd = (ZstdReader *)reader;
284
285 ZSTD_freeDCtx(zstd->ctx);
286 if (zstd->reader.seek) {
289 /* When an error has occurred this may be nullptr, see: #99744. */
290 if (zstd->seek.cached_content) {
292 }
293 }
294 else {
295 MEM_freeN(const_cast<void *>(zstd->in_buf.src));
296 }
297
298 zstd->base->close(zstd->base);
299 MEM_freeN(zstd);
300}
301
303{
304 ZstdReader *zstd = MEM_callocN<ZstdReader>(__func__);
305
306 zstd->ctx = ZSTD_createDCtx();
307 zstd->base = base;
308
309 if (zstd_read_seek_table(zstd)) {
311 zstd->reader.seek = zstd_seek;
312 }
313 else {
314 zstd->reader.read = zstd_read;
315 zstd->reader.seek = nullptr;
316
317 zstd->in_buf_max_size = ZSTD_DStreamInSize();
318 zstd->in_buf.src = MEM_mallocN(zstd->in_buf_max_size, "zstd in buf");
319 zstd->in_buf.size = zstd->in_buf_max_size;
320 /* This signals that the buffer has run out,
321 * which will make the read function refill it on the first call. */
322 zstd->in_buf.pos = zstd->in_buf_max_size;
323 }
324 zstd->reader.close = zstd_close;
325
326 /* Rewind after the seek table check so that zstd_read starts at the file's start. */
327 zstd->base->seek(zstd->base, 0, SEEK_SET);
328
329 return (FileReader *)zstd;
330}
BLI_INLINE void BLI_endian_switch_uint32(unsigned int *val) ATTR_NONNULL(1)
File and directory operations.
Wrapper for reading from various sources (e.g. raw files, compressed files, memory....
Read Guarded memory(de)allocation.
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static void zstd_close(FileReader *reader)
static off64_t zstd_seek(FileReader *reader, off64_t offset, int whence)
static int64_t zstd_read_seekable(FileReader *reader, void *buffer, size_t size)
static int64_t zstd_read(FileReader *reader, void *buffer, size_t size)
static int zstd_frame_from_pos(ZstdReader *zstd, size_t pos)
static bool zstd_read_seek_table(ZstdReader *zstd)
static const char * zstd_ensure_cache(ZstdReader *zstd, int frame)
static bool zstd_read_u32(FileReader *base, uint32_t *val)
FileReader * BLI_filereader_new_zstd(FileReader *base)
uint pos
#define output
#define MEM_SAFE_FREE(v)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
FileReaderSeekFn seek
off64_t offset
FileReaderCloseFn close
FileReaderReadFn read
ZSTD_DCtx * ctx
FileReader * base
size_t in_buf_max_size
size_t * compressed_ofs
ZSTD_inBuffer in_buf
char * cached_content
struct ZstdReader::@372275061133003355155321374141105013145102167013 seek
FileReader reader
size_t * uncompressed_ofs
i
Definition text_draw.cc:230
static int magic(const Tex *tex, const float texvec[3], TexResult *texres)