<commit_before>#include <vector>
#include "linalg/util.h"
#include "render/color.h"
#include "textures/texture_mapping.h"
#include "textures/mipmap.h"

MipMap::MipLevel::MipLevel(int width, int height, const std::vector<uint8_t> &texels)
	: width(width), height(height), texels(texels)
{}

const int MipMap::WEIGHT_LUT_SIZE;
std::array<float, MipMap::WEIGHT_LUT_SIZE> MipMap::weight_table = {-1};

MipMap::MipMap(){}
MipMap::MipMap(const std::vector<uint8_t> &texels, int width, int height, int ncomp, WRAP_MODE wrap_mode)
	: wrap_mode(wrap_mode), width(width), height(height), ncomp(ncomp)
{
	if (weight_table[0] == -1){
		init_weight_table();
	}
	//TODO: Handle non-pow2 image dimensions
	int n_levels = 1 + static_cast<int>(log_2(std::max(width, height)));
	pyramid.reserve(n_levels);
	pyramid.emplace_back(width, height, texels);
	for (int i = 1; i < n_levels; ++i){
		int w = std::max(1, pyramid[i - 1].width / 2);
		int h = std::max(1, pyramid[i - 1].height / 2);
		pyramid.emplace_back(w, h);
		std::vector<uint8_t> &img = pyramid.back().texels;
		img.resize(w * h * ncomp);
		for (int t = 0; t < h; ++t){
			for (int s = 0; s < w; ++s){
				Colorf cf = 0.25 * (texel(i - 1, 2 * s, 2 * t) + texel(i - 1, 2 * s + 1, 2 * t)
					+ texel(i - 1, 2 * s, 2 * t + 1) + texel(i - 1, 2 * s + 1, 2 * t + 1));
				cf.normalize();
				Color24 c{cf};
				//Write the number of components that we have only
				for (int j = 0; j < ncomp; ++j){
					img[t * w * ncomp + s * ncomp + j] = c[j];
				}
			}
		}
	}
}
Colorf MipMap::texel(int level, int s, int t) const {
	if (width == 0){
		return Colorf{0};
	}
	const MipLevel &mlvl = pyramid[level];
	switch (wrap_mode){
		case WRAP_MODE::REPEAT:
			s = std::abs(s) % mlvl.width;
			t = std::abs(t) % mlvl.height;
			break;
		case WRAP_MODE::CLAMP:
			s = clamp(s, 0, mlvl.width - 1);
			t = clamp(t, 0, mlvl.height - 1);
			break;
		case WRAP_MODE::BLACK:
			if (s < 0 || s >= mlvl.width || t < 0 || t >= mlvl.height){
				return Colorf{0};
			}
	}
	static const float inv = 1.f / 255.f;
	switch (ncomp){
		case 1:
			return Colorf{mlvl.texels[t * mlvl.width + s] * inv};
		case 2:
			return Colorf{mlvl.texels[t * mlvl.width * ncomp + s * ncomp] * inv,
				mlvl.texels[t * mlvl.width * ncomp + s * ncomp + 1] * inv, 0};
		default:
			return Colorf{mlvl.texels[t * mlvl.width * ncomp + s * ncomp] * inv,
				mlvl.texels[t * mlvl.width * ncomp + s * ncomp + 1] * inv,
				mlvl.texels[t * mlvl.width * ncomp + s * ncomp + 2] * inv};
	}
}
Colorf MipMap::sample(const TextureSample &samp) const {
#ifdef TEX_TRILINEAR
	return sample_trilinear(samp, 2 * std::max(std::max(std::abs(samp.ds_dx), std::abs(samp.dt_dx)),
		std::max(std::abs(samp.ds_dy), std::abs(samp.dt_dy))));
#else
	return sample_ewa(samp, {samp.ds_dx, samp.ds_dy}, {samp.dt_dx, samp.dt_dy});
#endif
}
Colorf MipMap::sample_trilinear(const TextureSample &samp, float w) const {
	float level = pyramid.size() - 1 + std::log2(std::max(w, 1e-8f));
	if (level < 0){
		return triangle_filter(0, samp.s, samp.t);
	}
	if (level >= pyramid.size() - 1){
		return texel(pyramid.size() - 1, 0, 0);
	}
	//Find integer coord of the level and the percentage we're in each
	int lvl = static_cast<int>(level);
	float d = level - lvl;
	return (1 - d) * triangle_filter(lvl, samp.s, samp.t)
		+ d * triangle_filter(lvl + 1, samp.s, samp.t);
}
Colorf MipMap::triangle_filter(int lvl, float s, float t) const {
	lvl = clamp(lvl, 0, static_cast<int>(pyramid.size() - 1));
	s = s * pyramid[lvl].width - 0.5f;
	t = t * pyramid[lvl].height - 0.5f;
	int si = static_cast<int>(s);
	int ti = static_cast<int>(t);
	float ds = s - si;
	float dt = t - ti;
	return (1 - ds) * (1 - dt) * texel(lvl, si, ti)
		+ (1 - ds) * dt * texel(lvl, si, ti + 1)
		+ ds * (1 - dt) * texel(lvl, si + 1, ti)
		+ ds * dt * texel(lvl, si + 1, ti + 1);
}
Colorf MipMap::sample_ewa(const TextureSample &samp, std::array<float, 2> ds, std::array<float, 2> dt) const {
	//Compute the ellipse's major and minor axes, we pick the major axis to be (ds[0], dt[0]) so
	//swap if this wouldn't be the case
	if (ds[0] * ds[0] + dt[0] * dt[0] < ds[1] * ds[1] + dt[1] * dt[1]){
		std::swap(ds[0], ds[1]);
		std::swap(dt[0], dt[1]);
	}
	float major_len = std::sqrt(ds[0] * ds[0] + dt[0] * dt[0]);
	float minor_len = std::sqrt(ds[1] * ds[1] + dt[1] * dt[1]);

	//Clamp the ellipse eccentricity if it's too anisotropic by increasing the minor axis
	static const float max_anisotropy = 8;
	if (minor_len > 0 && minor_len * max_anisotropy < major_len){
		float s = major_len / (minor_len * max_anisotropy);
		ds[1] *= s;
		dt[1] *= s;
		minor_len *= s;
	}
	if (minor_len == 0){
		return triangle_filter(0, samp.s, samp.t);
	}

	float level = pyramid.size() - 1 + std::log2(minor_len);
	if (level >= pyramid.size()){
		return texel(pyramid.size() - 1, 0, 0);
	}
	int lvl = static_cast<int>(level);
	float d = level - lvl;
	return (1 - d) * ewa_filter(lvl, samp.s, samp.t, ds, dt)
		+ d * ewa_filter(lvl + 1, samp.s, samp.t, ds, dt);
}
Colorf MipMap::ewa_filter(int lvl, float s, float t, std::array<float, 2> ds, std::array<float, 2> dt) const {
	lvl = clamp(lvl, 0, static_cast<int>(pyramid.size() - 1));
	s = s * pyramid[lvl].width - 0.5f;
	t = t * pyramid[lvl].height - 0.5f;
	for (size_t i = 0; i < ds.size(); ++i){
		ds[i] *= pyramid[lvl].width;
		dt[i] *= pyramid[lvl].height;
	}
	float a = dt[0] * dt[0] + dt[1] * dt[1] + 1;
	float b = -2 * (ds[0] * dt[0] + ds[1] * dt[1]);
	float c = ds[0] * ds[0] + ds[1] * ds[1] + 1;
	float inv_f = 1 / (a * c - b * b * 0.25f);
	a *= inv_f;
	b *= inv_f;
	c *= inv_f;

	//Compute the ellipse's AABB so we can find which texels to loop over and filter
	float det = -b * b + 4 * a * c;
	float inv_det = 1 / det;
	float u_sqrt = std::sqrt(det * c);
	float v_sqrt = std::sqrt(det * a);
	std::array<int, 2> s_bound{
		static_cast<int>(s - 2 * inv_det * u_sqrt),
		static_cast<int>(s + 2 * inv_det * u_sqrt)
	};
	std::array<int, 2> t_bound{
		static_cast<int>(t - 2 * inv_det * v_sqrt),
		static_cast<int>(t + 2 * inv_det * v_sqrt)
	};

	Colorf color;
	float weight_sum = 0;
	for (int it = t_bound[0]; it <= t_bound[1]; ++it){
		float t_pos = it - t;
		for (int is = s_bound[0]; is <= s_bound[1]; ++is){
			float s_pos = is - s;
			//Find the position of this texel relative to the ellipse and see if it's inside
			float r_sqr = a * s_pos * s_pos + b * s_pos * t_pos + c * t_pos * t_pos;
			if (r_sqr < 1){
				float w = weight_table[std::min(static_cast<int>(r_sqr * WEIGHT_LUT_SIZE), WEIGHT_LUT_SIZE - 1)];
				color += texel(lvl, is, it) * w;
				weight_sum += w;
			}
		}
	}
	return color / weight_sum;
}
void MipMap::init_weight_table(){
	for (int i = 0; i < WEIGHT_LUT_SIZE; ++i){
		float r_sqr = static_cast<float>(i) / (WEIGHT_LUT_SIZE - 1.f);
		weight_table[i] = std::exp(-2.f * r_sqr) - std::exp(-2.f);
	}
}

<commit_msg>Assert out if non-pow2 texture passed<commit_after>#include <cassert>
#include <vector>
#include "linalg/util.h"
#include "render/color.h"
#include "textures/texture_mapping.h"
#include "textures/mipmap.h"

MipMap::MipLevel::MipLevel(int width, int height, const std::vector<uint8_t> &texels)
	: width(width), height(height), texels(texels)
{}

const int MipMap::WEIGHT_LUT_SIZE;
std::array<float, MipMap::WEIGHT_LUT_SIZE> MipMap::weight_table = {-1};

MipMap::MipMap(){}
MipMap::MipMap(const std::vector<uint8_t> &texels, int width, int height, int ncomp, WRAP_MODE wrap_mode)
	: wrap_mode(wrap_mode), width(width), height(height), ncomp(ncomp)
{
	if (weight_table[0] == -1){
		init_weight_table();
	}
	//We only support power of two textures at the moment
	if (!(width && !(width & (width - 1))) || !(height && !(height & (height - 1)))){
		std::cout << "MipMap Error: Only powers of two are supported at the moment\n";
		assert("Non power of two texture");
	}
	int n_levels = 1 + static_cast<int>(log_2(std::max(width, height)));
	pyramid.reserve(n_levels);
	pyramid.emplace_back(width, height, texels);
	for (int i = 1; i < n_levels; ++i){
		int w = std::max(1, pyramid[i - 1].width / 2);
		int h = std::max(1, pyramid[i - 1].height / 2);
		pyramid.emplace_back(w, h);
		std::vector<uint8_t> &img = pyramid.back().texels;
		img.resize(w * h * ncomp);
		for (int t = 0; t < h; ++t){
			for (int s = 0; s < w; ++s){
				Colorf cf = 0.25 * (texel(i - 1, 2 * s, 2 * t) + texel(i - 1, 2 * s + 1, 2 * t)
					+ texel(i - 1, 2 * s, 2 * t + 1) + texel(i - 1, 2 * s + 1, 2 * t + 1));
				cf.normalize();
				Color24 c{cf};
				//Write the number of components that we have only
				for (int j = 0; j < ncomp; ++j){
					img[t * w * ncomp + s * ncomp + j] = c[j];
				}
			}
		}
	}
}
Colorf MipMap::texel(int level, int s, int t) const {
	if (width == 0){
		return Colorf{0};
	}
	const MipLevel &mlvl = pyramid[level];
	switch (wrap_mode){
		case WRAP_MODE::REPEAT:
			s = std::abs(s) % mlvl.width;
			t = std::abs(t) % mlvl.height;
			break;
		case WRAP_MODE::CLAMP:
			s = clamp(s, 0, mlvl.width - 1);
			t = clamp(t, 0, mlvl.height - 1);
			break;
		case WRAP_MODE::BLACK:
			if (s < 0 || s >= mlvl.width || t < 0 || t >= mlvl.height){
				return Colorf{0};
			}
	}
	static const float inv = 1.f / 255.f;
	switch (ncomp){
		case 1:
			return Colorf{mlvl.texels[t * mlvl.width + s] * inv};
		case 2:
			return Colorf{mlvl.texels[t * mlvl.width * ncomp + s * ncomp] * inv,
				mlvl.texels[t * mlvl.width * ncomp + s * ncomp + 1] * inv, 0};
		default:
			return Colorf{mlvl.texels[t * mlvl.width * ncomp + s * ncomp] * inv,
				mlvl.texels[t * mlvl.width * ncomp + s * ncomp + 1] * inv,
				mlvl.texels[t * mlvl.width * ncomp + s * ncomp + 2] * inv};
	}
}
Colorf MipMap::sample(const TextureSample &samp) const {
#ifdef TEX_TRILINEAR
	return sample_trilinear(samp, 2 * std::max(std::max(std::abs(samp.ds_dx), std::abs(samp.dt_dx)),
		std::max(std::abs(samp.ds_dy), std::abs(samp.dt_dy))));
#else
	return sample_ewa(samp, {samp.ds_dx, samp.ds_dy}, {samp.dt_dx, samp.dt_dy});
#endif
}
Colorf MipMap::sample_trilinear(const TextureSample &samp, float w) const {
	float level = pyramid.size() - 1 + std::log2(std::max(w, 1e-8f));
	if (level < 0){
		return triangle_filter(0, samp.s, samp.t);
	}
	if (level >= pyramid.size() - 1){
		return texel(pyramid.size() - 1, 0, 0);
	}
	//Find integer coord of the level and the percentage we're in each
	int lvl = static_cast<int>(level);
	float d = level - lvl;
	return (1 - d) * triangle_filter(lvl, samp.s, samp.t)
		+ d * triangle_filter(lvl + 1, samp.s, samp.t);
}
Colorf MipMap::triangle_filter(int lvl, float s, float t) const {
	lvl = clamp(lvl, 0, static_cast<int>(pyramid.size() - 1));
	s = s * pyramid[lvl].width - 0.5f;
	t = t * pyramid[lvl].height - 0.5f;
	int si = static_cast<int>(s);
	int ti = static_cast<int>(t);
	float ds = s - si;
	float dt = t - ti;
	return (1 - ds) * (1 - dt) * texel(lvl, si, ti)
		+ (1 - ds) * dt * texel(lvl, si, ti + 1)
		+ ds * (1 - dt) * texel(lvl, si + 1, ti)
		+ ds * dt * texel(lvl, si + 1, ti + 1);
}
Colorf MipMap::sample_ewa(const TextureSample &samp, std::array<float, 2> ds, std::array<float, 2> dt) const {
	//Compute the ellipse's major and minor axes, we pick the major axis to be (ds[0], dt[0]) so
	//swap if this wouldn't be the case
	if (ds[0] * ds[0] + dt[0] * dt[0] < ds[1] * ds[1] + dt[1] * dt[1]){
		std::swap(ds[0], ds[1]);
		std::swap(dt[0], dt[1]);
	}
	float major_len = std::sqrt(ds[0] * ds[0] + dt[0] * dt[0]);
	float minor_len = std::sqrt(ds[1] * ds[1] + dt[1] * dt[1]);

	//Clamp the ellipse eccentricity if it's too anisotropic by increasing the minor axis
	static const float max_anisotropy = 8;
	if (minor_len > 0 && minor_len * max_anisotropy < major_len){
		float s = major_len / (minor_len * max_anisotropy);
		ds[1] *= s;
		dt[1] *= s;
		minor_len *= s;
	}
	if (minor_len == 0){
		return triangle_filter(0, samp.s, samp.t);
	}

	float level = pyramid.size() - 1 + std::log2(minor_len);
	if (level >= pyramid.size()){
		return texel(pyramid.size() - 1, 0, 0);
	}
	int lvl = static_cast<int>(level);
	float d = level - lvl;
	return (1 - d) * ewa_filter(lvl, samp.s, samp.t, ds, dt)
		+ d * ewa_filter(lvl + 1, samp.s, samp.t, ds, dt);
}
Colorf MipMap::ewa_filter(int lvl, float s, float t, std::array<float, 2> ds, std::array<float, 2> dt) const {
	lvl = clamp(lvl, 0, static_cast<int>(pyramid.size() - 1));
	s = s * pyramid[lvl].width - 0.5f;
	t = t * pyramid[lvl].height - 0.5f;
	for (size_t i = 0; i < ds.size(); ++i){
		ds[i] *= pyramid[lvl].width;
		dt[i] *= pyramid[lvl].height;
	}
	float a = dt[0] * dt[0] + dt[1] * dt[1] + 1;
	float b = -2 * (ds[0] * dt[0] + ds[1] * dt[1]);
	float c = ds[0] * ds[0] + ds[1] * ds[1] + 1;
	float inv_f = 1 / (a * c - b * b * 0.25f);
	a *= inv_f;
	b *= inv_f;
	c *= inv_f;

	//Compute the ellipse's AABB so we can find which texels to loop over and filter
	float det = -b * b + 4 * a * c;
	float inv_det = 1 / det;
	float u_sqrt = std::sqrt(det * c);
	float v_sqrt = std::sqrt(det * a);
	std::array<int, 2> s_bound{
		static_cast<int>(s - 2 * inv_det * u_sqrt),
		static_cast<int>(s + 2 * inv_det * u_sqrt)
	};
	std::array<int, 2> t_bound{
		static_cast<int>(t - 2 * inv_det * v_sqrt),
		static_cast<int>(t + 2 * inv_det * v_sqrt)
	};

	Colorf color;
	float weight_sum = 0;
	for (int it = t_bound[0]; it <= t_bound[1]; ++it){
		float t_pos = it - t;
		for (int is = s_bound[0]; is <= s_bound[1]; ++is){
			float s_pos = is - s;
			//Find the position of this texel relative to the ellipse and see if it's inside
			float r_sqr = a * s_pos * s_pos + b * s_pos * t_pos + c * t_pos * t_pos;
			if (r_sqr < 1){
				float w = weight_table[std::min(static_cast<int>(r_sqr * WEIGHT_LUT_SIZE), WEIGHT_LUT_SIZE - 1)];
				color += texel(lvl, is, it) * w;
				weight_sum += w;
			}
		}
	}
	return color / weight_sum;
}
void MipMap::init_weight_table(){
	for (int i = 0; i < WEIGHT_LUT_SIZE; ++i){
		float r_sqr = static_cast<float>(i) / (WEIGHT_LUT_SIZE - 1.f);
		weight_table[i] = std::exp(-2.f * r_sqr) - std::exp(-2.f);
	}
}

<|endoftext|>


<commit_before>#include "thread_renderer.h"
#include "audio_nodes.h"
#include "recorder_node.h"

#include <cinder/app/App.h>

namespace cieq {

ThreadRenderer::ThreadRenderer(AudioNodes& nodes, std::size_t frames_per_surface, std::size_t fft_size)
	: mFramesPerSurface(frames_per_surface)
	, mFftSize(fft_size)
	, mAudioNodes(nodes)
	, mLastPopPos(0)
	, mLastSurfaceLength(0)
	, mTotalSurfacesLength(0)
{
	mNumSurfaces = mAudioNodes.getBufferRecorderNode()->getMaxPossiblePops() / mFramesPerSurface;
	if (mAudioNodes.getBufferRecorderNode()->getMaxPossiblePops() % mFramesPerSurface != 0)
		mNumSurfaces += 1;

	mSurfaceTexturePool.resize(mNumSurfaces);

	mLastSurfaceLength = calculateLastSurfaceLength();
	mTotalSurfacesLength = calculateTotalSurfacesLength();
}

void ThreadRenderer::update()
{
	for (container_pair& pair : mSurfaceTexturePool)
	{
		if (pair.first && pair.first->allRowsTouched())
		{
			if (pair.second)
				pair.second->update(*pair.first);
			else
				pair.second = ci::gl::Texture::create(*pair.first);

			pair.first.reset();
		}
	}

	// loop recording endlessly
	if (!mAudioNodes.getBufferRecorderNode()->isRecording())
	{
		/*mAudioNodes.getBufferRecorderNode()->start();
		mAudioNodes.getBufferRecorderNode()->reset();
		cleanSurfaces();*/
	}
}

void ThreadRenderer::draw()
{
	ci::gl::pushMatrices();
	ci::gl::clear(ci::Color::black());
	ci::gl::rotate(90.0f); //rotate scene 90 degrees

	// just a convenience
	auto _recorder_node = mAudioNodes.getBufferRecorderNode();

	const float _y_scale = 2.0f;
	const float _x_scale = static_cast<float>(ci::app::getWindowWidth()) / mTotalSurfacesLength;

	// get current record position
	const int _current_write_pos = mLastPopPos;
	// percentage of the entire audio done recording
	const float _percentage_done = static_cast<float>(_current_write_pos) / _recorder_node->getNumFrames();
	// get the index of last active surface for drawing
	int _current_last_surface = static_cast<int>(_percentage_done * mNumSurfaces);
	// number of samples that will be empty drawn (last surface)
	// get last thread-reported pop position and divide it over max pops possible
	const float _current_index_in_surface = static_cast<float>(getIndexInSurfaceByQueryPos(mLastPopPos));
	// shift to left for OpenGL (we're moving textures upside-down, therefore we shift one entire surface to right + estimate index in surface + static offset)
	const float _shift_right = static_cast<float>(_current_index_in_surface - mLastSurfaceLength) - (1.0f / _x_scale) * ci::app::getWindowWidth();
	const float _shift_up = (1.0f / _y_scale - 1.0f) * ci::app::getWindowHeight();

	ci::gl::scale(_y_scale, _x_scale);
	ci::gl::translate(_shift_up, _shift_right); //after rotation, moving x is like moving y

	for (int index = _current_last_surface, count = 0;index >= 0; --index, ++count)
	{
		const auto x1 = static_cast<float>(mFftSize);
		const auto y1 = static_cast<float>(count + 1) * mFramesPerSurface;
		const auto x2 = 0.0f;
		const auto y2 = static_cast<float>(count)* mFramesPerSurface;

		ci::Rectf draw_rect(x1, y1, x2, y2);

		if (mSurfaceTexturePool[index].first)
		{
			// draw surface
			ci::gl::draw(*mSurfaceTexturePool[index].first, draw_rect);
		}
		else if (mSurfaceTexturePool[index].second)
		{
			// draw texture
			ci::gl::draw(mSurfaceTexturePool[index].second, draw_rect);
		}
	}

	ci::gl::popMatrices();
}

SpectralSurface& ThreadRenderer::getSurface(int index, int pop_pos)
{
	// if surface does not exist
	if (!mSurfaceTexturePool[index].first)
	{
		// lock and construct the surface
		std::lock_guard<std::mutex> _lock(mPoolLock);
		if (!mSurfaceTexturePool[index].first) //double check
		{
			mSurfaceTexturePool[index].first = std::make_unique<SpectralSurface>(mFftSize, getFramesPerSurface());
		}
	}

	mLastPopPos = pop_pos; //no lock needed, atomic
	return *(mSurfaceTexturePool[index].first);
}

std::size_t ThreadRenderer::getFramesPerSurface() const
{
	return mFramesPerSurface;
}

std::size_t ThreadRenderer::getSurfaceIndexByQueryPos(std::size_t pos) const
{
	const auto pop_index = mAudioNodes.getBufferRecorderNode()->getQueryIndexByQueryPos(pos);
	std::cout << "pop: " << pop_index << " pos: " << pos << " sindex: " << pop_index / getFramesPerSurface() << std::endl;
	return pop_index / getFramesPerSurface();
}

std::size_t ThreadRenderer::getIndexInSurfaceByQueryPos(std::size_t pos) const
{
	const auto pop_index = mAudioNodes.getBufferRecorderNode()->getQueryIndexByQueryPos(pos);
	return pop_index % getFramesPerSurface();
}

void ThreadRenderer::cleanSurfaces()
{
	for (container_pair& pair : mSurfaceTexturePool)
	{
		if (pair.first)
		{
			pair.first.reset();
		}
	}
}

std::size_t ThreadRenderer::calculateLastSurfaceLength() const
{
	const auto _max_pops = mAudioNodes.getBufferRecorderNode()->getMaxPossiblePops();
	const auto _max_num_pops_in_surfaces = mNumSurfaces * mFramesPerSurface;
	const auto _actual_minus_real_diff = _max_num_pops_in_surfaces - _max_pops;
	return getFramesPerSurface() - _actual_minus_real_diff;
}

std::size_t ThreadRenderer::calculateTotalSurfacesLength() const
{
	const auto _max_pops = mAudioNodes.getBufferRecorderNode()->getMaxPossiblePops();
	const auto _max_num_pops_in_surfaces = mNumSurfaces * mFramesPerSurface;
	const auto _actual_minus_real_diff = _max_num_pops_in_surfaces - _max_pops;
	return (mNumSurfaces * mFramesPerSurface) - _actual_minus_real_diff;
}

} //!cieq<commit_msg>Updated rendering to work with FBO instead of direct drawing<commit_after>#include "thread_renderer.h"
#include "audio_nodes.h"
#include "recorder_node.h"

#include <cinder/app/App.h>

namespace cieq {

ThreadRenderer::ThreadRenderer(AudioNodes& nodes, std::size_t frames_per_surface, std::size_t fft_size)
	: mFramesPerSurface(frames_per_surface)
	, mFftSize(fft_size)
	, mAudioNodes(nodes)
	, mLastPopPos(0)
	, mLastSurfaceLength(0)
	, mTotalSurfacesLength(0)
{
	mNumSurfaces = mAudioNodes.getBufferRecorderNode()->getMaxPossiblePops() / mFramesPerSurface;
	if (mAudioNodes.getBufferRecorderNode()->getMaxPossiblePops() % mFramesPerSurface != 0)
		mNumSurfaces += 1;

	mSurfaceTexturePool.resize(mNumSurfaces);

	mLastSurfaceLength = calculateLastSurfaceLength();
	mTotalSurfacesLength = calculateTotalSurfacesLength();

	// I hate APIs with booleans in them :(
	mCompleteAudioFbo = ci::gl::Fbo(
		mTotalSurfacesLength, //width
		mFftSize, //height
		false, // alpha
		true, // color
		false); //depth
}

void ThreadRenderer::update()
{
	for (container_pair& pair : mSurfaceTexturePool)
	{
		if (pair.first && pair.first->allRowsTouched())
		{
			if (pair.second)
			{
				pair.second->update(*pair.first);
			}
			else
			{
				pair.second = ci::gl::Texture::create(*pair.first);
				pair.second->setMinFilter(GL_NEAREST); //disable GPU blur
				pair.second->setMagFilter(GL_NEAREST); //disable GPU blur
			}

			pair.first.reset();
		}
	}
}

void ThreadRenderer::draw()
{
	{ //enter FBO scope
		ci::gl::SaveFramebufferBinding _save_fb;
		mCompleteAudioFbo.bindFramebuffer();

		ci::gl::pushMatrices();
		ci::gl::clear();
		ci::gl::rotate(90.0f); //rotate scene 90 degrees

		// just a convenience
		auto _recorder_node = mAudioNodes.getBufferRecorderNode();

		// get current record position
		const int _current_write_pos = mLastPopPos;
		// percentage of the entire audio done recording
		const float _percentage_done = static_cast<float>(_current_write_pos) / _recorder_node->getNumFrames();
		// get the index of last active surface for drawing
		int _current_last_surface = static_cast<int>(_percentage_done * mNumSurfaces);
		// number of samples that will be empty drawn (last surface)
		// get last thread-reported pop position and divide it over max pops possible
		const float _current_index_in_surface = static_cast<float>(getIndexInSurfaceByQueryPos(mLastPopPos));
		// shift to left for OpenGL (we're moving textures upside-down, therefore we shift one entire surface to right + estimate index in surface + static offset)
		const float _shift_right = static_cast<float>(_current_index_in_surface - mFramesPerSurface) - ci::app::getWindowWidth();

		ci::gl::translate(0.0f, _shift_right); //after rotation, moving x is like moving y

		for (int index = _current_last_surface, count = 0; index >= 0; --index, ++count)
		{
			const auto x1 = static_cast<float>(mFftSize) * ci::app::getWindowHeight() / mCompleteAudioFbo.getHeight();
			const auto y1 = static_cast<float>(count + 1) * mFramesPerSurface;
			const auto x2 = 0.0f;
			const auto y2 = static_cast<float>(count)* mFramesPerSurface;

			ci::Rectf draw_rect(x1, y1, x2, y2);

			if (mSurfaceTexturePool[index].first)
			{
				// draw surface
				ci::gl::draw(*mSurfaceTexturePool[index].first, draw_rect);
			}
			else if (mSurfaceTexturePool[index].second)
			{
				// draw texture
				ci::gl::draw(mSurfaceTexturePool[index].second, draw_rect);
			}
		}

		ci::gl::popMatrices();

		mCompleteAudioFbo.unbindFramebuffer();
	}

	{// enter screen drawing scope
		ci::gl::pushMatrices();

		ci::Rectf _target_rect(mCompleteAudioFbo.getBounds());
		ci::gl::draw(mCompleteAudioFbo.getTexture(), _target_rect);

		ci::gl::popMatrices();
	}
}

SpectralSurface& ThreadRenderer::getSurface(int index, int pop_pos)
{
	// if surface does not exist
	if (!mSurfaceTexturePool[index].first)
	{
		// lock and construct the surface
		std::lock_guard<std::mutex> _lock(mPoolLock);
		if (!mSurfaceTexturePool[index].first) //double check
		{
			if (index != mNumSurfaces - 1)
			{
				mSurfaceTexturePool[index].first = std::make_unique<SpectralSurface>(mFftSize, mLastSurfaceLength);
			}
			else
			{
				mSurfaceTexturePool[index].first = std::make_unique<SpectralSurface>(mFftSize, getFramesPerSurface());
			}
		}
	}

	mLastPopPos = pop_pos; //no lock needed, atomic
	return *(mSurfaceTexturePool[index].first);
}

std::size_t ThreadRenderer::getFramesPerSurface() const
{
	return mFramesPerSurface;
}

std::size_t ThreadRenderer::getSurfaceIndexByQueryPos(std::size_t pos) const
{
	const auto pop_index = mAudioNodes.getBufferRecorderNode()->getQueryIndexByQueryPos(pos);
	return pop_index / getFramesPerSurface();
}

std::size_t ThreadRenderer::getIndexInSurfaceByQueryPos(std::size_t pos) const
{
	const auto pop_index = mAudioNodes.getBufferRecorderNode()->getQueryIndexByQueryPos(pos);
	return pop_index % getFramesPerSurface();
}

void ThreadRenderer::cleanSurfaces()
{
	for (container_pair& pair : mSurfaceTexturePool)
	{
		if (pair.first)
		{
			pair.first.reset();
		}
	}
}

std::size_t ThreadRenderer::calculateLastSurfaceLength() const
{
	const auto _max_pops = mAudioNodes.getBufferRecorderNode()->getMaxPossiblePops();
	const auto _max_num_pops_in_surfaces = mNumSurfaces * mFramesPerSurface;
	const auto _actual_minus_real_diff = _max_num_pops_in_surfaces - _max_pops;
	return getFramesPerSurface() - _actual_minus_real_diff;
}

std::size_t ThreadRenderer::calculateTotalSurfacesLength() const
{
	const auto _max_pops = mAudioNodes.getBufferRecorderNode()->getMaxPossiblePops();
	const auto _max_num_pops_in_surfaces = mNumSurfaces * mFramesPerSurface;
	const auto _actual_minus_real_diff = _max_num_pops_in_surfaces - _max_pops;
	return (mNumSurfaces * mFramesPerSurface) - _actual_minus_real_diff;
}

} //!cieq<|endoftext|>


<commit_before>//
// Created by harold on 30/05/17.
//

#include "im.h"

#include <threepl/ThreeplConnection.h>

int threepl_send_im(PurpleConnection* gc, const char *who, const char *message, PurpleMessageFlags) {
    ThreeplConnection* connection = static_cast<ThreeplConnection*>(purple_connection_get_protocol_data(gc));

    std::basic_string<char> text(message);
    ceema::PayloadText payload;
    payload.m_text = text;

    auto msg_fut = connection->send_message(ceema::client_id::fromString(who), payload);
    if (!msg_fut) {
        return -ENOTCONN;
    } else {
        msg_fut.next([connection, msgtxt = std::basic_string<char>(message)](
                ceema::future<std::unique_ptr<ceema::Message>> fut){
            try {
                auto msg = fut.get();
                const char* who_rcpt = msg->recipient().toString().c_str();
                PurpleConversation* conv = purple_find_conversation_with_account(
                        PURPLE_CONV_TYPE_IM, who_rcpt, connection->acct());
                if (conv) {
                    purple_conv_im_write(PURPLE_CONV_IM(conv), who_rcpt, msgtxt.c_str(), PURPLE_MESSAGE_SEND, msg->time());
                }
            } catch (message_exception& e) {
                const char* who_err = e.id().toString().c_str();
                gchar *errMsg = g_strdup_printf("Unable to send message to %s: %s", who_err, e.what());
                if (!purple_conv_present_error(who_err, connection->acct(), errMsg)) {
                    purple_notify_error(connection->connection(), "Error sending message", "Unable to send message", errMsg);
                }
                g_free(errMsg);
            }

        });
        return 0;
    }
}

unsigned int threepl_send_typing(PurpleConnection* gc, const char* who, PurpleTypingState state) {
    ThreeplConnection* connection = static_cast<ThreeplConnection*>(purple_connection_get_protocol_data(gc));

    ceema::PayloadTyping payload;
    payload.m_typing = (state == PURPLE_TYPING);

    try {
        connection->send_message(ceema::client_id::fromString(who), payload);
    } catch (std::exception& e) {
        // Log error, not much more to do
        LOG_ERROR(ceema::logging::loggerRoot, e.what());
    }

    return 0;
}<commit_msg>Fix invalid string read<commit_after>//
// Created by harold on 30/05/17.
//

#include "im.h"

#include <threepl/ThreeplConnection.h>

int threepl_send_im(PurpleConnection* gc, const char *who, const char *message, PurpleMessageFlags) {
    ThreeplConnection* connection = static_cast<ThreeplConnection*>(purple_connection_get_protocol_data(gc));

    std::basic_string<char> text(message);
    ceema::PayloadText payload;
    payload.m_text = text;

    auto msg_fut = connection->send_message(ceema::client_id::fromString(who), payload);
    if (!msg_fut) {
        return -ENOTCONN;
    } else {
        msg_fut.next([connection, msgtxt = std::basic_string<char>(message)](
                ceema::future<std::unique_ptr<ceema::Message>> fut){
            try {
                auto msg = fut.get();
                std::string who_rcpt = msg->recipient().toString();
                PurpleConversation* conv = purple_find_conversation_with_account(
                        PURPLE_CONV_TYPE_IM, who_rcpt.c_str(), connection->acct());
                if (conv) {
                    purple_conv_im_write(PURPLE_CONV_IM(conv), who_rcpt.c_str(), msgtxt.c_str(), PURPLE_MESSAGE_SEND, msg->time());
                }
            } catch (message_exception& e) {
                const char* who_err = e.id().toString().c_str();
                gchar *errMsg = g_strdup_printf("Unable to send message to %s: %s", who_err, e.what());
                if (!purple_conv_present_error(who_err, connection->acct(), errMsg)) {
                    purple_notify_error(connection->connection(), "Error sending message", "Unable to send message", errMsg);
                }
                g_free(errMsg);
            }

        });
        return 0;
    }
}

unsigned int threepl_send_typing(PurpleConnection* gc, const char* who, PurpleTypingState state) {
    ThreeplConnection* connection = static_cast<ThreeplConnection*>(purple_connection_get_protocol_data(gc));

    ceema::PayloadTyping payload;
    payload.m_typing = (state == PURPLE_TYPING);

    try {
        connection->send_message(ceema::client_id::fromString(who), payload);
    } catch (std::exception& e) {
        // Log error, not much more to do
        LOG_ERROR(ceema::logging::loggerRoot, e.what());
    }

    return 0;
}<|endoftext|>


<commit_before>#include "../../include/states/MainState.hpp"
#include "../../include/Constants.hpp"
#include "../../include/Utilities.hpp"
#include "../../include/StationPlayer.hpp"
#include "../../include/exceptions/NanaTextboxProcessingException.hpp"
#include <nana/gui/widgets/menubar.hpp>
#include <nana/gui/widgets/form.hpp>

using namespace constants;

MainState::MainState(StatesManager& manager, Context& context)
    : State(manager, context)
    , container_(context.window_)
    , current_song_label_(context.window_, context.localizer_.get_localized_text("no song is playing"))
    , current_station_label_(context.window_, context.localizer_.get_localized_text("no station is playing"))
    , play_button_(context.window_)
    , pause_button_(context.window_)
    , mute_button_(context.window_)
    , search_textbox_(context.window_)
    , stations_listbox_(context.window_)
    , volume_slider_(context.window_)
    , song_label_menu_()
    , listbox_item_menu_()
{
    build_interface();
    init_contextual_menus();
    init_listbox();
}

void MainState::build_interface()
{
    current_song_label_.events().mouse_up([this](const nana::arg_mouse& arg)
    {
        if (!arg.is_left_button())
        {
            pop_song_title_menu();
        }
    });
    play_button_.caption(context_.localizer_.get_localized_text("Play"));
    play_button_.events().click([this]()
    {
        notify(Observer::placeholder, radiostream::Event::PlayClicked);
    });
    pause_button_.caption(context_.localizer_.get_localized_text("Pause"));
    pause_button_.events().click([this]()
    {
        notify(Observer::placeholder, radiostream::Event::PauseClicked);
    });
    mute_button_.caption(context_.localizer_.get_localized_text("Mute"));
    mute_button_.enable_pushed(true);
    mute_button_.events().mouse_up([this]()
    {
        if (mute_button_.pushed())
        {
            notify(std::make_any<unsigned int>(volume_slider_.value()), radiostream::Event::MuteClicked);
        }
        else
        {
            notify(std::make_any<unsigned int>(volume_slider_.value()), radiostream::Event::MuteUnclicked);
        }
    });
    volume_slider_.scheme().color_vernier = VERNIER_COLOR;
    volume_slider_.maximum(100);
    volume_slider_.value(volume_float_to_int(context_.station_player_.get_volume()));
    volume_slider_.vernier([](unsigned int maximum, unsigned int cursor_value)
    {
        return std::string(std::to_string(cursor_value) + "/" + std::to_string(maximum));
    });
    volume_slider_.events().value_changed([this]()
    {
        notify(std::make_any<unsigned int>(volume_slider_.value()), radiostream::Event::VolumeChanged);
    });
    search_textbox_.line_wrapped(true).multi_lines(false).tip_string("Search...");
    search_textbox_.events().text_changed([this]()
    {
        search_stations();
    });
    container_.div(
        "<content vertical margin=[5%,0,0,0]"
        "<buttons weight=12% arrange=[10%,10%,10%,10%] gap=1% margin=1%>"
        "<labels weight=10% arrange=[49%,48%] gap=1% margin=1% >"
        "<misc weight=8% arrange=[25%,72%] gap=1% margin=1%>"
        "<listbox margin=[1%,1%,7%,1%]>"
        ">");
    container_.field("buttons") << play_button_ << pause_button_ << mute_button_;
    container_.field("labels") << current_station_label_ << current_song_label_;
    container_.field("misc") << search_textbox_ << volume_slider_;
    container_.field("listbox") << stations_listbox_;
    container_.collocate();
}

void MainState::change_visibility(bool visible)
{
    container_.erase(context_.window_);
    container_.field_display("content", visible);
    context_.menubar_.show();
    container_.collocate();
}

void MainState::set_station_name(const std::string& name)
{
    current_station_label_.caption(name);
}

void MainState::refresh_listbox()
{
    populate_listbox();
}

void MainState::station_being_played_changed(const Station& changed_station)
{
    current_station_label_.caption(changed_station.name_);
}

void MainState::song_has_changed(std::string_view song_title)
{
    current_song_label_.caption(std::string(song_title));
}

void MainState::init_contextual_menus()
{
    song_label_menu_.append(context_.localizer_.get_localized_text("Copy title to clipboard."), [this](auto&)
    {
        std::string title = current_song_label_.caption();
        copy_to_clipboard(title);
    });
    listbox_item_menu_.append(context_.localizer_.get_localized_text("Play"), [this](auto&)
    {
        set_new_station();
    });
    listbox_item_menu_.append(context_.localizer_.get_localized_text("Subscribe"), [this](auto&)
    {
        subscribe_to_station();
    });
    listbox_item_menu_.append(context_.localizer_.get_localized_text("Delete from list"), [this](auto&)
    {
        delete_station();
    });
}

void MainState::init_listbox()
{
    stations_listbox_.append_header(context_.localizer_.get_localized_text("Station's name"));
    stations_listbox_.append_header(context_.localizer_.get_localized_text("Ip"));
    stations_listbox_.append_header(context_.localizer_.get_localized_text("Favorite"));
    stations_listbox_.column_at(static_cast<std::size_t>(StationListboxColumns::Name)).width(300u);
    stations_listbox_.column_at(static_cast<std::size_t>(StationListboxColumns::Ip)).width(200u);
    stations_listbox_.column_at(static_cast<std::size_t>(StationListboxColumns::Favorite)).width(100u);
    populate_listbox();
    stations_listbox_.sort_col(static_cast<std::size_t>(StationListboxColumns::Favorite), true);
    stations_listbox_.enable_single(false, false);

    stations_listbox_.events().mouse_down([this](const nana::arg_mouse& arg)
    {
        sticky_select(arg);
        if (!arg.is_left_button())
        {
            pop_stations_listbox_menu();
        }
    });
    stations_listbox_.events().dbl_click([this](const nana::arg_mouse& arg)
    {
        if (!stations_listbox_.cast(arg.pos).is_category() && arg.is_left_button()) // this condition must be fulfilled because when we click category it selects the last item in it so when we dbl_click category it works just as we would click last item in it
        {
            set_new_station();
        }
    });
    stations_listbox_.auto_draw(true);
}
/**
 * \brief Changes favorite value in both listbox and StationsManager::stations_.
 */
void MainState::subscribe_to_station()
{
    const auto selected_item = stations_listbox_.selected().front();
    const auto category_index = static_cast<std::size_t>(StationListboxCategories::NanaDefault);
    const auto name_column_index = static_cast<std::size_t>(StationListboxColumns::Name);
    const auto ip_column_index = static_cast<std::size_t>(StationListboxColumns::Ip);
    const auto favorite_column_index = static_cast<std::size_t>(StationListboxColumns::Favorite);
    const auto station_category = stations_listbox_.at(category_index);
    const auto station_name = station_category.at(selected_item.item).text(name_column_index);
    const auto station_ip = station_category.at(selected_item.item).text(ip_column_index);
    const auto station_favorite = str_to_bool(station_category.at(selected_item.item).text(favorite_column_index));
    const auto station = Station(station_name, station_ip, station_favorite);
    context_.stations_database_.change_station_favorite_status(station);
    populate_listbox();
}

void MainState::populate_listbox()
{
    stations_listbox_.auto_draw(false);
    stations_listbox_.clear();
    for (const auto& station : context_.stations_database_.get_stations())
    {
        const auto category_index = static_cast<std::size_t>(StationListboxCategories::NanaDefault);
        stations_listbox_.at(category_index).append(station);
    }
    stations_listbox_.sort_col(static_cast<std::size_t>(StationListboxColumns::Favorite), true);
}

void MainState::search_stations()
{
    std::string string_to_find{};
    if (!search_textbox_.getline(0, string_to_find))
    {
        throw NanaTextboxProcessingException();
    }
    stations_listbox_.auto_draw(false);
    if (!string_to_find.empty())
    {
        stations_listbox_.clear();
        auto station_names_containing_substring = context_.stations_database_.get_stations_names_with_substring(string_to_find);
        const auto& all_stations = context_.stations_database_.get_stations();
        for (const auto& station : all_stations)
        {
            for (const auto& name : station_names_containing_substring)
            {
                if (string_to_lower(station.name_) == name)
                {
                    const auto category_index = static_cast<std::size_t>(StationListboxCategories::NanaDefault);
                    stations_listbox_.at(category_index).append(station);
                }
            }
        }
    }
    else
    {
        stations_listbox_.clear();
        populate_listbox();
    }
    stations_listbox_.auto_draw(true);
}

void MainState::pop_song_title_menu()
{
    auto position = nana::API::cursor_position();
    nana::API::calc_window_point(context_.window_, position);
    song_label_menu_.popup(context_.window_, position.x, position.y);
}

void MainState::pop_stations_listbox_menu()
{
    auto position = nana::API::cursor_position();
    nana::API::calc_window_point(context_.window_, position);
    listbox_item_menu_.popup(context_.window_, position.x, position.y);
}

void MainState::set_new_station()
{

    if (!stations_listbox_.selected().empty())
    {
        const auto selected_item = stations_listbox_.selected().front();
        const auto category_index = static_cast<std::size_t>(StationListboxCategories::NanaDefault);
        const auto column_index = static_cast<std::size_t>(StationListboxColumns::Name);
        const auto station_category = stations_listbox_.at(category_index);
        const auto station_name = station_category.at(selected_item.item).text(column_index);
        const auto station_url = station_category.at(selected_item.item).text(static_cast<std::size_t>(StationListboxColumns::Ip));
        const bool station_favorite = bool_to_str("true") == station_category.at(selected_item.item).text(static_cast<std::size_t>(StationListboxColumns::Favorite));
        notify(Station{ station_name,station_url, station_favorite }, radiostream::Event::NewStationRequested);
    }
}

void MainState::delete_station()
{
    Station station{};
    const auto index = stations_listbox_.selected().front();
    stations_listbox_.at(index.cat).at(index.item).resolve_to(station);
    notify(std::make_any<Station>(station), radiostream::Event::DeleteStationFromDatabase);
}

void MainState::sticky_select(const nana::arg_mouse & mouse)
{
    if (!stations_listbox_.selected().empty())
    {
        for (const auto& pair : stations_listbox_.selected())
        {
            if (pair.item == stations_listbox_.selected().front().item)
            {
                continue;
            }
            else
            {
                stations_listbox_.at(pair.cat).at(pair.item).select(false);
            }
        }
    }
}

<commit_msg>Added missing function call. This function call is responisble for redrawing the main listbox.<commit_after>#include "../../include/states/MainState.hpp"
#include "../../include/Constants.hpp"
#include "../../include/Utilities.hpp"
#include "../../include/StationPlayer.hpp"
#include "../../include/exceptions/NanaTextboxProcessingException.hpp"
#include <nana/gui/widgets/menubar.hpp>
#include <nana/gui/widgets/form.hpp>

using namespace constants;

MainState::MainState(StatesManager& manager, Context& context)
    : State(manager, context)
    , container_(context.window_)
    , current_song_label_(context.window_, context.localizer_.get_localized_text("no song is playing"))
    , current_station_label_(context.window_, context.localizer_.get_localized_text("no station is playing"))
    , play_button_(context.window_)
    , pause_button_(context.window_)
    , mute_button_(context.window_)
    , search_textbox_(context.window_)
    , stations_listbox_(context.window_)
    , volume_slider_(context.window_)
    , song_label_menu_()
    , listbox_item_menu_()
{
    build_interface();
    init_contextual_menus();
    init_listbox();
}

void MainState::build_interface()
{
    current_song_label_.events().mouse_up([this](const nana::arg_mouse& arg)
    {
        if (!arg.is_left_button())
        {
            pop_song_title_menu();
        }
    });
    play_button_.caption(context_.localizer_.get_localized_text("Play"));
    play_button_.events().click([this]()
    {
        notify(Observer::placeholder, radiostream::Event::PlayClicked);
    });
    pause_button_.caption(context_.localizer_.get_localized_text("Pause"));
    pause_button_.events().click([this]()
    {
        notify(Observer::placeholder, radiostream::Event::PauseClicked);
    });
    mute_button_.caption(context_.localizer_.get_localized_text("Mute"));
    mute_button_.enable_pushed(true);
    mute_button_.events().mouse_up([this]()
    {
        if (mute_button_.pushed())
        {
            notify(std::make_any<unsigned int>(volume_slider_.value()), radiostream::Event::MuteClicked);
        }
        else
        {
            notify(std::make_any<unsigned int>(volume_slider_.value()), radiostream::Event::MuteUnclicked);
        }
    });
    volume_slider_.scheme().color_vernier = VERNIER_COLOR;
    volume_slider_.maximum(100);
    volume_slider_.value(volume_float_to_int(context_.station_player_.get_volume()));
    volume_slider_.vernier([](unsigned int maximum, unsigned int cursor_value)
    {
        return std::string(std::to_string(cursor_value) + "/" + std::to_string(maximum));
    });
    volume_slider_.events().value_changed([this]()
    {
        notify(std::make_any<unsigned int>(volume_slider_.value()), radiostream::Event::VolumeChanged);
    });
    search_textbox_.line_wrapped(true).multi_lines(false).tip_string("Search...");
    search_textbox_.events().text_changed([this]()
    {
        search_stations();
    });
    container_.div(
        "<content vertical margin=[5%,0,0,0]"
        "<buttons weight=12% arrange=[10%,10%,10%,10%] gap=1% margin=1%>"
        "<labels weight=10% arrange=[49%,48%] gap=1% margin=1% >"
        "<misc weight=8% arrange=[25%,72%] gap=1% margin=1%>"
        "<listbox margin=[1%,1%,7%,1%]>"
        ">");
    container_.field("buttons") << play_button_ << pause_button_ << mute_button_;
    container_.field("labels") << current_station_label_ << current_song_label_;
    container_.field("misc") << search_textbox_ << volume_slider_;
    container_.field("listbox") << stations_listbox_;
    container_.collocate();
}

void MainState::change_visibility(bool visible)
{
    container_.erase(context_.window_);
    container_.field_display("content", visible);
    context_.menubar_.show();
    container_.collocate();
}

void MainState::set_station_name(const std::string& name)
{
    current_station_label_.caption(name);
}

void MainState::refresh_listbox()
{
    populate_listbox();
}

void MainState::station_being_played_changed(const Station& changed_station)
{
    current_station_label_.caption(changed_station.name_);
}

void MainState::song_has_changed(std::string_view song_title)
{
    current_song_label_.caption(std::string(song_title));
}

void MainState::init_contextual_menus()
{
    song_label_menu_.append(context_.localizer_.get_localized_text("Copy title to clipboard."), [this](auto&)
    {
        std::string title = current_song_label_.caption();
        copy_to_clipboard(title);
    });
    listbox_item_menu_.append(context_.localizer_.get_localized_text("Play"), [this](auto&)
    {
        set_new_station();
    });
    listbox_item_menu_.append(context_.localizer_.get_localized_text("Subscribe"), [this](auto&)
    {
        subscribe_to_station();
    });
    listbox_item_menu_.append(context_.localizer_.get_localized_text("Delete from list"), [this](auto&)
    {
        delete_station();
    });
}

void MainState::init_listbox()
{
    stations_listbox_.append_header(context_.localizer_.get_localized_text("Station's name"));
    stations_listbox_.append_header(context_.localizer_.get_localized_text("Ip"));
    stations_listbox_.append_header(context_.localizer_.get_localized_text("Favorite"));
    stations_listbox_.column_at(static_cast<std::size_t>(StationListboxColumns::Name)).width(300u);
    stations_listbox_.column_at(static_cast<std::size_t>(StationListboxColumns::Ip)).width(200u);
    stations_listbox_.column_at(static_cast<std::size_t>(StationListboxColumns::Favorite)).width(100u);
    populate_listbox();
    stations_listbox_.sort_col(static_cast<std::size_t>(StationListboxColumns::Favorite), true);
    stations_listbox_.enable_single(false, false);

    stations_listbox_.events().mouse_down([this](const nana::arg_mouse& arg)
    {
        sticky_select(arg);
        if (!arg.is_left_button())
        {
            pop_stations_listbox_menu();
        }
    });
    stations_listbox_.events().dbl_click([this](const nana::arg_mouse& arg)
    {
        if (!stations_listbox_.cast(arg.pos).is_category() && arg.is_left_button()) // this condition must be fulfilled because when we click category it selects the last item in it so when we dbl_click category it works just as we would click last item in it
        {
            set_new_station();
        }
    });
    stations_listbox_.auto_draw(true);
}
/**
 * \brief Changes favorite value in both listbox and StationsManager::stations_.
 */
void MainState::subscribe_to_station()
{
    const auto selected_item = stations_listbox_.selected().front();
    const auto category_index = static_cast<std::size_t>(StationListboxCategories::NanaDefault);
    const auto name_column_index = static_cast<std::size_t>(StationListboxColumns::Name);
    const auto ip_column_index = static_cast<std::size_t>(StationListboxColumns::Ip);
    const auto favorite_column_index = static_cast<std::size_t>(StationListboxColumns::Favorite);
    const auto station_category = stations_listbox_.at(category_index);
    const auto station_name = station_category.at(selected_item.item).text(name_column_index);
    const auto station_ip = station_category.at(selected_item.item).text(ip_column_index);
    const auto station_favorite = str_to_bool(station_category.at(selected_item.item).text(favorite_column_index));
    const auto station = Station(station_name, station_ip, station_favorite);
    context_.stations_database_.change_station_favorite_status(station);
    populate_listbox();
}

void MainState::populate_listbox()
{
    stations_listbox_.auto_draw(false);
    stations_listbox_.clear();
    for (const auto& station : context_.stations_database_.get_stations())
    {
        const auto category_index = static_cast<std::size_t>(StationListboxCategories::NanaDefault);
        stations_listbox_.at(category_index).append(station);
    }
    stations_listbox_.sort_col(static_cast<std::size_t>(StationListboxColumns::Favorite), true);
    stations_listbox_.auto_draw(true);
}

void MainState::search_stations()
{
    std::string string_to_find{};
    if (!search_textbox_.getline(0, string_to_find))
    {
        throw NanaTextboxProcessingException();
    }
    stations_listbox_.auto_draw(false);
    if (!string_to_find.empty())
    {
        stations_listbox_.clear();
        auto station_names_containing_substring = context_.stations_database_.get_stations_names_with_substring(string_to_find);
        const auto& all_stations = context_.stations_database_.get_stations();
        for (const auto& station : all_stations)
        {
            for (const auto& name : station_names_containing_substring)
            {
                if (string_to_lower(station.name_) == name)
                {
                    const auto category_index = static_cast<std::size_t>(StationListboxCategories::NanaDefault);
                    stations_listbox_.at(category_index).append(station);
                }
            }
        }
    }
    else
    {
        stations_listbox_.clear();
        populate_listbox();
    }
    stations_listbox_.auto_draw(true);
}

void MainState::pop_song_title_menu()
{
    auto position = nana::API::cursor_position();
    nana::API::calc_window_point(context_.window_, position);
    song_label_menu_.popup(context_.window_, position.x, position.y);
}

void MainState::pop_stations_listbox_menu()
{
    auto position = nana::API::cursor_position();
    nana::API::calc_window_point(context_.window_, position);
    listbox_item_menu_.popup(context_.window_, position.x, position.y);
}

void MainState::set_new_station()
{

    if (!stations_listbox_.selected().empty())
    {
        const auto selected_item = stations_listbox_.selected().front();
        const auto category_index = static_cast<std::size_t>(StationListboxCategories::NanaDefault);
        const auto column_index = static_cast<std::size_t>(StationListboxColumns::Name);
        const auto station_category = stations_listbox_.at(category_index);
        const auto station_name = station_category.at(selected_item.item).text(column_index);
        const auto station_url = station_category.at(selected_item.item).text(static_cast<std::size_t>(StationListboxColumns::Ip));
        const bool station_favorite = bool_to_str("true") == station_category.at(selected_item.item).text(static_cast<std::size_t>(StationListboxColumns::Favorite));
        notify(Station{ station_name,station_url, station_favorite }, radiostream::Event::NewStationRequested);
    }
}

void MainState::delete_station()
{
    Station station{};
    const auto index = stations_listbox_.selected().front();
    stations_listbox_.at(index.cat).at(index.item).resolve_to(station);
    notify(std::make_any<Station>(station), radiostream::Event::DeleteStationFromDatabase);
}

void MainState::sticky_select(const nana::arg_mouse & mouse)
{
    if (!stations_listbox_.selected().empty())
    {
        for (const auto& pair : stations_listbox_.selected())
        {
            if (pair.item == stations_listbox_.selected().front().item)
            {
                continue;
            }
            else
            {
                stations_listbox_.at(pair.cat).at(pair.item).select(false);
            }
        }
    }
}

<|endoftext|>


<commit_before>/********************************************************************
 * livegrep -- main.cc
 * Copyright (c) 2011-2013 Nelson Elhage
 *
 * This program is free software. You may use, redistribute, and/or
 * modify it under the terms listed in the COPYING file.
 ********************************************************************/
#include "codesearch.h"
#include "timer.h"
#include "re_width.h"

#include "interface.h"

#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <semaphore.h>

#include <iostream>

#include <gflags/gflags.h>

#include <re2/regexp.h>
#include "re2/walker-inl.h"

DEFINE_bool(json, false, "Use JSON output.");
DEFINE_int32(concurrency, 16, "Number of concurrent queries to allow.");
DEFINE_string(dump_index, "", "Dump the produced index to a specified file");
DEFINE_string(load_index, "", "Load the index from a file instead of walking the repository");
DEFINE_bool(quiet, false, "Do the search, but don't print results.");
DEFINE_string(listen, "", "Listen on a UNIX socket for connections");

using namespace std;
using namespace re2;

void die_errno(const char *str) {
    perror(str);
    exit(1);
}

struct print_match {
    print_match(codesearch_interface *ui) : ui_(ui) {}

    void operator()(const match_result *m) const {
        if (FLAGS_quiet)
            return;
        ui_->print_match(m);
    }
protected:
    codesearch_interface *ui_;
};

const int kMaxProgramSize = 4000;
const int kMaxWidth       = 200;

sem_t interact_sem;

void interact(code_searcher *cs, codesearch_interface *ui) {
    code_searcher::search_thread search(cs);
    WidthWalker width;

    RE2::Options opts;
    default_re2_options(opts);

    while (true) {
        ui->print_prompt(cs);
        string input;
        if (!ui->getline(input))
            break;

        string line, file, tree;
        if (!ui->parse_query(input, line, file, tree))
            continue;

        RE2 re(line, opts);
        RE2 file_re(file, opts);
        RE2 tree_re(tree, opts);
        if (!re.ok()) {
            ui->print_error(re.error());
            continue;
        }
        if (!file_re.ok()) {
            ui->print_error(file_re.error());
            continue;
        }
        if (!tree_re.ok()) {
            ui->print_error(tree_re.error());
            continue;
        }
        if (re.ProgramSize() > kMaxProgramSize) {
            ui->print_error("Parse error.");
            continue;
        }
        int w = width.Walk(re.Regexp(), 0);
        if (w > kMaxWidth) {
            ui->print_error("Parse error.");
            continue;
        }
        {
            timer tm;
            struct timeval elapsed;
            match_stats stats;

            ui->info("ProgramSize: %d\n", re.ProgramSize());

            {
                sem_wait(&interact_sem);
                search.match(re,
                             file.size() ? &file_re : 0,
                             tree.size() ? &tree_re : 0,
                             print_match(ui), &stats);
                sem_post(&interact_sem);
            }
            elapsed = tm.elapsed();
            ui->print_stats(elapsed, &stats);
        }
    }
}

void initialize_search(code_searcher *search,
                       codesearch_interface *ui,
                       int argc, char **argv) {
    if (FLAGS_load_index.size() == 0) {
        if (FLAGS_dump_index.size())
            search->set_alloc(make_dump_allocator(search, FLAGS_dump_index));
        else
            search->set_alloc(make_mem_allocator());

        vector<std::string> args;
        for (int i = 0; i < argc; ++i)
            args.push_back(argv[i]);

        timer tm;
        struct timeval elapsed;
        ui->build_index(search, args);
        ui->info("Finalizing...\n");
        search->finalize();
        elapsed = tm.elapsed();
        ui->info("repository indexed in %d.%06ds\n",
                 (int)elapsed.tv_sec, (int)elapsed.tv_usec);
    } else {
        search->load_index(FLAGS_load_index);
    }
    if (!FLAGS_json && !FLAGS_load_index.size())
        search->dump_stats();
    if (FLAGS_dump_index.size() && FLAGS_load_index.size())
        search->dump_index(FLAGS_dump_index);
}

struct child_state {
    int fd;
    code_searcher *search;
};

void *handle_client(void *data) {
    child_state *child = static_cast<child_state*>(data);
    FILE *r = fdopen(child->fd, "r");
    FILE *w = fdopen(dup(child->fd), "w");
    assert(!setvbuf(r,  NULL, _IOFBF, 4096*4));
    assert(!setvbuf(w, NULL, _IONBF, 0));
    codesearch_interface *interface;
    if (FLAGS_json)
        interface = make_json_interface(r, w);
    else
        interface = make_cli_interface(r, w);
    interact(child->search, interface);
    delete interface;
    delete child;
    fclose(r);
    fclose(w);
    return 0;
}

int bind_to_address(string spec) {
    int off = spec.find("://");
    string proto, address;

    if (off == string::npos) {
        proto = "unix";
        address = spec;
    } else {
        proto = spec.substr(0, off);
        address = spec.substr(off + 3);
    }

    int server;

    if (proto == "unix") {
        server = socket(AF_UNIX, SOCK_STREAM, 0);
        if (server < 0)
            die_errno("socket(AF_UNIX)");

        struct sockaddr_un addr;

        memset(&addr, 0, sizeof addr);
        addr.sun_family = AF_UNIX;
        memcpy(addr.sun_path, address.c_str(), min(address.size(), sizeof(addr.sun_path) - 1));

        if (::bind(server, reinterpret_cast<sockaddr*>(&addr), sizeof addr) < 0)
            die_errno("Unable to bind socket");
    } else if (proto == "tcp") {
        int colon = address.find(':');
        if (colon == string::npos) {
            fprintf(stderr, "-listen: TCP addresses must be HOST:PORT.\n");
            exit(1);
        }
        string host = address.substr(0, colon);
        struct addrinfo hint = {};
        hint.ai_family = AF_INET;
        hint.ai_socktype = SOCK_STREAM;

        struct addrinfo *addrs = NULL;
        int err;
        if ((err = getaddrinfo(host.c_str(), address.c_str() + colon + 1,
                               &hint, &addrs)) != 0) {
            fprintf(stderr, "Error resolving %s: %s\n", host.c_str(), gai_strerror(err));
        }

        server = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
        if (server < 0)
            die_errno("Creating socket");
        int one = 1;
        if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != 0)
            die_errno("set reuseaddr");
        if (::bind(server, addrs->ai_addr, addrs->ai_addrlen) != 0)
            die_errno("Binding to address");
        freeaddrinfo(addrs);
    } else {
        fprintf(stderr, "Unknown protocol: %s\n", proto.c_str());
        exit(1);
    }

    if (listen(server, 4) < 0)
        die_errno("listen()");

    return server;
}

void listen(code_searcher *search, string path) {
    int server = bind_to_address(path);

    while(1) {
        int fd = accept(server, NULL, NULL);
        if (fd < 0)
            die_errno("accept");

        child_state *state = new child_state;
        state->fd = fd;
        state->search = search;

        pthread_t thread;
        pthread_create(&thread, NULL, handle_client, state);
    }
}

int main(int argc, char **argv) {
    google::SetUsageMessage("Usage: " + string(argv[0]) + " <options> REFS");
    google::ParseCommandLineFlags(&argc, &argv, true);

    prctl(PR_SET_PDEATHSIG, SIGINT);

    code_searcher search;
    codesearch_interface *interface = 0;
    if (FLAGS_json)
        interface = make_json_interface(stdin, stdout);
    else
        interface = make_cli_interface(stdin, stdout);

    signal(SIGPIPE, SIG_IGN);

    initialize_search(&search, interface, argc, argv);

    if (sem_init(&interact_sem, 0, FLAGS_concurrency) < 0)
        die_errno("sem_init");

    if (FLAGS_listen.size())
        listen(&search, FLAGS_listen);
    else
        interact(&search, interface);

    return 0;
}
<commit_msg>Default to JSON UI.<commit_after>/********************************************************************
 * livegrep -- main.cc
 * Copyright (c) 2011-2013 Nelson Elhage
 *
 * This program is free software. You may use, redistribute, and/or
 * modify it under the terms listed in the COPYING file.
 ********************************************************************/
#include "codesearch.h"
#include "timer.h"
#include "re_width.h"

#include "interface.h"

#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <semaphore.h>

#include <iostream>

#include <gflags/gflags.h>

#include <re2/regexp.h>
#include "re2/walker-inl.h"

DEFINE_bool(cli, false, "Use an interactive CLI format instead of JSON.");
DEFINE_int32(concurrency, 16, "Number of concurrent queries to allow.");
DEFINE_string(dump_index, "", "Dump the produced index to a specified file");
DEFINE_string(load_index, "", "Load the index from a file instead of walking the repository");
DEFINE_bool(quiet, false, "Do the search, but don't print results.");
DEFINE_string(listen, "", "Listen on a UNIX socket for connections");

using namespace std;
using namespace re2;

void die_errno(const char *str) {
    perror(str);
    exit(1);
}

codesearch_interface *build_interface(FILE *in, FILE *out) {
    if (FLAGS_cli)
        return make_json_interface(in, out);
    else
        return make_cli_interface(in, out);
}

struct print_match {
    print_match(codesearch_interface *ui) : ui_(ui) {}

    void operator()(const match_result *m) const {
        if (FLAGS_quiet)
            return;
        ui_->print_match(m);
    }
protected:
    codesearch_interface *ui_;
};

const int kMaxProgramSize = 4000;
const int kMaxWidth       = 200;

sem_t interact_sem;

void interact(code_searcher *cs, codesearch_interface *ui) {
    code_searcher::search_thread search(cs);
    WidthWalker width;

    RE2::Options opts;
    default_re2_options(opts);

    while (true) {
        ui->print_prompt(cs);
        string input;
        if (!ui->getline(input))
            break;

        string line, file, tree;
        if (!ui->parse_query(input, line, file, tree))
            continue;

        RE2 re(line, opts);
        RE2 file_re(file, opts);
        RE2 tree_re(tree, opts);
        if (!re.ok()) {
            ui->print_error(re.error());
            continue;
        }
        if (!file_re.ok()) {
            ui->print_error(file_re.error());
            continue;
        }
        if (!tree_re.ok()) {
            ui->print_error(tree_re.error());
            continue;
        }
        if (re.ProgramSize() > kMaxProgramSize) {
            ui->print_error("Parse error.");
            continue;
        }
        int w = width.Walk(re.Regexp(), 0);
        if (w > kMaxWidth) {
            ui->print_error("Parse error.");
            continue;
        }
        {
            timer tm;
            struct timeval elapsed;
            match_stats stats;

            ui->info("ProgramSize: %d\n", re.ProgramSize());

            {
                sem_wait(&interact_sem);
                search.match(re,
                             file.size() ? &file_re : 0,
                             tree.size() ? &tree_re : 0,
                             print_match(ui), &stats);
                sem_post(&interact_sem);
            }
            elapsed = tm.elapsed();
            ui->print_stats(elapsed, &stats);
        }
    }
}

void initialize_search(code_searcher *search,
                       codesearch_interface *ui,
                       int argc, char **argv) {
    if (FLAGS_load_index.size() == 0) {
        if (FLAGS_dump_index.size())
            search->set_alloc(make_dump_allocator(search, FLAGS_dump_index));
        else
            search->set_alloc(make_mem_allocator());

        vector<std::string> args;
        for (int i = 0; i < argc; ++i)
            args.push_back(argv[i]);

        timer tm;
        struct timeval elapsed;
        ui->build_index(search, args);
        ui->info("Finalizing...\n");
        search->finalize();
        elapsed = tm.elapsed();
        ui->info("repository indexed in %d.%06ds\n",
                 (int)elapsed.tv_sec, (int)elapsed.tv_usec);
    } else {
        search->load_index(FLAGS_load_index);
    }
    if (FLAGS_cli && !FLAGS_load_index.size())
        search->dump_stats();
    if (FLAGS_dump_index.size() && FLAGS_load_index.size())
        search->dump_index(FLAGS_dump_index);
}

struct child_state {
    int fd;
    code_searcher *search;
};

void *handle_client(void *data) {
    child_state *child = static_cast<child_state*>(data);
    FILE *r = fdopen(child->fd, "r");
    FILE *w = fdopen(dup(child->fd), "w");
    assert(!setvbuf(r,  NULL, _IOFBF, 4096*4));
    assert(!setvbuf(w, NULL, _IONBF, 0));
    codesearch_interface *interface = build_interface(r, w);
    interact(child->search, interface);
    delete interface;
    delete child;
    fclose(r);
    fclose(w);
    return 0;
}

int bind_to_address(string spec) {
    int off = spec.find("://");
    string proto, address;

    if (off == string::npos) {
        proto = "unix";
        address = spec;
    } else {
        proto = spec.substr(0, off);
        address = spec.substr(off + 3);
    }

    int server;

    if (proto == "unix") {
        server = socket(AF_UNIX, SOCK_STREAM, 0);
        if (server < 0)
            die_errno("socket(AF_UNIX)");

        struct sockaddr_un addr;

        memset(&addr, 0, sizeof addr);
        addr.sun_family = AF_UNIX;
        memcpy(addr.sun_path, address.c_str(), min(address.size(), sizeof(addr.sun_path) - 1));

        if (::bind(server, reinterpret_cast<sockaddr*>(&addr), sizeof addr) < 0)
            die_errno("Unable to bind socket");
    } else if (proto == "tcp") {
        int colon = address.find(':');
        if (colon == string::npos) {
            fprintf(stderr, "-listen: TCP addresses must be HOST:PORT.\n");
            exit(1);
        }
        string host = address.substr(0, colon);
        struct addrinfo hint = {};
        hint.ai_family = AF_INET;
        hint.ai_socktype = SOCK_STREAM;

        struct addrinfo *addrs = NULL;
        int err;
        if ((err = getaddrinfo(host.c_str(), address.c_str() + colon + 1,
                               &hint, &addrs)) != 0) {
            fprintf(stderr, "Error resolving %s: %s\n", host.c_str(), gai_strerror(err));
        }

        server = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
        if (server < 0)
            die_errno("Creating socket");
        int one = 1;
        if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != 0)
            die_errno("set reuseaddr");
        if (::bind(server, addrs->ai_addr, addrs->ai_addrlen) != 0)
            die_errno("Binding to address");
        freeaddrinfo(addrs);
    } else {
        fprintf(stderr, "Unknown protocol: %s\n", proto.c_str());
        exit(1);
    }

    if (listen(server, 4) < 0)
        die_errno("listen()");

    return server;
}

void listen(code_searcher *search, string path) {
    int server = bind_to_address(path);

    while(1) {
        int fd = accept(server, NULL, NULL);
        if (fd < 0)
            die_errno("accept");

        child_state *state = new child_state;
        state->fd = fd;
        state->search = search;

        pthread_t thread;
        pthread_create(&thread, NULL, handle_client, state);
    }
}

int main(int argc, char **argv) {
    google::SetUsageMessage("Usage: " + string(argv[0]) + " <options> REFS");
    google::ParseCommandLineFlags(&argc, &argv, true);

    prctl(PR_SET_PDEATHSIG, SIGINT);

    code_searcher search;
    codesearch_interface *interface = build_interface(stdin, stdout);

    signal(SIGPIPE, SIG_IGN);

    initialize_search(&search, interface, argc, argv);

    if (sem_init(&interact_sem, 0, FLAGS_concurrency) < 0)
        die_errno("sem_init");

    if (FLAGS_listen.size())
        listen(&search, FLAGS_listen);
    else
        interact(&search, interface);

    return 0;
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2003, Arvid Norberg
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 the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>

#include "zlib.h"

#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"

using namespace libtorrent;

namespace
{
	enum
	{
		minimum_tracker_response_length = 3,
		http_buffer_size = 2048
	};

	enum
	{
		FTEXT = 0x01,
		FHCRC = 0x02,
		FEXTRA = 0x04,
		FNAME = 0x08,
		FCOMMENT = 0x10,
		FRESERVED = 0xe0,

		GZIP_MAGIC0 = 0x1f,
		GZIP_MAGIC1 = 0x8b
	};

}

namespace libtorrent
{

	// returns -1 if gzip header is invalid or the header size in bytes
	int gzip_header(const char* buf, int size)
	{
		assert(buf != 0);
		assert(size > 0);

		const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
		const int total_size = size;

		// The zip header cannot be shorter than 10 bytes
		if (size < 10) return -1;

		// check the magic header of gzip
		if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1;

		int method = buffer[2];
		int flags = buffer[3];

		// check for reserved flag and make sure it's compressed with the correct metod
		if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1;

		// skip time, xflags, OS code
		size -= 10;
		buffer += 10;

		if (flags & FEXTRA)
		{
			int extra_len;

			if (size < 2) return -1;

			extra_len = (buffer[1] << 8) | buffer[0];

			if (size < (extra_len+2)) return -1;
			size -= (extra_len + 2);
			buffer += (extra_len + 2);
		}

		if (flags & FNAME)
		{
			while (size && *buffer)
			{
				--size;
				++buffer;
			}
			if (!size || *buffer) return -1;

			--size;
			++buffer;
		}

		if (flags & FCOMMENT)
		{
			while (size && *buffer)
			{
				--size;
				++buffer;
			}
			if (!size || *buffer) return -1;

			--size;
			++buffer;
		}

		if (flags & FHCRC)
		{
			if (size < 2) return -1;

			size -= 2;
			buffer += 2;
		}

		return total_size - size;
	}

	bool inflate_gzip(
		std::vector<char>& buffer
		, request_callback* requester
		, int maximum_tracker_response_length)
	{
		assert(maximum_tracker_response_length > 0);

		int header_len = gzip_header(&buffer[0], (int)buffer.size());
		if (header_len < 0)
		{
			requester->tracker_request_error(200, "invalid gzip header in tracker response");
			return true;
		}

		// start off wth one kilobyte and grow
		// if needed
		std::vector<char> inflate_buffer(1024);

		// initialize the zlib-stream
		z_stream str;

		// subtract 8 from the end of the buffer since that's CRC32 and input size
		// and those belong to the gzip file
		str.avail_in = (int)buffer.size() - header_len - 8;
		str.next_in = reinterpret_cast<Bytef*>(&buffer[header_len]);
		str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[0]);
		str.avail_out = (int)inflate_buffer.size();
		str.zalloc = Z_NULL;
		str.zfree = Z_NULL;
		str.opaque = 0;
		// -15 is really important. It will make inflate() not look for a zlib header
		// and just deflate the buffer
		if (inflateInit2(&str, -15) != Z_OK)
		{
			requester->tracker_request_error(200, "gzip out of memory");
			return true;
		}

		// inflate and grow inflate_buffer as needed
		int ret = inflate(&str, Z_SYNC_FLUSH);
		while (ret == Z_OK)
		{
			if (str.avail_out == 0)
			{
				if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length)
				{
					inflateEnd(&str);
					requester->tracker_request_error(200, "tracker response too large");
					return true;
				}
				int new_size = (int)inflate_buffer.size() * 2;
				if (new_size > maximum_tracker_response_length) new_size = maximum_tracker_response_length;
				int old_size = (int)inflate_buffer.size();

				inflate_buffer.resize(new_size);
				str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[old_size]);
				str.avail_out = new_size - old_size;
			}

			ret = inflate(&str, Z_SYNC_FLUSH);
		}

		inflate_buffer.resize(inflate_buffer.size() - str.avail_out);
		inflateEnd(&str);

		if (ret != Z_STREAM_END)
		{
			requester->tracker_request_error(200, "gzip error");
			return true;
		}

		// commit the resulting buffer
		std::swap(buffer, inflate_buffer);
		return false;
	}

	std::string base64encode(const std::string& s)
	{
		static const char base64_table[] =
		{
			'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
			'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
			'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
			'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
			'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
			'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
			'w', 'x', 'y', 'z', '0', '1', '2', '3',
			'4', '5', '6', '7', '8', '9', '+', '/'
		};

		unsigned char inbuf[3];
		unsigned char outbuf[4];

		std::string ret;
		for (std::string::const_iterator i = s.begin(); i != s.end();)
		{
			// available input is 1,2 or 3 bytes
			// since we read 3 bytes at a time at most
			int available_input = std::min(3, (int)std::distance(i, s.end()));

			// clear input buffer
			std::fill(inbuf, inbuf+3, 0);

			// read a chunk of input into inbuf
			for (int j = 0; j < available_input; ++j)
			{
				inbuf[j] = *i;
				++i;
			}

			// encode inbuf to outbuf
			outbuf[0] = (inbuf[0] & 0xfc) >> 2;
			outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4);
			outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6);
			outbuf[3] = inbuf[2] & 0x3f;

			// write output
			for (int j = 0; j < available_input+1; ++j)
			{
				ret += base64_table[outbuf[j]];
			}

			// write pad
			for (int j = 0; j < 3 - available_input; ++j)
			{
				ret += '=';
			}
		}
		return ret;
	}

	void tracker_manager::tick()
	{
		std::vector<boost::shared_ptr<tracker_connection> >::iterator i;
		for (i = m_connections.begin(); i != m_connections.end(); ++i)
		{
			boost::shared_ptr<tracker_connection>& c = *i;
			try
			{
				if (!c->tick()) continue;
			}
			catch (const std::exception& e)
			{
				if (c->requester())
					c->requester()->tracker_request_error(-1, e.what());
			}
			if (c->requester()) c->requester()->m_manager = 0;
			m_connections.erase(i);
			--i; // compensate for the remove
		}
	}

	void tracker_manager::queue_request(
		tracker_request req
		, request_callback* c
		, std::string const& password)
	{
		assert(req.num_want >= 0);
		if (req.event == tracker_request::stopped)
			req.num_want = 0;

		try
		{
			std::string hostname; // hostname only
			std::string protocol; // should be http
			int port = 80;

			// PARSE URL
			std::string::iterator start = req.url.begin();
			std::string::iterator end
				= std::find(req.url.begin(), req.url.end(), ':');
			protocol = std::string(start, end);

			if (end == req.url.end()) throw std::runtime_error("invalid url");
			++end;
			if (end == req.url.end()) throw std::runtime_error("invalid url");
			if (*end != '/') throw std::runtime_error("invalid url");
			++end;
			if (end == req.url.end()) throw std::runtime_error("invalid url");
			if (*end != '/') throw std::runtime_error("invalid url");
			++end;
			start = end;

			end = std::find(start, req.url.end(), '/');
			std::string::const_iterator port_pos
				= std::find(start, req.url.end(), ':');

			if (port_pos < end)
			{
				hostname.assign(start, port_pos);
				++port_pos;
				try
				{
					port = boost::lexical_cast<int>(std::string(port_pos, end));
				}
				catch(boost::bad_lexical_cast&)
				{
					throw std::runtime_error("invalid url");
				}
			}
			else
			{
				hostname.assign(start, end);
			}

			start = end;
			std::string request_string = std::string(start, req.url.end());

			boost::shared_ptr<tracker_connection> con;

			if (protocol == "http")
			{
				con.reset(new http_tracker_connection(
					req
					, hostname
					, port
					, request_string
					, c
					, m_settings
					, password));
			}
			else if (protocol == "udp")
			{
				con.reset(new udp_tracker_connection(
					req
					, hostname
					, port
					, c
					, m_settings));
			}
			else
			{
				throw std::runtime_error("unkown protocol in tracker url");
			}

			m_connections.push_back(con);

			if (con->requester()) con->requester()->m_manager = this;
		}
		catch (std::exception& e)
		{
			if (c) c->tracker_request_error(-1, e.what());
		}
	}

	void tracker_manager::abort_request(request_callback* c)
	{
		assert(c != 0);
		std::vector<boost::shared_ptr<tracker_connection> >::iterator i;
		for (i = m_connections.begin(); i != m_connections.end(); ++i)
		{
			if ((*i)->requester() == c)
			{
				m_connections.erase(i);
				break;
			}
		}
	}

	void tracker_manager::abort_all_requests()
	{
		// removes all connections from m_connections
		// except those with a requester == 0 (since those are
		// 'event=stopped'-requests)

		std::vector<boost::shared_ptr<tracker_connection> > keep_connections;

		for (std::vector<boost::shared_ptr<tracker_connection> >::const_iterator i =
				m_connections.begin();
			i != m_connections.end();
			++i)
		{
			if ((*i)->requester() == 0) keep_connections.push_back(*i);
		}

		std::swap(m_connections, keep_connections);
	}

	bool tracker_manager::send_finished() const
	{
		for (std::vector<boost::shared_ptr<tracker_connection> >::const_iterator i =
				m_connections.begin();
			i != m_connections.end();
			++i)
		{
			if (!(*i)->send_finished()) return false;
		}
		return true;
	}

}
<commit_msg>*** empty log message ***<commit_after>/*

Copyright (c) 2003, Arvid Norberg
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 the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>

#include "zlib.h"

#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"

using namespace libtorrent;

namespace
{
	enum
	{
		minimum_tracker_response_length = 3,
		http_buffer_size = 2048
	};

	enum
	{
		FTEXT = 0x01,
		FHCRC = 0x02,
		FEXTRA = 0x04,
		FNAME = 0x08,
		FCOMMENT = 0x10,
		FRESERVED = 0xe0,

		GZIP_MAGIC0 = 0x1f,
		GZIP_MAGIC1 = 0x8b
	};

}

namespace libtorrent
{

	// returns -1 if gzip header is invalid or the header size in bytes
	int gzip_header(const char* buf, int size)
	{
		assert(buf != 0);
		assert(size > 0);

		const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
		const int total_size = size;

		// The zip header cannot be shorter than 10 bytes
		if (size < 10) return -1;

		// check the magic header of gzip
		if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1;

		int method = buffer[2];
		int flags = buffer[3];

		// check for reserved flag and make sure it's compressed with the correct metod
		if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1;

		// skip time, xflags, OS code
		size -= 10;
		buffer += 10;

		if (flags & FEXTRA)
		{
			int extra_len;

			if (size < 2) return -1;

			extra_len = (buffer[1] << 8) | buffer[0];

			if (size < (extra_len+2)) return -1;
			size -= (extra_len + 2);
			buffer += (extra_len + 2);
		}

		if (flags & FNAME)
		{
			while (size && *buffer)
			{
				--size;
				++buffer;
			}
			if (!size || *buffer) return -1;

			--size;
			++buffer;
		}

		if (flags & FCOMMENT)
		{
			while (size && *buffer)
			{
				--size;
				++buffer;
			}
			if (!size || *buffer) return -1;

			--size;
			++buffer;
		}

		if (flags & FHCRC)
		{
			if (size < 2) return -1;

			size -= 2;
			buffer += 2;
		}

		return total_size - size;
	}

	bool inflate_gzip(
		std::vector<char>& buffer
		, request_callback* requester
		, int maximum_tracker_response_length)
	{
		assert(maximum_tracker_response_length > 0);

		int header_len = gzip_header(&buffer[0], (int)buffer.size());
		if (header_len < 0)
		{
			requester->tracker_request_error(200, "invalid gzip header in tracker response");
			return true;
		}

		// start off wth one kilobyte and grow
		// if needed
		std::vector<char> inflate_buffer(1024);

		// initialize the zlib-stream
		z_stream str;

		// subtract 8 from the end of the buffer since that's CRC32 and input size
		// and those belong to the gzip file
		str.avail_in = (int)buffer.size() - header_len - 8;
		str.next_in = reinterpret_cast<Bytef*>(&buffer[header_len]);
		str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[0]);
		str.avail_out = (int)inflate_buffer.size();
		str.zalloc = Z_NULL;
		str.zfree = Z_NULL;
		str.opaque = 0;
		// -15 is really important. It will make inflate() not look for a zlib header
		// and just deflate the buffer
		if (inflateInit2(&str, -15) != Z_OK)
		{
			requester->tracker_request_error(200, "gzip out of memory");
			return true;
		}

		// inflate and grow inflate_buffer as needed
		int ret = inflate(&str, Z_SYNC_FLUSH);
		while (ret == Z_OK)
		{
			if (str.avail_out == 0)
			{
				if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length)
				{
					inflateEnd(&str);
					requester->tracker_request_error(200, "tracker response too large");
					return true;
				}
				int new_size = (int)inflate_buffer.size() * 2;
				if (new_size > maximum_tracker_response_length) new_size = maximum_tracker_response_length;
				int old_size = (int)inflate_buffer.size();

				inflate_buffer.resize(new_size);
				str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[old_size]);
				str.avail_out = new_size - old_size;
			}

			ret = inflate(&str, Z_SYNC_FLUSH);
		}

		inflate_buffer.resize(inflate_buffer.size() - str.avail_out);
		inflateEnd(&str);

		if (ret != Z_STREAM_END)
		{
			requester->tracker_request_error(200, "gzip error");
			return true;
		}

		// commit the resulting buffer
		std::swap(buffer, inflate_buffer);
		return false;
	}

	std::string base64encode(const std::string& s)
	{
		static const char base64_table[] =
		{
			'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
			'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
			'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
			'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
			'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
			'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
			'w', 'x', 'y', 'z', '0', '1', '2', '3',
			'4', '5', '6', '7', '8', '9', '+', '/'
		};

		unsigned char inbuf[3];
		unsigned char outbuf[4];

		std::string ret;
		for (std::string::const_iterator i = s.begin(); i != s.end();)
		{
			// available input is 1,2 or 3 bytes
			// since we read 3 bytes at a time at most
			int available_input = std::min(3, (int)std::distance(i, s.end()));

			// clear input buffer
			std::fill(inbuf, inbuf+3, 0);

			// read a chunk of input into inbuf
			for (int j = 0; j < available_input; ++j)
			{
				inbuf[j] = *i;
				++i;
			}

			// encode inbuf to outbuf
			outbuf[0] = (inbuf[0] & 0xfc) >> 2;
			outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4);
			outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6);
			outbuf[3] = inbuf[2] & 0x3f;

			// write output
			for (int j = 0; j < available_input+1; ++j)
			{
				ret += base64_table[outbuf[j]];
			}

			// write pad
			for (int j = 0; j < 3 - available_input; ++j)
			{
				ret += '=';
			}
		}
		return ret;
	}

	void tracker_manager::tick()
	{
		std::vector<boost::shared_ptr<tracker_connection> >::iterator i;
		for (i = m_connections.begin(); i != m_connections.end(); ++i)
		{
			boost::shared_ptr<tracker_connection>& c = *i;
			try
			{
				if (!c->tick()) continue;
			}
			catch (const std::exception& e)
			{
				if (c->requester())
					c->requester()->tracker_request_error(-1, e.what());
			}
			if (c->requester()) c->requester()->m_manager = 0;
			m_connections.erase(i);
			--i; // compensate for the remove
		}
	}

	void tracker_manager::queue_request(
		tracker_request req
		, request_callback* c
		, std::string const& password)
	{
		assert(req.num_want >= 0);
		if (req.event == tracker_request::stopped)
			req.num_want = 0;

		try
		{
			std::string hostname; // hostname only
			std::string protocol; // should be http
			int port = 80;

			// PARSE URL
			std::string::iterator start = req.url.begin();
			std::string::iterator end
				= std::find(req.url.begin(), req.url.end(), ':');
			protocol = std::string(start, end);

			if (end == req.url.end()) throw std::runtime_error("invalid url");
			++end;
			if (end == req.url.end()) throw std::runtime_error("invalid url");
			if (*end != '/') throw std::runtime_error("invalid url");
			++end;
			if (end == req.url.end()) throw std::runtime_error("invalid url");
			if (*end != '/') throw std::runtime_error("invalid url");
			++end;
			start = end;

			end = std::find(start, req.url.end(), '/');
			std::string::iterator port_pos
				= std::find(start, req.url.end(), ':');

			if (port_pos < end)
			{
				hostname.assign(start, port_pos);
				++port_pos;
				try
				{
					port = boost::lexical_cast<int>(std::string(port_pos, end));
				}
				catch(boost::bad_lexical_cast&)
				{
					throw std::runtime_error("invalid url");
				}
			}
			else
			{
				hostname.assign(start, end);
			}

			start = end;
			std::string request_string = std::string(start, req.url.end());

			boost::shared_ptr<tracker_connection> con;

			if (protocol == "http")
			{
				con.reset(new http_tracker_connection(
					req
					, hostname
					, port
					, request_string
					, c
					, m_settings
					, password));
			}
			else if (protocol == "udp")
			{
				con.reset(new udp_tracker_connection(
					req
					, hostname
					, port
					, c
					, m_settings));
			}
			else
			{
				throw std::runtime_error("unkown protocol in tracker url");
			}

			m_connections.push_back(con);

			if (con->requester()) con->requester()->m_manager = this;
		}
		catch (std::exception& e)
		{
			if (c) c->tracker_request_error(-1, e.what());
		}
	}

	void tracker_manager::abort_request(request_callback* c)
	{
		assert(c != 0);
		std::vector<boost::shared_ptr<tracker_connection> >::iterator i;
		for (i = m_connections.begin(); i != m_connections.end(); ++i)
		{
			if ((*i)->requester() == c)
			{
				m_connections.erase(i);
				break;
			}
		}
	}

	void tracker_manager::abort_all_requests()
	{
		// removes all connections from m_connections
		// except those with a requester == 0 (since those are
		// 'event=stopped'-requests)

		std::vector<boost::shared_ptr<tracker_connection> > keep_connections;

		for (std::vector<boost::shared_ptr<tracker_connection> >::const_iterator i =
				m_connections.begin();
			i != m_connections.end();
			++i)
		{
			if ((*i)->requester() == 0) keep_connections.push_back(*i);
		}

		std::swap(m_connections, keep_connections);
	}

	bool tracker_manager::send_finished() const
	{
		for (std::vector<boost::shared_ptr<tracker_connection> >::const_iterator i =
				m_connections.begin();
			i != m_connections.end();
			++i)
		{
			if (!(*i)->send_finished()) return false;
		}
		return true;
	}

}
<|endoftext|>


<commit_before>#define _BSD_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <unistd.h>
#include "ptbox.h"

pt_debugger::pt_debugger() : on_return_callback(NULL) {}

bool has_null(char *buf, unsigned long size) {
    for (unsigned long i = 0; i < size; ++i) {
        if (buf[i] == '\0')
            return true;
    }
    return false;
}

void pt_debugger::set_process(pt_process *proc) {
    process = proc;
}

void pt_debugger::new_process() {}

void pt_debugger::settid(pid_t tid) {
    this->tid = tid;
#if defined(__FreeBSD__)
    reg bsd_regs;
    ptrace(PT_GETREGS, tid, (caddr_t) &bsd_regs, 0);
    map_regs_to_linux(&bsd_regs, &bsd_converted_regs);
#endif
}

long pt_debugger::peek_reg(int idx) {
#if defined(__FreeBSD__)
    return ((unsigned long*)&bsd_converted_regs)[idx];
#else
    return ptrace(PTRACE_PEEKUSER, tid, sizeof(long) * idx, 0);
#endif
}

void pt_debugger::poke_reg(int idx, long data) {
#if defined(__FreeBSD__)
    ((unsigned long*)&bsd_converted_regs)[idx] = data;

    reg bsd_regs;
    map_regs_from_linux(&bsd_regs, &bsd_converted_regs);
    ptrace(PT_SETREGS, tid, (caddr_t) &bsd_regs, 0);
#else
    ptrace(PTRACE_POKEUSER, tid, sizeof(long) * idx, data);
#endif
}

char *pt_debugger::readstr(unsigned long addr, size_t max_size) {
    size_t size = 4096, read = 0;
    char *buf = (char *) malloc(size);
    union {
        long val;
        char byte[sizeof(long)];
    } data;

    while (true) {
        if (read + sizeof(long) > size) {
            if (max_size && size >= max_size) {
                buf[max_size-1] = 0;
                break;
            }

            size += 4096;
            if (max_size && size > max_size)
                size = max_size;

            void *nbuf = realloc(buf, size);
            if (!nbuf) {
                buf[size-4097] = 0;
                break;
            }
            buf = (char *) nbuf;
        }
#if defined(__FreeBSD__)
        data.val = ptrace(PT_READ_D, tid, (caddr_t) (addr + read), 0);
#else
        data.val = ptrace(PTRACE_PEEKDATA, tid, addr + read, NULL);
#endif
        memcpy(buf + read, data.byte, sizeof(long));
        if (has_null(data.byte, sizeof(long)))
            break;
        read += sizeof(long);
    }
    return buf;
}

void pt_debugger::freestr(char *buf) {
    free(buf);
}

pt_debugger::~pt_debugger() {}
<commit_msg>Add reminder; #192<commit_after>#define _BSD_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <unistd.h>
#include "ptbox.h"

pt_debugger::pt_debugger() : on_return_callback(NULL) {}

bool has_null(char *buf, unsigned long size) {
    for (unsigned long i = 0; i < size; ++i) {
        if (buf[i] == '\0')
            return true;
    }
    return false;
}

void pt_debugger::set_process(pt_process *proc) {
    process = proc;
}

void pt_debugger::new_process() {}

void pt_debugger::settid(pid_t tid) {
    this->tid = tid;
#if defined(__FreeBSD__)
    struct reg bsd_regs;
    ptrace(PT_GETREGS, tid, (caddr_t) &bsd_regs, 0);
    map_regs_to_linux(&bsd_regs, &bsd_converted_regs);
#endif
}

long pt_debugger::peek_reg(int idx) {
#if defined(__FreeBSD__)
    return ((unsigned long*)&bsd_converted_regs)[idx];
#else
    return ptrace(PTRACE_PEEKUSER, tid, sizeof(long) * idx, 0);
#endif
}

void pt_debugger::poke_reg(int idx, long data) {
#if defined(__FreeBSD__)
    ((unsigned long*)&bsd_converted_regs)[idx] = data;

    struct reg bsd_regs;
    map_regs_from_linux(&bsd_regs, &bsd_converted_regs);
    ptrace(PT_SETREGS, tid, (caddr_t) &bsd_regs, 0);
#else
    ptrace(PTRACE_POKEUSER, tid, sizeof(long) * idx, data);
#endif
}

char *pt_debugger::readstr(unsigned long addr, size_t max_size) {
    size_t size = 4096, read = 0;
    char *buf = (char *) malloc(size);
    union {
        long val;
        char byte[sizeof(long)];
    } data;

    while (true) {
        if (read + sizeof(long) > size) {
            if (max_size && size >= max_size) {
                buf[max_size-1] = 0;
                break;
            }

            size += 4096;
            if (max_size && size > max_size)
                size = max_size;

            void *nbuf = realloc(buf, size);
            if (!nbuf) {
                buf[size-4097] = 0;
                break;
            }
            buf = (char *) nbuf;
        }
#if defined(__FreeBSD__)
        // TODO: we could use PT_IO to speed up this entire function by reading chunks rather than byte
        data.val = ptrace(PT_READ_D, tid, (caddr_t) (addr + read), 0);
#else
        data.val = ptrace(PTRACE_PEEKDATA, tid, addr + read, NULL);
#endif
        memcpy(buf + read, data.byte, sizeof(long));
        if (has_null(data.byte, sizeof(long)))
            break;
        read += sizeof(long);
    }
    return buf;
}

void pt_debugger::freestr(char *buf) {
    free(buf);
}

pt_debugger::~pt_debugger() {}
<|endoftext|>