diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fd8d46..d731dda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,4 +17,5 @@ add_subdirectory(Math) add_subdirectory(Allocators) add_subdirectory(Strings) add_subdirectory(Tokenizer) -add_subdirectory(CommandLine) \ No newline at end of file +add_subdirectory(CommandLine) +add_subdirectory(Storage) diff --git a/Storage/CMakeLists.txt b/Storage/CMakeLists.txt new file mode 100644 index 0000000..28e593f --- /dev/null +++ b/Storage/CMakeLists.txt @@ -0,0 +1,28 @@ + +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_CXX_STANDARD 23) + +project(Storage) + +### ---------------------- Static Library --------------------- ### +file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp") +file(GLOB HEADERS "./public/*.hpp") +add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS}) +target_include_directories(${PROJECT_NAME} PUBLIC ./public/) +target_link_libraries(${PROJECT_NAME} PUBLIC Strings) + +### ---------------------- Applications --------------------- ### +add_executable(Server ./applications/Server.cpp) +add_executable(Client ./applications/Client.cpp) +target_link_libraries(Server PUBLIC ${PROJECT_NAME}) +target_link_libraries(Client PUBLIC ${PROJECT_NAME}) + +### -------------------------- Tests -------------------------- ### +enable_testing() +file(GLOB TEST_SOURCES "./tests/*.cpp") +add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES}) +target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils) +add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests) + +install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib) \ No newline at end of file diff --git a/Storage/applications/Client.cpp b/Storage/applications/Client.cpp new file mode 100644 index 0000000..3f7a622 --- /dev/null +++ b/Storage/applications/Client.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include + +constexpr int PORT = 8080; +constexpr int BUFFER_SIZE = 1024; +const char* SERVER_IP = "127.0.0.1"; + +int main() { + // Create socket + int clientSocket = socket(AF_INET, SOCK_STREAM, 0); + if (clientSocket == -1) { + std::cerr << "Failed to create socket" << std::endl; + return 1; + } + + // Connect to server + sockaddr_in serverAddress{}; + serverAddress.sin_family = AF_INET; + serverAddress.sin_port = htons(PORT); + if (inet_pton(AF_INET, SERVER_IP, &serverAddress.sin_addr) <= 0) { + std::cerr << "Invalid address or address not supported" << std::endl; + return 1; + } + + if (connect(clientSocket, reinterpret_cast(&serverAddress), sizeof(serverAddress)) == -1) { + std::cerr << "Failed to connect to server" << std::endl; + return 1; + } + + // Send message to server + const char* message = "Hello, server!"; + if (write(clientSocket, message, strlen(message)) == -1) { + std::cerr << "Failed to write to socket" << std::endl; + return 1; + } + + // Close socket + close(clientSocket); + + return 0; +} diff --git a/Storage/applications/Server.cpp b/Storage/applications/Server.cpp new file mode 100644 index 0000000..7f508be --- /dev/null +++ b/Storage/applications/Server.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include + + +class Server { +public: + Server(int port) : serverSocket(-1), port(port) {} + + bool start() { + // Create socket + serverSocket = socket(AF_INET, SOCK_STREAM, 0); + if (serverSocket == -1) { + std::cerr << "Failed to create socket" << std::endl; + return false; + } + + // Bind socket to port + sockaddr_in serverAddress{}; + serverAddress.sin_family = AF_INET; + serverAddress.sin_port = htons(port); + serverAddress.sin_addr.s_addr = INADDR_ANY; + if (bind(serverSocket, reinterpret_cast(&serverAddress), sizeof(serverAddress)) == -1) { + std::cerr << "Failed to bind socket" << std::endl; + return false; + } + + // Listen for connections + if (listen(serverSocket, 5) == -1) { + std::cerr << "Failed to listen on socket" << std::endl; + return false; + } + + std::cout << "Server listening on port " << port << std::endl; + + // Start accepting clients + while (true) { + // Accept client connection + sockaddr_in clientAddress{}; + socklen_t clientAddressSize = sizeof(clientAddress); + int clientSocket = accept(serverSocket, reinterpret_cast(&clientAddress), &clientAddressSize); + if (clientSocket == -1) { + std::cerr << "Failed to accept client connection" << std::endl; + return false; + } + + // Create a new thread to handle the client + pthread_t threadId; + if (pthread_create(&threadId, nullptr, handleClient, &clientSocket) != 0) { + std::cerr << "Failed to create thread for client" << std::endl; + return false; + } + + // Detach the thread so it can run independently + pthread_detach(threadId); + } + + // Close server socket + close(serverSocket); + + return true; + } + +private: + int serverSocket; + int port; + + static void* handleClient(void* clientSocketPtr) { + int clientSocket = *(reinterpret_cast(clientSocketPtr)); + constexpr int BUFFER_SIZE = 1024; + + // Receive and print client message + char buffer[BUFFER_SIZE]; + ssize_t bytesRead = read(clientSocket, buffer, BUFFER_SIZE - 1); + if (bytesRead == -1) { + std::cerr << "Failed to read from socket" << std::endl; + return nullptr; + } + + buffer[bytesRead] = '\0'; + std::cout << "Received message from client: " << buffer << std::endl; + + // Close socket + close(clientSocket); + + return nullptr; + } +}; + +int main() { + Server server(8080); + server.start(); + + return 0; +} diff --git a/Storage/private/LocalStorage.cpp b/Storage/private/LocalStorage.cpp new file mode 100644 index 0000000..9e33fe5 --- /dev/null +++ b/Storage/private/LocalStorage.cpp @@ -0,0 +1,76 @@ +#include "LocalStorage.hpp" + +#include "SystemHandle.hpp" + +using namespace tp; + +bool File::connect(const FileLocation& location, const FileConnectionType& connectionInfo) { + DEBUG_ASSERT(!mStatus.isOpened()); + if (mStatus.isOpened()) return false; + + auto handle = new FileSystemHandle(); + + switch (connectionInfo.getType()) { + case FileConnectionType::READ: + handle->open(location.getLocation().read(), true); + break; + + case FileConnectionType::WRITE: + handle->open(location.getLocation().read(), false); + break; + + default: + break; + }; + + if (!handle->isOpen()) { + mStatus.setStatus(FileConnectionStatus::DENIED); + delete handle; + return false; + } + + mStatus.setStatus(FileConnectionStatus::OPENED); + mHandle = handle; + return true; +} + +bool File::disconnect() { + DEBUG_ASSERT(mStatus.isOpened()); + if (!mStatus.isOpened() || !mHandle) return false; + mHandle->close(); + delete mHandle; + mStatus.setStatus(FileConnectionStatus::CLOSED); + return true; +} + +bool File::setPointer(BytePointer pointer) { + DEBUG_ASSERT(mStatus.isOpened()); + if (!mStatus.isOpened()) return false; + return true; +} + +bool File::readBytes(Byte* bytes, SizeBytes size) { + DEBUG_ASSERT(mStatus.isOpened() && mConnectionType.isWrite()); + if (!mStatus.isOpened() || !mConnectionType.isWrite()) return false; + + mHandle->seekp(mPointer); + mHandle->read(bytes, size); + mPointer += size; + return true; +} + +bool File::writeBytes(const Byte* bytes, SizeBytes size) { + DEBUG_ASSERT(mStatus.isOpened() && mConnectionType.isRead()); + if (!mStatus.isOpened() || !mConnectionType.isRead()) return false; + + mHandle->seekp(mPointer); + mHandle->write(bytes, size); + mPointer += size; + return true; +} + +File::SizeBytes File::size() { + DEBUG_ASSERT(mStatus.isOpened()); + if (!mStatus.isOpened() || !mHandle) return 0; + return mHandle->size(); +} \ No newline at end of file diff --git a/Storage/private/Storage.cpp b/Storage/private/Storage.cpp new file mode 100644 index 0000000..197a710 --- /dev/null +++ b/Storage/private/Storage.cpp @@ -0,0 +1,9 @@ + +#include "Storage.hpp" + +static tp::ModuleManifest* sModuleDependencies[] = { + &tp::gModuleStrings, + nullptr +}; + +tp::ModuleManifest tp::gModuleStorage = ModuleManifest("Storage", nullptr, nullptr, sModuleDependencies); \ No newline at end of file diff --git a/Storage/private/SystemHandle.cpp b/Storage/private/SystemHandle.cpp new file mode 100644 index 0000000..1386b2a --- /dev/null +++ b/Storage/private/SystemHandle.cpp @@ -0,0 +1,57 @@ +#include "SystemHandle.hpp" + +#include + +using namespace tp; + +ualni tp::FileSystemHandle::size() { + auto strm = (std::fstream*) stream; + ualni out = 0; + strm->seekg(0, std::ios::beg); + out = strm->tellg(); + strm->seekg(0, std::ios::end); + out = (ualni) strm->tellg() - out; + return out; +} + +FileSystemHandle::FileSystemHandle() { + stream = new std::fstream(); +} + +FileSystemHandle::~FileSystemHandle() { + auto strm = (std::fstream*) stream; + delete strm; +} + +bool FileSystemHandle::isOpen() { + auto strm = (std::fstream*) stream; + return strm->is_open(); +} + +void FileSystemHandle::close() { + auto strm = (std::fstream*) stream; + strm->close(); +} + +void FileSystemHandle::seekp(ualni in) { + auto strm = (std::fstream*) stream; + strm->seekp(in); +} + +void FileSystemHandle::read(int1* in, ualni size) { + auto strm = (std::fstream*) stream; + strm->write(in, size); +} + +void FileSystemHandle::write(const int1* in, ualni size) { + auto strm = (std::fstream*) stream; + strm->write(in, size); +} + +void FileSystemHandle::open(const char* path, bool read) { + auto strm = (std::fstream*) stream; + if (read) + strm->open(path, std::ios::in | std::ios::binary | std::ios::app); + else + strm->open(path, std::ios::out | std::ios::binary | std::ios::trunc); +} diff --git a/Storage/private/SystemHandle.hpp b/Storage/private/SystemHandle.hpp new file mode 100644 index 0000000..17d85c7 --- /dev/null +++ b/Storage/private/SystemHandle.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "Common.hpp" + +namespace tp { + class FileSystemHandle { + void* stream; + public: + FileSystemHandle(); + ~FileSystemHandle(); + void open(const char*, bool); + bool isOpen(); + void close(); + void seekp(ualni); + void read(int1*, ualni); + void write(const int1*, ualni); + ualni size(); + }; +} \ No newline at end of file diff --git a/Storage/public/LocalStorage.hpp b/Storage/public/LocalStorage.hpp new file mode 100644 index 0000000..e832db0 --- /dev/null +++ b/Storage/public/LocalStorage.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include "Storage.hpp" + +namespace tp { + + class FileSystemHandle; + + class FileLocation { + String mLocation; + public: + FileLocation() : mLocation("tmp") {}; + explicit FileLocation(const String& location) : mLocation(location) {} + void setLocation(const String& location) { mLocation = location; } + [[nodiscard]] const String& getLocation() const { return mLocation; } + }; + + class FileConnectionType { + public: + enum HandleType { + READ, + WRITE, + NONE, + }; + + private: + HandleType mHandleType; + + public: + FileConnectionType() : mHandleType(NONE) {} + explicit FileConnectionType(bool read) : mHandleType((HandleType) read) {} + explicit FileConnectionType(HandleType handleType) : mHandleType(handleType) {} + [[nodiscard]] HandleType getType() const { return mHandleType; } + [[nodiscard]] bool isRead() const { return mHandleType == READ; } + [[nodiscard]] bool isWrite() const { return mHandleType == WRITE; } + }; + + class FileConnectionStatus { + public: + enum Status { + NONE, + OPENED, + CLOSED, + DENIED, + INVALID, + }; + + private: + Status mStatus = NONE; + + public: + FileConnectionStatus() = default; + [[nodiscard]] Status getStatus() const { return mStatus; } + void setStatus(Status status) { mStatus = status; } + [[nodiscard]] bool isOpened() const { return mStatus == OPENED; } + }; + + class File { + typedef ualni SizeBytes; + typedef ualni BytePointer; + typedef int1 Byte; + + private: + FileSystemHandle* mHandle = nullptr; + + FileLocation mLocation; + FileConnectionType mConnectionType; + FileConnectionStatus mStatus; + + BytePointer mPointer = 0; + + public: + File() { + MODULE_SANITY_CHECK(gModuleStorage) + }; + + virtual ~File() { + File::disconnect(); + } + + public: + virtual bool connect(const FileLocation& location, const FileConnectionType& connectionInfo); + virtual bool disconnect(); + + public: + virtual const FileConnectionStatus& getConnectionStatus() { return mStatus; } + virtual const FileConnectionType& getConnectionType() { return mConnectionType; } + virtual const FileLocation& getLocation() { return mLocation; } + + public: + virtual bool setPointer(BytePointer pointer); + virtual bool readBytes(Byte* bytes, SizeBytes size); + virtual bool writeBytes(const Byte* bytes, SizeBytes size); + + public: + virtual SizeBytes size(); + }; +} \ No newline at end of file diff --git a/Storage/public/RemoteStorage.hpp b/Storage/public/RemoteStorage.hpp new file mode 100644 index 0000000..7f525be --- /dev/null +++ b/Storage/public/RemoteStorage.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "Storage.hpp" + +namespace tp { + class Client : public Storage { + // same as local storage file but transfers all commands to server + }; + + class Server { + // opens local storage and transfers all client command to it + // manages multiple clients + }; +} \ No newline at end of file diff --git a/Storage/public/Storage.hpp b/Storage/public/Storage.hpp new file mode 100644 index 0000000..b2f2b87 --- /dev/null +++ b/Storage/public/Storage.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include "Strings.hpp" + +namespace tp { + + extern ModuleManifest gModuleStorage; + + class StorageLocation { + public: + StorageLocation() = default; + virtual ~StorageLocation() = default; + }; + + class ConnectionType { + public: + ConnectionType() = default; + virtual ~ConnectionType() = default; + }; + + class ConnectionStatus { + public: + ConnectionStatus() = default; + virtual ~ConnectionStatus() = default; + }; + + class Storage { + typedef ualni SizeBytes; + typedef ualni BytePointer; + typedef int1 Byte; + + private: + StorageLocation mLocation; + ConnectionType mConnectionType; + ConnectionStatus mStatus; + + public: + Storage() = default; + virtual ~Storage() = default; + + public: + virtual bool connect(const StorageLocation& location, const ConnectionType& connectionInfo) = 0; + virtual bool disconnect() = 0; + virtual const ConnectionStatus& getConnectionStatus() = 0; + + public: + virtual bool readBytes(BytePointer pointer, Byte* bytes, SizeBytes size) = 0; + virtual bool writeBytes(BytePointer pointer, const Byte* bytes, SizeBytes size) = 0; + + public: + virtual SizeBytes size() = 0; + }; +}; \ No newline at end of file diff --git a/Storage/tests/TestLocalStorage.cpp b/Storage/tests/TestLocalStorage.cpp new file mode 100644 index 0000000..996782a --- /dev/null +++ b/Storage/tests/TestLocalStorage.cpp @@ -0,0 +1,33 @@ + +#include "Testing.hpp" +#include "LocalStorage.hpp" + +using namespace tp; + +TEST_DEF_STATIC(Simple) { + + int1 data[] = { 0, 1, 2, 3, 4 }; + int1 dataRead[]{}; + + { + File file; + file.connect(FileLocation(String("tmp2")), FileConnectionType(false)); + file.writeBytes(data, 5); + file.disconnect(); + } + + { + File file; + file.connect(FileLocation(String("tmp2")), FileConnectionType(true)); + file.readBytes(dataRead, 5); + file.disconnect(); + } + + for (auto i = 0; i < 5; i++) { + TEST(data[i] == dataRead[i]); + } +} + +TEST_DEF(LocalStorage) { + testSimple(); +} \ No newline at end of file diff --git a/Storage/tests/Tests.cpp b/Storage/tests/Tests.cpp new file mode 100644 index 0000000..3dec993 --- /dev/null +++ b/Storage/tests/Tests.cpp @@ -0,0 +1,19 @@ + +#include "Testing.hpp" +#include "Utils.hpp" + +void testLocalStorage(); + +int main() { + + tp::ModuleManifest* deps[] = { &tp::gModuleUtils, nullptr }; + tp::ModuleManifest testModule("StorageTest", nullptr, nullptr, deps); + + if (!testModule.initialize()) { + return 1; + } + + testLocalStorage(); + + testModule.deinitialize(); +} \ No newline at end of file diff --git a/Strings/public/Strings.hpp b/Strings/public/Strings.hpp index 46d124b..c3b6472 100644 --- a/Strings/public/Strings.hpp +++ b/Strings/public/Strings.hpp @@ -1,6 +1,6 @@ #pragma once -#include "PoolAllocator.hpp" +#include "Allocators.hpp" #include "StringLogic.hpp" namespace tp {