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>
#define PROT_RW (PROT_WRITE|PROT_READ)
#elif defined(__WIN64__) || defined(__WIN32__)
#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
#include <Windows.h>
#include <winbase.h>
#include <fileapi.h>
#include <strsafe.h>
#endif
#include <ios>
@ -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<PLARGE_INTEGER>(&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<LPDWORD>(&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)
{

55
Arena.h
View File

@ -22,14 +22,34 @@
#include <cstddef>
#include <cstdint>
#include "Exceptions.h"
#if defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
#include <Windows.h>
#include <fileapi.h>
#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

View File

@ -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()

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.
//
#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<char *>(this->msg.c_str());
}
} // namespace klib

15
Test.h
View File

@ -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

8
klib.h
View File

@ -25,6 +25,14 @@
#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.
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();
#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();