-
-
Notifications
You must be signed in to change notification settings - Fork 7.6k
feat: added serializers #2756
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
+248
−0
Closed
feat: added serializers #2756
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
0b19d86
feat: added serializers
SharonIV0x86 6e1f86d
fix: added assertions in test function.
SharonIV0x86 79539ae
fix: added test assertions.
SharonIV0x86 4df4977
fix: made requested changes.
SharonIV0x86 8b8f915
fix: replaced ssize_t with std::uint32_t
SharonIV0x86 afb2b7a
fix: made required changes.
SharonIV0x86 a436849
fix: added missing @brief
SharonIV0x86 447d463
docs: add documentation to main
realstealthninja 33573a9
fix: made required changes
SharonIV0x86 155df97
fix: added Serialization namespace
SharonIV0x86 4e4089c
fix: fixed namespace issues
SharonIV0x86 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
/** | ||
* @file serialization.cpp | ||
* @brief Utility for binary serialization and deserialization of fundamental | ||
* data types and strings. | ||
* @details | ||
* Binary serialization converts data into its binary format for | ||
* efficient storage and transmission, which is more compact and faster than | ||
* text-based formats. This utility handles fundamental types like integers, | ||
* doubles, and characters by writing their binary representation directly to a | ||
* file using `reinterpret_cast`. | ||
* | ||
* ### Serializing: | ||
* - **Fundamental Types**: Written directly in their binary form. | ||
* - **Strings**: The string's length is written first, followed by the string | ||
* data, and a `|` delimiter to mark the end. String size should not exceed 1MB. | ||
* | ||
* ### Deserializing: | ||
* - **Fundamental Types**: Reads the binary data back into the appropriate | ||
* type. | ||
* - **Strings**: Reads the string's length, followed by the string data, and | ||
* checks for the `|` delimiter. An error is raised if the delimiter is missing. | ||
* | ||
* @author [SharonIV0X86](https://github.com/SharonIV0X86) | ||
*/ | ||
|
||
#include <cassert> // for assert | ||
#include <cstdint> // for std::uint32_t | ||
#include <cstdio> // for std::remove() | ||
#include <fstream> // for std::ifstream std::ofstream | ||
#include <iostream> // for std::ios std::cout std::cerr | ||
#include <string> // for std::string | ||
#include <type_traits> // for std::is_fundamental | ||
|
||
/** @namespace serialization | ||
* @brief Classes for binary Serialization and Deserialization | ||
*/ | ||
namespace serialization { | ||
/** | ||
* @class Serializer | ||
* @brief A utility class for serializing fundamental data types and strings to | ||
* a binary file. | ||
realstealthninja marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
class Serializer { | ||
realstealthninja marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public: | ||
/** | ||
* @brief Serializes fundamental data types to a binary file. | ||
* @tparam T The type of the data to be serialized. | ||
* @param out The output stream (std::ofstream). | ||
* @param data The data to be serialized. | ||
* | ||
* @note This function only works for fundamental types (primitives). | ||
*/ | ||
template <typename T> | ||
static void serialize(std::ofstream &out, const T &data) { | ||
static_assert( | ||
std::is_fundamental<T>::value, | ||
"Non-fundamental types are not allowed for this function!"); | ||
out.write(reinterpret_cast<const char *>(&data), sizeof(T)); | ||
} | ||
|
||
/** | ||
* @brief Serializes a string to a binary file. | ||
* @param out The output stream (std::ofstream). | ||
* @param data The string to be serialized. | ||
* | ||
* @note The string is serialized by first storing its length, followed by | ||
* the content. String length should not exceed 1MB | ||
*/ | ||
static void serialize(std::ofstream &out, const std::string &data) { | ||
std::uint32_t length = data.size(); | ||
|
||
// Check if the string exceeds the size limit of 1MB. | ||
const std::uint32_t max_size = 1024 * 1024; | ||
if (length > max_size) { | ||
throw std::runtime_error("String exceeds the maximum size of 1MB."); | ||
} | ||
|
||
serialize(out, length); // Serialize the length of the string. | ||
out.write(data.c_str(), length); // Serialize the string characters. | ||
out.put('|'); // Add a delimiter to denote the end of the string. | ||
realstealthninja marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
}; | ||
|
||
/** | ||
* A utility class for deserializing fundamental data types and strings to a | ||
* binary file | ||
*/ | ||
class Deserializer { | ||
public: | ||
/** | ||
* @brief Deserializes fundamental data types (like int, float, double, | ||
* etc.) from a binary file. | ||
* @tparam T The type of the data to be deserialized. | ||
* @param in The input stream (std::ifstream). | ||
* @param data The variable where the deserialized data will be stored. | ||
* | ||
* @note This function only works for fundamental types (primitives). | ||
*/ | ||
template <typename T> | ||
static void deserialize(std::ifstream &in, T &data) { | ||
static_assert( | ||
std::is_fundamental<T>::value, | ||
"Non-fundamental types are not allowed for this function!"); | ||
in.read(reinterpret_cast<char *>(&data), sizeof(T)); | ||
} | ||
|
||
/** | ||
* @brief Deserializes a string from a binary file. | ||
* @param in The input stream (std::ifstream). | ||
* @param data The string where the deserialized content will be stored. | ||
* | ||
* The string is deserialized by reading its length, followed by the | ||
* characters, and finally validating the delimiter throws error if the | ||
* delimiter '|' is not found. | ||
*/ | ||
static void deserialize(std::ifstream &in, std::string &data) { | ||
std::uint32_t length; | ||
deserialize(in, length); // Deserialize the length of the string. | ||
|
||
if (length > 1024 * 1024) // Sanity check to prevent huge strings. | ||
realstealthninja marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
throw std::runtime_error( | ||
"Deserialized string length is too large."); | ||
} | ||
|
||
data.resize(length); // Resize the string to fit the incoming data. | ||
in.read(&data[0], length); // Read the string characters. | ||
char delimiter; | ||
in.get(delimiter); // Check the delimiter. | ||
if (delimiter != '|') { | ||
throw std::runtime_error("Delimiter '|' not found after string."); | ||
} | ||
} | ||
}; | ||
} // namespace Serialization | ||
|
||
/** | ||
* @brief self test implementation | ||
* @return void | ||
*/ | ||
void tests() { | ||
// Serialize fundamental types and string | ||
std::ofstream outFile("test_output.bin", std::ios::binary); | ||
if (!outFile) { | ||
throw std::runtime_error("Error opening file for output."); | ||
} | ||
|
||
int testInt = 12345; | ||
double testDouble = 9876.54321; | ||
char testChar = 'A'; | ||
std::string testString = "Testing String Serialization!"; | ||
|
||
// Serialize the data | ||
serialization::Serializer::serialize(outFile, testInt); | ||
serialization::Serializer::serialize(outFile, testDouble); | ||
serialization::Serializer::serialize(outFile, testChar); | ||
serialization::Serializer::serialize(outFile, testString); | ||
|
||
outFile.close(); | ||
|
||
// Deserialize the data and assert | ||
std::ifstream inFile("test_output.bin", std::ios::binary); | ||
if (!inFile) { | ||
throw std::runtime_error("Error opening file for input."); | ||
} | ||
|
||
int intResult; | ||
double doubleResult; | ||
char charResult; | ||
std::string stringResult; | ||
|
||
// Deserialize the data | ||
serialization::Deserializer::deserialize(inFile, intResult); | ||
serialization::Deserializer::deserialize(inFile, doubleResult); | ||
serialization::Deserializer::deserialize(inFile, charResult); | ||
serialization::Deserializer::deserialize(inFile, stringResult); | ||
|
||
inFile.close(); | ||
|
||
SharonIV0x86 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Assert that the original and deserialized values are the same | ||
assert(testInt == intResult); | ||
assert(testDouble == doubleResult); | ||
assert(testChar == charResult); | ||
assert(testString == stringResult); | ||
|
||
std::cout << "Basic serialization/deserialization tests passed!\n"; | ||
|
||
// Test for string too large error | ||
try { | ||
std::ofstream largeOutFile("large_string.bin", std::ios::binary); | ||
// Create a string larger than 1MB | ||
std::string largeString(1024 * 1024 + 1, 'A'); | ||
serialization::Serializer::serialize( | ||
largeOutFile, | ||
largeString); // Should throw an error | ||
largeOutFile.close(); | ||
assert(false); // If we reach here, the test has failed. | ||
} catch (const std::runtime_error &e) { | ||
assert(std::string(e.what()) == | ||
"String exceeds the maximum size of 1MB."); | ||
std::cout << "Large string serialization error test passed!\n"; | ||
} | ||
// Test for missing delimiter in string serialization | ||
try { | ||
std::ofstream missingDelimiterOutFile("missing_delimiter.bin", | ||
std::ios::binary); | ||
|
||
std::string incompleteString = "Incomplete string test"; | ||
std::uint32_t length = incompleteString.size(); | ||
|
||
// Serialize string length and content without the delimiter | ||
missingDelimiterOutFile.write(reinterpret_cast<const char *>(&length), | ||
sizeof(length)); | ||
missingDelimiterOutFile.write(incompleteString.c_str(), | ||
length); // No delimiter '|' | ||
missingDelimiterOutFile.close(); | ||
|
||
std::ifstream missingDelimiterInFile("missing_delimiter.bin", | ||
std::ios::binary); | ||
|
||
std::string deserializedString; | ||
serialization::Deserializer::deserialize( | ||
missingDelimiterInFile, | ||
deserializedString); // Should throw an error | ||
missingDelimiterInFile.close(); | ||
|
||
assert(false); | ||
} catch (const std::runtime_error &e) { | ||
assert(std::string(e.what()) == | ||
"Delimiter '|' not found after string."); | ||
std::cout << "Missing delimiter error test passed!\n"; | ||
} | ||
|
||
// Clean up temporary files that were created during testing | ||
std::remove("large_string.bin"); | ||
std::remove("missing_delimiter.bin"); | ||
std::remove("test_output.bin"); | ||
|
||
realstealthninja marked this conversation as resolved.
Show resolved
Hide resolved
|
||
std::cout << "All tests passed successfully!\n"; | ||
} | ||
/** | ||
* @brief Main function | ||
* @returns 0 on successful exit | ||
*/ | ||
int main() { | ||
tests(); // run self test implementations | ||
return 0; | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.