From a6d7b948d4d3c3a92e8b630d169088679bbc38dc Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Tue, 10 Oct 2023 06:02:21 -0700 Subject: [PATCH] Start work on phonebook tool. --- Arena.cc | 97 ++++++++++++++++++++++++++++++++++++++++++++------ Arena.h | 55 ++++++++++++++++++++++------ CMakeLists.txt | 10 +++--- Exceptions.cpp | 24 +++++++++++++ Exceptions.h | 44 +++++++++++++++++++++++ Test.cc | 12 +------ Test.h | 15 -------- klib.h | 8 +++++ phonebook.cpp | 70 ++++++++++++++++++++++++++++++++++++ tlvTest.cc | 2 -- 10 files changed, 283 insertions(+), 54 deletions(-) create mode 100644 Exceptions.cpp create mode 100644 Exceptions.h create mode 100644 phonebook.cpp diff --git a/Arena.cc b/Arena.cc index 7a3aafa..9261a03 100644 --- a/Arena.cc +++ b/Arena.cc @@ -12,9 +12,11 @@ #include #define PROT_RW (PROT_WRITE|PROT_READ) -#elif defined(__WIN64__) || defined(__WIN32__) +#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32) #include +#include #include +#include #endif #include @@ -27,7 +29,8 @@ namespace klib { Arena::Arena() : store(nullptr), size(0), fd(0), arenaType(ArenaType::Uninit) -{} +{ +} Arena::~Arena() @@ -141,17 +144,52 @@ Arena::Create(const char *path, size_t fileSize, mode_t mode) return this->Open(path); } -#elif defined(__WIN64__) || defined(__WIN32__) +#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32) +static void +displayWinErr(LPTSTR lpszFunction) +{ + // Retrieve the system error message for the last-error code + + LPVOID lpMsgBuf; + LPVOID lpDisplayBuf; + DWORD dw = GetLastError(); + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, NULL ); + + // Display the error message and exit the process + + lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, + (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); + StringCchPrintf((LPTSTR)lpDisplayBuf, + LocalSize(lpDisplayBuf) / sizeof(TCHAR), + TEXT("%s failed with error %d: %s"), + lpszFunction, dw, lpMsgBuf); + MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); + + LocalFree(lpMsgBuf); + LocalFree(lpDisplayBuf); +} + + int Arena::Open(const char *path) { - HANDLE fHandle; - size_t fSize; - size_t fRead = 0; - size_t fRemaining; - auto cursor = this->store; - OVERLAPPED overlap; + HANDLE fHandle; + DWORD fRead = 0; + size_t fSize; + size_t fRemaining; + auto *cursor = this->store; + OVERLAPPED overlap; + std::cout << "CreateFileA\n"; fHandle = CreateFileA( (LPSTR)path, GENERIC_READ, @@ -161,10 +199,13 @@ Arena::Open(const char *path) FILE_ATTRIBUTE_NORMAL, NULL); if (fHandle == INVALID_HANDLE_VALUE) { + displayWinErr("CreateFileA"); return -1; } + std::cout << "GetFileSizeEx\n"; if (!GetFileSizeEx(fHandle, reinterpret_cast(&fSize))) { + displayWinErr("GetFileSizeEx"); CloseHandle(fHandle); return -1; } @@ -173,8 +214,12 @@ Arena::Open(const char *path) fRemaining = fSize; while (fRemaining != 0) { + std::cout << "ReadFile\n"; overlap.Offset = (fSize - fRemaining); - if (!ReadFile(fHandle, cursor, fSize, reinterpret_cast(&fRead), &overlap)) { + if (ReadFile(fHandle, cursor, fSize, + &fRead, + &overlap) != TRUE) { + displayWinErr("ReadFile"); CloseHandle(fHandle); this->Destroy(); return -1; @@ -187,9 +232,39 @@ Arena::Open(const char *path) CloseHandle(fHandle); return 0; } -#endif +int +Arena::Create(const char *path, size_t fileSize, DWORD mode) +{ + HANDLE fHandle; + + std::cout << "Create::CreateFileA\n"; + fHandle = CreateFileA( + (LPSTR)path, + GENERIC_READ|GENERIC_WRITE, + mode, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (fHandle == INVALID_HANDLE_VALUE) { + displayWinErr("Create::CreateFileA"); + return -1; + } + + std::cout << "SetFileValidData\n"; + if (SetFileValidData(fHandle, fileSize) != fileSize) { + displayWinErr("SetFileValidData"); + CloseHandle(fHandle); + return -1; + } + + CloseHandle(fHandle); + return this->Open(path); +} +#endif + bool Arena::CursorInArena(const uint8_t *cursor) { diff --git a/Arena.h b/Arena.h index 74e256a..f65c1a8 100644 --- a/Arena.h +++ b/Arena.h @@ -22,14 +22,34 @@ #include #include +#include "Exceptions.h" + + +#if defined(__WIN64__) || defined(__WIN32__) || defined(WIN32) + +#include +#include + +#endif + namespace klib { +/// DefaultFileMode is a sane set of default permissions that can be used for a +/// new Arena. +#if defined(__linux__) +static constexpr mode_t DefaultFileMode = 0644; +#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32) +static constexpr DWORD DefaultFileMode = + (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE); +#endif + /// \enum ArenaType /// /// ArenaType describes the type of \class Arena. -enum class ArenaType : uint8_t { +enum class ArenaType + : uint8_t { /// Uninit is an unintialized arena. Uninit, /// Static is an arena backed by a static block of memory. @@ -83,7 +103,7 @@ public: /// \return Returns 0 on success and -1 on error. int SetAlloc(size_t allocSize); -#if defined(__linux__) + /// MemoryMap points the arena to a memory-mapped file. This is /// currently only supported on Linux. If the arena is already backed, /// then #Destroy will be called first. @@ -91,8 +111,14 @@ public: /// \param memFileDes File descriptor to map into memory. /// \param memSize The size of memory to map. /// \return Returns 0 on success and -1 on error. - int MemoryMap(int memFileDes, size_t memSize); // Arena will own fd. +#if defined(__linux__) + int MemoryMap(int memFileDes, size_t memSize); +#else + int MemoryMap(int memFileDes, size_t memSize) + { throw NotImplemented("WIN32"); } + +#endif /// Create creates a new file, truncating it if it already exists. On /// Unix-based platforms, the arena will be backed by a memory via /// #MemoryMap. On other platforms (e.g. Windows), the arena will read @@ -102,8 +128,13 @@ public: /// \param fileSize The size of the file to create. /// \param mode The permissions to load. /// \return Returns 0 on success and -1 on error. +#if defined(__linux__) int Create(const char *path, size_t fileSize, mode_t mode); +#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32) + int Create(const char *path, size_t fileSize, DWORD mode); + +#endif /// Open reads a file into the arena; the file must already exist. On /// Unix-based platforms, the arena will be backed by a memory via /// #MemoryMap. On other platforms (e.g. Windows), the arena will read @@ -113,27 +144,30 @@ public: /// /// \param path The path to the file to be loaded. /// \return Returns 0 on success and -1 on error. +#if defined(__linux__) int Open(const char *path); -#elif defined(__WIN64__) || defined(__WIN32__) - int Open(const char *path); +#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32) + int Open(const char *path); #endif /// NewCursor returns a pointer to the start of the memory in the arena. /// /// \return A pointer to the start of the arena memory. - uint8_t *NewCursor() const { return this->store; } + uint8_t *NewCursor() const + { return this->store; } /// End returns a pointer to the end of the arena memory. /// /// \return A pointer to the end of the arena memory. - uint8_t *End() { return this->store + this->size; } + uint8_t *End() + { return this->store + this->size; } /// CursorInArena checks whether the cursor is still in the arena. /// /// \param cursor A pointer that ostensibly points to the arena's /// memory. /// \return True if the cursor is still in the arena. - bool CursorInArena(const uint8_t *cursor); + bool CursorInArena(const uint8_t *cursor); /// Returns the current size of the arena. /// @@ -148,7 +182,8 @@ public: { return this->arenaType; } /// Ready returns whether the arena is initialized. - bool Ready() const { return this->Type() != ArenaType::Uninit; }; + bool Ready() const + { return this->Type() != ArenaType::Uninit; }; /// Clear zeroizes the memory in the arena. void Clear(); @@ -198,7 +233,7 @@ private: /// \param os /// \param arena /// \return -std::ostream &operator<<(std::ostream& os, Arena &arena); +std::ostream &operator<<(std::ostream &os, Arena &arena); } // namespace klib diff --git a/CMakeLists.txt b/CMakeLists.txt index c9f67cb..2b2be50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ set(HEADER_FILES Arena.h Buffer.h Dictionary.h + Exceptions.h Test.h TLV.h) @@ -31,14 +32,13 @@ set(SOURCE_FILES Arena.cc Buffer.cc Dictionary.cc + Exceptions.cpp Test.cc TLV.cc) -add_library(klib STATIC - Arena.cc - Buffer.cc - Dictionary.cc - TLV.cc) +add_library(klib STATIC ${SOURCE_FILES} ${HEADER_FILES}) +add_executable(phonebook phonebook.cpp) +target_link_libraries(phonebook klib) include(CTest) enable_testing() diff --git a/Exceptions.cpp b/Exceptions.cpp new file mode 100644 index 0000000..9bd5274 --- /dev/null +++ b/Exceptions.cpp @@ -0,0 +1,24 @@ +// +// Created by kyle on 2023-10-10. +// + +#include "Exceptions.h" + + +namespace klib { + + +AssertionFailed::AssertionFailed(std::string message) : msg(message) +{ +} + +char * +AssertionFailed::what() +{ + return const_cast(this->msg.c_str()); +} + + + + +} \ No newline at end of file diff --git a/Exceptions.h b/Exceptions.h new file mode 100644 index 0000000..6265cdb --- /dev/null +++ b/Exceptions.h @@ -0,0 +1,44 @@ +// +// Created by kyle on 2023-10-10. +// + +#ifndef KLIB_EXCEPTIONS_H +#define KLIB_EXCEPTIONS_H + + +#include +#include + +namespace klib { + + +class NotImplemented : public std::exception { +public: + explicit NotImplemented(const char *pl) : platform((char *)pl) {} + char *what() { + return this->platform; + } +private: + char *platform; +}; + + +/// AssertionFailed indicates that some invariant didn't hold. +class AssertionFailed : public std::exception { +public: + /// AssertionFailed is constructed with a message describing what + /// failed. + explicit AssertionFailed(std::string message); + + /// what returns a message describing the exception. + char *what(); + +private: + std::string msg; +}; + + +} // namespace klib + + +#endif //KLIB_EXCEPTIONS_H diff --git a/Test.cc b/Test.cc index cb114b6..f0637bd 100644 --- a/Test.cc +++ b/Test.cc @@ -2,6 +2,7 @@ // Created by kyle on 2023-10-09. // +#include "Exceptions.h" #include "Test.h" @@ -28,15 +29,4 @@ TestAssert(bool condition, std::string message = "Assertion failed.") } -AssertionFailed::AssertionFailed(std::string message) : msg(message) -{ -} - -char * -AssertionFailed::what() -{ - return const_cast(this->msg.c_str()); -} - - } // namespace klib diff --git a/Test.h b/Test.h index 7a8ea31..23b810d 100644 --- a/Test.h +++ b/Test.h @@ -27,21 +27,6 @@ namespace klib { inline void TestAssert(bool condition, std::string message); -/// AssertionFailed indicates that some invariant didn't hold. -class AssertionFailed : public std::exception { -public: - /// AssertionFailed is constructed with a message describing what - /// failed. - explicit AssertionFailed(std::string message); - - /// what returns a message describing the exception. - char *what(); - -private: - std::string msg; -}; - - } // namespace klib #endif //KLIB_TEST_H diff --git a/klib.h b/klib.h index c7897cd..077a8aa 100644 --- a/klib.h +++ b/klib.h @@ -25,6 +25,14 @@ #define KLIB_KLIB_H +#include +#include +#include +#include +#include +#include + + /// klib is the top-level namespace containing all the code in this library. namespace klib { diff --git a/phonebook.cpp b/phonebook.cpp new file mode 100644 index 0000000..4adb94e --- /dev/null +++ b/phonebook.cpp @@ -0,0 +1,70 @@ +// +// Created by kyle on 2023-10-10. +// + +#include +#include +using namespace std; + +#include "Arena.h" +#include "Dictionary.h" +using namespace klib; + +static const char *defaultPhonebook = "pb.dat"; + +static void +usage(ostream &os, int exc) +{ + os << "phonebook is a tool for interacting with phonebook files.\n"; + os << "\nThe default filename is pb.dat.\n\n"; + os << "Usage:\n"; + os << "\tphonebook [-f file] list\n"; + os << "\tphonebook [-f file] del key\n"; + os << "\tphonebook [-f file] has key\n"; + os << "\tphonebook [-f file] get key value\n"; + os << "\tphonebook [-f file] put key value\n"; + os << "\n"; + + exit(exc); +} + +int +main(int argc, char *argv[]) +{ + Arena arena; + Dictionary pb(arena); + char *pbFile = (char *)defaultPhonebook; + int optind = 1; + + for (optind; optind < argc; optind++) { + auto arg = string(argv[optind]); + if (arg[0] != '-') break; + if (arg == "-h") usage(cout, 0); + if (arg == "-f") { + pbFile = argv[optind+1]; + optind++; + continue; + } + + usage(cerr, 1); + } + + if (argc <= 1) { + usage(cout, 0); + } + + cout << "[+] loading phonebook from " << pbFile << "\n"; + if (arena.Open(pbFile) != 0) { + cerr << "Failed to open " << pbFile << "\n"; + exit(1); + } + + auto command = string(argv[optind++]); + + if (command == "list") { + cout << pb << "\n"; + } else if (command == "del") { + + } + +} \ No newline at end of file diff --git a/tlvTest.cc b/tlvTest.cc index 04f378c..c53661e 100644 --- a/tlvTest.cc +++ b/tlvTest.cc @@ -97,7 +97,6 @@ main(int argc, const char *argv[]) } arenaStatic.Clear(); - #if defined(__linux__) Arena arenaFile; if (-1 == arenaFile.Create(ARENA_FILE, ARENA_SIZE, 0644)) { @@ -105,7 +104,6 @@ main(int argc, const char *argv[]) } else if (!runSuite(arenaFile, "arenaFile")) { abort(); } - #endif if (-1 == arenaMem.SetAlloc(ARENA_SIZE)) { abort();