Start work on phonebook tool.

This commit is contained in:
Kyle Isom 2023-10-10 06:02:21 -07:00
parent cc17abea53
commit a6d7b948d4
10 changed files with 283 additions and 54 deletions

View File

@ -12,9 +12,11 @@
#include <unistd.h> #include <unistd.h>
#define PROT_RW (PROT_WRITE|PROT_READ) #define PROT_RW (PROT_WRITE|PROT_READ)
#elif defined(__WIN64__) || defined(__WIN32__) #elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
#include <Windows.h> #include <Windows.h>
#include <winbase.h>
#include <fileapi.h> #include <fileapi.h>
#include <strsafe.h>
#endif #endif
#include <ios> #include <ios>
@ -27,7 +29,8 @@ namespace klib {
Arena::Arena() Arena::Arena()
: store(nullptr), size(0), fd(0), arenaType(ArenaType::Uninit) : store(nullptr), size(0), fd(0), arenaType(ArenaType::Uninit)
{} {
}
Arena::~Arena() Arena::~Arena()
@ -141,17 +144,52 @@ Arena::Create(const char *path, size_t fileSize, mode_t mode)
return this->Open(path); 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 int
Arena::Open(const char *path) Arena::Open(const char *path)
{ {
HANDLE fHandle; HANDLE fHandle;
size_t fSize; DWORD fRead = 0;
size_t fRead = 0; size_t fSize;
size_t fRemaining; size_t fRemaining;
auto cursor = this->store; auto *cursor = this->store;
OVERLAPPED overlap; OVERLAPPED overlap;
std::cout << "CreateFileA\n";
fHandle = CreateFileA( fHandle = CreateFileA(
(LPSTR)path, (LPSTR)path,
GENERIC_READ, GENERIC_READ,
@ -161,10 +199,13 @@ Arena::Open(const char *path)
FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL,
NULL); NULL);
if (fHandle == INVALID_HANDLE_VALUE) { if (fHandle == INVALID_HANDLE_VALUE) {
displayWinErr("CreateFileA");
return -1; return -1;
} }
std::cout << "GetFileSizeEx\n";
if (!GetFileSizeEx(fHandle, reinterpret_cast<PLARGE_INTEGER>(&fSize))) { if (!GetFileSizeEx(fHandle, reinterpret_cast<PLARGE_INTEGER>(&fSize))) {
displayWinErr("GetFileSizeEx");
CloseHandle(fHandle); CloseHandle(fHandle);
return -1; return -1;
} }
@ -173,8 +214,12 @@ Arena::Open(const char *path)
fRemaining = fSize; fRemaining = fSize;
while (fRemaining != 0) { while (fRemaining != 0) {
std::cout << "ReadFile\n";
overlap.Offset = (fSize - fRemaining); overlap.Offset = (fSize - fRemaining);
if (!ReadFile(fHandle, cursor, fSize, reinterpret_cast<LPDWORD>(&fRead), &overlap)) { if (ReadFile(fHandle, cursor, fSize,
&fRead,
&overlap) != TRUE) {
displayWinErr("ReadFile");
CloseHandle(fHandle); CloseHandle(fHandle);
this->Destroy(); this->Destroy();
return -1; return -1;
@ -187,9 +232,39 @@ Arena::Open(const char *path)
CloseHandle(fHandle); CloseHandle(fHandle);
return 0; 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 bool
Arena::CursorInArena(const uint8_t *cursor) Arena::CursorInArena(const uint8_t *cursor)
{ {

55
Arena.h
View File

@ -22,14 +22,34 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include "Exceptions.h"
#if defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
#include <Windows.h>
#include <fileapi.h>
#endif
namespace klib { 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 /// \enum ArenaType
/// ///
/// ArenaType describes the type of \class Arena. /// ArenaType describes the type of \class Arena.
enum class ArenaType : uint8_t { enum class ArenaType
: uint8_t {
/// Uninit is an unintialized arena. /// Uninit is an unintialized arena.
Uninit, Uninit,
/// Static is an arena backed by a static block of memory. /// 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. /// \return Returns 0 on success and -1 on error.
int SetAlloc(size_t allocSize); int SetAlloc(size_t allocSize);
#if defined(__linux__)
/// MemoryMap points the arena to a memory-mapped file. This is /// MemoryMap points the arena to a memory-mapped file. This is
/// currently only supported on Linux. If the arena is already backed, /// currently only supported on Linux. If the arena is already backed,
/// then #Destroy will be called first. /// then #Destroy will be called first.
@ -91,8 +111,14 @@ public:
/// \param memFileDes File descriptor to map into memory. /// \param memFileDes File descriptor to map into memory.
/// \param memSize The size of memory to map. /// \param memSize The size of memory to map.
/// \return Returns 0 on success and -1 on error. /// \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 /// Create creates a new file, truncating it if it already exists. On
/// Unix-based platforms, the arena will be backed by a memory via /// Unix-based platforms, the arena will be backed by a memory via
/// #MemoryMap. On other platforms (e.g. Windows), the arena will read /// #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 fileSize The size of the file to create.
/// \param mode The permissions to load. /// \param mode The permissions to load.
/// \return Returns 0 on success and -1 on error. /// \return Returns 0 on success and -1 on error.
#if defined(__linux__)
int Create(const char *path, size_t fileSize, mode_t mode); 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 /// 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 /// Unix-based platforms, the arena will be backed by a memory via
/// #MemoryMap. On other platforms (e.g. Windows), the arena will read /// #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. /// \param path The path to the file to be loaded.
/// \return Returns 0 on success and -1 on error. /// \return Returns 0 on success and -1 on error.
#if defined(__linux__)
int Open(const char *path); int Open(const char *path);
#elif defined(__WIN64__) || defined(__WIN32__) #elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
int Open(const char *path); int Open(const char *path);
#endif #endif
/// NewCursor returns a pointer to the start of the memory in the arena. /// NewCursor returns a pointer to the start of the memory in the arena.
/// ///
/// \return A pointer to the start of the arena memory. /// \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. /// End returns a pointer to the end of the arena memory.
/// ///
/// \return 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. /// CursorInArena checks whether the cursor is still in the arena.
/// ///
/// \param cursor A pointer that ostensibly points to the arena's /// \param cursor A pointer that ostensibly points to the arena's
/// memory. /// memory.
/// \return True if the cursor is still in the arena. /// \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. /// Returns the current size of the arena.
/// ///
@ -148,7 +182,8 @@ public:
{ return this->arenaType; } { return this->arenaType; }
/// Ready returns whether the arena is initialized. /// 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. /// Clear zeroizes the memory in the arena.
void Clear(); void Clear();
@ -198,7 +233,7 @@ private:
/// \param os /// \param os
/// \param arena /// \param arena
/// \return /// \return
std::ostream &operator<<(std::ostream& os, Arena &arena); std::ostream &operator<<(std::ostream &os, Arena &arena);
} // namespace klib } // namespace klib

View File

@ -24,6 +24,7 @@ set(HEADER_FILES
Arena.h Arena.h
Buffer.h Buffer.h
Dictionary.h Dictionary.h
Exceptions.h
Test.h Test.h
TLV.h) TLV.h)
@ -31,14 +32,13 @@ set(SOURCE_FILES
Arena.cc Arena.cc
Buffer.cc Buffer.cc
Dictionary.cc Dictionary.cc
Exceptions.cpp
Test.cc Test.cc
TLV.cc) TLV.cc)
add_library(klib STATIC add_library(klib STATIC ${SOURCE_FILES} ${HEADER_FILES})
Arena.cc add_executable(phonebook phonebook.cpp)
Buffer.cc target_link_libraries(phonebook klib)
Dictionary.cc
TLV.cc)
include(CTest) include(CTest)
enable_testing() enable_testing()

24
Exceptions.cpp Normal file
View File

@ -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<char *>(this->msg.c_str());
}
}

44
Exceptions.h Normal file
View File

@ -0,0 +1,44 @@
//
// Created by kyle on 2023-10-10.
//
#ifndef KLIB_EXCEPTIONS_H
#define KLIB_EXCEPTIONS_H
#include <exception>
#include <string>
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

12
Test.cc
View File

@ -2,6 +2,7 @@
// Created by kyle on 2023-10-09. // Created by kyle on 2023-10-09.
// //
#include "Exceptions.h"
#include "Test.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<char *>(this->msg.c_str());
}
} // namespace klib } // namespace klib

15
Test.h
View File

@ -27,21 +27,6 @@ namespace klib {
inline void TestAssert(bool condition, std::string message); 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 } // namespace klib
#endif //KLIB_TEST_H #endif //KLIB_TEST_H

8
klib.h
View File

@ -25,6 +25,14 @@
#define KLIB_KLIB_H #define KLIB_KLIB_H
#include <klib/Arena.h>
#include <klib/Buffer.h>
#include <klib/Dictionary.h>
#include <klib/Exceptions.h>
#include <klib/TLV.h>
#include <klib/Test.h>
/// klib is the top-level namespace containing all the code in this library. /// klib is the top-level namespace containing all the code in this library.
namespace klib { namespace klib {

70
phonebook.cpp Normal file
View File

@ -0,0 +1,70 @@
//
// Created by kyle on 2023-10-10.
//
#include <iostream>
#include <string>
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") {
}
}

View File

@ -97,7 +97,6 @@ main(int argc, const char *argv[])
} }
arenaStatic.Clear(); arenaStatic.Clear();
#if defined(__linux__)
Arena arenaFile; Arena arenaFile;
if (-1 == arenaFile.Create(ARENA_FILE, ARENA_SIZE, 0644)) { if (-1 == arenaFile.Create(ARENA_FILE, ARENA_SIZE, 0644)) {
@ -105,7 +104,6 @@ main(int argc, const char *argv[])
} else if (!runSuite(arenaFile, "arenaFile")) { } else if (!runSuite(arenaFile, "arenaFile")) {
abort(); abort();
} }
#endif
if (-1 == arenaMem.SetAlloc(ARENA_SIZE)) { if (-1 == arenaMem.SetAlloc(ARENA_SIZE)) {
abort(); abort();