/.deps
/.options.*
/.profile
+/arm
/debug
/grep
+/libmspcore.a
+/libmspcore.so
/libmspstrings.a
/libmspstrings.so
-/mspstrings.pc
+/mspcore.pc
/release
/temp
/transcode
/win32
-
-// $Id$
+/* $Id$ */
-package "mspstrings"
+package "mspcore"
{
version "1.1";
- description "String utilities library";
+ description "Mikkosoft Productions core library";
- require "mspcore";
+ require "sigc++-2.0";
+ if "arch!=win32"
+ {
+ build_info
+ {
+ library "pthread";
+ };
+ };
+ if "arch=linux"
+ {
+ build_info
+ {
+ library "dl";
+ };
+ };
+
+ feature "exception_backtrace" "Generate a backtrace when an exception is thrown.";
+
+ headers "msp/core"
+ {
+ source "source/core";
+ install true;
+ };
+
+ headers "msp/time"
+ {
+ source "source/time";
+ install true;
+ };
+
+ headers "msp/debug"
+ {
+ source "source/debug";
+ install true;
+ };
headers "msp/strings"
{
install true;
};
- library "mspstrings"
+ library "mspcore"
{
+ source "source/core";
+ source "source/debug";
+ source "source/time";
source "source/strings";
source "source/stringcodec";
install true;
source "grep.cpp";
build_info
{
- library "mspstrings";
+ library "mspcore";
};
};
source "transcode.cpp";
build_info
{
- library "mspstrings";
+ library "mspcore";
};
};
+2.0
+
+== Changes from pre-2.0 mspcore ==
+
+1.1
+* Time zone support
+* Some more time operations
+* Bugfixes
+ - Compatibility fixes for FreeBSD and 64-bit systems
+ - Proper handling of string arguments with spaces in GetOpt
+ - DateTime addition fixes
+ - Timer no longer drops duplicate timeouts
+
+1.0
+* First released version
+
+== Changes from pre-2.0 mspstrings ==
+
1.1
* Codec autodetection
* lexical_cast rewritten from scratch
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006-2008, 2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <signal.h>
+#include <iostream>
+#include <typeinfo>
+#include "../debug/backtrace.h"
+#include "../debug/demangle.h"
+#include "../time/units.h"
+#include "../time/utils.h"
+#include "application.h"
+#include "except.h"
+
+using namespace std;
+
+namespace Msp {
+
+Application *Application::app_ = 0;
+Application::Starter *Application::starter_ = 0;
+void *Application::data_ = 0;
+
+Application::Application():
+ exit_code(0)
+{ }
+
+/**
+Constructs an instance of the registered application class and runs it. If the
+application throws a UsageError, the static usage() function is called.
+
+This function can only be called once. The global main() function provided by
+the library normally does it automatically at program startup.
+*/
+int Application::run(int argc, char **argv, void *data)
+{
+ static bool called = false;
+ if(called)
+ {
+ cerr<<"Trying to call Application::run_app twice!\n";
+ return 125;
+ }
+ called = true;
+
+ if(!starter_)
+ {
+ cerr<<"Trying to run with no RegisteredApplication class!\n";
+ return 126;
+ }
+
+ data_ = data;
+
+ try
+ {
+ try
+ {
+ app_ = starter_->create_app(argc, argv);
+ }
+ catch(const UsageError &e)
+ {
+ starter_->usage(e.what(), argv[0], e.get_brief());
+ return 1;
+ }
+
+ int result = app_->main();
+ Application *a = app_;
+ app_ = 0;
+ delete a;
+ return result;
+ }
+ catch(const exception &e)
+ {
+ delete app_;
+
+#ifdef WIN32
+ string msg = Debug::demangle(typeid(e).name())+":\n"+e.what();
+ MessageBoxA(0, msg.c_str(), "Uncaught exception", MB_OK|MB_ICONERROR);
+#else
+ cerr<<"An uncaught exception occurred.\n";
+ cerr<<" type: "<<Debug::demangle(typeid(e).name())<<'\n';
+ cerr<<" what(): "<<e.what()<<'\n';
+
+ const Exception *exc = dynamic_cast<const Exception *>(&e);
+ if(exc && !exc->get_backtrace().get_frames().empty())
+ {
+ cerr<<" backtrace:\n";
+ const list<Debug::Backtrace::StackFrame> &frames = exc->get_backtrace().get_frames();
+ for(list<Debug::Backtrace::StackFrame>::const_iterator i=frames.begin(); i!=frames.end(); ++i)
+ cerr<<" "<<*i<<'\n';
+ }
+#endif
+
+ return 124;
+ }
+}
+
+/**
+Prints a message describing the usage of the application. The default version
+will blame the programmer for being lazy.
+
+@param reason Why the function was called
+@param argv0 The value of argv[0], to be used in the message
+@param brief Whether to print a brief or long usage message
+*/
+void Application::usage(const char *reason, const char *, bool)
+{
+ if(reason)
+ cerr<<"UsageError: "<<reason<<'\n';
+ cerr<<"The programmer was lazy and didn't write a usage() function for this application.\n";
+}
+
+/**
+Default main loop. Calls tick() repeatedly until exit() is called. A custom
+main loop should monitor the done member variable and return exit_code.
+*/
+int Application::main()
+{
+ done = false;
+ while(!done)
+ tick();
+
+ return exit_code;
+}
+
+/**
+Sets the specified signal to be delivered to the sighandler member function.
+*/
+void Application::catch_signal(int s)
+{
+ signal(s, &sighandler_);
+}
+
+/**
+Causes the application to exit gracefully with the given exit code.
+*/
+void Application::exit(int c)
+{
+ done = true;
+ exit_code = c;
+}
+
+/**
+Static wrapper function to call a member function of the Application instance.
+*/
+void Application::sighandler_(int s)
+{
+ app_->sighandler(s);
+}
+
+
+Application::Starter::Starter()
+{
+ if(starter_)
+ throw InvalidState("Can't create more than one Starter instance");
+
+ starter_ = this;
+}
+
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006-2008, 2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_CORE_APPLICATION_H_
+#define MSP_CORE_APPLICATION_H_
+
+namespace Msp {
+
+/**
+Base class for applications. Inherit the main class from this and add a static
+member of type RegApp<MainClass>.
+*/
+class Application
+{
+protected:
+ class Starter
+ {
+ protected:
+ Starter();
+ public:
+ virtual ~Starter() { }
+
+ virtual Application *create_app(int, char **) = 0;
+ virtual void usage(const char *, const char *, bool) = 0;
+ };
+
+ bool done;
+ int exit_code;
+
+private:
+ static Starter *starter_;
+ static Application *app_;
+ static void *data_;
+
+ Application(const Application &);
+ Application &operator=(const Application &);
+protected:
+ Application();
+public:
+ virtual ~Application() { }
+
+ static int run(int, char **, void * =0);
+ static void usage(const char *, const char *, bool);
+ static void *get_data() { return data_; }
+
+protected:
+ virtual int main();
+ void catch_signal(int);
+ void exit(int);
+ virtual void tick() { }
+ virtual void sighandler(int) { }
+private:
+ static void sighandler_(int);
+};
+
+
+template<typename T>
+class RegisteredApplication: public Application
+{
+private:
+ class Starter: public Application::Starter
+ {
+ public:
+ Application *create_app(int argc, char **argv) { return new T(argc, argv); }
+ void usage(const char *r, const char *a, bool b) { T::usage(r, a, b); }
+ };
+
+ static Starter starter_;
+
+protected:
+ // Force the starter into existence
+ RegisteredApplication() { (void)starter_; }
+};
+
+template<typename T>
+typename RegisteredApplication<T>::Starter RegisteredApplication<T>::starter_;
+
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <sstream>
+#include <cstring>
+#ifdef WIN32
+#include <windows.h>
+#endif
+#include "except.h"
+
+using namespace std;
+
+namespace Msp {
+
+Exception::Exception(const string &w):
+ wot(w)
+{
+#ifdef WITH_EXCEPTION_BACKTRACE
+ bt = Debug::Backtrace::create();
+#endif
+}
+
+Exception &Exception::at(const std::string &w) throw()
+{
+ wer = w;
+ wot = wer+": "+wot;
+ return *this;
+}
+
+
+SystemError::SystemError(const string &w_, int e):
+ Exception(build_what(w_, e)),
+ err(e)
+{ }
+
+string SystemError::build_what(const string &w, int e)
+{
+ ostringstream buf;
+ buf<<w<<": ";
+#ifdef WIN32
+ char msg[1024];
+ if(FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, e, 0, msg, sizeof(msg), 0))
+ buf<<msg;
+ else
+ buf<<e;
+#else
+ buf<<strerror(e);
+#endif
+ return buf.str();
+}
+
+
+KeyError::KeyError(const string &w_, const string &k):
+ Exception(w_+" ("+k+")"),
+ key(k)
+{ }
+
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_CORE_ERROR_H_
+#define MSP_CORE_ERROR_H_
+
+#include <exception>
+#include <string>
+#include "../debug/backtrace.h"
+
+namespace Msp {
+
+/**
+Base class for all Msp exceptions.
+*/
+class Exception: public std::exception
+{
+private:
+ std::string wot;
+ std::string wer;
+ Debug::Backtrace bt;
+
+public:
+ Exception(const std::string &);
+ ~Exception() throw() { }
+
+ const char *what() const throw() { return wot.c_str(); }
+ Exception &at(const std::string &) throw();
+ const char *where() const throw() { return wer.c_str(); }
+ const Debug::Backtrace &get_backtrace() const throw() { return bt; }
+};
+
+/**
+Thrown when a function parameter has an invalid value.
+*/
+class InvalidParameterValue: public Exception
+{
+public:
+ InvalidParameterValue(const std::string &w_): Exception(w_) { }
+};
+
+/**
+Thrown when a lookup from a map fails.
+*/
+class KeyError: public Exception
+{
+private:
+ std::string key;
+
+public:
+ KeyError(const std::string &w_): Exception(w_) { }
+ KeyError(const std::string &w_, const std::string &k);
+ ~KeyError() throw() { }
+
+ const std::string &get_key() const { return key; }
+};
+
+/**
+Thrown when the current object state doesn't allow the requested action.
+*/
+class InvalidState: public Exception
+{
+public:
+ InvalidState(const std::string &w_): Exception(w_) { }
+};
+
+/**
+Thrown when the application is invoked with wrong parameters.
+*/
+class UsageError: public Exception
+{
+private:
+ bool brief;
+
+public:
+ UsageError(const std::string &r, bool b = true): Exception(r), brief(b) { }
+ bool get_brief() const { return brief; }
+};
+
+/**
+Thrown when a system call fails.
+*/
+class SystemError: public Exception
+{
+private:
+ int err;
+
+public:
+ SystemError(const std::string &, int);
+ int get_error_code() const { return err; }
+
+private:
+ static std::string build_what(const std::string &, int);
+};
+
+/**
+Thrown when "impossible" things happen.
+*/
+class LogicError: public Exception
+{
+public:
+ LogicError(const std::string &w_): Exception(w_) { }
+};
+
+template<typename E>
+void throw_at(E e, const std::string &a)
+{ e.at(a); throw e; }
+
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006-2009, 2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "getopt.h"
+
+using namespace std;
+
+namespace Msp {
+
+GetOpt::~GetOpt()
+{
+ for(list<OptBase *>::iterator i=opts.begin(); i!=opts.end(); ++i)
+ delete *i;
+}
+
+GetOpt::OptBase &GetOpt::get_option(char s)
+{
+ for(list<OptBase *>::iterator i=opts.begin(); i!=opts.end(); ++i)
+ if((*i)->get_short()==s)
+ return **i;
+ throw UsageError(string("Unknown option -")+s);
+}
+
+GetOpt::OptBase &GetOpt::get_option(const string &l)
+{
+ for(list<OptBase *>::iterator i=opts.begin(); i!=opts.end(); ++i)
+ if((*i)->get_long()==l)
+ return **i;
+ throw UsageError(string("Unknown option --")+l);
+}
+
+void GetOpt::operator()(unsigned argc, const char *const *argv)
+{
+ unsigned i = 1;
+ for(; i<argc;)
+ {
+ if(argv[i][0]=='-')
+ {
+ if(argv[i][1]=='-')
+ {
+ if(!argv[i][2])
+ break;
+
+ i += process_long(argv+i);
+ }
+ else
+ i += process_short(argv+i);
+ }
+ else
+ args.push_back(argv[i++]);
+ }
+
+ for(; i<argc; ++i)
+ args.push_back(argv[i]);
+}
+
+unsigned GetOpt::process_long(const char *const *argp)
+{
+ // Skip the --
+ const char *arg = argp[0]+2;
+
+ // See if the argument contains an =
+ unsigned equals = 0;
+ for(; arg[equals] && arg[equals]!='='; ++equals) ;
+
+ OptBase &opt = get_option(string(arg, equals));
+
+ if(arg[equals])
+ // Process the part after the = as option argument
+ opt.process(arg+equals+1);
+ else if(opt.get_arg_type()==REQUIRED_ARG)
+ {
+ if(!argp[1])
+ throw UsageError("Premature end of arguments");
+
+ // Process the next argument as option argument
+ opt.process(argp[1]);
+ return 2;
+ }
+ else
+ opt.process();
+
+ return 1;
+}
+
+unsigned GetOpt::process_short(const char *const *argp)
+{
+ // Skip the -
+ const char *arg = argp[0]+1;
+
+ // Loop through all characters in the argument
+ for(; *arg; ++arg)
+ {
+ OptBase &opt = get_option(*arg);
+
+ if(arg[1] && opt.get_arg_type()!=NO_ARG)
+ {
+ // Need an option argument and we have characters left - use them
+ opt.process(arg+1);
+ return 1;
+ }
+ else if(opt.get_arg_type()==REQUIRED_ARG)
+ {
+ if(!argp[1])
+ throw UsageError("Premature end of arguments");
+
+ // Use the next argument as option argument
+ opt.process(argp[1]);
+ return 2;
+ }
+ else
+ opt.process();
+ }
+
+ return 1;
+}
+
+string GetOpt::generate_usage(const string &argv0) const
+{
+ ostringstream line;
+
+ line<<argv0;
+ for(list<OptBase *>::const_iterator i=opts.begin(); i!=opts.end(); ++i)
+ {
+ line<<" [";
+ if((*i)->get_short())
+ {
+ line<<'-'<<(*i)->get_short();
+ if(!(*i)->get_long().empty())
+ line<<'|';
+ else if((*i)->get_arg_type()==OPTIONAL_ARG)
+ line<<'['<<(*i)->get_metavar()<<']';
+ else if((*i)->get_arg_type()==REQUIRED_ARG)
+ line<<' '<<(*i)->get_metavar();
+ }
+ if(!(*i)->get_long().empty())
+ {
+ line<<"--"<<(*i)->get_long();
+
+ if((*i)->get_arg_type()==OPTIONAL_ARG)
+ line<<"[="<<(*i)->get_metavar()<<']';
+ else if((*i)->get_arg_type()==REQUIRED_ARG)
+ line<<'='<<(*i)->get_metavar();
+ }
+ line<<']';
+ }
+
+ return line.str();
+}
+
+string GetOpt::generate_help() const
+{
+ bool any_short = false;
+ for(list<OptBase *>::const_iterator i=opts.begin(); (!any_short && i!=opts.end()); ++i)
+ any_short = (*i)->get_short();
+
+ string::size_type maxw = 0;
+ list<string> switches;
+ for(list<OptBase *>::const_iterator i=opts.begin(); i!=opts.end(); ++i)
+ {
+ ostringstream swtch;
+ if((*i)->get_short())
+ {
+ swtch<<'-'<<(*i)->get_short();
+ if(!(*i)->get_long().empty())
+ swtch<<", ";
+ else if((*i)->get_arg_type()==OPTIONAL_ARG)
+ swtch<<'['<<(*i)->get_metavar()<<']';
+ else if((*i)->get_arg_type()==REQUIRED_ARG)
+ swtch<<' '<<(*i)->get_metavar();
+ }
+ else if(any_short)
+ swtch<<" ";
+ if(!(*i)->get_long().empty())
+ {
+ swtch<<"--"<<(*i)->get_long();
+
+ if((*i)->get_arg_type()==OPTIONAL_ARG)
+ swtch<<"[="<<(*i)->get_metavar()<<']';
+ else if((*i)->get_arg_type()==REQUIRED_ARG)
+ swtch<<'='<<(*i)->get_metavar();
+ }
+ switches.push_back(swtch.str());
+ maxw = max(maxw, switches.back().size());
+ }
+
+ string result;
+ list<string>::const_iterator j = switches.begin();
+ for(list<OptBase *>::const_iterator i=opts.begin(); i!=opts.end(); ++i, ++j)
+ {
+ result += " "+*j;
+ result += string(maxw+2-j->size(), ' ');
+ result += (*i)->get_help();
+ result += '\n';
+ }
+
+ return result;
+}
+
+
+GetOpt::OptBase::OptBase(char s, const std::string &l, ArgType a):
+ shrt(s),
+ lng(l),
+ arg_type(a),
+ seen_count(0),
+ metavar("ARG")
+{ }
+
+GetOpt::OptBase &GetOpt::OptBase::set_help(const string &h)
+{
+ help = h;
+ return *this;
+}
+
+GetOpt::OptBase &GetOpt::OptBase::set_help(const string &h, const string &m)
+{
+ help = h;
+ metavar = m;
+ return *this;
+}
+
+void GetOpt::OptBase::process()
+{
+ if(arg_type==REQUIRED_ARG)
+ throw UsageError("--"+lng+" requires an argument");
+ ++seen_count;
+
+ store();
+}
+
+void GetOpt::OptBase::process(const string &arg)
+{
+ if(arg_type==NO_ARG)
+ throw UsageError("--"+lng+" takes no argument");
+ ++seen_count;
+
+ store(arg);
+}
+
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006-2009, 2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_CORE_GETOPT_H_
+#define MSP_CORE_GETOPT_H_
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include "except.h"
+
+namespace Msp {
+
+class GetOpt
+{
+public:
+ enum ArgType
+ {
+ NO_ARG,
+ OPTIONAL_ARG,
+ REQUIRED_ARG
+ };
+
+ class OptBase
+ {
+ protected:
+ char shrt;
+ std::string lng;
+ ArgType arg_type;
+ unsigned seen_count;
+ std::string help;
+ std::string metavar;
+
+ OptBase(char, const std::string &, ArgType);
+ public:
+ virtual ~OptBase() { }
+
+ OptBase &set_help(const std::string &);
+ OptBase &set_help(const std::string &, const std::string &);
+ char get_short() const { return shrt; }
+ const std::string &get_long() const { return lng; }
+ ArgType get_arg_type() const { return arg_type; }
+ const std::string &get_help() const { return help; }
+ const std::string &get_metavar() const { return metavar; }
+ unsigned get_seen_count() const { return seen_count; }
+ void process();
+ void process(const std::string &);
+ protected:
+ virtual void store() = 0;
+ virtual void store(const std::string &) = 0;
+ };
+
+private:
+ template<typename T>
+ class Option: public OptBase
+ {
+ public:
+ Option(char s, const std::string &l, T &d, ArgType a): OptBase(s, l, a), data(d) { }
+
+ virtual void store() { }
+
+ virtual void store(const std::string &a)
+ {
+ T tmp;
+ std::istringstream ss(a);
+ ss>>tmp;
+ if(ss.fail())
+ throw UsageError("Invalid argument for --"+lng);
+
+ data = tmp;
+ }
+ private:
+ T &data;
+ };
+
+ template<typename T>
+ class ListOption: public OptBase
+ {
+ public:
+ ListOption(char s, const std::string &l, T &d, ArgType a): OptBase(s, l, a), data(d)
+ { if(arg_type!=REQUIRED_ARG) throw Exception("ListOption with arg_type!=REQUIRED makes no sense"); }
+
+ virtual void store() { }
+
+ virtual void store(const std::string &a)
+ {
+ typename T::value_type tmp;
+ std::istringstream ss(a);
+ ss>>tmp;
+ if(ss.fail())
+ throw UsageError("Invalid argument for --"+lng);
+
+ data.push_back(tmp);
+ }
+ private:
+ T &data;
+ };
+
+ std::list<OptBase *> opts;
+ std::vector<std::string> args;
+
+public:
+ ~GetOpt();
+
+ const std::vector<std::string> &get_args() const { return args; }
+
+ template<typename T>
+ OptBase &add_option(char s, const std::string &l, T &d, ArgType a = NO_ARG)
+ { opts.push_back(new Option<T>(s, l, d, a)); return *opts.back(); }
+
+ template<typename T>
+ OptBase &add_option(char s, const std::string &l, std::list<T> &d, ArgType a = REQUIRED_ARG)
+ { opts.push_back(new ListOption<std::list<T> >(s, l, d, a)); return *opts.back(); }
+
+ template<typename T>
+ OptBase &add_option(const std::string &l, T &d, ArgType a)
+ { return add_option(0, l, d, a); }
+
+private:
+ OptBase &get_option(char);
+ OptBase &get_option(const std::string &);
+
+public:
+ /** Processes argc/argv style command line arguments. The contents of argv
+ will be unchanged; use get_args to access non-option arguments. */
+ void operator()(unsigned, const char *const *);
+
+private:
+ /** Processes a long option. Returns the number of arguments eaten. */
+ unsigned process_long(const char *const *);
+
+ /** Processes short options. Returns the number of arguments eaten. */
+ unsigned process_short(const char *const *);
+
+public:
+ /** Generates a single line that describes known options. */
+ std::string generate_usage(const std::string &) const;
+
+ /** Generates help for known options in tabular format, one option per
+ line. The returned string will have a linefeed at the end. */
+ std::string generate_help() const;
+};
+
+template<> inline void GetOpt::Option<bool>::store() { data = true; }
+template<> inline void GetOpt::Option<unsigned>::store() { ++data; }
+
+template<> inline void GetOpt::Option<std::string>::store(const std::string &a)
+{ data = a; }
+
+template<> inline void GetOpt::ListOption<std::list<std::string> >::store(const std::string &a)
+{ data.push_back(a); }
+
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#include "application.h"
+
+#ifdef WIN32
+int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/)
+{
+ int argc = 0;
+ LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc);
+ return Msp::Application::run(argc, (char **)argv, hInstance);
+}
+#endif
+
+int main(int argc, char **argv)
+{ return Msp::Application::run(argc, argv); }
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_CORE_META_H_
+#define MSP_CORE_META_H_
+
+namespace Msp {
+
+template<typename T>
+struct RemoveConst
+{ typedef T Type; };
+
+template<typename T>
+struct RemoveConst<const T>
+{ typedef T Type; };
+
+template<typename T>
+struct RemoveReference
+{ typedef T Type; };
+
+template<typename T>
+struct RemoveReference<T &>
+{ typedef T Type; };
+
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_CORE_MUTEX_H_
+#define MSP_CORE_MUTEX_H_
+
+#include "refptr.h"
+#include "types.h"
+
+namespace Msp {
+
+class Mutex
+{
+ friend class Semaphore;
+
+private:
+ MutexHandle mutex;
+
+public:
+#ifndef WIN32
+ Mutex() { pthread_mutex_init(&mutex, 0); }
+ int lock() { return pthread_mutex_lock(&mutex); }
+ int trylock() { return pthread_mutex_trylock(&mutex); }
+ int unlock() { return pthread_mutex_unlock(&mutex); }
+ ~Mutex() { pthread_mutex_destroy(&mutex); }
+#else
+ Mutex() { mutex = CreateMutex(0, false, 0); }
+ int lock() { return WaitForSingleObject(mutex, INFINITE)==WAIT_OBJECT_0; }
+ int trylock() { return WaitForSingleObject(mutex, 0)==WAIT_OBJECT_0; }
+ int unlock() { return !ReleaseMutex(mutex); }
+ ~Mutex() { CloseHandle(mutex); }
+#endif
+};
+
+/**
+Locks the mutex for te lifetime of the object.
+*/
+class MutexLock
+{
+private:
+ Mutex &mutex;
+
+public:
+ MutexLock(Mutex &m, bool l = true): mutex(m) { if(l) mutex.lock(); }
+ ~MutexLock() { mutex.unlock(); }
+
+ int lock() { return mutex.lock(); }
+private:
+ MutexLock(const MutexLock &);
+ MutexLock &operator=(const MutexLock &);
+};
+
+/**
+Protects a pointer with a mutex. As long as the MutexPtr (or a copy of it)
+exists, the mutex will stay locked.
+*/
+template<typename T>
+class MutexPtr
+{
+public:
+ MutexPtr(T *d, Mutex &m): mutex(new MutexLock(m)), data(d) { }
+
+ T &operator*() const { return *data; }
+ T *operator->() const { return data; }
+ void clear() { mutex=0; data = 0; }
+private:
+ RefPtr<MutexLock> mutex;
+ T *data;
+};
+
+/*template<typename T>
+class MutexPtr: public RefCount
+{
+public:
+ MutexPtr(T *d, Mutex &m): mutex(m), data(d) { mutex.lock(); }
+ MutexPtr(const MutexPtr<T> &p): RefCount(p), mutex(p.mutex), data(p.data) { }
+ T &operator*() const { return *data; }
+ T *operator->() const { return data; }
+ void clear() { decref(); data = 0; }
+ ~MutexPtr() { decref(); }
+protected:
+ Mutex &mutex;
+ T *data;
+
+ bool decref()
+ {
+ if(!RefCount::decref())
+ {
+ mutex.unlock();
+ return false;
+ }
+ return true;
+ }
+};*/
+
+}
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006-2007, 2010-2011 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_CORE_REFPTR_H_
+#define MSP_CORE_REFPTR_H_
+
+namespace Msp {
+
+/**
+A reference counting smart pointer. When the last RefPtr for the data gets
+destroyed, the data is deleted as well.
+*/
+template<typename T>
+class RefPtr
+{
+ template<typename U> friend class RefPtr;
+
+private:
+ enum
+ {
+ KEEP = 1U<<(sizeof(unsigned)*8-1)
+ };
+
+ T *data;
+ unsigned *count;
+
+public:
+ RefPtr(): data(0), count(0) { }
+ RefPtr(T *d): data(d), count(data ? new unsigned(1) : 0) { }
+private:
+ RefPtr(T *d, unsigned *c): data(d), count(d ? c : 0) { incref(); }
+
+public:
+ /* Must have this or the compiler will generate a default copy-c'tor despite
+ the template version */
+ RefPtr(const RefPtr &p): data(p.data), count(p.count) { incref(); }
+
+ template<typename U>
+ RefPtr(const RefPtr<U> &p): data(p.data), count(p.count) { incref(); }
+
+ ~RefPtr() { decref(); }
+
+ RefPtr &operator=(T *d)
+ {
+ decref();
+ data = d;
+ count = (d ? new unsigned(1) : 0);
+ return *this;
+ }
+
+ // Likewise for the assignment operator
+ RefPtr &operator=(const RefPtr &p) { return assign(p); }
+
+ template<typename U>
+ RefPtr &operator=(const RefPtr<U> &p) { return assign(p); }
+
+private:
+ template<typename U>
+ RefPtr &assign(const RefPtr<U> &p)
+ {
+ decref();
+ data = p.data;
+ count = p.count;
+ incref();
+ return *this;
+ }
+
+public:
+ /** Makes the RefPtr release its reference of the data without deleting it.
+ Note that if there are other RefPtrs left with the same data, it might
+ still get deleted automatically. */
+ T *release()
+ {
+ T *d = data;
+ data = 0;
+ decref();
+ count = 0;
+ return d;
+ }
+
+ /** Marks the data to not be deleted. This affects all RefPtrs with the
+ same data. */
+ void keep()
+ {
+ if(count)
+ *count |= KEEP;
+ }
+
+ T *get() const { return data; }
+ T &operator*() const { return *data; }
+ T *operator->() const { return data; }
+ operator bool() const { return data!=0; }
+
+ template<typename U>
+ static RefPtr<T> cast_dynamic(const RefPtr<U> &p)
+ { return RefPtr<T>(dynamic_cast<T *>(p.data), p.count); }
+
+private:
+ void incref()
+ {
+ if(!count) return;
+ ++*count;
+ }
+
+ void decref()
+ {
+ if(!count) return;
+ --*count;
+ if(!*count)
+ {
+ delete data;
+ delete count;
+ data = 0;
+ count = 0;
+ }
+ else if(*count==KEEP)
+ {
+ delete count;
+ data = 0;
+ count = 0;
+ }
+ }
+};
+
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef WIN32
+#include <sys/time.h>
+#endif
+#include <errno.h>
+#include "semaphore.h"
+#include "../time/timestamp.h"
+#include "../time/units.h"
+#include "../time/utils.h"
+
+namespace Msp {
+
+Semaphore::Semaphore():
+ mutex(new Mutex),
+ own_mutex(true)
+{
+ init();
+}
+
+Semaphore::Semaphore(Mutex &m):
+ mutex(&m),
+ own_mutex(false)
+{
+ init();
+}
+
+void Semaphore::init()
+{
+#ifdef WIN32
+ count = 0;
+ sem = CreateSemaphore(0, 0, 32, 0);
+#else
+ pthread_cond_init(&sem, 0);
+#endif
+}
+
+Semaphore::~Semaphore()
+{
+ if(own_mutex)
+ delete mutex;
+#ifdef WIN32
+ CloseHandle(sem);
+#else
+ pthread_cond_destroy(&sem);
+#endif
+}
+
+#ifdef WIN32
+int Semaphore::signal()
+{
+ if(count==0)
+ return 0;
+
+ int ret = !ReleaseSemaphore(sem, 1, 0);
+
+ unsigned old_count = count;
+ mutex->unlock();
+ while(count==old_count)
+ Sleep(0);
+ mutex->lock();
+
+ return ret;
+}
+
+int Semaphore::broadcast()
+{
+ if(count==0)
+ return 0;
+ int ret = !ReleaseSemaphore(sem, count, 0);
+
+ mutex->unlock();
+ while(count)
+ Sleep(0);
+ mutex->lock();
+
+ return ret;
+}
+
+int Semaphore::wait()
+{
+ ++count;
+ mutex->unlock();
+ DWORD ret = WaitForSingleObject(sem, INFINITE);
+ mutex->lock();
+ --count;
+
+ return ret==WAIT_OBJECT_0;
+}
+#endif
+
+int Semaphore::wait(const Time::TimeDelta &d)
+{
+#ifndef WIN32
+ Time::TimeStamp ts = Time::now()+d;
+
+ timespec timeout;
+ timeout.tv_sec = ts.raw()/1000000;
+ timeout.tv_nsec = (ts.raw()%1000000)*1000;
+
+ int r = pthread_cond_timedwait(&sem, &mutex->mutex, &timeout);
+ if(r==ETIMEDOUT)
+ return 1;
+ else if(r)
+ return -1;
+ return 0;
+#else
+ ++count;
+ mutex->lock();
+ DWORD ret = WaitForSingleObject(sem, (DWORD)(d/Time::usec));
+ mutex->unlock();
+ --count;
+ return ret==WAIT_OBJECT_0;
+#endif
+}
+
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_CORE_SEMAPHORE_H_
+#define MSP_CORE_SEMAPHORE_H_
+
+#include "mutex.h"
+#include "types.h"
+#include "../time/timedelta.h"
+
+namespace Msp {
+
+class Semaphore
+{
+private:
+ Mutex *mutex;
+ bool own_mutex;
+ SemaphoreHandle sem;
+#ifdef WIN32
+ unsigned count;
+#endif
+
+public:
+ Semaphore();
+ Semaphore(Mutex &);
+private:
+ void init();
+public:
+ ~Semaphore();
+
+ int signal();
+ int broadcast();
+ int wait();
+ int wait(const Time::TimeDelta &);
+ Mutex &get_mutex() { return *mutex; }
+};
+
+#ifndef WIN32
+inline int Semaphore::signal()
+{ return pthread_cond_signal(&sem); }
+
+inline int Semaphore::broadcast()
+{ return pthread_cond_broadcast(&sem); }
+
+inline int Semaphore::wait()
+{ return pthread_cond_wait(&sem, &mutex->mutex); }
+#endif
+
+}
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef WIN32
+#include <signal.h>
+#endif
+#include "thread.h"
+
+namespace Msp {
+
+/**
+Waits for the thread to exit. Calling this from the thread will cause a
+deadlock.
+*/
+void Thread::join()
+{
+ if(!launched_)
+ return;
+
+#ifdef WIN32
+ WaitForSingleObject(thread_, INFINITE);
+#else
+ pthread_join(thread_, 0);
+#endif
+ launched_ = false;
+}
+
+/**
+Requests the thread to terminate gracefully. Currently unimplemented on win32.
+*/
+void Thread::cancel()
+{
+#ifndef WIN32 //XXX
+ pthread_cancel(thread_);
+#endif
+}
+
+/**
+Violently terminates the thread.
+*/
+void Thread::kill()
+{
+#ifdef WIN32
+ TerminateThread(thread_, 0);
+#else
+ pthread_kill(thread_, SIGKILL);
+#endif
+}
+
+Thread::~Thread()
+{
+ if(launched_)
+ {
+ kill();
+ join();
+ }
+}
+
+void Thread::launch()
+{
+ if(launched_)
+ return;
+
+#ifdef WIN32
+ DWORD dummy; // Win9x needs the lpTthreadId parameter
+ thread_ = CreateThread(0, 0, &main_, this, 0, &dummy);
+#else
+ pthread_create(&thread_, 0, &main_, this);
+#endif
+ launched_ = true;
+}
+
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_CORE_THREAD_H_
+#define MSP_CORE_THREAD_H_
+
+#include "types.h"
+
+namespace Msp {
+
+/**
+Base class for threads. To create a thread for some task, derive it from this
+class and implement the main() function. Note that threads are not
+automatically started upon creation - you must manually call launch() instead.
+This is to allow initializing variables of the derived class before the thread
+is started.
+*/
+class Thread
+{
+private:
+ ThreadHandle thread_;
+ bool launched_;
+
+protected:
+ Thread(): launched_(false) { }
+public:
+ virtual ~Thread();
+
+ void join();
+ void cancel();
+ void kill();
+protected:
+ void launch();
+ virtual void main() = 0;
+ void check_cancel();
+
+private:
+ static
+#ifdef WIN32
+ DWORD WINAPI
+#else
+ void *
+#endif
+ main_(void *t) { (reinterpret_cast<Thread *>(t))->main(); return 0; }
+
+ Thread(const Thread &);
+ Thread &operator=(const Thread &);
+};
+
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_CORE_TYPES_H_
+#define MSP_CORE_TYPES_H_
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif
+
+namespace Msp {
+
+#ifdef WIN32
+typedef HANDLE ThreadHandle;
+typedef HANDLE MutexHandle;
+typedef HANDLE SemaphoreHandle;
+#else
+typedef pthread_t ThreadHandle;
+typedef pthread_mutex_t MutexHandle;
+typedef pthread_cond_t SemaphoreHandle;
+#endif
+
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_CORE_VARIANT_H_
+#define MSP_CORE_VARIANT_H_
+
+#include "except.h"
+#include "meta.h"
+
+namespace Msp {
+
+class Variant
+{
+private:
+ struct StoreBase
+ {
+ virtual ~StoreBase() { }
+ virtual StoreBase *clone() const =0;
+ };
+
+ template<typename T>
+ struct Store: public StoreBase
+ {
+ T data;
+
+ Store(T d): data(d) { }
+ virtual StoreBase *clone() const { return new Store<T>(data); }
+ };
+
+ StoreBase *store;
+
+public:
+ Variant(): store(0) { }
+ template<typename T>
+ Variant(const T &v): store(new Store<typename RemoveConst<T>::Type>(v)) { }
+ Variant(const Variant &v): store(v.store ? v.store->clone() : 0) { }
+ ~Variant() { delete store; }
+
+ template<typename T>
+ Variant &operator=(const T &v)
+ {
+ delete store;
+ store = new Store<typename RemoveConst<T>::Type>(v);
+ return *this;
+ }
+
+ Variant &operator=(const Variant &v)
+ {
+ delete store;
+ store = (v.store ? v.store->clone() : 0);
+ return *this;
+ }
+
+ template<typename T>
+ T &value() const
+ {
+ typedef typename RemoveConst<T>::Type NCT;
+ Store<NCT> *s = dynamic_cast<Store<NCT> *>(store);
+ if(!s)
+ throw InvalidState("Type mismatch");
+ return s->data;
+ }
+
+ template<typename T>
+ bool check_type() const
+ {
+ return dynamic_cast<Store<typename RemoveConst<T>::Type> *>(store)!=0;
+ }
+
+ template<typename T>
+ operator T() const
+ { return value<T>(); }
+};
+
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+// Must include something to test for glibc
+#include <cstdlib>
+#if !defined(WIN32) && defined(__GLIBC__)
+#include <dlfcn.h>
+#include <execinfo.h>
+#endif
+#include "backtrace.h"
+#include "demangle.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Debug {
+
+Backtrace Backtrace::create()
+{
+#if !defined(WIN32) && defined(__GLIBC__)
+ void *addresses[50];
+ int count = ::backtrace(addresses, 50);
+
+ Backtrace bt;
+ Dl_info dli;
+ for(int i=0; i<count; ++i)
+ {
+ StackFrame frame;
+ frame.address = addresses[i];
+ if(dladdr(addresses[i], &dli))
+ {
+ frame.file = dli.dli_fname;
+ if(dli.dli_sname)
+ frame.symbol = demangle(dli.dli_sname);
+ }
+ else
+ frame.file = "<unknown>";
+ bt.frames.push_back(frame);
+ }
+
+ return bt;
+#else
+ return Backtrace();
+#endif
+}
+
+ostream &operator<<(ostream &out, const Backtrace &bt)
+{
+ const list<Backtrace::StackFrame> &frames = bt.get_frames();
+ for(list<Backtrace::StackFrame>::const_iterator i=frames.begin(); i!=frames.end(); ++i)
+ out<<*i<<'\n';
+
+ return out;
+}
+
+ostream &operator<<(ostream &out, const Backtrace::StackFrame &sf)
+{
+ out<<sf.address;
+ if(!sf.symbol.empty())
+ out<<" in "<<sf.symbol;
+ out<<" from "<<sf.file;
+
+ return out;
+}
+
+} // namespace Debug
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_DEBUG_BACKTRACE_H_
+#define MSP_DEBUG_BACKTRACE_H_
+
+#include <list>
+#include <ostream>
+#include <string>
+
+namespace Msp {
+namespace Debug {
+
+class Backtrace
+{
+public:
+ struct StackFrame
+ {
+ void *address;
+ std::string file;
+ std::string symbol;
+ };
+
+private:
+ std::list<StackFrame> frames;
+
+public:
+ const std::list<StackFrame> &get_frames() const { return frames; }
+
+ static Backtrace create();
+};
+
+std::ostream &operator<<(std::ostream &, const Backtrace &);
+std::ostream &operator<<(std::ostream &, const Backtrace::StackFrame &);
+
+} // namespace Debug
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <cstdlib>
+#ifdef __GNUC__
+#include <cxxabi.h>
+#endif
+#include "demangle.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Debug {
+
+string demangle(const string &sym)
+{
+#ifdef __GNUC__
+ int status;
+ char *dm = abi::__cxa_demangle(sym.c_str(), 0, 0, &status);
+
+ string result;
+ if(status==0)
+ result = dm;
+ else
+ result = sym;
+
+ free(dm);
+
+ return result;
+#else
+ return sym;
+#endif
+}
+
+} // namespace Debug
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_DEBUG_DEMANGLE_H_
+#define MSP_DEBUG_DEMANGLE_H_
+
+#include <string>
+
+namespace Msp {
+namespace Debug {
+
+std::string demangle(const std::string &);
+
+} // namespace Debug
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "../core/except.h"
+#include "../time/units.h"
+#include "profiler.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Debug {
+
+Profiler::Profiler():
+ period(0),
+ inner(0)
+{ }
+
+void Profiler::set_period(unsigned p)
+{
+ if(p==period)
+ return;
+
+ period = p;
+ for(map<string, ScopeInfo>::iterator i=scopes.begin(); i!=scopes.end(); ++i)
+ {
+ ScopeInfo &si = i->second;
+ if(p==0)
+ si.history.clear();
+ else
+ si.history.assign(period, Time::zero);
+ si.hist_pos = 0;
+ }
+}
+
+void Profiler::add_scope(const std::string &name)
+{
+ if(!scopes.count(name))
+ {
+ map<string, ScopeInfo>::iterator i = scopes.insert(map<string, ScopeInfo>::value_type(name, ScopeInfo())).first;
+ i->second.history.resize(period);
+ }
+}
+
+ProfilingScope *Profiler::enter(ProfilingScope *ps)
+{
+ ProfilingScope *old = inner;
+ inner = ps;
+ return old;
+}
+
+void Profiler::record(const string &scope_name, const string &parent, const Time::TimeDelta &time, const Time::TimeDelta &child_t)
+{
+ map<string, ScopeInfo>::iterator i = scopes.find(scope_name);
+ if(i==scopes.end())
+ {
+ i = scopes.insert(map<string, ScopeInfo>::value_type(scope_name, ScopeInfo())).first;
+ i->second.history.resize(period);
+ }
+
+ ScopeInfo &si = i->second;
+ ++si.calls;
+ ++si.called_from[parent];
+ si.total_time += time;
+ si.self_time += time-child_t;
+ if(period)
+ {
+ si.avg_time += time/period-si.history[si.hist_pos]/period;
+ si.history[si.hist_pos++] = time;
+ if(si.hist_pos>=period)
+ si.hist_pos -= period;
+ }
+ else
+ si.avg_time = si.total_time/si.calls;
+}
+
+const Profiler::ScopeInfo &Profiler::scope(const string &sn) const
+{
+ map<string, ScopeInfo>::const_iterator i = scopes.find(sn);
+ if(i==scopes.end())
+ throw KeyError("Unknown scope");
+
+ return i->second;
+}
+
+
+Profiler::ScopeInfo::ScopeInfo():
+ calls(0),
+ hist_pos(0)
+{ }
+
+} // namespace Debug
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_DEBUG_PROFILER_H_
+#define MSP_DEBUG_PROFILER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+#include "../time/timedelta.h"
+
+namespace Msp {
+namespace Debug {
+
+class ProfilingScope;
+
+/**
+A class for collecting timing data from a program. It's not as efficient as
+external profilers, but allows profiling of custom scopes and retrieving the
+profiling data at run time. An example usage could be showing realtime
+performance statistics of a game or a simulation.
+
+See also class ProfilingScope.
+
+Note: This is not thread-safe. To profile multiple threads, create a separate
+Profiler for each thread.
+*/
+class Profiler
+{
+public:
+ struct ScopeInfo
+ {
+ unsigned calls;
+ Time::TimeDelta total_time;
+ Time::TimeDelta self_time;
+ Time::TimeDelta avg_time;
+ std::vector<Time::TimeDelta> history;
+ unsigned hist_pos;
+ std::map<std::string, unsigned> called_from;
+
+ ScopeInfo();
+ };
+
+private:
+ unsigned period;
+ std::map<std::string, ScopeInfo> scopes;
+ ProfilingScope *inner;
+
+public:
+ Profiler();
+
+ /**
+ Sets the averaging period for timing data, measured in calls. Previous
+ average timings are cleared.
+ */
+ void set_period(unsigned p);
+
+ /**
+ Adds a scope without recording any calls to it. Useful if you might need to
+ access a scope before it has finished for the first time.
+ */
+ void add_scope(const std::string &name);
+
+ /**
+ Changes the recorded innermost scope pointer and returns the old one. This
+ is used by ProfilingScope to track child time and should not be called
+ manually.
+ */
+ ProfilingScope *enter(ProfilingScope *ps);
+
+ /**
+ Records a call to a scope. You'll probably want to use a ProfilingScope
+ instead of calling this manually.
+
+ @param sn Scope name
+ @param pn Parent scope name
+ @param t Time spent in the scope
+ @param ct Time spent in child scopes
+ */
+ void record(const std::string &sn, const std::string &pn, const Time::TimeDelta &t, const Time::TimeDelta &ct);
+
+ /**
+ Returns informations about a scope.
+ */
+ const ScopeInfo &scope(const std::string &) const;
+};
+
+} // namespace Debug
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "../time/utils.h"
+#include "profilingscope.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Debug {
+
+ProfilingScope::ProfilingScope(Profiler &p, const string &n):
+ profiler(p),
+ name(n),
+ parent(profiler.enter(this)),
+ start_t(Time::now())
+{ }
+
+ProfilingScope::~ProfilingScope()
+{
+ const Time::TimeDelta dt = Time::now()-start_t;
+ if(parent)
+ {
+ parent->child_t += dt;
+ profiler.record(name, parent->name, dt, child_t);
+ }
+ else
+ profiler.record(name, string(), dt, child_t);
+ profiler.enter(parent);
+}
+
+} // namespace Debug
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_DEBUG_PROFILINGSCOPE_H_
+#define MSP_DEBUG_PROFILINGSCOPE_H_
+
+#include "../time/timestamp.h"
+#include "profiler.h"
+
+namespace Msp {
+namespace Debug {
+
+/**
+RAII timing class to accompany Profiler. Timing starts when an object is
+created and ends when it goes out of scope. If there was another object in an
+outer scope, it is notified of the time used in inner scopes.
+*/
+class ProfilingScope
+{
+private:
+ Profiler &profiler;
+ std::string name;
+ ProfilingScope *parent;
+ Time::TimeStamp start_t;
+ Time::TimeDelta child_t;
+
+ ProfilingScope(const ProfilingScope &);
+ ProfilingScope &operator=(const ProfilingScope &);
+public:
+ ProfilingScope(Profiler &p, const std::string &n);
+ ~ProfilingScope();
+};
+
+} // namespace Debug
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <cstdlib>
+#include <sstream>
+#include <iomanip>
+#include "../core/except.h"
+#include "datetime.h"
+#include "timestamp.h"
+#include "units.h"
+
+using namespace std;
+
+namespace {
+
+inline bool is_leap_year(int y)
+{ return y%4==0 && (y%100 || y%400==0); }
+
+inline unsigned char month_days(int y, unsigned char m)
+{
+ switch(m)
+ {
+ case 4:
+ case 6:
+ case 9:
+ case 11:
+ return 30;
+ case 2:
+ return is_leap_year(y)?29:28;
+ default:
+ return 31;
+ }
+}
+
+template<typename T>
+inline int cmp_(T a, T b)
+{
+ if(a<b)
+ return -1;
+ if(a>b)
+ return 1;
+ return 0;
+}
+
+}
+
+namespace Msp {
+namespace Time {
+
+DateTime::DateTime(const TimeStamp &ts)
+{
+ init(ts);
+}
+
+DateTime::DateTime(const TimeStamp &ts, const TimeZone &tz)
+{
+ init(ts);
+ convert_timezone(tz);
+}
+
+DateTime::DateTime(int y, unsigned char m, unsigned char d)
+{
+ init(y, m, d, 0, 0, 0, 0);
+}
+
+DateTime::DateTime(int y, unsigned char m, unsigned char d, unsigned char h, unsigned char n, unsigned char s)
+{
+ init(y, m, d, h, n, s, 0);
+}
+
+DateTime::DateTime(int y, unsigned char m, unsigned char d, unsigned char h, unsigned char n, unsigned char s, unsigned u)
+{
+ init(y, m, d, h, n, s, u);
+}
+
+void DateTime::add_days(int days)
+{
+ int new_year = year;
+
+ /* Leap years have a 400 year cycle, so any 400 consecutive years have a
+ constant number of days (400*365+97 = 146097) */
+ new_year += days/146097*400;
+ days %= 146097;
+
+ if(days<0)
+ {
+ new_year -= 400;
+ days += 146097;
+ }
+
+ // Fudge factor for leap day
+ int fudge = (month<=2)?1:0;
+
+ // (Almost) every 4 year cycle has 1 leap year and 3 normal years
+ unsigned cycles = days/1461;
+ days %= 1461;
+
+ new_year += cycles*4;
+
+ // See how many non-leap-years we counted as leap years and reclaim the lost days
+ // XXX This breaks with negative years
+ unsigned missed_leap_days = ((year-fudge)%100+cycles*4)/100;
+ if((year-fudge)%400+cycles*4>=400)
+ --missed_leap_days;
+
+ days += missed_leap_days;
+
+ // Count single years from the 4 year cycle
+ cycles = days/365;
+ days %= 365;
+
+ new_year += cycles;
+
+ if((year-fudge)%4+cycles>=4)
+ {
+ // We passed a leap year - decrement days
+ if(days==0)
+ {
+ days = is_leap_year(new_year-fudge)?365:364;
+ --new_year;
+ }
+ else
+ --days;
+ }
+
+ year = new_year;
+
+ // Step months
+ while(mday+days>month_days(year, month))
+ {
+ days -= month_days(year, month);
+ ++month;
+ if(month>12)
+ {
+ ++year;
+ month = 1;
+ }
+ }
+
+ mday += days;
+}
+
+void DateTime::set_timezone(const TimeZone &tz)
+{
+ zone = tz;
+}
+
+void DateTime::convert_timezone(const TimeZone &tz)
+{
+ add_raw((zone.get_offset()-tz.get_offset()).raw());
+ zone = tz;
+}
+
+DateTime DateTime::operator+(const TimeDelta &td) const
+{
+ DateTime dt(*this);
+ dt.add_raw(td.raw());
+ return dt;
+}
+
+DateTime &DateTime::operator+=(const TimeDelta &td)
+{
+ add_raw(td.raw());
+ return *this;
+}
+
+DateTime DateTime::operator-(const TimeDelta &td) const
+{
+ DateTime dt(*this);
+ dt.add_raw(-td.raw());
+ return dt;
+}
+
+DateTime &DateTime::operator-=(const TimeDelta &td)
+{
+ add_raw(-td.raw());
+ return *this;
+}
+
+int DateTime::cmp(const DateTime &dt) const
+{
+ if(int c = cmp_(year, dt.year))
+ return c;
+ if(int c = cmp_(month, dt.month))
+ return c;
+ if(int c = cmp_(mday, dt.mday))
+ return c;
+ if(int c = cmp_(hour, dt.hour))
+ return c;
+ if(int c = cmp_(minute, dt.minute))
+ return c;
+ if(int c = cmp_(second, dt.second))
+ return c;
+ if(int c = cmp_(usec, dt.usec))
+ return c;
+ return 0;
+}
+
+TimeStamp DateTime::get_timestamp() const
+{
+ if(year<-289701 || year>293641)
+ throw InvalidState("DateTime is not representable as a TimeStamp");
+
+ RawTime raw = (((hour*60LL)+minute)*60+second)*1000000+usec;
+ int days = (year-1970)*365;
+ days += (year-1)/4-(year-1)/100+(year-1)/400-477;
+ for(unsigned i=1; i<month; ++i)
+ days += month_days(year, i);
+ days += mday-1;
+
+ raw += days*86400000000LL;
+
+ return TimeStamp(raw);
+}
+
+string DateTime::format(const string &fmt) const
+{
+ ostringstream ss;
+ ss.fill('0');
+ for(string::const_iterator i=fmt.begin(); i!=fmt.end(); ++i)
+ {
+ if(*i=='%')
+ {
+ ++i;
+ if(i==fmt.end())
+ break;
+ else if(*i=='d')
+ ss<<setw(2)<<int(mday);
+ else if(*i=='H')
+ ss<<setw(2)<<int(hour);
+ else if(*i=='I')
+ ss<<setw(2)<<hour%12;
+ else if(*i=='m')
+ ss<<setw(2)<<int(month);
+ else if(*i=='M')
+ ss<<setw(2)<<int(minute);
+ else if(*i=='p')
+ ss<<((hour>=12) ? "PM" : "AM");
+ else if(*i=='S')
+ ss<<setw(2)<<int(second);
+ else if(*i=='y')
+ ss<<setw(2)<<year%100;
+ else if(*i=='Y')
+ ss<<setw(4)<<internal<<year;
+ else if(*i=='%')
+ ss<<'%';
+ }
+ else
+ ss<<*i;
+ }
+
+ return ss.str();
+}
+
+string DateTime::format_rfc3339() const
+{
+ string result = format("%Y-%m-%dT%H:%M:%S");
+ if(const TimeDelta &offs = zone.get_offset())
+ {
+ ostringstream ss;
+ ss.fill('0');
+ int m = abs(static_cast<int>(offs/Time::min));
+ ss<<(offs<zero ? '+' : '-')<<setw(2)<<m/60<<':'<<setw(2)<<m%60;
+ result += ss.str();
+ }
+ else
+ result += 'Z';
+ return result;
+}
+
+void DateTime::init(const TimeStamp &ts)
+{
+ year = 1970;
+ month = 1;
+ mday = 1;
+ hour = 0;
+ minute = 0;
+ second = 0;
+ usec = 0;
+ add_raw(ts.raw());
+}
+
+void DateTime::init(int y, unsigned char m, unsigned char d, unsigned char h, unsigned char n, unsigned char s, unsigned u)
+{
+ year = y;
+ month = m;
+ mday = d;
+ hour = h;
+ minute = n;
+ second = s;
+ usec = u;
+ validate();
+}
+
+void DateTime::add_raw(RawTime raw)
+{
+ int days = static_cast<int>(raw/86400000000LL);
+ raw %= 86400000000LL;
+ if(raw<0)
+ {
+ --days;
+ raw += 86400000000LL;
+ }
+
+ usec+=raw%1000000; raw /= 1000000;
+ second+=raw%60; raw /= 60;
+ minute+=raw%60; raw /= 60;
+ hour+=raw%24; raw /= 24;
+
+ add_days(days);
+ normalize();
+}
+
+void DateTime::normalize()
+{
+ second += usec/1000000;
+ usec %= 1000000;
+ minute += second/60;
+ second %= 60;
+ hour += minute/60;
+ minute %= 60;
+ mday += hour/24;
+ hour %= 24;
+ while(mday>month_days(year, month))
+ {
+ mday -= month_days(year, month);
+ ++month;
+ if(month>12)
+ {
+ ++year;
+ month = 1;
+ }
+ }
+}
+
+void DateTime::validate() const
+{
+ if(usec>=1000000)
+ throw InvalidParameterValue("Microseconds out of range");
+ if(second>=60)
+ throw InvalidParameterValue("Seconds out of range");
+ if(minute>=60)
+ throw InvalidParameterValue("Minutes out of range");
+ if(hour>=24)
+ throw InvalidParameterValue("Hours out of range");
+ if(month<1 || month>12)
+ throw InvalidParameterValue("Month out of range");
+ if(mday<1 || mday>month_days(year, month))
+ throw InvalidParameterValue("Day of month out of range");
+}
+
+} // namespace Time
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_TIME_DATETIME_H_
+#define MSP_TIME_DATETIME_H_
+
+#include <string>
+#include "timezone.h"
+#include "types.h"
+
+namespace Msp {
+namespace Time {
+
+class TimeDelta;
+class TimeStamp;
+
+/**
+Provides handling of arbitary dates and times. Can represent a moment of time
+in the range of about ±2.1×10⁹ years. It can also be formatted into a string
+for presentation to the user.
+
+Due to the complex internal representation, arithmetic operations on a DateTime
+are relatively slow. For purposes of internal scheduling in a program, a
+TimeStamp is a better choice.
+*/
+class DateTime
+{
+private:
+ int year;
+ unsigned char month;
+ unsigned char mday;
+ unsigned char hour;
+ unsigned char minute;
+ unsigned char second;
+ unsigned usec;
+ TimeZone zone;
+
+public:
+ DateTime(const TimeStamp &);
+ DateTime(const TimeStamp &, const TimeZone &);
+ DateTime(int, unsigned char, unsigned char);
+ DateTime(int, unsigned char, unsigned char, unsigned char, unsigned char, unsigned char);
+ DateTime(int, unsigned char, unsigned char, unsigned char, unsigned char, unsigned char, unsigned);
+private:
+ void init(const TimeStamp &);
+ void init(int, unsigned char, unsigned char, unsigned char, unsigned char, unsigned char, unsigned);
+
+public:
+ int get_year() const { return year; }
+ unsigned char get_month() const { return month; }
+ unsigned char get_mday() const { return mday; }
+ unsigned char get_hour() const { return hour; }
+ unsigned char get_minute() const { return minute; }
+ unsigned char get_second() const { return second; }
+ unsigned get_usec() const { return usec; }
+
+ void add_days(int);
+ void set_timezone(const TimeZone &);
+ void convert_timezone(const TimeZone &);
+
+ DateTime operator+(const TimeDelta &) const;
+ DateTime &operator+=(const TimeDelta &);
+ DateTime operator-(const TimeDelta &) const;
+ DateTime &operator-=(const TimeDelta &);
+
+ bool operator==(const DateTime &d) const { return cmp(d)==0; }
+ bool operator!=(const DateTime &d) const { return cmp(d)!=0; }
+ bool operator<(const DateTime &d) const { return cmp(d)<0; }
+ bool operator<=(const DateTime &d) const { return cmp(d)<=0; }
+ bool operator>(const DateTime &d) const { return cmp(d)>0; }
+ bool operator>=(const DateTime &d) const { return cmp(d)>=0; }
+
+ int cmp(const DateTime &) const;
+ TimeStamp get_timestamp() const;
+ std::string format(const std::string &) const;
+ std::string format_rfc3339() const;
+
+private:
+ void add_raw(RawTime);
+ void normalize();
+ void validate() const;
+};
+
+} // namespace Time
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <sstream>
+#include <iomanip>
+#include "timedelta.h"
+#include "units.h"
+
+using namespace std;
+
+namespace {
+
+using Msp::Time::RawTime;
+
+void print_part(ostream &out, RawTime &value, RawTime unit, char sep, bool &first)
+{
+ if(value<unit && first)
+ return;
+
+ if(!first)
+ out<<sep<<setw(2);
+
+ out<<value/unit;
+ value %= unit;
+ first = false;
+}
+
+}
+
+namespace Msp {
+namespace Time {
+
+ostream &operator<<(ostream &out, const TimeDelta &td)
+{
+ ostringstream ss;
+ ss.fill('0');
+
+ RawTime value = td.raw();
+
+ if(value<0)
+ {
+ ss<<'-';
+ value = -value;
+ }
+
+ if(value==0)
+ ss<<'0';
+ else if(value<1000)
+ ss<<value<<"µs";
+ else if(value<1000000)
+ {
+ ss<<value/1000;
+ value %= 1000;
+ if(value)
+ ss<<'.'<<setw(3)<<value;
+ ss<<"ms";
+ }
+ else
+ {
+ bool first = true;
+ print_part(ss, value, 86400000000LL, 0, first);
+ print_part(ss, value, 3600000000LL, '-', first);
+ print_part(ss, value, 60000000LL, ':', first);
+ print_part(ss, value, 1000000LL, ':', first);
+
+ if(value)
+ {
+ ss<<'.';
+ if(value%1000)
+ ss<<setw(6)<<value;
+ else
+ ss<<setw(3)<<value/1000;
+ }
+ ss<<"s";
+ }
+
+ out<<ss.str();
+
+ return out;
+}
+
+} // namespace Time
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_TIME_TIMEDELTA_H_
+#define MSP_TIME_TIMEDELTA_H_
+
+#include <time.h>
+#include <sys/time.h>
+#include <ostream>
+#include "types.h"
+
+namespace Msp {
+namespace Time {
+
+/**
+Represents a quantity of time, such as five seconds.
+*/
+class TimeDelta
+{
+private:
+ RawTime usec;
+
+public:
+ /**
+ Constructs a zero TimeDelta.
+ */
+ TimeDelta(): usec(0) { }
+
+ /**
+ Constructs a TimeDelta from a plain number. The purpose of this is to allow
+ serialization together with the raw() function. For creating TimeDeltas
+ with a specific length, see units.h.
+ */
+ explicit TimeDelta(RawTime u): usec(u) { }
+
+ /**
+ Returns the raw number stored inside the TimeDelta. This should only be used
+ for serialization and the result should not be interpreted in any way.
+ */
+ RawTime raw() const { return usec; }
+
+#ifndef WIN32
+ /**
+ Fills in a timespec struct. To get a meaningful scalar value from the
+ TimeDelta, divide with one of the values in units.h.
+ */
+ void fill_timespec(timespec &ts) const { ts.tv_sec=usec/1000000; ts.tv_nsec = (usec%1000000)*1000; }
+
+ void fill_timeval(timeval &tv) const { tv.tv_sec=usec/1000000; tv.tv_usec = usec%1000000; }
+#endif
+
+ TimeDelta operator+(const TimeDelta &t) const { return TimeDelta(usec+t.usec); }
+ TimeDelta &operator+=(const TimeDelta &t) { usec+=t.usec; return *this; }
+ TimeDelta operator-(const TimeDelta &t) const { return TimeDelta(usec-t.usec); }
+ TimeDelta &operator-=(const TimeDelta &t) { usec-=t.usec; return *this; }
+
+ template<typename T>
+ TimeDelta operator*(T a) const { return TimeDelta(RawTime(usec*a)); }
+ template<typename T>
+ TimeDelta &operator*=(T a) { usec=RawTime(usec*a); return *this; }
+
+ template<typename T>
+ TimeDelta operator/(T a) const { return TimeDelta(RawTime(usec/a)); }
+ template<typename T>
+ TimeDelta &operator/=(T a) { usec=RawTime(usec/a); return *this; }
+
+ double operator/(const TimeDelta &t) const { return double(usec)/t.usec; }
+
+ bool operator>(const TimeDelta &t) const { return usec>t.usec; }
+ bool operator>=(const TimeDelta &t) const { return usec>=t.usec; }
+ bool operator<(const TimeDelta &t) const { return usec<t.usec; }
+ bool operator<=(const TimeDelta &t) const { return usec<=t.usec; }
+ bool operator==(const TimeDelta &t) const { return usec==t.usec; }
+ bool operator!=(const TimeDelta &t) const { return usec!=t.usec; }
+
+ operator const void *() const { return usec ? this : 0; }
+};
+
+template<typename T>
+inline TimeDelta operator*(T a, const TimeDelta &t) { return t*a; }
+
+extern std::ostream &operator<<(std::ostream &, const TimeDelta &);
+
+} // namespace Time
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006, 2009 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <algorithm>
+#include "timer.h"
+#include "utils.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Time {
+
+Timer::~Timer()
+{
+ for(vector<SlotProxy>::iterator i=slots.begin(); i!=slots.end(); ++i)
+ delete i->slot;
+}
+
+Timer::Slot &Timer::add(const TimeDelta &td)
+{
+ Slot *s = new Slot(td);
+ mutex.lock();
+ slots.push_back(s);
+ push_heap(slots.begin(), slots.end());
+ mutex.unlock();
+ sem.signal();
+ return *s;
+}
+
+Timer::Slot &Timer::add(const TimeStamp &ts)
+{
+ Slot *s = new Slot(ts);
+ {
+ MutexLock l(mutex);
+ slots.push_back(s);
+ push_heap(slots.begin(), slots.end());
+ }
+ sem.signal();
+ return *s;
+}
+
+void Timer::cancel(Slot &slot)
+{
+ MutexLock l(mutex);
+ for(vector<SlotProxy>::iterator i=slots.begin(); i!=slots.end(); ++i)
+ if(i->slot==&slot)
+ {
+ delete i->slot;
+ slots.erase(i);
+ make_heap(slots.begin(), slots.end());
+ return;
+ }
+}
+
+void Timer::tick(bool block)
+{
+ Slot *next = 0;
+ {
+ MutexLock l(mutex);
+ while(1)
+ {
+ if(slots.empty())
+ {
+ if(block)
+ sem.wait();
+ else
+ return;
+ }
+
+ next = slots.begin()->slot;
+ const TimeStamp &stamp = next->get_timeout();
+ const TimeStamp t = now();
+ if(stamp<=t)
+ break;
+ else if(block)
+ sem.wait(stamp-t);
+ else
+ return;
+ }
+
+ pop_heap(slots.begin(), slots.end());
+ slots.pop_back();
+ }
+
+ try
+ {
+ if(next->signal_timeout.emit() && next->increment())
+ {
+ MutexLock l(mutex);
+ slots.push_back(next);
+ push_heap(slots.begin(), slots.end());
+ }
+ else
+ delete next;
+ }
+ catch(...)
+ {
+ delete next;
+ throw;
+ }
+}
+
+TimeStamp Timer::get_next_timeout() const
+{
+ if(slots.empty())
+ return TimeStamp();
+ return slots.begin()->slot->get_timeout();
+}
+
+
+Timer::Slot::Slot(const TimeDelta &td):
+ interval(td),
+ timeout(now()+interval)
+{ }
+
+Timer::Slot::Slot(const TimeStamp &ts):
+ timeout(ts)
+{ }
+
+bool Timer::Slot::increment()
+{
+ if(!interval)
+ return false;
+ timeout += interval;
+ return true;
+}
+
+
+Timer::SlotProxy::SlotProxy(Slot *s):
+ slot(s)
+{ }
+
+bool Timer::SlotProxy::operator<(const SlotProxy &sp) const
+{
+ return slot->get_timeout()>sp.slot->get_timeout();
+}
+
+} // namespace Time
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006, 2009 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_TIME_TIMER_H_
+#define MSP_TIME_TIMER_H_
+
+#include <vector>
+#include <sigc++/sigc++.h>
+#include "../core/mutex.h"
+#include "../core/semaphore.h"
+#include "timedelta.h"
+#include "timestamp.h"
+
+namespace Msp {
+namespace Time {
+
+/**
+A class for executing functions in a deferred or periodical fashion. The add a
+timer, use one of the add functions and connect a functor to the timeout signal
+of the returned slot.
+
+This class is thread-safe, to allow running timers in a separate thread.
+*/
+class Timer
+{
+public:
+ class Slot
+ {
+ public:
+ sigc::signal<bool> signal_timeout;
+
+ private:
+ TimeDelta interval;
+ TimeStamp timeout;
+
+ public:
+ Slot(const TimeDelta &);
+ Slot(const TimeStamp &);
+ const TimeStamp &get_timeout() const { return timeout; }
+ bool increment();
+ };
+
+private:
+ struct SlotProxy
+ {
+ Slot *slot;
+
+ SlotProxy(Slot *);
+ bool operator<(const SlotProxy &) const;
+ };
+
+ std::vector<SlotProxy> slots;
+ Semaphore sem;
+ Mutex mutex;
+
+public:
+ ~Timer();
+
+ /**
+ Adds a timer that will be executed periodically as long as the timeout
+ signal hander returns true.
+ */
+ Slot &add(const TimeDelta &);
+
+ /**
+ Adds a timer that will be executed once at a specific time. The return
+ value of the timeout signal handler is ignored.
+ */
+ Slot &add(const TimeStamp &);
+
+ /**
+ Cancels a previously added timer.
+ */
+ void cancel(Slot &);
+
+ /**
+ Checks all timers, executing any that have timed out. If block is true,
+ waits until one times out.
+
+ Note: If there are no active timers when a blocking tick is executed, it
+ won't return until a timer is added from another thread.
+ */
+ void tick(bool block = true);
+
+ TimeStamp get_next_timeout() const;
+};
+
+} // namespace Time
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006, 2009 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_TIME_TIMESTAMP_H_
+#define MSP_TIME_TIMESTAMP_H_
+
+#include "timedelta.h"
+#include "types.h"
+
+namespace Msp {
+namespace Time {
+
+/**
+Represents a moment in time. The main source of TimeStamps is the now()
+function.
+
+For representing user-specified times, use the DateTime class.
+*/
+class TimeStamp
+{
+private:
+ RawTime usec;
+
+public:
+ /**
+ Construct a TimeStamp that represents an arbitarily distant point in the
+ past. It's guaranteed to be less than any valid timestamp.
+ */
+ TimeStamp(): usec(0) { }
+
+ /**
+ Constructs a TimeStamp from a plain number. The purpose of this is to allow
+ serialization together with the raw() function.
+ */
+ explicit TimeStamp(RawTime u): usec(u) { }
+
+ /**
+ Returns the raw number stored inside the TimeStamp. This value should be
+ considered opaque and only be used for serialization.
+ */
+ RawTime raw() const { return usec; }
+
+ time_t to_unixtime() const { return usec/1000000LL; }
+
+ TimeStamp operator+(const TimeDelta &t) const { return TimeStamp(usec+t.raw()); }
+ TimeStamp &operator+=(const TimeDelta &t) { usec+=t.raw(); return *this; }
+ TimeStamp operator-(const TimeDelta &t) const { return TimeStamp(usec-t.raw()); }
+ TimeStamp &operator-=(const TimeDelta &t) { usec-=t.raw(); return *this; }
+ TimeDelta operator-(const TimeStamp &t) const { return TimeDelta(usec-t.usec); }
+
+ bool operator>=(const TimeStamp &t) const { return usec>=t.usec; }
+ bool operator>(const TimeStamp &t) const { return usec>t.usec; }
+ bool operator<=(const TimeStamp &t) const { return usec<=t.usec; }
+ bool operator<(const TimeStamp &t) const { return usec<t.usec; }
+ bool operator==(const TimeStamp &t) const { return usec==t.usec; }
+ bool operator!=(const TimeStamp &t) const { return usec!=t.usec; }
+
+ operator const void *() const { return usec>0 ? this : 0; }
+
+ static TimeStamp from_unixtime(time_t t) { return TimeStamp(t*1000000LL); }
+};
+
+} // namespace Time
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2008-2009 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <cstdlib>
+#include <sstream>
+#include <iomanip>
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <fcntl.h>
+#endif
+#include "../core/except.h"
+#include "timestamp.h"
+#include "timezone.h"
+#include "units.h"
+#include "utils.h"
+
+using namespace std;
+
+namespace {
+
+using Msp::Time::TimeZone;
+
+#ifndef WIN32
+long get_long(char *&ptr)
+{
+ long result = 0;
+ for(unsigned i=0; i<4; ++i)
+ result = (result<<8)+static_cast<unsigned char >(*ptr++);
+ return result;
+}
+#endif
+
+TimeZone get_local_timezone()
+{
+#ifdef WIN32
+ TIME_ZONE_INFORMATION tzinfo;
+ DWORD dst = GetTimeZoneInformation(&tzinfo);
+ if(dst==TIME_ZONE_ID_INVALID)
+ throw Msp::SystemError("Failed to get time zone information", GetLastError());
+
+ int offset = tzinfo.Bias;
+ if(dst==TIME_ZONE_ID_STANDARD)
+ offset += tzinfo.StandardBias;
+ else if(dst==TIME_ZONE_ID_DAYLIGHT)
+ offset += tzinfo.DaylightBias;
+
+ return TimeZone(offset);
+#else
+ int fd = open("/etc/localtime", O_RDONLY);
+ if(fd>=-1)
+ {
+ char hdr[44];
+ int len = read(fd, hdr, sizeof(hdr));
+ long gmtoff = -1;
+ string name;
+ if(len==44 && hdr[0]=='T' && hdr[1]=='Z' && hdr[2]=='i' && hdr[3]=='f')
+ {
+ char *ptr = hdr+20;
+ long isgmtcnt = get_long(ptr);
+ long isstdcnt = get_long(ptr);
+ long leapcnt = get_long(ptr);
+ long timecnt = get_long(ptr);
+ long typecnt = get_long(ptr);
+ long charcnt = get_long(ptr);
+ int size = timecnt*5+typecnt*6+isgmtcnt+isstdcnt+leapcnt*8+charcnt;
+ char buf[size];
+ len = read(fd, buf, size);
+ if(len==size)
+ {
+ ptr = buf;
+ int index = -1;
+ time_t cur_time = Msp::Time::now().to_unixtime();
+ for(int i=0; i<timecnt; ++i)
+ if(get_long(ptr)<=cur_time)
+ index = i;
+
+ if(index>0)
+ index = ptr[index];
+ ptr += timecnt;
+
+ int abbrind = 0;
+ for(int i=0; i<typecnt; ++i)
+ {
+ if((index>=0 && i==index) || (index<0 && !ptr[4] && gmtoff==-1))
+ {
+ gmtoff = get_long(ptr);
+ ++ptr;
+ abbrind = *ptr++;
+ }
+ else
+ ptr += 6;
+ }
+
+ name = ptr+abbrind;
+ }
+ }
+ close(fd);
+
+ if(gmtoff!=-1)
+ return TimeZone(-gmtoff/60, name);
+ }
+ return TimeZone();
+#endif
+}
+
+}
+
+namespace Msp {
+namespace Time {
+
+TimeZone::TimeZone():
+ name("UTC")
+{ }
+
+TimeZone::TimeZone(int minutes_west):
+ offset(minutes_west*min)
+{
+ if(minutes_west)
+ {
+ ostringstream ss;
+ ss.fill('0');
+ int m = abs(minutes_west);
+ ss<<"UTC"<<(minutes_west<0 ? '-' : '+')<<m/60;
+ if(m%60)
+ ss<<':'<<setw(2)<<m%60;
+ }
+ else
+ name = "UTC";
+}
+
+TimeZone::TimeZone(int minutes_west, const string &n):
+ name(n),
+ offset(minutes_west*min)
+{ }
+
+const TimeZone &TimeZone::utc()
+{
+ static TimeZone tz(0);
+ return tz;
+}
+
+const TimeZone &TimeZone::local()
+{
+ static TimeZone tz = get_local_timezone();
+ return tz;
+}
+
+} // namespace Time
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2008-2009 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_TIME_TIMEZONE_H_
+#define MSP_TIME_TIMEZONE_H_
+
+#include "timedelta.h"
+
+namespace Msp {
+namespace Time {
+
+class TimeZone
+{
+private:
+ std::string name;
+ TimeDelta offset;
+
+public:
+ TimeZone();
+ TimeZone(int);
+ TimeZone(int, const std::string &);
+
+ const std::string &get_name() const { return name; }
+ const TimeDelta &get_offset() const { return offset; }
+
+ static const TimeZone &utc();
+ static const TimeZone &local();
+};
+
+} // namespace Time
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006,2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_TIME_TYPES_H_
+#define MSP_TIME_TYPES_H_
+
+#ifndef WIN32
+#include <stdint.h>
+#endif
+
+namespace Msp {
+namespace Time {
+
+#ifdef WIN32
+typedef __int64 RawTime;
+#else
+typedef int64_t RawTime;
+#endif
+
+} // namespace Time
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "units.h"
+
+namespace Msp {
+namespace Time {
+
+// Constants to be used in creation of TimeDeltas
+const TimeDelta zero(0);
+const TimeDelta usec(1);
+const TimeDelta msec(1000);
+const TimeDelta sec(1000000);
+const TimeDelta min(60*1000000);
+const TimeDelta hour(3600*1000000LL);
+const TimeDelta day(86400*1000000LL);
+const TimeDelta week(7*86400*1000000LL);
+
+} // namespace Time
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_TIME_UNITS_H_
+#define MSP_TIME_UNITS_H_
+
+#include "timedelta.h"
+
+namespace Msp {
+namespace Time {
+
+extern const TimeDelta zero;
+extern const TimeDelta usec;
+extern const TimeDelta msec;
+extern const TimeDelta sec;
+extern const TimeDelta min;
+extern const TimeDelta hour;
+extern const TimeDelta day;
+extern const TimeDelta week;
+
+} // namespace Time
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006-2008 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <sys/resource.h>
+#include <sys/time.h>
+#endif
+#include "datetime.h"
+#include "timedelta.h"
+#include "timestamp.h"
+#include "units.h"
+#include "utils.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Time {
+
+/**
+Returns the current timestamp.
+*/
+TimeStamp now()
+{
+#ifndef WIN32
+ timeval tv;
+ gettimeofday(&tv, 0);
+ return TimeStamp(tv.tv_sec*1000000LL+tv.tv_usec);
+#else
+ static RawTime epoch = 0;
+ if(!epoch)
+ {
+ SYSTEMTIME st;
+ st.wYear = 1970;
+ st.wMonth = 1;
+ st.wDay = 1;
+ st.wHour = 0;
+ st.wMinute = 0;
+ st.wSecond = 0;
+ st.wMilliseconds = 0;
+
+ FILETIME ft;
+ SystemTimeToFileTime(&st, &ft);
+ epoch = (ft.dwLowDateTime+(static_cast<RawTime>(ft.dwHighDateTime)<<32))/10;
+ }
+
+ FILETIME ft;
+ GetSystemTimeAsFileTime(&ft);
+ return TimeStamp((ft.dwLowDateTime+(static_cast<RawTime>(ft.dwHighDateTime)<<32))/10-epoch);
+#endif
+}
+
+string format_now(const string &fmt)
+{
+ return DateTime(now()).format(fmt);
+}
+
+/**
+Returns the CPU time used by the program so far.
+*/
+TimeDelta get_cpu_time()
+{
+#ifndef WIN32
+ rusage ru;
+ getrusage(RUSAGE_SELF, &ru);
+ return (ru.ru_utime.tv_sec+ru.ru_stime.tv_sec)*sec + (ru.ru_utime.tv_usec+ru.ru_stime.tv_usec)*usec;
+#else
+ //XXX Figure out the function to use on Win32
+ return TimeDelta();
+#endif
+}
+
+/**
+Sleeps for the given time.
+*/
+int sleep(const TimeDelta &d)
+{
+#ifndef WIN32
+ timespec ts;
+ d.fill_timespec(ts);
+ return nanosleep(&ts, 0);
+#else
+ Sleep((DWORD)(d/msec));
+ return 0;
+#endif
+}
+
+} // namespace Time
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2006-2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_TIME_UTILS_H_
+#define MSP_TIME_UTILS_H_
+
+#include <string>
+
+namespace Msp {
+namespace Time {
+
+class TimeDelta;
+class TimeStamp;
+
+extern TimeStamp now();
+extern std::string format_now(const std::string &);
+extern TimeDelta get_cpu_time();
+extern int sleep(const TimeDelta &);
+
+} // namespace Time
+} // namespace Msp
+
+#endif