mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00

Support the bindings to be more integrable with a system's coherent memory management. Pleaes note that thorvg now only allow the desinated memory allocators here: malloc -> tvg::malloc calloc -> tvg::calloc realloc -> tvg::realloc free -> tvg::free issue: https://github.com/thorvg/thorvg/issues/2652
311 lines
No EOL
11 KiB
C++
311 lines
No EOL
11 KiB
C++
/*
|
||
* Copyright (c) 2020 - 2025 the ThorVG project. All rights reserved.
|
||
|
||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
* of this software and associated documentation files (the "Software"), to deal
|
||
* in the Software without restriction, including without limitation the rights
|
||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
* copies of the Software, and to permit persons to whom the Software is
|
||
* furnished to do so, subject to the following conditions:
|
||
|
||
* The above copyright notice and this permission notice shall be included in all
|
||
* copies or substantial portions of the Software.
|
||
|
||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
* SOFTWARE.
|
||
*/
|
||
|
||
/*
|
||
* Lempel–Ziv–Welch (LZW) decoder by Guilherme R. Lampert(guilherme.ronaldo.lampert@gmail.com)
|
||
|
||
* This is the compression scheme used by the GIF image format and the Unix 'compress' tool.
|
||
* Main differences from this implementation is that End Of Input (EOI) and Clear Codes (CC)
|
||
* are not stored in the output and the max code length in bits is 12, vs 16 in compress.
|
||
*
|
||
* EOI is simply detected by the end of the data stream, while CC happens if the
|
||
* dictionary gets filled. Data is written/read from bit streams, which handle
|
||
* byte-alignment for us in a transparent way.
|
||
|
||
* The decoder relies on the hardcoded data layout produced by the encoder, since
|
||
* no additional reconstruction data is added to the output, so they must match.
|
||
* The nice thing about LZW is that we can reconstruct the dictionary directly from
|
||
* the stream of codes generated by the encoder, so this avoids storing additional
|
||
* headers in the bit stream.
|
||
|
||
* The output code length is variable. It starts with the minimum number of bits
|
||
* required to store the base byte-sized dictionary and automatically increases
|
||
* as the dictionary gets larger (it starts at 9-bits and grows to 10-bits when
|
||
* code 512 is added, then 11-bits when 1024 is added, and so on). If the dictionary
|
||
* is filled (4096 items for a 12-bits dictionary), the whole thing is cleared and
|
||
* the process starts over. This is the main reason why the encoder and the decoder
|
||
* must match perfectly, since the lengths of the codes will not be specified with
|
||
* the data itself.
|
||
|
||
* USEFUL LINKS:
|
||
* https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch
|
||
* http://rosettacode.org/wiki/LZW_compression
|
||
* http://www.cs.duke.edu/csed/curious/compression/lzw.html
|
||
* http://www.cs.cf.ac.uk/Dave/Multimedia/node214.html
|
||
* http://marknelson.us/1989/10/01/lzw-data-compression/
|
||
*/
|
||
#include "config.h"
|
||
#include "tvgCommon.h"
|
||
#include "tvgCompressor.h"
|
||
|
||
namespace tvg {
|
||
|
||
|
||
/************************************************************************/
|
||
/* LZW Implementation */
|
||
/************************************************************************/
|
||
|
||
//LZW Dictionary helper:
|
||
constexpr int Nil = -1;
|
||
constexpr int MaxDictBits = 12;
|
||
constexpr int StartBits = 9;
|
||
constexpr int FirstCode = (1 << (StartBits - 1)); // 256
|
||
constexpr int MaxDictEntries = (1 << MaxDictBits); // 4096
|
||
|
||
struct BitStreamReader
|
||
{
|
||
const uint8_t* stream; // Pointer to the external bit stream. Not owned by the reader.
|
||
const int sizeInBytes; // Size of the stream *in bytes*. Might include padding.
|
||
const int sizeInBits; // Size of the stream *in bits*, padding *not* include.
|
||
int currBytePos = 0; // Current byte being read in the stream.
|
||
int nextBitPos = 0; // Bit position within the current byte to access next. 0 to 7.
|
||
int numBitsRead = 0; // Total bits read from the stream so far. Never includes byte-rounding padding.
|
||
|
||
BitStreamReader(const uint8_t* bitStream, const int byteCount, const int bitCount) : stream(bitStream), sizeInBytes(byteCount), sizeInBits(bitCount)
|
||
{
|
||
}
|
||
|
||
bool readNextBit(int& bitOut)
|
||
{
|
||
if (numBitsRead >= sizeInBits) return false; //We are done.
|
||
|
||
const uint32_t mask = uint32_t(1) << nextBitPos;
|
||
bitOut = !!(stream[currBytePos] & mask);
|
||
++numBitsRead;
|
||
|
||
if (++nextBitPos == 8) {
|
||
nextBitPos = 0;
|
||
++currBytePos;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
uint64_t readBitsU64(const int bitCount)
|
||
{
|
||
uint64_t num = 0;
|
||
for (int b = 0; b < bitCount; ++b) {
|
||
int bit;
|
||
if (!readNextBit(bit)) break;
|
||
/* Based on a "Stanford bit-hack":
|
||
http://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching */
|
||
const uint64_t mask = uint64_t(1) << b;
|
||
num = (num & ~mask) | (-bit & mask);
|
||
}
|
||
return num;
|
||
}
|
||
|
||
bool isEndOfStream() const
|
||
{
|
||
return numBitsRead >= sizeInBits;
|
||
}
|
||
};
|
||
|
||
|
||
struct Dictionary
|
||
{
|
||
struct Entry
|
||
{
|
||
int code;
|
||
int value;
|
||
};
|
||
|
||
//Dictionary entries 0-255 are always reserved to the byte/ASCII range.
|
||
int size;
|
||
Entry entries[MaxDictEntries];
|
||
|
||
Dictionary()
|
||
{
|
||
/* First 256 dictionary entries are reserved to the byte/ASCII range.
|
||
Additional entries follow for the character sequences found in the input.
|
||
Up to 4096 - 256 (MaxDictEntries - FirstCode). */
|
||
size = FirstCode;
|
||
|
||
for (int i = 0; i < size; ++i) {
|
||
entries[i].code = Nil;
|
||
entries[i].value = i;
|
||
}
|
||
}
|
||
|
||
bool add(const int code, const int value)
|
||
{
|
||
if (size == MaxDictEntries) return false;
|
||
entries[size].code = code;
|
||
entries[size].value = value;
|
||
++size;
|
||
return true;
|
||
}
|
||
|
||
bool flush(int & codeBitsWidth)
|
||
{
|
||
if (size == (1 << codeBitsWidth)) {
|
||
++codeBitsWidth;
|
||
if (codeBitsWidth > MaxDictBits) {
|
||
//Clear the dictionary (except the first 256 byte entries).
|
||
codeBitsWidth = StartBits;
|
||
size = FirstCode;
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
};
|
||
|
||
|
||
static bool outputByte(int code, uint8_t*& output, int outputSizeBytes, int& bytesDecodedSoFar)
|
||
{
|
||
if (bytesDecodedSoFar >= outputSizeBytes) return false;
|
||
*output++ = static_cast<uint8_t>(code);
|
||
++bytesDecodedSoFar;
|
||
return true;
|
||
}
|
||
|
||
|
||
static bool outputSequence(const Dictionary& dict, int code, uint8_t*& output, int outputSizeBytes, int& bytesDecodedSoFar, int& firstByte)
|
||
{
|
||
/* A sequence is stored backwards, so we have to write
|
||
it to a temp then output the buffer in reverse. */
|
||
int i = 0;
|
||
uint8_t sequence[MaxDictEntries];
|
||
|
||
do {
|
||
sequence[i++] = dict.entries[code].value;
|
||
code = dict.entries[code].code;
|
||
} while (code >= 0);
|
||
|
||
firstByte = sequence[--i];
|
||
|
||
for (; i >= 0; --i) {
|
||
if (!outputByte(sequence[i], output, outputSizeBytes, bytesDecodedSoFar)) return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
|
||
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes)
|
||
{
|
||
int code = Nil;
|
||
int prevCode = Nil;
|
||
int firstByte = 0;
|
||
int bytesDecoded = 0;
|
||
int codeBitsWidth = StartBits;
|
||
auto uncompressed = tvg::malloc<uint8_t*>(sizeof(uint8_t) * uncompressedSizeBytes);
|
||
auto ptr = uncompressed;
|
||
|
||
/* We'll reconstruct the dictionary based on the bit stream codes.
|
||
Unlike Huffman encoding, we don't store the dictionary as a prefix to the data. */
|
||
Dictionary dictionary;
|
||
BitStreamReader bitStream(compressed, compressedSizeBytes, compressedSizeBits);
|
||
|
||
/* We check to avoid an overflow of the user buffer.
|
||
If the buffer is smaller than the decompressed size, we break the loop and return the current decompression count. */
|
||
while (!bitStream.isEndOfStream()) {
|
||
code = static_cast<int>(bitStream.readBitsU64(codeBitsWidth));
|
||
|
||
if (prevCode == Nil) {
|
||
if (!outputByte(code, ptr, uncompressedSizeBytes, bytesDecoded)) break;
|
||
firstByte = code;
|
||
prevCode = code;
|
||
continue;
|
||
}
|
||
if (code >= dictionary.size) {
|
||
if (!outputSequence(dictionary, prevCode, ptr, uncompressedSizeBytes, bytesDecoded, firstByte)) break;
|
||
if (!outputByte(firstByte, ptr, uncompressedSizeBytes, bytesDecoded)) break;
|
||
} else if (!outputSequence(dictionary, code, ptr, uncompressedSizeBytes, bytesDecoded, firstByte)) break;
|
||
|
||
dictionary.add(prevCode, firstByte);
|
||
if (dictionary.flush(codeBitsWidth)) prevCode = Nil;
|
||
else prevCode = code;
|
||
}
|
||
|
||
return uncompressed;
|
||
}
|
||
|
||
|
||
/************************************************************************/
|
||
/* B64 Implementation */
|
||
/************************************************************************/
|
||
|
||
size_t b64Decode(const char* encoded, const size_t len, char** decoded)
|
||
{
|
||
static constexpr const char B64_INDEX[256] =
|
||
{
|
||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57,
|
||
58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 63, 0, 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
|
||
};
|
||
|
||
|
||
if (!decoded || !encoded || len == 0) return 0;
|
||
|
||
auto reserved = 3 * (1 + (len >> 2)) + 1;
|
||
auto output = tvg::malloc<char*>(reserved * sizeof(char));
|
||
if (!output) return 0;
|
||
output[reserved - 1] = '\0';
|
||
|
||
size_t idx = 0;
|
||
|
||
while (*encoded && *(encoded + 1)) {
|
||
if (*encoded <= 0x20) {
|
||
++encoded;
|
||
continue;
|
||
}
|
||
|
||
auto value1 = B64_INDEX[(size_t)encoded[0]];
|
||
auto value2 = B64_INDEX[(size_t)encoded[1]];
|
||
output[idx++] = (value1 << 2) + ((value2 & 0x30) >> 4);
|
||
|
||
if (!encoded[2] || encoded[3] < 0 || encoded[2] == '=' || encoded[2] == '.') break;
|
||
auto value3 = B64_INDEX[(size_t)encoded[2]];
|
||
output[idx++] = ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
|
||
|
||
if (!encoded[3] || encoded[3] < 0 || encoded[3] == '=' || encoded[3] == '.') break;
|
||
auto value4 = B64_INDEX[(size_t)encoded[3]];
|
||
output[idx++] = ((value3 & 0x03) << 6) + value4;
|
||
encoded += 4;
|
||
}
|
||
*decoded = output;
|
||
return idx;
|
||
}
|
||
|
||
|
||
/************************************************************************/
|
||
/* DJB2 Implementation */
|
||
/************************************************************************/
|
||
|
||
unsigned long djb2Encode(const char* str)
|
||
{
|
||
if (!str) return 0;
|
||
|
||
unsigned long hash = 5381;
|
||
int c;
|
||
|
||
while ((c = *str++)) {
|
||
hash = ((hash << 5) + hash) + c; // hash * 33 + c
|
||
}
|
||
return hash;
|
||
}
|
||
|
||
} |