/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #ifndef CRYINCLUDE_CRYSYSTEM_HUFFMAN_H #define CRYINCLUDE_CRYSYSTEM_HUFFMAN_H #pragma once class HuffmanCoder { private: struct HuffmanTreeNode { uint32 count; uint32 savedCount; int child0; int child1; }; struct HuffmanSymbolCode { uint32 value; uint32 numBits; }; struct BitStreamBuilder { enum EModes { eM_WRITE, eM_READ }; union buf_ptr { uint8* ptr; const uint8* const_ptr; }; EModes m_mode; uint8 m_mask; buf_ptr m_pBufferStart; buf_ptr m_pBufferCursor; buf_ptr m_pBufferEnd; //Pointer to the last byte in the buffer BitStreamBuilder(uint8* pBufferStart, uint8* pBufferEnd) : m_mode(eM_WRITE) , m_mask(0x80) { m_pBufferStart.ptr = pBufferStart; m_pBufferCursor.ptr = pBufferStart; m_pBufferEnd.ptr = pBufferEnd; } BitStreamBuilder(const uint8* pBufferStart, const uint8* pBufferEnd) : m_mode(eM_READ) , m_mask(0x80) { m_pBufferStart.const_ptr = pBufferStart; m_pBufferCursor.const_ptr = pBufferStart; m_pBufferEnd.const_ptr = pBufferEnd; } void AddBits(uint32 value, uint32 numBits); void AddBits(uint8 value, uint32 numBits); //Returns 1 or 0 for valid values. Returns 2 if the buffer has run out or is the wrong type of builder. uint8 GetBit(); }; const static int MAX_SYMBOL_VALUE = (255); const static int MAX_NUM_SYMBOLS = (MAX_SYMBOL_VALUE + 1); const static int END_OF_STREAM = (MAX_NUM_SYMBOLS); const static int MAX_NUM_CODES = (MAX_NUM_SYMBOLS + 1); const static int MAX_NUM_NODES = (MAX_NUM_CODES * 2); const static int MAX_NODE = (MAX_NUM_NODES - 1); enum EHuffmanCoderState { eHCS_NEW, //Has been created, Init not called eHCS_OPEN, //Init has been called, tree not yet constructed. Can accept new data. eHCS_FINAL //Finalize has been called. Can no longer accept data, but can encode/decode. }; HuffmanTreeNode* m_TreeNodes; HuffmanSymbolCode* m_Codes; uint32* m_Counts; int m_RootNode; uint32 m_RefCount; EHuffmanCoderState m_State; public: HuffmanCoder() : m_TreeNodes(NULL) , m_Codes(NULL) , m_Counts(NULL) , m_State(eHCS_NEW) , m_RootNode(0) , m_RefCount(0) {} ~HuffmanCoder() { SAFE_DELETE_ARRAY(m_TreeNodes); SAFE_DELETE_ARRAY(m_Codes); SAFE_DELETE_ARRAY(m_Counts); } //A bit like an MD5 generator, has three phases. //Clears the existing data void Init(); //Adds the values of an array of chars to the counts void Update(const uint8* const pSource, const size_t numBytes); //Construct the coding tree using the counts void Finalize(); //We typically create a Huffman Coder per localized string table loaded. Since we can and do unload strings at runtime, it's useful to keep a ref count of each coder. inline void AddRef() { m_RefCount++; } inline void DecRef() { m_RefCount = m_RefCount > 0 ? m_RefCount - 1 : 0; } inline uint32 RefCount() { return m_RefCount; } void CompressInput(const uint8* const pInput, const size_t numBytes, uint8* const pOutput, size_t* const outputSize); size_t UncompressInput(const uint8* const pInput, const size_t numBytes, uint8* const pOutput, const size_t maxOutputSize); private: void ScaleCountsAndUpdateNodes(); int BuildTree(); void ConvertTreeToCode(const HuffmanTreeNode* const pNodes, HuffmanSymbolCode* const pCodes, const unsigned int value, const unsigned int numBits, const int node); }; #endif // CRYINCLUDE_CRYSYSTEM_HUFFMAN_H