diff --git a/.build/install-gtest b/.build/install-gtest new file mode 100755 index 0000000..03902b1 --- /dev/null +++ b/.build/install-gtest @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -ef + +# Build GoogleTest from source and install it +# +# See https://github.com/google/googletest/blob/d83fee138a9ae6cb7c03688a2d08d4043a39815d/googletest/README.md#build-with-cmake + +gtest_version_tag=v1.14.0 + +temp_dir=$(mktemp -d) +function cleanup { + rm -rf -- "$temp_dir" +} +trap 'cleanup' EXIT + +# Download +git clone --depth 1 -b "$gtest_version_tag" -- https://github.com/google/googletest.git "$temp_dir" +cd -- "$temp_dir" +mkdir build +cd build +# Configure - build only GoogleTest, not GoogleMock +cmake .. -DBUILD_GMOCK=OFF +# Build +make +# Install +sudo make install diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 38e86ce..3886ad7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,14 +18,28 @@ jobs: - '20' steps: - uses: actions/checkout@v4 - - name: restore + - name: install GoogleTest + run: .build/install-gtest + - name: install include-what-you-use (iwyu) + # NB: https://packages.ubuntu.com/jammy/iwyu apparently doesn't declare the `libclang-common-XXX-dev` package it + # needs as a dependency (without it, `include-what-you-use` fails with "fatal error: 'stddef.h' file not found" + # or similar), although this problem has been reported in 7 out of 7 bug reports at + # https://bugs.launchpad.net/ubuntu/+source/iwyu, the oldest being from 2014. + # + # Therefore, we deliberately require a fixed version of `iwyu` along with the compatible + # `libclang-common-XXX-dev` package. When a new version becomes available and we want to update to it, we'll + # have to change this hardcoded version manually and bump the `libclang-common-XXX-dev` version accordingly (see + # https://github.com/include-what-you-use/include-what-you-use/blob/master/README.md#clang-compatibility). run: | sudo apt-get update - sudo apt-get install -y libgtest-dev + sudo apt-get install -y iwyu=8.17-1 libclang-common-13-dev - name: build env: CPP_STANDARD: ${{ matrix.cpp-standard }} - run: .build/build -DCMAKE_CXX_STANDARD="$CPP_STANDARD" -DCMAKE_CXX_STANDARD_REQUIRED=ON -DCMAKE_CXX_EXTENSIONS=OFF + run: | + .build/build \ + -DCMAKE_CXX_STANDARD="$CPP_STANDARD" -DCMAKE_CXX_STANDARD_REQUIRED=ON -DCMAKE_CXX_EXTENSIONS=OFF \ + -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE='include-what-you-use;-Xiwyu;--verbose=3' - name: unittest run: .build/run-unittest diff --git a/kaitai/exceptions.h b/kaitai/exceptions.h index 8033b98..589c15e 100644 --- a/kaitai/exceptions.h +++ b/kaitai/exceptions.h @@ -3,15 +3,15 @@ #include -#include -#include +#include // std::runtime_error +#include // std::string // We need to use "noexcept" in virtual destructor of our exceptions // subclasses. Different compilers have different ideas on how to // achieve that: C++98 compilers prefer `throw()`, C++11 and later // use `noexcept`. We define KS_NOEXCEPT macro for that. -#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) +#ifdef KAITAI_STREAM_H_CPP11_SUPPORT #define KS_NOEXCEPT noexcept #else #define KS_NOEXCEPT throw() diff --git a/kaitai/kaitaistream.cpp b/kaitai/kaitaistream.cpp index 044aaab..940cc37 100644 --- a/kaitai/kaitaistream.cpp +++ b/kaitai/kaitaistream.cpp @@ -29,7 +29,7 @@ #define __LITTLE_ENDIAN LITTLE_ENDIAN #else // At this point it's either Linux or BSD. Both have "sys/param.h", so it's safe to include -#include +#include // `BSD` macro // IWYU pragma: keep #if defined(BSD) // Supposed to work on FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=bswap16&manpath=FreeBSD+14.0-RELEASE // Supposed to work on NetBSD: https://man.netbsd.org/NetBSD-10.0/bswap16.3 @@ -47,10 +47,18 @@ #endif #endif +#include // int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t + +#include // std::reverse +#include // errno, EINVAL, E2BIG, EILSEQ, ERANGE +#include // std::size_t, std::strtoll #include // std::memcpy -#include -#include -#include +#include // std::streamsize +#include // std::istream // IWYU pragma: keep +#include // std::stringstream, std::ostringstream // IWYU pragma: keep +#include // std::runtime_error, std::invalid_argument, std::out_of_range +#include // std::string, std::getline +#include // std::vector #ifdef KAITAI_STREAM_H_CPP11_SUPPORT #include // std::enable_if, std::is_trivially_copyable, std::is_trivially_constructible @@ -156,9 +164,9 @@ uint64_t kaitai::kstream::pos() { } uint64_t kaitai::kstream::size() { - std::iostream::pos_type cur_pos = m_io->tellg(); - m_io->seekg(0, std::ios::end); - std::iostream::pos_type len = m_io->tellg(); + std::istream::pos_type cur_pos = m_io->tellg(); + m_io->seekg(0, std::istream::end); + std::istream::pos_type len = m_io->tellg(); m_io->seekg(cur_pos); return len; } @@ -466,10 +474,10 @@ std::string kaitai::kstream::read_bytes(std::streamsize len) { } std::string kaitai::kstream::read_bytes_full() { - std::iostream::pos_type p1 = m_io->tellg(); - m_io->seekg(0, std::ios::end); - std::iostream::pos_type p2 = m_io->tellg(); - size_t len = p2 - p1; + std::istream::pos_type p1 = m_io->tellg(); + m_io->seekg(0, std::istream::end); + std::istream::pos_type p2 = m_io->tellg(); + std::size_t len = p2 - p1; // Note: this requires a std::string to be backed with a // contiguous buffer. Officially, it's a only requirement since @@ -540,22 +548,22 @@ std::string kaitai::kstream::bytes_terminate(std::string src, char term, bool in // ======================================================================== std::string kaitai::kstream::process_xor_one(std::string data, uint8_t key) { - size_t len = data.length(); + std::size_t len = data.length(); std::string result(len, ' '); - for (size_t i = 0; i < len; i++) + for (std::size_t i = 0; i < len; i++) result[i] = data[i] ^ key; return result; } std::string kaitai::kstream::process_xor_many(std::string data, std::string key) { - size_t len = data.length(); - size_t kl = key.length(); + std::size_t len = data.length(); + std::size_t kl = key.length(); std::string result(len, ' '); - size_t ki = 0; - for (size_t i = 0; i < len; i++) { + std::size_t ki = 0; + for (std::size_t i = 0; i < len; i++) { result[i] = data[i] ^ key[ki]; ki++; if (ki >= kl) @@ -566,10 +574,10 @@ std::string kaitai::kstream::process_xor_many(std::string data, std::string key) } std::string kaitai::kstream::process_rotate_left(std::string data, int amount) { - size_t len = data.length(); + std::size_t len = data.length(); std::string result(len, ' '); - for (size_t i = 0; i < len; i++) { + for (std::size_t i = 0; i < len; i++) { uint8_t bits = data[i]; result[i] = (bits << amount) | (bits >> (8 - amount)); } @@ -580,6 +588,14 @@ std::string kaitai::kstream::process_rotate_left(std::string data, int amount) { #ifdef KS_ZLIB #include +// This instructs include-what-you-use not to suggest `#include ` just because it contains +// the definition of `Bytef`. It seems `` is not a header for public use or at least it's +// not considered necessary to include it on top of ``, because official usage examples that +// use `Bytef` only include ``, see +// https://github.com/madler/zlib/blob/0f51fb4933fc9ce18199cb2554dacea8033e7fd3/test/example.c#L71 +// +// IWYU pragma: no_include + std::string kaitai::kstream::process_zlib(std::string data) { int ret; @@ -638,7 +654,6 @@ int kaitai::kstream::mod(int a, int b) { return r; } -#include void kaitai::kstream::unsigned_to_decimal(uint64_t number, char *buffer) { // Implementation from https://ideone.com/nrQfA8 by Alf P. Steinbach // (see https://www.zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html#comment-1033931478) @@ -659,7 +674,7 @@ int64_t kaitai::kstream::string_to_int(const std::string& str, int base) { char *str_end; errno = 0; - int64_t res = strtoll(str.c_str(), &str_end, base); + int64_t res = std::strtoll(str.c_str(), &str_end, base); // Check for successful conversion and throw an exception if the entire string was not converted if (str_end != str.c_str() + str.size()) { @@ -712,10 +727,7 @@ uint8_t kaitai::kstream::byte_array_max(const std::string val) { #endif #ifdef KS_STR_ENCODING_ICONV - #include -#include -#include std::string kaitai::kstream::bytes_to_str(const std::string src, const char *src_enc) { iconv_t cd = iconv_open(KS_STR_DEFAULT_ENCODING, src_enc); @@ -728,13 +740,13 @@ std::string kaitai::kstream::bytes_to_str(const std::string src, const char *src } } - size_t src_len = src.length(); - size_t src_left = src_len; + std::size_t src_len = src.length(); + std::size_t src_left = src_len; // Start with a buffer length of double the source length. - size_t dst_len = src_len * 2; + std::size_t dst_len = src_len * 2; std::string dst(dst_len, ' '); - size_t dst_left = dst_len; + std::size_t dst_left = dst_len; // NB: this should be const char *, but for some reason iconv() requires non-const in its 2nd argument, // so we force it with a cast. @@ -742,13 +754,13 @@ std::string kaitai::kstream::bytes_to_str(const std::string src, const char *src char *dst_ptr = &dst[0]; while (true) { - size_t res = iconv(cd, &src_ptr, &src_left, &dst_ptr, &dst_left); + std::size_t res = iconv(cd, &src_ptr, &src_left, &dst_ptr, &dst_left); - if (res == (size_t)-1) { + if (res == (std::size_t)-1) { if (errno == E2BIG) { // dst buffer is not enough to accomodate whole string // enlarge the buffer and try again - size_t dst_used = dst_len - dst_left; + std::size_t dst_used = dst_len - dst_left; dst_left += dst_len; dst_len += dst_len; dst.resize(dst_len); diff --git a/kaitai/kaitaistream.h b/kaitai/kaitaistream.h index d58ae8e..58b4dee 100644 --- a/kaitai/kaitaistream.h +++ b/kaitai/kaitaistream.h @@ -9,13 +9,16 @@ #define KAITAI_STREAM_H_CPP11_SUPPORT #endif -#include -#include -#include -#include -#include -#include -#include +#include // int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t + +#include // std::streamsize, forward declaration of std::istream // IWYU pragma: keep +#include // std::numeric_limits +#include // std::istringstream // IWYU pragma: keep +#include // std::string + +#ifdef KAITAI_STREAM_H_CPP11_SUPPORT +#include // std::enable_if, std::is_integral +#endif namespace kaitai { @@ -233,8 +236,7 @@ class kstream { * since C++11) in older C++ implementations. */ template -// check for C++11 support - https://stackoverflow.com/a/40512515 -#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) +#ifdef KAITAI_STREAM_H_CPP11_SUPPORT // https://stackoverflow.com/a/27913885 typename std::enable_if< std::is_integral::value && diff --git a/tests/gtest-nano.h b/tests/gtest-nano.h index 4b03fb5..00bae3c 100644 --- a/tests/gtest-nano.h +++ b/tests/gtest-nano.h @@ -1,9 +1,9 @@ // gtest-nano.h implements very minimalistic GTest-compatible API that can be used to run tests in older // (C++98-compatible) environments. +#include #include #include -#include namespace testing { struct TestInfo { @@ -72,7 +72,7 @@ namespace testing { // Floating point comparison macro #define EXPECT_FLOAT_EQ(a, b) \ do { \ - if (fabs(a - b) < 1e-6) { \ + if (std::fabs(a - b) < 1e-6) { \ } else { \ ::testing::g_testPass = false; \ } \ diff --git a/tests/unittest.cpp b/tests/unittest.cpp index ce34152..ae1a56d 100644 --- a/tests/unittest.cpp +++ b/tests/unittest.cpp @@ -1,12 +1,18 @@ #ifdef GTEST_NANO #include "tests/gtest-nano.h" #else -#include +#include "gtest/gtest.h" #endif #include "kaitai/kaitaistream.h" #include "kaitai/exceptions.h" -#include + +#include // int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t + +#include // std::numeric_limits +#include // std::istringstream +#include // std::out_of_range, std::invalid_argument +#include // std::string #define SETUP_STREAM(...) \ const uint8_t input_bytes[] = { __VA_ARGS__ }; \