]> gitweb.hamatoma.de Git - cpidjinn/commitdiff
initial state
authorhama <hama@siduction.net>
Tue, 7 Jun 2016 18:52:06 +0000 (20:52 +0200)
committerhama <hama@siduction.net>
Tue, 7 Jun 2016 18:52:06 +0000 (20:52 +0200)
43 files changed:
.gitignore [new file with mode: 0644]
Server/Makefile [new file with mode: 0644]
Server/client.cpp [new file with mode: 0644]
Server/cpidjinn.cpp [new file with mode: 0644]
Server/cpidjinn.hpp [new file with mode: 0644]
Server/dynbuffer.cpp [new symlink]
Server/dynbuffer.hpp [new symlink]
Server/gpioprocessor.cpp [new file with mode: 0644]
Server/gpioprocessor.hpp [new file with mode: 0644]
Server/logger.cpp [new symlink]
Server/logger.hpp [new symlink]
Server/tcpclient.cpp [new file with mode: 0644]
Server/tcpclient.hpp [new file with mode: 0644]
Server/tcpserver.cpp [new file with mode: 0644]
Server/tcpserver.hpp [new file with mode: 0644]
Server/thread.cpp [new symlink]
Server/thread.hpp [new symlink]
Server/timer.cpp [new symlink]
Server/timer.hpp [new symlink]
Server/timeutils.cpp [new symlink]
Server/timeutils.hpp [new symlink]
Server/trace.hpp [new symlink]
Server/util.hpp [new symlink]
util/Makefile [new file with mode: 0644]
util/cudynbuffer.cpp [new file with mode: 0644]
util/cutimer.cpp [new file with mode: 0644]
util/cutimeutils.cpp [new file with mode: 0644]
util/dynbuffer.cpp [new file with mode: 0644]
util/dynbuffer.hpp [new file with mode: 0644]
util/logger.cpp [new file with mode: 0644]
util/logger.hpp [new file with mode: 0644]
util/test.cpp [new file with mode: 0644]
util/test.hpp [new file with mode: 0644]
util/thread.cpp [new file with mode: 0644]
util/thread.hpp [new file with mode: 0644]
util/timer.cpp [new file with mode: 0644]
util/timer.hpp [new file with mode: 0644]
util/timeutils.cpp [new file with mode: 0644]
util/timeutils.hpp [new file with mode: 0644]
util/trace.hpp [new file with mode: 0644]
util/unittest.cpp [new file with mode: 0644]
util/unittest.hpp [new file with mode: 0644]
util/util.hpp [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..874c63c
--- /dev/null
@@ -0,0 +1,2 @@
+*.o
+
diff --git a/Server/Makefile b/Server/Makefile
new file mode 100644 (file)
index 0000000..c07910b
--- /dev/null
@@ -0,0 +1,35 @@
+CC = g++
+CL = g++
+CFLAGS = -Wall
+LDFLAGS = -lbcm2835 -lpthread
+
+OBJ_UTIL = timer.o thread.o dynbuffer.o logger.o
+OBJ = cpidjinn.o $(OBJ_UTIL) tcpserver.o gpioprocessor.o
+PROG = cpidjinn
+
+OBJ_CLIENT = $(OBJ_UTIL) tcpclient.o client.o
+CLIENT = client
+
+all: $(PROG)
+
+$(CLIENT) : $(OBJ_CLIENT)
+       $(CL) -o $(CLIENT) $(OBJ_CLIENT)
+
+debug:all
+       $(CC) $(CFLAGS) -g -o $(PROG) $(OBJ)
+stable:all
+       $(CC) $(CFLAGS) -o cipdjinn $(OBJ)
+
+.PHONY: clean
+clean:
+       rm -vfr *~ $(PROG) $(OBJ)
+
+$(PROG): $(OBJ)
+       $(CL) $(LDFLAGS) -o $(PROG) $(OBJ)
+
+%.hpp: %.cpp
+       $(CC) $(CFLAGS) -g  -c $<
+
+%.o: %.cpp
+       $(CC) $(CFLAGS) -g  -c $<
+
diff --git a/Server/client.cpp b/Server/client.cpp
new file mode 100644 (file)
index 0000000..f914f50
--- /dev/null
@@ -0,0 +1,44 @@
+#include "util.hpp"
+#include "tcpclient.hpp"
+
+#if 0
+static void prepareBlink(DynBuffer& buffer){
+
+}
+#endif
+
+static void addTone(int duration, int millihz, DynBuffer& buffer){
+       int high, low;
+       high = low = 1000*1000*1000 / millihz / 2 + 1;
+       buffer.appendAsLE(duration, 2);
+       buffer.appendAsLE(high, 3);
+       buffer.appendAsLE(low, 3);
+}
+
+static void prepareMelody(DynBuffer& buffer){
+       int duration = 2000;
+       int pin = 0;
+       int countTones = 3;
+       buffer.clear().append("MELO");
+       buffer.appendAsLE(pin, 1);
+       buffer.appendAsLE(countTones, 1);
+       // tone 'a' 440 Hz
+       addTone(duration, 440000, buffer);
+       // tone c#: 554,365 Hz
+       addTone(duration, 554365, buffer);
+       // tone e: 659,255 Hz
+       addTone(duration, 659255, buffer);
+}
+int main (int argc, char **argv) {
+       const char* host = "127.0.0.1";
+       int port = 15000;
+       Logger logger;
+       DynBuffer buffer;
+       TcpClient client(&logger);
+       if (client.connect(host, port)){
+               prepareMelody(buffer);
+               client.sendAndReceive(buffer);
+       }
+       client.disconnect();
+       return 0;
+}
diff --git a/Server/cpidjinn.cpp b/Server/cpidjinn.cpp
new file mode 100644 (file)
index 0000000..4ab53d5
--- /dev/null
@@ -0,0 +1,22 @@
+#include "cpidjinn.hpp"
+
+/**
+ * Main function.
+ *
+ * @param argc number of arguments
+ * @param argv argument vector
+ * @return             exit code
+ */
+int main(int argc, char** argv){
+       Logger logger;
+       int port = argc < 2 ? 15000 : atol(argv[1]);
+       logger.say(LOG_INFO, "start");
+       GPIOProcessor processor;
+       TcpServer server(port, &logger);
+       server.addProcessor(processor);
+       // interpreter for the standard commands:
+       server.addProcessor(server);
+       server.listen();
+       return 0;
+}
+
diff --git a/Server/cpidjinn.hpp b/Server/cpidjinn.hpp
new file mode 100644 (file)
index 0000000..49cb5c1
--- /dev/null
@@ -0,0 +1,5 @@
+#ifndef CPIDJINN_HPP
+#include "util.hpp"
+#include "tcpserver.hpp"
+#include "gpioprocessor.hpp"
+#endif
diff --git a/Server/dynbuffer.cpp b/Server/dynbuffer.cpp
new file mode 120000 (symlink)
index 0000000..45fdb14
--- /dev/null
@@ -0,0 +1 @@
+../util/dynbuffer.cpp
\ No newline at end of file
diff --git a/Server/dynbuffer.hpp b/Server/dynbuffer.hpp
new file mode 120000 (symlink)
index 0000000..e367f33
--- /dev/null
@@ -0,0 +1 @@
+../util/dynbuffer.hpp
\ No newline at end of file
diff --git a/Server/gpioprocessor.cpp b/Server/gpioprocessor.cpp
new file mode 100644 (file)
index 0000000..d2ca966
--- /dev/null
@@ -0,0 +1,117 @@
+#include "cpidjinn.hpp"
+#include "bcm2835.h"
+
+/**
+ * Constructor.
+ */
+GPIOProcessor::GPIOProcessor()
+{
+}
+/**
+ * Destructor.
+ */
+GPIOProcessor::~GPIOProcessor()
+{
+
+}
+/**
+ * Lets a GPIO pin blink.
+ *
+ * Syntax: BLNK[pin][period_high][period_low]<br>
+ * [pin]: 1 byte. Numbering like WiringPi: 0 is GPIO17...<br>
+ * [period_x]: 4 byte little endian, in microseconds (< 35 minutes)
+ *
+ * @param buffer               IN: contains the blink command<br>
+ *                                             OUT: answer for the client
+ */
+void GPIOProcessor::blink(DynBuffer& buffer){
+       if (buffer.length() != 4 + 1 + 2*4){
+               buffer.clear().appendFormatted("ERR wrong packet length %d instead of %d",
+                                                                       buffer.length(), 4 + 1 + 2*4);
+       } else {
+               int pin = buffer.str()[4];
+               int periodHigh = buffer.valueOfLE(4, 5);
+               int periodLow = buffer.valueOfLE(4, 5 + 4);
+               printf("Pin: %d high: %d low: %d\n", pin, periodHigh, periodLow);
+               buffer.set("OK  ");
+       }
+}
+
+/**
+ * "Writes" a melody to a GPIO pin.
+ *
+ * Syntax: MELO[pin][count_tones][duration_1][period_high_1][period_high_1]...<br>
+ * [pin]: 1 byte. Numbering like WiringPi: 0 is GPIO17...<br>
+ * [count_tones]: 1 byte<br>
+ * [duration_y]: 2 byte little endian, number of millisec (1 msec - 65 sec)<br>
+ * [period_x_y]: 3 byte little endian,  unit: microsec (1 usec - 16.8 sec)
+ *
+ * @param buffer       IN: contains the command describing a melody to play<br>
+ *                                     OUT: answer for the client
+ */
+void GPIOProcessor::melody(DynBuffer& buffer){
+       if (buffer.length() < 4 + 1 + 1 + 2 + 2*3){
+               buffer.clear().appendFormatted("ERR wrong packet length %d instead of %d",
+                                                       buffer.length(), 4 + 1 + 2*4);
+       } else {
+               int pin = buffer.at(4);
+               int countTones = buffer.at(5);
+               if ( (buffer.length() - 4 - 1 - 1) % (2 + 2*3) != 0){
+                       buffer.clear().appendFormatted("ERR incomplete tones definitions %d count: %d",
+                                               buffer.length(), countTones);
+               } else {
+                       int offset = 4 + 1 + 1;
+                       printf("Pin: %d count: %d\n", pin, countTones);
+                       while(countTones-- > 0) {
+                               int periodHigh, periodLow;
+                               int duration = buffer.valueOfLE(2, offset);
+                               offset += 2;
+                               periodHigh = buffer.valueOfLE(3, offset);
+                               offset += 3;
+                               periodLow = buffer.valueOfLE(3, offset);
+                               offset += 3;
+                               int count = duration * 1000 / (periodHigh + periodLow);
+                               printf("high: %d low: %d duration: %d msec count: %d\n",
+                                          periodHigh, periodLow, duration, count);
+                       }
+                       buffer.set("OK  ", 4);
+               }
+       }
+}
+/**
+ * "Writes" a PWM code to the a pin.
+ *
+ * Syntax: PWM [pin][divider][countValues][pause][size_values][value1]...<br>
+ * [pin]: '1' (constant): GPIO18 is the only available pin for PWM<br>
+ * [divider]: 1 byte, 1 << [divider] is the real value<br>
+ * [countValues]: 2 byte little endian: number of values to write as PWM<br>
+ * [pause]: 4 byte little endian in microseconds (< 35 minutes), pause between 2 data<br>
+ *
+ *
+ * @param buffer       IN: the command describing a PulseWidthModulation command<br>
+ *                                     OUT: answer for the client
+ */
+void GPIOProcessor::pulseWidthModulation(DynBuffer& buffer){
+       buffer.set("ERROR not implemented");
+}
+
+/**
+ * Processes the received buffer content.
+ *
+ * @param buffer       IN: the client command<br>
+ *                                     OUT: the answer to th client
+ */
+TcpProcessor::State GPIOProcessor::process(DynBuffer& buffer){
+       State rc = stProcessed;
+
+       if (buffer.startsWith("BLNK", 4)){
+                blink(buffer);
+       } else if (buffer.startsWith("MELO", 4)){
+               melody(buffer);
+       } else if (buffer.startsWith("PWM ", 4)){
+               pulseWidthModulation(buffer);
+       } else {
+               rc = stIgnored;
+       }
+       return rc;
+}
diff --git a/Server/gpioprocessor.hpp b/Server/gpioprocessor.hpp
new file mode 100644 (file)
index 0000000..d8b855b
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef GPIOPROCESSOR_H
+#define GPIOPROCESSOR_H
+
+class GPIOProcessor : public TcpProcessor
+{
+public:
+       GPIOProcessor();
+       ~GPIOProcessor();
+private:
+       // not implemented, private use only
+       GPIOProcessor(const GPIOProcessor& other);
+       GPIOProcessor& operator=(const GPIOProcessor& other);
+       void blink(DynBuffer& buffer);
+       void melody(DynBuffer& buffer);
+       void pulseWidthModulation(DynBuffer& buffer);
+public:
+       virtual State process(DynBuffer& buffer);
+
+};
+
+#endif // GPIOPROCESSOR_H
diff --git a/Server/logger.cpp b/Server/logger.cpp
new file mode 120000 (symlink)
index 0000000..6522cd3
--- /dev/null
@@ -0,0 +1 @@
+../util/logger.cpp
\ No newline at end of file
diff --git a/Server/logger.hpp b/Server/logger.hpp
new file mode 120000 (symlink)
index 0000000..b81183c
--- /dev/null
@@ -0,0 +1 @@
+../util/logger.hpp
\ No newline at end of file
diff --git a/Server/tcpclient.cpp b/Server/tcpclient.cpp
new file mode 100644 (file)
index 0000000..afb095c
--- /dev/null
@@ -0,0 +1,89 @@
+#include "util.hpp"
+#include "tcpclient.hpp"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/**
+ * Constructor.
+ */
+TcpClient::TcpClient(Logger* logger) :
+       m_socket(socket (AF_INET, SOCK_STREAM, 0)),
+       m_buffer(64*1024),
+       m_logger(logger),
+       m_connected(false)
+{
+}
+/**
+ * Destructor.
+ */
+TcpClient::~TcpClient() {
+       disconnect();
+}
+/**
+ * Connects to a server.
+ *
+ * @param host the host as numerical iP address
+ * @param port the port
+ * @return             <i>true</i>: success
+ */
+bool TcpClient::connect(const char* host, int port){
+       bool rc = true;
+       struct sockaddr_in address;
+       address.sin_family = AF_INET;
+       address.sin_port = htons (port);
+       inet_aton (host, &address.sin_addr);
+       if (m_connected)
+               disconnect();
+       if (::connect ( m_socket, (struct sockaddr *) &address, sizeof (address)) != 0){
+               m_logger->sayf(LOG_ERROR, "connect() failed: %d", errno);
+               rc = false;
+       } else {
+               m_connected = true;
+       }
+       return rc;
+}
+/**
+ * Closes the connection.
+ */
+void TcpClient::disconnect(){
+       if (m_connected){
+               close(m_socket);
+               m_connected = false;
+       }
+}
+/**
+ * Sends a message to the server.
+ *
+ * @param buffer       the message to send
+ */
+void TcpClient::send(DynBuffer& buffer){
+       if (::send(m_socket, buffer.str(), buffer.length(), 0) != 0){
+               m_logger->sayf(LOG_ERROR, "send(%.4s, %d) failed: %d",
+                       buffer.str(), buffer.length(), errno);
+       }
+}
+/**
+ * Sends a message to the server and receives the answer.
+ *
+ * @param buffer       IN: the message to send<br>
+ *                                     OUT: the answer from the server
+ */
+void TcpClient::sendAndReceive(DynBuffer& buffer){
+       if (::send(m_socket, buffer.str(), buffer.length(), 0) <= 0){
+               m_logger->sayf(LOG_ERROR, "send(%.4s, %d) failed: %d",
+                       buffer.str(), buffer.length(), errno);
+       } else {
+               int length = ::recv(m_socket, buffer.buffer(), buffer.size(), 0);
+               if (length < 0){
+                       m_logger->sayf(LOG_ERROR, "receive() failed: %d", errno);
+                       buffer.clear();
+               } else {
+                       buffer.setLength(length);
+                       if (buffer.startsWith("ERRO", 4)){
+                               m_logger->say(LOG_ERROR, buffer.str());
+                       }
+               }
+       }
+}
diff --git a/Server/tcpclient.hpp b/Server/tcpclient.hpp
new file mode 100644 (file)
index 0000000..26571b8
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef TCPCLIENT_H
+#define TCPCLIENT_H
+
+class TcpClient {
+public:
+    TcpClient(Logger* logger);
+    ~TcpClient();
+private:
+       // not implemented, only private use
+    TcpClient ( const TcpClient& other );
+    TcpClient& operator= ( const TcpClient& other );
+public:
+       bool connect(const char* host, int port);
+       void disconnect();
+       void send(DynBuffer& buffer);
+       void sendAndReceive(DynBuffer& buffer);
+private:
+       int m_socket;
+       DynBuffer m_buffer;
+       Logger* m_logger;
+       bool m_connected;
+};
+
+#endif // TCPCLIENT_H
diff --git a/Server/tcpserver.cpp b/Server/tcpserver.cpp
new file mode 100644 (file)
index 0000000..8a92e1c
--- /dev/null
@@ -0,0 +1,187 @@
+#include "cpidjinn.hpp"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <signal.h>
+
+//#define SIGPIPE 13
+volatile sig_atomic_t s_brokenPipes = 0;
+typedef void (*sighandler_t)(int);
+
+static sighandler_t prepareSignal (int signalNo, sighandler_t signalHandler) {
+#if 1
+       sighandler_t oldHandler = signal(signalNo, signalHandler);
+       (void) oldHandler;
+       return NULL;
+#else
+   struct sigaction newSignal, oldSignal;
+   newSignal.sa_handler = signalHandler;
+   sigemptyset (&newSignal.sa_mask);
+   newSignal.sa_flags = SA_RESTART;
+   if (sigaction (signalNo, &newSignal, &oldSignal) < 0)
+      return SIG_ERR;
+   return oldSignal.sa_handler;
+#endif
+}
+static void catchSignal(int signal) {
+       if (signal == SIGPIPE)
+               s_brokenPipes++;
+   prepareSignal(signal, &catchSignal);
+}
+
+/**
+ * Constructor.
+ *
+ * @param port the port to listen
+ */
+TcpServer::TcpServer(int port, Logger* logger) :
+       m_port(port),
+       m_buffer(1024),
+       m_listeningSocket( socket (AF_INET, SOCK_STREAM, 0)),
+       m_logger(logger),
+       m_processors(),
+       m_processorCount(0)
+
+{
+       int set = 1;
+       setsockopt(m_listeningSocket, SOL_SOCKET, MSG_NOSIGNAL, (void *)&set, sizeof(int));
+       prepareSignal(SIGPIPE, catchSignal);
+
+}
+/**
+ * Destructor.
+ */
+TcpServer::~TcpServer()
+{
+}
+
+/**
+ * Adds a processor for the client messages.
+ *
+ * @param processor    processor to add
+ */
+void TcpServer::addProcessor(TcpProcessor& processor){
+       if ((size_t) m_processorCount >= sizeof m_processors / sizeof m_processors[0])
+               m_logger->sayf(LOG_ERROR, "too many processors: %d", m_processorCount);
+       else {
+               m_processors[m_processorCount++] = &processor;
+       }
+}
+
+/**
+ * Processes standard messages.
+ *
+ * @param buffer       IN: the client message<br>
+ *                                     OUT: the answer for the client
+ * @return                     <i>stIgnored</i>: unknown message<br>
+ *                                     <i>stProcessed</i>: command recognized and processed.
+ *                                     <i>stStop</i>QUIT recognized
+ */
+TcpProcessor::State TcpServer::process(DynBuffer& buffer){
+       State rc = stProcessed;
+       if (buffer.startsWith("ECHO", 4)){
+               // nothing to do: the buffer is returned unchanged.
+       } else if (buffer.startsWith("QUIT", 4)){
+               rc = stStop;
+               buffer.clear();
+       } else if (buffer.startsWith("TIME", 4)){
+               buffer.resize(64);
+               struct tm* tm_info;
+               struct timeval tv;
+
+               gettimeofday(&tv, NULL);
+               tm_info = localtime(&tv.tv_sec);
+               strftime(buffer.buffer(), 64, "TIME: %Y.%m:%d %H:%M:%S", tm_info);
+               buffer.appendInt(tv.tv_usec/1000, ".%03d");
+       } else {
+               rc = stIgnored;
+       }
+       return rc;
+}
+/**
+ * Starts the listening state.
+ */
+void TcpServer::listen(){
+       socklen_t addrlen;
+       size_t length;
+       bool again = true;
+       struct sockaddr_in address;
+       const int dummy = 1;
+       setsockopt( m_listeningSocket, SOL_SOCKET, SO_REUSEADDR, &dummy, sizeof(int));
+       address.sin_family = AF_INET;
+       address.sin_addr.s_addr = INADDR_ANY;
+       address.sin_port = htons (m_port);
+       int lastBrokenPipe = s_brokenPipes;
+       if (bind ( m_listeningSocket,   (struct sockaddr *) &address, sizeof (address)) != 0) {
+               m_logger->sayf(LOG_ERROR, "port in use: %d", m_port);
+       } else {
+               m_logger->sayf(LOG_INFO, "listen on port %d...", m_port);
+               ::listen (m_listeningSocket, 5);
+               addrlen = sizeof (struct sockaddr_in);
+               while (again) {
+                       int connectionSocket = accept ( m_listeningSocket, (struct sockaddr *) &address, &addrlen );
+                       if (connectionSocket <= 0)
+                               m_logger->sayf (LOG_ERROR, "accept() failed: %d", errno);
+                       else {
+                               int set = 1;
+                               setsockopt(connectionSocket, SOL_SOCKET, MSG_NOSIGNAL, (void *)&set, sizeof(int));
+                               m_logger->sayf (LOG_INFO, "new connection: %s ...",  inet_ntoa (address.sin_addr));
+                               State state = stUndef;
+                               do {
+                                       length = ::recv (connectionSocket, m_buffer.buffer(), m_buffer.size()-1, 0);
+                                       if (lastBrokenPipe != s_brokenPipes && length <= 0){
+                                               m_logger->sayf(LOG_INFO, "connection lost");
+                                               lastBrokenPipe = s_brokenPipes;
+                                               break;
+                                       }
+                                       if (length > 0) {
+                                               m_buffer.setLength(length);
+                                               m_logger->sayf(LOG_DEBUG, "received: %d bytes, %.4s", length, m_buffer.str());
+                                       } else if (length == 0){
+                                               int errorNo = errno;
+                                               if (errorNo == ENOPROTOOPT){
+                                                       m_logger->say(LOG_INFO, "connection lost.");
+                                                       break;
+                                               } else {
+                                                       m_logger->sayf(LOG_ERROR, "recv() returns 0: %d", errorNo);
+                                               }
+                                       } else {
+                                               m_logger->sayf(LOG_ERROR, "recv() returns %d: %d", length, errno);
+                                       }
+                                       state = stUndef;
+                                       if (length >= 4){
+                                               bool again = true;
+                                               for (int ix = 0; again && ix < m_processorCount; ix++){
+                                                       State state = m_processors[ix]->process(m_buffer);
+                                                       switch(state){
+                                                               case stUndef:
+                                                               case stIgnored:
+                                                                       break;
+                                                               case stProcessed:
+                                                                       again = false;
+                                                                       break;
+                                                               case stStop:
+                                                                       break;
+                                                       }
+                                               }
+                                       }
+                                       if (state == stIgnored){
+                                               char buf[5];
+                                               memcpy(buf, m_buffer.str(), 4);
+                                               buf[4] = '\n';
+                                               m_buffer.clear().appendFormatted("ERROR: unknown command: %s", buf);
+                                               m_logger->say(LOG_ERROR, m_buffer.str());
+                                       }
+                                       if (m_buffer.length() > 0){
+                                               ::send (connectionSocket, m_buffer.str(), m_buffer.length(), 0);
+                                       }
+                               } while (state != stStop);
+                               close (connectionSocket);
+                       }
+               }
+       }
+       close (m_listeningSocket);
+}
+
+
diff --git a/Server/tcpserver.hpp b/Server/tcpserver.hpp
new file mode 100644 (file)
index 0000000..d64516c
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef TCPSERVER_H
+#define TCPSERVER_H
+typedef unsigned char ubyte_t;
+
+class TcpProcessor {
+public:
+       enum State {
+               stUndef = 0,
+               /// buffer content recognized and processed:
+               stProcessed,
+               /// unknown buffer content, not processed
+               stIgnored,
+               // server should stop
+               stStop
+       };
+public:
+       virtual State process(DynBuffer& buffer) = 0;
+};
+
+class TcpServer : public TcpProcessor
+{
+public:
+       TcpServer(int port, Logger* logger);
+       virtual ~TcpServer();
+private:
+       // not implemented! Fails if used!
+       TcpServer(const TcpServer& other);
+       TcpServer& operator=(const TcpServer& other);
+       bool operator==(const TcpServer& other) const;
+public:
+       void addProcessor(TcpProcessor& processor);
+       void listen();
+       virtual State process(DynBuffer& buffer);
+private:
+    int m_port;
+       DynBuffer m_buffer;
+       int m_listeningSocket;
+       Logger* m_logger;
+       TcpProcessor* m_processors[8];
+       int m_processorCount;
+};
+
+#endif // TCPSERVER_H
diff --git a/Server/thread.cpp b/Server/thread.cpp
new file mode 120000 (symlink)
index 0000000..f37fbb6
--- /dev/null
@@ -0,0 +1 @@
+../util/thread.cpp
\ No newline at end of file
diff --git a/Server/thread.hpp b/Server/thread.hpp
new file mode 120000 (symlink)
index 0000000..a68448a
--- /dev/null
@@ -0,0 +1 @@
+../util/thread.hpp
\ No newline at end of file
diff --git a/Server/timer.cpp b/Server/timer.cpp
new file mode 120000 (symlink)
index 0000000..2a0d81a
--- /dev/null
@@ -0,0 +1 @@
+../util/timer.cpp
\ No newline at end of file
diff --git a/Server/timer.hpp b/Server/timer.hpp
new file mode 120000 (symlink)
index 0000000..04dacbc
--- /dev/null
@@ -0,0 +1 @@
+../util/timer.hpp
\ No newline at end of file
diff --git a/Server/timeutils.cpp b/Server/timeutils.cpp
new file mode 120000 (symlink)
index 0000000..e07e439
--- /dev/null
@@ -0,0 +1 @@
+../util/timeutils.cpp
\ No newline at end of file
diff --git a/Server/timeutils.hpp b/Server/timeutils.hpp
new file mode 120000 (symlink)
index 0000000..4f43923
--- /dev/null
@@ -0,0 +1 @@
+../util/timeutils.hpp
\ No newline at end of file
diff --git a/Server/trace.hpp b/Server/trace.hpp
new file mode 120000 (symlink)
index 0000000..4360f00
--- /dev/null
@@ -0,0 +1 @@
+../util/trace.hpp
\ No newline at end of file
diff --git a/Server/util.hpp b/Server/util.hpp
new file mode 120000 (symlink)
index 0000000..ea72e00
--- /dev/null
@@ -0,0 +1 @@
+../util/util.hpp
\ No newline at end of file
diff --git a/util/Makefile b/util/Makefile
new file mode 100644 (file)
index 0000000..a385304
--- /dev/null
@@ -0,0 +1,25 @@
+CC = g++
+CFLAGS = "-Wall"
+LDFLAGS = -lm -lpthread
+CL = g++
+
+OBJ = cutimeutils.o timeutils.o cutimer.o cudynbuffer.o thread.o timer.o dynbuffer.o \
+       logger.o unittest.o test.o
+PROG = test
+
+all : $(PROG)
+
+$(PROG): $(OBJ)
+       $(CC) $(LDFLAGS) -g -o $(PROG) $(OBJ)
+
+%.o: %.cpp
+       $(CC) $(CFLAGS) -g -c $<
+
+debug: all
+       $(CC) $(LDFLAGS) -g -o $(PROG) $(OBJ)
+stable: all
+       $(CL) $(LDFLAGS) -o $(PROG) $(OBJ)
+
+.PHONY: clean
+clean:
+       rm -vfr *~ $(PROG) $(OBJ)
diff --git a/util/cudynbuffer.cpp b/util/cudynbuffer.cpp
new file mode 100644 (file)
index 0000000..aa90fce
--- /dev/null
@@ -0,0 +1,291 @@
+#include "test.hpp"
+
+class TestDynBuffer : public UnitTest {
+public:
+       TestDynBuffer() :
+               UnitTest("dynbuffer")
+       {
+
+       }
+public:
+       virtual void run(){
+               testBasic();
+               testAppend();
+               testAppendFormatted();
+               testAppendInt();
+               testAppendLittleEndian();
+               testAt();
+               testBufferClearLength();
+               testResize();
+               testSet();
+               testSetLength();
+               testStartsWith();
+               testValueOfLE();
+               testMemory();
+       }
+       void testBasic(){
+               DynBuffer buf("Hi", 0, 2);
+               buf.setBlocksize(2);
+               DynBuffer buf2(buf);
+               checkE("Hi", buf2);
+               DynBuffer buf3("hi!", 2);
+               checkE("hi", buf3);
+               DynBuffer buf4(4, 8);
+               buf4.append("1234");
+               checkE(4, buf4.length());
+               checkE(4, buf4.size());
+               buf4.append("5");
+               checkE(5, buf4.length());
+               checkE("12345", buf4);
+               checkE(4+8, buf4.size());
+               checkT(buf.length() <= buf.size());
+
+               buf = "abc";
+               checkE("abc", buf);
+               checkE(3, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               buf = DynBuffer("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+               checkE("ABCDEFGHIJKLMNOPQRSTUVWXYZ", buf);
+               checkE(26, buf.length());
+               checkT(buf.length() <= buf.size());
+       }
+       void testAppend(){
+               DynBuffer buf("1", 1, 1);
+               checkE(1, buf.length());
+               checkT(buf.length() <= buf.size());
+               buf.append("2x", 1);
+               checkE("12", buf);
+               checkE(2, buf.length());
+               checkT(buf.length() <= buf.size());
+               DynBuffer buf2("xy");
+               buf.append(buf2);
+               checkE("12xy", buf);
+               checkE(4, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               buf.clear();
+               checkE(0, buf.length());
+               buf.append("a");
+               checkE("a", buf);
+               checkE(1, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               buf.append("zzz", 2);
+               checkE("azz", buf);
+               checkE(3, buf.length());
+               checkT(buf.length() <= buf.size());
+       }
+       void testAppendFormatted(){
+               DynBuffer buf(2, 4);
+               buf.appendFormatted("%03d%c%s", 7, 'x', "Z.").appendFormatted("%.2s", "+++++");
+               checkE("007xZ.++", buf);
+               checkE(8, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               buf.clear();
+               buf.appendFormatted("%x", 15);
+               checkE("f", buf);
+               checkE(1, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               buf.appendFormatted("%c", '!');
+               checkE("f!", buf);
+               checkE(2, buf.length());
+               checkT(buf.length() <= buf.size());
+       }
+       void testAppendInt(){
+               DynBuffer buf(2,2);
+               buf.appendInt(123);
+               checkE("123", buf);
+               checkE(3, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               buf.appendInt(64, "%4x");
+               checkE("123  40", buf);
+               checkE(3 + 4, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               buf.clear();
+               buf.appendInt(2);
+               checkE("2", buf);
+               checkE(1, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               buf.appendInt(8, "%02d");
+               checkE("208", buf);
+               checkE(3, buf.length());
+               checkT(buf.length() <= buf.size());
+       }
+       void testAppendLittleEndian(){
+               DynBuffer buf(2,2);
+               buf.appendAsLE(0x31323334, 4);
+               checkE("4321", buf);
+               checkE(4, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               buf.appendAsLE(0x414243, 3);
+               checkE("4321CBA", buf);
+               checkE(7, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               buf.clear();
+               buf.appendAsLE(0x6162, 2);
+               checkE("ba", buf);
+               checkE(2, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               buf.appendAsLE(0x21);
+               checkE("ba!", buf);
+               checkE(3, buf.length());
+               checkT(buf.length() <= buf.size());
+       }
+       void testAt(){
+               DynBuffer buf("abcd");
+               checkT('a' == buf.at(0));
+               checkT('b' == buf.at(1));
+               checkT('c' == buf.at(2));
+               checkT('d' == buf.at(3));
+               checkT(0 == buf.at(-1));
+               checkT(0 == buf.at(4));
+       }
+
+       void testBufferClearLength(){
+               DynBuffer buf(2, 2);
+               checkE(2, buf.size());
+               checkE(0, buf.length());
+               const char* longString = "123456789 123456789 123456789";
+               int len = strlen(longString);
+               strcpy(buf.buffer(len), longString);
+               buf.setLength(len);
+               checkE(longString, buf);
+               checkE(29, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               buf.clear();
+               checkE("", buf);
+               checkE(0, buf.length());
+               checkT(buf.length() <= buf.size());
+       }
+       void testResize(){
+               DynBuffer buf(2, 2);
+               checkE(2, buf.size());
+               checkE(0, buf.length());
+               // blocksize is 2:
+               buf.resize(3);
+               checkE(4, buf.size());
+               checkE(0, buf.length());
+               // reserve more than blocksize:
+               buf.resize(7);
+               checkE(7, buf.size());
+               checkE(0, buf.length());
+       }
+       void testSet(){
+               DynBuffer buf("123");
+               checkE("123", buf);
+               checkE(3, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               // a shorter string:
+               buf.set("ab");
+               checkE("ab", buf);
+               checkE(2, buf.length());
+               checkT(buf.length() <= buf.size());
+
+               // a longer string:
+               buf.set("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+               checkE("ABCDEFGHIJKLMNOPQRSTUVWXYZ", buf);
+               checkE(26, buf.length());
+               checkT(buf.length() <= buf.size());
+       }
+       void testSetBlocksize(){
+               DynBuffer buf(2, 4);
+               checkE(2, buf.size());
+               checkE(0, buf.length());
+               checkE(4, buf.blocksize());
+
+               buf.setBlocksize(23);
+               checkE(2, buf.size());
+               checkE(0, buf.length());
+               checkE(23, buf.blocksize());
+       }
+       void testSetLength(){
+               DynBuffer buf("12345");
+               buf.setBlocksize(2);
+               checkE("12345", buf);
+               checkE(5, buf.size());
+               checkE(5, buf.length());
+
+               // make it shorter:
+               buf.setLength(4);
+               checkE("1234", buf);
+               checkE(4, buf.length());
+
+               // make it larger:
+               buf.setLength(8);
+               checkT(buf.startsWith("1234"));
+               checkE(8, buf.length());
+               checkE(8, buf.size());
+       }
+       // testSize() not implemented
+       void testStartsWith(){
+               DynBuffer buf("abc");
+               checkE(3, buf.length());
+
+               checkT(buf.startsWith("abc"));
+               checkT(buf.startsWith("ab"));
+               checkT(buf.startsWith("a"));
+
+               checkF(buf.startsWith("A"));
+               checkF(buf.startsWith("aBc"));
+
+               buf.clear().appendAsLE(0x32310041, 4);
+               checkE(4, buf.length());
+               checkE(0x41, buf.at(0));
+               checkE(0, buf.at(1));
+               checkE(0x31, buf.at(2));
+               checkE(0x32, buf.at(3));
+
+               checkT(buf.startsWith("A"));
+               checkE(4, buf.length());
+               DynBuffer buf2(buf);
+               checkT(buf.startsWith(buf2.str()));
+               for (int len = 3; len > 0; len--){
+                       buf2.append("x");
+                       checkF(buf.startsWith(buf2.str(), buf2.length()));
+                       buf2.setLength(len);
+                       checkT(buf.startsWith(buf2.str()));
+               }
+       }
+       void testValueOfLE(){
+               // int valueOfLE(int size, int index = 0, int defaultValue = -1);
+               DynBuffer buf("abc");
+               buf.appendAsLE(0x01020304, 4).append("xyz");
+               checkE(0x04, buf.valueOfLE(1, 3));
+               checkE(0x0304, buf.valueOfLE(2, 3));
+               checkE(0x020304, buf.valueOfLE(3, 3));
+               checkE(0x01020304, buf.valueOfLE(4, 3));
+               buf.buffer()[0] = 0x7f;
+               buf.buffer()[1] = 0xab;
+               buf.buffer()[2] = 0x77;
+               checkE(0x7f, buf.valueOfLE(1));
+               checkE(0xab7f, buf.valueOfLE(2));
+               checkE(0x77ab7f, buf.valueOfLE(3));
+       }
+       void testMemory(){
+               clock_t start = clock();
+               int count = 1000*1000;
+               int size = 10*1024*1024;
+               for (int ix = 0; ix < 1000; ix++){
+                       DynBuffer big(size);
+                       big.setLength(size + 10);
+               }
+               double duration = (clock() - start) / CLOCKS_PER_SEC;
+               printf("allocation of %d blocks with %d MiByte: %.3f sec\n", count, size / 1024 / 1024, duration);
+       }
+};
+
+void testDynBuffer(){
+       TestDynBuffer test;
+       test.run();
+}
diff --git a/util/cutimer.cpp b/util/cutimer.cpp
new file mode 100644 (file)
index 0000000..47d86a6
--- /dev/null
@@ -0,0 +1,142 @@
+#include "test.hpp"
+//#define TRACE_ON
+#include "trace.hpp"
+
+static Logger s_logger;
+static ThreadPool s_timerPool(256, &s_logger);
+
+class Counter : public Timer{
+public:
+       Counter(Unit unit, int count, int* result, Announcer* logger, ThreadPool* pool) :
+               Timer(count, 10, 1, unit, true, logger, pool),
+       m_counter(result)
+       {
+
+       }
+public:
+       virtual void timerTask(){
+               trace1("Counter::timerTask %d\n", threadId());
+               m_pool.lock();
+               ++*m_counter;
+               m_pool.unlock();
+       }
+public:
+       int* m_counter;
+};
+class TestTimer : public UnitTest {
+public:
+       TestTimer() :
+               UnitTest("cutimer")
+       {
+
+       }
+public:
+       virtual void run(){
+               testMany();
+               test2Timers();
+               testBasic();
+               testTimer();
+       }
+       void testTimer(){
+               m_logger.say(LOG_INFO, "Start testTimer");
+
+               TimeMeasurement m;
+               int max = 1000000;
+               for (int ix = max; ix > 0; --ix){
+                       struct timespec time;
+                       clock_gettime(CLOCK_REALTIME, &time);
+               }
+               m.stop();
+               m.logDuration(max, &m_logger, "clock_gettime()");
+
+               m.start();
+               for (int ix = 50; ix > 0; --ix){
+                       Thread::microSleep(10*1000);
+               }
+               m.stop();
+               m.logDuration(50, &m_logger, "microsleep(10*1000):");
+
+               m.start();
+               for (int ix = 500; ix > 0; --ix){
+                       Thread::microSleep(1000);
+               }
+               m.stop();
+               m.logDuration(500, &m_logger, "microsleep(1000):   ");
+
+               //extern int gv_calls, gv_rounds;
+               //m_logger.sayf(LOG_INFO, "calls: %d rounds: %d", gv_calls, gv_rounds);
+               
+               m.start();
+               for (int ix = 10000; ix > 0; --ix){
+                       Thread::microSleep(10);
+               }
+               m.stop();
+               m.logDuration(10000, &m_logger, "microsleep(10):   ");
+
+               m.start();
+               for (int ix = 10000; ix > 0; --ix){
+                       Thread::microSleep(1);
+               }
+               m.stop();
+               m.logDuration(10000, &m_logger, "microsleep(1):    ");
+
+       }
+
+       void testBasic(){
+               int count = 0;
+               TimeMeasurement m;
+               Counter* counter1 = new Counter(Timer::uMilliSeconds, 50, &count, &s_logger, &s_timerPool);
+               int id1 = counter1->threadId();
+               counter1->start();
+               s_timerPool.join(id1);
+               m.stop();
+               m.logDuration(&m_logger);
+               checkT(m.m_durationReal >= 0.06);
+               checkE(50, count);
+       }
+       void test2Timers(){
+               int count = 0;
+               TimeMeasurement m;
+               Counter* counter1 = new Counter(Timer::uMilliSeconds, 10, &count, &s_logger, &s_timerPool);
+               int id1 = counter1->threadId();
+               Counter* counter2 = new Counter(Timer::uMilliSeconds, 10, &count, &s_logger, &s_timerPool);
+               int id2 = counter2->threadId();
+               counter1->start();
+               counter2->start();
+               m_logger.say(LOG_INFO, "counter1 + 2 started");
+               s_timerPool.join(id1);
+               s_timerPool.join(id2);
+               m.stop();
+               m.logDuration(&m_logger, "test2timers:");
+               checkT(m.m_durationReal >= 0.02);
+               checkE(20, count);
+       }
+       void testMany(){
+               TimeMeasurement m;
+               int count = 0;
+               const int MAX = 2000;
+               int id = 1;
+               while(id <= MAX){
+                       if (s_timerPool.count() >= 256){
+                               Thread::microSleep(100);
+                       } else {
+                               Counter* counter = new Counter(Timer::uMicroSeconds, 5, &count,
+                                                                                       &s_logger, &s_timerPool);
+                               counter->start();
+                               id++;
+                       }
+               }
+
+               for (int id = 1; id <= MAX; id++){
+                       s_timerPool.join(id);
+               }
+               m.stop();
+               m.logDuration(MAX, &m_logger, "testMany:");
+               checkE(MAX*5, count);
+       }
+};
+
+void testTimer(){
+       TestTimer test;
+       test.run();
+}
diff --git a/util/cutimeutils.cpp b/util/cutimeutils.cpp
new file mode 100644 (file)
index 0000000..5efbd1e
--- /dev/null
@@ -0,0 +1,71 @@
+#include "test.hpp"
+#include "math.h"
+
+static Logger s_logger;
+static ThreadPool s_timerPool(2, &s_logger);
+
+class TestTimeUtils : public UnitTest {
+public:
+       TestTimeUtils() :
+               UnitTest("cutimeutils")
+       {
+
+       }
+public:
+       virtual void run(){
+               testBasic();
+               testTimeMeasurement();
+       }
+       void testBasic(){
+               const uint64_t MRD = 1000000000;
+               time_t start = time(NULL);
+               while(time(NULL) == start){
+                       // do nothing
+               }
+               start++;
+               double dummy = 0.0;
+               uint64_t startReal = TimeUtils::nanosecSinceEpoche();
+               uint64_t startCpu = TimeUtils::nanosecSinceBoot();
+               int count;
+               uint64_t current;
+               while(startReal + MRD > (current = TimeUtils::nanosecSinceEpoche())){
+                       dummy = dummy * 12.45 + 1.12345 * sin(dummy) + tan(dummy*7);
+                       if (dummy > 1 || dummy < -1)
+                               dummy = 1 / dummy;
+                       ++count;
+               }
+               double durationCpu = (TimeUtils::nanosecSinceBoot() - startCpu) / (double) MRD;
+               double durationReal = (current - startReal) / (double) MRD;
+               int duration = time(NULL) - start;
+               m_logger.sayf(LOG_INFO, "duration: %.3f / %.3f", durationReal, durationCpu);
+               checkT(durationReal >= 1.0);
+               m_logger.sayf(LOG_INFO, "diff real-cpu: %f", durationReal - durationCpu);
+               checkT(durationCpu <= durationReal);
+               checkE(1, duration);
+       }
+       void testTimeMeasurement(){
+               time_t start = time(NULL);
+               while(time(NULL) == start){
+                       // do nothing
+               }
+               TimeMeasurement m;
+               ++start;
+               double dummy = 0.0;
+               int count = 0;
+               while(time(NULL) == start){
+                       dummy = dummy * 12.45 + 1.12345 * sin(dummy) + tan(dummy*7);
+                       if (dummy > 1 || dummy < -1)
+                               dummy = 1 / dummy;
+                       ++count;
+               }
+               m.stop();
+               m.logDuration(&m_logger);
+               m.logDuration(count, &m_logger);
+       }
+
+};
+
+void testTimerUtils(){
+       TestTimeUtils test;
+       test.run();
+}
diff --git a/util/dynbuffer.cpp b/util/dynbuffer.cpp
new file mode 100644 (file)
index 0000000..6dd1773
--- /dev/null
@@ -0,0 +1,181 @@
+#include "util.hpp"
+
+/**
+ * Constructor.
+ *
+ * @param bufferSize   initial size of the buffer
+ * @param blocksize            minimal increment of size when reallocation is needed<br>
+ *                                             0: use <i>bufferSize</i> as block size
+ */
+DynBuffer::DynBuffer(size_t bufferSize, size_t blocksize) :
+       m_size(bufferSize == 0 ? 16 : bufferSize),
+       m_length(0),
+       m_blocksize(blocksize == 0 ? m_size : blocksize),
+       m_buffer(new char[m_size + 1]){
+       m_buffer[0] = '\0';
+}
+/**
+ * Constructor.
+ *
+ * @param value                initial value
+ * @param length       0: <i>strlen(value)</i> will be taken<br>
+ *                                     otherwise: the length of value
+ * @param size         the initial buffer size<br>
+ *                                     0: size depends on <i>length</i>
+ */
+DynBuffer::DynBuffer(const char* value, size_t length, size_t size) :
+       m_size(0),
+       m_length(length == 0 ? strlen(value) : length),
+       m_blocksize(256),
+       m_buffer(NULL){
+               m_size = size == 0 ? m_length : size < m_length ? m_length : size;
+               m_buffer = new char[m_size + 1];
+               memcpy(m_buffer, value, m_length);
+               m_buffer[m_length] = '\0';
+       }
+
+/**
+ * Destructor.
+ */
+DynBuffer::~DynBuffer(){
+       if (m_buffer != NULL){
+               delete[] m_buffer;
+       }
+       m_buffer = NULL;
+       m_length = 0;
+       m_size = 0;
+}
+/**
+ * Copy constructor.
+ *
+ * @param source       the source to copy
+ */
+DynBuffer::DynBuffer(const DynBuffer& source) :
+       m_size(source.m_length < 16 ? 16 : source.m_length),
+       m_length(source.m_length),
+       m_blocksize(256),
+       m_buffer(new char[m_size + 1])
+{
+       // copy including the trailing '\0':
+       memcpy(m_buffer, source.m_buffer, m_length + 1);
+}
+/**
+ * Assign operator.
+ *
+ * @param source       the source to copy
+ */
+DynBuffer& DynBuffer::operator =(const DynBuffer& source){
+       if (source.m_length == 0){
+               clear();
+       } else if (m_size >= source.m_length){
+               // copy with '\0':
+               memcpy(m_buffer, source.m_buffer, (m_length = source.m_length) + 1);
+       } else {
+               delete[] m_buffer;
+               m_buffer = new char[(m_size = m_length = source.m_length) + 1];
+               // copy with '\0':
+               memcpy(m_buffer, source.m_buffer, m_length + 1);
+       }
+       return *this;
+}
+/**
+ * Appends a string to the buffer.
+ *
+ * @param string       the string to append
+ * @param size         the length of the <i>string</i>. 0: use <i>strlen(string)</i>
+ * @return                     <i>*this</i> (for chaining)
+ */
+DynBuffer& DynBuffer::append(const char* string, size_t length){
+       if (length == 0)
+               length = strlen(string);
+       resize(m_length + length);
+       memcpy(m_buffer + m_length, string, length);
+       m_length += length;
+       m_buffer[m_length] = '\0';
+       return *this;
+}
+/**
+ * Appends a formatted string to the buffer.
+ *
+ * @param format       the format like <i>sprintf()</i>
+ * @param ...          the argument matching the format
+ * @return                     <i>*this</i> (for chaining)
+ */
+DynBuffer& DynBuffer::appendFormatted(const char* format, ...)
+{
+       char buffer[1024 * 64];
+       va_list argptr;
+       va_start(argptr, format);
+       vsnprintf(buffer, sizeof buffer, format, argptr);
+       va_end(argptr);
+       return append(buffer, strlen(buffer));
+}
+/**
+ * Appends an integer as string.
+ *
+ * @param value                the value to add
+ * @param format       the format like <i>sprintf()</i>
+ * @return                     <i>*this</i> (for chaining)
+ */
+DynBuffer& DynBuffer::appendInt(int value, const char* format){
+       char buffer[256];
+       snprintf(buffer, sizeof buffer, format, value);
+       return append(buffer, strlen(buffer));
+}
+/**
+ * Appends an integer as little endian binary number.
+ *
+ * @param value                the value to append
+ * @param size         the number of bytes to store: 1..sizeof(int)
+ * @return                     <i>*this</i> (for chaining)
+ */
+DynBuffer& DynBuffer::appendAsLE(int value, size_t size){
+       resize(m_length + size);
+       while(size-- > 0){
+               m_buffer[m_length++] = (char) value;
+               value >>= 8;
+       }
+       m_buffer[m_length] = '\0';
+       return *this;
+}
+/**
+ * Ensures that the size is greater or equal a given size.
+ *
+ * @param size         the wanted size (not including the implicite trailing '\0')
+ * @return                     <i>*this</i> (for chaining)
+ */
+DynBuffer& DynBuffer::resize(size_t size){
+       if (m_length + size > m_size){
+               m_size = m_size + m_blocksize;
+               if (size > m_size)
+                       m_size = size;
+               char* newBuffer = new char[m_size + 1];
+               // copy with '\0':
+               memcpy(newBuffer, m_buffer, m_length + 1);
+               delete[] m_buffer;
+               m_buffer = newBuffer;
+       }
+       return *this;
+}
+/**
+ * Gets a binary number in little endian format from the buffer.
+ *
+ * @param size         the size of the number: 1..8
+ * @param index                the first index
+ * @param defaultValue the return value if error occurs
+ * @return                     <i>defaultValue</i>: the index is wrong<br>
+ *                                     otherwise: the integer from the given position
+ */
+int DynBuffer::valueOfLE(int size, int index, int defaultValue){
+       int ix;
+       int rc = 0;
+       if (index < 0 || size_t(index + size) > m_length)
+               rc = defaultValue;
+       else {
+               for (ix = index + size - 1; ix >= index; ix--){
+                       rc = (rc << 8) + (ubyte_t) m_buffer[ix];
+               }
+       }
+       return rc;
+}
+
diff --git a/util/dynbuffer.hpp b/util/dynbuffer.hpp
new file mode 100644 (file)
index 0000000..ea7e39a
--- /dev/null
@@ -0,0 +1,152 @@
+#ifndef DYNBUFFER_H
+#define DYNBUFFER_H
+
+/**
+ * Implements a dynamic growing buffer.
+ * Can be used as "poor man's string implementation":
+ * the buffer always is terminated with a '\0' (like a C string).
+ * This ending zero is not counted by <i>m_length</i>.
+ */
+class DynBuffer {
+public:
+       DynBuffer(size_t bufferSize = 16, size_t blocksize = 0);
+       DynBuffer(const char* value, size_t length = 0, size_t size = 0);
+       ~DynBuffer();
+       DynBuffer(const DynBuffer& source);
+       DynBuffer& operator =(const DynBuffer& source);
+public:
+       DynBuffer& append(const char* string, size_t length = 0);
+       inline DynBuffer& append(const DynBuffer& source){
+               return append(source.str(), source.length());
+       }
+       DynBuffer& appendFormatted(const char* format, ...);
+       DynBuffer& appendInt(int value, const char* format = "%d");
+       DynBuffer& appendAsLE(int value, size_t size = 1);
+       /**
+        * Returns the character at a given position.
+        *
+        * @param index the index of the wanted character
+        * @return              '\0': wrong index<br>
+        *                              otherwise: the character at the position <i>index</i>
+        */
+       inline char at(int index) const {
+               return index < 0 || index >= (int) m_length ? '\0' : m_buffer[index];
+       }
+       /**
+        * Returns the current blocksize.
+        *
+        * @return      the current blocksize
+        */
+       inline size_t blocksize(){
+               return m_blocksize;
+       }
+       /**
+        * Returns the address of the internal buffer for writing to it.
+        *
+        * @param neededSize    0: do not change the size<br>
+        *                                              otherwise: the buffer is resized to this value
+        * @return                              the internal buffer
+        */
+       inline char* buffer(size_t neededSize = 0){
+               if (neededSize > 0)
+                       resize(neededSize);
+               return m_buffer;
+       }
+       /**
+        * Remove the buffer contents.
+        *
+        * @return      <i>*this</i> (for chaining)
+        */
+       inline DynBuffer& clear(){
+               m_length = 0;
+               m_buffer[0] = '\0';
+               return *this;
+       }
+       /**
+        * Returns the current buffer length.
+        *
+        * @return      the count of bytes in the buffer
+        */
+       inline size_t length() const {
+               return m_length;
+       }
+       DynBuffer& resize(size_t length);
+       /**
+        * Replace the buffer contents with a given string.
+        *
+        * @param source        the string to set
+        * @param length        0: <i>strlen(source)</i> will be used<br>
+        *                                      otherwise: the length of <i>source</i>
+        * @return                      <i>*this</i> (for chaining)
+        */
+       DynBuffer& set(const char* source, size_t length = 0){
+               return clear().append(source, length);
+       }
+       /**
+        * Sets the block size.
+        *
+        * @param blocksize     the new block size, the minimal increment when reallocation
+        */
+       void setBlocksize(size_t blocksize){
+               m_blocksize = blocksize;
+       }
+       /**
+        * Sets the length of the buffer.
+        *
+        * @param length        the new length. If larger than the size the size is increased
+        *                                      but the content is not changed.
+        * @return                      <i>*this</i> (for chaining)
+        */
+    inline DynBuffer& setLength(int length){
+               if (length > (int) m_size)
+                       resize(length);
+               m_buffer[m_length = length] = '\0';
+               return *this;
+    }
+       /**
+        * Returns the current size of the buffer.
+        *
+        * @return the current size
+        */
+       inline size_t size() const{
+               return m_size;
+       }
+       /**
+        * Tests whether the buffer starts with a given string.
+        *
+        * @param text          text to inspect
+        * @param length        length of <i>length</i><br>
+        *                                      0: use <i>strlen(text)</i>
+        * @return                      <i>true</i>: the text is found at the beginning of the buffer
+        */
+       inline bool startsWith(const char* text, size_t length = 0){
+               return memcmp(m_buffer, text, length == 0 ? strlen(text) : length) == 0;
+       }
+       /**
+        * Returns the buffer content as constant string.
+        */
+       inline const char* str() const {
+               return m_buffer;
+       }
+       /**
+        * Returns a binary stored integer (stored with <i>appendAsLE()</i>).
+        *
+        * @param size                  the size of the stored integer: 1..8
+        * @param index                 the index of the first byte of the integer
+        * @param defaultValue  the value returned if the index is out of bounds
+        * @return                              <i>defaultValue</i>: index is wrong<br>
+        *                                              otherwise: the integer stored at the given position
+        */
+       int valueOfLE(int size, int index = 0, int defaultValue = -1);
+private:
+       /// size of the buffer (without the trailing '\0')
+       size_t m_size;
+       /// length without the trailing '\0'
+       size_t m_length;
+       /// the minimal increment when reallocation is done
+       size_t m_blocksize;
+       /// Holds the buffer contents
+       char* m_buffer;
+};
+
+#endif // DYNBUFFER_H
diff --git a/util/logger.cpp b/util/logger.cpp
new file mode 100644 (file)
index 0000000..d0c1c7c
--- /dev/null
@@ -0,0 +1,41 @@
+#include "util.hpp"
+
+/**
+ * Constructor.
+ */
+Logger::Logger() :
+       m_buffer(1024, 2048)
+{
+
+}
+
+/**
+ * Destructor.
+ */
+Logger::~Logger()
+{
+}
+
+/**
+ * Writes a log message.
+ *
+ * @param level                type of the message: LOG_ERROR, LOG_WARNING, LOG_INFO
+ * @param message      the message to Writes
+ */
+void Logger::say(LogLevel level, const char* message){
+       const char* prefix;
+       switch(level){
+               case LOG_ERROR:
+                       m_errors++;
+                       prefix = "+++ ";
+                       break;
+               case LOG_WARNING:
+                       m_warnings++;
+                       prefix = "*** ";
+                       break;
+               default:
+                       prefix = "";
+                       break;
+       }
+       printf("%s%s\n", prefix, message);
+}
diff --git a/util/logger.hpp b/util/logger.hpp
new file mode 100644 (file)
index 0000000..627ba96
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef LOGGER_H
+#define LOGGER_H
+
+enum LogLevel {
+       LOG_ERROR = 1,
+       LOG_WARNING,
+       LOG_INFO,
+       LOG_DEBUG
+};
+class Announcer{
+public:
+       Announcer() :
+               m_errors(0),
+               m_warnings(0)
+       {}
+public:
+       virtual void say(LogLevel level, const char* message) = 0;
+public:
+       void say(LogLevel level, const DynBuffer& message){
+               say(level, message.str());
+       }
+       void sayf(LogLevel level, const char* format, ...){
+               char dest[1024 * 64];
+               va_list argptr;
+               va_start(argptr, format);
+               vsnprintf(dest, sizeof dest, format, argptr);
+               va_end(argptr);
+               say(level, dest);
+       }
+       int errors() const {
+               return m_errors;
+       }
+       int warnings() const {
+               return m_warnings;
+       }
+protected:
+       int m_errors;
+       int m_warnings;
+};
+
+class Logger : public Announcer
+{
+public:
+       Logger();
+       ~Logger();
+private:
+       // no implementation, no external usage!
+       Logger(const Logger& other);
+       Logger& operator=(const Logger& other);
+public:
+       void say(LogLevel level, const char* message);
+private:
+       DynBuffer m_buffer;
+};
+
+#endif // LOGGER_H
diff --git a/util/test.cpp b/util/test.cpp
new file mode 100644 (file)
index 0000000..96f6b5a
--- /dev/null
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv) {
+
+       extern void testDynBuffer();
+       extern void testTimer();
+       extern void testTimerUtils();
+       
+       testTimer();
+       testTimerUtils();
+       testDynBuffer();
+    return 0;
+}
diff --git a/util/test.hpp b/util/test.hpp
new file mode 100644 (file)
index 0000000..49095a0
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __TEST_HPP
+#define __TEST_HPP
+#include "util.hpp"
+#include "unittest.hpp"
+
+#endif
diff --git a/util/thread.cpp b/util/thread.cpp
new file mode 100644 (file)
index 0000000..41ac1e6
--- /dev/null
@@ -0,0 +1,256 @@
+#include "util.hpp"
+//#define TRACE_ON
+#include "trace.hpp"
+
+int ThreadPool::m_currentMaxThreads = 128;
+ThreadPool* ThreadPool::m_instance = NULL;
+const pthread_mutex_t ThreadPool::m_mutexInitializer = PTHREAD_MUTEX_INITIALIZER;
+
+int gv_rounds = 0;
+int gv_calls;
+
+/**
+ * Connects the posix thread to the class instance.
+ */
+void* threadStarter(void *param){
+       trace("threadstarter");
+       Thread* thread = reinterpret_cast<Thread*>(param);
+       thread->execute();
+       return param;
+}
+/**
+ * Constructor.
+ *
+ * @param pool                 the thread pool
+ * @param autoDelete   <i>true</i>: the instance destroys itself after <i>run()</i>
+ *                                             If <i>true</i> the <i>new</i> operator must be used for creation
+ * @param logger               the logger
+ */
+Thread::Thread(Announcer* logger, bool autoDelete, ThreadPool* pool) :
+       m_threadId(0),
+       m_shouldStop(false),
+       m_pool(pool == NULL ? *ThreadPool::instance(logger) : *pool),
+       m_logger(logger),
+       m_pthread(0),
+       m_autoDelete(autoDelete),
+       m_running(false)
+{
+       pool->append(*this);
+       trace1("Thread(%d)\n", m_threadId);
+}
+
+/**
+ * Destructor.
+ */
+Thread::~Thread() {
+       trace1("~Thread(%d)\n", m_threadId);
+}
+/**
+ * Executes the "run" of the thread and deactivates itself.
+ */
+void Thread::execute(){
+       trace1("Thread::execute(%d)\n", m_threadId);
+       m_running = true;
+       run();
+       m_pool.remove(*this);
+       if (m_autoDelete){
+               trace1("Thread::execute(%d) autodelete\n", m_threadId);
+               delete this;
+       }
+}
+
+/**
+ * Waits at least a given amount of microseconds.
+ *
+ * @param microseconds the time to wait
+ */
+void Thread::microSleep(uint64_t microseconds){
+       struct timespec wait;
+       wait.tv_sec = microseconds / 1000000;
+       wait.tv_nsec = microseconds % 1000000 * 1000;
+       nanosleep(&wait , NULL);
+}
+
+/**
+ * Lets the thread run.
+ */
+void Thread::start(){
+       pthread_create(&m_pthread, NULL, &threadStarter, this);
+       trace2("Thread::start(%d): %lx\n", m_threadId, m_pthread);
+}
+/**
+ * Introduces the termination of the thread.
+ *
+ * Cooperative mode: Sets only a flag. The thread must test this flag
+ * and terminate if set.
+ */
+void Thread::terminate(){
+       trace1("Thread::terminate(%d)\n", m_threadId);
+       m_shouldStop = true;
+}
+/**
+ * Returns the thread id.
+ *
+ * @return     the thread id
+ */
+int Thread::threadId() const{
+       return m_threadId;
+}
+
+/**
+ * Constructor.
+ *
+ * @param maxThreads   the maximum of active threads
+ * @param factory              a factory for creating threads
+ * @param logger               the logger
+ */
+ThreadPool::ThreadPool(int maxThreads, Announcer* logger) :
+       m_maxThreads(maxThreads),
+       m_list(new Thread*[m_currentMaxThreads]),
+       m_count(0),
+       m_nextId(1),
+       m_maxExitTime(50),
+       m_logger(logger),
+       m_mutex(m_mutexInitializer)
+{
+}
+/**
+ * Destructor.
+ */
+ThreadPool::~ThreadPool() {
+       trace1("~ThreadPool: %d\n", m_count);
+       terminateAll();
+       int maxMillisec = m_maxExitTime * 1000  + 500;
+       while (m_count != 0 && maxMillisec > 0){
+               usleep(10*1000);
+               maxMillisec -= 10;
+       }
+       // Force the termination of the non terminated:
+       pthread_t pthread;
+       trace1("~ThreadPool: force: %d\n", m_count);
+       while (m_count-- > 0){
+               lock();
+               Thread* thread = m_list[m_count];
+               pthread = thread->m_pthread;
+               unlock();
+               pthread_cancel(pthread);
+       }
+
+}
+/**
+ * Appends a thread to the pool.
+ *
+ * @param thread       the thred to add
+ */
+void ThreadPool::append(Thread& thread){
+       lock();
+       thread.m_threadId = m_nextId++;
+       if (m_count == m_maxThreads){
+               unlock();
+               m_logger->sayf(LOG_ERROR, "too many threads: %d", m_maxThreads);
+       } else {
+               m_list[m_count++] = &thread;
+               unlock();
+               trace1("ThreadPool::append(): id: %d\n", thread.m_threadId);
+       }
+}
+
+/**
+ * Waits for the finishing of a given thread.
+ *
+ * @param threadId     the thread identifier
+ * @return                     <i>NULL</i>: the thread does not already exist<br>
+ *                                     otherwise: the exit code of the thread
+ */
+void* ThreadPool::join(int threadId){
+       trace1("ThreadPool::join(%d):\n", threadId);
+       void* rc = NULL;
+       Thread* thread = NULL;
+       pthread_t pthread = -1;
+       bool running = false;
+       do {
+               lock();
+               thread = findById(threadId);
+               if(thread == NULL){
+                       unlock();
+                       break;
+               } else {
+                       pthread = thread->m_pthread;
+                       running = thread->m_running;
+               }
+               unlock();
+               if (! running)
+                       Thread::microSleep(10*1000);
+       } while(! running);
+       trace2("join: thread: %c %lx\n", thread != NULL ? 't' : 'f', pthread);
+       // Does the thread already exist?
+       if (pthread != (pthread_t) -1)
+               pthread_join(pthread, &rc);
+       trace1("join: rc: %c\n", rc == NULL ? 't' : 'f');
+       return rc;
+}
+
+/**
+ * Returns a singleton instance of the </i>ThreadPool</i>.
+ *
+ * @param logger       the logger
+ * @return                     the singleton instance
+ */
+ThreadPool* ThreadPool::instance(Announcer* logger)
+{
+       if (m_instance == NULL)
+               m_instance = new ThreadPool(m_currentMaxThreads, logger);
+       return m_instance;
+}
+
+/**
+ * Deactivates a thread.
+ *
+ * @param thread       the thread to deactivate.
+ */
+void ThreadPool::remove(Thread& thread){
+       // Destructor already called?
+       trace1("ThreadPool::remove(%d)\n", thread.threadId());
+       lock();
+       for (int ix = 0; ix < m_count; ix++){
+               if (m_list[ix] == &thread){
+                       // 012x45
+                       //    (count - ix - 1)
+                       memmove(m_list + ix, m_list + ix + 1,
+                               (--m_count - ix) * sizeof m_list[0]);
+                       thread.m_threadId = 0;
+                       break;
+               }
+       }
+       unlock();
+}
+
+/**
+ * Search a thread given by its id.
+ *
+ * @param id   the thread id
+ * @return             NULL: not found<br>
+ *                             the thread with the given id
+ */
+Thread* ThreadPool::findById(int id){
+       Thread* rc = NULL;
+       for (int ix = 0; ix < m_count; ix++){
+               if (m_list[ix]->threadId() == id){
+                       rc = m_list[ix];
+                       break;
+               }
+       }
+       trace2("ThreadPool::findBy(%d): %c\n", id, rc == NULL ? 'f' : 't');
+       return rc;
+}
+/**
+ * Deactivates a thread.
+ *
+ * @param thread       the thread to deactivate.
+ */
+void ThreadPool::terminateAll(){
+       trace("terminateAll()\n");
+       for (int ix = m_count - 1; ix >= 0; ix--){
+               m_list[ix]->terminate();
+       }
+}
diff --git a/util/thread.hpp b/util/thread.hpp
new file mode 100644 (file)
index 0000000..484b9d3
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef THREAD_H
+#define THREAD_H
+#include "pthread.h"
+class ThreadPool;
+
+/**
+ * Implements a base class of a posix thread.
+ */
+class Thread {
+       friend ThreadPool;
+public:
+    Thread(Announcer* logger, bool autoDelete = true, ThreadPool* pool = NULL);
+    virtual ~Thread();
+private:
+       // not implemented, private avoids usage.
+    Thread ( const Thread& other );
+    Thread& operator= ( const Thread& other );
+public:
+       int threadId() const;
+       /**
+        * Does the real things.
+        */
+       virtual void run() = 0;
+       void start();
+       void terminate();
+public:
+       static void microSleep(uint64_t microseconds);
+private:
+       friend void* threadStarter(void *);
+       void execute();
+protected:
+       int m_threadId;
+       bool m_shouldStop;
+       ThreadPool& m_pool;
+       Announcer* m_logger;
+       pthread_t m_pthread;
+       bool m_autoDelete;
+    bool m_running;
+};
+
+/**
+ * Manages a pool of threads.
+ *
+ * Allows waiting for all threads...
+ * Can (but not must) be used as singleton.
+ */
+class ThreadPool {
+public:
+       ThreadPool(int maxThreads, Announcer* logger);
+       virtual ~ThreadPool();
+private:
+       // not implemented, private avoids usage:
+       ThreadPool(const ThreadPool& source);
+       ThreadPool& operator =(const ThreadPool& source);
+public:
+       static ThreadPool* instance(Announcer* logger);
+       static void microSleep(int microseconds);
+public:
+       void append(Thread& thread);
+       /**
+        * Returns the number of threads.
+        *
+        * @return      the number of threads in the pool
+        */
+       inline int count() const {
+               return m_count;
+       }
+       void* join(int threadId);
+       /**
+        * Locks the pool specific mutual exclusion object.
+        */
+       inline void lock(){
+               pthread_mutex_lock(&m_mutex);
+       }
+       void remove(Thread& thread);
+       void terminateAll();
+       /**
+        * Unlocks the pool specific mutual exclusion object.
+        */
+       inline void unlock(){
+               pthread_mutex_unlock(&m_mutex);
+       }
+private:
+       Thread* findById(int id);
+public:
+       /// Change this value before calling the constructor.
+       /// The constructor can be called implicite by <i>instance()</i>.
+       static int m_currentMaxThreads;
+       static const pthread_mutex_t m_mutexInitializer;
+private:
+       static ThreadPool* m_instance;
+private:
+       int m_maxThreads;
+       Thread** m_list;
+       int m_count;
+       int m_nextId;
+       /// maximal waiting time for deactivating active threads while destructing (in 10 msec)
+       int m_maxExitTime;
+       Announcer* m_logger;
+       pthread_mutex_t m_mutex;
+};
+#endif // THREAD_H
diff --git a/util/timer.cpp b/util/timer.cpp
new file mode 100644 (file)
index 0000000..92fff64
--- /dev/null
@@ -0,0 +1,67 @@
+#include "util.hpp"
+//#define TRACE_ON
+#include "trace.hpp"
+/**
+ * Constructor.
+ *
+ * @param count                        number of actions
+ * @param delay                        the number of units to wait after the action
+ * @param startDelay   number of units to wait before the first action
+ * @param unit                 the unit used for the times: <i>uMicroSecond</i>...
+ * @param autoDelete   <i>true</i>: the instance destroys itself after the actions.
+ *                                             If <i>true</i> the <i>new</i> operator must be used for creation
+ * @param logger               the logger
+ * @param pool                 the thread pool. If <i>NULL</i> the global singleton is used
+ */
+Timer::Timer(int count, int delay, int m_startDelay, Unit unit,
+                 bool autoDelete, Announcer* logger, ThreadPool* pool) :
+       Thread(logger, autoDelete, pool),
+       m_count(count),
+       m_delay(delay),
+       m_startDelay(delay),
+       m_unit(unit)
+{
+       trace2("Timer(%d, %d)\n", count, delay);
+}
+
+Timer::~Timer() {
+
+}
+
+/**
+ * The action called by <i>Thread</i>.
+ */
+void Timer::run(){
+       trace1("Timer::run(%d)\n", threadId());
+       if (m_startDelay > 0){
+               switch (m_unit){
+               case uSeconds:
+                       sleep(m_startDelay);
+                       break;
+               case uMilliSeconds:
+                       microSleep(m_startDelay * 1000);
+                       break;
+               case uMicroSeconds:
+               default:
+                       microSleep(m_startDelay);
+                       break;
+               }
+       }
+       while(m_count-- > 0){
+               trace2("Timer::run(%d): %d\n", threadId(), m_count);
+               timerTask();
+               switch (m_unit){
+               case Timer::uSeconds:
+                       sleep(m_delay);
+                       break;
+               case Timer::uMilliSeconds:
+                       microSleep(m_delay * 1000);
+                       break;
+               case Timer::uMicroSeconds:
+                       usleep(m_delay);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
diff --git a/util/timer.hpp b/util/timer.hpp
new file mode 100644 (file)
index 0000000..95e1da2
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef TIMER_H
+#define TIMER_H
+
+/**
+ * Administrates threads for delayed actions.
+ *
+ * This is a abstract base class.
+ */
+class Timer : public Thread{
+public:
+       enum Unit {
+               uMicroSeconds,
+               uMilliSeconds,
+               uSeconds,
+       };
+public:
+    Timer(int count, int delay, int m_startDelay, Unit unit,
+                 bool autoDelete, Announcer* logger, ThreadPool* pool = NULL);
+    ~Timer();
+private:
+       // not implemented, private to avoid usage:
+    Timer ( const Timer& other );
+    Timer& operator= ( const Timer& other );
+public:
+       virtual void timerTask() = 0;
+private:
+       virtual void run();
+private:
+       int m_count;
+       int m_delay;
+       int m_startDelay;
+       Unit m_unit;
+};
+
+#endif // TIMER_H
diff --git a/util/timeutils.cpp b/util/timeutils.cpp
new file mode 100644 (file)
index 0000000..5266689
--- /dev/null
@@ -0,0 +1,63 @@
+#include "util.hpp"
+
+/**
+ * Constructor.
+ */
+TimeMeasurement::TimeMeasurement() :
+       m_startReal(0),
+       m_startCpu(0)
+{
+       start();
+}
+
+/**
+ * Destructor.
+ */
+TimeMeasurement::~TimeMeasurement() {
+
+}
+
+/**
+ * Logs the duration.
+ *
+ * @param logger       the logger
+ * @param format       the format (like in <i>sprintf()</i>) with 2 double placeholders<br>
+ *                                     if <i>NULL</i>: "duration real: %.3f cpu: %3f"
+ */
+void TimeMeasurement::logDuration(Announcer* logger, const char* format){
+       static const char* stdFormat1 = "duration real: %.3f sec cpu: %3f sec";
+       static const char* stdFormat2 = "duration real: %.6f sec cpu: %6f sec";
+       DynBuffer format2;
+       if (format == NULL)
+               format = m_durationReal >= 1.0 ? stdFormat1 : stdFormat2;
+       else if (strchr(format, '%') == NULL){
+               format2.append(format).append(" ").append(m_durationReal >= 1 ? stdFormat1 : stdFormat2);
+               format = format2.str();
+       }
+       logger->sayf(LOG_INFO, format, m_durationReal, m_durationCpu);
+}
+/**
+ * Logs the duration of a mass event measurement.
+ *
+ * @param count                number of events
+ * @param logger       the logger
+ * @param format       the format (like in <i>sprintf()</i>) with 1 integer
+ *                                     and 4 double placeholders<br>
+ *                                     if the format does not contain a placeholder ('%') it will be used
+ *                                     as format2 to the standard format<br>
+ *                                     if <i>NULL</i>: "count: %d duration real: %.3f (%.1f/sec) cpu: %3f (%.1f/s)"
+ */
+void TimeMeasurement::logDuration(int count, Announcer* logger, const char* format){
+       DynBuffer format2;
+       static const char* stdFormat1 = "count: %d duration real: %.3f sec (%.3g/sec) cpu: %.3f sec (%.3g/sec)";
+       static const char* stdFormat2 = "count: %d duration real: %.6f sec (%.3g/sec) cpu: %.6f sec (%.3g/sec)";
+       if (format == NULL)
+               format = m_durationReal >= 1.0 ? stdFormat1 : stdFormat2;
+       else if (strchr(format, '%') == NULL){
+               format2.append(format).append(" ").append(m_durationReal >= 1 ? stdFormat1 : stdFormat2);
+               format = format2.str();
+       }
+       logger->sayf(LOG_INFO, format,
+                       count, m_durationReal, m_durationReal == 0 ? 0.0 : count / m_durationReal,
+                       m_durationCpu, m_durationCpu == 0 ? 0.0 : count / m_durationCpu);
+}
diff --git a/util/timeutils.hpp b/util/timeutils.hpp
new file mode 100644 (file)
index 0000000..37eaaa2
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef TIME_UTILS_HPP
+#define TIME_UTILS_HPP
+
+class TimeUtils {
+public:
+       inline static uint64_t nanosecSinceEpoche(){
+               struct timespec time;
+               clock_gettime(CLOCK_REALTIME, &time);
+               return time.tv_sec * (uint64_t)1000000000 + time.tv_nsec;
+       }
+       inline static uint64_t nanosecSinceBoot(){
+               struct timespec time;
+               clock_gettime(CLOCK_REALTIME, &time);
+               return time.tv_sec * (uint64_t)1000000000 + time.tv_nsec;
+       }
+};
+
+class Announcer;
+class TimeMeasurement{
+public:
+    TimeMeasurement();
+    ~TimeMeasurement();
+private:
+       // not implemented, private to avoid usage:
+    TimeMeasurement ( const TimeMeasurement& other );
+    TimeMeasurement& operator= ( const TimeMeasurement& other );
+public:
+       void logDuration(Announcer* logger, const char* format = NULL);
+       void logDuration(int count, Announcer* logger, const char* format = NULL);
+       /**
+        * Starts a measurement event.
+        */
+       inline void start(){
+               m_startReal = TimeUtils::nanosecSinceEpoche();
+               m_startCpu = TimeUtils::nanosecSinceBoot();
+       }
+       /**
+        * Stops a measurement event.
+        */
+       inline void stop(){
+               m_durationCpu = (TimeUtils::nanosecSinceBoot() - m_startCpu) * 1E-9;
+               m_durationReal = (TimeUtils::nanosecSinceEpoche() - m_startReal) * 1E-9;
+       }
+public:
+       uint64_t m_startReal;
+       uint64_t m_startCpu;
+       double m_durationReal;
+       double m_durationCpu;
+};
+#endif // TIME_UTILS_HPP
diff --git a/util/trace.hpp b/util/trace.hpp
new file mode 100644 (file)
index 0000000..a298ac7
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef TRACE_HPP
+#define TRACE_HPP
+
+#ifdef TRACE_ON
+#define trace(msg) printf(msg)
+#define trace1(format, arg1) printf(format, arg1)
+#define trace2(format, arg1, arg2) printf(format, arg1, arg2)
+#define tracef(arg) printf(arg)
+#else
+#define trace(msg)
+#define trace1(format, arg1)
+#define trace2(format, arg1, arg2)
+#define tracef(arg)
+#endif // TRACE_ON
+
+#endif // TRACE_HPP
diff --git a/util/unittest.cpp b/util/unittest.cpp
new file mode 100644 (file)
index 0000000..dbfb355
--- /dev/null
@@ -0,0 +1,114 @@
+#include "test.hpp"
+
+/**
+ * Constructor.
+ *
+ * @param name name of the tested module
+ */
+UnitTest::UnitTest(const char* name) :
+       m_name(name),
+       m_logger()
+{
+       m_logger.sayf(LOG_INFO, "%s:", name);
+}
+
+/**
+ * Destructor.
+ */
+UnitTest::~UnitTest() {
+       if (m_logger.errors() > 0)
+               m_logger.sayf(LOG_ERROR, "%s has %d error(s)", m_name.str(), m_logger.errors());
+       else
+               m_logger.sayf(LOG_INFO, "%s successfully finished", m_name.str());
+}
+/**
+ * Tests, whether two strings are equals.
+ *
+ * @param expected     the expected value
+ * @param current      the current value
+ * @param line         line number for the error mesage
+ */
+void UnitTest::equals(const char* expected, const char* current, int line){
+       if (strcmp(expected, current) != 0){
+               char divider = strlen(expected) > 20 ? '\n' : ' ';
+               error("%s-%d different:%c'%s'%c'%s'", m_name.str(), line,
+                         divider, expected, divider, current);
+       }
+}
+/**
+ * Tests, whether two strings are equals.
+ *
+ * @param expected     the expected value
+ * @param current      the current value
+ * @param line         line number for the error mesage
+ */
+void UnitTest::equals(const char* expected, const DynBuffer& current, int line){
+       equals(expected, current.str(), line);
+}
+/**
+ * Tests, whether two numbers are equals.
+ *
+ * @param expected     the expected value
+ * @param current      the current value
+ * @param line         line number for the error mesage
+ */
+void UnitTest::equals(int expected, int current, int line){
+       if (expected != current){
+               error("%s-%d: different:%d/%d [%x/%x]", m_name.str(), line,
+                         expected, current, expected, current);
+       }
+}
+/**
+ * Tests, whether the condition is false
+ *
+ * @param condition    the condition to test
+ * @param line         line number for the error mesage
+ */
+void UnitTest::isFalse(bool condition, int line){
+       if (condition)
+               error("%s-%d: is true", m_name.str(), line);
+}
+/**
+ * Tests, whether the pointer is not null
+ *
+ * @param pointer      the pointer to test
+ * @param line         line number for the error mesage
+ */
+void UnitTest::isNotNull(void* ptr, int line){
+       if (ptr == NULL)
+               error("%s-%d: is null", m_name.str(), line);
+}
+/**
+ * Tests, whether the pointer is null
+ *
+ * @param pointer      the pointer to test
+ * @param line         line number for the error mesage
+ */
+void UnitTest::isNull(void* ptr, int line){
+       if (ptr != NULL)
+               error("%s-%d: is not null", m_name.str(), line);
+}
+/**
+ * Tests, whether the condition is false
+ *
+ * @param condition    the condition to test
+ * @param line         line number for the error mesage
+ */
+void UnitTest::isTrue(bool condition, int line){
+       if (! condition)
+               error("%s-%d: is false", m_name.str(), line);
+}
+/**
+ * Tests, whether the condition is false
+ *
+ * @param condition    the condition to test
+ * @param line         line number for the error mesage
+ */
+void UnitTest::error(const char* format, ...){
+       char buffer[64000];
+       va_list argptr;
+       va_start(argptr, format);
+       vsnprintf(buffer, sizeof buffer, format, argptr);
+       va_end(argptr);
+       m_logger.say(LOG_ERROR, buffer);
+}
diff --git a/util/unittest.hpp b/util/unittest.hpp
new file mode 100644 (file)
index 0000000..6154e89
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef UNITTEST_HPP
+#define UNITTEST_HPP
+
+/**
+ * Unit tests like JUnit (java)
+ */
+class UnitTest {
+public:
+    UnitTest(const char* name);
+    ~UnitTest();
+private:
+       // not implemented, only private use.
+    UnitTest ( const UnitTest& other );
+    UnitTest& operator= ( const UnitTest& other );
+public:
+       void equals(const char* expected, const char* current, int line);
+       void equals(const char* expected, const DynBuffer& current, int line);
+       void equals(int expected, int current, int line);
+       void isFalse(bool condition, int line);
+       void isNotNull(void* ptr, int line);
+       void isNull(void* ptr, int line);
+       void isTrue(bool condition, int line);
+       virtual void run() = 0;
+protected:
+       void error(const char* format, ...);
+protected:
+       DynBuffer m_name;
+       Logger m_logger;
+};
+
+#define checkE(expected, current) equals(expected, current, __LINE__)
+#define checkF(condition) isFalse(condition, __LINE__)
+#define checkT(condition) isTrue(condition, __LINE__)
+#define checkNN(condition) isNotNull(pointer, __LINE__)
+#define checkN(condition) isNull(pointer, __LINE__)
+
+#endif // UNITTEST_H
diff --git a/util/util.hpp b/util/util.hpp
new file mode 100644 (file)
index 0000000..b3e983b
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef UTIL_HPP
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <time.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <pthread.h>
+
+typedef unsigned char ubyte_t;
+typedef long long unsigned uint64_t;
+#include "dynbuffer.hpp"
+#include "timeutils.hpp"
+#include "logger.hpp"
+#include "thread.hpp"
+#include "timer.hpp"
+#endif