Coverage Report

Created: 2025-10-10 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zstd/contrib/seekable_format/zstdseek_decompress.c
Line
Count
Source
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 * All rights reserved.
4
 *
5
 * This source code is licensed under both the BSD-style license (found in the
6
 * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7
 * in the COPYING file in the root directory of this source tree).
8
 * You may select, at your option, one of the above-listed licenses.
9
 */
10
11
/* *********************************************************
12
*  Turn on Large Files support (>4GB) for 32-bit Linux/Unix
13
***********************************************************/
14
#if !defined(__64BIT__) || defined(__MINGW32__)       /* No point defining Large file for 64 bit but MinGW-w64 requires it */
15
#  if !defined(_FILE_OFFSET_BITS)
16
#    define _FILE_OFFSET_BITS 64                      /* turn off_t into a 64-bit type for ftello, fseeko */
17
#  endif
18
#  if !defined(_LARGEFILE_SOURCE)                     /* obsolete macro, replaced with _FILE_OFFSET_BITS */
19
#    define _LARGEFILE_SOURCE 1                       /* Large File Support extension (LFS) - fseeko, ftello */
20
#  endif
21
#  if defined(_AIX) || defined(__hpux)
22
#    define _LARGE_FILES                              /* Large file support on 32-bits AIX and HP-UX */
23
#  endif
24
#endif
25
26
/* ************************************************************
27
*  Detect POSIX version
28
*  PLATFORM_POSIX_VERSION = 0 for non-Unix e.g. Windows
29
*  PLATFORM_POSIX_VERSION = 1 for Unix-like but non-POSIX
30
*  PLATFORM_POSIX_VERSION > 1 is equal to found _POSIX_VERSION
31
*  Value of PLATFORM_POSIX_VERSION can be forced on command line
32
***************************************************************/
33
#ifndef PLATFORM_POSIX_VERSION
34
35
#  if (defined(__APPLE__) && defined(__MACH__)) || defined(__SVR4) || defined(_AIX) || defined(__hpux) /* POSIX.1-2001 (SUSv3) conformant */ \
36
     || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)  /* BSD distros */
37
     /* exception rule : force posix version to 200112L,
38
      * note: it's better to use unistd.h's _POSIX_VERSION whenever possible */
39
#    define PLATFORM_POSIX_VERSION 200112L
40
41
/* try to determine posix version through official unistd.h's _POSIX_VERSION (https://pubs.opengroup.org/onlinepubs/7908799/xsh/unistd.h.html).
42
 * note : there is no simple way to know in advance if <unistd.h> is present or not on target system,
43
 * Posix specification mandates its presence and its content, but target system must respect this spec.
44
 * It's necessary to _not_ #include <unistd.h> whenever target OS is not unix-like
45
 * otherwise it will block preprocessing stage.
46
 * The following list of build macros tries to "guess" if target OS is likely unix-like, and therefore can #include <unistd.h>
47
 */
48
#  elif !defined(_WIN32) \
49
     && ( defined(__unix__) || defined(__unix) \
50
       || defined(__midipix__) || defined(__VMS) || defined(__HAIKU__) )
51
52
#    if defined(__linux__) || defined(__linux) || defined(__CYGWIN__)
53
#      ifndef _POSIX_C_SOURCE
54
#        define _POSIX_C_SOURCE 200809L  /* feature test macro : https://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html */
55
#      endif
56
#    endif
57
#    include <unistd.h>  /* declares _POSIX_VERSION */
58
#    if defined(_POSIX_VERSION)  /* POSIX compliant */
59
#      define PLATFORM_POSIX_VERSION _POSIX_VERSION
60
#    else
61
#      define PLATFORM_POSIX_VERSION 1
62
#    endif
63
64
#    ifdef __UCLIBC__
65
#     ifndef __USE_MISC
66
#      define __USE_MISC /* enable st_mtim on uclibc */
67
#     endif
68
#    endif
69
70
#  else  /* non-unix target platform (like Windows) */
71
#    define PLATFORM_POSIX_VERSION 0
72
#  endif
73
74
#endif   /* PLATFORM_POSIX_VERSION */
75
76
77
/* ************************************************************
78
* Avoid fseek()'s 2GiB barrier with MSVC, macOS, *BSD, MinGW
79
***************************************************************/
80
#if defined(LIBC_NO_FSEEKO)
81
/* Some older libc implementations don't include these functions (e.g. Bionic < 24) */
82
#   define LONG_SEEK fseek
83
#elif defined(_MSC_VER) && _MSC_VER >= 1400
84
#   define LONG_SEEK _fseeki64
85
#elif !defined(__64BIT__) && (PLATFORM_POSIX_VERSION >= 200112L) /* No point defining Large file for 64 bit */
86
0
#   define LONG_SEEK fseeko
87
#elif defined(__MINGW32__) && !defined(__STRICT_ANSI__) && !defined(__NO_MINGW_LFS) && defined(__MSVCRT__)
88
#   define LONG_SEEK fseeko64
89
#elif defined(_WIN32) && !defined(__DJGPP__)
90
#   include <windows.h>
91
    static int LONG_SEEK(FILE* file, __int64 offset, int origin) {
92
        LARGE_INTEGER off;
93
        DWORD method;
94
        off.QuadPart = offset;
95
        if (origin == SEEK_END)
96
            method = FILE_END;
97
        else if (origin == SEEK_CUR)
98
            method = FILE_CURRENT;
99
        else
100
            method = FILE_BEGIN;
101
102
        if (SetFilePointerEx((HANDLE) _get_osfhandle(_fileno(file)), off, NULL, method))
103
            return 0;
104
        else
105
            return -1;
106
    }
107
#else
108
#   define LONG_SEEK fseek
109
#endif
110
111
#include <stdlib.h>  /* malloc, free */
112
#include <stdio.h>   /* FILE* */
113
#include <limits.h>  /* UNIT_MAX */
114
#include <assert.h>
115
116
#define XXH_STATIC_LINKING_ONLY
117
#include "xxhash.h"
118
119
#define ZSTD_STATIC_LINKING_ONLY
120
#include "zstd.h"
121
#include "zstd_errors.h"
122
#include "mem.h"
123
#include "zstd_seekable.h"
124
125
#undef ERROR
126
0
#define ERROR(name) ((size_t)-ZSTD_error_##name)
127
128
86.4k
#define CHECK_IO(f) { int const errcod = (f); if (errcod < 0) return ERROR(seekableIO); }
129
130
#undef MIN
131
#undef MAX
132
59.2k
#define MIN(a, b) ((a) < (b) ? (a) : (b))
133
#define MAX(a, b) ((a) > (b) ? (a) : (b))
134
135
15.1k
#define ZSTD_SEEKABLE_NO_OUTPUT_PROGRESS_MAX 16
136
137
/* Special-case callbacks for FILE* and in-memory modes, so that we can treat
138
 * them the same way as the advanced API */
139
static int ZSTD_seekable_read_FILE(void* opaque, void* buffer, size_t n)
140
0
{
141
0
    size_t const result = fread(buffer, 1, n, (FILE*)opaque);
142
0
    if (result != n) {
143
0
        return -1;
144
0
    }
145
0
    return 0;
146
0
}
147
148
static int ZSTD_seekable_seek_FILE(void* opaque, long long offset, int origin)
149
0
{
150
0
    int const ret = LONG_SEEK((FILE*)opaque, offset, origin);
151
0
    if (ret) return ret;
152
0
    return fflush((FILE*)opaque);
153
0
}
154
155
typedef struct {
156
    const void *ptr;
157
    size_t size;
158
    size_t pos;
159
} buffWrapper_t;
160
161
static int ZSTD_seekable_read_buff(void* opaque, void* buffer, size_t n)
162
51.3k
{
163
51.3k
    buffWrapper_t* const buff = (buffWrapper_t*)opaque;
164
51.3k
    assert(buff != NULL);
165
51.3k
    if (buff->pos + n > buff->size) return -1;
166
51.3k
    memcpy(buffer, (const BYTE*)buff->ptr + buff->pos, n);
167
51.3k
    buff->pos += n;
168
51.3k
    return 0;
169
51.3k
}
170
171
static int ZSTD_seekable_seek_buff(void* opaque, long long offset, int origin)
172
35.1k
{
173
35.1k
    buffWrapper_t* const buff = (buffWrapper_t*) opaque;
174
35.1k
    unsigned long long newOffset;
175
35.1k
    assert(buff != NULL);
176
35.1k
    switch (origin) {
177
11.7k
    case SEEK_SET:
178
11.7k
        assert(offset >= 0);
179
11.7k
        newOffset = (unsigned long long)offset;
180
11.7k
        break;
181
0
    case SEEK_CUR:
182
0
        newOffset = (unsigned long long)((long long)buff->pos + offset);
183
0
        break;
184
23.4k
    case SEEK_END:
185
23.4k
        newOffset = (unsigned long long)((long long)buff->size + offset);
186
23.4k
        break;
187
0
    default:
188
0
        assert(0);  /* not possible */
189
35.1k
    }
190
35.1k
    if (newOffset > buff->size) {
191
0
        return -1;
192
0
    }
193
35.1k
    buff->pos = newOffset;
194
35.1k
    return 0;
195
35.1k
}
196
197
typedef struct {
198
    U64 cOffset;
199
    U64 dOffset;
200
    U32 checksum;
201
} seekEntry_t;
202
203
struct ZSTD_seekTable_s {
204
    seekEntry_t* entries;
205
    size_t tableLen;
206
207
    int checksumFlag;
208
};
209
210
23.4k
#define SEEKABLE_BUFF_SIZE ZSTD_BLOCKSIZE_MAX
211
212
struct ZSTD_seekable_s {
213
    ZSTD_DStream* dstream;
214
    ZSTD_seekTable seekTable;
215
    ZSTD_seekable_customFile src;
216
217
    U64 decompressedOffset;
218
    U32 curFrame;
219
220
    BYTE inBuff[SEEKABLE_BUFF_SIZE]; /* need to do our own input buffering */
221
    BYTE outBuff[SEEKABLE_BUFF_SIZE]; /* so we can efficiently decompress the
222
                                         starts of chunks before we get to the
223
                                         desired section */
224
    ZSTD_inBuffer in; /* maintain continuity across ZSTD_seekable_decompress operations */
225
    buffWrapper_t buffWrapper; /* for `src.opaque` in in-memory mode */
226
227
    XXH64_state_t xxhState;
228
};
229
230
ZSTD_seekable* ZSTD_seekable_create(void)
231
11.7k
{
232
11.7k
    ZSTD_seekable* const zs = (ZSTD_seekable*)malloc(sizeof(ZSTD_seekable));
233
11.7k
    if (zs == NULL) return NULL;
234
235
    /* also initializes stage to zsds_init */
236
11.7k
    memset(zs, 0, sizeof(*zs));
237
238
11.7k
    zs->dstream = ZSTD_createDStream();
239
11.7k
    if (zs->dstream == NULL) {
240
0
        free(zs);
241
0
        return NULL;
242
0
    }
243
244
11.7k
    return zs;
245
11.7k
}
246
247
size_t ZSTD_seekable_free(ZSTD_seekable* zs)
248
11.7k
{
249
11.7k
    if (zs == NULL) return 0; /* support free on null */
250
11.7k
    ZSTD_freeDStream(zs->dstream);
251
11.7k
    free(zs->seekTable.entries);
252
11.7k
    free(zs);
253
11.7k
    return 0;
254
11.7k
}
255
256
ZSTD_seekTable* ZSTD_seekTable_create_fromSeekable(const ZSTD_seekable* zs)
257
0
{
258
0
    assert(zs != NULL);
259
0
    if (zs->seekTable.entries == NULL) return NULL;
260
0
    ZSTD_seekTable* const st = (ZSTD_seekTable*)malloc(sizeof(ZSTD_seekTable));
261
0
    if (st==NULL) return NULL;
262
263
0
    st->checksumFlag = zs->seekTable.checksumFlag;
264
0
    st->tableLen = zs->seekTable.tableLen;
265
266
    /* Allocate an extra entry at the end to match logic of initial allocation */
267
0
    size_t const entriesSize = sizeof(seekEntry_t) * (zs->seekTable.tableLen + 1);
268
0
    seekEntry_t* const entries = (seekEntry_t*)malloc(entriesSize);
269
0
    if (entries==NULL) {
270
0
        free(st);
271
0
        return NULL;
272
0
    }
273
274
0
    memcpy(entries, zs->seekTable.entries, entriesSize);
275
0
    st->entries = entries;
276
0
    return st;
277
0
}
278
279
size_t ZSTD_seekTable_free(ZSTD_seekTable* st)
280
0
{
281
0
    if (st == NULL) return 0; /* support free on null */
282
0
    free(st->entries);
283
0
    free(st);
284
0
    return 0;
285
0
}
286
287
/** ZSTD_seekable_offsetToFrameIndex() :
288
 *  Performs a binary search to find the last frame with a decompressed offset
289
 *  <= pos
290
 *  @return : the frame's index */
291
unsigned ZSTD_seekable_offsetToFrameIndex(const ZSTD_seekable* zs, unsigned long long pos)
292
11.7k
{
293
11.7k
    return ZSTD_seekTable_offsetToFrameIndex(&zs->seekTable, pos);
294
11.7k
}
295
296
unsigned ZSTD_seekTable_offsetToFrameIndex(const ZSTD_seekTable* st, unsigned long long pos)
297
11.7k
{
298
11.7k
    U32 lo = 0;
299
11.7k
    U32 hi = (U32)st->tableLen;
300
11.7k
    assert(st->tableLen <= UINT_MAX);
301
302
11.7k
    if (pos >= st->entries[st->tableLen].dOffset) {
303
64
        return (unsigned)st->tableLen;
304
64
    }
305
306
23.3k
    while (lo + 1 < hi) {
307
11.6k
        U32 const mid = lo + ((hi - lo) >> 1);
308
11.6k
        if (st->entries[mid].dOffset <= pos) {
309
0
            lo = mid;
310
11.6k
        } else {
311
11.6k
            hi = mid;
312
11.6k
        }
313
11.6k
    }
314
11.6k
    return lo;
315
11.7k
}
316
317
unsigned ZSTD_seekable_getNumFrames(const ZSTD_seekable* zs)
318
0
{
319
0
    return ZSTD_seekTable_getNumFrames(&zs->seekTable);
320
0
}
321
322
unsigned ZSTD_seekTable_getNumFrames(const ZSTD_seekTable* st)
323
0
{
324
0
    assert(st->tableLen <= UINT_MAX);
325
0
    return (unsigned)st->tableLen;
326
0
}
327
328
unsigned long long ZSTD_seekable_getFrameCompressedOffset(const ZSTD_seekable* zs, unsigned frameIndex)
329
0
{
330
0
    return ZSTD_seekTable_getFrameCompressedOffset(&zs->seekTable, frameIndex);
331
0
}
332
333
unsigned long long ZSTD_seekTable_getFrameCompressedOffset(const ZSTD_seekTable* st, unsigned frameIndex)
334
0
{
335
0
    if (frameIndex >= st->tableLen) return ZSTD_SEEKABLE_FRAMEINDEX_TOOLARGE;
336
0
    return st->entries[frameIndex].cOffset;
337
0
}
338
339
unsigned long long ZSTD_seekable_getFrameDecompressedOffset(const ZSTD_seekable* zs, unsigned frameIndex)
340
0
{
341
0
    return ZSTD_seekTable_getFrameDecompressedOffset(&zs->seekTable, frameIndex);
342
0
}
343
344
unsigned long long ZSTD_seekTable_getFrameDecompressedOffset(const ZSTD_seekTable* st, unsigned frameIndex)
345
0
{
346
0
    if (frameIndex >= st->tableLen) return ZSTD_SEEKABLE_FRAMEINDEX_TOOLARGE;
347
0
    return st->entries[frameIndex].dOffset;
348
0
}
349
350
size_t ZSTD_seekable_getFrameCompressedSize(const ZSTD_seekable* zs, unsigned frameIndex)
351
0
{
352
0
    return ZSTD_seekTable_getFrameCompressedSize(&zs->seekTable, frameIndex);
353
0
}
354
355
size_t ZSTD_seekTable_getFrameCompressedSize(const ZSTD_seekTable* st, unsigned frameIndex)
356
0
{
357
0
    if (frameIndex >= st->tableLen) return ERROR(frameIndex_tooLarge);
358
0
    return st->entries[frameIndex + 1].cOffset -
359
0
           st->entries[frameIndex].cOffset;
360
0
}
361
362
size_t ZSTD_seekable_getFrameDecompressedSize(const ZSTD_seekable* zs, unsigned frameIndex)
363
0
{
364
0
    return ZSTD_seekTable_getFrameDecompressedSize(&zs->seekTable, frameIndex);
365
0
}
366
367
size_t ZSTD_seekTable_getFrameDecompressedSize(const ZSTD_seekTable* st, unsigned frameIndex)
368
0
{
369
0
    if (frameIndex > st->tableLen) return ERROR(frameIndex_tooLarge);
370
0
    return st->entries[frameIndex + 1].dOffset -
371
0
           st->entries[frameIndex].dOffset;
372
0
}
373
374
static size_t ZSTD_seekable_loadSeekTable(ZSTD_seekable* zs)
375
11.7k
{
376
11.7k
    int checksumFlag;
377
11.7k
    ZSTD_seekable_customFile src = zs->src;
378
    /* read the footer, fixed size */
379
11.7k
    CHECK_IO(src.seek(src.opaque, -(int)ZSTD_seekTableFooterSize, SEEK_END));
380
11.7k
    CHECK_IO(src.read(src.opaque, zs->inBuff, ZSTD_seekTableFooterSize));
381
382
11.7k
    if (MEM_readLE32(zs->inBuff + 5) != ZSTD_SEEKABLE_MAGICNUMBER) {
383
0
        return ERROR(prefix_unknown);
384
0
    }
385
386
11.7k
    {   BYTE const sfd = zs->inBuff[4];
387
11.7k
        checksumFlag = sfd >> 7;
388
389
        /* check reserved bits */
390
11.7k
        if ((sfd >> 2) & 0x1f) {
391
0
            return ERROR(corruption_detected);
392
0
    }   }
393
394
11.7k
    {   U32 const numFrames = MEM_readLE32(zs->inBuff);
395
11.7k
        U32 const sizePerEntry = 8 + (checksumFlag?4:0);
396
11.7k
        U32 const tableSize = sizePerEntry * numFrames;
397
11.7k
        U32 const frameSize = tableSize + ZSTD_seekTableFooterSize + ZSTD_SKIPPABLEHEADERSIZE;
398
399
11.7k
        U32 remaining = frameSize - ZSTD_seekTableFooterSize; /* don't need to re-read footer */
400
11.7k
        {   U32 const toRead = MIN(remaining, SEEKABLE_BUFF_SIZE);
401
11.7k
            CHECK_IO(src.seek(src.opaque, -(S64)frameSize, SEEK_END));
402
11.7k
            CHECK_IO(src.read(src.opaque, zs->inBuff, toRead));
403
11.7k
            remaining -= toRead;
404
11.7k
        }
405
406
11.7k
        if (MEM_readLE32(zs->inBuff) != (ZSTD_MAGIC_SKIPPABLE_START | 0xE)) {
407
0
            return ERROR(prefix_unknown);
408
0
        }
409
11.7k
        if (MEM_readLE32(zs->inBuff+4) + ZSTD_SKIPPABLEHEADERSIZE != frameSize) {
410
0
            return ERROR(prefix_unknown);
411
0
        }
412
413
11.7k
        {   /* Allocate an extra entry at the end so that we can do size
414
             * computations on the last element without special case */
415
11.7k
            seekEntry_t* const entries = (seekEntry_t*)malloc(sizeof(seekEntry_t) * (numFrames + 1));
416
417
11.7k
            U32 idx = 0;
418
11.7k
            U32 pos = 8;
419
420
11.7k
            U64 cOffset = 0;
421
11.7k
            U64 dOffset = 0;
422
423
11.7k
            if (entries == NULL) return ERROR(memory_allocation);
424
425
            /* compute cumulative positions */
426
35.1k
            for (; idx < numFrames; idx++) {
427
23.4k
                if (pos + sizePerEntry > SEEKABLE_BUFF_SIZE) {
428
0
                    U32 const offset = SEEKABLE_BUFF_SIZE - pos;
429
0
                    U32 const toRead = MIN(remaining, SEEKABLE_BUFF_SIZE - offset);
430
0
                    memmove(zs->inBuff, zs->inBuff + pos, offset); /* move any data we haven't read yet */
431
0
                    CHECK_IO(src.read(src.opaque, zs->inBuff+offset, toRead));
432
0
                    remaining -= toRead;
433
0
                    pos = 0;
434
0
                }
435
23.4k
                entries[idx].cOffset = cOffset;
436
23.4k
                entries[idx].dOffset = dOffset;
437
438
23.4k
                cOffset += MEM_readLE32(zs->inBuff + pos);
439
23.4k
                pos += 4;
440
23.4k
                dOffset += MEM_readLE32(zs->inBuff + pos);
441
23.4k
                pos += 4;
442
23.4k
                if (checksumFlag) {
443
6.50k
                    entries[idx].checksum = MEM_readLE32(zs->inBuff + pos);
444
6.50k
                    pos += 4;
445
6.50k
                }
446
23.4k
            }
447
11.7k
            entries[numFrames].cOffset = cOffset;
448
11.7k
            entries[numFrames].dOffset = dOffset;
449
450
11.7k
            zs->seekTable.entries = entries;
451
11.7k
            zs->seekTable.tableLen = numFrames;
452
11.7k
            zs->seekTable.checksumFlag = checksumFlag;
453
11.7k
            return 0;
454
11.7k
        }
455
11.7k
    }
456
11.7k
}
457
458
size_t ZSTD_seekable_initBuff(ZSTD_seekable* zs, const void* src, size_t srcSize)
459
11.7k
{
460
11.7k
    zs->buffWrapper = (buffWrapper_t){src, srcSize, 0};
461
11.7k
    {   ZSTD_seekable_customFile srcFile = {&zs->buffWrapper,
462
11.7k
                                            &ZSTD_seekable_read_buff,
463
11.7k
                                            &ZSTD_seekable_seek_buff};
464
11.7k
        return ZSTD_seekable_initAdvanced(zs, srcFile); }
465
11.7k
}
466
467
size_t ZSTD_seekable_initFile(ZSTD_seekable* zs, FILE* src)
468
0
{
469
0
    ZSTD_seekable_customFile srcFile = {src, &ZSTD_seekable_read_FILE,
470
0
                                        &ZSTD_seekable_seek_FILE};
471
0
    return ZSTD_seekable_initAdvanced(zs, srcFile);
472
0
}
473
474
size_t ZSTD_seekable_initAdvanced(ZSTD_seekable* zs, ZSTD_seekable_customFile src)
475
11.7k
{
476
11.7k
    zs->src = src;
477
478
11.7k
    {   const size_t seekTableInit = ZSTD_seekable_loadSeekTable(zs);
479
11.7k
        if (ZSTD_isError(seekTableInit)) return seekTableInit; }
480
481
11.7k
    zs->decompressedOffset = (U64)-1;
482
11.7k
    zs->curFrame = (U32)-1;
483
484
11.7k
    {   const size_t dstreamInit = ZSTD_initDStream(zs->dstream);
485
11.7k
        if (ZSTD_isError(dstreamInit)) return dstreamInit; }
486
11.7k
    return 0;
487
11.7k
}
488
489
size_t ZSTD_seekable_decompress(ZSTD_seekable* zs, void* dst, size_t len, unsigned long long offset)
490
11.7k
{
491
11.7k
    unsigned long long const eos = zs->seekTable.entries[zs->seekTable.tableLen].dOffset;
492
11.7k
    if (offset + len > eos) {
493
0
        len = eos - offset;
494
0
    }
495
496
11.7k
    U32 targetFrame = ZSTD_seekable_offsetToFrameIndex(zs, offset);
497
11.7k
    U32 noOutputProgressCount = 0;
498
11.7k
    size_t srcBytesRead = 0;
499
11.7k
    do {
500
        /* check if we can continue from a previous decompress job */
501
11.7k
        if (targetFrame != zs->curFrame || offset < zs->decompressedOffset) {
502
11.7k
            zs->decompressedOffset = zs->seekTable.entries[targetFrame].dOffset;
503
11.7k
            zs->curFrame = targetFrame;
504
505
11.7k
            assert(zs->seekTable.entries[targetFrame].cOffset < LLONG_MAX);
506
11.7k
            CHECK_IO(zs->src.seek(zs->src.opaque,
507
11.7k
                                  (long long)zs->seekTable.entries[targetFrame].cOffset,
508
11.7k
                                  SEEK_SET));
509
11.7k
            zs->in = (ZSTD_inBuffer){zs->inBuff, 0, 0};
510
11.7k
            XXH64_reset(&zs->xxhState, 0);
511
11.7k
            ZSTD_DCtx_reset(zs->dstream, ZSTD_reset_session_only);
512
11.7k
            if (zs->buffWrapper.size && srcBytesRead > zs->buffWrapper.size) {
513
0
                return ERROR(seekableIO);
514
0
            }
515
11.7k
        }
516
517
51.7k
        while (zs->decompressedOffset < offset + len) {
518
40.1k
            size_t toRead;
519
40.1k
            ZSTD_outBuffer outTmp;
520
40.1k
            size_t prevOutPos;
521
40.1k
            size_t prevInPos;
522
40.1k
            size_t forwardProgress;
523
40.1k
            if (zs->decompressedOffset < offset) {
524
                /* dummy decompressions until we get to the target offset */
525
19.6k
                outTmp = (ZSTD_outBuffer){zs->outBuff, (size_t) (MIN(SEEKABLE_BUFF_SIZE, offset - zs->decompressedOffset)), 0};
526
20.4k
            } else {
527
20.4k
                outTmp = (ZSTD_outBuffer){dst, len, (size_t) (zs->decompressedOffset - offset)};
528
20.4k
            }
529
530
40.1k
            prevOutPos = outTmp.pos;
531
40.1k
            prevInPos = zs->in.pos;
532
40.1k
            toRead = ZSTD_decompressStream(zs->dstream, &outTmp, &zs->in);
533
40.1k
            if (ZSTD_isError(toRead)) {
534
0
                return toRead;
535
0
            }
536
537
40.1k
            if (zs->seekTable.checksumFlag) {
538
16.1k
                XXH64_update(&zs->xxhState, (BYTE*)outTmp.dst + prevOutPos,
539
16.1k
                             outTmp.pos - prevOutPos);
540
16.1k
            }
541
40.1k
            forwardProgress = outTmp.pos - prevOutPos;
542
40.1k
            if (forwardProgress == 0) {
543
15.1k
                if (noOutputProgressCount++ > ZSTD_SEEKABLE_NO_OUTPUT_PROGRESS_MAX) {
544
0
                    return ERROR(seekableIO);
545
0
                }
546
25.0k
            } else {
547
25.0k
                noOutputProgressCount = 0;
548
25.0k
            }
549
40.1k
            zs->decompressedOffset += forwardProgress;
550
40.1k
            srcBytesRead += zs->in.pos - prevInPos;
551
552
40.1k
            if (toRead == 0) {
553
                /* frame complete */
554
555
                /* verify checksum */
556
151
                if (zs->seekTable.checksumFlag &&
557
99
                    (XXH64_digest(&zs->xxhState) & 0xFFFFFFFFU) !=
558
99
                            zs->seekTable.entries[targetFrame].checksum) {
559
0
                    return ERROR(corruption_detected);
560
0
                }
561
562
151
                if (zs->decompressedOffset < offset + len) {
563
                    /* go back to the start and force a reset of the stream */
564
0
                    targetFrame = ZSTD_seekable_offsetToFrameIndex(zs, zs->decompressedOffset);
565
                    /* in this case it will fail later with corruption_detected, since last block does not have checksum */
566
0
                    assert(targetFrame != zs->seekTable.tableLen);
567
0
                }
568
151
                break;
569
151
            }
570
571
            /* read in more data if we're done with this buffer */
572
39.9k
            if (zs->in.pos == zs->in.size) {
573
27.8k
                toRead = MIN(toRead, SEEKABLE_BUFF_SIZE);
574
27.8k
                CHECK_IO(zs->src.read(zs->src.opaque, zs->inBuff, toRead));
575
27.8k
                zs->in.size = toRead;
576
27.8k
                zs->in.pos = 0;
577
27.8k
            }
578
39.9k
        }  /* while (zs->decompressedOffset < offset + len) */
579
11.7k
    } while (zs->decompressedOffset != offset + len);
580
581
11.7k
    return len;
582
11.7k
}
583
584
size_t ZSTD_seekable_decompressFrame(ZSTD_seekable* zs, void* dst, size_t dstSize, unsigned frameIndex)
585
0
{
586
0
    if (frameIndex >= zs->seekTable.tableLen) {
587
0
        return ERROR(frameIndex_tooLarge);
588
0
    }
589
590
0
    {   size_t const decompressedSize =
591
0
                zs->seekTable.entries[frameIndex + 1].dOffset -
592
0
                zs->seekTable.entries[frameIndex].dOffset;
593
0
        if (dstSize < decompressedSize) {
594
0
            return ERROR(dstSize_tooSmall);
595
0
        }
596
0
        return ZSTD_seekable_decompress(
597
0
                zs, dst, decompressedSize,
598
0
                zs->seekTable.entries[frameIndex].dOffset);
599
0
    }
600
0
}