<commit_before>// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
#include "meshoptimizer.h"

#include <assert.h>
#include <float.h>
#include <string.h>

// This work is based on:
// Fabian Giesen. Decoding Morton codes. 2009
namespace meshopt
{

// "Insert" two 0 bits after each of the 10 low bits of x
unsigned int part1By2(unsigned int x)
{
	x &= 0x000003ff;                  // x = ---- ---- ---- ---- ---- --98 7654 3210
	x = (x ^ (x << 16)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210
	x = (x ^ (x << 8)) & 0x0300f00f;  // x = ---- --98 ---- ---- 7654 ---- ---- 3210
	x = (x ^ (x << 4)) & 0x030c30c3;  // x = ---- --98 ---- 76-- --54 ---- 32-- --10
	x = (x ^ (x << 2)) & 0x09249249;  // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0
	return x;
}

static void computeOrder(unsigned int* result, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride)
{
	size_t vertex_stride_float = vertex_positions_stride / sizeof(float);

	float minv[3] = {FLT_MAX, FLT_MAX, FLT_MAX};
	float maxv[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};

	for (size_t i = 0; i < vertex_count; ++i)
	{
		const float* v = vertex_positions_data + i * vertex_stride_float;

		for (int j = 0; j < 3; ++j)
		{
			float vj = v[j];

			minv[j] = minv[j] > vj ? vj : minv[j];
			maxv[j] = maxv[j] < vj ? vj : maxv[j];
		}
	}

	float extent = 0.f;

	extent = (maxv[0] - minv[0]) < extent ? extent : (maxv[0] - minv[0]);
	extent = (maxv[1] - minv[1]) < extent ? extent : (maxv[1] - minv[1]);
	extent = (maxv[2] - minv[2]) < extent ? extent : (maxv[2] - minv[2]);

	float scale = extent == 0 ? 0.f : 1.f / extent;

	for (size_t i = 0; i < vertex_count; ++i)
	{
		const float* v = vertex_positions_data + i * vertex_stride_float;

		int x = int((v[0] - minv[0]) * scale * 1023.f + 0.5f);
		int y = int((v[1] - minv[1]) * scale * 1023.f + 0.5f);
		int z = int((v[2] - minv[2]) * scale * 1023.f + 0.5f);

		result[i] = part1By2(x) | (part1By2(y) << 1) | (part1By2(z) << 2);
	}
}

// TODO: is it faster to use [3][1024]?
static void computeHistogram(unsigned int (&hist)[1024][3], const unsigned int* data, size_t count)
{
	memset(hist, 0, sizeof(hist));

	for (size_t i = 0; i < count; ++i)
	{
		unsigned int id = data[i];

		hist[(id >> 0) & 1023][0]++;
		hist[(id >> 10) & 1023][1]++;
		hist[(id >> 20) & 1023][2]++;
	}

	unsigned int sumx = 0, sumy = 0, sumz = 0;

	for (int i = 0; i < 1024; ++i)
	{
		unsigned int hx = hist[i][0], hy = hist[i][1], hz = hist[i][2];

		hist[i][0] = sumx;
		hist[i][1] = sumy;
		hist[i][2] = sumz;

		sumx += hx;
		sumy += hy;
		sumz += hz;
	}

	assert(sumx == count && sumy == count && sumz == count);
}

static void radixPass(unsigned int* destination, const unsigned int* source, const unsigned int* keys, size_t count, unsigned int (&hist)[1024][3], int pass)
{
	int bitoff = pass * 10;

	for (size_t i = 0; i < count; ++i)
	{
		unsigned int id = (keys[source[i]] >> bitoff) & 1023;

		destination[hist[id][pass]++] = source[i];
	}
}

} // namespace meshopt

void meshopt_spatialSortRemap(unsigned int* destination, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
{
	using namespace meshopt;

	assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256);
	assert(vertex_positions_stride % sizeof(float) == 0);

	meshopt_Allocator allocator;

	unsigned int* keys = allocator.allocate<unsigned int>(vertex_count);
	computeOrder(keys, vertex_positions, vertex_count, vertex_positions_stride);

	unsigned int hist[1024][3];
	computeHistogram(hist, keys, vertex_count);

	unsigned int* scratch = allocator.allocate<unsigned int>(vertex_count);

	for (size_t i = 0; i < vertex_count; ++i)
		destination[i] = unsigned(i);

	// 3-pass radix sort computes the resulting order into scratch
	radixPass(scratch, destination, keys, vertex_count, hist, 0);
	radixPass(destination, scratch, keys, vertex_count, hist, 1);
	radixPass(scratch, destination, keys, vertex_count, hist, 2);

	// since our remap table is mapping old=>new, we need to reverse it
	for (size_t i = 0; i < vertex_count; ++i)
		destination[scratch[i]] = unsigned(i);
}

void meshopt_spatialSortTriangles(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
{
	using namespace meshopt;

	assert(index_count % 3 == 0);
	assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256);
	assert(vertex_positions_stride % sizeof(float) == 0);

	(void)vertex_count;

	meshopt_Allocator allocator;

	float* centroids = allocator.allocate<float>(index_count);

	size_t vertex_stride_float = vertex_positions_stride / sizeof(float);

	for (size_t i = 0; i < index_count; i += 3)
	{
		unsigned int a = indices[i + 0], b = indices[i + 1], c = indices[i + 2];
		assert(a < vertex_count && b < vertex_count && c < vertex_count);

		const float* va = vertex_positions + a * vertex_stride_float;
		const float* vb = vertex_positions + b * vertex_stride_float;
		const float* vc = vertex_positions + c * vertex_stride_float;

		centroids[i + 0] = (va[0] + vb[0] + vc[0]) / 3.f;
		centroids[i + 1] = (va[1] + vb[1] + vc[1]) / 3.f;
		centroids[i + 2] = (va[2] + vb[2] + vc[2]) / 3.f;
	}

	size_t face_count = index_count / 3;

	unsigned int* remap = allocator.allocate<unsigned int>(face_count);

	meshopt_spatialSortRemap(remap, centroids, face_count, sizeof(float) * 3);

	// support in-order remap
	if (destination == indices)
	{
		unsigned int* indices_copy = allocator.allocate<unsigned int>(index_count);
		memcpy(indices_copy, indices, index_count * sizeof(unsigned int));
		indices = indices_copy;
	}

	for (size_t i = 0; i < face_count; ++i)
	{
		unsigned int a = indices[i * 3 + 0], b = indices[i * 3 + 1], c = indices[i * 3 + 2];
		unsigned int r = remap[i];

		destination[r * 3 + 0] = a;
		destination[r * 3 + 1] = b;
		destination[r * 3 + 2] = c;
	}
}
<commit_msg>spatial: Minor refactoring<commit_after>// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
#include "meshoptimizer.h"

#include <assert.h>
#include <float.h>
#include <string.h>

// This work is based on:
// Fabian Giesen. Decoding Morton codes. 2009
namespace meshopt
{

// "Insert" two 0 bits after each of the 10 low bits of x
unsigned int part1By2(unsigned int x)
{
	x &= 0x000003ff;                  // x = ---- ---- ---- ---- ---- --98 7654 3210
	x = (x ^ (x << 16)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210
	x = (x ^ (x << 8)) & 0x0300f00f;  // x = ---- --98 ---- ---- 7654 ---- ---- 3210
	x = (x ^ (x << 4)) & 0x030c30c3;  // x = ---- --98 ---- 76-- --54 ---- 32-- --10
	x = (x ^ (x << 2)) & 0x09249249;  // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0
	return x;
}

static void computeOrder(unsigned int* result, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride)
{
	size_t vertex_stride_float = vertex_positions_stride / sizeof(float);

	float minv[3] = {FLT_MAX, FLT_MAX, FLT_MAX};
	float maxv[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};

	for (size_t i = 0; i < vertex_count; ++i)
	{
		const float* v = vertex_positions_data + i * vertex_stride_float;

		for (int j = 0; j < 3; ++j)
		{
			float vj = v[j];

			minv[j] = minv[j] > vj ? vj : minv[j];
			maxv[j] = maxv[j] < vj ? vj : maxv[j];
		}
	}

	float extent = 0.f;

	extent = (maxv[0] - minv[0]) < extent ? extent : (maxv[0] - minv[0]);
	extent = (maxv[1] - minv[1]) < extent ? extent : (maxv[1] - minv[1]);
	extent = (maxv[2] - minv[2]) < extent ? extent : (maxv[2] - minv[2]);

	float scale = extent == 0 ? 0.f : 1.f / extent;

	// generate Morton order based on the position inside a unit cube
	for (size_t i = 0; i < vertex_count; ++i)
	{
		const float* v = vertex_positions_data + i * vertex_stride_float;

		int x = int((v[0] - minv[0]) * scale * 1023.f + 0.5f);
		int y = int((v[1] - minv[1]) * scale * 1023.f + 0.5f);
		int z = int((v[2] - minv[2]) * scale * 1023.f + 0.5f);

		result[i] = part1By2(x) | (part1By2(y) << 1) | (part1By2(z) << 2);
	}
}

static void computeHistogram(unsigned int (&hist)[1024][3], const unsigned int* data, size_t count)
{
	memset(hist, 0, sizeof(hist));

	// compute 3 10-bit histograms in parallel
	for (size_t i = 0; i < count; ++i)
	{
		unsigned int id = data[i];

		hist[(id >> 0) & 1023][0]++;
		hist[(id >> 10) & 1023][1]++;
		hist[(id >> 20) & 1023][2]++;
	}

	unsigned int sumx = 0, sumy = 0, sumz = 0;

	// replace histogram data with prefix histogram sums in-place
	for (int i = 0; i < 1024; ++i)
	{
		unsigned int hx = hist[i][0], hy = hist[i][1], hz = hist[i][2];

		hist[i][0] = sumx;
		hist[i][1] = sumy;
		hist[i][2] = sumz;

		sumx += hx;
		sumy += hy;
		sumz += hz;
	}

	assert(sumx == count && sumy == count && sumz == count);
}

static void radixPass(unsigned int* destination, const unsigned int* source, const unsigned int* keys, size_t count, unsigned int (&hist)[1024][3], int pass)
{
	int bitoff = pass * 10;

	for (size_t i = 0; i < count; ++i)
	{
		unsigned int id = (keys[source[i]] >> bitoff) & 1023;

		destination[hist[id][pass]++] = source[i];
	}
}

} // namespace meshopt

void meshopt_spatialSortRemap(unsigned int* destination, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
{
	using namespace meshopt;

	assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256);
	assert(vertex_positions_stride % sizeof(float) == 0);

	meshopt_Allocator allocator;

	unsigned int* keys = allocator.allocate<unsigned int>(vertex_count);
	computeOrder(keys, vertex_positions, vertex_count, vertex_positions_stride);

	unsigned int hist[1024][3];
	computeHistogram(hist, keys, vertex_count);

	unsigned int* scratch = allocator.allocate<unsigned int>(vertex_count);

	for (size_t i = 0; i < vertex_count; ++i)
		destination[i] = unsigned(i);

	// 3-pass radix sort computes the resulting order into scratch
	radixPass(scratch, destination, keys, vertex_count, hist, 0);
	radixPass(destination, scratch, keys, vertex_count, hist, 1);
	radixPass(scratch, destination, keys, vertex_count, hist, 2);

	// since our remap table is mapping old=>new, we need to reverse it
	for (size_t i = 0; i < vertex_count; ++i)
		destination[scratch[i]] = unsigned(i);
}

void meshopt_spatialSortTriangles(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
{
	using namespace meshopt;

	assert(index_count % 3 == 0);
	assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256);
	assert(vertex_positions_stride % sizeof(float) == 0);

	(void)vertex_count;

	size_t face_count = index_count / 3;
	size_t vertex_stride_float = vertex_positions_stride / sizeof(float);

	meshopt_Allocator allocator;

	float* centroids = allocator.allocate<float>(face_count * 3);

	for (size_t i = 0; i < face_count; ++i)
	{
		unsigned int a = indices[i * 3 + 0], b = indices[i * 3 + 1], c = indices[i * 3 + 2];
		assert(a < vertex_count && b < vertex_count && c < vertex_count);

		const float* va = vertex_positions + a * vertex_stride_float;
		const float* vb = vertex_positions + b * vertex_stride_float;
		const float* vc = vertex_positions + c * vertex_stride_float;

		centroids[i * 3 + 0] = (va[0] + vb[0] + vc[0]) / 3.f;
		centroids[i * 3 + 1] = (va[1] + vb[1] + vc[1]) / 3.f;
		centroids[i * 3 + 2] = (va[2] + vb[2] + vc[2]) / 3.f;
	}

	unsigned int* remap = allocator.allocate<unsigned int>(face_count);

	meshopt_spatialSortRemap(remap, centroids, face_count, sizeof(float) * 3);

	// support in-order remap
	if (destination == indices)
	{
		unsigned int* indices_copy = allocator.allocate<unsigned int>(index_count);
		memcpy(indices_copy, indices, index_count * sizeof(unsigned int));
		indices = indices_copy;
	}

	for (size_t i = 0; i < face_count; ++i)
	{
		unsigned int a = indices[i * 3 + 0], b = indices[i * 3 + 1], c = indices[i * 3 + 2];
		unsigned int r = remap[i];

		destination[r * 3 + 0] = a;
		destination[r * 3 + 1] = b;
		destination[r * 3 + 2] = c;
	}
}
<|endoftext|>


<commit_before>/*
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "Server.hxx"
#include "Config.hxx"
#include "Protocol.hxx"
#include "Parser.hxx"
#include "Builder.hxx"
#include "Prepared.hxx"
#include "mount_list.hxx"
#include "Direct.hxx"
#include "Registry.hxx"
#include "ExitListener.hxx"
#include "event/Event.hxx"
#include "event/Callback.hxx"
#include "util/DeleteDisposer.hxx"
#include "util/ConstBuffer.hxx"
#include "util/StaticArray.hxx"

#include <daemon/log.h>

#include <boost/intrusive/list.hpp>

#include <algorithm>
#include <memory>
#include <map>

#include <unistd.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>

class SpawnServerProcess;

class SpawnFd {
    int fd;

public:
    explicit SpawnFd(int _fd=-1):fd(_fd) {}

    SpawnFd(SpawnFd &&src):fd(src.fd) {
        src.fd = -1;
    }

    ~SpawnFd() {
        if (fd >= 0)
            close(fd);
    }

    SpawnFd &operator=(SpawnFd &&src) {
        std::swap(fd, src.fd);
        return *this;
    }

    int Release() {
        int result = fd;
        fd = -1;
        return result;
    }
};

class SpawnFdList {
    ConstBuffer<int> list;

public:
    SpawnFdList(std::nullptr_t n):list(n) {}

    explicit SpawnFdList(ConstBuffer<int> _list)
        :list(_list) {}

    SpawnFdList(SpawnFdList &&src)
        :list(src.list) {
        src.list = nullptr;
    }

    ~SpawnFdList() {
        for (auto fd : list)
            close(fd);
    }

    SpawnFdList &operator=(SpawnFdList &&src) {
        std::swap(list, src.list);
        return *this;
    }

    bool IsEmpty() {
        return list.IsEmpty();
    }

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

    SpawnFd Get() {
        if (IsEmpty())
            throw MalformedSpawnPayloadError();

        return SpawnFd(list.shift());
    }
};

class SpawnServerConnection;

class SpawnServerChild final : public ExitListener {
    SpawnServerConnection &connection;

    const int id;

    const pid_t pid;

    const std::string name;

public:
    explicit SpawnServerChild(SpawnServerConnection &_connection,
                              int _id, pid_t _pid,
                              const char *_name)
        :connection(_connection), id(_id), pid(_pid), name(_name) {}

    SpawnServerChild(const SpawnServerChild &) = delete;
    SpawnServerChild &operator=(const SpawnServerChild &) = delete;

    const char *GetName() const {
        return name.c_str();
    }

    void Kill(ChildProcessRegistry &child_process_registry, int signo) {
        child_process_registry.Kill(pid, signo);
    }

    /* virtual methods from ExitListener */
    void OnChildProcessExit(int status) override;

    /* boost::instrusive::set hooks */
    typedef boost::intrusive::set_member_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> IdHook;
    IdHook id_hook;

    struct CompareId {
        bool operator()(const SpawnServerChild &a, const SpawnServerChild &b) const {
            return a.id < b.id;
        }

        bool operator()(int a, const SpawnServerChild &b) const {
            return a < b.id;
        }

        bool operator()(const SpawnServerChild &a, int b) const {
            return a.id < b;
        }
    };
};

class SpawnServerConnection
    : public boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> {
    SpawnServerProcess &process;
    const int fd;

    Event event;

    typedef boost::intrusive::set<SpawnServerChild,
                                  boost::intrusive::member_hook<SpawnServerChild,
                                                                SpawnServerChild::IdHook,
                                                                &SpawnServerChild::id_hook>,
                                  boost::intrusive::compare<SpawnServerChild::CompareId>> ChildIdMap;
    ChildIdMap children;

public:
    SpawnServerConnection(SpawnServerProcess &_process, int _fd);
    ~SpawnServerConnection();

    void OnChildProcessExit(int id, int status, SpawnServerChild *child);

private:
    void RemoveConnection();

    void SendExit(int id, int status);
    void SpawnChild(int id, const char *name, PreparedChildProcess &&p);

    void HandleExecMessage(SpawnPayload payload, SpawnFdList &&fds);
    void HandleKillMessage(SpawnPayload payload, SpawnFdList &&fds);
    void HandleMessage(ConstBuffer<uint8_t> payload, SpawnFdList &&fds);
    void HandleMessage(const struct msghdr &msg, ConstBuffer<uint8_t> payload);

    void ReadEventCallback();
};

void
SpawnServerChild::OnChildProcessExit(int status)
{
    connection.OnChildProcessExit(id, status, this);
}

void
SpawnServerConnection::OnChildProcessExit(int id, int status,
                                          SpawnServerChild *child)
{
    children.erase(children.iterator_to(*child));
    delete child;

    SendExit(id, status);
}

class SpawnServerProcess {
    const SpawnConfig config;

    EventBase base;

    ChildProcessRegistry child_process_registry;

    typedef boost::intrusive::list<SpawnServerConnection,
                                   boost::intrusive::constant_time_size<false>> ConnectionList;
    ConnectionList connections;

public:
    explicit SpawnServerProcess(const SpawnConfig &_config)
        :config(_config) {}

    const SpawnConfig &GetConfig() const {
        return config;
    }

    EventBase &GetEventBase() {
        return base;
    }

    ChildProcessRegistry &GetChildProcessRegistry() {
        return child_process_registry;
    }

    void AddConnection(int fd) {
        auto connection = new SpawnServerConnection(*this, fd);
        connections.push_back(*connection);
    }

    void RemoveConnection(SpawnServerConnection &connection) {
        connections.erase_and_dispose(connections.iterator_to(connection),
                                      DeleteDisposer());

        if (connections.empty())
            /* all connections are gone */
            Quit();
    }

    void Run();

private:
    void Quit() {
        assert(connections.empty());

        child_process_registry.SetVolatile();
    }
};

SpawnServerConnection::SpawnServerConnection(SpawnServerProcess &_process,
                                             int _fd)
    :process(_process), fd(_fd) {
    event.Set(process.GetEventBase(), fd, EV_READ|EV_PERSIST,
              MakeSimpleEventCallback(SpawnServerConnection,
                                      ReadEventCallback),
              this);
    event.Add();
}

SpawnServerConnection::~SpawnServerConnection()
{
    event.Delete();
    close(fd);

    auto &registry = process.GetChildProcessRegistry();
    children.clear_and_dispose([&registry](SpawnServerChild *child){
            child->Kill(registry, SIGTERM);
            delete child;
        });
}

inline void
SpawnServerConnection::RemoveConnection()
{
    process.RemoveConnection(*this);
}

void
SpawnServerConnection::SendExit(int id, int status)
{
    SpawnSerializer s(SpawnResponseCommand::EXIT);
    s.WriteInt(id);
    s.WriteInt(status);

    try {
        ::Send<1>(fd, s);
    } catch (const std::runtime_error &e) {
        daemon_log(1, "Failed to send EXIT to worker: %s\n",
                   e.what());
        RemoveConnection();
    }
}

inline void
SpawnServerConnection::SpawnChild(int id, const char *name,
                                  PreparedChildProcess &&p)
{
    const auto &config = process.GetConfig();

    if (!p.uid_gid.IsEmpty() && !config.Verify(p.uid_gid)) {
        daemon_log(1, "uid/gid not allowed: %d/%d\n",
                   int(p.uid_gid.uid), int(p.uid_gid.gid));
        SendExit(id, W_EXITCODE(0xff, 0));
        return;
    }

    pid_t pid = SpawnChildProcess(std::move(p), config);
    if (pid < 0) {
        daemon_log(1, "Failed to spawn child process: %s\n", strerror(-pid));
        SendExit(id, W_EXITCODE(0xff, 0));
        return;
    }

    auto *child = new SpawnServerChild(*this, id, pid, name);
    children.insert(*child);

    process.GetChildProcessRegistry().Add(pid, name, child);
}

inline void
SpawnServerConnection::HandleExecMessage(SpawnPayload payload,
                                         SpawnFdList &&fds)
{
    int id;
    payload.ReadInt(id);
    const char *name = payload.ReadString();

    PreparedChildProcess p;

    MountList **mount_tail = &p.ns.mounts;
    assert(*mount_tail == nullptr);

    std::forward_list<MountList> mounts;

    while (!payload.IsEmpty()) {
        const SpawnExecCommand cmd = (SpawnExecCommand)payload.ReadByte();
        switch (cmd) {
        case SpawnExecCommand::ARG:
            if (!p.Append(payload.ReadString()))
                throw MalformedSpawnPayloadError();
            break;

        case SpawnExecCommand::SETENV:
            if (!p.PutEnv(payload.ReadString()))
                throw MalformedSpawnPayloadError();
            break;

        case SpawnExecCommand::STDIN:
            p.SetStdin(fds.Get().Release());
            break;

        case SpawnExecCommand::STDOUT:
            p.SetStdout(fds.Get().Release());
            break;

        case SpawnExecCommand::STDERR:
            p.SetStderr(fds.Get().Release());
            break;

        case SpawnExecCommand::CONTROL:
            p.SetControl(fds.Get().Release());
            break;

        case SpawnExecCommand::REFENCE:
            p.refence.Set(payload.ReadString());
            break;

        case SpawnExecCommand::USER_NS:
            p.ns.enable_user = true;
            break;

        case SpawnExecCommand::PID_NS:
            p.ns.enable_pid = true;
            break;

        case SpawnExecCommand::NETWORK_NS:
            p.ns.enable_network = true;
            break;

        case SpawnExecCommand::IPC_NS:
            p.ns.enable_ipc = true;
            break;

        case SpawnExecCommand::MOUNT_NS:
            p.ns.enable_mount = true;
            break;

        case SpawnExecCommand::MOUNT_PROC:
            p.ns.mount_proc = true;
            break;

        case SpawnExecCommand::PIVOT_ROOT:
            p.ns.pivot_root = payload.ReadString();
            break;

        case SpawnExecCommand::MOUNT_HOME:
            p.ns.mount_home = payload.ReadString();
            p.ns.home = payload.ReadString();
            break;

        case SpawnExecCommand::MOUNT_TMP_TMPFS:
            p.ns.mount_tmp_tmpfs = payload.ReadString();
            break;

        case SpawnExecCommand::MOUNT_TMPFS:
            p.ns.mount_tmpfs = payload.ReadString();
            break;

        case SpawnExecCommand::BIND_MOUNT:
            {
                const char *source = payload.ReadString();
                const char *target = payload.ReadString();
                bool writable = payload.ReadByte();
                mounts.emplace_front(source, target, false,
                                     writable);
            }

            *mount_tail = &mounts.front();
            mount_tail = &mounts.front().next;
            break;

        case SpawnExecCommand::HOSTNAME:
            p.ns.hostname = payload.ReadString();
            break;

        case SpawnExecCommand::RLIMIT:
            {
                unsigned i = payload.ReadByte();
                struct rlimit &data = p.rlimits.values[i];
                payload.ReadT(data);
            }
            break;

        case SpawnExecCommand::UID_GID:
            payload.ReadT(p.uid_gid.uid);
            payload.ReadT(p.uid_gid.gid);
            break;

        case SpawnExecCommand::NO_NEW_PRIVS:
            p.no_new_privs = true;
            break;
        }
    }

    SpawnChild(id, name, std::move(p));
}

inline void
SpawnServerConnection::HandleKillMessage(SpawnPayload payload,
                                         SpawnFdList &&fds)
{
    if (!fds.IsEmpty())
        throw MalformedSpawnPayloadError();

    int id, signo;
    payload.ReadInt(id);
    payload.ReadInt(signo);
    if (!payload.IsEmpty())
        throw MalformedSpawnPayloadError();

    auto i = children.find(id, SpawnServerChild::CompareId());
    if (i == children.end())
        return;

    SpawnServerChild *child = &*i;
    children.erase(i);

    child->Kill(process.GetChildProcessRegistry(), signo);
    delete child;
}

inline void
SpawnServerConnection::HandleMessage(ConstBuffer<uint8_t> payload,
                                     SpawnFdList &&fds)
{
    const auto cmd = (SpawnRequestCommand)payload.shift();

    switch (cmd) {
    case SpawnRequestCommand::CONNECT:
        if (!payload.IsEmpty() || fds.size() != 1)
            throw MalformedSpawnPayloadError();

        process.AddConnection(fds.Get().Release());
        break;

    case SpawnRequestCommand::EXEC:
        HandleExecMessage(SpawnPayload(payload), std::move(fds));
        break;

    case SpawnRequestCommand::KILL:
        HandleKillMessage(SpawnPayload(payload), std::move(fds));
        break;
    }
}

inline void
SpawnServerConnection::HandleMessage(const struct msghdr &msg,
                                     ConstBuffer<uint8_t> payload)
{
    SpawnFdList fds = nullptr;

    const struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
    if (cmsg != nullptr && cmsg->cmsg_level == SOL_SOCKET &&
        cmsg->cmsg_type == SCM_RIGHTS) {
        auto data = ConstBuffer<void>(CMSG_DATA(cmsg),
                                      cmsg->cmsg_len - CMSG_LEN(0));
        fds = SpawnFdList(ConstBuffer<int>::FromVoid(data));
    }

    HandleMessage(payload, std::move(fds));
}

inline void
SpawnServerConnection::ReadEventCallback()
{
    uint8_t payload[8192];

    struct iovec iov;
    iov.iov_base = payload;
    iov.iov_len = sizeof(payload);

    int fds[32];
    char ccmsg[CMSG_SPACE(sizeof(fds))];
    struct msghdr msg = {
        .msg_name = nullptr,
        .msg_namelen = 0,
        .msg_iov = &iov,
        .msg_iovlen = 1,
        .msg_control = ccmsg,
        .msg_controllen = sizeof(ccmsg),
    };

    ssize_t nbytes = recvmsg(fd, &msg, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
    if (nbytes <= 0) {
        if (nbytes < 0)
            daemon_log(2, "recvmsg() failed: %s\n", strerror(errno));
        RemoveConnection();
        return;
    }

    try {
        HandleMessage(msg, {payload, size_t(nbytes)});
    } catch (MalformedSpawnPayloadError) {
        daemon_log(3, "Malformed spawn payload\n");
    }
}

inline void
SpawnServerProcess::Run()
{
    base.Dispatch();
}

void
RunSpawnServer(const SpawnConfig &config, int fd)
{
    SpawnServerProcess process(config);
    process.AddConnection(fd);
    process.Run();
}
<commit_msg>spawn/Server: move code to Read()<commit_after>/*
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "Server.hxx"
#include "Config.hxx"
#include "Protocol.hxx"
#include "Parser.hxx"
#include "Builder.hxx"
#include "Prepared.hxx"
#include "mount_list.hxx"
#include "Direct.hxx"
#include "Registry.hxx"
#include "ExitListener.hxx"
#include "event/Event.hxx"
#include "event/Callback.hxx"
#include "util/DeleteDisposer.hxx"
#include "util/ConstBuffer.hxx"
#include "util/StaticArray.hxx"

#include <daemon/log.h>

#include <boost/intrusive/list.hpp>

#include <algorithm>
#include <memory>
#include <map>

#include <unistd.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>

class SpawnServerProcess;

class SpawnFd {
    int fd;

public:
    explicit SpawnFd(int _fd=-1):fd(_fd) {}

    SpawnFd(SpawnFd &&src):fd(src.fd) {
        src.fd = -1;
    }

    ~SpawnFd() {
        if (fd >= 0)
            close(fd);
    }

    SpawnFd &operator=(SpawnFd &&src) {
        std::swap(fd, src.fd);
        return *this;
    }

    int Release() {
        int result = fd;
        fd = -1;
        return result;
    }
};

class SpawnFdList {
    ConstBuffer<int> list;

public:
    SpawnFdList(std::nullptr_t n):list(n) {}

    explicit SpawnFdList(ConstBuffer<int> _list)
        :list(_list) {}

    SpawnFdList(SpawnFdList &&src)
        :list(src.list) {
        src.list = nullptr;
    }

    ~SpawnFdList() {
        for (auto fd : list)
            close(fd);
    }

    SpawnFdList &operator=(SpawnFdList &&src) {
        std::swap(list, src.list);
        return *this;
    }

    bool IsEmpty() {
        return list.IsEmpty();
    }

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

    SpawnFd Get() {
        if (IsEmpty())
            throw MalformedSpawnPayloadError();

        return SpawnFd(list.shift());
    }
};

class SpawnServerConnection;

class SpawnServerChild final : public ExitListener {
    SpawnServerConnection &connection;

    const int id;

    const pid_t pid;

    const std::string name;

public:
    explicit SpawnServerChild(SpawnServerConnection &_connection,
                              int _id, pid_t _pid,
                              const char *_name)
        :connection(_connection), id(_id), pid(_pid), name(_name) {}

    SpawnServerChild(const SpawnServerChild &) = delete;
    SpawnServerChild &operator=(const SpawnServerChild &) = delete;

    const char *GetName() const {
        return name.c_str();
    }

    void Kill(ChildProcessRegistry &child_process_registry, int signo) {
        child_process_registry.Kill(pid, signo);
    }

    /* virtual methods from ExitListener */
    void OnChildProcessExit(int status) override;

    /* boost::instrusive::set hooks */
    typedef boost::intrusive::set_member_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> IdHook;
    IdHook id_hook;

    struct CompareId {
        bool operator()(const SpawnServerChild &a, const SpawnServerChild &b) const {
            return a.id < b.id;
        }

        bool operator()(int a, const SpawnServerChild &b) const {
            return a < b.id;
        }

        bool operator()(const SpawnServerChild &a, int b) const {
            return a.id < b;
        }
    };
};

class SpawnServerConnection
    : public boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> {
    SpawnServerProcess &process;
    const int fd;

    Event event;

    typedef boost::intrusive::set<SpawnServerChild,
                                  boost::intrusive::member_hook<SpawnServerChild,
                                                                SpawnServerChild::IdHook,
                                                                &SpawnServerChild::id_hook>,
                                  boost::intrusive::compare<SpawnServerChild::CompareId>> ChildIdMap;
    ChildIdMap children;

public:
    SpawnServerConnection(SpawnServerProcess &_process, int _fd);
    ~SpawnServerConnection();

    void OnChildProcessExit(int id, int status, SpawnServerChild *child);

private:
    void RemoveConnection();

    void SendExit(int id, int status);
    void SpawnChild(int id, const char *name, PreparedChildProcess &&p);

    void HandleExecMessage(SpawnPayload payload, SpawnFdList &&fds);
    void HandleKillMessage(SpawnPayload payload, SpawnFdList &&fds);
    void HandleMessage(ConstBuffer<uint8_t> payload, SpawnFdList &&fds);
    void HandleMessage(const struct msghdr &msg, ConstBuffer<uint8_t> payload);

    void ReadEventCallback();
};

void
SpawnServerChild::OnChildProcessExit(int status)
{
    connection.OnChildProcessExit(id, status, this);
}

void
SpawnServerConnection::OnChildProcessExit(int id, int status,
                                          SpawnServerChild *child)
{
    children.erase(children.iterator_to(*child));
    delete child;

    SendExit(id, status);
}

class SpawnServerProcess {
    const SpawnConfig config;

    EventBase base;

    ChildProcessRegistry child_process_registry;

    typedef boost::intrusive::list<SpawnServerConnection,
                                   boost::intrusive::constant_time_size<false>> ConnectionList;
    ConnectionList connections;

public:
    explicit SpawnServerProcess(const SpawnConfig &_config)
        :config(_config) {}

    const SpawnConfig &GetConfig() const {
        return config;
    }

    EventBase &GetEventBase() {
        return base;
    }

    ChildProcessRegistry &GetChildProcessRegistry() {
        return child_process_registry;
    }

    void AddConnection(int fd) {
        auto connection = new SpawnServerConnection(*this, fd);
        connections.push_back(*connection);
    }

    void RemoveConnection(SpawnServerConnection &connection) {
        connections.erase_and_dispose(connections.iterator_to(connection),
                                      DeleteDisposer());

        if (connections.empty())
            /* all connections are gone */
            Quit();
    }

    void Run();

private:
    void Quit() {
        assert(connections.empty());

        child_process_registry.SetVolatile();
    }
};

SpawnServerConnection::SpawnServerConnection(SpawnServerProcess &_process,
                                             int _fd)
    :process(_process), fd(_fd) {
    event.Set(process.GetEventBase(), fd, EV_READ|EV_PERSIST,
              MakeSimpleEventCallback(SpawnServerConnection,
                                      ReadEventCallback),
              this);
    event.Add();
}

SpawnServerConnection::~SpawnServerConnection()
{
    event.Delete();
    close(fd);

    auto &registry = process.GetChildProcessRegistry();
    children.clear_and_dispose([&registry](SpawnServerChild *child){
            child->Kill(registry, SIGTERM);
            delete child;
        });
}

inline void
SpawnServerConnection::RemoveConnection()
{
    process.RemoveConnection(*this);
}

void
SpawnServerConnection::SendExit(int id, int status)
{
    SpawnSerializer s(SpawnResponseCommand::EXIT);
    s.WriteInt(id);
    s.WriteInt(status);

    try {
        ::Send<1>(fd, s);
    } catch (const std::runtime_error &e) {
        daemon_log(1, "Failed to send EXIT to worker: %s\n",
                   e.what());
        RemoveConnection();
    }
}

inline void
SpawnServerConnection::SpawnChild(int id, const char *name,
                                  PreparedChildProcess &&p)
{
    const auto &config = process.GetConfig();

    if (!p.uid_gid.IsEmpty() && !config.Verify(p.uid_gid)) {
        daemon_log(1, "uid/gid not allowed: %d/%d\n",
                   int(p.uid_gid.uid), int(p.uid_gid.gid));
        SendExit(id, W_EXITCODE(0xff, 0));
        return;
    }

    pid_t pid = SpawnChildProcess(std::move(p), config);
    if (pid < 0) {
        daemon_log(1, "Failed to spawn child process: %s\n", strerror(-pid));
        SendExit(id, W_EXITCODE(0xff, 0));
        return;
    }

    auto *child = new SpawnServerChild(*this, id, pid, name);
    children.insert(*child);

    process.GetChildProcessRegistry().Add(pid, name, child);
}

static void
Read(SpawnPayload &payload, ResourceLimits &rlimits)
{
    unsigned i = payload.ReadByte();
    struct rlimit &data = rlimits.values[i];
    payload.ReadT(data);
}

static void
Read(SpawnPayload &payload, UidGid &uid_gid)
{
    payload.ReadT(uid_gid.uid);
    payload.ReadT(uid_gid.gid);
}

inline void
SpawnServerConnection::HandleExecMessage(SpawnPayload payload,
                                         SpawnFdList &&fds)
{
    int id;
    payload.ReadInt(id);
    const char *name = payload.ReadString();

    PreparedChildProcess p;

    MountList **mount_tail = &p.ns.mounts;
    assert(*mount_tail == nullptr);

    std::forward_list<MountList> mounts;

    while (!payload.IsEmpty()) {
        const SpawnExecCommand cmd = (SpawnExecCommand)payload.ReadByte();
        switch (cmd) {
        case SpawnExecCommand::ARG:
            if (!p.Append(payload.ReadString()))
                throw MalformedSpawnPayloadError();
            break;

        case SpawnExecCommand::SETENV:
            if (!p.PutEnv(payload.ReadString()))
                throw MalformedSpawnPayloadError();
            break;

        case SpawnExecCommand::STDIN:
            p.SetStdin(fds.Get().Release());
            break;

        case SpawnExecCommand::STDOUT:
            p.SetStdout(fds.Get().Release());
            break;

        case SpawnExecCommand::STDERR:
            p.SetStderr(fds.Get().Release());
            break;

        case SpawnExecCommand::CONTROL:
            p.SetControl(fds.Get().Release());
            break;

        case SpawnExecCommand::REFENCE:
            p.refence.Set(payload.ReadString());
            break;

        case SpawnExecCommand::USER_NS:
            p.ns.enable_user = true;
            break;

        case SpawnExecCommand::PID_NS:
            p.ns.enable_pid = true;
            break;

        case SpawnExecCommand::NETWORK_NS:
            p.ns.enable_network = true;
            break;

        case SpawnExecCommand::IPC_NS:
            p.ns.enable_ipc = true;
            break;

        case SpawnExecCommand::MOUNT_NS:
            p.ns.enable_mount = true;
            break;

        case SpawnExecCommand::MOUNT_PROC:
            p.ns.mount_proc = true;
            break;

        case SpawnExecCommand::PIVOT_ROOT:
            p.ns.pivot_root = payload.ReadString();
            break;

        case SpawnExecCommand::MOUNT_HOME:
            p.ns.mount_home = payload.ReadString();
            p.ns.home = payload.ReadString();
            break;

        case SpawnExecCommand::MOUNT_TMP_TMPFS:
            p.ns.mount_tmp_tmpfs = payload.ReadString();
            break;

        case SpawnExecCommand::MOUNT_TMPFS:
            p.ns.mount_tmpfs = payload.ReadString();
            break;

        case SpawnExecCommand::BIND_MOUNT:
            {
                const char *source = payload.ReadString();
                const char *target = payload.ReadString();
                bool writable = payload.ReadByte();
                mounts.emplace_front(source, target, false,
                                     writable);
            }

            *mount_tail = &mounts.front();
            mount_tail = &mounts.front().next;
            break;

        case SpawnExecCommand::HOSTNAME:
            p.ns.hostname = payload.ReadString();
            break;

        case SpawnExecCommand::RLIMIT:
            Read(payload, p.rlimits);
            break;

        case SpawnExecCommand::UID_GID:
            Read(payload, p.uid_gid);
            break;

        case SpawnExecCommand::NO_NEW_PRIVS:
            p.no_new_privs = true;
            break;
        }
    }

    SpawnChild(id, name, std::move(p));
}

inline void
SpawnServerConnection::HandleKillMessage(SpawnPayload payload,
                                         SpawnFdList &&fds)
{
    if (!fds.IsEmpty())
        throw MalformedSpawnPayloadError();

    int id, signo;
    payload.ReadInt(id);
    payload.ReadInt(signo);
    if (!payload.IsEmpty())
        throw MalformedSpawnPayloadError();

    auto i = children.find(id, SpawnServerChild::CompareId());
    if (i == children.end())
        return;

    SpawnServerChild *child = &*i;
    children.erase(i);

    child->Kill(process.GetChildProcessRegistry(), signo);
    delete child;
}

inline void
SpawnServerConnection::HandleMessage(ConstBuffer<uint8_t> payload,
                                     SpawnFdList &&fds)
{
    const auto cmd = (SpawnRequestCommand)payload.shift();

    switch (cmd) {
    case SpawnRequestCommand::CONNECT:
        if (!payload.IsEmpty() || fds.size() != 1)
            throw MalformedSpawnPayloadError();

        process.AddConnection(fds.Get().Release());
        break;

    case SpawnRequestCommand::EXEC:
        HandleExecMessage(SpawnPayload(payload), std::move(fds));
        break;

    case SpawnRequestCommand::KILL:
        HandleKillMessage(SpawnPayload(payload), std::move(fds));
        break;
    }
}

inline void
SpawnServerConnection::HandleMessage(const struct msghdr &msg,
                                     ConstBuffer<uint8_t> payload)
{
    SpawnFdList fds = nullptr;

    const struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
    if (cmsg != nullptr && cmsg->cmsg_level == SOL_SOCKET &&
        cmsg->cmsg_type == SCM_RIGHTS) {
        auto data = ConstBuffer<void>(CMSG_DATA(cmsg),
                                      cmsg->cmsg_len - CMSG_LEN(0));
        fds = SpawnFdList(ConstBuffer<int>::FromVoid(data));
    }

    HandleMessage(payload, std::move(fds));
}

inline void
SpawnServerConnection::ReadEventCallback()
{
    uint8_t payload[8192];

    struct iovec iov;
    iov.iov_base = payload;
    iov.iov_len = sizeof(payload);

    int fds[32];
    char ccmsg[CMSG_SPACE(sizeof(fds))];
    struct msghdr msg = {
        .msg_name = nullptr,
        .msg_namelen = 0,
        .msg_iov = &iov,
        .msg_iovlen = 1,
        .msg_control = ccmsg,
        .msg_controllen = sizeof(ccmsg),
    };

    ssize_t nbytes = recvmsg(fd, &msg, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
    if (nbytes <= 0) {
        if (nbytes < 0)
            daemon_log(2, "recvmsg() failed: %s\n", strerror(errno));
        RemoveConnection();
        return;
    }

    try {
        HandleMessage(msg, {payload, size_t(nbytes)});
    } catch (MalformedSpawnPayloadError) {
        daemon_log(3, "Malformed spawn payload\n");
    }
}

inline void
SpawnServerProcess::Run()
{
    base.Dispatch();
}

void
RunSpawnServer(const SpawnConfig &config, int fd)
{
    SpawnServerProcess process(config);
    process.AddConnection(fd);
    process.Run();
}
<|endoftext|>


<commit_before>//  (C) Copyright Gennadiy Rozental 2004.
//  Distributed under the Boost Software License, Version 1.0.
//  (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/test for the library home page.
//
//  File        : $RCSfile$
//
//  Version     : $Revision$
//
//  Description : common code used by any agent serving as XML printer
// ***************************************************************************

#ifndef BOOST_TEST_XML_PRINTER_HPP_071894GER
#define BOOST_TEST_XML_PRINTER_HPP_071894GER

// Boost.Test
#include <boost/test/utils/basic_cstring/basic_cstring.hpp>
#include <boost/test/utils/fixed_mapping.hpp>
#include <boost/test/utils/custom_manip.hpp>

// BOOST
#include <boost/config.hpp>

// STL
#include <iostream>

#include <boost/test/detail/suppress_warnings.hpp>

namespace boost {

namespace unit_test {

// ************************************************************************** //
// **************               xml print helpers              ************** //
// ************************************************************************** //

inline void
print_escaped( std::ostream& where_to, const_string value )
{
    static fixed_mapping<char,char const*> char_type(
        '<' , "lt",
        '>' , "gt",
        '&' , "amp",
        '\'', "apos" ,
        '"' , "quot",

        0
    );

    for( const_string::iterator it = value.begin(); it != value.end(); ++it ) {
        char const* ref = char_type[*it];

        if( ref )
            where_to << '&' << ref << ';';
        else
            where_to << *it;
    }
}

//____________________________________________________________________________//

typedef custom_manip<struct attr_value_t> attr_value;

inline std::ostream&
operator<<( custom_printer<attr_value> const& p, const_string value )
{
    *p << "=\"";
    print_escaped( *p, value );
    *p << '"';

    return *p;
}

//____________________________________________________________________________//

template<typename T>
inline std::ostream&
operator<<( custom_printer<attr_value> const& p, T const& value )
{
    return *p<< "=\"" << value << '"';
}

//____________________________________________________________________________//

typedef custom_manip<struct pcdata_t> pcdata;

inline std::ostream&
operator<<( custom_printer<pcdata> const& p, const_string value )
{
    print_escaped( *p, value );

    return *p;
}

//____________________________________________________________________________//

} // namespace unit_test

} // namespace boost

#include <boost/test/detail/enable_warnings.hpp>

// ***************************************************************************
//  Revision History :
//
//  $Log$
//  Revision 1.2  2005/01/22 19:22:13  rogeeff
//  implementation moved into headers section to eliminate dependency of included/minimal component on src directory
//
//  Revision 1.1  2005/01/22 18:21:40  rogeeff
//  moved sharable staff into utils
//
//  Revision 1.3  2005/01/21 07:31:44  rogeeff
//  xml helper facilities reworked to present manipulator interfaces
//
//  Revision 1.2  2004/08/04 02:50:27  rogeeff
//  darwin workarounds
//
//  Revision 1.1  2004/07/19 12:22:15  rogeeff
//  shared xml printer utils
//
// ***************************************************************************

#endif // BOOST_TEST_XML_PRINTER_HPP_071894GER
<commit_msg>Changed - all the \r\r\n to \r\n in the windows flavor of the file           because VC++ 8.0 complains and refuses to compile<commit_after>//  (C) Copyright Gennadiy Rozental 2004.
//  Distributed under the Boost Software License, Version 1.0.
//  (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/test for the library home page.
//
//  File        : $RCSfile$
//
//  Version     : $Revision$
//
//  Description : common code used by any agent serving as XML printer
// ***************************************************************************

#ifndef BOOST_TEST_XML_PRINTER_HPP_071894GER
#define BOOST_TEST_XML_PRINTER_HPP_071894GER

// Boost.Test
#include <boost/test/utils/basic_cstring/basic_cstring.hpp>
#include <boost/test/utils/fixed_mapping.hpp>
#include <boost/test/utils/custom_manip.hpp>

// BOOST
#include <boost/config.hpp>

// STL
#include <iostream>

#include <boost/test/detail/suppress_warnings.hpp>

namespace boost {

namespace unit_test {

// ************************************************************************** //
// **************               xml print helpers              ************** //
// ************************************************************************** //

inline void
print_escaped( std::ostream& where_to, const_string value )
{
    static fixed_mapping<char,char const*> char_type(
        '<' , "lt",
        '>' , "gt",
        '&' , "amp",
        '\'', "apos" ,
        '"' , "quot",

        0
    );

    for( const_string::iterator it = value.begin(); it != value.end(); ++it ) {
        char const* ref = char_type[*it];

        if( ref )
            where_to << '&' << ref << ';';
        else
            where_to << *it;
    }
}

//____________________________________________________________________________//

typedef custom_manip<struct attr_value_t> attr_value;

inline std::ostream&
operator<<( custom_printer<attr_value> const& p, const_string value )
{
    *p << "=\"";
    print_escaped( *p, value );
    *p << '"';

    return *p;
}

//____________________________________________________________________________//

template<typename T>
inline std::ostream&
operator<<( custom_printer<attr_value> const& p, T const& value )
{
    return *p<< "=\"" << value << '"';
}

//____________________________________________________________________________//

typedef custom_manip<struct pcdata_t> pcdata;

inline std::ostream&
operator<<( custom_printer<pcdata> const& p, const_string value )
{
    print_escaped( *p, value );

    return *p;
}

//____________________________________________________________________________//

} // namespace unit_test

} // namespace boost

#include <boost/test/detail/enable_warnings.hpp>

// ***************************************************************************
//  Revision History :
//
//  $Log$
//  Revision 1.3  2005/01/23 09:59:34  vawjr
//  Changed - all the \r\r\n to \r\n in the windows flavor of the file
//            because VC++ 8.0 complains and refuses to compile
//
//  Revision 1.2  2005/01/22 19:22:13  rogeeff
//  implementation moved into headers section to eliminate dependency of included/minimal component on src directory
//
//  Revision 1.1  2005/01/22 18:21:40  rogeeff
//  moved sharable staff into utils
//
//  Revision 1.3  2005/01/21 07:31:44  rogeeff
//  xml helper facilities reworked to present manipulator interfaces
//
//  Revision 1.2  2004/08/04 02:50:27  rogeeff
//  darwin workarounds
//
//  Revision 1.1  2004/07/19 12:22:15  rogeeff
//  shared xml printer utils
//
// ***************************************************************************

#endif // BOOST_TEST_XML_PRINTER_HPP_071894GER
<|endoftext|>


<commit_before>/* Siconos-sample version 3.0.0, Copyright INRIA 2005-2008.
 * Siconos is a program dedicated to modeling, simulation and control
 * of non smooth dynamical systems.
 * Siconos is a free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * Siconos is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Siconos; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Contact: Vincent ACARY vincent.acary@inrialpes.fr
 */

// =============================== Double Pendulum Example ===============================
//
// Author: Vincent Acary
//
// Keywords: LagrangianDS, LagrangianLinear relation, Moreau TimeStepping, LCP.
//
// =============================================================================================

#include "SiconosKernel.hpp"
#include <stdlib.h>
using namespace std;

#include <boost/progress.hpp>

double gravity = 10.0;
double m1 = 1.0;
double m2 = 1.0 ;
double l1 = 1.0 ;
double l2 = 1.0 ;

int main(int argc, char* argv[])
{
  try
  {

    // ================= Creation of the model =======================

    // User-defined main parameters
    unsigned int nDof = 2;           // degrees of freedom for robot arm
    double t0 = 0;                   // initial computation time
    double T = 6.0;                   // final computation time
    double h = 0.0005;                // time step
    double criterion = 0.05;
    unsigned int maxIter = 20000;
    double e = 1.0;                  // nslaw
    double e1 = 0.0;

    // -> mind to set the initial conditions below.

    // -------------------------
    // --- Dynamical systems ---
    // -------------------------

    // unsigned int i;
    DynamicalSystemsSet allDS; // the list of DS

    // --- DS: Double Pendulum ---

    // Initial position (angles in radian)
    SimpleVector q0(nDof), v0(nDof);
    q0.zero();
    v0.zero();
    q0(0) = 1.5;
    q0(1) = 1.5;

    SP::LagrangianDS doublependulum(new LagrangianDS(q0, v0, "DoublePendulumPlugin:mass"));

    // external plug-in
    doublependulum->setComputeNNLFunction("DoublePendulumPlugin.so", "NNL");
    doublependulum->setComputeJacobianNNLFunction(1, "DoublePendulumPlugin.so", "jacobianVNNL");
    doublependulum->setComputeJacobianNNLFunction(0, "DoublePendulumPlugin.so", "jacobianQNNL");
    doublependulum->setComputeFIntFunction("DoublePendulumPlugin.so", "FInt");
    doublependulum->setComputeJacobianFIntFunction(1, "DoublePendulumPlugin.so", "jacobianVFInt");
    doublependulum->setComputeJacobianFIntFunction(0, "DoublePendulumPlugin.so", "jacobianQFInt");

    allDS.insert(doublependulum);

    // -------------------
    // --- Interactions---
    // -------------------

    InteractionsSet allInteractions;

    // -- relations --
    string G = "DoublePendulumPlugin:G0";
    SP::NonSmoothLaw nslaw(new NewtonImpactNSL(e));
    SP::Relation relation(new LagrangianScleronomousR("DoublePendulumPlugin:h0", G));
    SP::Interaction inter(new Interaction("floor-mass1", allDS, 1, 1, nslaw, relation));

    string G1 = "DoublePendulumPlugin:G1";
    SP::NonSmoothLaw nslaw1(new NewtonImpactNSL(e1));
    SP::Relation relation1(new LagrangianScleronomousR("DoublePendulumPlugin:h1", G1));
    SP::Interaction inter1(new Interaction("floor-mass2", allDS, 2, 1, nslaw1, relation1));

    allInteractions.insert(inter);
    allInteractions.insert(inter1);

    // -------------
    // --- Model ---
    // -------------

    SP::Model Pendulum(new Model(t0, T, allDS, allInteractions));

    // ----------------
    // --- Simulation ---
    // ----------------

    // -- Time discretisation --
    SP::TimeDiscretisation t(new TimeDiscretisation(t0, h));

    SP::TimeStepping s(new TimeStepping(t));

    // -- OneStepIntegrators --

    //double theta=0.500001;
    double theta = 0.500001;

    SP::Moreau OSI(new Moreau(doublependulum, theta));
    s->recordIntegrator(OSI);

    // -- OneStepNsProblem --
    IntParameters iparam(5);
    iparam[0] = 2001; // Max number of iteration
    DoubleParameters dparam(5);
    dparam[0] = 0.0000005; // Tolerance
    string solverName = "Lemke" ;
    SP::NonSmoothSolver mySolver(new NonSmoothSolver(solverName, iparam, dparam));
    SP::OneStepNSProblem osnspb(new LCP(mySolver));
    s->recordNonSmoothProblem(osnspb);

    cout << "=== End of model loading === " << endl;

    // =========================== End of model definition ===========================  dataPlot(k,7) = (inter->getY(0))(0);

    // ================================= Computation =================================

    // --- Simulation initialization ---
    Pendulum->initialize(s);
    cout << "End of simulation initialisation" << endl;

    int k = 0;
    int N = (int)((T - t0) / h) + 1;
    cout << "Number of time step   " << N << endl;
    // --- Get the values to be plotted ---
    // -> saved in a matrix dataPlot
    unsigned int outputSize = 12;
    SimpleMatrix dataPlot(N + 1, outputSize);
    // For the initial time step:
    // time
    SP::SiconosVector q = doublependulum->getQPtr();
    SP::SiconosVector v = doublependulum->getVelocityPtr();

    dataPlot(k, 0) =  t0;
    dataPlot(k, 1) = (*q)(0);
    dataPlot(k, 2) = (*v)(0);
    dataPlot(k, 3) = (*q)(1);
    dataPlot(k, 4) = (*v)(1);
    dataPlot(k, 5) =  l1 * sin((*q)(0));
    dataPlot(k, 6) = -l1 * cos((*q)(0));
    dataPlot(k, 7) =  l1 * sin((*q)(0)) + l2 * sin((*q)(1));
    dataPlot(k, 8) = -l1 * cos((*q)(0)) - l2 * cos((*q)(1));
    dataPlot(k, 9) =  l1 * cos((*q)(0)) * ((*v)(0));
    dataPlot(k, 10) = l1 * cos((*q)(0)) * ((*v)(0)) + l2 * cos((*q)(1)) * ((*v)(1));

    boost::timer time;
    time.restart();

    // --- Time loop ---
    cout << "Start computation ... " << endl;

    boost::progress_display show_progress(N);

    while (s->getNextTime() <= Pendulum->getFinalT())
    {
      k++;
      ++show_progress;
      //  if (!(div(k,1000).rem))  cout <<"Step number "<< k << "\n";

      // Solve problem
      s->newtonSolve(criterion, maxIter);
      // Data Output
      dataPlot(k, 0) =  s->getNextTime();
      dataPlot(k, 1) = (*q)(0);
      dataPlot(k, 2) = (*v)(0);
      dataPlot(k, 3) = (*q)(1);
      dataPlot(k, 4) = (*v)(1);
      dataPlot(k, 5) =  l1 * sin((*q)(0));
      dataPlot(k, 6) = -l1 * cos((*q)(0));
      dataPlot(k, 7) =  l1 * sin((*q)(0)) + l2 * sin((*q)(1));
      dataPlot(k, 8) = -l1 * cos((*q)(0)) - l2 * cos((*q)(1));
      dataPlot(k, 9) =  l1 * cos((*q)(0)) * ((*v)(0));
      dataPlot(k, 10) = l1 * cos((*q)(0)) * ((*v)(0)) + l2 * cos((*q)(1)) * ((*v)(1));
      s->nextStep();
    }

    cout << "End of computation - Number of iterations done: " << k << endl;
    cout << "Computation Time " << time.elapsed()  << endl;

    // --- Output files ---
    ioMatrix out("DoublePendulumResult.dat", "ascii");
    out.write(dataPlot, "noDim");
  }

  catch (SiconosException e)
  {
    cout << e.report() << endl;
  }
  catch (...)
  {
    cout << "Exception caught in \'sample/MultiBeadsColumn\'" << endl;
  }
}
<commit_msg>avoid foat comparison<commit_after>/* Siconos-sample version 3.0.0, Copyright INRIA 2005-2008.
 * Siconos is a program dedicated to modeling, simulation and control
 * of non smooth dynamical systems.
 * Siconos is a free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * Siconos is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Siconos; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Contact: Vincent ACARY vincent.acary@inrialpes.fr
 */

// =============================== Double Pendulum Example ===============================
//
// Author: Vincent Acary
//
// Keywords: LagrangianDS, LagrangianLinear relation, Moreau TimeStepping, LCP.
//
// =============================================================================================

#include "SiconosKernel.hpp"
#include <stdlib.h>
using namespace std;

#include <boost/progress.hpp>

double gravity = 10.0;
double m1 = 1.0;
double m2 = 1.0 ;
double l1 = 1.0 ;
double l2 = 1.0 ;

int main(int argc, char* argv[])
{
  try
  {

    // ================= Creation of the model =======================

    // User-defined main parameters
    unsigned int nDof = 2;           // degrees of freedom for robot arm
    double t0 = 0;                   // initial computation time
    double T = 6.0;                   // final computation time
    double h = 0.0005;                // time step
    double criterion = 0.05;
    unsigned int maxIter = 20000;
    double e = 1.0;                  // nslaw
    double e1 = 0.0;

    // -> mind to set the initial conditions below.

    // -------------------------
    // --- Dynamical systems ---
    // -------------------------

    // unsigned int i;
    DynamicalSystemsSet allDS; // the list of DS

    // --- DS: Double Pendulum ---

    // Initial position (angles in radian)
    SimpleVector q0(nDof), v0(nDof);
    q0.zero();
    v0.zero();
    q0(0) = 1.5;
    q0(1) = 1.5;

    SP::LagrangianDS doublependulum(new LagrangianDS(q0, v0, "DoublePendulumPlugin:mass"));

    // external plug-in
    doublependulum->setComputeNNLFunction("DoublePendulumPlugin.so", "NNL");
    doublependulum->setComputeJacobianNNLFunction(1, "DoublePendulumPlugin.so", "jacobianVNNL");
    doublependulum->setComputeJacobianNNLFunction(0, "DoublePendulumPlugin.so", "jacobianQNNL");
    doublependulum->setComputeFIntFunction("DoublePendulumPlugin.so", "FInt");
    doublependulum->setComputeJacobianFIntFunction(1, "DoublePendulumPlugin.so", "jacobianVFInt");
    doublependulum->setComputeJacobianFIntFunction(0, "DoublePendulumPlugin.so", "jacobianQFInt");

    allDS.insert(doublependulum);

    // -------------------
    // --- Interactions---
    // -------------------

    InteractionsSet allInteractions;

    // -- relations --
    string G = "DoublePendulumPlugin:G0";
    SP::NonSmoothLaw nslaw(new NewtonImpactNSL(e));
    SP::Relation relation(new LagrangianScleronomousR("DoublePendulumPlugin:h0", G));
    SP::Interaction inter(new Interaction("floor-mass1", allDS, 1, 1, nslaw, relation));

    string G1 = "DoublePendulumPlugin:G1";
    SP::NonSmoothLaw nslaw1(new NewtonImpactNSL(e1));
    SP::Relation relation1(new LagrangianScleronomousR("DoublePendulumPlugin:h1", G1));
    SP::Interaction inter1(new Interaction("floor-mass2", allDS, 2, 1, nslaw1, relation1));

    allInteractions.insert(inter);
    allInteractions.insert(inter1);

    // -------------
    // --- Model ---
    // -------------

    SP::Model Pendulum(new Model(t0, T, allDS, allInteractions));

    // ----------------
    // --- Simulation ---
    // ----------------

    // -- Time discretisation --
    SP::TimeDiscretisation t(new TimeDiscretisation(t0, h));

    SP::TimeStepping s(new TimeStepping(t));
    //        s->setUseRelativeConvergenceCriteron(true);

    // -- OneStepIntegrators --

    //double theta=0.500001;
    double theta = 0.500001;

    SP::Moreau OSI(new Moreau(doublependulum, theta));
    s->recordIntegrator(OSI);

    // -- OneStepNsProblem --
    IntParameters iparam(5);
    iparam[0] = 2001; // Max number of iteration
    DoubleParameters dparam(5);
    dparam[0] = 0.0000005; // Tolerance
    string solverName = "Lemke" ;
    SP::NonSmoothSolver mySolver(new NonSmoothSolver(solverName, iparam, dparam));
    SP::OneStepNSProblem osnspb(new LCP(mySolver));
    s->recordNonSmoothProblem(osnspb);

    cout << "=== End of model loading === " << endl;

    // =========================== End of model definition ===========================  dataPlot(k,7) = (inter->getY(0))(0);

    // ================================= Computation =================================

    // --- Simulation initialization ---
    Pendulum->initialize(s);
    cout << "End of simulation initialisation" << endl;

    int k = 0;
    int N = (int)((T - t0) / h) + 1;
    cout << "Number of time step   " << N << endl;
    // --- Get the values to be plotted ---
    // -> saved in a matrix dataPlot
    unsigned int outputSize = 12;
    SimpleMatrix dataPlot(N + 1, outputSize);
    // For the initial time step:
    // time
    SP::SiconosVector q = doublependulum->getQPtr();
    SP::SiconosVector v = doublependulum->getVelocityPtr();

    dataPlot(k, 0) =  t0;
    dataPlot(k, 1) = (*q)(0);
    dataPlot(k, 2) = (*v)(0);
    dataPlot(k, 3) = (*q)(1);
    dataPlot(k, 4) = (*v)(1);
    dataPlot(k, 5) =  l1 * sin((*q)(0));
    dataPlot(k, 6) = -l1 * cos((*q)(0));
    dataPlot(k, 7) =  l1 * sin((*q)(0)) + l2 * sin((*q)(1));
    dataPlot(k, 8) = -l1 * cos((*q)(0)) - l2 * cos((*q)(1));
    dataPlot(k, 9) =  l1 * cos((*q)(0)) * ((*v)(0));
    dataPlot(k, 10) = l1 * cos((*q)(0)) * ((*v)(0)) + l2 * cos((*q)(1)) * ((*v)(1));

    boost::timer time;
    time.restart();

    // --- Time loop ---
    cout << "Start computation ... " << endl;

    boost::progress_display show_progress(N);

    while (k < N)
    {
      k++;
      ++show_progress;
      //  if (!(div(k,1000).rem))  cout <<"Step number "<< k << "\n";

      // Solve problem
      s->newtonSolve(criterion, maxIter);
      // Data Output
      dataPlot(k, 0) =  s->getNextTime();
      dataPlot(k, 1) = (*q)(0);
      dataPlot(k, 2) = (*v)(0);
      dataPlot(k, 3) = (*q)(1);
      dataPlot(k, 4) = (*v)(1);
      dataPlot(k, 5) =  l1 * sin((*q)(0));
      dataPlot(k, 6) = -l1 * cos((*q)(0));
      dataPlot(k, 7) =  l1 * sin((*q)(0)) + l2 * sin((*q)(1));
      dataPlot(k, 8) = -l1 * cos((*q)(0)) - l2 * cos((*q)(1));
      dataPlot(k, 9) =  l1 * cos((*q)(0)) * ((*v)(0));
      dataPlot(k, 10) = l1 * cos((*q)(0)) * ((*v)(0)) + l2 * cos((*q)(1)) * ((*v)(1));
      s->nextStep();
    }

    cout << "End of computation - Number of iterations done: " << k << endl;
    cout << "Computation Time " << time.elapsed()  << endl;

    // --- Output files ---
    ioMatrix out("DoublePendulumResult.dat", "ascii");
    out.write(dataPlot, "noDim");
  }

  catch (SiconosException e)
  {
    cout << e.report() << endl;
  }
  catch (...)
  {
    cout << "Exception caught in \'sample/MultiBeadsColumn\'" << endl;
  }
}
<|endoftext|>


<commit_before>/***************************************************************************
 *                                                                         *
 * Copyright (C) 2007-2015 by frePPLe bvba                                 *
 *                                                                         *
 * This library is free software; you can redistribute it and/or modify it *
 * under the terms of the GNU Affero General Public License as published   *
 * by the Free Software Foundation; either version 3 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 of          *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
 * GNU Affero General Public License for more details.                     *
 *                                                                         *
 * You should have received a copy of the GNU Affero General Public        *
 * License along with this program.                                        *
 * If not, see <http://www.gnu.org/licenses/>.                             *
 *                                                                         *
 ***************************************************************************/

#define FREPPLE_CORE
#include "frepple/model.h"

namespace frepple
{

void Resource::updateProblems()
{
  // Delete existing problems for this resource
  Problem::clearProblems(*this, true, false);

  // Problem detection disabled on this resource
  if (!getDetectProblems()) return;

  // Loop through the loadplans
  Date excessProblemStart;
  Date shortageProblemStart;
  bool excessProblem = false;
  bool shortageProblem = false;
  double curMax(0.0);
  double shortageQty(0.0);
  double curMin(0.0);
  double excessQty(0.0);
  for (loadplanlist::const_iterator iter = loadplans.begin();
      iter != loadplans.end(); )
  {
    // Process changes in the maximum or minimum targets
    if (iter->getEventType() == 4)
      curMax = iter->getMax();
    else if (iter->getEventType() == 3)
      curMin = iter->getMin();

    // Only consider the last loadplan for a certain date
    const TimeLine<LoadPlan>::Event *f = &*(iter++);
    if (iter!=loadplans.end() && iter->getDate()==f->getDate()) continue;

    // Check against minimum target
    double delta = f->getOnhand() - curMin;
    if (delta < -ROUNDING_ERROR)
    {
      if (!shortageProblem)
      {
        shortageProblemStart = f->getDate();
        shortageQty = delta;
        shortageProblem = true;
      }
      else if (delta < shortageQty)
        // New shortage qty
        shortageQty = delta;
    }
    else
    {
      if (shortageProblem)
      {
        // New problem now ends
        if (f->getDate() != shortageProblemStart)
          new ProblemCapacityUnderload(this, DateRange(shortageProblemStart,
              f->getDate()), -shortageQty);
        shortageProblem = false;
      }
    }

    // Note that theoretically we can have a minimum and a maximum problem for
    // the same moment in time.

    // Check against maximum target
    delta = f->getOnhand() - curMax;
    if (delta > ROUNDING_ERROR)
    {
      if (!excessProblem)
      {
        excessProblemStart = f->getDate();
        excessQty = delta;
        excessProblem = true;
      }
      else if (delta > excessQty)
        excessQty = delta;
    }
    else
    {
      if (excessProblem)
      {
        // New problem now ends
        if (f->getDate() != excessProblemStart)
          new ProblemCapacityOverload(this, excessProblemStart,
              f->getDate(), excessQty);
        excessProblem = false;
      }
    }

  }  // End of for-loop through the loadplans

  // The excess lasts till the end of the horizon...
  if (excessProblem)
    new ProblemCapacityOverload(this, excessProblemStart,
        Date::infiniteFuture, excessQty);

  // The shortage lasts till the end of the horizon...
  if (shortageProblem)
    new ProblemCapacityUnderload(this, DateRange(shortageProblemStart,
        Date::infiniteFuture), -shortageQty);
}

void ResourceBuckets::updateProblems()
{
  // Delete existing problems for this resource
  Problem::clearProblems(*this, true, false);

  // Problem detection disabled on this resource
  if (!getDetectProblems()) return;

  // Loop over all events
  Date startdate = Date::infinitePast;
  double load = 0.0;
  for (loadplanlist::const_iterator iter = loadplans.begin();
      iter != loadplans.end(); iter++)
  {
    if (iter->getEventType() != 2)
      load = iter->getOnhand();
    else
    {
      // Evaluate previous bucket
      if (load < 0.0)
        new ProblemCapacityOverload(this, startdate,
          iter->getDate(), -load);
      // Reset evaluation for the new bucket
      startdate = iter->getDate();
      load = 0.0;
    }
  }
  // Evaluate the final bucket
  if (load < 0.0)
    new ProblemCapacityOverload(this, startdate,
      Date::infiniteFuture, -load);
}

string ProblemCapacityUnderload::getDescription() const
{
  ostringstream ch;
  ch << "Resource '" << getResource() << "' has excess capacity of " << qty;
  return ch.str();
}

string ProblemCapacityOverload::getDescription() const
{
  ostringstream ch;
  ch << "Resource '" << getResource() << "' has capacity shortage of " << qty;
  return ch.str();
}

}
<commit_msg>rounding issue causing false capacity problems on bucketized resources<commit_after>/***************************************************************************
 *                                                                         *
 * Copyright (C) 2007-2015 by frePPLe bvba                                 *
 *                                                                         *
 * This library is free software; you can redistribute it and/or modify it *
 * under the terms of the GNU Affero General Public License as published   *
 * by the Free Software Foundation; either version 3 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 of          *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
 * GNU Affero General Public License for more details.                     *
 *                                                                         *
 * You should have received a copy of the GNU Affero General Public        *
 * License along with this program.                                        *
 * If not, see <http://www.gnu.org/licenses/>.                             *
 *                                                                         *
 ***************************************************************************/

#define FREPPLE_CORE
#include "frepple/model.h"

namespace frepple
{

void Resource::updateProblems()
{
  // Delete existing problems for this resource
  Problem::clearProblems(*this, true, false);

  // Problem detection disabled on this resource
  if (!getDetectProblems()) return;

  // Loop through the loadplans
  Date excessProblemStart;
  Date shortageProblemStart;
  bool excessProblem = false;
  bool shortageProblem = false;
  double curMax(0.0);
  double shortageQty(0.0);
  double curMin(0.0);
  double excessQty(0.0);
  for (loadplanlist::const_iterator iter = loadplans.begin();
      iter != loadplans.end(); )
  {
    // Process changes in the maximum or minimum targets
    if (iter->getEventType() == 4)
      curMax = iter->getMax();
    else if (iter->getEventType() == 3)
      curMin = iter->getMin();

    // Only consider the last loadplan for a certain date
    const TimeLine<LoadPlan>::Event *f = &*(iter++);
    if (iter!=loadplans.end() && iter->getDate()==f->getDate()) continue;

    // Check against minimum target
    double delta = f->getOnhand() - curMin;
    if (delta < -ROUNDING_ERROR)
    {
      if (!shortageProblem)
      {
        shortageProblemStart = f->getDate();
        shortageQty = delta;
        shortageProblem = true;
      }
      else if (delta < shortageQty)
        // New shortage qty
        shortageQty = delta;
    }
    else
    {
      if (shortageProblem)
      {
        // New problem now ends
        if (f->getDate() != shortageProblemStart)
          new ProblemCapacityUnderload(this, DateRange(shortageProblemStart,
              f->getDate()), -shortageQty);
        shortageProblem = false;
      }
    }

    // Note that theoretically we can have a minimum and a maximum problem for
    // the same moment in time.

    // Check against maximum target
    delta = f->getOnhand() - curMax;
    if (delta > ROUNDING_ERROR)
    {
      if (!excessProblem)
      {
        excessProblemStart = f->getDate();
        excessQty = delta;
        excessProblem = true;
      }
      else if (delta > excessQty)
        excessQty = delta;
    }
    else
    {
      if (excessProblem)
      {
        // New problem now ends
        if (f->getDate() != excessProblemStart)
          new ProblemCapacityOverload(this, excessProblemStart,
              f->getDate(), excessQty);
        excessProblem = false;
      }
    }

  }  // End of for-loop through the loadplans

  // The excess lasts till the end of the horizon...
  if (excessProblem)
    new ProblemCapacityOverload(this, excessProblemStart,
        Date::infiniteFuture, excessQty);

  // The shortage lasts till the end of the horizon...
  if (shortageProblem)
    new ProblemCapacityUnderload(this, DateRange(shortageProblemStart,
        Date::infiniteFuture), -shortageQty);
}

void ResourceBuckets::updateProblems()
{
  // Delete existing problems for this resource
  Problem::clearProblems(*this, true, false);

  // Problem detection disabled on this resource
  if (!getDetectProblems()) return;

  // Loop over all events
  Date startdate = Date::infinitePast;
  double load = 0.0;
  for (loadplanlist::const_iterator iter = loadplans.begin();
      iter != loadplans.end(); iter++)
  {
    if (iter->getEventType() != 2)
      load = iter->getOnhand();
    else
    {
      // Evaluate previous bucket
      if (load < - ROUNDING_ERROR)
        new ProblemCapacityOverload(this, startdate, iter->getDate(), -load);
      // Reset evaluation for the new bucket
      startdate = iter->getDate();
      load = 0.0;
    }
  }
  // Evaluate the final bucket
  if (load < - ROUNDING_ERROR)
    new ProblemCapacityOverload(this, startdate, Date::infiniteFuture, -load);
}

string ProblemCapacityUnderload::getDescription() const
{
  ostringstream ch;
  ch << "Resource '" << getResource() << "' has excess capacity of " << qty;
  return ch.str();
}

string ProblemCapacityOverload::getDescription() const
{
  ostringstream ch;
  ch << "Resource '" << getResource() << "' has capacity shortage of " << qty;
  return ch.str();
}

}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/

#ifndef TAO_PEGTL_CONTRIB_PARSE_TREE_HPP
#define TAO_PEGTL_CONTRIB_PARSE_TREE_HPP

#include <cassert>
#include <memory>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>

#include "../config.hpp"
#include "../normal.hpp"
#include "../nothing.hpp"
#include "../parse.hpp"

#include "../internal/demangle.hpp"
#include "../internal/iterator.hpp"

namespace tao
{
   namespace TAO_PEGTL_NAMESPACE
   {
      namespace parse_tree
      {
         struct node
         {
            std::vector< std::unique_ptr< node > > children;

            const std::type_info* id_ = nullptr;
            internal::iterator begin_;
            internal::iterator end_;
            std::string source_;

            // each node will be default constructed
            node() = default;

            // no copy/move is necessary
            // (nodes are always owned/handled by a std::unique_ptr)
            node( const node& ) = delete;
            node( node&& ) = delete;

            ~node() = default;

            // no assignment either
            node& operator=( const node& ) = delete;
            node& operator=( node&& ) = delete;

            bool is_root() const noexcept
            {
               return id_ == nullptr;
            }

            std::string name() const
            {
               assert( !is_root() );
               return TAO_PEGTL_NAMESPACE::internal::demangle( id_->name() );
            }

            position begin() const
            {
               return position( begin_, source_ );
            }

            position end() const
            {
               return position( end_, source_ );
            }

            const std::string& source() const noexcept
            {
               return source_;
            }

            void remove_content() noexcept
            {
               end_.reset();
            }

            bool has_content() const noexcept
            {
               return end_.data != nullptr;
            }

            std::string content() const
            {
               assert( has_content() );
               return std::string( begin_.data, end_.data );
            }

            // all non-root nodes are initialized by calling this method
            template< typename Rule, typename Input >
            void start( const Input& in )
            {
               begin_ = in.iterator();
               source_ = in.source();
            }

            // if parsing of the rule succeeded, this method is called
            template< typename Rule, typename Input >
            void success( const Input& in )
            {
               id_ = &typeid( Rule );
               end_ = in.iterator();
               // idea: check for source == in.source()?
            }

            // if parsing succeeded and the (optional) transform call
            // did not discard the node, it is appended to its parent.
            // note that "child" is the node whose Rule just succeeded
            // and *this is the parent where the node should be appended.
            void append( std::unique_ptr< node > child )
            {
               assert( child );
               children.emplace_back( std::move( child ) );
            }
         };

         namespace internal
         {
            template< typename Node >
            struct state
            {
               std::vector< std::unique_ptr< Node > > stack;

               state()
               {
                  emplace_back();
               }

               void emplace_back()
               {
                  stack.emplace_back( std::unique_ptr< Node >( new Node ) );  // NOLINT: std::make_unique requires C++14
               }

               std::unique_ptr< Node >& back() noexcept
               {
                  return stack.back();
               }

               void pop_back() noexcept
               {
                  return stack.pop_back();
               }
            };

            template< typename Node, typename S, typename = void >
            struct transform
            {
               static void call( std::unique_ptr< Node >& /*unused*/ ) noexcept
               {
               }
            };

            template< typename Node, typename S >
            struct transform< Node, S, decltype( S::transform( std::declval< std::unique_ptr< Node >& >() ), void() ) >
            {
               static void call( std::unique_ptr< Node >& n ) noexcept( noexcept( S::transform( n ) ) )
               {
                  S::transform( n );
               }
            };

            template< template< typename > class S >
            struct make_control
            {
               template< typename Rule, bool = S< Rule >::value >
               struct control;

               template< typename Rule >
               using type = control< Rule >;
            };

            template< template< typename > class S >
            template< typename Rule >
            struct make_control< S >::control< Rule, false >
               : normal< Rule >
            {
            };

            template< template< typename > class S >
            template< typename Rule >
            struct make_control< S >::control< Rule, true >
               : normal< Rule >
            {
               template< typename Input, typename Node >
               static void start( const Input& in, state< Node >& st )
               {
                  st.emplace_back();
                  st.back()->template start< Rule >( in );
               }

               template< typename Input, typename Node >
               static void success( const Input& in, state< Node >& st )
               {
                  auto n = std::move( st.back() );
                  st.pop_back();
                  n->template success< Rule >( in );
                  transform< Node, S< Rule > >::call( n );
                  if( n ) {
                     st.back()->append( std::move( n ) );
                  }
               }

               template< typename Input, typename Node >
               static void failure( const Input& /*unused*/, state< Node >& st ) noexcept
               {
                  st.pop_back();
               }
            };

            template< typename >
            struct store_all : std::true_type
            {
            };

         }  // namespace internal

         // some nodes don't need to store their content,
         // use this helper as a base class to derive your specialization from.
         struct remove_content : std::true_type
         {
            template< typename Node >
            static void transform( std::unique_ptr< Node >& n ) noexcept( noexcept( n->remove_content() ) )
            {
               n->remove_content();
            }
         };

         template< typename Rule, typename Node, template< typename > class S = internal::store_all, typename Input >
         std::unique_ptr< Node > parse( Input& in )
         {
            internal::state< Node > st;
            if( !TAO_PEGTL_NAMESPACE::parse< Rule, nothing, internal::make_control< S >::template type >( in, st ) ) {
               return nullptr;
            }
            assert( st.stack.size() == 1 );
            return std::move( st.back() );
         }

         template< typename Rule, template< typename > class S = internal::store_all, typename Input >
         std::unique_ptr< node > parse( Input& in )
         {
            return parse< Rule, node, S >( in );
         }

      }  // namespace parse_tree

   }  // namespace TAO_PEGTL_NAMESPACE

}  // namespace tao

#endif
<commit_msg>Pass-through of additional states<commit_after>// Copyright (c) 2017-2018 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/

#ifndef TAO_PEGTL_CONTRIB_PARSE_TREE_HPP
#define TAO_PEGTL_CONTRIB_PARSE_TREE_HPP

#include <cassert>
#include <memory>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>

#include "../config.hpp"
#include "../normal.hpp"
#include "../nothing.hpp"
#include "../parse.hpp"

#include "../internal/demangle.hpp"
#include "../internal/iterator.hpp"

namespace tao
{
   namespace TAO_PEGTL_NAMESPACE
   {
      namespace parse_tree
      {
         struct node
         {
            std::vector< std::unique_ptr< node > > children;

            const std::type_info* id_ = nullptr;
            internal::iterator begin_;
            internal::iterator end_;
            std::string source_;

            // each node will be default constructed
            node() = default;

            // no copy/move is necessary
            // (nodes are always owned/handled by a std::unique_ptr)
            node( const node& ) = delete;
            node( node&& ) = delete;

            ~node() = default;

            // no assignment either
            node& operator=( const node& ) = delete;
            node& operator=( node&& ) = delete;

            bool is_root() const noexcept
            {
               return id_ == nullptr;
            }

            std::string name() const
            {
               assert( !is_root() );
               return TAO_PEGTL_NAMESPACE::internal::demangle( id_->name() );
            }

            position begin() const
            {
               return position( begin_, source_ );
            }

            position end() const
            {
               return position( end_, source_ );
            }

            const std::string& source() const noexcept
            {
               return source_;
            }

            bool has_content() const noexcept
            {
               return end_.data != nullptr;
            }

            std::string content() const
            {
               assert( has_content() );
               return std::string( begin_.data, end_.data );
            }

            // all non-root nodes are initialized by calling this method
            template< typename Rule, typename Input, typename... States >
            void start( const Input& in, States&&... /*unused*/ )
            {
               begin_ = in.iterator();
               source_ = in.source();
            }

            // if parsing of the rule succeeded, this method is called
            template< typename Rule, typename Input, typename... States >
            void success( const Input& in, States&&... /*unused*/ )
            {
               id_ = &typeid( Rule );
               end_ = in.iterator();
            }

            // if parsing succeeded and the (optional) transform call
            // did not discard the node, it is appended to its parent.
            // note that "child" is the node whose Rule just succeeded
            // and *this is the parent where the node should be appended.
            template< typename... States >
            void append( std::unique_ptr< node > child, States&&... /*unused*/ )
            {
               assert( child );
               children.emplace_back( std::move( child ) );
            }

            template< typename... States >
            void remove_content( States&&... /*unused*/ ) noexcept
            {
               end_.reset();
            }
         };

         namespace internal
         {
            template< typename Node >
            struct state
            {
               std::vector< std::unique_ptr< Node > > stack;

               state()
               {
                  emplace_back();
               }

               void emplace_back()
               {
                  stack.emplace_back( std::unique_ptr< Node >( new Node ) );  // NOLINT: std::make_unique requires C++14
               }

               std::unique_ptr< Node >& back() noexcept
               {
                  return stack.back();
               }

               void pop_back() noexcept
               {
                  return stack.pop_back();
               }
            };

            template< typename Node, typename S, typename = void >
            struct transform
            {
               template< typename... States >
               static void call( std::unique_ptr< Node >& /*unused*/, States&&... /*unused*/ ) noexcept
               {
               }
            };

            template< typename Node, typename S >
            struct transform< Node, S, decltype( S::transform( std::declval< std::unique_ptr< Node >& >() ), void() ) >
            {
               template< typename... States >
               static void call( std::unique_ptr< Node >& n, States&&... st ) noexcept( noexcept( S::transform( n ) ) )
               {
                  S::transform( n, st... );
               }
            };

            template< template< typename > class S >
            struct make_control
            {
               template< typename Rule, bool = S< Rule >::value >
               struct control;

               template< typename Rule >
               using type = control< Rule >;
            };

            template< template< typename > class S >
            template< typename Rule >
            struct make_control< S >::control< Rule, false >
               : normal< Rule >
            {
            };

            template< template< typename > class S >
            template< typename Rule >
            struct make_control< S >::control< Rule, true >
               : normal< Rule >
            {
               template< typename Input, typename Node, typename... States >
               static void start( const Input& in, state< Node >& state, States&&... st )
               {
                  state.emplace_back();
                  state.back()->template start< Rule >( in, st... );
               }

               template< typename Input, typename Node, typename... States >
               static void success( const Input& in, state< Node >& state, States&&... st )
               {
                  auto n = std::move( state.back() );
                  state.pop_back();
                  n->template success< Rule >( in, st... );
                  transform< Node, S< Rule > >::call( n, st... );
                  if( n ) {
                     state.back()->append( std::move( n ), st... );
                  }
               }

               template< typename Input, typename Node, typename... States >
               static void failure( const Input& /*unused*/, state< Node >& state, States&&... /*unused*/ ) noexcept
               {
                  state.pop_back();
               }
            };

            template< typename >
            struct store_all : std::true_type
            {
            };

         }  // namespace internal

         // some nodes don't need to store their content,
         // use this helper as a base class to derive your specialization from.
         struct remove_content : std::true_type
         {
            template< typename Node, typename... States >
            static void transform( std::unique_ptr< Node >& n, States&&... st ) noexcept( noexcept( n->remove_content( st... ) ) )
            {
               n->remove_content( st... );
            }
         };

         template< typename Rule, typename Node, template< typename > class S = internal::store_all, typename Input, typename... States >
         std::unique_ptr< Node > parse( Input& in, States&&... st )
         {
            internal::state< Node > state;
            if( !TAO_PEGTL_NAMESPACE::parse< Rule, nothing, internal::make_control< S >::template type >( in, state, st... ) ) {
               return nullptr;
            }
            assert( state.stack.size() == 1 );
            return std::move( state.back() );
         }

         template< typename Rule, template< typename > class S = internal::store_all, typename Input, typename... States >
         std::unique_ptr< node > parse( Input& in, States&&... st )
         {
            return parse< Rule, node, S >( in, st... );
         }

      }  // namespace parse_tree

   }  // namespace TAO_PEGTL_NAMESPACE

}  // namespace tao

#endif
<|endoftext|>


<commit_before>#include "magic.h"

using namespace cv;

Point point1, point2; /* vertical points of the bounding box */
bool drag = false;
bool select_flag = false;
Rect rect; /* bounding box */
Mat img, roiImg; /* roiImg - the part of the image in the bounding box */

void mouseHandler(int event, int x, int y, int flags, void* param){
    if (event == CV_EVENT_LBUTTONDOWN && !drag){
        /* left button clicked. ROI selection begins */
        point1 = Point(x, y);
        drag = true;
    }

    if (event == CV_EVENT_MOUSEMOVE && drag){
        /* mouse dragged. ROI being selected */
        Mat img1 = img.clone();
        point2 = Point(x, y);
        rectangle(img1, point1, point2, CV_RGB(255, 0, 0), 3, 8, 0);
        imshow("image", img1);
    }

    if (event == CV_EVENT_LBUTTONUP && drag){
        point2 = Point(x, y);
        rect = Rect(point1.x,point1.y,x-point1.x,y-point1.y);
        drag = false;
        roiImg = img(rect);
    }

    if (event == CV_EVENT_LBUTTONUP){
       /* ROI selected */
        select_flag = true;
        drag = false;
    }
}

int main(int argc, char* argv[]){
  if(argc < 2){
    printf("usage: Magic <Video_Path> [output_dir] [skip_frames] [background]\n");
  }

  std::string output;
  if(argc >= 3) {
    output = argv[2];
  } else {
    output = ".";
  }

  int skip_frames = 0;
  if(argc >= 4) {
    skip_frames = std::atoi(argv[3]);
  }

  VideoCapture cap(argv[1]); // load the video
  if(!cap.isOpened())  // check if we succeeded
        return -1;

  // Write some info about File
  int frames = cap.get(CV_CAP_PROP_FRAME_COUNT);
  double fps = cap.get(CV_CAP_PROP_FPS);
  printf("Number of Frames: %d\n", frames);
  printf("FPS: %f\n", fps);

  // Contains the working image
  Mat dst, in, temp;

  if(argc >= 5) {
    in = imread(argv[4]);
  } else {
    cap >> in;
  }

  img = in.clone();
  imshow("image", img);
  int k;
  while(1){
    cvSetMouseCallback("image", mouseHandler, NULL);
    if (select_flag){
        imshow("ROI", roiImg);  //show the image bounded by the box
    }
    rectangle(img, rect, CV_RGB(255, 0, 0), 3, 8, 0);
    imshow("image", img);
    k = waitKey(10);
    // if(k != -1) {
    //   printf("Key pressed %d\n", k);
    // }
    if (k == 27){ //ESC
        destroyAllWindows();
        waitKey(1);
        break;
    }
  }

  if(!select_flag){
    rect = Rect(0,0,img.cols, img.rows);
  }

  // Background
  temp = in(rect);
  Mat background;
  cvtColor(temp, background, CV_RGB2GRAY);

  vector<Vec3f> circles;

  /// Apply the Hough Transform to find the circles
  HoughCircles(background, circles, CV_HOUGH_GRADIENT, 1, 100, 200, 100, 0, 0);

  /// Draw the circles detected
  for( size_t i = 0; i < circles.size(); i++ )
  {
      Point2f center(circles[i][0], circles[i][1]);
      float radius = circles[i][2];
      // circle center
      circle(temp, center, 3, Scalar(0,255,0), -1, 8, 0 );
      // circle outline
      circle(temp, center, radius, Scalar(0,0,255), 3, 8, 0 );
      printf("Circle %d with r=%f at x=%f, y=%f\n", i, radius, center.x, center.y);
   }

  imshow("image", temp);
  waitKey(0);
  destroyWindow("image");
  waitKey(1);

  //Set image parameter
  vector<int> imageout_params;
  imwrite(output + "/bg_circle.tiff", temp, imageout_params);
  imwrite(output + "/bg.tiff", background, imageout_params);

  std::vector<Point2i> points;
  Point2f center;
  float radius;

  std::ofstream fs (output + "/positions.csv", std::ofstream::out);
  fs << "Frame,Time,Radius,x,y\n";

  for(int i = 0; i <= frames-1;i++){
    cap >> in;
    if(i > skip_frames) {
    temp = in(rect);
    cvtColor(temp, dst, CV_RGB2GRAY, 0);
    absdiff(dst, background, temp);
    threshold(temp, dst, 30, 255, THRESH_BINARY);

    points.clear();
    findNonZero(dst, points);
    minEnclosingCircle(points, center, radius);

    fs << i <<","<< i/fps <<","<< radius <<","<< center.x <<","<< center.y << "\n";
    std::cout << "\r" << i << "/" << frames;
    // imwrite(output + "/test-"+ std::to_string(i)+".tiff", dst, imageout_params);
    }
  }

  fs << std::endl;
  fs.close();
}<commit_msg>adds todo<commit_after>#include "magic.h"

using namespace cv;

Point point1, point2; /* vertical points of the bounding box */
bool drag = false;
bool select_flag = false;
Rect rect; /* bounding box */
Mat img, roiImg; /* roiImg - the part of the image in the bounding box */

void mouseHandler(int event, int x, int y, int flags, void* param){
    if (event == CV_EVENT_LBUTTONDOWN && !drag){
        /* left button clicked. ROI selection begins */
        point1 = Point(x, y);
        drag = true;
    }

    if (event == CV_EVENT_MOUSEMOVE && drag){
        /* mouse dragged. ROI being selected */
        Mat img1 = img.clone();
        point2 = Point(x, y);
        rectangle(img1, point1, point2, CV_RGB(255, 0, 0), 3, 8, 0);
        imshow("image", img1);
    }

    if (event == CV_EVENT_LBUTTONUP && drag){
        point2 = Point(x, y);
        rect = Rect(point1.x,point1.y,x-point1.x,y-point1.y);
        drag = false;
        roiImg = img(rect);
    }

    if (event == CV_EVENT_LBUTTONUP){
       /* ROI selected */
        select_flag = true;
        drag = false;
    }
}

int main(int argc, char* argv[]){
  if(argc < 2){
    printf("usage: Magic <Video_Path> [output_dir] [skip_frames] [background]\n");
  }

  std::string output;
  if(argc >= 3) {
    output = argv[2];
  } else {
    output = ".";
  }

  int skip_frames = 0;
  if(argc >= 4) {
    skip_frames = std::atoi(argv[3]);
  }

  VideoCapture cap(argv[1]); // load the video
  if(!cap.isOpened())  // check if we succeeded
        return -1;

  // Write some info about File
  int frames = cap.get(CV_CAP_PROP_FRAME_COUNT);
  double fps = cap.get(CV_CAP_PROP_FPS);
  printf("Number of Frames: %d\n", frames);
  printf("FPS: %f\n", fps);

  // Contains the working image
  Mat dst, in, temp;

  if(argc >= 5) {
    in = imread(argv[4]);
  } else {
    cap >> in;
  }

  img = in.clone();
  imshow("image", img);
  int k;
  while(1){
    cvSetMouseCallback("image", mouseHandler, NULL);
    if (select_flag){
        imshow("ROI", roiImg);  //show the image bounded by the box
    }
    rectangle(img, rect, CV_RGB(255, 0, 0), 3, 8, 0);
    imshow("image", img);
    k = waitKey(10);
    // if(k != -1) {
    //   printf("Key pressed %d\n", k);
    // }
    if (k == 27){ //ESC
        destroyAllWindows();
        waitKey(1);
        break;
    }
  }

  if(!select_flag){
    rect = Rect(0,0,img.cols, img.rows);
  }

  // Background
  temp = in(rect);
  Mat background;
  cvtColor(temp, background, CV_RGB2GRAY);

  vector<Vec3f> circles;

  /// Apply the Hough Transform to find the circles
  HoughCircles(background, circles, CV_HOUGH_GRADIENT, 1, 100, 200, 100, 0, 0);

  /// Draw the circles detected
  for( size_t i = 0; i < circles.size(); i++ )
  {
      Point2f center(circles[i][0], circles[i][1]);
      float radius = circles[i][2];
      // circle center
      circle(temp, center, 3, Scalar(0,255,0), -1, 8, 0 );
      // circle outline
      circle(temp, center, radius, Scalar(0,0,255), 3, 8, 0 );
      printf("Circle %d with r=%f at x=%f, y=%f\n", i, radius, center.x, center.y);
   }

  imshow("image", temp);
  waitKey(0);
  destroyWindow("image");
  waitKey(1);

  //Set image parameter
  vector<int> imageout_params;
  imwrite(output + "/bg_circle.tiff", temp, imageout_params);
  imwrite(output + "/bg.tiff", background, imageout_params);

  std::vector<Point2i> points;
  Point2f center;
  float radius;

  std::ofstream fs (output + "/positions.csv", std::ofstream::out);
  fs << "Frame,Time,Radius,x,y\n";

  for(int i = 0; i <= frames-1;i++){
    cap >> in;
    if(i > skip_frames) {
    temp = in(rect);
    cvtColor(temp, dst, CV_RGB2GRAY, 0);
    absdiff(dst, background, temp);
    threshold(temp, dst, 30, 255, THRESH_BINARY);

    points.clear();
    // if there is no non zero point this throws an exception.
    findNonZero(dst, points);
    minEnclosingCircle(points, center, radius);

    fs << i <<","<< i/fps <<","<< radius <<","<< center.x <<","<< center.y << "\n";
    std::cout << "\r" << i << "/" << frames;
    // imwrite(output + "/test-"+ std::to_string(i)+".tiff", dst, imageout_params);
    }
  }

  fs << std::endl;
  fs.close();
}<|endoftext|>