2023-10-16 09:18:09 +00:00
|
|
|
///
|
|
|
|
/// \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-16 09:18:09 +00:00
|
|
|
|
2023-10-06 03:13:46 +00:00
|
|
|
#include "TLV.h"
|
|
|
|
|
2023-10-15 01:38:01 +00:00
|
|
|
using namespace scsl;
|
2023-10-09 22:23:23 +00:00
|
|
|
|
2023-10-06 03:13:46 +00:00
|
|
|
|
2023-10-16 09:18:09 +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
|
|
|
{
|
2023-10-10 09:35:43 +00:00
|
|
|
if (cursor == nullptr) {
|
2023-10-06 03:13:46 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-10-09 22:23:23 +00:00
|
|
|
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)
|
|
|
|
{
|
2023-10-09 22:23:23 +00:00
|
|
|
uint8_t trail = TLV_MAX_LEN - rec.Len;
|
2023-10-06 06:08:35 +00:00
|
|
|
|
2023-10-09 22:23:23 +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
|
|
|
{
|
2023-10-10 09:35:43 +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).
|
2023-10-10 09:35:43 +00:00
|
|
|
if (cursor == nullptr) {
|
2023-10-06 06:08:35 +00:00
|
|
|
cursor = FindEmpty(arena, cursor);
|
2023-10-10 09:35:43 +00:00
|
|
|
if (cursor == nullptr) {
|
|
|
|
return nullptr;
|
2023-10-06 03:13:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-10 09:35:43 +00:00
|
|
|
if (!arena.CursorInArena(cursor)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2023-10-06 06:08:35 +00:00
|
|
|
if (!spaceAvailable(arena, cursor, rec.Len)) {
|
2023-10-10 09:35:43 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-10-09 22:23:23 +00:00
|
|
|
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];
|
2023-10-09 22:23:23 +00:00
|
|
|
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) {
|
2023-10-16 09:18:09 +00:00
|
|
|
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
|
|
|
{
|
2023-10-09 22:23:23 +00:00
|
|
|
uint8_t tag, len;
|
2023-10-06 03:13:46 +00:00
|
|
|
|
2023-10-16 09:18:09 +00:00
|
|
|
if (!arena.CursorInArena(cursor)) {
|
|
|
|
cursor = nullptr;
|
|
|
|
}
|
|
|
|
|
2023-10-10 09:35:43 +00:00
|
|
|
if (cursor == nullptr) {
|
2023-10-16 09:18:09 +00:00
|
|
|
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) {
|
2023-10-16 09:18:09 +00:00
|
|
|
assert(arena.CursorInArena(cursor));
|
|
|
|
std::cout << "cursor is in arena\n";
|
2023-10-06 03:13:46 +00:00
|
|
|
len = cursor[1];
|
2023-10-16 09:18:09 +00:00
|
|
|
std::cout << "record length" << len << "\n";
|
2023-10-06 06:08:35 +00:00
|
|
|
if (!spaceAvailable(arena, cursor, len)) {
|
2023-10-16 09:18:09 +00:00
|
|
|
std::cout << "no space available\n";
|
2023-10-10 09:35:43 +00:00
|
|
|
return nullptr;
|
2023-10-06 03:13:46 +00:00
|
|
|
}
|
|
|
|
cursor += len;
|
|
|
|
cursor += 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tag != rec.Tag) {
|
2023-10-10 09:35:43 +00:00
|
|
|
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 *
|
2023-10-09 22:23:23 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-10-09 22:23:23 +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
|
|
|
{
|
2023-10-09 22:23:23 +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
|
|
|
{
|
2023-10-10 09:35:43 +00:00
|
|
|
if (cursor == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!arena.CursorInArena(cursor)) {
|
2023-10-06 03:13:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-10-09 22:23:23 +00:00
|
|
|
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;
|
|
|
|
|
2023-10-06 03:49:17 +00:00
|
|
|
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
|