Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make each .cpp/.h file self-sufficient in #includes #72

Merged
merged 9 commits into from
Jun 30, 2024
26 changes: 26 additions & 0 deletions .build/install-gtest
Original file line number Diff line number Diff line change
@@ -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
20 changes: 17 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 3 additions & 3 deletions kaitai/exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@

#include <kaitai/kaitaistream.h>

#include <string>
#include <stdexcept>
#include <stdexcept> // std::runtime_error
#include <string> // 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()
Expand Down
74 changes: 43 additions & 31 deletions kaitai/kaitaistream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <sys/param.h>
#include <sys/param.h> // `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
Expand All @@ -47,10 +47,18 @@
#endif
#endif

#include <stdint.h> // int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t

#include <algorithm> // std::reverse
#include <cerrno> // errno, EINVAL, E2BIG, EILSEQ, ERANGE
#include <cstdlib> // std::size_t, std::strtoll
#include <cstring> // std::memcpy
#include <iostream>
#include <vector>
#include <stdexcept>
#include <ios> // std::streamsize
#include <istream> // std::istream // IWYU pragma: keep
#include <sstream> // std::stringstream, std::ostringstream // IWYU pragma: keep
#include <stdexcept> // std::runtime_error, std::invalid_argument, std::out_of_range
#include <string> // std::string, std::getline
#include <vector> // std::vector

#ifdef KAITAI_STREAM_H_CPP11_SUPPORT
#include <type_traits> // std::enable_if, std::is_trivially_copyable, std::is_trivially_constructible
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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));
}
Expand All @@ -580,6 +588,14 @@ std::string kaitai::kstream::process_rotate_left(std::string data, int amount) {
#ifdef KS_ZLIB
#include <zlib.h>

// This instructs include-what-you-use not to suggest `#include <zconf.h>` just because it contains
// the definition of `Bytef`. It seems `<zconf.h>` is not a header for public use or at least it's
// not considered necessary to include it on top of `<zlib.h>`, because official usage examples that
// use `Bytef` only include `<zlib.h>`, see
// https://github.com/madler/zlib/blob/0f51fb4933fc9ce18199cb2554dacea8033e7fd3/test/example.c#L71
//
// IWYU pragma: no_include <zconf.h>

std::string kaitai::kstream::process_zlib(std::string data) {
int ret;

Expand Down Expand Up @@ -638,7 +654,6 @@ int kaitai::kstream::mod(int a, int b) {
return r;
}

#include <algorithm>
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)
Expand All @@ -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()) {
Expand Down Expand Up @@ -712,10 +727,7 @@ uint8_t kaitai::kstream::byte_array_max(const std::string val) {
#endif

#ifdef KS_STR_ENCODING_ICONV

#include <iconv.h>
#include <cerrno>
#include <stdexcept>

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);
Expand All @@ -728,27 +740,27 @@ 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.
char *src_ptr = const_cast<char*>(src.data());
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);
Expand Down
20 changes: 11 additions & 9 deletions kaitai/kaitaistream.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@
#define KAITAI_STREAM_H_CPP11_SUPPORT
#endif

#include <istream>
#include <sstream>
#include <stdint.h>
#include <sys/types.h>
#include <limits>
#include <stdexcept>
#include <errno.h>
#include <stdint.h> // int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t

#include <ios> // std::streamsize, forward declaration of std::istream // IWYU pragma: keep
#include <limits> // std::numeric_limits
#include <sstream> // std::istringstream // IWYU pragma: keep
#include <string> // std::string

#ifdef KAITAI_STREAM_H_CPP11_SUPPORT
#include <type_traits> // std::enable_if, std::is_integral
generalmimon marked this conversation as resolved.
Show resolved Hide resolved
#endif

namespace kaitai {

Expand Down Expand Up @@ -233,8 +236,7 @@ class kstream {
* since C++11) in older C++ implementations.
*/
template<typename I>
// 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<I>::value &&
Expand Down
4 changes: 2 additions & 2 deletions tests/gtest-nano.h
Original file line number Diff line number Diff line change
@@ -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 <cmath>
#include <iostream>
#include <vector>
#include <math.h>

namespace testing {
struct TestInfo {
Expand Down Expand Up @@ -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; \
} \
Expand Down
10 changes: 8 additions & 2 deletions tests/unittest.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
#ifdef GTEST_NANO
#include "tests/gtest-nano.h"
#else
#include <gtest/gtest.h>
#include "gtest/gtest.h"
#endif

#include "kaitai/kaitaistream.h"
#include "kaitai/exceptions.h"
#include <sstream>

#include <stdint.h> // int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t

#include <limits> // std::numeric_limits
#include <sstream> // std::istringstream
#include <stdexcept> // std::out_of_range, std::invalid_argument
#include <string> // std::string

#define SETUP_STREAM(...) \
const uint8_t input_bytes[] = { __VA_ARGS__ }; \
Expand Down