<commit_before>/****************************************************************************
 *
 * Copyright (c) 2015 Mark Charlebois. All rights reserved.
 * Copyright (c) 2018 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#include "cdev_platform.hpp"

#include <string>
#include <map>

#include "vfile.h"
#include "../CDev.hpp"

#include <px4_log.h>
#include <px4_posix.h>
#include <px4_time.h>

#include "DevMgr.hpp"

using namespace std;
using namespace DriverFramework;

const cdev::px4_file_operations_t cdev::CDev::fops = {};

pthread_mutex_t devmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t filemutex = PTHREAD_MUTEX_INITIALIZER;

px4_sem_t lockstep_sem;
bool sim_lockstep = false;
volatile bool sim_delay = false;

#define PX4_MAX_FD 350
static map<string, void *> devmap;
static cdev::file_t filemap[PX4_MAX_FD] = {};

extern "C" {

	int px4_errno;

	static cdev::CDev *getDev(const char *path)
	{
		PX4_DEBUG("CDev::getDev");

		pthread_mutex_lock(&devmutex);

		auto item = devmap.find(path);

		if (item != devmap.end()) {
			pthread_mutex_unlock(&devmutex);
			return (cdev::CDev *)item->second;
		}

		pthread_mutex_unlock(&devmutex);

		return nullptr;
	}

	static cdev::CDev *get_vdev(int fd)
	{
		pthread_mutex_lock(&filemutex);
		bool valid = (fd < PX4_MAX_FD && fd >= 0 && filemap[fd].vdev);
		cdev::CDev *dev;

		if (valid) {
			dev = (cdev::CDev *)(filemap[fd].vdev);

		} else {
			dev = nullptr;
		}

		pthread_mutex_unlock(&filemutex);
		return dev;
	}

	int register_driver(const char *name, const cdev::px4_file_operations_t *fops, cdev::mode_t mode, void *data)
	{
		PX4_DEBUG("CDev::register_driver %s", name);
		int ret = 0;

		if (name == nullptr || data == nullptr) {
			return -EINVAL;
		}

		pthread_mutex_lock(&devmutex);

		// Make sure the device does not already exist
		auto item = devmap.find(name);

		if (item != devmap.end()) {
			pthread_mutex_unlock(&devmutex);
			return -EEXIST;
		}

		devmap[name] = (void *)data;
		PX4_DEBUG("Registered DEV %s", name);

		pthread_mutex_unlock(&devmutex);

		return ret;
	}

	int unregister_driver(const char *name)
	{
		PX4_DEBUG("CDev::unregister_driver %s", name);
		int ret = -EINVAL;

		if (name == nullptr) {
			return -EINVAL;
		}

		pthread_mutex_lock(&devmutex);

		if (devmap.erase(name) > 0) {
			PX4_DEBUG("Unregistered DEV %s", name);
			ret = 0;
		}

		pthread_mutex_unlock(&devmutex);

		return ret;
	}

	int px4_open(const char *path, int flags, ...)
	{
		PX4_DEBUG("px4_open");
		cdev::CDev *dev = getDev(path);
		int ret = 0;
		int i;
		mode_t mode;

		if (!dev && (flags & PX4_F_WRONLY) != 0 &&
		    strncmp(path, "/obj/", 5) != 0 &&
		    strncmp(path, "/dev/", 5) != 0) {
			va_list p;
			va_start(p, flags);
			mode = va_arg(p, int);
			va_end(p);

			// Create the file
			PX4_DEBUG("Creating virtual file %s", path);
			dev = cdev::VFile::createFile(path, mode);
		}

		if (dev) {

			pthread_mutex_lock(&filemutex);

			for (i = 0; i < PX4_MAX_FD; ++i) {
				if (filemap[i].vdev == nullptr) {
					filemap[i] = cdev::file_t(flags, dev);
					break;
				}
			}

			pthread_mutex_unlock(&filemutex);

			if (i < PX4_MAX_FD) {
				ret = dev->open(&filemap[i]);

			} else {

				const unsigned NAMELEN = 32;
				char thread_name[NAMELEN] = {};

				PX4_WARN("%s: exceeded maximum number of file descriptors, accessing %s",
					 thread_name, path);
#ifndef __PX4_QURT
				int nret = pthread_getname_np(pthread_self(), thread_name, NAMELEN);

				if (nret || thread_name[0] == 0) {
					PX4_WARN("failed getting thread name");
				}

				PX4_BACKTRACE();
#endif

				ret = -ENOENT;
			}

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			return -1;
		}

		PX4_DEBUG("px4_open fd = %d", i);
		return i;
	}

	int px4_close(int fd)
	{
		int ret;

		cdev::CDev *dev = get_vdev(fd);

		if (dev) {
			pthread_mutex_lock(&filemutex);
			ret = dev->close(&filemap[fd]);

			filemap[fd].vdev = nullptr;

			pthread_mutex_unlock(&filemutex);
			PX4_DEBUG("px4_close fd = %d", fd);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			ret = PX4_ERROR;
		}

		return ret;
	}

	ssize_t px4_read(int fd, void *buffer, size_t buflen)
	{
		int ret;

		cdev::CDev *dev = get_vdev(fd);

		if (dev) {
			PX4_DEBUG("px4_read fd = %d", fd);
			ret = dev->read(&filemap[fd], (char *)buffer, buflen);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			ret = PX4_ERROR;
		}

		return ret;
	}

	ssize_t px4_write(int fd, const void *buffer, size_t buflen)
	{
		int ret;

		cdev::CDev *dev = get_vdev(fd);

		if (dev) {
			PX4_DEBUG("px4_write fd = %d", fd);
			ret = dev->write(&filemap[fd], (const char *)buffer, buflen);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			ret = PX4_ERROR;
		}

		return ret;
	}

	int px4_ioctl(int fd, int cmd, unsigned long arg)
	{
		PX4_DEBUG("px4_ioctl fd = %d", fd);
		int ret = 0;

		cdev::CDev *dev = get_vdev(fd);

		if (dev) {
			ret = dev->ioctl(&filemap[fd], cmd, arg);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
		}

		return ret;
	}

	int px4_poll(px4_pollfd_struct_t *fds, nfds_t nfds, int timeout)
	{
		if (nfds == 0) {
			PX4_WARN("px4_poll with no fds");
			return -1;
		}

		px4_sem_t sem;
		int count = 0;
		int ret = -1;
		unsigned int i;

		const unsigned NAMELEN = 32;
		char thread_name[NAMELEN] = {};

#ifndef __PX4_QURT
		int nret = pthread_getname_np(pthread_self(), thread_name, NAMELEN);

		if (nret || thread_name[0] == 0) {
			PX4_WARN("failed getting thread name");
		}

#endif

		while (sim_delay) {
			px4_usleep(100);
		}

		PX4_DEBUG("Called px4_poll timeout = %d", timeout);

		px4_sem_init(&sem, 0, 0);

		// sem use case is a signal
		px4_sem_setprotocol(&sem, SEM_PRIO_NONE);

		// Go through all fds and check them for a pollable state
		bool fd_pollable = false;

		for (i = 0; i < nfds; ++i) {
			fds[i].sem     = &sem;
			fds[i].revents = 0;
			fds[i].priv    = nullptr;

			cdev::CDev *dev = get_vdev(fds[i].fd);

			// If fd is valid
			if (dev) {
				PX4_DEBUG("%s: px4_poll: CDev->poll(setup) %d", thread_name, fds[i].fd);
				ret = dev->poll(&filemap[fds[i].fd], &fds[i], true);

				if (ret < 0) {
					PX4_WARN("%s: px4_poll() error: %s",
						 thread_name, strerror(errno));
					break;
				}

				if (ret >= 0) {
					fd_pollable = true;
				}
			}
		}

		// If any FD can be polled, lock the semaphore and
		// check for new data
		if (fd_pollable) {
			if (timeout > 0) {

				// Get the current time
				struct timespec ts;
				// Note, we can't actually use CLOCK_MONOTONIC on macOS
				// but that's hidden and implemented in px4_clock_gettime.
				px4_clock_gettime(CLOCK_MONOTONIC, &ts);

				// Calculate an absolute time in the future
				const unsigned billion = (1000 * 1000 * 1000);
				uint64_t nsecs = ts.tv_nsec + (timeout * 1000 * 1000);
				ts.tv_sec += nsecs / billion;
				nsecs -= (nsecs / billion) * billion;
				ts.tv_nsec = nsecs;

				ret = px4_sem_timedwait(&sem, &ts);

				if (ret && errno != ETIMEDOUT) {
					PX4_WARN("%s: px4_poll() sem error: %s", thread_name, strerror(errno));
				}

			} else if (timeout < 0) {
				px4_sem_wait(&sem);
			}

			// We have waited now (or not, depending on timeout),
			// go through all fds and count how many have data
			for (i = 0; i < nfds; ++i) {

				cdev::CDev *dev = get_vdev(fds[i].fd);

				// If fd is valid
				if (dev) {
					PX4_DEBUG("%s: px4_poll: CDev->poll(teardown) %d", thread_name, fds[i].fd);
					ret = dev->poll(&filemap[fds[i].fd], &fds[i], false);

					if (ret < 0) {
						PX4_WARN("%s: px4_poll() 2nd poll fail", thread_name);
						break;
					}

					if (fds[i].revents) {
						count += 1;
					}
				}
			}
		}

		px4_sem_destroy(&sem);

		// Return the positive count if present,
		// return the negative error number if failed
		return (count) ? count : ret;
	}

	int px4_fsync(int fd)
	{
		return 0;
	}

	int px4_access(const char *pathname, int mode)
	{
		if (mode != F_OK) {
			errno = EINVAL;
			return -1;
		}

		cdev::CDev *dev = getDev(pathname);
		return (dev != nullptr) ? 0 : -1;
	}

	void px4_show_devices()
	{
		int i = 0;
		PX4_INFO("PX4 Devices:");

		pthread_mutex_lock(&devmutex);

		for (const auto &dev : devmap) {
			if (strncmp(dev.first.c_str(), "/dev/", 5) == 0) {
				PX4_INFO("   %s", dev.first.c_str());
			}
		}

		pthread_mutex_unlock(&devmutex);

		PX4_INFO("DF Devices:");
		const char *dev_path;
		unsigned int index = 0;
		i = 0;

		do {
			// Each look increments index and returns -1 if end reached
			i = DevMgr::getNextDeviceName(index, &dev_path);

			if (i == 0) {
				PX4_INFO("   %s", dev_path);
			}
		} while (i == 0);
	}

	void px4_show_topics()
	{
		PX4_INFO("Devices:");

		pthread_mutex_lock(&devmutex);

		for (const auto &dev : devmap) {
			if (strncmp(dev.first.c_str(), "/obj/", 5) == 0) {
				PX4_INFO("   %s", dev.first.c_str());
			}
		}

		pthread_mutex_unlock(&devmutex);
	}

	void px4_show_files()
	{
		PX4_INFO("Files:");

		pthread_mutex_lock(&devmutex);

		for (const auto &dev : devmap) {
			if (strncmp(dev.first.c_str(), "/obj/", 5) != 0 &&
			    strncmp(dev.first.c_str(), "/dev/", 5) != 0) {
				PX4_INFO("   %s", dev.first.c_str());
			}
		}

		pthread_mutex_unlock(&devmutex);
	}

} // extern "C"
<commit_msg>cdev_platform: remove unused code (sim_delay)<commit_after>/****************************************************************************
 *
 * Copyright (c) 2015 Mark Charlebois. All rights reserved.
 * Copyright (c) 2018 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#include "cdev_platform.hpp"

#include <string>
#include <map>

#include "vfile.h"
#include "../CDev.hpp"

#include <px4_log.h>
#include <px4_posix.h>
#include <px4_time.h>

#include "DevMgr.hpp"

using namespace std;
using namespace DriverFramework;

const cdev::px4_file_operations_t cdev::CDev::fops = {};

pthread_mutex_t devmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t filemutex = PTHREAD_MUTEX_INITIALIZER;

#define PX4_MAX_FD 350
static map<string, void *> devmap;
static cdev::file_t filemap[PX4_MAX_FD] = {};

extern "C" {

	int px4_errno;

	static cdev::CDev *getDev(const char *path)
	{
		PX4_DEBUG("CDev::getDev");

		pthread_mutex_lock(&devmutex);

		auto item = devmap.find(path);

		if (item != devmap.end()) {
			pthread_mutex_unlock(&devmutex);
			return (cdev::CDev *)item->second;
		}

		pthread_mutex_unlock(&devmutex);

		return nullptr;
	}

	static cdev::CDev *get_vdev(int fd)
	{
		pthread_mutex_lock(&filemutex);
		bool valid = (fd < PX4_MAX_FD && fd >= 0 && filemap[fd].vdev);
		cdev::CDev *dev;

		if (valid) {
			dev = (cdev::CDev *)(filemap[fd].vdev);

		} else {
			dev = nullptr;
		}

		pthread_mutex_unlock(&filemutex);
		return dev;
	}

	int register_driver(const char *name, const cdev::px4_file_operations_t *fops, cdev::mode_t mode, void *data)
	{
		PX4_DEBUG("CDev::register_driver %s", name);
		int ret = 0;

		if (name == nullptr || data == nullptr) {
			return -EINVAL;
		}

		pthread_mutex_lock(&devmutex);

		// Make sure the device does not already exist
		auto item = devmap.find(name);

		if (item != devmap.end()) {
			pthread_mutex_unlock(&devmutex);
			return -EEXIST;
		}

		devmap[name] = (void *)data;
		PX4_DEBUG("Registered DEV %s", name);

		pthread_mutex_unlock(&devmutex);

		return ret;
	}

	int unregister_driver(const char *name)
	{
		PX4_DEBUG("CDev::unregister_driver %s", name);
		int ret = -EINVAL;

		if (name == nullptr) {
			return -EINVAL;
		}

		pthread_mutex_lock(&devmutex);

		if (devmap.erase(name) > 0) {
			PX4_DEBUG("Unregistered DEV %s", name);
			ret = 0;
		}

		pthread_mutex_unlock(&devmutex);

		return ret;
	}

	int px4_open(const char *path, int flags, ...)
	{
		PX4_DEBUG("px4_open");
		cdev::CDev *dev = getDev(path);
		int ret = 0;
		int i;
		mode_t mode;

		if (!dev && (flags & PX4_F_WRONLY) != 0 &&
		    strncmp(path, "/obj/", 5) != 0 &&
		    strncmp(path, "/dev/", 5) != 0) {
			va_list p;
			va_start(p, flags);
			mode = va_arg(p, int);
			va_end(p);

			// Create the file
			PX4_DEBUG("Creating virtual file %s", path);
			dev = cdev::VFile::createFile(path, mode);
		}

		if (dev) {

			pthread_mutex_lock(&filemutex);

			for (i = 0; i < PX4_MAX_FD; ++i) {
				if (filemap[i].vdev == nullptr) {
					filemap[i] = cdev::file_t(flags, dev);
					break;
				}
			}

			pthread_mutex_unlock(&filemutex);

			if (i < PX4_MAX_FD) {
				ret = dev->open(&filemap[i]);

			} else {

				const unsigned NAMELEN = 32;
				char thread_name[NAMELEN] = {};

				PX4_WARN("%s: exceeded maximum number of file descriptors, accessing %s",
					 thread_name, path);
#ifndef __PX4_QURT
				int nret = pthread_getname_np(pthread_self(), thread_name, NAMELEN);

				if (nret || thread_name[0] == 0) {
					PX4_WARN("failed getting thread name");
				}

				PX4_BACKTRACE();
#endif

				ret = -ENOENT;
			}

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			return -1;
		}

		PX4_DEBUG("px4_open fd = %d", i);
		return i;
	}

	int px4_close(int fd)
	{
		int ret;

		cdev::CDev *dev = get_vdev(fd);

		if (dev) {
			pthread_mutex_lock(&filemutex);
			ret = dev->close(&filemap[fd]);

			filemap[fd].vdev = nullptr;

			pthread_mutex_unlock(&filemutex);
			PX4_DEBUG("px4_close fd = %d", fd);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			ret = PX4_ERROR;
		}

		return ret;
	}

	ssize_t px4_read(int fd, void *buffer, size_t buflen)
	{
		int ret;

		cdev::CDev *dev = get_vdev(fd);

		if (dev) {
			PX4_DEBUG("px4_read fd = %d", fd);
			ret = dev->read(&filemap[fd], (char *)buffer, buflen);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			ret = PX4_ERROR;
		}

		return ret;
	}

	ssize_t px4_write(int fd, const void *buffer, size_t buflen)
	{
		int ret;

		cdev::CDev *dev = get_vdev(fd);

		if (dev) {
			PX4_DEBUG("px4_write fd = %d", fd);
			ret = dev->write(&filemap[fd], (const char *)buffer, buflen);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
			ret = PX4_ERROR;
		}

		return ret;
	}

	int px4_ioctl(int fd, int cmd, unsigned long arg)
	{
		PX4_DEBUG("px4_ioctl fd = %d", fd);
		int ret = 0;

		cdev::CDev *dev = get_vdev(fd);

		if (dev) {
			ret = dev->ioctl(&filemap[fd], cmd, arg);

		} else {
			ret = -EINVAL;
		}

		if (ret < 0) {
			px4_errno = -ret;
		}

		return ret;
	}

	int px4_poll(px4_pollfd_struct_t *fds, nfds_t nfds, int timeout)
	{
		if (nfds == 0) {
			PX4_WARN("px4_poll with no fds");
			return -1;
		}

		px4_sem_t sem;
		int count = 0;
		int ret = -1;
		unsigned int i;

		const unsigned NAMELEN = 32;
		char thread_name[NAMELEN] = {};

#ifndef __PX4_QURT
		int nret = pthread_getname_np(pthread_self(), thread_name, NAMELEN);

		if (nret || thread_name[0] == 0) {
			PX4_WARN("failed getting thread name");
		}

#endif

		PX4_DEBUG("Called px4_poll timeout = %d", timeout);

		px4_sem_init(&sem, 0, 0);

		// sem use case is a signal
		px4_sem_setprotocol(&sem, SEM_PRIO_NONE);

		// Go through all fds and check them for a pollable state
		bool fd_pollable = false;

		for (i = 0; i < nfds; ++i) {
			fds[i].sem     = &sem;
			fds[i].revents = 0;
			fds[i].priv    = nullptr;

			cdev::CDev *dev = get_vdev(fds[i].fd);

			// If fd is valid
			if (dev) {
				PX4_DEBUG("%s: px4_poll: CDev->poll(setup) %d", thread_name, fds[i].fd);
				ret = dev->poll(&filemap[fds[i].fd], &fds[i], true);

				if (ret < 0) {
					PX4_WARN("%s: px4_poll() error: %s",
						 thread_name, strerror(errno));
					break;
				}

				if (ret >= 0) {
					fd_pollable = true;
				}
			}
		}

		// If any FD can be polled, lock the semaphore and
		// check for new data
		if (fd_pollable) {
			if (timeout > 0) {

				// Get the current time
				struct timespec ts;
				// Note, we can't actually use CLOCK_MONOTONIC on macOS
				// but that's hidden and implemented in px4_clock_gettime.
				px4_clock_gettime(CLOCK_MONOTONIC, &ts);

				// Calculate an absolute time in the future
				const unsigned billion = (1000 * 1000 * 1000);
				uint64_t nsecs = ts.tv_nsec + (timeout * 1000 * 1000);
				ts.tv_sec += nsecs / billion;
				nsecs -= (nsecs / billion) * billion;
				ts.tv_nsec = nsecs;

				ret = px4_sem_timedwait(&sem, &ts);

				if (ret && errno != ETIMEDOUT) {
					PX4_WARN("%s: px4_poll() sem error: %s", thread_name, strerror(errno));
				}

			} else if (timeout < 0) {
				px4_sem_wait(&sem);
			}

			// We have waited now (or not, depending on timeout),
			// go through all fds and count how many have data
			for (i = 0; i < nfds; ++i) {

				cdev::CDev *dev = get_vdev(fds[i].fd);

				// If fd is valid
				if (dev) {
					PX4_DEBUG("%s: px4_poll: CDev->poll(teardown) %d", thread_name, fds[i].fd);
					ret = dev->poll(&filemap[fds[i].fd], &fds[i], false);

					if (ret < 0) {
						PX4_WARN("%s: px4_poll() 2nd poll fail", thread_name);
						break;
					}

					if (fds[i].revents) {
						count += 1;
					}
				}
			}
		}

		px4_sem_destroy(&sem);

		// Return the positive count if present,
		// return the negative error number if failed
		return (count) ? count : ret;
	}

	int px4_fsync(int fd)
	{
		return 0;
	}

	int px4_access(const char *pathname, int mode)
	{
		if (mode != F_OK) {
			errno = EINVAL;
			return -1;
		}

		cdev::CDev *dev = getDev(pathname);
		return (dev != nullptr) ? 0 : -1;
	}

	void px4_show_devices()
	{
		int i = 0;
		PX4_INFO("PX4 Devices:");

		pthread_mutex_lock(&devmutex);

		for (const auto &dev : devmap) {
			if (strncmp(dev.first.c_str(), "/dev/", 5) == 0) {
				PX4_INFO("   %s", dev.first.c_str());
			}
		}

		pthread_mutex_unlock(&devmutex);

		PX4_INFO("DF Devices:");
		const char *dev_path;
		unsigned int index = 0;
		i = 0;

		do {
			// Each look increments index and returns -1 if end reached
			i = DevMgr::getNextDeviceName(index, &dev_path);

			if (i == 0) {
				PX4_INFO("   %s", dev_path);
			}
		} while (i == 0);
	}

	void px4_show_topics()
	{
		PX4_INFO("Devices:");

		pthread_mutex_lock(&devmutex);

		for (const auto &dev : devmap) {
			if (strncmp(dev.first.c_str(), "/obj/", 5) == 0) {
				PX4_INFO("   %s", dev.first.c_str());
			}
		}

		pthread_mutex_unlock(&devmutex);
	}

	void px4_show_files()
	{
		PX4_INFO("Files:");

		pthread_mutex_lock(&devmutex);

		for (const auto &dev : devmap) {
			if (strncmp(dev.first.c_str(), "/obj/", 5) != 0 &&
			    strncmp(dev.first.c_str(), "/dev/", 5) != 0) {
				PX4_INFO("   %s", dev.first.c_str());
			}
		}

		pthread_mutex_unlock(&devmutex);
	}

} // extern "C"
<|endoftext|>


<commit_before>#include "scanner/engine/python_kernel.h"
#include "scanner/util/tinyformat.h"
#include "scanner/util/util.h"

#include <pybind11/eval.h>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

namespace scanner {

namespace py = pybind11;
using namespace pybind11::literals;

void gen_random(char* s, const int len) {
  static const char alphanum[] =
      "0123456789"
      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      "abcdefghijklmnopqrstuvwxyz";

  for (int i = 0; i < len; ++i) {
    s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
  }

  s[len] = 0;
}

PythonKernel::PythonKernel(const KernelConfig &config,
                           const std::string &op_name,
                           const std::string &kernel_code,
                           const std::string &pickled_config,
                           const bool can_batch,
                           const bool can_stencil)
    : StenciledBatchedKernel(config), config_(config),
      device_(config.devices[0]), op_name_(op_name) {
  py::gil_scoped_acquire acquire;
  can_batch_ = can_batch;
  can_stencil_ = can_stencil;

  // Remove uuid from op_name
  op_name_ = op_name_.substr(0, op_name_.find(":"));

  char rand[256];
  gen_random(rand, 256);
  kernel_name_ = tfm::format("%s_kernel_%s", op_name_, rand);
  std::string kernel_ns_name_ = tfm::format("ns_%s", op_name_);

  try {
    py::module main = py::module::import("__main__");
    py::object scope = main.attr("__dict__");
    // FIXME((apoms): for some reason, serializing the config object in release
    // mode on MacOS fails....
    main.attr(kernel_ns_name_.c_str()) = py::dict(
        "kernel_code"_a=py::bytes(kernel_code),
        "user_config_str"_a=py::bytes(pickled_config),
        "config"_a=config);

    std::string pycode = tfm::format(R"(
def %s_fn():
  import pickle
  import cloudpickle
  import traceback
  from scannerpy import Config, DeviceType, DeviceHandle, KernelConfig
  from scannerpy.protobuf_generator import ProtobufGenerator

  n = %s
  user_config = pickle.loads(n['user_config_str'])
  protobufs = ProtobufGenerator(user_config)
  kernel_config = KernelConfig(n['config'])
  kernel_config.protobufs = protobufs
  return cloudpickle.loads(n['kernel_code'])(kernel_config)
%s = %s_fn()
)", kernel_name_, kernel_ns_name_, kernel_name_, kernel_name_);
    py::exec(pycode, scope);
  } catch (py::error_already_set &e) {
    LOG(FATAL) << e.what();
  }
}

PythonKernel::~PythonKernel() {
  py::gil_scoped_acquire acquire;
  try {
    py::module::import("__main__").attr(kernel_name_.c_str()).attr("close")();
  } catch (py::error_already_set &e) {
    LOG(FATAL) << e.what();
  }
}

void PythonKernel::reset() {
  py::gil_scoped_acquire acquire;
  try {
    py::module::import("__main__").attr(kernel_name_.c_str()).attr("reset")();
  } catch (py::error_already_set &e) {
    LOG(FATAL) << e.what();
  }
}

void PythonKernel::new_stream(const std::vector<u8> &args) {
  py::gil_scoped_acquire acquire;

  try {
    py::module main = py::module::import("__main__");
    py::object kernel = main.attr(kernel_name_.c_str());
    main.attr("args_str") =
        py::bytes(reinterpret_cast<const char *>(args.data()), args.size());
    std::string pycode = tfm::format(R"(
import pickle
if len(args_str) == 0:
  args = None
else:
  args = pickle.loads(args_str)
%s.new_stream(args))",
                                     kernel_name_);
    py::exec(pycode.c_str(), main.attr("__dict__"));
  } catch (py::error_already_set &e) {
    LOG(FATAL) << e.what();
  }
}

void PythonKernel::execute(const StenciledBatchedElements &input_columns,
                           BatchedElements &output_columns) {
  i32 input_count = (i32)input_columns[0].size();
  py::gil_scoped_acquire acquire;

  try {
    py::object kernel =
        py::module::import("__main__").attr(kernel_name_.c_str());

    std::vector<std::vector<std::vector<py::object>>> batched_cols;
    for (i32 j = 0; j < input_columns.size(); ++j) {
      batched_cols.emplace_back();
    }

    for (i32 j = 0; j < input_columns.size(); ++j) {
      std::vector<std::vector<py::object>> &batched_col = batched_cols[j];
      if (config_.input_column_types[j] == proto::ColumnType::Video) {
        for (i32 i = 0; i < input_count; ++i) {
          batched_col.emplace_back();
          std::vector<py::object> &col = batched_col.back();
          for (i32 k = 0; k < input_columns[j][i].size(); ++k) {
            const Frame *frame = input_columns[j][i][k].as_const_frame();
            std::string dtype;
            u32 dtype_size;
            if (frame->type == FrameType::U8) {
              dtype = py::format_descriptor<u8>::format();
              dtype_size = 1;
            } else if (frame->type == FrameType::F32) {
              dtype = py::format_descriptor<f32>::format();
              dtype_size = 4;
            } else if (frame->type == FrameType::F64) {
              dtype = py::format_descriptor<f64>::format();
              dtype_size = 8;
            }

            py::buffer_info buffer;
            if (frame->channels() == 3) {
              buffer = py::buffer_info(
                                     frame->data, (size_t)dtype_size, dtype, 3,
                                     {(long int)frame->height(), (long int)frame->width(),
                                         (long int)frame->channels()},
                                     {(long int)frame->width() * frame->channels() * dtype_size,
                                         (long int)frame->channels() * dtype_size,
                                         (long int)dtype_size});
            } else {
              buffer = py::buffer_info(
                                     frame->data, (size_t)dtype_size, dtype, 1,
                                     {(long int)frame->height()},
                                     {(long int)dtype_size});
            }

            if (frame->type == FrameType::U8) {
              col.push_back(py::object(py::array_t<u8>(buffer)));
            } else if (frame->type == FrameType::F32) {
              col.push_back(py::object(py::array_t<f32>(buffer)));
            } else if (frame->type == FrameType::F64) {
              col.push_back(py::object(py::array_t<f64>(buffer)));
            }
          }
        }
      } else {
        for (i32 i = 0; i < input_count; ++i) {
          batched_col.emplace_back();
          std::vector<py::object> &col = batched_col.back();
          for (i32 k = 0; k < input_columns[j][i].size(); ++k) {
            std::string s((char const *)input_columns[j][i][k].buffer,
                          input_columns[j][i][k].size);
            col.push_back(py::object(py::bytes(s)));
          }
        }
      }
    }

    std::vector<std::vector<py::object>> batched_out_cols;

    if (can_batch_ && can_stencil_) {
      py::tuple out_cols = kernel.attr("execute")(batched_cols)
          .cast<py::tuple>();
      for (i32 j = 0; j < out_cols.size(); ++j) {
        batched_out_cols.push_back(out_cols[j].cast<std::vector<py::object>>());
      }
    } else if (can_stencil_) {
      // Remove the batch dimension since we are only stenciling
      std::vector<std::vector<py::object>> in_row(batched_cols.size());
      for (size_t i = 0; i < batched_cols.size(); ++i) {
        in_row[i] = batched_cols[i][0];
      }
      py::tuple out_row = kernel.attr("execute")(in_row)
          .cast<py::tuple>();
      for (i32 j = 0; j < output_columns.size(); ++j) {
        batched_out_cols.emplace_back();
      }
      for (i32 j = 0; j < out_row.size(); ++j) {
        batched_out_cols[j].push_back(out_row[j]);
      }

    } else if (can_batch_) {
      // Remove the stencil dimension since we are only batching
      std::vector<std::vector<py::object>> in_row(batched_cols.size());
      for (size_t i = 0; i < batched_cols.size(); ++i) {
        for (size_t j = 0; j < batched_cols[i].size(); ++j) {
          in_row[i].push_back(batched_cols[i][j][0]);
        }
      }
      py::tuple out_cols = kernel.attr("execute")(in_row)
          .cast<py::tuple>();
      for (i32 j = 0; j < out_cols.size(); ++j) {
        batched_out_cols.push_back(out_cols[j].cast<std::vector<py::object>>());
      }
    } else {
      // Remove both the stencil and batch dimension
      std::vector<py::object> in_row;
      for (i32 j = 0; j < batched_cols.size(); ++j) {
        in_row.push_back(batched_cols[j][0][0]);
      }
      py::tuple out_row =
          kernel.attr("execute")(in_row).cast<py::tuple>();

      for (i32 j = 0; j < output_columns.size(); ++j) {
        batched_out_cols.emplace_back();
      }
      for (i32 j = 0; j < out_row.size(); ++j) {
        batched_out_cols[j].push_back(out_row[j]);
      }
    }
    LOG_IF(FATAL, batched_out_cols.size() != output_columns.size())
        << "Incorrect number of output columns. Expected "
        << output_columns.size() << ", got " << batched_out_cols.size();

    for (i32 j = 0; j < output_columns.size(); ++j) {
      // push all rows to that column
      LOG_IF(FATAL, batched_out_cols[j].size() != input_count)
          << "Incorrect number of output rows at column " << j << ". Expected "
          << input_count << ", got " << batched_out_cols[j].size();
      if (config_.output_column_types[j] == proto::ColumnType::Video) {
        for (i32 i = 0; i < input_count; ++i) {
          py::array frame_np = batched_out_cols[j][i].cast<py::array>();
          FrameType frame_type;
          if (frame_np.dtype().is(py::dtype("uint8"))) {
            frame_type = FrameType::U8;
          } else if (frame_np.dtype().is(py::dtype("float32"))) {
            frame_type = FrameType::F32;
          } else if (frame_np.dtype().is(py::dtype("float64"))) {
            frame_type = FrameType::F64;
          } else {
            LOG(FATAL) << "Invalid numpy dtype: " << frame_np.dtype();
          }

          i32 ndim = frame_np.ndim();
          if (ndim > 3) {
            LOG(FATAL) << "Invalid number of dimensions (must be less than 4): "
                       << ndim;
          }
          std::vector<i32> shapes;
          std::vector<i32> strides;
          for (int n = 0; n < ndim; ++n) {
            shapes.push_back(frame_np.shape(n));
            strides.push_back(frame_np.strides(n));
            if (frame_np.strides(n) < 0) {
              LOG(FATAL) << "We do not yet support negative strides in PythonKernel!";
            }
          }
          FrameInfo frame_info(shapes, frame_type);
          Frame *frame = new_frame(CPU_DEVICE, frame_info);
          const char *frame_data = (const char *)frame_np.data();

          if (ndim == 3) {
            assert(strides[1] % strides[2] == 0);
            for (int i = 0; i < shapes[0]; ++i) {
              u64 offset = strides[0] * i;
              memcpy(frame->data + offset, frame_data + offset,
                     shapes[2] * shapes[1] * strides[2]);
            }
          } else {
            LOG(FATAL) << "Can not support ndim != 3.";
          }
          insert_frame(output_columns[j], frame);
        }
      } else {
        std::vector<std::string> outputs;
        size_t total_size = 0;
        for (i32 i = 0; i < input_count; ++i) {
          std::string field = batched_out_cols[j][i].cast<std::string>();
          outputs.push_back(field);
          total_size += field.size();
        }

        u8 *output_block =
            new_block_buffer(CPU_DEVICE, total_size, input_count);
        for (i32 i = 0; i < input_count; ++i) {
          u8 *buf = output_block;
          memcpy_buffer(buf, CPU_DEVICE, (u8 *)outputs[i].data(), CPU_DEVICE,
                        outputs[i].size());
          insert_element(output_columns[j], buf, outputs[i].size());
          output_block += outputs[i].size();
        }
      }
    }

  } catch (py::error_already_set &e) {
    LOG(FATAL) << e.what();
  }
}
}
<commit_msg>Fix python kernel not using proper offset for output frames<commit_after>#include "scanner/engine/python_kernel.h"
#include "scanner/util/tinyformat.h"
#include "scanner/util/util.h"

#include <pybind11/eval.h>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

namespace scanner {

namespace py = pybind11;
using namespace pybind11::literals;

void gen_random(char* s, const int len) {
  static const char alphanum[] =
      "0123456789"
      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      "abcdefghijklmnopqrstuvwxyz";

  for (int i = 0; i < len; ++i) {
    s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
  }

  s[len] = 0;
}

PythonKernel::PythonKernel(const KernelConfig &config,
                           const std::string &op_name,
                           const std::string &kernel_code,
                           const std::string &pickled_config,
                           const bool can_batch,
                           const bool can_stencil)
    : StenciledBatchedKernel(config), config_(config),
      device_(config.devices[0]), op_name_(op_name) {
  py::gil_scoped_acquire acquire;
  can_batch_ = can_batch;
  can_stencil_ = can_stencil;

  // Remove uuid from op_name
  op_name_ = op_name_.substr(0, op_name_.find(":"));

  char rand[256];
  gen_random(rand, 256);
  kernel_name_ = tfm::format("%s_kernel_%s", op_name_, rand);
  std::string kernel_ns_name_ = tfm::format("ns_%s", op_name_);

  try {
    py::module main = py::module::import("__main__");
    py::object scope = main.attr("__dict__");
    // FIXME((apoms): for some reason, serializing the config object in release
    // mode on MacOS fails....
    main.attr(kernel_ns_name_.c_str()) = py::dict(
        "kernel_code"_a=py::bytes(kernel_code),
        "user_config_str"_a=py::bytes(pickled_config),
        "config"_a=config);

    std::string pycode = tfm::format(R"(
def %s_fn():
  import pickle
  import cloudpickle
  import traceback
  from scannerpy import Config, DeviceType, DeviceHandle, KernelConfig
  from scannerpy.protobuf_generator import ProtobufGenerator

  n = %s
  user_config = pickle.loads(n['user_config_str'])
  protobufs = ProtobufGenerator(user_config)
  kernel_config = KernelConfig(n['config'])
  kernel_config.protobufs = protobufs
  return cloudpickle.loads(n['kernel_code'])(kernel_config)
%s = %s_fn()
)", kernel_name_, kernel_ns_name_, kernel_name_, kernel_name_);
    py::exec(pycode, scope);
  } catch (py::error_already_set &e) {
    LOG(FATAL) << e.what();
  }
}

PythonKernel::~PythonKernel() {
  py::gil_scoped_acquire acquire;
  try {
    py::module::import("__main__").attr(kernel_name_.c_str()).attr("close")();
  } catch (py::error_already_set &e) {
    LOG(FATAL) << e.what();
  }
}

void PythonKernel::reset() {
  py::gil_scoped_acquire acquire;
  try {
    py::module::import("__main__").attr(kernel_name_.c_str()).attr("reset")();
  } catch (py::error_already_set &e) {
    LOG(FATAL) << e.what();
  }
}

void PythonKernel::new_stream(const std::vector<u8> &args) {
  py::gil_scoped_acquire acquire;

  try {
    py::module main = py::module::import("__main__");
    py::object kernel = main.attr(kernel_name_.c_str());
    main.attr("args_str") =
        py::bytes(reinterpret_cast<const char *>(args.data()), args.size());
    std::string pycode = tfm::format(R"(
import pickle
if len(args_str) == 0:
  args = None
else:
  args = pickle.loads(args_str)
%s.new_stream(args))",
                                     kernel_name_);
    py::exec(pycode.c_str(), main.attr("__dict__"));
  } catch (py::error_already_set &e) {
    LOG(FATAL) << e.what();
  }
}

void PythonKernel::execute(const StenciledBatchedElements &input_columns,
                           BatchedElements &output_columns) {
  i32 input_count = (i32)input_columns[0].size();
  py::gil_scoped_acquire acquire;

  try {
    py::object kernel =
        py::module::import("__main__").attr(kernel_name_.c_str());

    std::vector<std::vector<std::vector<py::object>>> batched_cols;
    for (i32 j = 0; j < input_columns.size(); ++j) {
      batched_cols.emplace_back();
    }

    for (i32 j = 0; j < input_columns.size(); ++j) {
      std::vector<std::vector<py::object>> &batched_col = batched_cols[j];
      if (config_.input_column_types[j] == proto::ColumnType::Video) {
        for (i32 i = 0; i < input_count; ++i) {
          batched_col.emplace_back();
          std::vector<py::object> &col = batched_col.back();
          for (i32 k = 0; k < input_columns[j][i].size(); ++k) {
            const Frame *frame = input_columns[j][i][k].as_const_frame();
            std::string dtype;
            u32 dtype_size;
            if (frame->type == FrameType::U8) {
              dtype = py::format_descriptor<u8>::format();
              dtype_size = 1;
            } else if (frame->type == FrameType::F32) {
              dtype = py::format_descriptor<f32>::format();
              dtype_size = 4;
            } else if (frame->type == FrameType::F64) {
              dtype = py::format_descriptor<f64>::format();
              dtype_size = 8;
            }

            py::buffer_info buffer;
            if (frame->channels() == 3) {
              buffer = py::buffer_info(
                                     frame->data, (size_t)dtype_size, dtype, 3,
                                     {(long int)frame->height(), (long int)frame->width(),
                                         (long int)frame->channels()},
                                     {(long int)frame->width() * frame->channels() * dtype_size,
                                         (long int)frame->channels() * dtype_size,
                                         (long int)dtype_size});
            } else {
              buffer = py::buffer_info(
                                     frame->data, (size_t)dtype_size, dtype, 1,
                                     {(long int)frame->height()},
                                     {(long int)dtype_size});
            }

            if (frame->type == FrameType::U8) {
              col.push_back(py::object(py::array_t<u8>(buffer)));
            } else if (frame->type == FrameType::F32) {
              col.push_back(py::object(py::array_t<f32>(buffer)));
            } else if (frame->type == FrameType::F64) {
              col.push_back(py::object(py::array_t<f64>(buffer)));
            }
          }
        }
      } else {
        for (i32 i = 0; i < input_count; ++i) {
          batched_col.emplace_back();
          std::vector<py::object> &col = batched_col.back();
          for (i32 k = 0; k < input_columns[j][i].size(); ++k) {
            std::string s((char const *)input_columns[j][i][k].buffer,
                          input_columns[j][i][k].size);
            col.push_back(py::object(py::bytes(s)));
          }
        }
      }
    }

    std::vector<std::vector<py::object>> batched_out_cols;

    if (can_batch_ && can_stencil_) {
      py::tuple out_cols = kernel.attr("execute")(batched_cols)
          .cast<py::tuple>();
      for (i32 j = 0; j < out_cols.size(); ++j) {
        batched_out_cols.push_back(out_cols[j].cast<std::vector<py::object>>());
      }
    } else if (can_stencil_) {
      // Remove the batch dimension since we are only stenciling
      std::vector<std::vector<py::object>> in_row(batched_cols.size());
      for (size_t i = 0; i < batched_cols.size(); ++i) {
        in_row[i] = batched_cols[i][0];
      }
      py::tuple out_row = kernel.attr("execute")(in_row)
          .cast<py::tuple>();
      for (i32 j = 0; j < output_columns.size(); ++j) {
        batched_out_cols.emplace_back();
      }
      for (i32 j = 0; j < out_row.size(); ++j) {
        batched_out_cols[j].push_back(out_row[j]);
      }

    } else if (can_batch_) {
      // Remove the stencil dimension since we are only batching
      std::vector<std::vector<py::object>> in_row(batched_cols.size());
      for (size_t i = 0; i < batched_cols.size(); ++i) {
        for (size_t j = 0; j < batched_cols[i].size(); ++j) {
          in_row[i].push_back(batched_cols[i][j][0]);
        }
      }
      py::tuple out_cols = kernel.attr("execute")(in_row)
          .cast<py::tuple>();
      for (i32 j = 0; j < out_cols.size(); ++j) {
        batched_out_cols.push_back(out_cols[j].cast<std::vector<py::object>>());
      }
    } else {
      // Remove both the stencil and batch dimension
      std::vector<py::object> in_row;
      for (i32 j = 0; j < batched_cols.size(); ++j) {
        in_row.push_back(batched_cols[j][0][0]);
      }
      py::tuple out_row =
          kernel.attr("execute")(in_row).cast<py::tuple>();

      for (i32 j = 0; j < output_columns.size(); ++j) {
        batched_out_cols.emplace_back();
      }
      for (i32 j = 0; j < out_row.size(); ++j) {
        batched_out_cols[j].push_back(out_row[j]);
      }
    }
    LOG_IF(FATAL, batched_out_cols.size() != output_columns.size())
        << "Incorrect number of output columns. Expected "
        << output_columns.size() << ", got " << batched_out_cols.size();

    for (i32 j = 0; j < output_columns.size(); ++j) {
      // push all rows to that column
      LOG_IF(FATAL, batched_out_cols[j].size() != input_count)
          << "Incorrect number of output rows at column " << j << ". Expected "
          << input_count << ", got " << batched_out_cols[j].size();
      if (config_.output_column_types[j] == proto::ColumnType::Video) {
        for (i32 i = 0; i < input_count; ++i) {
          py::array frame_np = batched_out_cols[j][i].cast<py::array>();
          FrameType frame_type;
          if (frame_np.dtype().is(py::dtype("uint8"))) {
            frame_type = FrameType::U8;
          } else if (frame_np.dtype().is(py::dtype("float32"))) {
            frame_type = FrameType::F32;
          } else if (frame_np.dtype().is(py::dtype("float64"))) {
            frame_type = FrameType::F64;
          } else {
            LOG(FATAL) << "Invalid numpy dtype: " << frame_np.dtype();
          }

          i32 ndim = frame_np.ndim();
          if (ndim > 3) {
            LOG(FATAL) << "Invalid number of dimensions (must be less than 4): "
                       << ndim;
          }
          std::vector<i32> shapes;
          std::vector<i32> strides;
          for (int n = 0; n < ndim; ++n) {
            shapes.push_back(frame_np.shape(n));
            strides.push_back(frame_np.strides(n));
            if (frame_np.strides(n) < 0) {
              LOG(FATAL) << "We do not yet support negative strides in PythonKernel!";
            }
          }
          FrameInfo frame_info(shapes, frame_type);
          Frame *frame = new_frame(CPU_DEVICE, frame_info);
          const char *frame_data = (const char *)frame_np.data();

          if (ndim == 3) {
            assert(strides[1] % strides[2] == 0);
            u64 dest_offset = 0;
            for (int i = 0; i < shapes[0]; ++i) {
              u64 source_offset = strides[0] * i;
              memcpy(frame->data + dest_offset, frame_data + source_offset,
                     shapes[2] * shapes[1] * strides[2]);
              dest_offset += shapes[2] * shapes[1] * strides[2];
            }
          } else {
            LOG(FATAL) << "Can not support ndim != 3.";
          }
          insert_frame(output_columns[j], frame);
        }
      } else {
        std::vector<std::string> outputs;
        size_t total_size = 0;
        for (i32 i = 0; i < input_count; ++i) {
          std::string field = batched_out_cols[j][i].cast<std::string>();
          outputs.push_back(field);
          total_size += field.size();
        }

        u8 *output_block =
            new_block_buffer(CPU_DEVICE, total_size, input_count);
        for (i32 i = 0; i < input_count; ++i) {
          u8 *buf = output_block;
          memcpy_buffer(buf, CPU_DEVICE, (u8 *)outputs[i].data(), CPU_DEVICE,
                        outputs[i].size());
          insert_element(output_columns[j], buf, outputs[i].size());
          output_block += outputs[i].size();
        }
      }
    }

  } catch (py::error_already_set &e) {
    LOG(FATAL) << e.what();
  }
}
}
<|endoftext|>


<commit_before>// @(#)root/graf:$Name:  $:$Id: TArrow.cxx,v 1.11 2004/02/07 08:54:03 brun Exp $
// Author: Rene Brun   17/10/95

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "Riostream.h"
#include "TROOT.h"
#include "TArrow.h"
#include "TVirtualPad.h"

ClassImp(TArrow)

//______________________________________________________________________________
//  Different arrow formats as explained in TArrow::TArrow or in
//  the picture below are provided.
//  Once an arrow is drawn on the screen:
//    - One can click on one of the edges and move this edge.
//    - One can click on any other arrow part to move the entire arrow.
//Begin_Html
/*

*/
//End_Html
//

//______________________________________________________________________________
TArrow::TArrow(): TLine(),TAttFill()
{
//*-*-*-*-*-*-*-*-*-*-*arrow default constructor*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                  ========================

   fAngle = 60;
}

//______________________________________________________________________________
TArrow::TArrow(Double_t x1, Double_t y1,Double_t x2, Double_t  y2,
             Float_t arrowsize ,Option_t *option)
       :TLine(x1,y1,x2,y2), TAttFill(0,1001)
{
//*-*-*-*-*-*-*-*-*-*-*arrow normal constructor*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                  ========================
// Define an arrow between points x1,y1 and x2,y2
// the arrowsize is in percentage of the pad height
// Opening angle between the two sides of the arrow is fAngle (60 degrees)
//  option = ">"      -------->
//  option = "<"      <--------
//  option = "->-"    ---->----
//  option = "-<-"    ----<----
//  option = "-|>-"   ---|>----
//  option = "<>"     <------->
//  option = "<|>"    <|-----|>  arrow defined by a triangle
//                   If FillColor == 0 draw open triangle
//                   else  draw full triangle with fillcolor
//

   fAngle       = 60;
   fArrowSize   = arrowsize;
   fOption      = option;
   SetLineColor(1);
   SetLineStyle(1);
}

//______________________________________________________________________________
TArrow::~TArrow()
{
//*-*-*-*-*-*-*-*-*-*-*arrow default destructor*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                  ========================
}

//______________________________________________________________________________
TArrow::TArrow(const TArrow &arrow) : TLine(arrow), TAttFill(arrow)
{
   ((TArrow&)arrow).Copy(*this);
}

//______________________________________________________________________________
void TArrow::Copy(TObject &obj) const
{
//*-*-*-*-*-*-*-*-*-*-*Copy this arrow to arrow*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                  ========================

   TLine::Copy(obj);
   TAttFill::Copy(((TArrow&)obj));
   ((TArrow&)obj).fAngle      = fAngle;
   ((TArrow&)obj).fArrowSize  = fArrowSize;
   ((TArrow&)obj).fOption     = fOption;
}

//______________________________________________________________________________
void TArrow::Draw(Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*Draw this arrow with its current attributes*-*-*-*-*-*-*
//*-*                  ===========================================

   Option_t *opt;
   if (strlen(option)) opt = option;
   else                opt = (char*)GetOption();

   AppendPad(opt);

}

//______________________________________________________________________________
void TArrow::DrawArrow(Double_t x1, Double_t y1,Double_t x2, Double_t  y2,
                     Float_t arrowsize ,Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*Draw this arrow with new coordinates*-*-*-*-*-*-*-*-*-*
//*-*                  ====================================
// if arrowsize is <= 0, arrowsize will be the current arrow size
// if option="", option will be the current arrow option

   Float_t size = arrowsize;
   if (size <= 0) size = fArrowSize;
   if (size <= 0) size = 0.05;
   const char* opt = option;
   if (!opt || strlen(opt) == 0) opt = fOption.Data();
   if (!opt || strlen(opt) == 0) opt = "|>";
   TArrow *newarrow = new TArrow(x1,y1,x2,y2,size,opt);
   newarrow->SetAngle(fAngle);
   TAttLine::Copy(*newarrow);
   TAttFill::Copy(*newarrow);
   newarrow->AppendPad(opt);
}

//______________________________________________________________________________
void TArrow::Paint(Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*Paint this arrow with its current attributes*-*-*-*-*-*-*
//*-*                  ============================================

   PaintArrow(gPad->XtoPad(fX1),gPad->YtoPad(fY1),gPad->XtoPad(fX2),gPad->YtoPad(fY2), fArrowSize, option);
}

//______________________________________________________________________________
void TArrow::PaintArrow(Double_t x1, Double_t y1, Double_t x2, Double_t y2,
                        Float_t arrowsize, Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*Draw this arrow with new coordinates*-*-*-*-*-*-*-*-*-*
//*-*                  ====================================
//
//                                               (P2)
//                                                -
//                                                .  -
//                                                .     -
//                                                .        -
//     -------------------------------------------.(P0)------- (P1)
//                                                .        -
//                                                .     -
//                                                .  -
//                                                -
//                                               (P3)
//
//

   TString opt = option;
   opt.ToLower();
   TAttLine::Modify();
   TAttFill::Modify();

//*-*- take u1,v1 as origin. take arrow line as new x axis
   Int_t px1    = gPad->XtoAbsPixel(x1);
   Int_t py1    = gPad->YtoAbsPixel(y1);
   Int_t px2    = gPad->XtoAbsPixel(x2);
   Int_t py2    = gPad->YtoAbsPixel(y2);
   Float_t lp   = TMath::Sqrt(Double_t((px2-px1)*(px2-px1) + (py2-py1)*(py2-py1)));
   Float_t rSiz = arrowsize*gPad->GetAbsHNDC()*gPad->GetWh();
   Float_t dSiz = rSiz*TMath::Tan(3.141592*fAngle/360);
   if (lp <= 0) return;
   Double_t ct = (px2-px1)/lp;
   Double_t st = (py1-py2)/lp;
   Int_t P2x,P2y,P3x,P3y,P0x,P0y;

// Otto start:  define default line  before move of origin of arrow
   Double_t XP0;
   Double_t YP0;
   Double_t XP0L;
   Double_t YP0L;
   XP0  = gPad->AbsPixeltoX(px2);
   YP0  = gPad->AbsPixeltoY(py2);
   XP0L = gPad->AbsPixeltoX(px1);
   YP0L = gPad->AbsPixeltoY(py1);
// move origin of arrow
   if (opt.Contains("-") && opt.Contains(">")) {
      px1 = Int_t(0.5 *(px2 + px1) + ct*rSiz/2);
      py1 = Int_t(0.5 *(py2 + py1) - st*rSiz/2);
      px2 = px1;
      py2 = py1;
   }
   if (opt.Contains("-") && opt.Contains("<")) {
      px1 = Int_t(0.5 *(px2 + px1) - ct*rSiz/2);
      py1 = Int_t(0.5 *(py2 + py1) + st*rSiz/2);
      px2 = px1;
      py2 = py1;
   }
// ----------
   if (opt.Contains(">")) {
      P2x = px2 - Int_t(rSiz*ct+st*dSiz-0.5);
      P2y = py2 + Int_t(rSiz*st-ct*dSiz+0.5);
      P3x = px2 - Int_t(rSiz*ct-st*dSiz-0.5);
      P3y = py2 + Int_t(rSiz*st+ct*dSiz+0.5);
      P0x = px2 - Int_t(rSiz*ct-0.5);
      P0y = py2 + Int_t(rSiz*st+0.5);
   } else {
      P2x = px2;
      P2y = py2;
      P3x = px2;
      P3y = py2;
      P0x = px2;
      P0y = py2;
   }
   Int_t P2xL,P2yL,P3xL,P3yL,P0xL,P0yL;

   if (opt.Contains("<")) {
      P2xL = px1 + Int_t(rSiz*ct-st*dSiz+0.5);
      P2yL = py1 - Int_t(rSiz*st+ct*dSiz-0.5);
      P3xL = px1 + Int_t(rSiz*ct+st*dSiz+0.5);
      P3yL = py1 - Int_t(rSiz*st-ct*dSiz-0.5);
      P0xL = px1 + Int_t(rSiz*ct+0.5);
      P0yL = py1 - Int_t(rSiz*st-0.5);
   } else {
      P2xL = px1;
      P2yL = py1;
      P3xL = px1;
      P3yL = py1;
      P0xL = px1;
      P0yL = py1;
   }
   Double_t XP2  = gPad->AbsPixeltoX(P2x);
   Double_t YP2  = gPad->AbsPixeltoY(P2y);
   Double_t XP3  = gPad->AbsPixeltoX(P3x);
   Double_t YP3  = gPad->AbsPixeltoY(P3y);
   Double_t XP2L = gPad->AbsPixeltoX(P2xL);
   Double_t YP2L = gPad->AbsPixeltoY(P2yL);
   Double_t XP3L = gPad->AbsPixeltoX(P3xL);
   Double_t YP3L = gPad->AbsPixeltoY(P3yL);
//   move up
   if (opt.Contains("|") && !opt.Contains("-")) {
      XP0  = gPad->AbsPixeltoX(P0x);
      YP0  = gPad->AbsPixeltoY(P0y);
      XP0L = gPad->AbsPixeltoX(P0xL);
      YP0L = gPad->AbsPixeltoY(P0yL);
   }

   gPad->PaintLine(XP0,YP0,XP0L,YP0L);

//*-*- Convert points to pad reference system
   Double_t xp1[4],yp1[4],xp2[4],yp2[4];

   xp1[0] = XP2;    yp1[0] = YP2;
   xp1[1] = gPad->AbsPixeltoX(px2);     yp1[1] = gPad->AbsPixeltoY(py2);
   xp1[2] = XP3;    yp1[2] = YP3;
   xp1[3] = XP2;    yp1[3] = YP2;

   xp2[0] = XP2L;   yp2[0] = YP2L;
   xp2[1] = gPad->AbsPixeltoX(px1);     yp2[1] = gPad->AbsPixeltoY(py1);
   xp2[2] = XP3L;   yp2[2] = YP3L;
   xp2[3] = XP2L;   yp2[3] = YP2L;
   if (opt.Contains(">")) {
      if (opt.Contains("|>")) {
         if (GetFillColor()) {
            gPad->PaintFillArea(3,xp1,yp1);
            gPad->PaintPolyLine(4,xp1,yp1);
         } else {
            gPad->PaintPolyLine(4,xp1,yp1);
         }
      } else {
         gPad->PaintPolyLine(3,xp1,yp1);
      }
   }
   if (opt.Contains("<")) {
      if (opt.Contains("<|")) {
         if (GetFillColor()) {
            gPad->PaintFillArea(3,xp2,yp2);
            gPad->PaintPolyLine(4,xp2,yp2);
         } else {
            gPad->PaintPolyLine(4,xp2,yp2);
         }
      } else {
         gPad->PaintPolyLine(3,xp2,yp2);
      }
   }

}

//______________________________________________________________________________
void TArrow::SavePrimitive(ofstream &out, Option_t *)
{
    // Save primitive as a C++ statement(s) on output stream out

   char quote = '"';
   if (gROOT->ClassSaved(TArrow::Class())) {
       out<<"   ";
   } else {
       out<<"   TArrow *";
   }
   out<<"arrow = new TArrow("<<fX1<<","<<fY1<<","<<fX2<<","<<fY2
      <<","<<fArrowSize<<","<<quote<<GetDrawOption()<<quote<<");"<<endl;

   SaveFillAttributes(out,"arrow",0,1);
   SaveLineAttributes(out,"arrow",1,1,1);

   if (fAngle !=60) {
       out << "   arrow->SetAngle(" << GetAngle() << ");" << endl;
   }

   out<<"   arrow->Draw();"<<endl;
}
<commit_msg>Fix in TArrow::Paint to take into account the default option set via the constructor.<commit_after>// @(#)root/graf:$Name:  $:$Id: TArrow.cxx,v 1.12 2004/11/15 10:26:27 brun Exp $
// Author: Rene Brun   17/10/95

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "Riostream.h"
#include "TROOT.h"
#include "TArrow.h"
#include "TVirtualPad.h"

ClassImp(TArrow)

//______________________________________________________________________________
//  Different arrow formats as explained in TArrow::TArrow or in
//  the picture below are provided.
//  Once an arrow is drawn on the screen:
//    - One can click on one of the edges and move this edge.
//    - One can click on any other arrow part to move the entire arrow.
//Begin_Html
/*

*/
//End_Html
//

//______________________________________________________________________________
TArrow::TArrow(): TLine(),TAttFill()
{
//*-*-*-*-*-*-*-*-*-*-*arrow default constructor*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                  ========================

   fAngle = 60;
}

//______________________________________________________________________________
TArrow::TArrow(Double_t x1, Double_t y1,Double_t x2, Double_t  y2,
             Float_t arrowsize ,Option_t *option)
       :TLine(x1,y1,x2,y2), TAttFill(0,1001)
{
//*-*-*-*-*-*-*-*-*-*-*arrow normal constructor*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                  ========================
// Define an arrow between points x1,y1 and x2,y2
// the arrowsize is in percentage of the pad height
// Opening angle between the two sides of the arrow is fAngle (60 degrees)
//  option = ">"      -------->
//  option = "<"      <--------
//  option = "->-"    ---->----
//  option = "-<-"    ----<----
//  option = "-|>-"   ---|>----
//  option = "<>"     <------->
//  option = "<|>"    <|-----|>  arrow defined by a triangle
//                   If FillColor == 0 draw open triangle
//                   else  draw full triangle with fillcolor
//

   fAngle       = 60;
   fArrowSize   = arrowsize;
   fOption      = option;
   SetLineColor(1);
   SetLineStyle(1);
}

//______________________________________________________________________________
TArrow::~TArrow()
{
//*-*-*-*-*-*-*-*-*-*-*arrow default destructor*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                  ========================
}

//______________________________________________________________________________
TArrow::TArrow(const TArrow &arrow) : TLine(arrow), TAttFill(arrow)
{
   ((TArrow&)arrow).Copy(*this);
}

//______________________________________________________________________________
void TArrow::Copy(TObject &obj) const
{
//*-*-*-*-*-*-*-*-*-*-*Copy this arrow to arrow*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                  ========================

   TLine::Copy(obj);
   TAttFill::Copy(((TArrow&)obj));
   ((TArrow&)obj).fAngle      = fAngle;
   ((TArrow&)obj).fArrowSize  = fArrowSize;
   ((TArrow&)obj).fOption     = fOption;
}

//______________________________________________________________________________
void TArrow::Draw(Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*Draw this arrow with its current attributes*-*-*-*-*-*-*
//*-*                  ===========================================

   Option_t *opt;
   if (option && strlen(option)) opt = option;
   else                          opt = (char*)GetOption();

   AppendPad(opt);

}

//______________________________________________________________________________
void TArrow::DrawArrow(Double_t x1, Double_t y1,Double_t x2, Double_t  y2,
                     Float_t arrowsize ,Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*Draw this arrow with new coordinates*-*-*-*-*-*-*-*-*-*
//*-*                  ====================================
// if arrowsize is <= 0, arrowsize will be the current arrow size
// if option="", option will be the current arrow option

   Float_t size = arrowsize;
   if (size <= 0) size = fArrowSize;
   if (size <= 0) size = 0.05;
   const char* opt = option;
   if (!opt || strlen(opt) == 0) opt = fOption.Data();
   if (!opt || strlen(opt) == 0) opt = "|>";
   TArrow *newarrow = new TArrow(x1,y1,x2,y2,size,opt);
   newarrow->SetAngle(fAngle);
   TAttLine::Copy(*newarrow);
   TAttFill::Copy(*newarrow);
   newarrow->AppendPad(opt);
}

//______________________________________________________________________________
void TArrow::Paint(Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*Paint this arrow with its current attributes*-*-*-*-*-*-*
//*-*                  ============================================

   Option_t *opt;
   if (option && strlen(option)) opt = option;
   else                          opt = (char*)GetOption();
   PaintArrow(gPad->XtoPad(fX1),gPad->YtoPad(fY1),gPad->XtoPad(fX2),gPad->YtoPad(fY2), fArrowSize, opt);
}

//______________________________________________________________________________
void TArrow::PaintArrow(Double_t x1, Double_t y1, Double_t x2, Double_t y2,
                        Float_t arrowsize, Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*Draw this arrow with new coordinates*-*-*-*-*-*-*-*-*-*
//*-*                  ====================================
//
//                                               (P2)
//                                                -
//                                                .  -
//                                                .     -
//                                                .        -
//     -------------------------------------------.(P0)------- (P1)
//                                                .        -
//                                                .     -
//                                                .  -
//                                                -
//                                               (P3)
//
//

   TString opt = option;
   opt.ToLower();
   TAttLine::Modify();
   TAttFill::Modify();

//*-*- take u1,v1 as origin. take arrow line as new x axis
   Int_t px1    = gPad->XtoAbsPixel(x1);
   Int_t py1    = gPad->YtoAbsPixel(y1);
   Int_t px2    = gPad->XtoAbsPixel(x2);
   Int_t py2    = gPad->YtoAbsPixel(y2);
   Float_t lp   = TMath::Sqrt(Double_t((px2-px1)*(px2-px1) + (py2-py1)*(py2-py1)));
   Float_t rSiz = arrowsize*gPad->GetAbsHNDC()*gPad->GetWh();
   Float_t dSiz = rSiz*TMath::Tan(3.141592*fAngle/360);
   if (lp <= 0) return;
   Double_t ct = (px2-px1)/lp;
   Double_t st = (py1-py2)/lp;
   Int_t P2x,P2y,P3x,P3y,P0x,P0y;

// Otto start:  define default line  before move of origin of arrow
   Double_t XP0;
   Double_t YP0;
   Double_t XP0L;
   Double_t YP0L;
   XP0  = gPad->AbsPixeltoX(px2);
   YP0  = gPad->AbsPixeltoY(py2);
   XP0L = gPad->AbsPixeltoX(px1);
   YP0L = gPad->AbsPixeltoY(py1);
// move origin of arrow
   if (opt.Contains("-") && opt.Contains(">")) {
      px1 = Int_t(0.5 *(px2 + px1) + ct*rSiz/2);
      py1 = Int_t(0.5 *(py2 + py1) - st*rSiz/2);
      px2 = px1;
      py2 = py1;
   }
   if (opt.Contains("-") && opt.Contains("<")) {
      px1 = Int_t(0.5 *(px2 + px1) - ct*rSiz/2);
      py1 = Int_t(0.5 *(py2 + py1) + st*rSiz/2);
      px2 = px1;
      py2 = py1;
   }
// ----------
   if (opt.Contains(">")) {
      P2x = px2 - Int_t(rSiz*ct+st*dSiz-0.5);
      P2y = py2 + Int_t(rSiz*st-ct*dSiz+0.5);
      P3x = px2 - Int_t(rSiz*ct-st*dSiz-0.5);
      P3y = py2 + Int_t(rSiz*st+ct*dSiz+0.5);
      P0x = px2 - Int_t(rSiz*ct-0.5);
      P0y = py2 + Int_t(rSiz*st+0.5);
   } else {
      P2x = px2;
      P2y = py2;
      P3x = px2;
      P3y = py2;
      P0x = px2;
      P0y = py2;
   }
   Int_t P2xL,P2yL,P3xL,P3yL,P0xL,P0yL;

   if (opt.Contains("<")) {
      P2xL = px1 + Int_t(rSiz*ct-st*dSiz+0.5);
      P2yL = py1 - Int_t(rSiz*st+ct*dSiz-0.5);
      P3xL = px1 + Int_t(rSiz*ct+st*dSiz+0.5);
      P3yL = py1 - Int_t(rSiz*st-ct*dSiz-0.5);
      P0xL = px1 + Int_t(rSiz*ct+0.5);
      P0yL = py1 - Int_t(rSiz*st-0.5);
   } else {
      P2xL = px1;
      P2yL = py1;
      P3xL = px1;
      P3yL = py1;
      P0xL = px1;
      P0yL = py1;
   }
   Double_t XP2  = gPad->AbsPixeltoX(P2x);
   Double_t YP2  = gPad->AbsPixeltoY(P2y);
   Double_t XP3  = gPad->AbsPixeltoX(P3x);
   Double_t YP3  = gPad->AbsPixeltoY(P3y);
   Double_t XP2L = gPad->AbsPixeltoX(P2xL);
   Double_t YP2L = gPad->AbsPixeltoY(P2yL);
   Double_t XP3L = gPad->AbsPixeltoX(P3xL);
   Double_t YP3L = gPad->AbsPixeltoY(P3yL);
//   move up
   if (opt.Contains("|") && !opt.Contains("-")) {
      XP0  = gPad->AbsPixeltoX(P0x);
      YP0  = gPad->AbsPixeltoY(P0y);
      XP0L = gPad->AbsPixeltoX(P0xL);
      YP0L = gPad->AbsPixeltoY(P0yL);
   }

   gPad->PaintLine(XP0,YP0,XP0L,YP0L);

//*-*- Convert points to pad reference system
   Double_t xp1[4],yp1[4],xp2[4],yp2[4];

   xp1[0] = XP2;    yp1[0] = YP2;
   xp1[1] = gPad->AbsPixeltoX(px2);     yp1[1] = gPad->AbsPixeltoY(py2);
   xp1[2] = XP3;    yp1[2] = YP3;
   xp1[3] = XP2;    yp1[3] = YP2;

   xp2[0] = XP2L;   yp2[0] = YP2L;
   xp2[1] = gPad->AbsPixeltoX(px1);     yp2[1] = gPad->AbsPixeltoY(py1);
   xp2[2] = XP3L;   yp2[2] = YP3L;
   xp2[3] = XP2L;   yp2[3] = YP2L;
   if (opt.Contains(">")) {
      if (opt.Contains("|>")) {
         if (GetFillColor()) {
            gPad->PaintFillArea(3,xp1,yp1);
            gPad->PaintPolyLine(4,xp1,yp1);
         } else {
            gPad->PaintPolyLine(4,xp1,yp1);
         }
      } else {
         gPad->PaintPolyLine(3,xp1,yp1);
      }
   }
   if (opt.Contains("<")) {
      if (opt.Contains("<|")) {
         if (GetFillColor()) {
            gPad->PaintFillArea(3,xp2,yp2);
            gPad->PaintPolyLine(4,xp2,yp2);
         } else {
            gPad->PaintPolyLine(4,xp2,yp2);
         }
      } else {
         gPad->PaintPolyLine(3,xp2,yp2);
      }
   }

}

//______________________________________________________________________________
void TArrow::SavePrimitive(ofstream &out, Option_t *)
{
    // Save primitive as a C++ statement(s) on output stream out

   char quote = '"';
   if (gROOT->ClassSaved(TArrow::Class())) {
       out<<"   ";
   } else {
       out<<"   TArrow *";
   }
   out<<"arrow = new TArrow("<<fX1<<","<<fY1<<","<<fX2<<","<<fY2
      <<","<<fArrowSize<<","<<quote<<GetDrawOption()<<quote<<");"<<endl;

   SaveFillAttributes(out,"arrow",0,1);
   SaveLineAttributes(out,"arrow",1,1,1);

   if (fAngle !=60) {
       out << "   arrow->SetAngle(" << GetAngle() << ");" << endl;
   }

   out<<"   arrow->Draw();"<<endl;
}
<|endoftext|>


<commit_before>/*
* Discrete Logarithm Parameters
* (C) 1999-2008,2015,2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/dl_group.h>
#include <botan/numthry.h>
#include <botan/reducer.h>
#include <botan/der_enc.h>
#include <botan/ber_dec.h>
#include <botan/pem.h>
#include <botan/workfactor.h>
#include <botan/internal/monty_exp.h>
#include <map>

namespace Botan {

class DL_Group_Data final
   {
   public:
      DL_Group_Data(const BigInt& p, const BigInt& q, const BigInt& g) :
         m_p(p), m_q(q), m_g(g),
         m_mod_p(p),
         m_monty(monty_precompute(m_g, m_p, m_mod_p, /*window bits=*/4)),
         m_p_bits(p.bits())
         {}

      ~DL_Group_Data() = default;

      DL_Group_Data(const DL_Group_Data& other) = delete;
      DL_Group_Data& operator=(const DL_Group_Data& other) = delete;

      const BigInt& p() const { return m_p; }
      const BigInt& q() const { return m_q; }
      const BigInt& g() const { return m_g; }

      BigInt mod_p(const BigInt& x) const { return m_mod_p.reduce(x); }

      BigInt multiply_mod_p(const BigInt& x, const BigInt& y) const
         {
         return m_mod_p.multiply(x, y);
         }

      size_t p_bits() const { return m_p_bits; }
      size_t p_bytes() const { return (m_p_bits + 7) / 8; }

      BigInt power_g_p(const BigInt& k) const { return monty_execute(*m_monty, k); }

   private:
      BigInt m_p;
      BigInt m_q;
      BigInt m_g;
      Modular_Reducer m_mod_p;
      std::shared_ptr<const Montgomery_Exponentation_State> m_monty;
      size_t m_p_bits;
   };

//static
std::shared_ptr<DL_Group_Data> DL_Group::BER_decode_DL_group(const uint8_t data[], size_t data_len, DL_Group::Format format)
   {
   BigInt p, q, g;

   BER_Decoder decoder(data, data_len);
   BER_Decoder ber = decoder.start_cons(SEQUENCE);

   if(format == DL_Group::ANSI_X9_57)
      {
      ber.decode(p)
         .decode(q)
         .decode(g)
         .verify_end();
      }
   else if(format == DL_Group::ANSI_X9_42)
      {
      ber.decode(p)
         .decode(g)
         .decode(q)
         .discard_remaining();
      }
   else if(format == DL_Group::PKCS_3)
      {
      // q is left as zero
      ber.decode(p)
         .decode(g)
         .discard_remaining();
      }
   else
      throw Invalid_Argument("Unknown DL_Group encoding " + std::to_string(format));

   return std::make_shared<DL_Group_Data>(p, q, g);
   }

//static
std::shared_ptr<DL_Group_Data>
DL_Group::load_DL_group_info(const char* p_str,
                             const char* q_str,
                             const char* g_str)
   {
   const BigInt p(p_str);
   const BigInt q(q_str);
   const BigInt g(g_str);

   return std::make_shared<DL_Group_Data>(p, q, g);
   }

//static
std::shared_ptr<DL_Group_Data>
DL_Group::load_DL_group_info(const char* p_str,
                             const char* g_str)
   {
   const BigInt p(p_str);
   const BigInt q = (p - 1) / 2;
   const BigInt g(g_str);

   return std::make_shared<DL_Group_Data>(p, q, g);
   }

namespace {

DL_Group::Format pem_label_to_dl_format(const std::string& label)
   {
   if(label == "DH PARAMETERS")
      return DL_Group::PKCS_3;
   else if(label == "DSA PARAMETERS")
      return DL_Group::ANSI_X9_57;
   else if(label == "X942 DH PARAMETERS" || label == "X9.42 DH PARAMETERS")
      return DL_Group::ANSI_X9_42;
   else
      throw Decoding_Error("DL_Group: Invalid PEM label " + label);
   }

}

/*
* DL_Group Constructor
*/
DL_Group::DL_Group(const std::string& str)
   {
   // Either a name or a PEM block, try name first
   m_data = DL_group_info(str);

   if(m_data == nullptr)
      {
      try
         {
         std::string label;
         const std::vector<uint8_t> ber = unlock(PEM_Code::decode(str, label));
         Format format = pem_label_to_dl_format(label);

         m_data = BER_decode_DL_group(ber.data(), ber.size(), format);
         }
      catch(...) {}
      }

   if(m_data == nullptr)
      throw Invalid_Argument("DL_Group: Unknown group " + str);
   }

namespace {

/*
* Create generator of the q-sized subgroup (DSA style generator)
*/
BigInt make_dsa_generator(const BigInt& p, const BigInt& q)
   {
   const BigInt e = (p - 1) / q;

   if(e == 0 || (p - 1) % q > 0)
      throw Invalid_Argument("make_dsa_generator q does not divide p-1");

   for(size_t i = 0; i != PRIME_TABLE_SIZE; ++i)
      {
      // TODO precompute!
      BigInt g = power_mod(PRIMES[i], e, p);
      if(g > 1)
         return g;
      }

   throw Internal_Error("DL_Group: Couldn't create a suitable generator");
   }

}

/*
* DL_Group Constructor
*/
DL_Group::DL_Group(RandomNumberGenerator& rng,
                   PrimeType type, size_t pbits, size_t qbits)
   {
   if(pbits < 1024)
      throw Invalid_Argument("DL_Group: prime size " + std::to_string(pbits) + " is too small");

   if(type == Strong)
      {
      if(qbits != 0 && qbits != pbits - 1)
         throw Invalid_Argument("Cannot create strong-prime DL_Group with specified q bits");

      const BigInt p = random_safe_prime(rng, pbits);
      const BigInt q = (p - 1) / 2;

      /*
      Always choose a generator that is quadratic reside mod p,
      this forces g to be a generator of the subgroup of size q.
      */
      BigInt g = 2;
      if(jacobi(g, p) != 1)
         {
         // prime table does not contain 2
         for(size_t i = 0; i < PRIME_TABLE_SIZE; ++i)
            {
            g = PRIMES[i];
            if(jacobi(g, p) == 1)
               break;
            }
         }

      m_data = std::make_shared<DL_Group_Data>(p, q, g);
      }
   else if(type == Prime_Subgroup)
      {
      if(qbits == 0)
         qbits = dl_exponent_size(pbits);

      const BigInt q = random_prime(rng, qbits);
      Modular_Reducer mod_2q(2*q);
      BigInt X;
      BigInt p;
      while(p.bits() != pbits || !is_prime(p, rng))
         {
         X.randomize(rng, pbits);
         p = X - mod_2q.reduce(X) + 1;
         }

      const BigInt g = make_dsa_generator(p, q);
      m_data = std::make_shared<DL_Group_Data>(p, q, g);
      }
   else if(type == DSA_Kosherizer)
      {
      if(qbits == 0)
         qbits = ((pbits <= 1024) ? 160 : 256);

      BigInt p, q;
      generate_dsa_primes(rng, p, q, pbits, qbits);
      const BigInt g = make_dsa_generator(p, q);
      m_data = std::make_shared<DL_Group_Data>(p, q, g);
      }
   else
      {
      throw Invalid_Argument("DL_Group unknown PrimeType");
      }
   }

/*
* DL_Group Constructor
*/
DL_Group::DL_Group(RandomNumberGenerator& rng,
                   const std::vector<uint8_t>& seed,
                   size_t pbits, size_t qbits)
   {
   BigInt p, q;

   if(!generate_dsa_primes(rng, p, q, pbits, qbits, seed))
      throw Invalid_Argument("DL_Group: The seed given does not generate a DSA group");

   BigInt g = make_dsa_generator(p, q);

   m_data = std::make_shared<DL_Group_Data>(p, q, g);
   }

/*
* DL_Group Constructor
*/
DL_Group::DL_Group(const BigInt& p, const BigInt& g)
   {
   m_data = std::make_shared<DL_Group_Data>(p, 0, g);
   }

/*
* DL_Group Constructor
*/
DL_Group::DL_Group(const BigInt& p, const BigInt& q, const BigInt& g)
   {
   m_data = std::make_shared<DL_Group_Data>(p, q, g);
   }

const DL_Group_Data& DL_Group::data() const
   {
   if(m_data)
      return *m_data;

   throw Invalid_State("DL_Group uninitialized");
   }

/*
* Verify the parameters
*/
bool DL_Group::verify_group(RandomNumberGenerator& rng,
                            bool strong) const
   {
   const BigInt& p = get_p();
   const BigInt& q = get_q();
   const BigInt& g = get_g();

   if(g < 2 || p < 3 || q < 0)
      return false;

   const size_t prob = (strong) ? 128 : 10;

   if(q != 0)
      {
      if((p - 1) % q != 0)
         {
         return false;
         }
      if(this->power_g_p(q) != 1)
         {
         return false;
         }
      if(!is_prime(q, rng, prob))
         {
         return false;
         }
      }

   if(!is_prime(p, rng, prob))
      {
      return false;
      }
   return true;
   }

/*
* Return the prime
*/
const BigInt& DL_Group::get_p() const
   {
   return data().p();
   }

/*
* Return the generator
*/
const BigInt& DL_Group::get_g() const
   {
   return data().g();
   }

/*
* Return the subgroup
*/
const BigInt& DL_Group::get_q() const
   {
   return data().q();
   }

size_t DL_Group::p_bits() const
   {
   return data().p_bits();
   }

size_t DL_Group::p_bytes() const
   {
   return data().p_bytes();
   }

BigInt DL_Group::inverse_mod_p(const BigInt& x) const
   {
   // precompute??
   return inverse_mod(x, get_p());
   }

BigInt DL_Group::mod_p(const BigInt& x) const
   {
   return data().mod_p(x);
   }

BigInt DL_Group::multiply_mod_p(const BigInt& x, const BigInt& y) const
   {
   return data().multiply_mod_p(x, y);
   }

BigInt DL_Group::power_g_p(const BigInt& x) const
   {
   return data().power_g_p(x);
   }

/*
* DER encode the parameters
*/
std::vector<uint8_t> DL_Group::DER_encode(Format format) const
   {
   if(get_q().is_zero() && (format == ANSI_X9_57 || format == ANSI_X9_42))
      throw Encoding_Error("Cannot encode DL_Group in ANSI formats when q param is missing");

   if(format == ANSI_X9_57)
      {
      return DER_Encoder()
         .start_cons(SEQUENCE)
            .encode(get_p())
            .encode(get_q())
            .encode(get_g())
         .end_cons()
      .get_contents_unlocked();
      }
   else if(format == ANSI_X9_42)
      {
      return DER_Encoder()
         .start_cons(SEQUENCE)
            .encode(get_p())
            .encode(get_g())
            .encode(get_q())
         .end_cons()
      .get_contents_unlocked();
      }
   else if(format == PKCS_3)
      {
      return DER_Encoder()
         .start_cons(SEQUENCE)
            .encode(get_p())
            .encode(get_g())
         .end_cons()
      .get_contents_unlocked();
      }

   throw Invalid_Argument("Unknown DL_Group encoding " + std::to_string(format));
   }

/*
* PEM encode the parameters
*/
std::string DL_Group::PEM_encode(Format format) const
   {
   const std::vector<uint8_t> encoding = DER_encode(format);

   if(format == PKCS_3)
      return PEM_Code::encode(encoding, "DH PARAMETERS");
   else if(format == ANSI_X9_57)
      return PEM_Code::encode(encoding, "DSA PARAMETERS");
   else if(format == ANSI_X9_42)
      return PEM_Code::encode(encoding, "X9.42 DH PARAMETERS");
   else
      throw Invalid_Argument("Unknown DL_Group encoding " + std::to_string(format));
   }

DL_Group::DL_Group(const uint8_t ber[], size_t ber_len, Format format)
   {
   m_data = BER_decode_DL_group(ber, ber_len, format);
   }

void DL_Group::BER_decode(const std::vector<uint8_t>& ber, Format format)
   {
   m_data = BER_decode_DL_group(ber.data(), ber.size(), format);
   }

/*
* Decode PEM encoded parameters
*/
void DL_Group::PEM_decode(const std::string& pem)
   {
   std::string label;
   const std::vector<uint8_t> ber = unlock(PEM_Code::decode(pem, label));
   Format format = pem_label_to_dl_format(label);

   m_data = BER_decode_DL_group(ber.data(), ber.size(), format);
   }

//static
std::string DL_Group::PEM_for_named_group(const std::string& name)
   {
   DL_Group group(name);
   DL_Group::Format format = group.get_q().is_zero() ? DL_Group::PKCS_3 : DL_Group::ANSI_X9_42;
   return group.PEM_encode(format);
   }

}
<commit_msg>Remove unused include [ci skip]<commit_after>/*
* Discrete Logarithm Parameters
* (C) 1999-2008,2015,2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/dl_group.h>
#include <botan/numthry.h>
#include <botan/reducer.h>
#include <botan/der_enc.h>
#include <botan/ber_dec.h>
#include <botan/pem.h>
#include <botan/workfactor.h>
#include <botan/internal/monty_exp.h>

namespace Botan {

class DL_Group_Data final
   {
   public:
      DL_Group_Data(const BigInt& p, const BigInt& q, const BigInt& g) :
         m_p(p), m_q(q), m_g(g),
         m_mod_p(p),
         m_monty(monty_precompute(m_g, m_p, m_mod_p, /*window bits=*/4)),
         m_p_bits(p.bits())
         {}

      ~DL_Group_Data() = default;

      DL_Group_Data(const DL_Group_Data& other) = delete;
      DL_Group_Data& operator=(const DL_Group_Data& other) = delete;

      const BigInt& p() const { return m_p; }
      const BigInt& q() const { return m_q; }
      const BigInt& g() const { return m_g; }

      BigInt mod_p(const BigInt& x) const { return m_mod_p.reduce(x); }

      BigInt multiply_mod_p(const BigInt& x, const BigInt& y) const
         {
         return m_mod_p.multiply(x, y);
         }

      size_t p_bits() const { return m_p_bits; }
      size_t p_bytes() const { return (m_p_bits + 7) / 8; }

      BigInt power_g_p(const BigInt& k) const { return monty_execute(*m_monty, k); }

   private:
      BigInt m_p;
      BigInt m_q;
      BigInt m_g;
      Modular_Reducer m_mod_p;
      std::shared_ptr<const Montgomery_Exponentation_State> m_monty;
      size_t m_p_bits;
   };

//static
std::shared_ptr<DL_Group_Data> DL_Group::BER_decode_DL_group(const uint8_t data[], size_t data_len, DL_Group::Format format)
   {
   BigInt p, q, g;

   BER_Decoder decoder(data, data_len);
   BER_Decoder ber = decoder.start_cons(SEQUENCE);

   if(format == DL_Group::ANSI_X9_57)
      {
      ber.decode(p)
         .decode(q)
         .decode(g)
         .verify_end();
      }
   else if(format == DL_Group::ANSI_X9_42)
      {
      ber.decode(p)
         .decode(g)
         .decode(q)
         .discard_remaining();
      }
   else if(format == DL_Group::PKCS_3)
      {
      // q is left as zero
      ber.decode(p)
         .decode(g)
         .discard_remaining();
      }
   else
      throw Invalid_Argument("Unknown DL_Group encoding " + std::to_string(format));

   return std::make_shared<DL_Group_Data>(p, q, g);
   }

//static
std::shared_ptr<DL_Group_Data>
DL_Group::load_DL_group_info(const char* p_str,
                             const char* q_str,
                             const char* g_str)
   {
   const BigInt p(p_str);
   const BigInt q(q_str);
   const BigInt g(g_str);

   return std::make_shared<DL_Group_Data>(p, q, g);
   }

//static
std::shared_ptr<DL_Group_Data>
DL_Group::load_DL_group_info(const char* p_str,
                             const char* g_str)
   {
   const BigInt p(p_str);
   const BigInt q = (p - 1) / 2;
   const BigInt g(g_str);

   return std::make_shared<DL_Group_Data>(p, q, g);
   }

namespace {

DL_Group::Format pem_label_to_dl_format(const std::string& label)
   {
   if(label == "DH PARAMETERS")
      return DL_Group::PKCS_3;
   else if(label == "DSA PARAMETERS")
      return DL_Group::ANSI_X9_57;
   else if(label == "X942 DH PARAMETERS" || label == "X9.42 DH PARAMETERS")
      return DL_Group::ANSI_X9_42;
   else
      throw Decoding_Error("DL_Group: Invalid PEM label " + label);
   }

}

/*
* DL_Group Constructor
*/
DL_Group::DL_Group(const std::string& str)
   {
   // Either a name or a PEM block, try name first
   m_data = DL_group_info(str);

   if(m_data == nullptr)
      {
      try
         {
         std::string label;
         const std::vector<uint8_t> ber = unlock(PEM_Code::decode(str, label));
         Format format = pem_label_to_dl_format(label);

         m_data = BER_decode_DL_group(ber.data(), ber.size(), format);
         }
      catch(...) {}
      }

   if(m_data == nullptr)
      throw Invalid_Argument("DL_Group: Unknown group " + str);
   }

namespace {

/*
* Create generator of the q-sized subgroup (DSA style generator)
*/
BigInt make_dsa_generator(const BigInt& p, const BigInt& q)
   {
   const BigInt e = (p - 1) / q;

   if(e == 0 || (p - 1) % q > 0)
      throw Invalid_Argument("make_dsa_generator q does not divide p-1");

   for(size_t i = 0; i != PRIME_TABLE_SIZE; ++i)
      {
      // TODO precompute!
      BigInt g = power_mod(PRIMES[i], e, p);
      if(g > 1)
         return g;
      }

   throw Internal_Error("DL_Group: Couldn't create a suitable generator");
   }

}

/*
* DL_Group Constructor
*/
DL_Group::DL_Group(RandomNumberGenerator& rng,
                   PrimeType type, size_t pbits, size_t qbits)
   {
   if(pbits < 1024)
      throw Invalid_Argument("DL_Group: prime size " + std::to_string(pbits) + " is too small");

   if(type == Strong)
      {
      if(qbits != 0 && qbits != pbits - 1)
         throw Invalid_Argument("Cannot create strong-prime DL_Group with specified q bits");

      const BigInt p = random_safe_prime(rng, pbits);
      const BigInt q = (p - 1) / 2;

      /*
      Always choose a generator that is quadratic reside mod p,
      this forces g to be a generator of the subgroup of size q.
      */
      BigInt g = 2;
      if(jacobi(g, p) != 1)
         {
         // prime table does not contain 2
         for(size_t i = 0; i < PRIME_TABLE_SIZE; ++i)
            {
            g = PRIMES[i];
            if(jacobi(g, p) == 1)
               break;
            }
         }

      m_data = std::make_shared<DL_Group_Data>(p, q, g);
      }
   else if(type == Prime_Subgroup)
      {
      if(qbits == 0)
         qbits = dl_exponent_size(pbits);

      const BigInt q = random_prime(rng, qbits);
      Modular_Reducer mod_2q(2*q);
      BigInt X;
      BigInt p;
      while(p.bits() != pbits || !is_prime(p, rng))
         {
         X.randomize(rng, pbits);
         p = X - mod_2q.reduce(X) + 1;
         }

      const BigInt g = make_dsa_generator(p, q);
      m_data = std::make_shared<DL_Group_Data>(p, q, g);
      }
   else if(type == DSA_Kosherizer)
      {
      if(qbits == 0)
         qbits = ((pbits <= 1024) ? 160 : 256);

      BigInt p, q;
      generate_dsa_primes(rng, p, q, pbits, qbits);
      const BigInt g = make_dsa_generator(p, q);
      m_data = std::make_shared<DL_Group_Data>(p, q, g);
      }
   else
      {
      throw Invalid_Argument("DL_Group unknown PrimeType");
      }
   }

/*
* DL_Group Constructor
*/
DL_Group::DL_Group(RandomNumberGenerator& rng,
                   const std::vector<uint8_t>& seed,
                   size_t pbits, size_t qbits)
   {
   BigInt p, q;

   if(!generate_dsa_primes(rng, p, q, pbits, qbits, seed))
      throw Invalid_Argument("DL_Group: The seed given does not generate a DSA group");

   BigInt g = make_dsa_generator(p, q);

   m_data = std::make_shared<DL_Group_Data>(p, q, g);
   }

/*
* DL_Group Constructor
*/
DL_Group::DL_Group(const BigInt& p, const BigInt& g)
   {
   m_data = std::make_shared<DL_Group_Data>(p, 0, g);
   }

/*
* DL_Group Constructor
*/
DL_Group::DL_Group(const BigInt& p, const BigInt& q, const BigInt& g)
   {
   m_data = std::make_shared<DL_Group_Data>(p, q, g);
   }

const DL_Group_Data& DL_Group::data() const
   {
   if(m_data)
      return *m_data;

   throw Invalid_State("DL_Group uninitialized");
   }

/*
* Verify the parameters
*/
bool DL_Group::verify_group(RandomNumberGenerator& rng,
                            bool strong) const
   {
   const BigInt& p = get_p();
   const BigInt& q = get_q();
   const BigInt& g = get_g();

   if(g < 2 || p < 3 || q < 0)
      return false;

   const size_t prob = (strong) ? 128 : 10;

   if(q != 0)
      {
      if((p - 1) % q != 0)
         {
         return false;
         }
      if(this->power_g_p(q) != 1)
         {
         return false;
         }
      if(!is_prime(q, rng, prob))
         {
         return false;
         }
      }

   if(!is_prime(p, rng, prob))
      {
      return false;
      }
   return true;
   }

/*
* Return the prime
*/
const BigInt& DL_Group::get_p() const
   {
   return data().p();
   }

/*
* Return the generator
*/
const BigInt& DL_Group::get_g() const
   {
   return data().g();
   }

/*
* Return the subgroup
*/
const BigInt& DL_Group::get_q() const
   {
   return data().q();
   }

size_t DL_Group::p_bits() const
   {
   return data().p_bits();
   }

size_t DL_Group::p_bytes() const
   {
   return data().p_bytes();
   }

BigInt DL_Group::inverse_mod_p(const BigInt& x) const
   {
   // precompute??
   return inverse_mod(x, get_p());
   }

BigInt DL_Group::mod_p(const BigInt& x) const
   {
   return data().mod_p(x);
   }

BigInt DL_Group::multiply_mod_p(const BigInt& x, const BigInt& y) const
   {
   return data().multiply_mod_p(x, y);
   }

BigInt DL_Group::power_g_p(const BigInt& x) const
   {
   return data().power_g_p(x);
   }

/*
* DER encode the parameters
*/
std::vector<uint8_t> DL_Group::DER_encode(Format format) const
   {
   if(get_q().is_zero() && (format == ANSI_X9_57 || format == ANSI_X9_42))
      throw Encoding_Error("Cannot encode DL_Group in ANSI formats when q param is missing");

   if(format == ANSI_X9_57)
      {
      return DER_Encoder()
         .start_cons(SEQUENCE)
            .encode(get_p())
            .encode(get_q())
            .encode(get_g())
         .end_cons()
      .get_contents_unlocked();
      }
   else if(format == ANSI_X9_42)
      {
      return DER_Encoder()
         .start_cons(SEQUENCE)
            .encode(get_p())
            .encode(get_g())
            .encode(get_q())
         .end_cons()
      .get_contents_unlocked();
      }
   else if(format == PKCS_3)
      {
      return DER_Encoder()
         .start_cons(SEQUENCE)
            .encode(get_p())
            .encode(get_g())
         .end_cons()
      .get_contents_unlocked();
      }

   throw Invalid_Argument("Unknown DL_Group encoding " + std::to_string(format));
   }

/*
* PEM encode the parameters
*/
std::string DL_Group::PEM_encode(Format format) const
   {
   const std::vector<uint8_t> encoding = DER_encode(format);

   if(format == PKCS_3)
      return PEM_Code::encode(encoding, "DH PARAMETERS");
   else if(format == ANSI_X9_57)
      return PEM_Code::encode(encoding, "DSA PARAMETERS");
   else if(format == ANSI_X9_42)
      return PEM_Code::encode(encoding, "X9.42 DH PARAMETERS");
   else
      throw Invalid_Argument("Unknown DL_Group encoding " + std::to_string(format));
   }

DL_Group::DL_Group(const uint8_t ber[], size_t ber_len, Format format)
   {
   m_data = BER_decode_DL_group(ber, ber_len, format);
   }

void DL_Group::BER_decode(const std::vector<uint8_t>& ber, Format format)
   {
   m_data = BER_decode_DL_group(ber.data(), ber.size(), format);
   }

/*
* Decode PEM encoded parameters
*/
void DL_Group::PEM_decode(const std::string& pem)
   {
   std::string label;
   const std::vector<uint8_t> ber = unlock(PEM_Code::decode(pem, label));
   Format format = pem_label_to_dl_format(label);

   m_data = BER_decode_DL_group(ber.data(), ber.size(), format);
   }

//static
std::string DL_Group::PEM_for_named_group(const std::string& name)
   {
   DL_Group group(name);
   DL_Group::Format format = group.get_q().is_zero() ? DL_Group::PKCS_3 : DL_Group::ANSI_X9_42;
   return group.PEM_encode(format);
   }

}
<|endoftext|>