mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-07 01:06:45 +08:00
add: [imp] byte & ut
This commit is contained in:
parent
2e0f074287
commit
15d89aed5c
169
include/libimp/byte.h
Normal file
169
include/libimp/byte.h
Normal file
@ -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 <type_traits>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef> // 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 <typename T>
|
||||||
|
using is_integral =
|
||||||
|
typename std::enable_if<std::is_integral<T>::value>::type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using is_not_byte =
|
||||||
|
typename std::enable_if<!std::is_same<
|
||||||
|
typename std::remove_cv<T>::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 <typename T, typename = detail::is_integral<T>>
|
||||||
|
constexpr byte(T v) noexcept
|
||||||
|
: bits_(static_cast<std::uint8_t>(v)) {}
|
||||||
|
|
||||||
|
#ifdef LIBIMP_CPP_LIB_BYTE_
|
||||||
|
constexpr byte(std::byte b) noexcept
|
||||||
|
: byte(std::to_integer<std::uint8_t>(b)) {}
|
||||||
|
#endif // LIBIMP_CPP_LIB_BYTE_
|
||||||
|
|
||||||
|
template <typename T, typename = detail::is_integral<T>>
|
||||||
|
explicit constexpr operator T() const noexcept {
|
||||||
|
return static_cast<T>(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 <typename T, typename = detail::is_integral<T>>
|
||||||
|
constexpr T to_integer(byte b) noexcept {
|
||||||
|
return T(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief std::operator<<, operator>>
|
||||||
|
|
||||||
|
template <typename T, typename = detail::is_integral<T>>
|
||||||
|
constexpr byte operator<<(byte b, T shift) noexcept {
|
||||||
|
return byte(to_integer<unsigned>(b) << shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = detail::is_integral<T>>
|
||||||
|
constexpr byte operator>>(byte b, T shift) noexcept {
|
||||||
|
return byte(to_integer<unsigned>(b) >> shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief std::operator<<=, operator>>=
|
||||||
|
|
||||||
|
template <typename T, typename = detail::is_integral<T>>
|
||||||
|
constexpr byte &operator<<=(byte &b, T shift) noexcept {
|
||||||
|
return b = b << shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = detail::is_integral<T>>
|
||||||
|
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<unsigned>(l) | to_integer<unsigned>(r)); }
|
||||||
|
constexpr byte operator&(byte l, byte r) noexcept { return byte(to_integer<unsigned>(l) & to_integer<unsigned>(r)); }
|
||||||
|
constexpr byte operator^(byte l, byte r) noexcept { return byte(to_integer<unsigned>(l) ^ to_integer<unsigned>(r)); }
|
||||||
|
constexpr byte operator~(byte b) noexcept { return byte(~to_integer<unsigned>(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 <typename T, typename = detail::is_not_byte<T>>
|
||||||
|
byte *byte_cast(T *p) noexcept {
|
||||||
|
return reinterpret_cast<byte *>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = detail::is_not_byte<T>>
|
||||||
|
byte const *byte_cast(T const *p) noexcept {
|
||||||
|
return reinterpret_cast<byte const *>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Cast byte* to a pointer of another type.
|
||||||
|
|
||||||
|
template <typename T, typename = detail::is_not_byte<T>>
|
||||||
|
T *byte_cast(byte *p) noexcept {
|
||||||
|
if (reinterpret_cast<std::size_t>(p) % alignof(T) != 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return reinterpret_cast<T *>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U = typename std::add_const<T>::type,
|
||||||
|
typename = detail::is_not_byte<T>>
|
||||||
|
U *byte_cast(byte const *p) noexcept {
|
||||||
|
if (reinterpret_cast<std::size_t>(p) % alignof(T) != 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return reinterpret_cast<U *>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBIMP_NAMESPACE_END_
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<::LIBIMP_::byte> {
|
||||||
|
constexpr auto parse(format_parse_context& ctx) const {
|
||||||
|
return ctx.end();
|
||||||
|
}
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(::LIBIMP_::byte b, FormatContext &ctx) {
|
||||||
|
return format_to(ctx.out(), "{:#04x}", static_cast<std::uint8_t>(b));
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -16,12 +16,17 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#ifdef LIBIMP_CPP_20
|
|
||||||
#include <span>
|
#include "fmt/format.h"
|
||||||
#endif // LIBIMP_CPP_20
|
|
||||||
|
|
||||||
#include "libimp/def.h"
|
#include "libimp/def.h"
|
||||||
#include "libimp/detect_plat.h"
|
#include "libimp/detect_plat.h"
|
||||||
|
#include "libimp/byte.h"
|
||||||
|
|
||||||
|
#if defined(LIBIMP_CPP_20) && defined(__cpp_lib_span)
|
||||||
|
#include <span>
|
||||||
|
#define LIBIMP_CPP_LIB_SPAN_
|
||||||
|
#endif // __cpp_lib_span
|
||||||
|
|
||||||
LIBIMP_NAMESPACE_BEG_
|
LIBIMP_NAMESPACE_BEG_
|
||||||
namespace detail {
|
namespace detail {
|
||||||
@ -138,13 +143,13 @@ public:
|
|||||||
: ptr_ (s.data())
|
: ptr_ (s.data())
|
||||||
, extent_(s.size()) {}
|
, extent_(s.size()) {}
|
||||||
|
|
||||||
#if defined(LIBIMP_CPP_20) || defined(__cpp_lib_span)
|
#ifdef LIBIMP_CPP_LIB_SPAN_
|
||||||
template <typename U, std::size_t E,
|
template <typename U, std::size_t E,
|
||||||
typename = detail::is_array_convertible<U, T>>
|
typename = detail::is_array_convertible<U, T>>
|
||||||
constexpr span(std::span<U, E> const &s) noexcept
|
constexpr span(std::span<U, E> const &s) noexcept
|
||||||
: ptr_ (s.data())
|
: ptr_ (s.data())
|
||||||
, extent_(s.size()) {}
|
, extent_(s.size()) {}
|
||||||
#endif // LIBIMP_CPP_20
|
#endif // LIBIMP_CPP_LIB_SPAN_
|
||||||
|
|
||||||
constexpr size_type size() const noexcept {
|
constexpr size_type size() const noexcept {
|
||||||
return extent_;
|
return extent_;
|
||||||
@ -221,9 +226,9 @@ bool operator==(span<T> a, span<U> b) noexcept {
|
|||||||
/// @see https://en.cppreference.com/w/cpp/container/span/as_bytes
|
/// @see https://en.cppreference.com/w/cpp/container/span/as_bytes
|
||||||
|
|
||||||
template <typename T,
|
template <typename T,
|
||||||
typename Byte = typename std::conditional<std::is_const<T>::value, std::uint8_t const, std::uint8_t>::type>
|
typename Byte = typename std::conditional<std::is_const<T>::value, byte const, byte>::type>
|
||||||
auto as_bytes(span<T> s) noexcept -> span<Byte> {
|
auto as_bytes(span<T> s) noexcept -> span<Byte> {
|
||||||
return {reinterpret_cast<Byte *>(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.
|
/// @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<char const> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LIBIMP_NAMESPACE_END_
|
LIBIMP_NAMESPACE_END_
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct fmt::formatter<::LIBIMP_::span<T>> {
|
||||||
|
constexpr auto parse(format_parse_context& ctx) const {
|
||||||
|
return ctx.end();
|
||||||
|
}
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(::LIBIMP_::span<T> 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@ -13,8 +13,12 @@ include_directories(
|
|||||||
${LIBIPC_PROJECT_DIR}/src
|
${LIBIPC_PROJECT_DIR}/src
|
||||||
${LIBIPC_PROJECT_DIR}/test)
|
${LIBIPC_PROJECT_DIR}/test)
|
||||||
|
|
||||||
file(GLOB SRC_FILES ${LIBIPC_PROJECT_DIR}/test/*.cpp)
|
file(GLOB SRC_FILES
|
||||||
file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/*.h)
|
${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})
|
add_executable(${PROJECT_NAME} ${SRC_FILES} ${HEAD_FILES})
|
||||||
|
|
||||||
|
|||||||
65
test/imp/test_imp_byte.cpp
Normal file
65
test/imp/test_imp_byte.cpp
Normal file
@ -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<std::int32_t>(pb);
|
||||||
|
EXPECT_EQ(*pc, a);
|
||||||
|
|
||||||
|
// byte alignment check
|
||||||
|
EXPECT_EQ(imp::byte_cast<int>(pb + 1), nullptr);
|
||||||
|
}
|
||||||
@ -41,7 +41,7 @@ TEST(span, span) {
|
|||||||
test_proc(imp::make_span({6, 7, 8, 9}), sp.last(4));
|
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}), sp.subspan(3, 4));
|
||||||
test_proc(imp::make_span({3, 4, 5, 6, 7, 8, 9}), sp.subspan(3));
|
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";
|
std::string buf = "0123456789";
|
||||||
@ -54,3 +54,8 @@ TEST(span, span) {
|
|||||||
test_proc(imp::make_span("3456789", 7), sp.subspan(3));
|
test_proc(imp::make_span("3456789", 7), sp.subspan(3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(span, fmt) {
|
||||||
|
EXPECT_EQ(fmt::format("{}", imp::span<int>{}), "");
|
||||||
|
EXPECT_EQ(fmt::format("{}", imp::make_span({1, 3, 2, 4, 5, 6, 7})), "1 3 2 4 5 6 7");
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user