Clean project structure.
This commit is contained in:
parent
78a4fa99ff
commit
947bf937fd
496 changed files with 206 additions and 137 deletions
6
src/base/CMakeLists.txt
Normal file
6
src/base/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
add_subdirectory(compiler)
|
||||
add_subdirectory(compression)
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(database)
|
||||
add_subdirectory(geometry)
|
||||
add_subdirectory(network)
|
28
src/base/compiler/CMakeLists.txt
Normal file
28
src/base/compiler/CMakeLists.txt
Normal file
|
@ -0,0 +1,28 @@
|
|||
set(MODULE_NAME compiler)
|
||||
|
||||
list(APPEND HEADERS
|
||||
Lexer.h
|
||||
template_engine/TemplatingEngine.h
|
||||
template_engine/TemplateFile.h
|
||||
template_engine/TemplateNode.h
|
||||
template_engine/TemplateElements.h
|
||||
)
|
||||
|
||||
list(APPEND SOURCES
|
||||
Lexer.cpp
|
||||
template_engine/TemplatingEngine.cpp
|
||||
template_engine/TemplateFile.cpp
|
||||
template_engine/TemplateNode.cpp
|
||||
template_engine/TemplateElements.cpp
|
||||
)
|
||||
|
||||
add_library(${MODULE_NAME} SHARED ${HEADERS} ${SOURCES})
|
||||
|
||||
target_include_directories(${MODULE_NAME} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/template_engine
|
||||
)
|
||||
target_link_libraries( ${MODULE_NAME} PUBLIC core)
|
||||
|
||||
set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src/base)
|
72
src/base/compiler/Lexer.cpp
Normal file
72
src/base/compiler/Lexer.cpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
#include "Lexer.h"
|
||||
|
||||
bool Lexer::matchPattern(const std::string& pattern, const std::string& checkString, char delimiter, std::vector<std::string>& hitSequence)
|
||||
{
|
||||
if (checkString.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pattern.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found_pattern = true;
|
||||
unsigned check_idx = 0;
|
||||
unsigned pattern_idx = 0;
|
||||
|
||||
std::vector<std::string> hits;
|
||||
std::string working_hit;
|
||||
while(check_idx < checkString.size())
|
||||
{
|
||||
if (pattern_idx == pattern.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto check_char = checkString[check_idx];
|
||||
auto pattern_char = pattern[pattern_idx];
|
||||
if (pattern_char == delimiter)
|
||||
{
|
||||
if (pattern_idx + 1 < pattern.size())
|
||||
{
|
||||
if (check_char == pattern[pattern_idx + 1])
|
||||
{
|
||||
hits.push_back(working_hit);
|
||||
working_hit.clear();
|
||||
pattern_idx++;
|
||||
}
|
||||
else
|
||||
{
|
||||
working_hit+=check_char;
|
||||
check_idx++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
working_hit+=check_char;
|
||||
check_idx++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (check_char == pattern_char)
|
||||
{
|
||||
check_idx++;
|
||||
pattern_idx++;
|
||||
}
|
||||
else
|
||||
{
|
||||
found_pattern = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_pattern)
|
||||
{
|
||||
hitSequence = hits;
|
||||
}
|
||||
return found_pattern;
|
||||
}
|
11
src/base/compiler/Lexer.h
Normal file
11
src/base/compiler/Lexer.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Lexer
|
||||
{
|
||||
public:
|
||||
// e.g. Pattern [@](@) returns <source, tag> for input: [source](tag) and delimiter @
|
||||
static bool matchPattern(const std::string& pattern, const std::string& checkString, char delimiter, std::vector<std::string>& hitSequence);
|
||||
};
|
172
src/base/compiler/template_engine/TemplateElements.cpp
Normal file
172
src/base/compiler/template_engine/TemplateElements.cpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
#include "TemplateElements.h"
|
||||
|
||||
#include "StringUtils.h"
|
||||
#include "TemplateSubstitutionContext.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
TemplateExtends::TemplateExtends(TemplateNode* parent, const std::string& path)
|
||||
: TemplateNode(parent)
|
||||
{
|
||||
mPath = StringUtils::stripQuotes(path);
|
||||
};
|
||||
|
||||
std::string TemplateExtends::getRawContent() const
|
||||
{
|
||||
return "TemplateExtends: " + mPath;
|
||||
}
|
||||
|
||||
std::string TemplateExtends::getPath() const
|
||||
{
|
||||
return mPath;
|
||||
}
|
||||
|
||||
TemplateBlock::TemplateBlock(TemplateNode* parent, const std::string& name)
|
||||
: TemplateNode(parent),
|
||||
mName(name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TemplateBlock::addLine(const std::string& line)
|
||||
{
|
||||
mBody.push_back(line);
|
||||
}
|
||||
|
||||
std::string TemplateBlock::getRawContent() const
|
||||
{
|
||||
std::string content = "TemplateBlock: " + mName + " - Start \n";
|
||||
content += TemplateNode::getRawContent();
|
||||
content += "TemplateBlock: " + mName + " - End";
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string TemplateBlock::getIdentifier() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
std::string TemplateBlock::renderAsParent(TemplateSubstitutionContext* substitutions, TemplateBlock* base)
|
||||
{
|
||||
std::string content;
|
||||
for (auto& child : mChildren)
|
||||
{
|
||||
if (child->getType() == Type::EXPRESSION)
|
||||
{
|
||||
auto expression = dynamic_cast<TemplateExpression*>(child.get());
|
||||
if (expression->getContent() == "super()")
|
||||
{
|
||||
content += base->render(substitutions, nullptr);
|
||||
}
|
||||
}
|
||||
else if(child->getType() == Type::TEXT_BODY)
|
||||
{
|
||||
content += child->render(substitutions, nullptr);
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string TemplateBlock::renderAsLeaf(TemplateSubstitutionContext* substitutions)
|
||||
{
|
||||
std::string content;
|
||||
for (auto& child : mChildren)
|
||||
{
|
||||
if(child->getType() == Type::TEXT_BODY)
|
||||
{
|
||||
content += child->render(substitutions, nullptr);
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string TemplateBlock::render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext)
|
||||
{
|
||||
std::string content;
|
||||
if (parentContext)
|
||||
{
|
||||
if (auto parent_node = parentContext->getFirstChildShallow(Type::BLOCK, getIdentifier()))
|
||||
{
|
||||
content = dynamic_cast<TemplateBlock*>(parent_node)->renderAsParent(substitutions, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
content= renderAsLeaf(substitutions);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
content= renderAsLeaf(substitutions);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
TemplateExpression::TemplateExpression(TemplateNode* parent, const std::string& content)
|
||||
: TemplateNode(parent),
|
||||
mContent(content)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
const std::string& TemplateExpression::getContent() const
|
||||
{
|
||||
return mContent;
|
||||
}
|
||||
|
||||
std::string TemplateExpression::render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext)
|
||||
{
|
||||
if (substitutions && substitutions->hasSubstitution(mContent))
|
||||
{
|
||||
return substitutions->getSubstitution(mContent);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string TemplateExpression::getRawContent() const
|
||||
{
|
||||
return "TemplateExpression: " + mContent;
|
||||
}
|
||||
|
||||
TemplateTextBody::TemplateTextBody(TemplateNode* parent)
|
||||
: TemplateNode(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TemplateTextBody::addLine(const std::string& content)
|
||||
{
|
||||
mContent.push_back(content);
|
||||
}
|
||||
|
||||
bool TemplateTextBody::hasContent() const
|
||||
{
|
||||
return !mContent.empty();
|
||||
}
|
||||
|
||||
std::string TemplateTextBody::render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext)
|
||||
{
|
||||
std::string content;
|
||||
for(unsigned idx=0; idx<mContent.size(); idx++)
|
||||
{
|
||||
content += mContent[idx];
|
||||
if(idx != mContent.size() - 1)
|
||||
{
|
||||
content += '\n';
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string TemplateTextBody::getRawContent() const
|
||||
{
|
||||
std::string content;
|
||||
for(unsigned idx=0; idx<mContent.size(); idx++)
|
||||
{
|
||||
content += "Template Body L-" + std::to_string(idx) + ": " + mContent[idx];
|
||||
if(idx != mContent.size() - 1)
|
||||
{
|
||||
content += '\n';
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
96
src/base/compiler/template_engine/TemplateElements.h
Normal file
96
src/base/compiler/template_engine/TemplateElements.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
#pragma once
|
||||
|
||||
#include "TemplateNode.h"
|
||||
|
||||
class TemplateExtends : public TemplateNode
|
||||
{
|
||||
public:
|
||||
TemplateExtends(TemplateNode* parent, const std::string& path);
|
||||
|
||||
virtual ~TemplateExtends() = default;
|
||||
|
||||
std::string getRawContent() const override;
|
||||
std::string getPath() const;
|
||||
|
||||
Type getType() const override
|
||||
{
|
||||
return Type::EXTENDS;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string mPath;
|
||||
};
|
||||
|
||||
class TemplateBlock : public TemplateNode
|
||||
{
|
||||
public:
|
||||
TemplateBlock(TemplateNode* parent, const std::string& name);
|
||||
|
||||
virtual ~TemplateBlock() = default;
|
||||
|
||||
void addLine(const std::string& line);
|
||||
|
||||
std::string getRawContent() const override;
|
||||
|
||||
std::string getIdentifier() const override;
|
||||
|
||||
Type getType() const override
|
||||
{
|
||||
return Type::BLOCK;
|
||||
}
|
||||
|
||||
std::string renderAsParent(TemplateSubstitutionContext* substitutions, TemplateBlock* base);
|
||||
|
||||
std::string renderAsLeaf(TemplateSubstitutionContext* substitutions);
|
||||
|
||||
std::string render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext) override;
|
||||
|
||||
private:
|
||||
std::string mName;
|
||||
std::vector<std::string> mBody;
|
||||
};
|
||||
|
||||
class TemplateExpression : public TemplateNode
|
||||
{
|
||||
public:
|
||||
TemplateExpression(TemplateNode* parent, const std::string& content);
|
||||
|
||||
virtual ~TemplateExpression() = default;
|
||||
|
||||
std::string getRawContent() const override;
|
||||
|
||||
const std::string& getContent() const;
|
||||
|
||||
Type getType() const override
|
||||
{
|
||||
return Type::EXPRESSION;
|
||||
}
|
||||
|
||||
std::string render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext) override;
|
||||
|
||||
private:
|
||||
std::string mContent;
|
||||
};
|
||||
|
||||
class TemplateTextBody : public TemplateNode
|
||||
{
|
||||
public:
|
||||
TemplateTextBody(TemplateNode* parent);
|
||||
|
||||
virtual ~TemplateTextBody() = default;
|
||||
|
||||
void addLine(const std::string& content);
|
||||
|
||||
std::string getRawContent() const override;
|
||||
|
||||
bool hasContent() const;
|
||||
|
||||
std::string render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext) override;
|
||||
|
||||
Type getType() const override
|
||||
{
|
||||
return Type::TEXT_BODY;
|
||||
}
|
||||
private:
|
||||
std::vector<std::string> mContent;
|
||||
};
|
207
src/base/compiler/template_engine/TemplateFile.cpp
Normal file
207
src/base/compiler/template_engine/TemplateFile.cpp
Normal file
|
@ -0,0 +1,207 @@
|
|||
#include "TemplateFile.h"
|
||||
|
||||
#include "TemplateElements.h"
|
||||
#include "TemplateNode.h"
|
||||
|
||||
#include "Lexer.h"
|
||||
#include "File.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
TemplateFile::TemplateFile(const Path& path)
|
||||
: mPath(path),
|
||||
mRootNode(std::make_unique<TemplateNode>(nullptr))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TemplateFile::~TemplateFile()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string TemplateFile::getName() const
|
||||
{
|
||||
return mPath.stem().string();
|
||||
}
|
||||
|
||||
bool TemplateFile::hasLoaded() const
|
||||
{
|
||||
return mHasLoaded;
|
||||
}
|
||||
|
||||
TemplateNode* TemplateFile::getContent() const
|
||||
{
|
||||
return mRootNode.get();
|
||||
}
|
||||
|
||||
void TemplateFile::loadContent()
|
||||
{
|
||||
if (mHasLoaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mRawContent = File(mPath).readLines();
|
||||
|
||||
mWorkingNode = mRootNode.get();
|
||||
mWorkingTextSpan = std::make_unique<TemplateTextBody>(mWorkingNode);
|
||||
for (const auto& line : mRawContent)
|
||||
{
|
||||
processLine(line);
|
||||
}
|
||||
onTextSpanFinished();
|
||||
|
||||
mHasLoaded = true;
|
||||
}
|
||||
|
||||
std::string TemplateFile::dumpContent()
|
||||
{
|
||||
return mRootNode->getRawContent();
|
||||
}
|
||||
|
||||
void TemplateFile::onTextSpanFinished()
|
||||
{
|
||||
if (!mWorkingLineContent.empty())
|
||||
{
|
||||
mWorkingTextSpan->addLine(mWorkingLineContent);
|
||||
}
|
||||
|
||||
if (mWorkingTextSpan->hasContent())
|
||||
{
|
||||
mWorkingNode->addChild(std::move(mWorkingTextSpan));
|
||||
mWorkingTextSpan = std::make_unique<TemplateTextBody>(mWorkingNode);
|
||||
}
|
||||
mWorkingLineContent.clear();
|
||||
}
|
||||
|
||||
std::size_t TemplateFile::checkForStatement(const std::string& lineSection)
|
||||
{
|
||||
if (lineSection.empty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> hits;
|
||||
std::size_t hit_size{0};
|
||||
if (Lexer::matchPattern("{%@%}", lineSection, '@', hits))
|
||||
{
|
||||
if (hits.size() == 1)
|
||||
{
|
||||
auto content = hits[0];
|
||||
|
||||
onTextSpanFinished();
|
||||
|
||||
onFoundStatement(content);
|
||||
hit_size = 4 + content.size();
|
||||
}
|
||||
}
|
||||
return hit_size;
|
||||
}
|
||||
|
||||
std::size_t TemplateFile::checkForExpression(const std::string& lineSection)
|
||||
{
|
||||
if (lineSection.empty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> hits;
|
||||
std::size_t hit_size{0};
|
||||
if (Lexer::matchPattern("{{@}}", lineSection, '@', hits))
|
||||
{
|
||||
if (hits.size() == 1)
|
||||
{
|
||||
auto content = hits[0];
|
||||
|
||||
onTextSpanFinished();
|
||||
|
||||
onFoundExpression(content);
|
||||
hit_size = 4 + content.size();
|
||||
}
|
||||
}
|
||||
return hit_size;
|
||||
}
|
||||
|
||||
void TemplateFile::processLine(const std::string& line)
|
||||
{
|
||||
std::size_t line_position = 0;
|
||||
mWorkingLineContent.clear();
|
||||
while(line_position < line.size())
|
||||
{
|
||||
const auto remaining = line.substr(line_position, line.size() - line_position);
|
||||
if(auto length = checkForStatement(remaining))
|
||||
{
|
||||
line_position += length;
|
||||
}
|
||||
else if(auto length = checkForExpression(remaining))
|
||||
{
|
||||
line_position += length;
|
||||
}
|
||||
else
|
||||
{
|
||||
mWorkingLineContent += line[line_position];
|
||||
line_position++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mWorkingLineContent.empty())
|
||||
{
|
||||
mWorkingTextSpan->addLine(mWorkingLineContent);
|
||||
mWorkingLineContent.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void TemplateFile::onFoundStatement(const std::string& statement_string)
|
||||
{
|
||||
const auto statement_elements = StringUtils::split(statement_string);
|
||||
if (statement_elements.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto statement_type = statement_elements[0];
|
||||
if (statement_type == "block" && statement_elements.size() == 2)
|
||||
{
|
||||
onFoundBlock(statement_elements);
|
||||
}
|
||||
else if (statement_type == "endblock")
|
||||
{
|
||||
onFoundEndBlock(statement_elements);
|
||||
}
|
||||
else if (statement_type == "extends")
|
||||
{
|
||||
onFoundExtends(statement_elements);
|
||||
}
|
||||
}
|
||||
|
||||
void TemplateFile::onFoundExpression(const std::string& expression_string)
|
||||
{
|
||||
const auto stripped = StringUtils::stripSurroundingWhitepsace(expression_string);
|
||||
auto expression = std::make_unique<TemplateExpression>(mWorkingNode, stripped);
|
||||
mWorkingNode->addChild(std::move(expression));
|
||||
}
|
||||
|
||||
void TemplateFile::onFoundBlock(const std::vector<std::string> args)
|
||||
{
|
||||
if (args.size() != 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto block = std::make_unique<TemplateBlock>(mWorkingNode, args[1]);
|
||||
auto temp = block.get();
|
||||
mWorkingNode->addChild(std::move(block));
|
||||
mWorkingNode = temp;
|
||||
}
|
||||
|
||||
void TemplateFile::onFoundEndBlock(const std::vector<std::string> args)
|
||||
{
|
||||
mWorkingNode = mWorkingNode->getParent();
|
||||
}
|
||||
|
||||
void TemplateFile::onFoundExtends(const std::vector<std::string> args)
|
||||
{
|
||||
if (args.size() != 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto extends = std::make_unique<TemplateExtends>(mWorkingNode, args[1]);
|
||||
mWorkingNode->addChild(std::move(extends));
|
||||
}
|
58
src/base/compiler/template_engine/TemplateFile.h
Normal file
58
src/base/compiler/template_engine/TemplateFile.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
class TemplateNode;
|
||||
class TemplateTextBody;
|
||||
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
class TemplateFile
|
||||
{
|
||||
public:
|
||||
TemplateFile(const Path& path);
|
||||
|
||||
~TemplateFile();
|
||||
|
||||
std::string dumpContent();
|
||||
|
||||
std::string getName() const;
|
||||
|
||||
TemplateNode* getContent() const;
|
||||
|
||||
bool hasLoaded() const;
|
||||
|
||||
void loadContent();
|
||||
|
||||
private:
|
||||
std::size_t checkForStatement(const std::string& lineSection);
|
||||
|
||||
std::size_t checkForExpression(const std::string& lineSection);
|
||||
|
||||
void onTextSpanFinished();
|
||||
|
||||
void onFoundStatement(const std::string& statement_string);
|
||||
|
||||
void onFoundExpression(const std::string& expression_string);
|
||||
|
||||
void onFoundBlock(const std::vector<std::string> args);
|
||||
|
||||
void onFoundEndBlock(const std::vector<std::string> args);
|
||||
|
||||
void onFoundExtends(const std::vector<std::string> args);
|
||||
|
||||
void processLine(const std::string& line);
|
||||
|
||||
Path mPath;
|
||||
std::string mParentName;
|
||||
std::vector<std::string> mRawContent;
|
||||
bool mHasLoaded{false};
|
||||
|
||||
std::unique_ptr<TemplateNode> mRootNode;
|
||||
TemplateNode* mWorkingNode{ nullptr };
|
||||
|
||||
std::string mWorkingLineContent;
|
||||
std::unique_ptr<TemplateTextBody> mWorkingTextSpan;
|
||||
};
|
101
src/base/compiler/template_engine/TemplateNode.cpp
Normal file
101
src/base/compiler/template_engine/TemplateNode.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
#include "TemplateNode.h"
|
||||
|
||||
#include "TemplateSubstitutionContext.h"
|
||||
|
||||
TemplateNode::TemplateNode(TemplateNode* parent)
|
||||
: mParent(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TemplateNode* TemplateNode::getParent() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
void TemplateNode::addChild(std::unique_ptr<TemplateNode> child)
|
||||
{
|
||||
mChildren.push_back(std::move(child));
|
||||
}
|
||||
|
||||
std::size_t TemplateNode::getNumChildren() const
|
||||
{
|
||||
return mChildren.size();
|
||||
}
|
||||
|
||||
TemplateNode* TemplateNode::getFirstChildShallow(Type type, const std::string& identifier) const
|
||||
{
|
||||
for (const auto& child : mChildren)
|
||||
{
|
||||
if (child->getType() == type)
|
||||
{
|
||||
if (!identifier.empty())
|
||||
{
|
||||
if (child->getIdentifier() == identifier)
|
||||
{
|
||||
return child.get();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return child.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TemplateNode::Type TemplateNode::getType() const
|
||||
{
|
||||
return Type::NONE;
|
||||
}
|
||||
|
||||
std::string TemplateNode::getIdentifier() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
TemplateNode* TemplateNode::getChild(std::size_t index) const
|
||||
{
|
||||
return mChildren[index].get();
|
||||
}
|
||||
|
||||
std::string TemplateNode::getRawContent() const
|
||||
{
|
||||
std::string content;
|
||||
for (const auto& child : mChildren)
|
||||
{
|
||||
content += child->getRawContent() + "\n";
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
void TemplateNode::setExtensionParent(TemplateNode* parent)
|
||||
{
|
||||
mExtensionParent = parent;
|
||||
}
|
||||
|
||||
void TemplateNode::setExtensionBase(TemplateNode* base)
|
||||
{
|
||||
mExtensionBase = base;
|
||||
}
|
||||
|
||||
std::string TemplateNode::render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext)
|
||||
{
|
||||
if (mExtensionBase)
|
||||
{
|
||||
return mExtensionBase->render(substitutions, this);
|
||||
}
|
||||
|
||||
if (!parentContext && mExtensionParent)
|
||||
{
|
||||
parentContext = mExtensionParent;
|
||||
}
|
||||
|
||||
std::string content;
|
||||
for (size_t idx = 0; idx < mChildren.size(); idx++)
|
||||
{
|
||||
content += mChildren[idx]->render(substitutions, parentContext);
|
||||
}
|
||||
return content;
|
||||
}
|
54
src/base/compiler/template_engine/TemplateNode.h
Normal file
54
src/base/compiler/template_engine/TemplateNode.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class TemplateSubstitutionContext;
|
||||
|
||||
class TemplateNode
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
NONE,
|
||||
BLOCK,
|
||||
EXPRESSION,
|
||||
EXTENDS,
|
||||
TEXT_BODY
|
||||
};
|
||||
|
||||
TemplateNode(TemplateNode* parent);
|
||||
|
||||
virtual ~TemplateNode() = default;
|
||||
|
||||
virtual void addChild(std::unique_ptr<TemplateNode> child);
|
||||
|
||||
TemplateNode* getChild(std::size_t index) const;
|
||||
|
||||
TemplateNode* getFirstChildShallow(Type type, const std::string& identifier = {}) const;
|
||||
|
||||
virtual std::string getIdentifier() const;
|
||||
|
||||
std::size_t getNumChildren() const;
|
||||
|
||||
TemplateNode* getParent() const;
|
||||
|
||||
virtual std::string getRawContent() const;
|
||||
|
||||
virtual Type getType() const;
|
||||
|
||||
virtual std::string render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext = nullptr);
|
||||
|
||||
void setExtensionParent(TemplateNode* parent);
|
||||
|
||||
void setExtensionBase(TemplateNode* base);
|
||||
|
||||
protected:
|
||||
std::vector<std::unique_ptr<TemplateNode> > mChildren;
|
||||
TemplateNode* mParent{ nullptr };
|
||||
TemplateNode* mExtensionParent{ nullptr };
|
||||
TemplateNode* mExtensionBase{ nullptr };
|
||||
};
|
||||
|
||||
using TemplateNodePtr = std::unique_ptr<TemplateNode>;
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class TemplateSubstitutionContext
|
||||
{
|
||||
public:
|
||||
|
||||
void addSubstitution(const std::string& key, const std::string& value)
|
||||
{
|
||||
mSubstitutions[key] = value;
|
||||
}
|
||||
|
||||
bool hasSubstitution(const std::string& key) const
|
||||
{
|
||||
return mSubstitutions.find(key) != mSubstitutions.end();
|
||||
}
|
||||
|
||||
std::string getSubstitution(const std::string& key) const
|
||||
{
|
||||
auto iter = mSubstitutions.find(key);
|
||||
if(iter != mSubstitutions.end())
|
||||
{
|
||||
return iter->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> mSubstitutions;
|
||||
};
|
80
src/base/compiler/template_engine/TemplatingEngine.cpp
Normal file
80
src/base/compiler/template_engine/TemplatingEngine.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
#include "TemplatingEngine.h"
|
||||
|
||||
#include "Directory.h"
|
||||
#include "FileLogger.h"
|
||||
|
||||
#include "TemplateElements.h"
|
||||
#include "TemplateSubstitutionContext.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
TemplatingEngine::TemplatingEngine(const Path& workingDirectory)
|
||||
: mWorkingDirectory(workingDirectory)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string TemplatingEngine::renderTemplate(const std::string& name, TemplateSubstitutionContext* substitutionContext)
|
||||
{
|
||||
if (mTemplateFiles.empty())
|
||||
{
|
||||
loadTemplateFiles();
|
||||
}
|
||||
|
||||
if (auto file = getTemplateFile(name))
|
||||
{
|
||||
if (!file->hasLoaded())
|
||||
{
|
||||
file->loadContent();
|
||||
//std::cout << file->dumpContent();
|
||||
|
||||
processTemplate(file, nullptr);
|
||||
}
|
||||
return file->getContent()->render(substitutionContext, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void TemplatingEngine::loadTemplateFiles()
|
||||
{
|
||||
const auto files = Directory::getFilesWithExtension(mWorkingDirectory, mTemplateExtension);
|
||||
for (const auto& path : files)
|
||||
{
|
||||
mTemplateFiles[path.stem().string()] = std::make_unique<TemplateFile>(path);
|
||||
}
|
||||
MLOG_INFO("Found: " << mTemplateFiles.size() << " templates in " << mWorkingDirectory);
|
||||
}
|
||||
|
||||
TemplateFile* TemplatingEngine::getTemplateFile(const Path& path)
|
||||
{
|
||||
return getTemplateFile(path.stem().string());
|
||||
}
|
||||
|
||||
TemplateFile* TemplatingEngine::getTemplateFile(const std::string& name)
|
||||
{
|
||||
return mTemplateFiles[name].get();
|
||||
}
|
||||
|
||||
void TemplatingEngine::processTemplate(TemplateFile* file, TemplateNode* parent)
|
||||
{
|
||||
auto content = file->getContent();
|
||||
if (parent)
|
||||
{
|
||||
content->setExtensionParent(parent);
|
||||
parent->setExtensionBase(content);
|
||||
}
|
||||
|
||||
if (auto extension_node = dynamic_cast<TemplateExtends*>(content->getFirstChildShallow(TemplateNode::Type::EXTENDS)))
|
||||
{
|
||||
if (auto extension_template = getTemplateFile(Path(extension_node->getPath())))
|
||||
{
|
||||
extension_template->loadContent();
|
||||
//std::cout << extension_template->dumpContent();
|
||||
|
||||
processTemplate(extension_template, content);
|
||||
}
|
||||
}
|
||||
}
|
30
src/base/compiler/template_engine/TemplatingEngine.h
Normal file
30
src/base/compiler/template_engine/TemplatingEngine.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "File.h"
|
||||
#include "TemplateFile.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
class TemplateSubstitutionContext;
|
||||
|
||||
class TemplatingEngine
|
||||
{
|
||||
public:
|
||||
TemplatingEngine(const Path& workingDirectory);
|
||||
|
||||
std::string renderTemplate(const std::string& name, TemplateSubstitutionContext* substitutionContext);
|
||||
|
||||
private:
|
||||
TemplateFile* getTemplateFile(const std::string& name);
|
||||
TemplateFile* getTemplateFile(const Path& path);
|
||||
|
||||
void loadTemplateFiles();
|
||||
void processTemplate(TemplateFile* file, TemplateNode* parent = nullptr);
|
||||
|
||||
std::unordered_map<std::string, std::unique_ptr<TemplateFile> > mTemplateFiles;
|
||||
Path mWorkingDirectory;
|
||||
std::string mTemplateExtension{ ".html" };
|
||||
};
|
34
src/base/compression/AbstractEncoder.h
Normal file
34
src/base/compression/AbstractEncoder.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include "AbstractChecksumCalculator.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class BitStream;
|
||||
|
||||
class AbstractEncoder
|
||||
{
|
||||
public:
|
||||
AbstractEncoder(BitStream* inputStream, BitStream* outputStream)
|
||||
: mInputStream(inputStream),
|
||||
mOutputStream(outputStream)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~AbstractEncoder() = default;
|
||||
|
||||
void addChecksumCalculator(AbstractChecksumCalculator* calculator)
|
||||
{
|
||||
mChecksumCalculators.push_back(calculator);
|
||||
}
|
||||
|
||||
virtual bool encode() = 0;
|
||||
virtual bool decode() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
std::vector<AbstractChecksumCalculator*> mChecksumCalculators;
|
||||
BitStream* mInputStream{nullptr};
|
||||
BitStream* mOutputStream{nullptr};
|
||||
};
|
29
src/base/compression/Adler32Checksum.h
Normal file
29
src/base/compression/Adler32Checksum.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "AbstractChecksumCalculator.h"
|
||||
|
||||
class Adler32Checksum : public AbstractChecksumCalculator
|
||||
{
|
||||
public:
|
||||
void addValue(unsigned char val) override
|
||||
{
|
||||
mSum1 = (mSum1 + val) % MOD_ADLER32;
|
||||
mSum2 = (mSum2 + mSum1) % MOD_ADLER32;
|
||||
}
|
||||
|
||||
uint32_t getChecksum() const override
|
||||
{
|
||||
return (mSum2 << 16) | mSum1;
|
||||
}
|
||||
|
||||
void reset() override
|
||||
{
|
||||
mSum1 = 1;
|
||||
mSum2 = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr unsigned MOD_ADLER32{65536};
|
||||
uint32_t mSum1{1};
|
||||
uint32_t mSum2{0};
|
||||
};
|
27
src/base/compression/CMakeLists.txt
Normal file
27
src/base/compression/CMakeLists.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
set(MODULE_NAME compression)
|
||||
|
||||
list(APPEND SOURCES
|
||||
StreamCompressor.cpp
|
||||
huffman/HuffmanEncoder.cpp
|
||||
huffman/HuffmanStream.cpp
|
||||
huffman/HuffmanCodeLengthTable.cpp
|
||||
huffman/HuffmanTree.cpp
|
||||
RunLengthEncoder.cpp
|
||||
ZlibEncoder.cpp
|
||||
deflate/DeflateEncoder.cpp
|
||||
deflate/DeflateBlock.cpp
|
||||
Lz77Encoder.cpp
|
||||
CyclicRedundancyChecker.cpp
|
||||
)
|
||||
|
||||
add_library(${MODULE_NAME} SHARED ${SOURCES})
|
||||
|
||||
target_include_directories(${MODULE_NAME} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/deflate
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/huffman
|
||||
)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} PUBLIC core)
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src/base)
|
||||
set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
|
43
src/base/compression/CyclicRedundancyChecker.cpp
Normal file
43
src/base/compression/CyclicRedundancyChecker.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "CyclicRedundancyChecker.h"
|
||||
|
||||
void CyclicRedundancyChecker::createTable()
|
||||
{
|
||||
mTable = std::vector<unsigned long>(TABLE_SIZE, 0);
|
||||
unsigned long c{0};
|
||||
for (int n = 0; n < TABLE_SIZE; n++)
|
||||
{
|
||||
c = (unsigned long) n;
|
||||
for (int k = 0; k < 8; k++)
|
||||
{
|
||||
if (c & 1)
|
||||
{
|
||||
c = 0xedb88320L ^ (c >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
c = c >> 1;
|
||||
}
|
||||
}
|
||||
mTable[n] = c;
|
||||
}
|
||||
mTableComputed = true;
|
||||
}
|
||||
|
||||
void CyclicRedundancyChecker::addValue(unsigned char val)
|
||||
{
|
||||
if (!mTableComputed)
|
||||
{
|
||||
createTable();
|
||||
}
|
||||
mLastValue = mTable[(mLastValue ^ val) & 0xff] ^ (mLastValue >> 8);
|
||||
}
|
||||
|
||||
uint32_t CyclicRedundancyChecker::getChecksum() const
|
||||
{
|
||||
return mLastValue ^ 0xffffffffL;
|
||||
}
|
||||
|
||||
void CyclicRedundancyChecker::reset()
|
||||
{
|
||||
mLastValue = 0xffffffffL;
|
||||
}
|
24
src/base/compression/CyclicRedundancyChecker.h
Normal file
24
src/base/compression/CyclicRedundancyChecker.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "AbstractChecksumCalculator.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class CyclicRedundancyChecker : public AbstractChecksumCalculator
|
||||
{
|
||||
public:
|
||||
void addValue(unsigned char val) override;
|
||||
|
||||
uint32_t getChecksum() const override;
|
||||
|
||||
void reset() override;
|
||||
|
||||
private:
|
||||
void createTable();
|
||||
bool mTableComputed{false};
|
||||
|
||||
uint32_t mLastValue{0xffffffffL};
|
||||
|
||||
static const std::size_t TABLE_SIZE{ 256 };
|
||||
std::vector<unsigned long> mTable;
|
||||
};
|
330
src/base/compression/Lz77Encoder.cpp
Normal file
330
src/base/compression/Lz77Encoder.cpp
Normal file
|
@ -0,0 +1,330 @@
|
|||
#include "Lz77Encoder.h"
|
||||
|
||||
#include "StringUtils.h"
|
||||
#include "BitStream.h"
|
||||
#include "ByteUtils.h"
|
||||
#include "HuffmanEncoder.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
Lz77Encoder::Lz77Encoder(BitStream* inputStream, BitStream* outputStream)
|
||||
: AbstractEncoder(inputStream, outputStream),
|
||||
mSearchBuffer(mSearchBufferSize),
|
||||
mLookaheadBuffer(mLookAheadBufferSize)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Lz77Encoder::setPrefixCodeGenerator(std::unique_ptr<PrefixCodeGenerator> generator)
|
||||
{
|
||||
mCodeGenerator = std::move(generator);
|
||||
}
|
||||
|
||||
bool Lz77Encoder::hitBufferFull() const
|
||||
{
|
||||
return mHitBuffer.size() == mMaxHitBufferSize;
|
||||
}
|
||||
|
||||
void Lz77Encoder::populateSearchBuffer(const Hit& hit)
|
||||
{
|
||||
const auto& [length, distance, next_char] = hit;
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
mSearchBuffer.addItem(next_char);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<unsigned char> new_items(distance, 0);
|
||||
for(unsigned idx=0 ;idx<distance; idx++)
|
||||
{
|
||||
new_items[idx] = getSearchBufferItem(idx);
|
||||
}
|
||||
for(auto item : new_items)
|
||||
{
|
||||
mSearchBuffer.addItem(item);
|
||||
}
|
||||
|
||||
int difference = int(length) - distance;
|
||||
if (difference > 0)
|
||||
{
|
||||
for(unsigned idx=0; idx<unsigned(difference); idx++)
|
||||
{
|
||||
mSearchBuffer.addItem(mLookaheadBuffer.getItem(idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char Lz77Encoder::getSearchBufferItem(unsigned index) const
|
||||
{
|
||||
return mSearchBuffer.getItem(mSearchBuffer.getNumItems() - 1 - index);
|
||||
}
|
||||
|
||||
unsigned Lz77Encoder::lookAheadForMatchingChars(unsigned distance)
|
||||
{
|
||||
unsigned length{0};
|
||||
for(unsigned idx=0; idx<unsigned(mMaxLookAheadBufferIndex + 1); idx++)
|
||||
{
|
||||
int search_offset = int(distance-1) - idx;
|
||||
unsigned char search_char{0};
|
||||
if (search_offset < 0)
|
||||
{
|
||||
search_char = mLookaheadBuffer.getItem(-search_offset - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
search_char = getSearchBufferItem(static_cast<unsigned>(search_offset));
|
||||
}
|
||||
unsigned char lookahead_char = mLookaheadBuffer.getItem(idx);
|
||||
if ((lookahead_char != search_char) || (idx == mMaxLookAheadBufferIndex))
|
||||
{
|
||||
if (idx + 1>= mMinLengthMatchSize)
|
||||
{
|
||||
length = idx + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
void Lz77Encoder::lookForMatches(unsigned char searchChar, unsigned& hitLength, unsigned& hitOffset)
|
||||
{
|
||||
for (unsigned idx = 0; idx< mSearchBuffer.getNumItems(); idx++)
|
||||
{
|
||||
if (mSearchBuffer.getItem(mSearchBuffer.getNumItems() - 1 - idx) == searchChar)
|
||||
{
|
||||
auto num_hits = lookAheadForMatchingChars(idx + 1);
|
||||
if (num_hits > 0 && num_hits >= hitLength)
|
||||
{
|
||||
hitLength = num_hits;
|
||||
hitOffset = idx + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Lz77Encoder::lookAheadSourceEmpty() const
|
||||
{
|
||||
if (mLookaheadBuffer.getNumItems() < mLookAheadBufferSize)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mMaxLookAheadBufferIndex < int(mLookAheadBufferSize) - 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Lz77Encoder::populateLookaheadBuffer(unsigned size, bool firstPass)
|
||||
{
|
||||
if (!firstPass && lookAheadSourceEmpty())
|
||||
{
|
||||
for(unsigned idx=0; idx<size; idx++)
|
||||
{
|
||||
mLookaheadBuffer.addItem(0);
|
||||
mMaxLookAheadBufferIndex--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool stream_finished{false};
|
||||
unsigned stream_end_id{0};
|
||||
for(unsigned idx=0; idx<size; idx++)
|
||||
{
|
||||
if (!stream_finished)
|
||||
{
|
||||
auto byte = mInputStream->readNextByte();
|
||||
if (!byte)
|
||||
{
|
||||
stream_finished = true;
|
||||
stream_end_id = idx -1;
|
||||
mLookaheadBuffer.addItem(0);
|
||||
mMaxLookAheadBufferIndex--;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
mLookaheadBuffer.addItem(*byte);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mLookaheadBuffer.addItem(0);
|
||||
mMaxLookAheadBufferIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
if (stream_finished && firstPass)
|
||||
{
|
||||
mMaxLookAheadBufferIndex = stream_end_id;
|
||||
}
|
||||
}
|
||||
|
||||
bool Lz77Encoder::encode()
|
||||
{
|
||||
if (!mCodeGenerator)
|
||||
{
|
||||
mCodeGenerator = std::make_unique<HuffmanEncoder>();
|
||||
}
|
||||
|
||||
// Fill the lookahead buffer
|
||||
mMaxLookAheadBufferIndex = mLookAheadBufferSize - 1;
|
||||
populateLookaheadBuffer(mLookAheadBufferSize, true);
|
||||
if(mMaxLookAheadBufferIndex < 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool input_stream_ended{false};
|
||||
while(!hitBufferFull())
|
||||
{
|
||||
if (mMaxLookAheadBufferIndex < 0)
|
||||
{
|
||||
input_stream_ended = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const auto working_byte = mLookaheadBuffer.getItem(0);
|
||||
unsigned hit_length{0};
|
||||
unsigned hit_distance{0};
|
||||
lookForMatches(working_byte, hit_length, hit_distance);
|
||||
|
||||
const Hit hit{hit_length, hit_distance, working_byte};
|
||||
mHitBuffer.push_back(hit);
|
||||
|
||||
populateSearchBuffer(hit);
|
||||
if (hit_length == 0)
|
||||
{
|
||||
populateLookaheadBuffer(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
populateLookaheadBuffer(hit_length);
|
||||
}
|
||||
}
|
||||
|
||||
return input_stream_ended;
|
||||
}
|
||||
|
||||
const std::vector<Lz77Encoder::Hit>& Lz77Encoder::getHitBuffer() const
|
||||
{
|
||||
return mHitBuffer;
|
||||
}
|
||||
|
||||
/*
|
||||
void Lz77Encoder::flushHitBuffer()
|
||||
{
|
||||
// If dynamic huffman build trees
|
||||
if (!mCodeGenerator)
|
||||
{
|
||||
mCodeGenerator = std::make_unique<HuffmanEncoder>();
|
||||
}
|
||||
|
||||
// Convert hit buffer to prefix codes and write to output stream
|
||||
for (const auto& hit : mHitBuffer)
|
||||
{
|
||||
const auto& [length, distance, next_char] = hit;
|
||||
|
||||
PrefixCode code;
|
||||
if (length == 0)
|
||||
{
|
||||
code = *mCodeGenerator->getLiteralValue(next_char);
|
||||
std::cout << "Writing symbol " << static_cast<int>(next_char) << " with code " << ByteUtils::toString(code.getData(), code.getLength()) << "\n";
|
||||
|
||||
mOutputStream->writeNBits(code.getData(), code.getLength());
|
||||
}
|
||||
else
|
||||
{
|
||||
code = *mCodeGenerator->getLengthValue(length);
|
||||
const auto distance_code = mCodeGenerator->getDistanceValue(distance);
|
||||
|
||||
std::cout << "Writing length " << length << " with code " << ByteUtils::toString(code.getData(), code.getLength()) << "\n";
|
||||
mOutputStream->writeNBits(code.getData(), code.getLength());
|
||||
|
||||
std::cout << "Writing distance " << distance << " with code " << ByteUtils::toString(distance_code.getData(), distance_code.getLength()) << "\n";
|
||||
mOutputStream->writeNBits(distance_code.getData(), distance_code.getLength());
|
||||
}
|
||||
}
|
||||
|
||||
auto eos_code = mCodeGenerator->getEndOfStreamValue();
|
||||
std::cout << "Writing EOS value with code " << ByteUtils::toString(eos_code->getData(), eos_code->getLength()) << "\n";
|
||||
|
||||
mOutputStream->writeNBits(eos_code->getData(), eos_code->getLength());
|
||||
}
|
||||
*/
|
||||
|
||||
bool Lz77Encoder::decode()
|
||||
{
|
||||
/*
|
||||
std::string ret;
|
||||
|
||||
unsigned loc{0};
|
||||
while(loc < stream.size())
|
||||
{
|
||||
auto working_char = stream[loc];
|
||||
if (working_char == '@')
|
||||
{
|
||||
unsigned loc_working = loc;
|
||||
|
||||
auto remainder = stream.size() - loc;
|
||||
std::string offset;
|
||||
|
||||
unsigned length_loc{0};
|
||||
for(unsigned jdx=0; jdx< remainder; jdx++)
|
||||
{
|
||||
loc++;
|
||||
|
||||
auto offset_char = stream[loc];
|
||||
if (offset_char == 'L')
|
||||
{
|
||||
loc++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset += offset_char;
|
||||
}
|
||||
}
|
||||
unsigned offset_amount = std::stoul(offset);
|
||||
|
||||
std::string length;
|
||||
remainder = stream.size() - loc;
|
||||
|
||||
for(unsigned jdx=0; jdx< remainder; jdx++)
|
||||
{
|
||||
auto length_char = stream[loc];
|
||||
if (StringUtils::IsAlphabetical(length_char) || length_char == '@')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
loc++;
|
||||
length += length_char;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned length_amount = std::stoul(length);
|
||||
|
||||
auto buffer_index = ret.size() - offset_amount;
|
||||
for(unsigned jdx=buffer_index;jdx<buffer_index+length_amount; jdx++)
|
||||
{
|
||||
ret += ret[jdx];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
loc++;
|
||||
ret += working_char;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
60
src/base/compression/Lz77Encoder.h
Normal file
60
src/base/compression/Lz77Encoder.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include "AbstractEncoder.h"
|
||||
#include "HuffmanEncoder.h"
|
||||
#include "CircleBuffer.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
class PrefixCodeGenerator;
|
||||
|
||||
class Lz77Encoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
using Hit = std::tuple<unsigned, unsigned, unsigned char>;
|
||||
|
||||
Lz77Encoder(BitStream* inputStream, BitStream* outputStream);
|
||||
|
||||
bool encode() override;
|
||||
|
||||
bool decode() override;
|
||||
|
||||
const std::vector<Hit>& getHitBuffer() const;
|
||||
|
||||
void setSearchBufferSize(unsigned size);
|
||||
|
||||
void setLookAheadBufferSize(unsigned size);
|
||||
|
||||
void setPrefixCodeGenerator(std::unique_ptr<PrefixCodeGenerator> generator);
|
||||
|
||||
bool hitBufferFull() const;
|
||||
|
||||
private:
|
||||
|
||||
bool lookAheadSourceEmpty() const;
|
||||
unsigned char getSearchBufferItem(unsigned index) const;
|
||||
|
||||
unsigned lookAheadForMatchingChars(unsigned searchIndex);
|
||||
|
||||
void lookForMatches(unsigned char searchChar, unsigned& hitLength, unsigned& hitOffset);
|
||||
|
||||
void populateLookaheadBuffer(unsigned size, bool firstPass = false);
|
||||
|
||||
void populateSearchBuffer(const Hit& hit);
|
||||
|
||||
unsigned mMaxHitBufferSize{32000};
|
||||
std::vector<Hit> mHitBuffer;
|
||||
|
||||
unsigned mSearchBufferSize{32000};
|
||||
CircleBuffer<unsigned char> mSearchBuffer;
|
||||
|
||||
unsigned mLookAheadBufferSize{256};
|
||||
int mMaxLookAheadBufferIndex{0};
|
||||
unsigned mMinLengthMatchSize{1};
|
||||
|
||||
CircleBuffer<unsigned char> mLookaheadBuffer;
|
||||
std::unique_ptr<PrefixCodeGenerator> mCodeGenerator;
|
||||
};
|
54
src/base/compression/RunLengthEncoder.cpp
Normal file
54
src/base/compression/RunLengthEncoder.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
#include "RunLengthEncoder.h"
|
||||
|
||||
std::vector<RunLengthEncoder::Hit> RunLengthEncoder::encode(const std::vector<unsigned char>& input)
|
||||
{
|
||||
std::vector<RunLengthEncoder::Hit> ret;
|
||||
if (input.empty())
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
char working_char{0};
|
||||
unsigned count = 1;
|
||||
for(unsigned idx=0; idx<input.size(); idx++)
|
||||
{
|
||||
auto c = input[idx];
|
||||
if (idx == 0)
|
||||
{
|
||||
working_char = c;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == working_char)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.push_back({working_char, count});
|
||||
working_char = c;
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
ret.push_back({working_char, count});
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> RunLengthEncoder::decode(const std::vector<RunLengthEncoder::Hit>& input)
|
||||
{
|
||||
std::vector<unsigned char> ret;
|
||||
if (input.empty())
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (const auto& hit : input)
|
||||
{
|
||||
for(unsigned idx=0; idx< hit.second; idx++)
|
||||
{
|
||||
ret.push_back(hit.first);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
15
src/base/compression/RunLengthEncoder.h
Normal file
15
src/base/compression/RunLengthEncoder.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
class RunLengthEncoder
|
||||
{
|
||||
public:
|
||||
using Hit = std::pair<unsigned char, unsigned>;
|
||||
|
||||
std::vector<Hit> encode(const std::vector<unsigned char>& input);
|
||||
|
||||
std::vector<unsigned char> decode(const std::vector<Hit>& input);
|
||||
|
||||
private:
|
||||
};
|
0
src/base/compression/StreamCompressor.cpp
Normal file
0
src/base/compression/StreamCompressor.cpp
Normal file
6
src/base/compression/StreamCompressor.h
Normal file
6
src/base/compression/StreamCompressor.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
class StreamCompressor
|
||||
{
|
||||
|
||||
};
|
170
src/base/compression/ZlibEncoder.cpp
Normal file
170
src/base/compression/ZlibEncoder.cpp
Normal file
|
@ -0,0 +1,170 @@
|
|||
#include "ZlibEncoder.h"
|
||||
|
||||
#include "ByteUtils.h"
|
||||
#include "DeflateEncoder.h"
|
||||
#include "FileLogger.h"
|
||||
#include "BitStream.h"
|
||||
|
||||
#include "Adler32Checksum.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
ZlibEncoder::ZlibEncoder(BitStream* inputStream, BitStream* outputStream)
|
||||
: AbstractEncoder(inputStream, outputStream)
|
||||
{
|
||||
mChecksumCalculator = std::make_unique<Adler32Checksum>();
|
||||
}
|
||||
|
||||
ZlibEncoder::~ZlibEncoder()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ZlibEncoder::setWindowSize(unsigned size)
|
||||
{
|
||||
mWindowSize = size;
|
||||
}
|
||||
|
||||
std::string ZlibEncoder::toString(CompressionLevel level) const
|
||||
{
|
||||
switch(level)
|
||||
{
|
||||
case CompressionLevel::FASTEST:
|
||||
return "FASTEST";
|
||||
case CompressionLevel::FAST:
|
||||
return "FAST";
|
||||
case CompressionLevel::DEFAULT:
|
||||
return "DEFAULT";
|
||||
case CompressionLevel::MAX_COMPRESSION:
|
||||
return "MAX_COMPRESSION";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
std::string ZlibEncoder::toString(CompressionMethod method) const
|
||||
{
|
||||
return method == CompressionMethod::DEFLATE ? "DEFLATE" : "UNKNOWN";
|
||||
}
|
||||
|
||||
void ZlibEncoder::parseCompressionMethod(unsigned char method)
|
||||
{
|
||||
//std::cout << "Got compression input " << static_cast<int>(method) << std::endl;
|
||||
mCompressionMethod = static_cast<CompressionMethod>(ByteUtils::getLowerNBits(method, 4));
|
||||
auto compression_info = ByteUtils::getHigherNBits(method, 4);
|
||||
|
||||
if (mCompressionMethod == CompressionMethod::DEFLATE)
|
||||
{
|
||||
mWindowSize = static_cast<unsigned>(pow(2, compression_info + 8));
|
||||
}
|
||||
}
|
||||
|
||||
void ZlibEncoder::parseExtraFlags(unsigned char extraFlags, unsigned char compression_byte)
|
||||
{
|
||||
//std::cout << "Got flags " << static_cast<int>(extraFlags) << std::endl;
|
||||
|
||||
auto mod = ((static_cast<unsigned>(compression_byte) << 8) | extraFlags) % 31;
|
||||
if (mod != 0)
|
||||
{
|
||||
//std::cout << "Invalid header. Mod is " << mod << std::endl;
|
||||
}
|
||||
|
||||
mFlagCheck = ByteUtils::getLowerNBits(extraFlags, 5);
|
||||
mUseDictionary = bool(ByteUtils::getBitN(extraFlags, 5));
|
||||
mFlagLevel = static_cast<CompressionLevel>(ByteUtils::getHigherNBits(extraFlags, 2));
|
||||
}
|
||||
|
||||
std::string ZlibEncoder::getData() const
|
||||
{
|
||||
std::stringstream sstream;
|
||||
sstream << "ZlibEncoder data \n";
|
||||
sstream << "Compression method: " << toString(mCompressionMethod) << '\n';
|
||||
sstream << "Window size: " << mWindowSize << '\n';
|
||||
sstream << "Flag check: " << static_cast<int>(mFlagCheck) << '\n';
|
||||
sstream << "Use dictionary: " << mUseDictionary << '\n';
|
||||
sstream << "Flag level: " << toString(mFlagLevel) << '\n';
|
||||
return sstream.str();
|
||||
}
|
||||
|
||||
bool ZlibEncoder::encode()
|
||||
{
|
||||
DeflateEncoder* deflate_encoder{nullptr};
|
||||
if (!mWorkingEncoder)
|
||||
{
|
||||
if (mCompressionMethod == CompressionMethod::DEFLATE)
|
||||
{
|
||||
auto uq_deflate_encoder = std::make_unique<DeflateEncoder>(mInputStream, mOutputStream);
|
||||
deflate_encoder = uq_deflate_encoder.get();
|
||||
mWorkingEncoder = std::move(uq_deflate_encoder);
|
||||
mWorkingEncoder->addChecksumCalculator(mChecksumCalculator.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
MLOG_ERROR("Zib requested decoder not recognized: " << static_cast<int>(mCompressionMethod) << " aborting encode");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
deflate_encoder->setCompressionMethod(mDeflateCompressionMethod);
|
||||
|
||||
auto compression_info = static_cast<unsigned char>(log2(mWindowSize) - 8);
|
||||
const unsigned char compression_byte = (compression_info << 4) | static_cast<unsigned char>(mCompressionMethod);
|
||||
|
||||
//std::cout << "ZlibEncoder Writing compression byte " << static_cast<int>(compression_byte) << " with info " << static_cast<int>(compression_info) << std::endl;
|
||||
mOutputStream->writeByte(compression_byte);
|
||||
|
||||
unsigned char flag_byte{0};
|
||||
flag_byte |= (static_cast<unsigned char>(mUseDictionary) << 5);
|
||||
flag_byte |= (static_cast<unsigned char>(mFlagLevel) << 6);
|
||||
|
||||
const auto mod = (unsigned(compression_byte)*256 + flag_byte) % 31;
|
||||
flag_byte += (31 - mod);
|
||||
|
||||
//std::cout << "ZlibEncoder Writing Flag byte " << static_cast<int>(flag_byte) << std::endl;
|
||||
mOutputStream->writeByte(flag_byte);
|
||||
|
||||
if(!mWorkingEncoder->encode())
|
||||
{
|
||||
MLOG_ERROR("Sub-Encoder failed - aborting zlib encode");
|
||||
//return false;
|
||||
}
|
||||
|
||||
const auto checksum = mChecksumCalculator->getChecksum();
|
||||
//std::cout << "ZlibEncoder Writing Adler32 Checksum " << checksum << std::endl;
|
||||
mOutputStream->write(checksum);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZlibEncoder::decode()
|
||||
{
|
||||
auto compression_byte = *mInputStream->readNextByte();
|
||||
|
||||
parseCompressionMethod(compression_byte);
|
||||
parseExtraFlags(*mInputStream->readNextByte(), compression_byte);
|
||||
|
||||
if (!mWorkingEncoder)
|
||||
{
|
||||
if (mCompressionMethod == CompressionMethod::DEFLATE)
|
||||
{
|
||||
mWorkingEncoder = std::make_unique<DeflateEncoder>(mInputStream, mOutputStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
MLOG_ERROR("Zib requested decoder not recognized: " << static_cast<int>(mCompressionMethod) << " aborting decode");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto valid = mWorkingEncoder->decode();
|
||||
|
||||
unsigned char byte0 = *mInputStream->readNextByte();
|
||||
unsigned char byte1 = *mInputStream->readNextByte();
|
||||
unsigned char byte2 = *mInputStream->readNextByte();
|
||||
unsigned char byte3 = *mInputStream->readNextByte();
|
||||
|
||||
uint32_t adler32 = (byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3;
|
||||
//std::cout << "Got adler 32 checksum " << adler32 << std::endl;
|
||||
|
||||
return valid;
|
||||
}
|
59
src/base/compression/ZlibEncoder.h
Normal file
59
src/base/compression/ZlibEncoder.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include "AbstractEncoder.h"
|
||||
|
||||
#include "DeflateElements.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class AbstractChecksumCalculator;
|
||||
|
||||
class ZlibEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
|
||||
enum class CompressionMethod : unsigned char
|
||||
{
|
||||
DEFLATE = 8,
|
||||
};
|
||||
|
||||
enum class CompressionLevel : unsigned char
|
||||
{
|
||||
FASTEST,
|
||||
FAST,
|
||||
DEFAULT,
|
||||
MAX_COMPRESSION
|
||||
};
|
||||
|
||||
ZlibEncoder(BitStream* inputStream, BitStream* outputStream);
|
||||
~ZlibEncoder();
|
||||
|
||||
void setWindowSize(unsigned size);
|
||||
void setDeflateCompressionMethod(Deflate::CompressionMethod method)
|
||||
{
|
||||
mDeflateCompressionMethod = method;
|
||||
}
|
||||
|
||||
bool encode() override;
|
||||
bool decode() override;
|
||||
|
||||
std::string getData() const;
|
||||
std::string toString(CompressionLevel level) const;
|
||||
std::string toString(CompressionMethod method) const;
|
||||
|
||||
private:
|
||||
void parseCompressionMethod(unsigned char method);
|
||||
void parseExtraFlags(unsigned char extraFlags, unsigned char compression_byte);
|
||||
|
||||
CompressionMethod mCompressionMethod{CompressionMethod::DEFLATE};
|
||||
Deflate::CompressionMethod mDeflateCompressionMethod{Deflate::CompressionMethod::NONE};
|
||||
unsigned mWindowSize{32768}; // Window size, n in 2^(n+8) bytes
|
||||
|
||||
unsigned char mFlagCheck{0};
|
||||
bool mUseDictionary{false};
|
||||
CompressionLevel mFlagLevel{CompressionLevel::DEFAULT};
|
||||
|
||||
std::unique_ptr<AbstractChecksumCalculator> mChecksumCalculator;
|
||||
std::unique_ptr<AbstractEncoder> mWorkingEncoder;
|
||||
};
|
157
src/base/compression/deflate/DeflateBlock.cpp
Normal file
157
src/base/compression/deflate/DeflateBlock.cpp
Normal file
|
@ -0,0 +1,157 @@
|
|||
#include "DeflateBlock.h"
|
||||
|
||||
#include "ByteUtils.h"
|
||||
#include "AbstractChecksumCalculator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
DeflateBlock::DeflateBlock(BitStream* inputStream, BitStream* outputStream)
|
||||
: mInputStream(inputStream),
|
||||
mOutputStream(outputStream)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string DeflateBlock::getMetaData() const
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << "DeflateBlock Metadata \n";
|
||||
|
||||
sstr << "Final block: " << mInFinalBlock << '\n';
|
||||
sstr << "Compression method: " << Deflate::toString(mCompressionMethod) << '\n';
|
||||
sstr << "Uncompressed block length: " << mUncompressedBlockLength << '\n';
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
void DeflateBlock::setIsFinalBlock(bool isFinal)
|
||||
{
|
||||
mInFinalBlock = isFinal;
|
||||
}
|
||||
|
||||
bool DeflateBlock::isFinalBlock() const
|
||||
{
|
||||
return mInFinalBlock;
|
||||
}
|
||||
|
||||
bool DeflateBlock::read()
|
||||
{
|
||||
auto working_byte = *mInputStream->readNextByte();
|
||||
|
||||
//std::cout << mInputStream->logNextNBytes(60);
|
||||
//std::cout << "DeflateBlock::read location " << mInputStream->logLocation();
|
||||
|
||||
unsigned char final_block{0};
|
||||
mInputStream->readNextNBits(1, final_block);
|
||||
mInFinalBlock = bool(final_block);
|
||||
|
||||
unsigned char compression_type{0};
|
||||
mInputStream->readNextNBits(2, compression_type);
|
||||
mCompressionMethod = static_cast<Deflate::CompressionMethod>(compression_type);
|
||||
|
||||
if (mCompressionMethod == Deflate::CompressionMethod::NONE)
|
||||
{
|
||||
return readUncompressedStream();
|
||||
}
|
||||
else if(mCompressionMethod == Deflate::CompressionMethod::FIXED_HUFFMAN)
|
||||
{
|
||||
return readFixedHuffmanStream();
|
||||
}
|
||||
else if(mCompressionMethod == Deflate::CompressionMethod::DYNAMIC_HUFFMAN)
|
||||
{
|
||||
return readDynamicHuffmanStream();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeflateBlock::readUncompressedStream()
|
||||
{
|
||||
auto byte0 = *mInputStream->readNextByte();
|
||||
auto byte1 = *mInputStream->readNextByte();
|
||||
mUncompressedBlockLength = (byte0 << 8) | byte1;
|
||||
|
||||
std::cout << "Check block 0: " << ByteUtils::toString(byte0) << std::endl;
|
||||
std::cout << "Check block 1: " << ByteUtils::toString(byte1) << std::endl;
|
||||
|
||||
auto byte2 = *mInputStream->readNextByte();
|
||||
auto byte3 = *mInputStream->readNextByte();
|
||||
uint16_t len_check = (byte2 << 8) | byte3;
|
||||
|
||||
//std::cout << "Check block 2: " << ByteUtils::toString(byte2) << std::endl;
|
||||
//std::cout << "Check block 3: " << ByteUtils::toString(byte3) << std::endl;
|
||||
//if (!(byte0 ==(~byte2) && byte1 ==(~byte3)))
|
||||
//{
|
||||
//std::cout << "Uncompressed block length check failed - aborting." << std::endl;
|
||||
//return false;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
for(unsigned idx=0; idx<mUncompressedBlockLength;idx++)
|
||||
{
|
||||
mOutputStream->writeByte(*mInputStream->readNextByte());
|
||||
}
|
||||
//}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeflateBlock::readFixedHuffmanStream()
|
||||
{
|
||||
//std::cout << "Reading fixed huffman stream" << std::endl;
|
||||
mHuffmanStream = std::make_unique<HuffmanStream>(mInputStream, mOutputStream);
|
||||
|
||||
mHuffmanStream->generateFixedCodeMapping();
|
||||
return mHuffmanStream->decode();
|
||||
}
|
||||
|
||||
bool DeflateBlock::readDynamicHuffmanStream()
|
||||
{
|
||||
mHuffmanStream = std::make_unique<HuffmanStream>(mInputStream, mOutputStream);
|
||||
return mHuffmanStream->decode();
|
||||
}
|
||||
|
||||
void DeflateBlock::write(uint16_t datalength)
|
||||
{
|
||||
mUncompressedBlockLength = datalength;
|
||||
|
||||
unsigned char working_block{0};
|
||||
working_block |= static_cast<unsigned char>(mInFinalBlock);
|
||||
working_block |= (static_cast<unsigned char>(mCompressionMethod) << 1);
|
||||
|
||||
if (mCompressionMethod == Deflate::CompressionMethod::NONE)
|
||||
{
|
||||
writeUncompressedStream(working_block, datalength);
|
||||
}
|
||||
else if (mCompressionMethod == Deflate::CompressionMethod::FIXED_HUFFMAN)
|
||||
{
|
||||
mOutputStream->writeNBits(working_block, 3);
|
||||
while(auto byte = mInputStream->readNextByte())
|
||||
{
|
||||
mOutputStream->writeByte(*byte);
|
||||
}
|
||||
|
||||
if (const auto& remaining_bits = mInputStream->getRemainingBits(); remaining_bits.second > 0)
|
||||
{
|
||||
mOutputStream->writeNBits(remaining_bits.first, remaining_bits.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeflateBlock::writeUncompressedStream(unsigned char working_byte, uint16_t datalength)
|
||||
{
|
||||
//std::cout << "Writing compression block header " << ByteUtils::toString(working_byte) << std::endl;
|
||||
mOutputStream->writeByte(working_byte);
|
||||
|
||||
//std::cout << "Writing data length " << mUncompressedBlockLength << " " << ByteUtils::toString(mUncompressedBlockLength) << std::endl;
|
||||
mOutputStream->writeWord(datalength);
|
||||
|
||||
//std::cout << "Writing iverse data length " << ~mUncompressedBlockLength << " " << ByteUtils::toString(~mUncompressedBlockLength) << std::endl;
|
||||
mOutputStream->writeWord(static_cast<uint16_t>(~mUncompressedBlockLength));
|
||||
|
||||
for(unsigned idx=0; idx<mUncompressedBlockLength;idx++)
|
||||
{
|
||||
auto byte = *mInputStream->readNextByte();
|
||||
//std::cout << "Writing next byte " << static_cast<int>(byte) << std::endl;
|
||||
mOutputStream->writeByte(byte);
|
||||
}
|
||||
}
|
46
src/base/compression/deflate/DeflateBlock.h
Normal file
46
src/base/compression/deflate/DeflateBlock.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include "DeflateElements.h"
|
||||
#include "HuffmanStream.h"
|
||||
|
||||
#include "BitStream.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class AbstractChecksumCalculator;
|
||||
|
||||
class DeflateBlock
|
||||
{
|
||||
public:
|
||||
DeflateBlock(BitStream* inputStream, BitStream* outputStream);
|
||||
|
||||
std::string getMetaData() const;
|
||||
|
||||
bool isFinalBlock() const;
|
||||
|
||||
bool read();
|
||||
|
||||
void setIsFinalBlock(bool isFinal);
|
||||
void setCompressionMethod(Deflate::CompressionMethod method)
|
||||
{
|
||||
mCompressionMethod = method;
|
||||
}
|
||||
|
||||
void write(uint16_t datalength);
|
||||
|
||||
private:
|
||||
bool readUncompressedStream();
|
||||
bool readFixedHuffmanStream();
|
||||
bool readDynamicHuffmanStream();
|
||||
|
||||
void writeUncompressedStream(unsigned char working_byte, uint16_t datalength);
|
||||
|
||||
BitStream* mInputStream;
|
||||
BitStream* mOutputStream;
|
||||
|
||||
std::unique_ptr<HuffmanStream> mHuffmanStream;
|
||||
uint16_t mUncompressedBlockLength{0};
|
||||
|
||||
bool mInFinalBlock{false};
|
||||
Deflate::CompressionMethod mCompressionMethod{Deflate::CompressionMethod::NONE};
|
||||
};
|
33
src/base/compression/deflate/DeflateElements.h
Normal file
33
src/base/compression/deflate/DeflateElements.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Deflate
|
||||
{
|
||||
enum class CompressionMethod
|
||||
{
|
||||
NONE,
|
||||
FIXED_HUFFMAN,
|
||||
DYNAMIC_HUFFMAN,
|
||||
ERROR
|
||||
};
|
||||
|
||||
inline std::string toString(CompressionMethod method)
|
||||
{
|
||||
switch (method)
|
||||
{
|
||||
case CompressionMethod::NONE:
|
||||
return "NONE";
|
||||
case CompressionMethod::FIXED_HUFFMAN:
|
||||
return "FIXED_HUFFMAN";
|
||||
case CompressionMethod::DYNAMIC_HUFFMAN:
|
||||
return "DYNAMIC_HUFFMAN";
|
||||
case CompressionMethod::ERROR:
|
||||
return "ERROR";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
88
src/base/compression/deflate/DeflateEncoder.cpp
Normal file
88
src/base/compression/deflate/DeflateEncoder.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
#include "DeflateEncoder.h"
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "ByteUtils.h"
|
||||
#include "DeflateBlock.h"
|
||||
#include "BufferBitStream.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
DeflateEncoder::DeflateEncoder(BitStream* inputStream, BitStream* outputStream)
|
||||
: AbstractEncoder(inputStream, outputStream)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DeflateEncoder::~DeflateEncoder()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool DeflateEncoder::encode()
|
||||
{
|
||||
uint16_t count = 0;
|
||||
BufferBitStream stream;
|
||||
std::unique_ptr<DeflateBlock> working_block = std::make_unique<DeflateBlock>(&stream, mOutputStream);
|
||||
working_block->setCompressionMethod(mCompressionMethod);
|
||||
|
||||
if (mChecksumCalculators.size() > 0)
|
||||
{
|
||||
//std::cout << "Setting checksum calculator " << std::endl;
|
||||
mOutputStream->setChecksumCalculator(mChecksumCalculators[0]);
|
||||
}
|
||||
|
||||
while(true)
|
||||
{
|
||||
if (count == mMaxBlockSize)
|
||||
{
|
||||
//std::cout << working_block->getMetaData();
|
||||
working_block->write(count);
|
||||
|
||||
working_block = std::make_unique<DeflateBlock>(&stream, mOutputStream);
|
||||
working_block->setCompressionMethod(mCompressionMethod);
|
||||
stream.reset();
|
||||
}
|
||||
|
||||
if (auto byte = mInputStream->readNextByte())
|
||||
{
|
||||
//std::cout << "Adding byte " << ByteUtils::toString(*byte) << " to deflate block input" << std::endl;
|
||||
stream.writeByte(*byte);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (const auto& remaining_bits = mInputStream->getRemainingBits(); remaining_bits.second > 0)
|
||||
{
|
||||
stream.writeNBits(remaining_bits.first, remaining_bits.second);
|
||||
}
|
||||
|
||||
stream.resetOffsets();
|
||||
working_block->setIsFinalBlock(true);
|
||||
|
||||
//std::cout << working_block->getMetaData();
|
||||
working_block->write(count);
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
mOutputStream->flushRemainingBits();
|
||||
mOutputStream->clearChecksumCalculator();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeflateEncoder::decode()
|
||||
{
|
||||
auto working_block = std::make_unique<DeflateBlock>(mInputStream, mOutputStream);
|
||||
working_block->read();
|
||||
|
||||
//std::cout << working_block->getMetaData() << std::endl;
|
||||
|
||||
DeflateBlock* raw_block = working_block.get();
|
||||
|
||||
while(!raw_block->isFinalBlock())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
33
src/base/compression/deflate/DeflateEncoder.h
Normal file
33
src/base/compression/deflate/DeflateEncoder.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "AbstractEncoder.h"
|
||||
#include "DeflateElements.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
class DeflateBlock;
|
||||
|
||||
class DeflateEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
DeflateEncoder(BitStream* inputStream, BitStream* outputStream);
|
||||
|
||||
~DeflateEncoder();
|
||||
|
||||
bool encode() override;
|
||||
|
||||
bool decode() override;
|
||||
|
||||
void setCompressionMethod(Deflate::CompressionMethod method)
|
||||
{
|
||||
mCompressionMethod = method;
|
||||
}
|
||||
|
||||
private:
|
||||
uint16_t mMaxBlockSize{65535};
|
||||
Deflate::CompressionMethod mCompressionMethod{Deflate::CompressionMethod::NONE};
|
||||
std::unique_ptr<DeflateBlock > mLastBlock;
|
||||
};
|
||||
|
||||
|
253
src/base/compression/huffman/HuffmanCodeLengthTable.cpp
Normal file
253
src/base/compression/huffman/HuffmanCodeLengthTable.cpp
Normal file
|
@ -0,0 +1,253 @@
|
|||
#include "HuffmanCodeLengthTable.h"
|
||||
|
||||
#include "ByteUtils.h"
|
||||
#include "RunLengthEncoder.h"
|
||||
#include "BitStream.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
void HuffmanCodeLengthTable::buildCompressedLengthSequence()
|
||||
{
|
||||
RunLengthEncoder rl_encoder;
|
||||
auto rle_encoded = rl_encoder.encode(mInputLengthSequence);
|
||||
|
||||
for (const auto& entry : rle_encoded)
|
||||
{
|
||||
//std::cout << "Got rle " << static_cast<int>(entry.first) << " | " << entry.second << std::endl;
|
||||
}
|
||||
mCompressedLengthSequence.clear();
|
||||
|
||||
for (const auto& entry : rle_encoded)
|
||||
{
|
||||
const auto length = entry.first;
|
||||
const auto count = entry.second;
|
||||
if (count < 3)
|
||||
{
|
||||
for(std::size_t idx=0; idx<count; idx++)
|
||||
{
|
||||
mCompressedLengthSequence.push_back({length, 0});
|
||||
}
|
||||
}
|
||||
else if (length == 0)
|
||||
{
|
||||
std::size_t num_big = count / 138;
|
||||
for(std::size_t idx=0; idx<num_big; idx++)
|
||||
{
|
||||
mCompressedLengthSequence.push_back({18, 127});
|
||||
}
|
||||
auto remainder_big = count % 138;
|
||||
if (remainder_big > 10)
|
||||
{
|
||||
mCompressedLengthSequence.push_back({18, remainder_big-11});
|
||||
}
|
||||
else if(remainder_big > 2)
|
||||
{
|
||||
mCompressedLengthSequence.push_back({17, remainder_big-3});
|
||||
}
|
||||
else
|
||||
{
|
||||
for(std::size_t idx=0; idx<remainder_big; idx++)
|
||||
{
|
||||
mCompressedLengthSequence.push_back({0, 0});
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mCompressedLengthSequence.push_back({length, 0});
|
||||
auto num_blocks_of_six = (count-1)/6;
|
||||
for(std::size_t idx=0; idx<num_blocks_of_six; idx++)
|
||||
{
|
||||
mCompressedLengthSequence.push_back({16, 3});
|
||||
}
|
||||
auto remaining_counts = (count-1) % 6;
|
||||
if (remaining_counts >= 3)
|
||||
{
|
||||
mCompressedLengthSequence.push_back({16, remaining_counts - 3});
|
||||
}
|
||||
else
|
||||
{
|
||||
for(std::size_t idx=0; idx<remaining_counts; idx++)
|
||||
{
|
||||
mCompressedLengthSequence.push_back({length, 0});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mCompressedLengthCounts = std::vector<std::size_t>(19, 0);
|
||||
for (const auto& entry : mCompressedLengthSequence)
|
||||
{
|
||||
mCompressedLengthCounts[entry.first]++;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<HuffmanCodeLengthTable::CompressedSequenceEntry>& HuffmanCodeLengthTable::getCompressedLengthSequence() const
|
||||
{
|
||||
return mCompressedLengthSequence;
|
||||
}
|
||||
|
||||
const std::vector<std::size_t> HuffmanCodeLengthTable::getCompressedLengthCounts() const
|
||||
{
|
||||
return mCompressedLengthCounts;
|
||||
}
|
||||
|
||||
std::optional<PrefixCode> HuffmanCodeLengthTable::getCodeForSymbol(unsigned symbol) const
|
||||
{
|
||||
return mTree.getCode(symbol);
|
||||
}
|
||||
|
||||
bool HuffmanCodeLengthTable::readNextSymbol(unsigned& result, BitStream* stream)
|
||||
{
|
||||
if (getNumCodeLengths() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t working_index{0};
|
||||
auto length = getCodeLength(working_index);
|
||||
auto delta = length;
|
||||
|
||||
bool found{false};
|
||||
unsigned char buffer{0};
|
||||
uint32_t working_bits{0};
|
||||
unsigned working_symbol{0};
|
||||
|
||||
while(!found)
|
||||
{
|
||||
auto valid = stream->readNextNBits(delta, buffer);
|
||||
//std::cout << "Got buffer " << ByteUtils::toString(buffer) << std::endl;;
|
||||
|
||||
unsigned hold = buffer;
|
||||
working_bits = working_bits | (hold << (length - delta));
|
||||
//std::cout << "Read " << delta << " bits with length " << length << " and value " << ByteUtils::toString(working_bits) << std::endl;
|
||||
|
||||
if (const auto symbol = findMatch(working_index, working_bits))
|
||||
{
|
||||
found = true;
|
||||
working_symbol = *symbol;
|
||||
}
|
||||
else
|
||||
{
|
||||
working_index++;
|
||||
if (working_index >= getNumCodeLengths())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto new_length = getCodeLength(working_index);
|
||||
delta = new_length - length;
|
||||
length = new_length;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
result = working_symbol;
|
||||
// std::cout << "Found symbol " << working_symbol << " with bits " << ByteUtils::toString(working_bits) << std::endl;
|
||||
// std::cout << "At Byte offset " << stream->getCurrentByteOffset() << " and bit offset " << stream->getCurrentBitOffset() << std::endl;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//std::cout << "SYMBOL NOT FOUND " << " with bits " << ByteUtils::toString(working_bits) << " and index " << working_index << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void HuffmanCodeLengthTable::buildPrefixCodes()
|
||||
{
|
||||
if(mInputLengthSequence.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char max_length = *std::max_element(mInputLengthSequence.begin(), mInputLengthSequence.end());
|
||||
std::vector<unsigned> counts(max_length+1, 0);
|
||||
for (const auto length : mInputLengthSequence)
|
||||
{
|
||||
counts[length]++;
|
||||
}
|
||||
counts[0] = 0;
|
||||
|
||||
uint32_t code{0};
|
||||
std::vector<uint32_t> next_code(max_length + 1, 0);
|
||||
for (unsigned bits = 1; bits <= max_length; bits++)
|
||||
{
|
||||
code = (code + counts[bits-1]) << 1;
|
||||
//std::cout << "Start code for bit " << bits << " is " << ByteUtils::toString(code) << " | dec " << code << " count " << counts[bits-1] << std::endl;
|
||||
next_code[bits] = code;
|
||||
}
|
||||
|
||||
for(std::size_t idx=0; idx<mInputLengthSequence.size(); idx++)
|
||||
{
|
||||
if (const auto length = mInputLengthSequence[idx]; length != 0)
|
||||
{
|
||||
const auto code = next_code[length];
|
||||
next_code[length]++;
|
||||
auto prefix_code = PrefixCode(code, length);
|
||||
mTree.addCodeLengthEntry(length, {PrefixCode(code, length), static_cast<unsigned>(idx)});
|
||||
mCodes.push_back(prefix_code);
|
||||
}
|
||||
}
|
||||
mTree.sortTable();
|
||||
//std::cout << dumpPrefixCodes();
|
||||
}
|
||||
|
||||
const PrefixCode& HuffmanCodeLengthTable::getCode(std::size_t index) const
|
||||
{
|
||||
return mCodes[index];
|
||||
}
|
||||
|
||||
std::string HuffmanCodeLengthTable::dumpPrefixCodes() const
|
||||
{
|
||||
return mTree.dump();
|
||||
}
|
||||
|
||||
std::size_t HuffmanCodeLengthTable::mapToDeflateIndex(std::size_t index) const
|
||||
{
|
||||
if (index>= DEFLATE_PERMUTATION_SIZE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DEFLATE_PERMUTATION[index];
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t HuffmanCodeLengthTable::getNumCodeLengths() const
|
||||
{
|
||||
return mTree.getNumCodeLengths();
|
||||
}
|
||||
|
||||
std::optional<HuffmanTree::Symbol> HuffmanCodeLengthTable::findMatch(std::size_t treeIndex, uint32_t code) const
|
||||
{
|
||||
return mTree.findMatch(treeIndex, code);
|
||||
}
|
||||
|
||||
unsigned HuffmanCodeLengthTable::getCodeLength(std::size_t index) const
|
||||
{
|
||||
return mTree.getCodeLength(index);
|
||||
}
|
||||
|
||||
void HuffmanCodeLengthTable::setInputLengthSequence(const std::vector<unsigned char>& sequence, bool targetDeflate)
|
||||
{
|
||||
mTargetDeflate = targetDeflate;
|
||||
|
||||
if (targetDeflate)
|
||||
{
|
||||
mInputLengthSequence = std::vector<unsigned char>(DEFLATE_PERMUTATION_SIZE, 0);
|
||||
for(std::size_t idx=0; idx<sequence.size(); idx++)
|
||||
{
|
||||
mInputLengthSequence[mapToDeflateIndex(idx)] = sequence[idx];
|
||||
//std::cout << "Got code length for " << mapToDeflateIndex(idx) << " of " << static_cast<unsigned>(sequence[idx]) << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mInputLengthSequence = sequence;
|
||||
}
|
||||
}
|
56
src/base/compression/huffman/HuffmanCodeLengthTable.h
Normal file
56
src/base/compression/huffman/HuffmanCodeLengthTable.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include "HuffmanTree.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
class BitStream;
|
||||
|
||||
class HuffmanCodeLengthTable
|
||||
{
|
||||
public:
|
||||
void buildPrefixCodes();
|
||||
|
||||
void buildCompressedLengthSequence();
|
||||
|
||||
std::string dumpPrefixCodes() const;
|
||||
|
||||
std::optional<HuffmanTree::Symbol> findMatch(std::size_t treeIndex, uint32_t code) const;
|
||||
|
||||
const HuffmanTree& getTree() const;
|
||||
|
||||
const PrefixCode& getCode(std::size_t index) const;
|
||||
|
||||
std::optional<PrefixCode> getCodeForSymbol(unsigned symbol) const;
|
||||
|
||||
using CompressedSequenceEntry = std::pair<unsigned, unsigned>;
|
||||
const std::vector<CompressedSequenceEntry>& getCompressedLengthSequence() const;
|
||||
|
||||
const std::vector<std::size_t> getCompressedLengthCounts() const;
|
||||
|
||||
std::size_t getNumCodeLengths() const;
|
||||
|
||||
unsigned getCodeLength(std::size_t treeIndex) const;
|
||||
|
||||
std::size_t mapToDeflateIndex(std::size_t index) const;
|
||||
|
||||
void setInputLengthSequence(const std::vector<unsigned char>& sequence, bool targetDeflate = true);
|
||||
|
||||
bool readNextSymbol(unsigned& buffer, BitStream* stream);
|
||||
|
||||
private:
|
||||
|
||||
HuffmanTree mTree;
|
||||
bool mTargetDeflate{true};
|
||||
|
||||
std::vector<unsigned char> mInputLengthSequence;
|
||||
std::vector<PrefixCode> mCodes;
|
||||
|
||||
std::vector<CompressedSequenceEntry> mCompressedLengthSequence;
|
||||
std::vector<std::size_t> mCompressedLengthCounts;
|
||||
|
||||
static constexpr unsigned DEFLATE_PERMUTATION_SIZE{19};
|
||||
static constexpr unsigned DEFLATE_PERMUTATION[DEFLATE_PERMUTATION_SIZE]{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
|
||||
};
|
173
src/base/compression/huffman/HuffmanEncoder.cpp
Normal file
173
src/base/compression/huffman/HuffmanEncoder.cpp
Normal file
|
@ -0,0 +1,173 @@
|
|||
#include "HuffmanEncoder.h"
|
||||
|
||||
#include "RawTree.h"
|
||||
|
||||
#include "HuffmanFixedCodes.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <tuple>
|
||||
#include <iostream>
|
||||
|
||||
void HuffmanEncoder::dumpNode(RawNode<CountPair>* node, unsigned depth) const
|
||||
{
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = node->getData();
|
||||
|
||||
std::string prefix(depth, '_');
|
||||
|
||||
if (node->isLeaf())
|
||||
{
|
||||
//std::cout << prefix << "Leaf with value: " << data.first << " and sum " << data.second << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
//std::cout << prefix << "Intermediate with sum " << data.second << std::endl;
|
||||
//std::cout << prefix << "Doing Left.." << std::endl;
|
||||
dumpNode(node->getLeftChild(), depth+1);
|
||||
|
||||
//std::cout << prefix << "Doing Right.." << std::endl;
|
||||
dumpNode(node->getRightChild(), depth+1);
|
||||
|
||||
//std::cout << prefix << "*****" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void HuffmanEncoder::dumpTree(const RawTree<CountPair>& tree) const
|
||||
{
|
||||
dumpNode(tree.getRootNode(), 0);
|
||||
}
|
||||
|
||||
void HuffmanEncoder::encode(const std::vector<unsigned>& counts)
|
||||
{
|
||||
auto cmp = [](RawNode<CountPair>* left, RawNode<CountPair>* right)
|
||||
{
|
||||
return left->getData().second > right->getData().second;
|
||||
};
|
||||
|
||||
std::priority_queue<RawNode<CountPair>*, std::vector<RawNode<CountPair>* >, decltype(cmp)> q(cmp);
|
||||
unsigned offset{0};
|
||||
for (auto count : counts)
|
||||
{
|
||||
if (count > 0)
|
||||
{
|
||||
q.push(new RawNode<CountPair>({offset, count}));
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
|
||||
while(q.size() > 1)
|
||||
{
|
||||
auto node0 = q.top();
|
||||
q.pop();
|
||||
|
||||
auto node1 = q.top();
|
||||
q.pop();
|
||||
|
||||
const auto sum = node0->getData().second + node1->getData().second;
|
||||
auto new_node = new RawNode<CountPair>(CountPair{0, sum});
|
||||
|
||||
new_node->addChild(node0);
|
||||
new_node->addChild(node1);
|
||||
q.push(new_node);
|
||||
}
|
||||
|
||||
auto root = q.top();
|
||||
q.pop();
|
||||
|
||||
RawTree<CountPair> tree;
|
||||
tree.addRootNode(root);
|
||||
|
||||
//using TableEntry = std::tuple<>
|
||||
|
||||
//dumpTree(tree);
|
||||
|
||||
//std::cout << "********" << std::endl;
|
||||
}
|
||||
|
||||
void HuffmanEncoder::encode(const std::unordered_map<unsigned char, unsigned>& counts)
|
||||
{
|
||||
std::vector<unsigned> just_counts;
|
||||
for (const auto& data: counts)
|
||||
{
|
||||
mSymbolMapping.push_back(data.first);
|
||||
just_counts.push_back(data.second);
|
||||
}
|
||||
|
||||
encode(just_counts);
|
||||
}
|
||||
|
||||
void HuffmanEncoder::setUseFixedCode(bool useFixed)
|
||||
{
|
||||
mUseFixedCode = useFixed;
|
||||
}
|
||||
|
||||
uint32_t HuffmanEncoder::getLengthValue(unsigned length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::optional<PrefixCode> HuffmanEncoder::getLiteralValue(unsigned char value) const
|
||||
{
|
||||
return mLiteralLengthTable.getCodeForSymbol(value);
|
||||
}
|
||||
|
||||
std::optional<PrefixCode> HuffmanEncoder::getLengthValue(unsigned length) const
|
||||
{
|
||||
return mLiteralLengthTable.getCodeForSymbol(length);
|
||||
}
|
||||
|
||||
std::optional<PrefixCode> HuffmanEncoder::getDistanceValue(unsigned distance) const
|
||||
{
|
||||
return mDistanceTable.getCodeForSymbol(distance);
|
||||
}
|
||||
|
||||
std::optional<PrefixCode> HuffmanEncoder::getEndOfStreamValue() const
|
||||
{
|
||||
return mLiteralLengthTable.getCodeForSymbol(256);
|
||||
}
|
||||
|
||||
void HuffmanEncoder::initializeTrees(const std::vector<Hit>& hits)
|
||||
{
|
||||
initializeLiteralLengthTable(hits);
|
||||
}
|
||||
|
||||
void HuffmanEncoder::initializeLiteralLengthTable(const std::vector<Hit>& hits)
|
||||
{
|
||||
if(mUseFixedCode)
|
||||
{
|
||||
mLiteralLengthTable.setInputLengthSequence(HuffmanFixedCodes::getDeflateFixedHuffmanCodes(), false);
|
||||
mLiteralLengthTable.buildPrefixCodes();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<unsigned> counts(285, 0);
|
||||
counts[256] = 1;
|
||||
for (const auto& hit : hits)
|
||||
{
|
||||
const auto& [length, distance, next_char] = hit;
|
||||
if (length > 0 )
|
||||
{
|
||||
const auto& [code, extra_bits, num_extra_bits] = HuffmanFixedCodes::getCodeForLength(length);
|
||||
counts[code]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
counts[next_char]++;
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned idx=0; idx<counts.size(); idx++)
|
||||
{
|
||||
if (counts[idx]>0)
|
||||
{
|
||||
//std::cout << "Count for " << idx << " is " << counts[idx] << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
encode(counts);
|
||||
}
|
57
src/base/compression/huffman/HuffmanEncoder.h
Normal file
57
src/base/compression/huffman/HuffmanEncoder.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include "RawTree.h"
|
||||
|
||||
#include "HuffmanCodeLengthTable.h"
|
||||
#include "HuffmanFixedCodes.h"
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
class PrefixCodeGenerator
|
||||
{
|
||||
public:
|
||||
virtual ~PrefixCodeGenerator() = default;
|
||||
virtual std::optional<PrefixCode> getLiteralValue(unsigned char symbol) const = 0;
|
||||
virtual std::optional<PrefixCode> getLengthValue(unsigned length) const = 0;
|
||||
virtual std::optional<PrefixCode> getDistanceValue(unsigned distance) const = 0;
|
||||
|
||||
virtual std::optional<PrefixCode> getEndOfStreamValue() const = 0;
|
||||
};
|
||||
|
||||
class HuffmanEncoder : public PrefixCodeGenerator
|
||||
{
|
||||
using CountPair = std::pair<unsigned, unsigned>;
|
||||
using Hit = std::tuple<unsigned, unsigned, unsigned char>;
|
||||
|
||||
public:
|
||||
void encode(const std::vector<unsigned>& counts);
|
||||
void encode(const std::unordered_map<unsigned char, unsigned>& counts);
|
||||
|
||||
uint32_t getLengthValue(unsigned length);
|
||||
|
||||
std::optional<PrefixCode> getLiteralValue(unsigned char symbol) const override;
|
||||
|
||||
std::optional<PrefixCode> getLengthValue(unsigned length) const override;
|
||||
|
||||
std::optional<PrefixCode> getDistanceValue(unsigned distance) const override;
|
||||
|
||||
std::optional<PrefixCode> getEndOfStreamValue() const override;
|
||||
|
||||
void initializeTrees(const std::vector<Hit>& hits);
|
||||
|
||||
void setUseFixedCode(bool useFixed);
|
||||
private:
|
||||
void initializeLiteralLengthTable(const std::vector<Hit>& hits);
|
||||
|
||||
void dumpTree(const RawTree<CountPair>& tree) const;
|
||||
void dumpNode(RawNode<CountPair>* node, unsigned depth) const;
|
||||
|
||||
bool mUseFixedCode{false};
|
||||
bool mTableIsInitialized{false};
|
||||
|
||||
std::vector<unsigned char> mSymbolMapping;
|
||||
HuffmanCodeLengthTable mLiteralLengthTable;
|
||||
HuffmanCodeLengthTable mDistanceTable;
|
||||
};
|
145
src/base/compression/huffman/HuffmanFixedCodes.h
Normal file
145
src/base/compression/huffman/HuffmanFixedCodes.h
Normal file
|
@ -0,0 +1,145 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
namespace HuffmanFixedCodes
|
||||
{
|
||||
inline std::vector<unsigned char> getDeflateFixedHuffmanCodes()
|
||||
{
|
||||
std::vector<std::pair<unsigned, unsigned char> > mappings {{144, 8}, {112, 9}, {24, 7}, {8 ,8}};
|
||||
std::vector<unsigned char> sequence;
|
||||
for(const auto& entry : mappings)
|
||||
{
|
||||
for(unsigned idx=0;idx<entry.first;idx++)
|
||||
{
|
||||
sequence.push_back(entry.second);
|
||||
}
|
||||
}
|
||||
return sequence;
|
||||
}
|
||||
|
||||
inline std::tuple<unsigned, unsigned char, unsigned char> getCodeForLength(unsigned length)
|
||||
{
|
||||
if (length <= 10)
|
||||
{
|
||||
return {length - 3, 0, 0};
|
||||
}
|
||||
|
||||
unsigned base = 2;
|
||||
unsigned last_offset = 10;
|
||||
for (unsigned n = 1; n < 5; n++)
|
||||
{
|
||||
const auto diff = length - last_offset;
|
||||
if (diff <= 4*base)
|
||||
{
|
||||
auto extra = diff/base + diff % base;
|
||||
return {last_offset + diff/base, extra, n};
|
||||
}
|
||||
|
||||
last_offset += 4*n;
|
||||
base = base*2;
|
||||
}
|
||||
return {258, 0, 0};
|
||||
}
|
||||
|
||||
/*
|
||||
inline std::pair<unsigned, unsigned char> getCodeForLength(unsigned length)
|
||||
{
|
||||
if (length <= 10)
|
||||
{
|
||||
return {257 + length - 3, 0};
|
||||
}
|
||||
else if(length <= 18)
|
||||
{
|
||||
auto offset = length - 10;
|
||||
auto extra = offset/2 + offset % 2;
|
||||
return {265 + offset/2, extra};
|
||||
}
|
||||
else if(length <= 34)
|
||||
{
|
||||
auto offset = length - 19;
|
||||
auto extra = offset/4 + offset % 4;
|
||||
return {269 + offset/4, extra};
|
||||
}
|
||||
else if(length <= 66)
|
||||
{
|
||||
auto offset = length - 35;
|
||||
auto extra = offset/8 + offset % 8;
|
||||
return {273 + offset/8, extra};
|
||||
}
|
||||
else if(length <= 114)
|
||||
{
|
||||
auto offset = length - 67;
|
||||
auto extra = offset/16 + offset % 16;
|
||||
return {277 + offset/16, extra};
|
||||
}
|
||||
else if(length <= 257)
|
||||
{
|
||||
auto offset = length - 115;
|
||||
auto extra = offset/32 + offset % 32;
|
||||
return {281 + offset/32, extra};
|
||||
}
|
||||
else
|
||||
{
|
||||
return {258, 0};
|
||||
}
|
||||
}
|
||||
|
||||
inline unsigned getLengthForCode(unsigned symbol, unsigned extra)
|
||||
{
|
||||
if (symbol <= 264)
|
||||
{
|
||||
return 3 + symbol - 257;
|
||||
}
|
||||
else if (symbol <= 268)
|
||||
{
|
||||
return 11 + 2*(symbol - 265) + extra;
|
||||
const auto valid_dist = readNextDistanceSymbol(distance);
|
||||
copyFromBuffer(length, distance);
|
||||
}
|
||||
else if (symbol <= 272)
|
||||
{
|
||||
unsigned char extra{0};
|
||||
mInputStream->readNextNBits(2, extra);
|
||||
|
||||
auto length = 19 + 4*(symbol - 269) + extra;
|
||||
const auto valid_dist = readNextDistanceSymbol(distance);
|
||||
copyFromBuffer(length, distance);
|
||||
}
|
||||
else if (symbol <= 276)
|
||||
{
|
||||
unsigned char extra{0};
|
||||
mInputStream->readNextNBits(3, extra);
|
||||
|
||||
auto length = 35 + 8*(symbol - 273) + extra;
|
||||
const auto valid_dist = readNextDistanceSymbol(distance);
|
||||
copyFromBuffer(length, distance);
|
||||
}
|
||||
else if (symbol <= 280)
|
||||
{
|
||||
unsigned char extra{0};
|
||||
mInputStream->readNextNBits(4, extra);
|
||||
|
||||
auto length = 67 + 16*(symbol - 277) + extra;
|
||||
const auto valid_dist = readNextDistanceSymbol(distance);
|
||||
copyFromBuffer(length, distance);
|
||||
}
|
||||
else if (symbol <= 284)
|
||||
{
|
||||
unsigned char extra{0};
|
||||
mInputStream->readNextNBits(5, extra);
|
||||
|
||||
auto length = 131 + 32*(symbol - 281) + extra;
|
||||
const auto valid_dist = readNextDistanceSymbol(distance);
|
||||
copyFromBuffer(length, distance);
|
||||
}
|
||||
else if (symbol == 285)
|
||||
{
|
||||
auto length = 258;
|
||||
const auto valid_dist = readNextDistanceSymbol(distance);
|
||||
copyFromBuffer(length, distance);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
346
src/base/compression/huffman/HuffmanStream.cpp
Normal file
346
src/base/compression/huffman/HuffmanStream.cpp
Normal file
|
@ -0,0 +1,346 @@
|
|||
#include "HuffmanStream.h"
|
||||
|
||||
#include "ByteUtils.h"
|
||||
#include "HuffmanFixedCodes.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <sstream>
|
||||
|
||||
std::vector<unsigned> DISTANCE_OFFSETS
|
||||
{
|
||||
5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
|
||||
258, 385, 513, 769, 1025, 1537, 2049, 3073, 4097,
|
||||
6145, 8193, 12289, 16385, 24577
|
||||
};
|
||||
|
||||
HuffmanStream::HuffmanStream(BitStream* inputStream, BitStream* outputStream)
|
||||
: mInputStream(inputStream),
|
||||
mOutputStream(outputStream)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HuffmanStream::generateFixedCodeMapping()
|
||||
{
|
||||
mUsingFixedCodes = true;
|
||||
mCodeLengthTable.setInputLengthSequence(HuffmanFixedCodes::getDeflateFixedHuffmanCodes(), false);
|
||||
mCodeLengthTable.buildPrefixCodes();
|
||||
}
|
||||
|
||||
bool HuffmanStream::readNextCodeLengthSymbol(unsigned& buffer)
|
||||
{
|
||||
return mCodeLengthTable.readNextSymbol(buffer, mInputStream);
|
||||
}
|
||||
|
||||
bool HuffmanStream::readNextLiteralSymbol(unsigned& buffer)
|
||||
{
|
||||
return mLiteralTable.readNextSymbol(buffer, mInputStream);
|
||||
}
|
||||
|
||||
bool HuffmanStream::readNextDistanceSymbol(unsigned& buffer)
|
||||
{
|
||||
unsigned base_symbol{0};
|
||||
unsigned char extra_bits{0};
|
||||
const auto valid = mDistanceTable.readNextSymbol(base_symbol, mInputStream);
|
||||
if (!valid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//std::cout << "Got distance base symbol " << base_symbol << std::endl;
|
||||
|
||||
if (base_symbol <= 3)
|
||||
{
|
||||
buffer = 1 + base_symbol;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto num_extra_bits = (base_symbol - 3 - 1)/2 + 1;
|
||||
unsigned extra_sum{0};
|
||||
if (num_extra_bits > 8)
|
||||
{
|
||||
auto byte_val = *mInputStream->readNextByte();
|
||||
mInputStream->readNextNBits(num_extra_bits-8, extra_bits);
|
||||
extra_sum = extra_bits;
|
||||
extra_sum = extra_sum << (num_extra_bits - 8);
|
||||
extra_sum |= byte_val;
|
||||
}
|
||||
else
|
||||
{
|
||||
mInputStream->readNextNBits(num_extra_bits, extra_bits);
|
||||
extra_sum = extra_bits;
|
||||
}
|
||||
buffer = DISTANCE_OFFSETS[base_symbol - 4] + extra_sum;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void HuffmanStream::addValue(unsigned value, unsigned& count, unsigned& lastValue, std::vector<unsigned char>& literals, unsigned numLiterals, std::vector<unsigned char>& distances)
|
||||
{
|
||||
if (count < mNumLiterals)
|
||||
{
|
||||
literals[count] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
distances[count - mNumLiterals] = value;
|
||||
}
|
||||
lastValue = value;
|
||||
count++;
|
||||
}
|
||||
|
||||
void HuffmanStream::readCodeLengths()
|
||||
{
|
||||
std::vector<unsigned char> literal_lengths(288, 0);
|
||||
std::vector<unsigned char> distance_lengths(32, 0);
|
||||
unsigned symbol{0};
|
||||
unsigned count{0};
|
||||
|
||||
unsigned last_value{0};
|
||||
|
||||
while(count < mNumLiterals + mNumDistances)
|
||||
{
|
||||
bool valid = readNextCodeLengthSymbol(symbol);
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
//std::cout << "Hit unknown symbol - bailing out" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
if (symbol < 16)
|
||||
{
|
||||
addValue(symbol, count, last_value, literal_lengths, mNumLiterals, distance_lengths);
|
||||
}
|
||||
else if(symbol == 16)
|
||||
{
|
||||
unsigned char num_reps{0};
|
||||
mInputStream->readNextNBits(2, num_reps);
|
||||
|
||||
//std::cout << "Got val 16 doing " << 3 + num_reps << std::endl;
|
||||
for(unsigned char idx=0; idx< 3 + num_reps; idx++)
|
||||
{
|
||||
addValue(last_value, count, last_value, literal_lengths, mNumLiterals, distance_lengths);
|
||||
}
|
||||
}
|
||||
else if(symbol == 17)
|
||||
{
|
||||
unsigned char num_reps{0};
|
||||
mInputStream->readNextNBits(3, num_reps);
|
||||
|
||||
//std::cout << "Got val 17 doing " << 3 + num_reps << std::endl;
|
||||
for(unsigned char idx=0; idx< 3 + num_reps; idx++)
|
||||
{
|
||||
addValue(0, count, last_value, literal_lengths, mNumLiterals, distance_lengths);
|
||||
}
|
||||
}
|
||||
else if(symbol == 18)
|
||||
{
|
||||
unsigned char num_reps{0};
|
||||
mInputStream->readNextNBits(7, num_reps);
|
||||
|
||||
//std::cout << "Got val 18 doing " << 11 + num_reps << std::endl;
|
||||
for(unsigned idx=0; idx< 11 + unsigned(num_reps); idx++)
|
||||
{
|
||||
addValue(0, count, last_value, literal_lengths, mNumLiterals, distance_lengths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//std::cout << "Got final literal length sequence " << std::endl;
|
||||
for(unsigned idx=0; idx<literal_lengths.size(); idx++)
|
||||
{
|
||||
//std::cout << static_cast<int>(literal_lengths[idx]) << "," ;
|
||||
}
|
||||
//std::cout << std::endl;
|
||||
|
||||
//std::cout << "Got final distance length sequence " << std::endl;
|
||||
for(unsigned idx=0; idx<distance_lengths.size(); idx++)
|
||||
{
|
||||
//std::cout << static_cast<int>(distance_lengths[idx]) << "," ;
|
||||
}
|
||||
//std::cout << std::endl;
|
||||
|
||||
mLiteralTable.setInputLengthSequence(literal_lengths, false);
|
||||
mLiteralTable.buildPrefixCodes();
|
||||
|
||||
mDistanceTable.setInputLengthSequence(distance_lengths, false);
|
||||
mDistanceTable.buildPrefixCodes();
|
||||
}
|
||||
|
||||
void HuffmanStream::copyFromBuffer(unsigned length, unsigned distance)
|
||||
{
|
||||
std::size_t offset = mBuffer.size() - 1 - distance;
|
||||
for(unsigned idx=0; idx<length; idx++)
|
||||
{
|
||||
auto symbol = mBuffer[offset + idx];
|
||||
|
||||
mOutputStream->writeByte(symbol);
|
||||
mBuffer.push_back(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
void HuffmanStream::readSymbols()
|
||||
{
|
||||
bool hit_end_stream{false};
|
||||
unsigned symbol{0};
|
||||
unsigned distance{0};
|
||||
while(!hit_end_stream)
|
||||
{
|
||||
const auto valid = readNextLiteralSymbol(symbol);
|
||||
if (!valid)
|
||||
{
|
||||
//std::cout << "Hit unknown symbol - bailing out" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
//std::cout << "Got symbol " << symbol << std::endl;
|
||||
|
||||
if(symbol <= 255)
|
||||
{
|
||||
mOutputStream->writeByte(symbol);
|
||||
mBuffer.push_back(symbol);
|
||||
}
|
||||
else if(symbol == 256)
|
||||
{
|
||||
hit_end_stream = true;
|
||||
break;
|
||||
}
|
||||
else if (symbol <= 264)
|
||||
{
|
||||
auto length = 3 + symbol - 257;
|
||||
const auto valid_dist = readNextDistanceSymbol(distance);
|
||||
copyFromBuffer(length, distance);
|
||||
}
|
||||
else if (symbol <= 268)
|
||||
{
|
||||
unsigned char extra{0};
|
||||
mInputStream->readNextNBits(1, extra);
|
||||
|
||||
auto length = 11 + 2*(symbol - 265) + extra;
|
||||
const auto valid_dist = readNextDistanceSymbol(distance);
|
||||
copyFromBuffer(length, distance);
|
||||
}
|
||||
else if (symbol <= 272)
|
||||
{
|
||||
unsigned char extra{0};
|
||||
mInputStream->readNextNBits(2, extra);
|
||||
|
||||
auto length = 19 + 4*(symbol - 269) + extra;
|
||||
const auto valid_dist = readNextDistanceSymbol(distance);
|
||||
copyFromBuffer(length, distance);
|
||||
}
|
||||
else if (symbol <= 276)
|
||||
{
|
||||
unsigned char extra{0};
|
||||
mInputStream->readNextNBits(3, extra);
|
||||
|
||||
auto length = 35 + 8*(symbol - 273) + extra;
|
||||
const auto valid_dist = readNextDistanceSymbol(distance);
|
||||
copyFromBuffer(length, distance);
|
||||
}
|
||||
else if (symbol <= 280)
|
||||
{
|
||||
unsigned char extra{0};
|
||||
mInputStream->readNextNBits(4, extra);
|
||||
|
||||
auto length = 67 + 16*(symbol - 277) + extra;
|
||||
const auto valid_dist = readNextDistanceSymbol(distance);
|
||||
copyFromBuffer(length, distance);
|
||||
}
|
||||
else if (symbol <= 284)
|
||||
{
|
||||
unsigned char extra{0};
|
||||
mInputStream->readNextNBits(5, extra);
|
||||
|
||||
auto length = 131 + 32*(symbol - 281) + extra;
|
||||
const auto valid_dist = readNextDistanceSymbol(distance);
|
||||
copyFromBuffer(length, distance);
|
||||
}
|
||||
else if (symbol == 285)
|
||||
{
|
||||
auto length = 258;
|
||||
const auto valid_dist = readNextDistanceSymbol(distance);
|
||||
copyFromBuffer(length, distance);
|
||||
}
|
||||
}
|
||||
|
||||
if (hit_end_stream)
|
||||
{
|
||||
//std::cout << "Found end of stream ok" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool HuffmanStream::decode()
|
||||
{
|
||||
if (!mUsingFixedCodes)
|
||||
{
|
||||
readCodingsTable();
|
||||
|
||||
readSymbols();
|
||||
|
||||
//std::cout << "Got final buffer size " << mBuffer.size() << std::endl;
|
||||
for(unsigned idx=0; idx< 100; idx++)
|
||||
{
|
||||
//std::cout << idx << " | " << mBuffer[idx] << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool found_end_seq{false};
|
||||
unsigned symbol{0};
|
||||
while(!found_end_seq)
|
||||
{
|
||||
bool valid = readNextCodeLengthSymbol(symbol);
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
//std::cout << "Hit unknown symbol - bailing out" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
if (symbol == 256)
|
||||
{
|
||||
found_end_seq = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HuffmanStream::readCodingsTable()
|
||||
{
|
||||
unsigned char h_lit{0};
|
||||
mInputStream->readNextNBits(5, h_lit);
|
||||
mNumLiterals = h_lit + 257;
|
||||
//std::cout << "Got HLIT " << mNumLiterals << std::endl;
|
||||
|
||||
unsigned char h_dist{0};
|
||||
mInputStream->readNextNBits(5, h_dist);
|
||||
mNumDistances = h_dist + 1;
|
||||
//std::cout << "Got HDIST " << mNumDistances << std::endl;
|
||||
|
||||
unsigned char h_clen{0};
|
||||
mInputStream->readNextNBits(4, h_clen);
|
||||
|
||||
unsigned num_code_lengths = h_clen + 4;
|
||||
//std::cout << "Got HCLEN " << num_code_lengths << std::endl;
|
||||
|
||||
auto sequence = std::vector<unsigned char>(num_code_lengths, 0);
|
||||
unsigned char buffer{0};
|
||||
for(unsigned idx = 0; idx< num_code_lengths; idx++)
|
||||
{
|
||||
mInputStream->readNextNBits(3, buffer);
|
||||
//std::cout << "Got coding table value " << idx << " | " << static_cast<int>(buffer) << " | " << ByteUtils::toString(buffer) << std::endl;
|
||||
sequence[idx] = buffer;
|
||||
}
|
||||
|
||||
mCodeLengthTable.setInputLengthSequence(sequence, true);
|
||||
mCodeLengthTable.buildPrefixCodes();
|
||||
|
||||
readCodeLengths();
|
||||
}
|
51
src/base/compression/huffman/HuffmanStream.h
Normal file
51
src/base/compression/huffman/HuffmanStream.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "HuffmanCodeLengthTable.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
class HuffmanStream
|
||||
{
|
||||
|
||||
public:
|
||||
HuffmanStream(BitStream* inputStream, BitStream* outputStream);
|
||||
|
||||
bool decode();
|
||||
|
||||
void generateFixedCodeMapping();
|
||||
|
||||
void setCodeLengthAlphabetLengths(const std::vector<unsigned char>& lengths);
|
||||
|
||||
private:
|
||||
void readCodingsTable();
|
||||
|
||||
void readCodeLengths();
|
||||
|
||||
void readSymbols();
|
||||
|
||||
void copyFromBuffer(unsigned length, unsigned distance);
|
||||
|
||||
bool readNextLiteralSymbol(unsigned& buffer);
|
||||
|
||||
bool readNextDistanceSymbol(unsigned& buffer);
|
||||
|
||||
bool readNextCodeLengthSymbol(unsigned& buffer);
|
||||
|
||||
void addValue(unsigned value, unsigned& count, unsigned& lastValue, std::vector<unsigned char>& literals, unsigned numLiterals, std::vector<unsigned char>& distances);
|
||||
|
||||
BitStream* mInputStream;
|
||||
BitStream* mOutputStream;
|
||||
|
||||
std::vector<unsigned> mBuffer;
|
||||
|
||||
unsigned mNumLiterals{0}; // HLIT + 257
|
||||
unsigned mNumDistances{0}; // HDIST + 1
|
||||
|
||||
bool mUsingFixedCodes{false};
|
||||
HuffmanCodeLengthTable mCodeLengthTable;
|
||||
HuffmanCodeLengthTable mLiteralTable;
|
||||
HuffmanCodeLengthTable mDistanceTable;
|
||||
};
|
121
src/base/compression/huffman/HuffmanTree.cpp
Normal file
121
src/base/compression/huffman/HuffmanTree.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
#include "HuffmanTree.h"
|
||||
|
||||
#include "ByteUtils.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
PrefixCode::PrefixCode(uint32_t data, unsigned length)
|
||||
: mLength(length)
|
||||
{
|
||||
mData = ByteUtils::mirror(data, length);
|
||||
}
|
||||
|
||||
bool PrefixCode::matches(unsigned length, uint32_t code) const
|
||||
{
|
||||
return (mLength == length) && (mData == code);
|
||||
}
|
||||
|
||||
std::string PrefixCode::toString(bool bitsAsRightToLeft) const
|
||||
{
|
||||
if (bitsAsRightToLeft)
|
||||
{
|
||||
if (mLength <=8 )
|
||||
{
|
||||
return ByteUtils::toString(mData).substr(8 - mLength, mLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ByteUtils::toString(mData, mLength);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mLength <=8 )
|
||||
{
|
||||
return ByteUtils::toString(ByteUtils::mirror(mData, mLength)).substr(0, mLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ByteUtils::toString(mData, mLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HuffmanTree::addCodeLengthEntry(unsigned length, const CodeSymbolPair& data)
|
||||
{
|
||||
bool found{false};
|
||||
for (auto& entry : mTable)
|
||||
{
|
||||
if (entry.first == length)
|
||||
{
|
||||
entry.second.push_back(data);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
mTable.push_back({length, {data}});
|
||||
}
|
||||
}
|
||||
|
||||
void HuffmanTree::sortTable()
|
||||
{
|
||||
std::sort(mTable.begin(), mTable.end(), [](CodeLengthData a, CodeLengthData b){return a.first < b.first;});
|
||||
}
|
||||
|
||||
std::optional<HuffmanTree::Symbol> HuffmanTree::findMatch(std::size_t treeIndex, uint32_t code) const
|
||||
{
|
||||
const auto& legth_data = mTable[treeIndex];
|
||||
for(const auto& entry : legth_data.second)
|
||||
{
|
||||
//std::cout << "Checking if " << entry.second << " matches code " << ByteUtils::toString(code) << std::endl;;
|
||||
if (entry.first.matches(legth_data.first, code))
|
||||
{
|
||||
return entry.second;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<PrefixCode> HuffmanTree::getCode(Symbol symbol) const
|
||||
{
|
||||
for(const auto& entry : mTable)
|
||||
{
|
||||
for(const auto& data : entry.second)
|
||||
{
|
||||
if (data.second == symbol)
|
||||
{
|
||||
return data.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::size_t HuffmanTree::getNumCodeLengths() const
|
||||
{
|
||||
return mTable.size();
|
||||
}
|
||||
|
||||
unsigned HuffmanTree::getCodeLength(std::size_t idx) const
|
||||
{
|
||||
return mTable[idx].first;
|
||||
}
|
||||
|
||||
std::string HuffmanTree::dump(bool bitsAsRightToLeft) const
|
||||
{
|
||||
std::stringstream sstr;
|
||||
for (const auto& code_length_data : mTable)
|
||||
{
|
||||
sstr << "Prefix table for Code Length " << code_length_data.first << " has vals: \n";
|
||||
for (const auto& entry : code_length_data.second)
|
||||
{
|
||||
sstr << "Code " << entry.first.toString(bitsAsRightToLeft) << " Symbol: " << entry.second << '\n';
|
||||
}
|
||||
}
|
||||
return sstr.str();
|
||||
}
|
54
src/base/compression/huffman/HuffmanTree.h
Normal file
54
src/base/compression/huffman/HuffmanTree.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
class PrefixCode
|
||||
{
|
||||
public:
|
||||
PrefixCode(uint32_t data, unsigned length);
|
||||
|
||||
std::string toString(bool bitsAsRightToLeft = true) const;
|
||||
|
||||
bool matches(unsigned length, uint32_t code) const;
|
||||
|
||||
uint32_t getData() const
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
unsigned getLength() const
|
||||
{
|
||||
return mLength;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned mLength{0};
|
||||
uint32_t mData{0};
|
||||
};
|
||||
|
||||
class HuffmanTree
|
||||
{
|
||||
public:
|
||||
using Symbol = unsigned;
|
||||
using CodeLength = unsigned;
|
||||
using CodeSymbolPair = std::pair<PrefixCode, Symbol>;
|
||||
using CodeLengthData = std::pair<CodeLength, std::vector<CodeSymbolPair> >;
|
||||
|
||||
void addCodeLengthEntry(unsigned length, const CodeSymbolPair& data);
|
||||
|
||||
std::string dump(bool bitsAsRightToLeft = true) const;
|
||||
|
||||
std::optional<HuffmanTree::Symbol> findMatch(std::size_t treeIndex, uint32_t code) const;
|
||||
|
||||
std::size_t getNumCodeLengths() const;
|
||||
|
||||
unsigned getCodeLength(std::size_t idx) const;
|
||||
|
||||
std::optional<PrefixCode> getCode(Symbol symbol) const;
|
||||
|
||||
void sortTable();
|
||||
private:
|
||||
std::vector<CodeLengthData> mTable;
|
||||
};
|
26
src/base/core/AbstractApp.h
Normal file
26
src/base/core/AbstractApp.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
class IApplicationContext
|
||||
{
|
||||
public:
|
||||
IApplicationContext() = default;
|
||||
virtual ~IApplicationContext() = default;
|
||||
};
|
||||
|
||||
class AbstractApp
|
||||
{
|
||||
public:
|
||||
AbstractApp() = default;
|
||||
|
||||
virtual ~AbstractApp() = default;
|
||||
IApplicationContext* GetApplicationContext() const
|
||||
{
|
||||
return mApplicationContext.get();
|
||||
}
|
||||
protected:
|
||||
std::unique_ptr<IApplicationContext> mApplicationContext{ nullptr };
|
||||
};
|
||||
|
||||
using AbstractAppPtr = std::unique_ptr<AbstractApp>;
|
12
src/base/core/AbstractNamedItem.h
Normal file
12
src/base/core/AbstractNamedItem.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
class AbstractNamedItem
|
||||
{
|
||||
public:
|
||||
~AbstractNamedItem() = default;
|
||||
protected:
|
||||
std::string mName;
|
||||
std::string mSymbol;
|
||||
};
|
12
src/base/core/AbstractWebApp.h
Normal file
12
src/base/core/AbstractWebApp.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "AbstractApp.h"
|
||||
|
||||
class HttpRequest;
|
||||
class HttpResponse;
|
||||
|
||||
class AbstractWebApp : public AbstractApp
|
||||
{
|
||||
public:
|
||||
virtual HttpResponse onHttpRequest(const HttpRequest& request) = 0;
|
||||
};
|
70
src/base/core/CMakeLists.txt
Normal file
70
src/base/core/CMakeLists.txt
Normal file
|
@ -0,0 +1,70 @@
|
|||
set(MODULE_NAME core)
|
||||
|
||||
list(APPEND HEADERS
|
||||
AbstractApp.h
|
||||
AbstractNamedItem.h
|
||||
AbstractWebApp.h
|
||||
Dictionary.h
|
||||
Event.h
|
||||
Color.h
|
||||
CommandLineArgs.h
|
||||
data_structures/RawTree.h
|
||||
data_structures/List.h
|
||||
data_structures/Tree.h
|
||||
encoding/ByteUtils.h
|
||||
encoding/StringUtils.h
|
||||
encoding/UnicodeUtils.h
|
||||
loggers/FileLogger.h
|
||||
file_utilities/Directory.h
|
||||
file_utilities/File.h
|
||||
file_utilities/FileFormats.h
|
||||
file_utilities/PathUtils.h
|
||||
http/HttpResponse.h
|
||||
http/HttpHeader.h
|
||||
http/HttpRequest.h
|
||||
serializers/TomlReader.h
|
||||
Win32BaseIncludes.h
|
||||
)
|
||||
|
||||
list(APPEND SOURCES
|
||||
Event.cpp
|
||||
Dictionary.cpp
|
||||
Color.cpp
|
||||
CommandLineArgs.cpp
|
||||
data_structures/RawTree.cpp
|
||||
data_structures/Tree.cpp
|
||||
loggers/FileLogger.cpp
|
||||
file_utilities/Directory.cpp
|
||||
file_utilities/File.cpp
|
||||
file_utilities/FileFormats.cpp
|
||||
file_utilities/PathUtils.cpp
|
||||
memory/SharedMemory.cpp
|
||||
RandomUtils.cpp
|
||||
encoding/StringUtils.cpp
|
||||
encoding/ByteUtils.cpp
|
||||
encoding/UnicodeUtils.cpp
|
||||
streams/BinaryStream.cpp
|
||||
streams/BitStream.cpp
|
||||
streams/InputBitStream.cpp
|
||||
streams/OutputBitStream.cpp
|
||||
streams/BufferBitStream.cpp
|
||||
http/HttpResponse.cpp
|
||||
http/HttpHeader.cpp
|
||||
http/HttpRequest.cpp
|
||||
serializers/TomlReader.cpp)
|
||||
|
||||
add_library(${MODULE_NAME} SHARED ${SOURCES} ${HEADERS})
|
||||
|
||||
target_include_directories(${MODULE_NAME} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/encoding
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/file_utilities
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/loggers
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/memory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/streams
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/http
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/data_structures
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/serializers
|
||||
)
|
||||
set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src/base)
|
55
src/base/core/Color.cpp
Normal file
55
src/base/core/Color.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#include "Color.h"
|
||||
|
||||
Color::Color(unsigned char r, unsigned char g, unsigned char b, double a)
|
||||
: mR(r),
|
||||
mG(g),
|
||||
mB(b),
|
||||
mAlpha(a)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<Color> Color::Create(unsigned char r, unsigned char g, unsigned char b, double a )
|
||||
{
|
||||
return std::make_unique<Color>(r, g, b, a);
|
||||
}
|
||||
|
||||
std::unique_ptr<Color> Color::Create(const Color& color)
|
||||
{
|
||||
return std::make_unique<Color>(color);
|
||||
}
|
||||
|
||||
unsigned char Color::getR() const
|
||||
{
|
||||
return mR;
|
||||
}
|
||||
|
||||
unsigned char Color::getG() const
|
||||
{
|
||||
return mG;
|
||||
}
|
||||
|
||||
unsigned char Color::getB() const
|
||||
{
|
||||
return mB;
|
||||
}
|
||||
|
||||
double Color::getAlpha() const
|
||||
{
|
||||
return mAlpha;
|
||||
}
|
||||
|
||||
std::vector<double> Color::getAsVectorDouble() const
|
||||
{
|
||||
return { mR / 255.0, mG / 255.0, mB / 255.0, mAlpha };
|
||||
}
|
||||
|
||||
uint32_t Color::getAsUInt32() const
|
||||
{
|
||||
return static_cast<uint32_t>(mB + (mG << 8) + (mR << 16));
|
||||
}
|
||||
|
||||
std::string Color::toString() const
|
||||
{
|
||||
return std::to_string(static_cast<int>(mR)) + "," + std::to_string(static_cast<int>(mG)) + "," + std::to_string(static_cast<int>(mB));
|
||||
}
|
47
src/base/core/Color.h
Normal file
47
src/base/core/Color.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class Color
|
||||
{
|
||||
public:
|
||||
Color(unsigned char r = 0, unsigned char g = 0, unsigned char b = 0, double a = 1.0);
|
||||
|
||||
static std::unique_ptr<Color> Create(unsigned char r, unsigned char g, unsigned char b, double a = 1.0);
|
||||
static std::unique_ptr<Color> Create(const Color& color);
|
||||
|
||||
unsigned char getR() const;
|
||||
unsigned char getG() const;
|
||||
unsigned char getB() const;
|
||||
double getAlpha() const;
|
||||
|
||||
std::vector<double> getAsVectorDouble() const;
|
||||
|
||||
uint32_t getAsUInt32() const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
bool operator==(const Color& rhs) const
|
||||
{
|
||||
return (mR == rhs.mR)
|
||||
&& (mG == rhs.mG)
|
||||
&& (mB == rhs.mB)
|
||||
&& (mAlpha == rhs.mAlpha);
|
||||
}
|
||||
|
||||
bool operator!=(const Color& rhs) const
|
||||
{
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned char mR{0};
|
||||
unsigned char mG{0};
|
||||
unsigned char mB{0};
|
||||
double mAlpha{0.0};
|
||||
};
|
||||
|
||||
using ColorPtr = std::shared_ptr<Color>;
|
||||
using ColorUPtr = std::unique_ptr<Color>;
|
76
src/base/core/CommandLineArgs.cpp
Normal file
76
src/base/core/CommandLineArgs.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include "CommandLineArgs.h"
|
||||
|
||||
#include "UnicodeUtils.h"
|
||||
|
||||
#include "Win32BaseIncludes.h"
|
||||
|
||||
CommandLineArgs::CommandLineArgs()
|
||||
: mArugments(),
|
||||
mLaunchPath()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CommandLineArgs::initialize(CommandLineArgs* args)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int nArgs{ 0 };
|
||||
auto szArglist = ::CommandLineToArgvW(::GetCommandLineW(), &nArgs);
|
||||
if (szArglist == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> windowsArgs(nArgs);
|
||||
for (int idx = 0; idx < nArgs; idx++)
|
||||
{
|
||||
windowsArgs[idx] = UnicodeUtils::utf16ToUtf8String(szArglist[idx]);
|
||||
}
|
||||
::LocalFree(szArglist);
|
||||
|
||||
args->process(windowsArgs);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<CommandLineArgs> CommandLineArgs::Create()
|
||||
{
|
||||
return std::make_unique<CommandLineArgs>();
|
||||
}
|
||||
|
||||
std::filesystem::path CommandLineArgs::getLaunchPath()
|
||||
{
|
||||
return mLaunchPath;
|
||||
}
|
||||
|
||||
void CommandLineArgs::recordLaunchPath()
|
||||
{
|
||||
mLaunchPath = std::filesystem::current_path();
|
||||
}
|
||||
|
||||
void CommandLineArgs::process(int argc, char *argv[])
|
||||
{
|
||||
for(int idx=0; idx<argc; idx++)
|
||||
{
|
||||
mArugments.push_back(std::string(argv[idx]));
|
||||
}
|
||||
}
|
||||
|
||||
void CommandLineArgs::process(const std::vector<std::string>& args)
|
||||
{
|
||||
mArugments = args;
|
||||
}
|
||||
|
||||
std::vector<std::string> CommandLineArgs::getUserArgs() const
|
||||
{
|
||||
std::vector<std::string> user_args;
|
||||
for(unsigned idx=1; idx<mArugments.size(); idx++)
|
||||
{
|
||||
user_args.push_back(mArugments[idx]);
|
||||
}
|
||||
return user_args;
|
||||
}
|
||||
|
||||
const std::vector<std::string> CommandLineArgs::getArgs() const
|
||||
{
|
||||
return mArugments;
|
||||
}
|
33
src/base/core/CommandLineArgs.h
Normal file
33
src/base/core/CommandLineArgs.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
class CommandLineArgs
|
||||
{
|
||||
public:
|
||||
CommandLineArgs();
|
||||
|
||||
static std::unique_ptr<CommandLineArgs> Create();
|
||||
|
||||
std::filesystem::path getLaunchPath();
|
||||
|
||||
const std::vector<std::string> getArgs() const;
|
||||
|
||||
std::vector<std::string> getUserArgs() const;
|
||||
|
||||
static void initialize(CommandLineArgs* args);
|
||||
|
||||
void process(int argc, char *argv[]);
|
||||
|
||||
void process(const std::vector<std::string>& args);
|
||||
|
||||
void recordLaunchPath();
|
||||
private:
|
||||
std::vector<std::string> mArugments;
|
||||
std::filesystem::path mLaunchPath;
|
||||
};
|
||||
|
||||
using CommandLineArgsUPtr = std::unique_ptr<CommandLineArgs>;
|
56
src/base/core/Dictionary.cpp
Normal file
56
src/base/core/Dictionary.cpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#include "Dictionary.h"
|
||||
|
||||
bool Dictionary::hasKey(const std::string& key) const
|
||||
{
|
||||
return hasStringKey(key) || hasDictKey(key);
|
||||
}
|
||||
|
||||
bool Dictionary::hasStringKey(const std::string& key) const
|
||||
{
|
||||
return mStringData.count(key) > 0;
|
||||
}
|
||||
|
||||
bool Dictionary::hasDictKey(const std::string& key) const
|
||||
{
|
||||
return mDictData.count(key) > 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> Dictionary::getStringKeys() const
|
||||
{
|
||||
std::vector<std::string> keys;
|
||||
for (const auto& item : mStringData)
|
||||
{
|
||||
keys.push_back(item.first);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
std::vector<std::string> Dictionary::getDictKeys() const
|
||||
{
|
||||
std::vector<std::string> keys;
|
||||
for (const auto& item : mDictData)
|
||||
{
|
||||
keys.push_back(item.first);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
Dictionary* Dictionary::getDict(const std::string& key) const
|
||||
{
|
||||
return mDictData.at(key).get();
|
||||
}
|
||||
|
||||
std::string Dictionary::getItem(const std::string& key) const
|
||||
{
|
||||
return mStringData.at(key);
|
||||
}
|
||||
|
||||
void Dictionary::addStringItem(const std::string& key, const std::string& item)
|
||||
{
|
||||
mStringData[key] = item;
|
||||
}
|
||||
|
||||
void Dictionary::addDictItem(const std::string& key, std::unique_ptr<Dictionary> dict)
|
||||
{
|
||||
mDictData[key] = std::move(dict);
|
||||
}
|
34
src/base/core/Dictionary.h
Normal file
34
src/base/core/Dictionary.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
class Dictionary
|
||||
{
|
||||
public:
|
||||
Dictionary() = default;
|
||||
virtual ~Dictionary() = default;
|
||||
|
||||
void addStringItem(const std::string& key, const std::string& item);
|
||||
|
||||
void addDictItem(const std::string& key, std::unique_ptr<Dictionary> dict);
|
||||
|
||||
Dictionary* getDict(const std::string& key) const;
|
||||
|
||||
std::vector<std::string> getDictKeys() const;
|
||||
|
||||
std::vector<std::string> getStringKeys() const;
|
||||
|
||||
std::string getItem(const std::string& key) const;
|
||||
|
||||
bool hasKey(const std::string& key) const;
|
||||
|
||||
bool hasStringKey(const std::string& key) const;
|
||||
|
||||
bool hasDictKey(const std::string& key) const;
|
||||
protected:
|
||||
|
||||
std::map<std::string, std::string> mStringData;
|
||||
std::map<std::string, std::unique_ptr<Dictionary> > mDictData;
|
||||
};
|
11
src/base/core/Event.cpp
Normal file
11
src/base/core/Event.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include "Event.h"
|
||||
|
||||
Event::Event()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Event::~Event()
|
||||
{
|
||||
|
||||
}
|
8
src/base/core/Event.h
Normal file
8
src/base/core/Event.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
class Event
|
||||
{
|
||||
public:
|
||||
|
||||
Event();
|
||||
|
||||
~Event();
|
||||
};
|
22
src/base/core/RandomUtils.cpp
Normal file
22
src/base/core/RandomUtils.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include "RandomUtils.h"
|
||||
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
std::vector<unsigned> RandomUtils::getRandomVecUnsigned(std::size_t size)
|
||||
{
|
||||
std::random_device rnd_device;
|
||||
|
||||
std::mt19937 mersenne_engine {rnd_device()};
|
||||
std::uniform_int_distribution<unsigned> dist {0, 9};
|
||||
|
||||
auto generator = [&dist, &mersenne_engine]()
|
||||
{
|
||||
return dist(mersenne_engine);
|
||||
};
|
||||
|
||||
std::vector<unsigned> vec(size);
|
||||
std::generate(std::begin(vec), std::end(vec), generator);
|
||||
return vec;
|
||||
}
|
10
src/base/core/RandomUtils.h
Normal file
10
src/base/core/RandomUtils.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
class RandomUtils
|
||||
{
|
||||
public:
|
||||
|
||||
static std::vector<unsigned> getRandomVecUnsigned(std::size_t size);
|
||||
};
|
13
src/base/core/Win32BaseIncludes.h
Normal file
13
src/base/core/Win32BaseIncludes.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef UNICODE
|
||||
#define UNICODE
|
||||
#endif
|
||||
|
||||
//#ifndef WIN32_LEAN_AND_MEAN
|
||||
//#define WIN32_LEAN_AND_MEAN
|
||||
//#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Windows.h"
|
||||
#endif
|
64
src/base/core/data_structures/CircleBuffer.h
Normal file
64
src/base/core/data_structures/CircleBuffer.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
template<typename T>
|
||||
class CircleBuffer
|
||||
{
|
||||
public:
|
||||
CircleBuffer(std::size_t size)
|
||||
: mData(size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void addItem(const T& item)
|
||||
{
|
||||
if (mEndPointer < mData.size())
|
||||
{
|
||||
mData[mEndPointer] = item;
|
||||
mEndPointer++;
|
||||
}
|
||||
else
|
||||
{
|
||||
mData[mStartPointer] = item;
|
||||
if (mStartPointer < mData.size() - 1)
|
||||
{
|
||||
mStartPointer++;
|
||||
}
|
||||
else
|
||||
{
|
||||
mStartPointer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t getNumItems() const
|
||||
{
|
||||
return mEndPointer;
|
||||
}
|
||||
|
||||
const T& getItem(std::size_t index) const
|
||||
{
|
||||
if (mEndPointer < mData.size())
|
||||
{
|
||||
return mData[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
auto offset = mStartPointer + index;
|
||||
if (offset >= mData.size())
|
||||
{
|
||||
offset -= mData.size();
|
||||
}
|
||||
return mData[offset];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t mStartPointer{0};
|
||||
std::size_t mEndPointer{0};
|
||||
std::vector<T> mData;
|
||||
};
|
69
src/base/core/data_structures/List.h
Normal file
69
src/base/core/data_structures/List.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
class AbstractList
|
||||
{
|
||||
public:
|
||||
virtual ~AbstractList() = default;
|
||||
|
||||
virtual std::size_t getLength() const = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class List : public AbstractList
|
||||
{
|
||||
public:
|
||||
List() = default;
|
||||
|
||||
void initializeTo(std::size_t size, T value)
|
||||
{
|
||||
mData = std::vector<T>(size, value);
|
||||
}
|
||||
|
||||
const T* getDataPtr() const
|
||||
{
|
||||
return mData.data();
|
||||
}
|
||||
|
||||
const std::vector<T>& getData() const
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
T getItem(std::size_t index) const
|
||||
{
|
||||
if (index < mData.size())
|
||||
{
|
||||
return mData[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto msg = "Tried to access out of range index: " + std::to_string(index) + " with size " + std::to_string(mData.size());
|
||||
throw std::out_of_range(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void setData(const std::vector<T>& data)
|
||||
{
|
||||
mData = data;
|
||||
}
|
||||
|
||||
void setItem(std::size_t index, T item)
|
||||
{
|
||||
if (index < mData.size())
|
||||
{
|
||||
mData[index] = item;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t getLength() const override
|
||||
{
|
||||
return mData.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<T> mData;
|
||||
};
|
0
src/base/core/data_structures/RawTree.cpp
Normal file
0
src/base/core/data_structures/RawTree.cpp
Normal file
97
src/base/core/data_structures/RawTree.h
Normal file
97
src/base/core/data_structures/RawTree.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
template<typename T>
|
||||
class RawNode
|
||||
{
|
||||
public:
|
||||
RawNode(T data)
|
||||
: mData(data)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
~RawNode()
|
||||
{
|
||||
if(mLeftChild)
|
||||
{
|
||||
delete mLeftChild;
|
||||
}
|
||||
|
||||
if(mRightChild)
|
||||
{
|
||||
delete mRightChild;
|
||||
}
|
||||
}
|
||||
|
||||
void addChild(RawNode* child)
|
||||
{
|
||||
if(!mLeftChild)
|
||||
{
|
||||
mLeftChild = child;
|
||||
}
|
||||
else
|
||||
{
|
||||
mRightChild = child;
|
||||
}
|
||||
}
|
||||
|
||||
bool isLeaf() const
|
||||
{
|
||||
return !mLeftChild && !mRightChild;
|
||||
}
|
||||
|
||||
T getData() const
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
RawNode* getLeftChild() const
|
||||
{
|
||||
return mLeftChild;
|
||||
}
|
||||
|
||||
RawNode* getRightChild() const
|
||||
{
|
||||
return mRightChild;
|
||||
}
|
||||
|
||||
private:
|
||||
T mData;
|
||||
unsigned char mTag{0};
|
||||
RawNode* mLeftChild{nullptr};
|
||||
RawNode* mRightChild{nullptr};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class RawTree
|
||||
{
|
||||
public:
|
||||
RawTree()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~RawTree()
|
||||
{
|
||||
if (mRootNode)
|
||||
{
|
||||
delete mRootNode;
|
||||
}
|
||||
}
|
||||
|
||||
void addRootNode(RawNode<T>* root)
|
||||
{
|
||||
mRootNode = root;
|
||||
}
|
||||
|
||||
RawNode<T>* getRootNode() const
|
||||
{
|
||||
return mRootNode;
|
||||
}
|
||||
|
||||
private:
|
||||
RawNode<T>* mRootNode{nullptr};
|
||||
};
|
0
src/base/core/data_structures/Tree.cpp
Normal file
0
src/base/core/data_structures/Tree.cpp
Normal file
76
src/base/core/data_structures/Tree.h
Normal file
76
src/base/core/data_structures/Tree.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
template<typename T>
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
Node(T data)
|
||||
: mData(data)
|
||||
{}
|
||||
|
||||
void addChild(std::unique_ptr<Node> child)
|
||||
{
|
||||
if(!mLeftChild)
|
||||
{
|
||||
mLeftChild = std::move(child);
|
||||
}
|
||||
else
|
||||
{
|
||||
mRightChild = std::move(child);
|
||||
}
|
||||
}
|
||||
|
||||
bool isLeaf() const
|
||||
{
|
||||
return !mLeftChild && !mRightChild;
|
||||
}
|
||||
|
||||
T getData() const
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
Node* getLeftChild() const
|
||||
{
|
||||
return mLeftChild.get();
|
||||
}
|
||||
|
||||
Node* getRightChild() const
|
||||
{
|
||||
return mRightChild.get();
|
||||
}
|
||||
|
||||
private:
|
||||
T mData;
|
||||
unsigned char mTag{0};
|
||||
std::unique_ptr<Node> mLeftChild;
|
||||
std::unique_ptr<Node> mRightChild;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using NodePtr = std::unique_ptr<Node<T> >;
|
||||
|
||||
template<typename T>
|
||||
class Tree
|
||||
{
|
||||
public:
|
||||
Tree()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void addRootNode(NodePtr<T> root)
|
||||
{
|
||||
mRootNode = std::move(root);
|
||||
}
|
||||
|
||||
Node<T>* getRootNode() const
|
||||
{
|
||||
return mRootNode.get();
|
||||
}
|
||||
|
||||
private:
|
||||
NodePtr<T> mRootNode;
|
||||
};
|
197
src/base/core/encoding/ByteUtils.cpp
Normal file
197
src/base/core/encoding/ByteUtils.cpp
Normal file
|
@ -0,0 +1,197 @@
|
|||
#include "ByteUtils.h"
|
||||
|
||||
|
||||
bool ByteUtils::MostSignificantBitIsOne(char c)
|
||||
{
|
||||
return c & (1 << 7);
|
||||
}
|
||||
|
||||
ByteUtils::Word ByteUtils::GetWordFirstBit(const Word word)
|
||||
{
|
||||
return word & ByteUtils::WORD_FIRST_BIT;
|
||||
};
|
||||
|
||||
ByteUtils::Word ByteUtils::GetWordLastByte(const Word word)
|
||||
{
|
||||
return word & ByteUtils::WORD_LAST_BYTE;
|
||||
}
|
||||
|
||||
unsigned char ByteUtils::getHigherNBits(unsigned char input, unsigned num)
|
||||
{
|
||||
return input >> (8 - num);
|
||||
}
|
||||
|
||||
unsigned char ByteUtils::getByteN(uint32_t input, unsigned n)
|
||||
{
|
||||
return (input << 8*n) >> 24;
|
||||
}
|
||||
|
||||
uint32_t ByteUtils::mirror(uint32_t byte, unsigned length)
|
||||
{
|
||||
uint32_t ret{0};
|
||||
for(unsigned idx=0; idx<length; idx++)
|
||||
{
|
||||
if (getBitN(byte, length - 1 - idx))
|
||||
{
|
||||
ret |= (0x01 << idx);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned char ByteUtils::getLowerNBits(uint32_t input, unsigned num)
|
||||
{
|
||||
switch (num)
|
||||
{
|
||||
case 1:
|
||||
return input & 0x01;
|
||||
case 2:
|
||||
return input & 0x03;
|
||||
case 3:
|
||||
return input & 0x07;
|
||||
case 4:
|
||||
return input & 0x0F;
|
||||
case 5:
|
||||
return input & 0x1F;
|
||||
case 6:
|
||||
return input & 0x3F;
|
||||
case 7:
|
||||
return input & 0x7F;
|
||||
case 8:
|
||||
return static_cast<unsigned char>(input);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char ByteUtils::getTwoBitsAtN(unsigned char input, unsigned n)
|
||||
{
|
||||
return (input & (0x03 << n)) >> n;
|
||||
}
|
||||
|
||||
unsigned char ByteUtils::getMBitsAtN(unsigned char input, unsigned m, unsigned n)
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case 1:
|
||||
return (input & (0x01 << n)) >> n;
|
||||
case 2:
|
||||
return (input & (0x03 << n)) >> n;
|
||||
case 3:
|
||||
return (input & (0x07 << n)) >> n;
|
||||
case 4:
|
||||
return (input & (0x0F << n)) >> n;
|
||||
case 5:
|
||||
return (input & (0x1F << n)) >> n;
|
||||
case 6:
|
||||
return (input & (0x3F << n)) >> n;
|
||||
case 7:
|
||||
return (input & (0x7F << n)) >> n;
|
||||
case 8:
|
||||
return input;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ByteUtils::getBitN(uint32_t input, unsigned n)
|
||||
{
|
||||
return input & (1 << n);
|
||||
}
|
||||
|
||||
unsigned char ByteUtils::getFromString(const std::string& string)
|
||||
{
|
||||
unsigned char ret{0};
|
||||
|
||||
if (string.length() < 8)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
for(unsigned idx=0; idx<8; idx++)
|
||||
{
|
||||
if (string[idx] == '1')
|
||||
{
|
||||
ret |= (0x01 << (7 - idx));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string ByteUtils::toString(uint32_t input, unsigned length)
|
||||
{
|
||||
std::string ret;
|
||||
if (length > 8)
|
||||
{
|
||||
unsigned overshoot = length - 8;
|
||||
for(unsigned idx=0; idx<overshoot; idx++)
|
||||
{
|
||||
ret += getBitN(input, length - 1 - idx) ? '1' : '0';
|
||||
}
|
||||
ret += "-";
|
||||
for(unsigned idx=0; idx<8; idx++)
|
||||
{
|
||||
ret += getBitN(input, 7 - idx) ? '1' : '0';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(unsigned idx=0; idx<length; idx++)
|
||||
{
|
||||
ret += getBitN(input, 7 - idx) ? '1' : '0';
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ByteUtils::ReverseBuffer(char* buffer, char* reverse, unsigned size, unsigned targetSize)
|
||||
{
|
||||
for(unsigned idx=0; idx<targetSize; idx++)
|
||||
{
|
||||
if (idx < size)
|
||||
{
|
||||
reverse[idx] = buffer[size - 1 -idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
reverse[idx] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ByteUtils::Word ByteUtils::ToWord(char* buffer, bool reverse)
|
||||
{
|
||||
return ToType<Word>(buffer, reverse);
|
||||
}
|
||||
|
||||
ByteUtils::DWord ByteUtils::ToDWord(char* buffer, bool reverse)
|
||||
{
|
||||
return ToType<DWord>(buffer, reverse);
|
||||
}
|
||||
|
||||
ByteUtils::QWord ByteUtils::ToQWord(char* buffer, bool reverse)
|
||||
{
|
||||
return ToType<QWord>(buffer, reverse);
|
||||
}
|
||||
|
||||
bool ByteUtils::Compare(char* buffer, const char* tag, unsigned size)
|
||||
{
|
||||
for(unsigned idx=0; idx<size; idx++)
|
||||
{
|
||||
if(tag[idx] != buffer[idx])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ByteUtils::CompareDWords(char* buffer, const char* tag)
|
||||
{
|
||||
return Compare(buffer, tag, sizeof(DWord));
|
||||
}
|
||||
|
||||
bool ByteUtils::CompareWords(char* buffer, const char* tag)
|
||||
{
|
||||
return Compare(buffer, tag, sizeof(Word));
|
||||
}
|
72
src/base/core/encoding/ByteUtils.h
Normal file
72
src/base/core/encoding/ByteUtils.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
class ByteUtils
|
||||
{
|
||||
public:
|
||||
using Word = int16_t;
|
||||
using DWord = int32_t;
|
||||
using QWord = int64_t;
|
||||
|
||||
static bool MostSignificantBitIsOne(char c);
|
||||
|
||||
static Word GetWordFirstBit(const Word word);
|
||||
|
||||
static Word GetWordLastByte(const Word word);
|
||||
|
||||
static unsigned char getByteN(uint32_t input, unsigned n);
|
||||
|
||||
static unsigned char getHigherNBits(unsigned char input, unsigned num);
|
||||
|
||||
static unsigned char getLowerNBits(uint32_t input, unsigned num);
|
||||
|
||||
static unsigned char getTwoBitsAtN(unsigned char input, unsigned n);
|
||||
|
||||
static unsigned char getMBitsAtN(unsigned char input, unsigned m, unsigned n);
|
||||
|
||||
static bool getBitN(uint32_t input, unsigned n);
|
||||
|
||||
static unsigned char getFromString(const std::string& string);
|
||||
|
||||
static std::string toString(uint32_t input, unsigned length = 8);
|
||||
|
||||
static uint32_t mirror(uint32_t input, unsigned length=0);
|
||||
|
||||
static void ReverseBuffer(char* buffer, char* reverse, unsigned size, unsigned targetSize);
|
||||
|
||||
template<typename T>
|
||||
static T ToType(char* buffer, bool reverse = true)
|
||||
{
|
||||
T result {0};
|
||||
if(reverse)
|
||||
{
|
||||
char reversed[sizeof(T)];
|
||||
ReverseBuffer(buffer, reversed, sizeof(T), sizeof(T));
|
||||
std::memcpy(&result, reversed, sizeof(T));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(&result, buffer, sizeof(T));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static Word ToWord(char* buffer, bool reverse = true);
|
||||
|
||||
static DWord ToDWord(char* buffer, bool reverse = true);
|
||||
|
||||
static QWord ToQWord(char* buffer, bool reverse = true);
|
||||
|
||||
static bool Compare(char* buffer, const char* tag, unsigned size);
|
||||
|
||||
static bool CompareDWords(char* buffer, const char* tag);
|
||||
|
||||
static bool CompareWords(char* buffer, const char* tag);
|
||||
|
||||
static const int BYTE_FIRST_BIT = 0x40; // 1000 0000
|
||||
static const Word WORD_FIRST_BIT = static_cast<Word>(0x8000); // 1000 0000 - 0000 0000
|
||||
static const Word WORD_LAST_BYTE = 0x00FF; // 0000 0000 - 1111 1111
|
||||
};
|
209
src/base/core/encoding/StringUtils.cpp
Normal file
209
src/base/core/encoding/StringUtils.cpp
Normal file
|
@ -0,0 +1,209 @@
|
|||
#include "StringUtils.h"
|
||||
|
||||
#include <locale>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
bool StringUtils::isAlphaNumeric(char c)
|
||||
{
|
||||
return std::isalnum(c);
|
||||
}
|
||||
|
||||
bool StringUtils::isSpace(char c)
|
||||
{
|
||||
return std::isspace(c);
|
||||
}
|
||||
|
||||
bool StringUtils::isAlphabetical(char c)
|
||||
{
|
||||
return std::isalpha(c);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> StringUtils::toBytes(const std::string& input)
|
||||
{
|
||||
return {input.begin(), input.end()};
|
||||
}
|
||||
|
||||
std::string StringUtils::toString(const std::vector<unsigned char>& bytes)
|
||||
{
|
||||
return {bytes.begin(), bytes.end()};
|
||||
}
|
||||
|
||||
std::vector<std::string> StringUtils::toLines(const std::string& input)
|
||||
{
|
||||
auto result = std::vector<std::string>{};
|
||||
auto ss = std::stringstream{input};
|
||||
for (std::string line; std::getline(ss, line, '\n');)
|
||||
{
|
||||
result.push_back(line);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StringUtils::isWhitespaceOnly(const std::string& input)
|
||||
{
|
||||
if (input.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::all_of(input.cbegin(), input.cend(), [](char c){ return std::isspace(c); });
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t StringUtils::countFirstConsecutiveHits(const std::string& input, char c)
|
||||
{
|
||||
auto found_id = input.find(c);
|
||||
if(found_id == std::string::npos)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::size_t count = 1;
|
||||
for(std::size_t idx=found_id+1; idx<input.size(); idx++)
|
||||
{
|
||||
if(input[idx] == c)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
std::string StringUtils::stripSurroundingWhitepsace(const std::string& input)
|
||||
{
|
||||
if (input.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::size_t first_nonspace = 0;
|
||||
std::size_t last_nonspace = input.size() - 1;
|
||||
for (std::size_t idx = 0; idx < input.size(); idx++)
|
||||
{
|
||||
if (!std::isspace(input[idx]))
|
||||
{
|
||||
first_nonspace = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (first_nonspace == last_nonspace)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
for (std::size_t idx = last_nonspace; idx > 0; idx--)
|
||||
{
|
||||
if (!std::isspace(input[idx]))
|
||||
{
|
||||
last_nonspace = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return input.substr(first_nonspace, last_nonspace-first_nonspace + 1);
|
||||
}
|
||||
|
||||
std::vector<std::string> StringUtils::split(const std::string& input)
|
||||
{
|
||||
std::vector<std::string> substrings;
|
||||
std::string working_string;
|
||||
std::locale loc;
|
||||
bool last_was_nonspace{ false };
|
||||
for (auto c : input)
|
||||
{
|
||||
if (std::isspace(c, loc))
|
||||
{
|
||||
if (last_was_nonspace)
|
||||
{
|
||||
substrings.push_back(working_string);
|
||||
working_string = "";
|
||||
last_was_nonspace = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
last_was_nonspace = true;
|
||||
working_string += c;
|
||||
}
|
||||
}
|
||||
if (!working_string.empty())
|
||||
{
|
||||
substrings.push_back(working_string);
|
||||
}
|
||||
return substrings;
|
||||
}
|
||||
|
||||
std::string StringUtils::toLower(const std::string& s)
|
||||
{
|
||||
std::string ret;
|
||||
std::transform(s.begin(), s.end(), ret.begin(), [](unsigned char c){ return std::tolower(c); });
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string StringUtils::toPaddedString(unsigned numBytes, unsigned entry)
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << std::setfill('0') << std::setw(numBytes) << entry;
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
std::string StringUtils::stripQuotes(const std::string& input)
|
||||
{
|
||||
if (input.size() < 3)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
std::size_t start_index = 0;
|
||||
std::size_t end_index = input.size()-1;
|
||||
if (input[start_index] == '"')
|
||||
{
|
||||
start_index = 1;
|
||||
}
|
||||
if (input[end_index] == '"')
|
||||
{
|
||||
end_index = end_index - 1;
|
||||
}
|
||||
return input.substr(start_index, end_index - start_index + 1);
|
||||
}
|
||||
|
||||
std::string StringUtils::removeUpTo(const std::string& input, const std::string& prefix)
|
||||
{
|
||||
std::size_t found = input.find(prefix);
|
||||
if (found != std::string::npos)
|
||||
{
|
||||
return input.substr(found + prefix.size(), input.size()-found);
|
||||
}
|
||||
else
|
||||
{
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
bool StringUtils::startsWith(const std::string& input, const std::string& prefix, bool ignoreWhitespace)
|
||||
{
|
||||
if(ignoreWhitespace)
|
||||
{
|
||||
const auto loc = input.find(prefix);
|
||||
if (loc == std::string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return isWhitespaceOnly(input.substr(0, loc));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return input.find(prefix) == 0;
|
||||
}
|
||||
}
|
48
src/base/core/encoding/StringUtils.h
Normal file
48
src/base/core/encoding/StringUtils.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class StringUtils
|
||||
{
|
||||
public:
|
||||
static constexpr char LEFT_BRACKET = '<';
|
||||
static constexpr char RIGHT_BRACKET = '>';
|
||||
static constexpr char FORWARD_SLASH = '/';
|
||||
static constexpr char BACK_SLASH = '\\';
|
||||
static constexpr char QUESTION_MARK = '?';
|
||||
static constexpr char EQUALS = '=';
|
||||
static constexpr char DOUBLE_QUOTE = '"';
|
||||
static constexpr char SINGLE_QUOTE = '\'';
|
||||
static constexpr char COLON = ':';
|
||||
|
||||
static std::size_t countFirstConsecutiveHits(const std::string& input, char c);
|
||||
|
||||
static bool isAlphaNumeric(char c);
|
||||
|
||||
static bool isAlphabetical(char c);
|
||||
|
||||
static bool isSpace(char c);
|
||||
|
||||
static bool isWhitespaceOnly(const std::string& input);
|
||||
|
||||
static std::string removeUpTo(const std::string& input, const std::string& prefix);
|
||||
|
||||
static std::vector<std::string> split(const std::string& input);
|
||||
|
||||
static bool startsWith(const std::string& input, const std::string& prefix, bool ignoreWhitespace = false);
|
||||
|
||||
static std::string stripSurroundingWhitepsace(const std::string& input);
|
||||
|
||||
static std::string stripQuotes(const std::string& input);
|
||||
|
||||
static std::vector<unsigned char> toBytes(const std::string& input);
|
||||
|
||||
static std::string toLower(const std::string& s);
|
||||
|
||||
static std::vector<std::string> toLines(const std::string& input);
|
||||
|
||||
static std::string toPaddedString(unsigned numBytes, unsigned entry);
|
||||
|
||||
static std::string toString(const std::vector<unsigned char>& bytes);
|
||||
};
|
41
src/base/core/encoding/UnicodeUtils.cpp
Normal file
41
src/base/core/encoding/UnicodeUtils.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include "UnicodeUtils.h"
|
||||
|
||||
#include "Win32BaseIncludes.h"
|
||||
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
std::string UnicodeUtils::utf16ToUtf8String(const std::wstring& input)
|
||||
{
|
||||
if (input.empty())
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const auto size = ::WideCharToMultiByte(CP_UTF8, 0, &input[0], static_cast<int>(input.size()), nullptr, 0, nullptr, nullptr);
|
||||
|
||||
std::string result(size, 0);
|
||||
::WideCharToMultiByte(CP_UTF8, 0, &input[0], static_cast<int>(input.size()), &result[0], size, nullptr, nullptr);
|
||||
return result;
|
||||
#else
|
||||
throw std::logic_error("Not implemented");
|
||||
#endif
|
||||
}
|
||||
|
||||
std::wstring UnicodeUtils::utf8ToUtf16WString(const std::string& input)
|
||||
{
|
||||
if (input.empty())
|
||||
{
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const auto charsNeeded = ::MultiByteToWideChar(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
|
||||
std::vector<wchar_t> buffer(charsNeeded);
|
||||
const auto charsConverted = ::MultiByteToWideChar(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), &buffer[0], static_cast<int>(buffer.size()));
|
||||
return std::wstring(&buffer[0], charsConverted);
|
||||
#else
|
||||
throw std::logic_error("Not implemented");
|
||||
#endif
|
||||
}
|
11
src/base/core/encoding/UnicodeUtils.h
Normal file
11
src/base/core/encoding/UnicodeUtils.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
class UnicodeUtils
|
||||
{
|
||||
public:
|
||||
static std::string utf16ToUtf8String(const std::wstring& input);
|
||||
|
||||
static std::wstring utf8ToUtf16WString(const std::string& input);
|
||||
};
|
22
src/base/core/file_utilities/Directory.cpp
Normal file
22
src/base/core/file_utilities/Directory.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include "Directory.h"
|
||||
|
||||
std::vector<Path> Directory::getFilesWithExtension(const Path& path, const std::string& extension, bool recursive)
|
||||
{
|
||||
std::vector<Path> paths;
|
||||
if (std::filesystem::is_directory(path))
|
||||
{
|
||||
for (const auto& entry : std::filesystem::directory_iterator(path))
|
||||
{
|
||||
if (std::filesystem::is_regular_file(entry) && entry.path().extension() == extension)
|
||||
{
|
||||
paths.push_back(entry.path());
|
||||
}
|
||||
else if(recursive && std::filesystem::is_directory(entry))
|
||||
{
|
||||
const auto child_paths = getFilesWithExtension(entry, extension, recursive);
|
||||
paths.insert(paths.end(), child_paths.begin(), child_paths.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
13
src/base/core/file_utilities/Directory.h
Normal file
13
src/base/core/file_utilities/Directory.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
class Directory
|
||||
{
|
||||
public:
|
||||
|
||||
static std::vector<Path> getFilesWithExtension(const Path& path, const std::string& extension, bool recursive=false);
|
||||
};
|
220
src/base/core/file_utilities/File.cpp
Normal file
220
src/base/core/file_utilities/File.cpp
Normal file
|
@ -0,0 +1,220 @@
|
|||
#include "File.h"
|
||||
|
||||
#include "FileLogger.h"
|
||||
#include "ByteUtils.h"
|
||||
|
||||
#include <streambuf>
|
||||
#include <sstream>
|
||||
|
||||
File::File(std::filesystem::path path)
|
||||
: mFullPath(path),
|
||||
mInHandle(),
|
||||
mOutHandle()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
File::~File()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
std::string File::getExtension() const
|
||||
{
|
||||
return mFullPath.extension().string();
|
||||
}
|
||||
|
||||
std::string File::dumpBinary()
|
||||
{
|
||||
open(AccessMode::Read);
|
||||
|
||||
std::stringstream sstr;
|
||||
sstr << "Count | Binary | Decimal | ASCII \n";
|
||||
unsigned count = 0;
|
||||
while(mInHandle->peek() != EOF)
|
||||
{
|
||||
const unsigned char val = static_cast<unsigned char>(mInHandle->get());
|
||||
const unsigned char ascii_val = std::isalpha(val) ? val : '.';
|
||||
sstr << count << " | " << ByteUtils::toString(val) << " | " << static_cast<int>(val) << " | " << ascii_val << '\n';
|
||||
if (count < 0 && count % 10 == 0)
|
||||
{
|
||||
sstr << "\n";
|
||||
}
|
||||
count++;
|
||||
}
|
||||
const auto out = sstr.str();
|
||||
close();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ifstream* File::getInHandle() const
|
||||
{
|
||||
return mInHandle.get();
|
||||
}
|
||||
|
||||
std::ofstream* File::getOutHandle() const
|
||||
{
|
||||
return mOutHandle.get();
|
||||
}
|
||||
|
||||
std::optional<unsigned char> File::readNextByte()
|
||||
{
|
||||
if (!mInHandle)
|
||||
{
|
||||
if (!open(AccessMode::Read))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
if (mInHandle->good())
|
||||
{
|
||||
if (auto val = mInHandle->get(); val == EOF)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
return val;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
bool File::open(AccessMode accessMode)
|
||||
{
|
||||
if (mFullPath.is_absolute() && !std::filesystem::exists(mFullPath.parent_path()))
|
||||
{
|
||||
std::filesystem::create_directories(mFullPath.parent_path());
|
||||
}
|
||||
|
||||
if(accessMode == AccessMode::Read)
|
||||
{
|
||||
auto flags = std::ifstream::in;
|
||||
mInHandle = std::make_unique<std::ifstream>();
|
||||
mInHandle->open(mFullPath, flags);
|
||||
if (!mInHandle->is_open())
|
||||
{
|
||||
MLOG_ERROR("Failed to open file at :" << mFullPath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto flags = std::ofstream::out;
|
||||
mOutHandle = std::make_unique<std::ofstream>();
|
||||
mOutHandle->open(mFullPath, flags);
|
||||
if (!mOutHandle->is_open())
|
||||
{
|
||||
MLOG_ERROR("Failed to open file at :" << mFullPath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void File::close()
|
||||
{
|
||||
if(mOutHandle)
|
||||
{
|
||||
mOutHandle->close();
|
||||
}
|
||||
if(mInHandle)
|
||||
{
|
||||
mInHandle->close();
|
||||
}
|
||||
}
|
||||
|
||||
FileFormat::Format File::inferFormat() const
|
||||
{
|
||||
const auto extension = getExtension();
|
||||
return FileFormat::inferFormat(extension);
|
||||
}
|
||||
|
||||
void File::writeText(const std::string& text)
|
||||
{
|
||||
bool had_to_open{false};
|
||||
if (!mOutHandle)
|
||||
{
|
||||
had_to_open = true;
|
||||
if (!open(File::AccessMode::Write))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
(*mOutHandle) << text;
|
||||
|
||||
if (had_to_open)
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> File::readLines()
|
||||
{
|
||||
std::vector<std::string> content;
|
||||
|
||||
if (!pathExists())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if(!open(AccessMode::Read))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string str;
|
||||
while(std::getline(*mInHandle, str))
|
||||
{
|
||||
content.push_back(str);
|
||||
}
|
||||
|
||||
close();
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string File::read()
|
||||
{
|
||||
if (!pathExists())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if(!open(AccessMode::Read))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << mInHandle->rdbuf();
|
||||
|
||||
close();
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
std::string File::readText()
|
||||
{
|
||||
if (!pathExists())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if(!open(AccessMode::Read))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string str((std::istreambuf_iterator<char>(*mInHandle)),
|
||||
std::istreambuf_iterator<char>());
|
||||
return str;
|
||||
}
|
||||
|
||||
bool File::pathExists() const
|
||||
{
|
||||
return std::filesystem::exists(mFullPath);
|
||||
}
|
57
src/base/core/file_utilities/File.h
Normal file
57
src/base/core/file_utilities/File.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include "FileFormats.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
class File
|
||||
{
|
||||
public:
|
||||
enum class AccessMode{
|
||||
Read,
|
||||
Write
|
||||
};
|
||||
|
||||
public:
|
||||
File(std::filesystem::path fullPath);
|
||||
|
||||
~File();
|
||||
|
||||
void close();
|
||||
|
||||
std::string dumpBinary();
|
||||
|
||||
std::string getExtension() const;
|
||||
|
||||
std::ifstream* getInHandle() const;
|
||||
|
||||
std::ofstream* getOutHandle() const;
|
||||
|
||||
FileFormat::Format inferFormat() const;
|
||||
|
||||
std::string readText();
|
||||
|
||||
std::vector<std::string> readLines();
|
||||
|
||||
std::string read();
|
||||
|
||||
bool pathExists() const;
|
||||
|
||||
bool open(AccessMode mode);
|
||||
|
||||
std::optional<unsigned char> readNextByte();
|
||||
|
||||
void writeText(const std::string& text);
|
||||
|
||||
private:
|
||||
std::filesystem::path mFullPath;
|
||||
std::unique_ptr<std::ifstream> mInHandle;
|
||||
std::unique_ptr<std::ofstream> mOutHandle;
|
||||
};
|
34
src/base/core/file_utilities/FileFormats.cpp
Normal file
34
src/base/core/file_utilities/FileFormats.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include "FileFormats.h"
|
||||
|
||||
#include "StringUtils.h"
|
||||
|
||||
FileFormat::ExtensionMap FileFormat::mExtensions = []
|
||||
{
|
||||
ExtensionMap ret;
|
||||
ret[Format::Markdown] = ".md";
|
||||
ret[Format::Html] = ".html";
|
||||
ret[Format::Wav] = ".wav";
|
||||
return ret;
|
||||
}();
|
||||
|
||||
bool FileFormat::isFormat(const std::string& extension, Format format)
|
||||
{
|
||||
return StringUtils::toLower(extension) == mExtensions[format];
|
||||
}
|
||||
|
||||
FileFormat::Format FileFormat::inferFormat(const std::string& query)
|
||||
{
|
||||
for(const auto& extension : mExtensions)
|
||||
{
|
||||
if(extension.second == query)
|
||||
{
|
||||
return extension.first;
|
||||
}
|
||||
}
|
||||
return Format::Unknown;
|
||||
}
|
||||
|
||||
std::string FileFormat::getExtension(Format format)
|
||||
{
|
||||
return mExtensions[format];
|
||||
}
|
38
src/base/core/file_utilities/FileFormats.h
Normal file
38
src/base/core/file_utilities/FileFormats.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
class FileFormat{
|
||||
|
||||
public:
|
||||
enum class Encoding{
|
||||
Ascii,
|
||||
UTF8,
|
||||
Binary
|
||||
};
|
||||
|
||||
enum class Format{
|
||||
Html,
|
||||
Xml,
|
||||
Markdown,
|
||||
Pdf,
|
||||
Json,
|
||||
Png,
|
||||
Wav,
|
||||
Midi,
|
||||
Unknown
|
||||
};
|
||||
|
||||
using ExtensionMap = std::map<Format, std::string>;
|
||||
public:
|
||||
static bool isFormat(const std::string& extension, Format format);
|
||||
|
||||
static Format inferFormat(const std::string& query);
|
||||
|
||||
static std::string getExtension(Format format);
|
||||
|
||||
private:
|
||||
static ExtensionMap mExtensions;
|
||||
};
|
||||
|
33
src/base/core/file_utilities/PathUtils.cpp
Normal file
33
src/base/core/file_utilities/PathUtils.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "PathUtils.h"
|
||||
|
||||
|
||||
std::string PathUtils::getBaseFilename(const Path& path)
|
||||
{
|
||||
return path.stem().string();
|
||||
}
|
||||
|
||||
Path PathUtils::getRelativePath(const Path& input, const Path& relativeTo)
|
||||
{
|
||||
return std::filesystem::relative(input, relativeTo);
|
||||
}
|
||||
|
||||
std::string PathUtils::getPathDelimited(const Path& path, char delimiter)
|
||||
{
|
||||
std::string name;
|
||||
|
||||
unsigned count = 0;
|
||||
for(const auto& element : path)
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
name += element.stem().string();
|
||||
}
|
||||
else
|
||||
{
|
||||
name += delimiter + element.stem().string();
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
17
src/base/core/file_utilities/PathUtils.h
Normal file
17
src/base/core/file_utilities/PathUtils.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
class PathUtils
|
||||
{
|
||||
public:
|
||||
static std::string getBaseFilename(const Path& path);
|
||||
|
||||
static Path getRelativePath(const Path& path, const Path& relativeTo);
|
||||
|
||||
static std::string getPathDelimited(const Path& path, char delimiter='-');
|
||||
};
|
97
src/base/core/http/HttpHeader.cpp
Normal file
97
src/base/core/http/HttpHeader.cpp
Normal file
|
@ -0,0 +1,97 @@
|
|||
#include "HttpHeader.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
|
||||
HttpHeader::HttpHeader()
|
||||
: mContentType("text / html"),
|
||||
mHttpVersion("1.1")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string HttpHeader::getContentType() const
|
||||
{
|
||||
return mContentType;
|
||||
}
|
||||
|
||||
std::string HttpHeader::getHttpVersion() const
|
||||
{
|
||||
return mHttpVersion;
|
||||
}
|
||||
|
||||
void HttpHeader::parse(const std::vector<std::string >& message)
|
||||
{
|
||||
std::string tag;
|
||||
std::string value;
|
||||
bool foundDelimiter{false};
|
||||
for (const auto& line : message)
|
||||
{
|
||||
for(std::size_t idx = 0; idx< line.size(); idx++)
|
||||
{
|
||||
const auto c = line[idx];
|
||||
if (c == StringUtils::COLON)
|
||||
{
|
||||
foundDelimiter = true;
|
||||
}
|
||||
else if(foundDelimiter)
|
||||
{
|
||||
value.push_back(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
tag.push_back(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tag.empty() || value.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (tag == "Host")
|
||||
{
|
||||
mHost = value;
|
||||
}
|
||||
else if (tag == "User-Agent")
|
||||
{
|
||||
mUserAgent = value;
|
||||
}
|
||||
else if (tag == "Accept")
|
||||
{
|
||||
mAccept = value;
|
||||
}
|
||||
else if (tag == "Accept-Language")
|
||||
{
|
||||
mAcceptLanguage = value;
|
||||
}
|
||||
else if (tag == "Accept-Encoding")
|
||||
{
|
||||
mAcceptEncoding = value;
|
||||
}
|
||||
else if (tag == "Connection")
|
||||
{
|
||||
mConnection = value;
|
||||
}
|
||||
else if (tag == "Referer")
|
||||
{
|
||||
mReferer = value;
|
||||
}
|
||||
else if (tag == "Sec-Fetch-Dest")
|
||||
{
|
||||
mSecFetchDest = value;
|
||||
}
|
||||
else if (tag == "Sec-Fetch-Mode")
|
||||
{
|
||||
mSecFetchMode = value;
|
||||
}
|
||||
else if (tag == "Sec-Fetch-Site")
|
||||
{
|
||||
mSecFetchSite = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
mOtherFields[tag] = value;
|
||||
}
|
||||
|
||||
}
|
32
src/base/core/http/HttpHeader.h
Normal file
32
src/base/core/http/HttpHeader.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class HttpHeader
|
||||
{
|
||||
public:
|
||||
HttpHeader();
|
||||
void parse(const std::vector<std::string >& message);
|
||||
|
||||
std::string getContentType() const;
|
||||
|
||||
std::string getHttpVersion() const;
|
||||
|
||||
private:
|
||||
std::string mHttpVersion;
|
||||
std::string mContentType;
|
||||
std::string mHost;
|
||||
std::string mUserAgent;
|
||||
std::string mAccept;
|
||||
std::string mAcceptLanguage;
|
||||
std::string mAcceptEncoding;
|
||||
std::string mConnection;
|
||||
std::string mReferer;
|
||||
std::string mSecFetchDest;
|
||||
std::string mSecFetchMode;
|
||||
std::string mSecFetchSite;
|
||||
|
||||
std::map<std::string, std::string> mOtherFields;
|
||||
};
|
85
src/base/core/http/HttpRequest.cpp
Normal file
85
src/base/core/http/HttpRequest.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
#include "HttpRequest.h"
|
||||
|
||||
#include "StringUtils.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
HttpRequest::HttpRequest(Verb verb, const std::string& path)
|
||||
: mVerb(verb),
|
||||
mPath(path)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HttpRequest::Verb HttpRequest::getVerb() const
|
||||
{
|
||||
return mVerb;
|
||||
}
|
||||
|
||||
std::string HttpRequest::getPath() const
|
||||
{
|
||||
return mPath;
|
||||
}
|
||||
|
||||
void HttpRequest::parseMessage(const std::string& message)
|
||||
{
|
||||
std::stringstream ss(message);
|
||||
|
||||
std::string buffer;
|
||||
bool firstLine {true};
|
||||
|
||||
std::vector<std::string> headers;
|
||||
while(std::getline(ss, buffer, '\n'))
|
||||
{
|
||||
if (firstLine)
|
||||
{
|
||||
parseFirstLine(buffer);
|
||||
firstLine = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
headers.push_back(buffer);
|
||||
}
|
||||
}
|
||||
mHeader.parse(headers);
|
||||
}
|
||||
|
||||
void HttpRequest::parseFirstLine(const std::string& line)
|
||||
{
|
||||
bool inPath{false};
|
||||
bool inMethod{true};
|
||||
bool inProtocol{false};
|
||||
|
||||
for (std::size_t idx=0; idx<line.size();idx++)
|
||||
{
|
||||
const auto c = line[idx];
|
||||
if (inPath)
|
||||
{
|
||||
if (StringUtils::isSpace(c))
|
||||
{
|
||||
inPath = false;
|
||||
inMethod = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mMethod.push_back(c);
|
||||
}
|
||||
}
|
||||
else if (inMethod)
|
||||
{
|
||||
if (StringUtils::isSpace(c))
|
||||
{
|
||||
inMethod = false;
|
||||
inProtocol = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mPath.push_back(c);
|
||||
}
|
||||
}
|
||||
else if (inProtocol)
|
||||
{
|
||||
mProtocolVersion.push_back(c);
|
||||
}
|
||||
}
|
||||
}
|
39
src/base/core/http/HttpRequest.h
Normal file
39
src/base/core/http/HttpRequest.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "HttpHeader.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class HttpRequest
|
||||
{
|
||||
public:
|
||||
|
||||
enum class Verb
|
||||
{
|
||||
GET,
|
||||
PUT,
|
||||
POST,
|
||||
PATCH,
|
||||
_DELETE,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
HttpRequest() = default;
|
||||
|
||||
HttpRequest(Verb verb, const std::string& path);
|
||||
|
||||
Verb getVerb() const;
|
||||
|
||||
std::string getPath() const;
|
||||
|
||||
void parseMessage(const std::string& message);
|
||||
|
||||
private:
|
||||
void parseFirstLine(const std::string& line);
|
||||
|
||||
Verb mVerb = Verb::UNKNOWN;
|
||||
HttpHeader mHeader;
|
||||
std::string mMethod;
|
||||
std::string mPath;
|
||||
std::string mProtocolVersion;
|
||||
};
|
67
src/base/core/http/HttpResponse.cpp
Normal file
67
src/base/core/http/HttpResponse.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include "HttpResponse.h"
|
||||
|
||||
HttpResponse::HttpResponse()
|
||||
: mStatusCode(200),
|
||||
mResponseReason("OK"),
|
||||
mBody()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HttpResponse::~HttpResponse()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
const HttpHeader& HttpResponse::getHeader() const
|
||||
{
|
||||
return mHeader;
|
||||
}
|
||||
|
||||
unsigned short HttpResponse::getStatusCode() const
|
||||
{
|
||||
return mStatusCode;
|
||||
}
|
||||
|
||||
const std::string& HttpResponse::getBody() const
|
||||
{
|
||||
return mBody;
|
||||
}
|
||||
|
||||
void HttpResponse::setBody(const std::string& body)
|
||||
{
|
||||
mBody = body;
|
||||
}
|
||||
|
||||
unsigned HttpResponse::getBodyLength() const
|
||||
{
|
||||
return unsigned(mBody.length());
|
||||
}
|
||||
|
||||
std::string HttpResponse::getHeaderString() const
|
||||
{
|
||||
std::string header = "HTTP/" + mHeader.getHttpVersion() + " " + std::to_string(mStatusCode) + " " + mResponseReason + "\n";
|
||||
header += "Content-Type: " + mHeader.getContentType() + "\n";
|
||||
header += "Content-Length: " + std::to_string(getBodyLength()) + "\n";
|
||||
return header;
|
||||
}
|
||||
|
||||
const std::string& HttpResponse::getResponseReason() const
|
||||
{
|
||||
return mResponseReason;
|
||||
}
|
||||
|
||||
std::string HttpResponse::toString() const
|
||||
{
|
||||
return getHeaderString() + "\n\n" + mBody;
|
||||
}
|
||||
|
||||
void HttpResponse::setStatusCode(unsigned short code)
|
||||
{
|
||||
mStatusCode = code;
|
||||
}
|
||||
|
||||
void HttpResponse::setResponseReason(const std::string& reason)
|
||||
{
|
||||
mResponseReason = reason;
|
||||
}
|
39
src/base/core/http/HttpResponse.h
Normal file
39
src/base/core/http/HttpResponse.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "HttpHeader.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class HttpResponse
|
||||
{
|
||||
public:
|
||||
HttpResponse();
|
||||
|
||||
~HttpResponse();
|
||||
|
||||
unsigned getBodyLength() const;
|
||||
|
||||
const std::string& getBody() const;
|
||||
|
||||
const HttpHeader& getHeader() const;
|
||||
|
||||
std::string getHeaderString() const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
unsigned short getStatusCode() const;
|
||||
|
||||
const std::string& getResponseReason() const;
|
||||
|
||||
void setStatusCode(unsigned short code);
|
||||
|
||||
void setResponseReason(const std::string& reason);
|
||||
|
||||
void setBody(const std::string& body);
|
||||
|
||||
private:
|
||||
HttpHeader mHeader;
|
||||
unsigned short mStatusCode{ 200 };
|
||||
std::string mResponseReason{ };
|
||||
std::string mBody;
|
||||
};
|
73
src/base/core/loggers/FileLogger.cpp
Normal file
73
src/base/core/loggers/FileLogger.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include "FileLogger.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
||||
FileLogger::~FileLogger()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FileLogger::SetWorkDirectory(const std::string& workDir)
|
||||
{
|
||||
mWorkDirectory = workDir;
|
||||
}
|
||||
|
||||
void FileLogger::SetFileName(const std::string& fileName)
|
||||
{
|
||||
mFileName = fileName;
|
||||
}
|
||||
|
||||
void FileLogger::Open()
|
||||
{
|
||||
if (mWorkDirectory.empty())
|
||||
{
|
||||
mWorkDirectory = std::filesystem::current_path().string();
|
||||
}
|
||||
mFileStream.open(mWorkDirectory + "/" + mFileName);
|
||||
}
|
||||
|
||||
void FileLogger::Close()
|
||||
{
|
||||
mFileStream.close();
|
||||
}
|
||||
|
||||
void FileLogger::LogLine(const std::ostringstream& line)
|
||||
{
|
||||
if (mDisabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mFileStream.is_open())
|
||||
{
|
||||
Open();
|
||||
}
|
||||
mFileStream << line.str() << std::endl;
|
||||
}
|
||||
|
||||
void FileLogger::disable()
|
||||
{
|
||||
mDisabled = true;
|
||||
}
|
||||
|
||||
void FileLogger::LogLine(const std::string& logType, const std::ostringstream& line, const std::string& fileName, const std::string& functionName, int lineNumber)
|
||||
{
|
||||
if (mDisabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::time_t t = std::time(nullptr);
|
||||
const std::string cleanedFileName = fileName.substr(fileName.find_last_of("/\\") + 1);
|
||||
std::tm time_buf = { 0 };
|
||||
#ifdef WIN32
|
||||
gmtime_s(&time_buf, &t);
|
||||
#else
|
||||
gmtime_r(&t, &time_buf);
|
||||
#endif
|
||||
std::cout << logType << "|" << std::put_time(&time_buf, "%T") << "|" << cleanedFileName << "::" << functionName << "::" << lineNumber << "|" << line.str() << std::endl;
|
||||
mFileStream << logType << "|" << std::put_time(&time_buf, "%T") << "|" << cleanedFileName << "::" << functionName << "::" << lineNumber << "|" << line.str() << std::endl;
|
||||
}
|
57
src/base/core/loggers/FileLogger.h
Normal file
57
src/base/core/loggers/FileLogger.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#define MLOG_ALL(msg, level) {std::ostringstream mt_logstream;\
|
||||
mt_logstream << msg; \
|
||||
FileLogger::GetInstance().LogLine(level, mt_logstream, __FILE__, __FUNCTION__, __LINE__);};
|
||||
|
||||
#define MLOG_INFO(msg) MLOG_ALL(msg, "Info");
|
||||
#define MLOG_ERROR(msg) MLOG_ALL(msg, "Error");
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
class FileLogger
|
||||
{
|
||||
FileLogger()
|
||||
:mWorkDirectory(),
|
||||
mFileName("MT_Log.txt"),
|
||||
mFileStream()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
static FileLogger& GetInstance()
|
||||
{
|
||||
static FileLogger instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
FileLogger(FileLogger const&) = delete;
|
||||
void operator=(FileLogger const&) = delete;
|
||||
|
||||
~FileLogger();
|
||||
|
||||
void disable();
|
||||
|
||||
void SetWorkDirectory(const std::string& workDir);
|
||||
|
||||
void SetFileName(const std::string& fileName);
|
||||
|
||||
void Open();
|
||||
|
||||
void Close();
|
||||
|
||||
void LogLine(const std::ostringstream& line);
|
||||
|
||||
void LogLine(const std::string& logType, const std::ostringstream& line, const std::string& fileName = "", const std::string& functionName = "", int lineNumber=-1);
|
||||
private:
|
||||
bool mDisabled{false};
|
||||
std::string mWorkDirectory;
|
||||
std::string mFileName;
|
||||
std::ofstream mFileStream;
|
||||
};
|
||||
|
||||
using FileLoggerPtr = std::shared_ptr<FileLogger>;
|
78
src/base/core/memory/SharedMemory.cpp
Normal file
78
src/base/core/memory/SharedMemory.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include "SharedMemory.h"
|
||||
|
||||
#include "RandomUtils.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
void SharedMemory::allocate(const std::string& namePrefix, std::size_t size)
|
||||
{
|
||||
createFile(namePrefix);
|
||||
|
||||
if (!mIsValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
int ret{-1};
|
||||
do {
|
||||
ret = ftruncate(mFileDescriptor, size);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
close(mFileDescriptor);
|
||||
mIsValid = false;
|
||||
}
|
||||
#else
|
||||
(void)(size);
|
||||
#endif
|
||||
}
|
||||
|
||||
int SharedMemory::getFileDescriptor() const
|
||||
{
|
||||
return mFileDescriptor;
|
||||
}
|
||||
|
||||
bool SharedMemory::isValid() const
|
||||
{
|
||||
return mIsValid;
|
||||
}
|
||||
|
||||
void SharedMemory::createFile(const std::string& namePrefix)
|
||||
{
|
||||
#ifdef __linux__
|
||||
unsigned retries = 100;
|
||||
do {
|
||||
const auto name = getRandomName(namePrefix);
|
||||
--retries;
|
||||
|
||||
const int fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||
if (fd >= 0)
|
||||
{
|
||||
shm_unlink(name.c_str());
|
||||
mFileDescriptor = fd;
|
||||
mIsValid = true;
|
||||
break;
|
||||
}
|
||||
} while (retries > 0 && errno == EEXIST);
|
||||
#else
|
||||
(void)(namePrefix);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string SharedMemory::getRandomName(const std::string& namePrefix) const
|
||||
{
|
||||
std::string randomSuffix;
|
||||
for (const auto entry : RandomUtils::getRandomVecUnsigned(6))
|
||||
{
|
||||
randomSuffix += std::to_string(entry);
|
||||
}
|
||||
return namePrefix + randomSuffix;
|
||||
}
|
22
src/base/core/memory/SharedMemory.h
Normal file
22
src/base/core/memory/SharedMemory.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
class SharedMemory
|
||||
{
|
||||
public:
|
||||
void allocate(const std::string& namePrefix, std::size_t size);
|
||||
|
||||
int getFileDescriptor() const;
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
private:
|
||||
|
||||
void createFile(const std::string& namePrefix);
|
||||
|
||||
std::string getRandomName(const std::string& namePrefix) const;
|
||||
|
||||
int mFileDescriptor{0};
|
||||
bool mIsValid{false};
|
||||
};
|
151
src/base/core/serializers/TomlReader.cpp
Normal file
151
src/base/core/serializers/TomlReader.cpp
Normal file
|
@ -0,0 +1,151 @@
|
|||
#include "TomlReader.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
#include <algorithm>
|
||||
|
||||
TomlTable::TomlTable(const std::string& header)
|
||||
: mHeader(header)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TomlTable::addComment(const Comment& comment)
|
||||
{
|
||||
mComments.push_back(comment);
|
||||
}
|
||||
|
||||
void TomlTable::addTable(std::unique_ptr<TomlTable> table)
|
||||
{
|
||||
mTables[table->getHeader()] = std::move(table);
|
||||
}
|
||||
|
||||
void TomlTable::addKeyValuePair(const std::string& key, const std::string& value)
|
||||
{
|
||||
mMap[key] = value;
|
||||
}
|
||||
|
||||
std::string TomlTable::getHeader() const
|
||||
{
|
||||
return mHeader;
|
||||
}
|
||||
|
||||
TomlTable* TomlTable::getTable(const std::string& path)
|
||||
{
|
||||
return mTables[path].get();
|
||||
}
|
||||
|
||||
TomlTable::KeyValuePairs TomlTable::getKeyValuePairs() const
|
||||
{
|
||||
return mMap;
|
||||
}
|
||||
|
||||
|
||||
TomlContent::TomlContent()
|
||||
: mRootTable(std::make_unique<TomlTable>("root"))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TomlTable* TomlContent::getRootTable() const
|
||||
{
|
||||
return mRootTable.get();
|
||||
}
|
||||
|
||||
TomlTable* TomlContent::getTable(const std::string& path) const
|
||||
{
|
||||
return mRootTable->getTable(path);
|
||||
}
|
||||
|
||||
TomlReader::TomlReader()
|
||||
: mContent(std::make_unique<TomlContent>())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TomlContent* TomlReader::getContent() const
|
||||
{
|
||||
return mContent.get();
|
||||
}
|
||||
|
||||
void TomlReader::read(const Path& input_path)
|
||||
{
|
||||
const auto lines = File(input_path).readLines();
|
||||
mLastSectionOffset = 0;
|
||||
mWorkingTable = mContent->getRootTable();
|
||||
|
||||
for (const auto& line : lines)
|
||||
{
|
||||
processLine(line);
|
||||
mLastSectionOffset++;
|
||||
}
|
||||
|
||||
mWorkingTable = nullptr;
|
||||
}
|
||||
|
||||
void TomlReader::processLine(const std::string& line)
|
||||
{
|
||||
bool in_comment{ false };
|
||||
bool in_header{ false };
|
||||
bool found_key{ false };
|
||||
std::string working_string;
|
||||
std::string key_string;
|
||||
for (auto c : line)
|
||||
{
|
||||
if (c == '#' && !in_comment)
|
||||
{
|
||||
in_comment = true;
|
||||
}
|
||||
else if (c == '[' && !in_comment && !in_header)
|
||||
{
|
||||
in_header = true;
|
||||
}
|
||||
else if (c == '=' && !in_comment && !in_header)
|
||||
{
|
||||
found_key = true;
|
||||
key_string = working_string;
|
||||
working_string = "";
|
||||
}
|
||||
else if (c == ']' && in_header)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
working_string += c;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_comment)
|
||||
{
|
||||
mWorkingTable->addComment({ mLastSectionOffset, working_string });
|
||||
}
|
||||
else if (in_header)
|
||||
{
|
||||
onHeader(working_string);
|
||||
}
|
||||
else if (found_key)
|
||||
{
|
||||
key_string.erase(std::remove_if(key_string.begin(), key_string.end(), [](char c) {return std::isspace(c); }), key_string.end());
|
||||
working_string.erase(std::remove_if(working_string.begin(), working_string.end(), [](char c) {return std::isspace(c); }), working_string.end());
|
||||
if (working_string.size()>2 && working_string[0] == '"' && working_string[working_string.size() - 1] == '"')
|
||||
{
|
||||
working_string = working_string.substr(1, working_string.size() - 2);
|
||||
}
|
||||
|
||||
onKeyValuePair(key_string, working_string);
|
||||
}
|
||||
}
|
||||
|
||||
void TomlReader::onHeader(const std::string& header)
|
||||
{
|
||||
auto new_table = std::make_unique<TomlTable>(header);
|
||||
auto table_temp = new_table.get();
|
||||
mWorkingTable->addTable(std::move(new_table));
|
||||
mWorkingTable = table_temp;
|
||||
}
|
||||
|
||||
void TomlReader::onKeyValuePair(const std::string key, const std::string value)
|
||||
{
|
||||
mWorkingTable->addKeyValuePair(key, value);
|
||||
}
|
72
src/base/core/serializers/TomlReader.h
Normal file
72
src/base/core/serializers/TomlReader.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include "File.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
class TomlTable
|
||||
{
|
||||
public:
|
||||
using Comment = std::pair<unsigned, std::string>;
|
||||
using KeyValuePairs = std::unordered_map<std::string, std::string>;
|
||||
|
||||
TomlTable(const std::string& header);
|
||||
|
||||
void addComment(const Comment& comment);
|
||||
|
||||
void addTable(std::unique_ptr<TomlTable> table);
|
||||
|
||||
void addKeyValuePair(const std::string& key, const std::string& value);
|
||||
|
||||
std::string getHeader() const;
|
||||
|
||||
TomlTable* getTable(const std::string& path);
|
||||
|
||||
KeyValuePairs getKeyValuePairs() const;
|
||||
|
||||
private:
|
||||
unsigned mLineOffset{ 0 };
|
||||
std::string mHeader;
|
||||
std::unordered_map<std::string, std::unique_ptr<TomlTable> > mTables;
|
||||
KeyValuePairs mMap;
|
||||
std::vector<Comment> mComments;
|
||||
};
|
||||
|
||||
class TomlContent
|
||||
{
|
||||
public:
|
||||
TomlContent();
|
||||
|
||||
TomlTable* getRootTable() const;
|
||||
|
||||
TomlTable* getTable(const std::string& path) const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<TomlTable> mRootTable;
|
||||
};
|
||||
|
||||
class TomlReader
|
||||
{
|
||||
public:
|
||||
TomlReader();
|
||||
|
||||
TomlContent* getContent() const;
|
||||
|
||||
void read(const Path& input_path);
|
||||
|
||||
void processLine(const std::string& line);
|
||||
|
||||
void onHeader(const std::string& header);
|
||||
|
||||
void onKeyValuePair(const std::string key, const std::string value);
|
||||
|
||||
private:
|
||||
unsigned mLastSectionOffset{ 0 };
|
||||
std::unique_ptr<TomlContent> mContent;
|
||||
TomlTable* mWorkingTable{nullptr};
|
||||
};
|
16
src/base/core/streams/AbstractChecksumCalculator.h
Normal file
16
src/base/core/streams/AbstractChecksumCalculator.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
class AbstractChecksumCalculator
|
||||
{
|
||||
public:
|
||||
virtual ~AbstractChecksumCalculator() = default;
|
||||
|
||||
virtual void addValue(unsigned char val) = 0;
|
||||
virtual uint32_t getChecksum() const = 0;
|
||||
|
||||
virtual void reset()
|
||||
{
|
||||
}
|
||||
};
|
97
src/base/core/streams/BinaryStream.cpp
Normal file
97
src/base/core/streams/BinaryStream.cpp
Normal file
|
@ -0,0 +1,97 @@
|
|||
#include "BinaryStream.h"
|
||||
|
||||
std::optional<int> BinaryStream::getNextByteAsInt(std::basic_istream<char>* stream)
|
||||
{
|
||||
return stream->get();
|
||||
}
|
||||
|
||||
bool BinaryStream::getNextNBytes(std::basic_istream<char>* stream, char* buffer, unsigned number)
|
||||
{
|
||||
char c;
|
||||
for(unsigned idx=0; idx<number; idx++)
|
||||
{
|
||||
if(stream->get(c))
|
||||
{
|
||||
buffer[idx] = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinaryStream::getNextWord(std::basic_istream<char>* stream, char* buffer)
|
||||
{
|
||||
return getNextNBytes(stream, buffer, sizeof(ByteUtils::Word));
|
||||
}
|
||||
|
||||
bool BinaryStream::getNextDWord(std::basic_istream<char>* stream, char* buffer)
|
||||
{
|
||||
return getNextNBytes(stream, buffer, sizeof(ByteUtils::DWord));
|
||||
}
|
||||
|
||||
bool BinaryStream::getNextQWord(std::basic_istream<char>* stream, char* buffer)
|
||||
{
|
||||
return getNextNBytes(stream, buffer, sizeof(ByteUtils::QWord));
|
||||
}
|
||||
|
||||
std::optional<ByteUtils::Word> BinaryStream::getNextWord(std::basic_istream<char>* stream, bool reverse)
|
||||
{
|
||||
char buffer[sizeof(ByteUtils::Word)];
|
||||
if(!BinaryStream::getNextWord(stream, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ByteUtils::ToWord(buffer, reverse);
|
||||
}
|
||||
|
||||
std::optional<ByteUtils::DWord> BinaryStream::getNextDWord(std::basic_istream<char>* stream)
|
||||
{
|
||||
char buffer[sizeof(ByteUtils::DWord)];
|
||||
if(!BinaryStream::getNextDWord(stream, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ByteUtils::ToDWord(buffer);;
|
||||
}
|
||||
|
||||
std::optional<ByteUtils::QWord> BinaryStream::getNextQWord(std::basic_istream<char>* stream)
|
||||
{
|
||||
char buffer[sizeof(ByteUtils::QWord)];
|
||||
if(!BinaryStream::getNextQWord(stream, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ByteUtils::ToQWord(buffer);;
|
||||
}
|
||||
|
||||
bool BinaryStream::checkNextDWord(std::basic_istream<char>* stream, const char* target)
|
||||
{
|
||||
char buffer[sizeof(ByteUtils::DWord)];
|
||||
if(!BinaryStream::getNextDWord(stream, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ByteUtils::CompareDWords(buffer, target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinaryStream::getNextString(std::basic_istream<char>* stream, std::string& target, unsigned numBytes)
|
||||
{
|
||||
char c;
|
||||
for(unsigned idx=0; idx<numBytes; idx++)
|
||||
{
|
||||
if(!stream->get(c))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
target += c;
|
||||
}
|
||||
return true;
|
||||
}
|
40
src/base/core/streams/BinaryStream.h
Normal file
40
src/base/core/streams/BinaryStream.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
#include "ByteUtils.h"
|
||||
|
||||
class BinaryStream
|
||||
{
|
||||
public:
|
||||
|
||||
template<typename T>
|
||||
static bool write(std::basic_ostream<char>* stream, T data)
|
||||
{
|
||||
stream->write(reinterpret_cast<char*>(&data), sizeof(data));
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::optional<int> getNextByteAsInt(std::basic_istream<char>* stream);
|
||||
|
||||
static bool getNextNBytes(std::basic_istream<char>* stream, char* buffer, unsigned numBytes);
|
||||
|
||||
static bool getNextWord(std::basic_istream<char>* stream, char* buffer);
|
||||
|
||||
static bool getNextDWord(std::basic_istream<char>* stream, char* buffer);
|
||||
|
||||
static bool getNextQWord(std::basic_istream<char>* stream, char* buffer);
|
||||
|
||||
static std::optional<ByteUtils::Word> getNextWord(std::basic_istream<char>* stream, bool reverse = true);
|
||||
|
||||
static std::optional<ByteUtils::DWord> getNextDWord(std::basic_istream<char>* stream);
|
||||
|
||||
static std::optional<ByteUtils::QWord> getNextQWord(std::basic_istream<char>* stream);
|
||||
|
||||
static bool checkNextDWord(std::basic_istream<char>* stream, const char* target);
|
||||
|
||||
static bool getNextString(std::basic_istream<char>* stream, std::string& target, unsigned numBytes);
|
||||
};
|
147
src/base/core/streams/BitStream.cpp
Normal file
147
src/base/core/streams/BitStream.cpp
Normal file
|
@ -0,0 +1,147 @@
|
|||
#include "BitStream.h"
|
||||
|
||||
#include "ByteUtils.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
BitStream::~BitStream()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
unsigned char BitStream::getCurrentByte()
|
||||
{
|
||||
if (mByteOffset < 0)
|
||||
{
|
||||
readNextByte();
|
||||
}
|
||||
return mCurrentByte;
|
||||
}
|
||||
|
||||
void BitStream::write(uint32_t data)
|
||||
{
|
||||
unsigned num_bytes = sizeof(uint32_t);
|
||||
for(unsigned idx=0; idx<num_bytes;idx++)
|
||||
{
|
||||
writeByte(ByteUtils::getByteN(data, idx));
|
||||
}
|
||||
}
|
||||
|
||||
void BitStream::writeWord(uint16_t data)
|
||||
{
|
||||
const auto byte0 = static_cast<unsigned char>(data >> 8);
|
||||
const auto byte1 = static_cast<unsigned char>((data << 8) >> 8);
|
||||
writeByte(byte0);
|
||||
writeByte(byte1);
|
||||
}
|
||||
|
||||
int BitStream::getCurrentByteOffset() const
|
||||
{
|
||||
return mByteOffset;
|
||||
}
|
||||
|
||||
unsigned BitStream::getCurrentBitOffset() const
|
||||
{
|
||||
return mBitOffset;
|
||||
}
|
||||
|
||||
std::string BitStream::logLocation()
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << "Byte offset " << mByteOffset<< " | Bit offset " << mBitOffset;
|
||||
sstr << " | Working byte " << ByteUtils::toString(getCurrentByte()) << '\n';
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
std::string BitStream::logNextNBytes(unsigned n) const
|
||||
{
|
||||
std::stringstream sstr;
|
||||
unsigned count{0};
|
||||
for(auto byte : peekNextNBytes(n))
|
||||
{
|
||||
sstr << mByteOffset + count << " | " << ByteUtils::toString(byte) + '\n';
|
||||
count++;
|
||||
}
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
void BitStream::writeNBits(uint32_t data, unsigned length)
|
||||
{
|
||||
const auto num_left = 8 - mBitOffset;
|
||||
const int overshoot = length - num_left;
|
||||
|
||||
if (overshoot > 0)
|
||||
{
|
||||
unsigned char lower_bits = ByteUtils::getLowerNBits(data, num_left);
|
||||
mCurrentByte |= lower_bits << mBitOffset;
|
||||
|
||||
writeByte(mCurrentByte, false);
|
||||
|
||||
unsigned num_bytes = overshoot / 8;
|
||||
for (unsigned idx=0; idx< num_bytes; idx++)
|
||||
{
|
||||
mCurrentByte = ByteUtils::getMBitsAtN(static_cast<unsigned char>(data), overshoot, idx*8 + num_left);
|
||||
writeByte(mCurrentByte, false);
|
||||
}
|
||||
|
||||
if (const auto remainder = overshoot % 8; remainder > 0)
|
||||
{
|
||||
mCurrentByte = ByteUtils::getMBitsAtN(static_cast<unsigned char>(data), remainder, num_bytes*8 + num_left);
|
||||
mBitOffset = remainder;
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentByte = 0;
|
||||
mBitOffset = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentByte |= (static_cast<unsigned char>(data) << mBitOffset);
|
||||
mBitOffset += length;
|
||||
if (mBitOffset == 8)
|
||||
{
|
||||
writeByte(mCurrentByte, false);
|
||||
mCurrentByte = 0;
|
||||
mBitOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool BitStream::readNextNBits(unsigned n, unsigned char& buffer)
|
||||
{
|
||||
if (mByteOffset < 0)
|
||||
{
|
||||
if (!readNextByte())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int overshoot = n + mBitOffset - 8;
|
||||
if (overshoot > 0)
|
||||
{
|
||||
unsigned char last_byte = mCurrentByte;
|
||||
if (!readNextByte())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto num_lower = 8 - mBitOffset;
|
||||
char lower_bits = ByteUtils::getHigherNBits(last_byte, num_lower);
|
||||
char higher_bits = ByteUtils::getLowerNBits(mCurrentByte, overshoot);
|
||||
|
||||
buffer = (higher_bits << num_lower) | lower_bits;
|
||||
|
||||
mBitOffset = overshoot;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = ByteUtils::getMBitsAtN(mCurrentByte, n, mBitOffset);
|
||||
mBitOffset += n;
|
||||
return true;
|
||||
}
|
||||
}
|
92
src/base/core/streams/BitStream.h
Normal file
92
src/base/core/streams/BitStream.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
#pragma once
|
||||
|
||||
#include "AbstractChecksumCalculator.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
class BitStream
|
||||
{
|
||||
public:
|
||||
virtual ~BitStream();
|
||||
|
||||
unsigned char getCurrentByte();
|
||||
|
||||
int getCurrentByteOffset() const;
|
||||
|
||||
unsigned getCurrentBitOffset() const;
|
||||
|
||||
virtual bool isFinished() const = 0;
|
||||
|
||||
std::string logNextNBytes(unsigned n) const;
|
||||
|
||||
std::string logLocation();
|
||||
|
||||
virtual std::vector<unsigned char> peekNextNBytes(unsigned n) const = 0;
|
||||
|
||||
virtual bool readNextNBits(unsigned n, unsigned char& buffer);
|
||||
|
||||
virtual std::optional<unsigned char> readNextByte() = 0;
|
||||
|
||||
virtual void writeNBits(uint32_t data, unsigned length);
|
||||
|
||||
virtual void writeByte(unsigned char data, bool checkOverflow = true) = 0;
|
||||
|
||||
void write(uint32_t data);
|
||||
|
||||
void writeWord(uint16_t data);
|
||||
|
||||
virtual void writeBytes(const std::vector<unsigned char> data) = 0;
|
||||
|
||||
void resetOffsets()
|
||||
{
|
||||
mEndByteOffset = mByteOffset;
|
||||
mEndBitOffset = mBitOffset;
|
||||
mEndByte = mCurrentByte;
|
||||
|
||||
mCurrentByte = 0;
|
||||
mByteOffset = -1;
|
||||
mBitOffset = 0;
|
||||
}
|
||||
|
||||
virtual void reset()
|
||||
{
|
||||
resetOffsets();
|
||||
mCurrentByte = 0;
|
||||
}
|
||||
|
||||
void flushRemainingBits()
|
||||
{
|
||||
if (mBitOffset > 0)
|
||||
{
|
||||
writeByte(mCurrentByte, false);
|
||||
mBitOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<unsigned char, unsigned> getRemainingBits() const
|
||||
{
|
||||
return {mEndByte, mEndBitOffset};
|
||||
}
|
||||
|
||||
void setChecksumCalculator(AbstractChecksumCalculator* calc)
|
||||
{
|
||||
mChecksumCalculator = calc;
|
||||
}
|
||||
|
||||
void clearChecksumCalculator()
|
||||
{
|
||||
mChecksumCalculator = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
int mByteOffset{-1};
|
||||
unsigned mBitOffset{0};
|
||||
unsigned char mCurrentByte{0};
|
||||
|
||||
int mEndByteOffset{-1};
|
||||
unsigned mEndBitOffset{0};
|
||||
unsigned char mEndByte{0};
|
||||
AbstractChecksumCalculator* mChecksumCalculator{nullptr};
|
||||
};
|
78
src/base/core/streams/BufferBitStream.cpp
Normal file
78
src/base/core/streams/BufferBitStream.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include "BufferBitStream.h"
|
||||
|
||||
#include "ByteUtils.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
bool BufferBitStream::isFinished() const
|
||||
{
|
||||
return mByteOffset == mBuffer.size() - 1;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> BufferBitStream::peekNextNBytes(unsigned n) const
|
||||
{
|
||||
std::vector<unsigned char> ret (n, 0);
|
||||
unsigned count = 0;
|
||||
|
||||
int start = mByteOffset;
|
||||
if (start<0)
|
||||
{
|
||||
start = 0;
|
||||
}
|
||||
|
||||
for(unsigned idx=start; idx<start + n; idx++)
|
||||
{
|
||||
if (idx == mBuffer.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ret[count] = mBuffer[idx];
|
||||
count ++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<unsigned char> BufferBitStream::readNextByte()
|
||||
{
|
||||
if (mByteOffset + 1 == mBuffer.size())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
mByteOffset++;
|
||||
mCurrentByte = mBuffer[mByteOffset];
|
||||
return mCurrentByte;
|
||||
}
|
||||
}
|
||||
|
||||
void BufferBitStream::setBuffer(const std::vector<unsigned char>& data)
|
||||
{
|
||||
mBuffer = data;
|
||||
}
|
||||
|
||||
void BufferBitStream::writeByte(unsigned char data, bool checkOverflow)
|
||||
{
|
||||
unsigned char out_byte{0};
|
||||
if (checkOverflow && mBitOffset > 0)
|
||||
{
|
||||
out_byte = ByteUtils::getLowerNBits(mCurrentByte, mBitOffset);
|
||||
out_byte |= data << mBitOffset;
|
||||
|
||||
mCurrentByte = ByteUtils::getHigherNBits(data, mBitOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_byte = data;
|
||||
}
|
||||
|
||||
if (mChecksumCalculator)
|
||||
{
|
||||
mChecksumCalculator->addValue(out_byte);
|
||||
}
|
||||
//std::cout << "Writing byte " << ByteUtils::toString(out_byte) << " had bitoffset of " << mBitOffset << std::endl;
|
||||
mBuffer.push_back(out_byte);
|
||||
}
|
||||
|
||||
|
40
src/base/core/streams/BufferBitStream.h
Normal file
40
src/base/core/streams/BufferBitStream.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include "BitStream.h"
|
||||
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
class BufferBitStream : public BitStream
|
||||
{
|
||||
public:
|
||||
bool isFinished() const override;
|
||||
|
||||
std::vector<unsigned char> peekNextNBytes(unsigned n) const override;
|
||||
|
||||
std::optional<unsigned char> readNextByte() override;
|
||||
|
||||
void setBuffer(const std::vector<unsigned char>& data);
|
||||
|
||||
void writeByte(unsigned char data, bool checkOverflow = true) override;
|
||||
|
||||
void writeBytes(const std::vector<unsigned char> data) override
|
||||
{
|
||||
std::copy(data.begin(), data.end(), std::back_inserter(mBuffer));
|
||||
}
|
||||
|
||||
const std::vector<unsigned char>& getBuffer() const
|
||||
{
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
void reset() override
|
||||
{
|
||||
BitStream::reset();
|
||||
mBuffer.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned mBufferSize{0};
|
||||
std::vector<unsigned char> mBuffer;
|
||||
};
|
35
src/base/core/streams/InputBitStream.cpp
Normal file
35
src/base/core/streams/InputBitStream.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include "InputBitStream.h"
|
||||
|
||||
InputBitStream::InputBitStream(std::basic_istream<unsigned char>* stream)
|
||||
: BitStream(),
|
||||
mStream(stream)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool InputBitStream::isFinished() const
|
||||
{
|
||||
return mStream->good();
|
||||
}
|
||||
|
||||
std::vector<unsigned char> InputBitStream::peekNextNBytes(unsigned) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<unsigned char> InputBitStream::readNextByte()
|
||||
{
|
||||
if (mStream->good())
|
||||
{
|
||||
return static_cast<unsigned char>(mStream->get());
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void InputBitStream::writeByte(unsigned char, bool)
|
||||
{
|
||||
|
||||
}
|
26
src/base/core/streams/InputBitStream.h
Normal file
26
src/base/core/streams/InputBitStream.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "BitStream.h"
|
||||
|
||||
#include <istream>
|
||||
|
||||
class InputBitStream : public BitStream
|
||||
{
|
||||
InputBitStream(std::basic_istream<unsigned char>* stream);
|
||||
|
||||
bool isFinished() const override;
|
||||
|
||||
std::vector<unsigned char> peekNextNBytes(unsigned n) const override;
|
||||
|
||||
std::optional<unsigned char> readNextByte() override;
|
||||
|
||||
void writeByte(unsigned char data, bool checkOverflow = true) override;
|
||||
|
||||
void writeBytes(const std::vector<unsigned char> data) override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
std::basic_istream<unsigned char>* mStream{nullptr};
|
||||
};
|
36
src/base/core/streams/OutputBitStream.cpp
Normal file
36
src/base/core/streams/OutputBitStream.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "OutputBitStream.h"
|
||||
|
||||
OutputBitStream::OutputBitStream(std::basic_ostream<char>* stream)
|
||||
: BitStream(),
|
||||
mStream(stream)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool OutputBitStream::isFinished() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> OutputBitStream::peekNextNBytes(unsigned) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<unsigned char> OutputBitStream::readNextByte()
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void OutputBitStream::writeByte(unsigned char data, bool)
|
||||
{
|
||||
(*mStream) << data;
|
||||
}
|
||||
|
||||
void OutputBitStream::writeBytes(const std::vector<unsigned char> data)
|
||||
{
|
||||
for(auto byte : data)
|
||||
{
|
||||
writeByte(byte);
|
||||
}
|
||||
}
|
24
src/base/core/streams/OutputBitStream.h
Normal file
24
src/base/core/streams/OutputBitStream.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "BitStream.h"
|
||||
|
||||
#include <ostream>
|
||||
|
||||
class OutputBitStream : public BitStream
|
||||
{
|
||||
public:
|
||||
OutputBitStream(std::basic_ostream<char>* stream);
|
||||
|
||||
bool isFinished() const override;
|
||||
|
||||
std::vector<unsigned char> peekNextNBytes(unsigned n) const override;
|
||||
|
||||
std::optional<unsigned char> readNextByte() override;
|
||||
|
||||
void writeByte(unsigned char data, bool checkOverflow = true) override;
|
||||
|
||||
void writeBytes(const std::vector<unsigned char> data) override;
|
||||
|
||||
private:
|
||||
std::basic_ostream<char>* mStream{nullptr};
|
||||
};
|
28
src/base/database/CMakeLists.txt
Normal file
28
src/base/database/CMakeLists.txt
Normal file
|
@ -0,0 +1,28 @@
|
|||
set(MODULE_NAME database)
|
||||
|
||||
set(SQLite3_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/src/third_party/sqlite3")
|
||||
set(SQLite3_SOURCE_FILE "${CMAKE_SOURCE_DIR}/src/third_party/sqlite3/sqlite3.c")
|
||||
|
||||
list(APPEND HEADERS
|
||||
Database.h
|
||||
DatabaseManager.h
|
||||
database_interfaces/SqliteInterface.h
|
||||
${SQLite3_SOURCE_FILE})
|
||||
|
||||
list(APPEND SOURCES
|
||||
Database.cpp
|
||||
DatabaseManager.cpp
|
||||
database_interfaces/SqliteInterface.cpp
|
||||
${SQLite3_SOURCE_FILE})
|
||||
|
||||
add_library(${MODULE_NAME} SHARED ${SOURCES} ${HEADERS})
|
||||
|
||||
target_include_directories(${MODULE_NAME} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/database_interfaces
|
||||
${SQLite3_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} core)
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src/base)
|
||||
set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
|
27
src/base/database/Database.cpp
Normal file
27
src/base/database/Database.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include "Database.h"
|
||||
|
||||
Database::Database()
|
||||
: mPath()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Database::~Database()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<Database> Database::Create()
|
||||
{
|
||||
return std::make_unique<Database>();
|
||||
}
|
||||
|
||||
void Database::setPath(const Path& path)
|
||||
{
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
const Path& Database::getPath() const
|
||||
{
|
||||
return mPath;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue