Skip to content

Commit 7190186

Browse files
committed
Add User APIs to Read and Write DCZ Header Frames
1 parent e9cb7cb commit 7190186

File tree

6 files changed

+145
-3
lines changed

6 files changed

+145
-3
lines changed

lib/common/zstd_internal.h

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#ifndef XXH_STATIC_LINKING_ONLY
3333
# define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */
3434
#endif
35+
#include "sha256.h" /* ZSTD_SHA256_DIGEST_SIZE */
3536
#include "xxhash.h" /* XXH_reset, update, digest */
3637
#ifndef ZSTD_NO_TRACE
3738
# include "zstd_trace.h"
@@ -87,6 +88,9 @@ typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e;
8788

8889
#define ZSTD_FRAMECHECKSUMSIZE 4
8990

91+
#define ZSTD_HTTPDCZ_HEADER_SIZE (ZSTD_SKIPPABLEHEADERSIZE + ZSTD_SHA256_DIGEST_SIZE)
92+
#define ZSTD_HTTPDCZ_HEADER_SKIPPABLE_VARIANT (0x0e)
93+
9094
#define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */
9195
#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */) /* for a non-null block */
9296
#define MIN_LITERALS_FOR_4_STREAMS 6

lib/compress/zstd_compress.c

+24-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
#include "zstd_opt.h"
2929
#include "zstd_ldm.h"
3030
#include "zstd_compress_superblock.h"
31-
#include "../common/bits.h" /* ZSTD_highbit32, ZSTD_rotateRight_U64 */
31+
#include "../common/bits.h" /* ZSTD_highbit32, ZSTD_rotateRight_U64 */
32+
#include "../common/sha256.h" /* ZSTD_SHA256_Result, ZSTD_SHA256_hash, ZSTD_SHA256_DIGEST_SIZE */
3233

3334
/* ***************************************************************
3435
* Tuning parameters
@@ -5200,7 +5201,7 @@ ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs,
52005201
void* workspace)
52015202
{
52025203
DEBUGLOG(4, "ZSTD_compress_insertDictionary (dictSize=%u)", (U32)dictSize);
5203-
if ((dict==NULL) || (dictSize<8)) {
5204+
if ((dict == NULL) || (dictSize < ZSTD_DICTIONARYSIZE_MIN)) {
52045205
RETURN_ERROR_IF(dictContentType == ZSTD_dct_fullDict, dictionary_wrong, "");
52055206
return 0;
52065207
}
@@ -7836,3 +7837,24 @@ void ZSTD_CCtxParams_registerSequenceProducer(
78367837
params->extSeqProdState = NULL;
78377838
}
78387839
}
7840+
7841+
size_t ZSTD_writeHeaderForHTTPDCZ(
7842+
void* dst, size_t dstCapacity,
7843+
const void* dict, size_t dictSize) {
7844+
ZSTD_SHA256_Result hash;
7845+
RETURN_ERROR_IF(dst == NULL, dstBuffer_null, "NULL dst buffer.");
7846+
RETURN_ERROR_IF(dstCapacity < ZSTD_HTTPDCZ_HEADER_SIZE, dstSize_tooSmall, "Too small to write frame header.");
7847+
RETURN_ERROR_IF(dict == NULL, dictionary_corrupted, "Dictionary invalid: NULL pointer.");
7848+
RETURN_ERROR_IF(dictSize < ZSTD_DICTIONARYSIZE_MIN, dictionary_corrupted, "Dictionary invalid: too small.");
7849+
7850+
hash = ZSTD_SHA256_hash(dict, dictSize);
7851+
return ZSTD_writeSkippableFrame(dst, dstCapacity, hash.digest, sizeof(hash.digest), ZSTD_HTTPDCZ_HEADER_SKIPPABLE_VARIANT);
7852+
}
7853+
7854+
size_t ZSTD_writeHeaderForHTTPDCZ_fromCDict(
7855+
void* dst, size_t dstCapacity,
7856+
const ZSTD_CDict* cdict) {
7857+
RETURN_ERROR_IF(cdict == NULL, parameter_unsupported, "NULL cdict pointer.");
7858+
RETURN_ERROR_IF(cdict->dictContentType != ZSTD_dct_rawContent, dictionary_corrupted, "Dictionary invalid: must be loaded as raw content for DCZ.");
7859+
return ZSTD_writeHeaderForHTTPDCZ(dst, dstCapacity, cdict->dictContent, cdict->dictContentSize);
7860+
}

lib/decompress/zstd_ddict.c

+6
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ size_t ZSTD_DDict_dictSize(const ZSTD_DDict* ddict)
5555
return ddict->dictSize;
5656
}
5757

58+
ZSTD_dictContentType_e ZSTD_DDict_type(const ZSTD_DDict* ddict)
59+
{
60+
assert(ddict != NULL);
61+
return (ddict->entropyPresent) ? ZSTD_dct_fullDict : ZSTD_dct_rawContent;
62+
}
63+
5864
void ZSTD_copyDDictParameters(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict)
5965
{
6066
DEBUGLOG(4, "ZSTD_copyDDictParameters");

lib/decompress/zstd_ddict.h

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
const void* ZSTD_DDict_dictContent(const ZSTD_DDict* ddict);
3838
size_t ZSTD_DDict_dictSize(const ZSTD_DDict* ddict);
3939

40+
ZSTD_dictContentType_e ZSTD_DDict_type(const ZSTD_DDict* ddict);
41+
4042
void ZSTD_copyDDictParameters(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
4143

4244

lib/decompress/zstd_decompress.c

+40-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@
6464
#define FSE_STATIC_LINKING_ONLY
6565
#include "../common/fse.h"
6666
#include "../common/huf.h"
67-
#include "../common/xxhash.h" /* XXH64_reset, XXH64_update, XXH64_digest, XXH64 */
67+
#include "../common/sha256.h" /* ZSTD_SHA256_Result, ZSTD_SHA256_hash, ZSTD_SHA256_DIGEST_SIZE */
68+
#include "../common/xxhash.h" /* XXH64_reset, XXH64_update, XXH64_digest, XXH64 */
6869
#include "zstd_decompress_internal.h" /* ZSTD_DCtx */
6970
#include "zstd_ddict.h" /* ZSTD_DDictDictContent */
7071
#include "zstd_decompress_block.h" /* ZSTD_decompressBlock_internal */
@@ -623,6 +624,7 @@ size_t ZSTD_readSkippableFrame(void* dst, size_t dstCapacity,
623624

624625
/* check input validity */
625626
RETURN_ERROR_IF(!ZSTD_isSkippableFrame(src, srcSize), frameParameter_unsupported, "");
627+
FORWARD_IF_ERROR(skippableFrameSize, "");
626628
RETURN_ERROR_IF(skippableFrameSize < ZSTD_SKIPPABLEHEADERSIZE || skippableFrameSize > srcSize, srcSize_wrong, "");
627629
RETURN_ERROR_IF(skippableContentSize > dstCapacity, dstSize_tooSmall, "");
628630

@@ -2408,3 +2410,40 @@ size_t ZSTD_decompressStream_simpleArgs (
24082410
return cErr;
24092411
}
24102412
}
2413+
2414+
size_t ZSTD_readHeaderForHTTPDCZ(
2415+
void* dst, size_t dstCapacity,
2416+
const void* src, size_t srcSize) {
2417+
unsigned variant;
2418+
size_t result;
2419+
RETURN_ERROR_IF(dst == NULL, dstBuffer_null, "NULL dst buffer.");
2420+
RETURN_ERROR_IF(dstCapacity < ZSTD_SHA256_DIGEST_SIZE, dstSize_tooSmall, "Too small");
2421+
2422+
result = ZSTD_readSkippableFrame(dst, dstCapacity, &variant, src, srcSize);
2423+
2424+
FORWARD_IF_ERROR(result, "Couldn't read skippable frame.");
2425+
RETURN_ERROR_IF(variant != ZSTD_HTTPDCZ_HEADER_SKIPPABLE_VARIANT, prefix_unknown, "Skippable frame magic is wrong for DCZ.");
2426+
RETURN_ERROR_IF(result != ZSTD_SHA256_DIGEST_SIZE, corruption_detected, "Wrong skippable frame size for DCZ.");
2427+
return result;
2428+
}
2429+
2430+
size_t ZSTD_readHeaderForHTTPDCZ_validateDictMatches(
2431+
const void* src, size_t srcSize,
2432+
const void* dict, size_t dictSize) {
2433+
ZSTD_SHA256_Result frameHash, dictHash;
2434+
size_t result = ZSTD_readHeaderForHTTPDCZ(frameHash.digest, ZSTD_SHA256_DIGEST_SIZE, src, srcSize);
2435+
FORWARD_IF_ERROR(result, "Couldn't read DCZ header.");
2436+
RETURN_ERROR_IF(dict == NULL, dictionary_corrupted, "Dictionary invalid: NULL pointer.");
2437+
RETURN_ERROR_IF(dictSize < ZSTD_DICTIONARYSIZE_MIN, dictionary_corrupted, "Dictionary invalid: too small.");
2438+
dictHash = ZSTD_SHA256_hash(dict, dictSize);
2439+
RETURN_ERROR_IF(memcmp(frameHash.digest, dictHash.digest, ZSTD_SHA256_DIGEST_SIZE), dictionary_wrong, "DCZ hashes don't match.");
2440+
return 1;
2441+
}
2442+
2443+
size_t ZSTD_readHeaderForHTTPDCZ_validateDDictMatches(
2444+
const void* src, size_t srcSize,
2445+
const ZSTD_DDict* ddict) {
2446+
RETURN_ERROR_IF(ddict == NULL, parameter_unsupported, "NULL ddict pointer.");
2447+
RETURN_ERROR_IF(ZSTD_DDict_type(ddict) != ZSTD_dct_rawContent, dictionary_corrupted, "Dictionary invalid: must be loaded as raw content for DCZ.");
2448+
return ZSTD_readHeaderForHTTPDCZ_validateDictMatches(src, srcSize, ZSTD_DDict_dictContent(ddict), ZSTD_DDict_dictSize(ddict));
2449+
}

lib/zstd.h

+69
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,8 @@ extern "C" {
12591259
#define ZSTD_FRAMEHEADERSIZE_MAX 18 /* can be useful for static allocation */
12601260
#define ZSTD_SKIPPABLEHEADERSIZE 8
12611261

1262+
#define ZSTD_DICTIONARYSIZE_MIN 8
1263+
12621264
/* compression parameter bounds */
12631265
#define ZSTD_WINDOWLOG_MAX_32 30
12641266
#define ZSTD_WINDOWLOG_MAX_64 31
@@ -1746,6 +1748,73 @@ ZSTDLIB_STATIC_API size_t ZSTD_readSkippableFrame(void* dst, size_t dstCapacity,
17461748
*/
17471749
ZSTDLIB_STATIC_API unsigned ZSTD_isSkippableFrame(const void* buffer, size_t size);
17481750

1751+
/*! ZSTD_writeHeaderForHTTPDCZ() :
1752+
*
1753+
* The `dcz` HTTP Content-Encoding specifies that payloads using that encoding
1754+
* begin with a skippable frame containing a SHA-256 hash of the dictionary
1755+
* contents used to compress the subsequent Zstandard frame(s).
1756+
*
1757+
* This function produces that header.
1758+
*
1759+
* Note: You must ensure you use the same dictionary provided to this function
1760+
* for the associated compressions (obviously!).
1761+
*
1762+
* @return : Number of bytes written (always 40) or a ZSTD error.
1763+
*/
1764+
ZSTDLIB_STATIC_API size_t ZSTD_writeHeaderForHTTPDCZ(
1765+
void* dst, size_t dstCapacity,
1766+
const void* dict, size_t dictSize);
1767+
1768+
/*! ZSTD_writeHeaderForHTTPDCZ_fromCDict() :
1769+
*
1770+
* The `dcz` HTTP Content-Encoding specifies that payloads using that encoding
1771+
* begin with a skippable frame containing a SHA-256 hash of the dictionary
1772+
* contents used to compress the subsequent Zstandard frame(s).
1773+
*
1774+
* This function produces that header.
1775+
*
1776+
* Note: You must ensure you use the same dictionary provided to this function
1777+
* for the associated compressions (obviously!).
1778+
*
1779+
* Note: The output of this is a function solely of the dictionary content and
1780+
* not the input. This function has to hash the dictionary contents.
1781+
*
1782+
* @return : Number of bytes written (always 40) or a ZSTD error.
1783+
*/
1784+
ZSTDLIB_STATIC_API size_t ZSTD_writeHeaderForHTTPDCZ_fromCDict(
1785+
void* dst, size_t dstCapacity,
1786+
const ZSTD_CDict* cdict);
1787+
1788+
/*! ZSTD_readHeaderForHTTPDCZ() :
1789+
*
1790+
* This function checks that the provided input begins with a skippable
1791+
* frame conforming to the `dcz` HTTP Content-Encoding specification. If it
1792+
* does, it extracts the contained hash into the provided @dst buffer.
1793+
* @dstCapacity must be at least 32.
1794+
*
1795+
* @return : If the hash was successfully read, the number of bytes written
1796+
* into @dst (always 32). Otherwise, a ZSTD error.
1797+
*/
1798+
ZSTDLIB_STATIC_API size_t ZSTD_readHeaderForHTTPDCZ(
1799+
void* dst, size_t dstCapacity,
1800+
const void* src, size_t srcSize);
1801+
1802+
/*! ZSTD_readHeaderForHTTPDCZ_validate*Matches() :
1803+
*
1804+
* This function checks that the provided input (a) begins with a skippable
1805+
* frame conforming to the `dcz` HTTP Content-Encoding specification and (b)
1806+
* that the hash indicated by that frame matches the hash of the provided
1807+
* dictionary.
1808+
*
1809+
* @return : 1, if the header was successfully read and the hashes match.
1810+
* Otherwise, a ZSTD error.
1811+
*/
1812+
ZSTDLIB_STATIC_API size_t ZSTD_readHeaderForHTTPDCZ_validateDictMatches(
1813+
const void* src, size_t srcSize,
1814+
const void* dict, size_t dictSize);
1815+
ZSTDLIB_STATIC_API size_t ZSTD_readHeaderForHTTPDCZ_validateDDictMatches(
1816+
const void* src, size_t srcSize,
1817+
const ZSTD_DDict* ddict);
17491818

17501819

17511820
/***************************************

0 commit comments

Comments
 (0)