1//! @file data.cpp
2//! @author ryftchen
3//! @brief The definitions (data) in the application module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2026 ryftchen. All rights reserved.
6
7#include "data.hpp"
8
9#ifndef _PRECOMPILED_HEADER
10#include <openssl/evp.h>
11#include <lz4.h>
12#include <array>
13#include <cstring>
14#include <iomanip>
15#else
16#include "application/pch/precompiled_header.hpp"
17#endif
18
19namespace application::data
20{
21Packet::Packet(char* const pktBuf, const std::size_t pktLen) :
22 head{pktBuf}, tail{head + pktLen}, writer{head}, reader{head}
23{
24 if (!pktBuf)
25 {
26 throw std::runtime_error{"The attempt to create the data packet failed."};
27 }
28}
29
30bool Packet::write(const void* const dst, const std::size_t offset)
31{
32 if (!dst || (offset == 0))
33 {
34 return false;
35 }
36
37 std::memcpy(dest: writer, src: dst, n: offset);
38 writer += offset;
39 return writer < tail;
40}
41
42bool Packet::read(void* const dst, const std::size_t offset)
43{
44 if (!dst || (offset == 0))
45 {
46 return false;
47 }
48
49 std::memcpy(dest: dst, src: reader, n: offset);
50 reader += offset;
51 return reader < tail;
52}
53
54// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
55//! @brief Encrypt the message with AES-128-CFB-128.
56//! @param buffer - message buffer
57//! @param length - buffer length
58void encryptMessage(char* const buffer, const std::size_t length)
59{
60 if (!buffer || (length == 0))
61 {
62 return;
63 }
64
65 auto* const ctx = ::EVP_CIPHER_CTX_new();
66 do
67 {
68 if (constexpr std::array<unsigned char, 16> key =
69 {0x37, 0x47, 0x10, 0x33, 0x6F, 0x18, 0xC8, 0x9A, 0x4B, 0xC1, 0x2B, 0x97, 0x92, 0x19, 0x25, 0x6D},
70 iv = {0x9F, 0x7B, 0x0E, 0x68, 0x2D, 0x2F, 0x4E, 0x7F, 0x1A, 0xFA, 0x61, 0xD3, 0xC6, 0x18, 0xF4, 0xC1};
71 ::EVP_EncryptInit_ex(ctx, cipher: ::EVP_aes_128_cfb128(), impl: nullptr, key: key.data(), iv: iv.data()) == 0)
72 {
73 break;
74 }
75
76 int outLen = 0;
77 if (::EVP_EncryptUpdate(
78 ctx,
79 out: reinterpret_cast<unsigned char*>(buffer),
80 outl: &outLen,
81 in: reinterpret_cast<unsigned char*>(buffer),
82 inl: static_cast<int>(length))
83 == 0)
84 {
85 break;
86 }
87
88 if (int tempLen = 0;
89 ::EVP_EncryptFinal_ex(ctx, out: reinterpret_cast<unsigned char*>(buffer) + outLen, outl: &tempLen) == 0)
90 {
91 break;
92 }
93 }
94 while (false);
95 ::EVP_CIPHER_CTX_free(c: ctx);
96}
97
98//! @brief Decrypt the message with AES-128-CFB-128.
99//! @param buffer - message buffer
100//! @param length - buffer length
101void decryptMessage(char* const buffer, const std::size_t length)
102{
103 if (!buffer || (length == 0))
104 {
105 return;
106 }
107
108 auto* const ctx = ::EVP_CIPHER_CTX_new();
109 do
110 {
111 if (constexpr std::array<unsigned char, 16> key =
112 {0x37, 0x47, 0x10, 0x33, 0x6F, 0x18, 0xC8, 0x9A, 0x4B, 0xC1, 0x2B, 0x97, 0x92, 0x19, 0x25, 0x6D},
113 iv = {0x9F, 0x7B, 0x0E, 0x68, 0x2D, 0x2F, 0x4E, 0x7F, 0x1A, 0xFA, 0x61, 0xD3, 0xC6, 0x18, 0xF4, 0xC1};
114 ::EVP_DecryptInit_ex(ctx, cipher: ::EVP_aes_128_cfb128(), impl: nullptr, key: key.data(), iv: iv.data()) == 0)
115 {
116 break;
117 }
118
119 int outLen = 0;
120 if (::EVP_DecryptUpdate(
121 ctx,
122 out: reinterpret_cast<unsigned char*>(buffer),
123 outl: &outLen,
124 in: reinterpret_cast<unsigned char*>(buffer),
125 inl: static_cast<int>(length))
126 == 0)
127 {
128 break;
129 }
130
131 if (int tempLen = 0;
132 ::EVP_DecryptFinal_ex(ctx, outm: reinterpret_cast<unsigned char*>(buffer) + outLen, outl: &tempLen) == 0)
133 {
134 break;
135 }
136 }
137 while (false);
138 ::EVP_CIPHER_CTX_free(c: ctx);
139}
140// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
141
142//! @brief Compress the data with LZ4.
143//! @param cache - data cache
144void compressData(std::vector<char>& cache)
145{
146 if (cache.empty())
147 {
148 return;
149 }
150
151 const int compressedCap = ::LZ4_compressBound(inputSize: static_cast<int>(cache.size()));
152 std::vector<char> compressed(compressedCap);
153 const int compressedSize =
154 ::LZ4_compress_default(src: cache.data(), dst: compressed.data(), srcSize: static_cast<int>(cache.size()), dstCapacity: compressedCap);
155 if (compressedSize < 0)
156 {
157 throw std::runtime_error{"Failed to compress data, return " + std::to_string(val: compressedSize) + '.'};
158 }
159 compressed.resize(new_size: compressedSize);
160 compressed.shrink_to_fit();
161 cache = std::move(compressed);
162}
163
164//! @brief Decompress the data with LZ4.
165//! @param cache - data cache
166void decompressData(std::vector<char>& cache)
167{
168 if (cache.empty())
169 {
170 return;
171 }
172
173 constexpr int decompressedCap = 65536 * 10 * 10;
174 std::vector<char> decompressed(decompressedCap);
175 const int decompressedSize =
176 ::LZ4_decompress_safe(src: cache.data(), dst: decompressed.data(), compressedSize: static_cast<int>(cache.size()), dstCapacity: decompressedCap);
177 if (decompressedSize < 0)
178 {
179 throw std::runtime_error{"Failed to decompress data, return " + std::to_string(val: decompressedSize) + '.'};
180 }
181 decompressed.resize(new_size: decompressedSize);
182 decompressed.shrink_to_fit();
183 cache = std::move(decompressed);
184}
185
186//! @brief Convert a byte buffer to a space-separated hexadecimal string.
187//! @param buffer - byte buffer
188//! @param length - buffer length
189//! @return hexadecimal string representation of the buffer
190std::string toHexString(const char* const buffer, const std::size_t length)
191{
192 if (!buffer || (length == 0))
193 {
194 return {};
195 }
196
197 std::ostringstream body{};
198 for (std::size_t i = 0; i < length; ++i)
199 {
200 body << "0x" << std::setfill('0') << std::setw(2) << std::hex
201 << static_cast<int>(static_cast<unsigned char>(buffer[i]));
202 if ((i + 1) != length)
203 {
204 body << ' ';
205 }
206 }
207 return body.str();
208}
209} // namespace application::data
210