raw: Add class for extracting bytes into structures
Change-Id: I30364d9fc5a5f02ee27e6b8fd17f40226b44dcc4
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/meson.build b/src/meson.build
index aadb68d..11080b0 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,5 +1,54 @@
stdplus_headers = include_directories('.')
+fmt_dep = dependency('fmt', required: false)
+fmt_ext = fmt_dep
+if not fmt_dep.found()
+ fmt_proj = import('cmake').subproject(
+ 'fmt',
+ cmake_options: [
+ '-DCMAKE_POSITION_INDEPENDENT_CODE=ON',
+ '-DMASTER_PROJECT=OFF'
+ ],
+ required: false)
+ assert(fmt_proj.found(), 'fmtlib is required')
+ fmt_dep = fmt_proj.dependency('fmt')
+endif
+
+# span-lite might not have a pkg-config. It is header only so just make
+# sure we can access the needed symbols from the header.
+span_dep = dependency('', required: false)
+span_ext = span_dep
+has_span = meson.get_compiler('cpp').has_header_symbol(
+ 'span',
+ 'std::dynamic_extent',
+ dependencies: span_dep,
+ required: false)
+if not has_span
+ span_dep = dependency('span-lite', required: false)
+ span_ext = span_dep
+ has_span = meson.get_compiler('cpp').has_header_symbol(
+ 'nonstd/span.hpp',
+ 'nonstd::dynamic_extent',
+ dependencies: span_dep,
+ required: false)
+ if not has_span
+ span_lite_proj = import('cmake').subproject(
+ 'span-lite',
+ cmake_options: [
+ ],
+ required: false)
+ if span_lite_proj.found()
+ span_dep = span_lite_proj.dependency('span-lite')
+ has_span = true
+ endif
+ endif
+endif
+
+stdplus_deps = [
+ fmt_dep,
+ span_dep,
+]
+
stdplus_lib = library(
'stdplus',
[
@@ -7,10 +56,12 @@
],
include_directories: stdplus_headers,
implicit_include_directories: false,
+ dependencies: stdplus_deps,
version: meson.project_version(),
install: true)
stdplus = declare_dependency(
+ dependencies: stdplus_deps,
include_directories: stdplus_headers,
link_with: stdplus_lib)
@@ -18,9 +69,11 @@
name: 'stdplus',
description: 'C++ helper utilities',
version: meson.project_version(),
- libraries: stdplus)
+ libraries: stdplus,
+ requires: [fmt_ext, span_ext])
install_headers(
+ 'stdplus/raw.hpp',
'stdplus/signal.hpp',
subdir: 'stdplus')
diff --git a/src/stdplus/raw.hpp b/src/stdplus/raw.hpp
new file mode 100644
index 0000000..1f3893f
--- /dev/null
+++ b/src/stdplus/raw.hpp
@@ -0,0 +1,137 @@
+#pragma once
+#include <fmt/format.h>
+#include <stdexcept>
+#include <stdplus/types.hpp>
+#include <string_view>
+#include <type_traits>
+
+namespace stdplus
+{
+namespace raw
+{
+
+namespace detail
+{
+
+/** @brief Determines if the container holds trivially copyable data
+ */
+template <typename Container>
+inline constexpr bool trivialContainer =
+ std::is_trivially_copyable_v<std::remove_pointer_t<decltype(
+ std::data(std::declval<std::add_lvalue_reference_t<Container>>()))>>;
+
+} // namespace detail
+
+/** @brief Copies data from a buffer into a copyable type
+ *
+ * @param[in] data - The data buffer being copied from
+ * @return The copyable type with data populated
+ */
+template <typename T, typename Container>
+T copyFrom(const Container& c)
+{
+ static_assert(std::is_trivially_copyable_v<T>);
+ static_assert(detail::trivialContainer<Container>);
+ T ret;
+ const size_t bytes = std::size(c) * sizeof(*std::data(c));
+ if (bytes < sizeof(ret))
+ {
+ throw std::runtime_error(
+ fmt::format("CopyFrom: {} < {}", bytes, sizeof(ret)));
+ }
+ std::memcpy(&ret, std::data(c), sizeof(ret));
+ return ret;
+}
+
+/** @brief Extracts data from a buffer into a copyable type
+ * Updates the data buffer to show that data was removed
+ *
+ * @param[in,out] data - The data buffer being extracted from
+ * @return The copyable type with data populated
+ */
+template <typename T, typename CharT>
+T extract(std::basic_string_view<CharT>& data)
+{
+ T ret = copyFrom<T>(data);
+ static_assert(sizeof(T) % sizeof(CharT) == 0);
+ data.remove_prefix(sizeof(T) / sizeof(CharT));
+ return ret;
+}
+#ifdef STDPLUS_SPAN_TYPE
+template <typename T, typename IntT,
+ typename = std::enable_if_t<std::is_integral_v<IntT>>>
+T extract(span<const IntT>& data)
+{
+ T ret = copyFrom<T>(data);
+ static_assert(sizeof(T) % sizeof(IntT) == 0);
+ data = data.subspan(sizeof(T) / sizeof(IntT));
+ return ret;
+}
+#endif
+
+/** @brief Returns the span referencing the data of the raw trivial type
+ * or of trivial types in a contiguous container.
+ *
+ * @param[in] t - The trivial raw data
+ * @return A view over the input with the given output integral type
+ */
+template <typename CharT, typename T,
+ typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+std::basic_string_view<CharT> asView(const T& t) noexcept
+{
+ static_assert(sizeof(T) % sizeof(CharT) == 0);
+ return {reinterpret_cast<const CharT*>(&t), sizeof(T) / sizeof(CharT)};
+}
+template <typename CharT, typename Container,
+ typename = std::enable_if_t<!std::is_trivially_copyable_v<Container>>,
+ typename = decltype(std::data(std::declval<Container>()))>
+std::basic_string_view<CharT> asView(const Container& c) noexcept
+{
+ static_assert(detail::trivialContainer<Container>);
+ static_assert(sizeof(*std::data(c)) % sizeof(CharT) == 0);
+ return {reinterpret_cast<const CharT*>(std::data(c)),
+ std::size(c) * sizeof(*std::data(c)) / sizeof(CharT)};
+}
+#ifdef STDPLUS_SPAN_TYPE
+template <typename IntT, typename T,
+ typename = std::enable_if_t<std::is_integral_v<IntT>>,
+ typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+span<IntT> asSpan(T& t) noexcept
+{
+ static_assert(sizeof(T) % sizeof(IntT) == 0);
+ return {reinterpret_cast<IntT*>(&t), sizeof(T) / sizeof(IntT)};
+}
+template <typename IntT, typename Container,
+ typename = std::enable_if_t<std::is_integral_v<IntT>>,
+ typename = std::enable_if_t<!std::is_trivially_copyable_v<Container>>,
+ typename = decltype(std::data(std::declval<Container>()))>
+span<IntT> asSpan(Container& c) noexcept
+{
+ static_assert(detail::trivialContainer<Container>);
+ static_assert(sizeof(*std::data(c)) % sizeof(IntT) == 0);
+ return {reinterpret_cast<IntT*>(std::data(c)),
+ std::size(c) * sizeof(*std::data(c)) / sizeof(IntT)};
+}
+template <typename IntT, typename T,
+ typename = std::enable_if_t<std::is_integral_v<IntT>>,
+ typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+span<const IntT> asSpan(const T& t) noexcept
+{
+ static_assert(sizeof(T) % sizeof(IntT) == 0);
+ return {reinterpret_cast<const IntT*>(&t), sizeof(T) / sizeof(IntT)};
+}
+template <typename IntT, typename Container,
+ typename = std::enable_if_t<std::is_integral_v<IntT>>,
+ typename = std::enable_if_t<!std::is_trivially_copyable_v<Container>>,
+ typename = decltype(std::data(std::declval<Container>()))>
+span<const IntT> asSpan(const Container& c) noexcept
+{
+ static_assert(detail::trivialContainer<Container>);
+ static_assert(sizeof(*std::data(c)) % sizeof(IntT) == 0);
+ return {reinterpret_cast<const IntT*>(std::data(c)),
+ std::size(c) * sizeof(*std::data(c)) / sizeof(IntT)};
+}
+#endif
+
+} // namespace raw
+} // namespace stdplus
diff --git a/src/stdplus/types.hpp b/src/stdplus/types.hpp
new file mode 100644
index 0000000..5ac3ed8
--- /dev/null
+++ b/src/stdplus/types.hpp
@@ -0,0 +1,18 @@
+#pragma once
+#if defined(__cpp_lib_span) && __cpp_lib_span >= 202002L
+#include <span>
+#define STDPLUS_SPAN_TYPE std::span
+#elif __has_include(<nonstd/span.hpp>)
+#include <nonstd/span.hpp>
+#define STDPLUS_SPAN_TYPE nonstd::span
+#endif
+
+namespace stdplus
+{
+
+#ifdef STDPLUS_SPAN_TYPE
+template <typename... Args>
+using span = STDPLUS_SPAN_TYPE<Args...>;
+#endif
+
+} // namespace stdplus