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

utils: partial port to C++

This commit is contained in:
Steffen Vogel 2018-08-27 11:09:25 +02:00
parent 13a9fcd10b
commit b71eee85b8
9 changed files with 587 additions and 309 deletions

View file

@ -0,0 +1,200 @@
/** Human readable cpusets.
*
* @file
* @author Steffen Vogel <github@daniel-krebs.net>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLAScommon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#ifdef __linux__
#include <sched.h>
#include <cstdint>
#include <villas/exceptions.hpp>
namespace villas {
namespace utils {
class CpuSet {
protected:
cpu_set_t *setp;
unsigned num_cpus;
size_t sz;
public:
CpuSet() :
num_cpus(sizeof(uintmax_t) * 8),
sz(CPU_ALLOC_SIZE(num_cpus))
{
setp = CPU_ALLOC(num_cpus);
if (!setp)
throw new villas::RuntimeError("Failed to allocated memory");
zero();
}
/** Parses string with list of CPU ranges.
*
* @param str Human readable representation of the set.
*/
CpuSet(const std::string &str);
/** Convert integer to cpu_set_t.
*
* @param set An integer number which is used as the mask
*/
CpuSet(uintmax_t set);
/** Convert cpu_set_t to an integer. */
operator uintmax_t();
operator const cpu_set_t*()
{
return setp;
}
/** Returns human readable representation of the cpuset.
*
* The output format is a list of CPUs with ranges (for example, "0,1,3-9").
*/
operator std::string();
~CpuSet()
{
CPU_FREE(setp);
}
CpuSet(const CpuSet &src) :
CpuSet(src.num_cpus)
{
memcpy(setp, src.setp, sz);
}
bool empty() const
{
return count() == 0;
}
bool full() const
{
return count() == num_cpus;
}
unsigned count() const
{
return CPU_COUNT_S(sz, setp);
}
void zero()
{
CPU_ZERO_S(sz, setp);
}
size_t size() const
{
return sz;
}
CpuSet operator~()
{
CpuSet full = UINTMAX_MAX;
return full ^ *this;
}
bool operator==(const CpuSet &rhs)
{
return CPU_EQUAL_S(sz, setp, rhs.setp);
}
CpuSet& operator&=(const CpuSet &rhs)
{
CPU_AND_S(sz, setp, setp, rhs.setp);
return *this;
}
CpuSet& operator|=(const CpuSet &rhs)
{
CPU_OR_S(sz, setp, setp, rhs.setp);
return *this;
}
CpuSet& operator^=(const CpuSet &rhs)
{
CPU_XOR_S(sz, setp, setp, rhs.setp);
return *this;
}
friend CpuSet operator&(CpuSet lhs, const CpuSet &rhs)
{
lhs &= rhs;
return lhs;
}
friend CpuSet operator|(CpuSet lhs, const CpuSet &rhs)
{
lhs |= rhs;
return lhs;
}
friend CpuSet operator^(CpuSet lhs, const CpuSet &rhs)
{
lhs ^= rhs;
return lhs;
}
//bool& operator[](std::size_t cpu)
//{
// void cpuset_set_cpu(cpuset_t*setp, cpu_t cpu, int state)
//}
bool operator[](size_t cpu) const
{
return isset(cpu);
}
bool isset(size_t cpu) const
{
return CPU_ISSET_S(cpu, sz, setp);
}
void clear(size_t cpu)
{
CPU_CLR_S(cpu, sz, setp);
}
void set(size_t cpu)
{
CPU_SET_S(cpu, sz, setp);
}
};
#endif
} // namespace villas
} // namespace utils
#include <fmt/format.h>

View file

@ -27,7 +27,6 @@
#include <stdint.h>
#include <sched.h>
#include <assert.h>
#include <signal.h>
#include <sys/types.h>
#include <villas/config.h>
@ -37,6 +36,8 @@
extern "C" {
#endif
extern pthread_t main_thread;
#ifdef __GNUC__
#define LIKELY(x) __builtin_expect((x),1)
#define UNLIKELY(x) __builtin_expect((x),0)
@ -137,15 +138,6 @@ extern "C" {
#define BITMASK(h, l) (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONGLONG - 1 - (h))))
#define BIT(nr) (1UL << (nr))
/* Forward declarations */
struct timespec;
/** Print copyright message to stdout. */
void print_copyright();
/** Print version to stdout. */
void print_version();
/** Normal random variate generator using the Box-Muller method
*
* @param m Mean
@ -178,38 +170,6 @@ char * vstrcatf(char **dest, const char *fmt, va_list va)
#define strf(fmt, ...) strcatf(&(char *) { NULL }, fmt, ##__VA_ARGS__)
#define vstrf(fmt, va) vstrcatf(&(char *) { NULL }, fmt, va)
#ifdef __linux__
/** Convert integer to cpu_set_t.
*
* @param set An integer number which is used as the mask
* @param cset A pointer to the cpu_set_t datastructure
*/
void cpuset_from_integer(uintmax_t set, cpu_set_t *cset);
/** Convert cpu_set_t to an integer. */
void cpuset_to_integer(cpu_set_t *cset, uintmax_t *set);
/** Parses string with list of CPU ranges.
*
* From: https://github.com/mmalecki/util-linux/blob/master/lib/cpuset.c
*
* @retval 0 On success.
* @retval 1 On error.
* @retval 2 If fail is set and a cpu number passed in the list doesn't fit
* into the cpu_set. If fail is not set cpu numbers that do not fit are
* ignored and 0 is returned instead.
*/
int cpulist_parse(const char *str, cpu_set_t *set, int fail);
/** Returns human readable representation of the cpuset.
*
* From: https://github.com/mmalecki/util-linux/blob/master/lib/cpuset.c
*
* The output format is a list of CPUs with ranges (for example, "0,1,3-9").
*/
char * cpulist_create(char *str, size_t len, cpu_set_t *set);
#endif
/** Allocate and initialize memory. */
void * alloc(size_t bytes);
@ -219,18 +179,6 @@ void * memdup(const void *src, size_t bytes);
/** Call quit() in the main thread. */
void die();
/** Used by version_parse(), version_compare() */
struct version {
int major;
int minor;
};
/** Compare two versions. */
int version_cmp(struct version *a, struct version *b);
/** Parse a dotted version string. */
int version_parse(const char *s, struct version *v);
/** Check assertion and exit if failed. */
#ifndef assert
#define assert(exp) do { \
@ -240,9 +188,6 @@ int version_parse(const char *s, struct version *v);
} while (0)
#endif
/** Fill buffer with random data */
ssize_t read_random(char *buf, size_t len);
/** Get log2 of long long integers */
static inline int log2i(long long x) {
if (x == 0)
@ -251,9 +196,6 @@ static inline int log2i(long long x) {
return sizeof(x) * 8 - __builtin_clzll(x) - 1;
}
/** Register a exit callback for program termination: SIGINT, SIGKILL & SIGALRM. */
int signals_init(void (*cb)(int signal, siginfo_t *sinfo, void *ctx));
/** Send signal \p sig to main thread. */
void killme(int sig);
@ -262,10 +204,6 @@ pid_t spawn(const char *name, char *const argv[]);
/** Determines the string length as printed on the screen (ignores escable sequences). */
size_t strlenp(const char *str);
/** Remove ANSI control sequences for colored output. */
char * decolor(char *str);
#ifdef __cplusplus
}
#endif

View file

@ -1,6 +1,7 @@
/** Utilities.
*
* @file
* @author Steffen Vogel <github@daniel-krebs.net>
* @author Daniel Krebs <github@daniel-krebs.net>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
@ -26,6 +27,8 @@
#include <string>
#include <vector>
#include <signal.h>
namespace villas {
namespace utils {
@ -41,6 +44,21 @@ assertExcept(bool condition, const T& exception)
throw exception;
}
/** Print copyright message to stdout. */
void print_copyright();
/** Print version to stdout. */
void print_version();
/** Register a exit callback for program termination: SIGINT, SIGKILL & SIGALRM. */
int signals_init(void (*cb)(int signal, siginfo_t *sinfo, void *ctx));
/** Fill buffer with random data */
ssize_t read_random(char *buf, size_t len);
/** Remove ANSI control sequences for colored output. */
char * decolor(char *str);
} // namespace utils
} // namespace villas

View file

@ -0,0 +1,53 @@
/** Version.
*
* @file
* @author Steffen Vogel <github@daniel-krebs.net>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLAScommon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#include <string>
namespace villas {
namespace utils {
class Version {
protected:
int components[3];
static int cmp(const Version& lhs, const Version& rhs);
public:
/** Parse a dotted version string. */
Version(const std::string &s);
Version(int maj, int min = 0, int pat = 0);
inline bool operator==(const Version& rhs) { return cmp(*this, rhs) == 0; }
inline bool operator!=(const Version& rhs) { return cmp(*this, rhs) != 0; }
inline bool operator< (const Version& rhs) { return cmp(*this, rhs) < 0; }
inline bool operator> (const Version& rhs) { return cmp(*this, rhs) > 0; }
inline bool operator<=(const Version& rhs) { return cmp(*this, rhs) <= 0; }
inline bool operator>=(const Version& rhs) { return cmp(*this, rhs) >= 0; }
};
} // namespace villas
} // namespace utils

View file

@ -44,6 +44,8 @@ add_library(villas-common SHARED
tsc.c
utils.c
utils.cpp
cpuset.cpp
version.cpp
)
if(CMAKE_SYSTEM_NAME STREQUAL Linux)

127
common/lib/cpuset.cpp Normal file
View file

@ -0,0 +1,127 @@
/** Human readable cpusets.
*
* @file
* @author Steffen Vogel <github@daniel-krebs.net>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLAScommon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <villas/cpuset.hpp>
#include <villas/utils.hpp>
using namespace villas::utils;
#ifdef __linux__
CpuSet::operator uintmax_t()
{
uintmax_t iset = 0;
for (size_t i = 0; i < num_cpus; i++) {
if (isset(i))
iset |= 1ULL << i;
}
return iset;
}
CpuSet::CpuSet(uintmax_t iset) :
CpuSet()
{
zero();
for (size_t i = 0; i < num_cpus; i++) {
if (iset & (1L << i))
set(i);
}
}
CpuSet::CpuSet(const std::string &str) :
CpuSet()
{
size_t endpos, start, end;
for (auto token : tokenize(str, ",")) {
auto sep = token.find('-');
if (sep == std::string::npos) {
start = std::stoi(token, &endpos);
if (token.begin() + endpos != token.end())
throw new std::invalid_argument("Not a valid CPU set");
if (start < num_cpus)
set(start);
}
else {
start = std::stoi(token, &endpos);
if (token.begin() + endpos != token.begin() + sep)
throw new std::invalid_argument("Not a valid CPU set");
auto token2 = token.substr(endpos + 1);
end = std::stoi(token2, &endpos);
if (token2.begin() + endpos != token2.end())
throw new std::invalid_argument("Not a valid CPU set");
for (size_t i = start; i <= end && i < num_cpus; i++)
set(i);
}
}
}
CpuSet::operator std::string ()
{
std::stringstream ss;
bool first = true;
for (size_t i = 0; i < num_cpus; i++) {
if (isset(i)) {
size_t run = 0;
for (size_t j = i + 1; j < num_cpus; j++) {
if (!isset(j))
break;
run++;
}
if (first)
first = false;
else
ss << ",";
ss << i;
if (run == 1) {
ss << "," << (i + 1);
i++;
}
else if (run > 1) {
ss << "-" << (i + run);
i += run;
}
}
}
return ss.str();
}
#endif /* __linux__ */

View file

@ -23,44 +23,20 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <pthread.h>
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#include <villas/config.h>
#include <villas/utils.h>
pthread_t main_thread;
void print_copyright()
{
printf(PROJECT_NAME " %s (built on %s %s)\n",
CLR_BLU(PROJECT_BUILD_ID), CLR_MAG(__DATE__), CLR_MAG(__TIME__));
printf(" Copyright 2014-2017, Institute for Automation of Complex Power Systems, EONERC\n");
printf(" Steffen Vogel <StVogel@eonerc.rwth-aachen.de>\n");
}
void print_version()
{
printf("%s\n", PROJECT_BUILD_ID);
}
int version_parse(const char *s, struct version *v)
{
return sscanf(s, "%u.%u", &v->major, &v->minor) != 2;
}
int version_cmp(struct version *a, struct version *b) {
int major = a->major - b->major;
int minor = a->minor - b->minor;
return major ? major : minor;
}
double box_muller(float m, float s)
{
double x1, x2, y1;
@ -118,128 +94,6 @@ char * vstrcatf(char **dest, const char *fmt, va_list ap)
return *dest;
}
#ifdef __linux__
void cpuset_to_integer(cpu_set_t *cset, uintmax_t *set)
{
*set = 0;
for (int i = 0; i < MIN(sizeof(*set) * 8, CPU_SETSIZE); i++) {
if (CPU_ISSET(i, cset))
*set |= 1ULL << i;
}
}
void cpuset_from_integer(uintmax_t set, cpu_set_t *cset)
{
CPU_ZERO(cset);
for (int i = 0; i < MIN(sizeof(set) * 8, CPU_SETSIZE); i++) {
if (set & (1L << i))
CPU_SET(i, cset);
}
}
/* From: https://github.com/mmalecki/util-linux/blob/master/lib/cpuset.c */
static const char *nexttoken(const char *q, int sep)
{
if (q)
q = strchr(q, sep);
if (q)
q++;
return q;
}
int cpulist_parse(const char *str, cpu_set_t *set, int fail)
{
const char *p, *q;
int r = 0;
q = str;
CPU_ZERO(set);
while (p = q, q = nexttoken(q, ','), p) {
unsigned int a; /* beginning of range */
unsigned int b; /* end of range */
unsigned int s; /* stride */
const char *c1, *c2;
char c;
if ((r = sscanf(p, "%u%c", &a, &c)) < 1)
return 1;
b = a;
s = 1;
c1 = nexttoken(p, '-');
c2 = nexttoken(p, ',');
if (c1 != NULL && (c2 == NULL || c1 < c2)) {
if ((r = sscanf(c1, "%u%c", &b, &c)) < 1)
return 1;
c1 = nexttoken(c1, ':');
if (c1 != NULL && (c2 == NULL || c1 < c2)) {
if ((r = sscanf(c1, "%u%c", &s, &c)) < 1)
return 1;
if (s == 0)
return 1;
}
}
if (!(a <= b))
return 1;
while (a <= b) {
if (fail && (a >= CPU_SETSIZE))
return 2;
CPU_SET(a, set);
a += s;
}
}
if (r == 2)
return 1;
return 0;
}
char *cpulist_create(char *str, size_t len, cpu_set_t *set)
{
size_t i;
char *ptr = str;
int entry_made = 0;
for (i = 0; i < CPU_SETSIZE; i++) {
if (CPU_ISSET(i, set)) {
int rlen;
size_t j, run = 0;
entry_made = 1;
for (j = i + 1; j < CPU_SETSIZE; j++) {
if (CPU_ISSET(j, set))
run++;
else
break;
}
if (!run)
rlen = snprintf(ptr, len, "%zd,", i);
else if (run == 1) {
rlen = snprintf(ptr, len, "%zd,%zd,", i, i + 1);
i++;
} else {
rlen = snprintf(ptr, len, "%zd-%zd,", i, i + run);
i += run;
}
if (rlen < 0 || (size_t) rlen + 1 > len)
return NULL;
ptr += rlen;
if (rlen > 0 && len > (size_t) rlen)
len -= rlen;
else
len = 0;
}
}
ptr -= entry_made;
*ptr = '\0';
return str;
}
#endif /* __linux__ */
void * alloc(size_t bytes)
{
void *p = malloc(bytes);
@ -260,70 +114,6 @@ void * memdup(const void *src, size_t bytes)
return dst;
}
ssize_t read_random(char *buf, size_t len)
{
int fd;
ssize_t bytes, total;
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0)
return -1;
bytes = 0;
total = 0;
while (total < len) {
bytes = read(fd, buf + total, len - total);
if (bytes < 0)
break;
total += bytes;
}
close(fd);
return bytes;
}
/* Setup exit handler */
int signals_init(void (*cb)(int signal, siginfo_t *sinfo, void *ctx))
{
int ret;
info("Initialize signals");
struct sigaction sa_quit = {
.sa_flags = SA_SIGINFO | SA_NODEFER,
.sa_sigaction = cb
};
struct sigaction sa_chld = {
.sa_flags = 0,
.sa_handler = SIG_IGN
};
main_thread = pthread_self();
sigemptyset(&sa_quit.sa_mask);
ret = sigaction(SIGINT, &sa_quit, NULL);
if (ret)
return ret;
ret = sigaction(SIGTERM, &sa_quit, NULL);
if (ret)
return ret;
ret = sigaction(SIGALRM, &sa_quit, NULL);
if (ret)
return ret;
ret = sigaction(SIGCHLD, &sa_chld, NULL);
if (ret)
return ret;
return 0;
}
void killme(int sig)
{
/* Send only to main thread in case the ID was initilized by signals_init() */
@ -384,36 +174,3 @@ size_t strlenp(const char *str)
return sz;
}
char * decolor(char *str)
{
char *p, *q;
bool inseq = false;
for (p = q = str; *p; p++) {
switch (*p) {
case 0x1b:
if (*(++p) == '[') {
inseq = true;
continue;
}
break;
case 'm':
if (inseq) {
inseq = false;
continue;
}
break;
}
if (!inseq) {
*q = *p;
q++;
}
}
*q = '\0';
return str;
}

View file

@ -22,8 +22,14 @@
#include <vector>
#include <string>
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <villas/utils.h>
#include <villas/utils.hpp>
#include <villas/config.h>
namespace villas {
namespace utils {
@ -53,5 +59,112 @@ tokenize(std::string s, std::string delimiter)
return tokens;
}
void print_copyright()
{
std::cout << PROJECT_NAME " " << CLR_BLU(PROJECT_BUILD_ID)
<< " (built on " CLR_MAG(__DATE__) " " CLR_MAG(__TIME__) ")" << std::endl
<< " Copyright 2014-2017, Institute for Automation of Complex Power Systems, EONERC" << std::endl
<< " Steffen Vogel <StVogel@eonerc.rwth-aachen.de>" << std::endl;
}
void print_version()
{
std::cout << PROJECT_BUILD_ID << std::endl;
}
ssize_t read_random(char *buf, size_t len)
{
int fd;
ssize_t bytes;
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0)
return -1;
while (len) {
bytes = read(fd, buf, len);
if (bytes < 0)
break;
len -= bytes;
buf += bytes;
}
close(fd);
return bytes;
}
/* Setup exit handler */
int signals_init(void (*cb)(int signal, siginfo_t *sinfo, void *ctx))
{
int ret;
info("Initialize signals");
struct sigaction sa_quit;
sa_quit.sa_flags = SA_SIGINFO | SA_NODEFER;
sa_quit.sa_sigaction = cb;
struct sigaction sa_chld;
sa_chld.sa_flags = 0;
sa_chld.sa_handler = SIG_IGN;
main_thread = pthread_self();
sigemptyset(&sa_quit.sa_mask);
ret = sigaction(SIGINT, &sa_quit, NULL);
if (ret)
return ret;
ret = sigaction(SIGTERM, &sa_quit, NULL);
if (ret)
return ret;
ret = sigaction(SIGALRM, &sa_quit, NULL);
if (ret)
return ret;
ret = sigaction(SIGCHLD, &sa_chld, NULL);
if (ret)
return ret;
return 0;
}
char * decolor(char *str)
{
char *p, *q;
bool inseq = false;
for (p = q = str; *p; p++) {
switch (*p) {
case 0x1b:
if (*(++p) == '[') {
inseq = true;
continue;
}
break;
case 'm':
if (inseq) {
inseq = false;
continue;
}
break;
}
if (!inseq) {
*q = *p;
q++;
}
}
*q = '\0';
return str;
}
} // namespace utils
} // namespace villas

70
common/lib/version.cpp Normal file
View file

@ -0,0 +1,70 @@
/** Version.
*
* @author Steffen Vogel <github@daniel-krebs.net>
* @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLAScommon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <stdexcept>
#include <string>
#include <villas/log.hpp>
#include <villas/utils.hpp>
#include <villas/version.hpp>
using namespace villas::utils;
Version::Version(const std::string &str)
{
size_t endpos;
auto comp = tokenize(str, ".");
if (comp.size() > 3)
throw new std::invalid_argument("Not a valid version string");
for (unsigned i = 0; i < 3; i++) {
if (i < comp.size()) {
components[i] = std::stoi(comp[i], &endpos, 10);
if (comp[i].begin() + endpos != comp[i].end())
throw new std::invalid_argument("Not a valid version string");
}
else
components[i] = 0;
}
}
Version::Version(int maj, int min, int pat) :
components{maj, min, pat}
{
}
int Version::cmp(const Version& lhs, const Version& rhs)
{
int d;
for (int i = 0; i < 3; i++) {
d = lhs.components[i] - rhs.components[i];
if (d)
return d;
}
return 0;
}