aboutsummaryrefslogtreecommitdiffstats
path: root/common/albyte.h
diff options
context:
space:
mode:
Diffstat (limited to 'common/albyte.h')
-rw-r--r--common/albyte.h99
1 files changed, 99 insertions, 0 deletions
diff --git a/common/albyte.h b/common/albyte.h
new file mode 100644
index 00000000..23864636
--- /dev/null
+++ b/common/albyte.h
@@ -0,0 +1,99 @@
+#ifndef AL_BYTE_H
+#define AL_BYTE_H
+
+#include <cstddef>
+#include <limits>
+#include <type_traits>
+
+namespace al {
+
+/* The "canonical" way to store raw byte data. Like C++17's std::byte, it's not
+ * treated as a character type and does not work with arithmatic ops. Only
+ * bitwise ops are allowed.
+ */
+enum class byte : unsigned char { };
+
+#define REQUIRES(...) typename std::enable_if<(__VA_ARGS__),bool>::type = true
+
+template<typename T, REQUIRES(std::is_integral<T>::value)>
+inline constexpr T to_integer(al::byte b) noexcept { return T(b); }
+
+
+template<typename T, REQUIRES(std::is_integral<T>::value)>
+inline constexpr al::byte operator<<(al::byte lhs, T rhs) noexcept
+{ return al::byte(to_integer<unsigned int>(lhs) << rhs); }
+
+template<typename T, REQUIRES(std::is_integral<T>::value)>
+inline constexpr al::byte operator>>(al::byte lhs, T rhs) noexcept
+{ return al::byte(to_integer<unsigned int>(lhs) >> rhs); }
+
+template<typename T, REQUIRES(std::is_integral<T>::value)>
+inline al::byte& operator<<=(al::byte &lhs, T rhs) noexcept
+{ lhs = lhs << rhs; return lhs; }
+
+template<typename T, REQUIRES(std::is_integral<T>::value)>
+inline al::byte& operator>>=(al::byte &lhs, T rhs) noexcept
+{ lhs = lhs >> rhs; return lhs; }
+
+#define AL_DECL_OP(op) \
+template<typename T, REQUIRES(std::is_integral<T>::value)> \
+inline constexpr al::byte operator op (al::byte lhs, T rhs) noexcept \
+{ return al::byte(to_integer<unsigned int>(lhs) op static_cast<unsigned int>(rhs)); } \
+template<typename T, REQUIRES(std::is_integral<T>::value)> \
+inline al::byte& operator op##= (al::byte &lhs, T rhs) noexcept \
+{ lhs = lhs op rhs; return lhs; } \
+inline constexpr al::byte operator op (al::byte lhs, al::byte rhs) noexcept \
+{ return al::byte(lhs op to_integer<unsigned int>(rhs)); } \
+inline al::byte& operator op##= (al::byte &lhs, al::byte rhs) noexcept \
+{ lhs = lhs op rhs; return lhs; }
+
+AL_DECL_OP(|)
+AL_DECL_OP(&)
+AL_DECL_OP(^)
+
+#undef AL_DECL_OP
+
+inline constexpr al::byte operator~(al::byte b) noexcept
+{ return al::byte(~to_integer<unsigned int>(b)); }
+
+
+template<size_t N>
+class bitfield {
+ static constexpr size_t bits_per_byte{std::numeric_limits<unsigned char>::digits};
+ static constexpr size_t NumElems{(N+bits_per_byte-1) / bits_per_byte};
+
+ byte vals[NumElems]{};
+
+public:
+ template<size_t b>
+ inline void set() noexcept
+ {
+ static_assert(b < N, "Bit index out of range");
+ vals[b/bits_per_byte] |= 1 << (b%bits_per_byte);
+ }
+ template<size_t b>
+ inline void unset() noexcept
+ {
+ static_assert(b < N, "Bit index out of range");
+ vals[b/bits_per_byte] &= ~(1 << (b%bits_per_byte));
+ }
+ template<size_t b>
+ inline bool get() const noexcept
+ {
+ static_assert(b < N, "Bit index out of range");
+ return (vals[b/bits_per_byte] & (1 << (b%bits_per_byte))) != byte{};
+ }
+
+ template<size_t b, size_t ...args, REQUIRES(sizeof...(args) > 0)>
+ void set() noexcept
+ {
+ set<b>();
+ set<args...>();
+ }
+};
+
+#undef REQUIRES
+
+} // namespace al
+
+#endif /* AL_BYTE_H */