<commit_before>#include <linux/videodev2.h>
#include <cstdio>
#include <string.h>
#include <poll.h>
#include <fcntl.h>
#include <unistd.h>
#include <fstream>
#include <sys/ioctl.h>
#include <xf86drm.h>

#include "kms++.h"
#include "test.h"
#include "opts.h"

#define CAMERA_BUF_QUEUE_SIZE	3
#define MAX_CAMERA		9

using namespace std;
using namespace kms;

enum class BufferProvider {
	DRM,
	V4L2,
};

class Camera
{
public:
	Camera(int camera_id, Card& card, Plane* plane, uint32_t x, uint32_t y,
	       uint32_t iw, uint32_t ih, PixelFormat pixfmt,
	       BufferProvider buffer_provider);
	~Camera();

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

	void show_next_frame(Crtc* crtc);
	int fd() const { return m_fd; }
private:
	ExtFramebuffer* GetExtFrameBuffer(Card& card, int i, PixelFormat pixfmt);
	int m_fd;	/* camera file descriptor */
	Plane* m_plane;
	BufferProvider m_buffer_provider;
	vector<DumbFramebuffer*> m_fb; /* framebuffers for DRM buffers */
	vector<ExtFramebuffer*> m_extfb; /* framebuffers for V4L2 buffers */
	int m_prev_fb_index;
	uint32_t m_in_width, m_in_height; /* camera capture resolution */
	/* image properties for display */
	uint32_t m_out_width, m_out_height;
	uint32_t m_out_x, m_out_y;
};

static int buffer_export(int v4lfd, enum v4l2_buf_type bt, int index, int *dmafd)
{
	struct v4l2_exportbuffer expbuf;

	memset(&expbuf, 0, sizeof(expbuf));
	expbuf.type = bt;
	expbuf.index = index;
	if (ioctl(v4lfd, VIDIOC_EXPBUF, &expbuf) == -1) {
		perror("VIDIOC_EXPBUF");
		return -1;
	}

	*dmafd = expbuf.fd;

	return 0;
}

ExtFramebuffer* Camera::GetExtFrameBuffer(Card& card, int i, PixelFormat pixfmt)
{
	int r, dmafd;

	r = buffer_export(m_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, i, &dmafd);
	ASSERT(r == 0);

	uint32_t handle;
	r = drmPrimeFDToHandle(card.fd(), dmafd, &handle);
	ASSERT(r == 0);

	const PixelFormatInfo& format_info = get_pixel_format_info(pixfmt);
	ASSERT(format_info.num_planes == 1);

	uint32_t handles[4] { handle };
	uint32_t pitches[4] { m_in_width * (format_info.planes[0].bitspp / 8) };
	uint32_t offsets[4] { };

	return new ExtFramebuffer(card, m_in_width, m_in_height, pixfmt,
				  handles, pitches, offsets);
}

bool inline better_size(struct v4l2_frmsize_discrete* v4ldisc,
			uint32_t iw, uint32_t ih,
			uint32_t best_w, uint32_t best_h)
{
	if (v4ldisc->width <= iw && v4ldisc->height <= ih &&
	    (v4ldisc->width >= best_w || v4ldisc->height >= best_h))
		return true;

	return false;
}

Camera::Camera(int camera_id, Card& card, Plane* plane, uint32_t x, uint32_t y,
	       uint32_t iw, uint32_t ih, PixelFormat pixfmt,
	       BufferProvider buffer_provider)
{
	char dev_name[20];
	int r, i;
	uint32_t best_w = 320;
	uint32_t best_h = 240;
	uint32_t v4l_mem;

	m_buffer_provider = buffer_provider;
	if (m_buffer_provider == BufferProvider::V4L2)
		v4l_mem = V4L2_MEMORY_MMAP;
	else
		v4l_mem = V4L2_MEMORY_DMABUF;

	sprintf(dev_name, "/dev/video%d", camera_id);
	m_fd = ::open(dev_name, O_RDWR | O_NONBLOCK);

	ASSERT(m_fd >= 0);

	struct v4l2_frmsizeenum v4lfrms = { };
	v4lfrms.pixel_format = (uint32_t) pixfmt;
	while (ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms) == 0) {
		if (v4lfrms.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
			if (better_size(&v4lfrms.discrete, iw, ih,
					best_w, best_h)) {
				best_w = v4lfrms.discrete.width;
				best_h = v4lfrms.discrete.height;
			}
		} else {
			break;
		}
		v4lfrms.index++;
	};

	m_out_width = m_in_width = best_w;
	m_out_height = m_in_height = best_h;
	/* Move it to the middle of the requested area */
	m_out_x = x + iw / 2 - m_out_width / 2;
	m_out_y = y + ih / 2 - m_out_height / 2;

	struct v4l2_format v4lfmt = { };
	v4lfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	r = ioctl(m_fd, VIDIOC_G_FMT, &v4lfmt);
	ASSERT(r == 0);

	v4lfmt.fmt.pix.pixelformat = (uint32_t) pixfmt;
	v4lfmt.fmt.pix.width = m_in_width;
	v4lfmt.fmt.pix.height = m_in_height;

	r = ioctl(m_fd, VIDIOC_S_FMT, &v4lfmt);
	ASSERT(r == 0);

	struct v4l2_requestbuffers v4lreqbuf = { };
	v4lreqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	v4lreqbuf.memory = v4l_mem;
	v4lreqbuf.count = CAMERA_BUF_QUEUE_SIZE;
	r = ioctl(m_fd, VIDIOC_REQBUFS, &v4lreqbuf);
	ASSERT(r == 0);
	ASSERT(v4lreqbuf.count == CAMERA_BUF_QUEUE_SIZE);

	struct v4l2_buffer v4lbuf = { };
	v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	v4lbuf.memory = v4l_mem;

	for (i = 0; i < CAMERA_BUF_QUEUE_SIZE; i++) {
		DumbFramebuffer *fb = NULL;
		ExtFramebuffer *extfb = NULL;

		if (m_buffer_provider == BufferProvider::V4L2)
			extfb = GetExtFrameBuffer(card, i, pixfmt);
		else
			fb = new DumbFramebuffer(card, m_in_width,
						 m_in_height, pixfmt);

		v4lbuf.index = i;
		if (m_buffer_provider == BufferProvider::DRM)
			v4lbuf.m.fd = fb->prime_fd(0);
		r = ioctl(m_fd, VIDIOC_QBUF, &v4lbuf);
		ASSERT(r == 0);

		if (m_buffer_provider == BufferProvider::V4L2)
			m_extfb.push_back(extfb);
		else
			m_fb.push_back(fb);
	}
	m_prev_fb_index = -1;
	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	r = ioctl(m_fd, VIDIOC_STREAMON, &type);
		ASSERT(r == 0);

	m_plane = plane;
}

Camera::~Camera()
{
	for (unsigned i = 0; i < m_fb.size(); i++)
		delete m_fb[i];

	for (unsigned i = 0; i < m_extfb.size(); i++)
		delete m_extfb[i];

	::close(m_fd);
}

void Camera::show_next_frame(Crtc* crtc)
{
	int r;
	int fb_index;
	uint32_t v4l_mem;

	if (m_buffer_provider == BufferProvider::V4L2)
		v4l_mem = V4L2_MEMORY_MMAP;
	else
		v4l_mem = V4L2_MEMORY_DMABUF;

	struct v4l2_buffer v4l2buf = { };
	v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	v4l2buf.memory = v4l_mem;
	r = ioctl(m_fd, VIDIOC_DQBUF, &v4l2buf);
	if (r != 0) {
		printf("VIDIOC_DQBUF ioctl failed with %d\n", errno);
		return;
	}

	fb_index = v4l2buf.index;
	if (m_buffer_provider == BufferProvider::V4L2)
		r = crtc->set_plane(m_plane, *m_extfb[fb_index],
				    m_out_x, m_out_y, m_out_width, m_out_height,
				    0, 0, m_in_width, m_in_height);
	else
		r = crtc->set_plane(m_plane, *m_fb[fb_index],
				    m_out_x, m_out_y, m_out_width, m_out_height,
				    0, 0, m_in_width, m_in_height);

	ASSERT(r == 0);

	if (m_prev_fb_index >= 0) {
		memset(&v4l2buf, 0, sizeof(v4l2buf));
		v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		v4l2buf.memory = v4l_mem;
		v4l2buf.index = m_prev_fb_index;
		if (m_buffer_provider == BufferProvider::DRM)
			v4l2buf.m.fd = m_fb[m_prev_fb_index]->prime_fd(0);
		r = ioctl(m_fd, VIDIOC_QBUF, &v4l2buf);
		ASSERT(r == 0);

	}
	m_prev_fb_index = fb_index;
}

static bool is_capture_dev(int fd)
{
	struct v4l2_capability cap = { };
	int r;

	r = ioctl(fd, VIDIOC_QUERYCAP, &cap);
	ASSERT(r == 0);
	if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
		return false;

	return true;
}

static vector<int> count_cameras()
{
	int i, fd;
	vector<int> camera_idx;
	char dev_name[20];

	for (i = 0; i < MAX_CAMERA; i++) {
		sprintf(dev_name, "/dev/video%d", i);
		fd = ::open(dev_name, O_RDWR | O_NONBLOCK);
		if (fd >= 0) {
			if (is_capture_dev(fd))
				camera_idx.push_back(i);
			close(fd);
		}
	}

	return camera_idx;
}

static const char* usage_str =
"Usage: kmscapture [OPTIONS]\n\n"
"Options:\n"
"  -s, --single                Single camera mode. Open only /dev/video0\n"
"      --buffer-type=<drm|v4l> Use DRM or V4L provided buffers. Default: DRM\n"
"  -h, --help                  Print this help\n"
;

int main(int argc, char** argv)
{
	uint32_t w;
	BufferProvider buffer_provider = BufferProvider::DRM;
	int i;

	auto camera_idx = count_cameras();
	int nr_cameras = camera_idx.size();

	FAIL_IF(!nr_cameras, "Not a single camera has been found.");

	OptionSet optionset = {
		Option("s|single", [&](string s)
		{
			nr_cameras = 1;
		}),
		Option("|buffer-type=", [&](string s)
		{
			if (!s.compare("v4l"))
				buffer_provider = BufferProvider::V4L2;
			else if (s.compare("drm"))
				printf("invalid buffer-type: %s\n", s.c_str());
		}),
		Option("h|help", [&]()
		{
			puts(usage_str);
			exit(-1);
		}),
	};

	optionset.parse(argc, argv);

	if (optionset.params().size() > 0) {
		puts(usage_str);
		exit(-1);
	}

	auto pixfmt = FourCCToPixelFormat("YUYV");

	Card card;

	auto conn = card.get_first_connected_connector();
	auto crtc = conn->get_current_crtc();
	printf("Display: %dx%d\n", crtc->width(), crtc->height());
	printf("Buffer provider: %s\n",
	       buffer_provider == BufferProvider::V4L2? "V4L" : "DRM");

	w = crtc->width() / nr_cameras;
	vector<Camera*> cameras;
	i = 0;
	for (Plane* p : crtc->get_possible_planes()) {
		if (p->plane_type() != PlaneType::Overlay)
			continue;

		if (!p->supports_format(pixfmt))
			continue;

		auto cam = new Camera(camera_idx[i], card, p, i * w, 0,
				      w, crtc->height(), pixfmt, buffer_provider);
		cameras.push_back(cam);
		if (++i == nr_cameras)
			break;
	}

	FAIL_IF(i < nr_cameras, "available plane not found");

	vector<pollfd> fds(nr_cameras + 1);

	for (i = 0; i < nr_cameras; i++) {
		fds[i].fd = cameras[i]->fd();
		fds[i].events =  POLLIN;
	}
	fds[nr_cameras].fd = 0;
	fds[nr_cameras].events =  POLLIN;

	while (true) {
		int r = poll(fds.data(), nr_cameras + 1, -1);
		ASSERT(r > 0);

		if (fds[nr_cameras].revents != 0)
			break;

		for (i = 0; i < nr_cameras; i++) {
			if (!fds[i].revents)
				continue;
			cameras[i]->show_next_frame(crtc);
			fds[i].revents = 0;
		}
	}

	for (auto cam : cameras)
		delete cam;
}
<commit_msg>kmscapture: cleanups & signedness fixes<commit_after>#include <linux/videodev2.h>
#include <cstdio>
#include <string.h>
#include <poll.h>
#include <fcntl.h>
#include <unistd.h>
#include <fstream>
#include <sys/ioctl.h>
#include <xf86drm.h>

#include "kms++.h"
#include "test.h"
#include "opts.h"

#define CAMERA_BUF_QUEUE_SIZE	3
#define MAX_CAMERA		9

using namespace std;
using namespace kms;

enum class BufferProvider {
	DRM,
	V4L2,
};

class Camera
{
public:
	Camera(int camera_id, Card& card, Plane* plane, uint32_t x, uint32_t y,
	       uint32_t iw, uint32_t ih, PixelFormat pixfmt,
	       BufferProvider buffer_provider);
	~Camera();

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

	void show_next_frame(Crtc* crtc);
	int fd() const { return m_fd; }
private:
	ExtFramebuffer* GetExtFrameBuffer(Card& card, uint32_t i, PixelFormat pixfmt);
	int m_fd;	/* camera file descriptor */
	Plane* m_plane;
	BufferProvider m_buffer_provider;
	vector<DumbFramebuffer*> m_fb; /* framebuffers for DRM buffers */
	vector<ExtFramebuffer*> m_extfb; /* framebuffers for V4L2 buffers */
	int m_prev_fb_index;
	uint32_t m_in_width, m_in_height; /* camera capture resolution */
	/* image properties for display */
	uint32_t m_out_width, m_out_height;
	uint32_t m_out_x, m_out_y;
};

static int buffer_export(int v4lfd, enum v4l2_buf_type bt, uint32_t index, int *dmafd)
{
	struct v4l2_exportbuffer expbuf;

	memset(&expbuf, 0, sizeof(expbuf));
	expbuf.type = bt;
	expbuf.index = index;
	if (ioctl(v4lfd, VIDIOC_EXPBUF, &expbuf) == -1) {
		perror("VIDIOC_EXPBUF");
		return -1;
	}

	*dmafd = expbuf.fd;

	return 0;
}

ExtFramebuffer* Camera::GetExtFrameBuffer(Card& card, uint32_t i, PixelFormat pixfmt)
{
	int r, dmafd;

	r = buffer_export(m_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, i, &dmafd);
	ASSERT(r == 0);

	uint32_t handle;
	r = drmPrimeFDToHandle(card.fd(), dmafd, &handle);
	ASSERT(r == 0);

	const PixelFormatInfo& format_info = get_pixel_format_info(pixfmt);
	ASSERT(format_info.num_planes == 1);

	uint32_t handles[4] { handle };
	uint32_t pitches[4] { m_in_width * (format_info.planes[0].bitspp / 8) };
	uint32_t offsets[4] { };

	return new ExtFramebuffer(card, m_in_width, m_in_height, pixfmt,
				  handles, pitches, offsets);
}

bool inline better_size(struct v4l2_frmsize_discrete* v4ldisc,
			uint32_t iw, uint32_t ih,
			uint32_t best_w, uint32_t best_h)
{
	if (v4ldisc->width <= iw && v4ldisc->height <= ih &&
	    (v4ldisc->width >= best_w || v4ldisc->height >= best_h))
		return true;

	return false;
}

Camera::Camera(int camera_id, Card& card, Plane* plane, uint32_t x, uint32_t y,
	       uint32_t iw, uint32_t ih, PixelFormat pixfmt,
	       BufferProvider buffer_provider)
{
	char dev_name[20];
	int r;
	uint32_t best_w = 320;
	uint32_t best_h = 240;
	uint32_t v4l_mem;

	m_buffer_provider = buffer_provider;
	if (m_buffer_provider == BufferProvider::V4L2)
		v4l_mem = V4L2_MEMORY_MMAP;
	else
		v4l_mem = V4L2_MEMORY_DMABUF;

	sprintf(dev_name, "/dev/video%d", camera_id);
	m_fd = ::open(dev_name, O_RDWR | O_NONBLOCK);

	ASSERT(m_fd >= 0);

	struct v4l2_frmsizeenum v4lfrms = { };
	v4lfrms.pixel_format = (uint32_t)pixfmt;
	while (ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms) == 0) {
		if (v4lfrms.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
			if (better_size(&v4lfrms.discrete, iw, ih,
					best_w, best_h)) {
				best_w = v4lfrms.discrete.width;
				best_h = v4lfrms.discrete.height;
			}
		} else {
			break;
		}
		v4lfrms.index++;
	};

	m_out_width = m_in_width = best_w;
	m_out_height = m_in_height = best_h;
	/* Move it to the middle of the requested area */
	m_out_x = x + iw / 2 - m_out_width / 2;
	m_out_y = y + ih / 2 - m_out_height / 2;

	struct v4l2_format v4lfmt = { };
	v4lfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	r = ioctl(m_fd, VIDIOC_G_FMT, &v4lfmt);
	ASSERT(r == 0);

	v4lfmt.fmt.pix.pixelformat = (uint32_t)pixfmt;
	v4lfmt.fmt.pix.width = m_in_width;
	v4lfmt.fmt.pix.height = m_in_height;

	r = ioctl(m_fd, VIDIOC_S_FMT, &v4lfmt);
	ASSERT(r == 0);

	struct v4l2_requestbuffers v4lreqbuf = { };
	v4lreqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	v4lreqbuf.memory = v4l_mem;
	v4lreqbuf.count = CAMERA_BUF_QUEUE_SIZE;
	r = ioctl(m_fd, VIDIOC_REQBUFS, &v4lreqbuf);
	ASSERT(r == 0);
	ASSERT(v4lreqbuf.count == CAMERA_BUF_QUEUE_SIZE);

	struct v4l2_buffer v4lbuf = { };
	v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	v4lbuf.memory = v4l_mem;

	for (unsigned i = 0; i < CAMERA_BUF_QUEUE_SIZE; i++) {
		DumbFramebuffer *fb = NULL;
		ExtFramebuffer *extfb = NULL;

		if (m_buffer_provider == BufferProvider::V4L2)
			extfb = GetExtFrameBuffer(card, i, pixfmt);
		else
			fb = new DumbFramebuffer(card, m_in_width,
						 m_in_height, pixfmt);

		v4lbuf.index = i;
		if (m_buffer_provider == BufferProvider::DRM)
			v4lbuf.m.fd = fb->prime_fd(0);
		r = ioctl(m_fd, VIDIOC_QBUF, &v4lbuf);
		ASSERT(r == 0);

		if (m_buffer_provider == BufferProvider::V4L2)
			m_extfb.push_back(extfb);
		else
			m_fb.push_back(fb);
	}
	m_prev_fb_index = -1;
	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	r = ioctl(m_fd, VIDIOC_STREAMON, &type);
		ASSERT(r == 0);

	m_plane = plane;
}

Camera::~Camera()
{
	for (unsigned i = 0; i < m_fb.size(); i++)
		delete m_fb[i];

	for (unsigned i = 0; i < m_extfb.size(); i++)
		delete m_extfb[i];

	::close(m_fd);
}

void Camera::show_next_frame(Crtc* crtc)
{
	int r;
	uint32_t v4l_mem;

	if (m_buffer_provider == BufferProvider::V4L2)
		v4l_mem = V4L2_MEMORY_MMAP;
	else
		v4l_mem = V4L2_MEMORY_DMABUF;

	struct v4l2_buffer v4l2buf = { };
	v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	v4l2buf.memory = v4l_mem;
	r = ioctl(m_fd, VIDIOC_DQBUF, &v4l2buf);
	if (r != 0) {
		printf("VIDIOC_DQBUF ioctl failed with %d\n", errno);
		return;
	}

	unsigned fb_index = v4l2buf.index;
	if (m_buffer_provider == BufferProvider::V4L2)
		r = crtc->set_plane(m_plane, *m_extfb[fb_index],
				    m_out_x, m_out_y, m_out_width, m_out_height,
				    0, 0, m_in_width, m_in_height);
	else
		r = crtc->set_plane(m_plane, *m_fb[fb_index],
				    m_out_x, m_out_y, m_out_width, m_out_height,
				    0, 0, m_in_width, m_in_height);

	ASSERT(r == 0);

	if (m_prev_fb_index >= 0) {
		memset(&v4l2buf, 0, sizeof(v4l2buf));
		v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		v4l2buf.memory = v4l_mem;
		v4l2buf.index = m_prev_fb_index;
		if (m_buffer_provider == BufferProvider::DRM)
			v4l2buf.m.fd = m_fb[m_prev_fb_index]->prime_fd(0);
		r = ioctl(m_fd, VIDIOC_QBUF, &v4l2buf);
		ASSERT(r == 0);

	}
	m_prev_fb_index = fb_index;
}

static bool is_capture_dev(int fd)
{
	struct v4l2_capability cap = { };
	int r;

	r = ioctl(fd, VIDIOC_QUERYCAP, &cap);
	ASSERT(r == 0);
	if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
		return false;

	return true;
}

static vector<int> count_cameras()
{
	int i, fd;
	vector<int> camera_idx;
	char dev_name[20];

	for (i = 0; i < MAX_CAMERA; i++) {
		sprintf(dev_name, "/dev/video%d", i);
		fd = ::open(dev_name, O_RDWR | O_NONBLOCK);
		if (fd >= 0) {
			if (is_capture_dev(fd))
				camera_idx.push_back(i);
			close(fd);
		}
	}

	return camera_idx;
}

static const char* usage_str =
"Usage: kmscapture [OPTIONS]\n\n"
"Options:\n"
"  -s, --single                Single camera mode. Open only /dev/video0\n"
"      --buffer-type=<drm|v4l> Use DRM or V4L provided buffers. Default: DRM\n"
"  -h, --help                  Print this help\n"
;

int main(int argc, char** argv)
{
	uint32_t w;
	BufferProvider buffer_provider = BufferProvider::DRM;

	auto camera_idx = count_cameras();
	unsigned nr_cameras = camera_idx.size();

	FAIL_IF(!nr_cameras, "Not a single camera has been found.");

	OptionSet optionset = {
		Option("s|single", [&](string s)
		{
			nr_cameras = 1;
		}),
		Option("|buffer-type=", [&](string s)
		{
			if (!s.compare("v4l"))
				buffer_provider = BufferProvider::V4L2;
			else if (s.compare("drm"))
				printf("invalid buffer-type: %s\n", s.c_str());
		}),
		Option("h|help", [&]()
		{
			puts(usage_str);
			exit(-1);
		}),
	};

	optionset.parse(argc, argv);

	if (optionset.params().size() > 0) {
		puts(usage_str);
		exit(-1);
	}

	auto pixfmt = FourCCToPixelFormat("YUYV");

	Card card;

	auto conn = card.get_first_connected_connector();
	auto crtc = conn->get_current_crtc();
	printf("Display: %dx%d\n", crtc->width(), crtc->height());
	printf("Buffer provider: %s\n",
	       buffer_provider == BufferProvider::V4L2? "V4L" : "DRM");

	w = crtc->width() / nr_cameras;
	vector<Camera*> cameras;

	unsigned cam_idx = 0;
	for (Plane* p : crtc->get_possible_planes()) {
		if (p->plane_type() != PlaneType::Overlay)
			continue;

		if (!p->supports_format(pixfmt))
			continue;

		auto cam = new Camera(camera_idx[cam_idx], card, p, cam_idx * w, 0,
				      w, crtc->height(), pixfmt, buffer_provider);
		cameras.push_back(cam);
		if (++cam_idx == nr_cameras)
			break;
	}

	FAIL_IF(cam_idx < nr_cameras, "available plane not found");

	vector<pollfd> fds(nr_cameras + 1);

	for (unsigned i = 0; i < nr_cameras; i++) {
		fds[i].fd = cameras[i]->fd();
		fds[i].events =  POLLIN;
	}
	fds[nr_cameras].fd = 0;
	fds[nr_cameras].events =  POLLIN;

	while (true) {
		int r = poll(fds.data(), nr_cameras + 1, -1);
		ASSERT(r > 0);

		if (fds[nr_cameras].revents != 0)
			break;

		for (unsigned i = 0; i < nr_cameras; i++) {
			if (!fds[i].revents)
				continue;
			cameras[i]->show_next_frame(crtc);
			fds[i].revents = 0;
		}
	}

	for (auto cam : cameras)
		delete cam;
}
<|endoftext|>


<commit_before>// Copyright 2013 Mirus Project

// 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.

//
// kernel.cpp - main source file + kernel entry point
//              we all have to begin somewhere, right?
//

#include <stdafx.hpp>
#include <boot/multiboot.hpp>
#include <boot/ramdisk.hpp>
#include <cpu/gdt.hpp>
#include <cpu/idt.hpp>
#include <cpu/isr.hpp>
#include <cpu/irq.hpp>
#include <system/process/task.hpp>
#include <system/syscall.hpp>
#include <hardware/serial.hpp>
#include <mem/paging.hpp>

namespace mirus
{
    extern "C" void test_func();

    // Memory size
    uint32_t memory_size = 0;
    uint32_t memory_size_m = 0;
    uint32_t min_mem = 512;

    // Memory map
    memory_map_t* mmap = nullptr;

    // Modules
    uint32_t mod_count = 0;
    module_t* mods = nullptr;

    //
    // kernel_main - our kernel entry point
    //
    extern "C" void kernel_main(multiboot_info_t* mbd, unsigned int magic)
    {
        // Print debug stub
        ktrace(trace_level::msg, "Mirus 0.3.6-dev\n\n");

        // Set up screen early on for boot information output
        screen::terminal::install();

        // Print kernel information
        kprintf("Mirus [%d.%d.%d-dev]\n\n", BUILD_MAJOR, BUILD_MINOR,
            BUILD_REV);

        // Get avalible memory
        if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
            ktrace(trace_level::error,
                "Multiboot bootloader magic doesn't match!\n");

        // Retrieve number of multiboot modules
        mod_count = mbd->mods_count;

        // Verify if multiboot flags are ok to go
        if (mbd->flags & 1)
        {
            // Read header and assign
            ktrace(trace_level::msg, "Reading multiboot header\n");
            mmap = (memory_map_t*)mbd->mmap_addr;

            // Parse entries
            while ((unsigned int)mmap < (unsigned int)(mbd->mmap_addr)
                + mbd->mmap_length)
            {
                memory_size += mmap->length_low;
                mmap = (memory_map_t*)((unsigned int)mmap + mmap->size
                    + sizeof(unsigned int));
            }

            // Get memory size in megabytes
            memory_size_m = ((memory_size / 1024) / 1024);
            ktrace(trace_level::msg, "Avalible memory: %dm\n", memory_size_m);
        }

        // Check for any modules, the only of which should be the ramdisk
        ktrace(trace_level::msg, "Trying to get ramdisk...\n");

        if (mod_count > 0)
        {
            ktrace(trace_level::msg, "Modules found: %d\n", mod_count);

            uint32_t i = 0;
            for (i = 0, mods = (module_t*)mbd->mods_addr;
                i < mod_count;
                i++, mods++)
            {
                ktrace(trace_level::none, "==========\n");
                ktrace(trace_level::msg, "Module found: [%d:%d]\n",
                    mods->mod_start,
                    mods->mod_end);

                // load the ramdisk
                boot::parse_tar(mods->mod_start);
                kprintf("Loaded ramdisk\n");

                ktrace(trace_level::none, "==========\n");
            }
        }
        else
        {
            ktrace(trace_level::msg, "No modules found.\n");
            kprintf("[ERROR]\n");
        }

        // Check for minimum memory size
        if (memory_size_m < min_mem)
            ktrace(trace_level::warn, "Memory is less than expected minimum\n");

        // Setup CPU
        cpu::gdt::install();
        cpu::idt::install();
        cpu::isr::install();
        cpu::irq::install();

        // Enable paging
        mem::paging::init(memory_size);

        // Enable interrupts
        asm volatile("sti");

        // Setup hardware
        hardware::serial::install();

        // Set up system calls
        system::init_syscalls();
        ktrace(trace_level::msg, "Installed system calls\n");
        kprintf("Installed system calls\n");

        // Enter usermode
        system::enter_userspace();
        ktrace(trace_level::notice, "Entered usermode...\n");

        // System call test function
        test_func();

        // YOU SHALL NOT PASS!!!!
        while (true);
    }
} // !namespace<commit_msg>Updated kernel.cpp<commit_after>// Copyright 2013 Mirus Project

// 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.

//
// kernel.cpp - main source file + kernel entry point
//              we all have to begin somewhere, right?
//

#include <stdafx.hpp>
#include <boot/multiboot.hpp>
#include <boot/ramdisk.hpp>
#include <cpu/gdt.hpp>
#include <cpu/idt.hpp>
#include <cpu/isr.hpp>
#include <cpu/irq.hpp>
#include <system/process/task.hpp>
#include <system/syscall.hpp>
#include <hardware/serial.hpp>
#include <mem/paging.hpp>

namespace mirus
{
    extern "C" void test_func();

    // Memory size
    uint32_t memory_size = 0;
    uint32_t memory_size_m = 0;
    uint32_t min_mem = 512;

    // Memory map
    memory_map_t* mmap = nullptr;

    // Modules
    uint32_t mod_count = 0;
    module_t* mods = nullptr;

    //
    // kernel_main - our kernel entry point
    //
    extern "C" void kernel_main(multiboot_info_t* mbd, unsigned int magic)
    {
        // Print debug stub
        ktrace(trace_level::msg, "Mirus 0.3.6-dev\n\n");

        // Set up screen early on for boot information output
        screen::terminal::install();

        // Print kernel information
        kprintf("Mirus [%d.%d.%d-dev]\n\n", BUILD_MAJOR, BUILD_MINOR,
            BUILD_REV);

        // Get avalible memory
        if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
            ktrace(trace_level::error,
                "Multiboot bootloader magic doesn't match!\n");

        // Retrieve number of multiboot modules
        mod_count = mbd->mods_count;

        // Verify if multiboot flags are ok to go
        if (mbd->flags & 1)
        {
            // Read header and assign
            ktrace(trace_level::msg, "Reading multiboot header\n");
            mmap = (memory_map_t*)mbd->mmap_addr;

            // Parse entries
            while ((unsigned int)mmap < (unsigned int)(mbd->mmap_addr)
                + mbd->mmap_length)
            {
                memory_size += mmap->length_low;
                mmap = (memory_map_t*)((unsigned int)mmap + mmap->size
                    + sizeof(unsigned int));
            }

            // Get memory size in megabytes
            memory_size_m = ((memory_size / 1024) / 1024);
            ktrace(trace_level::msg, "Avalible memory: %dm\n", memory_size_m);
        }

        // Check for any modules, the only of which should be the ramdisk
        ktrace(trace_level::msg, "Trying to get ramdisk...\n");

        if (mod_count > 0)
        {
            ktrace(trace_level::msg, "Modules found: %d\n", mod_count);

            uint32_t i = 0;
            for (i = 0, mods = (module_t*)mbd->mods_addr;
                i < mod_count;
                i++, mods++)
            {
                ktrace(trace_level::none, "==========\n");
                ktrace(trace_level::msg, "Module found: [%d:%d]\n",
                    mods->mod_start,
                    mods->mod_end);

                // load the ramdisk
                boot::parse_tar(mods->mod_start);
                kprintf("Loaded ramdisk\n");

                ktrace(trace_level::none, "==========\n");
            }
        }
        else
        {
            ktrace(trace_level::msg, "No modules found.\n");
            kprintf("[ERROR]\n");
        }

        // Check for minimum memory size
        if (memory_size_m < min_mem)
            ktrace(trace_level::warn, "Memory is less than expected minimum\n");

        // Setup CPU
        cpu::gdt::install();
        cpu::idt::install();
        cpu::isr::install();
        cpu::irq::install();

        // Enable paging
        // TODO: see the "paging" branch for this
        // mem::paging::init(memory_size);

        // Enable interrupts
        asm volatile("sti");

        // Setup hardware
        hardware::serial::install();

        // Set up system calls
        system::init_syscalls();
        ktrace(trace_level::msg, "Installed system calls\n");
        kprintf("Installed system calls\n");

        // Enter usermode
        system::enter_userspace();
        ktrace(trace_level::notice, "Entered usermode...\n");

        // System call test function
        test_func();

        // YOU SHALL NOT PASS!!!!
        while (true);
    }
} // !namespace<|endoftext|>


<commit_before>// Copyright 2004-present Facebook. All Rights Reserved.

#include <ctime>
#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include "osquery/database.h"

using std::string;
using boost::lexical_cast;

namespace osquery {
namespace tables {

const char *xattr_quarantine = "com.apple.quarantine";

QueryData genQuarantine() {
  Row r;
  QueryData results;

  boost::filesystem::recursive_directory_iterator it =
      boost::filesystem::recursive_directory_iterator(
          boost::filesystem::path("/"));
  boost::filesystem::recursive_directory_iterator end;

  while (it != end) {
    boost::filesystem::path path = *it;
    try {
      std::vector<std::string> values;
      std::string filePathQuotes = boost::lexical_cast<std::string>(path);
      std::string filePath = filePathQuotes.substr(1, filePathQuotes.length() - 2);

      int bufferLength = getxattr(filePath.c_str(), xattr_quarantine, NULL, 0, 0, 0);
      if (bufferLength > 0) {
	char *value = (char *) malloc(sizeof(char *) * bufferLength);
	getxattr(filePath.c_str(), xattr_quarantine, value, bufferLength, 0, 0);

	boost::split(values, value, boost::is_any_of(";"));
	boost::trim(values[2]);

	r["path"] = filePath;
	r["creator"] = values[2];

	results.push_back(r);
	free(value);
      }
    } catch (...) {
      // handle invalid files like /dev/fd/3
    }
    try {
      ++it;
    } catch (std::exception &ex) {
      it.no_push(); // handle permission error.
    }
  }

  return results;
}
}
}
<commit_msg>Feedback fixes to clean the code a bit<commit_after>// Copyright 2004-present Facebook. All Rights Reserved.

#include <ctime>
#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include "osquery/database.h"

using std::string;
using boost::lexical_cast;

namespace osquery {
namespace tables {

const std::string kXattrQuarantine = "com.apple.quarantine";

QueryData genQuarantine() {
  QueryData results;

  auto it = boost::filesystem::recursive_directory_iterator(boost::filesystem::path("/"));
  boost::filesystem::recursive_directory_iterator end;

  while (it != end) {
    boost::filesystem::path path = *it;
    try {
      Row r;
      std::vector<std::string> values;
      std::string filePathQuotes = boost::lexical_cast<std::string>(path);
      std::string filePath = filePathQuotes.substr(1, filePathQuotes.length() - 2);

      int bufferLength = getxattr(filePath.c_str(), kXattrQuarantine.c_str(), NULL, 0, 0, 0);
      if (bufferLength > 0) {
	char *value = (char *) malloc(sizeof(char *) * bufferLength);
	getxattr(filePath.c_str(), kXattrQuarantine.c_str(), value, bufferLength, 0, 0);

	boost::split(values, value, boost::is_any_of(";"));
	boost::trim(values[2]);

	r["path"] = filePath;
	r["creator"] = values[2];

	results.push_back(r);
	free(value);
      }
    } catch (...) {
      LOG(ERROR) << "Couldn't handle file " << boost::lexical_cast<std::string>(*it);
    }
    try {
      ++it;
    } catch (std::exception &ex) {
      LOG(WARNING) << "Permissions error on " << boost::lexical_cast<std::string>(*it);
      it.no_push();
    }
  }

  return results;
}
}
}
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2007-2010, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * 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.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software 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.
//
//////////////////////////////////////////////////////////////////////////

// This include needs to be the very first to prevent problems with warnings
// regarding redefinition of _POSIX_C_SOURCE
#include "boost/python.hpp"

#include "IECore/Renderable.h"
#include "IECore/Renderer.h"
#include "IECorePython/RenderableBinding.h"
#include "IECorePython/RunTimeTypedBinding.h"

using namespace boost::python;
using namespace IECore;

namespace IECorePython
{

void bindRenderable()
{
	RunTimeTypedClass<Renderable>( "An abstract class to define objects which are renderable" )
		.def("render", &Renderable::render)
	;
}

}
<commit_msg>Releasing the GIL during Renderable::render() methods. This gave considerable speedups when rendering a hierarchy of nested procedurals into an IECoreGL::Renderer. Much of the time was taken triangulating meshes so this unlock allowed that to happen in parallel. A typical procedural nearly doubled in framerate with this change.<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2007-2010, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * 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.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software 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.
//
//////////////////////////////////////////////////////////////////////////

// This include needs to be the very first to prevent problems with warnings
// regarding redefinition of _POSIX_C_SOURCE
#include "boost/python.hpp"

#include "IECore/Renderable.h"
#include "IECore/Renderer.h"
#include "IECorePython/RenderableBinding.h"
#include "IECorePython/RunTimeTypedBinding.h"
#include "IECorePython/ScopedGILRelease.h"

using namespace boost::python;
using namespace IECore;

namespace IECorePython
{

static void render( const Renderable &renderable, Renderer *renderer )
{
	IECorePython::ScopedGILRelease gilRelease;
	renderable.render( renderer );
}

void bindRenderable()
{
	RunTimeTypedClass<Renderable>( "An abstract class to define objects which are renderable" )
		.def( "render", &render )
	;
}

}
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// Implementation of AliPerformanceTPC class. It keeps information from
// comparison of reconstructed and MC particle tracks. In addtion,
// it keeps selection cuts used during comparison. The comparison
// information is stored in the ROOT histograms. Analysis of these
// histograms can be done by using Analyse() class function. The result of
// the analysis (histograms/graphs) are stored in the folder which is
// a data member of AliPerformanceTPC.
//
// Author: J.Otwinowski 04/02/2008
//------------------------------------------------------------------------------

/*

  // after running comparison task, read the file, and get component
  gROOT->LoadMacro("$ALICE_ROOT/PWG1/Macros/LoadMyLibs.C");
  LoadMyLibs();

  TFile f("Output.root");
  AliPerformanceTPC * compObj = (AliPerformanceTPC*)coutput->FindObject("AliPerformanceTPC");

  // analyse comparison data
  compObj->Analyse();

  // the output histograms/graphs will be stored in the folder "folderTPC"
  compObj->GetAnalysisFolder()->ls("*");

  // user can save whole comparison object (or only folder with anlysed histograms)
  // in the seperate output file (e.g.)
  TFile fout("Analysed_TPC.root","recreate");
  compObj->Write(); // compObj->GetAnalysisFolder()->Write();
  fout.Close();

*/

#include "TCanvas.h"
#include "TH1.h"
#include "TH2.h"
#include "TAxis.h"
#include "TPostScript.h"

#include "AliPerformanceTPC.h"
#include "AliESDEvent.h"
#include "AliESDVertex.h"
#include "AliESDtrack.h"
#include "AliLog.h"
#include "AliMCEvent.h"
#include "AliHeader.h"
#include "AliGenEventHeader.h"
#include "AliStack.h"
#include "AliMCInfoCuts.h"
#include "AliRecInfoCuts.h"
#include "AliTracker.h"
#include "AliTreeDraw.h"

using namespace std;

ClassImp(AliPerformanceTPC)

//_____________________________________________________________________________
AliPerformanceTPC::AliPerformanceTPC():
  AliPerformanceObject("AliPerformanceTPC"),
  fTPCHisto(0),

  // Cuts
  fCutsRC(0),
  fCutsMC(0),

  // histogram folder
  fAnalysisFolder(0)
{
  Init();
}

//_____________________________________________________________________________
AliPerformanceTPC::AliPerformanceTPC(Char_t* name="AliPerformanceTPC", Char_t* title="AliPerformanceTPC",Int_t analysisMode=0,Bool_t hptGenerator=kFALSE):
  AliPerformanceObject(name,title),
  fTPCHisto(0),

  // Cuts
  fCutsRC(0),
  fCutsMC(0),

  // histogram folder
  fAnalysisFolder(0)
{
  // named constructor
  //
  SetAnalysisMode(analysisMode);
  SetHptGenerator(hptGenerator);

  Init();
}

//_____________________________________________________________________________
AliPerformanceTPC::~AliPerformanceTPC()
{
  // destructor

  if(fTPCHisto) delete fTPCHisto; fTPCHisto=0;
  if(fAnalysisFolder) delete fAnalysisFolder; fAnalysisFolder=0;
}

//_____________________________________________________________________________
void AliPerformanceTPC::Init(){
  //
  // histogram bining
  //

  // set pt bins
  Int_t nPtBins = 50;
  Double_t ptMin = 1.e-2, ptMax = 10.;

  Double_t *binsPt = 0;
  if (IsHptGenerator())  {
    nPtBins = 100; ptMax = 100.;
    binsPt = CreateLogAxis(nPtBins,ptMin,ptMax);
  } else {
    binsPt = CreateLogAxis(nPtBins,ptMin,ptMax);
  }

  /*
  Int_t nPtBins = 31;
  Double_t binsPt[32] = {0.,0.05,0.1,0.15,0.2,0.25,0.3,0.35,0.4,0.45,0.5,0.55,0.6,0.7,0.8,0.9,1.0,1.2,1.4,1.6,1.8,2.0,2.25,2.5,2.75,3.,3.5,4.,5.,6.,8.,10.};
  Double_t ptMin = 0., ptMax = 10.;

  if(IsHptGenerator() == kTRUE) {
    nPtBins = 100;
    ptMin = 0.; ptMax = 100.;
  }
  */

  // nTPCClust:chi2PerTPCClust:nTPCClustFindRatio:eta:phi:pt
  Int_t binsTPCHisto[6]={160,100,100,30,144,nPtBins};
  Double_t minTPCHisto[6]={0., 0., 0., -1.5, 0., ptMin};
  Double_t maxTPCHisto[6]={160.,10.,1.2, 1.5, 2.*TMath::Pi(), ptMax};

  fTPCHisto = new THnSparseF("fTPCHisto","nClust:chi2PerClust:nClust/nFindableClust:eta:phi:pt",6,binsTPCHisto,minTPCHisto,maxTPCHisto);
  fTPCHisto->SetBinEdges(5,binsPt);

  fTPCHisto->GetAxis(0)->SetTitle("nClust");
  fTPCHisto->GetAxis(1)->SetTitle("chi2PerClust");
  fTPCHisto->GetAxis(2)->SetTitle("nClust/nFindableClust");
  fTPCHisto->GetAxis(3)->SetTitle("#eta");
  fTPCHisto->GetAxis(4)->SetTitle("#phi (rad)");
  fTPCHisto->GetAxis(5)->SetTitle("p_{T} (GeV/c)");
  fTPCHisto->Sumw2();

  // Init cuts
  if(!fCutsMC)
    AliDebug(AliLog::kError, "ERROR: Cannot find AliMCInfoCuts object");
  if(!fCutsRC)
    AliDebug(AliLog::kError, "ERROR: Cannot find AliRecInfoCuts object");

  // init folder
  fAnalysisFolder = CreateFolder("folderTPC","Analysis Resolution Folder");
}

//_____________________________________________________________________________
void AliPerformanceTPC::ProcessTPC(AliStack* const stack, AliESDtrack *const esdTrack)
{
  if(!esdTrack) return;

  // Fill TPC only resolution comparison information
  const AliExternalTrackParam *track = esdTrack->GetTPCInnerParam();
  if(!track) return;

  Float_t dca[2], cov[3]; // dca_xy, dca_z, sigma_xy, sigma_xy_z, sigma_z
  esdTrack->GetImpactParametersTPC(dca,cov);

  //Float_t q = esdTrack->Charge();
  Float_t pt = track->Pt();
  Float_t eta = track->Eta();
  Float_t phi = track->Phi();
  Int_t nClust = esdTrack->GetTPCclusters(0);
  Int_t nFindableClust = esdTrack->GetTPCNclsF();

  Float_t chi2PerCluster = 0.;
  if(nClust>0.) chi2PerCluster = esdTrack->GetTPCchi2()/Float_t(nClust);

  Float_t clustPerFindClust = 0.;
  if(nFindableClust>0.) clustPerFindClust = Float_t(nClust)/nFindableClust;

  if(TMath::Abs(dca[0])<fCutsRC->GetMaxDCAToVertexXY() && TMath::Abs(dca[1])<fCutsRC->GetMaxDCAToVertexZ()) {
    Double_t vTPCHisto[6] = {nClust,chi2PerCluster,clustPerFindClust,eta,phi,pt};
    fTPCHisto->Fill(vTPCHisto);
  }

  //
  // Fill rec vs MC information
  //
  if(!stack) return;

}

//_____________________________________________________________________________
void AliPerformanceTPC::ProcessTPCITS(AliStack* const /*stack*/, AliESDtrack *const /*esdTrack*/)
{
  // Fill comparison information (TPC+ITS)
  AliDebug(AliLog::kWarning, "Warning: Not implemented");
}

//_____________________________________________________________________________
void AliPerformanceTPC::ProcessConstrained(AliStack* const /*stack*/, AliESDtrack *const /*esdTrack*/)
{
  // Fill comparison information (constarained parameters)
  AliDebug(AliLog::kWarning, "Warning: Not implemented");
}

//_____________________________________________________________________________
void AliPerformanceTPC::Exec(AliMCEvent* const mcEvent, AliESDEvent* const esdEvent, const Bool_t bUseMC)
{
  // Process comparison information
  //
  if(!esdEvent)
  {
      AliDebug(AliLog::kError, "esdEvent not available");
      return;
  }
  AliHeader* header = 0;
  AliGenEventHeader* genHeader = 0;
  AliStack* stack = 0;
  TArrayF vtxMC(3);

  if(bUseMC)
  {
    if(!mcEvent) {
      AliDebug(AliLog::kError, "mcEvent not available");
      return;
    }

    // get MC event header
    header = mcEvent->Header();
    if (!header) {
      AliDebug(AliLog::kError, "Header not available");
      return;
    }
    // MC particle stack
    stack = mcEvent->Stack();
    if (!stack) {
      AliDebug(AliLog::kError, "Stack not available");
      return;
    }

    // get MC vertex
    genHeader = header->GenEventHeader();
    if (!genHeader) {
      AliDebug(AliLog::kError, "Could not retrieve genHeader from Header");
      return;
    }
    genHeader->PrimaryVertex(vtxMC);

  } // end bUseMC

  //  Process events
  for (Int_t iTrack = 0; iTrack < esdEvent->GetNumberOfTracks(); iTrack++)
  {
    AliESDtrack*track = esdEvent->GetTrack(iTrack);
    if(!track) continue;

    if(GetAnalysisMode() == 0) ProcessTPC(stack,track);
    else if(GetAnalysisMode() == 1) ProcessTPCITS(stack,track);
    else if(GetAnalysisMode() == 2) ProcessConstrained(stack,track);
    else {
      printf("ERROR: AnalysisMode %d \n",fAnalysisMode);
      return;
    }
  }
}

//_____________________________________________________________________________
void AliPerformanceTPC::Analyse() {
  //
  // Analyse comparison information and store output histograms
  // in the folder "folderTPC"
  //
  TH1::AddDirectory(kFALSE);
  //TH1F *h=0;
  TH2F *h2D=0;
  TObjArray *aFolderObj = new TObjArray;

  char name[256];
  char title[256];
  for(Int_t i=0; i<5; i++)
  {
    for(Int_t j=i+1; j<6; j++)
    {
      if(j==5) fTPCHisto->GetAxis(5)->SetRangeUser(0.1,10.);
      h2D = (TH2F*)fTPCHisto->Projection(i,j);
      sprintf(name,"h_tpc_%d_vs_%d",i,j);
      h2D->SetName(name);
      h2D->GetXaxis()->SetTitle(fTPCHisto->GetAxis(j)->GetTitle());
      h2D->GetYaxis()->SetTitle(fTPCHisto->GetAxis(i)->GetTitle());
      sprintf(title,"%s vs %s",fTPCHisto->GetAxis(j)->GetTitle(),fTPCHisto->GetAxis(i)->GetTitle());
      h2D->SetTitle(title);

      if(j==5) h2D->SetBit(TH1::kLogX);
      aFolderObj->Add(h2D);
    }
  }

  // export objects to analysis folder
  fAnalysisFolder = ExportToFolder(aFolderObj);

  // delete only TObjArray
  if(aFolderObj) delete aFolderObj;
}

//_____________________________________________________________________________
TFolder* AliPerformanceTPC::ExportToFolder(TObjArray * array)
{
  // recreate folder avery time and export objects to new one
  //
  AliPerformanceTPC * comp=this;
  TFolder *folder = comp->GetAnalysisFolder();

  TString name, title;
  TFolder *newFolder = 0;
  Int_t i = 0;
  Int_t size = array->GetSize();

  if(folder) {
     // get name and title from old folder
     name = folder->GetName();
     title = folder->GetTitle();

	 // delete old one
     delete folder;

	 // create new one
     newFolder = CreateFolder(name.Data(),title.Data());
     newFolder->SetOwner();

	 // add objects to folder
     while(i < size) {
	   newFolder->Add(array->At(i));
	   i++;
	 }
  }

return newFolder;
}

//_____________________________________________________________________________
Long64_t AliPerformanceTPC::Merge(TCollection* const list)
{
  // Merge list of objects (needed by PROOF)

  if (!list)
  return 0;

  if (list->IsEmpty())
  return 1;

  TIterator* iter = list->MakeIterator();
  TObject* obj = 0;

  // collection of generated histograms
  Int_t count=0;
  while((obj = iter->Next()) != 0)
  {
  AliPerformanceTPC* entry = dynamic_cast<AliPerformanceTPC*>(obj);
  if (entry == 0) continue;

  fTPCHisto->Add(entry->fTPCHisto);

  count++;
  }

return count;
}

//_____________________________________________________________________________
TFolder* AliPerformanceTPC::CreateFolder(TString name,TString title) {
// create folder for analysed histograms
//
TFolder *folder = 0;
  folder = new TFolder(name.Data(),title.Data());

  return folder;
}
<commit_msg>possiblity to read ESD friends (Jacek)<commit_after>//------------------------------------------------------------------------------
// Implementation of AliPerformanceTPC class. It keeps information from
// comparison of reconstructed and MC particle tracks. In addtion,
// it keeps selection cuts used during comparison. The comparison
// information is stored in the ROOT histograms. Analysis of these
// histograms can be done by using Analyse() class function. The result of
// the analysis (histograms/graphs) are stored in the folder which is
// a data member of AliPerformanceTPC.
//
// Author: J.Otwinowski 04/02/2008
//------------------------------------------------------------------------------

/*

  // after running comparison task, read the file, and get component
  gROOT->LoadMacro("$ALICE_ROOT/PWG1/Macros/LoadMyLibs.C");
  LoadMyLibs();

  TFile f("Output.root");
  AliPerformanceTPC * compObj = (AliPerformanceTPC*)coutput->FindObject("AliPerformanceTPC");

  // analyse comparison data
  compObj->Analyse();

  // the output histograms/graphs will be stored in the folder "folderTPC"
  compObj->GetAnalysisFolder()->ls("*");

  // user can save whole comparison object (or only folder with anlysed histograms)
  // in the seperate output file (e.g.)
  TFile fout("Analysed_TPC.root","recreate");
  compObj->Write(); // compObj->GetAnalysisFolder()->Write();
  fout.Close();

*/

#include "TCanvas.h"
#include "TH1.h"
#include "TH2.h"
#include "TAxis.h"
#include "TPostScript.h"

#include "AliPerformanceTPC.h"
#include "AliESDEvent.h"
#include "AliESDVertex.h"
#include "AliESDtrack.h"
#include "AliLog.h"
#include "AliMCEvent.h"
#include "AliHeader.h"
#include "AliGenEventHeader.h"
#include "AliStack.h"
#include "AliMCInfoCuts.h"
#include "AliRecInfoCuts.h"
#include "AliTracker.h"
#include "AliTreeDraw.h"

using namespace std;

ClassImp(AliPerformanceTPC)

//_____________________________________________________________________________
AliPerformanceTPC::AliPerformanceTPC():
  AliPerformanceObject("AliPerformanceTPC"),
  fTPCHisto(0),

  // Cuts
  fCutsRC(0),
  fCutsMC(0),

  // histogram folder
  fAnalysisFolder(0)
{
  Init();
}

//_____________________________________________________________________________
AliPerformanceTPC::AliPerformanceTPC(Char_t* name="AliPerformanceTPC", Char_t* title="AliPerformanceTPC",Int_t analysisMode=0,Bool_t hptGenerator=kFALSE):
  AliPerformanceObject(name,title),
  fTPCHisto(0),

  // Cuts
  fCutsRC(0),
  fCutsMC(0),

  // histogram folder
  fAnalysisFolder(0)
{
  // named constructor
  //
  SetAnalysisMode(analysisMode);
  SetHptGenerator(hptGenerator);

  Init();
}

//_____________________________________________________________________________
AliPerformanceTPC::~AliPerformanceTPC()
{
  // destructor

  if(fTPCHisto) delete fTPCHisto; fTPCHisto=0;
  if(fAnalysisFolder) delete fAnalysisFolder; fAnalysisFolder=0;
}

//_____________________________________________________________________________
void AliPerformanceTPC::Init(){
  //
  // histogram bining
  //

  // set pt bins
  Int_t nPtBins = 50;
  Double_t ptMin = 1.e-2, ptMax = 10.;

  Double_t *binsPt = 0;
  if (IsHptGenerator())  {
    nPtBins = 100; ptMax = 100.;
    binsPt = CreateLogAxis(nPtBins,ptMin,ptMax);
  } else {
    binsPt = CreateLogAxis(nPtBins,ptMin,ptMax);
  }

  /*
  Int_t nPtBins = 31;
  Double_t binsPt[32] = {0.,0.05,0.1,0.15,0.2,0.25,0.3,0.35,0.4,0.45,0.5,0.55,0.6,0.7,0.8,0.9,1.0,1.2,1.4,1.6,1.8,2.0,2.25,2.5,2.75,3.,3.5,4.,5.,6.,8.,10.};
  Double_t ptMin = 0., ptMax = 10.;

  if(IsHptGenerator() == kTRUE) {
    nPtBins = 100;
    ptMin = 0.; ptMax = 100.;
  }
  */

  // nTPCClust:chi2PerTPCClust:nTPCClustFindRatio:eta:phi:pt
  Int_t binsTPCHisto[6]={160,100,100,30,144,nPtBins};
  Double_t minTPCHisto[6]={0., 0., 0., -1.5, 0., ptMin};
  Double_t maxTPCHisto[6]={160.,10.,1.2, 1.5, 2.*TMath::Pi(), ptMax};

  fTPCHisto = new THnSparseF("fTPCHisto","nClust:chi2PerClust:nClust/nFindableClust:eta:phi:pt",6,binsTPCHisto,minTPCHisto,maxTPCHisto);
  fTPCHisto->SetBinEdges(5,binsPt);

  fTPCHisto->GetAxis(0)->SetTitle("nClust");
  fTPCHisto->GetAxis(1)->SetTitle("chi2PerClust");
  fTPCHisto->GetAxis(2)->SetTitle("nClust/nFindableClust");
  fTPCHisto->GetAxis(3)->SetTitle("#eta");
  fTPCHisto->GetAxis(4)->SetTitle("#phi (rad)");
  fTPCHisto->GetAxis(5)->SetTitle("p_{T} (GeV/c)");
  fTPCHisto->Sumw2();

  // Init cuts
  if(!fCutsMC)
    AliDebug(AliLog::kError, "ERROR: Cannot find AliMCInfoCuts object");
  if(!fCutsRC)
    AliDebug(AliLog::kError, "ERROR: Cannot find AliRecInfoCuts object");

  // init folder
  fAnalysisFolder = CreateFolder("folderTPC","Analysis Resolution Folder");
}

//_____________________________________________________________________________
void AliPerformanceTPC::ProcessTPC(AliStack* const stack, AliESDtrack *const esdTrack)
{
  if(!esdTrack) return;

  // Fill TPC only resolution comparison information
  const AliExternalTrackParam *track = esdTrack->GetTPCInnerParam();
  if(!track) return;

  Float_t dca[2], cov[3]; // dca_xy, dca_z, sigma_xy, sigma_xy_z, sigma_z
  esdTrack->GetImpactParametersTPC(dca,cov);

  //Float_t q = esdTrack->Charge();
  Float_t pt = track->Pt();
  Float_t eta = track->Eta();
  Float_t phi = track->Phi();
  Int_t nClust = esdTrack->GetTPCclusters(0);
  Int_t nFindableClust = esdTrack->GetTPCNclsF();

  Float_t chi2PerCluster = 0.;
  if(nClust>0.) chi2PerCluster = esdTrack->GetTPCchi2()/Float_t(nClust);

  Float_t clustPerFindClust = 0.;
  if(nFindableClust>0.) clustPerFindClust = Float_t(nClust)/nFindableClust;

  if(TMath::Abs(dca[0])<fCutsRC->GetMaxDCAToVertexXY() && TMath::Abs(dca[1])<fCutsRC->GetMaxDCAToVertexZ()) {
    Double_t vTPCHisto[6] = {nClust,chi2PerCluster,clustPerFindClust,eta,phi,pt};
    fTPCHisto->Fill(vTPCHisto);
  }

  //
  // Fill rec vs MC information
  //
  if(!stack) return;

}

//_____________________________________________________________________________
void AliPerformanceTPC::ProcessTPCITS(AliStack* const /*stack*/, AliESDtrack *const /*esdTrack*/)
{
  // Fill comparison information (TPC+ITS)
  AliDebug(AliLog::kWarning, "Warning: Not implemented");
}

//_____________________________________________________________________________
void AliPerformanceTPC::ProcessConstrained(AliStack* const /*stack*/, AliESDtrack *const /*esdTrack*/)
{
  // Fill comparison information (constarained parameters)
  AliDebug(AliLog::kWarning, "Warning: Not implemented");
}

//_____________________________________________________________________________
void AliPerformanceTPC::Exec(AliMCEvent* const mcEvent, AliESDEvent *const esdEvent, AliESDfriend *const esdFriend, const Bool_t bUseMC, const Bool_t bUseESDfriend)
{
  // Process comparison information
  //
  if(!esdEvent)
  {
      AliDebug(AliLog::kError, "esdEvent not available");
      return;
  }
  AliHeader* header = 0;
  AliGenEventHeader* genHeader = 0;
  AliStack* stack = 0;
  TArrayF vtxMC(3);

  if(bUseMC)
  {
    if(!mcEvent) {
      AliDebug(AliLog::kError, "mcEvent not available");
      return;
    }

    // get MC event header
    header = mcEvent->Header();
    if (!header) {
      AliDebug(AliLog::kError, "Header not available");
      return;
    }
    // MC particle stack
    stack = mcEvent->Stack();
    if (!stack) {
      AliDebug(AliLog::kError, "Stack not available");
      return;
    }

    // get MC vertex
    genHeader = header->GenEventHeader();
    if (!genHeader) {
      AliDebug(AliLog::kError, "Could not retrieve genHeader from Header");
      return;
    }
    genHeader->PrimaryVertex(vtxMC);

  } // end bUseMC

  // use ESD friends
  if(bUseESDfriend) {
    if(!esdFriend) {
      AliDebug(AliLog::kError, "esdFriend not available");
      return;
    }
  }

  //  Process events
  for (Int_t iTrack = 0; iTrack < esdEvent->GetNumberOfTracks(); iTrack++)
  {
    AliESDtrack*track = esdEvent->GetTrack(iTrack);
    if(!track) continue;

    if(GetAnalysisMode() == 0) ProcessTPC(stack,track);
    else if(GetAnalysisMode() == 1) ProcessTPCITS(stack,track);
    else if(GetAnalysisMode() == 2) ProcessConstrained(stack,track);
    else {
      printf("ERROR: AnalysisMode %d \n",fAnalysisMode);
      return;
    }
  }
}

//_____________________________________________________________________________
void AliPerformanceTPC::Analyse() {
  //
  // Analyse comparison information and store output histograms
  // in the folder "folderTPC"
  //
  TH1::AddDirectory(kFALSE);
  //TH1F *h=0;
  TH2F *h2D=0;
  TObjArray *aFolderObj = new TObjArray;

  char name[256];
  char title[256];
  for(Int_t i=0; i<5; i++)
  {
    for(Int_t j=i+1; j<6; j++)
    {
      if(j==5) fTPCHisto->GetAxis(5)->SetRangeUser(0.1,10.);
      h2D = (TH2F*)fTPCHisto->Projection(i,j);
      sprintf(name,"h_tpc_%d_vs_%d",i,j);
      h2D->SetName(name);
      h2D->GetXaxis()->SetTitle(fTPCHisto->GetAxis(j)->GetTitle());
      h2D->GetYaxis()->SetTitle(fTPCHisto->GetAxis(i)->GetTitle());
      sprintf(title,"%s vs %s",fTPCHisto->GetAxis(j)->GetTitle(),fTPCHisto->GetAxis(i)->GetTitle());
      h2D->SetTitle(title);

      if(j==5) h2D->SetBit(TH1::kLogX);
      aFolderObj->Add(h2D);
    }
  }

  // export objects to analysis folder
  fAnalysisFolder = ExportToFolder(aFolderObj);

  // delete only TObjArray
  if(aFolderObj) delete aFolderObj;
}

//_____________________________________________________________________________
TFolder* AliPerformanceTPC::ExportToFolder(TObjArray * array)
{
  // recreate folder avery time and export objects to new one
  //
  AliPerformanceTPC * comp=this;
  TFolder *folder = comp->GetAnalysisFolder();

  TString name, title;
  TFolder *newFolder = 0;
  Int_t i = 0;
  Int_t size = array->GetSize();

  if(folder) {
     // get name and title from old folder
     name = folder->GetName();
     title = folder->GetTitle();

	 // delete old one
     delete folder;

	 // create new one
     newFolder = CreateFolder(name.Data(),title.Data());
     newFolder->SetOwner();

	 // add objects to folder
     while(i < size) {
	   newFolder->Add(array->At(i));
	   i++;
	 }
  }

return newFolder;
}

//_____________________________________________________________________________
Long64_t AliPerformanceTPC::Merge(TCollection* const list)
{
  // Merge list of objects (needed by PROOF)

  if (!list)
  return 0;

  if (list->IsEmpty())
  return 1;

  TIterator* iter = list->MakeIterator();
  TObject* obj = 0;

  // collection of generated histograms
  Int_t count=0;
  while((obj = iter->Next()) != 0)
  {
  AliPerformanceTPC* entry = dynamic_cast<AliPerformanceTPC*>(obj);
  if (entry == 0) continue;

  fTPCHisto->Add(entry->fTPCHisto);

  count++;
  }

return count;
}

//_____________________________________________________________________________
TFolder* AliPerformanceTPC::CreateFolder(TString name,TString title) {
// create folder for analysed histograms
//
TFolder *folder = 0;
  folder = new TFolder(name.Data(),title.Data());

  return folder;
}
<|endoftext|>


<commit_before>/*******************************************************************************

Copyright (c) 2016-2017 Denis Tikhomirov <dvtikhomirov@gmail.com>

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

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

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

*******************************************************************************/

#include <gtest/gtest.h>
#include "sjparser.h"

using namespace SJParser;

TEST(SArray, Empty) {
  std::string buf(R"([])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(0, parser.parser().get().size());

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, Null) {
  std::string buf(R"(null)");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_FALSE(parser.parser().isSet());
}

TEST(SArray, SArrayOfBooleans) {
  std::string buf(R"([true, false])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(true, parser.parser().get()[0]);
  ASSERT_EQ(false, parser.parser().get()[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, SArrayOfIntegers) {
  std::string buf(R"([10, 11])");

  Parser<SArray<Value<int64_t>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(10, parser.parser().get()[0]);
  ASSERT_EQ(11, parser.parser().get()[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, SArrayOfDoubles) {
  std::string buf(R"([10.5, 11.2])");

  Parser<SArray<Value<double>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(10.5, parser.parser().get()[0]);
  ASSERT_EQ(11.2, parser.parser().get()[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, SArrayOfStrings) {
  std::string buf(R"(["value1", "value2"])");

  Parser<SArray<Value<std::string>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ("value1", parser.parser().get()[0]);
  ASSERT_EQ("value2", parser.parser().get()[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, SArrayWithNull) {
  std::string buf(R"([null])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(0, parser.parser().get().size());

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, SArrayWithNullAndValuse) {
  std::string buf(R"([null, true, null, false])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(true, parser.parser().get()[0]);
  ASSERT_EQ(false, parser.parser().get()[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, UnexpectedBoolean) {
  std::string buf(R"(true)");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token boolean", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                    true
                     (right here) ------^
Unexpected token boolean
)",
      parser.getError(true));
}

TEST(SArray, UnexpectedInteger) {
  std::string buf(R"(10)");

  Parser<SArray<Value<int64_t>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_FALSE(parser.finish());

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token integer", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                        10
                     (right here) ------^
Unexpected token integer
)",
      parser.getError(true));
}

TEST(SArray, UnexpectedDouble) {
  std::string buf(R"(10.5)");

  Parser<SArray<Value<double>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_FALSE(parser.finish());

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token double", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                        10.5
                     (right here) ------^
Unexpected token double
)",
      parser.getError(true));
}

TEST(SArray, UnexpectedString) {
  std::string buf(R"("value")");

  Parser<SArray<Value<std::string>>> parser;

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token string", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                 "value"
                     (right here) ------^
Unexpected token string
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithUnexpectedBoolean) {
  std::string buf(R"([true])");

  Parser<SArray<Value<std::string>>> parser;

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token boolean", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                   [true]
                     (right here) ------^
Unexpected token boolean
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithUnexpectedInteger) {
  std::string buf(R"([10])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token integer", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                     [10]
                     (right here) ------^
Unexpected token integer
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithUnexpectedDouble) {
  std::string buf(R"([10.5])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token double", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                   [10.5]
                     (right here) ------^
Unexpected token double
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithUnexpectedString) {
  std::string buf(R"(["value"])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token string", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                ["value"]
                     (right here) ------^
Unexpected token string
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithElementCallbackError) {
  std::string buf(R"([true, false])");

  auto elementCb = [&](const bool &) { return false; };

  Parser<SArray<Value<bool>>> parser(elementCb);

  ASSERT_FALSE(parser.parse(buf));
  ASSERT_FALSE(parser.parser().isSet());

  ASSERT_EQ("Callback returned false", parser.getError());
  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                   [true, false]
                     (right here) ------^
Callback returned false
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithCallback) {
  std::string buf(R"([true, false])");
  std::vector<bool> values;

  auto arrayCb = [&](const std::vector<bool> &value) {
    values = value;
    return true;
  };

  Parser<SArray<Value<bool>>> parser(arrayCb);

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(true, parser.parser().get()[0]);
  ASSERT_EQ(false, parser.parser().get()[1]);

  ASSERT_EQ(2, values.size());
  ASSERT_EQ(true, values[0]);
  ASSERT_EQ(false, values[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, SArrayWithCallbackError) {
  std::string buf(R"([true, false])");

  auto arrayCb = [&](const std::vector<bool> &) { return false; };

  Parser<SArray<Value<bool>>> parser(arrayCb);

  ASSERT_FALSE(parser.parse(buf));
  ASSERT_TRUE(parser.parser().isSet());

  ASSERT_EQ("Callback returned false", parser.getError());
  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                           [true, false]
                     (right here) ------^
Callback returned false
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithArgsStruct) {
  std::string buf(R"([true, false])");
  std::vector<bool> values;

  auto elementCb = [&](const bool &value) {
    values.push_back(value);
    return true;
  };

  using SArrayParser = SArray<Value<bool>>;

  SArrayParser::Args array_args = {elementCb};

  Parser<SArrayParser> parser(array_args);

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(true, parser.parser().get()[0]);
  ASSERT_EQ(false, parser.parser().get()[1]);

  ASSERT_EQ(2, values.size());
  ASSERT_EQ(true, values[0]);
  ASSERT_EQ(false, values[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, UnexpectedSObject) {
  std::string buf(
      R"({"key": "value"})");

  Parser<SArray<SObject<Value<std::string>>>> parser("key");

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token map start", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                       {"key": "value"}
                     (right here) ------^
Unexpected token map start
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithUnexpectedSObject) {
  std::string buf(
      R"([{"key2": "value"}])");

  Parser<SArray<SObject<Value<std::string>>>> parser("key");

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected field key2", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                [{"key2": "value"}]
                     (right here) ------^
Unexpected field key2
)",
      parser.getError(true));
}

TEST(SArray, SArrayOfSCustomObjects) {
  std::string buf(
      R"([{"key": "value", "key2": 10}, {"key": "value2", "key2": 20}])");

  struct ObjectStruct {
    std::string field1;
    int64_t field2;
  };

  using ObjectParser =
      SObject<ObjectStruct, Value<std::string>, Value<int64_t>>;

  auto objectCb = [&](ObjectParser &parser, ObjectStruct &value) {
    value = {parser.get<0>().pop(), parser.get<1>().pop()};
    return true;
  };

  Parser<SArray<ObjectParser>> parser({{"key", "key2"}, objectCb});

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ("value", parser.parser().get()[0].field1);
  ASSERT_EQ(10, parser.parser().get()[0].field2);
  ASSERT_EQ("value2", parser.parser().get()[1].field1);
  ASSERT_EQ(20, parser.parser().get()[1].field2);
}

TEST(SArray, SArrayOfSAutoObjects) {
  std::string buf(
      R"([{"key": "value", "key2": 10}, {"key": "value2", "key2": 20}])");

  using ObjectParser = SObject<Value<std::string>, Value<int64_t>>;

  Parser<SArray<ObjectParser>> parser({"key", "key2"});

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ("value", std::get<0>(parser.parser().get()[0]));
  ASSERT_EQ(10, std::get<1>(parser.parser().get()[0]));
  ASSERT_EQ("value2", std::get<0>(parser.parser().get()[1]));
  ASSERT_EQ(20, std::get<1>(parser.parser().get()[1]));
}

TEST(SArray, SArrayOfSArrays) {
  std::string buf(R"([[true, true], [false, false]])");

  Parser<SArray<SArray<Value<bool>>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(2, parser.parser().get()[0].size());
  ASSERT_EQ(true, parser.parser().get()[0][0]);
  ASSERT_EQ(true, parser.parser().get()[0][1]);

  ASSERT_EQ(2, parser.parser().get()[1].size());
  ASSERT_EQ(false, parser.parser().get()[1][0]);
  ASSERT_EQ(false, parser.parser().get()[1][1]);
}
<commit_msg>Add a unit test for sarray reset<commit_after>/*******************************************************************************

Copyright (c) 2016-2017 Denis Tikhomirov <dvtikhomirov@gmail.com>

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

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

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

*******************************************************************************/

#include <gtest/gtest.h>
#include "sjparser.h"

using namespace SJParser;

TEST(SArray, Empty) {
  std::string buf(R"([])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(0, parser.parser().get().size());

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, Null) {
  std::string buf(R"(null)");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_FALSE(parser.parser().isSet());
}

TEST(SArray, SArrayOfBooleans) {
  std::string buf(R"([true, false])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(true, parser.parser().get()[0]);
  ASSERT_EQ(false, parser.parser().get()[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, SArrayOfIntegers) {
  std::string buf(R"([10, 11])");

  Parser<SArray<Value<int64_t>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(10, parser.parser().get()[0]);
  ASSERT_EQ(11, parser.parser().get()[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, SArrayOfDoubles) {
  std::string buf(R"([10.5, 11.2])");

  Parser<SArray<Value<double>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(10.5, parser.parser().get()[0]);
  ASSERT_EQ(11.2, parser.parser().get()[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, SArrayOfStrings) {
  std::string buf(R"(["value1", "value2"])");

  Parser<SArray<Value<std::string>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ("value1", parser.parser().get()[0]);
  ASSERT_EQ("value2", parser.parser().get()[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, SArrayWithNull) {
  std::string buf(R"([null])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(0, parser.parser().get().size());

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, SArrayWithNullAndValuse) {
  std::string buf(R"([null, true, null, false])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(true, parser.parser().get()[0]);
  ASSERT_EQ(false, parser.parser().get()[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, UnexpectedBoolean) {
  std::string buf(R"(true)");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token boolean", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                    true
                     (right here) ------^
Unexpected token boolean
)",
      parser.getError(true));
}

TEST(SArray, UnexpectedInteger) {
  std::string buf(R"(10)");

  Parser<SArray<Value<int64_t>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_FALSE(parser.finish());

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token integer", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                        10
                     (right here) ------^
Unexpected token integer
)",
      parser.getError(true));
}

TEST(SArray, UnexpectedDouble) {
  std::string buf(R"(10.5)");

  Parser<SArray<Value<double>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_FALSE(parser.finish());

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token double", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                        10.5
                     (right here) ------^
Unexpected token double
)",
      parser.getError(true));
}

TEST(SArray, UnexpectedString) {
  std::string buf(R"("value")");

  Parser<SArray<Value<std::string>>> parser;

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token string", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                 "value"
                     (right here) ------^
Unexpected token string
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithUnexpectedBoolean) {
  std::string buf(R"([true])");

  Parser<SArray<Value<std::string>>> parser;

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token boolean", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                   [true]
                     (right here) ------^
Unexpected token boolean
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithUnexpectedInteger) {
  std::string buf(R"([10])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token integer", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                     [10]
                     (right here) ------^
Unexpected token integer
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithUnexpectedDouble) {
  std::string buf(R"([10.5])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token double", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                   [10.5]
                     (right here) ------^
Unexpected token double
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithUnexpectedString) {
  std::string buf(R"(["value"])");

  Parser<SArray<Value<bool>>> parser;

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token string", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                ["value"]
                     (right here) ------^
Unexpected token string
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithElementCallbackError) {
  std::string buf(R"([true, false])");

  auto elementCb = [&](const bool &) { return false; };

  Parser<SArray<Value<bool>>> parser(elementCb);

  ASSERT_FALSE(parser.parse(buf));
  ASSERT_FALSE(parser.parser().isSet());

  ASSERT_EQ("Callback returned false", parser.getError());
  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                   [true, false]
                     (right here) ------^
Callback returned false
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithCallback) {
  std::string buf(R"([true, false])");
  std::vector<bool> values;

  auto arrayCb = [&](const std::vector<bool> &value) {
    values = value;
    return true;
  };

  Parser<SArray<Value<bool>>> parser(arrayCb);

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(true, parser.parser().get()[0]);
  ASSERT_EQ(false, parser.parser().get()[1]);

  ASSERT_EQ(2, values.size());
  ASSERT_EQ(true, values[0]);
  ASSERT_EQ(false, values[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, SArrayWithCallbackError) {
  std::string buf(R"([true, false])");

  auto arrayCb = [&](const std::vector<bool> &) { return false; };

  Parser<SArray<Value<bool>>> parser(arrayCb);

  ASSERT_FALSE(parser.parse(buf));
  ASSERT_TRUE(parser.parser().isSet());

  ASSERT_EQ("Callback returned false", parser.getError());
  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                           [true, false]
                     (right here) ------^
Callback returned false
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithArgsStruct) {
  std::string buf(R"([true, false])");
  std::vector<bool> values;

  auto elementCb = [&](const bool &value) {
    values.push_back(value);
    return true;
  };

  using SArrayParser = SArray<Value<bool>>;

  SArrayParser::Args array_args = {elementCb};

  Parser<SArrayParser> parser(array_args);

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(true, parser.parser().get()[0]);
  ASSERT_EQ(false, parser.parser().get()[1]);

  ASSERT_EQ(2, values.size());
  ASSERT_EQ(true, values[0]);
  ASSERT_EQ(false, values[1]);

  ASSERT_TRUE(parser.parser().isSet());
}

TEST(SArray, UnexpectedSObject) {
  std::string buf(
      R"({"key": "value"})");

  Parser<SArray<SObject<Value<std::string>>>> parser("key");

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected token map start", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                       {"key": "value"}
                     (right here) ------^
Unexpected token map start
)",
      parser.getError(true));
}

TEST(SArray, SArrayWithUnexpectedSObject) {
  std::string buf(
      R"([{"key2": "value"}])");

  Parser<SArray<SObject<Value<std::string>>>> parser("key");

  ASSERT_FALSE(parser.parse(buf));

  ASSERT_FALSE(parser.parser().isSet());
  ASSERT_EQ("Unexpected field key2", parser.getError());

  ASSERT_EQ(
      R"(parse error: client cancelled parse via callback return value
                                [{"key2": "value"}]
                     (right here) ------^
Unexpected field key2
)",
      parser.getError(true));
}

TEST(SArray, SArrayOfSCustomObjects) {
  std::string buf(
      R"([{"key": "value", "key2": 10}, {"key": "value2", "key2": 20}])");

  struct ObjectStruct {
    std::string field1;
    int64_t field2;
  };

  using ObjectParser =
      SObject<ObjectStruct, Value<std::string>, Value<int64_t>>;

  auto objectCb = [&](ObjectParser &parser, ObjectStruct &value) {
    value = {parser.get<0>().pop(), parser.get<1>().pop()};
    return true;
  };

  Parser<SArray<ObjectParser>> parser({{"key", "key2"}, objectCb});

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ("value", parser.parser().get()[0].field1);
  ASSERT_EQ(10, parser.parser().get()[0].field2);
  ASSERT_EQ("value2", parser.parser().get()[1].field1);
  ASSERT_EQ(20, parser.parser().get()[1].field2);
}

TEST(SArray, SArrayOfSAutoObjects) {
  std::string buf(
      R"([{"key": "value", "key2": 10}, {"key": "value2", "key2": 20}])");

  using ObjectParser = SObject<Value<std::string>, Value<int64_t>>;

  Parser<SArray<ObjectParser>> parser({"key", "key2"});

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ("value", std::get<0>(parser.parser().get()[0]));
  ASSERT_EQ(10, std::get<1>(parser.parser().get()[0]));
  ASSERT_EQ("value2", std::get<0>(parser.parser().get()[1]));
  ASSERT_EQ(20, std::get<1>(parser.parser().get()[1]));
}

TEST(SArray, SArrayOfSAutoObjectsWithDefaultValues) {
  std::string buf(
      R"([{"key": "value"}, {"key2": 20}])");

  using ObjectParser = SObject<Value<std::string>, Value<int64_t>>;

  Parser<SArray<ObjectParser>> parser({{"key", "key2"}, {"test", 10}});

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ("value", std::get<0>(parser.parser().get()[0]));
  ASSERT_EQ(10, std::get<1>(parser.parser().get()[0]));
  ASSERT_EQ("test", std::get<0>(parser.parser().get()[1]));
  ASSERT_EQ(20, std::get<1>(parser.parser().get()[1]));
}

TEST(SArray, SArrayOfSArrays) {
  std::string buf(R"([[true, true], [false, false]])");

  Parser<SArray<SArray<Value<bool>>>> parser;

  ASSERT_TRUE(parser.parse(buf));
  ASSERT_TRUE(parser.finish());

  ASSERT_EQ(2, parser.parser().get().size());
  ASSERT_EQ(2, parser.parser().get()[0].size());
  ASSERT_EQ(true, parser.parser().get()[0][0]);
  ASSERT_EQ(true, parser.parser().get()[0][1]);

  ASSERT_EQ(2, parser.parser().get()[1].size());
  ASSERT_EQ(false, parser.parser().get()[1][0]);
  ASSERT_EQ(false, parser.parser().get()[1][1]);
}
<|endoftext|>


<commit_before>#include <list>
#include <string>
#include <thread>
#include <atomic>

#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include "task_mock.hpp"

#include "task_deque.hpp"

using namespace staccato;
using namespace staccato::internal;

TEST(ctor, creating_and_deleteing) {
	auto d = new task_deque(4);
	delete d;
}

TEST(put_take, without_resize) {
	size_t size = 5;
	size_t ntasks = 1 << (size - 1);

	auto deque = new task_deque(size);

	std::vector<task *> tasks;

	for (size_t i = 0; i < ntasks; ++i) {
		auto t = new task_mock();
		tasks.push_back(t);
		deque->put(t);
	}

	for (size_t i = 0; i < ntasks; ++i) {
		auto t1 = deque->take();
		auto t2 = tasks.back();
		ASSERT_EQ(t1, t2);
		tasks.pop_back();

		delete t1;
	}

	delete deque;
}

TEST(put_take, with_resize) {
	size_t size = 5;
	size_t ntasks = 1 << (size + 2);

	auto deque = new task_deque(size);

	std::vector<task *> tasks;

	for (size_t i = 0; i < ntasks; ++i) {
		auto t = new task_mock();
		tasks.push_back(t);
		deque->put(t);
	}

	for (size_t i = 0; i < ntasks; ++i) {
		auto t1 = deque->take();
		auto t2 = tasks.back();
		ASSERT_EQ(t1, t2);
		tasks.pop_back();

		delete t1;
	}

	delete deque;
}

TEST(put_steal, sequential) {
	size_t size = 5;
	size_t ntasks = 1 << (size + 2);

	auto deque = new task_deque(size);

	std::vector<task *> tasks;

	for (size_t i = 0; i < ntasks; ++i) {
		auto t = new task_mock();
		tasks.push_back(t);
		deque->put(t);
	}

	std::reverse(tasks.begin(), tasks.end());

	for (size_t i = 0; i < ntasks; ++i) {
		auto t1 = deque->steal();
		auto t2 = tasks.back();
		ASSERT_EQ(t1, t2);
		tasks.pop_back();
		delete t1;
	}

	delete deque;
}

TEST(put_steal, multiple_steals) {
	size_t size = 12;
	size_t ntasks = 1 << (size + 2);

	auto deque = new task_deque(size);

	std::vector<task *> tasks;

	for (size_t i = 0; i < ntasks; ++i) {
		auto t = new task_mock();
		tasks.push_back(t);
		deque->put(t);
	}

	std::atomic_size_t nready(0);
	static const size_t nthreads = 4;
	std::vector<task *> stolen[nthreads];
	std::thread *threads[nthreads];

	auto steal = [&](size_t id, size_t nwait) {
		nready++;

		while (nready != nwait)
			std::this_thread::yield();

		while (true) {
			auto t = deque->steal();
			if (!t)
				break;

			stolen[id].push_back(t);
		}
	};

	for (size_t i = 0; i < nthreads; ++i) {
		threads[i] = new std::thread(steal, i, nthreads);
	}

	for (size_t i = 0; i < nthreads; ++i) {
		threads[i]->join();
	}

	for (size_t i = 0; i < nthreads; ++i) {
		for (auto t : stolen[i]) {
			auto found = std::find(tasks.begin(), tasks.end(), t);
			ASSERT_TRUE(found != tasks.end());
			tasks.erase(found);

			delete t;
		}
	}

	EXPECT_TRUE(tasks.empty());

	delete deque;
}

TEST(put_steal_take, multiple_steals) {
	size_t size = 12;
	size_t ntasks = 1 << (size + 2);

	auto deque = new task_deque(size);

	std::vector<task *> tasks;

	for (size_t i = 0; i < ntasks; ++i) {
		auto t = new task_mock();
		tasks.push_back(t);
		deque->put(t);
	}

	std::atomic_size_t nready(0);
	static const size_t nthreads = 4 + 1;
	std::vector<task *> stolen[nthreads];
	std::vector<task *> taken;
	std::thread *threads[nthreads];

	auto steal = [&](size_t id, size_t nwait, bool steal) {
		nready++;

		while (nready != nwait)
			std::this_thread::yield();

		while (true) {
			auto t = steal ? deque->steal() : deque->take();
			if (!t)
				break;

			stolen[id].push_back(t);
		}
	};

	for (size_t i = 0; i < nthreads; ++i) {
		threads[i] = new std::thread(steal, i, nthreads, i != 0);
	}

	for (size_t i = 0; i < nthreads; ++i) {
		threads[i]->join();
	}

	for (size_t i = 0; i < nthreads; ++i) {
		for (auto t : stolen[i]) {
			auto found = std::find(tasks.begin(), tasks.end(), t);
			ASSERT_TRUE(found != tasks.end());
			tasks.erase(found);

			delete t;
		}
	}

	EXPECT_TRUE(tasks.empty());

	delete deque;
}
<commit_msg>fix deque unit tests<commit_after>#include <list>
#include <string>
#include <thread>
#include <atomic>
#include <chrono>

#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include "task_mock.hpp"

#include "task_deque.hpp"

using namespace staccato;
using namespace staccato::internal;

TEST(ctor, creating_and_deleteing) {
	auto d = new task_deque(4);
	delete d;
}

TEST(take, without_resize) {
	size_t size = 5;
	size_t ntasks = 1 << (size - 1);

	auto deque = new task_deque(size);

	std::vector<task *> tasks;

	for (size_t i = 0; i < ntasks; ++i) {
		auto t = new task_mock();
		tasks.push_back(t);
		deque->put(t);
	}

	for (size_t i = 0; i < ntasks; ++i) {
		auto t1 = deque->take();
		auto t2 = tasks.back();
		ASSERT_EQ(t1, t2);
		tasks.pop_back();

		delete t1;
	}

	delete deque;
}

TEST(take, with_resize) {
	size_t size = 5;
	size_t ntasks = 1 << (size + 2);

	auto deque = new task_deque(size);

	std::vector<task *> tasks;

	for (size_t i = 0; i < ntasks; ++i) {
		auto t = new task_mock();
		tasks.push_back(t);
		deque->put(t);
	}

	for (size_t i = 0; i < ntasks; ++i) {
		auto t1 = deque->take();
		auto t2 = tasks.back();
		ASSERT_EQ(t1, t2);
		tasks.pop_back();

		delete t1;
	}

	delete deque;
}

TEST(steal, sequential) {
	size_t size = 5;
	size_t ntasks = 1 << (size + 2);

	auto deque = new task_deque(size);

	std::vector<task *> tasks;

	for (size_t i = 0; i < ntasks; ++i) {
		auto t = new task_mock();
		tasks.push_back(t);
		deque->put(t);
	}

	std::reverse(tasks.begin(), tasks.end());

	for (size_t i = 0; i < ntasks; ++i) {
		auto t1 = deque->steal();
		auto t2 = tasks.back();
		ASSERT_EQ(t1, t2);
		tasks.pop_back();
		delete t1;
	}

	delete deque;
}

TEST(steal, concurrent_steal) {
	size_t size = 12;
	size_t ntasks = 1 << (size + 2);

	auto deque = new task_deque(size);

	std::vector<task *> tasks;

	for (size_t i = 0; i < ntasks; ++i) {
		auto t = new task_mock();
		tasks.push_back(t);
		deque->put(t);
	}

	std::atomic_size_t nready(0);
	std::atomic_bool stop(false);
	static const size_t nthreads = 4;
	std::vector<task *> stolen[nthreads];
	std::thread threads[nthreads];

	auto steal = [&](size_t id) {
		nready++;

		while (nready != nthreads)
			std::this_thread::yield();

		while (!stop) {
			auto t = deque->steal();
			if (t)
				stolen[id].push_back(t);
		}
	};

	for (size_t i = 0; i < nthreads; ++i) {
		threads[i] = std::thread(steal, i);
	}

	std::this_thread::sleep_for(std::chrono::seconds(1));
	stop = true;

	for (size_t i = 0; i < nthreads; ++i) {
		threads[i].join();
		std::cerr << "thread #" << i << " tasks: " << stolen[i].size() << "\n";
	}

	for (size_t i = 0; i < nthreads; ++i) {
		for (auto t : stolen[i]) {
			auto found = std::find(tasks.begin(), tasks.end(), t);
			ASSERT_TRUE(found != tasks.end());
			tasks.erase(found);

			delete t;
		}
	}

	EXPECT_TRUE(tasks.empty());

	delete deque;
}

TEST(steal_take, concurrent_steal_and_take) {
	size_t size = 12;
	size_t ntasks = 1 << (size + 2);

	auto deque = new task_deque(size);

	std::vector<task *> tasks;

	for (size_t i = 0; i < ntasks; ++i) {
		auto t = new task_mock();
		tasks.push_back(t);
		deque->put(t);
	}

	std::atomic_size_t nready(0);
	std::atomic_bool stop(false);
	static const size_t nthreads = 3 + 1;
	std::vector<task *> stolen[nthreads];
	std::vector<task *> taken;
	std::thread threads[nthreads];

	auto steal = [&](size_t id, bool steal) {
		nready++;

		while (nready != nthreads)
			std::this_thread::yield();

		while (!stop) {
			auto t = steal ? deque->steal() : deque->take();
			if (t)
				stolen[id].push_back(t);
		}
	};

	for (size_t i = 0; i < nthreads; ++i) {
		threads[i] = std::thread(steal, i, i != 0);
	}

	std::this_thread::sleep_for(std::chrono::seconds(1));
	stop = true;

	for (size_t i = 0; i < nthreads; ++i) {
		threads[i].join();
		std::cerr << "thread #" << i << " tasks: " << stolen[i].size() << "\n";
	}

	for (size_t i = 0; i < nthreads; ++i) {
		for (auto t : stolen[i]) {
			auto found = std::find(tasks.begin(), tasks.end(), t);
			ASSERT_TRUE(found != tasks.end());
			tasks.erase(found);

			delete t;
		}
	}

	EXPECT_TRUE(tasks.empty());

	delete deque;
}

TEST(put_steal_take, concurrent_steal_take_and_put) {
	size_t size = 12;
	std::atomic_size_t ntasks(1 << (size + 2));

	auto deque = new task_deque(size);

	std::vector<task *> tasks;

	std::atomic_size_t nready(0);
	std::atomic_bool stop(false);
	static const size_t nthreads = 3 + 1;
	std::vector<task *> stolen[nthreads];
	std::vector<task *> taken;
	std::thread threads[nthreads];

	auto steal = [&](size_t id, bool steal) {
		nready++;

		while (nready != nthreads)
			std::this_thread::yield();

		while (!stop) {
			if (steal) {
				auto t = deque->steal();
				if (t)
					stolen[id].push_back(t);
			} else {
				if (ntasks > 0) {
					auto new_task = new task_mock();
					tasks.push_back(new_task);
					deque->put(new_task);
					ntasks--;
				}

				// std::this_thread::yield();

				auto t = deque->take();
				if (t)
					stolen[id].push_back(t);
			}
		}
	};

	for (size_t i = 0; i < nthreads; ++i) {
		threads[i] = std::thread(steal, i, i != 0);
	}

	std::this_thread::sleep_for(std::chrono::seconds(1));
	stop = true;

	for (size_t i = 0; i < nthreads; ++i) {
		threads[i].join();
		std::cerr << "thread #" << i << " tasks: " << stolen[i].size() << "\n";
	}

	for (size_t i = 0; i < nthreads; ++i) {
		for (auto t : stolen[i]) {
			auto found = std::find(tasks.begin(), tasks.end(), t);
			ASSERT_TRUE(found != tasks.end());
			tasks.erase(found);

			delete t;
		}
	}

	EXPECT_TRUE(tasks.empty());

	delete deque;
}
<|endoftext|>