From 15d89aed5c88b9498960bb4659d24b9973a9d0c6 Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sat, 12 Nov 2022 16:29:38 +0800 Subject: [PATCH] add: [imp] byte & ut --- include/libimp/byte.h | 169 ++++++++++++++++++++++++ include/libimp/span.h | 37 +++++- test/CMakeLists.txt | 8 +- test/imp/test_imp_byte.cpp | 65 +++++++++ test/{ => imp}/test_imp_codecvt.cpp | 0 test/{ => imp}/test_imp_detect_plat.cpp | 0 test/{ => imp}/test_imp_log.cpp | 0 test/{ => imp}/test_imp_result.cpp | 0 test/{ => imp}/test_imp_span.cpp | 7 +- test/{ => imp}/test_imp_system.cpp | 0 test/{ => imp}/test_imp_utility.cpp | 0 11 files changed, 276 insertions(+), 10 deletions(-) create mode 100644 include/libimp/byte.h create mode 100644 test/imp/test_imp_byte.cpp rename test/{ => imp}/test_imp_codecvt.cpp (100%) rename test/{ => imp}/test_imp_detect_plat.cpp (100%) rename test/{ => imp}/test_imp_log.cpp (100%) rename test/{ => imp}/test_imp_result.cpp (100%) rename test/{ => imp}/test_imp_span.cpp (87%) rename test/{ => imp}/test_imp_system.cpp (100%) rename test/{ => imp}/test_imp_utility.cpp (100%) diff --git a/include/libimp/byte.h b/include/libimp/byte.h new file mode 100644 index 0000000..89faeaa --- /dev/null +++ b/include/libimp/byte.h @@ -0,0 +1,169 @@ +/** + * @file libimp/byte.h + * @author mutouyun (orz@orzz.org) + * @brief Define the byte type + * @date 2022-11-12 + */ +#pragma once + +#include +#include +#include // std::byte (since C++17) + +#include "fmt/format.h" + +#include "libimp/def.h" +#include "libimp/detect_plat.h" + +#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_byte) +#define LIBIMP_CPP_LIB_BYTE_ +#endif // __cpp_lib_byte + +LIBIMP_NAMESPACE_BEG_ + +class byte; + +namespace detail { + +template +using is_integral = + typename std::enable_if::value>::type; + +template +using is_not_byte = + typename std::enable_if::type, byte>::value>::type; + +} // namespace detail + +/** + * @brief A distinct type that implements the concept of byte as specified in the C++ language definition. + * @see https://en.cppreference.com/w/cpp/types/byte + */ +class byte { + std::uint8_t bits_; + +public: + constexpr byte() noexcept + : byte(0) {} + + template > + constexpr byte(T v) noexcept + : bits_(static_cast(v)) {} + +#ifdef LIBIMP_CPP_LIB_BYTE_ + constexpr byte(std::byte b) noexcept + : byte(std::to_integer(b)) {} +#endif // LIBIMP_CPP_LIB_BYTE_ + + template > + explicit constexpr operator T() const noexcept { + return static_cast(bits_); + } + +#ifdef LIBIMP_CPP_LIB_BYTE_ + explicit constexpr operator std::byte() const noexcept { + /// @brief C++17 relaxed enum class initialization rules. + /// @see https://en.cppreference.com/w/cpp/language/enum#enum_relaxed_init_cpp17 + return std::byte{bits_}; + } +#endif // LIBIMP_CPP_LIB_BYTE_ + + friend bool operator==(byte const &lhs, byte const &rhs) noexcept { + return lhs.bits_ == rhs.bits_; + } + + friend bool operator!=(byte const &lhs, byte const &rhs) noexcept { + return !(lhs == rhs); + } +}; + +/** + * @brief Non-member functions. + */ + +template > +constexpr T to_integer(byte b) noexcept { + return T(b); +} + +/// @brief std::operator<<, operator>> + +template > +constexpr byte operator<<(byte b, T shift) noexcept { + return byte(to_integer(b) << shift); +} + +template > +constexpr byte operator>>(byte b, T shift) noexcept { + return byte(to_integer(b) >> shift); +} + +/// @brief std::operator<<=, operator>>= + +template > +constexpr byte &operator<<=(byte &b, T shift) noexcept { + return b = b << shift; +} + +template > +constexpr byte &operator>>=(byte &b, T shift) noexcept { + return b = b >> shift; +} + +/// @brief std::operator|, operator&, operator^, operator~ + +constexpr byte operator|(byte l, byte r) noexcept { return byte(to_integer(l) | to_integer(r)); } +constexpr byte operator&(byte l, byte r) noexcept { return byte(to_integer(l) & to_integer(r)); } +constexpr byte operator^(byte l, byte r) noexcept { return byte(to_integer(l) ^ to_integer(r)); } +constexpr byte operator~(byte b) noexcept { return byte(~to_integer(b)); } + +/// @brief std::operator|=, operator&=, operator^= + +constexpr byte &operator|=(byte &l, byte r) noexcept { return l = l | r; } +constexpr byte &operator&=(byte &l, byte r) noexcept { return l = l & r; } +constexpr byte &operator^=(byte &l, byte r) noexcept { return l = l ^ r; } + +/// @brief Cast pointer to byte*. + +template > +byte *byte_cast(T *p) noexcept { + return reinterpret_cast(p); +} + +template > +byte const *byte_cast(T const *p) noexcept { + return reinterpret_cast(p); +} + +/// @brief Cast byte* to a pointer of another type. + +template > +T *byte_cast(byte *p) noexcept { + if (reinterpret_cast(p) % alignof(T) != 0) { + return nullptr; + } + return reinterpret_cast(p); +} + +template ::type, + typename = detail::is_not_byte> +U *byte_cast(byte const *p) noexcept { + if (reinterpret_cast(p) % alignof(T) != 0) { + return nullptr; + } + return reinterpret_cast(p); +} + +LIBIMP_NAMESPACE_END_ + +template <> +struct fmt::formatter<::LIBIMP_::byte> { + constexpr auto parse(format_parse_context& ctx) const { + return ctx.end(); + } + template + auto format(::LIBIMP_::byte b, FormatContext &ctx) { + return format_to(ctx.out(), "{:#04x}", static_cast(b)); + } +}; diff --git a/include/libimp/span.h b/include/libimp/span.h index d2596bb..814c06e 100644 --- a/include/libimp/span.h +++ b/include/libimp/span.h @@ -16,12 +16,17 @@ #include #include #include -#ifdef LIBIMP_CPP_20 -#include -#endif // LIBIMP_CPP_20 + +#include "fmt/format.h" #include "libimp/def.h" #include "libimp/detect_plat.h" +#include "libimp/byte.h" + +#if defined(LIBIMP_CPP_20) && defined(__cpp_lib_span) +#include +#define LIBIMP_CPP_LIB_SPAN_ +#endif // __cpp_lib_span LIBIMP_NAMESPACE_BEG_ namespace detail { @@ -138,13 +143,13 @@ public: : ptr_ (s.data()) , extent_(s.size()) {} -#if defined(LIBIMP_CPP_20) || defined(__cpp_lib_span) +#ifdef LIBIMP_CPP_LIB_SPAN_ template > constexpr span(std::span const &s) noexcept : ptr_ (s.data()) , extent_(s.size()) {} -#endif // LIBIMP_CPP_20 +#endif // LIBIMP_CPP_LIB_SPAN_ constexpr size_type size() const noexcept { return extent_; @@ -221,9 +226,9 @@ bool operator==(span a, span b) noexcept { /// @see https://en.cppreference.com/w/cpp/container/span/as_bytes template ::value, std::uint8_t const, std::uint8_t>::type> + typename Byte = typename std::conditional::value, byte const, byte>::type> auto as_bytes(span s) noexcept -> span { - return {reinterpret_cast(s.data()), s.size_bytes()}; + return {byte_cast(s.data()), s.size_bytes()}; } /// @brief Constructs an object of type T and wraps it in a span. @@ -274,3 +279,21 @@ inline auto make_span(std::string const &str) noexcept -> span { } LIBIMP_NAMESPACE_END_ + +template +struct fmt::formatter<::LIBIMP_::span> { + constexpr auto parse(format_parse_context& ctx) const { + return ctx.end(); + } + template + auto format(::LIBIMP_::span s, FormatContext &ctx) { + if (s.empty()) { + return format_to(ctx.out(), ""); + } + auto appender = format_to(ctx.out(), "{}", s[0]); + for (std::size_t i = 1; i < s.size(); ++i) { + appender = format_to(appender, " {}", s[i]); + } + return appender; + } +}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d8c88ff..3bc49f0 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,8 +13,12 @@ include_directories( ${LIBIPC_PROJECT_DIR}/src ${LIBIPC_PROJECT_DIR}/test) -file(GLOB SRC_FILES ${LIBIPC_PROJECT_DIR}/test/*.cpp) -file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/*.h) +file(GLOB SRC_FILES + ${LIBIPC_PROJECT_DIR}/test/imp/*.cpp + ${LIBIPC_PROJECT_DIR}/test/*.cpp) + +file(GLOB HEAD_FILES + ${LIBIPC_PROJECT_DIR}/test/*.h) add_executable(${PROJECT_NAME} ${SRC_FILES} ${HEAD_FILES}) diff --git a/test/imp/test_imp_byte.cpp b/test/imp/test_imp_byte.cpp new file mode 100644 index 0000000..ad2fd37 --- /dev/null +++ b/test/imp/test_imp_byte.cpp @@ -0,0 +1,65 @@ + +#include "gtest/gtest.h" + +#include "libimp/byte.h" +#include "libimp/span.h" + +TEST(byte, construct) { + { + imp::byte b; + EXPECT_EQ(int(b), 0); + } + { + imp::byte b {123}; + EXPECT_EQ(int(b), 123); + } + { + imp::byte b {65535}; + EXPECT_EQ(int(b), 255); + EXPECT_EQ(std::int8_t(b), -1); + } + { + imp::byte b {65536}; + EXPECT_EQ(int(b), 0); + } +} + +TEST(byte, compare) { + { + imp::byte b1, b2; + EXPECT_EQ(b1, b2); + } + { + imp::byte b1, b2(321); + EXPECT_NE(b1, b2); + } +} + +TEST(byte, fmt) { + { + imp::byte b1, b2(31); + EXPECT_EQ(fmt::format("{}", b1), "0x00"); + EXPECT_EQ(fmt::format("{}", b2), "0x1f"); + } + { + imp::byte bs[] {31, 32, 33, 34, 35, 36, 37, 38}; + EXPECT_EQ(fmt::format("{}", imp::make_span(bs)), + "0x1f 0x20 0x21 0x22 0x23 0x24 0x25 0x26"); + } +} + +TEST(byte, byte_cast) { + int a = 654321; + int *pa = &a; + + // int * => byte * + imp::byte *pb = imp::byte_cast(pa); + EXPECT_EQ((std::size_t)pb, (std::size_t)pa); + + // byte * => int32_t * + std::int32_t *pc = imp::byte_cast(pb); + EXPECT_EQ(*pc, a); + + // byte alignment check + EXPECT_EQ(imp::byte_cast(pb + 1), nullptr); +} \ No newline at end of file diff --git a/test/test_imp_codecvt.cpp b/test/imp/test_imp_codecvt.cpp similarity index 100% rename from test/test_imp_codecvt.cpp rename to test/imp/test_imp_codecvt.cpp diff --git a/test/test_imp_detect_plat.cpp b/test/imp/test_imp_detect_plat.cpp similarity index 100% rename from test/test_imp_detect_plat.cpp rename to test/imp/test_imp_detect_plat.cpp diff --git a/test/test_imp_log.cpp b/test/imp/test_imp_log.cpp similarity index 100% rename from test/test_imp_log.cpp rename to test/imp/test_imp_log.cpp diff --git a/test/test_imp_result.cpp b/test/imp/test_imp_result.cpp similarity index 100% rename from test/test_imp_result.cpp rename to test/imp/test_imp_result.cpp diff --git a/test/test_imp_span.cpp b/test/imp/test_imp_span.cpp similarity index 87% rename from test/test_imp_span.cpp rename to test/imp/test_imp_span.cpp index 0c7a482..f49ae25 100644 --- a/test/test_imp_span.cpp +++ b/test/imp/test_imp_span.cpp @@ -41,7 +41,7 @@ TEST(span, span) { test_proc(imp::make_span({6, 7, 8, 9}), sp.last(4)); test_proc(imp::make_span({3, 4, 5, 6}), sp.subspan(3, 4)); test_proc(imp::make_span({3, 4, 5, 6, 7, 8, 9}), sp.subspan(3)); - test_proc(imp::make_span((char *)sp.data(), sp.size_bytes()), imp::as_bytes(sp)); + test_proc(imp::make_span((imp::byte *)sp.data(), sp.size_bytes()), imp::as_bytes(sp)); } { std::string buf = "0123456789"; @@ -53,4 +53,9 @@ TEST(span, span) { test_proc(imp::make_span("3456", 4), sp.subspan(3, 4)); test_proc(imp::make_span("3456789", 7), sp.subspan(3)); } +} + +TEST(span, fmt) { + EXPECT_EQ(fmt::format("{}", imp::span{}), ""); + EXPECT_EQ(fmt::format("{}", imp::make_span({1, 3, 2, 4, 5, 6, 7})), "1 3 2 4 5 6 7"); } \ No newline at end of file diff --git a/test/test_imp_system.cpp b/test/imp/test_imp_system.cpp similarity index 100% rename from test/test_imp_system.cpp rename to test/imp/test_imp_system.cpp diff --git a/test/test_imp_utility.cpp b/test/imp/test_imp_utility.cpp similarity index 100% rename from test/test_imp_utility.cpp rename to test/imp/test_imp_utility.cpp