Skip to content

Commit

Permalink
Introduce RawSerializer for component serialization
Browse files Browse the repository at this point in the history
Close: #149
  • Loading branch information
AndreasLrx committed Jun 11, 2024
1 parent ac2c59e commit c960798
Show file tree
Hide file tree
Showing 12 changed files with 732 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/ecstasy/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ add_subdirectory(integrations)
add_subdirectory(query)
add_subdirectory(registry)
add_subdirectory(resources)
add_subdirectory(serialization)
add_subdirectory(storages)
add_subdirectory(system)
add_subdirectory(thread)
Expand Down
11 changes: 11 additions & 0 deletions src/ecstasy/serialization/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
set(INCROOT ${INCROOT}/serialization)
set(SRCROOT ${SRCROOT}/serialization)

set(SRC
${SRC}
${INCROOT}/include.hpp
${INCROOT}/ISerializer.hpp
${INCROOT}/RawSerializer.hpp
${INCROOT}/Serializer.hpp
PARENT_SCOPE
)
38 changes: 38 additions & 0 deletions src/ecstasy/serialization/ISerializer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
///
/// @file ISerializer.hpp
/// @author Andréas Leroux ([email protected])
/// @brief
/// @version 1.0.0
/// @date 2024-04-29
///
/// @copyright Copyright (c) ECSTASY 2024
///
///

#ifndef ECSTASY_SERIALIZATION_ISERIALIZER_HPP_
#define ECSTASY_SERIALIZATION_ISERIALIZER_HPP_

namespace ecstasy::serialization
{
///
/// @brief Interface for all serializer classes.
///
/// @note As for many ecstasy classes, this class is empty and is used to group all serializer classes under the
/// same type. This is required to store them in a container or to use them in a virtual function.
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-06-11)
///
class ISerializer {
public:
///
/// @brief Destroy the ISerializer
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-06-11)
///
virtual ~ISerializer() = default;
};
} // namespace ecstasy::serialization

#endif /* !ECSTASY_SERIALIZATION_ISERIALIZER_HPP_ */
214 changes: 214 additions & 0 deletions src/ecstasy/serialization/RawSerializer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
///
/// @file Serializer.hpp
/// @author Andréas Leroux ([email protected])
/// @brief
/// @version 1.0.0
/// @date 2024-04-29
///
/// @copyright Copyright (c) ECSTASY 2024
///
///

#ifndef ECSTASY_SERIALIZATION_RAWSERIALIZER_HPP_
#define ECSTASY_SERIALIZATION_RAWSERIALIZER_HPP_

#include <sstream>
#include <vector>

#include "Serializer.hpp"
#include "util/meta/is_std_vector.hpp"

namespace ecstasy::serialization
{

///
/// @brief RawSerializer class. This class is used to serialize and deserialize raw data in a string stream used as
/// a bytes buffer. Fundamental types are serialized as is, and other types are serialized by calling the custom
/// save/update methods.
///
/// @note Some fundamental types are handled by the serializer:
/// - fundamental types are saved as is.
/// - std::string and std::string_view are saved as a u_int32_t size followed by the string bytes.
/// - Bounded arrays are saved by iterating over each element and saving them.
/// - std::vector are saved as a u_int32_t size followed by each element.
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-04-30)
///
class RawSerializer : public Serializer<RawSerializer> {
public:
///
/// @brief Construct a new Raw Serializer instance.
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-04-30)
///
RawSerializer() = default;

///
/// @brief Destroy the RawSerializer
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-06-11)
///
~RawSerializer() override = default;

/// @copydoc save
template <typename T>
requires(!std::is_fundamental_v<T>)
RawSerializer &save(const T &object)
{
if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
save(static_cast<u_int32_t>(object.size()));
_stream << object;
} else if constexpr (std::is_bounded_array_v<T>) {
for (size_t i = 0; i < std::extent_v<T>; i++)
save(object[i]);
} else if constexpr (util::meta::is_std_vector<T>::value) {
*this << object.size();
for (const auto &elem : object)
*this << elem;
} else {
return Parent::save(object);
}
return *this;
}

/// @copydoc save
template <typename U>
requires std::is_fundamental_v<U>
RawSerializer &save(U object)
{
write(reinterpret_cast<const char *>(&object), sizeof(object));
return *this;
}

/// @copydoc update
template <typename U>
RawSerializer &update(U &object)
{
if constexpr (std::is_fundamental_v<U> || std::is_same_v<U, std::string>)
object = load<U>();
else if constexpr (std::is_bounded_array_v<U>)
for (size_t i = 0; i < std::extent_v<U>; i++)
update(object[i]);
else if constexpr (util::meta::is_std_vector<U>::value) {
auto size = load<decltype(object.size())>();
object.clear();
object.reserve(size);
for (size_t i = 0; i < size; i++) {
if constexpr (is_constructible<typename U::value_type>)
object.emplace_back(*this);
else
object.push_back(std::move(load<typename U::value_type>()));
}
} else
return Parent::update(object);
return *this;
}

/// @copydoc load
template <typename U>
U load()
{
if constexpr (std::is_fundamental_v<U>)
return loadRaw<U>();
else if constexpr (std::is_same<U, std::string>::value) {
u_int32_t size = loadRaw<u_int32_t>();
std::streampos pos = _stream.tellg();
_stream.seekg(pos + std::streamoff(size));

return std::string(_stream.view().data() + pos, size);
} else {
return Parent::load<U>();
}
}

///
/// @brief Append a type to the serializer as it is in the memory.
///
/// @warning You must not use this for pointers or references.
///
/// @tparam U Type of the object to append.
///
/// @param[in] object Object to append.
///
/// @return RawSerializer& Reference to @b this for chain calls.
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-04-30)
///
template <typename U>
RawSerializer &appendRaw(const U &object)
{
write(reinterpret_cast<const char *>(&object), sizeof(object));
return *this;
}

///
/// @brief Append a string to the serializer.
///
/// @param[in] bytes Bytes to append.
/// @param[in] size Size of the bytes buffer to append.
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-04-30)
///
void write(const char *bytes, size_t size)
{
_stream.write(bytes, size);
}

///
/// @brief Load a raw type from the serializer.
///
/// @note This is the opposite of @ref appendRaw.
///
/// @tparam U Type of the object to load.
///
/// @return U Loaded object.
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-04-30)
///
template <typename U>
U loadRaw()
{
std::streampos pos = _stream.tellg();
const U *result = reinterpret_cast<const U *>(_stream.view().data() + pos);
_stream.seekg(pos + std::streamoff(sizeof(U)));
return *result;
}

///
/// @brief Get the string representation of the serializer content.
///
/// @return std::string String representation of the serializer content.
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-06-11)
///
std::string str()
{
return _stream.str();
}

///
/// @brief Get the string stream of the serializer.
///
/// @return std::stringstream& Reference to the string stream of the serializer.
///
/// @author Andréas Leroux ([email protected])
/// @since 1.0.0 (2024-06-11)
///
std::stringstream &getStream()
{
return _stream;
}

private:
std::stringstream _stream;
};
} // namespace ecstasy::serialization

#endif /* !ECSTASY_SERIALIZATION_RAWSERIALIZER_HPP_ */
Loading

0 comments on commit c960798

Please sign in to comment.