<commit_before>#include "LVPAInternal.h"
#include "LVPATools.h"

#include <algorithm>
#include <cctype>
#include <stack>

#if PLATFORM == PLATFORM_WIN32
#   define WIN32_LEAN_AND_MEAN
#   include <windows.h>
#   include <direct.h>
#else
#   include <sys/dir.h>
#   include <sys/stat.h>
#   include <sys/types.h>
#   include <sys/ioctl.h>
#   include <unistd.h>
#endif

LVPA_NAMESPACE_START

std::string stringToLower(std::string s)
{
    std::transform(s.begin(), s.end(), s.begin(), tolower);
    return s;
}

std::string stringToUpper(std::string s)
{
    std::transform(s.begin(), s.end(), s.begin(), toupper);
    return s;
}

void makeLowercase(std::string& s)
{
    std::transform(s.begin(), s.end(), s.begin(), tolower);
}

void makeUppercase(std::string& s)
{
    std::transform(s.begin(), s.end(), s.begin(), toupper);
}

// returns list of *plain* file names in given directory,
// without paths, and without anything else
void GetFileList(const char *path, StringList& files)
{
#if !_WIN32
    DIR * dirp;
    struct dirent * dp;
    dirp = opendir(path);
    if(dirp)
    {
        while((dp=readdir(dirp)) != NULL)
        {
            if (dp->d_type != DT_DIR) // only add if it is not a directory
            {
                std::string s(dp->d_name);
                files.push_back(s);
            }
        }
        closedir(dirp);
    }

# else

    WIN32_FIND_DATA fil;
    std::string search(path);
    MakeSlashTerminated(search);
    search += "*";
    HANDLE hFil = FindFirstFile(search.c_str(),&fil);
    if(hFil != INVALID_HANDLE_VALUE)
    {
        do
        {
            if(!(fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
            {
                std::string s(fil.cFileName);
                files.push_back(s);
            }
        }
        while(FindNextFile(hFil, &fil));

        FindClose(hFil);
    }

# endif
}

// returns a list of directory names in the given directory, *without* the source dir.
// if getting the dir list recursively, all paths are added, except *again* the top source dir beeing queried.
void GetDirList(const char *path, StringList &dirs, bool recursive /* = false */)
{
#if !_WIN32
    DIR * dirp;
    struct dirent * dp;
    dirp = opendir(path);
    if(dirp)
    {
        while((dp = readdir(dirp))) // assignment is intentional
        {
            if (dp->d_type == DT_DIR) // only add if it is a directory
            {
                if(strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
                {
                    dirs.push_back(dp->d_name);
                    if (recursive) // needing a better way to do that
                    {
                        std::deque<std::string> newdirs;
                        GetDirList(dp->d_name, newdirs, true);
                        std::string d(dp->d_name);
                        for(std::deque<std::string>::iterator it = newdirs.begin(); it != newdirs.end(); ++it)
                            dirs.push_back(d + *it);
                    }
                }
            }
        }
        closedir(dirp);
    }

#else

    std::string search(path);
    MakeSlashTerminated(search);
    search += "*";
    WIN32_FIND_DATA fil;
    HANDLE hFil = FindFirstFile(search.c_str(),&fil);
    if(hFil != INVALID_HANDLE_VALUE)
    {
        do
        {
            if( fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
            {
                if (!strcmp(fil.cFileName, ".") || !strcmp(fil.cFileName, ".."))
                    continue;

                std::string d(fil.cFileName);
                dirs.push_back(d);

                if (recursive) // need a better way to do that
                {
                    StringList newdirs;
                    GetDirList(d.c_str(), newdirs, true);

                    for(std::deque<std::string>::iterator it = newdirs.begin(); it != newdirs.end(); ++it)
                        dirs.push_back(d + *it);
                }
            }
        }
        while(FindNextFile(hFil, &fil));

        FindClose(hFil);
    }

#endif
}

void GetFileListRecursive(std::string dir, StringList& files, bool withQueriedDir /* = false */)
{
    std::stack<std::string> stk;

    if(withQueriedDir)
    {
        stk.push(dir);
        while(stk.size())
        {
            dir = stk.top();
            stk.pop();
            MakeSlashTerminated(dir);

            StringList li;
            GetFileList(dir.c_str(), li);
            for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); ++it)
                files.push_back(dir + *it);

            li.clear();
            GetDirList(dir.c_str(), li, true);
            for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); ++it)
                stk.push(dir + *it);
        }
    }
    else
    {
        std::string topdir = dir;
        MakeSlashTerminated(topdir);
        stk.push("");
        while(stk.size())
        {
            dir = stk.top();
            stk.pop();
            MakeSlashTerminated(dir);

            StringList li;
            dir = topdir + dir;
            GetFileList(dir.c_str(), li);
            for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); ++it)
                files.push_back(dir + *it);

            li.clear();
            GetDirList(dir.c_str(), li, true);
            for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); ++it)
                stk.push(dir + *it);
        }
    }
}

// must return true if creating the directory was successful
bool CreateDir(const char *dir)
{
    if(IsDirectory(dir))
        return true;
	bool result;
# ifdef _WIN32
	result = !!::CreateDirectory(dir,NULL);
# else
	result = !mkdir(dir,S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#endif
	return result;
}

bool CreateDirRec(const char *dir)
{
    if(IsDirectory(dir))
        return true;
    bool result = true;
    StringList li;
    StrSplit(dir, "/\\", li, false);
    std::string d;
    d.reserve(strlen(dir));
    bool last;
    for(StringList::iterator it = li.begin(); it != li.end(); it++)
    {
        d += *it;
        last = CreateDir(d.c_str());
        result = last && result;
        d += '/';
    }
    return result || last;
}

std::string FixSlashes(const std::string& s)
{
    std::string r;
    r.reserve(s.length() + 1);
    char last = 0, cur;
    for(size_t i = 0; i < s.length(); ++i)
    {
        cur = s[i];
        if(cur == '\\')
            cur = '/';
        if(last == '/' && cur == '/')
            continue;
        r += cur;
        last = cur;
    }
    return r;
}

// extracts the file name from a given path
const char *PathToFileName(const char *str)
{
    const char *p = strrchr(str, '/');
    return p ? p+1 : str;
}

void MakeSlashTerminated(std::string& s)
{
    if(s.length() && s[s.length() - 1] != '/')
        s += '/';
}

bool IsDirectory(const char *s)
{
#if PLATFORM == PLATFORM_WIN32
    DWORD dwFileAttr = GetFileAttributes(s);
    if(dwFileAttr == INVALID_FILE_ATTRIBUTES)
        return false;
    return !!(dwFileAttr & FILE_ATTRIBUTE_DIRECTORY);
#else
    if ( access( s, 0 ) == 0 )
    {
        struct stat status;
        stat( s, &status );
        return status.st_mode & S_IFDIR;
    }
    return false;
#endif
}

// from http://board.byuu.org/viewtopic.php?f=10&t=1089&start=15
bool WildcardMatch(const char *str, const char *pattern)
{
    const char *cp = 0, *mp = 0;
    while(*str && *pattern != '*')
    {
        if(*pattern != *str && *pattern != '?')
            return false;
        pattern++, str++;
    }

    while(*str)
    {
        if(*pattern == '*')
        {
            if(!*++pattern)
                return 1;
            mp = pattern;
            cp = str + 1;
        }
        else if(*pattern == *str || *pattern == '?')
        {
            ++pattern;
            ++str;
        }
        else
        {
            pattern = mp;
            str = cp++;
        }
    }

    while(*pattern++ == '*');

    return !*pattern;
}

uint32 GetConsoleWidth(void)
{
#if PLATFORM == PLATFORM_WIN32
    HANDLE hOut;
    CONSOLE_SCREEN_BUFFER_INFO SBInfo;
    hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    GetConsoleScreenBufferInfo(hOut,
        &SBInfo);
    return SBInfo.dwSize.X;
#else
    struct winsize ws;
    if (ioctl(0,TIOCGWINSZ,&ws))
        return 80; // the standard, because we don't know any better
    return ws.ws_col;
#endif
}

// from http://graphics.stanford.edu/~seander/bithacks.html
uint32 ilog2(uint32 v)
{
    const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
    const unsigned int S[] = {1, 2, 4, 8, 16};
    int i;

    register unsigned int r = 0; // result of log2(v) will go here
    for (i = 4; i >= 0; i--) // unroll for speed...
    {
        if (v & b[i])
        {
            v >>= S[i];
            r |= S[i];
        }
    }
    return r;
}

std::string GenerateTempFileName(const std::string& fn)
{
    // Start out simple.
    std::string ret = fn + ".tmp";
    FILE *fh = fopen(ret.c_str(), "r");
    if(!fh && FileIsWriteable(ret.c_str()))
        return ret; // good!

    // still in use... try a bit harder.
    if(fh)
        fclose(fh);
    char buf[L_tmpnam + 12];

    for(uint32 i = 0; i < 256; ++i)
    {
        sprintf(&buf[0], "%u", i);
        ret = fn + "_tmp" + &buf[0];
        fh = fopen(ret.c_str(), "r");
        if(!fh && FileIsWriteable(ret.c_str()))
            return ret;
        if(fh)
            fclose(fh);
    }

    // Last, let the OS decide (this sucks on windows, that's why it's last)
    if(tmpnam(&buf[0]))
        if(FileIsWriteable(&buf[0]))
            return &buf[0];

    // Give up.
    logerror("GenerateTempFileName(%s) - giving up");
    return "";
}

bool FileIsWriteable(const std::string& fn)
{
    FILE *fh = fopen(fn.c_str(), "w");
    if(fh)
    {
        fclose(fh);
        remove(fn.c_str());
        return true;
    }
    return false;
}

LVPA_NAMESPACE_END
<commit_msg>Generate temp file with mkstemp instead of tmpnam<commit_after>#include "LVPAInternal.h"
#include "LVPATools.h"

#include <algorithm>
#include <cctype>
#include <stack>

#if PLATFORM == PLATFORM_WIN32
#   define WIN32_LEAN_AND_MEAN
#   include <windows.h>
#   include <direct.h>
#else
#   include <sys/dir.h>
#   include <sys/stat.h>
#   include <sys/types.h>
#   include <sys/ioctl.h>
#   include <unistd.h>
#endif

LVPA_NAMESPACE_START

std::string stringToLower(std::string s)
{
    std::transform(s.begin(), s.end(), s.begin(), tolower);
    return s;
}

std::string stringToUpper(std::string s)
{
    std::transform(s.begin(), s.end(), s.begin(), toupper);
    return s;
}

void makeLowercase(std::string& s)
{
    std::transform(s.begin(), s.end(), s.begin(), tolower);
}

void makeUppercase(std::string& s)
{
    std::transform(s.begin(), s.end(), s.begin(), toupper);
}

// returns list of *plain* file names in given directory,
// without paths, and without anything else
void GetFileList(const char *path, StringList& files)
{
#if !_WIN32
    DIR * dirp;
    struct dirent * dp;
    dirp = opendir(path);
    if(dirp)
    {
        while((dp=readdir(dirp)) != NULL)
        {
            if (dp->d_type != DT_DIR) // only add if it is not a directory
            {
                std::string s(dp->d_name);
                files.push_back(s);
            }
        }
        closedir(dirp);
    }

# else

    WIN32_FIND_DATA fil;
    std::string search(path);
    MakeSlashTerminated(search);
    search += "*";
    HANDLE hFil = FindFirstFile(search.c_str(),&fil);
    if(hFil != INVALID_HANDLE_VALUE)
    {
        do
        {
            if(!(fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
            {
                std::string s(fil.cFileName);
                files.push_back(s);
            }
        }
        while(FindNextFile(hFil, &fil));

        FindClose(hFil);
    }

# endif
}

// returns a list of directory names in the given directory, *without* the source dir.
// if getting the dir list recursively, all paths are added, except *again* the top source dir beeing queried.
void GetDirList(const char *path, StringList &dirs, bool recursive /* = false */)
{
#if !_WIN32
    DIR * dirp;
    struct dirent * dp;
    dirp = opendir(path);
    if(dirp)
    {
        while((dp = readdir(dirp))) // assignment is intentional
        {
            if (dp->d_type == DT_DIR) // only add if it is a directory
            {
                if(strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
                {
                    dirs.push_back(dp->d_name);
                    if (recursive) // needing a better way to do that
                    {
                        std::deque<std::string> newdirs;
                        GetDirList(dp->d_name, newdirs, true);
                        std::string d(dp->d_name);
                        for(std::deque<std::string>::iterator it = newdirs.begin(); it != newdirs.end(); ++it)
                            dirs.push_back(d + *it);
                    }
                }
            }
        }
        closedir(dirp);
    }

#else

    std::string search(path);
    MakeSlashTerminated(search);
    search += "*";
    WIN32_FIND_DATA fil;
    HANDLE hFil = FindFirstFile(search.c_str(),&fil);
    if(hFil != INVALID_HANDLE_VALUE)
    {
        do
        {
            if( fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
            {
                if (!strcmp(fil.cFileName, ".") || !strcmp(fil.cFileName, ".."))
                    continue;

                std::string d(fil.cFileName);
                dirs.push_back(d);

                if (recursive) // need a better way to do that
                {
                    StringList newdirs;
                    GetDirList(d.c_str(), newdirs, true);

                    for(std::deque<std::string>::iterator it = newdirs.begin(); it != newdirs.end(); ++it)
                        dirs.push_back(d + *it);
                }
            }
        }
        while(FindNextFile(hFil, &fil));

        FindClose(hFil);
    }

#endif
}

void GetFileListRecursive(std::string dir, StringList& files, bool withQueriedDir /* = false */)
{
    std::stack<std::string> stk;

    if(withQueriedDir)
    {
        stk.push(dir);
        while(stk.size())
        {
            dir = stk.top();
            stk.pop();
            MakeSlashTerminated(dir);

            StringList li;
            GetFileList(dir.c_str(), li);
            for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); ++it)
                files.push_back(dir + *it);

            li.clear();
            GetDirList(dir.c_str(), li, true);
            for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); ++it)
                stk.push(dir + *it);
        }
    }
    else
    {
        std::string topdir = dir;
        MakeSlashTerminated(topdir);
        stk.push("");
        while(stk.size())
        {
            dir = stk.top();
            stk.pop();
            MakeSlashTerminated(dir);

            StringList li;
            dir = topdir + dir;
            GetFileList(dir.c_str(), li);
            for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); ++it)
                files.push_back(dir + *it);

            li.clear();
            GetDirList(dir.c_str(), li, true);
            for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); ++it)
                stk.push(dir + *it);
        }
    }
}

// must return true if creating the directory was successful
bool CreateDir(const char *dir)
{
    if(IsDirectory(dir))
        return true;
	bool result;
# ifdef _WIN32
	result = !!::CreateDirectory(dir,NULL);
# else
	result = !mkdir(dir,S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#endif
	return result;
}

bool CreateDirRec(const char *dir)
{
    if(IsDirectory(dir))
        return true;
    bool result = true;
    StringList li;
    StrSplit(dir, "/\\", li, false);
    std::string d;
    d.reserve(strlen(dir));
    bool last;
    for(StringList::iterator it = li.begin(); it != li.end(); it++)
    {
        d += *it;
        last = CreateDir(d.c_str());
        result = last && result;
        d += '/';
    }
    return result || last;
}

std::string FixSlashes(const std::string& s)
{
    std::string r;
    r.reserve(s.length() + 1);
    char last = 0, cur;
    for(size_t i = 0; i < s.length(); ++i)
    {
        cur = s[i];
        if(cur == '\\')
            cur = '/';
        if(last == '/' && cur == '/')
            continue;
        r += cur;
        last = cur;
    }
    return r;
}

// extracts the file name from a given path
const char *PathToFileName(const char *str)
{
    const char *p = strrchr(str, '/');
    return p ? p+1 : str;
}

void MakeSlashTerminated(std::string& s)
{
    if(s.length() && s[s.length() - 1] != '/')
        s += '/';
}

bool IsDirectory(const char *s)
{
#if PLATFORM == PLATFORM_WIN32
    DWORD dwFileAttr = GetFileAttributes(s);
    if(dwFileAttr == INVALID_FILE_ATTRIBUTES)
        return false;
    return !!(dwFileAttr & FILE_ATTRIBUTE_DIRECTORY);
#else
    if ( access( s, 0 ) == 0 )
    {
        struct stat status;
        stat( s, &status );
        return status.st_mode & S_IFDIR;
    }
    return false;
#endif
}

// from http://board.byuu.org/viewtopic.php?f=10&t=1089&start=15
bool WildcardMatch(const char *str, const char *pattern)
{
    const char *cp = 0, *mp = 0;
    while(*str && *pattern != '*')
    {
        if(*pattern != *str && *pattern != '?')
            return false;
        pattern++, str++;
    }

    while(*str)
    {
        if(*pattern == '*')
        {
            if(!*++pattern)
                return 1;
            mp = pattern;
            cp = str + 1;
        }
        else if(*pattern == *str || *pattern == '?')
        {
            ++pattern;
            ++str;
        }
        else
        {
            pattern = mp;
            str = cp++;
        }
    }

    while(*pattern++ == '*');

    return !*pattern;
}

uint32 GetConsoleWidth(void)
{
#if PLATFORM == PLATFORM_WIN32
    HANDLE hOut;
    CONSOLE_SCREEN_BUFFER_INFO SBInfo;
    hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    GetConsoleScreenBufferInfo(hOut,
        &SBInfo);
    return SBInfo.dwSize.X;
#else
    struct winsize ws;
    if (ioctl(0,TIOCGWINSZ,&ws))
        return 80; // the standard, because we don't know any better
    return ws.ws_col;
#endif
}

// from http://graphics.stanford.edu/~seander/bithacks.html
uint32 ilog2(uint32 v)
{
    const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
    const unsigned int S[] = {1, 2, 4, 8, 16};
    int i;

    register unsigned int r = 0; // result of log2(v) will go here
    for (i = 4; i >= 0; i--) // unroll for speed...
    {
        if (v & b[i])
        {
            v >>= S[i];
            r |= S[i];
        }
    }
    return r;
}

std::string GenerateTempFileName(const std::string& fn)
{
    // Start out simple.
    std::string ret = fn + ".tmp";
    FILE *fh = fopen(ret.c_str(), "r");
    if(!fh && FileIsWriteable(ret.c_str()))
        return ret; // good!

    // still in use... try a bit harder.
    if(fh)
        fclose(fh);
    char buf[L_tmpnam + 12];

    for(uint32 i = 0; i < 256; ++i)
    {
        sprintf(&buf[0], "%u", i);
        ret = fn + "_tmp" + &buf[0];
        fh = fopen(ret.c_str(), "r");
        if(!fh && FileIsWriteable(ret.c_str()))
            return ret;
        if(fh)
            fclose(fh);
    }

    // Last, let the OS decide (this sucks on windows, that's why it's last)
    if(mkstemp(&buf[0]))
        if(FileIsWriteable(&buf[0]))
            return &buf[0];

    // Give up.
    logerror("GenerateTempFileName(%s) - giving up");
    return "";
}

bool FileIsWriteable(const std::string& fn)
{
    FILE *fh = fopen(fn.c_str(), "w");
    if(fh)
    {
        fclose(fh);
        remove(fn.c_str());
        return true;
    }
    return false;
}

LVPA_NAMESPACE_END
<|endoftext|>


<commit_before>#include "shared.hh"
#include "local-store.hh"
#include "util.hh"
#include "serialise.hh"
#include "worker-protocol.hh"
#include "archive.hh"
#include "globals.hh"

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>

using namespace nix;

#ifndef SIGPOLL
#define SIGPOLL SIGIO
#endif

static FdSource from(STDIN_FILENO);
static FdSink to(STDOUT_FILENO);

bool canSendStderr;

/* This function is called anytime we want to write something to
   stderr.  If we're in a state where the protocol allows it (i.e.,
   when canSendStderr), send the message to the client over the
   socket. */
static void tunnelStderr(const unsigned char * buf, size_t count)
{
    if (canSendStderr) {
        try {
            writeInt(STDERR_NEXT, to);
            writeString(string((char *) buf, count), to);
        } catch (...) {
            /* Write failed; that means that the other side is
               gone. */
            canSendStderr = false;
            throw;
        }
    }
}

/* A SIGPOLL signal is received when data is available on the client
   communication scoket, or when the client has closed its side of the
   socket.  This handler is enabled at precisely those moments in the
   protocol when we're doing work and the client is supposed to be
   quiet.  Thus, if we get a SIGPOLL signal, it means that the client
   has quit.  So we should quit as well.

   Too bad most operating systems don't support the POLL_HUP value for
   si_code in siginfo_t.  That would make most of the SIGPOLL
   complexity unnecessary, i.e., we could just enable SIGPOLL all the
   time and wouldn't have to worry about races. */
static void sigPollHandler(int sigNo)
{
    if (!blockInt) {
        _isInterrupted = 1;
        blockInt = 1;
        canSendStderr = false;
        write(STDERR_FILENO, "SIGPOLL\n", 8);
    }
}

static void setSigPollAction(bool enable)
{
    struct sigaction act, oact;
    act.sa_handler = enable ? sigPollHandler : SIG_IGN;
    sigfillset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGPOLL, &act, &oact))
        throw SysError("setting handler for SIGPOLL");
}

/* startWork() means that we're starting an operation for which we
   want to send out stderr to the client. */
static void startWork()
{
    canSendStderr = true;

    /* Handle client death asynchronously. */
    setSigPollAction(true);

    /* Of course, there is a race condition here: the socket could
       have closed between when we last read from / wrote to it, and
       between the time we set the handler for SIGPOLL.  In that case
       we won't get the signal.  So do a non-blocking select() to find
       out if any input is available on the socket.  If there is, it
       has to be the 0-byte read that indicates that the socket has
       closed. */

    struct timeval timeout;
    timeout.tv_sec = timeout.tv_usec = 0;

    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(from.fd, &fds);

    if (select(from.fd + 1, &fds, 0, 0, &timeout) == -1)
        throw SysError("select()");

    if (FD_ISSET(from.fd, &fds)) {
        char c;
        if (read(from.fd, &c, 1) != 0)
            throw Error("EOF expected (protocol error?)");
        _isInterrupted = 1;
        checkInterrupt();
    }
}

/* stopWork() means that we're done; stop sending stderr to the
   client. */
static void stopWork(bool success = true, const string & msg = "")
{
    /* Stop handling async client death; we're going to a state where
       we're either sending or receiving from the client, so we'll be
       notified of client death anyway. */
    setSigPollAction(false);

    canSendStderr = false;

    if (success)
        writeInt(STDERR_LAST, to);
    else {
        writeInt(STDERR_ERROR, to);
        writeString(msg, to);
    }
}

static void performOp(Source & from, Sink & to, unsigned int op)
{
    switch (op) {

#if 0
    case wopQuit: {
        /* Close the database. */
        store.reset((StoreAPI *) 0);
        writeInt(1, to);
        break;
    }
#endif

    case wopIsValidPath: {
        Path path = readStorePath(from);
        startWork();
        bool result = store->isValidPath(path);
        stopWork();
        writeInt(result, to);
        break;
    }

    case wopHasSubstitutes: {
        Path path = readStorePath(from);
        startWork();
        bool result = store->hasSubstitutes(path);
        stopWork();
        writeInt(result, to);
        break;
    }

    case wopQueryPathHash: {
        Path path = readStorePath(from);
        startWork();
        Hash hash = store->queryPathHash(path);
        stopWork();
        writeString(printHash(hash), to);
        break;
    }

    case wopQueryReferences:
    case wopQueryReferrers: {
        Path path = readStorePath(from);
        startWork();
        PathSet paths;
        if (op == wopQueryReferences)
            store->queryReferences(path, paths);
        else
            store->queryReferrers(path, paths);
        stopWork();
        writeStringSet(paths, to);
        break;
    }

    case wopAddToStore: {
        /* !!! uberquick hack */
        string baseName = readString(from);
        bool fixed = readInt(from) == 1;
        bool recursive = readInt(from) == 1;
        string hashAlgo = readString(from);

        Path tmp = createTempDir();
        Path tmp2 = tmp + "/" + baseName;
        restorePath(tmp2, from);

        startWork();
        Path path = store->addToStore(tmp2, fixed, recursive, hashAlgo);
        stopWork();

        writeString(path, to);

        deletePath(tmp);
        break;
    }

    case wopAddTextToStore: {
        string suffix = readString(from);
        string s = readString(from);
        PathSet refs = readStorePaths(from);
        startWork();
        Path path = store->addTextToStore(suffix, s, refs);
        stopWork();
        writeString(path, to);
        break;
    }

    case wopBuildDerivations: {
        PathSet drvs = readStorePaths(from);
        startWork();
        store->buildDerivations(drvs);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopEnsurePath: {
        Path path = readStorePath(from);
        startWork();
        store->ensurePath(path);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopAddTempRoot: {
        Path path = readStorePath(from);
        startWork();
        store->addTempRoot(path);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopAddIndirectRoot: {
        Path path = absPath(readString(from));
        startWork();
        store->addIndirectRoot(path);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopSyncWithGC: {
        startWork();
        store->syncWithGC();
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopFindRoots: {
        startWork();
        Roots roots = store->findRoots();
        stopWork();
        writeInt(roots.size(), to);
        for (Roots::iterator i = roots.begin(); i != roots.end(); ++i) {
            writeString(i->first, to);
            writeString(i->second, to);
        }
        break;
    }

    case wopCollectGarbage: {
        GCAction action = (GCAction) readInt(from);
        PathSet pathsToDelete = readStorePaths(from);
        bool ignoreLiveness = readInt(from);

        PathSet result;
        unsigned long long bytesFreed;

        startWork();
        if (ignoreLiveness)
            throw Error("you are not allowed to ignore liveness");
        store->collectGarbage(action, pathsToDelete, ignoreLiveness,
            result, bytesFreed);
        stopWork();

        writeStringSet(result, to);
        writeInt(bytesFreed & 0xffffffff, to);
        writeInt(bytesFreed >> 32, to);

        break;
    }

    default:
        throw Error(format("invalid operation %1%") % op);
    }
}

static void processConnection()
{
    canSendStderr = false;
    writeToStderr = tunnelStderr;

    /* Allow us to receive SIGPOLL for events on the client socket. */
    setSigPollAction(false);
    if (fcntl(from.fd, F_SETOWN, getpid()) == -1)
        throw SysError("F_SETOWN");
    if (fcntl(from.fd, F_SETFL, fcntl(from.fd, F_GETFL, 0) | FASYNC) == -1)
        throw SysError("F_SETFL");

    /* Exchange the greeting. */
    unsigned int magic = readInt(from);
    if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch");
    verbosity = (Verbosity) readInt(from);
    writeInt(WORKER_MAGIC_2, to);

    /* Send startup error messages to the client. */
    startWork();

    try {

        /* Prevent users from doing something very dangerous. */
        if (setuidMode && geteuid() == 0 &&
            querySetting("build-users", Strings()).size() == 0)
            throw Error("if you run `nix-worker' setuid root, then you MUST set `build-users'!");

        /* Open the store. */
        store = boost::shared_ptr<StoreAPI>(new LocalStore(true));

        stopWork();

    } catch (Error & e) {
        stopWork(false, e.msg());
        return;
    }

    /* Process client requests. */
    unsigned int opCount = 0;

    while (true) {
        WorkerOp op;
        try {
            op = (WorkerOp) readInt(from);
        } catch (EndOfFile & e) {
            break;
        }

        opCount++;

        try {
            performOp(from, to, op);
        } catch (Error & e) {
            stopWork(false, e.msg());
        }

        assert(!canSendStderr);
    };

    printMsg(lvlError, format("%1% worker operations") % opCount);
}

static void setSigChldAction(bool ignore)
{
    struct sigaction act, oact;
    act.sa_handler = ignore ? SIG_IGN : SIG_DFL;
    sigfillset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGCHLD, &act, &oact))
        throw SysError("setting SIGCHLD handler");
}

static void daemonLoop()
{
    /* Get rid of children automatically; don't let them become
       zombies. */
    setSigChldAction(true);

    /* Create and bind to a Unix domain socket. */
    AutoCloseFD fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
    if (fdSocket == -1)
        throw SysError("cannot create Unix domain socket");

    string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;

    struct sockaddr_un addr;
    addr.sun_family = AF_UNIX;
    if (socketPath.size() >= sizeof(addr.sun_path))
        throw Error(format("socket path `%1%' is too long") % socketPath);
    strcpy(addr.sun_path, socketPath.c_str());

    unlink(socketPath.c_str());

    /* Make sure that the socket is created with 0666 permission
       (everybody can connect). */
    mode_t oldMode = umask(0111);
    int res = bind(fdSocket, (struct sockaddr *) &addr, sizeof(addr));
    umask(oldMode);
    if (res == -1)
        throw SysError(format("cannot bind to socket `%1%'") % socketPath);

    if (listen(fdSocket, 5) == -1)
        throw SysError(format("cannot listen on socket `%1%'") % socketPath);

    /* Loop accepting connections. */
    while (1) {

        try {
            /* Important: the server process *cannot* open the
               Berkeley DB environment, because it doesn't like forks
               very much. */
            assert(!store);

            /* Accept a connection. */
            struct sockaddr_un remoteAddr;
            socklen_t remoteAddrLen = sizeof(remoteAddr);

            AutoCloseFD remote = accept(fdSocket,
                (struct sockaddr *) &remoteAddr, &remoteAddrLen);
            checkInterrupt();
            if (remote == -1)
                throw SysError("accepting connection");

            printMsg(lvlInfo, format("accepted connection %1%") % remote);

            /* Fork a child to handle the connection. */
            pid_t child;
            child = fork();

            switch (child) {

            case -1:
                throw SysError("unable to fork");

            case 0:
                try { /* child */

                    /* Background the worker. */
                    if (setsid() == -1)
                        throw SysError(format("creating a new session"));

                    /* Restore normal handling of SIGCHLD. */
                    setSigChldAction(false);

                    /* Handle the connection. */
                    from.fd = remote;
                    to.fd = remote;
                    processConnection();

                } catch (std::exception & e) {
                    std::cerr << format("child error: %1%\n") % e.what();
                }
                exit(0);
            }

        } catch (Interrupted & e) {
            throw;
        } catch (Error & e) {
            printMsg(lvlError, format("error processing connection: %1%") % e.msg());
        }
    }
}

void run(Strings args)
{
    bool slave = false;
    bool daemon = false;

    for (Strings::iterator i = args.begin(); i != args.end(); ) {
        string arg = *i++;
        if (arg == "--slave") slave = true;
        if (arg == "--daemon") daemon = true;
    }

    if (slave) {
        /* This prevents us from receiving signals from the terminal
           when we're running in setuid mode. */
        if (setsid() == -1)
            throw SysError(format("creating a new session"));

        processConnection();
    }

    else if (daemon) {
        if (setuidMode)
            throw Error("daemon cannot be started in setuid mode");
        chdir("/");
        daemonLoop();
    }

    else
        throw Error("must be run in either --slave or --daemon mode");
}

#include "help.txt.hh"

void printHelp()
{
    std::cout << string((char *) helpText, sizeof helpText);
}

string programId = "nix-worker";
<commit_msg>* Ugly hack to handle spurious SIGPOLLs.<commit_after>#include "shared.hh"
#include "local-store.hh"
#include "util.hh"
#include "serialise.hh"
#include "worker-protocol.hh"
#include "archive.hh"
#include "globals.hh"

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>

using namespace nix;

#ifndef SIGPOLL
#define SIGPOLL SIGIO
#endif

static FdSource from(STDIN_FILENO);
static FdSink to(STDOUT_FILENO);

bool canSendStderr;

/* This function is called anytime we want to write something to
   stderr.  If we're in a state where the protocol allows it (i.e.,
   when canSendStderr), send the message to the client over the
   socket. */
static void tunnelStderr(const unsigned char * buf, size_t count)
{
    if (canSendStderr) {
        try {
            writeInt(STDERR_NEXT, to);
            writeString(string((char *) buf, count), to);
        } catch (...) {
            /* Write failed; that means that the other side is
               gone. */
            canSendStderr = false;
            throw;
        }
    }
}

/* Return true if the remote side has closed its end of the
   connection, false otherwise.  Should not be called on any socket on
   which we expect input! */
static bool isFarSideClosed(int socket)
{
    struct timeval timeout;
    timeout.tv_sec = timeout.tv_usec = 0;

    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(socket, &fds);

    if (select(socket + 1, &fds, 0, 0, &timeout) == -1)
        throw SysError("select()");

    if (!FD_ISSET(socket, &fds)) return false;

    /* Destructive read to determine whether the select() marked the
       socket as readable because there is actual input or because
       we've reached EOF (i.e., a read of size 0 is available). */
    char c;
    if (read(socket, &c, 1) != 0)
        throw Error("EOF expected (protocol error?)");

    return true;
}

/* A SIGPOLL signal is received when data is available on the client
   communication scoket, or when the client has closed its side of the
   socket.  This handler is enabled at precisely those moments in the
   protocol when we're doing work and the client is supposed to be
   quiet.  Thus, if we get a SIGPOLL signal, it means that the client
   has quit.  So we should quit as well.

   Too bad most operating systems don't support the POLL_HUP value for
   si_code in siginfo_t.  That would make most of the SIGPOLL
   complexity unnecessary, i.e., we could just enable SIGPOLL all the
   time and wouldn't have to worry about races. */
static void sigPollHandler(int sigNo)
{
    try {
        /* Check that the far side actually closed.  We're still
           getting spurious signals every once in a while.  I.e.,
           there is no input available, but we get a signal with
           POLL_IN set.  Maybe it's delayed or something. */
        if (isFarSideClosed(from.fd)) {
            if (!blockInt) {
                _isInterrupted = 1;
                blockInt = 1;
                canSendStderr = false;
                write(STDERR_FILENO, "SIGPOLL\n", 8);
            }
        } else {
            string s = "spurious SIGPOLL\n";
            write(STDERR_FILENO, s.c_str(), s.size());
        }
    }
    catch (Error & e) {
        /* Shouldn't happen. */
        write(STDERR_FILENO, e.msg().c_str(), e.msg().size());
        abort();
    }
}

static void setSigPollAction(bool enable)
{
    struct sigaction act, oact;
    act.sa_handler = enable ? sigPollHandler : SIG_IGN;
    sigfillset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGPOLL, &act, &oact))
        throw SysError("setting handler for SIGPOLL");
}

/* startWork() means that we're starting an operation for which we
   want to send out stderr to the client. */
static void startWork()
{
    canSendStderr = true;

    /* Handle client death asynchronously. */
    setSigPollAction(true);

    /* Of course, there is a race condition here: the socket could
       have closed between when we last read from / wrote to it, and
       between the time we set the handler for SIGPOLL.  In that case
       we won't get the signal.  So do a non-blocking select() to find
       out if any input is available on the socket.  If there is, it
       has to be the 0-byte read that indicates that the socket has
       closed. */
    if (isFarSideClosed(from.fd)) {
        _isInterrupted = 1;
        checkInterrupt();
    }
}

/* stopWork() means that we're done; stop sending stderr to the
   client. */
static void stopWork(bool success = true, const string & msg = "")
{
    /* Stop handling async client death; we're going to a state where
       we're either sending or receiving from the client, so we'll be
       notified of client death anyway. */
    setSigPollAction(false);

    canSendStderr = false;

    if (success)
        writeInt(STDERR_LAST, to);
    else {
        writeInt(STDERR_ERROR, to);
        writeString(msg, to);
    }
}

static void performOp(Source & from, Sink & to, unsigned int op)
{
    switch (op) {

#if 0
    case wopQuit: {
        /* Close the database. */
        store.reset((StoreAPI *) 0);
        writeInt(1, to);
        break;
    }
#endif

    case wopIsValidPath: {
        Path path = readStorePath(from);
        startWork();
        bool result = store->isValidPath(path);
        stopWork();
        writeInt(result, to);
        break;
    }

    case wopHasSubstitutes: {
        Path path = readStorePath(from);
        startWork();
        bool result = store->hasSubstitutes(path);
        stopWork();
        writeInt(result, to);
        break;
    }

    case wopQueryPathHash: {
        Path path = readStorePath(from);
        startWork();
        Hash hash = store->queryPathHash(path);
        stopWork();
        writeString(printHash(hash), to);
        break;
    }

    case wopQueryReferences:
    case wopQueryReferrers: {
        Path path = readStorePath(from);
        startWork();
        PathSet paths;
        if (op == wopQueryReferences)
            store->queryReferences(path, paths);
        else
            store->queryReferrers(path, paths);
        stopWork();
        writeStringSet(paths, to);
        break;
    }

    case wopAddToStore: {
        /* !!! uberquick hack */
        string baseName = readString(from);
        bool fixed = readInt(from) == 1;
        bool recursive = readInt(from) == 1;
        string hashAlgo = readString(from);

        Path tmp = createTempDir();
        Path tmp2 = tmp + "/" + baseName;
        restorePath(tmp2, from);

        startWork();
        Path path = store->addToStore(tmp2, fixed, recursive, hashAlgo);
        stopWork();

        writeString(path, to);

        deletePath(tmp);
        break;
    }

    case wopAddTextToStore: {
        string suffix = readString(from);
        string s = readString(from);
        PathSet refs = readStorePaths(from);
        startWork();
        Path path = store->addTextToStore(suffix, s, refs);
        stopWork();
        writeString(path, to);
        break;
    }

    case wopBuildDerivations: {
        PathSet drvs = readStorePaths(from);
        startWork();
        store->buildDerivations(drvs);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopEnsurePath: {
        Path path = readStorePath(from);
        startWork();
        store->ensurePath(path);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopAddTempRoot: {
        Path path = readStorePath(from);
        startWork();
        store->addTempRoot(path);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopAddIndirectRoot: {
        Path path = absPath(readString(from));
        startWork();
        store->addIndirectRoot(path);
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopSyncWithGC: {
        startWork();
        store->syncWithGC();
        stopWork();
        writeInt(1, to);
        break;
    }

    case wopFindRoots: {
        startWork();
        Roots roots = store->findRoots();
        stopWork();
        writeInt(roots.size(), to);
        for (Roots::iterator i = roots.begin(); i != roots.end(); ++i) {
            writeString(i->first, to);
            writeString(i->second, to);
        }
        break;
    }

    case wopCollectGarbage: {
        GCAction action = (GCAction) readInt(from);
        PathSet pathsToDelete = readStorePaths(from);
        bool ignoreLiveness = readInt(from);

        PathSet result;
        unsigned long long bytesFreed;

        startWork();
        if (ignoreLiveness)
            throw Error("you are not allowed to ignore liveness");
        store->collectGarbage(action, pathsToDelete, ignoreLiveness,
            result, bytesFreed);
        stopWork();

        writeStringSet(result, to);
        writeInt(bytesFreed & 0xffffffff, to);
        writeInt(bytesFreed >> 32, to);

        break;
    }

    default:
        throw Error(format("invalid operation %1%") % op);
    }
}

static void processConnection()
{
    canSendStderr = false;
    writeToStderr = tunnelStderr;

    /* Allow us to receive SIGPOLL for events on the client socket. */
    setSigPollAction(false);
    if (fcntl(from.fd, F_SETOWN, getpid()) == -1)
        throw SysError("F_SETOWN");
    if (fcntl(from.fd, F_SETFL, fcntl(from.fd, F_GETFL, 0) | FASYNC) == -1)
        throw SysError("F_SETFL");

    /* Exchange the greeting. */
    unsigned int magic = readInt(from);
    if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch");
    verbosity = (Verbosity) readInt(from);
    writeInt(WORKER_MAGIC_2, to);

    /* Send startup error messages to the client. */
    startWork();

    try {

        /* Prevent users from doing something very dangerous. */
        if (setuidMode && geteuid() == 0 &&
            querySetting("build-users", Strings()).size() == 0)
            throw Error("if you run `nix-worker' setuid root, then you MUST set `build-users'!");

        /* Open the store. */
        store = boost::shared_ptr<StoreAPI>(new LocalStore(true));

        stopWork();

    } catch (Error & e) {
        stopWork(false, e.msg());
        return;
    }

    /* Process client requests. */
    unsigned int opCount = 0;

    while (true) {
        WorkerOp op;
        try {
            op = (WorkerOp) readInt(from);
        } catch (EndOfFile & e) {
            break;
        }

        opCount++;

        try {
            performOp(from, to, op);
        } catch (Error & e) {
            stopWork(false, e.msg());
        }

        assert(!canSendStderr);
    };

    printMsg(lvlError, format("%1% worker operations") % opCount);
}

static void setSigChldAction(bool ignore)
{
    struct sigaction act, oact;
    act.sa_handler = ignore ? SIG_IGN : SIG_DFL;
    sigfillset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGCHLD, &act, &oact))
        throw SysError("setting SIGCHLD handler");
}

static void daemonLoop()
{
    /* Get rid of children automatically; don't let them become
       zombies. */
    setSigChldAction(true);

    /* Create and bind to a Unix domain socket. */
    AutoCloseFD fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
    if (fdSocket == -1)
        throw SysError("cannot create Unix domain socket");

    string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;

    struct sockaddr_un addr;
    addr.sun_family = AF_UNIX;
    if (socketPath.size() >= sizeof(addr.sun_path))
        throw Error(format("socket path `%1%' is too long") % socketPath);
    strcpy(addr.sun_path, socketPath.c_str());

    unlink(socketPath.c_str());

    /* Make sure that the socket is created with 0666 permission
       (everybody can connect). */
    mode_t oldMode = umask(0111);
    int res = bind(fdSocket, (struct sockaddr *) &addr, sizeof(addr));
    umask(oldMode);
    if (res == -1)
        throw SysError(format("cannot bind to socket `%1%'") % socketPath);

    if (listen(fdSocket, 5) == -1)
        throw SysError(format("cannot listen on socket `%1%'") % socketPath);

    /* Loop accepting connections. */
    while (1) {

        try {
            /* Important: the server process *cannot* open the
               Berkeley DB environment, because it doesn't like forks
               very much. */
            assert(!store);

            /* Accept a connection. */
            struct sockaddr_un remoteAddr;
            socklen_t remoteAddrLen = sizeof(remoteAddr);

            AutoCloseFD remote = accept(fdSocket,
                (struct sockaddr *) &remoteAddr, &remoteAddrLen);
            checkInterrupt();
            if (remote == -1)
                throw SysError("accepting connection");

            printMsg(lvlInfo, format("accepted connection %1%") % remote);

            /* Fork a child to handle the connection. */
            pid_t child;
            child = fork();

            switch (child) {

            case -1:
                throw SysError("unable to fork");

            case 0:
                try { /* child */

                    /* Background the worker. */
                    if (setsid() == -1)
                        throw SysError(format("creating a new session"));

                    /* Restore normal handling of SIGCHLD. */
                    setSigChldAction(false);

                    /* Handle the connection. */
                    from.fd = remote;
                    to.fd = remote;
                    processConnection();

                } catch (std::exception & e) {
                    std::cerr << format("child error: %1%\n") % e.what();
                }
                exit(0);
            }

        } catch (Interrupted & e) {
            throw;
        } catch (Error & e) {
            printMsg(lvlError, format("error processing connection: %1%") % e.msg());
        }
    }
}

void run(Strings args)
{
    bool slave = false;
    bool daemon = false;

    for (Strings::iterator i = args.begin(); i != args.end(); ) {
        string arg = *i++;
        if (arg == "--slave") slave = true;
        if (arg == "--daemon") daemon = true;
    }

    if (slave) {
        /* This prevents us from receiving signals from the terminal
           when we're running in setuid mode. */
        if (setsid() == -1)
            throw SysError(format("creating a new session"));

        processConnection();
    }

    else if (daemon) {
        if (setuidMode)
            throw Error("daemon cannot be started in setuid mode");
        chdir("/");
        daemonLoop();
    }

    else
        throw Error("must be run in either --slave or --daemon mode");
}

#include "help.txt.hh"

void printHelp()
{
    std::cout << string((char *) helpText, sizeof helpText);
}

string programId = "nix-worker";
<|endoftext|>


<commit_before>//
// (c) Copyright 2017 DESY,ESS
//
// This file is part of h5pp.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
// License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
// Boston, MA  02110-1301 USA
// ===========================================================================
//
// Author: Martin Shetty <martin.shetty@esss.se>
// Created on: Sep 9, 2017
//
#include <sstream>
#include <h5cpp/node/functions.hpp>

namespace hdf5 {
namespace node {

void copy(const Node &source, const Group& base, const Path &rel_path)
{
  //what if rel_path is actually absolute?
  if (base.links.exists(static_cast<std::string>(rel_path)))
  {
    std::stringstream ss;
    ss << "node::copy failed. "
       << base.link() << " / " << rel_path << " already exists!";
    throw std::runtime_error(ss.str());
  }

  if (0 > H5Ocopy(static_cast<hid_t>(source.link().parent()), //parent
                  source.link().path().back().c_str(),        //object name
                  static_cast<hid_t>(base),                   //destination parent
                  static_cast<std::string>(rel_path).c_str(), //destination name
                  0, 0))
  {
    std::stringstream ss;
    ss << "node::copy failed. Could not copy "
       << source.link() << " to "
       << base.link() << " / " << rel_path;
    throw std::runtime_error(ss.str());
  }
}

void copy(const Node &source, const Group& destination)
{
  //what if rel_path is actually absolute?
  auto name = source.link().path().back(); //this feels awkward
  if (destination.links.exists(name))
  {
    std::stringstream ss;
    ss << "node::copy failed. "
       << destination.link() << " / " << name << " already exists!";
    throw std::runtime_error(ss.str());
  }

  if (0 > H5Ocopy(static_cast<hid_t>(source.link().parent()), //parent
                  name.c_str(),                               //object name
                  static_cast<hid_t>(destination),            //destination parent
                  name.c_str(),                               //...same name
                  0, 0))
  {
    std::stringstream ss;
    ss << "node::copy failed. Could not copy "
       << source.link() << " to "
       << destination.link() << " / " << name;
    throw std::runtime_error(ss.str());
  }
}

void remove(const Node &object,
            const property::LinkAccessList &lapl)
{
  remove(object.link().parent(), Path(object.link().path().back()), lapl);
}

void remove(const Group &base, const Path &rel_path,
            const property::LinkAccessList &lapl)
{
  if (!base.links.exists(static_cast<std::string>(rel_path)))
  {
    std::stringstream ss;
    ss << "node::remove failed. "
       << base.link() << " / " << rel_path << " does not exist.";
    throw std::runtime_error(ss.str());
  }
  if (0 > H5Ldelete(static_cast<hid_t>(base),
                    static_cast<std::string>(rel_path).c_str(),
                    static_cast<hid_t>(lapl)))
  {
    std::stringstream ss;
    ss << "node::remove failed. Could not remove"
       << base.link() << " / " << rel_path;
    throw std::runtime_error(ss.str());
  }
}

void move(const Node &source,const Group &destination_group,
          const property::LinkCreationList &lcpl,
          const property::LinkAccessList &lapl)
{
  move(source, destination_group,
       Path(source.link().path().back()),
       lcpl, lapl);
}

void move(const Node &source,const Group &destination,const Path &rel_path,
          const property::LinkCreationList &lcpl,
          const property::LinkAccessList &lapl)
{
  auto name = source.link().path().back();
  if (destination.links.exists(name))
  {
    std::stringstream ss;
    ss << "node::move failed. "
       << destination.link() << " / " << name << " already exists!";
    throw std::runtime_error(ss.str());
  }

  if (0 > H5Lmove(static_cast<hid_t>(source.link().parent()),
                  name.c_str(),
                  static_cast<hid_t>(destination),
                  static_cast<std::string>(rel_path).c_str(),
                  static_cast<hid_t>(lcpl),
                  static_cast<hid_t>(lapl)))
  {
    std::stringstream ss;
    ss << "node::move failed. Could not move "
       << source.link() << " to "
       << destination.link() << " / " << rel_path;
    throw std::runtime_error(ss.str());
  }
}

void link(const boost::filesystem::path &target_file,
          const Path &target_path,
          const Group &link_base,
          const Path &link_path,
          const property::LinkCreationList &lcpl,
          const property::LinkAccessList &lapl)
{
  // if link_path absolute, then link_base = root_group?
  if (0 > H5Lcreate_external(target_file.string().c_str(),
                             static_cast<std::string>(target_path).c_str(),
                             static_cast<hid_t>(link_base),
                             static_cast<std::string>(link_path).c_str(),
                             static_cast<hid_t>(lcpl),
                             static_cast<hid_t>(lapl)))
  {
    std::stringstream ss;
    ss << "Failed to create external link "
       << link_base.link() << ": " << link_path
       << " -> "
       << target_file << ": " << target_path;
    throw std::runtime_error(ss.str());
  }
}

} // namespace node
} // namespace hdf5

<commit_msg>implemented soft and external link creation functions #5<commit_after>//
// (c) Copyright 2017 DESY,ESS
//
// This file is part of h5pp.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
// License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
// Boston, MA  02110-1301 USA
// ===========================================================================
//
// Author: Martin Shetty <martin.shetty@esss.se>
// Created on: Sep 9, 2017
//
#include <sstream>
#include <h5cpp/node/functions.hpp>

namespace hdf5 {
namespace node {

void copy(const Node &source, const Group& base, const Path &rel_path)
{
  //what if rel_path is actually absolute?
  if (base.links.exists(static_cast<std::string>(rel_path)))
  {
    std::stringstream ss;
    ss << "node::copy failed. "
       << base.link() << " / " << rel_path << " already exists!";
    throw std::runtime_error(ss.str());
  }

  if (0 > H5Ocopy(static_cast<hid_t>(source.link().parent()), //parent
                  source.link().path().back().c_str(),        //object name
                  static_cast<hid_t>(base),                   //destination parent
                  static_cast<std::string>(rel_path).c_str(), //destination name
                  0, 0))
  {
    std::stringstream ss;
    ss << "node::copy failed. Could not copy "
       << source.link() << " to "
       << base.link() << " / " << rel_path;
    throw std::runtime_error(ss.str());
  }
}

void copy(const Node &source, const Group& destination)
{
  //what if rel_path is actually absolute?
  auto name = source.link().path().back(); //this feels awkward
  if (destination.links.exists(name))
  {
    std::stringstream ss;
    ss << "node::copy failed. "
       << destination.link() << " / " << name << " already exists!";
    throw std::runtime_error(ss.str());
  }

  if (0 > H5Ocopy(static_cast<hid_t>(source.link().parent()), //parent
                  name.c_str(),                               //object name
                  static_cast<hid_t>(destination),            //destination parent
                  name.c_str(),                               //...same name
                  0, 0))
  {
    std::stringstream ss;
    ss << "node::copy failed. Could not copy "
       << source.link() << " to "
       << destination.link() << " / " << name;
    throw std::runtime_error(ss.str());
  }
}

void remove(const Node &object,
            const property::LinkAccessList &lapl)
{
  remove(object.link().parent(), Path(object.link().path().back()), lapl);
}

void remove(const Group &base, const Path &rel_path,
            const property::LinkAccessList &lapl)
{
  if (!base.links.exists(static_cast<std::string>(rel_path)))
  {
    std::stringstream ss;
    ss << "node::remove failed. "
       << base.link() << " / " << rel_path << " does not exist.";
    throw std::runtime_error(ss.str());
  }
  if (0 > H5Ldelete(static_cast<hid_t>(base),
                    static_cast<std::string>(rel_path).c_str(),
                    static_cast<hid_t>(lapl)))
  {
    std::stringstream ss;
    ss << "node::remove failed. Could not remove"
       << base.link() << " / " << rel_path;
    throw std::runtime_error(ss.str());
  }
}

void move(const Node &source,const Group &destination_group,
          const property::LinkCreationList &lcpl,
          const property::LinkAccessList &lapl)
{
  move(source, destination_group,
       Path(source.link().path().back()),
       lcpl, lapl);
}

void move(const Node &source,const Group &destination,const Path &rel_path,
          const property::LinkCreationList &lcpl,
          const property::LinkAccessList &lapl)
{
  auto name = source.link().path().back();
  if (destination.links.exists(name))
  {
    std::stringstream ss;
    ss << "node::move failed. "
       << destination.link() << " / " << name << " already exists!";
    throw std::runtime_error(ss.str());
  }

  if (0 > H5Lmove(static_cast<hid_t>(source.link().parent()),
                  name.c_str(),
                  static_cast<hid_t>(destination),
                  static_cast<std::string>(rel_path).c_str(),
                  static_cast<hid_t>(lcpl),
                  static_cast<hid_t>(lapl)))
  {
    std::stringstream ss;
    ss << "node::move failed. Could not move "
       << source.link() << " to "
       << destination.link() << " / " << rel_path;
    throw std::runtime_error(ss.str());
  }
}

void link(const boost::filesystem::path &target_file,
          const Path &target_path,
          const Group &link_base,
          const Path &link_path,
          const property::LinkCreationList &lcpl,
          const property::LinkAccessList &lapl)
{
  auto base = link_base;
  if (link_path.is_absolute_path())
    base = link_base.link().file().root();
  if (0 > H5Lcreate_external(target_file.string().c_str(),
                             static_cast<std::string>(target_path).c_str(),
                             static_cast<hid_t>(base),
                             static_cast<std::string>(link_path).c_str(),
                             static_cast<hid_t>(lcpl),
                             static_cast<hid_t>(lapl)))
  {
    std::stringstream ss;
    ss << "Failed to create external link "
       << link_base.link() << ": " << link_path
       << " -> "
       << target_file << ": " << target_path;
    throw std::runtime_error(ss.str());
  }
}

void link(const Group &target_base,const Path &target_path,
          const Group &link_base,const Path &link_path,
          const property::LinkCreationList &lcpl,
          const property::LinkAccessList &lapl)
{
  if (target_base.link().file().path() != link_base.link().file().path())
  {
    link(target_base.link().file().path(), target_path,
         link_base, link_path, lcpl, lapl);
  }
  else
  {
    auto base = link_base;
    if (link_path.is_absolute_path())
      base = link_base.link().file().root();
    if (0 > H5Lcreate_soft(
          static_cast<std::string>(target_base.link().path() + target_path).c_str(),
          static_cast<hid_t>(base),
          static_cast<std::string>(link_path).c_str(),
          static_cast<hid_t>(lcpl),
          static_cast<hid_t>(lapl)))
    {
      std::stringstream ss;
      ss << "Failed to create soft link "
         << base.link() << ": " << link_path
         << " -> "
         << target_base.link() << ": " << target_path;
      throw std::runtime_error(ss.str());
    }
  }
}

} // namespace node
} // namespace hdf5

<|endoftext|>


<commit_before>//
//  ofxSkinMesh.cpp
//
//  Created by Iwatani Nariaki on 2016/07/13.
//
//

#include "ofxSkinnedMesh.h"
#include <numeric>

using namespace ofx::skinnedmesh;
using namespace std;

void SkinnedMesh::setSkeleton(const vector<shared_ptr<ofNode>> &skeleton)
{
	skeleton_.clear();
	original_skeleton_.clear();
	for_each(skeleton.begin(), skeleton.end(), [this](const shared_ptr<ofNode> &s) {
		skeleton_.emplace_back(s);
		original_skeleton_.emplace_back(*s);
		original_skeleton_.back().clearParent();
	});
}

void SkinnedMesh::addWeight(ofIndexType vertex_index, ofIndexType node_index, float weight)
{
	auto result = weight_.insert(make_pair(vertex_index, unordered_map<ofIndexType,float>()));
	if(result.second) {
		result.first->second.insert(make_pair(node_index, weight));
	}
	else {
		auto result2 = weight_[vertex_index].insert(make_pair(node_index, weight));
		if(!result2.second) {
			weight_[vertex_index][node_index] += weight;
		}
	}
}
void SkinnedMesh::clearWeight(ofIndexType vertex_index)
{
	weight_.erase(vertex_index);
}
void SkinnedMesh::clearWeight()
{
	weight_.clear();
}

void SkinnedMesh::refreshWeightAutomatic(float distance_max, float strength, int num_joint_max)
{
	clearWeight();
	for(int i = 0, num = getNumVertices(); i < num; ++i) {
		const ofPoint &point = getVertex(i);
		typedef pair<int,float> value_type;
		vector<value_type> score;
		for(int j = 0, jnum = original_skeleton_.size(); j < jnum; ++j) {
			const ofNode &node = original_skeleton_[j];
			float distance = distance_max-node.getGlobalPosition().distance(point);
			if(distance > 0) {
				distance = pow(distance, strength);
				score.emplace_back(make_pair(j, distance));
			}
		};
		if(score.empty()) continue;
		sort(score.begin(), score.end(), [](const value_type &a, const value_type &b) {
			return a.second > b.second;
		});
		auto end_it = 0 < num_joint_max ? score.begin()+min<int>(num_joint_max,score.size()) : score.end();
		float sum = accumulate(score.begin(), end_it, 0.f, [](float sum, const value_type &v) { return sum+v.second; });
		for_each(score.begin(), end_it, [this,&i,&sum](const value_type &v) {
			addWeight(i, v.first, v.second/sum);
		});
	}
}
void SkinnedMesh::refreshMesh()
{
	deformed_ = *this;
}
void SkinnedMesh::update()
{
	for_each(weight_.begin(), weight_.end(), [this](const pair<ofIndexType, unordered_map<ofIndexType,float>> &it) {
		int vertex_index = it.first;
		ofPoint vertex(0,0,0);
		ofVec3f normal(0,0,0);
		for_each(it.second.begin(), it.second.end(), [this,&vertex,&normal,&vertex_index](const pair<ofIndexType,float> w) {
			auto &deform = skeleton_[w.first];
			auto &original = original_skeleton_[w.first];
			auto &&transformMat = original.getGlobalTransformMatrix().getInverse() * deform->getGlobalTransformMatrix();
			vertex += transformMat.preMult(getVertex(vertex_index)) * w.second;
			if(hasNormals()) {
				normal += transformMat.getRotate() * getNormal(vertex_index) * w.second;
			}
		});
		deformed_.setVertex(vertex_index, vertex);
		if(deformed_.hasNormals()) {
			deformed_.setNormal(vertex_index, normal);
		}
	});
}
<commit_msg>bug fix<commit_after>//
//  ofxSkinMesh.cpp
//
//  Created by Iwatani Nariaki on 2016/07/13.
//
//

#include "ofxSkinnedMesh.h"
#include <numeric>

using namespace ofx::skinnedmesh;
using namespace std;

void SkinnedMesh::setSkeleton(const vector<shared_ptr<ofNode>> &skeleton)
{
	skeleton_.clear();
	original_skeleton_.clear();
	for_each(skeleton.begin(), skeleton.end(), [this](const shared_ptr<ofNode> &s) {
		skeleton_.emplace_back(s);
		original_skeleton_.emplace_back(*s);
		original_skeleton_.back().clearParent(true);
	});
}

void SkinnedMesh::addWeight(ofIndexType vertex_index, ofIndexType node_index, float weight)
{
	auto result = weight_.insert(make_pair(vertex_index, unordered_map<ofIndexType,float>()));
	if(result.second) {
		result.first->second.insert(make_pair(node_index, weight));
	}
	else {
		auto result2 = weight_[vertex_index].insert(make_pair(node_index, weight));
		if(!result2.second) {
			weight_[vertex_index][node_index] += weight;
		}
	}
}
void SkinnedMesh::clearWeight(ofIndexType vertex_index)
{
	weight_.erase(vertex_index);
}
void SkinnedMesh::clearWeight()
{
	weight_.clear();
}

void SkinnedMesh::refreshWeightAutomatic(float distance_max, float strength, int num_joint_max)
{
	clearWeight();
	for(int i = 0, num = getNumVertices(); i < num; ++i) {
		const ofPoint &point = getVertex(i);
		typedef pair<int,float> value_type;
		vector<value_type> score;
		for(int j = 0, jnum = original_skeleton_.size(); j < jnum; ++j) {
			const ofNode &node = original_skeleton_[j];
			float distance = distance_max-node.getGlobalPosition().distance(point);
			if(distance > 0) {
				distance = pow(distance, strength);
				score.emplace_back(make_pair(j, distance));
			}
		};
		if(score.empty()) continue;
		sort(score.begin(), score.end(), [](const value_type &a, const value_type &b) {
			return a.second > b.second;
		});
		auto end_it = 0 < num_joint_max ? score.begin()+min<int>(num_joint_max,score.size()) : score.end();
		float sum = accumulate(score.begin(), end_it, 0.f, [](float sum, const value_type &v) { return sum+v.second; });
		for_each(score.begin(), end_it, [this,&i,&sum](const value_type &v) {
			addWeight(i, v.first, v.second/sum);
		});
	}
}
void SkinnedMesh::refreshMesh()
{
	deformed_ = *this;
}
void SkinnedMesh::update()
{
	for_each(weight_.begin(), weight_.end(), [this](const pair<ofIndexType, unordered_map<ofIndexType,float>> &it) {
		int vertex_index = it.first;
		ofPoint vertex(0,0,0);
		ofVec3f normal(0,0,0);
		for_each(it.second.begin(), it.second.end(), [this,&vertex,&normal,&vertex_index](const pair<ofIndexType,float> w) {
			auto &deform = skeleton_[w.first];
			auto &original = original_skeleton_[w.first];
			auto &&transformMat = original.getGlobalTransformMatrix().getInverse() * deform->getGlobalTransformMatrix();
			vertex += transformMat.preMult(getVertex(vertex_index)) * w.second;
			if(hasNormals()) {
				normal += transformMat.getRotate() * getNormal(vertex_index) * w.second;
			}
		});
		deformed_.setVertex(vertex_index, vertex);
		if(deformed_.hasNormals()) {
			deformed_.setNormal(vertex_index, normal);
		}
	});
}
<|endoftext|>


<commit_before>/*
MIT License

Copyright (c) 2016 Susumu Yamazaki

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <Zackernel.h>
#include <Arduino.h>

void Zackernel::init(bool isMicros) {
  Schedule::init(isMicros);
}

void printQueue() {
  Serial.print("q:");
  for(Schedule *s = Schedule::first(); !(s->isEnd()); s = s->next()) {
     Serial.print(s->delayTime());
     Serial.print(',');
  }
  Serial.print('\n');
}

void dispatch() {
  while(!Schedule::isEmpty()) {
    Schedule *s = Schedule::pull();
    s->wait();
    s->call();
    delete s;
  }
}

void sleep(unsigned long time, vl::Func<void(void)> func) {
  Schedule::add(time, func);
  dispatch();
}

void fork(vl::Func<void(void)> func1, vl::Func<void(void)> func2) {
  Schedule::add(0, func1);
  Schedule::add(0, func2);
  dispatch();
}

void zLoop(vl::Func<void(void)> func) {
  Schedule::add(0, func);
  //sleep(0, zLoop);
}

<commit_msg>delete body of zLoop<commit_after>/*
MIT License

Copyright (c) 2016 Susumu Yamazaki

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <Zackernel.h>
#include <Arduino.h>

void Zackernel::init(bool isMicros) {
  Schedule::init(isMicros);
}

void printQueue() {
  Serial.print("q:");
  for(Schedule *s = Schedule::first(); !(s->isEnd()); s = s->next()) {
     Serial.print(s->delayTime());
     Serial.print(',');
  }
  Serial.print('\n');
}

void dispatch() {
  while(!Schedule::isEmpty()) {
    Schedule *s = Schedule::pull();
    s->wait();
    s->call();
    delete s;
  }
}

void sleep(unsigned long time, vl::Func<void(void)> func) {
  Schedule::add(time, func);
  dispatch();
}

void fork(vl::Func<void(void)> func1, vl::Func<void(void)> func2) {
  Schedule::add(0, func1);
  Schedule::add(0, func2);
  dispatch();
}

void zLoop(vl::Func<void(void)> func) {
}

<|endoftext|>


<commit_before>#pragma once

#include <eosio/chain/transaction_metadata.hpp>
#include <eosio/chain/block_state.hpp>
#include <eosio/chain/exceptions.hpp>

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>

namespace fc {
  inline std::size_t hash_value( const fc::sha256& v ) {
     return v._hash[3];
  }
}

namespace eosio { namespace chain {

using namespace boost::multi_index;

enum class trx_enum_type {
   unknown = 0,
   persisted = 1,
   forked = 2,
   aborted = 3
};

struct unapplied_transaction {
   const transaction_metadata_ptr trx_meta;
   const fc::time_point           expiry;
   trx_enum_type                  trx_type = trx_enum_type::unknown;

   const transaction_id_type& id()const { return trx_meta->id(); }

   unapplied_transaction(const unapplied_transaction&) = delete;
   unapplied_transaction() = delete;
   unapplied_transaction& operator=(const unapplied_transaction&) = delete;
   unapplied_transaction(unapplied_transaction&&) = default;
};

/**
 * Track unapplied transactions for persisted, forked blocks, and aborted blocks.
 * Persisted are first so that they can be applied in each block until expired.
 */
class unapplied_transaction_queue {
public:
   enum class process_mode {
      non_speculative,           // HEAD, READ_ONLY, IRREVERSIBLE
      speculative_non_producer,  // will never produce
      speculative_producer       // can produce
   };

private:
   struct by_trx_id;
   struct by_type;
   struct by_expiry;

   typedef multi_index_container< unapplied_transaction,
      indexed_by<
         hashed_unique< tag<by_trx_id>,
               const_mem_fun<unapplied_transaction, const transaction_id_type&, &unapplied_transaction::id>
         >,
         ordered_non_unique< tag<by_type>, member<unapplied_transaction, trx_enum_type, &unapplied_transaction::trx_type> >,
         ordered_non_unique< tag<by_expiry>, member<unapplied_transaction, const fc::time_point, &unapplied_transaction::expiry> >
      >
   > unapplied_trx_queue_type;

   unapplied_trx_queue_type queue;
   process_mode mode = process_mode::speculative_producer;

public:

   void set_mode( process_mode new_mode ) {
      if( new_mode != mode ) {
         FC_ASSERT( empty(), "set_mode, queue required to be empty" );
      }
      mode = new_mode;
   }

   bool empty() const {
      return queue.empty();
   }

   size_t size() const {
      return queue.size();
   }

   void clear() {
      queue.clear();
   }

   bool contains_persisted()const {
      return queue.get<by_type>().find( trx_enum_type::persisted ) != queue.get<by_type>().end();
   }

   bool is_persisted(const transaction_metadata_ptr& trx)const {
      auto itr = queue.get<by_trx_id>().find( trx->id() );
      if( itr == queue.get<by_trx_id>().end() ) return false;
      return itr->trx_type == trx_enum_type::persisted;
   }

   transaction_metadata_ptr get_trx( const transaction_id_type& id ) const {
      auto itr = queue.get<by_trx_id>().find( id );
      if( itr == queue.get<by_trx_id>().end() ) return {};
      return itr->trx_meta;
   }

   template <typename Func>
   bool clear_expired( const time_point& pending_block_time, const time_point& deadline, Func&& callback ) {
      auto& persisted_by_expiry = queue.get<by_expiry>();
      while(!persisted_by_expiry.empty() && persisted_by_expiry.begin()->expiry <= pending_block_time) {
         if (deadline <= fc::time_point::now()) {
            return false;
         }
         callback( persisted_by_expiry.begin()->id(), persisted_by_expiry.begin()->trx_type );
         persisted_by_expiry.erase( persisted_by_expiry.begin() );
      }
      return true;
   }

   void clear_applied( const block_state_ptr& bs ) {
      if( empty() ) return;
      auto& idx = queue.get<by_trx_id>();
      for( const auto& receipt : bs->block->transactions ) {
         if( receipt.trx.contains<packed_transaction>() ) {
            const auto& pt = receipt.trx.get<packed_transaction>();
            auto itr = queue.get<by_trx_id>().find( pt.id() );
            if( itr != queue.get<by_trx_id>().end() ) {
               if( itr->trx_type != trx_enum_type::persisted ) {
                  idx.erase( pt.id() );
               }
            }
         }
      }
   }

   void add_forked( const branch_type& forked_branch ) {
      if( mode == process_mode::non_speculative || mode == process_mode::speculative_non_producer ) return;
      // forked_branch is in reverse order
      for( auto ritr = forked_branch.rbegin(), rend = forked_branch.rend(); ritr != rend; ++ritr ) {
         const block_state_ptr& bsptr = *ritr;
         for( auto itr = bsptr->trxs_metas().begin(), end = bsptr->trxs_metas().end(); itr != end; ++itr ) {
            const auto& trx = *itr;
            fc::time_point expiry = trx->packed_trx()->expiration();
            queue.insert( { trx, expiry, trx_enum_type::forked } );
         }
      }
   }

   void add_aborted( std::vector<transaction_metadata_ptr> aborted_trxs ) {
      if( mode == process_mode::non_speculative || mode == process_mode::speculative_non_producer ) return;
      for( auto& trx : aborted_trxs ) {
         fc::time_point expiry = trx->packed_trx()->expiration();
         queue.insert( { std::move( trx ), expiry, trx_enum_type::aborted } );
      }
   }

   void add_persisted( const transaction_metadata_ptr& trx ) {
      if( mode == process_mode::non_speculative ) return;
      auto itr = queue.get<by_trx_id>().find( trx->id() );
      if( itr == queue.get<by_trx_id>().end() ) {
         fc::time_point expiry = trx->packed_trx()->expiration();
         queue.insert( { trx, expiry, trx_enum_type::persisted } );
      } else if( itr->trx_type != trx_enum_type::persisted ) {
         queue.get<by_trx_id>().modify( itr, [](auto& un){
            un.trx_type = trx_enum_type::persisted;
         } );
      }
   }

   using iterator = unapplied_trx_queue_type::index<by_type>::type::iterator;

   iterator begin() { return queue.get<by_type>().begin(); }
   iterator end() { return queue.get<by_type>().end(); }

   iterator persisted_begin() { return queue.get<by_type>().lower_bound( trx_enum_type::persisted ); }
   iterator persisted_end() { return queue.get<by_type>().upper_bound( trx_enum_type::persisted ); }

   iterator erase( iterator itr ) { return queue.get<by_type>().erase( itr ); }

};

} } //eosio::chain
<commit_msg>Add next_func to unapplied_transaction_queue in preparation of adding pending_incoming_transactions to queue<commit_after>#pragma once

#include <eosio/chain/transaction_metadata.hpp>
#include <eosio/chain/trace.hpp>
#include <eosio/chain/block_state.hpp>
#include <eosio/chain/exceptions.hpp>

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>

namespace fc {
  inline std::size_t hash_value( const fc::sha256& v ) {
     return v._hash[3];
  }
}

namespace eosio { namespace chain {

using namespace boost::multi_index;

enum class trx_enum_type {
   unknown = 0,
   persisted = 1,
   forked = 2,
   aborted = 3
};

using next_func_t = std::function<void(const fc::static_variant<fc::exception_ptr, transaction_trace_ptr>&)>;

struct unapplied_transaction {
   const transaction_metadata_ptr trx_meta;
   const fc::time_point           expiry;
   trx_enum_type                  trx_type = trx_enum_type::unknown;
   next_func_t                    next;

   const transaction_id_type& id()const { return trx_meta->id(); }

   unapplied_transaction(const unapplied_transaction&) = delete;
   unapplied_transaction() = delete;
   unapplied_transaction& operator=(const unapplied_transaction&) = delete;
   unapplied_transaction(unapplied_transaction&&) = default;
};

/**
 * Track unapplied transactions for persisted, forked blocks, and aborted blocks.
 * Persisted are first so that they can be applied in each block until expired.
 */
class unapplied_transaction_queue {
public:
   enum class process_mode {
      non_speculative,           // HEAD, READ_ONLY, IRREVERSIBLE
      speculative_non_producer,  // will never produce
      speculative_producer       // can produce
   };

private:
   struct by_trx_id;
   struct by_type;
   struct by_expiry;

   typedef multi_index_container< unapplied_transaction,
      indexed_by<
         hashed_unique< tag<by_trx_id>,
               const_mem_fun<unapplied_transaction, const transaction_id_type&, &unapplied_transaction::id>
         >,
         ordered_non_unique< tag<by_type>, member<unapplied_transaction, trx_enum_type, &unapplied_transaction::trx_type> >,
         ordered_non_unique< tag<by_expiry>, member<unapplied_transaction, const fc::time_point, &unapplied_transaction::expiry> >
      >
   > unapplied_trx_queue_type;

   unapplied_trx_queue_type queue;
   process_mode mode = process_mode::speculative_producer;

public:

   void set_mode( process_mode new_mode ) {
      if( new_mode != mode ) {
         FC_ASSERT( empty(), "set_mode, queue required to be empty" );
      }
      mode = new_mode;
   }

   bool empty() const {
      return queue.empty();
   }

   size_t size() const {
      return queue.size();
   }

   void clear() {
      queue.clear();
   }

   bool contains_persisted()const {
      return queue.get<by_type>().find( trx_enum_type::persisted ) != queue.get<by_type>().end();
   }

   bool is_persisted(const transaction_metadata_ptr& trx)const {
      auto itr = queue.get<by_trx_id>().find( trx->id() );
      if( itr == queue.get<by_trx_id>().end() ) return false;
      return itr->trx_type == trx_enum_type::persisted;
   }

   transaction_metadata_ptr get_trx( const transaction_id_type& id ) const {
      auto itr = queue.get<by_trx_id>().find( id );
      if( itr == queue.get<by_trx_id>().end() ) return {};
      return itr->trx_meta;
   }

   template <typename Func>
   bool clear_expired( const time_point& pending_block_time, const time_point& deadline, Func&& callback ) {
      auto& persisted_by_expiry = queue.get<by_expiry>();
      while( !persisted_by_expiry.empty() ) {
         const auto& itr = persisted_by_expiry.begin();
         if( itr->expiry <= pending_block_time ) {
            break;
         }
         if( deadline <= fc::time_point::now() ) {
            return false;
         }
         callback( itr->id(), itr->trx_type );
         if( itr->next ) {
            itr->next( std::static_pointer_cast<fc::exception>(
                  std::make_shared<expired_tx_exception>(
                        FC_LOG_MESSAGE( error, "expired transaction ${id}, expiration ${e}, block time ${bt}",
                                        ("id", itr->id())("e", itr->trx_meta->packed_trx()->expiration())
                                        ("bt", pending_block_time) ) ) ) );
         }

         persisted_by_expiry.erase( itr );
      }
      return true;
   }

   void clear_applied( const block_state_ptr& bs ) {
      if( empty() ) return;
      auto& idx = queue.get<by_trx_id>();
      for( const auto& receipt : bs->block->transactions ) {
         if( receipt.trx.contains<packed_transaction>() ) {
            const auto& pt = receipt.trx.get<packed_transaction>();
            auto itr = queue.get<by_trx_id>().find( pt.id() );
            if( itr != queue.get<by_trx_id>().end() ) {
               if( itr->trx_type != trx_enum_type::persisted ) {
                  idx.erase( pt.id() );
               }
            }
         }
      }
   }

   void add_forked( const branch_type& forked_branch ) {
      if( mode == process_mode::non_speculative || mode == process_mode::speculative_non_producer ) return;
      // forked_branch is in reverse order
      for( auto ritr = forked_branch.rbegin(), rend = forked_branch.rend(); ritr != rend; ++ritr ) {
         const block_state_ptr& bsptr = *ritr;
         for( auto itr = bsptr->trxs_metas().begin(), end = bsptr->trxs_metas().end(); itr != end; ++itr ) {
            const auto& trx = *itr;
            fc::time_point expiry = trx->packed_trx()->expiration();
            queue.insert( { trx, expiry, trx_enum_type::forked } );
         }
      }
   }

   void add_aborted( std::vector<transaction_metadata_ptr> aborted_trxs ) {
      if( mode == process_mode::non_speculative || mode == process_mode::speculative_non_producer ) return;
      for( auto& trx : aborted_trxs ) {
         fc::time_point expiry = trx->packed_trx()->expiration();
         queue.insert( { std::move( trx ), expiry, trx_enum_type::aborted } );
      }
   }

   void add_persisted( const transaction_metadata_ptr& trx ) {
      if( mode == process_mode::non_speculative ) return;
      auto itr = queue.get<by_trx_id>().find( trx->id() );
      if( itr == queue.get<by_trx_id>().end() ) {
         fc::time_point expiry = trx->packed_trx()->expiration();
         queue.insert( { trx, expiry, trx_enum_type::persisted } );
      } else if( itr->trx_type != trx_enum_type::persisted ) {
         queue.get<by_trx_id>().modify( itr, [](auto& un){
            un.trx_type = trx_enum_type::persisted;
         } );
      }
   }

   using iterator = unapplied_trx_queue_type::index<by_type>::type::iterator;

   iterator begin() { return queue.get<by_type>().begin(); }
   iterator end() { return queue.get<by_type>().end(); }

   iterator persisted_begin() { return queue.get<by_type>().lower_bound( trx_enum_type::persisted ); }
   iterator persisted_end() { return queue.get<by_type>().upper_bound( trx_enum_type::persisted ); }

   iterator erase( iterator itr ) { return queue.get<by_type>().erase( itr ); }

};

} } //eosio::chain
<|endoftext|>


<commit_before>/*
Copyright (c) 2016 Ravi Peters

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "compute_ma_processing.h"

#include <limits>

#ifdef VERBOSEPRINT
#include <chrono>
#include <iostream>
#endif

#ifdef WITH_OPENMP
#include <omp.h>
#endif

#ifdef VERBOSEPRINT
typedef std::chrono::high_resolution_clock Clock;
#endif

//==============================
//   COMPUTE MA
//==============================

const Scalar delta_convergance = 1E-5f;
const unsigned int iteration_limit = 30;
const Vector3 nanPoint(std::numeric_limits<Scalar>::quiet_NaN(), std::numeric_limits<Scalar>::quiet_NaN(), std::numeric_limits<Scalar>::quiet_NaN());

inline Scalar compute_radius(const Vector3 &p, const Vector3 &n, const Vector3 &q) {
   // Compute radius of the ball that touches points p and q and whose center falls on the normal n from p
   Scalar d = (p - q).norm();
   Scalar cos_theta = n.dot(p - q) / d;
   return Scalar(d / (2 * cos_theta));
}

inline Scalar cos_angle(const Vector3 p, const Vector3 q) {
   // Calculate the cosine of angle between vector p and q, see http://en.wikipedia.org/wiki/Law_of_cosines#Vector_formulation
   Scalar result = p.dot(q) / (p.norm() * q.norm());
   if (result > 1) return 1;
   else if (result < -1) return -1;
   return result;
}

ma_result sb_point(const ma_parameters &input_parameters, const Vector3 &p, const Vector3 &n, pcl::search::KdTree<Point>::Ptr kd_tree) {
   // Calculate a medial ball for a given oriented point using the shrinking ball algorithm,
   // see https://3d.bk.tudelft.nl/rypeters/pdfs/16candg.pdf section 3.2 for details
   unsigned int j = 0;
   Scalar r = input_parameters.initial_radius, d;
   Vector3 q, c_next;
   int qidx = -1, qidx_next;
   Point c; c.getVector3fMap() = p - n * r;

   // Results from our search
   std::vector<int> k_indices(2);
   std::vector<Scalar> k_distances(2);

   while (true) {

      // find closest point to c
      kd_tree->nearestKSearch(c, 1, k_indices, k_distances);

      qidx_next = k_indices[0];
      q = kd_tree->getInputCloud()->at(qidx_next).getVector3fMap();
      d = k_distances[0];

      // This should handle all (special) cases where we want to break the loop
      // - normal case when ball no longer shrinks
      // - the case where q==p
      // - any duplicate point cases
      if ((d >= (r*r)-delta_convergance) || (p==q))
            break;

      // compute new radius
      r = compute_radius(p, n, q);

      // compute next ball center
      c_next = p - n * r;

      // denoising
      if (input_parameters.denoise_preserve || input_parameters.denoise_planar) {
         Scalar a = cos_angle(p - c_next, q - c_next);
         Scalar separation_angle = std::acos(a);

         if (input_parameters.denoise_preserve && (separation_angle < input_parameters.denoise_preserve && j>0 && r > (q - p).norm())) {
            break;
         }
         if (input_parameters.denoise_planar && (separation_angle < input_parameters.denoise_planar && j == 0)) {
            c.getVector3fMap() = input_parameters.nan_for_initr ? nanPoint : p - n * input_parameters.initial_radius;
            break;
         }
      }

      // stop iteration if this looks like an infinite loop:
      if (j > iteration_limit)
         break;

      c.getVector3fMap() = c_next;
      qidx = qidx_next;
      j++;
   }

   return{ c, qidx };
}

void sb_points(ma_parameters &input_parameters, ma_data &madata, bool inner = 1) {
   // outer mat should be written to second half of ma_coords/ma_qidx
   size_t offset = 0;
   if (inner == false)
      offset = madata.coords->size();

#pragma omp parallel for
   for (int i = 0; i < madata.coords->size(); i++) {
      Vector3 p = (*madata.coords)[i].getVector3fMap();
      Vector3 n;
      if (inner)
         n = (*madata.normals)[i].getNormalVector3fMap();
      else
         n = -(*madata.normals)[i].getNormalVector3fMap();

      ma_result r = sb_point(input_parameters, p, n, madata.kd_tree);

      (*madata.ma_coords)[i + offset] = r.c;
      madata.ma_qidx[i + offset] = r.qidx;
   }
}

void compute_masb_points(ma_parameters &input_parameters, ma_data &madata) {
#ifdef VERBOSEPRINT
   auto start_time = Clock::now();
#endif

   if (!madata.kd_tree) {
      madata.kd_tree.reset(new pcl::search::KdTree<Point>());
      madata.kd_tree->setInputCloud(madata.coords);
#ifdef VERBOSEPRINT
      auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - start_time);
      std::cout << "Constructed kd-tree in " << elapsed_time.count() << " ms" << std::endl;
      start_time = Clock::now();
#endif
   }

   // Inside processing
   sb_points(input_parameters, madata, 1);
#ifdef VERBOSEPRINT
   auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - start_time);
   std::cout << "Done shrinking interior balls, took " << elapsed_time.count() << " ms" << std::endl;
   start_time = Clock::now();
#endif

   // Outside processing
   sb_points(input_parameters, madata, 0);
#ifdef VERBOSEPRINT
   elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - start_time);
   std::cout << "Done shrinking exterior balls, took " << elapsed_time.count() << " ms" << std::endl;
#endif
}

<commit_msg>properly multiple convergence threshold in convergence test<commit_after>/*
Copyright (c) 2016 Ravi Peters

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "compute_ma_processing.h"

#include <limits>

#ifdef VERBOSEPRINT
#include <chrono>
#include <iostream>
#endif

#ifdef WITH_OPENMP
#include <omp.h>
#endif

#ifdef VERBOSEPRINT
typedef std::chrono::high_resolution_clock Clock;
#endif

//==============================
//   COMPUTE MA
//==============================

const Scalar delta_convergance = 1E-5f;
const unsigned int iteration_limit = 30;
const Vector3 nanPoint(std::numeric_limits<Scalar>::quiet_NaN(), std::numeric_limits<Scalar>::quiet_NaN(), std::numeric_limits<Scalar>::quiet_NaN());

inline Scalar compute_radius(const Vector3 &p, const Vector3 &n, const Vector3 &q) {
   // Compute radius of the ball that touches points p and q and whose center falls on the normal n from p
   Scalar d = (p - q).norm();
   Scalar cos_theta = n.dot(p - q) / d;
   return Scalar(d / (2 * cos_theta));
}

inline Scalar cos_angle(const Vector3 p, const Vector3 q) {
   // Calculate the cosine of angle between vector p and q, see http://en.wikipedia.org/wiki/Law_of_cosines#Vector_formulation
   Scalar result = p.dot(q) / (p.norm() * q.norm());
   if (result > 1) return 1;
   else if (result < -1) return -1;
   return result;
}

ma_result sb_point(const ma_parameters &input_parameters, const Vector3 &p, const Vector3 &n, pcl::search::KdTree<Point>::Ptr kd_tree) {
   // Calculate a medial ball for a given oriented point using the shrinking ball algorithm,
   // see https://3d.bk.tudelft.nl/rypeters/pdfs/16candg.pdf section 3.2 for details
   unsigned int j = 0;
   Scalar r = input_parameters.initial_radius, d;
   Vector3 q, c_next;
   int qidx = -1, qidx_next;
   Point c; c.getVector3fMap() = p - n * r;

   // Results from our search
   std::vector<int> k_indices(2);
   std::vector<Scalar> k_distances(2);

   while (true) {

      // find closest point to c
      kd_tree->nearestKSearch(c, 1, k_indices, k_distances);

      qidx_next = k_indices[0];
      q = kd_tree->getInputCloud()->at(qidx_next).getVector3fMap();
      d = k_distances[0];

      // This should handle all (special) cases where we want to break the loop
      // - normal case when ball no longer shrinks
      // - the case where q==p
      // - any duplicate point cases
      if ((d >= (r-delta_convergance*r-delta_convergance)) || (p==q))
            break;

      // compute new radius
      r = compute_radius(p, n, q);

      // compute next ball center
      c_next = p - n * r;

      // denoising
      if (input_parameters.denoise_preserve || input_parameters.denoise_planar) {
         Scalar a = cos_angle(p - c_next, q - c_next);
         Scalar separation_angle = std::acos(a);

         if (input_parameters.denoise_preserve && (separation_angle < input_parameters.denoise_preserve && j>0 && r > (q - p).norm())) {
            break;
         }
         if (input_parameters.denoise_planar && (separation_angle < input_parameters.denoise_planar && j == 0)) {
            c.getVector3fMap() = input_parameters.nan_for_initr ? nanPoint : p - n * input_parameters.initial_radius;
            break;
         }
      }

      // stop iteration if this looks like an infinite loop:
      if (j > iteration_limit)
         break;

      c.getVector3fMap() = c_next;
      qidx = qidx_next;
      j++;
   }

   return{ c, qidx };
}

void sb_points(ma_parameters &input_parameters, ma_data &madata, bool inner = 1) {
   // outer mat should be written to second half of ma_coords/ma_qidx
   size_t offset = 0;
   if (inner == false)
      offset = madata.coords->size();

#pragma omp parallel for
   for (int i = 0; i < madata.coords->size(); i++) {
      Vector3 p = (*madata.coords)[i].getVector3fMap();
      Vector3 n;
      if (inner)
         n = (*madata.normals)[i].getNormalVector3fMap();
      else
         n = -(*madata.normals)[i].getNormalVector3fMap();

      ma_result r = sb_point(input_parameters, p, n, madata.kd_tree);

      (*madata.ma_coords)[i + offset] = r.c;
      madata.ma_qidx[i + offset] = r.qidx;
   }
}

void compute_masb_points(ma_parameters &input_parameters, ma_data &madata) {
#ifdef VERBOSEPRINT
   auto start_time = Clock::now();
#endif

   if (!madata.kd_tree) {
      madata.kd_tree.reset(new pcl::search::KdTree<Point>());
      madata.kd_tree->setInputCloud(madata.coords);
#ifdef VERBOSEPRINT
      auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - start_time);
      std::cout << "Constructed kd-tree in " << elapsed_time.count() << " ms" << std::endl;
      start_time = Clock::now();
#endif
   }

   // Inside processing
   sb_points(input_parameters, madata, 1);
#ifdef VERBOSEPRINT
   auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - start_time);
   std::cout << "Done shrinking interior balls, took " << elapsed_time.count() << " ms" << std::endl;
   start_time = Clock::now();
#endif

   // Outside processing
   sb_points(input_parameters, madata, 0);
#ifdef VERBOSEPRINT
   elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - start_time);
   std::cout << "Done shrinking exterior balls, took " << elapsed_time.count() << " ms" << std::endl;
#endif
}

<|endoftext|>