UCDP-AEC

Models and data for the UCDP Abstractive Event analysis Corpus paper
git clone https://esimon.eu/repos/UCDP-AEC.git
Log | Files | Refs | README | LICENSE

zstd-seek.c (21952B)


      1 /* ******************************************************************
      2  * libzstd-seek
      3  * Copyright (c) 2020, Martinelli Marco
      4  *
      5  * You can contact the author at :
      6  * - Source repository : https://github.com/martinellimarco/libzstd-seek
      7  *
      8  * This source code is licensed under the GPLv3 (found in the LICENSE
      9  * file in the root directory of this source tree).
     10 ****************************************************************** */
     11 
     12 #include <fcntl.h>
     13 #include <stdlib.h>
     14 #include <stdint.h>
     15 #include <stdbool.h>
     16 #include <sys/stat.h>
     17 #include <string.h>
     18 #include "zstd-seek.h"
     19 
     20 #ifdef _WIN32
     21 #include "windows-mmap.h"
     22 #else
     23 #include <unistd.h>
     24 #include <sys/mman.h>
     25 #endif
     26 
     27 typedef struct {
     28     size_t compressedOffset; //how may bytes to skip from the beginning of the compressed stream (skip to target frame)
     29     size_t uncompressedOffset; //how many bytes skip from the beginning of the uncompressed frame (move inside target frame)
     30     ZSTDSeek_JumpTableRecord jtr; //copy of the jump table record that was used to calculate this jump coordinate
     31 } ZSTDSeek_JumpCoordinate;
     32 
     33 struct ZSTDSeek_Context_s{
     34     ZSTD_DCtx* dctx;
     35 
     36     void *buff; //the start of the buffer with the zstd frame(s)
     37     size_t size; //the length of buff
     38 
     39     size_t lastFrameCompressedSize; //the size of the last frame processed by read
     40 
     41     size_t currentUncompressedPos; //the current position in the uncompressed file, returned by tell
     42     size_t currentCompressedPos; //the position in the compressed file, returned by compressedTell
     43 
     44     ZSTDSeek_JumpTable* jt;
     45     int jumpTableFullyInitialized;
     46 
     47     ZSTDSeek_JumpCoordinate jc;
     48 
     49     size_t tmpOutBuffSize;
     50     uint8_t* tmpOutBuff;
     51     size_t tmpOutBuffPos; //the position where we read so far in the tmpOutBuff. if tmpOutBuffPos < output.pos we have data left in this buffer to read before we move on to uncompress more data
     52 
     53     int mmap_fd; //the file descriptor of the memory map, used only if the context is created with ZSTDSeek_createFromFile or ZSTDSeek_createFromFileWithoutJumpTable
     54     int close_fd; //1 if we own the mmap_fd and we are responsible of closing it, 0 otherwise
     55 
     56     uint8_t* inBuff; //it's a pointer to something inside buff
     57     ZSTD_inBuffer input;
     58     ZSTD_outBuffer output;
     59 };
     60 
     61 /* Jump Table API */
     62 
     63 ZSTDSeek_JumpTable* ZSTDSeek_getJumpTableOfContext(ZSTDSeek_Context *sctx){
     64     if(!sctx){
     65         return NULL;
     66     }
     67     return sctx->jt;
     68 }
     69 
     70 ZSTDSeek_JumpTable* ZSTDSeek_newJumpTable(){
     71     ZSTDSeek_JumpTable *jt = malloc(sizeof(ZSTDSeek_JumpTable));
     72 
     73     jt->records = malloc(sizeof(ZSTDSeek_JumpTableRecord));
     74     jt->length = 0;
     75     jt->capacity = 1;
     76 
     77     return jt;
     78 }
     79 
     80 void ZSTDSeek_freeJumpTable(ZSTDSeek_JumpTable* jt){
     81     if(!jt){
     82         DEBUG("Invalid argument");
     83         return;
     84     }
     85     free(jt->records);
     86     free(jt);
     87 }
     88 
     89 void ZSTDSeek_addJumpTableRecord(ZSTDSeek_JumpTable* jt, size_t compressedPos, size_t uncompressedPos){
     90     if(!jt){
     91         DEBUG("Invalid argument");
     92         return;
     93     }
     94 
     95     if(jt->length == jt->capacity){
     96         if(jt->capacity == UINT64_MAX){
     97             DEBUG("Jump Table: Maximum capacity reached\n");
     98             return;
     99         }else if(jt->capacity < (UINT64_MAX>>1)){
    100             jt->capacity *= 2;
    101         }else{
    102             jt->capacity = UINT64_MAX;
    103         }
    104         jt->records = realloc(jt->records, jt->capacity*sizeof(ZSTDSeek_JumpTableRecord));
    105     }
    106 
    107     jt->records[jt->length++] = (ZSTDSeek_JumpTableRecord){
    108             compressedPos,
    109             uncompressedPos
    110     };
    111 }
    112 
    113 int ZSTDSeek_initializeJumpTable(ZSTDSeek_Context *sctx){
    114     return ZSTDSeek_initializeJumpTableUpUntilPos(sctx, SIZE_MAX);
    115 }
    116 
    117 bool ZSTDSeek_isLittleEndian(){
    118     volatile int x = 1;
    119     return *(char*)(&x) == 1;
    120 }
    121 
    122 uint32_t ZSTDSeek_fromLE32(uint32_t data){
    123     if(ZSTDSeek_isLittleEndian()){
    124         return data;
    125     }else{
    126         uint32_t swap = ((data & 0xFF000000) >> 24) |
    127                         ((data & 0x00FF0000) >> 8)  |
    128                         ((data & 0x0000FF00) << 8)  |
    129                         ((data & 0x000000FF) << 24);
    130         return swap;
    131     }
    132 }
    133 
    134 int ZSTDSeek_initializeJumpTableUpUntilPos(ZSTDSeek_Context *sctx, size_t upUntilPos){
    135     if(!sctx){
    136         DEBUG("ZSTDSeek_Context is NULL\n");
    137         return -1;
    138     }
    139 
    140     void *buff = sctx->buff;
    141     size_t size = sctx->size;
    142 
    143     uint8_t *footer = (uint8_t *)buff + (size - ZSTD_SEEK_TABLE_FOOTER_SIZE);
    144     uint32_t magicnumber = ZSTDSeek_fromLE32(*((uint32_t *)(footer + 5)));
    145 
    146     if(magicnumber == ZSTD_SEEKABLE_MAGICNUMBER){
    147         DEBUG("Seektable detected\n");
    148         uint8_t sfd = *((uint8_t*)(footer + 4));
    149         uint8_t checksumFlag = sfd >> 7;
    150 
    151         /* check reserved bits */
    152         if((sfd >> 2) & 0x1f){
    153             DEBUG("Last frame checksumFlag= %x: Bits 3-7 should be zero. Ignoring malformed seektable.\n",(uint32_t)sfd);
    154         }else{
    155             uint32_t const numFrames = ZSTDSeek_fromLE32(*((uint32_t *)footer));
    156             uint32_t const sizePerEntry = 8 + (checksumFlag ? 4 : 0);
    157             uint32_t const tableSize = sizePerEntry * numFrames;
    158             uint32_t const frameSize = tableSize + ZSTD_SEEK_TABLE_FOOTER_SIZE + ZSTD_SKIPPABLE_HEADER_SIZE;
    159 
    160             uint8_t *frame = (uint8_t *)buff + (size - frameSize);
    161             uint32_t skippableHeader = ZSTDSeek_fromLE32(*((uint32_t *)frame));
    162             if(skippableHeader != (ZSTD_MAGIC_SKIPPABLE_START|0xE)){
    163                 DEBUG("Last frame Header = %u does not match magic number %u. Ignoring malformed seektable.\n", skippableHeader, (ZSTD_MAGIC_SKIPPABLE_START|0xE));
    164             }else{
    165                 uint32_t _frameSize = ZSTDSeek_fromLE32(*((uint32_t *)(frame + 4)));
    166                 if(_frameSize + ZSTD_SKIPPABLE_HEADER_SIZE != frameSize){
    167                     DEBUG("Last frame size = %u does not match expected size = %u. Ignoring malformed seektable.\n", _frameSize + ZSTD_SKIPPABLE_HEADER_SIZE, frameSize);
    168                 }else{
    169                     uint8_t *table = frame + ZSTD_SKIPPABLE_HEADER_SIZE;
    170                     size_t cOffset = 0;
    171                     size_t dOffset = 0;
    172                     for(uint32_t i = 0; i < numFrames; i++){
    173                         ZSTDSeek_addJumpTableRecord(sctx->jt, cOffset, dOffset);
    174                         cOffset += ZSTDSeek_fromLE32(*((uint32_t *)(table + (i * sizePerEntry))));
    175                         dOffset += ZSTDSeek_fromLE32(*((uint32_t *)(table + (i * sizePerEntry) + 4)));
    176                     }
    177                     ZSTDSeek_addJumpTableRecord(sctx->jt, cOffset, dOffset);
    178 
    179                     sctx->jumpTableFullyInitialized = 1;
    180                     return 0;
    181                 }
    182             }
    183         }
    184     }
    185 
    186     size_t frameCompressedSize;
    187     size_t compressedPos = 0;
    188     size_t uncompressedPos = 0;
    189 
    190     if(sctx->jt->length > 0){
    191         compressedPos = sctx->jt->records[sctx->jt->length-1].compressedPos;
    192         uncompressedPos = sctx->jt->records[sctx->jt->length-1].uncompressedPos;
    193     }
    194 
    195     buff = (uint8_t *)sctx->buff + compressedPos;
    196 
    197     sctx->jumpTableFullyInitialized = 1;
    198 
    199     while ((frameCompressedSize = ZSTD_findFrameCompressedSize(buff, size))>0 && !ZSTD_isError(frameCompressedSize)) {
    200         uint32_t const magic = ZSTDSeek_fromLE32(*((uint32_t *)buff));
    201         if((magic & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START){
    202             compressedPos += frameCompressedSize;
    203             buff = (uint8_t *)buff + frameCompressedSize;
    204             continue;
    205         }
    206 
    207         if(sctx->jt->length == 0 || sctx->jt->records[sctx->jt->length-1].uncompressedPos < uncompressedPos){
    208             ZSTDSeek_addJumpTableRecord(sctx->jt, compressedPos, uncompressedPos);
    209         }
    210 
    211         size_t frameContentSize = ZSTD_getFrameContentSize(buff, size);
    212         if(ZSTD_isError(frameContentSize)){//true if the uncompressed size is not known
    213             frameContentSize = 0;
    214 
    215             ZSTD_DCtx *dctx = ZSTD_createDCtx();
    216             size_t const buffOutSize = ZSTD_DStreamOutSize();
    217             void*  const buffOut = malloc(buffOutSize);
    218             size_t lastRet = 0;
    219             void *buffIn = buff;
    220 
    221             ZSTD_inBuffer input = { buffIn, frameCompressedSize, 0 };
    222             while (input.pos < input.size) {
    223                 ZSTD_outBuffer output = { buffOut, buffOutSize, 0 };
    224                 lastRet = ZSTD_decompressStream(dctx, &output , &input);
    225                 if(ZSTD_isError(lastRet)){
    226                     DEBUG("Error decompressing: %s\n", ZSTD_getErrorName(lastRet));
    227                     ZSTD_freeDCtx(dctx);
    228                     free(buffOut);
    229                     return -1;
    230                 }
    231                 frameContentSize += output.pos;
    232             }
    233             ZSTD_freeDCtx(dctx);
    234             free(buffOut);
    235 
    236             if (lastRet != 0) {
    237                 DEBUG("Unexpected EOF. Is the file truncated?\n");
    238                 return -1;
    239             }
    240         }
    241 
    242         compressedPos += frameCompressedSize;
    243         uncompressedPos += frameContentSize;
    244         buff = (uint8_t *)buff + frameCompressedSize;
    245 
    246         if(uncompressedPos >= upUntilPos){
    247             sctx->jumpTableFullyInitialized = 0;
    248             break;
    249         }
    250     }
    251     if(sctx->jt->length > 0){
    252         if(sctx->jt->records[sctx->jt->length-1].uncompressedPos < uncompressedPos){
    253             ZSTDSeek_addJumpTableRecord(sctx->jt, compressedPos, uncompressedPos);
    254         }
    255         return 0;
    256     }else{ //0 frames found
    257         DEBUG("No frames\n");
    258         return -1;
    259     }
    260 }
    261 
    262 int ZSTDSeek_jumpTableIsInitialized(ZSTDSeek_Context *sctx){
    263     return sctx->jumpTableFullyInitialized;
    264 }
    265 
    266 ZSTDSeek_JumpCoordinate ZSTDSeek_getJumpCoordinate(ZSTDSeek_Context *sctx, size_t uncompressedPos) {
    267     if(!sctx->jumpTableFullyInitialized && (sctx->jt->length == 0 || sctx->jt->records[sctx->jt->length-1].uncompressedPos <= uncompressedPos)){
    268         ZSTDSeek_initializeJumpTableUpUntilPos(sctx, uncompressedPos);
    269     }
    270 
    271     //search for the greater value of m where sctx->jt->records[m].uncompressedPos <= uncompressedPos
    272     uint64_t l = 0;
    273     uint64_t r = sctx->jt->length - 1;
    274     while(l <= r){
    275         uint64_t m = (l+r)/2;
    276         if(sctx->jt->records[m].uncompressedPos > uncompressedPos){
    277             r = m-1;
    278         }else if((m+1) < sctx->jt->length && sctx->jt->records[m+1].uncompressedPos <= uncompressedPos){
    279             l = m+1;
    280         }else{
    281             return (ZSTDSeek_JumpCoordinate){sctx->jt->records[m].compressedPos, uncompressedPos - sctx->jt->records[m].uncompressedPos, sctx->jt->records[m]};
    282         }
    283     }
    284 /*
    285     //old linear search
    286     for(uint64_t i = sctx->jt->length - 1; i >= 0; i--){
    287         if(sctx->jt->records[i].uncompressedPos <= uncompressedPos){
    288             return (ZSTDSeek_JumpCoordinate){sctx->jt->records[i].compressedPos, uncompressedPos - sctx->jt->records[i].uncompressedPos, sctx->jt->records[i]};
    289         }
    290     }
    291 */
    292     return (ZSTDSeek_JumpCoordinate){0, uncompressedPos, (ZSTDSeek_JumpTableRecord){0, 0}};
    293 }
    294 
    295 /* Seek API */
    296 
    297 ZSTDSeek_Context* ZSTDSeek_createFromFileWithoutJumpTable(const char* file){
    298     struct stat st;
    299     stat(file, &st);
    300 
    301     int fd = open(file, O_RDONLY, 0);
    302     if(fd < 0){
    303         DEBUG("Unable to open '%s'\n", file);
    304         return NULL;
    305     }
    306 
    307     void* const buff = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    308     if(buff == MAP_FAILED){
    309         DEBUG("Unable to mmap '%s'\n",  file);
    310         return NULL;
    311     }
    312 
    313     ZSTDSeek_Context *sctx = ZSTDSeek_createWithoutJumpTable(buff, st.st_size);
    314     if(sctx){
    315         sctx->mmap_fd = fd;
    316         sctx->close_fd = 1;
    317         return sctx;
    318     }else{
    319         munmap(buff, st.st_size);
    320         close(fd);
    321         return NULL;
    322     }
    323 }
    324 
    325 ZSTDSeek_Context* ZSTDSeek_createFromFile(const char* file){
    326     ZSTDSeek_Context* sctx = ZSTDSeek_createFromFileWithoutJumpTable(file);
    327     if(sctx && ZSTDSeek_initializeJumpTable(sctx)!=0){
    328         DEBUG("Can't initialize the jump table\n");
    329         ZSTDSeek_free(sctx);
    330         return NULL;
    331     }
    332     return sctx;
    333 }
    334 
    335 ZSTDSeek_Context* ZSTDSeek_createFromFileDescriptorWithoutJumpTable(int fd){
    336     size_t size = lseek(fd,0L,SEEK_END);
    337 
    338     void* const buff = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
    339     if(buff == MAP_FAILED){
    340         DEBUG("Unable to mmap file descriptor %d\n",  fd);
    341         return NULL;
    342     }
    343 
    344     ZSTDSeek_Context *sctx = ZSTDSeek_createWithoutJumpTable(buff, size);
    345     if(sctx){
    346         sctx->mmap_fd = fd;
    347         sctx->close_fd = 0;
    348         return sctx;
    349     }else{
    350         munmap(buff, size);
    351         close(fd);
    352         return NULL;
    353     }
    354 }
    355 
    356 ZSTDSeek_Context* ZSTDSeek_createFromFileDescriptor(int fd){
    357     ZSTDSeek_Context* sctx = ZSTDSeek_createFromFileDescriptorWithoutJumpTable(fd);
    358     if(sctx && ZSTDSeek_initializeJumpTable(sctx)!=0){
    359         DEBUG("Can't initialize the jump table\n");
    360         ZSTDSeek_free(sctx);
    361         return NULL;
    362     }
    363     return sctx;
    364 }
    365 
    366 ZSTDSeek_Context* ZSTDSeek_createWithoutJumpTable(void *buff, size_t size){
    367     ZSTD_DCtx *dctx = ZSTD_createDCtx();
    368 
    369     ZSTDSeek_Context* sctx = malloc(sizeof(ZSTDSeek_Context));
    370 
    371     sctx->dctx = dctx;
    372 
    373     sctx->buff = buff;
    374     sctx->size = size;
    375 
    376     sctx->inBuff = (uint8_t*)buff;
    377 
    378     sctx->currentUncompressedPos = 0;
    379     sctx->currentCompressedPos = 0;
    380 
    381     sctx->lastFrameCompressedSize = 0;
    382 
    383     sctx->jc = (ZSTDSeek_JumpCoordinate){0,0};
    384 
    385     sctx->tmpOutBuffSize = ZSTD_DStreamOutSize();
    386     sctx->tmpOutBuff = (uint8_t*)malloc(sctx->tmpOutBuffSize);
    387     sctx->tmpOutBuffPos = 0;
    388 
    389     sctx->mmap_fd = -1;
    390     sctx->close_fd = 0;
    391 
    392     sctx->input = (ZSTD_inBuffer){sctx->inBuff, 0, 0};
    393     sctx->output = (ZSTD_outBuffer){sctx->tmpOutBuff, 0, 0};
    394 
    395     sctx->jt = ZSTDSeek_newJumpTable();
    396     sctx->jumpTableFullyInitialized = 0;
    397 
    398     //test if the buffer starts with a valid frame
    399     if(ZSTD_isError(ZSTD_findFrameCompressedSize(sctx->buff, sctx->size))){
    400         DEBUG("Invalid format\n");
    401         ZSTDSeek_free(sctx);
    402         return NULL;
    403     }
    404 
    405     return sctx;
    406 }
    407 
    408 ZSTDSeek_Context* ZSTDSeek_create(void *buff, size_t size){
    409     ZSTDSeek_Context* sctx = ZSTDSeek_createWithoutJumpTable(buff, size);
    410     if(sctx && ZSTDSeek_initializeJumpTable(sctx)!=0){
    411         DEBUG("Can't initialize the jump table\n");
    412         ZSTDSeek_free(sctx);
    413         return NULL;
    414     }
    415     return sctx;
    416 }
    417 
    418 size_t ZSTDSeek_read(void *outBuff, size_t outBuffSize, ZSTDSeek_Context *sctx){
    419     if(!sctx){
    420         DEBUG("ZSTDSeek_Context is NULL\n");
    421         return 0;
    422     }
    423     
    424     ZSTDSeek_JumpCoordinate localJc = ZSTDSeek_getJumpCoordinate(sctx, sctx->currentUncompressedPos); //trigger the generation of a jump table record, if needed
    425     sctx->currentCompressedPos = localJc.jtr.compressedPos;
    426 
    427     size_t maxReadable = ZSTDSeek_lastKnownUncompressedFileSize(sctx) - sctx->currentUncompressedPos;
    428     size_t toRead = maxReadable < outBuffSize ? maxReadable : outBuffSize;
    429     size_t shouldRead = toRead;
    430 
    431     if(sctx->tmpOutBuffPos < sctx->output.pos){
    432         if(sctx->jc.uncompressedOffset > sctx->output.pos){
    433             sctx->jc.uncompressedOffset -= sctx->output.pos;
    434         }else{
    435             size_t maxCopy = (sctx->output.pos - sctx->tmpOutBuffPos) - sctx->jc.uncompressedOffset;
    436             size_t toCopy = maxCopy < toRead ? maxCopy : toRead;
    437 
    438             memcpy(outBuff, sctx->tmpOutBuff+sctx->tmpOutBuffPos+sctx->jc.uncompressedOffset, toCopy);
    439             toRead -= toCopy;
    440             outBuff = (uint8_t *)outBuff + toCopy;
    441             sctx->currentUncompressedPos += toCopy;
    442             sctx->tmpOutBuffPos += toCopy + sctx->jc.uncompressedOffset;
    443             sctx->jc.uncompressedOffset = 0;
    444         }
    445     }
    446 
    447     while (toRead > 0 && ((sctx->input.pos < sctx->input.size) || (sctx->lastFrameCompressedSize = ZSTD_findFrameCompressedSize(sctx->inBuff, sctx->size)) > 0)){
    448         if(sctx->input.pos == sctx->input.size){
    449             sctx->input = (ZSTD_inBuffer){sctx->inBuff, sctx->lastFrameCompressedSize, 0};
    450         }
    451 
    452         while (sctx->input.pos < sctx->input.size) {
    453             sctx->output = (ZSTD_outBuffer){ sctx->tmpOutBuff, sctx->tmpOutBuffSize, 0 };
    454             sctx->tmpOutBuffPos = 0;
    455             size_t const ret = ZSTD_decompressStream(sctx->dctx, &sctx->output , &sctx->input);
    456 
    457             if(ZSTD_isError(ret)){
    458                 DEBUG("Error decompressing: %s\n", ZSTD_getErrorName(ret));
    459                 return ZSTDSEEK_ERR_READ;
    460             }
    461 
    462             sctx->currentCompressedPos += sctx->input.pos;
    463 
    464             if(sctx->jc.uncompressedOffset > sctx->output.pos){
    465                 sctx->jc.uncompressedOffset -= sctx->output.pos;
    466             }else{
    467                 size_t maxCopy = (sctx->output.pos - sctx->tmpOutBuffPos) - sctx->jc.uncompressedOffset;
    468                 size_t toCopy = maxCopy < toRead ? maxCopy : toRead;
    469 
    470                 memcpy(outBuff, sctx->tmpOutBuff+sctx->tmpOutBuffPos+sctx->jc.uncompressedOffset, toCopy);
    471                 toRead -= toCopy;
    472                 outBuff = (uint8_t *)outBuff + toCopy;
    473                 sctx->currentUncompressedPos += toCopy;
    474                 sctx->tmpOutBuffPos += toCopy + sctx->jc.uncompressedOffset;
    475                 sctx->jc.uncompressedOffset = 0;
    476             }
    477 
    478             if(toRead == 0){
    479                 break;
    480             }
    481         }
    482 
    483         if(sctx->input.pos == sctx->input.size){ //end of frame
    484             sctx->inBuff+=sctx->lastFrameCompressedSize;
    485         }
    486 
    487         if(toRead == 0){
    488             break;
    489         }
    490     }
    491 
    492     return shouldRead - toRead;
    493 }
    494 
    495 int ZSTDSeek_seek(ZSTDSeek_Context *sctx, long offset, int origin){
    496     if(!sctx){
    497         DEBUG("ZSTDSeek_Context is NULL\n");
    498         return -1;
    499     }
    500     if(origin == SEEK_CUR){
    501         if(offset==0){
    502             return 0;
    503         }
    504         offset = (long)sctx->currentUncompressedPos + offset;
    505         origin = SEEK_SET;
    506     }else if(origin == SEEK_END){
    507         offset = (long)ZSTDSeek_uncompressedFileSize(sctx) + offset;
    508         origin = SEEK_SET;
    509     }
    510     if(origin == SEEK_SET){
    511         if(offset < 0){
    512             DEBUG("Negative seek\n");
    513             return ZSTDSEEK_ERR_NEGATIVE_SEEK;
    514         }else if(offset > 0){
    515             ZSTDSeek_getJumpCoordinate(sctx, sctx->currentUncompressedPos+offset); //trigger an update of the lastKnownUncompressedFileSize
    516             if(offset > ZSTDSeek_lastKnownUncompressedFileSize(sctx)){
    517                 DEBUG("Seek to a frame beyond the buffer length\n");
    518                 return ZSTDSEEK_ERR_BEYOND_END_SEEK;
    519             }
    520         }
    521 
    522         if(offset == sctx->currentUncompressedPos){ //we are already there, do nothing
    523             return 0;
    524         }
    525 
    526         ZSTDSeek_JumpCoordinate new_jc = ZSTDSeek_getJumpCoordinate(sctx, offset);
    527 
    528         if(sctx->jc.compressedOffset != new_jc.compressedOffset || offset < sctx->currentUncompressedPos){ //reset
    529             ZSTD_DCtx_reset(sctx->dctx, ZSTD_reset_session_only);
    530 
    531             sctx->jc = new_jc;
    532 
    533             sctx->inBuff = (uint8_t *)sctx->buff + sctx->jc.compressedOffset; //jump to the beginning of the frame..
    534             sctx->currentUncompressedPos = offset; //..and adjust the uncompressed position..
    535             sctx->currentCompressedPos = sctx->jc.compressedOffset;
    536             sctx->tmpOutBuffPos = 0; //..and reset the position in the tmp buffer
    537             sctx->input = (ZSTD_inBuffer){sctx->inBuff, 0, 0};
    538             sctx->output = (ZSTD_outBuffer){sctx->tmpOutBuff, 0, 0};
    539 
    540         }else{ //move forward
    541             size_t toSkipTotal = offset - sctx->currentUncompressedPos;
    542 
    543             size_t const buffOutSize = ZSTD_DStreamOutSize();
    544             void*  const buffOut = malloc(buffOutSize);
    545 
    546             while(toSkipTotal>0){
    547                 size_t toSkip = buffOutSize < toSkipTotal ? buffOutSize : toSkipTotal;
    548                 toSkipTotal -= ZSTDSeek_read(buffOut, toSkip, sctx);
    549             }
    550 
    551             free(buffOut);
    552         }
    553     }else{
    554         DEBUG("Invalid origin\n");
    555         return -1;
    556     }
    557 
    558     return 0;
    559 }
    560 
    561 long ZSTDSeek_tell(ZSTDSeek_Context *sctx){
    562     if(!sctx){
    563         DEBUG("ZSTDSeek_Context is NULL\n");
    564         return -1;
    565     }
    566 
    567     return sctx->currentUncompressedPos;
    568 }
    569 
    570 long ZSTDSeek_compressedTell(ZSTDSeek_Context *sctx){
    571     if(!sctx){
    572         DEBUG("ZSTDSeek_Context is NULL\n");
    573         return -1;
    574     }
    575 
    576     return sctx->currentCompressedPos;
    577 }
    578 
    579 size_t ZSTDSeek_uncompressedFileSize(ZSTDSeek_Context *sctx){
    580     if(!sctx){
    581         DEBUG("ZSTDSeek_Context is NULL\n");
    582         return 0;
    583     }
    584 
    585     ZSTDSeek_initializeJumpTable(sctx);
    586 
    587     return ZSTDSeek_lastKnownUncompressedFileSize(sctx);
    588 }
    589 
    590 size_t ZSTDSeek_lastKnownUncompressedFileSize(ZSTDSeek_Context *sctx){
    591     if(!sctx){
    592         DEBUG("ZSTDSeek_Context is NULL\n");
    593         return 0;
    594     }
    595 
    596     return sctx->jt->length > 0 ? sctx->jt->records[sctx->jt->length-1].uncompressedPos : 0;
    597 }
    598 
    599 int ZSTDSeek_fileno(ZSTDSeek_Context *sctx){
    600     return sctx->mmap_fd;
    601 }
    602 
    603 size_t ZSTDSeek_countFramesUpTo(ZSTDSeek_Context *sctx, size_t upTo){
    604     if(!sctx){
    605         DEBUG("ZSTDSeek_Context is NULL\n");
    606         return 0;
    607     }
    608 
    609     size_t frameCompressedSize;
    610 
    611     void *buff = sctx->buff;
    612     size_t size = sctx->size;
    613 
    614     size_t counter = 0;
    615 
    616     while ((frameCompressedSize = ZSTD_findFrameCompressedSize(buff, size))>0 && !ZSTD_isError(frameCompressedSize)) {
    617         counter++;
    618         buff = (uint8_t *)buff + frameCompressedSize;
    619         if(counter >= upTo){
    620             return upTo;
    621         }
    622     }
    623 
    624     return counter;
    625 }
    626 
    627 size_t ZSTDSeek_getNumberOfFrames(ZSTDSeek_Context *sctx){
    628     return ZSTDSeek_countFramesUpTo(sctx, SIZE_MAX);
    629 }
    630 
    631 int ZSTDSeek_isMultiframe(ZSTDSeek_Context *sctx){
    632     return ZSTDSeek_countFramesUpTo(sctx, 2) > 1;
    633 }
    634 
    635 void ZSTDSeek_free(ZSTDSeek_Context *sctx){
    636     if(!sctx){
    637         DEBUG("ZSTDSeek_Context is NULL\n");
    638         return;
    639     }
    640 
    641     if(sctx->dctx){
    642         ZSTD_freeDCtx(sctx->dctx);
    643     }
    644 
    645     ZSTDSeek_freeJumpTable(sctx->jt);
    646 
    647     if(sctx->mmap_fd>=0 && sctx->close_fd){
    648         munmap(sctx->buff, sctx->size);
    649         close(sctx->mmap_fd);
    650     }
    651 
    652     free(sctx->tmpOutBuff);
    653 
    654     free(sctx);
    655 }