mirror of
https://github.com/MadeOfJelly/MushMachine.git
synced 2025-12-06 09:36:35 +01:00
Compare commits
2 Commits
8d24976a13
...
4ab1d99529
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ab1d99529 | |||
| f27300a8fa |
28
.vimspector.json
Normal file
28
.vimspector.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"configurations": {
|
||||
"run_ctest_native": {
|
||||
"default": true,
|
||||
"adapter": "CodeLLDB",
|
||||
"configuration": {
|
||||
"request": "launch",
|
||||
"stopOnEntry": true,
|
||||
"console": "integratedTerminal",
|
||||
"program": "ctest",
|
||||
"cwd": "${workspaceRoot}/build"
|
||||
}
|
||||
},
|
||||
"run_native": {
|
||||
"adapter": "CodeLLDB",
|
||||
"variables": {
|
||||
"Executable": "s6zer_test"
|
||||
},
|
||||
"configuration": {
|
||||
"request": "launch",
|
||||
"stopOnEntry": true,
|
||||
"console": "integratedTerminal",
|
||||
"program": "${workspaceRoot}/build/bin/${Executable}",
|
||||
"cwd": "${workspaceRoot}/build"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ project(framework)
|
||||
add_subdirectory(engine)
|
||||
add_subdirectory(logger)
|
||||
add_subdirectory(resource_manager)
|
||||
add_subdirectory(s6zer)
|
||||
add_subdirectory(common_components)
|
||||
add_subdirectory(std_utils)
|
||||
add_subdirectory(random)
|
||||
|
||||
@@ -4,6 +4,8 @@ project(common_components CXX)
|
||||
|
||||
add_library(common_components INTERFACE)
|
||||
|
||||
add_library(MM::common_components ALIAS common_components)
|
||||
|
||||
target_include_directories(common_components INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
|
||||
target_link_libraries(common_components INTERFACE
|
||||
@@ -13,8 +15,23 @@ target_link_libraries(common_components INTERFACE
|
||||
|
||||
##########################
|
||||
|
||||
add_library(common_components_serialize_s6zer INTERFACE)
|
||||
|
||||
add_library(MM::common_components_serialize_s6zer ALIAS common_components_serialize_s6zer)
|
||||
|
||||
target_include_directories(common_components_serialize_s6zer INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
|
||||
target_link_libraries(common_components_serialize_s6zer INTERFACE
|
||||
common_components
|
||||
s6zer
|
||||
)
|
||||
|
||||
##########################
|
||||
|
||||
add_library(common_components_serialize_json INTERFACE)
|
||||
|
||||
add_library(MM::common_components_serialize_json ALIAS common_components_serialize_json)
|
||||
|
||||
target_include_directories(common_components_serialize_json INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
|
||||
target_link_libraries(common_components_serialize_json INTERFACE
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <mm/s6zer/serialize.hpp>
|
||||
|
||||
#include <mm/components/color.hpp>
|
||||
|
||||
namespace MM::Components {
|
||||
|
||||
MM_DEFINE_SERIALIZE(Color,
|
||||
MM_S6ZER_BAIL(stream.serializeFloat(data.color.r))
|
||||
MM_S6ZER_BAIL(stream.serializeFloat(data.color.g))
|
||||
MM_S6ZER_BAIL(stream.serializeFloat(data.color.b))
|
||||
MM_S6ZER_BAIL(stream.serializeFloat(data.color.a))
|
||||
)
|
||||
|
||||
} // MM::Components
|
||||
|
||||
@@ -7,10 +7,25 @@ target_include_directories(common_component_json_serialization_test PRIVATE ".")
|
||||
|
||||
target_link_libraries(common_component_json_serialization_test
|
||||
common_components_serialize_json
|
||||
engine
|
||||
|
||||
gtest_main
|
||||
)
|
||||
|
||||
add_test(NAME common_component_json_serialization_test COMMAND common_component_json_serialization_test)
|
||||
|
||||
########################################
|
||||
|
||||
add_executable(common_component_s6zer_serialization_test
|
||||
component_s6zer_serialization_test.cpp
|
||||
)
|
||||
|
||||
target_include_directories(common_component_s6zer_serialization_test PRIVATE ".")
|
||||
|
||||
target_link_libraries(common_component_s6zer_serialization_test
|
||||
common_components_serialize_s6zer
|
||||
|
||||
gtest_main
|
||||
)
|
||||
|
||||
add_test(NAME common_component_s6zer_serialization_test COMMAND common_component_s6zer_serialization_test)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "mm/components/position2d.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <mm/components/serialize/json_name.hpp>
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <mm/s6zer/serialize.hpp>
|
||||
|
||||
//#include <mm/components/serialize/json_name.hpp>
|
||||
#include <mm/components/serialize/s6zer_color.hpp>
|
||||
|
||||
//#include <mm/components/serialize/json_position2d.hpp>
|
||||
//#include <mm/components/serialize/json_position2d_zoffset.hpp>
|
||||
//#include <mm/components/serialize/json_position3d.hpp>
|
||||
//#include <mm/components/serialize/json_rotation2d.hpp>
|
||||
//#include <mm/components/serialize/json_scale2d.hpp>
|
||||
//#include <mm/components/serialize/json_transform4x4.hpp>
|
||||
//#include <mm/components/serialize/json_velocity2d_position.hpp>
|
||||
//#include <mm/components/serialize/json_velocity2d_position_intent.hpp>
|
||||
//#include <mm/components/serialize/json_velocity2d_rotation.hpp>
|
||||
//#include <mm/components/serialize/json_view_dir2d.hpp>
|
||||
//#include <mm/components/serialize/json_view_dir3d.hpp>
|
||||
|
||||
#define TEST_JSON_SERL_EXPAND(x) x
|
||||
#define TEST_JSON_SERL_IN_OUT(TYPE, JSON_STR, TEST_CORPUS) \
|
||||
TEST(common_components_json_serialization, in_out_##TYPE) { \
|
||||
MM::Components::TYPE comp; \
|
||||
{ /* in */ \
|
||||
auto j = nlohmann::json::parse(JSON_STR); \
|
||||
comp = j; \
|
||||
TEST_CORPUS \
|
||||
} \
|
||||
{ /* out */ \
|
||||
nlohmann::json j; \
|
||||
j = comp; \
|
||||
ASSERT_EQ(JSON_STR, j.dump()); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
//TEST(common_components_json_serialization, in_out_name) {
|
||||
//MM::Components::Name comp;
|
||||
|
||||
//const char* json_test_file = "{\"str\":\"test_name\"}";
|
||||
|
||||
//{ // in
|
||||
//auto j = nlohmann::json::parse(json_test_file);
|
||||
|
||||
//EXPECT_NO_THROW({
|
||||
//comp = j;
|
||||
//});
|
||||
|
||||
//ASSERT_EQ(comp.str, "test_name");
|
||||
//}
|
||||
|
||||
//{ // out
|
||||
//nlohmann::json j;
|
||||
|
||||
//EXPECT_NO_THROW({
|
||||
//j = comp;
|
||||
//});
|
||||
|
||||
//ASSERT_EQ(json_test_file, j.dump());
|
||||
//}
|
||||
//}
|
||||
|
||||
//TEST(common_components_json_serialization, in_out_name_fail) {
|
||||
//MM::Components::Name name_comp;
|
||||
|
||||
//// intentional malformed json string
|
||||
//const char* json_test_file = "{\"strasdf\":\"test_name\"}";
|
||||
|
||||
//{ // in
|
||||
//auto j = nlohmann::json::parse(json_test_file);
|
||||
|
||||
//ASSERT_ANY_THROW({
|
||||
//name_comp = j;
|
||||
//});
|
||||
//}
|
||||
//}
|
||||
|
||||
// ##############################################################
|
||||
|
||||
//TEST_S6ZER_SERL_IN_OUT(
|
||||
//Color,
|
||||
//R"({"color":{"w":1337.0,"x":0.0,"y":1.0,"z":3.0}})",
|
||||
//TEST_JSON_SERL_EXPAND({
|
||||
//glm::vec4 comp_val(0.f, 1.f, 3.f, 1337.f);
|
||||
//ASSERT_EQ(comp.color.x, comp_val.x);
|
||||
//ASSERT_EQ(comp.color.y, comp_val.y);
|
||||
//ASSERT_EQ(comp.color.z, comp_val.z);
|
||||
//ASSERT_EQ(comp.color.w, comp_val.w);
|
||||
//})
|
||||
//)
|
||||
|
||||
TEST(common_components_s6zer_serialization, out_in_color) {
|
||||
const MM::Components::Color comp_out{
|
||||
{1337.f, 0.f, 1.f, 3.f}
|
||||
};
|
||||
|
||||
std::array<uint32_t, 128> buffer;
|
||||
size_t buffer_size = buffer.size()*sizeof(uint32_t);
|
||||
|
||||
{ // to bits
|
||||
MM::s6zer::StreamWriter writer{buffer.data(), buffer_size};
|
||||
ASSERT_TRUE(MM::Components::mm_serialize(writer, comp_out));
|
||||
|
||||
ASSERT_TRUE(writer.flush());
|
||||
buffer_size = writer.bytesWritten();
|
||||
}
|
||||
|
||||
MM::Components::Color comp_in;
|
||||
|
||||
{ // from bits
|
||||
MM::s6zer::StreamReader reader{buffer.data(), buffer_size};
|
||||
|
||||
ASSERT_TRUE(MM::Components::mm_serialize(reader, comp_in));
|
||||
|
||||
ASSERT_EQ(reader.bytesRead(), buffer_size);
|
||||
}
|
||||
|
||||
ASSERT_EQ(comp_out.color.x, comp_in.color.x);
|
||||
ASSERT_EQ(comp_out.color.y, comp_in.color.y);
|
||||
ASSERT_EQ(comp_out.color.z, comp_in.color.z);
|
||||
ASSERT_EQ(comp_out.color.w, comp_in.color.w);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
// ##############################################################
|
||||
|
||||
TEST_JSON_SERL_IN_OUT(
|
||||
Position2D,
|
||||
R"({"pos":{"x":42.0,"y":6.0}})",
|
||||
TEST_JSON_SERL_EXPAND(
|
||||
ASSERT_EQ(comp.pos.x, 42.f);
|
||||
ASSERT_EQ(comp.pos.y, 6.f);
|
||||
)
|
||||
)
|
||||
|
||||
// ##############################################################
|
||||
|
||||
TEST_JSON_SERL_IN_OUT(
|
||||
Position2D_ZOffset,
|
||||
R"({"z_offset":3.0})",
|
||||
TEST_JSON_SERL_EXPAND(
|
||||
ASSERT_EQ(comp.z_offset, 3.f);
|
||||
)
|
||||
)
|
||||
|
||||
// ##############################################################
|
||||
|
||||
TEST_JSON_SERL_IN_OUT(
|
||||
Position3D,
|
||||
R"({"pos":{"x":42.0,"y":6.0,"z":44.0}})",
|
||||
TEST_JSON_SERL_EXPAND(
|
||||
ASSERT_EQ(comp.pos.x, 42.f);
|
||||
ASSERT_EQ(comp.pos.y, 6.f);
|
||||
ASSERT_EQ(comp.pos.z, 44.f);
|
||||
)
|
||||
)
|
||||
|
||||
// ##############################################################
|
||||
|
||||
TEST_JSON_SERL_IN_OUT(
|
||||
Rotation2D,
|
||||
R"({"rot":42.0})",
|
||||
TEST_JSON_SERL_EXPAND(
|
||||
ASSERT_EQ(comp.rot, 42.f);
|
||||
)
|
||||
)
|
||||
|
||||
// ##############################################################
|
||||
|
||||
TEST_JSON_SERL_IN_OUT(
|
||||
Scale2D,
|
||||
R"({"scale":{"x":42.0,"y":6.0}})",
|
||||
TEST_JSON_SERL_EXPAND(
|
||||
ASSERT_EQ(comp.scale.x, 42.f);
|
||||
ASSERT_EQ(comp.scale.y, 6.f);
|
||||
)
|
||||
)
|
||||
|
||||
// ##############################################################
|
||||
|
||||
TEST_JSON_SERL_IN_OUT(
|
||||
Transform4x4,
|
||||
R"({"trans":[{"w":0.0,"x":1.0,"y":0.0,"z":0.0},{"w":0.0,"x":0.0,"y":1.0,"z":0.0},{"w":0.0,"x":0.0,"y":0.0,"z":1.0},{"w":1.0,"x":0.0,"y":0.0,"z":0.0}]})",
|
||||
TEST_JSON_SERL_EXPAND(
|
||||
ASSERT_EQ(comp.trans, glm::mat4x4{1.f});
|
||||
)
|
||||
)
|
||||
|
||||
// ##############################################################
|
||||
|
||||
TEST_JSON_SERL_IN_OUT(
|
||||
Velocity2DPosition,
|
||||
R"({"pos_vel":{"x":42.0,"y":6.0}})",
|
||||
TEST_JSON_SERL_EXPAND(
|
||||
ASSERT_EQ(comp.pos_vel.x, 42.f);
|
||||
ASSERT_EQ(comp.pos_vel.y, 6.f);
|
||||
)
|
||||
)
|
||||
|
||||
// ##############################################################
|
||||
|
||||
TEST_JSON_SERL_IN_OUT(
|
||||
Velocity2DPositionIntent,
|
||||
R"({"intent":{"x":42.0,"y":6.0}})",
|
||||
TEST_JSON_SERL_EXPAND(
|
||||
ASSERT_EQ(comp.intent.x, 42.f);
|
||||
ASSERT_EQ(comp.intent.y, 6.f);
|
||||
)
|
||||
)
|
||||
|
||||
// ##############################################################
|
||||
|
||||
TEST_JSON_SERL_IN_OUT(
|
||||
Velocity2DRotation,
|
||||
R"({"rot_vel":42.0})",
|
||||
TEST_JSON_SERL_EXPAND(
|
||||
ASSERT_EQ(comp.rot_vel, 42.f);
|
||||
)
|
||||
)
|
||||
|
||||
// ##############################################################
|
||||
|
||||
TEST(common_components_json_serialization, in_out_view_dir2d) {
|
||||
MM::Components::ViewDir2D comp;
|
||||
|
||||
const char* json_test_file = R"({"dir":6.0})";
|
||||
|
||||
{ // in
|
||||
auto j = nlohmann::json::parse(json_test_file);
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
comp = j;
|
||||
});
|
||||
|
||||
ASSERT_EQ(comp.dir, 6.f);
|
||||
}
|
||||
|
||||
{ // out
|
||||
nlohmann::json j;
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
j = comp;
|
||||
});
|
||||
|
||||
ASSERT_EQ(json_test_file, j.dump());
|
||||
}
|
||||
}
|
||||
|
||||
// ##############################################################
|
||||
|
||||
TEST(common_components_json_serialization, in_out_view_dir3d) {
|
||||
MM::Components::ViewDir3D comp;
|
||||
|
||||
const char* json_test_file = R"({"pitch":6.0,"roll":99.0,"yaw":42.0})";
|
||||
|
||||
{ // in
|
||||
auto j = nlohmann::json::parse(json_test_file);
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
comp = j;
|
||||
});
|
||||
|
||||
ASSERT_EQ(comp.yaw, 42.f);
|
||||
ASSERT_EQ(comp.pitch, 6.f);
|
||||
ASSERT_EQ(comp.roll, 99.f);
|
||||
}
|
||||
|
||||
{ // out
|
||||
nlohmann::json j;
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
j = comp;
|
||||
});
|
||||
|
||||
ASSERT_EQ(json_test_file, j.dump());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
23
framework/s6zer/CMakeLists.txt
Normal file
23
framework/s6zer/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
||||
|
||||
project(s6zer CXX)
|
||||
|
||||
add_library(s6zer INTERFACE
|
||||
#./src/s6zer/stream.hpp
|
||||
#./src/s6zer/serialize.hpp
|
||||
)
|
||||
|
||||
add_library(MM::s6zer ALIAS s6zer)
|
||||
|
||||
target_include_directories(s6zer INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
|
||||
target_compile_features(s6zer INTERFACE cxx_std_17)
|
||||
|
||||
#target_link_libraries(s6zer
|
||||
#INTERFACE
|
||||
#)
|
||||
|
||||
if (BUILD_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
33
framework/s6zer/src/mm/s6zer/serialize.hpp
Normal file
33
framework/s6zer/src/mm/s6zer/serialize.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "./stream.hpp"
|
||||
|
||||
namespace MM::s6zer {
|
||||
|
||||
// serialize macros
|
||||
|
||||
// TODO: make use of ADL, like nlohmann::json does.
|
||||
|
||||
/*
|
||||
defines mm_serialize functions for you.
|
||||
a "stream" object is in scope (StreamWriter/StreamReader),
|
||||
as well as the object of Type called "data".
|
||||
eg:
|
||||
MM_DEFINE_SERIALIZE(Test1,
|
||||
MM_S6ZER_BAIL(stream.serializeBits(data.seq, 16))
|
||||
MM_S6ZER_BAIL(stream.serializeBits(data.data1, 8))
|
||||
)
|
||||
*/
|
||||
// TODO: refine, so we dont have to do MM_S6ZER_BAIL() everytime
|
||||
#define MM_DEFINE_SERIALIZE(Type, ...) \
|
||||
inline bool mm_serialize(MM::s6zer::StreamWriter& stream, const Type& data) { \
|
||||
__VA_ARGS__ \
|
||||
return true; \
|
||||
} \
|
||||
inline bool mm_serialize(MM::s6zer::StreamReader& stream, Type& data) { \
|
||||
__VA_ARGS__ \
|
||||
return true; \
|
||||
}
|
||||
|
||||
} // MM::s6zer
|
||||
|
||||
331
framework/s6zer/src/mm/s6zer/stream.hpp
Normal file
331
framework/s6zer/src/mm/s6zer/stream.hpp
Normal file
@@ -0,0 +1,331 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint8_t, etc
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
// TODO: make asserts redefinable
|
||||
|
||||
namespace MM::s6zer {
|
||||
|
||||
// this is heavily inspired by Glenn Fiedler's (Gaffer On Games) serializers
|
||||
// https://www.gafferongames.com/post/reading_and_writing_packets/
|
||||
// https://www.gafferongames.com/post/serialization_strategies/
|
||||
|
||||
// internal helpers
|
||||
namespace detail {
|
||||
// TODO: ugly, replace when c++20
|
||||
[[nodiscard]] constexpr size_t first_bit_set8(const uint8_t number) {
|
||||
return
|
||||
(number & 0b10000000) ? 8 :
|
||||
(number & 0b01000000) ? 7 :
|
||||
(number & 0b00100000) ? 6 :
|
||||
(number & 0b00010000) ? 5 :
|
||||
(number & 0b00001000) ? 4 :
|
||||
(number & 0b00000100) ? 3 :
|
||||
(number & 0b00000010) ? 2 :
|
||||
(number & 0b00000001) ? 1 :
|
||||
0
|
||||
;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr size_t first_bit_set32(const uint32_t number) {
|
||||
return
|
||||
(number & 0xff000000) ? first_bit_set8((number >> 24) & 0xff) + 24 :
|
||||
(number & 0x00ff0000) ? first_bit_set8((number >> 16) & 0xff) + 16 :
|
||||
(number & 0x0000ff00) ? first_bit_set8((number >> 8) & 0xff) + 8 :
|
||||
(number & 0x000000ff) ? first_bit_set8(number & 0xff) :
|
||||
0
|
||||
;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr uint32_t byte_swap(const uint32_t value) noexcept {
|
||||
return
|
||||
((value & 0xff000000) >> 24) |
|
||||
((value & 0x00ff0000) >> 8) |
|
||||
((value & 0x0000ff00) << 8) |
|
||||
((value & 0x000000ff) << 24)
|
||||
;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr uint16_t byte_swap(const uint16_t value) noexcept {
|
||||
return
|
||||
((value & 0xff00) >> 8) |
|
||||
((value & 0x00ff) << 8)
|
||||
;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr uint8_t byte_swap(const uint8_t value) noexcept {
|
||||
// noop
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] constexpr const T& max(const T& a, const T& b) noexcept {
|
||||
return (a < b) ? b : a;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
// TODO: maybe 64bit?
|
||||
// TODO: is this detail?
|
||||
[[nodiscard]] constexpr size_t bits_required(const uint32_t numbers) {
|
||||
return detail::first_bit_set32(numbers);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr uint32_t serialize_byte_order(const uint32_t value) {
|
||||
// TODO: only works on little endian for now
|
||||
if constexpr (true) { // native is little endian
|
||||
return value;
|
||||
} else { // native is big endian
|
||||
return detail::byte_swap(value);
|
||||
}
|
||||
}
|
||||
|
||||
// helper for fake exceptions
|
||||
#ifndef MM_S6ZER_BAIL
|
||||
#define MM_S6ZER_BAIL(...) { \
|
||||
if (! __VA_ARGS__) { \
|
||||
return false; \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
struct StreamWriter {
|
||||
StreamWriter(void) = delete;
|
||||
StreamWriter(uint32_t* data, size_t size) : _data(data), _data_size(size) {
|
||||
assert(size != 0);
|
||||
assert(size % sizeof(uint32_t) == 0);
|
||||
assert(data != nullptr);
|
||||
}
|
||||
|
||||
// do i still need them?
|
||||
[[nodiscard]] static constexpr bool isWriting(void) noexcept { return true; }
|
||||
[[nodiscard]] static constexpr bool isReading(void) noexcept { return false; }
|
||||
|
||||
[[nodiscard]] bool flush(void) noexcept {
|
||||
if (_scratch_bits != 0) {
|
||||
// check if space in buffer
|
||||
if (_data_size < (_word_index + 1) * sizeof(uint32_t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_data[_word_index] = serialize_byte_order(static_cast<uint32_t>(_scratch & 0xffffffff));
|
||||
_scratch >>= 32; // new bits are allways unset, so we can just allways 32
|
||||
// we dont like negative
|
||||
_scratch_bits = detail::max(static_cast<int32_t>(_scratch_bits) - 32, 0);
|
||||
_word_index++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] bool serializeBits(const T value, const size_t number_of_bits = sizeof(T)*8) noexcept {
|
||||
static_assert(std::is_integral_v<T>, "type needs to be an integer");
|
||||
static_assert(std::is_unsigned_v<T>, "type needs to be unsigned");
|
||||
static_assert(sizeof(T) <= 4, "not yet defined for > 32bit");
|
||||
assert(number_of_bits <= sizeof(T)*8);
|
||||
assert(number_of_bits > 0);
|
||||
|
||||
// do scratching
|
||||
_scratch |= static_cast<uint64_t>(value) << _scratch_bits;
|
||||
_scratch_bits += number_of_bits;
|
||||
_bits_written += number_of_bits;
|
||||
|
||||
if (_scratch_bits >= 32) {
|
||||
return flush();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool serializeBool(const bool value) noexcept {
|
||||
return serializeBits(static_cast<uint32_t>(value), 1);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] bool serializeInt(const T value, const T min, const T max) noexcept {
|
||||
static_assert(std::is_integral_v<T>, "type needs to be an integer");
|
||||
static_assert(sizeof(T) <= 4, "not yet defined for > 32bit");
|
||||
assert(max >= min);
|
||||
assert(value >= min);
|
||||
assert(value <= max);
|
||||
|
||||
const size_t bits = bits_required(max - min);
|
||||
|
||||
return serializeBits(static_cast<uint32_t>(value - min), bits);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool serializeFloat(const float value) noexcept {
|
||||
// TODO: dont use loop
|
||||
for (size_t i = 0; i < sizeof(float); i++) {
|
||||
MM_S6ZER_BAIL(serializeBits(reinterpret_cast<const uint8_t*>(&value)[i], 8));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool serializeDouble(const double value) noexcept {
|
||||
// TODO: dont use loop
|
||||
for (size_t i = 0; i < sizeof(double); i++) {
|
||||
MM_S6ZER_BAIL(serializeBits(reinterpret_cast<const uint8_t*>(&value)[i], 8));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool serializeFloatCompressed(const float value, const float min, const float max, const float resolution) noexcept {
|
||||
assert(max >= min);
|
||||
assert(value >= min);
|
||||
assert(value <= max);
|
||||
|
||||
// TODO: handle those rounding errors
|
||||
|
||||
const float numbers = (max - min) / resolution;
|
||||
const size_t bits = bits_required(static_cast<uint32_t>(numbers));
|
||||
|
||||
const uint32_t tmp_value = static_cast<uint32_t>((value - min) / resolution);
|
||||
|
||||
return serializeBits(tmp_value, bits);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t bytesWritten(void) noexcept {
|
||||
// TODO: is this assert valid?
|
||||
assert(_scratch_bits == 0);
|
||||
|
||||
//return _bits_written/8 + ((_bits_written % 8) ? 1 : 0);
|
||||
return (_bits_written+7) / 8;
|
||||
}
|
||||
|
||||
uint32_t* _data {nullptr};
|
||||
size_t _data_size {0};
|
||||
|
||||
uint64_t _scratch {0};
|
||||
size_t _scratch_bits {0};
|
||||
size_t _word_index {0};
|
||||
size_t _bits_written {0}; // includes bits still in scratch
|
||||
};
|
||||
|
||||
struct StreamReader {
|
||||
StreamReader(void) = delete;
|
||||
// !! StreamReader assumes the data buffer has whole uint32_t,
|
||||
// so at the end, even though data_size might be less then 4 bytes,
|
||||
// here is actually a full, empty uint32
|
||||
// !! enable AddressSanitzier during development and testing
|
||||
StreamReader(const uint32_t* data, size_t size) : _data(data), _data_size(size) {
|
||||
assert(size != 0);
|
||||
//assert(size % sizeof(uint32_t) == 0);
|
||||
assert(data != nullptr);
|
||||
}
|
||||
|
||||
// do i still need them?
|
||||
[[nodiscard]] static constexpr bool isWriting(void) noexcept { return false; }
|
||||
[[nodiscard]] static constexpr bool isReading(void) noexcept { return true; }
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] bool serializeBits(T& value, const size_t number_of_bits = sizeof(T)*8) noexcept {
|
||||
static_assert(std::is_integral_v<T>, "type needs to be an integer");
|
||||
static_assert(std::is_unsigned_v<T>, "type needs to be unsigned");
|
||||
static_assert(sizeof(T) <= 4, "not yet defined for > 32bit");
|
||||
assert(number_of_bits <= sizeof(T)*8);
|
||||
assert(number_of_bits > 0);
|
||||
|
||||
if (_scratch_bits < number_of_bits) {
|
||||
if (_bits_read + number_of_bits > _data_size*8) {
|
||||
// would read past end
|
||||
return false;
|
||||
}
|
||||
|
||||
_scratch |= static_cast<uint64_t>(serialize_byte_order(_data[_word_index])) << _scratch_bits;
|
||||
_word_index++;
|
||||
_scratch_bits += 32;
|
||||
}
|
||||
|
||||
value = _scratch & ((uint64_t(1) << number_of_bits) - 1);
|
||||
|
||||
_scratch >>= number_of_bits;
|
||||
_scratch_bits -= number_of_bits;
|
||||
_bits_read += number_of_bits;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool serializeBool(bool& value) noexcept {
|
||||
uint32_t tmp_value {0};
|
||||
MM_S6ZER_BAIL(serializeBits(tmp_value, 1));
|
||||
|
||||
// :)
|
||||
value = tmp_value != 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] bool serializeInt(T& value, const T min, const T max) noexcept {
|
||||
static_assert(std::is_integral_v<T>, "type needs to be an integer");
|
||||
static_assert(sizeof(T) <= 4, "not yet defined for > 32bit");
|
||||
assert(max >= min);
|
||||
|
||||
const size_t bits = bits_required(max - min);
|
||||
|
||||
uint32_t tmp_val {0};
|
||||
MM_S6ZER_BAIL(serializeBits(tmp_val, bits));
|
||||
|
||||
value = static_cast<T>(tmp_val) + min;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool serializeFloat(float& value) noexcept {
|
||||
// TODO: dont use loop
|
||||
for (size_t i = 0; i < sizeof(float); i++) {
|
||||
MM_S6ZER_BAIL(serializeBits(reinterpret_cast<uint8_t*>(&value)[i], 8));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool serializeDouble(double& value) noexcept {
|
||||
// TODO: dont use loop
|
||||
for (size_t i = 0; i < sizeof(double); i++) {
|
||||
MM_S6ZER_BAIL(serializeBits(reinterpret_cast<uint8_t*>(&value)[i], 8));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool serializeFloatCompressed(float& value, const float min, const float max, const float resolution) noexcept {
|
||||
assert(max >= min);
|
||||
|
||||
// TODO: use rounding, rn it snaps (floor)
|
||||
|
||||
const float numbers = (max - min) / resolution;
|
||||
const size_t bits = bits_required(static_cast<uint32_t>(numbers));
|
||||
|
||||
uint32_t tmp_value {0};
|
||||
MM_S6ZER_BAIL(serializeBits(tmp_value, bits));
|
||||
|
||||
value = static_cast<float>(tmp_value) * resolution + min;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t bytesRead(void) noexcept {
|
||||
return (_bits_read+7) / 8;
|
||||
}
|
||||
|
||||
const uint32_t* _data {nullptr};
|
||||
size_t _data_size {0};
|
||||
|
||||
uint64_t _scratch {0};
|
||||
size_t _scratch_bits {0};
|
||||
size_t _word_index {0};
|
||||
size_t _bits_read {0};
|
||||
};
|
||||
|
||||
} // MM::s6zer
|
||||
|
||||
18
framework/s6zer/test/CMakeLists.txt
Normal file
18
framework/s6zer/test/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
add_executable(s6zer_test
|
||||
test.cpp
|
||||
)
|
||||
|
||||
target_include_directories(s6zer_test PRIVATE ".")
|
||||
|
||||
target_compile_features(s6zer_test PRIVATE cxx_std_17)
|
||||
|
||||
target_link_libraries(s6zer_test
|
||||
gtest_main
|
||||
|
||||
s6zer
|
||||
|
||||
random
|
||||
)
|
||||
|
||||
add_test(NAME s6zer_test COMMAND s6zer_test)
|
||||
|
||||
553
framework/s6zer/test/test.cpp
Normal file
553
framework/s6zer/test/test.cpp
Normal file
@@ -0,0 +1,553 @@
|
||||
#include <mm/s6zer/serialize.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <ostream>
|
||||
|
||||
#include <mm/random/srng.hpp>
|
||||
#include <mm/scalar_range2.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace MM {
|
||||
template<typename Char, typename T>
|
||||
std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& out, const MM::ScalarRange2<T>& range) {
|
||||
return out << "{ min: " << static_cast<int64_t>(range.min()) << ", max: " << static_cast<int64_t>(range.max()) << " }";
|
||||
}
|
||||
} // MM
|
||||
|
||||
TEST(s6zer, bits_required_static) {
|
||||
static_assert(MM::s6zer::bits_required(0)== 0);
|
||||
static_assert(MM::s6zer::bits_required(1)== 1);
|
||||
static_assert(MM::s6zer::bits_required(2)== 2);
|
||||
static_assert(MM::s6zer::bits_required(3)== 2);
|
||||
static_assert(MM::s6zer::bits_required(4)== 3);
|
||||
static_assert(MM::s6zer::bits_required(32)== 6);
|
||||
static_assert(MM::s6zer::bits_required(0xffffffff)== 32);
|
||||
static_assert(MM::s6zer::bits_required(0xffffff00)== 32);
|
||||
static_assert(MM::s6zer::bits_required(0xf0000a00)== 32);
|
||||
static_assert(MM::s6zer::bits_required(0x0f000000)== 28);
|
||||
static_assert(MM::s6zer::bits_required(0x0000f000)== 16);
|
||||
}
|
||||
|
||||
TEST(s6zer, byte_swap) {
|
||||
static_assert(MM::s6zer::detail::byte_swap(static_cast<uint8_t>(0x00)) == 0x00);
|
||||
static_assert(MM::s6zer::detail::byte_swap(static_cast<uint8_t>(0xff)) == 0xff);
|
||||
static_assert(MM::s6zer::detail::byte_swap(static_cast<uint8_t>(0x10)) == 0x10);
|
||||
static_assert(MM::s6zer::detail::byte_swap(static_cast<uint8_t>(0xfe)) == 0xfe);
|
||||
|
||||
static_assert(MM::s6zer::detail::byte_swap(static_cast<uint16_t>(0x0000)) == 0x0000);
|
||||
static_assert(MM::s6zer::detail::byte_swap(static_cast<uint16_t>(0xffff)) == 0xffff);
|
||||
static_assert(MM::s6zer::detail::byte_swap(static_cast<uint16_t>(0x00fe)) == 0xfe00);
|
||||
static_assert(MM::s6zer::detail::byte_swap(static_cast<uint16_t>(0xfefe)) == 0xfefe);
|
||||
|
||||
static_assert(MM::s6zer::detail::byte_swap(static_cast<uint32_t>(0x00000000)) == 0x00000000);
|
||||
static_assert(MM::s6zer::detail::byte_swap(static_cast<uint32_t>(0xffffffff)) == 0xffffffff);
|
||||
static_assert(MM::s6zer::detail::byte_swap(static_cast<uint32_t>(0xf0f00000)) == 0x0000f0f0);
|
||||
static_assert(MM::s6zer::detail::byte_swap(static_cast<uint32_t>(0xfe0000ef)) == 0xef0000fe);
|
||||
}
|
||||
|
||||
TEST(s6zer, stream_normalcase1) {
|
||||
const uint32_t num1_orig {0b111};
|
||||
const uint32_t num1_orig_bits {3};
|
||||
const uint32_t num2_orig {0b1111111111};
|
||||
const uint32_t num2_orig_bits {10};
|
||||
const uint32_t num3_orig {0b111111111111111111111111};
|
||||
const uint32_t num3_orig_bits {24};
|
||||
|
||||
std::array<uint32_t, 2> buffer;
|
||||
size_t buffer_size = buffer.size()*sizeof(uint32_t);
|
||||
{
|
||||
MM::s6zer::StreamWriter writer{buffer.data(), buffer_size};
|
||||
|
||||
bool r = false;
|
||||
ASSERT_EQ(writer._scratch, 0x0);
|
||||
ASSERT_EQ(writer._scratch_bits, 0);
|
||||
ASSERT_EQ(writer._word_index, 0);
|
||||
ASSERT_EQ(writer._bits_written, 0);
|
||||
ASSERT_EQ(writer.bytesWritten(), 0);
|
||||
|
||||
r = writer.serializeBits(num1_orig, num1_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(writer._scratch, 0b0000000000000000000000000000000000000000000000000000000000000'111);
|
||||
ASSERT_EQ(writer._scratch_bits, 3);
|
||||
ASSERT_EQ(writer._word_index, 0);
|
||||
ASSERT_EQ(writer._bits_written, num1_orig_bits);
|
||||
|
||||
r = writer.serializeBits(num2_orig, num2_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(writer._scratch, 0b000000000000000000000000000000000000000000000000000'111'1111111111);
|
||||
ASSERT_EQ(writer._scratch_bits, 3+10);
|
||||
ASSERT_EQ(writer._word_index, 0);
|
||||
ASSERT_EQ(writer._bits_written, num1_orig_bits+num2_orig_bits);
|
||||
|
||||
r = writer.serializeBits(num3_orig, num3_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(writer._scratch, 0b00000000000000000000000000000000000000000000000000000000000'11111);
|
||||
ASSERT_EQ(writer._scratch_bits, (3+10+24)-32);
|
||||
ASSERT_EQ(writer._word_index, 1);
|
||||
ASSERT_EQ(writer._bits_written, num1_orig_bits+num2_orig_bits+num3_orig_bits);
|
||||
|
||||
r = writer.flush();
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(writer._scratch, 0);
|
||||
ASSERT_EQ(writer._scratch_bits, 0);
|
||||
ASSERT_EQ(writer._word_index, 2);
|
||||
ASSERT_EQ(writer._bits_written, num1_orig_bits+num2_orig_bits+num3_orig_bits); // flush does not change bits written
|
||||
ASSERT_EQ(writer.bytesWritten(), 5); // 4.625 , so ceil
|
||||
|
||||
buffer_size = writer.bytesWritten();
|
||||
}
|
||||
|
||||
std::cout << "buffer_size: " << buffer_size << "\n";
|
||||
|
||||
ASSERT_EQ(buffer[0], 0xffffffff);
|
||||
ASSERT_EQ(buffer[1], 0b000000000000000000000000000'11111);
|
||||
|
||||
{
|
||||
MM::s6zer::StreamReader reader{buffer.data(), buffer_size};
|
||||
|
||||
bool r = false;
|
||||
ASSERT_EQ(reader._scratch, 0x0);
|
||||
ASSERT_EQ(reader._scratch_bits, 0);
|
||||
ASSERT_EQ(reader._word_index, 0);
|
||||
|
||||
uint32_t num1 {0};
|
||||
r = reader.serializeBits(num1, num1_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(num1, num1_orig);
|
||||
ASSERT_EQ(reader._scratch, 0b00000000'00000000'00000000'00000000'000'11111111111111111111111111111);
|
||||
ASSERT_EQ(reader._scratch_bits, 29);
|
||||
ASSERT_EQ(reader._word_index, 1); // index refers to next dword
|
||||
|
||||
uint32_t num2 {0};
|
||||
r = reader.serializeBits(num2, num2_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(num2, num2_orig);
|
||||
ASSERT_EQ(reader._scratch, 0b00000000'00000000'00000000'00000000'000'00000'00000'1111111111111111111);
|
||||
ASSERT_EQ(reader._scratch_bits, 19);
|
||||
ASSERT_EQ(reader._word_index, 1); // <=32, so should not yet have read next dword
|
||||
|
||||
uint32_t num3 {0};
|
||||
r = reader.serializeBits(num3, num3_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(num3, num3_orig);
|
||||
ASSERT_EQ(reader._scratch, 0x0); // no data left
|
||||
ASSERT_EQ(reader._scratch_bits, 27);
|
||||
ASSERT_EQ(reader._word_index, 2);
|
||||
|
||||
// error case
|
||||
uint32_t num4 {0};
|
||||
r = reader.serializeBits(num4, 32);
|
||||
ASSERT_FALSE(r);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(s6zer, stream_normalcase1_1) {
|
||||
const uint32_t num1_orig {0b101};
|
||||
const uint32_t num1_orig_bits {3};
|
||||
const uint32_t num2_orig {0b1010101010};
|
||||
const uint32_t num2_orig_bits {10};
|
||||
const uint32_t num3_orig {0b101010101010101010101010};
|
||||
const uint32_t num3_orig_bits {24};
|
||||
|
||||
std::array<uint32_t, 2> buffer;
|
||||
size_t buffer_size = buffer.size()*sizeof(uint32_t);
|
||||
{
|
||||
MM::s6zer::StreamWriter writer{buffer.data(), buffer_size};
|
||||
|
||||
bool r = false;
|
||||
ASSERT_EQ(writer._scratch, 0x0);
|
||||
ASSERT_EQ(writer._scratch_bits, 0);
|
||||
ASSERT_EQ(writer._word_index, 0);
|
||||
ASSERT_EQ(writer._bits_written, 0);
|
||||
ASSERT_EQ(writer.bytesWritten(), 0);
|
||||
|
||||
r = writer.serializeBits(num1_orig, num1_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(writer._scratch, 0b0000000000000000000000000000000000000000000000000000000000000'101);
|
||||
ASSERT_EQ(writer._scratch_bits, 3);
|
||||
ASSERT_EQ(writer._word_index, 0);
|
||||
ASSERT_EQ(writer._bits_written, num1_orig_bits);
|
||||
|
||||
r = writer.serializeBits(num2_orig, num2_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(writer._scratch, 0b000000000000000000000000000000000000000000000000000'1010101010'101);
|
||||
ASSERT_EQ(writer._scratch_bits, 3+10);
|
||||
ASSERT_EQ(writer._word_index, 0);
|
||||
ASSERT_EQ(writer._bits_written, num1_orig_bits+num2_orig_bits);
|
||||
|
||||
r = writer.serializeBits(num3_orig, num3_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(writer._scratch, 0b00000000000000000000000000000000000000000000000000000000000'10101); // the high bits of the 24
|
||||
ASSERT_EQ(writer._scratch_bits, (3+10+24)-32);
|
||||
ASSERT_EQ(writer._word_index, 1);
|
||||
ASSERT_EQ(writer._bits_written, num1_orig_bits+num2_orig_bits+num3_orig_bits);
|
||||
|
||||
r = writer.flush();
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(writer._scratch, 0);
|
||||
ASSERT_EQ(writer._scratch_bits, 0);
|
||||
ASSERT_EQ(writer._word_index, 2);
|
||||
ASSERT_EQ(writer._bits_written, num1_orig_bits+num2_orig_bits+num3_orig_bits); // flush does not change bits written
|
||||
ASSERT_EQ(writer.bytesWritten(), 5); // 4.625 , so ceil
|
||||
|
||||
buffer_size = writer.bytesWritten();
|
||||
}
|
||||
|
||||
std::cout << "buffer_size: " << buffer_size << "\n";
|
||||
|
||||
ASSERT_EQ(buffer[0], 0b0101010101010101010'1010101010'101);
|
||||
ASSERT_EQ(buffer[1], 0b000000000000000000000000000'10101);
|
||||
|
||||
{
|
||||
MM::s6zer::StreamReader reader{buffer.data(), buffer_size};
|
||||
|
||||
bool r = false;
|
||||
ASSERT_EQ(reader._scratch, 0x0);
|
||||
ASSERT_EQ(reader._scratch_bits, 0);
|
||||
ASSERT_EQ(reader._word_index, 0);
|
||||
|
||||
uint32_t num1 {0};
|
||||
r = reader.serializeBits(num1, num1_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(num1, num1_orig);
|
||||
ASSERT_EQ(reader._scratch, 0b00000000'00000000'00000000'00000000'000'0101010101010101010'1010101010);
|
||||
ASSERT_EQ(reader._scratch_bits, 29);
|
||||
ASSERT_EQ(reader._word_index, 1); // index refers to next dword
|
||||
|
||||
uint32_t num2 {0};
|
||||
r = reader.serializeBits(num2, num2_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(num2, num2_orig);
|
||||
ASSERT_EQ(reader._scratch, 0b00000000'00000000'00000000'00000000'000'00000'00000'0101010101010101010);
|
||||
ASSERT_EQ(reader._scratch_bits, 19);
|
||||
ASSERT_EQ(reader._word_index, 1); // <=32, so should not yet have read next dword
|
||||
|
||||
uint32_t num3 {0};
|
||||
r = reader.serializeBits(num3, num3_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(num3, num3_orig);
|
||||
ASSERT_EQ(reader._scratch, 0x0); // no data left
|
||||
ASSERT_EQ(reader._scratch_bits, 27);
|
||||
ASSERT_EQ(reader._word_index, 2);
|
||||
|
||||
// error case
|
||||
uint32_t num4 {0};
|
||||
r = reader.serializeBits(num4, 32);
|
||||
ASSERT_FALSE(r);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(s6zer, stream_normalcase2) {
|
||||
// we now take each number as its maximum, synthetic for testing
|
||||
const uint32_t num1_orig {17};
|
||||
const uint32_t num1_orig_bits {MM::s6zer::bits_required(num1_orig)};
|
||||
const uint32_t num2_orig {1};
|
||||
const uint32_t num2_orig_bits {MM::s6zer::bits_required(num2_orig)};
|
||||
const uint32_t num3_orig {1298989};
|
||||
const uint32_t num3_orig_bits {MM::s6zer::bits_required(num3_orig)};
|
||||
|
||||
std::array<uint32_t, 2> buffer;
|
||||
size_t buffer_size = buffer.size()*sizeof(uint32_t);
|
||||
{
|
||||
MM::s6zer::StreamWriter writer{buffer.data(), buffer_size};
|
||||
|
||||
// fewer asserts
|
||||
bool r = false;
|
||||
ASSERT_EQ(writer._scratch, 0x0);
|
||||
ASSERT_EQ(writer._scratch_bits, 0);
|
||||
ASSERT_EQ(writer._word_index, 0);
|
||||
ASSERT_EQ(writer._bits_written, 0);
|
||||
ASSERT_EQ(writer.bytesWritten(), 0);
|
||||
|
||||
r = writer.serializeBits(num1_orig, num1_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(writer._bits_written, num1_orig_bits);
|
||||
|
||||
r = writer.serializeBits(num2_orig, num2_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(writer._bits_written, num1_orig_bits+num2_orig_bits);
|
||||
|
||||
r = writer.serializeBits(num3_orig, num3_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(writer._bits_written, num1_orig_bits+num2_orig_bits+num3_orig_bits);
|
||||
|
||||
r = writer.flush();
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(writer._scratch, 0);
|
||||
ASSERT_EQ(writer._scratch_bits, 0);
|
||||
ASSERT_EQ(writer._bits_written, num1_orig_bits+num2_orig_bits+num3_orig_bits); // flush does not change bits written
|
||||
|
||||
buffer_size = writer.bytesWritten();
|
||||
}
|
||||
|
||||
std::cout << "buffer_size: " << buffer_size << "\n";
|
||||
|
||||
{
|
||||
MM::s6zer::StreamReader reader{buffer.data(), buffer_size};
|
||||
|
||||
bool r = false;
|
||||
ASSERT_EQ(reader._scratch, 0x0);
|
||||
ASSERT_EQ(reader._scratch_bits, 0);
|
||||
ASSERT_EQ(reader._word_index, 0);
|
||||
ASSERT_EQ(reader._bits_read, 0);
|
||||
|
||||
uint32_t num1 {0};
|
||||
r = reader.serializeBits(num1, num1_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(num1, num1_orig);
|
||||
|
||||
uint32_t num2 {0};
|
||||
r = reader.serializeBits(num2, num2_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(num2, num2_orig);
|
||||
|
||||
uint32_t num3 {0};
|
||||
r = reader.serializeBits(num3, num3_orig_bits);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(num3, num3_orig);
|
||||
|
||||
// error case
|
||||
uint32_t num4 {0};
|
||||
r = reader.serializeBits(num4, 32);
|
||||
ASSERT_FALSE(r);
|
||||
}
|
||||
}
|
||||
|
||||
// emscripten cant do this
|
||||
#ifndef __EMSCRIPTEN__
|
||||
bool serialize_int_death_fn(void) {
|
||||
std::array<uint32_t, 32> dummy_buffer;
|
||||
MM::s6zer::StreamReader reader{dummy_buffer.data(), dummy_buffer.size()*sizeof(uint32_t)};
|
||||
int32_t value{0};
|
||||
|
||||
MM_S6ZER_BAIL(reader.serializeInt(value, 20, -20)); // wrong order
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(s6zer, serialize_int_death) {
|
||||
ASSERT_DEATH({
|
||||
[[maybe_unused]] bool ret = serialize_int_death_fn();
|
||||
}, "failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(s6zer, reader_bits_bug1) {
|
||||
const std::array<uint32_t, 2> buffer {
|
||||
0x27'5c'19'a1,
|
||||
0x00'00'3a'c3
|
||||
};
|
||||
const size_t buffer_size {6};
|
||||
|
||||
MM::s6zer::StreamReader reader{buffer.data(), buffer_size};
|
||||
|
||||
uint8_t value_0 {0};
|
||||
ASSERT_TRUE(reader.serializeBits(value_0));
|
||||
ASSERT_EQ(value_0, 0xa1);
|
||||
|
||||
ASSERT_EQ(reader._scratch_bits, 24);
|
||||
ASSERT_EQ(reader._scratch, 0x0000000000'27'5c'19);
|
||||
|
||||
uint32_t value_1 {0};
|
||||
ASSERT_TRUE(reader.serializeBits(value_1));
|
||||
ASSERT_EQ(value_1, 0xc3'27'5c'19);
|
||||
|
||||
ASSERT_EQ(reader._scratch_bits, 24);
|
||||
ASSERT_EQ(reader._scratch, 0x00000000000000'3a);
|
||||
|
||||
uint8_t value_2 {0};
|
||||
ASSERT_TRUE(reader.serializeBits(value_2));
|
||||
ASSERT_EQ(value_2, 0x3a);
|
||||
|
||||
ASSERT_EQ(reader._scratch, 0x0000000000000000);
|
||||
}
|
||||
|
||||
struct TestStruct {
|
||||
// integers bits
|
||||
uint8_t u8 {0};
|
||||
uint16_t u16 {0};
|
||||
uint32_t u32 {0};
|
||||
//uint64_t u64 {0};
|
||||
|
||||
bool b1 {false};
|
||||
|
||||
// integers ranges
|
||||
uint8_t r_u8 {0};
|
||||
constexpr static MM::ScalarRange2<uint8_t> r_u8_r{10, 60};
|
||||
int8_t r_i8 {0};
|
||||
constexpr static MM::ScalarRange2<int8_t> r_i8_r{-10, 5};
|
||||
uint16_t r_u16 {0};
|
||||
constexpr static MM::ScalarRange2<uint16_t> r_u16_r{1, 1026};
|
||||
int16_t r_i16 {0};
|
||||
constexpr static MM::ScalarRange2<int16_t> r_i16_r{-1, 1026};
|
||||
uint32_t r_u32 {0};
|
||||
constexpr static MM::ScalarRange2<uint32_t> r_u32_r{0, 12341234};
|
||||
int32_t r_i32 {0};
|
||||
constexpr static MM::ScalarRange2<int32_t> r_i32_r{-12341234, 10};
|
||||
|
||||
// floats
|
||||
float f32 {0.f};
|
||||
double f64 {0.};
|
||||
|
||||
// float compressed [0; 1] range
|
||||
constexpr static float c0_f32_resolution = 0.001;
|
||||
constexpr static MM::ScalarRange2<float> c0_f32_r{0.f, 1.f};
|
||||
float c0_f32_0 {0.f};
|
||||
float c0_f32_1 {0.f};
|
||||
float c0_f32_2 {0.f};
|
||||
float c0_f32_3 {0.f};
|
||||
|
||||
// float compressed [-1; 1] range
|
||||
constexpr static float c1_f32_resolution = 0.05;
|
||||
constexpr static MM::ScalarRange2<float> c1_f32_r{-1.f, 1.f};
|
||||
float c1_f32_0 {0.f};
|
||||
float c1_f32_1 {0.f};
|
||||
float c1_f32_2 {0.f};
|
||||
float c1_f32_3 {0.f};
|
||||
|
||||
// float compressed [-1000; 1000] range
|
||||
constexpr static float c2_f32_resolution = 0.01;
|
||||
constexpr static MM::ScalarRange2<float> c2_f32_r{-1000.f, 1000.f};
|
||||
float c2_f32_0 {0.f};
|
||||
float c2_f32_1 {0.f};
|
||||
float c2_f32_2 {0.f};
|
||||
float c2_f32_3 {0.f};
|
||||
};
|
||||
|
||||
MM_DEFINE_SERIALIZE(TestStruct,
|
||||
MM_S6ZER_BAIL(stream.serializeBits(data.u8))
|
||||
MM_S6ZER_BAIL(stream.serializeBits(data.u16))
|
||||
MM_S6ZER_BAIL(stream.serializeBits(data.u32))
|
||||
|
||||
MM_S6ZER_BAIL(stream.serializeBool(data.b1))
|
||||
|
||||
MM_S6ZER_BAIL(stream.serializeInt(data.r_u8, data.r_u8_r.min(), data.r_u8_r.max()))
|
||||
MM_S6ZER_BAIL(stream.serializeInt(data.r_i8, data.r_i8_r.min(), data.r_i8_r.max()))
|
||||
MM_S6ZER_BAIL(stream.serializeInt(data.r_u16, data.r_u16_r.min(), data.r_u16_r.max()))
|
||||
MM_S6ZER_BAIL(stream.serializeInt(data.r_i16, data.r_i16_r.min(), data.r_i16_r.max()))
|
||||
MM_S6ZER_BAIL(stream.serializeInt(data.r_u32, data.r_u32_r.min(), data.r_u32_r.max()))
|
||||
MM_S6ZER_BAIL(stream.serializeInt(data.r_i32, data.r_i32_r.min(), data.r_i32_r.max()))
|
||||
|
||||
MM_S6ZER_BAIL(stream.serializeFloat(data.f32))
|
||||
MM_S6ZER_BAIL(stream.serializeDouble(data.f64))
|
||||
|
||||
MM_S6ZER_BAIL(stream.serializeFloatCompressed(data.c0_f32_0, data.c0_f32_r.min(), data.c0_f32_r.max(), data.c0_f32_resolution))
|
||||
MM_S6ZER_BAIL(stream.serializeFloatCompressed(data.c0_f32_1, data.c0_f32_r.min(), data.c0_f32_r.max(), data.c0_f32_resolution))
|
||||
MM_S6ZER_BAIL(stream.serializeFloatCompressed(data.c0_f32_2, data.c0_f32_r.min(), data.c0_f32_r.max(), data.c0_f32_resolution))
|
||||
MM_S6ZER_BAIL(stream.serializeFloatCompressed(data.c0_f32_3, data.c0_f32_r.min(), data.c0_f32_r.max(), data.c0_f32_resolution))
|
||||
|
||||
MM_S6ZER_BAIL(stream.serializeFloatCompressed(data.c1_f32_0, data.c1_f32_r.min(), data.c1_f32_r.max(), data.c1_f32_resolution))
|
||||
MM_S6ZER_BAIL(stream.serializeFloatCompressed(data.c1_f32_1, data.c1_f32_r.min(), data.c1_f32_r.max(), data.c1_f32_resolution))
|
||||
MM_S6ZER_BAIL(stream.serializeFloatCompressed(data.c1_f32_2, data.c1_f32_r.min(), data.c1_f32_r.max(), data.c1_f32_resolution))
|
||||
MM_S6ZER_BAIL(stream.serializeFloatCompressed(data.c1_f32_3, data.c1_f32_r.min(), data.c1_f32_r.max(), data.c1_f32_resolution))
|
||||
|
||||
MM_S6ZER_BAIL(stream.serializeFloatCompressed(data.c2_f32_0, data.c2_f32_r.min(), data.c2_f32_r.max(), data.c2_f32_resolution))
|
||||
MM_S6ZER_BAIL(stream.serializeFloatCompressed(data.c2_f32_1, data.c2_f32_r.min(), data.c2_f32_r.max(), data.c2_f32_resolution))
|
||||
MM_S6ZER_BAIL(stream.serializeFloatCompressed(data.c2_f32_2, data.c2_f32_r.min(), data.c2_f32_r.max(), data.c2_f32_resolution))
|
||||
MM_S6ZER_BAIL(stream.serializeFloatCompressed(data.c2_f32_3, data.c2_f32_r.min(), data.c2_f32_r.max(), data.c2_f32_resolution))
|
||||
)
|
||||
|
||||
TEST(s6zer, stream_normalfull) {
|
||||
std::array<uint32_t, 128> buffer;
|
||||
size_t buffer_size = buffer.size()*sizeof(uint32_t);
|
||||
|
||||
MM::Random::SRNG rng{1337, 0};
|
||||
|
||||
const TestStruct data_in{
|
||||
static_cast<uint8_t>(rng()),
|
||||
static_cast<uint16_t>(rng()),
|
||||
static_cast<uint32_t>(rng()),
|
||||
|
||||
rng.roll(0.5f),
|
||||
|
||||
rng.range(TestStruct::r_u8_r),
|
||||
rng.range(TestStruct::r_i8_r),
|
||||
rng.range(TestStruct::r_u16_r),
|
||||
rng.range(TestStruct::r_i16_r),
|
||||
rng.range(TestStruct::r_u32_r),
|
||||
rng.range(TestStruct::r_i32_r),
|
||||
|
||||
rng.negOneToOne() * 10000000.f,
|
||||
rng.negOneToOne() * 10000000.,
|
||||
|
||||
rng.range(TestStruct::c0_f32_r),
|
||||
rng.range(TestStruct::c0_f32_r),
|
||||
rng.range(TestStruct::c0_f32_r),
|
||||
rng.range(TestStruct::c0_f32_r),
|
||||
|
||||
rng.range(TestStruct::c1_f32_r),
|
||||
rng.range(TestStruct::c1_f32_r),
|
||||
rng.range(TestStruct::c1_f32_r),
|
||||
rng.range(TestStruct::c1_f32_r),
|
||||
|
||||
rng.range(TestStruct::c2_f32_r),
|
||||
rng.range(TestStruct::c2_f32_r),
|
||||
rng.range(TestStruct::c2_f32_r),
|
||||
rng.range(TestStruct::c2_f32_r),
|
||||
};
|
||||
|
||||
std::cout << "struct size: " << sizeof(TestStruct) << "\n";
|
||||
|
||||
{
|
||||
MM::s6zer::StreamWriter writer{buffer.data(), buffer_size};
|
||||
|
||||
ASSERT_TRUE(mm_serialize(writer, data_in));
|
||||
|
||||
ASSERT_TRUE(writer.flush());
|
||||
buffer_size = writer.bytesWritten();
|
||||
}
|
||||
|
||||
std::cout << "buffer_size: " << buffer_size << "\n";
|
||||
|
||||
TestStruct data_out{}; // all zero
|
||||
|
||||
{
|
||||
MM::s6zer::StreamReader reader{buffer.data(), buffer_size};
|
||||
|
||||
ASSERT_TRUE(mm_serialize(reader, data_out));
|
||||
|
||||
ASSERT_EQ(reader._scratch, 0x0000000000000000);
|
||||
}
|
||||
|
||||
std::cout << "buffer: ";
|
||||
for (size_t i = 0; i < buffer_size; i++) {
|
||||
std::cout << std::hex << static_cast<uint32_t>(reinterpret_cast<uint8_t*>(buffer.data())[i]) << "'";
|
||||
}
|
||||
std::cout << "\n";
|
||||
|
||||
//std::cout << "data_out: \n" << data_out;
|
||||
|
||||
ASSERT_EQ(data_in.u8, data_out.u8);
|
||||
ASSERT_EQ(data_in.u16, data_out.u16);
|
||||
ASSERT_EQ(data_in.u32, data_out.u32);
|
||||
|
||||
ASSERT_EQ(data_in.b1, data_out.b1);
|
||||
|
||||
ASSERT_EQ(data_in.r_u8, data_out.r_u8) << "value range: " << TestStruct::r_u8_r;
|
||||
ASSERT_EQ(data_in.r_i8, data_out.r_i8) << "value range: " << TestStruct::r_i8_r;
|
||||
ASSERT_EQ(data_in.r_u16, data_out.r_u16) << "value range: " << TestStruct::r_u16_r;
|
||||
ASSERT_EQ(data_in.r_i16, data_out.r_i16) << "value range: " << TestStruct::r_i16_r;
|
||||
ASSERT_EQ(data_in.r_u32, data_out.r_u32) << "value range: " << TestStruct::r_u32_r;
|
||||
ASSERT_EQ(data_in.r_i32, data_out.r_i32) << "value range: " << TestStruct::r_i32_r;
|
||||
|
||||
// bit perfect copies, can have wrong results for special values <.<
|
||||
ASSERT_EQ(data_in.f32, data_out.f32);
|
||||
ASSERT_EQ(data_in.f64, data_out.f64);
|
||||
|
||||
ASSERT_NEAR(data_in.c0_f32_0, data_out.c0_f32_0, TestStruct::c0_f32_resolution);
|
||||
ASSERT_NEAR(data_in.c0_f32_1, data_out.c0_f32_1, TestStruct::c0_f32_resolution);
|
||||
ASSERT_NEAR(data_in.c0_f32_2, data_out.c0_f32_2, TestStruct::c0_f32_resolution);
|
||||
ASSERT_NEAR(data_in.c0_f32_3, data_out.c0_f32_3, TestStruct::c0_f32_resolution);
|
||||
|
||||
ASSERT_NEAR(data_in.c1_f32_0, data_out.c1_f32_0, TestStruct::c1_f32_resolution);
|
||||
ASSERT_NEAR(data_in.c1_f32_1, data_out.c1_f32_1, TestStruct::c1_f32_resolution);
|
||||
ASSERT_NEAR(data_in.c1_f32_2, data_out.c1_f32_2, TestStruct::c1_f32_resolution);
|
||||
ASSERT_NEAR(data_in.c1_f32_3, data_out.c1_f32_3, TestStruct::c1_f32_resolution);
|
||||
|
||||
ASSERT_NEAR(data_in.c2_f32_0, data_out.c2_f32_0, TestStruct::c2_f32_resolution);
|
||||
ASSERT_NEAR(data_in.c2_f32_1, data_out.c2_f32_1, TestStruct::c2_f32_resolution);
|
||||
ASSERT_NEAR(data_in.c2_f32_2, data_out.c2_f32_2, TestStruct::c2_f32_resolution);
|
||||
ASSERT_NEAR(data_in.c2_f32_3, data_out.c2_f32_3, TestStruct::c2_f32_resolution);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user