scsl/TLV.cc

212 lines
4.1 KiB
C++
Raw Permalink Normal View History

///
/// \file TLV.cc
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-06
/// \brief Tag-Length-Value records built on Arena.
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#include <cassert>
2023-10-06 03:13:46 +00:00
#include <cstring>
2023-10-06 03:13:46 +00:00
#include "TLV.h"
2023-10-15 01:38:01 +00:00
using namespace scsl;
2023-10-06 03:13:46 +00:00
/// REC_SIZE calculates the total length of a TLV record, including the
/// two byte header.
2023-10-06 03:13:46 +00:00
#define REC_SIZE(x) ((std::size_t)x.Len + 2)
2023-10-15 01:38:01 +00:00
namespace scsl {
2023-10-06 03:13:46 +00:00
namespace TLV {
static bool
2023-10-06 06:08:35 +00:00
spaceAvailable(Arena &arena, uint8_t *cursor, uint8_t len)
2023-10-06 03:13:46 +00:00
{
if (cursor == nullptr) {
2023-10-06 03:13:46 +00:00
return false;
}
return arena.CursorInArena(cursor + len);
2023-10-06 03:13:46 +00:00
}
2023-10-06 06:08:35 +00:00
static inline void
clearUnused(Record &rec)
{
uint8_t trail = TLV_MAX_LEN - rec.Len;
2023-10-06 06:08:35 +00:00
memset(rec.Val + rec.Len, 0, trail);
2023-10-06 06:08:35 +00:00
}
2023-10-06 03:13:46 +00:00
uint8_t *
2023-10-06 06:08:35 +00:00
WriteToMemory(Arena &arena, uint8_t *cursor, Record &rec)
2023-10-06 03:13:46 +00:00
{
// If cursor is nullptr, the user needs us to select an empty
2023-10-06 03:13:46 +00:00
// 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
2023-10-06 06:08:35 +00:00
// (though spaceAvailable will sanity check that cursor).
if (cursor == nullptr) {
2023-10-06 06:08:35 +00:00
cursor = FindEmpty(arena, cursor);
if (cursor == nullptr) {
return nullptr;
2023-10-06 03:13:46 +00:00
}
}
if (!arena.CursorInArena(cursor)) {
return nullptr;
}
2023-10-06 06:08:35 +00:00
if (!spaceAvailable(arena, cursor, rec.Len)) {
return nullptr;
2023-10-06 03:13:46 +00:00
}
memcpy(cursor, &rec, REC_SIZE(rec));
2023-10-06 06:08:35 +00:00
cursor = SkipRecord(rec, cursor);
2023-10-06 03:13:46 +00:00
return cursor;
}
void
2023-10-06 06:08:35 +00:00
SetRecord(Record &rec, uint8_t tag, uint8_t len, const char *val)
2023-10-06 03:13:46 +00:00
{
rec.Tag = tag;
rec.Len = len;
memcpy(rec.Val, val, len);
2023-10-06 06:08:35 +00:00
clearUnused(rec);
2023-10-06 03:13:46 +00:00
}
void
2023-10-06 06:08:35 +00:00
ReadFromMemory(Record &rec, uint8_t *cursor)
2023-10-06 03:13:46 +00:00
{
rec.Tag = cursor[0];
rec.Len = cursor[1];
memcpy(rec.Val, cursor + 2, rec.Len);
2023-10-06 06:08:35 +00:00
clearUnused(rec);
2023-10-06 03:13:46 +00:00
}
/*
* returns a pointer to memory where the record was found,
2023-10-06 06:08:35 +00:00
* e.g. FindTag(...)[0] is the tag of the found record.
2023-10-06 03:13:46 +00:00
*/
uint8_t *
2023-10-06 06:08:35 +00:00
FindTag(Arena &arena, uint8_t *cursor, Record &rec)
{
cursor = LocateTag(arena, cursor, rec);
if (rec.Tag != TAG_EMPTY) {
std::cout << "skipping record\n";
2023-10-06 06:08:35 +00:00
cursor = SkipRecord(rec, cursor);
}
return cursor;
}
uint8_t *
LocateTag(Arena &arena, uint8_t *cursor, Record &rec)
2023-10-06 03:13:46 +00:00
{
uint8_t tag, len;
2023-10-06 03:13:46 +00:00
if (!arena.CursorInArena(cursor)) {
cursor = nullptr;
}
if (cursor == nullptr) {
std::cout << "move cursor to arena start\n";
2023-10-15 01:38:01 +00:00
cursor = arena.Start();
2023-10-06 03:13:46 +00:00
}
while ((tag = cursor[0]) != rec.Tag) {
assert(arena.CursorInArena(cursor));
std::cout << "cursor is in arena\n";
2023-10-06 03:13:46 +00:00
len = cursor[1];
std::cout << "record length" << len << "\n";
2023-10-06 06:08:35 +00:00
if (!spaceAvailable(arena, cursor, len)) {
std::cout << "no space available\n";
return nullptr;
2023-10-06 03:13:46 +00:00
}
cursor += len;
cursor += 2;
}
if (tag != rec.Tag) {
return nullptr;
2023-10-06 03:13:46 +00:00
}
if (tag != TAG_EMPTY) {
2023-10-06 06:08:35 +00:00
ReadFromMemory(rec, cursor);
2023-10-06 03:13:46 +00:00
}
return cursor;
}
uint8_t *
FindEmpty(Arena &arena, uint8_t *cursor)
{
Record rec;
2023-10-06 03:13:46 +00:00
rec.Tag = TAG_EMPTY;
2023-10-06 06:08:35 +00:00
return FindTag(arena, cursor, rec);
2023-10-06 03:13:46 +00:00
}
uint8_t *
2023-10-06 06:08:35 +00:00
SkipRecord(Record &rec, uint8_t *cursor)
2023-10-06 03:13:46 +00:00
{
return (uint8_t *) ((uintptr_t) cursor + rec.Len + 2);
2023-10-06 03:13:46 +00:00
}
void
2023-10-06 06:08:35 +00:00
DeleteRecord(Arena &arena, uint8_t *cursor)
2023-10-06 03:13:46 +00:00
{
if (cursor == nullptr) {
return;
}
if (!arena.CursorInArena(cursor)) {
2023-10-06 03:13:46 +00:00
return;
}
uint8_t len = cursor[1] + 2;
2023-10-15 01:38:01 +00:00
uint8_t *stop = arena.Start() + arena.Size();
2023-10-06 03:13:46 +00:00
stop -= len;
while (cursor != stop) {
cursor[0] = cursor[len];
cursor++;
}
stop += len;
while (cursor != stop) {
cursor[0] = 0;
cursor++;
}
2023-10-06 03:13:46 +00:00
}
} // namespace TLV
2023-10-15 01:38:01 +00:00
} // namespace scsl