From bb96c368c20c4e5ef99de11f45ef776728ac8a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Wed, 18 Apr 2018 21:01:02 +0300 Subject: [PATCH] MXS-1810: Create generic Checksum class The Checksum class defines an interface which the SHA1Checksum and CRC32Checksum implement. Added test unit test cases to verify that the checksums work and perform as expected. --- include/maxscale/utils.hh | 134 ++++++++++++++++++++++++--------- server/core/test/test_utils.cc | 49 ++++++++++++ 2 files changed, 147 insertions(+), 36 deletions(-) diff --git a/include/maxscale/utils.hh b/include/maxscale/utils.hh index f5ea0681d..1feb71f5a 100644 --- a/include/maxscale/utils.hh +++ b/include/maxscale/utils.hh @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -347,34 +348,20 @@ std::string to_hex(Iter begin, Iter end) } /** - * A SHA1 wrapper class + * Base class for checksums */ class Checksum { public: - typedef std::array Sum; - - /** - * Create a new Checksum - */ - Checksum() - { - SHA1_Init(&m_ctx); - } + virtual ~Checksum() {} /** * Update the checksum calculation * * @param buffer Buffer to add to the calculation */ - void update(GWBUF* buffer) - { - for (GWBUF* b = buffer; b; b = b->next) - { - SHA1_Update(&m_ctx, GWBUF_DATA(b), GWBUF_LENGTH(b)); - } - } + virtual void update(GWBUF* buffer) = 0; /** * Finalize the calculation @@ -388,6 +375,38 @@ public: * * @param buffer Optional buffer to process before finalizing */ + virtual void finalize(GWBUF* buffer = NULL) = 0; + + /** + * Get hexadecimal representation of the checksum + * + * @return String containing the hexadecimal form of the checksum + */ + virtual std::string hex() const = 0; +}; + +/** + * A SHA1 checksum + */ +class SHA1Checksum: public Checksum +{ +public: + + typedef std::array Sum; + + SHA1Checksum() + { + SHA1_Init(&m_ctx); + } + + void update(GWBUF* buffer) + { + for (GWBUF* b = buffer; b; b = b->next) + { + SHA1_Update(&m_ctx, GWBUF_DATA(b), GWBUF_LENGTH(b)); + } + } + void finalize(GWBUF* buffer = NULL) { update(buffer); @@ -395,40 +414,83 @@ public: SHA1_Init(&m_ctx); } - /** - * Equality comparison to another checksum - * - * @param rhs Right hand side - * - * @return True both checksums are equal - */ - bool eq(const Checksum& rhs) const - { - return m_sum == rhs.m_sum; - } - - /** - * Get hexadecimal representation of the checksum - * - * @return String containing the hexadecimal form of the checksum - */ std::string hex() const { return mxs::to_hex(m_sum.begin(), m_sum.end()); } + bool eq(const SHA1Checksum& rhs) const + { + return m_sum == rhs.m_sum; + } + private: SHA_CTX m_ctx; /**< SHA1 context */ Sum m_sum; /**< Final checksum */ }; -static inline bool operator ==(const Checksum& lhs, const Checksum& rhs) +static inline bool operator ==(const SHA1Checksum& lhs, const SHA1Checksum& rhs) { return lhs.eq(rhs); } -static inline bool operator !=(const Checksum& lhs, const Checksum& rhs) +static inline bool operator !=(const SHA1Checksum& lhs, const SHA1Checksum& rhs) +{ + return !(lhs == rhs); +} + +/** + * A CRC32 checksum + */ +class CRC32Checksum: public Checksum +{ +public: + + CRC32Checksum() + { + m_ctx = crc32(0L, NULL, 0); + } + + void update(GWBUF* buffer) + { + for (GWBUF* b = buffer; b; b = b->next) + { + m_ctx = crc32(m_ctx, GWBUF_DATA(b), GWBUF_LENGTH(b)); + } + } + + void finalize(GWBUF* buffer = NULL) + { + update(buffer); + m_sum = m_ctx; + m_ctx = crc32(0L, NULL, 0); + } + + std::string hex() const + { + const uint8_t* start = reinterpret_cast(&m_sum); + const uint8_t* end = start + sizeof(m_sum); + return mxs::to_hex(start, end); + } + + bool eq(const CRC32Checksum& rhs) const + { + return m_sum == rhs.m_sum; + } + +private: + + uint32_t m_ctx; /**< Ongoing checksum value */ + uint32_t m_sum; /**< Final checksum */ +}; + +static inline bool operator ==(const CRC32Checksum& lhs, const CRC32Checksum& rhs) +{ + return lhs.eq(rhs); +} + +static inline bool operator !=(const CRC32Checksum& lhs, const CRC32Checksum& rhs) { return !(lhs == rhs); } diff --git a/server/core/test/test_utils.cc b/server/core/test/test_utils.cc index 6dcb3bb53..a3d6bdd70 100644 --- a/server/core/test/test_utils.cc +++ b/server/core/test/test_utils.cc @@ -12,6 +12,7 @@ */ #include +#include #include #include @@ -118,6 +119,52 @@ int test_trim_trailing() } +template +int test_checksums() +{ + uint8_t data[] = + { + 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!' + }; + + GWBUF* d1 = gwbuf_alloc_and_load(sizeof(data), data); + GWBUF* d2 = gwbuf_alloc_and_load(sizeof(data), data); + + T sum1, sum2; + sum1.update(d1); + sum1.finalize(); + sum2.finalize(d1); + ss_dassert(sum1 == sum2); + + // Check that the hex strings match + ss_dassert(sum1.hex() == sum2.hex()); + + std::string saved = sum1.hex(); + + // The checksum must not be empty + ss_dassert(!saved.empty()); + + // Repeat the same test, should produce the same checksums + sum1.update(d1); + sum1.finalize(); + sum2.finalize(d1); + ss_dassert(sum1 == sum2); + ss_dassert(sum1.hex() == saved); + ss_dassert(sum2.hex() == saved); + + // Check that different buffers but same content produce the same checksum + sum1.finalize(d2); + sum2.finalize(d1); + ss_dassert(sum1 == sum2); + ss_dassert(sum1.hex() == saved); + ss_dassert(sum2.hex() == saved); + + gwbuf_free(d1); + gwbuf_free(d2); + + return 0; +} + int main(int argc, char* argv[]) { int rv = 0; @@ -125,6 +172,8 @@ int main(int argc, char* argv[]) rv += test_trim(); rv += test_trim_leading(); rv += test_trim_trailing(); + rv += test_checksums(); + rv += test_checksums(); return rv; }