crc32
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a6ef824 --- /dev/null +++ b/.gitignore
@@ -0,0 +1 @@ +/bazel-*
diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..2433dd7 --- /dev/null +++ b/WORKSPACE
@@ -0,0 +1,15 @@ +workspace(name = "protodatastore-cpp") + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "com_google_absl", + strip_prefix = "abseil-cpp-master", + urls = ["https://github.com/abseil/abseil-cpp/archive/master.zip"], +) + +http_archive( + name = "googletest", + strip_prefix = "googletest-master", + urls = ["https://github.com/google/googletest/archive/master.zip"], +)
diff --git a/protostore/BUILD b/protostore/BUILD new file mode 100644 index 0000000..ee5be81 --- /dev/null +++ b/protostore/BUILD
@@ -0,0 +1,21 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "crc32", + srcs = ["crc32.cc"], + hdrs = ["crc32.h"], + visibility = ["//visibility:private"], + deps = [ + "@com_google_absl//absl/strings", + ] +) + +cc_test( + name = "crc32_test", + srcs = ["crc32_test.cc"], + visibility = ["//visibility:private"], + deps = [ + ":crc32", + "@googletest//:gtest_main", + ], +)
diff --git a/protostore/crc32.cc b/protostore/crc32.cc new file mode 100644 index 0000000..4adfe74 --- /dev/null +++ b/protostore/crc32.cc
@@ -0,0 +1,29 @@ +#include "protostore/crc32.h" + +#include <cstdint> +#include <zlib.h> + +#include "absl/strings/string_view.h" + +namespace protostore { + +namespace { +uint32_t UpdateCrc32(uint32_t crc, const absl::string_view str) { + if (str.length() > 0) { + // crc32() already includes a pre- and post-condition of taking the one's + // complement of the value. + crc = + ~crc32(~crc, reinterpret_cast<const Bytef*>(str.data()), str.length()); + } + return crc; +} +} // namespace + +uint32_t Crc32::Get() const { return crc_; } + +uint32_t Crc32::Append(const absl::string_view str) { + crc_ = UpdateCrc32(crc_, str); + return crc_; +} + +} // namespace mobstore
diff --git a/protostore/crc32.h b/protostore/crc32.h new file mode 100644 index 0000000..4aa123c --- /dev/null +++ b/protostore/crc32.h
@@ -0,0 +1,52 @@ +#ifndef PROTOSTORE_CRC32_H_ +#define PROTOSTORE_CRC32_H_ + +#include <cstdint> +#include <zlib.h> + +#include "absl/strings/string_view.h" + +namespace protostore { + +// Efficient mechanism to incrementally compute checksum of a file and keep it +// updated when its content changes. Internally uses zlib based crc32() +// implementation. +// +// See https://www.zlib.net/manual.html#Checksum for more details. +// +// CRC32C::Extend in //util/hash/crc32c.h crashes as described in b/145837799. +class Crc32 { + public: + // Default to the checksum of an empty string, that is "0". + Crc32() : crc_(0) {} + + explicit Crc32(uint32_t init_crc) : crc_(init_crc) {} + + inline bool operator==(const Crc32& other) const { + return crc_ == other.Get(); + } + + // Returns the checksum of all the data that has been processed till now. + uint32_t Get() const; + + // Incrementally update the current checksum to reflect the fact that the + // underlying data has been appended with 'str'. It calculates a new crc32 + // based on the current crc value and the newly appended string. + // + // NOTE: As this method accepts incremental appends, all these 3 will lead to + // the same checksum: + // 1) crc32.Append("AAA"); crc32.Append("BBB"); + // 2) crc32.Append("AAABBB"); + // 3) crc32.Append("AA"); crc32.Append("AB"); crc32.Append("BB"); + // + // NOTE: While this class internally uses zlib's crc32(), + // Crc32(base_crc).Append(str) is not the same as zlib::crc32(base_crc, str); + uint32_t Append(absl::string_view str); + + private: + uint32_t crc_; +}; + +} // namespace protostore + +#endif // PROTOSTORE_CRC32_H_
diff --git a/protostore/crc32_test.cc b/protostore/crc32_test.cc new file mode 100644 index 0000000..186db6b --- /dev/null +++ b/protostore/crc32_test.cc
@@ -0,0 +1,45 @@ +#include "protostore/crc32.h" + +#include <cstdint> + +#include "googlemock/include/gmock/gmock.h" +#include "googletest/include/gtest/gtest.h" + +namespace protostore { + +namespace { + +using ::testing::Eq; + +TEST(Crc32Test, Get) { + Crc32 crc32_test{10}; + Crc32 crc32_test_empty{}; + EXPECT_THAT(crc32_test.Get(), Eq(10)); + EXPECT_THAT(crc32_test_empty.Get(), Eq(0)); +} + +TEST(Crc32Test, Append) { + // Test the complement logic inside Append() + const uLong kCrcInitZero = crc32(0L, nullptr, 0); + uint32_t foo_crc = + crc32(kCrcInitZero, reinterpret_cast<const Bytef*>("foo"), 3); + uint32_t foobar_crc = + crc32(kCrcInitZero, reinterpret_cast<const Bytef*>("foobar"), 6); + + Crc32 crc32_test(~foo_crc); + ASSERT_THAT(~crc32_test.Append("bar"), Eq(foobar_crc)); + + // Test Append() that appending things separately should be the same as + // appending in one shot + Crc32 crc32_foobar{}; + crc32_foobar.Append("foobar"); + Crc32 crc32_foo_and_bar{}; + crc32_foo_and_bar.Append("foo"); + crc32_foo_and_bar.Append("bar"); + + EXPECT_THAT(crc32_foo_and_bar.Get(), Eq(crc32_foobar.Get())); +} + +} // namespace + +} // namespace mobstore