<commit_before>/*
	Copyright (C) 2013-2014 by Kristina Simpson <sweet.kristas@gmail.com>

	This software is provided 'as-is', without any express or implied
	warranty. In no event will the authors be held liable for any damages
	arising from the use of this software.

	Permission is granted to anyone to use this software for any purpose,
	including commercial applications, and to alter it and redistribute it
	freely, subject to the following restrictions:

	   1. The origin of this software must not be misrepresented; you must not
	   claim that you wrote the original software. If you use this software
	   in a product, an acknowledgement in the product documentation would be
	   appreciated but is not required.

	   2. Altered source versions must be plainly marked as such, and must not be
	   misrepresented as being the original software.

	   3. This notice may not be removed or altered from any source
	   distribution.
*/

#pragma comment(lib, "SDL2")
#pragma comment(lib, "SDL2main")
#pragma comment(lib, "SDL2_image")

#include <cctype>
#include <sstream>

#include "asserts.hpp"
#include "DisplayDevice.hpp"
#include "SurfaceSDL.hpp"
#include "SDL.h"
#include "SDL_image.h"
#include "WindowManager.hpp"

namespace KRE
{
	namespace
	{
		typedef std::shared_ptr<SDL_Window> SDL_WindowPtr;

		uint32_t next_pow2(uint32_t v)
		{
			--v;
			v |= v >> 1;
			v |= v >> 2;
			v |= v >> 4;
			v |= v >> 8;
			v |= v >> 16;
			return ++v;
		}

		DisplayDevicePtr& current_display_device()
		{
			static DisplayDevicePtr res;
			return res;
		}
	}

	class SDLWindowManager : public WindowManager
	{
	public:
		SDLWindowManager(const std::string& title, const std::string& renderer_hint)
			: WindowManager(title),
			renderer_hint_(renderer_hint),
			renderer_(nullptr),
			context_(nullptr) {
			if(renderer_hint_.empty()) {
				renderer_hint_ = "opengl";
			}
			current_display_device() = display_ = DisplayDevice::factory(renderer_hint_);
			// XXX figure out a better way to pass this hint.
			SDL_SetHint(SDL_HINT_RENDER_DRIVER, renderer_hint_.c_str());
		}
		~SDLWindowManager() {
			//destroyWindow();
		}

		void doCreateWindow(int width, int height) override {
			logical_width_ = width_ = width;
			logical_height_ = height_ = height;

			Uint32 wnd_flags = 0;

			if(display_->ID() == DisplayDevice::DISPLAY_DEVICE_OPENGL) {
				// We need to do extra SDL set-up for an OpenGL context.
				// Since these parameter's need to be set-up before context
				// creation.
				SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
				SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
				SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
				SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
				if(use16bpp()) {
					SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
					SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
					SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
					SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 1);
				} else {
					SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
					SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
					SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
					SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
				}
				if(useMultiSampling()) {
					if(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1) != 0) {
						LOG_WARN("MSAA(" << multiSamples() << ") requested but mutlisample buffer couldn't be allocated.");
					} else {
						int msaa = next_pow2(multiSamples());
						if(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaa) != 0) {
							LOG_INFO("Requested MSAA of " << msaa << " but couldn't allocate");
						}
					}
				}
				wnd_flags |= SDL_WINDOW_OPENGL;
			}

			if(resizeable()) {
				wnd_flags |= SDL_WINDOW_RESIZABLE;
			}

			int x = SDL_WINDOWPOS_CENTERED;
			int y = SDL_WINDOWPOS_CENTERED;
			int w = width_;
			int h = height_;
			switch(fullscreenMode()) {
			case FullScreenMode::WINDOWED:		break;
			case FullScreenMode::FULLSCREEN_WINDOWED:
				x = y = SDL_WINDOWPOS_UNDEFINED;
				w = h = 0;
				wnd_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
				break;
			//case FullscreenMode::FULLSCREEN:
			//	x = y = SDL_WINDOWPOS_UNDEFINED;
			//	wnd_flags |= SDL_WINDOW_FULLSCREEN;
			//	break;
			}
			window_.reset(SDL_CreateWindow(getTitle().c_str(), x, y, w, h, wnd_flags), [&](SDL_Window* wnd){
				if(display_->ID() != DisplayDevice::DISPLAY_DEVICE_SDL) {
					SDL_DestroyRenderer(renderer_);
				}
				display_.reset();
				if(context_) {
					SDL_GL_DeleteContext(context_);
					context_ = nullptr;
				}
				SDL_DestroyWindow(wnd);
			});

			if(display_->ID() != DisplayDevice::DISPLAY_DEVICE_SDL) {
				Uint32 rnd_flags = SDL_RENDERER_ACCELERATED;
				if(vSync()) {
					rnd_flags |= SDL_RENDERER_PRESENTVSYNC;
				}
				renderer_ = SDL_CreateRenderer(window_.get(), -1, rnd_flags);
				ASSERT_LOG(renderer_ != nullptr, "Failed to create renderer: " << SDL_GetError());
			}

			ASSERT_LOG(window_ != nullptr, "Failed to create window: " << SDL_GetError());
			if(display_->ID() == DisplayDevice::DISPLAY_DEVICE_OPENGL) {
				context_ = SDL_GL_CreateContext(window_.get());
				ASSERT_LOG(context_ != nullptr, "Failed to GL Context: " << SDL_GetError());
			}

			display_->setClearColor(clear_color_);
			display_->printDeviceInfo();
			display_->init(width_, height_);
			display_->clear(ClearFlags::ALL);
			swap();
		}

		void doDestroyWindow() override {
			window_.reset();
		}

		void clear(ClearFlags f) override {
			display_->clear(ClearFlags::ALL);
		}

		void swap() override {
			// This is a little bit hacky -- ideally the display device should swap buffers.
			// But SDL provides a device independent way of doing it which is really nice.
			// So we use that.
			if(display_->ID() == DisplayDevice::DISPLAY_DEVICE_OPENGL) {
				SDL_GL_SwapWindow(window_.get());
			} else {
				// default to delegating to the display device.
				display_->swap();
			}
		}

		void setViewPort(int x, int y, int width, int height) override {
			display_->setViewPort(x, y, width, height);
		}

		unsigned getWindowID() const override {
			return SDL_GetWindowID(window_.get());
		}

		void setWindowIcon(const std::string& name) override {
			SurfaceSDL icon(name);
			SDL_SetWindowIcon(window_.get(), icon.get());
		}

		bool setWindowSize(int width, int height) override {
			SDL_SetWindowSize(window_.get(), width, height);
			width_ = width;
			height_ = height;
			return false;
		}

		bool autoWindowSize(int& width, int& height) override {
			// XXX
			return false;
		}

		void setWindowTitle(const std::string& title) override {
			ASSERT_LOG(window_ != nullptr, "Window is null");
			SDL_SetWindowTitle(window_.get(), title.c_str());
		}

		virtual void render(const Renderable* r) const override {
			ASSERT_LOG(display_ != nullptr, "No display to render to.");
			display_->render(r);
		}

		std::vector<WindowMode> getWindowModes(std::function<bool(const WindowMode&)> mode_filter) override {
			std::vector<WindowMode> res;
			const int display_index = SDL_GetWindowDisplayIndex(window_.get());
			const int nmodes = SDL_GetNumDisplayModes(display_index);
			for(int n = 0; n != nmodes; ++n) {
				SDL_DisplayMode new_mode;
				const int nvalue = SDL_GetDisplayMode(display_index, n, &new_mode);
				if(nvalue != 0) {
					LOG_ERROR("QUERYING DISPLAY INFO: " << SDL_GetError());
					continue;
				}
				WindowMode mode = { new_mode.w, new_mode.h, std::make_shared<SDLPixelFormat>(new_mode.format), new_mode.refresh_rate };
				// filter modes based on pixel format here
				if(mode_filter(mode)) {
					res.push_back(mode);
				}
			}
			return res;
		}
	protected:
	private:
		void handleSetClearColor() const override {
			if(display_ != nullptr) {
				display_->setClearColor(clear_color_);
			}
		}
		void changeFullscreenMode() override {
			// XXX
		}
		bool handleLogicalWindowSizeChange() override {
			// XXX do nothing for now
			return true;
		}

		bool handlePhysicalWindowSizeChange() override {
			// XXX do nothing for now
			return true;
		}

		SDL_WindowPtr window_;
		SDL_GLContext context_;
		SDL_Renderer* renderer_;
		std::string renderer_hint_;
		SDLWindowManager(const SDLWindowManager&);
	};

	WindowManager::WindowManager(const std::string& title)
		: width_(0),
		height_(0),
		logical_width_(0),
		logical_height_(0),
		use_16bpp_(false),
		use_multi_sampling_(false),
		samples_(4),
		is_resizeable_(false),
		title_(title),
		clear_color_(0.0f,0.0f,0.0f,1.0f)
	{
	}

	WindowManager::~WindowManager()
	{
	}

	void WindowManager::enable16bpp(bool bpp) {
		use_16bpp_ = bpp;
	}

	void WindowManager::enableMultisampling(bool multi_sampling, int samples) {
		use_multi_sampling_ = multi_sampling;
		samples_ = samples;
	}

	void WindowManager::enableResizeableWindow(bool en) {
		is_resizeable_ = en;
	}

	void WindowManager::setFullscreenMode(FullScreenMode mode)
	{
		bool modes_differ = fullscreen_mode_ != mode;
		fullscreen_mode_ = mode;
		if(modes_differ) {
			changeFullscreenMode();
		}
	}

	void WindowManager::enableVsync(bool en)
	{
		use_vsync_ = en;
	}

	void WindowManager::mapMousePosition(int* x, int* y)
	{
		if(x) {
			*x = int(*x * double(logical_width_) / width_);
		}
		if(y) {
			*y = int(*y * double(logical_height_) / height_);
		}
	}

	bool WindowManager::setLogicalWindowSize(int width, int height)
	{
		logical_width_ = width;
		logical_height_ = height;
		return handleLogicalWindowSizeChange();
	}

	void WindowManager::setClearColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) const
	{
		clear_color_ = Color(r,g,b,a);
		handleSetClearColor();
	}

	void WindowManager::setClearColor(float r, float g, float b, float a) const
	{
		clear_color_ = Color(r,g,b,a);
		handleSetClearColor();
	}

	void WindowManager::setClearColor(const Color& color) const
	{
		clear_color_ = color;
		handleSetClearColor();
	}

	namespace
	{
		std::map<unsigned,WindowManagerPtr>& get_window_list()
		{
			static std::map<unsigned,WindowManagerPtr> res;
			return res;
		}

		WindowManagerPtr main_window = nullptr;
	}

	void WindowManager::createWindow(int width, int height)
	{
		doCreateWindow(width, height);
	}

	void WindowManager::destroyWindow()
	{
		if(!get_window_list().empty()) {
			for(auto it = get_window_list().begin(); it != get_window_list().end(); ) {
				if(it->second.get() == this) {
					get_window_list().erase(it++);
				} else {
					++it;
				}
			}
		}
		doDestroyWindow();
	}

	void WindowManager::notifyNewWindowSize(int new_width, int new_height)
	{
		width_ = new_width;
		height_ = new_height;
		handlePhysicalWindowSizeChange();
	}

	//! Save the current window display to a file
	void WindowManager::saveFrameBuffer(const std::string& filename)
	{
		auto surface = Surface::create(width_, height_, PixelFormat::PF::PIXELFORMAT_RGB24);
		std::vector<glm::u8vec3> pixels;
		if(display_->readPixels(0, 0, width_, height_, ReadFormat::RGB, AttrFormat::UNSIGNED_BYTE, pixels)) {
			surface->writePixels(&pixels[0], pixels.size() * sizeof(glm::u8vec3));
			surface->savePng(filename);
			LOG_INFO("Saved screenshot to: " << filename);
		} else {
			LOG_ERROR("Failed to save screenshot");
		}
	}

	WindowManagerPtr WindowManager::createInstance(const std::string& title, const std::string& wnd_hint, const std::string& rend_hint)
	{
		// We really only support one sub-class of the window manager
		// at the moment, so we just return it. We could use hint in the
		// future if we had more.
		WindowManagerPtr wm = std::make_shared<SDLWindowManager>(title, rend_hint);
		get_window_list()[wm->getWindowID()] = wm;
		// We consider the first window created the main one.
		if(main_window == nullptr) {
			main_window = wm;
		}
		LOG_DEBUG("Added window with id: " << wm->getWindowID());
		return wm;
	}

	std::vector<WindowManagerPtr> WindowManager::getWindowList()
	{
		std::vector<WindowManagerPtr> res;
		auto it = get_window_list().begin();
		for(auto w : get_window_list()) {
			res.push_back(w.second);
		}
		return res;
	}

	WindowManagerPtr WindowManager::getMainWindow()
	{
		return main_window;
	}

	WindowManagerPtr WindowManager::getWindowFromID(unsigned id)
	{
		auto it = get_window_list().find(id);
		//ASSERT_LOG(it != get_window_list().end(), "Couldn't get window from id: " << id);
		if(it == get_window_list().end()) {
			return nullptr;
		}
		return it->second;
	}
}
<commit_msg>Fixed a bug where if the clear color was changed, by an fbo clear for example it effects the *global* clear color state.<commit_after>/*
	Copyright (C) 2013-2014 by Kristina Simpson <sweet.kristas@gmail.com>

	This software is provided 'as-is', without any express or implied
	warranty. In no event will the authors be held liable for any damages
	arising from the use of this software.

	Permission is granted to anyone to use this software for any purpose,
	including commercial applications, and to alter it and redistribute it
	freely, subject to the following restrictions:

	   1. The origin of this software must not be misrepresented; you must not
	   claim that you wrote the original software. If you use this software
	   in a product, an acknowledgement in the product documentation would be
	   appreciated but is not required.

	   2. Altered source versions must be plainly marked as such, and must not be
	   misrepresented as being the original software.

	   3. This notice may not be removed or altered from any source
	   distribution.
*/

#pragma comment(lib, "SDL2")
#pragma comment(lib, "SDL2main")
#pragma comment(lib, "SDL2_image")

#include <cctype>
#include <sstream>

#include "asserts.hpp"
#include "DisplayDevice.hpp"
#include "SurfaceSDL.hpp"
#include "SDL.h"
#include "SDL_image.h"
#include "WindowManager.hpp"

namespace KRE
{
	namespace
	{
		typedef std::shared_ptr<SDL_Window> SDL_WindowPtr;

		uint32_t next_pow2(uint32_t v)
		{
			--v;
			v |= v >> 1;
			v |= v >> 2;
			v |= v >> 4;
			v |= v >> 8;
			v |= v >> 16;
			return ++v;
		}

		DisplayDevicePtr& current_display_device()
		{
			static DisplayDevicePtr res;
			return res;
		}
	}

	class SDLWindowManager : public WindowManager
	{
	public:
		SDLWindowManager(const std::string& title, const std::string& renderer_hint)
			: WindowManager(title),
			renderer_hint_(renderer_hint),
			renderer_(nullptr),
			context_(nullptr) {
			if(renderer_hint_.empty()) {
				renderer_hint_ = "opengl";
			}
			current_display_device() = display_ = DisplayDevice::factory(renderer_hint_);
			// XXX figure out a better way to pass this hint.
			SDL_SetHint(SDL_HINT_RENDER_DRIVER, renderer_hint_.c_str());
		}
		~SDLWindowManager() {
			//destroyWindow();
		}

		void doCreateWindow(int width, int height) override {
			logical_width_ = width_ = width;
			logical_height_ = height_ = height;

			Uint32 wnd_flags = 0;

			if(display_->ID() == DisplayDevice::DISPLAY_DEVICE_OPENGL) {
				// We need to do extra SDL set-up for an OpenGL context.
				// Since these parameter's need to be set-up before context
				// creation.
				SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
				SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
				SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
				SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
				if(use16bpp()) {
					SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
					SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
					SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
					SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 1);
				} else {
					SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
					SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
					SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
					SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
				}
				if(useMultiSampling()) {
					if(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1) != 0) {
						LOG_WARN("MSAA(" << multiSamples() << ") requested but mutlisample buffer couldn't be allocated.");
					} else {
						int msaa = next_pow2(multiSamples());
						if(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaa) != 0) {
							LOG_INFO("Requested MSAA of " << msaa << " but couldn't allocate");
						}
					}
				}
				wnd_flags |= SDL_WINDOW_OPENGL;
			}

			if(resizeable()) {
				wnd_flags |= SDL_WINDOW_RESIZABLE;
			}

			int x = SDL_WINDOWPOS_CENTERED;
			int y = SDL_WINDOWPOS_CENTERED;
			int w = width_;
			int h = height_;
			switch(fullscreenMode()) {
			case FullScreenMode::WINDOWED:		break;
			case FullScreenMode::FULLSCREEN_WINDOWED:
				x = y = SDL_WINDOWPOS_UNDEFINED;
				w = h = 0;
				wnd_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
				break;
			//case FullscreenMode::FULLSCREEN:
			//	x = y = SDL_WINDOWPOS_UNDEFINED;
			//	wnd_flags |= SDL_WINDOW_FULLSCREEN;
			//	break;
			}
			window_.reset(SDL_CreateWindow(getTitle().c_str(), x, y, w, h, wnd_flags), [&](SDL_Window* wnd){
				if(display_->ID() != DisplayDevice::DISPLAY_DEVICE_SDL) {
					SDL_DestroyRenderer(renderer_);
				}
				display_.reset();
				if(context_) {
					SDL_GL_DeleteContext(context_);
					context_ = nullptr;
				}
				SDL_DestroyWindow(wnd);
			});

			if(display_->ID() != DisplayDevice::DISPLAY_DEVICE_SDL) {
				Uint32 rnd_flags = SDL_RENDERER_ACCELERATED;
				if(vSync()) {
					rnd_flags |= SDL_RENDERER_PRESENTVSYNC;
				}
				renderer_ = SDL_CreateRenderer(window_.get(), -1, rnd_flags);
				ASSERT_LOG(renderer_ != nullptr, "Failed to create renderer: " << SDL_GetError());
			}

			ASSERT_LOG(window_ != nullptr, "Failed to create window: " << SDL_GetError());
			if(display_->ID() == DisplayDevice::DISPLAY_DEVICE_OPENGL) {
				context_ = SDL_GL_CreateContext(window_.get());
				ASSERT_LOG(context_ != nullptr, "Failed to GL Context: " << SDL_GetError());
			}

			display_->setClearColor(clear_color_);
			display_->printDeviceInfo();
			display_->init(width_, height_);
			display_->clear(ClearFlags::ALL);
			swap();
		}

		void doDestroyWindow() override {
			window_.reset();
		}

		void clear(ClearFlags f) override {
			// N.B. Clear color is global GL state, so we need to re-configure it everytime we clear.
			// Since it may have changed by some sneaky render target user.
			display_->setClearColor(clear_color_);
			display_->clear(ClearFlags::ALL);
		}

		void swap() override {
			// This is a little bit hacky -- ideally the display device should swap buffers.
			// But SDL provides a device independent way of doing it which is really nice.
			// So we use that.
			if(display_->ID() == DisplayDevice::DISPLAY_DEVICE_OPENGL) {
				SDL_GL_SwapWindow(window_.get());
			} else {
				// default to delegating to the display device.
				display_->swap();
			}
		}

		void setViewPort(int x, int y, int width, int height) override {
			display_->setViewPort(x, y, width, height);
		}

		unsigned getWindowID() const override {
			return SDL_GetWindowID(window_.get());
		}

		void setWindowIcon(const std::string& name) override {
			SurfaceSDL icon(name);
			SDL_SetWindowIcon(window_.get(), icon.get());
		}

		bool setWindowSize(int width, int height) override {
			SDL_SetWindowSize(window_.get(), width, height);
			width_ = width;
			height_ = height;
			return false;
		}

		bool autoWindowSize(int& width, int& height) override {
			// XXX
			return false;
		}

		void setWindowTitle(const std::string& title) override {
			ASSERT_LOG(window_ != nullptr, "Window is null");
			SDL_SetWindowTitle(window_.get(), title.c_str());
		}

		virtual void render(const Renderable* r) const override {
			ASSERT_LOG(display_ != nullptr, "No display to render to.");
			display_->render(r);
		}

		std::vector<WindowMode> getWindowModes(std::function<bool(const WindowMode&)> mode_filter) override {
			std::vector<WindowMode> res;
			const int display_index = SDL_GetWindowDisplayIndex(window_.get());
			const int nmodes = SDL_GetNumDisplayModes(display_index);
			for(int n = 0; n != nmodes; ++n) {
				SDL_DisplayMode new_mode;
				const int nvalue = SDL_GetDisplayMode(display_index, n, &new_mode);
				if(nvalue != 0) {
					LOG_ERROR("QUERYING DISPLAY INFO: " << SDL_GetError());
					continue;
				}
				WindowMode mode = { new_mode.w, new_mode.h, std::make_shared<SDLPixelFormat>(new_mode.format), new_mode.refresh_rate };
				// filter modes based on pixel format here
				if(mode_filter(mode)) {
					res.push_back(mode);
				}
			}
			return res;
		}
	protected:
	private:
		void handleSetClearColor() const override {
			if(display_ != nullptr) {
				display_->setClearColor(clear_color_);
			}
		}
		void changeFullscreenMode() override {
			// XXX
		}
		bool handleLogicalWindowSizeChange() override {
			// XXX do nothing for now
			return true;
		}

		bool handlePhysicalWindowSizeChange() override {
			// XXX do nothing for now
			return true;
		}

		SDL_WindowPtr window_;
		SDL_GLContext context_;
		SDL_Renderer* renderer_;
		std::string renderer_hint_;
		SDLWindowManager(const SDLWindowManager&);
	};

	WindowManager::WindowManager(const std::string& title)
		: width_(0),
		height_(0),
		logical_width_(0),
		logical_height_(0),
		use_16bpp_(false),
		use_multi_sampling_(false),
		samples_(4),
		is_resizeable_(false),
		title_(title),
		clear_color_(0.0f,0.0f,0.0f,1.0f)
	{
	}

	WindowManager::~WindowManager()
	{
	}

	void WindowManager::enable16bpp(bool bpp) {
		use_16bpp_ = bpp;
	}

	void WindowManager::enableMultisampling(bool multi_sampling, int samples) {
		use_multi_sampling_ = multi_sampling;
		samples_ = samples;
	}

	void WindowManager::enableResizeableWindow(bool en) {
		is_resizeable_ = en;
	}

	void WindowManager::setFullscreenMode(FullScreenMode mode)
	{
		bool modes_differ = fullscreen_mode_ != mode;
		fullscreen_mode_ = mode;
		if(modes_differ) {
			changeFullscreenMode();
		}
	}

	void WindowManager::enableVsync(bool en)
	{
		use_vsync_ = en;
	}

	void WindowManager::mapMousePosition(int* x, int* y)
	{
		if(x) {
			*x = int(*x * double(logical_width_) / width_);
		}
		if(y) {
			*y = int(*y * double(logical_height_) / height_);
		}
	}

	bool WindowManager::setLogicalWindowSize(int width, int height)
	{
		logical_width_ = width;
		logical_height_ = height;
		return handleLogicalWindowSizeChange();
	}

	void WindowManager::setClearColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) const
	{
		clear_color_ = Color(r,g,b,a);
		handleSetClearColor();
	}

	void WindowManager::setClearColor(float r, float g, float b, float a) const
	{
		clear_color_ = Color(r,g,b,a);
		handleSetClearColor();
	}

	void WindowManager::setClearColor(const Color& color) const
	{
		clear_color_ = color;
		handleSetClearColor();
	}

	namespace
	{
		std::map<unsigned,WindowManagerPtr>& get_window_list()
		{
			static std::map<unsigned,WindowManagerPtr> res;
			return res;
		}

		WindowManagerPtr main_window = nullptr;
	}

	void WindowManager::createWindow(int width, int height)
	{
		doCreateWindow(width, height);
	}

	void WindowManager::destroyWindow()
	{
		if(!get_window_list().empty()) {
			for(auto it = get_window_list().begin(); it != get_window_list().end(); ) {
				if(it->second.get() == this) {
					get_window_list().erase(it++);
				} else {
					++it;
				}
			}
		}
		doDestroyWindow();
	}

	void WindowManager::notifyNewWindowSize(int new_width, int new_height)
	{
		width_ = new_width;
		height_ = new_height;
		handlePhysicalWindowSizeChange();
	}

	//! Save the current window display to a file
	void WindowManager::saveFrameBuffer(const std::string& filename)
	{
		auto surface = Surface::create(width_, height_, PixelFormat::PF::PIXELFORMAT_RGB24);
		std::vector<glm::u8vec3> pixels;
		if(display_->readPixels(0, 0, width_, height_, ReadFormat::RGB, AttrFormat::UNSIGNED_BYTE, pixels)) {
			surface->writePixels(&pixels[0], pixels.size() * sizeof(glm::u8vec3));
			surface->savePng(filename);
			LOG_INFO("Saved screenshot to: " << filename);
		} else {
			LOG_ERROR("Failed to save screenshot");
		}
	}

	WindowManagerPtr WindowManager::createInstance(const std::string& title, const std::string& wnd_hint, const std::string& rend_hint)
	{
		// We really only support one sub-class of the window manager
		// at the moment, so we just return it. We could use hint in the
		// future if we had more.
		WindowManagerPtr wm = std::make_shared<SDLWindowManager>(title, rend_hint);
		get_window_list()[wm->getWindowID()] = wm;
		// We consider the first window created the main one.
		if(main_window == nullptr) {
			main_window = wm;
		}
		LOG_DEBUG("Added window with id: " << wm->getWindowID());
		return wm;
	}

	std::vector<WindowManagerPtr> WindowManager::getWindowList()
	{
		std::vector<WindowManagerPtr> res;
		auto it = get_window_list().begin();
		for(auto w : get_window_list()) {
			res.push_back(w.second);
		}
		return res;
	}

	WindowManagerPtr WindowManager::getMainWindow()
	{
		return main_window;
	}

	WindowManagerPtr WindowManager::getWindowFromID(unsigned id)
	{
		auto it = get_window_list().find(id);
		//ASSERT_LOG(it != get_window_list().end(), "Couldn't get window from id: " << id);
		if(it == get_window_list().end()) {
			return nullptr;
		}
		return it->second;
	}
}
<|endoftext|>


<commit_before>#include "httpp/utils/Thread.hpp"

#include <thread>
#include <sstream>

#if !HAS_THREAD_LOCAL_SPECIFIER
# include <boost/thread/tss.hpp>
#endif

namespace HTTPP
{
namespace UTILS
{

#if HAS_THREAD_LOCAL_SPECIFIER
static thread_local std::string current_name;
#else
static boost::thread_specific_ptr<std::string> current_name;
#endif

void setCurrentThreadName(const std::string& name)
{
#if HAS_THREAD_LOCAL_SPECIFIER
    current_name = name;
#else
    current_name.reset(new std::string(name));
#endif
}

const std::string& getCurrentThreadName()
{
#if HAS_THREAD_LOCAL_SPECIFIER
    if (!current_name.empty())
    {
        return current_name;
    }
#else
    static const std::string empty;
    if (current_name.get())
    {
        return *current_name;
    }
#endif

    std::ostringstream out;
    out << std::this_thread::get_id();
    setCurrentThreadName(out.str());

    return getCurrentThreadName();
}

} // namespace utils
} // namespace httpp
<commit_msg>Add missing header<commit_after>/*
 * Part of HTTPP.
 *
 * Distributed under the 3-clause BSD licence (See LICENCE.TXT file at the
 * project root).
 *
 * Copyright (c) 2014 Thomas Sanchez.  All rights reserved.
 *
 */

#include "httpp/utils/Thread.hpp"

#include <thread>
#include <sstream>

#if !HAS_THREAD_LOCAL_SPECIFIER
# include <boost/thread/tss.hpp>
#endif

namespace HTTPP
{
namespace UTILS
{

#if HAS_THREAD_LOCAL_SPECIFIER
static thread_local std::string current_name;
#else
static boost::thread_specific_ptr<std::string> current_name;
#endif

void setCurrentThreadName(const std::string& name)
{
#if HAS_THREAD_LOCAL_SPECIFIER
    current_name = name;
#else
    current_name.reset(new std::string(name));
#endif
}

const std::string& getCurrentThreadName()
{
#if HAS_THREAD_LOCAL_SPECIFIER
    if (!current_name.empty())
    {
        return current_name;
    }
#else
    static const std::string empty;
    if (current_name.get())
    {
        return *current_name;
    }
#endif

    std::ostringstream out;
    out << std::this_thread::get_id();
    setCurrentThreadName(out.str());

    return getCurrentThreadName();
}

} // namespace utils
} // namespace httpp
<|endoftext|>


<commit_before>
/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkMovie.h"
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "SkUtils.h"

#include "gif_lib.h"

#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
#define DGifCloseFile(a, b) DGifCloseFile(a)
#endif

class SkGIFMovie : public SkMovie {
public:
    SkGIFMovie(SkStream* stream);
    virtual ~SkGIFMovie();

protected:
    virtual bool onGetInfo(Info*);
    virtual bool onSetTime(SkMSec);
    virtual bool onGetBitmap(SkBitmap*);

private:
    GifFileType* fGIF;
    int fCurrIndex;
    int fLastDrawIndex;
    SkBitmap fBackup;
    SkColor fPaintingColor;
};

static int Decode(GifFileType* fileType, GifByteType* out, int size) {
    SkStream* stream = (SkStream*) fileType->UserData;
    return (int) stream->read(out, size);
}

SkGIFMovie::SkGIFMovie(SkStream* stream)
{
#if GIFLIB_MAJOR < 5
    fGIF = DGifOpen( stream, Decode );
#else
    fGIF = DGifOpen( stream, Decode, nullptr );
#endif
    if (nullptr == fGIF)
        return;

    if (DGifSlurp(fGIF) != GIF_OK)
    {
        DGifCloseFile(fGIF, nullptr);
        fGIF = nullptr;
    }
    fCurrIndex = -1;
    fLastDrawIndex = -1;
    fPaintingColor = SkPackARGB32(0, 0, 0, 0);
}

SkGIFMovie::~SkGIFMovie()
{
    if (fGIF)
        DGifCloseFile(fGIF, nullptr);
}

static SkMSec savedimage_duration(const SavedImage* image)
{
    for (int j = 0; j < image->ExtensionBlockCount; j++)
    {
        if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
        {
            SkASSERT(image->ExtensionBlocks[j].ByteCount >= 4);
            const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
            return ((b[2] << 8) | b[1]) * 10;
        }
    }
    return 0;
}

bool SkGIFMovie::onGetInfo(Info* info)
{
    if (nullptr == fGIF)
        return false;

    SkMSec dur = 0;
    for (int i = 0; i < fGIF->ImageCount; i++)
        dur += savedimage_duration(&fGIF->SavedImages[i]);

    info->fDuration = dur;
    info->fWidth = fGIF->SWidth;
    info->fHeight = fGIF->SHeight;
    info->fIsOpaque = false;    // how to compute?
    return true;
}

bool SkGIFMovie::onSetTime(SkMSec time)
{
    if (nullptr == fGIF)
        return false;

    SkMSec dur = 0;
    for (int i = 0; i < fGIF->ImageCount; i++)
    {
        dur += savedimage_duration(&fGIF->SavedImages[i]);
        if (dur >= time)
        {
            fCurrIndex = i;
            return fLastDrawIndex != fCurrIndex;
        }
    }
    fCurrIndex = fGIF->ImageCount - 1;
    return true;
}

static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap,
                     int transparent, int width)
{
    for (; width > 0; width--, src++, dst++) {
        if (*src != transparent) {
            const GifColorType& col = cmap->Colors[*src];
            *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
        }
    }
}

#if GIFLIB_MAJOR < 5
static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src,
                               const ColorMapObject* cmap, int transparent, int copyWidth,
                               int copyHeight, const GifImageDesc& imageDesc, int rowStep,
                               int startRow)
{
    int row;
    // every 'rowStep'th row, starting with row 'startRow'
    for (row = startRow; row < copyHeight; row += rowStep) {
        uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row);
        copyLine(dst, src, cmap, transparent, copyWidth);
        src += imageDesc.Width;
    }

    // pad for rest height
    src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep);
}

static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
                          int transparent)
{
    int width = bm->width();
    int height = bm->height();
    GifWord copyWidth = frame->ImageDesc.Width;
    if (frame->ImageDesc.Left + copyWidth > width) {
        copyWidth = width - frame->ImageDesc.Left;
    }

    GifWord copyHeight = frame->ImageDesc.Height;
    if (frame->ImageDesc.Top + copyHeight > height) {
        copyHeight = height - frame->ImageDesc.Top;
    }

    // deinterlace
    const unsigned char* src = (unsigned char*)frame->RasterBits;

    // group 1 - every 8th row, starting with row 0
    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0);

    // group 2 - every 8th row, starting with row 4
    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4);

    // group 3 - every 4th row, starting with row 2
    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2);

    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1);
}
#endif

static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
                       int transparent)
{
    int width = bm->width();
    int height = bm->height();
    const unsigned char* src = (unsigned char*)frame->RasterBits;
    uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top);
    GifWord copyWidth = frame->ImageDesc.Width;
    if (frame->ImageDesc.Left + copyWidth > width) {
        copyWidth = width - frame->ImageDesc.Left;
    }

    GifWord copyHeight = frame->ImageDesc.Height;
    if (frame->ImageDesc.Top + copyHeight > height) {
        copyHeight = height - frame->ImageDesc.Top;
    }

    for (; copyHeight > 0; copyHeight--) {
        copyLine(dst, src, cmap, transparent, copyWidth);
        src += frame->ImageDesc.Width;
        dst += width;
    }
}

static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height,
                     uint32_t col)
{
    int bmWidth = bm->width();
    int bmHeight = bm->height();
    uint32_t* dst = bm->getAddr32(left, top);
    GifWord copyWidth = width;
    if (left + copyWidth > bmWidth) {
        copyWidth = bmWidth - left;
    }

    GifWord copyHeight = height;
    if (top + copyHeight > bmHeight) {
        copyHeight = bmHeight - top;
    }

    for (; copyHeight > 0; copyHeight--) {
        sk_memset32(dst, col, copyWidth);
        dst += bmWidth;
    }
}

static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap)
{
    int transparent = -1;

    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
        ExtensionBlock* eb = frame->ExtensionBlocks + i;
        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
            eb->ByteCount == 4) {
            bool has_transparency = ((eb->Bytes[0] & 1) == 1);
            if (has_transparency) {
                transparent = (unsigned char)eb->Bytes[3];
            }
        }
    }

    if (frame->ImageDesc.ColorMap != nullptr) {
        // use local color table
        cmap = frame->ImageDesc.ColorMap;
    }

    if (cmap == nullptr || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
        SkDEBUGFAIL("bad colortable setup");
        return;
    }

#if GIFLIB_MAJOR < 5
    // before GIFLIB 5, de-interlacing wasn't done by library at load time
    if (frame->ImageDesc.Interlace) {
        blitInterlace(bm, frame, cmap, transparent);
        return;
    }
#endif

    blitNormal(bm, frame, cmap, transparent);
}

static bool checkIfWillBeCleared(const SavedImage* frame)
{
    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
        ExtensionBlock* eb = frame->ExtensionBlocks + i;
        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
            eb->ByteCount == 4) {
            // check disposal method
            int disposal = ((eb->Bytes[0] >> 2) & 7);
            if (disposal == 2 || disposal == 3) {
                return true;
            }
        }
    }
    return false;
}

static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal)
{
    *trans = false;
    *disposal = 0;
    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
        ExtensionBlock* eb = frame->ExtensionBlocks + i;
        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
            eb->ByteCount == 4) {
            *trans = ((eb->Bytes[0] & 1) == 1);
            *disposal = ((eb->Bytes[0] >> 2) & 7);
        }
    }
}

// return true if area of 'target' is completely covers area of 'covered'
static bool checkIfCover(const SavedImage* target, const SavedImage* covered)
{
    if (target->ImageDesc.Left <= covered->ImageDesc.Left
        && covered->ImageDesc.Left + covered->ImageDesc.Width <=
               target->ImageDesc.Left + target->ImageDesc.Width
        && target->ImageDesc.Top <= covered->ImageDesc.Top
        && covered->ImageDesc.Top + covered->ImageDesc.Height <=
               target->ImageDesc.Top + target->ImageDesc.Height) {
        return true;
    }
    return false;
}

static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next,
                                 SkBitmap* backup, SkColor color)
{
    // We can skip disposal process if next frame is not transparent
    // and completely covers current area
    bool curTrans;
    int curDisposal;
    getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal);
    bool nextTrans;
    int nextDisposal;
    getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal);
    if ((curDisposal == 2 || curDisposal == 3)
        && (nextTrans || !checkIfCover(next, cur))) {
        switch (curDisposal) {
        // restore to background color
        // -> 'background' means background under this image.
        case 2:
            fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top,
                     cur->ImageDesc.Width, cur->ImageDesc.Height,
                     color);
            break;

        // restore to previous
        case 3:
            bm->swap(*backup);
            break;
        }
    }

    // Save current image if next frame's disposal method == 3
    if (nextDisposal == 3) {
        const uint32_t* src = bm->getAddr32(0, 0);
        uint32_t* dst = backup->getAddr32(0, 0);
        int cnt = bm->width() * bm->height();
        memcpy(dst, src, cnt*sizeof(uint32_t));
    }
}

bool SkGIFMovie::onGetBitmap(SkBitmap* bm)
{
    const GifFileType* gif = fGIF;
    if (nullptr == gif)
        return false;

    if (gif->ImageCount < 1) {
        return false;
    }

    const int width = gif->SWidth;
    const int height = gif->SHeight;
    if (width <= 0 || height <= 0) {
        return false;
    }

    // no need to draw
    if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) {
        return true;
    }

    int startIndex = fLastDrawIndex + 1;
    if (fLastDrawIndex < 0 || !bm->readyToDraw()) {
        // first time

        startIndex = 0;

        // create bitmap
        if (!bm->tryAllocN32Pixels(width, height)) {
            return false;
        }
        // create bitmap for backup
        if (!fBackup.tryAllocN32Pixels(width, height)) {
            return false;
        }
    } else if (startIndex > fCurrIndex) {
        // rewind to 1st frame for repeat
        startIndex = 0;
    }

    int lastIndex = fCurrIndex;
    if (lastIndex < 0) {
        // first time
        lastIndex = 0;
    } else if (lastIndex > fGIF->ImageCount - 1) {
        // this block must not be reached.
        lastIndex = fGIF->ImageCount - 1;
    }

    SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
    if (gif->SColorMap != nullptr) {
        const GifColorType& col = gif->SColorMap->Colors[fGIF->SBackGroundColor];
        bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
    }

    // draw each frames - not intelligent way
    for (int i = startIndex; i <= lastIndex; i++) {
        const SavedImage* cur = &fGIF->SavedImages[i];
        if (i == 0) {
            bool trans;
            int disposal;
            getTransparencyAndDisposalMethod(cur, &trans, &disposal);
            if (!trans && gif->SColorMap != nullptr) {
                fPaintingColor = bgColor;
            } else {
                fPaintingColor = SkColorSetARGB(0, 0, 0, 0);
            }

            bm->eraseColor(fPaintingColor);
            fBackup.eraseColor(fPaintingColor);
        } else {
            // Dispose previous frame before move to next frame.
            const SavedImage* prev = &fGIF->SavedImages[i-1];
            disposeFrameIfNeeded(bm, prev, cur, &fBackup, fPaintingColor);
        }

        // Draw frame
        // We can skip this process if this index is not last and disposal
        // method == 2 or method == 3
        if (i == lastIndex || !checkIfWillBeCleared(cur)) {
            drawFrame(bm, cur, gif->SColorMap);
        }
    }

    // save index
    fLastDrawIndex = lastIndex;
    return true;
}

///////////////////////////////////////////////////////////////////////////////

#include "SkTRegistry.h"

SkMovie* Factory(SkStreamRewindable* stream) {
    char buf[GIF_STAMP_LEN];
    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
            // must rewind here, since our construct wants to re-read the data
            stream->rewind();
            return new SkGIFMovie(stream);
        }
    }
    return nullptr;
}

static SkTRegistry<SkMovie*(*)(SkStreamRewindable*)> gReg(Factory);
<commit_msg>Fix out of bounds memory read in GIFMovie.cpp am: 16882f7212 am: d2456e3a12 am: ffc0958605 am: df18259ff1 am: c9b947def7 am: 09ad23b21e am: 716de6b276 am: d1fb426850 am: d80a31cff8 am: 8b8fbb1e9c am: f054eb2904 am: 270a480de0<commit_after>
/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkMovie.h"
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "SkUtils.h"

#include "gif_lib.h"

#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
#define DGifCloseFile(a, b) DGifCloseFile(a)
#endif

class SkGIFMovie : public SkMovie {
public:
    SkGIFMovie(SkStream* stream);
    virtual ~SkGIFMovie();

protected:
    virtual bool onGetInfo(Info*);
    virtual bool onSetTime(SkMSec);
    virtual bool onGetBitmap(SkBitmap*);

private:
    GifFileType* fGIF;
    int fCurrIndex;
    int fLastDrawIndex;
    SkBitmap fBackup;
    SkColor fPaintingColor;
};

static int Decode(GifFileType* fileType, GifByteType* out, int size) {
    SkStream* stream = (SkStream*) fileType->UserData;
    return (int) stream->read(out, size);
}

SkGIFMovie::SkGIFMovie(SkStream* stream)
{
#if GIFLIB_MAJOR < 5
    fGIF = DGifOpen( stream, Decode );
#else
    fGIF = DGifOpen( stream, Decode, nullptr );
#endif
    if (nullptr == fGIF)
        return;

    if (DGifSlurp(fGIF) != GIF_OK)
    {
        DGifCloseFile(fGIF, nullptr);
        fGIF = nullptr;
    }
    fCurrIndex = -1;
    fLastDrawIndex = -1;
    fPaintingColor = SkPackARGB32(0, 0, 0, 0);
}

SkGIFMovie::~SkGIFMovie()
{
    if (fGIF)
        DGifCloseFile(fGIF, nullptr);
}

static SkMSec savedimage_duration(const SavedImage* image)
{
    for (int j = 0; j < image->ExtensionBlockCount; j++)
    {
        if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
        {
            SkASSERT(image->ExtensionBlocks[j].ByteCount >= 4);
            const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
            return ((b[2] << 8) | b[1]) * 10;
        }
    }
    return 0;
}

bool SkGIFMovie::onGetInfo(Info* info)
{
    if (nullptr == fGIF)
        return false;

    SkMSec dur = 0;
    for (int i = 0; i < fGIF->ImageCount; i++)
        dur += savedimage_duration(&fGIF->SavedImages[i]);

    info->fDuration = dur;
    info->fWidth = fGIF->SWidth;
    info->fHeight = fGIF->SHeight;
    info->fIsOpaque = false;    // how to compute?
    return true;
}

bool SkGIFMovie::onSetTime(SkMSec time)
{
    if (nullptr == fGIF)
        return false;

    SkMSec dur = 0;
    for (int i = 0; i < fGIF->ImageCount; i++)
    {
        dur += savedimage_duration(&fGIF->SavedImages[i]);
        if (dur >= time)
        {
            fCurrIndex = i;
            return fLastDrawIndex != fCurrIndex;
        }
    }
    fCurrIndex = fGIF->ImageCount - 1;
    return true;
}

static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap,
                     int transparent, int width)
{
    for (; width > 0; width--, src++, dst++) {
        if (*src != transparent && *src < cmap->ColorCount) {
            const GifColorType& col = cmap->Colors[*src];
            *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
        }
    }
}

#if GIFLIB_MAJOR < 5
static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src,
                               const ColorMapObject* cmap, int transparent, int copyWidth,
                               int copyHeight, const GifImageDesc& imageDesc, int rowStep,
                               int startRow)
{
    int row;
    // every 'rowStep'th row, starting with row 'startRow'
    for (row = startRow; row < copyHeight; row += rowStep) {
        uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row);
        copyLine(dst, src, cmap, transparent, copyWidth);
        src += imageDesc.Width;
    }

    // pad for rest height
    src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep);
}

static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
                          int transparent)
{
    int width = bm->width();
    int height = bm->height();
    GifWord copyWidth = frame->ImageDesc.Width;
    if (frame->ImageDesc.Left + copyWidth > width) {
        copyWidth = width - frame->ImageDesc.Left;
    }

    GifWord copyHeight = frame->ImageDesc.Height;
    if (frame->ImageDesc.Top + copyHeight > height) {
        copyHeight = height - frame->ImageDesc.Top;
    }

    // deinterlace
    const unsigned char* src = (unsigned char*)frame->RasterBits;

    // group 1 - every 8th row, starting with row 0
    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0);

    // group 2 - every 8th row, starting with row 4
    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4);

    // group 3 - every 4th row, starting with row 2
    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2);

    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1);
}
#endif

static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
                       int transparent)
{
    int width = bm->width();
    int height = bm->height();
    const unsigned char* src = (unsigned char*)frame->RasterBits;
    uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top);
    GifWord copyWidth = frame->ImageDesc.Width;
    if (frame->ImageDesc.Left + copyWidth > width) {
        copyWidth = width - frame->ImageDesc.Left;
    }

    GifWord copyHeight = frame->ImageDesc.Height;
    if (frame->ImageDesc.Top + copyHeight > height) {
        copyHeight = height - frame->ImageDesc.Top;
    }

    for (; copyHeight > 0; copyHeight--) {
        copyLine(dst, src, cmap, transparent, copyWidth);
        src += frame->ImageDesc.Width;
        dst += width;
    }
}

static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height,
                     uint32_t col)
{
    int bmWidth = bm->width();
    int bmHeight = bm->height();
    uint32_t* dst = bm->getAddr32(left, top);
    GifWord copyWidth = width;
    if (left + copyWidth > bmWidth) {
        copyWidth = bmWidth - left;
    }

    GifWord copyHeight = height;
    if (top + copyHeight > bmHeight) {
        copyHeight = bmHeight - top;
    }

    for (; copyHeight > 0; copyHeight--) {
        sk_memset32(dst, col, copyWidth);
        dst += bmWidth;
    }
}

static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap)
{
    int transparent = -1;

    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
        ExtensionBlock* eb = frame->ExtensionBlocks + i;
        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
            eb->ByteCount == 4) {
            bool has_transparency = ((eb->Bytes[0] & 1) == 1);
            if (has_transparency) {
                transparent = (unsigned char)eb->Bytes[3];
            }
        }
    }

    if (frame->ImageDesc.ColorMap != nullptr) {
        // use local color table
        cmap = frame->ImageDesc.ColorMap;
    }

    if (cmap == nullptr || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
        SkDEBUGFAIL("bad colortable setup");
        return;
    }

#if GIFLIB_MAJOR < 5
    // before GIFLIB 5, de-interlacing wasn't done by library at load time
    if (frame->ImageDesc.Interlace) {
        blitInterlace(bm, frame, cmap, transparent);
        return;
    }
#endif

    blitNormal(bm, frame, cmap, transparent);
}

static bool checkIfWillBeCleared(const SavedImage* frame)
{
    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
        ExtensionBlock* eb = frame->ExtensionBlocks + i;
        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
            eb->ByteCount == 4) {
            // check disposal method
            int disposal = ((eb->Bytes[0] >> 2) & 7);
            if (disposal == 2 || disposal == 3) {
                return true;
            }
        }
    }
    return false;
}

static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal)
{
    *trans = false;
    *disposal = 0;
    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
        ExtensionBlock* eb = frame->ExtensionBlocks + i;
        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
            eb->ByteCount == 4) {
            *trans = ((eb->Bytes[0] & 1) == 1);
            *disposal = ((eb->Bytes[0] >> 2) & 7);
        }
    }
}

// return true if area of 'target' is completely covers area of 'covered'
static bool checkIfCover(const SavedImage* target, const SavedImage* covered)
{
    if (target->ImageDesc.Left <= covered->ImageDesc.Left
        && covered->ImageDesc.Left + covered->ImageDesc.Width <=
               target->ImageDesc.Left + target->ImageDesc.Width
        && target->ImageDesc.Top <= covered->ImageDesc.Top
        && covered->ImageDesc.Top + covered->ImageDesc.Height <=
               target->ImageDesc.Top + target->ImageDesc.Height) {
        return true;
    }
    return false;
}

static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next,
                                 SkBitmap* backup, SkColor color)
{
    // We can skip disposal process if next frame is not transparent
    // and completely covers current area
    bool curTrans;
    int curDisposal;
    getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal);
    bool nextTrans;
    int nextDisposal;
    getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal);
    if ((curDisposal == 2 || curDisposal == 3)
        && (nextTrans || !checkIfCover(next, cur))) {
        switch (curDisposal) {
        // restore to background color
        // -> 'background' means background under this image.
        case 2:
            fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top,
                     cur->ImageDesc.Width, cur->ImageDesc.Height,
                     color);
            break;

        // restore to previous
        case 3:
            bm->swap(*backup);
            break;
        }
    }

    // Save current image if next frame's disposal method == 3
    if (nextDisposal == 3) {
        const uint32_t* src = bm->getAddr32(0, 0);
        uint32_t* dst = backup->getAddr32(0, 0);
        int cnt = bm->width() * bm->height();
        memcpy(dst, src, cnt*sizeof(uint32_t));
    }
}

bool SkGIFMovie::onGetBitmap(SkBitmap* bm)
{
    const GifFileType* gif = fGIF;
    if (nullptr == gif)
        return false;

    if (gif->ImageCount < 1) {
        return false;
    }

    const int width = gif->SWidth;
    const int height = gif->SHeight;
    if (width <= 0 || height <= 0) {
        return false;
    }

    // no need to draw
    if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) {
        return true;
    }

    int startIndex = fLastDrawIndex + 1;
    if (fLastDrawIndex < 0 || !bm->readyToDraw()) {
        // first time

        startIndex = 0;

        // create bitmap
        if (!bm->tryAllocN32Pixels(width, height)) {
            return false;
        }
        // create bitmap for backup
        if (!fBackup.tryAllocN32Pixels(width, height)) {
            return false;
        }
    } else if (startIndex > fCurrIndex) {
        // rewind to 1st frame for repeat
        startIndex = 0;
    }

    int lastIndex = fCurrIndex;
    if (lastIndex < 0) {
        // first time
        lastIndex = 0;
    } else if (lastIndex > fGIF->ImageCount - 1) {
        // this block must not be reached.
        lastIndex = fGIF->ImageCount - 1;
    }

    SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
    if (gif->SColorMap != nullptr) {
        const GifColorType& col = gif->SColorMap->Colors[fGIF->SBackGroundColor];
        bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
    }

    // draw each frames - not intelligent way
    for (int i = startIndex; i <= lastIndex; i++) {
        const SavedImage* cur = &fGIF->SavedImages[i];
        if (i == 0) {
            bool trans;
            int disposal;
            getTransparencyAndDisposalMethod(cur, &trans, &disposal);
            if (!trans && gif->SColorMap != nullptr) {
                fPaintingColor = bgColor;
            } else {
                fPaintingColor = SkColorSetARGB(0, 0, 0, 0);
            }

            bm->eraseColor(fPaintingColor);
            fBackup.eraseColor(fPaintingColor);
        } else {
            // Dispose previous frame before move to next frame.
            const SavedImage* prev = &fGIF->SavedImages[i-1];
            disposeFrameIfNeeded(bm, prev, cur, &fBackup, fPaintingColor);
        }

        // Draw frame
        // We can skip this process if this index is not last and disposal
        // method == 2 or method == 3
        if (i == lastIndex || !checkIfWillBeCleared(cur)) {
            drawFrame(bm, cur, gif->SColorMap);
        }
    }

    // save index
    fLastDrawIndex = lastIndex;
    return true;
}

///////////////////////////////////////////////////////////////////////////////

#include "SkTRegistry.h"

SkMovie* Factory(SkStreamRewindable* stream) {
    char buf[GIF_STAMP_LEN];
    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
            // must rewind here, since our construct wants to re-read the data
            stream->rewind();
            return new SkGIFMovie(stream);
        }
    }
    return nullptr;
}

static SkTRegistry<SkMovie*(*)(SkStreamRewindable*)> gReg(Factory);
<|endoftext|>


<commit_before>
#ifndef __POOL_ALLOC_HPP__
#define __POOL_ALLOC_HPP__

// TODO: this allocator shares the freelist with the pooled
// memory. This saves space but might negatively affect caching
// behavior (compared to a freelist stored in a separate
// array). Investigate this further once we can test real workloads.

// TODO: we might want to consider internal alignment of objects. We
// should test this out and see if the performance changes in the
// grand scheme of things.

#include <assert.h>
#include "utils.hpp"

template <class super_alloc_t>
struct pool_alloc_t : public super_alloc_t {
    pool_alloc_t(size_t _nobjects, size_t _object_size)
        : nobjects(_nobjects), object_size(_object_size)
    {
        check("Object size must be at least the size of a pointer.",
              object_size < sizeof(void*));

        // Allocate the memory we need
        size_t pool_size = nobjects * object_size;
        mem = super_alloc_t::malloc(pool_size);
        check("Could not allocate memory.", mem == NULL);

        // Fill up the free list
        free_list = (free_node_t*)mem;
        free_node_t *node = free_list;
        for(size_t i = 0; i < nobjects - 1; i++) {
            node->next = (free_node_t*)((char*)node + object_size);
            node = node->next;
        }
        node->next = NULL;
    }

    ~pool_alloc_t() {
        super_alloc_t::free(mem);
        mem = NULL;
    }

    void *malloc(size_t size) {
        assert(size == object_size);
        void *addr = (void*)free_list;
        if(addr) {
            free_list = free_list->next;
        }
        return addr;
    }

    void free(void *ptr) {
        ((free_node_t*)ptr)->next = free_list;
        free_list = (free_node_t*)ptr;
    }

    bool in_range(void *ptr) {
        return ptr >= mem && ptr < (char*)mem + object_size * nobjects;
    }

private:
    size_t nobjects;
    size_t object_size;
    void *mem;

    struct free_node_t {
        free_node_t *next;
    };
    free_node_t *free_list;
};

#endif // __POOL_ALLOC_HPP__

<commit_msg>Allocator will now overwrite freed memory with 0xFD in addition to overwriting allocated memory with 0xBD.<commit_after>
#ifndef __POOL_ALLOC_HPP__
#define __POOL_ALLOC_HPP__

// TODO: this allocator shares the freelist with the pooled
// memory. This saves space but might negatively affect caching
// behavior (compared to a freelist stored in a separate
// array). Investigate this further once we can test real workloads.

// TODO: we might want to consider internal alignment of objects. We
// should test this out and see if the performance changes in the
// grand scheme of things.

#include <assert.h>
#include "utils.hpp"

template <class super_alloc_t>
struct pool_alloc_t : public super_alloc_t {
    pool_alloc_t(size_t _nobjects, size_t _object_size)
        : nobjects(_nobjects), object_size(_object_size)
    {
        check("Object size must be at least the size of a pointer.",
              object_size < sizeof(void*));

        // Allocate the memory we need
        size_t pool_size = nobjects * object_size;
        mem = super_alloc_t::malloc(pool_size);
        check("Could not allocate memory.", mem == NULL);

        // Fill up the free list
        free_list = (free_node_t*)mem;
        free_node_t *node = free_list;
        for(size_t i = 0; i < nobjects - 1; i++) {
            node->next = (free_node_t*)((char*)node + object_size);
            node = node->next;
        }
        node->next = NULL;
    }

    ~pool_alloc_t() {
        super_alloc_t::free(mem);
        mem = NULL;
    }

    void *malloc(size_t size) {
        assert(size == object_size);
        void *addr = (void*)free_list;
        if(addr) {
            free_list = free_list->next;
            memset(addr, 0xBD, object_size);
        }
        return addr;
    }

    void free(void *ptr) {
        memset(ptr, 0xFD, object_size);   // Catch use-after-free errors
        ((free_node_t*)ptr)->next = free_list;
        free_list = (free_node_t*)ptr;
    }

    bool in_range(void *ptr) {
        return ptr >= mem && ptr < (char*)mem + object_size * nobjects;
    }

private:
    size_t nobjects;
    size_t object_size;
    void *mem;

    struct free_node_t {
        free_node_t *next;
    };
    free_node_t *free_list;
};

#endif // __POOL_ALLOC_HPP__

<|endoftext|>


<commit_before>/*
 * jsonrpc.c
 *
 * =========================================================================
 * Copyright (c) 2016-2019 Daniele De Sensi (d.desensi.software@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * =========================================================================
 */

#include <peafowl/inspectors/inspectors.h>
#include <peafowl/peafowl.h>

#include "../external/rapidjson/document.h"
#include "../external/rapidjson/error/en.h"

#include <iostream>
#include "../external/rapidjson/stringbuffer.h"
#include "../external/rapidjson/writer.h"

#define PFWL_DEBUG_DISS_JSONRPC 0
#define debug_print(fmt, ...)                                                  \
  do {                                                                         \
    if (PFWL_DEBUG_DISS_JSONRPC)                                               \
      fprintf(stdout, fmt, __VA_ARGS__);                                       \
  } while (0)

using namespace rapidjson;

bool hasField(Document& d, const char* field){
  auto it = d.FindMember(field);
  return it != d.MemberEnd();
}

const unsigned char* getFieldAsString(Document& d, pfwl_field_id_t fieldId, const char* field, int persistent, pfwl_flow_info_private_t* flow_info_private){
  auto it = d.FindMember(field);
  if(it != d.MemberEnd()){
    if(it->value.IsString() || it->value.IsNumber()){
      return reinterpret_cast<const unsigned char*>(it->value.GetString());
    }else{
      if(persistent){
        size_t position = fieldId - PFWL_FIELDS_L7_JSON_RPC_FIRST - 1;
        StringBuffer* sb;
        if(!flow_info_private->json_stringbuffers[position]){
          sb = new StringBuffer();
          flow_info_private->json_stringbuffers[position] = static_cast<void*>(sb);
        }else{
          sb = static_cast<StringBuffer*>(flow_info_private->json_stringbuffers[position]);
          sb->Clear();
        }
        Writer<StringBuffer> writer(*sb);
        it->value.Accept(writer);
        return reinterpret_cast<const unsigned char*>(sb->GetString());
      }else{
        StringBuffer sb;
        Writer<StringBuffer> writer(sb);
        it->value.Accept(writer);
        return reinterpret_cast<const unsigned char*>(sb.GetString());
      }
    }
  }else{
    return NULL;
  }
}

void setIfPresent(Document& d, pfwl_field_t* fields, pfwl_field_id_t fieldId, const char* fieldName, pfwl_flow_info_private_t* flow_info_private){
  const unsigned char* fieldValue = getFieldAsString(d, fieldId, fieldName, 1, flow_info_private);
  if(fieldValue){
    pfwl_field_string_set(fields, fieldId, fieldValue, strlen((const char*) fieldValue));
  }
}

typedef enum{
  PFWL_JSONRPC_MSG_TYPE_REQUEST = 0,
  PFWL_JSONRPC_MSG_TYPE_RESPONSE,
  PFWL_JSONRPC_MSG_TYPE_NOTIFICATION,
  PFWL_JSONRPC_MSG_TYPE_NONE, /// Not jsonrpc
}pfwl_jsonrpc_msg_type_t;

static void jsonrpc_flow_cleaner(pfwl_flow_info_private_t *flow_info_private){
  for(size_t i = PFWL_FIELDS_L7_JSON_RPC_FIRST + 1; i < PFWL_FIELDS_L7_JSON_RPC_LAST; i++){
    size_t position = i - PFWL_FIELDS_L7_JSON_RPC_FIRST - 1;
    if(flow_info_private->json_stringbuffers[position]){
      delete static_cast<StringBuffer*>(flow_info_private->json_stringbuffers[position]);
    }
  }
  if(flow_info_private->json_parser){
    delete static_cast<Document*>(flow_info_private->json_parser);
  }
}

static bool isJsonCt(const pfwl_string_t& ct){
  return !strncmp(reinterpret_cast<const char*>(ct.value), "application/json-rpc"   , ct.length) ||
         !strncmp(reinterpret_cast<const char*>(ct.value), "application/json"       , ct.length) ||
         !strncmp(reinterpret_cast<const char*>(ct.value), "application/jsonrequest", ct.length);
}

typedef enum{
  JRPC_HTTP_MAYBE = 0,
  JRPC_HTTP_NO,
  JRPC_HTTP_MORE_DATA,
}JsonRpcOverHttpCheck;

JsonRpcOverHttpCheck check_jsonrpc_over_http(pfwl_dissection_info_t *pkt_info,
                                             pfwl_flow_info_private_t *flow_info_private,
                                             const unsigned char* &http_body,
                                             size_t& http_body_length){
  debug_print("%s", "Checking json over http\n");
  if(flow_info_private->info_public->protocols_l7[flow_info_private->info_public->protocols_l7_num - 1] == PFWL_PROTO_L7_HTTP){
    // Previous protocol was HTTP, let's check if it was carrying json stuff
    debug_print("%s", "Previous proto was HTTP\n");
    int64_t http_method;
    if(!pfwl_field_number_get(pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_HTTP_METHOD, &http_method) &&
       http_method == HTTP_POST){
      debug_print("%s", "HTTP Method is POST\n");
      pfwl_string_t http_ct;
      if(!pfwl_http_get_header(pkt_info, "Content-Type", &http_ct) && isJsonCt(http_ct)){
        debug_print("%s", "HTTP content type was json\n");
        pfwl_string_t http_body_string;
        if(!pfwl_field_string_get(pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_HTTP_BODY, &http_body_string)){
          debug_print("%s", "HTTP has body, checking if it is jsonrpc\n");
          http_body = http_body_string.value;
          http_body_length = http_body_string.length;
          return JRPC_HTTP_MAYBE;
        }else{
          debug_print("%s", "HTTP has not body\n");
          return JRPC_HTTP_NO;
        }
      }else{
        debug_print("%s", "HTTP content type was not json\n");
        return JRPC_HTTP_MORE_DATA;
      }
    }else{
      debug_print("%s", "HTTP method was not POST but may be POST in the future\n");
      return JRPC_HTTP_MORE_DATA;
    }
  }else if(BITTEST(flow_info_private->possible_matching_protocols, PFWL_PROTO_L7_HTTP) &&
           flow_info_private->info_public->protocols_l7[flow_info_private->info_public->protocols_l7_num - 1] == PFWL_PROTO_L7_NOT_DETERMINED){
    // Could still become HTTP
    debug_print("%s", "Previous proto may still be HTTP\n");
    return JRPC_HTTP_MORE_DATA;
  }else{
    debug_print("%s", "Previous proto was not HTTP\n");
    return JRPC_HTTP_NO;
  }
}

uint8_t check_jsonrpc(pfwl_state_t *state, const unsigned char *app_data,
                      size_t data_length, pfwl_dissection_info_t *pkt_info,
                      pfwl_flow_info_private_t *flow_info_private) {
  // TODO: Check if 'in-situ' parsing is faster (https://github.com/Tencent/rapidjson/blob/master/doc/dom.md)
  // TODO: Manage segmented jsons (some in stratum.pcap)
  Document* d = NULL;
  if(!flow_info_private->json_parser){
    d = new Document();
    flow_info_private->json_parser = static_cast<void*>(d);
    flow_info_private->flow_cleaners_dissectors[PFWL_PROTO_L7_JSON_RPC] = &jsonrpc_flow_cleaner;
  }else{
    d = static_cast<Document*>(flow_info_private->json_parser);
  }

  ParseResult ok = d->Parse<kParseNumbersAsStringsFlag>((const char*) app_data, data_length);
  if(!ok){
    debug_print("%s\n", "It is not json-RPC");
    if(PFWL_DEBUG_DISS_JSONRPC)
      fprintf(stderr, "JSON parse error: %s (%lu)\n", GetParseError_En(ok.Code()), ok.Offset());
    // Check if JSON over HTTP
    const unsigned char* http_body;
    size_t http_body_length;
    switch(check_jsonrpc_over_http(pkt_info, flow_info_private, http_body, http_body_length)){
    case JRPC_HTTP_MAYBE:{
      app_data = http_body;
      data_length = http_body_length;
      ok = d->Parse<kParseNumbersAsStringsFlag>((const char*) app_data, data_length);
      if(!ok){
        debug_print("%s\n", "It is not json-RPC");
        if(PFWL_DEBUG_DISS_JSONRPC)
          fprintf(stderr, "JSON parse error: %s (%lu)\n", GetParseError_En(ok.Code()), ok.Offset());
        return PFWL_PROTOCOL_NO_MATCHES;
      }
    }break;
    case JRPC_HTTP_NO:{
      return PFWL_PROTOCOL_NO_MATCHES;
    }
    case JRPC_HTTP_MORE_DATA:{
      return PFWL_PROTOCOL_MORE_DATA_NEEDED;
    }
    }
  }

  enum protocol_check_statuses to_return = PFWL_PROTOCOL_NO_MATCHES;
  uint8_t version = 0;
  pfwl_jsonrpc_msg_type_t type = PFWL_JSONRPC_MSG_TYPE_NONE;
  const unsigned char* jsonrpc = getFieldAsString(*d, PFWL_FIELDS_L7_NUM, "jsonrpc", 0, flow_info_private);
  bool hasId = false;

  //const char *id = NULL, *method = NULL, *params = NULL, *result = NULL, *error = NULL;
  if(jsonrpc && !strcmp((const char*) jsonrpc, "2.0")){
    debug_print("%s\n", "It seems JSON-RPC v2.0.");
    // JSON-RPC v2 check
    version = 2;
    hasId = hasField(*d, "id");
    if(hasField(*d, "method")){
      debug_print("%s\n", "Method field found.");
      if(hasId){
        debug_print("%s\n", "Id found, is a request.");
        type = PFWL_JSONRPC_MSG_TYPE_REQUEST;
      }else{
        debug_print("%s\n", "Id not found, is a notification.");
        type = PFWL_JSONRPC_MSG_TYPE_NOTIFICATION; // In 2.0 notifications are requests without id
      }
      to_return = PFWL_PROTOCOL_MATCHES;
    }else{
      if(hasId && (hasField(*d, "result") || hasField(*d, "error"))){
        debug_print("%s\n", "Result or error found, is a response.");
        type = PFWL_JSONRPC_MSG_TYPE_RESPONSE;
        to_return = PFWL_PROTOCOL_MATCHES;
      }
    }
  }else{
    // JSON-RPC v1 check
    debug_print("%s\n", "It seems JSON-RPC v1.0.");
    version = 1;
    hasId = hasField(*d, "id");
    if(hasId && hasField(*d, "method") && hasField(*d, "params")){
      debug_print("%s\n", "Id, method and params found, is a request.");
      type = PFWL_JSONRPC_MSG_TYPE_REQUEST;
      to_return = PFWL_PROTOCOL_MATCHES;
    }else{
      if(hasId && hasField(*d, "result") && hasField(*d, "error")){
        debug_print("%s\n", "Id, result and error found, is a response.");
        type = PFWL_JSONRPC_MSG_TYPE_RESPONSE;
        to_return = PFWL_PROTOCOL_MATCHES;
      }else if(hasId){
        debug_print("%s\n", "Id found, is a notification.");
        type = PFWL_JSONRPC_MSG_TYPE_NOTIFICATION;
        to_return = PFWL_PROTOCOL_MATCHES;
      }else{
        debug_print("%s\n", "Nothing found, it is not JSON-RPC.");
      }
    }
  }

  // Set any required field
  if(to_return == PFWL_PROTOCOL_MATCHES){
    if(pfwl_protocol_field_required(state, flow_info_private, PFWL_FIELDS_L7_JSON_RPC_VERSION)){
      pfwl_field_number_set(pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_VERSION, version);
    }
    if(pfwl_protocol_field_required(state, flow_info_private,PFWL_FIELDS_L7_JSON_RPC_MSG_TYPE)){
      pfwl_field_number_set(pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_MSG_TYPE, type);
    }
    if(pfwl_protocol_field_required(state, flow_info_private,PFWL_FIELDS_L7_JSON_RPC_ID)){
      setIfPresent(*d, pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_ID, "id", flow_info_private);
    }
    if(pfwl_protocol_field_required(state, flow_info_private,PFWL_FIELDS_L7_JSON_RPC_METHOD)){
      setIfPresent(*d, pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_METHOD, "method", flow_info_private);
    }
    if(pfwl_protocol_field_required(state, flow_info_private,PFWL_FIELDS_L7_JSON_RPC_PARAMS)){
      setIfPresent(*d, pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_PARAMS, "params", flow_info_private);
    }
    if(pfwl_protocol_field_required(state, flow_info_private,PFWL_FIELDS_L7_JSON_RPC_RESULT)){
      setIfPresent(*d, pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_RESULT, "result", flow_info_private);
    }
    if(pfwl_protocol_field_required(state, flow_info_private,PFWL_FIELDS_L7_JSON_RPC_ERROR)){
      setIfPresent(*d, pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_ERROR, "error", flow_info_private);
    }
    return PFWL_PROTOCOL_MATCHES;
  }else{
    return to_return;
  }
}
<commit_msg>[FIX] Bug on JSON parsing.<commit_after>/*
 * jsonrpc.c
 *
 * =========================================================================
 * Copyright (c) 2016-2019 Daniele De Sensi (d.desensi.software@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * =========================================================================
 */

#include <peafowl/inspectors/inspectors.h>
#include <peafowl/peafowl.h>

#include "../external/rapidjson/document.h"
#include "../external/rapidjson/error/en.h"

#include <iostream>
#include "../external/rapidjson/stringbuffer.h"
#include "../external/rapidjson/writer.h"

#define PFWL_DEBUG_DISS_JSONRPC 0
#define debug_print(fmt, ...)                                                  \
  do {                                                                         \
    if (PFWL_DEBUG_DISS_JSONRPC)                                               \
      fprintf(stdout, fmt, __VA_ARGS__);                                       \
  } while (0)

using namespace rapidjson;

bool hasField(Document& d, const char* field){
  auto it = d.FindMember(field);
  return it != d.MemberEnd();
}

const unsigned char* getFieldAsString(Document& d, pfwl_field_id_t fieldId, const char* field, int persistent, pfwl_flow_info_private_t* flow_info_private){
  auto it = d.FindMember(field);
  if(it != d.MemberEnd()){
    if(it->value.IsString() || it->value.IsNumber()){
      return reinterpret_cast<const unsigned char*>(it->value.GetString());
    }else{
      if(persistent){
        size_t position = fieldId - PFWL_FIELDS_L7_JSON_RPC_FIRST - 1;
        StringBuffer* sb;
        if(!flow_info_private->json_stringbuffers[position]){
          sb = new StringBuffer();
          flow_info_private->json_stringbuffers[position] = static_cast<void*>(sb);
        }else{
          sb = static_cast<StringBuffer*>(flow_info_private->json_stringbuffers[position]);
          sb->Clear();
        }
        Writer<StringBuffer> writer(*sb);
        it->value.Accept(writer);
        return reinterpret_cast<const unsigned char*>(sb->GetString());
      }else{
        StringBuffer sb;
        Writer<StringBuffer> writer(sb);
        it->value.Accept(writer);
        return reinterpret_cast<const unsigned char*>(sb.GetString());
      }
    }
  }else{
    return NULL;
  }
}

void setIfPresent(Document& d, pfwl_field_t* fields, pfwl_field_id_t fieldId, const char* fieldName, pfwl_flow_info_private_t* flow_info_private){
  const unsigned char* fieldValue = getFieldAsString(d, fieldId, fieldName, 1, flow_info_private);
  if(fieldValue){
    pfwl_field_string_set(fields, fieldId, fieldValue, strlen((const char*) fieldValue));
  }
}

typedef enum{
  PFWL_JSONRPC_MSG_TYPE_REQUEST = 0,
  PFWL_JSONRPC_MSG_TYPE_RESPONSE,
  PFWL_JSONRPC_MSG_TYPE_NOTIFICATION,
  PFWL_JSONRPC_MSG_TYPE_NONE, /// Not jsonrpc
}pfwl_jsonrpc_msg_type_t;

static void jsonrpc_flow_cleaner(pfwl_flow_info_private_t *flow_info_private){
  for(size_t i = PFWL_FIELDS_L7_JSON_RPC_FIRST + 1; i < PFWL_FIELDS_L7_JSON_RPC_LAST; i++){
    size_t position = i - PFWL_FIELDS_L7_JSON_RPC_FIRST - 1;
    if(flow_info_private->json_stringbuffers[position]){
      delete static_cast<StringBuffer*>(flow_info_private->json_stringbuffers[position]);
    }
  }
  if(flow_info_private->json_parser){
    delete static_cast<Document*>(flow_info_private->json_parser);
  }
}

static bool isJsonCt(const pfwl_string_t& ct){
  return !strncmp(reinterpret_cast<const char*>(ct.value), "application/json-rpc"   , ct.length) ||
         !strncmp(reinterpret_cast<const char*>(ct.value), "application/json"       , ct.length) ||
         !strncmp(reinterpret_cast<const char*>(ct.value), "application/jsonrequest", ct.length);
}

typedef enum{
  JRPC_HTTP_MAYBE = 0,
  JRPC_HTTP_NO,
  JRPC_HTTP_MORE_DATA,
}JsonRpcOverHttpCheck;

JsonRpcOverHttpCheck check_jsonrpc_over_http(pfwl_dissection_info_t *pkt_info,
                                             pfwl_flow_info_private_t *flow_info_private,
                                             const unsigned char* &http_body,
                                             size_t& http_body_length){
  debug_print("%s", "Checking json over http\n");
  if(flow_info_private->info_public->protocols_l7[flow_info_private->info_public->protocols_l7_num - 1] == PFWL_PROTO_L7_HTTP){
    // Previous protocol was HTTP, let's check if it was carrying json stuff
    debug_print("%s", "Previous proto was HTTP\n");
    int64_t http_method;
    if(!pfwl_field_number_get(pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_HTTP_METHOD, &http_method) &&
       http_method == HTTP_POST){
      debug_print("%s", "HTTP Method is POST\n");
      pfwl_string_t http_ct;
      if(!pfwl_http_get_header(pkt_info, "Content-Type", &http_ct) && isJsonCt(http_ct)){
        debug_print("%s", "HTTP content type was json\n");
        pfwl_string_t http_body_string;
        if(!pfwl_field_string_get(pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_HTTP_BODY, &http_body_string)){
          debug_print("%s", "HTTP has body, checking if it is jsonrpc\n");
          http_body = http_body_string.value;
          http_body_length = http_body_string.length;
          return JRPC_HTTP_MAYBE;
        }else{
          debug_print("%s", "HTTP has not body\n");
          return JRPC_HTTP_NO;
        }
      }else{
        debug_print("%s", "HTTP content type was not json\n");
        return JRPC_HTTP_MORE_DATA;
      }
    }else{
      debug_print("%s", "HTTP method was not POST but may be POST in the future\n");
      return JRPC_HTTP_MORE_DATA;
    }
  }else if(BITTEST(flow_info_private->possible_matching_protocols, PFWL_PROTO_L7_HTTP) &&
           flow_info_private->info_public->protocols_l7[flow_info_private->info_public->protocols_l7_num - 1] == PFWL_PROTO_L7_NOT_DETERMINED){
    // Could still become HTTP
    debug_print("%s", "Previous proto may still be HTTP\n");
    return JRPC_HTTP_MORE_DATA;
  }else{
    debug_print("%s", "Previous proto was not HTTP\n");
    return JRPC_HTTP_NO;
  }
}

uint8_t check_jsonrpc(pfwl_state_t *state, const unsigned char *app_data,
                      size_t data_length, pfwl_dissection_info_t *pkt_info,
                      pfwl_flow_info_private_t *flow_info_private) {
  // TODO: Check if 'in-situ' parsing is faster (https://github.com/Tencent/rapidjson/blob/master/doc/dom.md)
  // TODO: Manage segmented jsons (some in stratum.pcap)
  Document* d = NULL;
  if(!flow_info_private->json_parser){
    d = new Document();
    flow_info_private->json_parser = static_cast<void*>(d);
    flow_info_private->flow_cleaners_dissectors[PFWL_PROTO_L7_JSON_RPC] = &jsonrpc_flow_cleaner;
  }else{
    d = static_cast<Document*>(flow_info_private->json_parser);
  }

  ParseResult ok = d->Parse<kParseNumbersAsStringsFlag>((const char*) app_data, data_length);
  if(!ok || !d->IsObject()){
    debug_print("%s\n", "It is not json-RPC");
    if(PFWL_DEBUG_DISS_JSONRPC)
      fprintf(stderr, "JSON parse error: %s (%lu)\n", GetParseError_En(ok.Code()), ok.Offset());
    // Check if JSON over HTTP
    const unsigned char* http_body;
    size_t http_body_length;
    switch(check_jsonrpc_over_http(pkt_info, flow_info_private, http_body, http_body_length)){
    case JRPC_HTTP_MAYBE:{
      app_data = http_body;
      data_length = http_body_length;
      ok = d->Parse<kParseNumbersAsStringsFlag>((const char*) app_data, data_length);
      if(!ok){
        debug_print("%s\n", "It is not json-RPC");
        if(PFWL_DEBUG_DISS_JSONRPC)
          fprintf(stderr, "JSON parse error: %s (%lu)\n", GetParseError_En(ok.Code()), ok.Offset());
        return PFWL_PROTOCOL_NO_MATCHES;
      }
    }break;
    case JRPC_HTTP_NO:{
      return PFWL_PROTOCOL_NO_MATCHES;
    }
    case JRPC_HTTP_MORE_DATA:{
      return PFWL_PROTOCOL_MORE_DATA_NEEDED;
    }
    }
  }

  enum protocol_check_statuses to_return = PFWL_PROTOCOL_NO_MATCHES;
  uint8_t version = 0;
  pfwl_jsonrpc_msg_type_t type = PFWL_JSONRPC_MSG_TYPE_NONE;
  const unsigned char* jsonrpc = getFieldAsString(*d, PFWL_FIELDS_L7_NUM, "jsonrpc", 0, flow_info_private);
  bool hasId = false;

  //const char *id = NULL, *method = NULL, *params = NULL, *result = NULL, *error = NULL;
  if(jsonrpc && !strcmp((const char*) jsonrpc, "2.0")){
    debug_print("%s\n", "It seems JSON-RPC v2.0.");
    // JSON-RPC v2 check
    version = 2;
    hasId = hasField(*d, "id");
    if(hasField(*d, "method")){
      debug_print("%s\n", "Method field found.");
      if(hasId){
        debug_print("%s\n", "Id found, is a request.");
        type = PFWL_JSONRPC_MSG_TYPE_REQUEST;
      }else{
        debug_print("%s\n", "Id not found, is a notification.");
        type = PFWL_JSONRPC_MSG_TYPE_NOTIFICATION; // In 2.0 notifications are requests without id
      }
      to_return = PFWL_PROTOCOL_MATCHES;
    }else{
      if(hasId && (hasField(*d, "result") || hasField(*d, "error"))){
        debug_print("%s\n", "Result or error found, is a response.");
        type = PFWL_JSONRPC_MSG_TYPE_RESPONSE;
        to_return = PFWL_PROTOCOL_MATCHES;
      }
    }
  }else{
    // JSON-RPC v1 check
    debug_print("%s\n", "It seems JSON-RPC v1.0.");
    version = 1;
    hasId = hasField(*d, "id");
    if(hasId && hasField(*d, "method") && hasField(*d, "params")){
      debug_print("%s\n", "Id, method and params found, is a request.");
      type = PFWL_JSONRPC_MSG_TYPE_REQUEST;
      to_return = PFWL_PROTOCOL_MATCHES;
    }else{
      if(hasId && hasField(*d, "result") && hasField(*d, "error")){
        debug_print("%s\n", "Id, result and error found, is a response.");
        type = PFWL_JSONRPC_MSG_TYPE_RESPONSE;
        to_return = PFWL_PROTOCOL_MATCHES;
      }else if(hasId){
        debug_print("%s\n", "Id found, is a notification.");
        type = PFWL_JSONRPC_MSG_TYPE_NOTIFICATION;
        to_return = PFWL_PROTOCOL_MATCHES;
      }else{
        debug_print("%s\n", "Nothing found, it is not JSON-RPC.");
      }
    }
  }

  // Set any required field
  if(to_return == PFWL_PROTOCOL_MATCHES){
    if(pfwl_protocol_field_required(state, flow_info_private, PFWL_FIELDS_L7_JSON_RPC_VERSION)){
      pfwl_field_number_set(pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_VERSION, version);
    }
    if(pfwl_protocol_field_required(state, flow_info_private,PFWL_FIELDS_L7_JSON_RPC_MSG_TYPE)){
      pfwl_field_number_set(pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_MSG_TYPE, type);
    }
    if(pfwl_protocol_field_required(state, flow_info_private,PFWL_FIELDS_L7_JSON_RPC_ID)){
      setIfPresent(*d, pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_ID, "id", flow_info_private);
    }
    if(pfwl_protocol_field_required(state, flow_info_private,PFWL_FIELDS_L7_JSON_RPC_METHOD)){
      setIfPresent(*d, pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_METHOD, "method", flow_info_private);
    }
    if(pfwl_protocol_field_required(state, flow_info_private,PFWL_FIELDS_L7_JSON_RPC_PARAMS)){
      setIfPresent(*d, pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_PARAMS, "params", flow_info_private);
    }
    if(pfwl_protocol_field_required(state, flow_info_private,PFWL_FIELDS_L7_JSON_RPC_RESULT)){
      setIfPresent(*d, pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_RESULT, "result", flow_info_private);
    }
    if(pfwl_protocol_field_required(state, flow_info_private,PFWL_FIELDS_L7_JSON_RPC_ERROR)){
      setIfPresent(*d, pkt_info->l7.protocol_fields, PFWL_FIELDS_L7_JSON_RPC_ERROR, "error", flow_info_private);
    }
    return PFWL_PROTOCOL_MATCHES;
  }else{
    return to_return;
  }
}
<|endoftext|>