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

   cvcontourtree.cpp -

   $Author: lsxi $

   Copyright (C) 2007 Masakazu Yonekura

************************************************************/
#include "cvcontour.h"
/*
 * Document-class: OpenCV::CvContourTree
 *
 * Contour tree. CvContour#create_tree
 *
 * C structure is here.
 *  typedef struct CvContourTree {
 *    CV_SEQUENCE_FIELDS()
 *    CvPoint p1;
 *    CvPoint p2;
 *  } CvContourTree;
 *
 */
__NAMESPACE_BEGIN_OPENCV
__NAMESPACE_BEGIN_CVCONTOURTREE

VALUE rb_klass;

VALUE
rb_class()
{
  return rb_klass;
}

void
define_ruby_class()
{
  if (rb_klass)
    return;
  /*
   * opencv = rb_define_module("OpenCV");
   * cvseq = rb_define_class_under(opencv, "CvSeq");
   *
   * note: this comment is used by rdoc.
   */
  VALUE opencv = rb_module_opencv();
  VALUE cvseq = cCvSeq::rb_class();

  rb_klass = rb_define_class_under(opencv, "CvContourTree", cvseq);
  rb_define_method(rb_klass, "p1", RUBY_METHOD_FUNC(rb_p1), 0);
  rb_define_method(rb_klass, "p2", RUBY_METHOD_FUNC(rb_p2), 0);
  rb_define_method(rb_klass, "contour", RUBY_METHOD_FUNC(rb_contour), 1);
}

VALUE
rb_p1(VALUE self)
{
  return REFER_OBJECT(cCvPoint::rb_class(), &CVCONTOURTREE(self)->p1, self);
}

VALUE
rb_p2(VALUE self)
{
  return REFER_OBJECT(cCvPoint::rb_class(), &CVCONTOURTREE(self)->p2, self);
}

/*
 * call-seq:
 *   contour(<i>[criteria = 0]</i>) -> cvcontour
 *
 * Restores the contour from its binary tree representation.
 * The parameter criteria determines the accuracy and/or the number of tree levels
 * used for reconstruction, so it is possible to build approximated contour.
 */
VALUE
rb_contour(VALUE self, VALUE criteria)
{
  VALUE storage = cCvMemStorage::new_object();
  CvSeq *contour = NULL;
  try {
    contour = cvContourFromContourTree(CVCONTOURTREE(self), CVMEMSTORAGE(storage),
				       VALUE_TO_CVTERMCRITERIA(criteria));
  }
  catch (cv::Exception& e) {
    raise_cverror(e);
  }
  return cCvSeq::new_sequence(cCvContour::rb_class(), contour, cCvPoint::rb_class(), storage);
}

__NAMESPACE_END_CVCONTOURTREE
__NAMESPACE_END_OPENCV
<commit_msg>add documents of CvContourTree<commit_after>/************************************************************

   cvcontourtree.cpp -

   $Author: lsxi $

   Copyright (C) 2007 Masakazu Yonekura

************************************************************/
#include "cvcontour.h"
/*
 * Document-class: OpenCV::CvContourTree
 *
 * Contour tree
 * @see CvContour#create_tree
 */
__NAMESPACE_BEGIN_OPENCV
__NAMESPACE_BEGIN_CVCONTOURTREE

VALUE rb_klass;

VALUE
rb_class()
{
  return rb_klass;
}

void
define_ruby_class()
{
  if (rb_klass)
    return;
  /*
   * opencv = rb_define_module("OpenCV");
   * cvseq = rb_define_class_under(opencv, "CvSeq");
   *
   * note: this comment is used by rdoc.
   */
  VALUE opencv = rb_module_opencv();
  VALUE cvseq = cCvSeq::rb_class();

  rb_klass = rb_define_class_under(opencv, "CvContourTree", cvseq);
  rb_define_method(rb_klass, "p1", RUBY_METHOD_FUNC(rb_p1), 0);
  rb_define_method(rb_klass, "p2", RUBY_METHOD_FUNC(rb_p2), 0);
  rb_define_method(rb_klass, "contour", RUBY_METHOD_FUNC(rb_contour), 1);
}

/*
 * Returns the first point of the binary tree root segment
 * @overload p1
 * @return [CvPoint] First point of the binary tree root segment
 */
VALUE
rb_p1(VALUE self)
{
  return REFER_OBJECT(cCvPoint::rb_class(), &CVCONTOURTREE(self)->p1, self);
}

/*
 * Returns the last point of the binary tree root segment
 * @overload p2
 * @return [CvPoint] Last point of the binary tree root segment
 */
VALUE
rb_p2(VALUE self)
{
  return REFER_OBJECT(cCvPoint::rb_class(), &CVCONTOURTREE(self)->p2, self);
}

/*
 * Restores the contour from its binary tree representation.
 *
 * The parameter +criteria+ determines the accuracy and/or the number of tree levels
 * used for reconstruction, so it is possible to build approximated contour.
 * @overload contour(criteria = 0)
 *   @param criteria [Integer] Criteria, where to stop reconstruction
 * @return [CvContour] Contour tree
 * @opencv_func cvContourFromContourTree
 */
VALUE
rb_contour(VALUE self, VALUE criteria)
{
  VALUE storage = cCvMemStorage::new_object();
  CvSeq *contour = NULL;
  try {
    contour = cvContourFromContourTree(CVCONTOURTREE(self), CVMEMSTORAGE(storage),
				       VALUE_TO_CVTERMCRITERIA(criteria));
  }
  catch (cv::Exception& e) {
    raise_cverror(e);
  }
  return cCvSeq::new_sequence(cCvContour::rb_class(), contour, cCvPoint::rb_class(), storage);
}

__NAMESPACE_END_CVCONTOURTREE
__NAMESPACE_END_OPENCV
<|endoftext|>


<commit_before>
#include "view.hpp"
#include "view/shade.hpp"

namespace View {

	void printErrors(const char *prefix = "OpenGL error(s): ") {
		GLenum err;
		bool once = false;
		std::ostringstream oss;

		while(!(err = glGetError())) {
			once = true;
			switch(err) {
			case GL_INVALID_ENUM:
				oss << "invalid enum; ";
				break;
			case GL_INVALID_VALUE:
				oss << "invalid value; ";
				break;
			case GL_INVALID_OPERATION:
				oss << "invalid operation; ";
				break;
			case GL_STACK_OVERFLOW:
				oss << "stack overflow; ";
				break;
			case GL_STACK_UNDERFLOW:
				oss << "stack underflow; ";
				break;
			case GL_OUT_OF_MEMORY:
				oss << "out of memory; ";
				break;
			case GL_INVALID_FRAMEBUFFER_OPERATION:
				oss << "invalid framebuffer operation; ";
				break;
			default:
				break;
			}
		}

		if(once) {
			std::cout << prefix << oss.str() << std::endl;
		}
	}

	void view::setUniforms(void) {
		int w, h;
		glfwGetFramebufferSize(win, &w, &h);
		float mag = float(1/tan(fov*M_PI/180));
		float projection[]{
				mag*h/w, 0, 0, 0, 0, mag, 0, 0,
				0, 0, (far+near)/(far-near), -1,
				0, 0, 2*far*near/(far-near), 0
		};
		glUniformMatrix4fv(projID, 1, GL_TRUE,  projection);

		/* TODO - static not intended, just persistence;
 		 * all of the data here should be passed or shared */
		static float theta = 0;
		theta += M_PI/180;
		float c = cos(theta), s = sin(theta);
		float modelData[]{
				c, 0,-s, 0,
				0, 1, 0, 0,
				s, 0, c, c,
				0, 0, 0, 1
		}, viewData[]{
				1, 0, 0, 0,
				0, 1, 0, 0,
				0, 0, 1, 0,
				0, 0, 2, 1
		};

		glUniformMatrix4fv(modelID, 1, GL_TRUE,  modelData);
		glUniformMatrix4fv(viewID, 1, GL_FALSE, viewData);
	}

	void view::redraw(void) {
		setUniforms();
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glEnableVertexAttribArray(0);
		glEnableVertexAttribArray(1);

		static long int offset = 3*sizeof(float),
				stride = 2*offset;
		glBindBuffer(GL_ARRAY_BUFFER, vbuf);
		glVertexAttribPointer(0, 3, GL_FLOAT,
				GL_FALSE, stride, nullptr);
		glVertexAttribPointer(1, 3, GL_FLOAT,
				GL_FALSE, stride, (void*) offset);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf);
		glDrawElements(GL_TRIANGLES, nTriangles*3,
				GL_UNSIGNED_INT, nullptr);

		glDisableVertexAttribArray(1);
		glDisableVertexAttribArray(0);

		glfwSwapBuffers(win);

		static int nFrames = -1;
		nFrames++;
		static double t0 = glfwGetTime();
		double t1 = glfwGetTime(), dt = t1-t0;
		if(dt >= 1) {
			std::cout << (nFrames/dt) << "FPS" << std::endl;
			t0 = t1;
			nFrames = -1;
		}
	}

	void view::run(std::function<bool()> update,
			std::function<void()> quit) {
		if(valid && win) {
			glfwMakeContextCurrent(win);
			while(true) {
				glfwPollEvents();
				if(glfwWindowShouldClose(win)) {
					quit();
				} else if(update()) {
					redraw();
					continue;
				}
				break;
			}
		}
	}

	view::view(void) {
		using Header = Model::Ply::Header;
		using Element = Model::Ply::Element;

		static constexpr const char
			*vpath = "resources/shade.vert",
			*fpath = "resources/shade.frag",
			*mpath = "resources/bunny.ply";

		Header model(mpath);
		if(!glfwInit()) {
			std::cout << "Could not initialize GLFW." << std::endl;
			return;
		}
		glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
		glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
		win = glfwCreateWindow(512, 512, "View", NULL, NULL);

		if(!win) {
			std::cout << "Could not create window." << std::endl;
			return;
		}

		glfwMakeContextCurrent(win);
		if(gl3wInit()) {
			std::cout << "Could not initialize gl3w." << std::endl;
			return;
		}
		glfwSetWindowSizeCallback(win,
			[](GLFWwindow*, int w, int h){
				glViewport(0, 0, w, h);
			}
		);

		glEnable(GL_DEPTH_TEST);
		glEnable(GL_CULL_FACE);

		glGenVertexArrays(1, &vaID);
		glBindVertexArray(vaID);

		if(model.status) {
			std::cout << model.statusContext << std::endl;
			return;
		}
		auto start = begin(model.elements),
				 stop = end(model.elements);
		auto getVertices = [](Element const& el) -> bool {
			return el.name == "vertex" && !el.has_list
				&& el.properties.size() == 6;
		};
		auto getIndices = [](Element const& el) -> bool {
			int sz = el.properties.size();
			return el.name == "face"
				&& (el.has_list ? sz==1 : sz==3);
		};
		auto vertices = std::find_if(start, stop, getVertices),
				 indices = std::find_if(start, stop, getIndices);
		if(vertices == stop || indices == stop) {
			valid = false;
			std::cout << "The model is valid, but does not match "
				"the anticipated structure." << std::endl;
			return;
		}

		glGenBuffers(1, &vbuf);
		glBindBuffer(GL_ARRAY_BUFFER, vbuf);
		glBufferData(GL_ARRAY_BUFFER, vertices->data.size(),
				(void*)(&vertices->data[0]), GL_STATIC_DRAW);

		glGenBuffers(1, &ibuf);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices->data.size(),
				(void*)(&indices->data[0]), GL_STATIC_DRAW);
		nTriangles = indices -> instances;

		progID = glCreateProgram();
		if(!link(vpath, fpath, progID)) {
			std::cout << "Could not compile/link shader(s)." << std::endl;
			return;
		}
		modelID = glGetUniformLocation(progID, "model");
		viewID = glGetUniformLocation(progID, "view");
		projID = glGetUniformLocation(progID, "proj");
		glUseProgram(progID);
		valid = true;
	}
	view::~view(void) {
		if(progID) {
			glDeleteProgram(progID);
		}
		glfwDestroyWindow(win);
	}
}
<commit_msg>Added TODO to view<commit_after>
#include "view.hpp"
#include "view/shade.hpp"

namespace View {

	void printErrors(const char *prefix = "OpenGL error(s): ") {
		GLenum err;
		bool once = false;
		std::ostringstream oss;

		while(!(err = glGetError())) {
			once = true;
			switch(err) {
			case GL_INVALID_ENUM:
				oss << "invalid enum; ";
				break;
			case GL_INVALID_VALUE:
				oss << "invalid value; ";
				break;
			case GL_INVALID_OPERATION:
				oss << "invalid operation; ";
				break;
			case GL_STACK_OVERFLOW:
				oss << "stack overflow; ";
				break;
			case GL_STACK_UNDERFLOW:
				oss << "stack underflow; ";
				break;
			case GL_OUT_OF_MEMORY:
				oss << "out of memory; ";
				break;
			case GL_INVALID_FRAMEBUFFER_OPERATION:
				oss << "invalid framebuffer operation; ";
				break;
			default:
				break;
			}
		}

		if(once) {
			std::cout << prefix << oss.str() << std::endl;
		}
	}

	void view::setUniforms(void) {
		int w, h;
		glfwGetFramebufferSize(win, &w, &h);
		float mag = float(1/tan(fov*M_PI/180));
		float projection[]{
				mag*h/w, 0, 0, 0, 0, mag, 0, 0,
				0, 0, (far+near)/(far-near), -1,
				0, 0, 2*far*near/(far-near), 0
		};
		glUniformMatrix4fv(projID, 1, GL_TRUE,  projection);

		/* TODO - static not intended, just persistence;
 		 * all of the data here should be passed or shared */
		static float theta = 0;
		theta += M_PI/180;
		float c = cos(theta), s = sin(theta);
		float modelData[]{
				c, 0,-s, 0,
				0, 1, 0, 0,
				s, 0, c, c,
				0, 0, 0, 1
		}, viewData[]{
				1, 0, 0, 0,
				0, 1, 0, 0,
				0, 0, 1, 0,
				0, 0, 2, 1
		};

		glUniformMatrix4fv(modelID, 1, GL_TRUE,  modelData);
		glUniformMatrix4fv(viewID, 1, GL_FALSE, viewData);
	}

	void view::redraw(void) {
		setUniforms();
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glEnableVertexAttribArray(0);
		glEnableVertexAttribArray(1);

		static long int offset = 3*sizeof(float),
				stride = 2*offset;
		glBindBuffer(GL_ARRAY_BUFFER, vbuf);
		glVertexAttribPointer(0, 3, GL_FLOAT,
				GL_FALSE, stride, nullptr);
		glVertexAttribPointer(1, 3, GL_FLOAT,
				GL_FALSE, stride, (void*) offset);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf);
		glDrawElements(GL_TRIANGLES, nTriangles*3,
				GL_UNSIGNED_INT, nullptr);

		glDisableVertexAttribArray(1);
		glDisableVertexAttribArray(0);

		glfwSwapBuffers(win);

		static int nFrames = -1;
		nFrames++;
		static double t0 = glfwGetTime();
		double t1 = glfwGetTime(), dt = t1-t0;
		if(dt >= 1) {
			std::cout << (nFrames/dt) << "FPS" << std::endl;
			t0 = t1;
			nFrames = -1;
		}
	}

	void view::run(std::function<bool()> update,
			std::function<void()> quit) {
		if(valid && win) {
			glfwMakeContextCurrent(win);
			while(true) {
				glfwPollEvents();
				if(glfwWindowShouldClose(win)) {
					quit();
				} else if(update()) {
					redraw();
					continue;
				}
				break;
			}
		}
	}

	view::view(void) {
		using Header = Model::Ply::Header;
		using Element = Model::Ply::Element;

		// TODO - safe model and shader loading at runtime
		static constexpr const char
			*vpath = "resources/shade.vert",
			*fpath = "resources/shade.frag",
			*mpath = "resources/bunny.ply";

		Header model(mpath);
		if(!glfwInit()) {
			std::cout << "Could not initialize GLFW." << std::endl;
			return;
		}
		glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
		glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
		win = glfwCreateWindow(512, 512, "View", NULL, NULL);

		if(!win) {
			std::cout << "Could not create window." << std::endl;
			return;
		}

		glfwMakeContextCurrent(win);
		if(gl3wInit()) {
			std::cout << "Could not initialize gl3w." << std::endl;
			return;
		}
		glfwSetWindowSizeCallback(win,
			[](GLFWwindow*, int w, int h){
				glViewport(0, 0, w, h);
			}
		);

		glEnable(GL_DEPTH_TEST);
		glEnable(GL_CULL_FACE);

		glGenVertexArrays(1, &vaID);
		glBindVertexArray(vaID);

		if(model.status) {
			std::cout << model.statusContext << std::endl;
			return;
		}
		auto start = begin(model.elements),
				 stop = end(model.elements);
		auto getVertices = [](Element const& el) -> bool {
			return el.name == "vertex" && !el.has_list
				&& el.properties.size() == 6;
		};
		auto getIndices = [](Element const& el) -> bool {
			int sz = el.properties.size();
			return el.name == "face"
				&& (el.has_list ? sz==1 : sz==3);
		};
		auto vertices = std::find_if(start, stop, getVertices),
				 indices = std::find_if(start, stop, getIndices);
		if(vertices == stop || indices == stop) {
			valid = false;
			std::cout << "The model is valid, but does not match "
				"the anticipated structure." << std::endl;
			return;
		}

		glGenBuffers(1, &vbuf);
		glBindBuffer(GL_ARRAY_BUFFER, vbuf);
		glBufferData(GL_ARRAY_BUFFER, vertices->data.size(),
				(void*)(&vertices->data[0]), GL_STATIC_DRAW);

		glGenBuffers(1, &ibuf);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices->data.size(),
				(void*)(&indices->data[0]), GL_STATIC_DRAW);
		nTriangles = indices -> instances;

		progID = glCreateProgram();
		if(!link(vpath, fpath, progID)) {
			std::cout << "Could not compile/link shader(s)." << std::endl;
			return;
		}
		modelID = glGetUniformLocation(progID, "model");
		viewID = glGetUniformLocation(progID, "view");
		projID = glGetUniformLocation(progID, "proj");
		glUseProgram(progID);
		valid = true;
	}
	view::~view(void) {
		if(progID) {
			glDeleteProgram(progID);
		}
		glfwDestroyWindow(win);
	}
}
<|endoftext|>


<commit_before><commit_msg>Adapt loplugin:derefnullptr to old Clang versions<commit_after><|endoftext|>


<commit_before>// Copyright (C) 2017, 2018 Rob Caelers <rob.caelers@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 "loopp/net/TLSStream.hpp"

#include <string>
#include <system_error>

#include <netdb.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include "loopp/net/NetworkErrors.hpp"

#include "lwip/sockets.h"
#include "lwip/sys.h"

extern "C"
{
#include "esp_heap_caps.h"
#include "mbedtls/esp_debug.h"
}

#include "esp_log.h"

static const char tag[] = "NET";

using namespace loopp;
using namespace loopp::net;

TLSStream::TLSStream(std::shared_ptr<loopp::core::MainLoop> loop) : Stream(loop), loop(loop)
{
  mbedtls_net_init(&server_fd);
  mbedtls_ssl_init(&ssl);
  mbedtls_ssl_config_init(&config);

  mbedtls_x509_crt_init(&client_crt);
  mbedtls_x509_crt_init(&ca_crt);
  mbedtls_pk_init(&client_key);

#ifdef CONFIG_MBEDTLS_DEBUG
  mbedtls_esp_enable_debug_log(&config, 4);
#endif

  init_entropy();
}

TLSStream::~TLSStream()
{
  if (server_fd.fd != -1)
    {
      ::close(server_fd.fd);
      loop->unnotify(server_fd.fd);
    }

  mbedtls_net_free(&server_fd);
  mbedtls_x509_crt_free(&client_crt);
  mbedtls_x509_crt_free(&ca_crt);
  mbedtls_pk_free(&client_key);
  mbedtls_ssl_config_free(&config);
  mbedtls_ctr_drbg_free(&ctr_drbg);
  mbedtls_entropy_free(&entropy);
}

void
TLSStream::set_client_certificate(const char *cert, const char *key)
{
  parse_cert(&client_crt, cert);
  parse_key(&client_key, key);
}

void
TLSStream::set_ca_certificate(const char *cert)
{
  parse_cert(&ca_crt, cert);
}

void
TLSStream::init_entropy()
{
  mbedtls_entropy_init(&entropy);
  mbedtls_ctr_drbg_init(&ctr_drbg);

  int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
  throw_if_failure("mbedtls_ctr_drbg_seed", ret);
}

void
TLSStream::parse_cert(mbedtls_x509_crt *crt, const char *cert_str)
{
  mbedtls_x509_crt_init(crt);
  int ret = mbedtls_x509_crt_parse(crt, (const unsigned char *)cert_str, strlen(cert_str) + 1);
  throw_if_failure("mbedtls_x509_crt_parse", ret);
}

void
TLSStream::parse_key(mbedtls_pk_context *key, const char *key_str)
{
  mbedtls_pk_init(key);
  int ret = mbedtls_pk_parse_key(key, (const unsigned char *)key_str, strlen(key_str) + 1, (const unsigned char *)"", 0);
  throw_if_failure("mbedtls_x509_crt_parse", ret);
}

int
TLSStream::socket_read(uint8_t *buffer, std::size_t count)
{
  int ret = mbedtls_ssl_read(&ssl, buffer, count);

  if (ret == MBEDTLS_ERR_SSL_WANT_READ)
    {
      ret = -EAGAIN;
    }

  return ret;
}

int
TLSStream::socket_write(uint8_t *buffer, std::size_t count)
{
  int ret = mbedtls_ssl_write(&ssl, buffer, count);

  if (ret == MBEDTLS_ERR_SSL_WANT_WRITE)
    {
      ret = -EAGAIN;
    }

  return ret;
}

void
TLSStream::socket_close()
{
  int ret = 0;
  do
    {
      ret = mbedtls_ssl_close_notify(&ssl);
    }
  while (ret == MBEDTLS_ERR_SSL_WANT_WRITE);
}

void
TLSStream::socket_on_connected(std::string host, connect_slot_t slot)
{
  ESP_LOGD(tag, "Connected. Starting handshake");

  server_fd.fd = get_socket();

  try
    {
      int ret = mbedtls_ssl_set_hostname(&ssl, host.c_str());
      throw_if_failure("mbedtls_ssl_set_hostname", ret);

      ret = mbedtls_ssl_config_defaults(&config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM,
                                        MBEDTLS_SSL_PRESET_DEFAULT);
      throw_if_failure("mbedtls_ssl_config_defaults", ret);

      mbedtls_ssl_conf_verify(&config, verify_certificate, NULL);
      mbedtls_ssl_conf_rng(&config, mbedtls_ctr_drbg_random, &ctr_drbg);
      mbedtls_ssl_conf_authmode(&config, have_ca_cert ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_OPTIONAL);
      if (have_ca_cert)
        {
          mbedtls_ssl_conf_ca_chain(&config, &ca_crt, NULL);
        }
      if (have_ca_cert)
        {
          ret = mbedtls_ssl_conf_own_cert(&config, &client_crt, &client_key);
          throw_if_failure("mbedtls_ssl_conf_own_cert", ret);
        }
      mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);

      ret = mbedtls_ssl_setup(&ssl, &config);
      throw_if_failure("mbedtls_ssl_setup", ret);

      on_handshake(slot);
    }
  catch (const std::system_error &ex)
    {
      slot.call(ex.code());
    }
}

void
TLSStream::on_handshake(connect_slot_t slot)
{
  try
    {
      auto self = shared_from_this();

      int ret = mbedtls_ssl_handshake(&ssl);
      ESP_LOGD(tag, "Handshake ret = %x", -ret);
      switch (ret)
        {
        case 0:
          ESP_LOGD(tag, "TLS Handshake complete");
          break;

        case MBEDTLS_ERR_SSL_WANT_READ:
          loop->notify_read(server_fd.fd, [this, self, slot](std::error_code ec) {
            if (!ec)
              {
                on_handshake(slot);
              }
            else
              {
                slot.call(ec);
              }
          });
          break;

        case MBEDTLS_ERR_SSL_WANT_WRITE:
          loop->notify_write(server_fd.fd, [this, self, slot](std::error_code ec) {
            if (!ec)
              {
                on_handshake(slot);
              }
            else
              {
                slot.call(ec);
              }
          });
          break;

        default:
          ESP_LOGE(tag, "TLS Handshake failed: %x", -ret);
          throw_if_failure("mbedtls_ssl_handshake", ret);
        }

      if (ret == 0)
        {
          ret = mbedtls_ssl_get_verify_result(&ssl);
          if (ret != 0)
            {
              char buf[512];
              mbedtls_x509_crt_verify_info(buf, sizeof(buf), "", ret);
              ESP_LOGE(tag, "TLS Verify failed: %s", buf);
            }

          if (mbedtls_ssl_get_peer_cert(&ssl) != NULL)
            {
              char buf[512];
              mbedtls_x509_crt_info((char *)buf, sizeof(buf) - 1, "", mbedtls_ssl_get_peer_cert(&ssl));
              ESP_LOGD(tag, "Peer certificate information: %s", buf);
            }

          connected_property.set(true);
          slot.call(std::error_code());
        }
    }
  catch (const std::system_error &ex)
    {
      slot.call(ex.code());
    }
}

void
TLSStream::log_failure(std::string msg, int error_code)
{
  if (error_code != 0)
    {
      char error_msg[512];
      mbedtls_strerror(error_code, error_msg, sizeof(error_msg));
      ESP_LOGE(tag, "Error: %s %s %d", msg.c_str(), error_msg, error_code);
    }
}

void
TLSStream::throw_if_failure(std::string msg, int error_code)
{
  if (error_code != 0)
    {
      char error_msg[512];
      mbedtls_strerror(error_code, error_msg, sizeof(error_msg));
      ESP_LOGE(tag, "Error: %s %s %d", msg.c_str(), error_msg, error_code);

      // TODO: convert errors.
      std::error_code ec(NetworkErrc::TLSProtocolError);
      throw std::system_error(ec, msg);
    }
}

int
TLSStream::verify_certificate(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
{
  char buf[1024];
  ((void)data);

  ESP_LOGD(tag, "Verify requested for (depth %d):", depth);
  mbedtls_x509_crt_info(buf, sizeof(buf) - 1, "", crt);
  ESP_LOGD(tag, "%s", buf);

  return 0;
}
<commit_msg>Fix use of certificate<commit_after>// Copyright (C) 2017, 2018 Rob Caelers <rob.caelers@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 "loopp/net/TLSStream.hpp"

#include <string>
#include <system_error>

#include <netdb.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include "loopp/net/NetworkErrors.hpp"

#include "lwip/sockets.h"
#include "lwip/sys.h"

extern "C"
{
#include "esp_heap_caps.h"
#include "mbedtls/esp_debug.h"
}

#include "esp_log.h"

static const char tag[] = "NET";

using namespace loopp;
using namespace loopp::net;

TLSStream::TLSStream(std::shared_ptr<loopp::core::MainLoop> loop) : Stream(loop), loop(loop)
{
  mbedtls_net_init(&server_fd);
  mbedtls_ssl_init(&ssl);
  mbedtls_ssl_config_init(&config);

  mbedtls_x509_crt_init(&client_crt);
  mbedtls_x509_crt_init(&ca_crt);
  mbedtls_pk_init(&client_key);

#ifdef CONFIG_MBEDTLS_DEBUG
  mbedtls_esp_enable_debug_log(&config, 4);
#endif

  init_entropy();
}

TLSStream::~TLSStream()
{
  if (server_fd.fd != -1)
    {
      ::close(server_fd.fd);
      loop->unnotify(server_fd.fd);
    }

  mbedtls_net_free(&server_fd);
  mbedtls_x509_crt_free(&client_crt);
  mbedtls_x509_crt_free(&ca_crt);
  mbedtls_pk_free(&client_key);
  mbedtls_ssl_config_free(&config);
  mbedtls_ctr_drbg_free(&ctr_drbg);
  mbedtls_entropy_free(&entropy);
}

void
TLSStream::set_client_certificate(const char *cert, const char *key)
{
  assert(cert != nullptr && key != nullptr);
  parse_cert(&client_crt, cert);
  parse_key(&client_key, key);
  have_client_cert = true;
}

void
TLSStream::set_ca_certificate(const char *cert)
{
  assert(cert != nullptr);
  parse_cert(&ca_crt, cert);
  have_ca_cert = true;
}

void
TLSStream::init_entropy()
{
  mbedtls_entropy_init(&entropy);
  mbedtls_ctr_drbg_init(&ctr_drbg);

  int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
  throw_if_failure("mbedtls_ctr_drbg_seed", ret);
}

void
TLSStream::parse_cert(mbedtls_x509_crt *crt, const char *cert_str)
{
  mbedtls_x509_crt_init(crt);
  int ret = mbedtls_x509_crt_parse(crt, (const unsigned char *)cert_str, strlen(cert_str) + 1);
  throw_if_failure("mbedtls_x509_crt_parse", ret);
}

void
TLSStream::parse_key(mbedtls_pk_context *key, const char *key_str)
{
  mbedtls_pk_init(key);
  int ret = mbedtls_pk_parse_key(key, (const unsigned char *)key_str, strlen(key_str) + 1, (const unsigned char *)"", 0);
  throw_if_failure("mbedtls_x509_crt_parse", ret);
}

int
TLSStream::socket_read(uint8_t *buffer, std::size_t count)
{
  int ret = mbedtls_ssl_read(&ssl, buffer, count);

  if (ret == MBEDTLS_ERR_SSL_WANT_READ)
    {
      ret = -EAGAIN;
    }

  return ret;
}

int
TLSStream::socket_write(uint8_t *buffer, std::size_t count)
{
  int ret = mbedtls_ssl_write(&ssl, buffer, count);

  if (ret == MBEDTLS_ERR_SSL_WANT_WRITE)
    {
      ret = -EAGAIN;
    }

  return ret;
}

void
TLSStream::socket_close()
{
  int ret = 0;
  do
    {
      ret = mbedtls_ssl_close_notify(&ssl);
    }
  while (ret == MBEDTLS_ERR_SSL_WANT_WRITE);
}

void
TLSStream::socket_on_connected(std::string host, connect_slot_t slot)
{
  ESP_LOGD(tag, "Connected. Starting handshake");

  server_fd.fd = get_socket();

  try
    {
      int ret = mbedtls_ssl_set_hostname(&ssl, host.c_str());
      throw_if_failure("mbedtls_ssl_set_hostname", ret);

      ret = mbedtls_ssl_config_defaults(&config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM,
                                        MBEDTLS_SSL_PRESET_DEFAULT);
      throw_if_failure("mbedtls_ssl_config_defaults", ret);

      mbedtls_ssl_conf_verify(&config, verify_certificate, NULL);
      mbedtls_ssl_conf_rng(&config, mbedtls_ctr_drbg_random, &ctr_drbg);
      mbedtls_ssl_conf_authmode(&config, have_ca_cert ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_OPTIONAL);
      if (have_ca_cert)
        {
          ESP_LOGD(tag, "Using CA cert");
          mbedtls_ssl_conf_ca_chain(&config, &ca_crt, NULL);
        }
      if (have_client_cert)
        {
          ESP_LOGD(tag, "Using Client cert");
          ret = mbedtls_ssl_conf_own_cert(&config, &client_crt, &client_key);
          throw_if_failure("mbedtls_ssl_conf_own_cert", ret);
        }
      mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);

      ret = mbedtls_ssl_setup(&ssl, &config);
      throw_if_failure("mbedtls_ssl_setup", ret);

      on_handshake(slot);
    }
  catch (const std::system_error &ex)
    {
      slot.call(ex.code());
    }
}

void
TLSStream::on_handshake(connect_slot_t slot)
{
  try
    {
      auto self = shared_from_this();

      int ret = mbedtls_ssl_handshake(&ssl);
      ESP_LOGD(tag, "Handshake ret = %x", -ret);
      switch (ret)
        {
        case 0:
          ESP_LOGD(tag, "TLS Handshake complete");
          break;

        case MBEDTLS_ERR_SSL_WANT_READ:
          loop->notify_read(server_fd.fd, [this, self, slot](std::error_code ec) {
            if (!ec)
              {
                on_handshake(slot);
              }
            else
              {
                slot.call(ec);
              }
          });
          break;

        case MBEDTLS_ERR_SSL_WANT_WRITE:
          loop->notify_write(server_fd.fd, [this, self, slot](std::error_code ec) {
            if (!ec)
              {
                on_handshake(slot);
              }
            else
              {
                slot.call(ec);
              }
          });
          break;

        default:
          ESP_LOGE(tag, "TLS Handshake failed: %x", -ret);
          throw_if_failure("mbedtls_ssl_handshake", ret);
        }

      if (ret == 0)
        {
          ret = mbedtls_ssl_get_verify_result(&ssl);
          if (ret != 0)
            {
              char buf[512];
              mbedtls_x509_crt_verify_info(buf, sizeof(buf), "", ret);
              ESP_LOGE(tag, "TLS Verify failed: %s", buf);
            }

          if (mbedtls_ssl_get_peer_cert(&ssl) != NULL)
            {
              char buf[512];
              mbedtls_x509_crt_info((char *)buf, sizeof(buf) - 1, "", mbedtls_ssl_get_peer_cert(&ssl));
              ESP_LOGD(tag, "Peer certificate information: %s", buf);
            }

          connected_property.set(true);
          slot.call(std::error_code());
        }
    }
  catch (const std::system_error &ex)
    {
      slot.call(ex.code());
    }
}

void
TLSStream::log_failure(std::string msg, int error_code)
{
  if (error_code != 0)
    {
      char error_msg[512];
      mbedtls_strerror(error_code, error_msg, sizeof(error_msg));
      ESP_LOGE(tag, "Error: %s %s %d", msg.c_str(), error_msg, error_code);
    }
}

void
TLSStream::throw_if_failure(std::string msg, int error_code)
{
  if (error_code != 0)
    {
      char error_msg[512];
      mbedtls_strerror(error_code, error_msg, sizeof(error_msg));
      ESP_LOGE(tag, "Error: %s %s %d", msg.c_str(), error_msg, error_code);

      // TODO: convert errors.
      std::error_code ec(NetworkErrc::TLSProtocolError);
      throw std::system_error(ec, msg);
    }
}

int
TLSStream::verify_certificate(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
{
  char buf[1024];
  ((void)data);

  ESP_LOGD(tag, "Verify requested for (depth %d):", depth);
  mbedtls_x509_crt_info(buf, sizeof(buf) - 1, "", crt);
  ESP_LOGD(tag, "%s", buf);

  return 0;
}
<|endoftext|>


<commit_before>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

#include <stdexcept>
#include <nupic/engine/RegionImplFactory.hpp>
#include <nupic/engine/RegionImpl.hpp>
#include <nupic/engine/Region.hpp>
#include <nupic/engine/Spec.hpp>
#include <nupic/os/DynamicLibrary.hpp>
#include <nupic/os/Path.hpp>
#include <nupic/os/OS.hpp>
#include <nupic/os/Env.hpp>
#include <nupic/ntypes/Value.hpp>
#include <nupic/ntypes/BundleIO.hpp>
#include <nupic/engine/YAMLUtils.hpp>
#include <nupic/engine/TestNode.hpp>
#include <nupic/regions/VectorFileEffector.hpp>
#include <nupic/regions/VectorFileSensor.hpp>
#include <nupic/utils/Log.hpp>
#include <nupic/utils/StringUtils.hpp>

// from http://stackoverflow.com/a/9096509/1781435
#define stringify(x)  #x
#define expand_and_stringify(x) stringify(x)

// Path from site-packages to packages that contain NuPIC Python regions
static std::vector<const char *> packages { "nupic.regions", "nupic.regions.extra" };

namespace nupic
{

  // Allows the user to add custom regions to the package list
  void RegionImplFactory::registerRegionPackage(const char * path)
  {
    packages.push_back(path);
  }

  class DynamicPythonLibrary
  {
    typedef void (*initPythonFunc)();
    typedef void (*finalizePythonFunc)();
    typedef void * (*createSpecFunc)(const char *, void **);
    typedef int (*destroySpecFunc)(const char *);
    typedef void * (*createPyNodeFunc)(const char *, void *, void *, void **);
    typedef void * (*deserializePyNodeFunc)(const char *, void *, void *, void *);
  public:
    DynamicPythonLibrary() :
      initPython_(nullptr),
      finalizePython_(nullptr),
      createSpec_(nullptr),
      destroySpec_(nullptr),
      createPyNode_(nullptr)
    {
      // To find the pynode plugin we need the nupic
      // installation directory.
#if defined(NTA_OS_WINDOWS)
      std::string command = "python -c \"import sys;import os;import nupic;sys.stdout.write(os.path.abspath(os.path.join(nupic.__file__, \"\"../..\"\")))\"";
#else
      std::string command = "python -c 'import sys;import os;import nupic;sys.stdout.write(os.path.abspath(os.path.join(nupic.__file__, \"../..\")))'";
#endif
      rootDir_ = OS::executeCommand(command);
      if (!Path::exists(rootDir_))
        NTA_THROW << "Unable to find NuPIC library in '" << rootDir_ << "'";

#if defined(NTA_OS_WINDOWS)
      const char * filename = "cpp_region.dll";
#else
      const char * filename = "libcpp_region.so";
#endif

      std::string libName = Path::join(rootDir_, "nupic", filename);

      if (!Path::exists(libName))
        NTA_THROW << "Unable to find library '" << libName << "'";

      std::string errorString;
      DynamicLibrary * p =
        DynamicLibrary::load(libName,
                             // export as LOCAL because we don't want
                             // the symbols to be globally visible;
                             // But the python module that we load
                             // has to be able to access symbols from
                             // libpython.so; Since libpython.so is linked
                             // to the pynode shared library, it appears
                             // we have to make the pynode shared library
                             // symbols global. TODO: investigate
                             DynamicLibrary::GLOBAL|
                             // Evaluate them NOW instead of LAZY to catch
                             // errors up front, even though this takes
                             // a little longer to load the library.
                             // However -- the current dependency chain
                             // PyNode->Region->RegionImplFactory apparently
                             // creates never-used dependencies on YAML
                             // so until this is resolved use LAZY
                             DynamicLibrary::LAZY,
                             errorString);
      NTA_CHECK(p) << "Unable to load the pynode library: " << errorString;

      pynodeLibrary_ = boost::shared_ptr<DynamicLibrary>(p);

      initPython_ = (initPythonFunc)pynodeLibrary_->getSymbol("NTA_initPython");
      NTA_CHECK(initPython_) << "Unable to find NTA_initPython symbol in " << filename;

      finalizePython_ = (finalizePythonFunc)pynodeLibrary_->getSymbol("NTA_finalizePython");
      NTA_CHECK(finalizePython_) << "Unable to find NTA_finalizePython symbol in " << filename;

      createPyNode_ = (createPyNodeFunc)pynodeLibrary_->getSymbol("NTA_createPyNode");
      NTA_CHECK(createPyNode_) << "Unable to find NTA_createPyNode symbol in " << filename;

      deserializePyNode_ = (deserializePyNodeFunc)pynodeLibrary_->getSymbol("NTA_deserializePyNode");
      NTA_CHECK(createPyNode_) << "Unable to find NTA_createPyNode symbol in " << filename;

      createSpec_ = (createSpecFunc)pynodeLibrary_->getSymbol("NTA_createSpec");
      NTA_CHECK(createSpec_) << "Unable to find NTA_createSpec symbol in " << filename;

      destroySpec_ = (destroySpecFunc)pynodeLibrary_->getSymbol("NTA_destroySpec");
      NTA_CHECK(destroySpec_) << "Unable to find NTA_destroySpec symbol in " << filename;

      (*initPython_)();
    }

    ~DynamicPythonLibrary()
    {
      //NTA_DEBUG << "In DynamicPythonLibrary Destructor";
      if (finalizePython_)
        finalizePython_();
    }

    void * createSpec(std::string nodeType, void ** exception)
    {
      //NTA_DEBUG << "RegionImplFactory::createSpec(" << nodeType << ")";
      return (*createSpec_)(nodeType.c_str(), exception);
    }

    int destroySpec(std::string nodeType)
    {
      NTA_INFO << "destroySpec(" << nodeType << ")";
      return (*destroySpec_)(nodeType.c_str());
    }

    void * createPyNode(const std::string& nodeType,
                        ValueMap * nodeParams,
                        Region * region,
                        void ** exception)
    {
      //NTA_DEBUG << "RegionImplFactory::createPyNode(" << nodeType << ")";
      return (*createPyNode_)(nodeType.c_str(),
                              reinterpret_cast<void *>(nodeParams),
                              reinterpret_cast<void*>(region),
                              exception);

    }

    void * deserializePyNode(const std::string& nodeType,
                             BundleIO* bundle,
                             Region * region,
                             void ** exception)
    {
      //NTA_DEBUG << "RegionImplFactory::deserializePyNode(" << nodeType << ")";
      return (*deserializePyNode_)(nodeType.c_str(),
                                   reinterpret_cast<void*>(bundle),
                                   reinterpret_cast<void*>(region),
                                   exception);
    }

    const std::string& getRootDir() const
    {
      return rootDir_;
    }

  private:
    std::string rootDir_;
    boost::shared_ptr<DynamicLibrary> pynodeLibrary_;
    initPythonFunc initPython_;
    finalizePythonFunc finalizePython_;
    createSpecFunc createSpec_;
    destroySpecFunc destroySpec_;
    createPyNodeFunc createPyNode_;
    deserializePyNodeFunc deserializePyNode_;
  };

RegionImplFactory & RegionImplFactory::getInstance()
{
  static RegionImplFactory instance;

  return instance;
}

// This function creates either a NuPIC 2 or NuPIC 1 Python node
static RegionImpl * createPyNode(DynamicPythonLibrary * pyLib,
                                 const std::string & nodeType,
                                 ValueMap * nodeParams,
                                 Region * region)
{
  for (auto package : packages)
  {

    // Construct the full module path to the requested node
    std::string fullNodeType = std::string(package);
    if (!fullNodeType.empty()) // Not in current directory
      fullNodeType += std::string(".");
    fullNodeType += std::string(nodeType.c_str() + 3);

    void * exception = nullptr;
    void * node = pyLib->createPyNode(fullNodeType, nodeParams, region, &exception);
    if (node)
      return static_cast<RegionImpl*>(node);
  }

  NTA_THROW << "Unable to create region " << region->getName() << " of type " << nodeType;
  return nullptr;
}

// This function deserializes either a NuPIC 2 or NuPIC 1 Python node
static RegionImpl * deserializePyNode(DynamicPythonLibrary * pyLib,
                                      const std::string & nodeType,
                                      BundleIO & bundle,
                                      Region * region)
{
  // We need to find the module so that we know if it is NuPIC 1 or NuPIC 2
  for (auto package : packages)
  {

    // Construct the full module path to the requested node
    std::string fullNodeType = std::string(package);
    if (!fullNodeType.empty()) // Not in current directory
      fullNodeType += std::string(".");
    fullNodeType += std::string(nodeType.c_str() + 3);

    void *exception = nullptr;
    void * node = pyLib->deserializePyNode(fullNodeType, &bundle, region, &exception);
    if (node)
      return static_cast<RegionImpl*>(node);
  }
  NTA_THROW << "Unable to deserialize region " << region->getName() << " of type " << nodeType;
  return nullptr;

}

RegionImpl* RegionImplFactory::createRegionImpl(const std::string nodeType,
                                                const std::string nodeParams,
                                                Region* region)
{

  RegionImpl *mn = nullptr;
  Spec *ns = getSpec(nodeType);
  ValueMap vm = YAMLUtils::toValueMap(
    nodeParams.c_str(),
    ns->parameters,
    nodeType,
    region->getName());

  if (nodeType == "TestNode")
  {
    mn = new TestNode(vm, region);
  } else if (nodeType == "VectorFileEffector")
  {
    mn = new VectorFileEffector(vm, region);
  } else if (nodeType == "VectorFileSensor")
  {
    mn = new VectorFileSensor(vm, region);
  } else if ((nodeType.find(std::string("py.")) == 0))
  {
    if (!pyLib_)
      pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    mn = createPyNode(pyLib_.get(), nodeType, &vm, region);
  } else
  {
    NTA_THROW << "Unsupported node type '" << nodeType << "'";
  }
  return mn;

}

RegionImpl* RegionImplFactory::deserializeRegionImpl(const std::string nodeType,
                                                     BundleIO& bundle,
                                                     Region* region)
{

  RegionImpl *mn = nullptr;

  if (nodeType == "TestNode")
  {
    mn = new TestNode(bundle, region);
  } else if (nodeType == "VectorFileEffector")
  {
    mn = new VectorFileEffector(bundle, region);
  } else if (nodeType == "VectorFileSensor")
  {
    mn = new VectorFileSensor(bundle, region);
  } else if (StringUtils::startsWith(nodeType, "py."))
  {
    if (!pyLib_)
      pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    mn = deserializePyNode(pyLib_.get(), nodeType, bundle, region);
  } else
  {
    NTA_THROW << "Unsupported node type '" << nodeType << "'";
  }
  return mn;

}

// This function returns the node spec of a NuPIC 2 or NuPIC 1 Python node
static Spec * getPySpec(DynamicPythonLibrary * pyLib,
                                const std::string & nodeType)
{
  for (auto package : packages)
  {

    // Construct the full module path to the requested node
    std::string fullNodeType = std::string(package);
    if (!fullNodeType.empty()) // Not in current directory
      fullNodeType += std::string(".");
    fullNodeType += std::string(nodeType.c_str() + 3);
    std::cout << fullNodeType << std::endl;

    void * exception = nullptr;
    void * ns = pyLib->createSpec(fullNodeType, &exception);
    if (ns) {
      return (Spec *)ns;
    }
  }

  NTA_THROW << "Matching Python module for " << nodeType << " not found.";
}

Spec * RegionImplFactory::getSpec(const std::string nodeType)
{
  std::map<std::string, Spec*>::iterator it;
  // return from cache if we already have it
  it = nodespecCache_.find(nodeType);
  if (it != nodespecCache_.end())
    return it->second;

  // grab the nodespec and cache it
  // one entry per supported node type
  Spec * ns = nullptr;
  if (nodeType == "TestNode")
  {
    ns = TestNode::createSpec();
  }
  else if (nodeType == "VectorFileEffector")
  {
    ns = VectorFileEffector::createSpec();
  }
  else if (nodeType == "VectorFileSensor")
  {
    ns = VectorFileSensor::createSpec();
  }
  else if (nodeType.find(std::string("py.")) == 0)
  {
    if (!pyLib_)
      pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    ns = getPySpec(pyLib_.get(), nodeType);
  }
  else
  {
    NTA_THROW << "getSpec() -- Unsupported node type '" << nodeType << "'";
  }

  if (!ns)
    NTA_THROW << "Unable to get node spec for: " << nodeType;

  nodespecCache_[nodeType] = ns;
  return ns;
}

void RegionImplFactory::cleanup()
{
  std::map<std::string, Spec*>::iterator ns;
  // destroy all nodespecs
  for (ns = nodespecCache_.begin(); ns != nodespecCache_.end(); ns++)
  {
    assert(ns->second != nullptr);
    // PyNode node specs are destroyed by the C++ PyNode
    if (ns->first.substr(0, 3) == "py.")
    {
      pyLib_->destroySpec(ns->first);
    }
    else
    {
      delete ns->second;
    }

    ns->second = nullptr;
  }

  nodespecCache_.clear();

  // Never release the Python dynamic library!
  // This is due to cleanup issues of Python itself
  // See: http://docs.python.org/c-api/init.html#Py_Finalize
  //pyLib_.reset();
}

}

<commit_msg>removed print statement<commit_after>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

#include <stdexcept>
#include <nupic/engine/RegionImplFactory.hpp>
#include <nupic/engine/RegionImpl.hpp>
#include <nupic/engine/Region.hpp>
#include <nupic/engine/Spec.hpp>
#include <nupic/os/DynamicLibrary.hpp>
#include <nupic/os/Path.hpp>
#include <nupic/os/OS.hpp>
#include <nupic/os/Env.hpp>
#include <nupic/ntypes/Value.hpp>
#include <nupic/ntypes/BundleIO.hpp>
#include <nupic/engine/YAMLUtils.hpp>
#include <nupic/engine/TestNode.hpp>
#include <nupic/regions/VectorFileEffector.hpp>
#include <nupic/regions/VectorFileSensor.hpp>
#include <nupic/utils/Log.hpp>
#include <nupic/utils/StringUtils.hpp>

// from http://stackoverflow.com/a/9096509/1781435
#define stringify(x)  #x
#define expand_and_stringify(x) stringify(x)

// Path from site-packages to packages that contain NuPIC Python regions
static std::vector<const char *> packages { "nupic.regions", "nupic.regions.extra" };

namespace nupic
{

  // Allows the user to add custom regions to the package list
  void RegionImplFactory::registerRegionPackage(const char * path)
  {
    packages.push_back(path);
  }

  class DynamicPythonLibrary
  {
    typedef void (*initPythonFunc)();
    typedef void (*finalizePythonFunc)();
    typedef void * (*createSpecFunc)(const char *, void **);
    typedef int (*destroySpecFunc)(const char *);
    typedef void * (*createPyNodeFunc)(const char *, void *, void *, void **);
    typedef void * (*deserializePyNodeFunc)(const char *, void *, void *, void *);
  public:
    DynamicPythonLibrary() :
      initPython_(nullptr),
      finalizePython_(nullptr),
      createSpec_(nullptr),
      destroySpec_(nullptr),
      createPyNode_(nullptr)
    {
      // To find the pynode plugin we need the nupic
      // installation directory.
#if defined(NTA_OS_WINDOWS)
      std::string command = "python -c \"import sys;import os;import nupic;sys.stdout.write(os.path.abspath(os.path.join(nupic.__file__, \"\"../..\"\")))\"";
#else
      std::string command = "python -c 'import sys;import os;import nupic;sys.stdout.write(os.path.abspath(os.path.join(nupic.__file__, \"../..\")))'";
#endif
      rootDir_ = OS::executeCommand(command);
      if (!Path::exists(rootDir_))
        NTA_THROW << "Unable to find NuPIC library in '" << rootDir_ << "'";

#if defined(NTA_OS_WINDOWS)
      const char * filename = "cpp_region.dll";
#else
      const char * filename = "libcpp_region.so";
#endif

      std::string libName = Path::join(rootDir_, "nupic", filename);

      if (!Path::exists(libName))
        NTA_THROW << "Unable to find library '" << libName << "'";

      std::string errorString;
      DynamicLibrary * p =
        DynamicLibrary::load(libName,
                             // export as LOCAL because we don't want
                             // the symbols to be globally visible;
                             // But the python module that we load
                             // has to be able to access symbols from
                             // libpython.so; Since libpython.so is linked
                             // to the pynode shared library, it appears
                             // we have to make the pynode shared library
                             // symbols global. TODO: investigate
                             DynamicLibrary::GLOBAL|
                             // Evaluate them NOW instead of LAZY to catch
                             // errors up front, even though this takes
                             // a little longer to load the library.
                             // However -- the current dependency chain
                             // PyNode->Region->RegionImplFactory apparently
                             // creates never-used dependencies on YAML
                             // so until this is resolved use LAZY
                             DynamicLibrary::LAZY,
                             errorString);
      NTA_CHECK(p) << "Unable to load the pynode library: " << errorString;

      pynodeLibrary_ = boost::shared_ptr<DynamicLibrary>(p);

      initPython_ = (initPythonFunc)pynodeLibrary_->getSymbol("NTA_initPython");
      NTA_CHECK(initPython_) << "Unable to find NTA_initPython symbol in " << filename;

      finalizePython_ = (finalizePythonFunc)pynodeLibrary_->getSymbol("NTA_finalizePython");
      NTA_CHECK(finalizePython_) << "Unable to find NTA_finalizePython symbol in " << filename;

      createPyNode_ = (createPyNodeFunc)pynodeLibrary_->getSymbol("NTA_createPyNode");
      NTA_CHECK(createPyNode_) << "Unable to find NTA_createPyNode symbol in " << filename;

      deserializePyNode_ = (deserializePyNodeFunc)pynodeLibrary_->getSymbol("NTA_deserializePyNode");
      NTA_CHECK(createPyNode_) << "Unable to find NTA_createPyNode symbol in " << filename;

      createSpec_ = (createSpecFunc)pynodeLibrary_->getSymbol("NTA_createSpec");
      NTA_CHECK(createSpec_) << "Unable to find NTA_createSpec symbol in " << filename;

      destroySpec_ = (destroySpecFunc)pynodeLibrary_->getSymbol("NTA_destroySpec");
      NTA_CHECK(destroySpec_) << "Unable to find NTA_destroySpec symbol in " << filename;

      (*initPython_)();
    }

    ~DynamicPythonLibrary()
    {
      //NTA_DEBUG << "In DynamicPythonLibrary Destructor";
      if (finalizePython_)
        finalizePython_();
    }

    void * createSpec(std::string nodeType, void ** exception)
    {
      //NTA_DEBUG << "RegionImplFactory::createSpec(" << nodeType << ")";
      return (*createSpec_)(nodeType.c_str(), exception);
    }

    int destroySpec(std::string nodeType)
    {
      NTA_INFO << "destroySpec(" << nodeType << ")";
      return (*destroySpec_)(nodeType.c_str());
    }

    void * createPyNode(const std::string& nodeType,
                        ValueMap * nodeParams,
                        Region * region,
                        void ** exception)
    {
      //NTA_DEBUG << "RegionImplFactory::createPyNode(" << nodeType << ")";
      return (*createPyNode_)(nodeType.c_str(),
                              reinterpret_cast<void *>(nodeParams),
                              reinterpret_cast<void*>(region),
                              exception);

    }

    void * deserializePyNode(const std::string& nodeType,
                             BundleIO* bundle,
                             Region * region,
                             void ** exception)
    {
      //NTA_DEBUG << "RegionImplFactory::deserializePyNode(" << nodeType << ")";
      return (*deserializePyNode_)(nodeType.c_str(),
                                   reinterpret_cast<void*>(bundle),
                                   reinterpret_cast<void*>(region),
                                   exception);
    }

    const std::string& getRootDir() const
    {
      return rootDir_;
    }

  private:
    std::string rootDir_;
    boost::shared_ptr<DynamicLibrary> pynodeLibrary_;
    initPythonFunc initPython_;
    finalizePythonFunc finalizePython_;
    createSpecFunc createSpec_;
    destroySpecFunc destroySpec_;
    createPyNodeFunc createPyNode_;
    deserializePyNodeFunc deserializePyNode_;
  };

RegionImplFactory & RegionImplFactory::getInstance()
{
  static RegionImplFactory instance;

  return instance;
}

// This function creates either a NuPIC 2 or NuPIC 1 Python node
static RegionImpl * createPyNode(DynamicPythonLibrary * pyLib,
                                 const std::string & nodeType,
                                 ValueMap * nodeParams,
                                 Region * region)
{
  for (auto package : packages)
  {

    // Construct the full module path to the requested node
    std::string fullNodeType = std::string(package);
    if (!fullNodeType.empty()) // Not in current directory
      fullNodeType += std::string(".");
    fullNodeType += std::string(nodeType.c_str() + 3);

    void * exception = nullptr;
    void * node = pyLib->createPyNode(fullNodeType, nodeParams, region, &exception);
    if (node)
      return static_cast<RegionImpl*>(node);
  }

  NTA_THROW << "Unable to create region " << region->getName() << " of type " << nodeType;
  return nullptr;
}

// This function deserializes either a NuPIC 2 or NuPIC 1 Python node
static RegionImpl * deserializePyNode(DynamicPythonLibrary * pyLib,
                                      const std::string & nodeType,
                                      BundleIO & bundle,
                                      Region * region)
{
  // We need to find the module so that we know if it is NuPIC 1 or NuPIC 2
  for (auto package : packages)
  {

    // Construct the full module path to the requested node
    std::string fullNodeType = std::string(package);
    if (!fullNodeType.empty()) // Not in current directory
      fullNodeType += std::string(".");
    fullNodeType += std::string(nodeType.c_str() + 3);

    void *exception = nullptr;
    void * node = pyLib->deserializePyNode(fullNodeType, &bundle, region, &exception);
    if (node)
      return static_cast<RegionImpl*>(node);
  }
  NTA_THROW << "Unable to deserialize region " << region->getName() << " of type " << nodeType;
  return nullptr;

}

RegionImpl* RegionImplFactory::createRegionImpl(const std::string nodeType,
                                                const std::string nodeParams,
                                                Region* region)
{

  RegionImpl *mn = nullptr;
  Spec *ns = getSpec(nodeType);
  ValueMap vm = YAMLUtils::toValueMap(
    nodeParams.c_str(),
    ns->parameters,
    nodeType,
    region->getName());

  if (nodeType == "TestNode")
  {
    mn = new TestNode(vm, region);
  } else if (nodeType == "VectorFileEffector")
  {
    mn = new VectorFileEffector(vm, region);
  } else if (nodeType == "VectorFileSensor")
  {
    mn = new VectorFileSensor(vm, region);
  } else if ((nodeType.find(std::string("py.")) == 0))
  {
    if (!pyLib_)
      pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    mn = createPyNode(pyLib_.get(), nodeType, &vm, region);
  } else
  {
    NTA_THROW << "Unsupported node type '" << nodeType << "'";
  }
  return mn;

}

RegionImpl* RegionImplFactory::deserializeRegionImpl(const std::string nodeType,
                                                     BundleIO& bundle,
                                                     Region* region)
{

  RegionImpl *mn = nullptr;

  if (nodeType == "TestNode")
  {
    mn = new TestNode(bundle, region);
  } else if (nodeType == "VectorFileEffector")
  {
    mn = new VectorFileEffector(bundle, region);
  } else if (nodeType == "VectorFileSensor")
  {
    mn = new VectorFileSensor(bundle, region);
  } else if (StringUtils::startsWith(nodeType, "py."))
  {
    if (!pyLib_)
      pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    mn = deserializePyNode(pyLib_.get(), nodeType, bundle, region);
  } else
  {
    NTA_THROW << "Unsupported node type '" << nodeType << "'";
  }
  return mn;

}

// This function returns the node spec of a NuPIC 2 or NuPIC 1 Python node
static Spec * getPySpec(DynamicPythonLibrary * pyLib,
                                const std::string & nodeType)
{
  for (auto package : packages)
  {

    // Construct the full module path to the requested node
    std::string fullNodeType = std::string(package);
    if (!fullNodeType.empty()) // Not in current directory
      fullNodeType += std::string(".");
    fullNodeType += std::string(nodeType.c_str() + 3);

    void * exception = nullptr;
    void * ns = pyLib->createSpec(fullNodeType, &exception);
    if (ns) {
      return (Spec *)ns;
    }
  }

  NTA_THROW << "Matching Python module for " << nodeType << " not found.";
}

Spec * RegionImplFactory::getSpec(const std::string nodeType)
{
  std::map<std::string, Spec*>::iterator it;
  // return from cache if we already have it
  it = nodespecCache_.find(nodeType);
  if (it != nodespecCache_.end())
    return it->second;

  // grab the nodespec and cache it
  // one entry per supported node type
  Spec * ns = nullptr;
  if (nodeType == "TestNode")
  {
    ns = TestNode::createSpec();
  }
  else if (nodeType == "VectorFileEffector")
  {
    ns = VectorFileEffector::createSpec();
  }
  else if (nodeType == "VectorFileSensor")
  {
    ns = VectorFileSensor::createSpec();
  }
  else if (nodeType.find(std::string("py.")) == 0)
  {
    if (!pyLib_)
      pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    ns = getPySpec(pyLib_.get(), nodeType);
  }
  else
  {
    NTA_THROW << "getSpec() -- Unsupported node type '" << nodeType << "'";
  }

  if (!ns)
    NTA_THROW << "Unable to get node spec for: " << nodeType;

  nodespecCache_[nodeType] = ns;
  return ns;
}

void RegionImplFactory::cleanup()
{
  std::map<std::string, Spec*>::iterator ns;
  // destroy all nodespecs
  for (ns = nodespecCache_.begin(); ns != nodespecCache_.end(); ns++)
  {
    assert(ns->second != nullptr);
    // PyNode node specs are destroyed by the C++ PyNode
    if (ns->first.substr(0, 3) == "py.")
    {
      pyLib_->destroySpec(ns->first);
    }
    else
    {
      delete ns->second;
    }

    ns->second = nullptr;
  }

  nodespecCache_.clear();

  // Never release the Python dynamic library!
  // This is due to cleanup issues of Python itself
  // See: http://docs.python.org/c-api/init.html#Py_Finalize
  //pyLib_.reset();
}

}

<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This application is open source and may be redistributed and/or modified
 * freely and without restriction, both in commericial and non commericial
 * applications, as long as this copyright notice is maintained.
 *
 * This application is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
*/

#include <sstream>

#include <osg/Notify>
#include <osgDB/ReaderWriter>
#include <osgDB/FileNameUtils>
#include <osgDB/Registry>

#include "daeReader.h"
#include "daeWriter.h"

#define EXTENSION_NAME "dae"

///////////////////////////////////////////////////////////////////////////
// OSG reader/writer plugin for the COLLADA 1.4.x ".dae" format.
// See http://collada.org/ and http://khronos.org/collada/

class ReaderWriterDAE : public osgDB::ReaderWriter
{
public:
    ReaderWriterDAE() : dae_(NULL)
    {
    }

    ~ReaderWriterDAE()
    {
        if(dae_ != NULL){
            delete dae_;
            DAE::cleanup();
            dae_ = NULL;
        }
    }

    const char* className() const { return "COLLADA 1.4.x DAE reader/writer"; }

    bool acceptsExtension(const std::string& extension) const
    {
        return osgDB::equalCaseInsensitive( extension, EXTENSION_NAME );
    }

    ReadResult readNode(const std::string&, const Options*) const;

    WriteResult writeNode(const osg::Node&, const std::string&, const Options*) const;

private:

    DAE *dae_;

};

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

osgDB::ReaderWriter::ReadResult
ReaderWriterDAE::readNode(const std::string& fname,
        const osgDB::ReaderWriter::Options* options) const
{
    std::string ext( osgDB::getLowerCaseFileExtension(fname) );
    if( ! acceptsExtension(ext) ) return ReadResult::FILE_NOT_HANDLED;

    std::string fileName( osgDB::findDataFile( fname, options ) );
    if( fileName.empty() ) return ReadResult::FILE_NOT_FOUND;

    osg::notify(osg::INFO) << "ReaderWriterDAE( \"" << fileName << "\" )" << std::endl;

    if (dae_ == NULL)
      const_cast<ReaderWriterDAE *>(this)->dae_ = new DAE();

    osgdae::daeReader daeReader(dae_);
    std::string fileURI( osgDB::convertFileNameToUnixStyle(fileName) );
    if ( ! daeReader.convert( fileURI ) )
    {
        osg::notify( osg::WARN ) << "Load failed in COLLADA DOM conversion" << std::endl;
        return ReadResult::ERROR_IN_READING_FILE;
    }

    osg::Node* rootNode( daeReader.getRootNode() );
    return rootNode;
}

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

osgDB::ReaderWriter::WriteResult
ReaderWriterDAE::writeNode( const osg::Node& node,
        const std::string& fname, const osgDB::ReaderWriter::Options* options ) const
{
    std::string ext( osgDB::getLowerCaseFileExtension(fname) );
    if( ! acceptsExtension(ext) ) return WriteResult::FILE_NOT_HANDLED;

    // Process options
    bool usePolygon(false);
    if( options )
    {
      std::istringstream iss( options->getOptionString() );
      std::string opt;

      while( std::getline( iss, opt, ',' ) )
      {
        if( opt == "polygon")  usePolygon = true;
        else
        {
          osg::notify(osg::WARN)
              << "\n" "COLLADA dae plugin: unrecognized option \"" << opt << "\"\n"
              << "comma-delimited options:\n"
              << "\tpolygon = use polygons instead of polylists for element\n"
              << "example: osgviewer -O polygon bar.dae" "\n"
              << std::endl;
        }
      }
    }

    if (dae_ == NULL)
      const_cast<ReaderWriterDAE *>(this)->dae_ = new DAE();

    osgdae::daeWriter daeWriter(dae_, fname, usePolygon );
    daeWriter.setRootNode( node );
    const_cast<osg::Node*>(&node)->accept( daeWriter );

    osgDB::ReaderWriter::WriteResult retVal( WriteResult::ERROR_IN_WRITING_FILE );
    if ( daeWriter.isSuccess() )
    {
        if ( daeWriter.writeFile() )
        {
            retVal = WriteResult::FILE_SAVED;
        }
    }

    return retVal;
}

///////////////////////////////////////////////////////////////////////////
// Add ourself to the Registry to instantiate the reader/writer.

osgDB::RegisterReaderWriterProxy<ReaderWriterDAE> g_readerWriter_DAE_Proxy;

// vim: set sw=4 ts=8 et ic ai:
<commit_msg>Added SERIALIZER to ReaderWriterDAE to make sure initialization is thread safe.<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This application is open source and may be redistributed and/or modified
 * freely and without restriction, both in commericial and non commericial
 * applications, as long as this copyright notice is maintained.
 *
 * This application is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
*/

#include <sstream>

#include <osg/Notify>
#include <osgDB/ReaderWriter>
#include <osgDB/FileNameUtils>
#include <osgDB/Registry>

#include <OpenThreads/ScopedLock>
#include <osgDB/ReentrantMutex>

#include "daeReader.h"
#include "daeWriter.h"

#define EXTENSION_NAME "dae"

#define SERIALIZER() OpenThreads::ScopedLock<osgDB::ReentrantMutex> lock(_serializerMutex)

///////////////////////////////////////////////////////////////////////////
// OSG reader/writer plugin for the COLLADA 1.4.x ".dae" format.
// See http://collada.org/ and http://khronos.org/collada/

class ReaderWriterDAE : public osgDB::ReaderWriter
{
public:
    ReaderWriterDAE() : _dae(NULL)
    {
    }

    ~ReaderWriterDAE()
    {
        if(_dae != NULL){
            delete _dae;
            DAE::cleanup();
            _dae = NULL;
        }
    }

    const char* className() const { return "COLLADA 1.4.x DAE reader/writer"; }

    bool acceptsExtension(const std::string& extension) const
    {
        return osgDB::equalCaseInsensitive( extension, EXTENSION_NAME );
    }

    ReadResult readNode(const std::string&, const Options*) const;

    WriteResult writeNode(const osg::Node&, const std::string&, const Options*) const;

private:

    mutable DAE *_dae;
    mutable osgDB::ReentrantMutex _serializerMutex;

};

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

osgDB::ReaderWriter::ReadResult
ReaderWriterDAE::readNode(const std::string& fname,
        const osgDB::ReaderWriter::Options* options) const
{
    SERIALIZER();

    std::string ext( osgDB::getLowerCaseFileExtension(fname) );
    if( ! acceptsExtension(ext) ) return ReadResult::FILE_NOT_HANDLED;

    std::string fileName( osgDB::findDataFile( fname, options ) );
    if( fileName.empty() ) return ReadResult::FILE_NOT_FOUND;

    osg::notify(osg::INFO) << "ReaderWriterDAE( \"" << fileName << "\" )" << std::endl;

    if (_dae == NULL)
      _dae = new DAE();

    osgdae::daeReader daeReader(_dae);
    std::string fileURI( osgDB::convertFileNameToUnixStyle(fileName) );
    if ( ! daeReader.convert( fileURI ) )
    {
        osg::notify( osg::WARN ) << "Load failed in COLLADA DOM conversion" << std::endl;
        return ReadResult::ERROR_IN_READING_FILE;
    }

    osg::Node* rootNode( daeReader.getRootNode() );
    return rootNode;
}

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

osgDB::ReaderWriter::WriteResult
ReaderWriterDAE::writeNode( const osg::Node& node,
        const std::string& fname, const osgDB::ReaderWriter::Options* options ) const
{
    SERIALIZER();

    std::string ext( osgDB::getLowerCaseFileExtension(fname) );
    if( ! acceptsExtension(ext) ) return WriteResult::FILE_NOT_HANDLED;

    // Process options
    bool usePolygon(false);
    if( options )
    {
      std::istringstream iss( options->getOptionString() );
      std::string opt;

      while( std::getline( iss, opt, ',' ) )
      {
        if( opt == "polygon")  usePolygon = true;
        else
        {
          osg::notify(osg::WARN)
              << "\n" "COLLADA dae plugin: unrecognized option \"" << opt << "\"\n"
              << "comma-delimited options:\n"
              << "\tpolygon = use polygons instead of polylists for element\n"
              << "example: osgviewer -O polygon bar.dae" "\n"
              << std::endl;
        }
      }
    }

    if (_dae == NULL)
      _dae = new DAE();

    osgdae::daeWriter daeWriter(_dae, fname, usePolygon );
    daeWriter.setRootNode( node );
    const_cast<osg::Node*>(&node)->accept( daeWriter );

    osgDB::ReaderWriter::WriteResult retVal( WriteResult::ERROR_IN_WRITING_FILE );
    if ( daeWriter.isSuccess() )
    {
        if ( daeWriter.writeFile() )
        {
            retVal = WriteResult::FILE_SAVED;
        }
    }

    return retVal;
}

///////////////////////////////////////////////////////////////////////////
// Add ourself to the Registry to instantiate the reader/writer.

osgDB::RegisterReaderWriterProxy<ReaderWriterDAE> g_readerWriter_DAE_Proxy;

// vim: set sw=4 ts=8 et ic ai:
<|endoftext|>


<commit_before>#include "MatrixTransform.h"
#include "Group.h"

#include <osg/Notify>

#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/Registry>

using namespace osg;
using namespace osgDB;

class IVEReaderWriter : public ReaderWriter
{
    public:
        virtual const char* className() const { return "IVE Reader/Writer"; }

        virtual bool acceptsExtension(const std::string& extension) const
        {
            return equalCaseInsensitive(extension,"ive");
        }

        virtual ReadResult readObject(const std::string& file, const Options* options) const
        {
            return readNode(file, options);
        }

        virtual ReadResult readNode(const std::string& file, const Options* options) const
        {
            std::string ext = osgDB::getLowerCaseFileExtension(file);
            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;

            std::string fileName = osgDB::findDataFile( file, options );
            if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;

            // code for setting up the database path so that internally referenced file are searched for on relative paths.
            osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
            local_opt->setDatabasePath(osgDB::getFilePath(fileName));

            std::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
            return readNode(istream,local_opt.get());
        }

        virtual ReadResult readObject(std::istream& fin, const Options* options) const
        {
            return readNode(fin, options);
        }

        virtual ReadResult readNode(std::istream& fin, const Options* options) const
        {
            try{
                // Create datainputstream.
                ive::DataInputStream in(&fin);
                in.setOptions(options);

                return in.readNode();
            }
            catch(ive::Exception e)
            {
                osg::notify(osg::NOTICE)<<"Error reading file: "<< e.getError()<<std::endl;
                return ReadResult::FILE_NOT_HANDLED;
            }
        }

        virtual WriteResult writeObject(const Object& object,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
        {
            const Node* node = dynamic_cast<const Node*>(&object);
            if (node) return writeNode( *node, fileName, options );
            return WriteResult::FILE_NOT_HANDLED;
        }

        virtual WriteResult writeNode(const Node& node,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
        {
            std::string ext = getFileExtension(fileName);
            if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;

            // code for setting up the database path so that internally referenced file are searched for on relative paths.
            osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
            if(local_opt->getDatabasePathList().empty())
                local_opt->setDatabasePath(osgDB::getFilePath(fileName));

            std::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);
            if (!fout) return WriteResult::ERROR_IN_WRITING_FILE;

            WriteResult result = writeNode(node, fout, local_opt.get());
            fout.close();
            return result;
        }

        virtual WriteResult writeObject(const Object& object,std::ostream& fout, const osgDB::ReaderWriter::Options* options) const
        {
            const Node* node = dynamic_cast<const Node*>(&object);
            if (node) return writeNode( *node, fout, options );
            return WriteResult::FILE_NOT_HANDLED;
        }

        virtual WriteResult writeNode(const Node& node,std::ostream& fout, const osgDB::ReaderWriter::Options* options) const
        {
            try
            {
                ive::DataOutputStream out(&fout);

                out.setOptions(options);

                out.writeNode(const_cast<osg::Node*>(&node));

                if ( fout.fail() ) return WriteResult::ERROR_IN_WRITING_FILE;

                return WriteResult::FILE_SAVED;
            }
            catch(ive::Exception e)
            {
                osg::notify(osg::WARN)<<"Error writing IVE file: "<< e.getError() << std::endl;
            }
            return WriteResult::FILE_NOT_HANDLED;

        }

};

// now register with Registry to instantiate the above
// reader/writer.
RegisterReaderWriterProxy<IVEReaderWriter> g_IVEReaderWriterProxy;
<commit_msg>From Eric Sokolowski, "Added the ability to read and write images directly in the ive plugin, through the osgDB::readImageFile and osgDB::writeImageFile functions. This is useful for storing compressed textures on disk for rapid playback for animations."<commit_after>#include "MatrixTransform.h"
#include "Group.h"

#include <osg/Notify>

#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/Registry>

using namespace osg;
using namespace osgDB;

class IVEReaderWriter : public ReaderWriter
{
    public:
        virtual const char* className() const { return "IVE Reader/Writer"; }

        virtual bool acceptsExtension(const std::string& extension) const
        {
            return equalCaseInsensitive(extension,"ive");
        }

        virtual ReadResult readObject(const std::string& file, const Options* options) const
        {
            return readNode(file, options);
        }

        virtual ReadResult readImage(const std::string& file, const Options* options) const
        {
            std::string ext = osgDB::getLowerCaseFileExtension(file);
            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
            std::string fileName = osgDB::findDataFile(file, options);
            if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;

            // code for setting up the database path so that internally referenced files are searched for on relative paths.
            osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
            local_opt->setDatabasePath(osgDB::getFilePath(fileName));
            std::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
            return readImage(istream, local_opt.get());
        }

        virtual ReadResult readNode(const std::string& file, const Options* options) const
        {
            std::string ext = osgDB::getLowerCaseFileExtension(file);
            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;

            std::string fileName = osgDB::findDataFile( file, options );
            if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;

            // code for setting up the database path so that internally referenced file are searched for on relative paths.
            osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
            local_opt->setDatabasePath(osgDB::getFilePath(fileName));

            std::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
            return readNode(istream,local_opt.get());
        }

        virtual ReadResult readObject(std::istream& fin, const Options* options) const
        {
            return readNode(fin, options);
        }

        virtual ReadResult readImage(std::istream& fin, const Options* options) const
        {
            try{
                ive::DataInputStream in(&fin);
                in.setOptions(options);
                return in.readImage(ive::IMAGE_INCLUDE_DATA);
            }
            catch(ive::Exception e)
            {
                osg::notify(osg::NOTICE)<<"Error reading image: "<<e.getError()<<std::endl;
                return ReadResult::FILE_NOT_HANDLED;
            }
        }

        virtual ReadResult readNode(std::istream& fin, const Options* options) const
        {
            try{
                // Create datainputstream.
                ive::DataInputStream in(&fin);
                in.setOptions(options);

                return in.readNode();
            }
            catch(ive::Exception e)
            {
                osg::notify(osg::NOTICE)<<"Error reading file: "<< e.getError()<<std::endl;
                return ReadResult::FILE_NOT_HANDLED;
            }
        }

        virtual WriteResult writeObject(const Object& object,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
        {
            const Node* node = dynamic_cast<const Node*>(&object);
            if (node) return writeNode( *node, fileName, options );
            const Image* image = dynamic_cast<const Image*>(&object);
            if (image) return writeImage(*image, fileName, options);
            return WriteResult::FILE_NOT_HANDLED;
        }

        virtual WriteResult writeImage(const Image& image,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
        {
            std::string ext = getFileExtension(fileName);
            if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
            // code for setting up the database path so that internally referenced file are searched for on relative paths.
            osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
            if(local_opt->getDatabasePathList().empty())
                local_opt->setDatabasePath(osgDB::getFilePath(fileName));
            std::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);
            if (!fout) return WriteResult::ERROR_IN_WRITING_FILE;
            WriteResult result = writeImage(image, fout, local_opt.get());
            fout.close();
            return result;
        }

        virtual WriteResult writeNode(const Node& node,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
        {
            std::string ext = getFileExtension(fileName);
            if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;

            // code for setting up the database path so that internally referenced file are searched for on relative paths.
            osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
            if(local_opt->getDatabasePathList().empty())
                local_opt->setDatabasePath(osgDB::getFilePath(fileName));

            std::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);
            if (!fout) return WriteResult::ERROR_IN_WRITING_FILE;

            WriteResult result = writeNode(node, fout, local_opt.get());
            fout.close();
            return result;
        }

        virtual WriteResult writeObject(const Object& object,std::ostream& fout, const osgDB::ReaderWriter::Options* options) const
        {
            const Node* node = dynamic_cast<const Node*>(&object);
            if (node) return writeNode( *node, fout, options );
            const Image* image = dynamic_cast<const Image*>(&object);
            if (image) return writeImage(*image, fout, options);
            return WriteResult::FILE_NOT_HANDLED;
        }

        virtual WriteResult writeImage(const Image& image,std::ostream& fout, const osgDB::ReaderWriter::Options* options) const
        {
            try
            {
                ive::DataOutputStream out(&fout);
                out.setOptions(options);
                out.writeImage(ive::IMAGE_INCLUDE_DATA, const_cast<osg::Image*>(&image));
                if (fout.fail()) return WriteResult::ERROR_IN_WRITING_FILE;
                return WriteResult::FILE_SAVED;
            }
            catch(ive::Exception e)
            {
                osg::notify(osg::WARN)<<"Error writing IVE image: "<< e.getError() << std::endl;
            }
            return WriteResult::FILE_NOT_HANDLED;
        }

        virtual WriteResult writeNode(const Node& node,std::ostream& fout, const osgDB::ReaderWriter::Options* options) const
        {
            try
            {
                ive::DataOutputStream out(&fout);

                out.setOptions(options);

                out.writeNode(const_cast<osg::Node*>(&node));

                if ( fout.fail() ) return WriteResult::ERROR_IN_WRITING_FILE;

                return WriteResult::FILE_SAVED;
            }
            catch(ive::Exception e)
            {
                osg::notify(osg::WARN)<<"Error writing IVE file: "<< e.getError() << std::endl;
            }
            return WriteResult::FILE_NOT_HANDLED;

        }

};

// now register with Registry to instantiate the above
// reader/writer.
RegisterReaderWriterProxy<IVEReaderWriter> g_IVEReaderWriterProxy;
<|endoftext|>