aboutsummaryrefslogtreecommitdiff
path: root/lib/embed_zstd.cpp
blob: d9be35af256aee3ccb6a1d7642d9cbc288f0eb68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include "embed.h"
#include <cassert>
#include <zstd.h>

using namespace embed;

ZSTD_DDict *dictPtr = nullptr;

template <>
Resources<Compression::Zstd>::Resources(const std::span<const unsigned char> &dictionary)
{
    if(!dictionary.empty()) {
        dictPtr = ZSTD_createDDict(dictionary.data(), dictionary.size());
    }
}

template <>
Resources<Compression::Zstd>::~Resources()
{
    if(dictPtr != nullptr) {
        ZSTD_freeDDict(dictPtr);
    }
}

template <>
[[nodiscard]] std::vector<unsigned char> Resources<Compression::Zstd>::decompress(const std::span<const unsigned char> &entry)
{
    /* Read the content size from the frame header. For simplicity we require
     * that it is always present. By default, zstd will write the content size
     * in the header when it is known. If you can't guarantee that the frame
     * content size is always written into the header, either use streaming
     * decompression, or ZSTD_decompressBound().
     */
    unsigned long long const rSize = ZSTD_getFrameContentSize(entry.data(), entry.size());
    assert(rSize != ZSTD_CONTENTSIZE_ERROR); //, "%s: not compressed by zstd!", fname);
    assert(rSize != ZSTD_CONTENTSIZE_UNKNOWN); //, "%s: original size unknown!", fname);
    //data<unsigned char> rBuff(new unsigned char[rSize], rSize, true);
    std::vector<unsigned char> rBuff(rSize);

    /* Check that the dictionary ID matches.
     * If a non-zstd dictionary is used, then both will be zero.
     * By default zstd always writes the dictionary ID into the frame.
     * Zstd will check if there is a dictionary ID mismatch as well.
     */
    unsigned const expectedDictID = ZSTD_getDictID_fromDDict(dictPtr);
    unsigned const actualDictID = ZSTD_getDictID_fromFrame(entry.data(), entry.size());
    assert(actualDictID == expectedDictID); //"DictID mismatch: expected %u got %u",

    /* Decompress using the dictionary.
     * If you need to control the decompression parameters, then use the
     * advanced API: ZSTD_DCtx_setParameter(), ZSTD_DCtx_refDDict(), and
     * ZSTD_decompressDCtx().
     */
    ZSTD_DCtx *const dctx = ZSTD_createDCtx();
    assert(dctx != NULL); //, "ZSTD_createDCtx() failed!");
    size_t const dSize = ZSTD_decompress_usingDDict(dctx, rBuff.data(), rSize, entry.data(), entry.size(), dictPtr);
    /* When zstd knows the content size, it will error if it doesn't match. */
    assert(dSize == rSize); //, "Impossible because zstd will check this condition!");

    ZSTD_freeDCtx(dctx);
    return rBuff;
}