Initial import.
This commit is contained in:
commit
8b8be9421a
|
@ -0,0 +1,6 @@
|
||||||
|
*.a
|
||||||
|
*.o
|
||||||
|
*.bin
|
||||||
|
build
|
||||||
|
cmake-build-*
|
||||||
|
tlv_test
|
|
@ -0,0 +1,195 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Arena.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define ARENA_UNINIT 0
|
||||||
|
#define ARENA_STATIC 1
|
||||||
|
#define ARENA_ALLOC 2
|
||||||
|
#define ARENA_MMAP 3
|
||||||
|
|
||||||
|
#define PROT_RW PROT_READ|PROT_WRITE
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
new_arena(Arena &arena, uint8_t *mem, size_t size)
|
||||||
|
{
|
||||||
|
arena.store = mem;
|
||||||
|
arena.size = size;
|
||||||
|
arena.type = ARENA_STATIC;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
alloc_new_arena(Arena &arena, size_t size)
|
||||||
|
{
|
||||||
|
if (arena.size > 0) {
|
||||||
|
if (arena_destroy(arena) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arena.type = ARENA_ALLOC;
|
||||||
|
arena.size = size;
|
||||||
|
arena.store = (uint8_t *)calloc(sizeof(uint8_t), size);
|
||||||
|
if (arena.store == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
int
|
||||||
|
mmap_arena(Arena &arena, int fd, size_t size)
|
||||||
|
{
|
||||||
|
if (arena.size > 0) {
|
||||||
|
if (arena_destroy(arena) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arena.type = ARENA_MMAP;
|
||||||
|
arena.size = size;
|
||||||
|
arena.store = (uint8_t *)mmap(NULL, size, PROT_RW, MAP_SHARED, fd, 0);
|
||||||
|
if ((void *)arena.store == MAP_FAILED) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
arena.fd = fd;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
open_arena(Arena &arena, const char *path)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (arena.size > 0) {
|
||||||
|
if (arena_destroy(arena) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat(path, &st) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
arena.fd = open(path, O_RDWR);
|
||||||
|
if (arena.fd == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mmap_arena(arena, arena.fd, (size_t)st.st_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
create_arena(Arena &arena, const char *path, size_t size, mode_t mode)
|
||||||
|
{
|
||||||
|
int fd = 0;
|
||||||
|
|
||||||
|
if (arena.size > 0) {
|
||||||
|
if (arena_destroy(arena) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(path, O_WRONLY|O_CREAT, mode);
|
||||||
|
if (fd == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ftruncate(fd, size) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return open_arena(arena, path);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
arena_clear(Arena &arena)
|
||||||
|
{
|
||||||
|
if (arena.size == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(arena.store, 0, arena.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
arena_destroy(Arena &arena)
|
||||||
|
{
|
||||||
|
if (arena.type == ARENA_UNINIT) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (arena.type) {
|
||||||
|
case ARENA_STATIC:
|
||||||
|
break;
|
||||||
|
case ARENA_ALLOC:
|
||||||
|
free(arena.store);
|
||||||
|
break;
|
||||||
|
case ARENA_MMAP:
|
||||||
|
if (munmap(arena.store, arena.size) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (close(arena.fd) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
arena.fd = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
arena.type = ARENA_UNINIT;
|
||||||
|
arena.size = 0;
|
||||||
|
arena.store = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
write_arena(Arena &arena, const char *path)
|
||||||
|
{
|
||||||
|
FILE *arenaFile = NULL;
|
||||||
|
int retc = -1;
|
||||||
|
|
||||||
|
arenaFile = fopen(path, "w");
|
||||||
|
if (arenaFile == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(arena.store, sizeof(*arena.store), arena.size,
|
||||||
|
arenaFile) == arena.size) {
|
||||||
|
retc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fclose(arenaFile) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retc;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef KIMODEM_ARENA_H
|
||||||
|
#define KIMODEM_ARENA_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *store;
|
||||||
|
size_t size;
|
||||||
|
int fd;
|
||||||
|
uint8_t type;
|
||||||
|
} Arena;
|
||||||
|
|
||||||
|
|
||||||
|
int new_arena(Arena &, uint8_t *, size_t);
|
||||||
|
int alloc_new_arena(Arena &, size_t);
|
||||||
|
#if defined(__linux__)
|
||||||
|
int mmap_arena(Arena &, int); /* arena will own fd */
|
||||||
|
int create_arena(Arena &arena, const char *path, size_t size, mode_t mode);
|
||||||
|
int open_arena(Arena &, const char *, size_t);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void arena_clear(Arena &);
|
||||||
|
int arena_destroy(Arena &); /* dispose of any memory used by arena */
|
||||||
|
|
||||||
|
/* DANGER: if arena is file backed (mmap or open), DO NOT WRITE TO THE
|
||||||
|
* BACKING FILE! */
|
||||||
|
int write_arena(const char *);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
cmake_minimum_required(VERSION 3.25)
|
||||||
|
project(klib CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
|
|
||||||
|
add_library(klib STATIC
|
||||||
|
Arena.cc
|
||||||
|
TLV.cc)
|
||||||
|
|
||||||
|
add_executable(tlv_test tlv_test.cc)
|
||||||
|
target_link_libraries(tlv_test klib)
|
|
@ -0,0 +1,44 @@
|
||||||
|
TARGET := klib.a
|
||||||
|
TESTS := tlv_test
|
||||||
|
HEADERS := $(wildcard *.h)
|
||||||
|
SOURCES := $(wildcard *.cc)
|
||||||
|
OBJS := Arena.o Phonebook.o TLV.o
|
||||||
|
|
||||||
|
CXX := clang++
|
||||||
|
CXXFLAGS := -g -std=c++14 -Werror -Wall
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: $(TARGET) $(TESTS) tags run-tests
|
||||||
|
|
||||||
|
tags: $(HEADERS) $(SOURCES)
|
||||||
|
ctags $(HEADERS) $(SOURCES)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJS)
|
||||||
|
$(AR) rcs $@ $(OBJS)
|
||||||
|
|
||||||
|
tlv_test: tlv_test.o $(TARGET)
|
||||||
|
$(CXX) -o $@ $(CXXFLAGS) $@.o $(TARGET)
|
||||||
|
|
||||||
|
.PHONY: print-%
|
||||||
|
print-%: ; @echo '$(subst ','\'',$*=$($*))'
|
||||||
|
|
||||||
|
klib.a: $(OBJS)
|
||||||
|
|
||||||
|
%.o: %.cc
|
||||||
|
$(CXX) -o $@ -c $(CXXFLAGS) $<
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
# build outputs
|
||||||
|
rm -f $(TARGET) $(TESTS) *.o
|
||||||
|
|
||||||
|
# test miscellaneous
|
||||||
|
rm -f core core.* tags arena_test.bin
|
||||||
|
|
||||||
|
.PHONY: run-tests
|
||||||
|
run-tests: $(TESTS)
|
||||||
|
for testbin in $(TESTS); \
|
||||||
|
do \
|
||||||
|
echo "./$${testbin}" ; \
|
||||||
|
./$${testbin}; \
|
||||||
|
done
|
|
@ -0,0 +1,82 @@
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include "Phonebook.h"
|
||||||
|
|
||||||
|
bool
|
||||||
|
Phonebook::lookup(const char *key, uint8_t klen, TLV::Record &res)
|
||||||
|
{
|
||||||
|
res.Tag = this->ktag;
|
||||||
|
uint8_t *cursor = TLV::find_tag(this->arena, NULL, res);
|
||||||
|
|
||||||
|
while (cursor != NULL) {
|
||||||
|
if ((klen == res.Len) &&
|
||||||
|
(memcmp(res.Val, key, klen) == 0)) {
|
||||||
|
TLV::read_from_memory(res, cursor);
|
||||||
|
if (res.Tag != this->vtag) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
cursor = TLV::find_tag(this->arena, cursor, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
Phonebook::set(const char *key, uint8_t klen, char *val, uint8_t vlen)
|
||||||
|
{
|
||||||
|
TLV::Record rec;
|
||||||
|
uint8_t *cursor = NULL;
|
||||||
|
|
||||||
|
set_record(rec, this->ktag, klen, key);
|
||||||
|
cursor = this->seek(key, klen);
|
||||||
|
if (cursor != NULL) {
|
||||||
|
TLV::delete_record(this->arena, cursor);
|
||||||
|
TLV::delete_record(this->arena, cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = TLV::write_to_memory(this->arena, NULL, rec);
|
||||||
|
if (cursor == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_record(rec, this->vtag, vlen, val);
|
||||||
|
if (TLV::write_to_memory(this->arena, NULL, rec) == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
Phonebook::seek(const char *key, uint8_t klen)
|
||||||
|
{
|
||||||
|
TLV::Record rec;
|
||||||
|
|
||||||
|
rec.Tag = this->ktag;
|
||||||
|
uint8_t *cursor = TLV::find_tag(this->arena, NULL, rec);
|
||||||
|
|
||||||
|
while (cursor != NULL) {
|
||||||
|
if ((klen == rec.Len) &&
|
||||||
|
(memcmp(rec.Val, key, klen) == 0)) {
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
cursor = TLV::skip_record(rec, cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Phonebook::has(const char *key, uint8_t klen)
|
||||||
|
{
|
||||||
|
return this->seek(key, klen) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef KLIB_PHONEBOOK_H
|
||||||
|
#define KLIB_PHONEBOOK_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "Arena.h"
|
||||||
|
#include "TLV.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define PHONEBOOK_KEY_TAG 1
|
||||||
|
#define PHONEBOOK_VAL_TAG 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Phonebook {
|
||||||
|
public:
|
||||||
|
Phonebook(Arena &arena) :
|
||||||
|
arena(arena),
|
||||||
|
ktag(PHONEBOOK_KEY_TAG),
|
||||||
|
vtag(PHONEBOOK_VAL_TAG) {} ;
|
||||||
|
Phonebook(Arena &arena, uint8_t kt, uint8_t vt) :
|
||||||
|
arena(arena),
|
||||||
|
ktag(kt),
|
||||||
|
vtag(vt) {};
|
||||||
|
|
||||||
|
bool lookup(const char *key, uint8_t klen, TLV::Record &res);
|
||||||
|
int set(const char *key, uint8_t klen, char *val, uint8_t vlen);
|
||||||
|
bool has(const char *key, uint8_t klen);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t *seek(const char *key, uint8_t klen);
|
||||||
|
|
||||||
|
Arena &arena;
|
||||||
|
uint8_t ktag;
|
||||||
|
uint8_t vtag;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,141 @@
|
||||||
|
#include <cstring>
|
||||||
|
#include "TLV.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define REC_SIZE(x) ((std::size_t)x.Len + 2)
|
||||||
|
|
||||||
|
|
||||||
|
namespace TLV {
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
space_available(Arena &arena, uint8_t *cursor, uint8_t len)
|
||||||
|
{
|
||||||
|
uintptr_t remaining = 0;
|
||||||
|
|
||||||
|
if (cursor == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining = (uintptr_t)cursor - (uintptr_t)arena.store;
|
||||||
|
remaining = arena.size - remaining;
|
||||||
|
return ((size_t)remaining >= ((size_t)len+2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
write_to_memory(Arena &arena, uint8_t *cursor, Record &rec)
|
||||||
|
{
|
||||||
|
// 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
|
||||||
|
// error.
|
||||||
|
//
|
||||||
|
// If, however, the user gives us a cursor, we'll trust it
|
||||||
|
// (though space_available will sanity check that cursor).
|
||||||
|
if (cursor == NULL) {
|
||||||
|
cursor = find_empty(arena, cursor);
|
||||||
|
if (cursor == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!space_available(arena, cursor, rec.Len)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(cursor, &rec, REC_SIZE(rec));
|
||||||
|
cursor = skip_record(rec, cursor);
|
||||||
|
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
set_record(Record &rec, uint8_t tag, uint8_t len, const char *val)
|
||||||
|
{
|
||||||
|
rec.Tag = tag;
|
||||||
|
rec.Len = len;
|
||||||
|
memcpy(rec.Val, val, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
read_from_memory(Record &rec, uint8_t *cursor)
|
||||||
|
{
|
||||||
|
rec.Tag = cursor[0];
|
||||||
|
rec.Len = cursor[1];
|
||||||
|
memcpy(rec.Val, cursor+2, rec.Len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* returns a pointer to memory where the record was found,
|
||||||
|
* e.g. find_tag(...)[0] is the tag of the found record.
|
||||||
|
*/
|
||||||
|
uint8_t *
|
||||||
|
find_tag(Arena &arena, uint8_t *cursor, Record &rec)
|
||||||
|
{
|
||||||
|
uint8_t tag, len;
|
||||||
|
|
||||||
|
if (cursor == NULL) {
|
||||||
|
cursor = arena.store;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((tag = cursor[0]) != rec.Tag) {
|
||||||
|
len = cursor[1];
|
||||||
|
if (!space_available(arena, cursor, len)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cursor += len;
|
||||||
|
cursor += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag != rec.Tag) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag != TAG_EMPTY) {
|
||||||
|
read_from_memory(rec, cursor);
|
||||||
|
cursor = skip_record(rec, cursor);
|
||||||
|
}
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
find_empty(Arena &arena, uint8_t *cursor) {
|
||||||
|
Record rec;
|
||||||
|
|
||||||
|
rec.Tag = TAG_EMPTY;
|
||||||
|
return find_tag(arena, cursor, rec);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
skip_record(Record &rec, uint8_t *cursor)
|
||||||
|
{
|
||||||
|
return (uint8_t *)((uintptr_t)cursor + rec.Len + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
delete_record(Arena &arena, uint8_t *cursor)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
if (cursor == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t len = cursor[1] + 2;
|
||||||
|
uintptr_t stop = (uintptr_t)arena.size - (uintptr_t)cursor;
|
||||||
|
|
||||||
|
stop -= len;
|
||||||
|
|
||||||
|
for (uintptr_t i = (uintptr_t)cursor; i < stop; i++) {
|
||||||
|
cursor[i] = cursor[i+len];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace TLV
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef KIMODEM_TLV_H
|
||||||
|
#define KIMODEM_TLV_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "Arena.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef TLV_MAX_LEN
|
||||||
|
#define TLV_MAX_LEN 253
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define TAG_EMPTY 0
|
||||||
|
|
||||||
|
|
||||||
|
namespace TLV {
|
||||||
|
|
||||||
|
|
||||||
|
struct Record {
|
||||||
|
uint8_t Tag;
|
||||||
|
uint8_t Len;
|
||||||
|
char Val[TLV_MAX_LEN];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t *write_to_memory(Arena &, uint8_t *, Record &);
|
||||||
|
void read_from_memory(Record &, uint8_t *);
|
||||||
|
void set_record(Record &, uint8_t, uint8_t, const char *);
|
||||||
|
void delete_record(Arena &, uint8_t *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* returns a pointer to memory where the record was found,
|
||||||
|
* e.g. find_tag(...)[0] is the tag of the found record.
|
||||||
|
*/
|
||||||
|
uint8_t *find_tag(Arena &, uint8_t *, Record &);
|
||||||
|
uint8_t *find_empty(Arena &, uint8_t *);
|
||||||
|
uint8_t *skip_record(Record &, uint8_t *);
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace TLV
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef KLIB_TEST_FIXTURES_H
|
||||||
|
#define KLIB_TEST_FIXTURES_H
|
||||||
|
|
||||||
|
|
||||||
|
#define ARENA_SIZE 128
|
||||||
|
#define ARENA_FILE "arena_test.bin"
|
||||||
|
|
||||||
|
/* strlen=13 */
|
||||||
|
#define TEST_STR1 "Hello, world"
|
||||||
|
#define TEST_STRLEN1 13
|
||||||
|
#define TEST_STR2 "Bye, world!!"
|
||||||
|
#define TEST_STRLEN2 13
|
||||||
|
#define TEST_STR3 "Hello, arena"
|
||||||
|
#define TEST_STRLEN3 13
|
||||||
|
|
||||||
|
/* strlen 35 */
|
||||||
|
#define TEST_STR4 "How is a raven like a writing desk?"
|
||||||
|
#define TEST_STRLEN4 35
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,120 @@
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "Arena.h"
|
||||||
|
#include "TLV.h"
|
||||||
|
|
||||||
|
#include "test_fixtures.h"
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
tlv_test_suite(Arena &backend)
|
||||||
|
{
|
||||||
|
TLV::Record rec1, rec2, rec3, rec4;
|
||||||
|
uint8_t *cursor = NULL;
|
||||||
|
|
||||||
|
TLV::set_record(rec1, 1, TEST_STRLEN1, TEST_STR1);
|
||||||
|
TLV::set_record(rec2, 2, TEST_STRLEN2, TEST_STR2);
|
||||||
|
TLV::set_record(rec3, 1, TEST_STRLEN4, TEST_STR4);
|
||||||
|
rec4.Tag = 1;
|
||||||
|
|
||||||
|
assert(TLV::write_to_memory(backend, cursor, rec1) != NULL);
|
||||||
|
assert((cursor = TLV::write_to_memory(backend, cursor, rec2)) != NULL);
|
||||||
|
assert(TLV::write_to_memory(backend, cursor, rec3) != NULL);
|
||||||
|
cursor = NULL;
|
||||||
|
|
||||||
|
// the cursor should point at the next record,
|
||||||
|
// and rec4 should contain the same data as rec1.
|
||||||
|
cursor = TLV::find_tag(backend, cursor, rec4);
|
||||||
|
assert(cursor != NULL);
|
||||||
|
assert(cursor != backend.store);
|
||||||
|
assert(cmp_record(rec1, rec4));
|
||||||
|
|
||||||
|
cursor = TLV::find_tag(backend, cursor, rec4);
|
||||||
|
assert(cursor != NULL);
|
||||||
|
assert(cmp_record(rec3, rec4));
|
||||||
|
|
||||||
|
TLV::set_record(rec4, 3, TEST_STRLEN3, TEST_STR3);
|
||||||
|
assert(TLV::write_to_memory(backend, NULL, rec4));
|
||||||
|
|
||||||
|
TLV::delete_record(backend, cursor);
|
||||||
|
assert(cursor[0] == TAG_EMPTY);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
run_suite(Arena &backend, const char *label)
|
||||||
|
{
|
||||||
|
std::cout << "running test suite " << label << ": ";
|
||||||
|
if (!tlv_test_suite(backend)) {
|
||||||
|
std::cout << "FAILED" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::cout << "OK" << std::endl;
|
||||||
|
|
||||||
|
std::cout << "\tdestroying arena: ";
|
||||||
|
if (arena_destroy(backend) != 0) {
|
||||||
|
std::cout << "FAILED" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "OK" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, const char *argv[])
|
||||||
|
{
|
||||||
|
Arena arena_static;
|
||||||
|
Arena arena_mem;
|
||||||
|
Arena arena_file;
|
||||||
|
|
||||||
|
std::cout << "TESTPROG: " << argv[0] << std::endl;
|
||||||
|
|
||||||
|
if (-1 == new_arena(arena_static, arena_buffer, ARENA_SIZE)) {
|
||||||
|
abort();
|
||||||
|
} else if (!run_suite(arena_static, "arena_static")) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-1 == create_arena(arena_file, ARENA_FILE, ARENA_SIZE, 0644)) {
|
||||||
|
abort();
|
||||||
|
} else if (!run_suite(arena_file, "arena_file")) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-1 == alloc_new_arena(arena_mem, ARENA_SIZE)) {
|
||||||
|
abort();
|
||||||
|
} else if (!run_suite(arena_mem, "arena_mem")) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::cout << "OK" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue