aboutsummaryrefslogtreecommitdiff
path: root/lib/embed.cpp
blob: c3b028689b06028a761a1e5e63fef780ce907521 (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
63
64
65
66
67
68
69
70
/*
 * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under both the BSD-style license (found in the
 * LICENSE file in the root directory of this source tree) and the GPLv2 (found
 * in the COPYING file in the root directory of this source tree).
 * You may select, at your option, one of the above-listed licenses.
 */

#include "embed.h"
#include <zstd.h>      // presumes zstd library is installed
#include <cassert>

using namespace embed;

ZSTD_DDict* dictPtr = nullptr;

Resources::Resources(const ResourceData &info)
    : m_info(info)
{
    if(!info.dictionary.empty()) {
        dictPtr = ZSTD_createDDict(info.dictionary.data(), info.dictionary.size());
    }
}

Resources::~Resources()
{
    if(dictPtr != nullptr) {
        ZSTD_freeDDict(dictPtr);
    }
}

[[nodiscard]] std::span<unsigned char> Resources::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);
    auto* rBuff = new unsigned char[(size_t) 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, 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 std::span(rBuff, rSize);
}