Skip to content

Commit 57edb43

Browse files
author
Binh Vo
committed
dd API for fetching skippable frame content
1 parent 89127e5 commit 57edb43

File tree

3 files changed

+139
-1
lines changed

3 files changed

+139
-1
lines changed

lib/decompress/zstd_decompress.c

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,19 @@ unsigned ZSTD_isFrame(const void* buffer, size_t size)
380380
return 0;
381381
}
382382

383+
/*! ZSTD_isSkippableFrame() :
384+
* Tells if the content of `buffer` starts with a valid Frame Identifier for a skippable frame.
385+
* Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0.
386+
*/
387+
unsigned ZSTD_isSkippableFrame(const void* buffer, size_t size)
388+
{
389+
if (size < ZSTD_FRAMEIDSIZE) return 0;
390+
{ U32 const magic = MEM_readLE32(buffer);
391+
if ((magic & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) return 1;
392+
}
393+
return 0;
394+
}
395+
383396
/** ZSTD_frameHeaderSize_internal() :
384397
* srcSize must be large enough to reach header size fields.
385398
* note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless.
@@ -503,7 +516,6 @@ size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t src
503516
return ZSTD_getFrameHeader_advanced(zfhPtr, src, srcSize, ZSTD_f_zstd1);
504517
}
505518

506-
507519
/** ZSTD_getFrameContentSize() :
508520
* compatible with legacy mode
509521
* @return : decompressed size of the single frame pointed to be `src` if known, otherwise
@@ -544,6 +556,59 @@ static size_t readSkippableFrameSize(void const* src, size_t srcSize)
544556
}
545557
}
546558

559+
/*! ZSTD_findFrameSize() :
560+
* Returns the raw size of the first frame in a buffer. Advancing the pointer by this amount will move to the next frame.
561+
* `src` should point to the start of a buffer with one or more ZSTD encoded frames.
562+
* `srcSize` must be at least as large as the frame header.
563+
* hint : any size >= `ZSTD_frameHeaderSize_max` is large enough.
564+
* @return : - size of `src` of the first frame, if known. Advancing the pointer by this amount will move to the next frame.
565+
* - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined
566+
* - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small)
567+
*/
568+
unsigned long long ZSTD_findFrameSize(const void *src, size_t srcSize)
569+
{
570+
ZSTD_frameHeader zfh;
571+
if (ZSTD_getFrameHeader(&zfh, src, srcSize) != 0)
572+
return ZSTD_CONTENTSIZE_ERROR;
573+
if (zfh.frameType == ZSTD_skippableFrame) {
574+
return readSkippableFrameSize(src, srcSize);
575+
} else {
576+
return ZSTD_findFrameCompressedSize(src, srcSize);
577+
}
578+
}
579+
580+
/*! ZSTD_readSkippableFrame() :
581+
* Retrieves a zstd skippable frame containing data given by src, and writes it to dst buffer.
582+
*
583+
* The parameter magicVariant will receive the magicVariant that was supplied when the frame was written,
584+
* i.e. magicNumber - ZSTD_MAGIC_SKIPPABLE_START
585+
*
586+
* Returns an error if destination buffer is not large enough, if the source size is not representable
587+
* with a 4-byte unsigned int, or if the frame is not skippable.
588+
*
589+
* @return : number of bytes written or a ZSTD error.
590+
*/
591+
592+
ZSTDLIB_API size_t ZSTD_readSkippableFrame(void* dst, size_t dstCapacity, unsigned* magicVariant,
593+
const void* src, size_t srcSize)
594+
{
595+
U32 const magicNumber = MEM_readLE32(src);
596+
size_t skippableFrameSize = readSkippableFrameSize(src, srcSize);
597+
size_t skippableContentSize = skippableFrameSize - ZSTD_SKIPPABLEHEADERSIZE;
598+
599+
/* check input validity */
600+
RETURN_ERROR_IF(!ZSTD_isSkippableFrame(src, srcSize), frameParameter_unsupported, "");
601+
RETURN_ERROR_IF(skippableFrameSize < ZSTD_SKIPPABLEHEADERSIZE || skippableFrameSize > srcSize, srcSize_wrong, "");
602+
RETURN_ERROR_IF(skippableContentSize > dstCapacity, dstSize_tooSmall, "");
603+
604+
/* deliver payload */
605+
if (skippableContentSize > 0 && dst != NULL)
606+
ZSTD_memcpy(dst, (const BYTE *)src + ZSTD_SKIPPABLEHEADERSIZE, skippableContentSize);
607+
if (magicVariant != NULL)
608+
*magicVariant = magicNumber - ZSTD_MAGIC_SKIPPABLE_START;
609+
return skippableContentSize;
610+
}
611+
547612
/** ZSTD_findDecompressedSize() :
548613
* compatible with legacy mode
549614
* `srcSize` must be the exact length of some number of ZSTD compressed and/or

lib/zstd.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,6 +1441,37 @@ ZSTDLIB_API size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size
14411441
ZSTDLIB_API size_t ZSTD_writeSkippableFrame(void* dst, size_t dstCapacity,
14421442
const void* src, size_t srcSize, unsigned magicVariant);
14431443

1444+
/*! ZSTD_readSkippableFrame() :
1445+
* Retrieves a zstd skippable frame containing data given by src, and writes it to dst buffer.
1446+
*
1447+
* The parameter magicVariant will receive the magicVariant that was supplied when the frame was written,
1448+
* i.e. magicNumber - ZSTD_MAGIC_SKIPPABLE_START
1449+
*
1450+
* Returns an error if destination buffer is not large enough, if the source size is not representable
1451+
* with a 4-byte unsigned int, or if the frame is not skippable.
1452+
*
1453+
* @return : number of bytes written or a ZSTD error.
1454+
*/
1455+
ZSTDLIB_API size_t ZSTD_readSkippableFrame(void* dst, size_t dstCapacity, unsigned* magicVariant,
1456+
const void* src, size_t srcSize);
1457+
1458+
/*! ZSTD_isSkippableFrame() :
1459+
* Tells if the content of `buffer` starts with a valid Frame Identifier for a skippable frame.
1460+
*/
1461+
ZSTDLIB_API unsigned ZSTD_isSkippableFrame(const void* buffer, size_t size);
1462+
1463+
/*! ZSTD_findFrameSize() :
1464+
* Returns the raw size of the first frame in a buffer.
1465+
* `src` should point to the start of a buffer with one or more ZSTD encoded frames.
1466+
* `srcSize` must be at least as large as the frame header.
1467+
* hint : any size >= `ZSTD_frameHeaderSize_max` is large enough.
1468+
* @return : - size of `src` of the first frame, if known. Advancing the pointer by this amount will move to the next frame.
1469+
* - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined
1470+
* - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small)
1471+
*/
1472+
ZSTDLIB_API unsigned long long ZSTD_findFrameSize(const void *src, size_t srcSize);
1473+
1474+
14441475

14451476
/***************************************
14461477
* Memory management

tests/fuzzer.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,6 +1830,48 @@ static int basicUnitTests(U32 const seed, double compressibility)
18301830
if (memcmp(decodedBuffer, CNBuffer, CNBuffSize / 2) != 0) goto _output_error;
18311831
DISPLAYLEVEL(3, "OK \n");
18321832

1833+
/* Simple API skippable frame test */
1834+
DISPLAYLEVEL(3, "test%3i : read/write a skippable frame : ", testNb++);
1835+
{
1836+
U32 i;
1837+
unsigned readMagic;
1838+
unsigned long long receivedSize;
1839+
size_t skippableSize;
1840+
const U32 skipLen = 129 KB;
1841+
char* const skipBuff = (char*)malloc(skipLen);
1842+
assert(skipBuff != NULL);
1843+
for (i = 0; i < skipLen; i++)
1844+
skipBuff[i] = (BYTE) ((seed + i) % 256);
1845+
skippableSize = ZSTD_writeSkippableFrame((BYTE*)compressedBuffer, compressedBufferSize,
1846+
skipBuff, skipLen, seed % 15);
1847+
CHECK_Z(skippableSize);
1848+
CHECK_EQ(1, ZSTD_isSkippableFrame(compressedBuffer, skippableSize));
1849+
CHECK_EQ(skippableSize, ZSTD_findFrameSize(compressedBuffer, skippableSize));
1850+
receivedSize = ZSTD_readSkippableFrame(decodedBuffer, CNBuffSize, &readMagic, compressedBuffer, skippableSize);
1851+
CHECK_EQ(skippableSize, receivedSize + ZSTD_SKIPPABLEHEADERSIZE);
1852+
CHECK_EQ(seed % 15, readMagic);
1853+
if (memcmp(decodedBuffer, skipBuff, skipLen) != 0) goto _output_error;
1854+
1855+
free(skipBuff);
1856+
}
1857+
DISPLAYLEVEL(3, "OK \n");
1858+
1859+
DISPLAYLEVEL(3, "test%3i : read/write an empty skippable frame : ", testNb++);
1860+
{
1861+
unsigned readMagic;
1862+
unsigned long long receivedSize;
1863+
size_t skippableSize;
1864+
skippableSize = ZSTD_writeSkippableFrame((BYTE*)compressedBuffer, compressedBufferSize,
1865+
CNBuffer, 0, seed % 15);
1866+
CHECK_EQ(ZSTD_SKIPPABLEHEADERSIZE, skippableSize);
1867+
CHECK_EQ(1, ZSTD_isSkippableFrame(compressedBuffer, skippableSize));
1868+
CHECK_EQ(skippableSize, ZSTD_findFrameSize(compressedBuffer, skippableSize));
1869+
receivedSize = ZSTD_readSkippableFrame(NULL, 0, &readMagic, compressedBuffer, skippableSize);
1870+
CHECK_EQ(skippableSize, receivedSize + ZSTD_SKIPPABLEHEADERSIZE);
1871+
CHECK_EQ(seed % 15, readMagic);
1872+
}
1873+
DISPLAYLEVEL(3, "OK \n");
1874+
18331875
/* Dictionary and CCtx Duplication tests */
18341876
{ ZSTD_CCtx* const ctxOrig = ZSTD_createCCtx();
18351877
ZSTD_CCtx* const ctxDuplicated = ZSTD_createCCtx();

0 commit comments

Comments
 (0)