Skip to content

Commit

Permalink
Merge pull request #10 from KredeGC/dev
Browse files Browse the repository at this point in the history
More compiletime bounds
  • Loading branch information
KredeGC committed Jun 22, 2023
2 parents 6eeae1a + 588ab41 commit 5780a19
Show file tree
Hide file tree
Showing 10 changed files with 418 additions and 39 deletions.
5 changes: 2 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,14 @@ jobs:
- name: Run premake
run: premake5 ${{ matrix.machine.action }} --toolset=${{ matrix.machine.toolset }} --dialect=${{ matrix.dialect }}
- name: Initialize CodeQL
continue-on-error: true
uses: github/codeql-action/init@v2
with:
languages: cpp
- name: Build
run: premake5 build --config=${{ matrix.config }} --architecture=${{ matrix.architecture }}
- name: Perform CodeQL Analysis
continue-on-error: true
uses: github/codeql-action/analyze@v2
- name: Print contents of bin
if: matrix.machine.os == 'ubuntu-latest'
run: ls bin
- name: Run test
run: premake5 test --config=${{ matrix.config }} --architecture=${{ matrix.architecture }}
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Based on [Glenn Fiedler's articles](https://gafferongames.com/post/reading_and_w
* [Compile-time bounded integers - bounded_int\<T, T Min, T Max\>](#compile-time-bounded-integers---bounded_intt-t-min-t-max)
* [C-style strings - const char*](#c-style-strings---const-char)
* [Modern strings - std::basic_string\<T\>](#modern-strings---stdbasic_stringt)
* [Double-precision float - double](#double-precision-float---double)
* [Single-precision float - float](#single-precision-float---float)
* [Half-precision float - half_precision](#half-precision-float---half_precision)
* [Bounded float - bounded_range](#bounded-float---bounded_range)
Expand Down Expand Up @@ -263,8 +264,8 @@ The examples below follow the same structure: First writing to a buffer and then
Writing the first 5 bits of an int to the buffer, then reading it back:
```cpp
// Create a writer, referencing the buffer and its size
byte_buffer<4> buffer; // Buffer must be a multiple of 4 bytes / 32 bits
bit_writer writer(buffer);
alignas(uint32_t) uint8_t buffer[4]; // Buffer must be a multiple of 4 bytes / 32 bits and 4-byte-aligned
bit_writer writer(buffer, 4);
// Write the value
uint32_t value = 27; // We can choose any value below 2^5. Otherwise we need more than 5 bits
Expand All @@ -284,7 +285,7 @@ reader.serialize_bits(out_value, 5); // out_value should now have a value of 27
Writing a signed int to the buffer, within a range:
```cpp
// Create a writer, referencing the buffer and its size
byte_buffer<4> buffer;
byte_buffer<4> buffer; // byte_bufer is just a wrapper for a 4-byte aligned buffer
bit_writer writer(buffer);

// Write the value
Expand Down
16 changes: 5 additions & 11 deletions include/bitstream/stream/bit_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -315,11 +315,9 @@ namespace bitstream
* @param ...args The arguments to pass to the serialize function
* @return Whether successful or not
*/
template<typename Trait, typename... Args>
[[nodiscard]] bool serialize(Args&&... args) noexcept(utility::is_noexcept_serialize_v<Trait, bit_reader, Args...>)
template<typename Trait, typename... Args, typename = utility::has_serialize_t<Trait, bit_reader, Args...>>
[[nodiscard]] bool serialize(Args&&... args) noexcept(utility::is_serialize_noexcept_v<Trait, bit_reader, Args...>)
{
static_assert(utility::has_serialize_v<Trait, bit_reader, Args...>, "Could not find serializable trait for the given type. Remember to specialize serializable_traits<> with the given type");

return serialize_traits<Trait>::serialize(*this, std::forward<Args>(args)...);
}

Expand All @@ -333,14 +331,10 @@ namespace bitstream
* @param ...args The rest of the arguments to pass to the serialize function
* @return Whether successful or not
*/
template<typename Trait, typename... Args>
[[nodiscard]] bool serialize(Trait&& arg, Args&&... args) noexcept(utility::is_noexcept_serialize_v<utility::deduce_trait_t<Trait, bit_reader, Args...>, bit_reader, Trait, Args...>)
template<typename... Args, typename Trait, typename = utility::has_deduce_serialize_t<Trait, bit_reader, Args...>>
[[nodiscard]] bool serialize(Trait&& arg, Args&&... args) noexcept(utility::is_deduce_serialize_noexcept_v<Trait, bit_reader, Args...>)
{
using deduce_t = utility::deduce_trait_t<Trait, bit_reader, Args...>;

static_assert(utility::has_serialize_v<deduce_t, bit_reader, Trait, Args...>, "Could not deduce serializable trait for the given arguments. Remember to specialize serializable_traits<> with the given type");

return serialize_traits<deduce_t>::serialize(*this, std::forward<Trait>(arg), std::forward<Args>(args)...);
return serialize_traits<utility::deduce_trait_t<Trait, bit_reader, Args...>>::serialize(*this, std::forward<Trait>(arg), std::forward<Args>(args)...);
}

private:
Expand Down
16 changes: 5 additions & 11 deletions include/bitstream/stream/bit_writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -372,11 +372,9 @@ namespace bitstream
* @param ...args The arguments to pass to the serialize function
* @return Whether successful or not
*/
template<typename Trait, typename... Args>
[[nodiscard]] bool serialize(Args&&... args) noexcept(utility::is_noexcept_serialize_v<Trait, bit_writer, Args...>)
template<typename Trait, typename... Args, typename = utility::has_serialize_t<Trait, bit_writer, Args...>>
[[nodiscard]] bool serialize(Args&&... args) noexcept(utility::is_serialize_noexcept_v<Trait, bit_writer, Args...>)
{
static_assert(utility::has_serialize_v<Trait, bit_writer, Args...>, "Could not find serializable trait for the given type. Remember to specialize serializable_traits<> with the given type");

return serialize_traits<Trait>::serialize(*this, std::forward<Args>(args)...);
}

Expand All @@ -390,14 +388,10 @@ namespace bitstream
* @param ...args The rest of the arguments to pass to the serialize function
* @return Whether successful or not
*/
template<typename Trait, typename... Args>
[[nodiscard]] bool serialize(Trait&& arg, Args&&... args) noexcept(utility::is_noexcept_serialize_v<utility::deduce_trait_t<Trait, bit_writer, Args...>, bit_writer, Trait, Args...>)
template<typename... Args, typename Trait, typename = utility::has_deduce_serialize_t<Trait, bit_writer, Args...>>
[[nodiscard]] bool serialize(Trait&& arg, Args&&... args) noexcept(utility::is_deduce_serialize_noexcept_v<Trait, bit_writer, Args...>)
{
using deduce_t = utility::deduce_trait_t<Trait, bit_writer, Args...>;

static_assert(utility::has_serialize_v<deduce_t, bit_writer, Trait, Args...>, "Could not deduce serializable trait for the given arguments. Remember to specialize serializable_traits<> with the given type");

return serialize_traits<deduce_t>::serialize(*this, std::forward<Trait>(arg), std::forward<Args>(args)...);
return serialize_traits<utility::deduce_trait_t<Trait, bit_writer, Args...>>::serialize(*this, std::forward<Trait>(arg), std::forward<Args>(args)...);
}

private:
Expand Down
2 changes: 2 additions & 0 deletions include/bitstream/stream/byte_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ namespace bitstream
static_assert(Size % 4 == 0, "Buffer size must be a multiple of 4");

alignas(uint32_t) uint8_t Bytes[Size];

uint8_t& operator[](size_t i) noexcept { return Bytes[i]; }
};
}
37 changes: 36 additions & 1 deletion include/bitstream/traits/enum_trait.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@
namespace bitstream
{
/**
* @brief A trait used to serialize an enum type
* @brief Wrapper type for compiletime known integer bounds
* @tparam T
*/
template<typename T, std::underlying_type_t<T> = (std::numeric_limits<T>::min)(), std::underlying_type_t<T> = (std::numeric_limits<T>::max)()>
struct bounded_enum;

/**
* @brief A trait used to serialize an enum type with runtime bounds
*/
template<typename T>
struct serialize_traits<T, typename std::enable_if_t<std::is_enum_v<T>>>
Expand All @@ -35,4 +42,32 @@ namespace bitstream
return true;
}
};

/**
* @brief A trait used to serialize an enum type with compiletime bounds
*/
template<typename T, std::underlying_type_t<T> Min, std::underlying_type_t<T> Max>
struct serialize_traits<bounded_enum<T, Min, Max>, typename std::enable_if_t<std::is_enum_v<T>>>
{
using value_type = std::underlying_type_t<T>;
using bound_type = bounded_int<value_type, Min, Max>;

static bool serialize(bit_writer& writer, T value) noexcept
{
value_type unsigned_value = static_cast<value_type>(value);

return writer.serialize<bound_type>(unsigned_value);
}

static bool serialize(bit_reader& reader, T& value) noexcept
{
value_type unsigned_value;

BS_ASSERT(reader.serialize<bound_type>(unsigned_value));

value = static_cast<T>(unsigned_value);

return true;
}
};
}
Loading

0 comments on commit 5780a19

Please sign in to comment.