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 }