<commit_before>/*  LibMary - C++ library for high-performance network servers
    Copyright (C) 2011 Dmitry Shatrov

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <libmary/types.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>

#include <libmary/util_posix.h>
#include <libmary/log.h>

#include <libmary/epoll_poll_group.h>

namespace M {

namespace {
LogGroup libMary_logGroup_epoll ("epoll", LogLevel::D);
}

mt_mutex (mutex) void
EpollPollGroup::processPollableDeletionQueue ()
{
    PollableDeletionQueue::iter iter (pollable_deletion_queue);
    while (!pollable_deletion_queue.iter_done (iter)) {
	PollableEntry * const pollable_entry = pollable_deletion_queue.iter_next (iter);
	delete pollable_entry;
    }
    pollable_deletion_queue.clear ();
}

mt_throws Result
EpollPollGroup::triggerPipeWrite ()
{
    return commonTriggerPipeWrite (trigger_pipe [1]);
}

mt_throws PollGroup::PollableKey
EpollPollGroup::addPollable (CbDesc<Pollable> const &pollable,
			     DeferredProcessor::Registration * const ret_reg,
			     bool const activate)
{
    PollableEntry * const pollable_entry = new PollableEntry;

    logD (epoll, _func, "0x", fmt_hex, (UintPtr) this, ": "
	  "pollable_entry: 0x", fmt_hex, (UintPtr) pollable_entry, ", "
	  "cb_data: 0x", fmt_hex, (UintPtr) pollable.cb_data);

    pollable_entry->epoll_poll_group = this;
    pollable_entry->pollable = pollable;
    // We're making an unsafe call, assuming that the pollable is available.
    pollable_entry->fd = pollable->getFd (pollable.cb_data);
    pollable_entry->valid = true;

    mutex.lock ();
    pollable_list.append (pollable_entry);
    mutex.unlock ();

    if (activate) {
	if (!doActivate (pollable_entry))
	    goto _failure;
    }

    if (ret_reg)
	ret_reg->setDeferredProcessor (&deferred_processor);

    return pollable_entry;

_failure:
    mutex.lock ();
    pollable_list.remove (pollable_entry);
    mutex.unlock ();
    delete pollable_entry;

    return NULL;
}

mt_throws Result
EpollPollGroup::doActivate (PollableEntry * const mt_nonnull pollable_entry)
{
    logD (epoll, _func, "0x", fmt_hex, (UintPtr) this, ": "
	  "pollable_entry: 0x", fmt_hex, (UintPtr) pollable_entry);

    struct epoll_event event;
    event.events = EPOLLET | EPOLLIN | EPOLLOUT | EPOLLRDHUP;
    event.data.u64 = 0; // For valgrind.
    event.data.ptr = pollable_entry;
    int const res = epoll_ctl (efd, EPOLL_CTL_ADD, pollable_entry->fd, &event);
    if (res == -1) {
	exc_throw <PosixException> (errno);
	logE_ (_func, "epoll_ctl() failed: ", errnoString (errno));
	return Result::Failure;
    }

    if (res != 0) {
	exc_throw <InternalException> (InternalException::BackendMalfunction);
	logE_ (_func, "epoll_ctl(): unexpected return value: ", res);
	return Result::Failure;
    }

    if (!trigger ()) {
	logE_ (_func, "trigger() failed: ", exc->toString());
	return Result::Failure;
    }

    return Result::Success;
}

mt_throws Result
EpollPollGroup::activatePollable (PollableKey const mt_nonnull key)
{
    PollableEntry * const pollable_entry = static_cast <PollableEntry*> (key);

    logD (epoll, _func, "0x", fmt_hex, (UintPtr) this, ": "
	  "pollable_entry: 0x", fmt_hex, (UintPtr) pollable_entry);

    return doActivate (pollable_entry);
}

void
EpollPollGroup::removePollable (PollableKey const mt_nonnull key)
{
    PollableEntry * const pollable_entry = static_cast <PollableEntry*> (key);

    logD_ (_func, "pollable_entry: 0x", fmt_hex, (UintPtr) pollable_entry);

    {
	int const res = epoll_ctl (efd, EPOLL_CTL_DEL, pollable_entry->fd, NULL /* event */);
	if (res == -1) {
	    logE_ (_func, "epoll_ctl() failed: ", errnoString (errno));
	} else {
	    if (res != 0)
		logE_ (_func, "epoll_ctl(): unexpected return value: ", res);
	}
    }

    mutex.lock ();
    pollable_entry->valid = false;
    pollable_list.remove (pollable_entry);
    pollable_deletion_queue.append (pollable_entry);
    mutex.unlock ();
}

mt_throws Result
EpollPollGroup::poll (Uint64 const timeout_microsec)
{
    logD (epoll, _func, "timeout: ", timeout_microsec);

    Time const start_microsec = getTimeMicroseconds ();

    struct epoll_event events [4096];
    for (;;) {
	Time const cur_microsec = getTimeMicroseconds ();

	Time const elapsed_microsec = cur_microsec - start_microsec;

//	logD_ (_func, "start: ", start_microsec, ", cur: ", cur_microsec, ", elapsed: ", elapsed_microsec);

	int timeout;
	if (!got_deferred_tasks) {
	    if (timeout_microsec != (Uint64) -1) {
		if (timeout_microsec > elapsed_microsec) {
		    timeout = (timeout_microsec - elapsed_microsec) / 1000;
		    if (timeout == 0)
			timeout = 1;
		} else {
		    timeout = 0;
		}
	    } else {
		timeout = -1;
	    }
	} else {
	    // We've got deferred tasks to process, hence we shouldn't block.
	    timeout = 0;
	}

	int const nfds = epoll_wait (efd, events, sizeof (events) / sizeof (events [0]), timeout);
	if (nfds == -1) {
	    if (errno == EINTR)
		continue;

	    exc_throw <PosixException> (errno);
	    logE_ (_func, "epoll_wait() failed: ", errnoString (errno));
	    return Result::Failure;
	}

	if (nfds < 0 || (Size) nfds > sizeof (events) / sizeof (events [0])) {
	    logE_ (_func, "epoll_wait(): unexpected return value: ", nfds);
	    return Result::Failure;
	}

	got_deferred_tasks = false;

	if (frontend)
	    frontend.call (frontend->pollIterationBegin);

	bool trigger_pipe_ready = false;

	mutex.lock ();
	block_trigger_pipe = true;

	if (nfds > 0) {
	    // We keep the mutex locked to ensure that we see valid contents
	    // of PollableEntry objects.
	    for (int i = 0; i < nfds; ++i) {
		PollableEntry * const pollable_entry = static_cast <PollableEntry*> (events [i].data.ptr);
		uint32_t const epoll_event_flags = events [i].events;
		Uint32 event_flags = 0;

		if (pollable_entry == NULL) {
		  // Trigger pipe event.
		    if (epoll_event_flags & EPOLLIN)
			trigger_pipe_ready = true;

		    if (epoll_event_flags & EPOLLOUT)
			logW_ (_func, "Unexpected EPOLLOUT event for trigger pipe");

		    if (epoll_event_flags & EPOLLHUP   ||
			epoll_event_flags & EPOLLRDHUP ||
			epoll_event_flags & EPOLLERR)
		    {
			logE_ (_func, "Trigger pipe error: 0x", fmt_hex, epoll_event_flags);
		    }

		    continue;
		}

		// This check is not really necessary, but we have 'mutex' locked anyway.
		if (pollable_entry->valid) {
		    if (epoll_event_flags & EPOLLIN)
			event_flags |= PollGroup::Input;

		    if (epoll_event_flags & EPOLLOUT)
			event_flags |= PollGroup::Output;

		    if (epoll_event_flags & EPOLLHUP ||
			epoll_event_flags & EPOLLRDHUP)
		    {
			event_flags |= PollGroup::Hup;
		    }

		    if (epoll_event_flags & EPOLLERR)
			event_flags |= PollGroup::Error;

		    if (event_flags) {
			pollable_entry->pollable.call_mutex (
				pollable_entry->pollable->processEvents, mutex, /* ( */ event_flags /* ) */);
		    }
		}
	    } // for (;;) - for all fds.
	} // if (nfds > 0)

	processPollableDeletionQueue ();
	mutex.unlock ();

	bool trigger_break = false;
	{
	  // Dealing with trigger() logics.
	  // It is important to allow re-triggering the PollGroup before calling
	  // frontend->pollIterationEnd(), so that no trigger requests get lost.

	    if (trigger_pipe_ready) {
		if (!commonTriggerPipeRead (trigger_pipe [0]))
		    return Result::Failure;
	    }

	    mutex.lock ();
	    // TODO We can keep 'block_trigger_pipe' set till the next call to epoll_wait().
	    block_trigger_pipe = false;
	    if (triggered) {
		triggered = false;
		mutex.unlock ();
		trigger_break = true;
	    } else {
		mutex.unlock ();
	    }
	}

	if (frontend) {
	    bool extra_iteration_needed = false;
	    frontend.call_ret (&extra_iteration_needed, frontend->pollIterationEnd);
	    if (extra_iteration_needed)
		got_deferred_tasks = true;
	}

	if (deferred_processor.process ())
	    got_deferred_tasks = true;

	if (trigger_break) {
	    logD (epoll, _func, "trigger_break");
	    break;
	}

	if (elapsed_microsec >= timeout_microsec) {
	  // Timeout expired.
	    break;
	}
    } // for (;;)

    return Result::Success;
}

mt_throws Result
EpollPollGroup::trigger ()
{
    if (poll_tlocal && poll_tlocal == libMary_getThreadLocal()) {
	mutex.lock ();
	triggered = true;
	mutex.unlock ();
	return Result::Success;
    }

    mutex.lock ();
    triggered = true;
    if (block_trigger_pipe) {
	mutex.unlock ();
	return Result::Success;
    }
    mutex.unlock ();

    return triggerPipeWrite ();
}

mt_throws Result
EpollPollGroup::open ()
{
    efd =  epoll_create (1 /* size, unused */);
    if (efd == -1) {
	exc_throw <PosixException> (errno);
	logE_ (_func, "epoll_create() failed: ", errnoString (errno));
	return Result::Failure;
    }

    if (!posix_createNonblockingPipe (&trigger_pipe))
	return Result::Failure;

    {
	struct epoll_event event;
	event.events = EPOLLET | EPOLLIN | EPOLLRDHUP;
	event.data.u64 = 0; // For valgrind.
	event.data.ptr = NULL; // 'NULL' tells that this is trigger pipe.
	int const res = epoll_ctl (efd, EPOLL_CTL_ADD, trigger_pipe [0], &event);
	if (res == -1) {
	    exc_throw <PosixException> (errno);
	    logE_ (_func, "epoll_ctl() failed: ", errnoString (errno));
	    return Result::Failure;
	}

	if (res != 0) {
	    exc_throw <InternalException> (InternalException::BackendMalfunction);
	    logE_ (_func, "epoll_ctl(): unexpected return value: ", res);
	    return Result::Failure;
	}
    }

    return Result::Success;
}

namespace {
void deferred_processor_trigger (void * const active_poll_group_)
{
    ActivePollGroup * const active_poll_group = static_cast <ActivePollGroup*> (active_poll_group_);
    active_poll_group->trigger ();
}

DeferredProcessor::Backend deferred_processor_backend = {
    deferred_processor_trigger
};
}

EpollPollGroup::EpollPollGroup (Object * const coderef_container)
    : DependentCodeReferenced (coderef_container),
      efd (-1),
      triggered (false),
      block_trigger_pipe (false),
      // Initializing to 'true' to process deferred tasks scheduled before we
      // enter poll() the first time.
      got_deferred_tasks (true),
      poll_tlocal (NULL)
{
    trigger_pipe [0] = -1;
    trigger_pipe [1] = -1;

    deferred_processor.setBackend (CbDesc<DeferredProcessor::Backend> (
	    &deferred_processor_backend,
	    static_cast <ActivePollGroup*> (this) /* cb_data */,
	    NULL /* coderef_container */));
}

EpollPollGroup::~EpollPollGroup ()
{
    mutex.lock ();

    {
	PollableList::iter iter (pollable_list);
	while (!pollable_list.iter_done (iter)) {
	    PollableEntry * const pollable_entry = pollable_list.iter_next (iter);
	    delete pollable_entry;
	}
    }

    {
	PollableDeletionQueue::iter iter (pollable_deletion_queue);
	while (!pollable_deletion_queue.iter_done (iter)) {
	    PollableEntry * const pollable_entry = pollable_deletion_queue.iter_next (iter);
	    delete pollable_entry;
	}
    }

    mutex.unlock ();

    if (efd != -1) {
	for (;;) {
	    int const res = close (efd);
	    if (res == -1) {
		if (errno == EINTR)
		    continue;

		logE_ (_func, "close() failed (efd): ", errnoString (errno));
	    } else
	    if (res != 0) {
		logE_ (_func, "close() (efd): unexpected return value: ", res);
	    }

	    break;
	}
    }

    for (int i = 0; i < 2; ++i) {
	if (trigger_pipe [i] != -1) {
	    for (;;) {
		int const res = close (trigger_pipe [i]);
		if (res == -1) {
		    if (errno == EINTR)
			continue;

		    logE_ (_func, "close() failed (trigger_pipe[", i, "]): ", errnoString (errno));
		} else
		if (res != 0) {
		    logE_ (_func, "close(): unexpected return value (trigger_pipe[", i, "]): ", res);
		}

		break;
	    }
	}
    }
}

}

<commit_msg>EpollPollGroup: no debug logs by default<commit_after>/*  LibMary - C++ library for high-performance network servers
    Copyright (C) 2011 Dmitry Shatrov

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <libmary/types.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>

#include <libmary/util_posix.h>
#include <libmary/log.h>

#include <libmary/epoll_poll_group.h>

namespace M {

namespace {
LogGroup libMary_logGroup_epoll ("epoll", LogLevel::I);
}

mt_mutex (mutex) void
EpollPollGroup::processPollableDeletionQueue ()
{
    PollableDeletionQueue::iter iter (pollable_deletion_queue);
    while (!pollable_deletion_queue.iter_done (iter)) {
	PollableEntry * const pollable_entry = pollable_deletion_queue.iter_next (iter);
	delete pollable_entry;
    }
    pollable_deletion_queue.clear ();
}

mt_throws Result
EpollPollGroup::triggerPipeWrite ()
{
    return commonTriggerPipeWrite (trigger_pipe [1]);
}

mt_throws PollGroup::PollableKey
EpollPollGroup::addPollable (CbDesc<Pollable> const &pollable,
			     DeferredProcessor::Registration * const ret_reg,
			     bool const activate)
{
    PollableEntry * const pollable_entry = new PollableEntry;

    logD (epoll, _func, "0x", fmt_hex, (UintPtr) this, ": "
	  "pollable_entry: 0x", fmt_hex, (UintPtr) pollable_entry, ", "
	  "cb_data: 0x", fmt_hex, (UintPtr) pollable.cb_data);

    pollable_entry->epoll_poll_group = this;
    pollable_entry->pollable = pollable;
    // We're making an unsafe call, assuming that the pollable is available.
    pollable_entry->fd = pollable->getFd (pollable.cb_data);
    pollable_entry->valid = true;

    mutex.lock ();
    pollable_list.append (pollable_entry);
    mutex.unlock ();

    if (activate) {
	if (!doActivate (pollable_entry))
	    goto _failure;
    }

    if (ret_reg)
	ret_reg->setDeferredProcessor (&deferred_processor);

    return pollable_entry;

_failure:
    mutex.lock ();
    pollable_list.remove (pollable_entry);
    mutex.unlock ();
    delete pollable_entry;

    return NULL;
}

mt_throws Result
EpollPollGroup::doActivate (PollableEntry * const mt_nonnull pollable_entry)
{
    logD (epoll, _func, "0x", fmt_hex, (UintPtr) this, ": "
	  "pollable_entry: 0x", fmt_hex, (UintPtr) pollable_entry);

    struct epoll_event event;
    event.events = EPOLLET | EPOLLIN | EPOLLOUT | EPOLLRDHUP;
    event.data.u64 = 0; // For valgrind.
    event.data.ptr = pollable_entry;
    int const res = epoll_ctl (efd, EPOLL_CTL_ADD, pollable_entry->fd, &event);
    if (res == -1) {
	exc_throw <PosixException> (errno);
	logE_ (_func, "epoll_ctl() failed: ", errnoString (errno));
	return Result::Failure;
    }

    if (res != 0) {
	exc_throw <InternalException> (InternalException::BackendMalfunction);
	logE_ (_func, "epoll_ctl(): unexpected return value: ", res);
	return Result::Failure;
    }

    if (!trigger ()) {
	logE_ (_func, "trigger() failed: ", exc->toString());
	return Result::Failure;
    }

    return Result::Success;
}

mt_throws Result
EpollPollGroup::activatePollable (PollableKey const mt_nonnull key)
{
    PollableEntry * const pollable_entry = static_cast <PollableEntry*> (key);

    logD (epoll, _func, "0x", fmt_hex, (UintPtr) this, ": "
	  "pollable_entry: 0x", fmt_hex, (UintPtr) pollable_entry);

    return doActivate (pollable_entry);
}

void
EpollPollGroup::removePollable (PollableKey const mt_nonnull key)
{
    PollableEntry * const pollable_entry = static_cast <PollableEntry*> (key);

    logD_ (_func, "pollable_entry: 0x", fmt_hex, (UintPtr) pollable_entry);

    {
	int const res = epoll_ctl (efd, EPOLL_CTL_DEL, pollable_entry->fd, NULL /* event */);
	if (res == -1) {
	    logE_ (_func, "epoll_ctl() failed: ", errnoString (errno));
	} else {
	    if (res != 0)
		logE_ (_func, "epoll_ctl(): unexpected return value: ", res);
	}
    }

    mutex.lock ();
    pollable_entry->valid = false;
    pollable_list.remove (pollable_entry);
    pollable_deletion_queue.append (pollable_entry);
    mutex.unlock ();
}

mt_throws Result
EpollPollGroup::poll (Uint64 const timeout_microsec)
{
    logD (epoll, _func, "timeout: ", timeout_microsec);

    Time const start_microsec = getTimeMicroseconds ();

    struct epoll_event events [4096];
    for (;;) {
	Time const cur_microsec = getTimeMicroseconds ();

	Time const elapsed_microsec = cur_microsec - start_microsec;

//	logD_ (_func, "start: ", start_microsec, ", cur: ", cur_microsec, ", elapsed: ", elapsed_microsec);

	int timeout;
	if (!got_deferred_tasks) {
	    if (timeout_microsec != (Uint64) -1) {
		if (timeout_microsec > elapsed_microsec) {
		    timeout = (timeout_microsec - elapsed_microsec) / 1000;
		    if (timeout == 0)
			timeout = 1;
		} else {
		    timeout = 0;
		}
	    } else {
		timeout = -1;
	    }
	} else {
	    // We've got deferred tasks to process, hence we shouldn't block.
	    timeout = 0;
	}

	int const nfds = epoll_wait (efd, events, sizeof (events) / sizeof (events [0]), timeout);
	if (nfds == -1) {
	    if (errno == EINTR)
		continue;

	    exc_throw <PosixException> (errno);
	    logE_ (_func, "epoll_wait() failed: ", errnoString (errno));
	    return Result::Failure;
	}

	if (nfds < 0 || (Size) nfds > sizeof (events) / sizeof (events [0])) {
	    logE_ (_func, "epoll_wait(): unexpected return value: ", nfds);
	    return Result::Failure;
	}

	got_deferred_tasks = false;

	if (frontend)
	    frontend.call (frontend->pollIterationBegin);

	bool trigger_pipe_ready = false;

	mutex.lock ();
	block_trigger_pipe = true;

	if (nfds > 0) {
	    // We keep the mutex locked to ensure that we see valid contents
	    // of PollableEntry objects.
	    for (int i = 0; i < nfds; ++i) {
		PollableEntry * const pollable_entry = static_cast <PollableEntry*> (events [i].data.ptr);
		uint32_t const epoll_event_flags = events [i].events;
		Uint32 event_flags = 0;

		if (pollable_entry == NULL) {
		  // Trigger pipe event.
		    if (epoll_event_flags & EPOLLIN)
			trigger_pipe_ready = true;

		    if (epoll_event_flags & EPOLLOUT)
			logW_ (_func, "Unexpected EPOLLOUT event for trigger pipe");

		    if (epoll_event_flags & EPOLLHUP   ||
			epoll_event_flags & EPOLLRDHUP ||
			epoll_event_flags & EPOLLERR)
		    {
			logE_ (_func, "Trigger pipe error: 0x", fmt_hex, epoll_event_flags);
		    }

		    continue;
		}

		// This check is not really necessary, but we have 'mutex' locked anyway.
		if (pollable_entry->valid) {
		    if (epoll_event_flags & EPOLLIN)
			event_flags |= PollGroup::Input;

		    if (epoll_event_flags & EPOLLOUT)
			event_flags |= PollGroup::Output;

		    if (epoll_event_flags & EPOLLHUP ||
			epoll_event_flags & EPOLLRDHUP)
		    {
			event_flags |= PollGroup::Hup;
		    }

		    if (epoll_event_flags & EPOLLERR)
			event_flags |= PollGroup::Error;

		    if (event_flags) {
			pollable_entry->pollable.call_mutex (
				pollable_entry->pollable->processEvents, mutex, /* ( */ event_flags /* ) */);
		    }
		}
	    } // for (;;) - for all fds.
	} // if (nfds > 0)

	processPollableDeletionQueue ();
	mutex.unlock ();

	bool trigger_break = false;
	{
	  // Dealing with trigger() logics.
	  // It is important to allow re-triggering the PollGroup before calling
	  // frontend->pollIterationEnd(), so that no trigger requests get lost.

	    if (trigger_pipe_ready) {
		if (!commonTriggerPipeRead (trigger_pipe [0]))
		    return Result::Failure;
	    }

	    mutex.lock ();
	    // TODO We can keep 'block_trigger_pipe' set till the next call to epoll_wait().
	    block_trigger_pipe = false;
	    if (triggered) {
		triggered = false;
		mutex.unlock ();
		trigger_break = true;
	    } else {
		mutex.unlock ();
	    }
	}

	if (frontend) {
	    bool extra_iteration_needed = false;
	    frontend.call_ret (&extra_iteration_needed, frontend->pollIterationEnd);
	    if (extra_iteration_needed)
		got_deferred_tasks = true;
	}

	if (deferred_processor.process ())
	    got_deferred_tasks = true;

	if (trigger_break) {
	    logD (epoll, _func, "trigger_break");
	    break;
	}

	if (elapsed_microsec >= timeout_microsec) {
	  // Timeout expired.
	    break;
	}
    } // for (;;)

    return Result::Success;
}

mt_throws Result
EpollPollGroup::trigger ()
{
    if (poll_tlocal && poll_tlocal == libMary_getThreadLocal()) {
	mutex.lock ();
	triggered = true;
	mutex.unlock ();
	return Result::Success;
    }

    mutex.lock ();
    triggered = true;
    if (block_trigger_pipe) {
	mutex.unlock ();
	return Result::Success;
    }
    mutex.unlock ();

    return triggerPipeWrite ();
}

mt_throws Result
EpollPollGroup::open ()
{
    efd =  epoll_create (1 /* size, unused */);
    if (efd == -1) {
	exc_throw <PosixException> (errno);
	logE_ (_func, "epoll_create() failed: ", errnoString (errno));
	return Result::Failure;
    }

    if (!posix_createNonblockingPipe (&trigger_pipe))
	return Result::Failure;

    {
	struct epoll_event event;
	event.events = EPOLLET | EPOLLIN | EPOLLRDHUP;
	event.data.u64 = 0; // For valgrind.
	event.data.ptr = NULL; // 'NULL' tells that this is trigger pipe.
	int const res = epoll_ctl (efd, EPOLL_CTL_ADD, trigger_pipe [0], &event);
	if (res == -1) {
	    exc_throw <PosixException> (errno);
	    logE_ (_func, "epoll_ctl() failed: ", errnoString (errno));
	    return Result::Failure;
	}

	if (res != 0) {
	    exc_throw <InternalException> (InternalException::BackendMalfunction);
	    logE_ (_func, "epoll_ctl(): unexpected return value: ", res);
	    return Result::Failure;
	}
    }

    return Result::Success;
}

namespace {
void deferred_processor_trigger (void * const active_poll_group_)
{
    ActivePollGroup * const active_poll_group = static_cast <ActivePollGroup*> (active_poll_group_);
    active_poll_group->trigger ();
}

DeferredProcessor::Backend deferred_processor_backend = {
    deferred_processor_trigger
};
}

EpollPollGroup::EpollPollGroup (Object * const coderef_container)
    : DependentCodeReferenced (coderef_container),
      efd (-1),
      triggered (false),
      block_trigger_pipe (false),
      // Initializing to 'true' to process deferred tasks scheduled before we
      // enter poll() the first time.
      got_deferred_tasks (true),
      poll_tlocal (NULL)
{
    trigger_pipe [0] = -1;
    trigger_pipe [1] = -1;

    deferred_processor.setBackend (CbDesc<DeferredProcessor::Backend> (
	    &deferred_processor_backend,
	    static_cast <ActivePollGroup*> (this) /* cb_data */,
	    NULL /* coderef_container */));
}

EpollPollGroup::~EpollPollGroup ()
{
    mutex.lock ();

    {
	PollableList::iter iter (pollable_list);
	while (!pollable_list.iter_done (iter)) {
	    PollableEntry * const pollable_entry = pollable_list.iter_next (iter);
	    delete pollable_entry;
	}
    }

    {
	PollableDeletionQueue::iter iter (pollable_deletion_queue);
	while (!pollable_deletion_queue.iter_done (iter)) {
	    PollableEntry * const pollable_entry = pollable_deletion_queue.iter_next (iter);
	    delete pollable_entry;
	}
    }

    mutex.unlock ();

    if (efd != -1) {
	for (;;) {
	    int const res = close (efd);
	    if (res == -1) {
		if (errno == EINTR)
		    continue;

		logE_ (_func, "close() failed (efd): ", errnoString (errno));
	    } else
	    if (res != 0) {
		logE_ (_func, "close() (efd): unexpected return value: ", res);
	    }

	    break;
	}
    }

    for (int i = 0; i < 2; ++i) {
	if (trigger_pipe [i] != -1) {
	    for (;;) {
		int const res = close (trigger_pipe [i]);
		if (res == -1) {
		    if (errno == EINTR)
			continue;

		    logE_ (_func, "close() failed (trigger_pipe[", i, "]): ", errnoString (errno));
		} else
		if (res != 0) {
		    logE_ (_func, "close(): unexpected return value (trigger_pipe[", i, "]): ", res);
		}

		break;
	    }
	}
    }
}

}

<|endoftext|>


<commit_before>#pragma once

#include "common.hpp"
#include "debug.hpp"
#include <cassert>
#include <climits>
#include <omp.h>

template<typename SizeType, typename WordType>
void copy_bits(WordType* const dst,
               WordType const* const src,
               SizeType& dst_off_ref,
               SizeType& src_off_ref,
               SizeType const block_size,
               SizeType const words_size,
               SizeType const full_words_size
              )
{
    if (block_size == 0) return;

    WordType constexpr BITS = (sizeof(WordType) * CHAR_BIT);
    WordType constexpr MOD_MASK = BITS - 1;
    WordType constexpr SHIFT = log2(MOD_MASK);

    SizeType dst_off = dst_off_ref;
    SizeType src_off = src_off_ref;
    auto const dst_off_end = dst_off + block_size;

    // NB: Check if source and target block are aligned the same way
    // This is needed because the non-aligned code path would
    // trigger undefined behavior in the aligned case.
    if ((dst_off & MOD_MASK) == (src_off & MOD_MASK)) {
        // Copy unaligned leading bits
        {
            auto& word = dst[dst_off >> SHIFT];

            while ((dst_off & MOD_MASK) != 0 && dst_off != dst_off_end) {
                bool const bit = bit_at<WordType>(src, src_off++);

                word |= (WordType(bit) << (MOD_MASK - (dst_off++ & MOD_MASK)));
            }
        }

        // Copy the the bulk in-between word-wise
        {
            auto const words = (dst_off_end - dst_off) >> SHIFT;

            WordType*       ds = dst + (dst_off >> SHIFT);
            WordType const* sr = src + (src_off >> SHIFT);

            WordType const* const ds_end = ds + words;

            while (ds != ds_end) {
                *ds++ = *sr++;
            }

            dst_off += words * BITS;
            src_off += words * BITS;
        }

        // Copy unaligned trailing bits
        {
            auto& word = dst[dst_off >> SHIFT];

            while (dst_off != dst_off_end) {
                bool const bit = bit_at<WordType>(src, src_off++);

                word |= (WordType(bit) << (MOD_MASK - (dst_off++ & MOD_MASK)));
            }
        }
    } else {
        // Copy unaligned leading bits
        {
            auto& word = dst[dst_off >> SHIFT];

            while ((dst_off & MOD_MASK) != 0 && dst_off != dst_off_end) {
                bool const bit = bit_at<WordType>(src, src_off++);

                word |= (WordType(bit) << (MOD_MASK - (dst_off++ & MOD_MASK)));
            }
        }

        // Copy the the bulk in-between word-wise
        {
            auto const words = (dst_off_end - dst_off) >> SHIFT;

            WordType const src_shift_a = src_off & MOD_MASK;
            WordType const src_shift_b = BITS - src_shift_a;

            WordType*       ds = dst + (dst_off >> SHIFT);
            WordType const* sr = src + (src_off >> SHIFT);
            WordType const* const ds_end = ds + words;

            auto chk = [full_words_size, src_shift_a, src_shift_b](auto ds, auto dst, auto sr, auto src) {
                std::cout
                    << "dst ptr: " << (ds - dst)
                    << ", src ptr: " << (sr - src)
                    << ", len: " << full_words_size
                    << ", shifts: " << size_t(src_shift_a) << ", " << size_t(src_shift_b)
                    << "\n";
                std::cout << "sr:  " << size_t(sr) << "\n";
                std::cout << "src: " << size_t(src) << "\n";
                assert((ds - dst) < full_words_size);
                assert((sr - src) < full_words_size);
                assert(((sr - src) + 1) < full_words_size);
            };

            while (ds != ds_end) {
                chk(ds, dst, sr, src);

                WordType const vala = *sr;
                std::cout << "sp pre:  " << size_t(sr) << "\n";
                sr++;
                std::cout << "sp post: " << size_t(sr) << "\n";
                WordType const valb = *sr;

                *ds++ = (vala << src_shift_a) | (valb >> src_shift_b);

            }

            dst_off += words * BITS;
            src_off += words * BITS;
        }

        // Copy unaligned trailing bits
        {
            auto& word = dst[dst_off >> SHIFT];

            while (dst_off != dst_off_end) {
                bool const bit = bit_at<WordType>(src, src_off++);

                word |= (WordType(bit) << (MOD_MASK - (dst_off++ & MOD_MASK)));
            }
        }
    }

    dst_off_ref += block_size;
    src_off_ref += block_size;
}

template<typename SizeType, typename Rho>
inline auto merge_bvs(SizeType size,
                      SizeType levels,
                      SizeType shards,
                      const std::vector<std::vector<std::vector<SizeType>>>& glob_hist,
                      const std::vector<Bvs<SizeType>>& glob_bv,
                      const Rho& rho) -> Bvs<SizeType>
{
    assert(shards == glob_bv.size());
    assert(shards == glob_hist.size());

    auto offsets = std::vector<SizeType>(shards);
    auto local_offsets = std::vector<std::vector<std::vector<SizeType>>>(
        levels, std::vector<std::vector<SizeType>>(
            shards, std::vector<SizeType>(shards + 1)));
    auto word_offsets = std::vector<std::vector<SizeType>>(
        levels, std::vector<SizeType>(shards + 1));
    auto block_seq_offsets = std::vector<std::vector<SizeType>>(
        levels, std::vector<SizeType>(shards + 1));
    auto glob_cursors = std::vector<std::vector<std::vector<size_t>>>(
        shards, std::vector<std::vector<size_t>>(
            levels, std::vector<size_t>(shards)
        ));

    for (size_t rank = 1; rank < shards; rank++) {
        const size_t omp_rank = rank;
        const size_t omp_size = shards;

        const SizeType offset = (omp_rank * (word_size(size) / omp_size)) +
            std::min<SizeType>(omp_rank, word_size(size) % omp_size);

        offsets[rank - 1] = offset * 64ull;
    }
    offsets[shards - 1] = word_size(size) * 64ull;

    // TODO: fix
    for(size_t level = 0; level < levels; level++) {
        const size_t br_size = 1ull << level;
        size_t j = 0;
        size_t oi = 0;

        for(size_t i = 0; i < br_size * shards; i++) {
            const auto bit_i = i / shards;
            const auto shard = i % shards;

            const auto h = glob_hist[shard][level];
            const auto block_size = h[rho(level, bit_i)];

            j += block_size;
            local_offsets[level][shard][oi + 1] += block_size;

            if (j <= offsets[oi]) {
                // ...
            } else {
                size_t right = j - offsets[oi];
                size_t left = block_size - right;

                j -= block_size;
                local_offsets[level][shard][oi + 1] -= block_size;

                j += left;
                local_offsets[level][shard][oi + 1] += left;

                // TODO: rename word_offset to something like
                // "offset in block"
                word_offsets[level][oi + 1] = left;
                block_seq_offsets[level][oi + 1] = i;

                if (oi + 2 < shards + 1) {
                    for(size_t s = 0; s < shards; s++) {
                        local_offsets[level][s][oi + 2] = local_offsets[level][s][oi + 1];
                    }
                }
                oi++;

                j += right;
                local_offsets[level][shard][oi + 1] += right;
            }
        }
    }

    auto r = Bvs<SizeType>(size, levels);
    auto& _bv = r.vec();

    //#pragma omp parallel
    for(size_t omp_rank = 0; omp_rank < shards; omp_rank++)
    {
        //assert(size_t(omp_get_num_threads()) == shards);
        //const size_t omp_rank = omp_get_thread_num();
        const size_t merge_shard = omp_rank;

        const auto target_right = std::min(offsets[merge_shard], size);
        const auto target_left = std::min((merge_shard > 0 ? offsets[merge_shard - 1] : 0), target_right);

        auto& cursors = glob_cursors[merge_shard];

        for (size_t level = 0; level < levels; level++) {
            for(size_t read_shard = 0; read_shard < shards; read_shard++) {
                cursors[level][read_shard] = local_offsets[level][read_shard][merge_shard];
            }
        }

        for (size_t level = 0; level < levels; level++) {
            auto seq_i = block_seq_offsets[level][merge_shard];

            size_t j = target_left;
            size_t init_offset = word_offsets[level][merge_shard];

            while (j < target_right) {
                const auto i = seq_i / shards;
                const auto shard = seq_i % shards;
                seq_i++;

                const auto& h = glob_hist[shard][level];
                const auto& local_bv = glob_bv[shard].vec()[level];

                auto const block_size = std::min(
                    target_right - j,
                    h[rho(level, i)] - init_offset
                );
                init_offset = 0; // TODO: remove this by doing a initial pass

                auto& local_cursor = cursors[level][shard];

                copy_bits(
                    _bv[level],
                    local_bv,
                    j,
                    local_cursor,
                    block_size,
                    word_size(target_right - target_left),
                    word_size(size)
                );
            }
        }
    }

    return r;
}
<commit_msg>debug more 6<commit_after>#pragma once

#include "common.hpp"
#include "debug.hpp"
#include <cassert>
#include <climits>
#include <omp.h>

template<typename SizeType, typename WordType>
void copy_bits(WordType* const dst,
               WordType const* const src,
               SizeType& dst_off_ref,
               SizeType& src_off_ref,
               SizeType const block_size,
               SizeType const words_size,
               SizeType const full_words_size
              )
{
    if (block_size == 0) return;

    WordType constexpr BITS = (sizeof(WordType) * CHAR_BIT);
    WordType constexpr MOD_MASK = BITS - 1;
    WordType constexpr SHIFT = log2(MOD_MASK);

    SizeType dst_off = dst_off_ref;
    SizeType src_off = src_off_ref;
    auto const dst_off_end = dst_off + block_size;

    // NB: Check if source and target block are aligned the same way
    // This is needed because the non-aligned code path would
    // trigger undefined behavior in the aligned case.
    if ((dst_off & MOD_MASK) == (src_off & MOD_MASK)) {
        // Copy unaligned leading bits
        {
            auto& word = dst[dst_off >> SHIFT];

            while ((dst_off & MOD_MASK) != 0 && dst_off != dst_off_end) {
                bool const bit = bit_at<WordType>(src, src_off++);

                word |= (WordType(bit) << (MOD_MASK - (dst_off++ & MOD_MASK)));
            }
        }

        // Copy the the bulk in-between word-wise
        {
            auto const words = (dst_off_end - dst_off) >> SHIFT;

            WordType*       ds = dst + (dst_off >> SHIFT);
            WordType const* sr = src + (src_off >> SHIFT);

            WordType const* const ds_end = ds + words;

            while (ds != ds_end) {
                *ds++ = *sr++;
            }

            dst_off += words * BITS;
            src_off += words * BITS;
        }

        // Copy unaligned trailing bits
        {
            auto& word = dst[dst_off >> SHIFT];

            while (dst_off != dst_off_end) {
                bool const bit = bit_at<WordType>(src, src_off++);

                word |= (WordType(bit) << (MOD_MASK - (dst_off++ & MOD_MASK)));
            }
        }
    } else {
        // Copy unaligned leading bits
        {
            auto& word = dst[dst_off >> SHIFT];

            while ((dst_off & MOD_MASK) != 0 && dst_off != dst_off_end) {
                bool const bit = bit_at<WordType>(src, src_off++);

                word |= (WordType(bit) << (MOD_MASK - (dst_off++ & MOD_MASK)));
            }
        }

        // Copy the the bulk in-between word-wise
        {
            auto const words = (dst_off_end - dst_off) >> SHIFT;

            WordType const src_shift_a = src_off & MOD_MASK;
            WordType const src_shift_b = BITS - src_shift_a;

            WordType*       ds = dst + (dst_off >> SHIFT);
            WordType const* sr = src + (src_off >> SHIFT);
            WordType const* const ds_end = ds + words;

            std::cout << "[start]\n";

            auto chk = [full_words_size, src_shift_a, src_shift_b](auto ds, auto dst, auto sr, auto src) {
                std::cout
                    << "dst ptr: " << (ds - dst)
                    << ", src ptr: " << (sr - src)
                    << ", len: " << full_words_size
                    << ", shifts: " << size_t(src_shift_a) << ", " << size_t(src_shift_b)
                    << "\n";
                std::cout << "sr:  " << size_t(sr) << "\n";
                std::cout << "src: " << size_t(src) << "\n";
                std::cout << "dst_off:  " << dst_off << "\n";
                std::cout << "src_off:  " << src_off << "\n";
                assert((ds - dst) < full_words_size);
                assert((sr - src) < full_words_size);
                assert(((sr - src) + 1) < full_words_size);
                std::cout << "---\n";
            };

            while (ds != ds_end) {
                chk(ds, dst, sr, src);

                WordType const vala = *sr;
                std::cout << "sp pre:  " << size_t(sr) << "\n";
                sr++;
                std::cout << "sp post: " << size_t(sr) << "\n";
                WordType const valb = *sr;

                *ds++ = (vala << src_shift_a) | (valb >> src_shift_b);

            }

            std::cout << "[end]\n";

            dst_off += words * BITS;
            src_off += words * BITS;
        }

        // Copy unaligned trailing bits
        {
            auto& word = dst[dst_off >> SHIFT];

            while (dst_off != dst_off_end) {
                bool const bit = bit_at<WordType>(src, src_off++);

                word |= (WordType(bit) << (MOD_MASK - (dst_off++ & MOD_MASK)));
            }
        }
    }

    dst_off_ref += block_size;
    src_off_ref += block_size;
}

template<typename SizeType, typename Rho>
inline auto merge_bvs(SizeType size,
                      SizeType levels,
                      SizeType shards,
                      const std::vector<std::vector<std::vector<SizeType>>>& glob_hist,
                      const std::vector<Bvs<SizeType>>& glob_bv,
                      const Rho& rho) -> Bvs<SizeType>
{
    assert(shards == glob_bv.size());
    assert(shards == glob_hist.size());

    auto offsets = std::vector<SizeType>(shards);
    auto local_offsets = std::vector<std::vector<std::vector<SizeType>>>(
        levels, std::vector<std::vector<SizeType>>(
            shards, std::vector<SizeType>(shards + 1)));
    auto word_offsets = std::vector<std::vector<SizeType>>(
        levels, std::vector<SizeType>(shards + 1));
    auto block_seq_offsets = std::vector<std::vector<SizeType>>(
        levels, std::vector<SizeType>(shards + 1));
    auto glob_cursors = std::vector<std::vector<std::vector<size_t>>>(
        shards, std::vector<std::vector<size_t>>(
            levels, std::vector<size_t>(shards)
        ));

    for (size_t rank = 1; rank < shards; rank++) {
        const size_t omp_rank = rank;
        const size_t omp_size = shards;

        const SizeType offset = (omp_rank * (word_size(size) / omp_size)) +
            std::min<SizeType>(omp_rank, word_size(size) % omp_size);

        offsets[rank - 1] = offset * 64ull;
    }
    offsets[shards - 1] = word_size(size) * 64ull;

    // TODO: fix
    for(size_t level = 0; level < levels; level++) {
        const size_t br_size = 1ull << level;
        size_t j = 0;
        size_t oi = 0;

        for(size_t i = 0; i < br_size * shards; i++) {
            const auto bit_i = i / shards;
            const auto shard = i % shards;

            const auto h = glob_hist[shard][level];
            const auto block_size = h[rho(level, bit_i)];

            j += block_size;
            local_offsets[level][shard][oi + 1] += block_size;

            if (j <= offsets[oi]) {
                // ...
            } else {
                size_t right = j - offsets[oi];
                size_t left = block_size - right;

                j -= block_size;
                local_offsets[level][shard][oi + 1] -= block_size;

                j += left;
                local_offsets[level][shard][oi + 1] += left;

                // TODO: rename word_offset to something like
                // "offset in block"
                word_offsets[level][oi + 1] = left;
                block_seq_offsets[level][oi + 1] = i;

                if (oi + 2 < shards + 1) {
                    for(size_t s = 0; s < shards; s++) {
                        local_offsets[level][s][oi + 2] = local_offsets[level][s][oi + 1];
                    }
                }
                oi++;

                j += right;
                local_offsets[level][shard][oi + 1] += right;
            }
        }
    }

    auto r = Bvs<SizeType>(size, levels);
    auto& _bv = r.vec();

    //#pragma omp parallel
    for(size_t omp_rank = 0; omp_rank < shards; omp_rank++)
    {
        //assert(size_t(omp_get_num_threads()) == shards);
        //const size_t omp_rank = omp_get_thread_num();
        const size_t merge_shard = omp_rank;

        const auto target_right = std::min(offsets[merge_shard], size);
        const auto target_left = std::min((merge_shard > 0 ? offsets[merge_shard - 1] : 0), target_right);

        auto& cursors = glob_cursors[merge_shard];

        for (size_t level = 0; level < levels; level++) {
            for(size_t read_shard = 0; read_shard < shards; read_shard++) {
                cursors[level][read_shard] = local_offsets[level][read_shard][merge_shard];
            }
        }

        for (size_t level = 0; level < levels; level++) {
            auto seq_i = block_seq_offsets[level][merge_shard];

            size_t j = target_left;
            size_t init_offset = word_offsets[level][merge_shard];

            while (j < target_right) {
                const auto i = seq_i / shards;
                const auto shard = seq_i % shards;
                seq_i++;

                const auto& h = glob_hist[shard][level];
                const auto& local_bv = glob_bv[shard].vec()[level];

                auto const block_size = std::min(
                    target_right - j,
                    h[rho(level, i)] - init_offset
                );
                init_offset = 0; // TODO: remove this by doing a initial pass

                auto& local_cursor = cursors[level][shard];

                copy_bits(
                    _bv[level],
                    local_bv,
                    j,
                    local_cursor,
                    block_size,
                    word_size(target_right - target_left),
                    word_size(size)
                );
            }
        }
    }

    return r;
}
<|endoftext|>


<commit_before>#ifdef WITH_BASISU

#define BASISU_NO_ITERATOR_DEBUG_LEVEL

#ifdef __clang__
#pragma GCC diagnostic ignored "-Wunknown-warning-option"
#endif

#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#pragma GCC diagnostic ignored "-Wunused-value"
#endif

#include "encoder/basisu_comp.h"

static std::unique_ptr<basisu::job_pool> gJobPool;
static std::unique_ptr<basisu::job_pool> gEncPool;

void encodeBasisInit(int jobs)
{
	using namespace basisu;

	basisu_encoder_init();

	uint32_t num_threads = jobs == 0 ? std::thread::hardware_concurrency() : jobs;
	uint32_t num_threads_enc = num_threads * 3 / 4;

	gJobPool.reset(new job_pool(std::max(1u, num_threads - num_threads_enc)));
	gEncPool.reset(new job_pool(std::max(1u, num_threads_enc)));
}

bool encodeBasisInternal(const char* input, const char* output, bool yflip, bool normal_map, bool linear, bool uastc, int uastc_l, float uastc_q, int etc1s_l, int etc1s_q, int zstd_l, int width, int height)
{
	using namespace basisu;

	basist::etc1_global_selector_codebook sel_codebook(basist::g_global_selector_cb_size, basist::g_global_selector_cb);

	basis_compressor_params params;

	params.m_multithreading = gEncPool->get_total_threads() > 1;
	params.m_pJob_pool = gEncPool.get();

	if (uastc)
	{
		static const uint32_t s_level_flags[TOTAL_PACK_UASTC_LEVELS] = { cPackUASTCLevelFastest, cPackUASTCLevelFaster, cPackUASTCLevelDefault, cPackUASTCLevelSlower, cPackUASTCLevelVerySlow };

		params.m_uastc = true;

		params.m_pack_uastc_flags &= ~cPackUASTCLevelMask;
		params.m_pack_uastc_flags |= s_level_flags[uastc_l];

		params.m_rdo_uastc = uastc_q > 0;
		params.m_rdo_uastc_quality_scalar = uastc_q;
		params.m_rdo_uastc_dict_size = 1024;
	}
	else
	{
		params.m_compression_level = etc1s_l;
		params.m_quality_level = etc1s_q;
		params.m_max_endpoint_clusters = 0;
		params.m_max_selector_clusters = 0;

		params.m_no_selector_rdo = normal_map;
		params.m_no_endpoint_rdo = normal_map;
	}

	params.m_perceptual = !linear;

	params.m_mip_gen = true;
	params.m_mip_srgb = !linear;

	params.m_resample_width = width;
	params.m_resample_height = height;

	params.m_y_flip = yflip;

	params.m_create_ktx2_file = true;
	params.m_ktx2_srgb_transfer_func = !linear;

	if (zstd_l)
	{
		params.m_ktx2_uastc_supercompression = basist::KTX2_SS_ZSTANDARD;
		params.m_ktx2_zstd_supercompression_level = zstd_l;
	}

	params.m_read_source_images = true;
	params.m_write_output_basis_files = true;
	params.m_pSel_codebook = &sel_codebook;

	params.m_source_filenames.resize(1);
	params.m_source_filenames[0] = input;

	params.m_out_filename = output;

	params.m_status_output = false;

	basis_compressor c;

	if (!c.init(params))
		return false;

	return c.process() == basis_compressor::cECSuccess;
}

void encodePush(const std::function<void()>& job)
{
	assert(gJobPool);

	gJobPool->add_job(job);
}

void encodeWait()
{
	assert(gJobPool);

	gJobPool->wait_for_all();
}
#endif
<commit_msg>Slightly better thread distribution<commit_after>#ifdef WITH_BASISU

#define BASISU_NO_ITERATOR_DEBUG_LEVEL

#ifdef __clang__
#pragma GCC diagnostic ignored "-Wunknown-warning-option"
#endif

#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#pragma GCC diagnostic ignored "-Wunused-value"
#endif

#include "encoder/basisu_comp.h"

static std::unique_ptr<basisu::job_pool> gJobPool;
static std::unique_ptr<basisu::job_pool> gEncPool;

void encodeBasisInit(int jobs)
{
	using namespace basisu;

	basisu_encoder_init();

	uint32_t num_threads = jobs == 0 ? std::thread::hardware_concurrency() : jobs;
	uint32_t num_threads_enc = num_threads * 2 / 3;

	// This is a little difficult to reason about. We want to distribute the pool capacity so that it adds up to num_threads.
	// We don't really know what the ratio of J:E work is (it's dependent on the mode among other things), but also encoding
	// work uses job threads as helpers if necessary, which is where "1+" is coming from.
	gJobPool.reset(new job_pool(num_threads - num_threads_enc));
	gEncPool.reset(new job_pool(1 + num_threads_enc));
}

bool encodeBasisInternal(const char* input, const char* output, bool yflip, bool normal_map, bool linear, bool uastc, int uastc_l, float uastc_q, int etc1s_l, int etc1s_q, int zstd_l, int width, int height)
{
	using namespace basisu;

	basist::etc1_global_selector_codebook sel_codebook(basist::g_global_selector_cb_size, basist::g_global_selector_cb);

	basis_compressor_params params;

	params.m_multithreading = gEncPool->get_total_threads() > 1;
	params.m_pJob_pool = gEncPool.get();

	if (uastc)
	{
		static const uint32_t s_level_flags[TOTAL_PACK_UASTC_LEVELS] = { cPackUASTCLevelFastest, cPackUASTCLevelFaster, cPackUASTCLevelDefault, cPackUASTCLevelSlower, cPackUASTCLevelVerySlow };

		params.m_uastc = true;

		params.m_pack_uastc_flags &= ~cPackUASTCLevelMask;
		params.m_pack_uastc_flags |= s_level_flags[uastc_l];

		params.m_rdo_uastc = uastc_q > 0;
		params.m_rdo_uastc_quality_scalar = uastc_q;
		params.m_rdo_uastc_dict_size = 1024;
	}
	else
	{
		params.m_compression_level = etc1s_l;
		params.m_quality_level = etc1s_q;
		params.m_max_endpoint_clusters = 0;
		params.m_max_selector_clusters = 0;

		params.m_no_selector_rdo = normal_map;
		params.m_no_endpoint_rdo = normal_map;
	}

	params.m_perceptual = !linear;

	params.m_mip_gen = true;
	params.m_mip_srgb = !linear;

	params.m_resample_width = width;
	params.m_resample_height = height;

	params.m_y_flip = yflip;

	params.m_create_ktx2_file = true;
	params.m_ktx2_srgb_transfer_func = !linear;

	if (zstd_l)
	{
		params.m_ktx2_uastc_supercompression = basist::KTX2_SS_ZSTANDARD;
		params.m_ktx2_zstd_supercompression_level = zstd_l;
	}

	params.m_read_source_images = true;
	params.m_write_output_basis_files = true;
	params.m_pSel_codebook = &sel_codebook;

	params.m_source_filenames.resize(1);
	params.m_source_filenames[0] = input;

	params.m_out_filename = output;

	params.m_status_output = false;

	basis_compressor c;

	if (!c.init(params))
		return false;

	return c.process() == basis_compressor::cECSuccess;
}

void encodePush(const std::function<void()>& job)
{
	assert(gJobPool);

	gJobPool->add_job(job);
}

void encodeWait()
{
	assert(gJobPool);

	gJobPool->wait_for_all();
}
#endif
<|endoftext|>


<commit_before>#ifndef STACK_HPP
#define STACK_HPP
#include <cstdlib>
#include <iostream>
#include <memory>
using namespace std;

template <typename T>
class stack
{
public:
	stack();
	size_t count() const;
	void push(T const &);
	T pop();
	~stack();
	stack(const stack &b);
	stack & operator=(const stack &b);
	bool operator==(stack const & _s);
private:
	size_t array_size_;
	size_t count_;
	T * array_;
};
template<typename T>
T*copy_new(const T*arr,size_t count,size_t array_size)
{
	T*l=new T[array_size];
	std::copy(arr,arr+count,l);
	return l;
}
#include "stack.cpp"
#endif
<commit_msg>Update stack.hpp<commit_after>#ifndef STACK_HPP
#define STACK_HPP
#include <cstdlib>
#include <iostream>
#include <memory>
using namespace std;

template <typename T>
class stack
{
public:
	stack();
	size_t count() const;
	void push(T const &);
	T pop();
	const T& top();
	~stack();
	stack(const stack &b);
	stack & operator=(const stack &b);
	bool operator==(stack const & _s);
private:
	size_t array_size_;
	size_t count_;
	T * array_;
};
template<typename T>
T*copy_new(const T*arr,size_t count,size_t array_size)
{
	T*l=new T[array_size];
	std::copy(arr,arr+count,l);
	return l;
}
#include "stack.cpp"
#endif
<|endoftext|>


<commit_before>//
//  Shell
//  Ryra
//  Because I can. Doing this to stem my boredom in class or whatevs.
//

#include <iostream>

//

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

    // A loop here. Lifetime of shell starts here.

    return 0;
}
<commit_msg>shelly<commit_after>//
//  Shell
//  Ryra
//  Because I can. Doing this to stem my boredom in class or whatevs.
//

#include <iostream>

//

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

    // A loop here. Lifetime of shell starts here.
    // The idea is to parse. Then...

    return 0;
}
<|endoftext|>


<commit_before>#ifndef stack_cpp
#define stack_cpp
#pragma once
#include <iostream>

template <typename T1, typename T2>
void construct(T1 * ptr, T2 const & value) {
    new(ptr) T1 (value);
}

template <typename T>
void destroy(T * ptr) noexcept
{
    ptr->~T();
}

template <typename FwdIter>
void destroy(FwdIter first, FwdIter last) noexcept
{
    for (; first != last; ++first) {
        destroy(&*first);
    }
}
/*template<typename T>
auto newcopy( const T * item, size_t size, size_t count) -> T* //strong
{
	T * buff = new T[size];
	try
	{
		std::copy(item, item + count, buff);
	}
	catch(...)
	{
		delete[] buff;
		throw;
	}
		return buff;
}*/

template <typename T>
class allocator
{
protected:
    allocator(size_t size = 0);
    ~allocator();
    auto swap(allocator & other) -> void;

    allocator(allocator const &) = delete;
    auto operator =(allocator const &) -> allocator & = delete;

    T * ptr_;
    size_t size_;
    size_t count_;
};

template<typename T>
allocator<T>::allocator(size_t size) :
	ptr_(static_cast<T *>(size == 0 ? nullptr : operator new(size * sizeof(T)))),
	size_(0),
	count_(size) {
}

template<typename T>
allocator<T>::~allocator() {
	delete ptr_;
}

template<typename T>
auto allocator<T>::swap(allocator & other) -> void {
	std::swap(ptr_, other.ptr_);
	std::swap(count_, other.count_);
	std::swap(size_, other.size_);
}

template <typename T>
class stack : private allocator<T>
{
public:
	stack(); //noexcept
	stack(stack const &);  //strong
	~stack(); //noexcept
	size_t count() const;  //noexcept
	auto push(T const &) -> void; //strong
	void pop(); //strong
	const T& top(); //strong
	auto operator=(stack const & right)->stack &; //strong
	auto empty() const -> bool; //noexcept

};
template <typename T>
size_t stack<T>::count() const
{
	return allocator<T>::count_;
}
template <typename T>
stack<T>::stack():allocator<T>(){}

template <typename T>
stack<T>::stack(const stack& item) : allocator<T>(item.size_){
	for (size_t i = 0; i < item.count_; i++) construct(allocator<T>::ptr_ + i, item.ptr_[i]);
	allocator<T>::count_ = item.count_;
};
/*
template <typename T>
stack<T>::stack(const stack& item) : allocator<T>(item.size_){
	allocator<T>::ptr_ = newcopy(item.allocator<T>::ptr_, item.allocator<T>::count_, item.allocator<T>::size_);
	allocator<T>::count_ = item.allocator<T>::count_;
};
*/
template <typename T>
stack<T>::~stack()
{
}
/*
template<typename T>
auto stack<T>::push(T const & item) -> void {
	if (allocator<T>::count_ == allocator<T>::size_) {
		T * buff = newcopy(allocator<T>::ptr_, allocator<T>::count_,  allocator<T>::size_ = allocator<T>::size_ * 2 + (allocator<T>::count_ == 0));
		delete allocator<T>::ptr_;
		allocator<T>::ptr_ = buff;
		allocator<T>::size_ = allocator<T>::size_ * 2 + (allocator<T>::count_ == 0) ;
	}
	allocator<T>::ptr_[allocator<T>::count_] = item;
	++allocator<T>::count_;
}
*/
template <typename T>
auto stack<T>::push( const T & item ) -> void
{
    if ( this->count_ == this->size_ ) {
        size_t array_size = this->size_ * 2 + ( this->size_ == 0 );

        stack temp { array_size };
        while ( temp.count() < this->count_ ) {
            temp.push( this->ptr_[ temp.count() ] );
        }

        this->swap( temp );
    }

    construct( this->ptr_ + this->count_, item);
    ++this->count_;
}

template<typename T>
void stack<T>::pop() {
	if (allocator<T>::count_ == 0) {
		throw std::logic_error("Stack is empty!");
	} else {
		allocator<T>::count_--;
	}
}

template<typename T>
const T& stack<T>::top()
{
	if (allocator<T>::count_ == 0) {
		throw ("Stack is empty!");
	}
	return allocator<T>::ptr_[allocator<T>::count_ - 1];
}

template<typename T>
auto stack<T>::operator=(stack const & right) -> stack & {
	if (this != &right) {
	T* buff = newcopy(right.allocator<T>::ptr_, right.allocator<T>::size_, right.allocator<T>::count_);
	delete[] allocator<T>::ptr_;
	allocator<T>::ptr_ = buff;
	allocator<T>::count_ = right.allocator<T>::count_;
	allocator<T>::size_ = right.allocator<T>::size_;
	}
	return *this;
}

template<typename T>
auto stack<T>::empty() const -> bool{
	if (allocator<T>::count_ == 0){
		return true;
	} else{
		return false;
	}
}

#endif
<commit_msg>Update stack.hpp<commit_after>#ifndef stack_cpp
#define stack_cpp
#pragma once
#include <iostream>

template <typename T1, typename T2>
void construct(T1 * ptr, T2 const & value) {
    new(ptr) T1 (value);
}

template <typename T>
void destroy(T * ptr) noexcept
{
    ptr->~T();
}

template <typename FwdIter>
void destroy(FwdIter first, FwdIter last) noexcept
{
    for (; first != last; ++first) {
        destroy(&*first);
    }
}
/*template<typename T>
auto newcopy( const T * item, size_t size, size_t count) -> T* //strong
{
	T * buff = new T[size];
	try
	{
		std::copy(item, item + count, buff);
	}
	catch(...)
	{
		delete[] buff;
		throw;
	}
		return buff;
}*/

template <typename T>
class allocator
{
protected:
    allocator(size_t size = 0);
    ~allocator();
    auto swap(allocator & other) -> void;

    allocator(allocator const &) = delete;
    auto operator =(allocator const &) -> allocator & = delete;

    T * ptr_;
    size_t size_;
    size_t count_;
};

template<typename T>
allocator<T>::allocator(size_t size) :
	ptr_(static_cast<T *>(size == 0 ? nullptr : operator new(size * sizeof(T)))),
	size_(0),
	count_(size) {
}

template<typename T>
allocator<T>::~allocator() {
	delete ptr_;
}

template<typename T>
auto allocator<T>::swap(allocator & other) -> void {
	std::swap(ptr_, other.ptr_);
	std::swap(count_, other.count_);
	std::swap(size_, other.size_);
}

template <typename T>
class stack : private allocator<T>
{
public:
	stack(); //noexcept
	stack(stack const &);  //strong
	~stack(); //noexcept
	size_t count() const;  //noexcept
	auto push(T const &) -> void; //strong
	void pop(); //strong
	const T& top(); //strong
	auto operator=(stack const & right)->stack &; //strong
	auto empty() const -> bool; //noexcept

};
template <typename T>
size_t stack<T>::count() const
{
	return allocator<T>::count_;
}
template <typename T>
stack<T>::stack():allocator<T>(){}

template <typename T>
stack<T>::stack(const stack& item) : allocator<T>(item.size_){
	for (size_t i = 0; i < item.count_; i++) construct(allocator<T>::ptr_ + i, item.ptr_[i]);
	allocator<T>::count_ = item.count_;
};
/*
template <typename T>
stack<T>::stack(const stack& item) : allocator<T>(item.size_){
	allocator<T>::ptr_ = newcopy(item.allocator<T>::ptr_, item.allocator<T>::count_, item.allocator<T>::size_);
	allocator<T>::count_ = item.allocator<T>::count_;
};
*/
template <typename T>
stack<T>::~stack()
{
}
/*
template<typename T>
auto stack<T>::push(T const & item) -> void {
	if (allocator<T>::count_ == allocator<T>::size_) {
		T * buff = newcopy(allocator<T>::ptr_, allocator<T>::count_,  allocator<T>::size_ = allocator<T>::size_ * 2 + (allocator<T>::count_ == 0));
		delete allocator<T>::ptr_;
		allocator<T>::ptr_ = buff;
		allocator<T>::size_ = allocator<T>::size_ * 2 + (allocator<T>::count_ == 0) ;
	}
	allocator<T>::ptr_[allocator<T>::count_] = item;
	++allocator<T>::count_;
}
*/
template <typename T>
auto stack<T>::push( const T & item ) -> void
{
    if ( this->count_ == this->size_ ) {
        size_t array_size = this->size_ * 2 + ( this->size_ == 0 );

        stack<T> temp { array_size };
        while ( temp.count() < this->count_ ) {
            temp.push( this->ptr_[ temp.count() ] );
        }

        this->swap( temp );
    }

    construct( this->ptr_ + this->count_, item);
    ++this->count_;
}

template<typename T>
void stack<T>::pop() {
	if (allocator<T>::count_ == 0) {
		throw std::logic_error("Stack is empty!");
	} else {
		allocator<T>::count_--;
	}
}

template<typename T>
const T& stack<T>::top()
{
	if (allocator<T>::count_ == 0) {
		throw ("Stack is empty!");
	}
	return allocator<T>::ptr_[allocator<T>::count_ - 1];
}

template<typename T>
auto stack<T>::operator=(stack const & right) -> stack & {
	if (this != &right) {
	T* buff = newcopy(right.allocator<T>::ptr_, right.allocator<T>::size_, right.allocator<T>::count_);
	delete[] allocator<T>::ptr_;
	allocator<T>::ptr_ = buff;
	allocator<T>::count_ = right.allocator<T>::count_;
	allocator<T>::size_ = right.allocator<T>::size_;
	}
	return *this;
}

template<typename T>
auto stack<T>::empty() const -> bool{
	if (allocator<T>::count_ == 0){
		return true;
	} else{
		return false;
	}
}

#endif
<|endoftext|>


<commit_before>/** @file
    @brief OSVR plugin for LYRobotix Nolo trackers

    @date 2017

    @author
    Yann Vernier
    <http://yann.vernier.se>
*/

// Copyright 2014 Sensics, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Internal Includes
#include <osvr/PluginKit/PluginKit.h>
#include <osvr/PluginKit/AnalogInterfaceC.h>
#include <osvr/PluginKit/ButtonInterfaceC.h>
#include <osvr/PluginKit/TrackerInterfaceC.h>

// Generated JSON header file
#include "com_osvr_Nolo_json.h"

// Library/third-party includes
#include <hidapi.h>

// Standard includes
#include <iostream>
#include <wchar.h>
#include <string.h>

// Anonymous namespace to avoid symbol collision
namespace {

  // btea_decrypt function
  #include "btea.c"

  #if 0
  void hexdump(unsigned char *data, int len) {
    char fill = std::cout.fill();
    std::streamsize w = std::cout.width();
    std::ios_base::fmtflags f = std::cout.flags();
    for (int i=0; i<len; i++) {
      std::cout << std::setw(2) << std::setfill('0')
		<< std::hex << (int)data[i];
      if (i%8==7)
	std::cout << " ";
    }
    std::cout << std::endl;
    std::cout.fill(fill);
    std::cout.width(w);
    std::cout.flags(f);
  };
  #endif

static int NUM_AXIS    = 4;
static int NUM_BUTTONS = 6;
class NoloDevice {
  public:
    NoloDevice(OSVR_PluginRegContext ctx,
	       char *path) {
        /// Create the initialization options
        OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);

	std::cout << "Nolo: opening " << path <<
	  " for " << this << std::endl;
	m_hid = hid_open_path(path);

        /// Indicate that we'll want 7 analog channels.
        //osvrDeviceAnalogConfigure(opts, &m_analog, 2*3+1);
	// update this to 9 analog channels to include the trigger
        osvrDeviceAnalogConfigure(opts, &m_analog, NUM_AXIS*2+1);
	/// And 6 buttons per controller
        osvrDeviceButtonConfigure(opts, &m_button, 2*NUM_BUTTONS);
	/// And tracker capability
        osvrDeviceTrackerConfigure(opts, &m_tracker);

        /// Create the device token with the options
        m_dev.initAsync(ctx, "LYRobotix Nolo", opts);

        /// Send JSON descriptor
        m_dev.sendJsonDescriptor(com_osvr_Nolo_json);

        /// Register update callback
        m_dev.registerUpdateCallback(this);

	memset(&oldreports[0][0], 0, sizeof oldreports);
    }
    ~NoloDevice() {
      std::cout << "Nolo: deleting " << this << std::endl;
      hid_close(m_hid);
    }

    OSVR_ReturnCode update() {
      unsigned char buf[64];
      const int cryptwords = (64-4)/4, cryptoffset=1;
      static const uint32_t key[4] =
	{0x875bcc51, 0xa7637a66, 0x50960967, 0xf8536c51};
      uint32_t cryptpart[cryptwords];
      int i;

      // TODO: timestamp frame received?
      if (!m_hid)
	 OSVR_RETURN_FAILURE;
      //std::cout << "Reading HID data from " << m_hid << std::endl;
      if(hid_read(m_hid, buf, sizeof buf) != sizeof buf)
	return OSVR_RETURN_FAILURE;
      osvrTimeValueGetNow(&m_lastreport_time);

      // Check for duplicate reports before decrypting
      switch (buf[0]) {
	case 0xa5:
	case 0xa6:
	  if (memcmp(oldreports[buf[0]-0xa5], buf, sizeof buf))
	    memcpy(oldreports[buf[0]-0xa5], buf, sizeof buf);
	  else
	    return OSVR_RETURN_SUCCESS;	// Duplicate report
	  break;
	default:
	  return OSVR_RETURN_SUCCESS; // Unknown report
      }
      //std::cout << "R ";
      //hexdump(buf, sizeof buf);
      // Decrypt encrypted portion
      for (i=0; i<cryptwords; i++) {
	cryptpart[i] =
	  ((uint32_t)buf[cryptoffset+4*i  ])<<0  |
	  ((uint32_t)buf[cryptoffset+4*i+1])<<8  |
	  ((uint32_t)buf[cryptoffset+4*i+2])<<16 |
	  ((uint32_t)buf[cryptoffset+4*i+3])<<24;
      }
      btea_decrypt(cryptpart, cryptwords, 1, key);
      for (i=0; i<cryptwords; i++) {
	buf[cryptoffset+4*i  ] = cryptpart[i]>>0;
	buf[cryptoffset+4*i+1] = cryptpart[i]>>8;
	buf[cryptoffset+4*i+2] = cryptpart[i]>>16;
	buf[cryptoffset+4*i+3] = cryptpart[i]>>24;
      }
      //std::cout << "D ";
      //hexdump(buf, sizeof buf);

      switch (buf[0]) {
      case 0xa5:  // controllers frame
	decodeControllerCV1(0, buf+1);
	decodeControllerCV1(1, buf+64-controllerLength);
	break;
      case 0xa6:
	decodeHeadsetMarkerCV1(buf+0x15);
	decodeBaseStationCV1(buf+0x36);
	break;
      }
      return OSVR_RETURN_SUCCESS;
    }

    // Sets vibration strength in percent
    int setVibration(unsigned char left,
		     unsigned char right) {
      unsigned char data[4] = {0xaa, 0x66, left, right};
      return hid_write(m_hid, data, sizeof data);
    }

  private:
    unsigned char oldreports[2][64];
    void decodePosition(const unsigned char *data,
			OSVR_PositionState *pos) {
      const double scale = 0.0001;
      osvrVec3SetX(pos, scale * (int16_t)(data[0]<<8 | data[1]));
      osvrVec3SetY(pos, scale * (int16_t)(data[2]<<8 | data[3]));
      osvrVec3SetZ(pos, scale *          (data[4]<<8 | data[5]));
    }
    void decodeOrientation(const unsigned char *data,
			   OSVR_OrientationState *quat) {
      double w,i,j,k, scale;
      // CV1 order
      w = (int16_t)(data[0]<<8 | data[1]);
      i = (int16_t)(data[2]<<8 | data[3]);
      j = (int16_t)(data[4]<<8 | data[5]);
      k = (int16_t)(data[6]<<8 | data[7]);
      // Normalize (unknown if scale is constant)
      //scale = 1.0/sqrt(i*i+j*j+k*k+w*w);
      // Turns out it is fixed point. But the android driver author
      // either didn't know, or didn't trust it.
      // Unknown if normalizing it helps OSVR!
      scale = 1.0 / 16384;
      //std::cout << "Scale: " << scale << std::endl;
      w *= scale;
      i *= scale;
      j *= scale;
      k *= scale;
      // Reorder
      osvrQuatSetW(quat, w);
      osvrQuatSetX(quat, i);
      osvrQuatSetY(quat, k);
      osvrQuatSetZ(quat, -j);
    }
    void decodeControllerCV1(int idx, unsigned char *data) {
      OSVR_PoseState pose;
      uint8_t buttons, bit;
      int trigger_pressed = 0;

      if (data[0] != 2 || data[1] != 1) {
	// Unknown version
	/* Happens when controllers aren't on.
	std::cout << "Nolo: Unknown controller " << idx
		  << " version " << (int)data[0] << " " << (int)data[1]
		  << std::endl;
	*/
	return;
      }

      decodePosition(data+3, &pose.translation);
      decodeOrientation(data+3+3*2, &pose.rotation);

      osvrDeviceTrackerSendPoseTimestamped(m_dev, m_tracker, &pose, 2+idx, &m_lastreport_time);

      buttons = data[3+3*2+4*2];
      // TODO: report buttons for both controllers in one call?
      for (bit=0; bit<NUM_BUTTONS; bit++){
	osvrDeviceButtonSetValueTimestamped(m_dev, m_button,
				 (buttons & 1<<bit ? OSVR_BUTTON_PRESSED
				  : OSVR_BUTTON_NOT_PRESSED), idx*6+bit,
				 &m_lastreport_time);
	if(bit == 1){
	    if(buttons & 1<<bit){
		trigger_pressed = 1;
	    }else{
		trigger_pressed = 0;
	    }
	}
      }
      // next byte is touch ID bitmask (identical to buttons bit 5)

      // Touch X and Y coordinates
      // //assumes 0 to 255
      // normalize from -1 to 1
      // z = 2*[x - min / (max - min) - 1]
      // z = 2*(x - 0 / (255 - 0) - 1]
      // z = 2*(x/255) -1
      // normalize 0 to 1
      // x/255
      double axis_value;
      if (data[3+3*2+4*2+1]) {  // Only report touch if there is one
        axis_value = 2*data[3+3*2+4*2+2]/255.0 - 1;
        // invert axis
        axis_value *= -1;
        osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, axis_value,   idx*NUM_AXIS+0, &m_lastreport_time);
        axis_value = 2*((int)data[3+3*2+4*2+2+1])/255.0 -1;
        axis_value *= -1;
        osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, axis_value, idx*NUM_AXIS+1, &m_lastreport_time);
      }
      // trigger
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, trigger_pressed, idx*NUM_AXIS+2, &m_lastreport_time);
      // battery level
      axis_value = data[3+3*2+4*2+2+2]/255.0;
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, axis_value, idx*NUM_AXIS+3, &m_lastreport_time);
    }
    void decodeHeadsetMarkerCV1(unsigned char *data) {
      if (data[0] != 2 || data[1] != 1) {
	/* Happens with corrupt packages (mixed with controller data)
	std::cout << "Nolo: Unknown headset marker"
		  << " version " << (int)data[0] << " " << (int)data[1]
		  << std::endl;
	*/
	// Unknown version
	return;
      }

      OSVR_PositionState home;
      OSVR_PoseState hmd;

      decodePosition(data+3, &hmd.translation);
      decodePosition(data+3+3*2, &home);
      decodeOrientation(data+3+2*3*2+1, &hmd.rotation);

      // Tracker viewer kept using the home for head.
      // Something wrong with how they handle the descriptors.
      osvrDeviceTrackerSendPositionTimestamped(m_dev, m_tracker, &home, 0,
		      &m_lastreport_time);

      osvrDeviceTrackerSendPoseTimestamped(m_dev, m_tracker, &hmd, 1,
		      &m_lastreport_time);
    }
    void decodeBaseStationCV1(unsigned char *data) {
      if (data[0] != 2 || data[1] != 1)
	// Unknown version
	return;

      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, data[2], 2*NUM_AXIS,
		      &m_lastreport_time);
    }

    static const int controllerLength = 3 + (3+4)*2 + 2 + 2 + 1;
    osvr::pluginkit::DeviceToken m_dev;
    hid_device* m_hid;
    OSVR_AnalogDeviceInterface m_analog;
    OSVR_ButtonDeviceInterface m_button;
    OSVR_TrackerDeviceInterface m_tracker;
    OSVR_TimeValue m_lastreport_time;
};

class HardwareDetection {
  public:
    HardwareDetection() : m_found(false) {}
    OSVR_ReturnCode operator()(OSVR_PluginRegContext ctx) {

      // TODO: probe for new devices only
      if (m_found)
	return OSVR_RETURN_SUCCESS;

      struct hid_device_info *hid_devices, *cur_dev;
      hid_devices = hid_enumerate(0x0483, 0x5750);
      if (!hid_devices)
	return OSVR_RETURN_FAILURE;

      for (cur_dev = hid_devices; cur_dev; cur_dev = cur_dev->next) {
	if (wcscmp(cur_dev->manufacturer_string, L"LYRobotix")==0 &&
	    wcscmp(cur_dev->product_string, L"NOLO")==0) {
	  /// TODO: Distinguish Headset Marker from other parts?
	  /// Create our device object
	  osvr::pluginkit::registerObjectForDeletion
	    (ctx, new NoloDevice(ctx, cur_dev->path));
	  m_found = true;
	}
      }

      hid_free_enumeration(hid_devices);
      return OSVR_RETURN_SUCCESS;
    }

  private:
    /// @brief Have we found our device yet? (this limits the plugin to one
    /// instance)
    bool m_found;
};
} // namespace

OSVR_PLUGIN(com_osvr_Nolo) {
    hid_init();
    osvr::pluginkit::PluginContext context(ctx);

    /// Register a detection callback function object.
    context.registerHardwareDetectCallback(new HardwareDetection());

    return OSVR_RETURN_SUCCESS;
}
<commit_msg>Collect data field offsets in enums for clarity.<commit_after>/** @file
    @brief OSVR plugin for LYRobotix Nolo trackers

    @date 2017

    @author
    Yann Vernier
    <http://yann.vernier.se>
*/

// Copyright 2014 Sensics, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Internal Includes
#include <osvr/PluginKit/PluginKit.h>
#include <osvr/PluginKit/AnalogInterfaceC.h>
#include <osvr/PluginKit/ButtonInterfaceC.h>
#include <osvr/PluginKit/TrackerInterfaceC.h>

// Generated JSON header file
#include "com_osvr_Nolo_json.h"

// Library/third-party includes
#include <hidapi.h>

// Standard includes
#include <iostream>
#include <wchar.h>
#include <string.h>

// Anonymous namespace to avoid symbol collision
namespace {

  // btea_decrypt function
  #include "btea.c"

  #if 0
  void hexdump(unsigned char *data, int len) {
    char fill = std::cout.fill();
    std::streamsize w = std::cout.width();
    std::ios_base::fmtflags f = std::cout.flags();
    for (int i=0; i<len; i++) {
      std::cout << std::setw(2) << std::setfill('0')
		<< std::hex << (int)data[i];
      if (i%8==7)
	std::cout << " ";
    }
    std::cout << std::endl;
    std::cout.fill(fill);
    std::cout.width(w);
    std::cout.flags(f);
  };
  #endif

static int NUM_AXIS    = 4;
static int NUM_BUTTONS = 6;
class NoloDevice {
  public:
    NoloDevice(OSVR_PluginRegContext ctx,
	       char *path) {
        /// Create the initialization options
        OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);

	std::cout << "Nolo: opening " << path <<
	  " for " << this << std::endl;
	m_hid = hid_open_path(path);

        /// Indicate that we'll want 7 analog channels.
        //osvrDeviceAnalogConfigure(opts, &m_analog, 2*3+1);
	// update this to 9 analog channels to include the trigger
        osvrDeviceAnalogConfigure(opts, &m_analog, NUM_AXIS*2+1);
	/// And 6 buttons per controller
        osvrDeviceButtonConfigure(opts, &m_button, 2*NUM_BUTTONS);
	/// And tracker capability
        osvrDeviceTrackerConfigure(opts, &m_tracker);

        /// Create the device token with the options
        m_dev.initAsync(ctx, "LYRobotix Nolo", opts);

        /// Send JSON descriptor
        m_dev.sendJsonDescriptor(com_osvr_Nolo_json);

        /// Register update callback
        m_dev.registerUpdateCallback(this);

	memset(&oldreports[0][0], 0, sizeof oldreports);
    }
    ~NoloDevice() {
      std::cout << "Nolo: deleting " << this << std::endl;
      hid_close(m_hid);
    }

    OSVR_ReturnCode update() {
      unsigned char buf[64];
      const int cryptwords = (64-4)/4, cryptoffset=1;
      static const uint32_t key[4] =
	{0x875bcc51, 0xa7637a66, 0x50960967, 0xf8536c51};
      uint32_t cryptpart[cryptwords];
      int i;

      // TODO: timestamp frame received?
      if (!m_hid)
	 OSVR_RETURN_FAILURE;
      //std::cout << "Reading HID data from " << m_hid << std::endl;
      if(hid_read(m_hid, buf, sizeof buf) != sizeof buf)
	return OSVR_RETURN_FAILURE;
      osvrTimeValueGetNow(&m_lastreport_time);

      // Check for duplicate reports before decrypting
      switch (buf[0]) {
	case 0xa5:
	case 0xa6:
	  if (memcmp(oldreports[buf[0]-0xa5], buf, sizeof buf))
	    memcpy(oldreports[buf[0]-0xa5], buf, sizeof buf);
	  else
	    return OSVR_RETURN_SUCCESS;	// Duplicate report
	  break;
	default:
	  return OSVR_RETURN_SUCCESS; // Unknown report
      }
      //std::cout << "R ";
      //hexdump(buf, sizeof buf);
      // Decrypt encrypted portion
      for (i=0; i<cryptwords; i++) {
	cryptpart[i] =
	  ((uint32_t)buf[cryptoffset+4*i  ])<<0  |
	  ((uint32_t)buf[cryptoffset+4*i+1])<<8  |
	  ((uint32_t)buf[cryptoffset+4*i+2])<<16 |
	  ((uint32_t)buf[cryptoffset+4*i+3])<<24;
      }
      btea_decrypt(cryptpart, cryptwords, 1, key);
      for (i=0; i<cryptwords; i++) {
	buf[cryptoffset+4*i  ] = cryptpart[i]>>0;
	buf[cryptoffset+4*i+1] = cryptpart[i]>>8;
	buf[cryptoffset+4*i+2] = cryptpart[i]>>16;
	buf[cryptoffset+4*i+3] = cryptpart[i]>>24;
      }
      //std::cout << "D ";
      //hexdump(buf, sizeof buf);

      switch (buf[0]) {
      case 0xa5:  // controllers frame
	decodeControllerCV1(0, buf+1);
	decodeControllerCV1(1, buf+64-controllerLength);
	break;
      case 0xa6:
	decodeHeadsetMarkerCV1(buf+0x15);
	decodeBaseStationCV1(buf+0x36);
	break;
      }
      return OSVR_RETURN_SUCCESS;
    }

    // Sets vibration strength in percent
    int setVibration(unsigned char left,
		     unsigned char right) {
      unsigned char data[4] = {0xaa, 0x66, left, right};
      return hid_write(m_hid, data, sizeof data);
    }

  private:
    unsigned char oldreports[2][64];
    void decodePosition(const unsigned char *data,
			OSVR_PositionState *pos) {
      const double scale = 0.0001;
      osvrVec3SetX(pos, scale * (int16_t)(data[0]<<8 | data[1]));
      osvrVec3SetY(pos, scale * (int16_t)(data[2]<<8 | data[3]));
      osvrVec3SetZ(pos, scale *          (data[4]<<8 | data[5]));
    }
    void decodeOrientation(const unsigned char *data,
			   OSVR_OrientationState *quat) {
      double w,i,j,k, scale;
      // CV1 order
      w = (int16_t)(data[0]<<8 | data[1]);
      i = (int16_t)(data[2]<<8 | data[3]);
      j = (int16_t)(data[4]<<8 | data[5]);
      k = (int16_t)(data[6]<<8 | data[7]);
      // Normalize (unknown if scale is constant)
      //scale = 1.0/sqrt(i*i+j*j+k*k+w*w);
      // Turns out it is fixed point. But the android driver author
      // either didn't know, or didn't trust it.
      // Unknown if normalizing it helps OSVR!
      scale = 1.0 / 16384;
      //std::cout << "Scale: " << scale << std::endl;
      w *= scale;
      i *= scale;
      j *= scale;
      k *= scale;
      // Reorder
      osvrQuatSetW(quat, w);
      osvrQuatSetX(quat, i);
      osvrQuatSetY(quat, k);
      osvrQuatSetZ(quat, -j);
    }
    void decodeControllerCV1(int idx, unsigned char *data) {
      enum ControllerOffsets {
	    hwversion   = 0,	// guessed!
	    fwversion   = 1,
	    position    = 3,
	    orientation = 3+3*2,
	    ofsbuttons  = 3+3*2+4*2,
	    touchid     = 3+3*2+4*2+1,
	    touchx      = 3+3*2+4*2+2,
	    touchy      = 3+3*2+4*2+3,
	    battery     = 3+3*2+4*2+4,
      };
      OSVR_PoseState pose;
      uint8_t buttons, bit;
      int trigger_pressed = 0;

      if (data[hwversion] != 2 || data[fwversion] != 1) {
	// Unknown version
	/* Happens when controllers aren't on.
	std::cout << "Nolo: Unknown controller " << idx
		  << " version " << (int)data[0] << " " << (int)data[1]
		  << std::endl;
	*/
	return;
      }

      decodePosition(data+position, &pose.translation);
      decodeOrientation(data+orientation, &pose.rotation);

      osvrDeviceTrackerSendPoseTimestamped(m_dev, m_tracker, &pose, 2+idx, &m_lastreport_time);

      buttons = data[ofsbuttons];
      // TODO: report buttons for both controllers in one call?
      for (bit=0; bit<NUM_BUTTONS; bit++){
	osvrDeviceButtonSetValueTimestamped(m_dev, m_button,
				 (buttons & 1<<bit ? OSVR_BUTTON_PRESSED
				  : OSVR_BUTTON_NOT_PRESSED), idx*6+bit,
				 &m_lastreport_time);
	if(bit == 1){
	    if(buttons & 1<<bit){
		trigger_pressed = 1;
	    }else{
		trigger_pressed = 0;
	    }
	}
      }
      // next byte is touch ID bitmask (identical to buttons bit 5)

      // Touch X and Y coordinates
      // //assumes 0 to 255
      // normalize from -1 to 1
      // z = 2*[x - min / (max - min) - 1]
      // z = 2*(x - 0 / (255 - 0) - 1]
      // z = 2*(x/255) -1
      // normalize 0 to 1
      // x/255
      double axis_value;
      if (data[touchid]) {  // Only report touch if there is one
        axis_value = 2*data[touchx]/255.0 - 1;
        // invert axis
        axis_value *= -1;
        osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, axis_value,   idx*NUM_AXIS+0, &m_lastreport_time);
        axis_value = 2*((int)data[touchy])/255.0 -1;
        axis_value *= -1;
        osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, axis_value, idx*NUM_AXIS+1, &m_lastreport_time);
      }
      // trigger (emulated analog axis)
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, trigger_pressed, idx*NUM_AXIS+2, &m_lastreport_time);
      // battery level
      axis_value = data[battery]/255.0;
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, axis_value, idx*NUM_AXIS+3, &m_lastreport_time);
    }
    void decodeHeadsetMarkerCV1(unsigned char *data) {
      enum MarkerOffsets {
	    hwversion    = 0,	// guessed!
	    fwversion    = 1,
	    position     = 3,
	    homeposition = 3+3*2,
	    orientation  = 3+2*3*2+1,
      };
      if (data[hwversion] != 2 || data[fwversion] != 1) {
	/* Happens with corrupt packages (mixed with controller data)
	std::cout << "Nolo: Unknown headset marker"
		  << " version " << (int)data[0] << " " << (int)data[1]
		  << std::endl;
	*/
	// Unknown version
	return;
      }

      OSVR_PositionState home;
      OSVR_PoseState hmd;

      decodePosition(data+position, &hmd.translation);
      decodePosition(data+homeposition, &home);
      decodeOrientation(data+orientation, &hmd.rotation);

      // Tracker viewer kept using the home for head.
      // Something wrong with how they handle the descriptors.
      osvrDeviceTrackerSendPositionTimestamped(m_dev, m_tracker, &home, 0,
		      &m_lastreport_time);

      osvrDeviceTrackerSendPoseTimestamped(m_dev, m_tracker, &hmd, 1,
		      &m_lastreport_time);
    }
    void decodeBaseStationCV1(unsigned char *data) {
      enum MarkerOffsets {
	    hwversion = 0,	// guessed!
	    fwversion = 1,
	    battery   = 2
      };
      if (data[hwversion] != 2 || data[fwversion] != 1)
	// Unknown version
	return;

      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, data[battery], 2*NUM_AXIS,
		      &m_lastreport_time);
    }

    static const int controllerLength = 3 + (3+4)*2 + 2 + 2 + 1;
    osvr::pluginkit::DeviceToken m_dev;
    hid_device* m_hid;
    OSVR_AnalogDeviceInterface m_analog;
    OSVR_ButtonDeviceInterface m_button;
    OSVR_TrackerDeviceInterface m_tracker;
    OSVR_TimeValue m_lastreport_time;
};

class HardwareDetection {
  public:
    HardwareDetection() : m_found(false) {}
    OSVR_ReturnCode operator()(OSVR_PluginRegContext ctx) {

      // TODO: probe for new devices only
      if (m_found)
	return OSVR_RETURN_SUCCESS;

      struct hid_device_info *hid_devices, *cur_dev;
      hid_devices = hid_enumerate(0x0483, 0x5750);
      if (!hid_devices)
	return OSVR_RETURN_FAILURE;

      for (cur_dev = hid_devices; cur_dev; cur_dev = cur_dev->next) {
	if (wcscmp(cur_dev->manufacturer_string, L"LYRobotix")==0 &&
	    wcscmp(cur_dev->product_string, L"NOLO")==0) {
	  /// TODO: Distinguish Headset Marker from other parts?
	  /// Create our device object
	  osvr::pluginkit::registerObjectForDeletion
	    (ctx, new NoloDevice(ctx, cur_dev->path));
	  m_found = true;
	}
      }

      hid_free_enumeration(hid_devices);
      return OSVR_RETURN_SUCCESS;
    }

  private:
    /// @brief Have we found our device yet? (this limits the plugin to one
    /// instance)
    bool m_found;
};
} // namespace

OSVR_PLUGIN(com_osvr_Nolo) {
    hid_init();
    osvr::pluginkit::PluginContext context(ctx);

    /// Register a detection callback function object.
    context.registerHardwareDetectCallback(new HardwareDetection());

    return OSVR_RETURN_SUCCESS;
}
<|endoftext|>


<commit_before>//  "Copyright (C) 2017, Koshkina Uliana"
#ifndef WORKSPACE_HOMEWORK_HOMEWORK_02_STACK_STACK_U_INCLUDE_STACK_HPP_
#define WORKSPACE_HOMEWORK_HOMEWORK_02_STACK_STACK_U_INCLUDE_STACK_HPP_

#include <iostream>
using std::cout;
using std::endl;
using std::runtime_error;
using std::exception;

template <typename T>
class Stack {
        T* array_;
        size_t array_size_;
        size_t count_ = 0;

 public:
        Stack() : Stack(100) {}

        explicit Stack(size_t _size):array_size_(_size), array_(new T[_size]) {}

        size_t count() const {
            return count_;
        }

        bool empty() const {
            return count();
        }

        void push(T const &item) {
            if (count_ == array_size_) {
                T* new_array_ = new T[array_size_*2];
                for (int i = 0; i < array_size_; i++) {
                    new_array_[i] = array_[i];
                }
                array_size_ = array_size_ * 2;

                delete[] array_;
                array_ = new_array_;
            }
            array_[count_++] = item;
        }

        T pop() {
            if(count_ == 0) throw runtime_error(1);
            return array_[--count_];
        }

        ~Stack() {
            delete[] array_;
        }
};

#endif  //  WORKSPACE_HOMEWORK_HOMEWORK_02_STACK_STACK_U_INCLUDE_STACK_HPP_
<commit_msg>added<commit_after>//  "Copyright (C) 2017, Koshkina Uliana"
#ifndef WORKSPACE_HOMEWORK_HOMEWORK_02_STACK_STACK_U_INCLUDE_STACK_HPP_
#define WORKSPACE_HOMEWORK_HOMEWORK_02_STACK_STACK_U_INCLUDE_STACK_HPP_

#include <iostream>
#include <stdexcept>
using std::cout;
using std::endl;

template <typename T>
class Stack {
        T* array_;
        size_t array_size_;
        size_t count_ = 0;

 public:
        Stack() : Stack(100) {}

        explicit Stack(size_t _size):array_size_(_size), array_(new T[_size]) {}

        size_t count() const {
            return count_;
        }

        bool empty() const {
            return count();
        }

        void push(T const &item) {
            if (count_ == array_size_) {
                T* new_array_ = new T[array_size_*2];
                for (int i = 0; i < array_size_; i++) {
                    new_array_[i] = array_[i];
                }
                array_size_ = array_size_ * 2;

                delete[] array_;
                array_ = new_array_;
            }
            array_[count_++] = item;
        }

        T pop() {
            if(count_ == 0) throw std::logic_error("Error");
            return array_[--count_];
        }

        ~Stack() {
            delete[] array_;
        }
};

#endif  //  WORKSPACE_HOMEWORK_HOMEWORK_02_STACK_STACK_U_INCLUDE_STACK_HPP_
<|endoftext|>


<commit_before>#ifndef stack_cpp
#define stack_cpp
#pragma once
#include <iostream>

template <typename T1, typename T2>
void construct(T1 * ptr, T2 const & value) {
    new(ptr) T1 (value);
}

template <typename T>
void destroy(T * ptr) noexcept
{
    ptr->~T();
}

template <typename FwdIter>
void destroy(FwdIter first, FwdIter last) noexcept
{
    for (; first != last; ++first) {
        destroy(&*first);
    }
}

template <typename T>
class allocator
{
protected:
    allocator(size_t size = 0);
    ~allocator();
    auto swap(allocator & other) -> void;

    allocator(allocator const &) = delete;
    auto operator =(allocator const &) -> allocator & = delete;

    T * ptr_;
    size_t size_;
    size_t count_;
};

template<typename T>
allocator<T>::allocator(size_t size) :
	ptr_(static_cast<T *>(size == 0 ? nullptr : operator new(size * sizeof(T)))),
	size_(0),
	count_(size) {
}

template<typename T>
allocator<T>::~allocator() {
	delete ptr_;
}

template<typename T>
auto allocator<T>::swap(allocator & other) -> void {
	std::swap(ptr_, other.ptr_);
	std::swap(count_, other.count_);
	std::swap(size_, other.size_);
}

template <typename T>
class stack : private allocator<T>
{
public:
	stack(size_t size = 0 ); //noexcept
	stack(stack const &);  //strong
	~stack(); //noexcept
	size_t count() const;  //noexcept
	auto push(T const &) -> void; //strong
	void pop(); //strong
	const T& top(); //strong
	auto operator=(stack const & right)->stack &; //strong
	auto empty() const -> bool; //noexcept

};
template <typename T>
size_t stack<T>::count() const
{
	return allocator<T>::count_;
}
template <typename T>
stack<T>::stack(size_t size):allocator<T>(size){}

template <typename T>
stack<T>::stack(const stack& item) : allocator<T>(item.size_){
	for (size_t i = 0; i < item.count_; i++) construct(allocator<T>::ptr_ + i, item.ptr_[i]);
	allocator<T>::count_ = item.count_;
};

template <typename T>
stack<T>::~stack()
{
}

template <typename T>
auto stack<T>::push( const T & item ) -> void
{
    if ( this->count_ == this->size_ ) {
        size_t array_size = this->size_ * 2 + ( this->size_ == 0 );

        stack<T> temp ( array_size );
        while ( temp.count() < this->count_ ) {
            temp.push( this->ptr_[ temp.count() ] );
        }

        this->swap( temp );
    }

    construct( this->ptr_ + this->count_, item);
    ++this->count_;
}

template<typename T>
void stack<T>::pop() {
	if (allocator<T>::count_ == 0) {
		throw std::logic_error("Stack is empty!");
	} else {
		allocator<T>::count_--;
	}
}

template<typename T>
const T& stack<T>::top()
{
	if (allocator<T>::count_ == 0) {
		throw ("Stack is empty!");
	}
	return allocator<T>::ptr_[allocator<T>::count_ - 1];
}

template<typename T>
auto stack<T>::operator=(stack const & right) -> stack & {
	if (this != &right) {
	stack<T> temp (right.size_);
	while (temp.count_ < right.count_){
		construct(temp.ptr_ + temp.count_, right.ptr_[temp.count_]);
		++temp.count_;
	}
	this -> swap(temp);
	}
	return *this;
}

template<typename T>
auto stack<T>::empty() const -> bool{
	if (allocator<T>::count_ == 0){
		return true;
	} else{
		return false;
	}
}

#endif
<commit_msg>Update stack.hpp<commit_after>#ifndef stack_cpp
#define stack_cpp
#pragma once
#include <iostream>

template <typename T1, typename T2>
void construct(T1 * ptr, T2 const & value) {
    new(ptr) T1 (value);
}

template <typename T>
void destroy(T * ptr) noexcept
{
    ptr->~T();
}

template <typename FwdIter>
void destroy(FwdIter first, FwdIter last) noexcept
{
    for (; first != last; ++first) {
        destroy(&*first);
    }
}

template <typename T>
class allocator
{
protected:
    allocator(size_t size = 0);
    ~allocator();
    auto swap(allocator & other) -> void;

    allocator(allocator const &) = delete;
    auto operator =(allocator const &) -> allocator & = delete;

    T * ptr_;
    size_t size_;
    size_t count_;
};

template<typename T>
allocator<T>::allocator(size_t size) :
	ptr_(static_cast<T *>(size == 0 ? nullptr : operator new(size * sizeof(T)))),
	size_(0),
	count_(size) {
}

template<typename T>
allocator<T>::~allocator() {
	delete ptr_;
}

template<typename T>
auto allocator<T>::swap(allocator & other) -> void {
	std::swap(ptr_, other.ptr_);
	std::swap(count_, other.count_);
	std::swap(size_, other.size_);
}

template <typename T>
class stack : private allocator<T>
{
public:
	stack(size_t size = 0 ); //noexcept
	stack(stack const &);  //strong
	~stack(); //noexcept
	size_t count() const;  //noexcept
	auto push(T const &) -> void; //strong
	void pop(); //strong
	const T& top(); //strong
	auto operator=(stack const & right)->stack &; //strong
	auto empty() const -> bool; //noexcept

};
template <typename T>
size_t stack<T>::count() const
{
	return allocator<T>::count_;
}
template <typename T>
stack<T>::stack(size_t size):allocator<T>(size){}

template <typename T>
stack<T>::stack(const stack& item) : allocator<T>(item.size_){
	for (size_t i = 0; i < item.count_; i++) construct(allocator<T>::ptr_ + i, item.ptr_[i]);
	allocator<T>::count_ = item.count_;
};

template <typename T>
stack<T>::~stack()
{
}

template <typename T>
void stack<T>::push(T const &item){
	if (allocator<T>::count_ == allocator<T>::size_) {
		size_t array_size = allocator<T>::size_ * 2 + (allocator<T>::size_ == 0);
		stack<T> temp(array_size);
		while (temp.count() < allocator<T>::count_) temp.push(allocator<T>::ptr_[temp.count()]);
		this->swap(temp);
	}
	construct(allocator<T>::ptr_ + allocator<T>::count_, item);
	++allocator<T>::count_;
}

template<typename T>
void stack<T>::pop() {
	if (allocator<T>::count_ == 0) {
		throw std::logic_error("Stack is empty!");
	} else {
		allocator<T>::count_--;
	}
}

template<typename T>
const T& stack<T>::top()
{
	if (allocator<T>::count_ == 0) {
		throw ("Stack is empty!");
	}
	return allocator<T>::ptr_[allocator<T>::count_ - 1];
}

template<typename T>
auto stack<T>::operator=(stack const & right) -> stack & {
	if (this != &right) {
	stack<T> temp (right.size_);
	while (temp.count_ < right.count_){
		construct(temp.ptr_ + temp.count_, right.ptr_[temp.count_]);
		++temp.count_;
	}
	this -> swap(temp);
	}
	return *this;
}

template<typename T>
auto stack<T>::empty() const -> bool{
	if (allocator<T>::count_ == 0){
		return true;
	} else{
		return false;
	}
}

#endif
<|endoftext|>


<commit_before>//
// Class for performing common math operations (e.g., dot, cross products)
//
#ifndef __vlMath_hh
#define __vlMath_hh

#include <math.h>

class vlMath
{
public:
  vlMath();
  float Pi() {return 3.14159265358979;};
  float DegreesToRadians() {return 0.018977369;};
  float Dot(float x[3], float y[3])
    {return x[0]*y[0] + x[1]*y[1] + x[2]*y[2];};
  float Norm(float x[3])
    {return sqrt(x[0]*x[0] + x[1]*x[1] + x[2]*x[2]);};
  void RandomSeed(long s);
  float Random();
private:
  long Seed;
};

#endif
<commit_msg>ERR: Created constructor.<commit_after>//
// Class for performing common math operations (e.g., dot, cross products)
//
#ifndef __vlMath_hh
#define __vlMath_hh

#include <math.h>

class vlMath
{
public:
  vlMath() {this->Seed = 1177;};
  float Pi() {return 3.14159265358979;};
  float DegreesToRadians() {return 0.018977369;};
  float Dot(float x[3], float y[3])
    {return x[0]*y[0] + x[1]*y[1] + x[2]*y[2];};
  float Norm(float x[3])
    {return sqrt(x[0]*x[0] + x[1]*x[1] + x[2]*x[2]);};
  void RandomSeed(long s);
  float Random();
private:
  long Seed;
};

#endif
<|endoftext|>