diff options
Diffstat (limited to 'archive/2025/summer/bsc_karidas/src/LogEntry.cpp')
| -rw-r--r-- | archive/2025/summer/bsc_karidas/src/LogEntry.cpp | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/archive/2025/summer/bsc_karidas/src/LogEntry.cpp b/archive/2025/summer/bsc_karidas/src/LogEntry.cpp new file mode 100644 index 000000000..af487a29d --- /dev/null +++ b/archive/2025/summer/bsc_karidas/src/LogEntry.cpp @@ -0,0 +1,375 @@ +#include "LogEntry.hpp" +#include <cstring> +#include <stdexcept> +#include <iostream> + +LogEntry::LogEntry() + : m_actionType(ActionType::CREATE), + m_dataLocation(""), + m_dataControllerId(""), + m_dataProcessorId(""), + m_dataSubjectId(""), + m_timestamp(std::chrono::system_clock::now()), + m_payload() {} + +LogEntry::LogEntry(ActionType actionType, + std::string dataLocation, + std::string dataControllerId, + std::string dataProcessorId, + std::string dataSubjectId, + std::vector<uint8_t> payload) + : m_actionType(actionType), + m_dataLocation(std::move(dataLocation)), + m_dataControllerId(std::move(dataControllerId)), + m_dataProcessorId(std::move(dataProcessorId)), + m_dataSubjectId(std::move(dataSubjectId)), + m_timestamp(std::chrono::system_clock::now()), + m_payload(std::move(payload)) +{ +} + +// Move version that consumes the LogEntry +std::vector<uint8_t> LogEntry::serialize() && +{ + // Calculate required size upfront + size_t totalSize = + sizeof(int) + // ActionType + sizeof(uint32_t) + m_dataLocation.size() + // Size + data location + sizeof(uint32_t) + m_dataControllerId.size() + // Size + data controller ID + sizeof(uint32_t) + m_dataProcessorId.size() + // Size + data processor ID + sizeof(uint32_t) + m_dataSubjectId.size() + // Size + data subject ID + sizeof(int64_t) + // Timestamp + sizeof(uint32_t) + m_payload.size(); // Size + payload data + + // Pre-allocate the vector + std::vector<uint8_t> result; + result.reserve(totalSize); + + // Push ActionType + int actionType = static_cast<int>(m_actionType); + appendToVector(result, &actionType, sizeof(actionType)); + + // Move strings + appendStringToVector(result, std::move(m_dataLocation)); + appendStringToVector(result, std::move(m_dataControllerId)); + appendStringToVector(result, std::move(m_dataProcessorId)); + appendStringToVector(result, std::move(m_dataSubjectId)); + + // Push timestamp + int64_t timestamp = std::chrono::duration_cast<std::chrono::milliseconds>( + m_timestamp.time_since_epoch()) + .count(); + appendToVector(result, ×tamp, sizeof(timestamp)); + + // Move payload + uint32_t payloadSize = static_cast<uint32_t>(m_payload.size()); + appendToVector(result, &payloadSize, sizeof(payloadSize)); + if (!m_payload.empty()) + { + result.insert(result.end(), + std::make_move_iterator(m_payload.begin()), + std::make_move_iterator(m_payload.end())); + } + + return result; +} + +// Const version for when you need to keep the LogEntry +std::vector<uint8_t> LogEntry::serialize() const & +{ + // Calculate required size upfront + size_t totalSize = + sizeof(int) + // ActionType + sizeof(uint32_t) + m_dataLocation.size() + // Size + data location + sizeof(uint32_t) + m_dataControllerId.size() + // Size + data controller ID + sizeof(uint32_t) + m_dataProcessorId.size() + // Size + data processor ID + sizeof(uint32_t) + m_dataSubjectId.size() + // Size + data subject ID + sizeof(int64_t) + // Timestamp + sizeof(uint32_t) + m_payload.size(); // Size + payload data + + // Pre-allocate the vector + std::vector<uint8_t> result; + result.reserve(totalSize); + + // Push ActionType + int actionType = static_cast<int>(m_actionType); + appendToVector(result, &actionType, sizeof(actionType)); + + // Copy strings + appendStringToVector(result, m_dataLocation); + appendStringToVector(result, m_dataControllerId); + appendStringToVector(result, m_dataProcessorId); + appendStringToVector(result, m_dataSubjectId); + + // Push timestamp + int64_t timestamp = std::chrono::duration_cast<std::chrono::milliseconds>( + m_timestamp.time_since_epoch()) + .count(); + appendToVector(result, ×tamp, sizeof(timestamp)); + + // Copy payload + uint32_t payloadSize = static_cast<uint32_t>(m_payload.size()); + appendToVector(result, &payloadSize, sizeof(payloadSize)); + if (!m_payload.empty()) + { + appendToVector(result, m_payload.data(), m_payload.size()); + } + + return result; +} + +bool LogEntry::deserialize(std::vector<uint8_t> &&data) +{ + try + { + size_t offset = 0; + + // Check if we have enough data for the basic structure + if (data.size() < sizeof(int)) + return false; + + // Extract action type + int actionType; + std::memcpy(&actionType, data.data() + offset, sizeof(actionType)); + offset += sizeof(actionType); + m_actionType = static_cast<ActionType>(actionType); + + // Extract data location + if (!extractStringFromVector(data, offset, m_dataLocation)) + return false; + + // Extract data controller ID + if (!extractStringFromVector(data, offset, m_dataControllerId)) + return false; + + // Extract data processor ID + if (!extractStringFromVector(data, offset, m_dataProcessorId)) + return false; + + // Extract data subject ID + if (!extractStringFromVector(data, offset, m_dataSubjectId)) + return false; + + // Extract timestamp + if (offset + sizeof(int64_t) > data.size()) + return false; + + int64_t timestamp; + std::memcpy(×tamp, data.data() + offset, sizeof(timestamp)); + offset += sizeof(timestamp); + m_timestamp = std::chrono::system_clock::time_point(std::chrono::milliseconds(timestamp)); + + // Extract payload + if (offset + sizeof(uint32_t) > data.size()) + return false; + + uint32_t payloadSize; + std::memcpy(&payloadSize, data.data() + offset, sizeof(payloadSize)); + offset += sizeof(payloadSize); + + if (offset + payloadSize > data.size()) + return false; + + if (payloadSize > 0) + { + m_payload.clear(); + m_payload.reserve(payloadSize); + + auto start_it = data.begin() + offset; + auto end_it = start_it + payloadSize; + m_payload.assign(std::make_move_iterator(start_it), + std::make_move_iterator(end_it)); + offset += payloadSize; + } + else + { + m_payload.clear(); + } + + return true; + } + catch (const std::exception &) + { + return false; + } +} + +std::vector<uint8_t> LogEntry::serializeBatch(std::vector<LogEntry> &&entries) +{ + if (entries.empty()) + { + // Just return a vector with count = 0 + std::vector<uint8_t> batchData(sizeof(uint32_t)); + uint32_t numEntries = 0; + std::memcpy(batchData.data(), &numEntries, sizeof(numEntries)); + return batchData; + } + + // Pre-calculate approximate total size to minimize reallocations + size_t estimatedSize = sizeof(uint32_t); // Number of entries + for (const auto &entry : entries) + { + // Rough estimate: header size + string sizes + payload size + estimatedSize += sizeof(uint32_t) + // Entry size field + sizeof(int) + // ActionType + 3 * sizeof(uint32_t) + // 3 string length fields + entry.getDataLocation().size() + + entry.getDataControllerId().size() + + entry.getDataProcessorId().size() + + entry.getDataSubjectId().size() + + sizeof(int64_t) + // Timestamp + sizeof(uint32_t) + // Payload size + entry.getPayload().size(); + } + + std::vector<uint8_t> batchData; + batchData.reserve(estimatedSize); + + // Store the number of entries + uint32_t numEntries = static_cast<uint32_t>(entries.size()); + batchData.resize(sizeof(numEntries)); + std::memcpy(batchData.data(), &numEntries, sizeof(numEntries)); + + // Serialize and append each entry using move semantics + for (auto &entry : entries) + { + // Move-serialize the entry + std::vector<uint8_t> entryData = std::move(entry).serialize(); + + // Store the size of the serialized entry + uint32_t entrySize = static_cast<uint32_t>(entryData.size()); + size_t currentSize = batchData.size(); + batchData.resize(currentSize + sizeof(entrySize)); + std::memcpy(batchData.data() + currentSize, &entrySize, sizeof(entrySize)); + + // Move the serialized entry data + batchData.insert(batchData.end(), + std::make_move_iterator(entryData.begin()), + std::make_move_iterator(entryData.end())); + } + + return batchData; +} + +std::vector<LogEntry> LogEntry::deserializeBatch(std::vector<uint8_t> &&batchData) +{ + std::vector<LogEntry> entries; + + try + { + // Read the number of entries + if (batchData.size() < sizeof(uint32_t)) + { + throw std::runtime_error("Batch data too small to contain entry count"); + } + + uint32_t numEntries; + std::memcpy(&numEntries, batchData.data(), sizeof(numEntries)); + + // Reserve space for entries to avoid reallocations + entries.reserve(numEntries); + + // Position in the batch data + size_t position = sizeof(numEntries); + + // Extract each entry + for (uint32_t i = 0; i < numEntries; ++i) + { + // Check if we have enough data left to read the entry size + if (position + sizeof(uint32_t) > batchData.size()) + { + throw std::runtime_error("Unexpected end of batch data"); + } + + // Read the size of the entry + uint32_t entrySize; + std::memcpy(&entrySize, batchData.data() + position, sizeof(entrySize)); + position += sizeof(entrySize); + + // Check if we have enough data left to read the entry + if (position + entrySize > batchData.size()) + { + throw std::runtime_error("Unexpected end of batch data"); + } + + // Create entry data by moving a slice from the batch data + std::vector<uint8_t> entryData; + entryData.reserve(entrySize); + + auto start_it = batchData.begin() + position; + auto end_it = start_it + entrySize; + entryData.assign(std::make_move_iterator(start_it), + std::make_move_iterator(end_it)); + position += entrySize; + + // Deserialize the entry using move semantics + LogEntry entry; + if (entry.deserialize(std::move(entryData))) + { + entries.emplace_back(std::move(entry)); + } + else + { + throw std::runtime_error("Failed to deserialize log entry"); + } + } + } + catch (const std::exception &e) + { + std::cerr << "Error deserializing log batch: " << e.what() << std::endl; + } + + return entries; +} + +// Helper method to append data to a vector +void LogEntry::appendToVector(std::vector<uint8_t> &vec, const void *data, size_t size) const +{ + const uint8_t *bytes = static_cast<const uint8_t *>(data); + vec.insert(vec.end(), bytes, bytes + size); +} + +// Helper method to append a string with its length (const version) +void LogEntry::appendStringToVector(std::vector<uint8_t> &vec, const std::string &str) const +{ + uint32_t length = static_cast<uint32_t>(str.size()); + appendToVector(vec, &length, sizeof(length)); + + if (length > 0) + { + appendToVector(vec, str.data(), str.size()); + } +} + +// Helper method to append a string with its length (move version) +void LogEntry::appendStringToVector(std::vector<uint8_t> &vec, std::string &&str) +{ + uint32_t length = static_cast<uint32_t>(str.size()); + appendToVector(vec, &length, sizeof(length)); + + if (length > 0) + { + vec.insert(vec.end(), str.begin(), str.end()); + } +} + +// Helper method to extract a string from a vector +bool LogEntry::extractStringFromVector(std::vector<uint8_t> &vec, size_t &offset, std::string &str) +{ + // Check if we have enough data for the string length + if (offset + sizeof(uint32_t) > vec.size()) + return false; + + uint32_t length; + std::memcpy(&length, vec.data() + offset, sizeof(length)); + offset += sizeof(length); + + // Check if we have enough data for the string content + if (offset + length > vec.size()) + return false; + + str.assign(reinterpret_cast<const char *>(vec.data() + offset), length); + offset += length; + + return true; +} \ No newline at end of file |