/* * KLDR.hpp * Copyright (c) 2023 Cyclone Team. Licensed under GNU GPLv3-only terms. * */ #ifndef LIBSTADIUM_KLDR_HPP #define LIBSTADIUM_KLDR_HPP #include #include #include #include #include namespace Stadium::v1 { enum KLDRDefaultKeys : uint8_t { Data = 0x01, // Main data of event ObjectID = 0x02, // ID of object in local context SrcEventAuthor = 0x03, // Source author of event PrevEvent = 0x04, // ID of previous event, logically connected to this NextEvent = 0x05, // ID of next event BatchNumber = 0x06, // Number of event in chain of events Path = 0x07, // Path to requested/uploaded resource Power = 0x08, // Access right to object ServerSession = 0x09, // ID of server-side session // Crypto-related CryptoAlgos = 0x11, // Array of used cryptography algos CryptoKeyID = 0x12, // ID of crypto key used for data encryption in this event SignedDataHash = 0x13 // Signed by sender hash of data }; /* * KLDR stands for "Key-Length-Data-Repeat". * Schematically, binary array with KLDR-formatted data looks like this. * Single cell: * [key: 1 or 2 bytes][length of data: 1/2/4 bytes][data: bytes] * Whole array: * [cell 1][cell 2]...[cell n] * * Suffix "F" in function name stands for "Fast", i.e. it is unsafe, but fast version. * * This can be more optimized for performance with cost of more memory usage. */ template class KLDRArray { private: std::vector Keys; std::vector Lengths; std::vector Values; public: // Constructor KLDRArray () {}; // TODO: check used types and/or parse given char array/vector // Destructor ~KLDRArray () { // Freeing all pointers if vector is not empty for (size_t i = 0; i < this->CellsAmount(); i++) { delete[] this->Values[i]; } } // Move operator WARNING: есть шанс, что я тут сделал ложно, ибо мб мув просто продлевает время жизни вместо перемещения. Ассигнмента тоже касается KLDRArray (KLDRArray&& src) : Keys(std::move(src.Keys)), Lengths(std::move(src.Lengths)), Values(std::move(src.Values)) {} // Move assignment operator KLDRArray& operator= (KLDRArray&& src) { if (this == &src) return *this; this->Keys = std::move(src.Keys); this->Lengths = std::move(src.Lengths); this->Values = std::move(src.Values); return *this; } // Copy operator (creating deep copy) KLDRArray (KLDRArray& src) : Keys(src.Keys), Lengths(src.Lengths) { size_t valSize = 0; for (size_t i = 0; i < src.Values.size(); i++) { valSize = src.Lengths[i]; this->Values.push_back(new char[valSize]); std::copy(src.Values[i], src.Values[i] + valSize, this->Values[i]); } } // Copy assignment operator (creating deep copy) KLDRArray& operator= (const KLDRArray& src) { if (this == &src) return *this; this->Keys = src.Keys; this->Lengths = src.Lengths; size_t valSize = 0; for (size_t i = 0; i < src.Values.size(); i++) { valSize = src.Lengths[i]; this->Values.push_back(new char[valSize]); std::copy(src.Values[i], src.Values[i] + valSize, this->Values[i]); } return *this; } // Write data to PREALLOCATED char array // WARNING: unsafe and probably UB (may be i test this later but who knows) void AsArrayF (char* arr) { char* ptr = arr; for (size_t i = 0; i < this->CellsAmount(); i++) { std::copy((char*)&this->Keys[i], (char*)(&this->Keys[i] + sizeof(KeyT)), ptr); ptr += sizeof(KeyT); std::copy((char*)&this->Lengths[i], (char*)(&this->Lengths[i] + sizeof(LengthT)), ptr); ptr += sizeof(LengthT); std::copy(this->Values[i], this->Values[i] + this->Lengths[i], ptr); ptr += this->Lengths[i]; } } // Return contents as char vector std::vector AsArray () const { std::vector result; for (size_t i = 0; i < this->CellsAmount(); i++) { for (uint j = 0; j < sizeof(KeyT); j++) result.push_back(((char*)(&this->Keys[i]))[j]); for (uint j = 0; j < sizeof(LengthT); j++) result.push_back(((char*)(&this->Lengths[i]))[j]); for (uint j = 0; j < this->Lengths[i]; j++) result.push_back(((char*)this->Values[i])[j]); } return result; } // Print all contents of array to stdout void Print () const { printf( "[KLDRArray] Object at: %lu; " "KeyT size: %lu; " "LengthT size: %lu; " "Elements amount: %lu\n", (size_t)this, sizeof(KeyT), sizeof(LengthT), this->CellsAmount() ); for (size_t i = 0; i < this->CellsAmount(); i++) { printf("\tKey: "); switch (this->Keys[i]) { case v1::KLDRDefaultKeys::Data: printf("Data"); break; case v1::KLDRDefaultKeys::ObjectID: printf("ObjectID"); break; case v1::KLDRDefaultKeys::SrcEventAuthor: printf("SrcEventAuthor"); break; case v1::KLDRDefaultKeys::PrevEvent: printf("PrevEvent"); break; case v1::KLDRDefaultKeys::NextEvent: printf("NextEvent"); break; case v1::KLDRDefaultKeys::BatchNumber: printf("BatchNumber"); break; case v1::KLDRDefaultKeys::Path: printf("Path"); break; case v1::KLDRDefaultKeys::Power: printf("Power"); break; case v1::KLDRDefaultKeys::ServerSession: printf("ServerSession"); break; case v1::KLDRDefaultKeys::CryptoAlgos: printf("CryptoAlgos"); break; case v1::KLDRDefaultKeys::CryptoKeyID: printf("CryptoKeyID"); break; case v1::KLDRDefaultKeys::SignedDataHash: printf("SignedDataHash"); break; default: printf("0x%.2X", (KeyT)this->Keys[i]); } printf("; Length: %u; ", (uint)this->Lengths[i]); printf("ValuePtr: %lu; Value: ", (uint64_t)this->Values[i]); for (LengthT j = 0; j < this->Lengths[i]; j++) printf("%.2X ", ((uint8_t*)this->Values[i])[j]); printf("\n"); } } // Get amount of cells in array inline size_t CellsAmount () const { return this->Keys.size(); } // Get size of all data in array as if they in packed condition size_t FlatSize () const { size_t result = this->Keys.size() * sizeof(KeyT) + this->Lengths.size() * sizeof(LengthT); for (size_t i = 0; i < this->CellsAmount(); i++) result += this->Lengths[i]; return result; } // If there are specified key bool KeyExists (KeyT key) const { for (size_t i = 0; i < this->CellsAmount(); i++) { if (this->Keys[i] == key) { return true; } } return false; } // Add new cell to array, fast version without key checks, data will be copied void AddF (KeyT key, LengthT length, char* data) { this->Keys.push_back(key); this->Lengths.push_back(length); char* newData = new char[length]; std::copy(data, data + length, newData); this->Values.push_back(newData); } // Add new cell to array, but only if key is unique void Add (KeyT key, LengthT length, char* data) { if (this->KeyExists(key)) throw std::invalid_argument("supplied key already exist"); this->AddF(key, length, data); } // Get just pointer to value from array by key char* Get (KeyT key) const { for (size_t i = 0; i < this->CellsAmount(); i++) { if (this->Keys[i] == key) { return this->Values[i]; } } throw std::invalid_argument("invalid KLDRArray key"); } // Get pointer to value and length from array by key char* Get (KeyT key, LengthT* length) const { for (size_t i = 0; i < this->CellsAmount(); i++) { if (this->Keys[i] == key) { *length = this->Lengths[i]; return this->Values[i]; } } throw std::invalid_argument("invalid KLDRArray key"); } // TODO: get copy of value // Delete cell from array by key void Del (KeyT key) { for (size_t i = 0; i < this->CellsAmount(); i++) { if (this->Keys[i] == key) { this->Keys.erase(this->Keys.begin() + i); this->Lengths.erase(this->Lengths.begin() + i); this->Values.erase(this->Values.begin() + i); return; } } throw std::invalid_argument("invalid KLDRArray key"); } // Shuffle array void Shuffle () { size_t elements = this->CellsAmount(); std::random_device seed; std::mt19937 gen{seed()}; std::uniform_int_distribution dist{0, elements-1}; size_t pickedIndex = 0; for (size_t i = 0; i < elements; i++) { do pickedIndex = dist(gen); while (pickedIndex == i); std::swap(this->Keys[i], this->Keys[pickedIndex]); std::swap(this->Lengths[i], this->Lengths[pickedIndex]); std::swap(this->Values[i], this->Values[pickedIndex]); } } }; } #endif