From f2ef752dcf8171ad5447daa47eeee34646f0ffc2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 1 Nov 2023 12:35:52 +0100 Subject: [PATCH] Hide templated stuff from core.hpp Remove Checkum class from public API Hard to maintain binary compatibility Fix includes Update python bindings, add cmake presets, fix GH builds Fix python tests --- .gitignore | 1 + deps/autobuild.cmake | 4 +- pybgcode/CMakePresets.json | 16 +++ pybgcode/pybgcode.cpp | 18 --- pybgcode/tests/test_convert.py | 3 - src/LibBGCode/binarize/binarize.cpp | 50 ++++---- src/LibBGCode/binarize/binarize.hpp | 5 - src/LibBGCode/core/CMakeLists.txt | 1 + src/LibBGCode/core/core.cpp | 36 +++--- src/LibBGCode/core/core.hpp | 161 ++----------------------- src/LibBGCode/core/core_impl.hpp | 180 ++++++++++++++++++++++++++++ 11 files changed, 256 insertions(+), 219 deletions(-) create mode 100644 pybgcode/CMakePresets.json create mode 100644 src/LibBGCode/core/core_impl.hpp diff --git a/.gitignore b/.gitignore index a1b1873..a016cff 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ deps/build* deps/.pkg_cache build* CMakeLists.txt.user +CMakeUserPresets.json pybgcode.egg-info dist/ Testing diff --git a/deps/autobuild.cmake b/deps/autobuild.cmake index e7d998a..edea93b 100644 --- a/deps/autobuild.cmake +++ b/deps/autobuild.cmake @@ -67,8 +67,8 @@ endif () if (${PROJECT_NAME}_DEPS_PRESET STREQUAL "wasm") list(APPEND CMAKE_FIND_ROOT_PATH ${_build_dir}/destdir/usr/local) - set(CMAKE_FIND_ROOT_PATH "${CMAKE_FIND_ROOT_PATH}" CACHE STRING "") + set(CMAKE_FIND_ROOT_PATH "${CMAKE_FIND_ROOT_PATH}" CACHE STRING "" FORCE) else () list(APPEND CMAKE_PREFIX_PATH ${_build_dir}/destdir/usr/local) - set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH}" CACHE STRING "") + set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH}" CACHE STRING "" FORCE) endif () diff --git a/pybgcode/CMakePresets.json b/pybgcode/CMakePresets.json new file mode 100644 index 0000000..663ec30 --- /dev/null +++ b/pybgcode/CMakePresets.json @@ -0,0 +1,16 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "default", + "displayName": "Default", + "description": "Default build for any desktop OS", + "binaryDir": "${sourceDir}/build-default", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_INSTALL_PREFIX": "${sourceDir}/build/dist", + "PyBGCode_LINK_SYSTEM_LIBBGCODE": false + } + } + ] +} \ No newline at end of file diff --git a/pybgcode/pybgcode.cpp b/pybgcode/pybgcode.cpp index 1fd5163..9c17ecc 100644 --- a/pybgcode/pybgcode.cpp +++ b/pybgcode/pybgcode.cpp @@ -163,20 +163,6 @@ PYBIND11_MODULE(pybgcode, m) { .value("JPG", core::EThumbnailFormat::JPG) .value("QOI", core::EThumbnailFormat::QOI); - py::class_(m, "Checksum") - .def(py::init()) - .def("get_type", &core::Checksum::get_type) - .def("append", [](core::Checksum &self, const std::string &buf) { - self.append(buf.data(), buf.size()); - }) - .def("matches", &core::Checksum::matches) - .def("read", [](core::Checksum &self, FILEWrapper &file) { - return self.read(*file.fptr); - }) - .def("write", [](core::Checksum &self, FILEWrapper &file) { - return self.write(*file.fptr); - }); - py::class_(m, "FileHeader") .def(py::init<>()) .def_readonly("magic", &core::FileHeader::magic) @@ -195,7 +181,6 @@ PYBIND11_MODULE(pybgcode, m) { .def_readonly("compression", &core::BlockHeader::compression) .def_readonly("uncompressed_size", &core::BlockHeader::type) .def_readonly("compressed_size", &core::BlockHeader::compressed_size) - .def("update_checksum", &core::BlockHeader::update_checksum) .def("get_size()", &core::BlockHeader::get_size) .def("read", [](core::BlockHeader &self, FILEWrapper &file) { return self.read(*file.fptr); @@ -379,9 +364,6 @@ PYBIND11_MODULE(pybgcode, m) { .def(py::init<>()) .def_readonly("encoding_type", &binarize::BaseMetadataBlock::encoding_type) .def_readonly("raw_data", &binarize::BaseMetadataBlock::raw_data) - .def("write", [](binarize::BaseMetadataBlock &self, FILEWrapper &file, core::EBlockType block_type, core::ECompressionType compression_type, core::Checksum& checksum){ - return self.write(*file.fptr, block_type, compression_type, checksum); - }, R"pbdoc(write block header and data in encoded format)pbdoc", py::arg("file"), py::arg("block_type"), py::arg("compression_type"), py::arg("checksum")) .def("read_data", [](binarize::BaseMetadataBlock &self, FILEWrapper &file, const core::BlockHeader& block_header) { return self.read_data(*file.fptr, block_header); }, R"pbdoc(read block data in encoded format)pbdoc", py::arg("file"), py::arg("block_header")); diff --git a/pybgcode/tests/test_convert.py b/pybgcode/tests/test_convert.py index c1eec22..9a23e09 100644 --- a/pybgcode/tests/test_convert.py +++ b/pybgcode/tests/test_convert.py @@ -57,9 +57,6 @@ def test_main(): assert(res == pybgcode.EResult.Success) - checksum = pybgcode.Checksum(pybgcode.ChecksumType.CRC32) - assert(checksum.get_type() == pybgcode.ChecksumType.CRC32) - pybgcode.close(out_f) pybgcode.close(in_f) diff --git a/src/LibBGCode/binarize/binarize.cpp b/src/LibBGCode/binarize/binarize.cpp index adf4096..4781ed6 100644 --- a/src/LibBGCode/binarize/binarize.cpp +++ b/src/LibBGCode/binarize/binarize.cpp @@ -1,6 +1,8 @@ #include "binarize.hpp" #include "meatpack.hpp" +#include "core/core_impl.hpp" + extern "C" { #include #include @@ -32,6 +34,14 @@ static bool read_from_file(FILE& file, T *data, size_t data_size) return !ferror(&file) && rsize == data_size; } +void update_checksum(Checksum& checksum, const ThumbnailBlock &th) +{ + checksum.append(th.params.format); + checksum.append(th.params.width); + checksum.append(th.params.height); + checksum.append(th.data); +} + static std::vector encode(const std::byte* data, size_t data_size) { std::vector ret(data_size); @@ -378,18 +388,19 @@ static bool uncompress(const std::vector& src, std::vector& ds return true; } -EResult BaseMetadataBlock::write(FILE& file, EBlockType block_type, ECompressionType compression_type, - Checksum& checksum) const + +// write block header and data in encoded format +core::EResult write(const BaseMetadataBlock &block, FILE& file, core::EBlockType block_type, core::ECompressionType compression_type, core::Checksum &checksum) { - if (encoding_type > metadata_encoding_types_count()) + if (block.encoding_type > metadata_encoding_types_count()) return EResult::InvalidMetadataEncodingType; BlockHeader block_header((uint16_t)block_type, (uint16_t)compression_type, (uint32_t)0); std::vector out_data; - if (!raw_data.empty()) { + if (!block.raw_data.empty()) { // process payload encoding std::vector uncompressed_data; - if (!encode_metadata(raw_data, uncompressed_data, (EMetadataEncodingType)encoding_type)) + if (!encode_metadata(block.raw_data, uncompressed_data, (EMetadataEncodingType)block.encoding_type)) return EResult::MetadataEncodingError; // process payload compression block_header.uncompressed_size = (uint32_t)uncompressed_data.size(); @@ -409,7 +420,7 @@ EResult BaseMetadataBlock::write(FILE& file, EBlockType block_type, ECompression return res; // write block payload - if (!write_to_file(file, &encoding_type, sizeof(encoding_type))) + if (!write_to_file(file, &block.encoding_type, sizeof(block.encoding_type))) return EResult::WriteError; if (!out_data.empty()) { if (!write_to_file(file, out_data.data(), out_data.size())) @@ -418,12 +429,13 @@ EResult BaseMetadataBlock::write(FILE& file, EBlockType block_type, ECompression if (checksum.get_type() != EChecksumType::None) { // update checksum with block header - block_header.update_checksum(checksum); + update_checksum(checksum, block_header); // update checksum with block payload - checksum.append(encoding_type); + checksum.append(block.encoding_type); if (!out_data.empty()) checksum.append(static_cast(out_data.data()), out_data.size()); } + return EResult::Success; } @@ -461,7 +473,7 @@ EResult FileMetadataBlock::write(FILE& file, ECompressionType compression_type, Checksum cs(checksum_type); // write block header, payload - EResult res = BaseMetadataBlock::write(file, EBlockType::FileMetadata, compression_type, cs); + EResult res = binarize::write(*this, file, EBlockType::FileMetadata, compression_type, cs); if (res != EResult::Success) // propagate error return res; @@ -498,7 +510,7 @@ EResult PrintMetadataBlock::write(FILE& file, ECompressionType compression_type, Checksum cs(checksum_type); // write block header, payload - EResult res = BaseMetadataBlock::write(file, EBlockType::PrintMetadata, compression_type, cs); + EResult res = binarize::write(*this, file, EBlockType::PrintMetadata, compression_type, cs); if (res != EResult::Success) // propagate error return res; @@ -535,7 +547,7 @@ EResult PrinterMetadataBlock::write(FILE& file, ECompressionType compression_typ Checksum cs(checksum_type); // write block header, payload - EResult res = BaseMetadataBlock::write(file, EBlockType::PrinterMetadata, compression_type, cs); + EResult res = binarize::write(*this, file, EBlockType::PrinterMetadata, compression_type, cs); if (res != EResult::Success) // propagate error return res; @@ -597,9 +609,9 @@ EResult ThumbnailBlock::write(FILE& file, EChecksumType checksum_type) if (checksum_type != EChecksumType::None) { Checksum cs(checksum_type); // update checksum with block header - block_header.update_checksum(cs); + update_checksum(cs, block_header); // update checksum with block payload - update_checksum(cs); + update_checksum(cs, *this); // write block checksum res = cs.write(file); if (res != EResult::Success) @@ -641,14 +653,6 @@ EResult ThumbnailBlock::read_data(FILE& file, const FileHeader& file_header, con return EResult::Success; } -void ThumbnailBlock::update_checksum(Checksum& checksum) const -{ - checksum.append(params.format); - checksum.append(params.width); - checksum.append(params.height); - checksum.append(data); -} - EResult GCodeBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const { if (encoding_type > gcode_encoding_types_count()) @@ -690,7 +694,7 @@ EResult GCodeBlock::write(FILE& file, ECompressionType compression_type, EChecks if (checksum_type != EChecksumType::None) { Checksum cs(checksum_type); // update checksum with block header - block_header.update_checksum(cs); + update_checksum(cs, block_header); // update checksum with block payload std::vector data_to_encode = encode(reinterpret_cast(&encoding_type), sizeof(encoding_type)); @@ -748,7 +752,7 @@ EResult SlicerMetadataBlock::write(FILE& file, ECompressionType compression_type Checksum cs(checksum_type); // write block header, payload - EResult res = BaseMetadataBlock::write(file, EBlockType::SlicerMetadata, compression_type, cs); + EResult res = binarize::write(*this, file, EBlockType::SlicerMetadata, compression_type, cs); if (res != EResult::Success) // propagate error return res; diff --git a/src/LibBGCode/binarize/binarize.hpp b/src/LibBGCode/binarize/binarize.hpp index 9b9adab..6fe6f0e 100644 --- a/src/LibBGCode/binarize/binarize.hpp +++ b/src/LibBGCode/binarize/binarize.hpp @@ -13,8 +13,6 @@ struct BGCODE_BINARIZE_EXPORT BaseMetadataBlock // data in key/value form std::vector> raw_data; - // write block header and data in encoded format - core::EResult write(FILE& file, core::EBlockType block_type, core::ECompressionType compression_type, core::Checksum& checksum) const; // read block data in encoded format core::EResult read_data(FILE& file, const core::BlockHeader& block_header); }; @@ -52,9 +50,6 @@ struct BGCODE_BINARIZE_EXPORT ThumbnailBlock core::EResult write(FILE& file, core::EChecksumType checksum_type); // read block data core::EResult read_data(FILE& file, const core::FileHeader& file_header, const core::BlockHeader& block_header); - -private: - void update_checksum(core::Checksum& checksum) const; }; struct BGCODE_BINARIZE_EXPORT GCodeBlock diff --git a/src/LibBGCode/core/CMakeLists.txt b/src/LibBGCode/core/CMakeLists.txt index 3c87fb9..9232b08 100644 --- a/src/LibBGCode/core/CMakeLists.txt +++ b/src/LibBGCode/core/CMakeLists.txt @@ -4,6 +4,7 @@ set(Core_DOWNSTREAM_DEPS "") add_library(${_libname}_core core.cpp core.hpp + core_impl.hpp # Add more source files here if needed ) diff --git a/src/LibBGCode/core/core.cpp b/src/LibBGCode/core/core.cpp index 5194b40..685b72d 100644 --- a/src/LibBGCode/core/core.cpp +++ b/src/LibBGCode/core/core.cpp @@ -1,4 +1,4 @@ -#include "core.hpp" +#include "core_impl.hpp" #include namespace bgcode { namespace core { @@ -35,7 +35,7 @@ EResult verify_block_checksum(FILE& file, const FileHeader& file_header, Checksum curr_cs((EChecksumType)file_header.checksum_type); // update block checksum block header - block_header.update_checksum(curr_cs); + update_checksum(curr_cs, block_header); // read block payload size_t remaining_payload_size = block_payload_size(block_header); @@ -61,18 +61,12 @@ EResult verify_block_checksum(FILE& file, const FileHeader& file_header, return EResult::Success; } -static uint16_t checksum_types_count() { return 1 + (uint16_t)EChecksumType::CRC32; } -static uint16_t block_types_count() { return 1 + (uint16_t)EBlockType::Thumbnail; } -static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::Heatshrink_12_4; } - Checksum::Checksum(EChecksumType type) : m_type(type), m_size(checksum_size(type)) { m_checksum.fill(std::byte{0}); } -EChecksumType Checksum::get_type() const { return m_type; } - void Checksum::append(const std::vector& data) { append(data.data(), data.size()); @@ -101,6 +95,16 @@ EResult Checksum::read(FILE& file) return EResult::Success; } +FileHeader::FileHeader() + : magic{MAGICi32} + , version{VERSION} + , checksum_type{static_cast(EChecksumType::None)} +{} + +FileHeader::FileHeader(uint32_t mg, uint32_t ver, uint16_t chk_type) + : magic{mg}, version{ver}, checksum_type{chk_type} +{} + EResult FileHeader::write(FILE& file) const { if (magic != MAGICi32) @@ -145,15 +149,6 @@ BlockHeader::BlockHeader(uint16_t type, uint16_t compression, uint32_t uncompres , compressed_size(compressed_size) {} -void BlockHeader::update_checksum(Checksum& checksum) const -{ - checksum.append(type); - checksum.append(compression); - checksum.append(uncompressed_size); - if (compression != (uint16_t)ECompressionType::None) - checksum.append(compressed_size); -} - long BlockHeader::get_position() const { return m_position; @@ -528,7 +523,12 @@ BGCODE_CORE_EXPORT size_t checksum_size(EChecksumType type) BGCODE_CORE_EXPORT size_t block_content_size(const FileHeader& file_header, const BlockHeader& block_header) { - return block_payload_size(block_header) + checksum_size((EChecksumType)file_header.checksum_type); + return block_payload_size(block_header) + checksum_size((EChecksumType)file_header.checksum_type); +} + +uint32_t version() noexcept +{ + return VERSION; } } // namespace core diff --git a/src/LibBGCode/core/core.hpp b/src/LibBGCode/core/core.hpp index 07e9eca..e4ec81f 100644 --- a/src/LibBGCode/core/core.hpp +++ b/src/LibBGCode/core/core.hpp @@ -1,5 +1,5 @@ -#ifndef _BGCODE_CORE_HPP_ -#define _BGCODE_CORE_HPP_ +#ifndef BGCODE_CORE_HPP +#define BGCODE_CORE_HPP #include "core/export.h" @@ -7,8 +7,6 @@ #include #include #include -#include -#include #include #include #include @@ -16,92 +14,6 @@ namespace bgcode { namespace core { -static constexpr const std::array MAGIC{ 'G', 'C', 'D', 'E' }; -// Library version -static constexpr const uint32_t VERSION = 1; -// Max size of checksum buffer data, in bytes -// Increase this value if you implement a checksum algorithm needing a bigger buffer -static constexpr const size_t MAX_CHECKSUM_SIZE = 4; - -template -using IntegerOnly = std::enable_if_t, T>; - -template -using remove_cvref_t = std::remove_cv_t>; - -template constexpr bool IsBufferType = - std::is_convertible_v, std::byte> || - std::is_convertible_v, unsigned char> || - std::is_convertible_v, char>; - -template constexpr bool IsBufferIterator = - IsBufferType< typename std::iterator_traits::value_type>; - -template -using BufferTypeOnly = std::enable_if_t, T>; - -template -using BufferIteratorOnly = std::enable_if_t, T>; - -// For LE byte sequences only -template> -constexpr IntegerOnly load_integer(It from, It to) noexcept -{ - IntT result{}; - - size_t i = 0; - for (It it = from; it != to && i < sizeof(IntT); ++it) { - result |= (static_cast(*it) << (i++ * sizeof(std::byte) * CHAR_BIT)); - } - - return result; -} - -template -constexpr BufferIteratorOnly -store_integer_le(IntT value, OutIt out, size_t sz = sizeof(IntT)) -{ - for (size_t i = 0; i < std::min(sizeof(IntT), sz); ++i) - { - *out++ = static_cast::value_type>( - (value >> (i * CHAR_BIT)) & UCHAR_MAX - ); - } -} - -template -std::enable_if_t, void> -store_integer_le(IntT value, OutIt out, size_t sz = sizeof(IntT)) -{ - for (size_t i = 0; i < std::min(sizeof(IntT), sz); ++i) - { - *out++ = (value >> (i * CHAR_BIT)) & UCHAR_MAX; - } -} - -template> -static constexpr uint32_t crc32_sw(It from, It to, uint32_t crc) -{ - constexpr uint32_t ui32Max = 0xFFFFFFFF; - constexpr uint32_t crcMagic = 0xEDB88320; - - uint32_t value = crc ^ ui32Max; - for (auto it = from; it != to; ++it) { - value ^= load_integer(it, std::next(it)); - for (int bit = 0; bit < CHAR_BIT; bit++) { - if (value & 1) - value = (value >> 1) ^ crcMagic; - else - value >>= 1; - } - } - value ^= ui32Max; - - return value; -} - -constexpr auto MAGICi32 = load_integer(std::begin(MAGIC), std::end(MAGIC)); - enum class EResult : uint16_t { Success, @@ -179,64 +91,14 @@ enum class EThumbnailFormat : uint16_t QOI }; -class BGCODE_CORE_EXPORT Checksum -{ -public: - // Constructs a checksum of the given type. - // The checksum data are sized accordingly. - explicit Checksum(EChecksumType type); - - EChecksumType get_type() const; - - // Append vector of data to checksum - void append(const std::vector& data); - - // Append data to the checksum - template - void append(const BufT* data, size_t size) - { - if (data == nullptr || size == 0) - return; - - switch (m_type) - { - case EChecksumType::None: - { - break; - } - case EChecksumType::CRC32: - { - static_assert(sizeof(m_checksum) >= sizeof(uint32_t), "CRC32 checksum requires at least 4 bytes"); - const auto old_crc = load_integer(m_checksum.begin(), m_checksum.end()); //*(uint32_t*)m_checksum.data(); - const uint32_t new_crc = crc32_sw(data, data + size, old_crc); - store_integer_le(new_crc, m_checksum.begin(), m_checksum.size()); - break; - } - } - } - - // Append any aritmetic data to the checksum (shorthand for aritmetic types) - template::value, T>::type> - void append(T& data) { append(reinterpret_cast(&data), sizeof(data)); } - - // Returns true if the given checksum is equal to this one - bool matches(Checksum& other); - - EResult write(FILE& file); - EResult read(FILE& file); - -private: - EChecksumType m_type; - // actual size of checksum buffer, type dependent - size_t m_size; - std::array m_checksum; -}; - struct BGCODE_CORE_EXPORT FileHeader { - uint32_t magic{ MAGICi32 }; - uint32_t version{ VERSION }; - uint16_t checksum_type{ static_cast(EChecksumType::None) }; + uint32_t magic; + uint32_t version; + uint16_t checksum_type; + + FileHeader(); + FileHeader(uint32_t mg, uint32_t ver, uint16_t chk_type); EResult write(FILE& file) const; EResult read(FILE& file, const uint32_t* const max_version); @@ -252,9 +114,6 @@ struct BGCODE_CORE_EXPORT BlockHeader BlockHeader() = default; BlockHeader(uint16_t type, uint16_t compression, uint32_t uncompressed_size, uint32_t compressed_size = 0); - // Updates the given checksum with the data of this BlockHeader - void update_checksum(Checksum& checksum) const; - // Returns the position of this block in the file. // Position is set by calling write() and read() methods. long get_position() const; @@ -347,6 +206,8 @@ extern BGCODE_CORE_EXPORT size_t checksum_size(EChecksumType type); // Returns the size of the content (parameters + data + checksum) of the block with the given header, in bytes. extern BGCODE_CORE_EXPORT size_t block_content_size(const FileHeader& file_header, const BlockHeader& block_header); +extern BGCODE_CORE_EXPORT uint32_t version() noexcept; + }} // bgcode::core -#endif // _BGCODE_CORE_HPP_ +#endif // BGCODE_CORE_HPP diff --git a/src/LibBGCode/core/core_impl.hpp b/src/LibBGCode/core/core_impl.hpp new file mode 100644 index 0000000..2e7f654 --- /dev/null +++ b/src/LibBGCode/core/core_impl.hpp @@ -0,0 +1,180 @@ +#ifndef CORE_IMPL_HPP +#define CORE_IMPL_HPP + +#include +#include +#include +#include + +#include "core.hpp" + +namespace bgcode { namespace core { + +// Max size of checksum buffer data, in bytes +// Increase this value if you implement a checksum algorithm needing a bigger buffer +static constexpr const size_t MAX_CHECKSUM_SIZE = 4; + +static constexpr const std::array MAGIC{ 'G', 'C', 'D', 'E' }; + +// Library version +static constexpr const uint32_t VERSION = 1; + +template +using IntegerOnly = std::enable_if_t, T>; + +template +using remove_cvref_t = std::remove_cv_t>; + +template constexpr bool IsBufferType = + std::is_convertible_v, std::byte> || + std::is_convertible_v, unsigned char> || + std::is_convertible_v, char>; + +template constexpr bool IsBufferIterator = + IsBufferType< typename std::iterator_traits::value_type>; + +template +using BufferTypeOnly = std::enable_if_t, T>; + +template +using BufferIteratorOnly = std::enable_if_t, T>; + +// For LE byte sequences only +template> +constexpr IntegerOnly load_integer(It from, It to) noexcept +{ + IntT result{}; + + size_t i = 0; + for (It it = from; it != to && i < sizeof(IntT); ++it) { + result |= (static_cast(*it) << (i++ * sizeof(std::byte) * CHAR_BIT)); + } + + return result; +} + +template +constexpr BufferIteratorOnly +store_integer_le(IntT value, OutIt out, size_t sz = sizeof(IntT)) +{ + for (size_t i = 0; i < std::min(sizeof(IntT), sz); ++i) + { + *out++ = static_cast::value_type>( + (value >> (i * CHAR_BIT)) & UCHAR_MAX + ); + } +} + +template +std::enable_if_t, void> +store_integer_le(IntT value, OutIt out, size_t sz = sizeof(IntT)) +{ + for (size_t i = 0; i < std::min(sizeof(IntT), sz); ++i) + { + *out++ = (value >> (i * CHAR_BIT)) & UCHAR_MAX; + } +} + +template> +static constexpr uint32_t crc32_sw(It from, It to, uint32_t crc) +{ + constexpr uint32_t ui32Max = 0xFFFFFFFF; + constexpr uint32_t crcMagic = 0xEDB88320; + + uint32_t value = crc ^ ui32Max; + for (auto it = from; it != to; ++it) { + value ^= load_integer(it, std::next(it)); + for (int bit = 0; bit < CHAR_BIT; bit++) { + if (value & 1) + value = (value >> 1) ^ crcMagic; + else + value >>= 1; + } + } + value ^= ui32Max; + + return value; +} + +template +constexpr auto to_underlying(Enum enumval) noexcept +{ + return static_cast>(enumval); +} + +class BGCODE_CORE_EXPORT Checksum +{ +public: + // Constructs a checksum of the given type. + // The checksum data are sized accordingly. + explicit Checksum(EChecksumType type); + + EChecksumType get_type() const noexcept { return m_type; } + + // Append vector of data to checksum + void append(const std::vector& data); + + // Append data to the checksum + template + void append(const BufT* data, size_t size); + + // Append any aritmetic data to the checksum (shorthand for aritmetic types) + template::value, T>::type> + void append(T& data) { append(reinterpret_cast(&data), sizeof(data)); } + + // Returns true if the given checksum is equal to this one + bool matches(Checksum& other); + + EResult write(FILE& file); + EResult read(FILE& file); + +private: + EChecksumType m_type; + // actual size of checksum buffer, type dependent + size_t m_size; + std::array m_checksum; +}; + +// Updates the given checksum with the data of this BlockHeader +inline void update_checksum(Checksum& checksum, const BlockHeader &block_header) +{ + checksum.append(block_header.type); + checksum.append(block_header.compression); + checksum.append(block_header.uncompressed_size); + if (block_header.compression != to_underlying(ECompressionType::None)) + checksum.append(block_header.compressed_size); +} + +template +void Checksum::append(const BufT *data, size_t size) +{ + if (data == nullptr || size == 0) + return; + + switch (m_type) + { + case EChecksumType::None: + { + break; + } + case EChecksumType::CRC32: + { + static_assert(sizeof(m_checksum) >= sizeof(uint32_t), "CRC32 checksum requires at least 4 bytes"); + const auto old_crc = load_integer(m_checksum.begin(), m_checksum.end()); //*(uint32_t*)m_checksum.data(); + const uint32_t new_crc = crc32_sw(data, data + size, old_crc); + store_integer_le(new_crc, m_checksum.begin(), m_checksum.size()); + break; + } + } +} + +static constexpr auto MAGICi32 = load_integer(std::begin(MAGIC), std::end(MAGIC)); + +constexpr auto checksum_types_count() noexcept { auto v = to_underlying(EChecksumType::CRC32); ++v; return v;} +constexpr auto block_types_count() noexcept { auto v = to_underlying(EBlockType::Thumbnail); ++v; return v; } +constexpr auto compression_types_count() noexcept { auto v = to_underlying(ECompressionType::Heatshrink_12_4); ++v; return v; } + +} // namespace core +} // namespace bgcode + +#endif // CORE_IMPL_HPP