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