Skip to content

Commit

Permalink
Added SortBy method
Browse files Browse the repository at this point in the history
  • Loading branch information
mrognor committed Feb 6, 2024
1 parent 1f6dcc2 commit 06e7767
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 70 deletions.
5 changes: 3 additions & 2 deletions Docs/Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.

PROJECT_NAME = "MVault"
PROJECT_NAME = MVault

# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
Expand Down Expand Up @@ -950,7 +950,8 @@ WARN_LOGFILE =
# Note: If this tag is empty the current directory is searched.

INPUT = ../Source \
.
. \
../Examples/SortExample.cpp

# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
Expand Down
7 changes: 5 additions & 2 deletions Examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ endif()

set(CMAKE_CXX_STANDARD 11)

project(Example1)
project(Examples)

include_directories(../Source)

add_executable(Example1 Example1.cpp)
target_link_libraries(Example1 MVault)
target_link_libraries(Example1 MVault)

add_executable(SortExample SortExample.cpp)
target_link_libraries(SortExample MVault)
35 changes: 35 additions & 0 deletions Examples/SortExample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "../Source/Vault.h"

int main()
{
mvlt::Vault vlt;

vlt.SetKey("id", -1);
vlt.SetKey("rid", -1);

for (int i = 0; i < 20; ++i)
{
vlt.CreateRecord({ {"id", i}, {"rid", 20 - i} });
}

vlt.PrintAsTable();

std::cout << "The first 5 entries after sorting in ascending order by id" << std::endl;
std::vector<mvlt::VaultRecordRef> sorted = vlt.GetSortedRecords("id", false, 5);
for (const auto& it : sorted)
{
it.ReadLock();
it.PrintRecord();
it.ReadUnlock();
}

std::cout << "The first 5 entries after sorting in descending order by id" << std::endl;
sorted = vlt.GetSortedRecords("id", true, 5);
for (const auto& it : sorted)
it.PrintRecord();

std::cout << "The first 5 entries after sorting in ascending order by rid" << std::endl;
sorted = vlt.GetSortedRecords("rid", false, 5);
for (const auto& it : sorted)
it.PrintRecord();
}
39 changes: 4 additions & 35 deletions Source/ReadWriteMutex.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,41 +57,6 @@ class ReadWriteMutex
The read lock will ensure that no thread using the write lock gets into the code section until all threads using the read lock are unblocked.
At the same time, after the write lock, no new threads with a read lock will enter the code section until all threads using the write lock are unblocked.
Recursiveness allows you to call blocking methods in the same thread multiple times without self-locking.
At the same time, it is important to follow the blocking procedure. 3 situations are allowed:
1. ReadLock -> ReadLock -> ReadUnlock -> ReadUnlock
\code
RecursiveReadWriteMutex rrwx;
rrwx.ReadLock();
rrwx.ReadLock();
rrwx.ReadUnlock();
rrwx.ReadUnlock();
\endcode
2. WriteLock -> WriteLock -> WriteUnlock -> WriteUnlock
\code
RecursiveReadWriteMutex rrwx;
rrwx.WriteLock();
rrwx.WriteLock();
rrwx.WriteUnlock();
rrwx.WriteUnlock();
\endcode
3. WriteLock -> ReadLock -> ReadUnlock -> WriteUnlock
\code
RecursiveReadWriteMutex rrwx;
rrwx.WriteLock();
rrwx.ReadLock();
rrwx.ReadUnlock();
rrwx.WriteUnlock();
\endcode
Situation: ReadLock -> WriteLock -> WriteUnlock -> ReadUnlock
\code
RecursiveReadWriteMutex rrwx;
rrwx.ReadLock();
rrwx.WriteLock();
rrwx.WriteUnlock();
rrwx.ReadUnlock();
\endcode
It is prohibited because it violates the logic of read and write operations and leads to deadlocking
*/
class RecursiveReadWriteMutex
{
Expand All @@ -104,6 +69,8 @@ class RecursiveReadWriteMutex
Using this method, you can lock the code section for reading, which means that all threads using the read lock will have access to data inside the code section
but threads using the write lock will wait until all read operations are completed.
Note that, in fact, blocking for reading inside writing does not make sense,
since the code section is already locked and therefore nothing will happen inside the function in such a situation.
*/
void ReadLock();

Expand All @@ -116,9 +83,11 @@ class RecursiveReadWriteMutex
This method provides exclusive access to a section of code for a single thread.
All write operations will be performed sequentially.
This method takes precedence over the read lock, which means that after calling this method, no new read operations will be started.
Note that if the write lock is called inside the read lock, then this will be equivalent to unlocking for reading and then locking for writing.
*/
void WriteLock();

/// \brief A method for unlocking a section of code for writing
/// Note that if the write unlock is called inside the read lock, then this will be equivalent to unlocking for writing and then locking for reading.
void WriteUnlock();
};
4 changes: 2 additions & 2 deletions Source/ToString.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace mvlt
For example, like this
\code
\code{.cpp}
template <>
std::string mvlt::ToString(const std::vector<int>& data)
{
Expand All @@ -41,7 +41,7 @@ namespace mvlt
By calling this function:
\code
\code{.cpp}
std::vector<int> vec = {1, 2, 3, 4, 5};
std::cout << ToString(vec) << std::endl;
\endcode
Expand Down
26 changes: 25 additions & 1 deletion Source/Vault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ namespace mvlt
VaultRecordAdders.erase(keyName);
VaultRecordClearers.erase(keyName);
VaultRecordErasers.erase(keyName);
VaultRecordSorters.erase(keyName);

// Erase key data from all records
for (auto& it : RecordsSet)
Expand Down Expand Up @@ -115,6 +116,7 @@ namespace mvlt
VaultRecordAdders.clear();
VaultRecordClearers.clear();
VaultRecordErasers.clear();
VaultRecordSorters.clear();

// Delete all Records
for (auto& it : RecordsSet)
Expand Down Expand Up @@ -186,6 +188,28 @@ namespace mvlt
return res;
}

std::vector<VaultRecordRef> Vault::GetSortedRecords(const std::string& keyName, const bool& isReverse, const std::size_t& amountOfRecords) const
{
std::vector<VaultRecordRef> res;
std::size_t counter = 0;

RecursiveReadWriteMtx.ReadLock();

/// \todo Проверка
VaultRecordSorters.find(keyName)->second([&](const VaultRecordRef& vaultRecordRef)
{
if (counter >= amountOfRecords) return false;

res.emplace_back(vaultRecordRef);
++counter;
return true;
}, isReverse);

RecursiveReadWriteMtx.ReadUnlock();

return res;
}

void Vault::PrintVault(const std::size_t amountOfRecords) const
{
RecursiveReadWriteMtx.ReadLock();
Expand All @@ -195,7 +219,7 @@ namespace mvlt

for (const auto& record : RecordsSet)
{
std::cout << "Data storage record " << record << ":" << std::endl;
std::cout << "Vault record " << record << ":" << std::endl;

for (const std::string& key : keys)
{
Expand Down
54 changes: 44 additions & 10 deletions Source/Vault.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,23 @@ namespace mvlt
*/
mutable VaultStructureMap VaultMapStructure;

// unordered_map of functions that add a new element to the VaultStructureHashMap
// Unordered_map of functions that add a new element to the VaultStructureHashMap
std::unordered_map<std::string, std::function<void(VaultRecord*)>> VaultRecordAdders;

// unordered_map of functions that delete all data from the unordered_map's stored in the VaultRecordAdders
// Unordered_map of functions that delete all data from the unordered_map's
std::unordered_map<std::string, std::function<void()>> VaultRecordClearers;

// unordered_map of functions that erase record from the unordered_map's stored in the VaultRecordAdders
// Unordered_map of functions that erase record from the unordered_map's
std::unordered_map<std::string, std::function<void(VaultRecord* newRecord)>> VaultRecordErasers;

// Unordered_map of functions for getting sorted data.
// The key is a string with the name of the key from the Vault.
// The std::function is used as the value, in which the lambda function is written at the time of adding the key.
// Lambda accepts a function that is called for each entry inside. VaultRecordRef is passed to her.
// By default, iteration by records occurs in ascending order.
// isReverse parameter is used for the reverse order.
std::unordered_map<std::string, std::function<void( std::function<bool(const VaultRecordRef&)> functionToSortedData, bool isReverse )>> VaultRecordSorters;

// Unordered set with all VaultRecord pointers
std::unordered_set<VaultRecord*> RecordsSet;

Expand Down Expand Up @@ -173,6 +181,22 @@ namespace mvlt
}
);

VaultRecordSorters.emplace(keyName, [=](std::function<bool(const VaultRecordRef&)> functionToSortedData, bool isReverse)
{
if (!isReverse)
{
for (const auto& it : *TtoVaultRecordMap)
if(!functionToSortedData(VaultRecordRef(it.second, &VaultHashMapStructure, &VaultMapStructure, &RecursiveReadWriteMtx)))
break;
}
else
{
for (auto it = TtoVaultRecordMap->rbegin(); it != TtoVaultRecordMap->rend(); ++it)
if(!functionToSortedData(VaultRecordRef(it->second, &VaultHashMapStructure, &VaultMapStructure, &RecursiveReadWriteMtx)))
break;
}
});

// Add new data to record set
for (auto& it : RecordsSet)
{
Expand All @@ -199,7 +223,7 @@ namespace mvlt
\tparam <T> Any type of data except for c arrays
\param [in] keyName the name of the key to search for
\param [in] keyValue the value of the key
\param [in] defaultKeyValue the value of the key
\return returns true if the key was found otherwise returns false
*/
Expand Down Expand Up @@ -232,36 +256,36 @@ namespace mvlt
The Vault in the example has 2 keys. One is the id of the int type, and the second is the name of the std::string type
Usage example:
\code
\code{.cpp}
// A record with id 0 and name mrognor will be created
vlt.CreateRecord({ {"id", 0}, {"name", std::string("mrognor")} });
\endcode
or
\code
\code{.cpp}
// A record with id 0 and name mrognor will be created
vlt.CreateRecord({ {"name", std::string("mrognor")}, {"id", 0} });
\endcode
or
\code
\code{.cpp}
// A record with name mrognor will be created. The Id will be set to the default value
vlt.CreateRecord({ {"name", std::string("mrognor")} });
\endcode
or
\code
\code{.cpp}
// A record with id 0 and name mrognor will be created
std::vector<std::pair<std::string, DataSaver>> params = { {"id", 0}, {"name", std::string("mrognor")} };
VaultRecordRef vltrr = vlt.CreateRecord(params);
\endcode
what is equivalent to such a code without passing values to a function
\code
\code{.cpp}
VaultRecordRef vltrr = vlt.CreateRecord();
vltrr.SetData("id", 0);
Expand All @@ -281,7 +305,6 @@ namespace mvlt
\param [in] keyName the name of the key to search for
\param [in] keyValue the value of the key to be found
\param [out] foundedRecord a ref to the VaultRecordRef where found record will be placed
\return ref to requested record
*/
Expand Down Expand Up @@ -329,6 +352,17 @@ namespace mvlt
/// \return vector with keys
std::vector<std::string> GetKeys() const;

/**
\brief Method for getting sorted records
\param[in] keyName The key by which the data should be sorted
\param[in] isReverse Sort in descending order or descending order. By default, ascending
\param[in] amountOfRecords The number of records. By default, everything is
\return A vector with links to records. The order of entries in the vector is determined by the amountOfRecords parameter
*/
std::vector<VaultRecordRef> GetSortedRecords(const std::string& keyName, const bool& isReverse = false, const std::size_t& amountOfRecords = -1) const;

/// \brief A method for displaying the contents of a Vault on the screen
void PrintVault(const std::size_t amountOfRecords = 0) const;

Expand Down
8 changes: 4 additions & 4 deletions Source/VaultRecordRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,14 @@ namespace mvlt
VaultMapStructure = nullptr;
}

void VaultRecordRef::WriteLock()
void VaultRecordRef::ReadLock() const
{
VaultRecucrsiveReadWriteMtx->WriteLock();
VaultRecucrsiveReadWriteMtx->ReadLock();
}

void VaultRecordRef::WriteUnlock()
void VaultRecordRef::ReadUnlock() const
{
VaultRecucrsiveReadWriteMtx->WriteUnlock();
VaultRecucrsiveReadWriteMtx->ReadUnlock();
}

VaultRecordRef::~VaultRecordRef()
Expand Down
Loading

0 comments on commit 06e7767

Please sign in to comment.