1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

mapping: rewrote using std::regex

This commit is contained in:
Steffen Vogel 2020-09-10 11:15:33 +02:00
parent de2b2d627c
commit a9eb2c1d67
4 changed files with 190 additions and 281 deletions

View file

@ -28,6 +28,16 @@
#include <villas/stats.hpp>
#include <villas/common.hpp>
#define RE_MAPPING_INDEX "[a-zA-Z0-9_]+"
#define RE_MAPPING_RANGE "(" RE_MAPPING_INDEX ")(?:-(" RE_MAPPING_INDEX "))?"
#define RE_MAPPING_STATS "stats\\.([a-z]+)\\.([a-z]+)"
#define RE_MAPPING_HDR "hdr\\.(sequence|length)"
#define RE_MAPPING_TS "ts\\.(origin|received)"
#define RE_MAPPING_DATA1 "data\\[" RE_MAPPING_RANGE "\\]"
#define RE_MAPPING_DATA2 "(?:data\\.)?(" RE_MAPPING_INDEX ")"
#define RE_MAPPING "(?:(" RE_NODE_NAME ")\\.(?:" RE_MAPPING_STATS "|" RE_MAPPING_HDR "|" RE_MAPPING_TS "|" RE_MAPPING_DATA1 "|" RE_MAPPING_DATA2 ")|(" RE_NODE_NAME ")(?:\\[" RE_MAPPING_RANGE "\\])?)"
/* Forward declarations */
struct vnode;
struct sample;
@ -52,6 +62,7 @@ enum class MappingTimestampType {
};
struct mapping_entry {
const char *node_name;
struct vnode *node; /**< The node to which this mapping refers. */
enum MappingType type; /**< The mapping type. Selects one of the union fields below. */
@ -67,6 +78,9 @@ struct mapping_entry {
struct {
int offset;
struct signal *signal;
const char *first;
const char *last;
} data;
struct {
@ -84,16 +98,18 @@ struct mapping_entry {
};
};
int mapping_update(const struct mapping_entry *e, struct sample *remapped, const struct sample *original);
int mapping_entry_prepare(struct mapping_entry *me, struct vlist *nodes);
int mapping_parse(struct mapping_entry *e, json_t *cfg, struct vlist *nodes);
int mapping_entry_update(const struct mapping_entry *e, struct sample *remapped, const struct sample *original);
int mapping_parse_str(struct mapping_entry *e, const char *str, struct vlist *nodes);
int mapping_entry_parse(struct mapping_entry *e, json_t *cfg);
int mapping_to_str(const struct mapping_entry *me, unsigned index, char **str);
int mapping_entry_parse_str(struct mapping_entry *e, const std::string &str);
int mapping_list_parse(struct vlist *ml, json_t *cfg, struct vlist *nodes);
int mapping_entry_to_str(const struct mapping_entry *me, unsigned index, char **str);
int mapping_list_prepare(struct vlist *ml);
int mapping_list_parse(struct vlist *ml, json_t *cfg);
int mapping_list_prepare(struct vlist *ml, struct vlist *nodes);
int mapping_list_remap(const struct vlist *ml, struct sample *remapped, const struct sample *original);

View file

@ -20,7 +20,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <cstring>
#include <regex>
#include <iostream>
#include <villas/mapping.h>
#include <villas/sample.h>
@ -33,162 +34,83 @@
using namespace villas;
using namespace villas::utils;
int mapping_parse_str(struct mapping_entry *me, const char *str, struct vlist *nodes)
int mapping_entry_parse_str(struct mapping_entry *me, const std::string &str)
{
char *cpy, *node, *type, *field, *end, *lasts;
std::smatch mr;
std::regex re(RE_MAPPING);
cpy = strdup(str);
if (!cpy)
return -1;
if (!std::regex_match(str, mr, re))
goto invalid_format;
if (nodes) {
node = strtok_r(cpy, ".", &lasts);
if (!node) {
warning("Missing node name");
goto invalid_format;
}
if (mr[1].matched)
me->node_name = strdup(mr.str(1).c_str());
if (mr[9].matched)
me->node_name = strdup(mr.str(9).c_str());
me->node = vlist_lookup_name<struct vnode>(nodes, node);
if (!me->node) {
warning("Unknown node %s", node);
goto invalid_format;
}
type = strtok_r(nullptr, ".[", &lasts);
if (!type)
type = strf("data");
}
else {
me->node = nullptr;
type = strtok_r(cpy, ".[", &lasts);
if (!type)
goto invalid_format;
}
if (!strcmp(type, "stats")) {
me->type = MappingType::STATS;
me->length = 1;
char *metric = strtok_r(nullptr, ".", &lasts);
if (!metric)
goto invalid_format;
type = strtok_r(nullptr, ".", &lasts);
if (!type)
goto invalid_format;
me->stats.metric = Stats::lookupMetric(metric);
me->stats.type = Stats::lookupType(type);
}
else if (!strcmp(type, "hdr")) {
me->type = MappingType::HEADER;
me->length = 1;
field = strtok_r(nullptr, ".", &lasts);
if (!field) {
warning("Missing header type");
goto invalid_format;
}
if (!strcmp(field, "sequence"))
me->header.type = MappingHeaderType::SEQUENCE;
else if (!strcmp(field, "length"))
me->header.type = MappingHeaderType::LENGTH;
else {
warning("Invalid header type");
goto invalid_format;
}
}
else if (!strcmp(type, "ts")) {
me->type = MappingType::TIMESTAMP;
me->length = 2;
field = strtok_r(nullptr, ".", &lasts);
if (!field) {
warning("Missing timestamp type");
goto invalid_format;
}
if (!strcmp(field, "origin"))
me->timestamp.type = MappingTimestampType::ORIGIN;
else if (!strcmp(field, "received"))
me->timestamp.type = MappingTimestampType::RECEIVED;
else {
warning("Invalid timestamp type");
goto invalid_format;
}
}
else if (!strcmp(type, "data")) {
char *first_str, *last_str;
int first = -1, last = -1;
if (mr[6].matched) {
me->data.first = strdup(mr.str(6).c_str());
if (mr[7].matched)
me->data.last = strdup(mr.str(7).c_str());
me->type = MappingType::DATA;
}
else if (mr[10].matched) {
me->data.first = strdup(mr.str(10).c_str());
if (mr[11].matched)
me->data.last = strdup(mr.str(11).c_str());
first_str = strtok_r(nullptr, "-]", &lasts);
if (first_str) {
if (me->node)
first = vlist_lookup_index<struct signal>(&me->node->in.signals, first_str);
me->type = MappingType::DATA;
}
else if (mr[8].matched) {
me->data.first = strdup(mr.str(8).c_str());
if (first < 0) {
char *endptr;
first = strtoul(first_str, &endptr, 10);
if (endptr != first_str + strlen(first_str)) {
warning("Failed to parse data range");
goto invalid_format;
}
}
}
else {
/* Map all signals */
me->data.offset = 0;
me->length = -1;
goto end;
}
me->type = MappingType::DATA;
}
else if (mr[2].matched) {
me->stats.type = Stats::lookupType(mr.str(3));
me->stats.metric = Stats::lookupMetric(mr.str(2));
last_str = strtok_r(nullptr, "]", &lasts);
if (last_str) {
if (me->node)
last = vlist_lookup_index<struct signal>(&me->node->in.signals, last_str);
if (last < 0) {
char *endptr;
last = strtoul(last_str, &endptr, 10);
if (endptr != last_str + strlen(last_str)) {
warning("Failed to parse data range");
goto invalid_format;
}
}
}
me->type = MappingType::STATS;
}
else if (mr[5].matched) {
if (mr.str(5) == "origin")
me->timestamp.type = MappingTimestampType::ORIGIN;
else if (mr.str(5) == "received")
me->timestamp.type = MappingTimestampType::RECEIVED;
else
last = first; /* single element: data[5] => data[5-5] */
if (last < first)
goto invalid_format;
me->data.offset = first;
me->length = last - first + 1;
me->type = MappingType::TIMESTAMP;
}
else
goto invalid_format;
else if (mr[4].matched) {
if (mr.str(4) == "sequence")
me->header.type = MappingHeaderType::SEQUENCE;
else if (mr.str(4) == "length")
me->header.type = MappingHeaderType::LENGTH;
else
goto invalid_format;
end: /* Check that there is no garbage at the end */
end = strtok_r(nullptr, "", &lasts);
if (end)
goto invalid_format;
me->type = MappingType::HEADER;
}
/* Only node name given.. We map all data */
else if (me->node_name) {
me->data.first = nullptr;
me->data.last = nullptr;
free(cpy);
me->type = MappingType::DATA;
}
return 0;
invalid_format:
free(cpy);
return -1;
throw RuntimeError("Failed to parse mapping expression: {}", str);
}
int mapping_parse(struct mapping_entry *me, json_t *cfg, struct vlist *nodes)
int mapping_entry_parse(struct mapping_entry *me, json_t *cfg)
{
const char *str;
@ -196,10 +118,10 @@ int mapping_parse(struct mapping_entry *me, json_t *cfg, struct vlist *nodes)
if (!str)
return -1;
return mapping_parse_str(me, str, nodes);
return mapping_entry_parse_str(me, str);
}
int mapping_list_parse(struct vlist *ml, json_t *cfg, struct vlist *nodes)
int mapping_list_parse(struct vlist *ml, json_t *cfg)
{
int ret;
@ -221,7 +143,7 @@ int mapping_list_parse(struct vlist *ml, json_t *cfg, struct vlist *nodes)
if (!me)
throw MemoryAllocationError();
ret = mapping_parse(me, json_entry, nodes);
ret = mapping_entry_parse(me, json_entry);
if (ret)
goto out;
@ -319,16 +241,76 @@ int mapping_list_remap(const struct vlist *ml, struct sample *remapped, const st
return 0;
}
int mapping_list_prepare(struct vlist *ml)
int mapping_entry_prepare(struct mapping_entry *me, struct vlist *nodes)
{
if (me->node_name && me->node == nullptr) {
me->node = vlist_lookup_name<struct vnode>(nodes, me->node_name);
if (!me->node)
throw RuntimeError("Invalid node name in mapping: {}", me->node_name);
}
if (me->type == MappingType::DATA) {
int first = -1, last = -1;
if (me->data.first) {
if (me->node)
first = vlist_lookup_index<struct signal>(&me->node->in.signals, me->data.first);
if (first < 0) {
char *endptr;
first = strtoul(me->data.first, &endptr, 10);
if (endptr != me->data.first + strlen(me->data.first))
throw RuntimeError("Failed to parse data index in mapping: {}", me->data.first);
}
}
else {
/* Map all signals */
me->data.offset = 0;
me->length = -1;
goto end;
}
if (me->data.last) {
if (me->node)
last = vlist_lookup_index<struct signal>(&me->node->in.signals, me->data.last);
if (last < 0) {
char *endptr;
last = strtoul(me->data.last, &endptr, 10);
if (endptr != me->data.last + strlen(me->data.last))
throw RuntimeError("Failed to parse data index in mapping: {}", me->data.last);
}
}
else
last = first; /* single element: data[5] => data[5-5] */
if (last < first)
throw RuntimeError("Invalid data range indices for mapping: {} < {}", last, first);
me->data.offset = first;
me->length = last - first + 1;
}
end:
if (me->length < 0) {
struct vlist *sigs = node_get_signals(me->node, NodeDir::IN);
me->length = vlist_length(sigs);
}
return 0;
}
int mapping_list_prepare(struct vlist *ml, struct vlist *nodes)
{
int ret;
for (size_t i = 0, off = 0; i < vlist_length(ml); i++) {
struct mapping_entry *me = (struct mapping_entry *) vlist_at(ml, i);
if (me->length < 0) {
struct vlist *sigs = node_get_signals(me->node, NodeDir::IN);
me->length = vlist_length(sigs);
}
ret = mapping_entry_prepare(me, nodes);
if (ret)
return ret;
me->offset = off;
off += me->length;
@ -337,7 +319,7 @@ int mapping_list_prepare(struct vlist *ml)
return 0;
}
int mapping_to_str(const struct mapping_entry *me, unsigned index, char **str)
int mapping_entry_to_str(const struct mapping_entry *me, unsigned index, char **str)
{
const char *type;

View file

@ -53,7 +53,7 @@ int signal_init_from_mapping(struct signal *s, const struct mapping_entry *me, u
if (ret)
return ret;
ret = mapping_to_str(me, index, &s->name);
ret = mapping_entry_to_str(me, index, &s->name);
if (ret)
return ret;

View file

@ -34,155 +34,66 @@ Test(mapping, parse_nodes)
{
int ret;
struct mapping_entry m;
struct vlist nodes;
const char *node_names[3] = { "apple", "cherry", "carrot" };
const char *signal_names[3][4] = {
{ "abra", "kadabra", "simsala", "bimm" },
{ "this", "is", "a", "test" },
{ "o", "sole", "mio", "italia" }
};
vlist_init(&nodes);
for (unsigned i = 0; i < ARRAY_LEN(node_names); i++) {
struct vnode *n = new struct vnode;
cr_assert_not_null(n);
n->name = strdup(node_names[i]);
vlist_init(&n->in.signals);
for (unsigned j = 0; j < ARRAY_LEN(signal_names[i]); j++) {
struct signal *sig;
sig = signal_create(signal_names[i][j], nullptr, SignalType::FLOAT);
cr_assert_not_null(sig);
vlist_push(&n->in.signals, sig);
}
vlist_push(&nodes, n);
}
ret = mapping_parse_str(&m, "apple.ts.origin", &nodes);
ret = mapping_entry_parse_str(&m, "apple.ts.origin");
cr_assert_eq(ret, 0);
cr_assert_eq(m.node, vlist_lookup_name<struct vnode>(&nodes, "apple"));
cr_assert_str_eq(m.node_name, "apple");
cr_assert_eq(m.type, MappingType::TIMESTAMP);
cr_assert_eq(m.timestamp.type, MappingTimestampType::ORIGIN);
ret = mapping_parse_str(&m, "cherry.stats.owd.mean", &nodes);
ret = mapping_entry_parse_str(&m, "cherry.stats.owd.mean");
cr_assert_eq(ret, 0);
cr_assert_eq(m.node, vlist_lookup_name<struct vnode>(&nodes, "cherry"));
cr_assert_str_eq(m.node_name, "cherry");
cr_assert_eq(m.type, MappingType::STATS);
cr_assert_eq(m.stats.metric, Stats::Metric::OWD);
cr_assert_eq(m.stats.type, Stats::Type::MEAN);
ret = mapping_parse_str(&m, "carrot.data[1-2]", &nodes);
ret = mapping_entry_parse_str(&m, "carrot.data[1-2]");
cr_assert_eq(ret, 0);
cr_assert_eq(m.node, vlist_lookup_name<struct vnode>(&nodes, "carrot"));
cr_assert_str_eq(m.node_name, "carrot");
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_eq(m.data.offset, 1);
cr_assert_eq(m.length, 2);
cr_assert_str_eq(m.data.first, "1");
cr_assert_str_eq(m.data.last, "2");
ret = mapping_parse_str(&m, "carrot", &nodes);
ret = mapping_entry_parse_str(&m, "carrot");
cr_assert_eq(ret, 0);
cr_assert_eq(m.node, vlist_lookup_name<struct vnode>(&nodes, "carrot"));
cr_assert_str_eq(m.node_name, "carrot");
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_eq(m.data.offset, 0);
cr_assert_eq(m.length, -1);
cr_assert_eq(m.data.first, nullptr);
cr_assert_eq(m.data.last, nullptr);
ret = mapping_parse_str(&m, "carrot.data[sole]", &nodes);
ret = mapping_entry_parse_str(&m, "carrot.data[sole]");
cr_assert_eq(ret, 0);
cr_assert_eq(m.node, vlist_lookup_name<struct vnode>(&nodes, "carrot"));
cr_assert_str_eq(m.node_name, "carrot");
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_eq(m.data.offset, 1);
cr_assert_eq(m.length, 1);
cr_assert_str_eq(m.data.first, "sole");
cr_assert_eq(m.data.last, nullptr);
ret = mapping_parse_str(&m, "carrot.data[sole-mio]", &nodes);
ret = mapping_entry_parse_str(&m, "carrot.sole");
cr_assert_eq(ret, 0);
cr_assert_eq(m.node, vlist_lookup_name<struct vnode>(&nodes, "carrot"));
cr_assert_str_eq(m.node_name, "carrot");
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_eq(m.data.offset, 1);
cr_assert_eq(m.length, 2);
cr_assert_str_eq(m.data.first, "sole");
cr_assert_eq(m.data.last, nullptr);
ret = vlist_destroy(&nodes, nullptr, true);
ret = mapping_entry_parse_str(&m, "carrot.data.sole");
cr_assert_eq(ret, 0);
}
Test(mapping, parse)
{
int ret;
struct mapping_entry m;
ret = mapping_parse_str(&m, "ts.origin", nullptr);
cr_assert_eq(ret, 0);
cr_assert_eq(m.type, MappingType::TIMESTAMP);
cr_assert_eq(m.timestamp.type, MappingTimestampType::ORIGIN);
ret = mapping_parse_str(&m, "hdr.sequence", nullptr);
cr_assert_eq(ret, 0);
cr_assert_eq(m.type, MappingType::HEADER);
cr_assert_eq(m.header.type, MappingHeaderType::SEQUENCE);
ret = mapping_parse_str(&m, "stats.owd.mean", nullptr);
cr_assert_eq(ret, 0);
cr_assert_eq(m.type, MappingType::STATS);
cr_assert_eq(m.stats.metric, Stats::Metric::OWD);
cr_assert_eq(m.stats.type, Stats::Type::MEAN);
ret = mapping_parse_str(&m, "data[1-2]", nullptr);
cr_assert_eq(ret, 0);
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_eq(m.data.offset, 1);
cr_assert_eq(m.length, 2);
ret = mapping_parse_str(&m, "data[5-5]", nullptr);
cr_assert_eq(ret, 0);
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_eq(m.data.offset, 5);
cr_assert_eq(m.length, 1);
ret = mapping_parse_str(&m, "data[22]", nullptr);
cr_assert_eq(ret, 0);
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_eq(m.data.offset, 22);
cr_assert_eq(m.length, 1);
ret = mapping_parse_str(&m, "data", nullptr);
cr_assert_eq(ret, 0);
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_eq(m.data.offset, 0);
cr_assert_eq(m.length, -1);
ret = mapping_parse_str(&m, "data[]", nullptr);
cr_assert_eq(ret, 0);
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_eq(m.data.offset, 0);
cr_assert_eq(m.length, -1);
ret = mapping_parse_str(&m, "data[1.1-2f]", nullptr);
cr_assert_neq(ret, 0);
/* Missing parts */
ret = mapping_parse_str(&m, "stats.owd", nullptr);
cr_assert_neq(ret, 0);
/* This a type */
ret = mapping_parse_str(&m, "hdr.sequences", nullptr);
cr_assert_neq(ret, 0);
/* Check for superfluous chars at the end */
ret = mapping_parse_str(&m, "hdr.ts.origin.bla", nullptr);
cr_assert_neq(ret, 0);
ret = mapping_parse_str(&m, "hdr.ts.origin.", nullptr);
cr_assert_neq(ret, 0);
ret = mapping_parse_str(&m, "data[1-2]bla", nullptr);
cr_assert_neq(ret, 0);
/* Negative length of chunk */
ret = mapping_parse_str(&m, "data[5-3]", nullptr);
cr_assert_neq(ret, 0);
cr_assert_str_eq(m.node_name, "carrot");
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_str_eq(m.data.first, "sole");
cr_assert_eq(m.data.last, nullptr);
ret = mapping_entry_parse_str(&m, "carrot.data[sole-mio]");
cr_assert_eq(ret, 0);
cr_assert_str_eq(m.node_name, "carrot");
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_str_eq(m.data.first, "sole");
cr_assert_str_eq(m.data.last, "mio");
ret = mapping_entry_parse_str(&m, "carrot[sole-mio]");
cr_assert_eq(ret, 0);
cr_assert_str_eq(m.node_name, "carrot");
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_str_eq(m.data.first, "sole");
cr_assert_str_eq(m.data.last, "mio");
}