Finish working phonebook.

This commit is contained in:
Kyle Isom 2023-10-05 23:08:35 -07:00
parent 82f5e32ddb
commit 0d28baef0e
10 changed files with 388 additions and 176 deletions

143
Arena.cc
View File

@ -10,40 +10,56 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#if defined(DESKTOP_BUILD)
#include <iostream>
#include <ios>
#endif
#include "Arena.h" #include "Arena.h"
#define ARENA_UNINIT 0 #define ARENA_UNINIT 0
#define ARENA_STATIC 1 #define ARENA_STATIC 1
#define ARENA_ALLOC 2 #define ARENA_ALLOC 2
#if defined(__linux__)
#define ARENA_MMAP 3 #define ARENA_MMAP 3
#define PROT_RW PROT_READ|PROT_WRITE #define PROT_RW PROT_READ|PROT_WRITE
#endif
void
InitializeArena(Arena &arena)
{
arena.Store = NULL;
arena.Size = 0;
arena.Type = ARENA_UNINIT;
arena.fd = 0;
}
int int
new_arena(Arena &arena, uint8_t *mem, size_t size) NewStaticArena(Arena &arena, uint8_t *mem, size_t size)
{ {
arena.store = mem; arena.Store = mem;
arena.size = size; arena.Size = size;
arena.type = ARENA_STATIC; arena.Type = ARENA_STATIC;
return 0; return 0;
} }
int int
alloc_new_arena(Arena &arena, size_t size) AllocNewArena(Arena & arena, size_t size)
{ {
if (arena.size > 0) { if (arena.Size > 0) {
if (arena_destroy(arena) != 0) { if (DestroyArena(arena) != 0) {
return -1; return -1;
} }
} }
arena.type = ARENA_ALLOC; arena.Type = ARENA_ALLOC;
arena.size = size; arena.Size = size;
arena.store = (uint8_t *)calloc(sizeof(uint8_t), size); arena.Store = (uint8_t *)calloc(sizeof(uint8_t), size);
if (arena.store == NULL) { if (arena.Store == NULL) {
return -1; return -1;
} }
@ -53,17 +69,17 @@ alloc_new_arena(Arena &arena, size_t size)
#if defined(__linux__) #if defined(__linux__)
int int
mmap_arena(Arena &arena, int fd, size_t size) MMapArena(Arena &arena, int fd, size_t Size)
{ {
if (arena.size > 0) { if (arena.Size > 0) {
if (arena_destroy(arena) != 0) { if (arena_destroy(arena) != 0) {
return -1; return -1;
} }
} }
arena.type = ARENA_MMAP; arena.Type = ARENA_MMAP;
arena.size = size; arena.Size = Size;
arena.store = (uint8_t *)mmap(NULL, size, PROT_RW, MAP_SHARED, fd, 0); arena.store = (uint8_t *)mmap(NULL, Size, PROT_RW, MAP_SHARED, fd, 0);
if ((void *)arena.store == MAP_FAILED) { if ((void *)arena.store == MAP_FAILED) {
return -1; return -1;
} }
@ -73,11 +89,11 @@ mmap_arena(Arena &arena, int fd, size_t size)
int int
open_arena(Arena &arena, const char *path) OpenArena(Arena &arena, const char *path)
{ {
struct stat st; struct stat st;
if (arena.size > 0) { if (arena.Size > 0) {
if (arena_destroy(arena) != 0) { if (arena_destroy(arena) != 0) {
return -1; return -1;
} }
@ -92,16 +108,16 @@ open_arena(Arena &arena, const char *path)
return -1; return -1;
} }
return mmap_arena(arena, arena.fd, (size_t)st.st_size); return MMapArena(arena, arena.fd, (size_t)st.st_size);
} }
int int
create_arena(Arena &arena, const char *path, size_t size, mode_t mode) CreateArena(Arena &arena, const char *path, size_t Size, mode_t mode)
{ {
int fd = 0; int fd = 0;
if (arena.size > 0) { if (arena.Size > 0) {
if (arena_destroy(arena) != 0) { if (arena_destroy(arena) != 0) {
return -1; return -1;
} }
@ -112,43 +128,49 @@ create_arena(Arena &arena, const char *path, size_t size, mode_t mode)
return -1; return -1;
} }
if (ftruncate(fd, size) == -1) { if (ftruncate(fd, Size) == -1) {
return -1; return -1;
} }
close(fd); close(fd);
return open_arena(arena, path); return OpenArena(arena, path);
} }
#endif #endif
/*
* ClearArena clears the memory being used, removing any data
* present. It does not free the memory; it is effectively a
* wrapper around memset.
*/
void void
arena_clear(Arena &arena) ClearArena(Arena &arena)
{ {
if (arena.size == 0) { if (arena.Size == 0) {
return; return;
} }
memset(arena.store, 0, arena.size); memset(arena.Store, 0, arena.Size);
} }
int int
arena_destroy(Arena &arena) DestroyArena(Arena &arena)
{ {
if (arena.type == ARENA_UNINIT) { if (arena.Type == ARENA_UNINIT) {
return 0; return 0;
} }
switch (arena.type) { switch (arena.Type) {
case ARENA_STATIC: case ARENA_STATIC:
break; break;
case ARENA_ALLOC: case ARENA_ALLOC:
free(arena.store); free(arena.Store);
break; break;
#if defined(__linux__)
case ARENA_MMAP: case ARENA_MMAP:
if (munmap(arena.store, arena.size) == -1) { if (munmap(arena.store, arena.Size) == -1) {
return -1; return -1;
} }
@ -158,20 +180,63 @@ arena_destroy(Arena &arena)
arena.fd = 0; arena.fd = 0;
break; break;
#endif
default: default:
abort(); #if defined(NDEBUG)
return -1; return -1;
#else
abort();
#endif
} }
arena.type = ARENA_UNINIT; arena.Type = ARENA_UNINIT;
arena.size = 0; arena.Size = 0;
arena.store = NULL; arena.Store = NULL;
return 0; return 0;
} }
#if defined(DESKTOP_BUILD)
void
DisplayArena(const Arena &arena)
{
std::cout << "Arena @ 0x";
std::cout << std::hex << (uintptr_t)&arena << std::endl;
std::cout << std::dec;
std::cout << "\tStore is " << arena.Size << " bytes at address 0x";
std::cout << std::hex << (uintptr_t)&(arena.Store) << std::endl;
std::cout << "\tType: ";
switch (arena.Type) {
case ARENA_UNINIT:
std::cout << "uninitialized";
break;
case ARENA_STATIC:
std::cout << "static";
break;
case ARENA_ALLOC:
std::cout << "allocated";
break;
#if defined(__linux__)
case ARENA_MMAP:
std::cout << "mmap/file"
break;
#endif
default:
std::cout << "unknown (this is a bug)";
}
std::cout << std::endl;
}
#else
void
DisplayArena(const Arena &arena)
{
}
#endif
int int
write_arena(Arena &arena, const char *path) WriteArena(const Arena &arena, const char *path)
{ {
FILE *arenaFile = NULL; FILE *arenaFile = NULL;
int retc = -1; int retc = -1;
@ -181,8 +246,8 @@ write_arena(Arena &arena, const char *path)
return -1; return -1;
} }
if (fwrite(arena.store, sizeof(*arena.store), arena.size, if (fwrite(arena.Store, sizeof(*arena.Store), arena.Size,
arenaFile) == arena.size) { arenaFile) == arena.Size) {
retc = 0; retc = 0;
} }

33
Arena.h
View File

@ -8,27 +8,38 @@
typedef struct { typedef struct {
uint8_t *store; uint8_t *Store;
size_t size; size_t Size;
int fd; int fd;
uint8_t type; uint8_t Type;
} Arena; } Arena;
int new_arena(Arena &, uint8_t *, size_t); /*
int alloc_new_arena(Arena &, size_t); * InitializeArena is intended for use only with systems that
* do not initialize new variables to zero. It should be called
* exactly once, at the start of the program. Any other time the
* arena needs to be reset, it should be called with clear_arena
* or destroy_arena.
*/
void InitializeArena(Arena &arena);
int NewStaticArena(Arena &, uint8_t *, size_t);
int AllocNewArena(Arena &, size_t);
#if defined(__linux__) #if defined(__linux__)
int mmap_arena(Arena &, int); /* arena will own fd */ int MMapArena(Arena &, int); /* arena will own fd */
int create_arena(Arena &arena, const char *path, size_t size, mode_t mode); int CreateArena(Arena &arena, const char *path, size_t size, mode_t mode);
int open_arena(Arena &, const char *, size_t); int OpenArena(Arena &, const char *, size_t);
#endif #endif
void arena_clear(Arena &); void ClearArena(Arena &);
int arena_destroy(Arena &); /* dispose of any memory used by arena */ int DestroyArena(Arena &); /* dispose of any memory used by arena */
/* DANGER: if arena is file backed (mmap or open), DO NOT WRITE TO THE /* DANGER: if arena is file backed (mmap or open), DO NOT WRITE TO THE
* BACKING FILE! */ * BACKING FILE! */
int write_arena(const char *); int WriteArena(const Arena &arena, const char *path);
void
DisplayArena(const Arena &arena);
#endif #endif

View File

@ -2,10 +2,26 @@ cmake_minimum_required(VERSION 3.25)
project(klib CXX) project(klib CXX)
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)
if(MSVC)
add_compile_options("/W4" "$<$<CONFIG:RELEASE>:/O2>")
else()
add_compile_options("-Wall" "-Wextra" "-Werror" "$<$<CONFIG:RELEASE>:-O3>")
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
add_compile_options("-stdlib=libc++")
else()
# nothing special for gcc at the moment
endif()
endif()
add_compile_options("-DDESKTOP_BUILD")
add_library(klib STATIC add_library(klib STATIC
Arena.cc Arena.cc
TLV.cc) TLV.cc
Phonebook.cc)
add_executable(tlv_test tlv_test.cc) add_executable(tlv_test tlv_test.cc)
target_link_libraries(tlv_test klib) target_link_libraries(tlv_test klib)
add_executable(phonebook_test phonebook_test.cc)
target_link_libraries(phonebook_test klib)

View File

@ -2,22 +2,26 @@
#include <cstdlib> #include <cstdlib>
#include "Phonebook.h" #include "Phonebook.h"
#if defined(DESKTOP_BUILD)
#include <iostream>
#endif
bool bool
Phonebook::lookup(const char *key, uint8_t klen, TLV::Record &res) Phonebook::Lookup(const char *key, uint8_t klen, TLV::Record &res)
{ {
res.Tag = this->ktag; res.Tag = this->kTag;
uint8_t *cursor = TLV::find_tag(this->arena, NULL, res); uint8_t *cursor = TLV::FindTag(this->arena, NULL, res);
while (cursor != NULL) { while (cursor != NULL) {
if ((klen == res.Len) && if ((klen == res.Len) &&
(memcmp(res.Val, key, klen) == 0)) { (memcmp(res.Val, key, klen) == 0)) {
TLV::read_from_memory(res, cursor); TLV::ReadFromMemory(res, cursor);
if (res.Tag != this->vtag) { if (res.Tag != this->vTag) {
abort(); abort();
} }
return true; return true;
} }
cursor = TLV::find_tag(this->arena, cursor, res); cursor = TLV::FindTag(this->arena, cursor, res);
} }
return false; return false;
@ -26,29 +30,29 @@ Phonebook::lookup(const char *key, uint8_t klen, TLV::Record &res)
int int
Phonebook::set(const char *key, uint8_t klen, const char *val, uint8_t vlen) Phonebook::Set(const char *key, uint8_t klen, const char *val, uint8_t vlen)
{ {
TLV::Record rec; TLV::Record rec;
uint8_t *cursor = NULL; uint8_t *cursor = NULL;
set_record(rec, this->ktag, klen, key); SetRecord(rec, this->kTag, klen, key);
cursor = this->seek(key, klen); cursor = this->seek(key, klen);
if (cursor != NULL) { if (cursor != NULL) {
TLV::delete_record(this->arena, cursor); TLV::DeleteRecord(this->arena, cursor);
TLV::delete_record(this->arena, cursor); TLV::DeleteRecord(this->arena, cursor);
} }
if (!space_available(klen, vlen)) { if (!spaceAvailable(klen, vlen)) {
return -1; return -1;
} }
cursor = TLV::write_to_memory(this->arena, NULL, rec); cursor = TLV::WriteToMemory(this->arena, NULL, rec);
if (cursor == NULL) { if (cursor == NULL) {
return -1; return -1;
} }
set_record(rec, this->vtag, vlen, val); SetRecord(rec, this->vTag, vlen, val);
if (TLV::write_to_memory(this->arena, NULL, rec) == NULL) { if (TLV::WriteToMemory(this->arena, NULL, rec) == NULL) {
return -1; return -1;
} }
@ -62,15 +66,17 @@ Phonebook::seek(const char *key, uint8_t klen)
{ {
TLV::Record rec; TLV::Record rec;
rec.Tag = this->ktag; rec.Tag = this->kTag;
uint8_t *cursor = TLV::find_tag(this->arena, NULL, rec); uint8_t *cursor = TLV::LocateTag(this->arena, NULL, rec);
while (cursor != NULL) { while (cursor != NULL) {
if ((klen == rec.Len) && if ((klen == rec.Len) && (this->kTag == rec.Tag)) {
(memcmp(rec.Val, key, klen) == 0)) { if (memcmp(rec.Val, key, klen) == 0) {
return cursor; return cursor;
} }
cursor = TLV::skip_record(rec, cursor); }
cursor = TLV::SkipRecord(rec, cursor);
cursor = TLV::LocateTag(this->arena, cursor, rec);
} }
return NULL; return NULL;
@ -78,29 +84,68 @@ Phonebook::seek(const char *key, uint8_t klen)
bool bool
Phonebook::has(const char *key, uint8_t klen) Phonebook::Has(const char *key, uint8_t klen)
{ {
return this->seek(key, klen) != NULL; return this->seek(key, klen) != NULL;
} }
bool bool
Phonebook::space_available(uint8_t kl, uint8_t vl) Phonebook::spaceAvailable(uint8_t klen, uint8_t vlen)
{ {
size_t required = 0; size_t required = 0;
uintptr_t remaining = 0; uintptr_t remaining = 0;
uint8_t *cursor = NULL; uint8_t *cursor = NULL;
cursor = TLV::FindEmpty(this->arena, NULL);
cursor = TLV::find_empty(this->arena, NULL);
if (cursor == NULL) { if (cursor == NULL) {
return false; return false;
} }
required += kl + 2; required += klen + 2;
required += vl + 2; required += vlen + 2;
remaining = (uintptr_t)cursor - (uintptr_t)arena.store; remaining = (uintptr_t)cursor - (uintptr_t)arena.Store;
remaining = arena.size - remaining; remaining = arena.Size - remaining;
return ((size_t)remaining >= required); return ((size_t)remaining >= required);
} }
#if defined(DESKTOP_BUILD)
void
Phonebook::DumpKVPairs()
{
uint8_t *cursor = (this->arena).Store;
TLV::Record rec;
TLV::ReadFromMemory(rec, cursor);
std::cout << "Phonebook KV pairs" << std::endl;
if (rec.Tag == TAG_EMPTY) {
std::cout << "\t(NONE)" << std::endl;
return;
}
while ((cursor != NULL) && (rec.Tag != TAG_EMPTY)) {
std::cout << "\t" << rec.Val << "->";
cursor = TLV::SkipRecord(rec, cursor);
TLV::ReadFromMemory(rec, cursor);
std::cout << rec.Val << std::endl;
cursor = TLV::SkipRecord(rec, cursor);
TLV::ReadFromMemory(rec, cursor);
}
}
void
Phonebook::DumpToFile(const char *path)
{
WriteArena(this->arena, path);
}
#else
void
Phonebook::dump_kvpairs()
{
}
#endif

View File

@ -15,25 +15,26 @@ class Phonebook {
public: public:
Phonebook(Arena &arena) : Phonebook(Arena &arena) :
arena(arena), arena(arena),
ktag(PHONEBOOK_KEY_TAG), kTag(PHONEBOOK_KEY_TAG),
vtag(PHONEBOOK_VAL_TAG) {} ; vTag(PHONEBOOK_VAL_TAG) {} ;
Phonebook(Arena &arena, uint8_t kt, uint8_t vt) : Phonebook(Arena &arena, uint8_t kt, uint8_t vt) :
arena(arena), arena(arena),
ktag(kt), kTag(kt),
vtag(vt) {}; vTag(vt) {};
bool lookup(const char *key, uint8_t klen, TLV::Record &res); bool Lookup(const char *key, uint8_t klen, TLV::Record &res);
int set(const char *key, uint8_t klen, const char *val, int Set(const char *key, uint8_t klen, const char *val,
uint8_t vlen); uint8_t vlen);
bool has(const char *key, uint8_t klen); bool Has(const char *key, uint8_t klen);
void DumpKVPairs();
void DumpToFile(const char *path);
private: private:
uint8_t *seek(const char *key, uint8_t klen); uint8_t *seek(const char *key, uint8_t klen);
bool space_available(uint8_t klen, uint8_t vlen); bool spaceAvailable(uint8_t klen, uint8_t vlen);
Arena &arena; Arena &arena;
uint8_t ktag; uint8_t kTag;
uint8_t vtag; uint8_t vTag;
}; };

67
TLV.cc
View File

@ -9,7 +9,7 @@ namespace TLV {
static bool static bool
space_available(Arena &arena, uint8_t *cursor, uint8_t len) spaceAvailable(Arena &arena, uint8_t *cursor, uint8_t len)
{ {
uintptr_t remaining = 0; uintptr_t remaining = 0;
@ -17,76 +17,95 @@ space_available(Arena &arena, uint8_t *cursor, uint8_t len)
return false; return false;
} }
remaining = (uintptr_t)cursor - (uintptr_t)arena.store; remaining = (uintptr_t)cursor - (uintptr_t)arena.Store;
remaining = arena.size - remaining; remaining = arena.Size - remaining;
return ((size_t)remaining >= ((size_t)len+2)); return ((size_t)remaining >= ((size_t)len+2));
} }
static inline void
clearUnused(Record &rec)
{
uint8_t trail = TLV_MAX_LEN-rec.Len;
memset(rec.Val+rec.Len, 0, trail);
}
uint8_t * uint8_t *
write_to_memory(Arena &arena, uint8_t *cursor, Record &rec) WriteToMemory(Arena &arena, uint8_t *cursor, Record &rec)
{ {
// If cursor is NULL, the user needs us to select an empty // If cursor is NULL, the user needs us to select an empty
// slot for the record. If we can't find one, that's an // slot for the record. If we can't find one, that's an
// error. // error.
// //
// If, however, the user gives us a cursor, we'll trust it // If, however, the user gives us a cursor, we'll trust it
// (though space_available will sanity check that cursor). // (though spaceAvailable will sanity check that cursor).
if (cursor == NULL) { if (cursor == NULL) {
cursor = find_empty(arena, cursor); cursor = FindEmpty(arena, cursor);
if (cursor == NULL) { if (cursor == NULL) {
return NULL; return NULL;
} }
} }
if (!space_available(arena, cursor, rec.Len)) { if (!spaceAvailable(arena, cursor, rec.Len)) {
return NULL; return NULL;
} }
memcpy(cursor, &rec, REC_SIZE(rec)); memcpy(cursor, &rec, REC_SIZE(rec));
cursor = skip_record(rec, cursor); cursor = SkipRecord(rec, cursor);
return cursor; return cursor;
} }
void void
set_record(Record &rec, uint8_t tag, uint8_t len, const char *val) SetRecord(Record &rec, uint8_t tag, uint8_t len, const char *val)
{ {
uint8_t trail = TLV_MAX_LEN-len;
rec.Tag = tag; rec.Tag = tag;
rec.Len = len; rec.Len = len;
memcpy(rec.Val, val, len); memcpy(rec.Val, val, len);
memset(rec.Val+len, 0, trail); clearUnused(rec);
} }
void void
read_from_memory(Record &rec, uint8_t *cursor) ReadFromMemory(Record &rec, uint8_t *cursor)
{ {
rec.Tag = cursor[0]; rec.Tag = cursor[0];
rec.Len = cursor[1]; rec.Len = cursor[1];
memcpy(rec.Val, cursor+2, rec.Len); memcpy(rec.Val, cursor+2, rec.Len);
clearUnused(rec);
} }
/* /*
* returns a pointer to memory where the record was found, * returns a pointer to memory where the record was found,
* e.g. find_tag(...)[0] is the tag of the found record. * e.g. FindTag(...)[0] is the tag of the found record.
*/ */
uint8_t * uint8_t *
find_tag(Arena &arena, uint8_t *cursor, Record &rec) FindTag(Arena &arena, uint8_t *cursor, Record &rec)
{
cursor = LocateTag(arena, cursor, rec);
if (rec.Tag != TAG_EMPTY) {
cursor = SkipRecord(rec, cursor);
}
return cursor;
}
uint8_t *
LocateTag(Arena &arena, uint8_t *cursor, Record &rec)
{ {
uint8_t tag, len; uint8_t tag, len;
if (cursor == NULL) { if (cursor == NULL) {
cursor = arena.store; cursor = arena.Store;
} }
while ((tag = cursor[0]) != rec.Tag) { while ((tag = cursor[0]) != rec.Tag) {
len = cursor[1]; len = cursor[1];
if (!space_available(arena, cursor, len)) { if (!spaceAvailable(arena, cursor, len)) {
return NULL; return NULL;
} }
cursor += len; cursor += len;
@ -98,40 +117,38 @@ find_tag(Arena &arena, uint8_t *cursor, Record &rec)
} }
if (tag != TAG_EMPTY) { if (tag != TAG_EMPTY) {
read_from_memory(rec, cursor); ReadFromMemory(rec, cursor);
cursor = skip_record(rec, cursor);
} }
return cursor; return cursor;
} }
uint8_t * uint8_t *
find_empty(Arena &arena, uint8_t *cursor) { FindEmpty(Arena &arena, uint8_t *cursor) {
Record rec; Record rec;
rec.Tag = TAG_EMPTY; rec.Tag = TAG_EMPTY;
return find_tag(arena, cursor, rec); return FindTag(arena, cursor, rec);
} }
uint8_t * uint8_t *
skip_record(Record &rec, uint8_t *cursor) SkipRecord(Record &rec, uint8_t *cursor)
{ {
return (uint8_t *)((uintptr_t)cursor + rec.Len + 2); return (uint8_t *)((uintptr_t)cursor + rec.Len + 2);
} }
void void
delete_record(Arena &arena, uint8_t *cursor) DeleteRecord(Arena &arena, uint8_t *cursor)
{ {
//
if (cursor == NULL) { if (cursor == NULL) {
return; return;
} }
uint8_t len = cursor[1] + 2; uint8_t len = cursor[1] + 2;
uint8_t *stop = arena.store + arena.size; uint8_t *stop = arena.Store + arena.Size;
stop -= len; stop -= len;

20
TLV.h
View File

@ -24,18 +24,22 @@ struct Record {
}; };
uint8_t *write_to_memory(Arena &, uint8_t *, Record &); uint8_t *WriteToMemory(Arena &, uint8_t *, Record &);
void read_from_memory(Record &, uint8_t *); void ReadFromMemory(Record &, uint8_t *);
void set_record(Record &, uint8_t, uint8_t, const char *); void SetRecord(Record &, uint8_t, uint8_t, const char *);
void delete_record(Arena &, uint8_t *); void DeleteRecord(Arena &, uint8_t *);
/* /*
* returns a pointer to memory where the record was found, * returns a pointer to memory where the record was found,
* e.g. find_tag(...)[0] is the tag of the found record. * e.g. LocateTag(...)[0] is the tag of the found record.
* FindTag will call LocateTag and then SkipRecord if the
* tag was found.
*/ */
uint8_t *find_tag(Arena &, uint8_t *, Record &); uint8_t *FindTag(Arena &, uint8_t *, Record &);
uint8_t *find_empty(Arena &, uint8_t *); uint8_t *LocateTag(Arena &, uint8_t *, Record &);
uint8_t *skip_record(Record &, uint8_t *);
uint8_t *FindEmpty(Arena &, uint8_t *);
uint8_t *SkipRecord(Record &, uint8_t *);
} // namespace TLV } // namespace TLV

View File

@ -12,14 +12,12 @@ constexpr char TEST_PBSTR2[] = "baz";
constexpr uint8_t TEST_PBSTRLEN2 = 3; constexpr uint8_t TEST_PBSTRLEN2 = 3;
constexpr char TEST_PBSTR3[] = "quux"; constexpr char TEST_PBSTR3[] = "quux";
constexpr uint8_t TEST_PBSTRLEN3 = 4; constexpr uint8_t TEST_PBSTRLEN3 = 4;
/*
constexpr char TEST_PBSTR4[] = "spam"; constexpr char TEST_PBSTR4[] = "spam";
constexpr uint8_t TEST_PBSTRLEN4 = 4; constexpr uint8_t TEST_PBSTRLEN4 = 4;
constexpr char TEST_PBSTR5[] = "xyzzx"; constexpr char TEST_PBSTR5[] = "xyzzx";
constexpr uint8_t TEST_PBSTRLEN5 = 5; constexpr uint8_t TEST_PBSTRLEN5 = 5;
constexpr char TEST_PBSTR6[] = "corvid"; constexpr char TEST_PBSTR6[] = "corvid";
constexpr uint8_t TEST_PBSTRLEN6 = 6; constexpr uint8_t TEST_PBSTRLEN6 = 6;
*/
static bool static bool
@ -27,9 +25,9 @@ test_setpb(Phonebook &pb, const char *k, uint8_t kl, const char *v,
uint8_t vl) uint8_t vl)
{ {
bool ok; bool ok;
std::cout << "test set " << k << "->" << v << std::endl; std::cout << "test Set " << k << "->" << v << std::endl;
ok = pb.set(k, kl, v, vl) == 0; ok = pb.Set(k, kl, v, vl) == 0;
std::cout << "\tset complete\n"; std::cout << "\tSet complete\n";
return ok; return ok;
} }
@ -38,21 +36,69 @@ int
main(int argc, const char *argv[]) main(int argc, const char *argv[])
{ {
Arena arena; Arena arena;
//TLV::Record value; TLV::Record value;
//TLV::Record expect; TLV::Record expect;
std::cout << "TESTPROG: " << argv[0] << std::endl; std::cout << "TESTPROG: " << argv[0] << std::endl;
if (create_arena(arena, ARENA_FILE, ARENA_SIZE, 0644) == -1) { InitializeArena(arena);
#if defined(__linux__)
if (CreateArena(arena, ARENA_FILE, ARENA_SIZE, 0644) == -1) {
abort(); abort();
} }
#else
if (AllocNewArena(arena, ARENA_SIZE) == -1) {
abort();
}
#endif
DisplayArena(arena);
TLV::SetRecord(expect, PHONEBOOK_VAL_TAG, TEST_PBSTRLEN3, TEST_PBSTR3);
Phonebook pb(arena); Phonebook pb(arena);
assert(!pb.has(TEST_PBSTR2, TEST_PBSTRLEN2)); assert(!pb.Has(TEST_PBSTR2, TEST_PBSTRLEN2));
assert(test_setpb(pb, TEST_PBSTR1, TEST_PBSTRLEN1, TEST_PBSTR3, assert(test_setpb(pb, TEST_PBSTR1, TEST_PBSTRLEN1, TEST_PBSTR3,
TEST_PBSTRLEN3)); TEST_PBSTRLEN3));
pb.DumpKVPairs();
assert(test_setpb(pb, TEST_PBSTR2, TEST_PBSTRLEN2, TEST_PBSTR3, assert(test_setpb(pb, TEST_PBSTR2, TEST_PBSTRLEN2, TEST_PBSTR3,
TEST_PBSTRLEN3)); TEST_PBSTRLEN3));
pb.DumpKVPairs();
assert(pb.Has(TEST_PBSTR2, TEST_PBSTRLEN2));
assert(test_setpb(pb, TEST_PBSTR4, TEST_PBSTRLEN4, TEST_PBSTR5,
TEST_PBSTRLEN5));
pb.DumpKVPairs();
assert(pb.Lookup(TEST_PBSTR2, TEST_PBSTRLEN2, value));
assert(cmp_record(value, expect));
std::cout << "test overwriting key" << std::endl;
assert(test_setpb(pb, TEST_PBSTR2, TEST_PBSTRLEN2, TEST_PBSTR6,
TEST_PBSTRLEN6));
pb.DumpKVPairs();
TLV::SetRecord(expect, PHONEBOOK_VAL_TAG, TEST_PBSTRLEN6, TEST_PBSTR6);
std::cout << "\tlookup" << std::endl;
assert(pb.Lookup(TEST_PBSTR2, TEST_PBSTRLEN2, value));
std::cout << "\tcompare records" << std::endl;
assert(cmp_record(value, expect));
std::cout << "\tadd new key to phonebook" << std::endl;
assert(test_setpb(pb, TEST_PBSTR3, TEST_PBSTRLEN3, TEST_PBSTR5,
TEST_PBSTRLEN5));
pb.DumpKVPairs();
TLV::SetRecord(expect, PHONEBOOK_VAL_TAG, TEST_PBSTRLEN5, TEST_PBSTR5);
assert(pb.Lookup(TEST_PBSTR4, TEST_PBSTRLEN4, value));
assert(cmp_record(value, expect));
std::cout << "OK" <<std::endl; std::cout << "OK" <<std::endl;
// Dump the generated arena for inspection later.
#if defined(__linux__)
#else
pb.DumpToFile(ARENA_FILE);
#endif
ClearArena(arena);
pb.DumpKVPairs();
} }

View File

@ -17,4 +17,23 @@
#define TEST_STR4 "How is a raven like a writing desk?" #define TEST_STR4 "How is a raven like a writing desk?"
#define TEST_STRLEN4 35 #define TEST_STRLEN4 35
static bool
cmp_record(TLV::Record &a, TLV::Record &b)
{
if (a.Tag != b .Tag) {
return false;
}
if (a.Len != b.Len) {
return false;
}
if (memcmp(a.Val, b.Val, a.Len) != 0) {
return false;
}
return true;
}
#endif #endif

View File

@ -11,60 +11,41 @@
static uint8_t arena_buffer[ARENA_SIZE]; static uint8_t arena_buffer[ARENA_SIZE];
static bool
cmp_record(TLV::Record &a, TLV::Record &b)
{
if (a.Tag != b .Tag) {
return false;
}
if (a.Len != b.Len) {
return false;
}
if (memcmp(a.Val, b.Val, a.Len) != 0) {
return false;
}
return true;
}
bool bool
tlv_test_suite(Arena &backend) tlv_test_suite(Arena &backend)
{ {
TLV::Record rec1, rec2, rec3, rec4; TLV::Record rec1, rec2, rec3, rec4;
uint8_t *cursor = NULL; uint8_t *cursor = NULL;
TLV::set_record(rec1, 1, TEST_STRLEN1, TEST_STR1); TLV::SetRecord(rec1, 1, TEST_STRLEN1, TEST_STR1);
TLV::set_record(rec2, 2, TEST_STRLEN2, TEST_STR2); TLV::SetRecord(rec2, 2, TEST_STRLEN2, TEST_STR2);
TLV::set_record(rec3, 1, TEST_STRLEN4, TEST_STR4); TLV::SetRecord(rec3, 1, TEST_STRLEN4, TEST_STR4);
rec4.Tag = 1; rec4.Tag = 1;
assert(TLV::write_to_memory(backend, cursor, rec1) != NULL); assert(TLV::WriteToMemory(backend, cursor, rec1) != NULL);
assert((cursor = TLV::write_to_memory(backend, cursor, rec2)) != NULL); assert((cursor = TLV::WriteToMemory(backend, cursor, rec2)) != NULL);
assert(TLV::write_to_memory(backend, cursor, rec3) != NULL); assert(TLV::WriteToMemory(backend, cursor, rec3) != NULL);
cursor = NULL; cursor = NULL;
// the cursor should point at the next record, // the cursor should point at the next record,
// and rec4 should contain the same data as rec1. // and rec4 should contain the same data as rec1.
cursor = TLV::find_tag(backend, cursor, rec4); cursor = TLV::FindTag(backend, cursor, rec4);
assert(cursor != NULL); assert(cursor != NULL);
assert(cursor != backend.store); assert(cursor != backend.Store);
assert(cmp_record(rec1, rec4)); assert(cmp_record(rec1, rec4));
cursor = TLV::find_tag(backend, cursor, rec4); cursor = TLV::FindTag(backend, cursor, rec4);
assert(cursor != NULL); assert(cursor != NULL);
assert(cmp_record(rec3, rec4)); assert(cmp_record(rec3, rec4));
TLV::set_record(rec4, 3, TEST_STRLEN3, TEST_STR3); TLV::SetRecord(rec4, 3, TEST_STRLEN3, TEST_STR3);
assert(TLV::write_to_memory(backend, NULL, rec4)); assert(TLV::WriteToMemory(backend, NULL, rec4));
rec4.Tag = 2; rec4.Tag = 2;
cursor = TLV::find_tag(backend, NULL, rec4); cursor = TLV::FindTag(backend, NULL, rec4);
assert(cursor != NULL); assert(cursor != NULL);
TLV::delete_record(backend, cursor); TLV::DeleteRecord(backend, cursor);
assert(cursor[0] == 3); assert(cursor[0] == 3);
return true; return true;
@ -73,6 +54,8 @@ tlv_test_suite(Arena &backend)
bool bool
run_suite(Arena &backend, const char *label) run_suite(Arena &backend, const char *label)
{ {
DisplayArena(backend);
std::cout << "running test suite " << label << ": "; std::cout << "running test suite " << label << ": ";
if (!tlv_test_suite(backend)) { if (!tlv_test_suite(backend)) {
std::cout << "FAILED" << std::endl; std::cout << "FAILED" << std::endl;
@ -81,7 +64,7 @@ run_suite(Arena &backend, const char *label)
std::cout << "OK" << std::endl; std::cout << "OK" << std::endl;
std::cout << "\tdestroying arena: "; std::cout << "\tdestroying arena: ";
if (arena_destroy(backend) != 0) { if (DestroyArena(backend) != 0) {
std::cout << "FAILED" << std::endl; std::cout << "FAILED" << std::endl;
return false; return false;
} }
@ -99,26 +82,31 @@ main(int argc, const char *argv[])
Arena arena_file; Arena arena_file;
std::cout << "TESTPROG: " << argv[0] << std::endl; std::cout << "TESTPROG: " << argv[0] << std::endl;
InitializeArena(arena_static);
InitializeArena(arena_mem);
InitializeArena(arena_file);
if (-1 == new_arena(arena_static, arena_buffer, ARENA_SIZE)) { if (-1 == NewStaticArena(arena_static, arena_buffer, ARENA_SIZE)) {
abort(); abort();
} else if (!run_suite(arena_static, "arena_static")) { } else if (!run_suite(arena_static, "arena_static")) {
abort(); abort();
} }
ClearArena(arena_static);
if (-1 == create_arena(arena_file, ARENA_FILE, ARENA_SIZE, 0644)) { #if defined(__linux__)
if (-1 == CreateArena(arena_file, ARENA_FILE, ARENA_SIZE, 0644)) {
abort(); abort();
} else if (!run_suite(arena_file, "arena_file")) { } else if (!run_suite(arena_file, "arena_file")) {
abort(); abort();
} }
#endif
if (-1 == alloc_new_arena(arena_mem, ARENA_SIZE)) { if (-1 == AllocNewArena(arena_mem, ARENA_SIZE)) {
abort(); abort();
} else if (!run_suite(arena_mem, "arena_mem")) { } else if (!run_suite(arena_mem, "arena_mem")) {
abort(); abort();
} }
std::cout << "OK" << std::endl; std::cout << "OK" << std::endl;
return 0; return 0;
} }