Skip to content

Commit

Permalink
Merge pull request #72 from kaitai-io/self-sufficient-includes
Browse files Browse the repository at this point in the history
Make each .cpp/.h file self-sufficient in `#include`s
  • Loading branch information
generalmimon authored Jun 30, 2024
2 parents 0a047c2 + 0373ae6 commit 675899a
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 50 deletions.
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
#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

0 comments on commit 675899a

Please sign in to comment.