|
1 | 1 | /**
|
2 |
| - * @file |
3 |
| - * @brief A simple Serializer and Deserializer utility for fundamental data |
4 |
| - * types and strings. |
| 2 | + * @file serialization.cpp |
| 3 | + * @brief Utility for binary serialization and deserialization of fundamental |
| 4 | + * data types and strings. |
| 5 | + * @details |
| 6 | + * Binary serialization converts data into its binary format for |
| 7 | + * efficient storage and transmission, which is more compact and faster than |
| 8 | + * text-based formats. This utility handles fundamental types like integers, |
| 9 | + * doubles, and characters by writing their binary representation directly to a |
| 10 | + * file using `reinterpret_cast`. |
| 11 | + * |
| 12 | + * ### Serializing: |
| 13 | + * - **Fundamental Types**: Written directly in their binary form. |
| 14 | + * - **Strings**: The string's length is written first, followed by the string |
| 15 | + * data, and a `|` delimiter to mark the end. |
| 16 | + * |
| 17 | + * ### Deserializing: |
| 18 | + * - **Fundamental Types**: Reads the binary data back into the appropriate |
| 19 | + * type. |
| 20 | + * - **Strings**: Reads the string's length, followed by the string data, and |
| 21 | + * checks for the `|` delimiter. An error is raised if the delimiter is missing. |
| 22 | + * |
| 23 | + * @author [SharonIV0X86](https://github.com/SharonIV0X86) |
5 | 24 | */
|
| 25 | + |
6 | 26 | #include <cassert> // for assert
|
7 | 27 | #include <cstdint> // for std::uint32_t
|
| 28 | +#include <cstdio> // for std::remove() |
8 | 29 | #include <fstream> // for std::ifstream std::ofstream
|
9 | 30 | #include <iostream> // for std::ios std::cout std::cerr
|
10 | 31 | #include <string> // for std::string
|
11 | 32 | #include <type_traits> // for std::is_fundamental
|
12 | 33 |
|
13 | 34 | /**
|
| 35 | + * @class Serializer |
14 | 36 | * @brief A utility class for serializing fundamental data types and strings to
|
15 | 37 | * a binary file.
|
16 | 38 | */
|
@@ -104,60 +126,132 @@ class Deserializer {
|
104 | 126 | * @return void
|
105 | 127 | */
|
106 | 128 | void tests() {
|
107 |
| - std::ofstream outFile("test_output.bin", std::ios::binary); |
108 |
| - if (!outFile) { |
109 |
| - std::cerr << "Error opening file for output.\n"; |
110 |
| - return; |
111 |
| - } |
| 129 | + try { |
| 130 | + std::ofstream outFile("test_output.bin", std::ios::binary); |
| 131 | + if (!outFile) { |
| 132 | + throw std::runtime_error("Error opening file for output."); |
| 133 | + } |
| 134 | + |
| 135 | + int testInt = 12345; |
| 136 | + double testDouble = 9876.54321; |
| 137 | + char testChar = 'A'; |
| 138 | + std::string testString = "Testing String Serialization!"; |
| 139 | + |
| 140 | + // Serialize the data |
| 141 | + Serializer::serialize(outFile, testInt); |
| 142 | + Serializer::serialize(outFile, testDouble); |
| 143 | + Serializer::serialize(outFile, testChar); |
| 144 | + Serializer::serialize(outFile, testString); |
| 145 | + |
| 146 | + outFile.close(); |
112 | 147 |
|
113 |
| - int testInt = 12345; |
114 |
| - double testDouble = 9876.54321; |
115 |
| - char testChar = 'A'; |
116 |
| - std::string testString = "Testing String Serialization!"; |
| 148 | + std::ifstream inFile("test_output.bin", std::ios::binary); |
| 149 | + if (!inFile) { |
| 150 | + throw std::runtime_error("Error opening file for input."); |
| 151 | + } |
| 152 | + |
| 153 | + int intResult; |
| 154 | + double doubleResult; |
| 155 | + char charResult; |
| 156 | + std::string stringResult; |
| 157 | + |
| 158 | + // Deserialize the data |
| 159 | + Deserializer::deserialize(inFile, intResult); |
| 160 | + Deserializer::deserialize(inFile, doubleResult); |
| 161 | + Deserializer::deserialize(inFile, charResult); |
| 162 | + Deserializer::deserialize(inFile, stringResult); |
117 | 163 |
|
118 |
| - // Serialize the data |
119 |
| - Serializer::serialize(outFile, testInt); |
120 |
| - Serializer::serialize(outFile, testDouble); |
121 |
| - Serializer::serialize(outFile, testChar); |
122 |
| - Serializer::serialize(outFile, testString); |
| 164 | + inFile.close(); |
123 | 165 |
|
124 |
| - outFile.close(); |
| 166 | + // Assert that the original and deserialized values are the same |
| 167 | + assert(testInt == intResult); |
| 168 | + assert(testDouble == doubleResult); |
| 169 | + assert(testChar == charResult); |
| 170 | + assert(testString == stringResult); |
125 | 171 |
|
126 |
| - std::ifstream inFile("test_output.bin", std::ios::binary); |
127 |
| - if (!inFile) { |
128 |
| - std::cerr << "Error opening file for input.\n"; |
129 |
| - return; |
| 172 | + std::cout << "All tests passed!\n"; |
| 173 | + } catch (const std::exception &e) { |
| 174 | + std::cerr << "Test failed: " << e.what() << std::endl; |
130 | 175 | }
|
131 | 176 |
|
132 |
| - int intResult; |
133 |
| - double doubleResult; |
134 |
| - char charResult; |
135 |
| - std::string stringResult; |
| 177 | + // Test for file opening failure |
| 178 | + try { |
| 179 | + std::ifstream inFile("non_existent_file.bin", std::ios::binary); |
| 180 | + if (!inFile) { |
| 181 | + throw std::runtime_error("Error opening non-existent file."); |
| 182 | + } |
| 183 | + } catch (const std::runtime_error &e) { |
| 184 | + assert(std::string(e.what()) == "Error opening non-existent file."); |
| 185 | + std::cout << "File opening error test passed!\n"; |
| 186 | + } |
136 | 187 |
|
137 |
| - // Deserialize the data |
138 |
| - Deserializer::deserialize(inFile, intResult); |
139 |
| - Deserializer::deserialize(inFile, doubleResult); |
140 |
| - Deserializer::deserialize(inFile, charResult); |
141 |
| - Deserializer::deserialize(inFile, stringResult); |
| 188 | + // Test for deserialization failure (e.g., wrong file format) |
| 189 | + try { |
| 190 | + std::ofstream outFile("wrong_format.bin", std::ios::binary); |
| 191 | + outFile << "This is not serialized data"; // Writing incorrect data |
| 192 | + outFile.close(); |
142 | 193 |
|
143 |
| - inFile.close(); |
| 194 | + std::ifstream inFile("wrong_format.bin", std::ios::binary); |
| 195 | + if (!inFile) { |
| 196 | + throw std::runtime_error("Error opening file for input."); |
| 197 | + } |
144 | 198 |
|
145 |
| - // Assert that the original and deserialized values are the same |
146 |
| - assert(testInt == intResult); |
147 |
| - assert(testDouble == doubleResult); |
148 |
| - assert(testChar == charResult); |
149 |
| - assert(testString == stringResult); |
| 199 | + int intResult; |
| 200 | + // Deserialize expecting binary data, this should fail |
| 201 | + Deserializer::deserialize(inFile, intResult); |
150 | 202 |
|
151 |
| - std::cout << "All tests passed!\n"; |
| 203 | + inFile.close(); |
| 204 | + } catch (const std::exception &e) { |
| 205 | + std::cout << "Deserialization error test passed: " << e.what() |
| 206 | + << std::endl; |
| 207 | + } |
| 208 | + |
| 209 | + // Test for string too large error |
| 210 | + try { |
| 211 | + std::ofstream outFile("large_string.bin", std::ios::binary); |
| 212 | + std::string largeString(1024 * 1024 + 1, |
| 213 | + 'A'); // String larger than 1MB |
| 214 | + Serializer::serialize(outFile, |
| 215 | + largeString); // Should succeed in serialization |
| 216 | + outFile.close(); |
| 217 | + |
| 218 | + std::ifstream inFile("large_string.bin", std::ios::binary); |
| 219 | + std::string deserializedString; |
| 220 | + Deserializer::deserialize( |
| 221 | + inFile, deserializedString); // Should fail in deserialization |
| 222 | + inFile.close(); |
| 223 | + } catch (const std::runtime_error &e) { |
| 224 | + assert(std::string(e.what()) == |
| 225 | + "Deserialized string length is too large."); |
| 226 | + std::cout << "Large string error test passed!\n"; |
| 227 | + } |
| 228 | + |
| 229 | + // Test for missing delimiter |
| 230 | + try { |
| 231 | + std::ofstream outFile("missing_delimiter.bin", std::ios::binary); |
| 232 | + std::string testString = "Test String"; |
| 233 | + std::uint32_t length = testString.size(); |
| 234 | + outFile.write(reinterpret_cast<const char *>(&length), sizeof(length)); |
| 235 | + outFile.write(testString.c_str(), length); // No delimiter |
| 236 | + outFile.close(); |
| 237 | + |
| 238 | + std::ifstream inFile("missing_delimiter.bin", std::ios::binary); |
| 239 | + std::string deserializedString; |
| 240 | + Deserializer::deserialize(inFile, deserializedString); // Should fail |
| 241 | + inFile.close(); |
| 242 | + } catch (const std::runtime_error &e) { |
| 243 | + assert(std::string(e.what()) == |
| 244 | + "Delimiter '|' not found after string."); |
| 245 | + std::cout << "Missing delimiter error test passed!\n"; |
| 246 | + } |
| 247 | + // Clean up temporary files that were created during testing. |
| 248 | + std::remove("test_output.bin"); |
| 249 | + std::remove("wrong_format.bin"); |
| 250 | + std::remove("large_string.bin"); |
| 251 | + std::remove("missing_delimiter.bin"); |
152 | 252 | }
|
153 | 253 |
|
154 | 254 | int main() {
|
155 | 255 | tests();
|
156 |
| - |
157 | 256 | return 0;
|
158 | 257 | }
|
159 |
| - |
160 |
| -/** |
161 |
| - * @brief A test suite to perform extensive testing on the Serializer and |
162 |
| - * Deserializer. |
163 |
| - */ |
|
0 commit comments