Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a8cd72ed17 | |||
| adf74e2427 | |||
| 603ed9c60e | |||
| 43c337d46a |
+1
@@ -22,6 +22,7 @@ struct SerializedNotification
|
||||
char title[128];
|
||||
char message[1024];
|
||||
util::Timestamp createdAt;
|
||||
util::State state;
|
||||
};
|
||||
|
||||
struct SerializedService
|
||||
|
||||
+330
@@ -0,0 +1,330 @@
|
||||
#include "SharedMemory.h"
|
||||
#include "Config.h"
|
||||
|
||||
/*
|
||||
Function: invalidateMapping
|
||||
Description: Releases all mapping resources and resets the mapping to an invalid state.
|
||||
Parameters:
|
||||
- mapping: MappingInfo&, mapping information and handles
|
||||
Returns:
|
||||
- None
|
||||
*/
|
||||
static void invalidateMapping(MappingInfo& mapping)
|
||||
{
|
||||
if (mapping.mappedView != nullptr)
|
||||
{
|
||||
UnmapViewOfFile(mapping.mappedView);
|
||||
mapping.mappedView = nullptr;
|
||||
}
|
||||
if (mapping.mappingHandle != NULL)
|
||||
{
|
||||
CloseHandle(mapping.mappingHandle);
|
||||
mapping.mappingHandle = NULL;
|
||||
}
|
||||
if (mapping.fileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(mapping.fileHandle);
|
||||
mapping.fileHandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
mapping.mappedCapacity = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Function: createOrOpenMapping
|
||||
Description: Creates or opens a file mapping and maps it into the process address space.
|
||||
Parameters:
|
||||
- mapping: MappingInfo&, mapping information and handles
|
||||
Returns:
|
||||
- bool: True if the mapping was successfully created/opened, otherwise false
|
||||
*/
|
||||
bool SharedMemory::createOrOpenMapping(MappingInfo& mapping)
|
||||
{
|
||||
if (mapping.recordSize == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
mapping.fileHandle =
|
||||
CreateFileA(
|
||||
mapping.fileName.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
if (mapping.fileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LARGE_INTEGER fileSize;
|
||||
if (!GetFileSizeEx(mapping.fileHandle, &fileSize))
|
||||
{
|
||||
invalidateMapping(mapping);
|
||||
return false;
|
||||
}
|
||||
bool isNewFile = (fileSize.QuadPart == 0);
|
||||
const size_t initialCapacity = config::file::INITIAL_CAPACITY;
|
||||
if (isNewFile)
|
||||
{
|
||||
LARGE_INTEGER newSize;
|
||||
newSize.QuadPart = sizeof(FileHeader) + initialCapacity * mapping.recordSize;
|
||||
if (!SetFilePointerEx(mapping.fileHandle, newSize, NULL, FILE_BEGIN))
|
||||
{
|
||||
invalidateMapping(mapping);
|
||||
return false;
|
||||
}
|
||||
if (!SetEndOfFile(mapping.fileHandle))
|
||||
{
|
||||
invalidateMapping(mapping);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
mapping.mappingHandle =
|
||||
CreateFileMappingA(
|
||||
mapping.fileHandle,
|
||||
NULL,
|
||||
PAGE_READWRITE,
|
||||
0,
|
||||
0,
|
||||
NULL);
|
||||
if (mapping.mappingHandle == NULL)
|
||||
{
|
||||
invalidateMapping(mapping);
|
||||
return false;
|
||||
}
|
||||
mapping.mappedView =
|
||||
MapViewOfFile(
|
||||
mapping.mappingHandle,
|
||||
FILE_MAP_ALL_ACCESS,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
if (mapping.mappedView == nullptr)
|
||||
{
|
||||
invalidateMapping(mapping);
|
||||
return false;
|
||||
}
|
||||
FileHeader* header = getHeader(mapping);
|
||||
if (header == nullptr)
|
||||
{
|
||||
invalidateMapping(mapping);
|
||||
return false;
|
||||
}
|
||||
if (isNewFile)
|
||||
{
|
||||
header->recordCount = 0;
|
||||
header->capacity = initialCapacity;
|
||||
}
|
||||
mapping.mappedCapacity = header->capacity;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Function: closeMapping
|
||||
Description: Unmaps and closes all resources associated with a file mapping.
|
||||
Parameters:
|
||||
- mapping: MappingInfo&, mapping to close
|
||||
Returns:
|
||||
- None
|
||||
*/
|
||||
void SharedMemory::closeMapping(MappingInfo& mapping)
|
||||
{
|
||||
invalidateMapping(mapping);
|
||||
}
|
||||
|
||||
/*
|
||||
Function: getHeader
|
||||
Description: Returns the file header stored at the beginning of the mapped memory region.
|
||||
Parameters:
|
||||
- mapping: MappingInfo&, mapping information and handles
|
||||
Returns:
|
||||
- FileHeader*: Pointer to the file header, or nullptr if the mapping is not valid
|
||||
*/
|
||||
FileHeader* SharedMemory::getHeader(MappingInfo& mapping)
|
||||
{
|
||||
if (mapping.mappedView == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<FileHeader*>(mapping.mappedView);
|
||||
}
|
||||
|
||||
/*
|
||||
Function: getRecordAddress
|
||||
Description: Returns the address of a record at the specified index within the mapped memory region.
|
||||
Parameters:
|
||||
- mapping: MappingInfo&, mapping information and handles
|
||||
- index: size_t, record index
|
||||
Returns:
|
||||
- void*: Pointer to the record, or nullptr if the mapping is not valid
|
||||
*/
|
||||
void* SharedMemory::getRecordAddress(MappingInfo& mapping, size_t index)
|
||||
{
|
||||
if (mapping.mappedView == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<char*>(mapping.mappedView) + sizeof(FileHeader) + index * mapping.recordSize;
|
||||
}
|
||||
|
||||
/*
|
||||
Function: getRecordAddress
|
||||
Description: Returns the address of a record at the specified index within the mapped memory region.
|
||||
Parameters:
|
||||
- mapping: MappingInfo&, mapping information and handles
|
||||
- index: size_t, record index
|
||||
Returns:
|
||||
- void*: Pointer to the record, or nullptr if the mapping is not valid
|
||||
*/
|
||||
void* SharedMemory::getRecordAddress(MappingInfo& mapping, size_t index)
|
||||
{
|
||||
if (mapping.mappedView == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<char*>(mapping.mappedView) + sizeof(FileHeader) + index * mapping.recordSize;
|
||||
}
|
||||
|
||||
/*
|
||||
Function: getRecordCount
|
||||
Description: Returns the number of records currently stored in the mapping.
|
||||
Parameters:
|
||||
- mapping: MappingInfo&, mapping information and handles
|
||||
Returns:
|
||||
- size_t: Number of records in the mapping
|
||||
*/
|
||||
size_t SharedMemory::getRecordCount(MappingInfo& mapping)
|
||||
{
|
||||
FileHeader* header = getHeader(mapping);
|
||||
if (header == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return header->recordCount;
|
||||
}
|
||||
|
||||
/*
|
||||
Function: setRecordCount
|
||||
Description: Updates the number of records stored in the mapping.
|
||||
Parameters:
|
||||
- mapping: MappingInfo&, mapping information and handles
|
||||
- count: size_t, new record count
|
||||
Returns:
|
||||
- None
|
||||
*/
|
||||
void SharedMemory::setRecordCount(MappingInfo& mapping, size_t count)
|
||||
{
|
||||
FileHeader* header = getHeader(mapping);
|
||||
if (header == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
header->recordCount = count;
|
||||
}
|
||||
|
||||
/*
|
||||
Function: getCapacity
|
||||
Description: Returns the current capacity of the mapping.
|
||||
Parameters:
|
||||
- mapping: MappingInfo&, mapping information and handles
|
||||
Returns:
|
||||
- size_t: Maximum number of records that can be stored in the mapping
|
||||
*/
|
||||
size_t SharedMemory::getCapacity(MappingInfo& mapping)
|
||||
{
|
||||
FileHeader* header = getHeader(mapping);
|
||||
if (header == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return header->capacity;
|
||||
}
|
||||
|
||||
/*
|
||||
Function: resizeMapping
|
||||
Description: Resizes the file mapping to the specified capacity.
|
||||
Parameters:
|
||||
- mapping: MappingInfo&, mapping information and handles
|
||||
- newCapacity: size_t, new mapping capacity
|
||||
Returns:
|
||||
- bool: True if the resize succeeded, otherwise false
|
||||
*/
|
||||
bool SharedMemory::resizeMapping(MappingInfo& mapping, size_t newCapacity)
|
||||
{
|
||||
FileHeader* header = getHeader(mapping);
|
||||
if (header == nullptr)
|
||||
{
|
||||
invalidateMapping(mapping);
|
||||
return false;
|
||||
}
|
||||
header->capacity = newCapacity;
|
||||
if (!FlushViewOfFile(mapping.mappedView, sizeof(FileHeader)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!UnmapViewOfFile(mapping.mappedView))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
mapping.mappedView = nullptr;
|
||||
CloseHandle(mapping.mappingHandle);
|
||||
mapping.mappingHandle = NULL;
|
||||
LARGE_INTEGER newSize;
|
||||
newSize.QuadPart = sizeof(FileHeader) + newCapacity * mapping.recordSize;
|
||||
if (!SetFilePointerEx(mapping.fileHandle, newSize, NULL, FILE_BEGIN))
|
||||
{
|
||||
invalidateMapping(mapping);
|
||||
return false;
|
||||
}
|
||||
if (!SetEndOfFile(mapping.fileHandle))
|
||||
{
|
||||
invalidateMapping(mapping);
|
||||
return false;
|
||||
}
|
||||
mapping.mappingHandle =
|
||||
CreateFileMappingA(
|
||||
mapping.fileHandle,
|
||||
NULL,
|
||||
PAGE_READWRITE,
|
||||
0,
|
||||
0,
|
||||
NULL);
|
||||
if (mapping.mappingHandle == NULL)
|
||||
{
|
||||
invalidateMapping(mapping);
|
||||
return false;
|
||||
}
|
||||
mapping.mappedView =
|
||||
MapViewOfFile(
|
||||
mapping.mappingHandle,
|
||||
FILE_MAP_ALL_ACCESS,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
if (mapping.mappedView == nullptr)
|
||||
{
|
||||
invalidateMapping(mapping);
|
||||
return false;
|
||||
}
|
||||
mapping.mappedCapacity = newCapacity;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Function: ensureCapacityForInsert
|
||||
Description: Ensures that the mapping has space for at least one additional record, growing it if necessary.
|
||||
Parameters:
|
||||
- mapping: MappingInfo&, mapping information and handles
|
||||
Returns:
|
||||
- bool: True if capacity is available, otherwise false
|
||||
*/
|
||||
bool SharedMemory::ensureCapacityForInsert(MappingInfo& mapping)
|
||||
{
|
||||
size_t recordCount = getRecordCount(mapping);
|
||||
size_t capacity = getCapacity(mapping);
|
||||
if (recordCount < capacity)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return resizeMapping(mapping, capacity * 2);
|
||||
}
|
||||
-1
@@ -15,5 +15,4 @@ namespace SharedMemory
|
||||
void setRecordCount(MappingInfo& mapping, size_t count);
|
||||
size_t getCapacity(MappingInfo& mapping);
|
||||
bool ensureCapacityForInsert(MappingInfo& mapping);
|
||||
bool shrinkIfNeeded(MappingInfo& mapping);
|
||||
};
|
||||
@@ -24,7 +24,7 @@ Returns:
|
||||
*/
|
||||
Notification::Notification()
|
||||
: m_id("NOT" + std::to_string(++m_uid)),
|
||||
m_recipient(nullptr) {}
|
||||
m_recipient(nullptr), m_state(util::State::ACTIVE) {}
|
||||
|
||||
/*
|
||||
Function: Notification
|
||||
@@ -44,6 +44,7 @@ Notification::Notification(const std::string& recipientUserId, User* recipient,
|
||||
m_recipient(recipient),
|
||||
m_title(title),
|
||||
m_message(message),
|
||||
m_state(util::State::ACTIVE),
|
||||
m_createdAt(createdAt){}
|
||||
|
||||
/*
|
||||
@@ -59,13 +60,14 @@ Parameters:
|
||||
Returns:
|
||||
- A new Notification object
|
||||
*/
|
||||
Notification::Notification(const std::string& id, const std::string& recipientUserId, const std::string& title, const std::string& message, const util::Timestamp& createdAt)
|
||||
Notification::Notification(const std::string& id, const std::string& recipientUserId, const std::string& title, const std::string& message, const util::Timestamp& createdAt, const util::State& state)
|
||||
: m_id(id),
|
||||
m_recipientUserId(recipientUserId),
|
||||
m_recipient(nullptr),
|
||||
m_title(title),
|
||||
m_message(message),
|
||||
m_createdAt(createdAt)
|
||||
m_createdAt(createdAt),
|
||||
m_state(state)
|
||||
{
|
||||
int idNumber = util::extractNumber(m_id);
|
||||
if (idNumber > m_uid)
|
||||
@@ -234,6 +236,7 @@ SerializedNotification Notification::serialize() const
|
||||
strcpy_s(serialized.title, sizeof(serialized.title), m_title.c_str());
|
||||
strcpy_s(serialized.message, sizeof(serialized.message), m_message.c_str());
|
||||
serialized.createdAt = m_createdAt;
|
||||
serialized.state = m_state;
|
||||
return serialized;
|
||||
}
|
||||
|
||||
@@ -252,5 +255,6 @@ Notification* Notification::deserialize(const SerializedNotification& serialized
|
||||
serializedNotification.recipientUserId,
|
||||
serializedNotification.title,
|
||||
serializedNotification.message,
|
||||
serializedNotification.createdAt);
|
||||
serializedNotification.createdAt,
|
||||
serializedNotification.state);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ Date: 19-May-2026
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "Timestamp.h"
|
||||
#include "Enums.h"
|
||||
|
||||
class User;
|
||||
struct SerializedNotification;
|
||||
@@ -23,10 +24,11 @@ private:
|
||||
std::string m_title;
|
||||
std::string m_message;
|
||||
util::Timestamp m_createdAt;
|
||||
util::State m_state;
|
||||
public:
|
||||
Notification();
|
||||
Notification(const std::string& recipientUserId, User* recipient, const std::string& title, const std::string& message, const util::Timestamp& createdAt);
|
||||
Notification(const std::string& id, const std::string& recipientUserId, const std::string& title, const std::string& message, const util::Timestamp& createdAt);
|
||||
Notification(const std::string& id, const std::string& recipientUserId, const std::string& title, const std::string& message, const util::Timestamp& createdAt, const util::State& state);
|
||||
const std::string& getId() const;
|
||||
const std::string& getRecipientUserId() const;
|
||||
User* getRecipient() const;
|
||||
@@ -39,6 +41,8 @@ public:
|
||||
void setTitle(const std::string& title);
|
||||
void setMessage(const std::string& message);
|
||||
void setCreatedAt(const util::Timestamp& createdAt);
|
||||
util::State getState();
|
||||
void setState(util::State state);
|
||||
SerializedNotification serialize() const;
|
||||
static Notification* deserialize(const SerializedNotification&);
|
||||
};
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace config
|
||||
|
||||
namespace file
|
||||
{
|
||||
const size_t INITIAL_CAPACITY = 100;
|
||||
constexpr const char* INVENTORYITEM_FILE = "files/InventoryItem.csv";
|
||||
constexpr const char* USER_FILE = "files/User.csv";
|
||||
constexpr const char* NOTIFICATION_FILE = "files/Notification.csv";
|
||||
|
||||
Reference in New Issue
Block a user