<commit_before>/*
 * File:   RTPPackage.cpp
 * Author: daniel
 *
 * Created on May 02, 2015, 13:03 PM
 */

#include "RTPPackageHandler.h"

RTPPackageHandler::RTPPackageHandler(unsigned int sizeOfAudioData, PayloadType payloadType, unsigned int sizeOfRTPHeader)
{
	this->sizeOfAudioData = sizeOfAudioData;
	this->sizeOfRTPHeader = sizeOfRTPHeader;
	this->size = sizeOfAudioData + sizeOfRTPHeader;
	this->payloadType = payloadType;

	newRTPPackageBuffer = new char[size];
	workBuffer = new char[size];

	unsigned seed1 = std::chrono::system_clock::now().time_since_epoch().count();
	std::mt19937 tmp(seed1);
	this->randomGenerator = tmp;

	sequenceNr = getRandomNumber();
	timestamp = getTimestamp();
	ssrc = getAudioSourceId();
}

auto RTPPackageHandler::getNewRTPPackage(void* audioData) -> void*
{
	RTPHeader RTPHeaderObject;

	RTPHeaderObject.version = 2;
	RTPHeaderObject.padding = 0;
	RTPHeaderObject.extension = 0;
	RTPHeaderObject.csrc_count = 0;
	RTPHeaderObject.marker = 0;
	RTPHeaderObject.payload_type = this->payloadType;
	RTPHeaderObject.sequence_number = (this->sequenceNr++) % UINT16_MAX;
        //we need steady clock so it will always change monotonically (etc. no change to/from daylight savings time)
        //additionally, we need to count with milliseconds precision
	RTPHeaderObject.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
	RTPHeaderObject.ssrc = this->ssrc;

	// Copy RTPHeader and Audiodata in the buffer
	memcpy((char*)newRTPPackageBuffer, &RTPHeaderObject, this->sizeOfRTPHeader);
	memcpy((char*)(newRTPPackageBuffer)+sizeOfRTPHeader, audioData, this->sizeOfAudioData);

	return newRTPPackageBuffer;
}

auto RTPPackageHandler::getRTPPackageData(void *rtpPackage) -> void*
{
	if (rtpPackage == nullptr)
	{
		return (char*)workBuffer + sizeOfRTPHeader;
	}
	memcpy((char*)(workBuffer) + sizeOfRTPHeader, (char*)rtpPackage + sizeOfRTPHeader, sizeOfAudioData);
	return (char*)(workBuffer)+sizeOfRTPHeader;
}

auto RTPPackageHandler::getRTPPackageHeader(void *rtpPackage) -> RTPHeader*
{
	if (rtpPackage == nullptr)
	{
		return (RTPHeader*)workBuffer;
	}
	memcpy((char*)(workBuffer), (char*)rtpPackage, sizeOfRTPHeader);
	return (RTPHeader*)workBuffer;
}

unsigned int RTPPackageHandler::getRandomNumber()
{
	return this->randomGenerator();
}

unsigned int RTPPackageHandler::getTimestamp()
{
    //start timestamp should be a random number
    return this->randomGenerator();
}

unsigned int RTPPackageHandler::getAudioSourceId()
{
    //SSRC should be a random number
    return this->randomGenerator();
}

auto RTPPackageHandler::getSize() -> unsigned int const
{
	return size;
}

auto RTPPackageHandler::getSizeOfRTPHeader() -> unsigned int const
{
	return sizeOfRTPHeader;
}

auto RTPPackageHandler::getSizeOfAudioData() -> unsigned int const
{
	return sizeOfAudioData;
}

auto RTPPackageHandler::getWorkBuffer() -> void*
{
	return this->workBuffer;
}<commit_msg>Added random starting timestamp to meet RTP standard Signed-off-by:doe300 <stadeldani@web.de><commit_after>/*
 * File:   RTPPackage.cpp
 * Author: daniel
 *
 * Created on May 02, 2015, 13:03 PM
 */

#include "RTPPackageHandler.h"

RTPPackageHandler::RTPPackageHandler(unsigned int sizeOfAudioData, PayloadType payloadType, unsigned int sizeOfRTPHeader)
{
	this->sizeOfAudioData = sizeOfAudioData;
	this->sizeOfRTPHeader = sizeOfRTPHeader;
	this->size = sizeOfAudioData + sizeOfRTPHeader;
	this->payloadType = payloadType;

	newRTPPackageBuffer = new char[size];
	workBuffer = new char[size];

	unsigned seed1 = std::chrono::system_clock::now().time_since_epoch().count();
	std::mt19937 tmp(seed1);
	this->randomGenerator = tmp;

	sequenceNr = getRandomNumber();
	timestamp = getTimestamp();
	ssrc = getAudioSourceId();
}

auto RTPPackageHandler::getNewRTPPackage(void* audioData) -> void*
{
	RTPHeader RTPHeaderObject;

	RTPHeaderObject.version = 2;
	RTPHeaderObject.padding = 0;
	RTPHeaderObject.extension = 0;
	RTPHeaderObject.csrc_count = 0;
	RTPHeaderObject.marker = 0;
	RTPHeaderObject.payload_type = this->payloadType;
	RTPHeaderObject.sequence_number = (this->sequenceNr++) % UINT16_MAX;
        //we need steady clock so it will always change monotonically (etc. no change to/from daylight savings time)
        //additionally, we need to count with milliseconds precision
        //we add the random starting timestamp to meet the condition specified in the RTP standard
	RTPHeaderObject.timestamp = this->timestamp + std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
	RTPHeaderObject.ssrc = this->ssrc;

	// Copy RTPHeader and Audiodata in the buffer
	memcpy((char*)newRTPPackageBuffer, &RTPHeaderObject, this->sizeOfRTPHeader);
	memcpy((char*)(newRTPPackageBuffer)+sizeOfRTPHeader, audioData, this->sizeOfAudioData);

	return newRTPPackageBuffer;
}

auto RTPPackageHandler::getRTPPackageData(void *rtpPackage) -> void*
{
	if (rtpPackage == nullptr)
	{
		return (char*)workBuffer + sizeOfRTPHeader;
	}
	memcpy((char*)(workBuffer) + sizeOfRTPHeader, (char*)rtpPackage + sizeOfRTPHeader, sizeOfAudioData);
	return (char*)(workBuffer)+sizeOfRTPHeader;
}

auto RTPPackageHandler::getRTPPackageHeader(void *rtpPackage) -> RTPHeader*
{
	if (rtpPackage == nullptr)
	{
		return (RTPHeader*)workBuffer;
	}
	memcpy((char*)(workBuffer), (char*)rtpPackage, sizeOfRTPHeader);
	return (RTPHeader*)workBuffer;
}

unsigned int RTPPackageHandler::getRandomNumber()
{
	return this->randomGenerator();
}

unsigned int RTPPackageHandler::getTimestamp()
{
    //start timestamp should be a random number
    return this->randomGenerator();
}

unsigned int RTPPackageHandler::getAudioSourceId()
{
    //SSRC should be a random number
    return this->randomGenerator();
}

auto RTPPackageHandler::getSize() -> unsigned int const
{
	return size;
}

auto RTPPackageHandler::getSizeOfRTPHeader() -> unsigned int const
{
	return sizeOfRTPHeader;
}

auto RTPPackageHandler::getSizeOfAudioData() -> unsigned int const
{
	return sizeOfAudioData;
}

auto RTPPackageHandler::getWorkBuffer() -> void*
{
	return this->workBuffer;
}<|endoftext|>


<commit_before>// $Id$

// The libMesh Finite Element Library.
// Copyright (C) 2002-2008 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library 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
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// C++ includes

// Local includes
#include "libmesh_config.h"
#include "boundary_info.h"
#include "boundary_mesh.h"
#include "elem.h"
#include "mesh_data.h"
#include "parallel.h"

//------------------------------------------------------
// BoundaryInfo static member initializations
const short int BoundaryInfo::invalid_id = -1234;

//------------------------------------------------------
// BoundaryInfo functions
BoundaryInfo::BoundaryInfo(const MeshBase& m) :
  _mesh (m)
{
}

BoundaryInfo::~BoundaryInfo()
{
  this->clear();
}

void BoundaryInfo::clear()
{
  _boundary_node_id.clear();
  _boundary_side_id.clear();
  _boundary_ids.clear();
}

void BoundaryInfo::sync(BoundaryMesh& boundary_mesh,
			MeshData*     boundary_mesh_data,
			MeshData*     this_mesh_data)
{
  boundary_mesh.clear();

  /**
   * Re-create the boundary mesh.
   */
  boundary_mesh.set_n_subdomains() = this->n_boundary_ids();
  boundary_mesh.set_n_partitions() = this->n_boundary_ids();

  // Add sides to the structure.
  std::map<short int, unsigned int> id_map;

  // Original Code
  //     unsigned int cnt = 0;
  //     for (std::set<short int>::iterator pos = boundary_ids.begin();
  // 	 pos != boundary_ids.end(); ++pos)
  //       id_map[*pos] = cnt++;

  //     id_map[invalid_id] = cnt;

  // New code
  // Here we need to use iota() once it is in the
  // Utility namespace.
  std::for_each(_boundary_ids.begin(),
		_boundary_ids.end(),
		Fill(id_map));

  boundary_mesh.set_n_subdomains() = id_map.size();

  // Make individual copies of all the nodes in the current mesh
  // and add them to the boundary mesh.  Yes, this is overkill because
  // all of the current mesh nodes will not end up in the the boundary
  // mesh.  These nodes can be trimmed later via a call to prepare_for_use().
  {
    libmesh_assert (boundary_mesh.n_nodes() == 0);
    boundary_mesh.reserve_nodes(_mesh.n_nodes());

    MeshBase::const_node_iterator it  = _mesh.nodes_begin();
    MeshBase::const_node_iterator end = _mesh.nodes_end();

    for(; it != end; ++it)
      {
	const Node* node = *it;
	boundary_mesh.add_point(*node); // calls Node::build(Point, id)
      }
  }

  // Add additional sides that aren't flagged with boundary conditions
  MeshBase::const_element_iterator       el     = _mesh.active_elements_begin();
  const MeshBase::const_element_iterator end_el = _mesh.active_elements_end();

  for ( ; el != end_el; ++el)
    {
      const Elem* elem = *el;

      for (unsigned int s=0; s<elem->n_sides(); s++)
	if (elem->neighbor(s) == NULL) // on the boundary
	  {

	    // Build the side - do not use a "proxy" element here:
	    // This will be going into the BoundaryMesh and needs to
	    // stand on its own.
	    AutoPtr<Elem> side (elem->build_side(s, false));

	    // Get the top-level parent for this element
	    const Elem* top_parent = elem->top_parent();

	    // A convenient typedef
	    typedef
	      std::multimap<const Elem*, std::pair<unsigned short int, short int> >::const_iterator
	      Iter;

	    // Find the right id number for that side
	    std::pair<Iter, Iter> pos = _boundary_side_id.equal_range(top_parent);

	    while (pos.first != pos.second)
	      {
		if (pos.first->second.first == s) // already flagged with a boundary condition
		  {
		    side->subdomain_id() =
		      id_map[pos.first->second.second];

		    side->processor_id() =
		      side->subdomain_id();
		    break;
		  }

		++pos.first;
	      }

	    // either the element wasn't found or side s
	    // doesn't have a boundary condition
	    if (pos.first == pos.second)
	      {
		side->subdomain_id() = id_map[invalid_id];
	      }

	    // Add the side
	    Elem* new_elem = boundary_mesh.add_elem(side.release());

	    // This side's Node pointers still point to the nodes of the  original mesh.
	    // We need to re-point them to the boundary mesh's nodes!  Since we copied *ALL* of
	    // the original mesh's nodes over, we should be guaranteed to have the same ordering.
	    for (unsigned int nn=0; nn<new_elem->n_nodes(); ++nn)
	      {
		// Get the correct node pointer, based on the id()
		Node* new_node = boundary_mesh.node_ptr(new_elem->node(nn));

		// sanity check: be sure that the new Nodes global id really matches
		libmesh_assert (new_node->id() == new_elem->node(nn));

		// Assign the new node pointer
		new_elem->set_node(nn) = new_node;
	      }
	  }
    } // end loop over active elements

  // When desired, copy the MeshData
  // to the boundary_mesh
  if ((boundary_mesh_data != NULL) && (this_mesh_data != NULL))
    boundary_mesh_data->assign(*this_mesh_data);

  // Don't repartition this mesh; we're using the processor_id values
  // as a hack to display bcids for now.
  boundary_mesh.partitioner().reset(NULL);

  // Trim any un-used nodes from the Mesh
  boundary_mesh.prepare_for_use();
}

void BoundaryInfo::add_node(const unsigned int node,
			    const short int id)
{
  this->add_node (_mesh.node_ptr(node), id);
}

void BoundaryInfo::add_node(const Node* node,
			    const short int id)
{
  if (id == invalid_id)
    {
      std::cerr << "ERROR: You may not set a boundary ID of "
		<< invalid_id << std::endl
		<< " That is reserved for internal use.\n"
		<< std::endl;

      libmesh_error();
    }

  _boundary_node_id[node] = id;
  _boundary_ids.insert(id);
}

void BoundaryInfo::add_side(const unsigned int e,
			    const unsigned short int side,
			    const short int id)
{
  this->add_side (_mesh.elem(e), side, id);
}

void BoundaryInfo::add_side(const Elem* elem,
			    const unsigned short int side,
			    const short int id)
{
  libmesh_assert (elem != NULL);

  // Only add BCs for level-0 elements.
  libmesh_assert (elem->level() == 0);

  if (id == invalid_id)
    {
      std::cerr << "ERROR: You may not set a boundary ID of "
		<< invalid_id << std::endl
		<< " That is reserved for internal use.\n"
		<< std::endl;

      libmesh_error();
    }

  std::pair<unsigned short int, short int> p(side,id);
  std::pair<const Elem*, std::pair<unsigned short int, short int> >
    kv (elem, p);

  _boundary_side_id.insert(kv);
  _boundary_ids.insert(id);
}

short int BoundaryInfo::boundary_id(const Node* node) const
{
  std::map<const Node*, short int>::const_iterator
    n = _boundary_node_id.find(node);

  // node not in the data structure
  if (n == _boundary_node_id.end())
    return invalid_id;

  return n->second;
}

short int BoundaryInfo::boundary_id(const Elem* const elem,
				    const unsigned short int side) const
{
  libmesh_assert (elem != NULL);

  // Only level-0 elements store BCs.  If this is not a level-0
  // element get its level-0 parent and infer the BCs.
  const Elem*  searched_elem = elem;
  if (elem->level() != 0)
    searched_elem = elem->top_parent ();

  std::pair<std::multimap<const Elem*,
                          std::pair<unsigned short int, short int> >::const_iterator,
            std::multimap<const Elem*,
                          std::pair<unsigned short int, short int> >::const_iterator >
    e = _boundary_side_id.equal_range(searched_elem);

  // elem not in the data structure
  if (e.first == e.second)
    return invalid_id;

  // elem is there, maybe multiple occurances
  while (e.first != e.second)
    {
      // if this is true we found the requested side
      // of the element and want to return the id
      if (e.first->second.first == side)
	return e.first->second.second;

      ++e.first;
    }

  // if we get here, we found elem in the data structure but not
  // the requested side, so return the default value
  return invalid_id;
}

unsigned int BoundaryInfo::side_with_boundary_id(const Elem* const elem,
                                                 const unsigned short int boundary_id) const
{
  const Elem* searched_elem = elem;
  if (elem->level() != 0)
    searched_elem = elem->top_parent();

  std::pair<std::multimap<const Elem*,
                          std::pair<unsigned short int, short int> >::const_iterator,
            std::multimap<const Elem*,
                          std::pair<unsigned short int, short int> >::const_iterator >
    e = _boundary_side_id.equal_range(searched_elem);

  // elem not in the data structure
  if (e.first == e.second)
    return libMesh::invalid_uint;

  // elem is there, maybe multiple occurances
  while (e.first != e.second)
    {
      // if this is true we found the requested boundary_id
      // of the element and want to return the side
      if (e.first->second.second == boundary_id)
	return e.first->second.first;

      ++e.first;
    }

  // if we get here, we found elem in the data structure but not
  // the requested boundary id, so return the default value
  return libMesh::invalid_uint;
}

unsigned int BoundaryInfo::n_boundary_conds () const
{
  // in serial we know the number of bcs from the
  // size of the container
  if (_mesh.is_serial())
    return _boundary_side_id.size();

  // in parallel we need to sum the number of local bcs
  parallel_only();

  unsigned int nbcs=0;

  std::multimap<const Elem*,
                std::pair<unsigned short int,
                          short int> >::const_iterator pos;

  for (pos=_boundary_side_id.begin(); pos != _boundary_side_id.end(); ++pos)
    if (pos->first->processor_id() == libMesh::processor_id())
      nbcs++;

  Parallel::sum (nbcs);

  return nbcs;
}

void BoundaryInfo::build_node_list (std::vector<unsigned int>& nl,
				    std::vector<short int>&    il) const
{
  // Reserve the size, then use push_back
  nl.reserve (_boundary_node_id.size());
  il.reserve (_boundary_node_id.size());

  std::map<const Node*, short int>::const_iterator pos
    = _boundary_node_id.begin();

  for (; pos != _boundary_node_id.end(); ++pos)
    {
      nl.push_back (pos->first->id());
      il.push_back (pos->second);
    }
}

void BoundaryInfo::build_side_list (std::vector<unsigned int>&       el,
				    std::vector<unsigned short int>& sl,
				    std::vector<short int>&          il) const
{
  // Reserve the size, then use push_back
  el.reserve (_boundary_side_id.size());
  sl.reserve (_boundary_side_id.size());
  il.reserve (_boundary_side_id.size());

  std::multimap<const Elem*,
                std::pair<unsigned short int,
                          short int> >::const_iterator pos;

  for (pos=_boundary_side_id.begin(); pos != _boundary_side_id.end();
       ++pos)
    {
      el.push_back (pos->first->id());
      sl.push_back (pos->second.first);
      il.push_back (pos->second.second);
    }
}

void BoundaryInfo::print_info() const
{
  // Print out the nodal BCs
  if (!_boundary_node_id.empty())
    {
      std::cout << "Nodal Boundary conditions:" << std::endl
		<< "--------------------------" << std::endl
		<< "  (Node No., ID)               " << std::endl;

//       std::for_each(_boundary_node_id.begin(),
// 		    _boundary_node_id.end(),
// 		    PrintNodeInfo());

      std::map<const Node*, short int>::const_iterator it        = _boundary_node_id.begin();
      const std::map<const Node*, short int>::const_iterator end = _boundary_node_id.end();

      for (; it != end; ++it)
	std::cout << "  (" << (*it).first->id()
		  << ", "  << (*it).second
		  << ")"  << std::endl;
    }

  // Print out the element BCs
  if (!_boundary_side_id.empty())
    {
      std::cout << std::endl
		<< "Side Boundary conditions:" << std::endl
		<< "-------------------------" << std::endl
		<< "  (Elem No., Side No., ID)      " << std::endl;

//       std::for_each(_boundary_side_id.begin(),
// 		    _boundary_side_id.end(),
//   		    PrintSideInfo());

      std::multimap<const Elem*,
	std::pair<unsigned short int, short int> >::const_iterator it = _boundary_side_id.begin();
      const std::multimap<const Elem*,
	std::pair<unsigned short int, short int> >::const_iterator end = _boundary_side_id.end();

     for (; it != end; ++it)
       std::cout << "  (" << (*it).first->id()
		 << ", "  << (*it).second.first
		 << ", "  << (*it).second.second
		 << ")"   << std::endl;
    }
}
<commit_msg>Don't accidentally add duplicate side boundary condition ids<commit_after>// $Id$

// The libMesh Finite Element Library.
// Copyright (C) 2002-2008 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library 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
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// C++ includes

// Local includes
#include "libmesh_config.h"
#include "boundary_info.h"
#include "boundary_mesh.h"
#include "elem.h"
#include "mesh_data.h"
#include "parallel.h"

//------------------------------------------------------
// BoundaryInfo static member initializations
const short int BoundaryInfo::invalid_id = -1234;

//------------------------------------------------------
// BoundaryInfo functions
BoundaryInfo::BoundaryInfo(const MeshBase& m) :
  _mesh (m)
{
}

BoundaryInfo::~BoundaryInfo()
{
  this->clear();
}

void BoundaryInfo::clear()
{
  _boundary_node_id.clear();
  _boundary_side_id.clear();
  _boundary_ids.clear();
}

void BoundaryInfo::sync(BoundaryMesh& boundary_mesh,
			MeshData*     boundary_mesh_data,
			MeshData*     this_mesh_data)
{
  boundary_mesh.clear();

  /**
   * Re-create the boundary mesh.
   */
  boundary_mesh.set_n_subdomains() = this->n_boundary_ids();
  boundary_mesh.set_n_partitions() = this->n_boundary_ids();

  // Add sides to the structure.
  std::map<short int, unsigned int> id_map;

  // Original Code
  //     unsigned int cnt = 0;
  //     for (std::set<short int>::iterator pos = boundary_ids.begin();
  // 	 pos != boundary_ids.end(); ++pos)
  //       id_map[*pos] = cnt++;

  //     id_map[invalid_id] = cnt;

  // New code
  // Here we need to use iota() once it is in the
  // Utility namespace.
  std::for_each(_boundary_ids.begin(),
		_boundary_ids.end(),
		Fill(id_map));

  boundary_mesh.set_n_subdomains() = id_map.size();

  // Make individual copies of all the nodes in the current mesh
  // and add them to the boundary mesh.  Yes, this is overkill because
  // all of the current mesh nodes will not end up in the the boundary
  // mesh.  These nodes can be trimmed later via a call to prepare_for_use().
  {
    libmesh_assert (boundary_mesh.n_nodes() == 0);
    boundary_mesh.reserve_nodes(_mesh.n_nodes());

    MeshBase::const_node_iterator it  = _mesh.nodes_begin();
    MeshBase::const_node_iterator end = _mesh.nodes_end();

    for(; it != end; ++it)
      {
	const Node* node = *it;
	boundary_mesh.add_point(*node); // calls Node::build(Point, id)
      }
  }

  // Add additional sides that aren't flagged with boundary conditions
  MeshBase::const_element_iterator       el     = _mesh.active_elements_begin();
  const MeshBase::const_element_iterator end_el = _mesh.active_elements_end();

  for ( ; el != end_el; ++el)
    {
      const Elem* elem = *el;

      for (unsigned int s=0; s<elem->n_sides(); s++)
	if (elem->neighbor(s) == NULL) // on the boundary
	  {

	    // Build the side - do not use a "proxy" element here:
	    // This will be going into the BoundaryMesh and needs to
	    // stand on its own.
	    AutoPtr<Elem> side (elem->build_side(s, false));

	    // Get the top-level parent for this element
	    const Elem* top_parent = elem->top_parent();

	    // A convenient typedef
	    typedef
	      std::multimap<const Elem*, std::pair<unsigned short int, short int> >::const_iterator
	      Iter;

	    // Find the right id number for that side
	    std::pair<Iter, Iter> pos = _boundary_side_id.equal_range(top_parent);

	    while (pos.first != pos.second)
	      {
		if (pos.first->second.first == s) // already flagged with a boundary condition
		  {
		    side->subdomain_id() =
		      id_map[pos.first->second.second];

		    side->processor_id() =
		      side->subdomain_id();
		    break;
		  }

		++pos.first;
	      }

	    // either the element wasn't found or side s
	    // doesn't have a boundary condition
	    if (pos.first == pos.second)
	      {
		side->subdomain_id() = id_map[invalid_id];
	      }

	    // Add the side
	    Elem* new_elem = boundary_mesh.add_elem(side.release());

	    // This side's Node pointers still point to the nodes of the  original mesh.
	    // We need to re-point them to the boundary mesh's nodes!  Since we copied *ALL* of
	    // the original mesh's nodes over, we should be guaranteed to have the same ordering.
	    for (unsigned int nn=0; nn<new_elem->n_nodes(); ++nn)
	      {
		// Get the correct node pointer, based on the id()
		Node* new_node = boundary_mesh.node_ptr(new_elem->node(nn));

		// sanity check: be sure that the new Nodes global id really matches
		libmesh_assert (new_node->id() == new_elem->node(nn));

		// Assign the new node pointer
		new_elem->set_node(nn) = new_node;
	      }
	  }
    } // end loop over active elements

  // When desired, copy the MeshData
  // to the boundary_mesh
  if ((boundary_mesh_data != NULL) && (this_mesh_data != NULL))
    boundary_mesh_data->assign(*this_mesh_data);

  // Don't repartition this mesh; we're using the processor_id values
  // as a hack to display bcids for now.
  boundary_mesh.partitioner().reset(NULL);

  // Trim any un-used nodes from the Mesh
  boundary_mesh.prepare_for_use();
}

void BoundaryInfo::add_node(const unsigned int node,
			    const short int id)
{
  this->add_node (_mesh.node_ptr(node), id);
}

void BoundaryInfo::add_node(const Node* node,
			    const short int id)
{
  if (id == invalid_id)
    {
      std::cerr << "ERROR: You may not set a boundary ID of "
		<< invalid_id << std::endl
		<< " That is reserved for internal use.\n"
		<< std::endl;

      libmesh_error();
    }

  _boundary_node_id[node] = id;
  _boundary_ids.insert(id);
}

void BoundaryInfo::add_side(const unsigned int e,
			    const unsigned short int side,
			    const short int id)
{
  this->add_side (_mesh.elem(e), side, id);
}

void BoundaryInfo::add_side(const Elem* elem,
			    const unsigned short int side,
			    const short int id)
{
  libmesh_assert (elem != NULL);

  // Only add BCs for level-0 elements.
  libmesh_assert (elem->level() == 0);

  if (id == invalid_id)
    {
      std::cerr << "ERROR: You may not set a boundary ID of "
		<< invalid_id << std::endl
		<< " That is reserved for internal use.\n"
		<< std::endl;

      libmesh_error();
    }

  // A convenient typedef
  typedef std::multimap<const Elem*, std::pair<unsigned short int, short int> >::const_iterator Iter;

  // Don't add the same ID twice
  std::pair<Iter, Iter> pos = _boundary_side_id.equal_range(elem);

  for (;pos.first != pos.second; ++pos.first)
    if (pos.first->second.first == side &&
        pos.first->second.second == id)
      return;

  std::pair<unsigned short int, short int> p(side,id);
  std::pair<const Elem*, std::pair<unsigned short int, short int> >
    kv (elem, p);

  _boundary_side_id.insert(kv);
  _boundary_ids.insert(id);
}

short int BoundaryInfo::boundary_id(const Node* node) const
{
  std::map<const Node*, short int>::const_iterator
    n = _boundary_node_id.find(node);

  // node not in the data structure
  if (n == _boundary_node_id.end())
    return invalid_id;

  return n->second;
}

short int BoundaryInfo::boundary_id(const Elem* const elem,
				    const unsigned short int side) const
{
  libmesh_assert (elem != NULL);

  // Only level-0 elements store BCs.  If this is not a level-0
  // element get its level-0 parent and infer the BCs.
  const Elem*  searched_elem = elem;
  if (elem->level() != 0)
    searched_elem = elem->top_parent ();

  std::pair<std::multimap<const Elem*,
                          std::pair<unsigned short int, short int> >::const_iterator,
            std::multimap<const Elem*,
                          std::pair<unsigned short int, short int> >::const_iterator >
    e = _boundary_side_id.equal_range(searched_elem);

  // elem not in the data structure
  if (e.first == e.second)
    return invalid_id;

  // elem is there, maybe multiple occurances
  while (e.first != e.second)
    {
      // if this is true we found the requested side
      // of the element and want to return the id
      if (e.first->second.first == side)
	return e.first->second.second;

      ++e.first;
    }

  // if we get here, we found elem in the data structure but not
  // the requested side, so return the default value
  return invalid_id;
}

unsigned int BoundaryInfo::side_with_boundary_id(const Elem* const elem,
                                                 const unsigned short int boundary_id) const
{
  const Elem* searched_elem = elem;
  if (elem->level() != 0)
    searched_elem = elem->top_parent();

  std::pair<std::multimap<const Elem*,
                          std::pair<unsigned short int, short int> >::const_iterator,
            std::multimap<const Elem*,
                          std::pair<unsigned short int, short int> >::const_iterator >
    e = _boundary_side_id.equal_range(searched_elem);

  // elem not in the data structure
  if (e.first == e.second)
    return libMesh::invalid_uint;

  // elem is there, maybe multiple occurances
  while (e.first != e.second)
    {
      // if this is true we found the requested boundary_id
      // of the element and want to return the side
      if (e.first->second.second == boundary_id)
	return e.first->second.first;

      ++e.first;
    }

  // if we get here, we found elem in the data structure but not
  // the requested boundary id, so return the default value
  return libMesh::invalid_uint;
}

unsigned int BoundaryInfo::n_boundary_conds () const
{
  // in serial we know the number of bcs from the
  // size of the container
  if (_mesh.is_serial())
    return _boundary_side_id.size();

  // in parallel we need to sum the number of local bcs
  parallel_only();

  unsigned int nbcs=0;

  std::multimap<const Elem*,
                std::pair<unsigned short int,
                          short int> >::const_iterator pos;

  for (pos=_boundary_side_id.begin(); pos != _boundary_side_id.end(); ++pos)
    if (pos->first->processor_id() == libMesh::processor_id())
      nbcs++;

  Parallel::sum (nbcs);

  return nbcs;
}

void BoundaryInfo::build_node_list (std::vector<unsigned int>& nl,
				    std::vector<short int>&    il) const
{
  // Reserve the size, then use push_back
  nl.reserve (_boundary_node_id.size());
  il.reserve (_boundary_node_id.size());

  std::map<const Node*, short int>::const_iterator pos
    = _boundary_node_id.begin();

  for (; pos != _boundary_node_id.end(); ++pos)
    {
      nl.push_back (pos->first->id());
      il.push_back (pos->second);
    }
}

void BoundaryInfo::build_side_list (std::vector<unsigned int>&       el,
				    std::vector<unsigned short int>& sl,
				    std::vector<short int>&          il) const
{
  // Reserve the size, then use push_back
  el.reserve (_boundary_side_id.size());
  sl.reserve (_boundary_side_id.size());
  il.reserve (_boundary_side_id.size());

  std::multimap<const Elem*,
                std::pair<unsigned short int,
                          short int> >::const_iterator pos;

  for (pos=_boundary_side_id.begin(); pos != _boundary_side_id.end();
       ++pos)
    {
      el.push_back (pos->first->id());
      sl.push_back (pos->second.first);
      il.push_back (pos->second.second);
    }
}

void BoundaryInfo::print_info() const
{
  // Print out the nodal BCs
  if (!_boundary_node_id.empty())
    {
      std::cout << "Nodal Boundary conditions:" << std::endl
		<< "--------------------------" << std::endl
		<< "  (Node No., ID)               " << std::endl;

//       std::for_each(_boundary_node_id.begin(),
// 		    _boundary_node_id.end(),
// 		    PrintNodeInfo());

      std::map<const Node*, short int>::const_iterator it        = _boundary_node_id.begin();
      const std::map<const Node*, short int>::const_iterator end = _boundary_node_id.end();

      for (; it != end; ++it)
	std::cout << "  (" << (*it).first->id()
		  << ", "  << (*it).second
		  << ")"  << std::endl;
    }

  // Print out the element BCs
  if (!_boundary_side_id.empty())
    {
      std::cout << std::endl
		<< "Side Boundary conditions:" << std::endl
		<< "-------------------------" << std::endl
		<< "  (Elem No., Side No., ID)      " << std::endl;

//       std::for_each(_boundary_side_id.begin(),
// 		    _boundary_side_id.end(),
//   		    PrintSideInfo());

      std::multimap<const Elem*,
	std::pair<unsigned short int, short int> >::const_iterator it = _boundary_side_id.begin();
      const std::multimap<const Elem*,
	std::pair<unsigned short int, short int> >::const_iterator end = _boundary_side_id.end();

     for (; it != end; ++it)
       std::cout << "  (" << (*it).first->id()
		 << ", "  << (*it).second.first
		 << ", "  << (*it).second.second
		 << ")"   << std::endl;
    }
}
<|endoftext|>


<commit_before>#define CATCH_CONFIG_MAIN
#include <catch.hpp>
#include <sol.hpp>

std::string free_function() {
    std::cout << "free_function()" << std::endl;
    return "test";
}

struct object {

    std::string operator() () {
        std::cout << "member_test()" << std::endl;
        return "test";
    }

};

int plop_xyz(int x, int y, std::string z) {
    std::cout << x << " " << y << " " << z << std::endl;
    return 11;
}

void run_script(sol::state& lua) {
    lua.script("assert(os.fun() == \"test\")\n"
        "assert(os.name == \"windows\")");
}

TEST_CASE("simple/set_global", "Check if the set_global works properly.") {
    sol::state lua;

    lua.set("a", 9);
    REQUIRE_NOTHROW(lua.script("if a ~= 9 then error('wrong value') end"));

    lua.set("d", "hello");
    REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end"));

    lua.set("e", std::string("hello"));
    REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end"));

    lua.set("f", true);
    REQUIRE_NOTHROW(lua.script("if f ~= true then error('wrong value') end"));
}

TEST_CASE("simple/get", "Tests if the get function works properly.") {
    sol::state lua;

    lua.script("a = 9");
    auto a = lua.get<int>("a");
    REQUIRE(a == 9.0);

    lua.script("b = nil");
    REQUIRE_NOTHROW(lua.get<sol::nil_t>("b"));

    lua.script("d = 'hello'");
    auto d = lua.get<std::string>("d");
    REQUIRE(d == "hello");

    lua.script("e = true");
    auto e = lua.get<bool>("e");
    REQUIRE(e == true);
}

TEST_CASE("simple/addition", "") {
    sol::state lua;

    lua.set("b", 0.2);
    lua.script("c = 9 + b");
    auto c = lua.get<double>("c");

    REQUIRE(c == 9.2);
}

TEST_CASE("simple/if", "") {
    sol::state lua;

    std::string program = "if true then f = 0.1 else f = 'test' end";
    lua.script(program);
    auto f = lua.get<double>("f");

    REQUIRE(f == 0.1);
}

TEST_CASE("simple/evalStream", "The VM evaluates a stream's contents using a reader") {
    sol::state lua;

    std::stringstream sscript;
    int g = 9;
    sscript << "g = " << g << ";";

    REQUIRE_NOTHROW(lua.script(sscript.str()));
    REQUIRE(lua.get<double>("g") == 9.0);
}

TEST_CASE("simple/callWithParameters", "Lua function is called with a few parameters from C++") {
    sol::state lua;

    REQUIRE_NOTHROW(lua.script("function my_add(i, j, k) return i + j + k end"));
    auto f = lua.get<sol::function>("my_add");
    int a;
    REQUIRE_NOTHROW(a = f.call<int>(1, 2, 3));
    REQUIRE(a == 6);
    REQUIRE_THROWS(a = f.call<int>(1, 2, "arf"));
}

TEST_CASE("simple/callCppFunction", "C++ function is called from lua") {
    sol::state lua;

    lua.set_function("plop_xyz", plop_xyz);
    lua.script("x = plop_xyz(2, 6, 'hello')");

    REQUIRE(lua.get<int>("x") == 11);
}

TEST_CASE("simple/callLambda", "A C++ lambda is exposed to lua and called") {
    sol::state lua;

    int x = 0;

    lua.set_function("foo", [ &x ] { x = 1; });

    lua.script("foo()");

    REQUIRE(x == 1);
}

TEST_CASE("advanced/callLambdaReturns", "Checks for lambdas returning values") {
    sol::state lua;

    lua.set_function("a", [ ] { return 42; });
    lua.set_function("b", [ ] { return 42u; });
    lua.set_function("c", [ ] { return 3.14; });
    lua.set_function("d", [ ] { return 6.28f; });
    lua.set_function("e", [ ] { return "lol"; });
    lua.set_function("f", [ ] { return true; });
    lua.set_function("g", [ ] { return std::string("str"); });
    lua.set_function("h", [ ] { });
    lua.set_function("i", [ ] { return sol::nil; });
}

TEST_CASE("advanced/callLambda2", "A C++ lambda is exposed to lua and called") {
    sol::state lua;

    int x = 0;
    lua.set_function("set_x", [ &] (int new_x) {
        x = new_x;
        return 0;
    });

    lua.script("set_x(9)");
    REQUIRE(x == 9);
}

TEST_CASE("negative/basicError", "Check if error handling works correctly") {
    sol::state lua;

    REQUIRE_THROWS(lua.script("nil[5]"));
}

/*
    lua.get<sol::table>("os").set("name", "windows");

    SECTION("")
    lua.get<sol::table>("os").set_function("fun",
        [ ] () {
        std::cout << "stateless lambda()" << std::endl;
        return "test";
    }
   );
    run_script(lua);

    lua.get<sol::table>("os").set_function("fun", &free_function);
    run_script(lua);

    // l-value, can optomize
    auto lval = object();
    lua.get<sol::table>("os").set_function("fun", &object::operator(), lval);
    run_script(lua);

    // Tests will begin failing here
    // stateful lambda: non-convertible, unoptomizable
    int breakit = 50;
    lua.get<sol::table>("os").set_function("fun",
        [ &breakit ] () {
        std::cout << "stateless lambda()" << std::endl;
        return "test";
    }
   );
    run_script(lua);

    // r-value, cannot optomize
    lua.get<sol::table>("os").set_function("fun", &object::operator(), object());
    run_script(lua);

    // r-value, cannot optomize
    auto rval = object();
    lua.get<sol::table>("os").set_function("fun", &object::operator(), std::move(rval));
    run_script(lua);

}
*/
<commit_msg>Test cases for tables and the opening of libraries.<commit_after>#define CATCH_CONFIG_MAIN
#include <catch.hpp>
#include <sol.hpp>

std::string free_function() {
    std::cout << "free_function()" << std::endl;
    return "test";
}

struct object {

    std::string operator() () {
        std::cout << "member_test()" << std::endl;
        return "test";
    }

};

int plop_xyz(int x, int y, std::string z) {
    std::cout << x << " " << y << " " << z << std::endl;
    return 11;
}

TEST_CASE("simple/set_global", "Check if the set_global works properly.") {
    sol::state lua;

    lua.set("a", 9);
    REQUIRE_NOTHROW(lua.script("if a ~= 9 then error('wrong value') end"));

    lua.set("d", "hello");
    REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end"));

    lua.set("e", std::string("hello"));
    REQUIRE_NOTHROW(lua.script("if d ~= 'hello' then error('expected \\'hello\\', got '.. tostring(d)) end"));

    lua.set("f", true);
    REQUIRE_NOTHROW(lua.script("if f ~= true then error('wrong value') end"));
}

TEST_CASE("simple/get", "Tests if the get function works properly.") {
    sol::state lua;

    lua.script("a = 9");
    auto a = lua.get<int>("a");
    REQUIRE(a == 9.0);

    lua.script("b = nil");
    REQUIRE_NOTHROW(lua.get<sol::nil_t>("b"));

    lua.script("d = 'hello'");
    auto d = lua.get<std::string>("d");
    REQUIRE(d == "hello");

    lua.script("e = true");
    auto e = lua.get<bool>("e");
    REQUIRE(e == true);
}

TEST_CASE("simple/addition", "") {
    sol::state lua;

    lua.set("b", 0.2);
    lua.script("c = 9 + b");
    auto c = lua.get<double>("c");

    REQUIRE(c == 9.2);
}

TEST_CASE("simple/if", "") {
    sol::state lua;

    std::string program = "if true then f = 0.1 else f = 'test' end";
    lua.script(program);
    auto f = lua.get<double>("f");

    REQUIRE(f == 0.1);
}

TEST_CASE("simple/callWithParameters", "Lua function is called with a few parameters from C++") {
    sol::state lua;

    REQUIRE_NOTHROW(lua.script("function my_add(i, j, k) return i + j + k end"));
    auto f = lua.get<sol::function>("my_add");
    int a;
    REQUIRE_NOTHROW(a = f.call<int>(1, 2, 3));
    REQUIRE(a == 6);
    REQUIRE_THROWS(a = f.call<int>(1, 2, "arf"));
}

TEST_CASE("simple/callCppFunction", "C++ function is called from lua") {
    sol::state lua;

    lua.set_function("plop_xyz", plop_xyz);
    lua.script("x = plop_xyz(2, 6, 'hello')");

    REQUIRE(lua.get<int>("x") == 11);
}

TEST_CASE("simple/callLambda", "A C++ lambda is exposed to lua and called") {
    sol::state lua;

    int x = 0;

    lua.set_function("foo", [ &x ] { x = 1; });

    lua.script("foo()");

    REQUIRE(x == 1);
}

TEST_CASE("advanced/callLambdaReturns", "Checks for lambdas returning values") {
    sol::state lua;

    lua.set_function("a", [ ] { return 42; });
    lua.set_function("b", [ ] { return 42u; });
    lua.set_function("c", [ ] { return 3.14; });
    lua.set_function("d", [ ] { return 6.28f; });
    lua.set_function("e", [ ] { return "lol"; });
    lua.set_function("f", [ ] { return true; });
    lua.set_function("g", [ ] { return std::string("str"); });
    lua.set_function("h", [ ] { });
    lua.set_function("i", [ ] { return sol::nil; });
}

TEST_CASE("advanced/callLambda2", "A C++ lambda is exposed to lua and called") {
    sol::state lua;

    int x = 0;
    lua.set_function("set_x", [ &] (int new_x) {
        x = new_x;
        return 0;
    });

    lua.script("set_x(9)");
    REQUIRE(x == 9);
}

TEST_CASE("negative/basicError", "Check if error handling works correctly") {
    sol::state lua;

    REQUIRE_THROWS(lua.script("nil[5]"));
}

TEST_CASE("libraries", "Check if we can open libraries through sol") {
    sol::state lua;
    REQUIRE_NOTHROW(lua.open_libraries(sol::lib::base, sol::lib::os));
}

TEST_CASE("tables/variables", "Check if tables and variables work as intended") {
    sol::state lua;
    lua.open_libraries(sol::lib::base, sol::lib::os);
    lua.get<sol::table>("os").set("name", "windows");
    REQUIRE_NOTHROW(lua.script("assert(os.name == \"windows\")"));
}

TEST_CASE("tables/functions_variables", "Check if tables and function calls work as intended") {
    sol::state lua;
    lua.open_libraries(sol::lib::base, sol::lib::os);
    auto run_script = [ ] (sol::state& lua) -> void {
        lua.script("assert(os.fun() == \"test\")");
    };

    lua.get<sol::table>("os").set_function("fun",
        [ ] () {
            std::cout << "stateless lambda()" << std::endl;
            return "test";
        }
    );
    REQUIRE_NOTHROW(run_script(lua));

    lua.get<sol::table>("os").set_function("fun", &free_function);
    REQUIRE_NOTHROW(run_script(lua));

    // l-value, can optomize
    auto lval = object();
    lua.get<sol::table>("os").set_function("fun", &object::operator(), lval);
    REQUIRE_NOTHROW((lua));

    // stateful lambda: non-convertible, unoptomizable
    int breakit = 50;
    lua.get<sol::table>("os").set_function("fun",
        [ &breakit ] () {
        std::cout << "stateless lambda()" << std::endl;
        return "test";
    }
    );
    REQUIRE_NOTHROW(run_script(lua));

    // r-value, cannot optomize
    lua.get<sol::table>("os").set_function("fun", &object::operator(), object());
    REQUIRE_NOTHROW(run_script(lua));

    // r-value, cannot optomize
    auto rval = object();
    lua.get<sol::table>("os").set_function("fun", &object::operator(), std::move(rval));
    REQUIRE_NOTHROW(run_script(lua));
}<|endoftext|>


<commit_before>#include "SUIT_FileDlg.h"

#include "SUIT_Tools.h"
#include "SUIT_Session.h"
#include "SUIT_Desktop.h"
#include "SUIT_MessageBox.h"
#include "SUIT_ResourceMgr.h"
#include "SUIT_FileValidator.h"

#include <qdir.h>
#include <qlabel.h>
#include <qregexp.h>
#include <qpalette.h>
#include <qobjectlist.h>
#include <qcombobox.h>
#include <qpushbutton.h>
#include <qapplication.h>

#define MIN_COMBO_SIZE 100

QString SUIT_FileDlg::myLastVisitedPath;

/*!
Constructor
*/
SUIT_FileDlg::SUIT_FileDlg( QWidget* parent, bool open, bool showQuickDir, bool modal ) :
QFileDialog( parent, 0, modal ),
myValidator( 0 ),
myQuickCombo( 0 ), myQuickButton( 0 ), myQuickLab( 0 ),
myOpen( open )
{
  if ( parent->icon() )
    setIcon( *parent->icon() );
  setSizeGripEnabled( true );

  if ( showQuickDir ) {
    // inserting quick dir combo box
    myQuickLab  = new QLabel(tr("LAB_QUICK_PATH"), this);
    myQuickCombo = new QComboBox(false, this);
    myQuickCombo->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
    myQuickCombo->setMinimumSize(MIN_COMBO_SIZE, 0);

    myQuickButton = new QPushButton(tr("BUT_ADD_PATH"), this);

    connect(myQuickCombo,  SIGNAL(activated(const QString&)), this, SLOT(quickDir(const QString&)));
    connect(myQuickButton, SIGNAL(clicked()),                 this, SLOT(addQuickDir()));
    addWidgets(myQuickLab, myQuickCombo, myQuickButton);

    // getting dir list from settings
    QString dirs;
    SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
    if ( resMgr )
      dirs = resMgr->stringValue( "FileDlg", QString( "QuickDirList" ) );

    QStringList dirList = QStringList::split(';', dirs, false);
    if (dirList.count() > 0) {
      for (unsigned i = 0; i < dirList.count(); i++)
        myQuickCombo->insertItem(dirList[i]);
    }
    else {
      myQuickCombo->insertItem(QDir::homeDirPath());
    }
  }
  setMode( myOpen ? ExistingFile : AnyFile );
  setCaption( myOpen ? tr( "INF_DESK_DOC_OPEN" ) : tr( "INF_DESK_DOC_SAVE" ) );

  // If last visited path doesn't exist -> switch to the first preferred path
  if ( !myLastVisitedPath.isEmpty() ) {
    if ( !processPath( myLastVisitedPath ) && showQuickDir )
      processPath( myQuickCombo->text( 0 ) );
  }
  else {
    if ( showQuickDir )
      processPath(myQuickCombo->text( 0 ) );
  }

  // set default file validator
  myValidator = new SUIT_FileValidator(this);
}

/*!
Destructor
*/
SUIT_FileDlg::~SUIT_FileDlg()
{
  setValidator( 0 );
}

/*!
  Redefined from QFileDialog.
*/
void SUIT_FileDlg::polish()
{
  QFileDialog::polish();
  if ( myQuickButton && myQuickLab ) {
    // the following is a workaround for proper layouting of custom widgets
    QValueList<QPushButton*> buttonList;
    QValueList<QLabel*> labelList;
    const QObjectList *list = children();
    QObjectListIt it(*list);
    int maxButWidth = myQuickLab->sizeHint().width();
    int maxLabWidth = myQuickButton->sizeHint().width();

    for (; it.current() ; ++it) {
      if ( it.current()->isA( "QLabel" ) ) {
	int tempW = ((QLabel*)it.current())->minimumWidth();
	if ( maxLabWidth < tempW ) maxLabWidth = tempW;
	labelList.append( (QLabel*)it.current() );
      }
      else if( it.current()->isA("QPushButton") ) {
	int tempW = ((QPushButton*)it.current())->minimumWidth();
	if ( maxButWidth < tempW ) maxButWidth = tempW;
	buttonList.append( (QPushButton*)it.current() );
      }
    }
    if (maxButWidth > 0) {
      QValueList<QPushButton*>::Iterator bListIt;
      for ( bListIt = buttonList.begin(); bListIt != buttonList.end(); ++bListIt )
	(*bListIt)->setFixedWidth( maxButWidth );
    }
    if (maxLabWidth > 0) {
      QValueList<QLabel*>::Iterator lListIt;
      for ( lListIt = labelList.begin(); lListIt != labelList.end(); ++lListIt )
	(*lListIt)->setFixedWidth( maxLabWidth );
    }
  }
}

/*!
Sets validator for file names to open/save
Deletes previous validator if the dialog owns it.
*/
void SUIT_FileDlg::setValidator( SUIT_FileValidator* v )
{
  if ( myValidator && myValidator->parent() == this )
    delete myValidator;
  myValidator = v;
}

/*!
Returns the selected file
*/
QString SUIT_FileDlg::selectedFile() const
{
  return mySelectedFile;
}

/*!
Returns 'true' if this is 'Open File' dialog
and 'false' if 'Save File' dialog
*/
bool SUIT_FileDlg::isOpenDlg() const
{
  return myOpen;
}

/*!
Closes this dialog and sets the return code to 'Accepted'
if the selected name is valid ( see 'acceptData()' )
*/
void SUIT_FileDlg::accept()
{
//  mySelectedFile = QFileDialog::selectedFile().simplifyWhiteSpace(); //VSR- 06/12/02
  if ( mode() != ExistingFiles ) {
    mySelectedFile = QFileDialog::selectedFile(); //VSR+ 06/12/02
    addExtension();
  }
//  mySelectedFile = mySelectedFile.simplifyWhiteSpace(); //VSR- 06/12/02

  /* Qt 2.2.2 BUG: accept() is called twice if you validate
  the selected file name by pressing 'Return' key in file
  name editor but this name is not acceptable for acceptData()
  */
  if ( acceptData() ) {
    myLastVisitedPath = dirPath();
    QFileDialog::accept();
  }
}

/*!
Closes this dialog and sets the return code to 'Rejected'
*/
void SUIT_FileDlg::reject()
{
  mySelectedFile = QString::null;
  QFileDialog::reject();
}

/*!
Returns 'true' if selected file is valid.
The validity is checked by a file validator,
if there is no validator the file is always
considered as valid
*/
bool SUIT_FileDlg::acceptData()
{
  if ( myValidator )
  {
    if ( isOpenDlg() )
      if ( mode() == ExistingFiles ) {
	QStringList fileNames = selectedFiles();
	for ( int i = 0; i < fileNames.count(); i++ ) {
	  if ( !myValidator->canOpen( fileNames[i] ) )
	    return false;
	}
	return true;
      }
      else {
	return myValidator->canOpen( selectedFile() );
      }
    else
      return myValidator->canSave( selectedFile() );
  }
  return true;
}

/*!
Adds an extension to the selected file name
if the file has not it.
The extension is extracted from the active filter.
*/
void SUIT_FileDlg::addExtension()
{
//  mySelectedFile.stripWhiteSpace();//VSR- 06/12/02
//  if ( mySelectedFile.isEmpty() )//VSR- 06/12/02
  if ( mySelectedFile.stripWhiteSpace().isEmpty() )//VSR+ 06/12/02
    return;

//  if ( SUIT_Tools::getFileExtensionFromPath( mySelectedFile ).isEmpty() ) //VSR- 06/12/02
//ota :   16/12/03  if ( SUIT_Tools::getFileExtensionFromPath( mySelectedFile ).isEmpty() ) //VSR+ 06/12/02
//  {

#if QT_VERSION < 0x030000
    QRegExp r( QString::fromLatin1("([a-zA-Z0-9.*? +;#]*)$") );
    int len, index = r.match( selectedFilter(), 0, &len );
#else
    QRegExp r( QString::fromLatin1("\\([a-zA-Z0-9.*? +;#]*\\)$") );
    int index = r.search(selectedFilter());
#endif
    if ( index >= 0 )
    {
#if QT_VERSION < 0x030000
//      QString wildcard = selectedFilter().mid( index + 1, len-2 ); //VSR- 06/12/02
      QString wildcard = selectedFilter().mid( index + 1, len-2 ).stripWhiteSpace(); //VSR+ 06/12/02
#else
//      QString wildcard = selectedFilter().mid( index + 1, r.matchedLength()-2 ); //VSR- 06/12/02
      QString wildcard = selectedFilter().mid( index + 1, r.matchedLength()-2 ).stripWhiteSpace(); //VSR+ 06/12/02
#endif
    int aLen = mySelectedFile.length();
    if ( mySelectedFile[aLen - 1] == '.')
	  //if the file name ends with the point remove it
	    mySelectedFile.truncate(aLen - 1);
    QString anExt = "." + SUIT_Tools::extension( mySelectedFile ).stripWhiteSpace();
      // From the filters list make a pattern to validate a file extension
      // Due to transformations from the filter list (*.txt *.*xx *.c++ SUIT*.* ) we
      // will have the pattern (\.txt|\..*xx|\.c\+\+|\..*) (as we validate extension only we remove
      // stay extension mask only in the pattern
      QString aPattern(wildcard);
      QRegExp anExtRExp("("+aPattern.replace(QRegExp("(^| )[0-9a-zA-Z*_?]*\\."), " \\.").
			stripWhiteSpace().replace(QRegExp("\\s+"), "|").
			replace(QRegExp("[*]"),".*").replace(QRegExp("[+]"),"\\+") + ")");

      if ( anExtRExp.match(anExt) == -1 ) //if a selected file extension does not match to filter's list
	{ //remove a point if it is at the word end
          int aExtLen = anExt.length();
	  if (anExt[ aExtLen - 1 ] == '.')  anExt.truncate( aExtLen - 1 );
	  index = wildcard.findRev( '.' );
	  if ( index >= 0 )
	    mySelectedFile += wildcard.mid( index ); //add the extension
	}
    }
  //  }
}

/*!
  Processes selection : tries to set given path or filename as selection
*/
bool SUIT_FileDlg::processPath( const QString& path )
{
  if ( !path.isNull() ) {
    QFileInfo fi( path );
    if ( fi.exists() ) {
      if ( fi.isFile() )
	setSelection( path );
      else if ( fi.isDir() )
	setDir( path );
      return true;
    }
    else {
      if ( QFileInfo( fi.dirPath() ).exists() ) {
	setDir( fi.dirPath() );
	setSelection( path );
	return true;
      }
    }
  }
  return false;
}
/*!
  Called when user selects item from "Quick Dir" combo box
*/
void SUIT_FileDlg::quickDir(const QString& dirPath)
{
  if ( !QDir(dirPath).exists() ) {
    SUIT_MessageBox::error1(this,
			   tr("ERR_ERROR"),
			   tr("ERR_DIR_NOT_EXIST").arg(dirPath),
			   tr("BUT_OK"));

  }
  else {
    processPath(dirPath);
  }
}
/*!
  Called when user presses "Add" button - adds current directory to quick directory
  list and to the preferences
*/
void SUIT_FileDlg::addQuickDir()
{
  QString dp = dirPath();
  if ( !dp.isEmpty() ) {
    QDir dir( dp );
    // getting dir list from settings
    QString dirs;
    SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
    if ( resMgr )
      dirs = resMgr->stringValue( "FileDlg", QString( "QuickDirList" ) );
    QStringList dirList = QStringList::split(';', dirs, false);
    bool found = false;
    bool emptyAndHome = false;
    if ( dirList.count() > 0 ) {
      for ( unsigned i = 0; i < dirList.count(); i++ ) {
	QDir aDir( dirList[i] );
	if ( aDir.canonicalPath().isNull() && dirList[i] == dir.absPath() ||
	    !aDir.canonicalPath().isNull() && aDir.exists() && aDir.canonicalPath() == dir.canonicalPath() ) {
	  found = true;
	  break;
	}
      }
    }
    else {
      emptyAndHome = dir.canonicalPath() == QDir(QDir::homeDirPath()).canonicalPath();
    }
    if ( !found ) {
      dirList.append( dp );
      resMgr->setValue( "FileDlg", QString( "QuickDirList" ), dirList.join(";") );
      if ( !emptyAndHome )
	myQuickCombo->insertItem( dp );
    }
  }
}
/*!
  Returns the file name for Open/Save [ static ]
*/
QString SUIT_FileDlg::getFileName( QWidget*            parent,
				   const QString&      initial,
                                   const QStringList&  filters,
                                   const QString&      caption,
                                   bool                open,
				   bool                showQuickDir,
				   SUIT_FileValidator* validator )
{
  SUIT_FileDlg* fd = new SUIT_FileDlg( parent, open, showQuickDir, true );
  if ( !caption.isEmpty() )
    fd->setCaption( caption );
  if ( !initial.isEmpty() ) {
    fd->processPath( initial ); // VSR 24/03/03 check for existing of directory has been added to avoid QFileDialog's bug
  }
  fd->setFilters( filters );
  if ( validator )
    fd->setValidator( validator );
  fd->exec();
  QString filename = fd->selectedFile();
  delete fd;
  qApp->processEvents();
  return filename;
}

/*!
  Returns the list of files to be opened [ static ]
*/
QStringList SUIT_FileDlg::getOpenFileNames( QWidget*            parent,
					    const QString&      initial,
					    const QStringList&  filters,
					    const QString&      caption,
					    bool                showQuickDir,
					    SUIT_FileValidator* validator )
{
  SUIT_FileDlg* fd = new SUIT_FileDlg( parent, true, showQuickDir, true );
  fd->setMode( ExistingFiles );
  if ( !caption.isEmpty() )
    fd->setCaption( caption );
  if ( !initial.isEmpty() ) {
    fd->processPath( initial ); // VSR 24/03/03 check for existing of directory has been added to avoid QFileDialog's bug
  }
  fd->setFilters( filters );
  if ( validator )
    fd->setValidator( validator );
  fd->exec();
  QStringList filenames = fd->selectedFiles();
  delete fd;
  qApp->processEvents();
  return filenames;
}

/*!
  Existing directory selection dialog [ static ]
*/
QString SUIT_FileDlg::getExistingDirectory( QWidget*       parent,
					    const QString& initial,
					    const QString& caption,
					    bool           showQuickDir )
{
  SUIT_FileDlg* fd = new SUIT_FileDlg( parent, true, showQuickDir, true);
  if ( !caption.isEmpty() )
    fd->setCaption( caption );
  if ( !initial.isEmpty() ) {
    fd->processPath( initial ); // VSR 24/03/03 check for existing of directory has been added to avoid QFileDialog's bug
  }
  fd->setMode( DirectoryOnly );
  fd->setFilters(tr("INF_DIRECTORIES_FILTER"));
  fd->exec();
  QString dirname = fd->selectedFile();
  delete fd;
  qApp->processEvents();
  return dirname;

}
<commit_msg>BUG 8990: Use HOME directory if selected one doesn't exist<commit_after>#include "SUIT_FileDlg.h"

#include "SUIT_Tools.h"
#include "SUIT_Session.h"
#include "SUIT_Desktop.h"
#include "SUIT_MessageBox.h"
#include "SUIT_ResourceMgr.h"
#include "SUIT_FileValidator.h"

#include <qdir.h>
#include <qlabel.h>
#include <qregexp.h>
#include <qpalette.h>
#include <qobjectlist.h>
#include <qcombobox.h>
#include <qpushbutton.h>
#include <qapplication.h>

#define MIN_COMBO_SIZE 100

QString SUIT_FileDlg::myLastVisitedPath;

/*!
Constructor
*/
SUIT_FileDlg::SUIT_FileDlg( QWidget* parent, bool open, bool showQuickDir, bool modal ) :
QFileDialog( parent, 0, modal ),
myValidator( 0 ),
myQuickCombo( 0 ), myQuickButton( 0 ), myQuickLab( 0 ),
myOpen( open )
{
  if ( parent->icon() )
    setIcon( *parent->icon() );
  setSizeGripEnabled( true );

  if ( showQuickDir ) {
    // inserting quick dir combo box
    myQuickLab  = new QLabel(tr("LAB_QUICK_PATH"), this);
    myQuickCombo = new QComboBox(false, this);
    myQuickCombo->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
    myQuickCombo->setMinimumSize(MIN_COMBO_SIZE, 0);

    myQuickButton = new QPushButton(tr("BUT_ADD_PATH"), this);

    connect(myQuickCombo,  SIGNAL(activated(const QString&)), this, SLOT(quickDir(const QString&)));
    connect(myQuickButton, SIGNAL(clicked()),                 this, SLOT(addQuickDir()));
    addWidgets(myQuickLab, myQuickCombo, myQuickButton);

    // getting dir list from settings
    QString dirs;
    SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
    if ( resMgr )
      dirs = resMgr->stringValue( "FileDlg", QString( "QuickDirList" ) );

    QStringList dirList = QStringList::split(';', dirs, false);
    if (dirList.count() > 0) {
      for (unsigned i = 0; i < dirList.count(); i++)
        myQuickCombo->insertItem(dirList[i]);
    }
    else {
      myQuickCombo->insertItem(QDir::homeDirPath());
    }
  }
  setMode( myOpen ? ExistingFile : AnyFile );
  setCaption( myOpen ? tr( "INF_DESK_DOC_OPEN" ) : tr( "INF_DESK_DOC_SAVE" ) );

  // If last visited path doesn't exist -> switch to the first preferred path
  if ( !myLastVisitedPath.isEmpty() ) {
    if ( !processPath( myLastVisitedPath ) && showQuickDir )
      processPath( myQuickCombo->text( 0 ) );
  }
  else {
    if ( showQuickDir )
      processPath(myQuickCombo->text( 0 ) );
  }

  // set default file validator
  myValidator = new SUIT_FileValidator(this);
}

/*!
Destructor
*/
SUIT_FileDlg::~SUIT_FileDlg()
{
  setValidator( 0 );
}

/*!
  Redefined from QFileDialog.
*/
void SUIT_FileDlg::polish()
{
  QFileDialog::polish();
  if ( myQuickButton && myQuickLab ) {
    // the following is a workaround for proper layouting of custom widgets
    QValueList<QPushButton*> buttonList;
    QValueList<QLabel*> labelList;
    const QObjectList *list = children();
    QObjectListIt it(*list);
    int maxButWidth = myQuickLab->sizeHint().width();
    int maxLabWidth = myQuickButton->sizeHint().width();

    for (; it.current() ; ++it) {
      if ( it.current()->isA( "QLabel" ) ) {
	int tempW = ((QLabel*)it.current())->minimumWidth();
	if ( maxLabWidth < tempW ) maxLabWidth = tempW;
	labelList.append( (QLabel*)it.current() );
      }
      else if( it.current()->isA("QPushButton") ) {
	int tempW = ((QPushButton*)it.current())->minimumWidth();
	if ( maxButWidth < tempW ) maxButWidth = tempW;
	buttonList.append( (QPushButton*)it.current() );
      }
    }
    if (maxButWidth > 0) {
      QValueList<QPushButton*>::Iterator bListIt;
      for ( bListIt = buttonList.begin(); bListIt != buttonList.end(); ++bListIt )
	(*bListIt)->setFixedWidth( maxButWidth );
    }
    if (maxLabWidth > 0) {
      QValueList<QLabel*>::Iterator lListIt;
      for ( lListIt = labelList.begin(); lListIt != labelList.end(); ++lListIt )
	(*lListIt)->setFixedWidth( maxLabWidth );
    }
  }
}

/*!
Sets validator for file names to open/save
Deletes previous validator if the dialog owns it.
*/
void SUIT_FileDlg::setValidator( SUIT_FileValidator* v )
{
  if ( myValidator && myValidator->parent() == this )
    delete myValidator;
  myValidator = v;
}

/*!
Returns the selected file
*/
QString SUIT_FileDlg::selectedFile() const
{
  return mySelectedFile;
}

/*!
Returns 'true' if this is 'Open File' dialog
and 'false' if 'Save File' dialog
*/
bool SUIT_FileDlg::isOpenDlg() const
{
  return myOpen;
}

/*!
Closes this dialog and sets the return code to 'Accepted'
if the selected name is valid ( see 'acceptData()' )
*/
void SUIT_FileDlg::accept()
{
//  mySelectedFile = QFileDialog::selectedFile().simplifyWhiteSpace(); //VSR- 06/12/02
  if ( mode() != ExistingFiles ) {
    mySelectedFile = QFileDialog::selectedFile(); //VSR+ 06/12/02
    addExtension();
  }
//  mySelectedFile = mySelectedFile.simplifyWhiteSpace(); //VSR- 06/12/02

  /* Qt 2.2.2 BUG: accept() is called twice if you validate
  the selected file name by pressing 'Return' key in file
  name editor but this name is not acceptable for acceptData()
  */
  if ( acceptData() ) {
    myLastVisitedPath = dirPath();
    QFileDialog::accept();
  }
}

/*!
Closes this dialog and sets the return code to 'Rejected'
*/
void SUIT_FileDlg::reject()
{
  mySelectedFile = QString::null;
  QFileDialog::reject();
}

/*!
Returns 'true' if selected file is valid.
The validity is checked by a file validator,
if there is no validator the file is always
considered as valid
*/
bool SUIT_FileDlg::acceptData()
{
  if ( myValidator )
  {
    if ( isOpenDlg() )
      if ( mode() == ExistingFiles ) {
	QStringList fileNames = selectedFiles();
	for ( int i = 0; i < fileNames.count(); i++ ) {
	  if ( !myValidator->canOpen( fileNames[i] ) )
	    return false;
	}
	return true;
      }
      else {
	return myValidator->canOpen( selectedFile() );
      }
    else
      return myValidator->canSave( selectedFile() );
  }
  return true;
}

/*!
Adds an extension to the selected file name
if the file has not it.
The extension is extracted from the active filter.
*/
void SUIT_FileDlg::addExtension()
{
//  mySelectedFile.stripWhiteSpace();//VSR- 06/12/02
//  if ( mySelectedFile.isEmpty() )//VSR- 06/12/02
  if ( mySelectedFile.stripWhiteSpace().isEmpty() )//VSR+ 06/12/02
    return;

//  if ( SUIT_Tools::getFileExtensionFromPath( mySelectedFile ).isEmpty() ) //VSR- 06/12/02
//ota :   16/12/03  if ( SUIT_Tools::getFileExtensionFromPath( mySelectedFile ).isEmpty() ) //VSR+ 06/12/02
//  {

#if QT_VERSION < 0x030000
    QRegExp r( QString::fromLatin1("([a-zA-Z0-9.*? +;#]*)$") );
    int len, index = r.match( selectedFilter(), 0, &len );
#else
    QRegExp r( QString::fromLatin1("\\([a-zA-Z0-9.*? +;#]*\\)$") );
    int index = r.search(selectedFilter());
#endif
    if ( index >= 0 )
    {
#if QT_VERSION < 0x030000
//      QString wildcard = selectedFilter().mid( index + 1, len-2 ); //VSR- 06/12/02
      QString wildcard = selectedFilter().mid( index + 1, len-2 ).stripWhiteSpace(); //VSR+ 06/12/02
#else
//      QString wildcard = selectedFilter().mid( index + 1, r.matchedLength()-2 ); //VSR- 06/12/02
      QString wildcard = selectedFilter().mid( index + 1, r.matchedLength()-2 ).stripWhiteSpace(); //VSR+ 06/12/02
#endif
    int aLen = mySelectedFile.length();
    if ( mySelectedFile[aLen - 1] == '.')
	  //if the file name ends with the point remove it
	    mySelectedFile.truncate(aLen - 1);
    QString anExt = "." + SUIT_Tools::extension( mySelectedFile ).stripWhiteSpace();
      // From the filters list make a pattern to validate a file extension
      // Due to transformations from the filter list (*.txt *.*xx *.c++ SUIT*.* ) we
      // will have the pattern (\.txt|\..*xx|\.c\+\+|\..*) (as we validate extension only we remove
      // stay extension mask only in the pattern
      QString aPattern(wildcard);
      QRegExp anExtRExp("("+aPattern.replace(QRegExp("(^| )[0-9a-zA-Z*_?]*\\."), " \\.").
			stripWhiteSpace().replace(QRegExp("\\s+"), "|").
			replace(QRegExp("[*]"),".*").replace(QRegExp("[+]"),"\\+") + ")");

      if ( anExtRExp.match(anExt) == -1 ) //if a selected file extension does not match to filter's list
	{ //remove a point if it is at the word end
          int aExtLen = anExt.length();
	  if (anExt[ aExtLen - 1 ] == '.')  anExt.truncate( aExtLen - 1 );
	  index = wildcard.findRev( '.' );
	  if ( index >= 0 )
	    mySelectedFile += wildcard.mid( index ); //add the extension
	}
    }
  //  }
}

/*!
  Processes selection : tries to set given path or filename as selection
*/
bool SUIT_FileDlg::processPath( const QString& path )
{
  if ( !path.isNull() ) {
    QFileInfo fi( path );
    if ( fi.exists() ) {
      if ( fi.isFile() )
	setSelection( path );
      else if ( fi.isDir() )
	setDir( path );
      return true;
    }
    else {
      if ( QFileInfo( fi.dirPath() ).exists() ) {
	setDir( fi.dirPath() );
	setSelection( path );
	return true;
      }
    }
  }
  return false;
}
/*!
  Called when user selects item from "Quick Dir" combo box
*/
void SUIT_FileDlg::quickDir(const QString& dirPath)
{
  QString aPath = dirPath;
  if ( !QDir(aPath).exists() ) {
    aPath = QDir::homeDirPath();
    /*    SUIT_MessageBox::error1(this,
			   tr("ERR_ERROR"),
			   tr("ERR_DIR_NOT_EXIST").arg(dirPath),
			   tr("BUT_OK"));*/

  }
  //  else {
  processPath(aPath);
    //  }
}
/*!
  Called when user presses "Add" button - adds current directory to quick directory
  list and to the preferences
*/
void SUIT_FileDlg::addQuickDir()
{
  QString dp = dirPath();
  if ( !dp.isEmpty() ) {
    QDir dir( dp );
    // getting dir list from settings
    QString dirs;
    SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
    if ( resMgr )
      dirs = resMgr->stringValue( "FileDlg", QString( "QuickDirList" ) );
    QStringList dirList = QStringList::split(';', dirs, false);
    bool found = false;
    bool emptyAndHome = false;
    if ( dirList.count() > 0 ) {
      for ( unsigned i = 0; i < dirList.count(); i++ ) {
	QDir aDir( dirList[i] );
	if ( aDir.canonicalPath().isNull() && dirList[i] == dir.absPath() ||
	    !aDir.canonicalPath().isNull() && aDir.exists() && aDir.canonicalPath() == dir.canonicalPath() ) {
	  found = true;
	  break;
	}
      }
    }
    else {
      emptyAndHome = dir.canonicalPath() == QDir(QDir::homeDirPath()).canonicalPath();
    }
    if ( !found ) {
      dirList.append( dp );
      resMgr->setValue( "FileDlg", QString( "QuickDirList" ), dirList.join(";") );
      if ( !emptyAndHome )
	myQuickCombo->insertItem( dp );
    }
  }
}
/*!
  Returns the file name for Open/Save [ static ]
*/
QString SUIT_FileDlg::getFileName( QWidget*            parent,
				   const QString&      initial,
                                   const QStringList&  filters,
                                   const QString&      caption,
                                   bool                open,
				   bool                showQuickDir,
				   SUIT_FileValidator* validator )
{
  SUIT_FileDlg* fd = new SUIT_FileDlg( parent, open, showQuickDir, true );
  if ( !caption.isEmpty() )
    fd->setCaption( caption );
  if ( !initial.isEmpty() ) {
    fd->processPath( initial ); // VSR 24/03/03 check for existing of directory has been added to avoid QFileDialog's bug
  }
  fd->setFilters( filters );
  if ( validator )
    fd->setValidator( validator );
  fd->exec();
  QString filename = fd->selectedFile();
  delete fd;
  qApp->processEvents();
  return filename;
}

/*!
  Returns the list of files to be opened [ static ]
*/
QStringList SUIT_FileDlg::getOpenFileNames( QWidget*            parent,
					    const QString&      initial,
					    const QStringList&  filters,
					    const QString&      caption,
					    bool                showQuickDir,
					    SUIT_FileValidator* validator )
{
  SUIT_FileDlg* fd = new SUIT_FileDlg( parent, true, showQuickDir, true );
  fd->setMode( ExistingFiles );
  if ( !caption.isEmpty() )
    fd->setCaption( caption );
  if ( !initial.isEmpty() ) {
    fd->processPath( initial ); // VSR 24/03/03 check for existing of directory has been added to avoid QFileDialog's bug
  }
  fd->setFilters( filters );
  if ( validator )
    fd->setValidator( validator );
  fd->exec();
  QStringList filenames = fd->selectedFiles();
  delete fd;
  qApp->processEvents();
  return filenames;
}

/*!
  Existing directory selection dialog [ static ]
*/
QString SUIT_FileDlg::getExistingDirectory( QWidget*       parent,
					    const QString& initial,
					    const QString& caption,
					    bool           showQuickDir )
{
  SUIT_FileDlg* fd = new SUIT_FileDlg( parent, true, showQuickDir, true);
  if ( !caption.isEmpty() )
    fd->setCaption( caption );
  if ( !initial.isEmpty() ) {
    fd->processPath( initial ); // VSR 24/03/03 check for existing of directory has been added to avoid QFileDialog's bug
  }
  fd->setMode( DirectoryOnly );
  fd->setFilters(tr("INF_DIRECTORIES_FILTER"));
  fd->exec();
  QString dirname = fd->selectedFile();
  delete fd;
  qApp->processEvents();
  return dirname;

}
<|endoftext|>


<commit_before>#include "Scene/SceneLoader.hpp"

#include "rapidjson/document.h"

#include "Engine/Engine.hpp"
#include "Rendering/Renderer.hpp"
#include "Resources/MeshManager.hpp"
#include "Entity/EntityManager.hpp"
#include "Resources/ResourceManager.hpp"
#include "Resources/MaterialManager.hpp"
#include "Resources/ValueSerialization.hpp"

SceneLoader::SceneLoader(Engine* engine, Scene* scene):
	scene(scene),
	renderer(engine->GetRenderer()),
	meshManager(engine->GetMeshManager()),
	materialManager(engine->GetMaterialManager()),
	entityManager(engine->GetEntityManager()),
	resourceManager(engine->GetResourceManager())
{
}

void SceneLoader::Load(BufferRef<char> sceneConfig)
{
	rapidjson::Document doc;
	doc.Parse(sceneConfig.data, sceneConfig.count);

	if (doc.IsObject() == false)
		return;

	MemberItr objectsItr = doc.FindMember("objects");
	if (objectsItr != doc.MemberEnd() && objectsItr->value.IsArray())
	{
		ValueItr itr = objectsItr->value.Begin();
		ValueItr end = objectsItr->value.End();

		CreateObjects(itr, end);
	}

	MemberItr colorItr = doc.FindMember("background-color");
	if (colorItr != doc.MemberEnd())
	{
		scene->backgroundColor = ValueSerialization::Deserialize_Color(colorItr->value);
	}

	MemberItr skyboxItr = doc.FindMember("skybox-material");
	if (skyboxItr != doc.MemberEnd() && skyboxItr->value.IsString())
	{
		StringRef materialPath(skyboxItr->value.GetString(), skyboxItr->value.GetStringLength());

		MaterialId matId = materialManager->GetIdByPath(materialPath);
		if (matId.IsNull() == false)
		{
			scene->SetSkyboxMaterial(matId);
		}
	}
}

void SceneLoader::CreateObjects(ValueItr itr, ValueItr end)
{
	for (; itr != end; ++itr)
	{
		if (itr->IsObject())
		{
			Entity entity = entityManager->Create();
			SceneObjectId sceneObj = scene->AddSceneObject(entity);
			CreateSceneObject(itr, sceneObj);
			CreateRenderObject(itr, entity);
		}
	}
}

void SceneLoader::CreateChildObjects(ValueItr itr, ValueItr end, SceneObjectId parent)
{
	for (; itr != end; ++itr)
	{
		if (itr->IsObject())
		{
			Entity entity = entityManager->Create();
			SceneObjectId sceneObj = scene->AddSceneObject(entity);
			scene->SetParent(sceneObj, parent);
			CreateSceneObject(itr, sceneObj);
			CreateRenderObject(itr, entity);
		}
	}
}

void SceneLoader::CreateSceneObject(ValueItr itr, SceneObjectId sceneObject)
{
	Mat4x4f transform;

	rapidjson::Value::ConstMemberIterator rotItr = itr->FindMember("rotation");
	if (rotItr != itr->MemberEnd())
	{
		Vec3f rot = ValueSerialization::Deserialize_Vec3f(rotItr->value);
		transform = Mat4x4f::RotateEuler(Math::DegreesToRadians(rot)) * transform;
	}

	rapidjson::Value::ConstMemberIterator positionItr = itr->FindMember("position");
	if (positionItr != itr->MemberEnd())
	{
		Vec3f pos = ValueSerialization::Deserialize_Vec3f(positionItr->value);
		transform = Mat4x4f::Translate(pos) * transform;
	}

	scene->SetLocalTransform(sceneObject, transform);

	rapidjson::Value::ConstMemberIterator childrenItr = itr->FindMember("children");
	if (childrenItr != itr->MemberEnd() && childrenItr->value.IsArray())
	{
		rapidjson::Value::ConstValueIterator itr = childrenItr->value.Begin();
		rapidjson::Value::ConstValueIterator end = childrenItr->value.End();

		CreateChildObjects(itr, end, sceneObject);
	}
}

void SceneLoader::CreateRenderObject(ValueItr itr, Entity entity)
{
	MemberItr meshItr = itr->FindMember("mesh");
	MemberItr materialItr = itr->FindMember("material");

	if (meshItr != itr->MemberEnd() && meshItr->value.IsString() &&
		materialItr != itr->MemberEnd() && materialItr->value.IsString())
	{
		RenderObjectId renderObj = renderer->AddRenderObject(entity);

		StringRef meshPath(meshItr->value.GetString(), meshItr->value.GetStringLength());
		MeshId meshId = meshManager->GetIdByPath(meshPath);
		if (meshId.IsValid())
			renderer->SetMeshId(renderObj, meshId);

		StringRef matPath(materialItr->value.GetString(), materialItr->value.GetStringLength());
		MaterialId matId = materialManager->GetIdByPath(matPath);

		if (matId.IsNull() == false)
		{
			RenderOrderData data;
			data.material = matId;
			data.transparency = materialManager->GetTransparency(matId);

			renderer->SetOrderData(renderObj, data);
		}
	}
}
<commit_msg>Add an assertion so scene objects load valid materials<commit_after>#include "Scene/SceneLoader.hpp"

#include "rapidjson/document.h"

#include "Engine/Engine.hpp"
#include "Rendering/Renderer.hpp"
#include "Resources/MeshManager.hpp"
#include "Entity/EntityManager.hpp"
#include "Resources/ResourceManager.hpp"
#include "Resources/MaterialManager.hpp"
#include "Resources/ValueSerialization.hpp"

SceneLoader::SceneLoader(Engine* engine, Scene* scene):
	scene(scene),
	renderer(engine->GetRenderer()),
	meshManager(engine->GetMeshManager()),
	materialManager(engine->GetMaterialManager()),
	entityManager(engine->GetEntityManager()),
	resourceManager(engine->GetResourceManager())
{
}

void SceneLoader::Load(BufferRef<char> sceneConfig)
{
	rapidjson::Document doc;
	doc.Parse(sceneConfig.data, sceneConfig.count);

	if (doc.IsObject() == false)
		return;

	MemberItr objectsItr = doc.FindMember("objects");
	if (objectsItr != doc.MemberEnd() && objectsItr->value.IsArray())
	{
		ValueItr itr = objectsItr->value.Begin();
		ValueItr end = objectsItr->value.End();

		CreateObjects(itr, end);
	}

	MemberItr colorItr = doc.FindMember("background-color");
	if (colorItr != doc.MemberEnd())
	{
		scene->backgroundColor = ValueSerialization::Deserialize_Color(colorItr->value);
	}

	MemberItr skyboxItr = doc.FindMember("skybox-material");
	if (skyboxItr != doc.MemberEnd() && skyboxItr->value.IsString())
	{
		StringRef materialPath(skyboxItr->value.GetString(), skyboxItr->value.GetStringLength());

		MaterialId matId = materialManager->GetIdByPath(materialPath);
		if (matId.IsNull() == false)
		{
			scene->SetSkyboxMaterial(matId);
		}
	}
}

void SceneLoader::CreateObjects(ValueItr itr, ValueItr end)
{
	for (; itr != end; ++itr)
	{
		if (itr->IsObject())
		{
			Entity entity = entityManager->Create();
			SceneObjectId sceneObj = scene->AddSceneObject(entity);
			CreateSceneObject(itr, sceneObj);
			CreateRenderObject(itr, entity);
		}
	}
}

void SceneLoader::CreateChildObjects(ValueItr itr, ValueItr end, SceneObjectId parent)
{
	for (; itr != end; ++itr)
	{
		if (itr->IsObject())
		{
			Entity entity = entityManager->Create();
			SceneObjectId sceneObj = scene->AddSceneObject(entity);
			scene->SetParent(sceneObj, parent);
			CreateSceneObject(itr, sceneObj);
			CreateRenderObject(itr, entity);
		}
	}
}

void SceneLoader::CreateSceneObject(ValueItr itr, SceneObjectId sceneObject)
{
	Mat4x4f transform;

	rapidjson::Value::ConstMemberIterator rotItr = itr->FindMember("rotation");
	if (rotItr != itr->MemberEnd())
	{
		Vec3f rot = ValueSerialization::Deserialize_Vec3f(rotItr->value);
		transform = Mat4x4f::RotateEuler(Math::DegreesToRadians(rot)) * transform;
	}

	rapidjson::Value::ConstMemberIterator positionItr = itr->FindMember("position");
	if (positionItr != itr->MemberEnd())
	{
		Vec3f pos = ValueSerialization::Deserialize_Vec3f(positionItr->value);
		transform = Mat4x4f::Translate(pos) * transform;
	}

	scene->SetLocalTransform(sceneObject, transform);

	rapidjson::Value::ConstMemberIterator childrenItr = itr->FindMember("children");
	if (childrenItr != itr->MemberEnd() && childrenItr->value.IsArray())
	{
		rapidjson::Value::ConstValueIterator itr = childrenItr->value.Begin();
		rapidjson::Value::ConstValueIterator end = childrenItr->value.End();

		CreateChildObjects(itr, end, sceneObject);
	}
}

void SceneLoader::CreateRenderObject(ValueItr itr, Entity entity)
{
	MemberItr meshItr = itr->FindMember("mesh");
	MemberItr materialItr = itr->FindMember("material");

	if (meshItr != itr->MemberEnd() && meshItr->value.IsString() &&
		materialItr != itr->MemberEnd() && materialItr->value.IsString())
	{
		RenderObjectId renderObj = renderer->AddRenderObject(entity);

		StringRef meshPath(meshItr->value.GetString(), meshItr->value.GetStringLength());
		MeshId meshId = meshManager->GetIdByPath(meshPath);
		if (meshId.IsValid())
			renderer->SetMeshId(renderObj, meshId);

		StringRef matPath(materialItr->value.GetString(), materialItr->value.GetStringLength());
		MaterialId matId = materialManager->GetIdByPath(matPath);

		assert(matId.IsNull() == false);

		RenderOrderData data;
		data.material = matId;
		data.transparency = materialManager->GetTransparency(matId);

		renderer->SetOrderData(renderObj, data);
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/compression/Zstd.h>

#if FOLLY_HAVE_LIBZSTD

#include <stdexcept>
#include <string>

#include <zstd.h>

#include <folly/Conv.h>
#include <folly/Range.h>
#include <folly/ScopeGuard.h>
#include <folly/compression/CompressionContextPoolSingletons.h>
#include <folly/compression/Utils.h>

static_assert(
    ZSTD_VERSION_NUMBER >= 10302,
    "zstd-1.3.2 is the minimum supported zstd version.");

using folly::io::compression::detail::dataStartsWithLE;
using folly::io::compression::detail::prefixToStringLE;

using namespace folly::compression::contexts;

namespace folly {
namespace io {
namespace zstd {
namespace {

// Compatibility helpers for zstd versions < 1.4.0
#if ZSTD_VERSION_NUMBER < 10400

#define ZSTD_CCtxParams_setParameter ZSTD_CCtxParam_setParameter

#endif

// Compatibility helpers for zstd versions < 1.3.8.
#if ZSTD_VERSION_NUMBER < 10308

#define ZSTD_compressStream2 ZSTD_compress_generic
#define ZSTD_c_compressionLevel ZSTD_p_compressionLevel
#define ZSTD_c_contentSizeFlag ZSTD_p_contentSizeFlag

void resetCCtxSessionAndParameters(ZSTD_CCtx* cctx) {
  ZSTD_CCtx_reset(cctx);
}

void resetDCtxSessionAndParameters(ZSTD_DCtx* dctx) {
  ZSTD_DCtx_reset(dctx);
}

#else

void resetCCtxSessionAndParameters(ZSTD_CCtx* cctx) {
  ZSTD_CCtx_reset(cctx, ZSTD_reset_session_and_parameters);
}

void resetDCtxSessionAndParameters(ZSTD_DCtx* dctx) {
  ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters);
}

#endif

size_t zstdThrowIfError(size_t rc) {
  if (!ZSTD_isError(rc)) {
    return rc;
  }
  throw std::runtime_error(
      to<std::string>("ZSTD returned an error: ", ZSTD_getErrorName(rc)));
}

ZSTD_EndDirective zstdTranslateFlush(StreamCodec::FlushOp flush) {
  switch (flush) {
    case StreamCodec::FlushOp::NONE:
      return ZSTD_e_continue;
    case StreamCodec::FlushOp::FLUSH:
      return ZSTD_e_flush;
    case StreamCodec::FlushOp::END:
      return ZSTD_e_end;
    default:
      throw std::invalid_argument("ZSTDStreamCodec: Invalid flush");
  }
}

class ZSTDStreamCodec final : public StreamCodec {
 public:
  explicit ZSTDStreamCodec(Options options);

  std::vector<std::string> validPrefixes() const override;
  bool canUncompress(
      const IOBuf* data, Optional<uint64_t> uncompressedLength) const override;

 private:
  bool doNeedsUncompressedLength() const override;
  uint64_t doMaxCompressedLength(uint64_t uncompressedLength) const override;
  Optional<uint64_t> doGetUncompressedLength(
      IOBuf const* data, Optional<uint64_t> uncompressedLength) const override;

  void doResetStream() override;
  bool doCompressStream(
      ByteRange& input,
      MutableByteRange& output,
      StreamCodec::FlushOp flushOp) override;
  bool doUncompressStream(
      ByteRange& input,
      MutableByteRange& output,
      StreamCodec::FlushOp flushOp) override;

  void resetCCtx();
  void resetDCtx();

  Options options_;
  bool needReset_{true};
  ZSTD_CCtx_Pool::Ref cctx_{getNULL_ZSTD_CCtx()};
  ZSTD_DCtx_Pool::Ref dctx_{getNULL_ZSTD_DCtx()};
};

constexpr uint32_t kZSTDMagicLE = 0xFD2FB528;

std::vector<std::string> ZSTDStreamCodec::validPrefixes() const {
  return {prefixToStringLE(kZSTDMagicLE)};
}

bool ZSTDStreamCodec::canUncompress(
    const IOBuf* data, Optional<uint64_t>) const {
  return dataStartsWithLE(data, kZSTDMagicLE);
}

CodecType codecType(Options const& options) {
  int const level = options.level();
  DCHECK_NE(level, 0);
  return level > 0 ? CodecType::ZSTD : CodecType::ZSTD_FAST;
}

ZSTDStreamCodec::ZSTDStreamCodec(Options options)
    : StreamCodec(codecType(options), options.level()),
      options_(std::move(options)) {}

bool ZSTDStreamCodec::doNeedsUncompressedLength() const {
  return false;
}

uint64_t ZSTDStreamCodec::doMaxCompressedLength(
    uint64_t uncompressedLength) const {
  return ZSTD_compressBound(uncompressedLength);
}

Optional<uint64_t> ZSTDStreamCodec::doGetUncompressedLength(
    IOBuf const* data, Optional<uint64_t> uncompressedLength) const {
  // Read decompressed size from frame if available in first IOBuf.
  auto const decompressedSize =
      ZSTD_getFrameContentSize(data->data(), data->length());
  if (decompressedSize == ZSTD_CONTENTSIZE_UNKNOWN ||
      decompressedSize == ZSTD_CONTENTSIZE_ERROR) {
    return uncompressedLength;
  }
  if (uncompressedLength && *uncompressedLength != decompressedSize) {
    throw std::runtime_error("ZSTD: invalid uncompressed length");
  }
  return decompressedSize;
}

void ZSTDStreamCodec::doResetStream() {
  needReset_ = true;
}

void ZSTDStreamCodec::resetCCtx() {
  if (!cctx_) {
    cctx_ = getZSTD_CCtx();
    if (!cctx_) {
      throw std::bad_alloc{};
    }
  }
  resetCCtxSessionAndParameters(cctx_.get());
  zstdThrowIfError(
      ZSTD_CCtx_setParametersUsingCCtxParams(cctx_.get(), options_.params()));
  zstdThrowIfError(ZSTD_CCtx_setPledgedSrcSize(
      cctx_.get(), uncompressedLength().value_or(ZSTD_CONTENTSIZE_UNKNOWN)));
}

bool ZSTDStreamCodec::doCompressStream(
    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp flushOp) {
  if (needReset_) {
    resetCCtx();
    needReset_ = false;
  }
  ZSTD_inBuffer in = {input.data(), input.size(), 0};
  ZSTD_outBuffer out = {output.data(), output.size(), 0};
  SCOPE_EXIT {
    input.uncheckedAdvance(in.pos);
    output.uncheckedAdvance(out.pos);
  };
  size_t const rc = zstdThrowIfError(ZSTD_compressStream2(
      cctx_.get(), &out, &in, zstdTranslateFlush(flushOp)));
  switch (flushOp) {
    case StreamCodec::FlushOp::NONE:
      return false;
    case StreamCodec::FlushOp::FLUSH:
    case StreamCodec::FlushOp::END:
      return rc == 0;
    default:
      throw std::invalid_argument("ZSTD: invalid FlushOp");
  }
}

void ZSTDStreamCodec::resetDCtx() {
  if (!dctx_) {
    dctx_ = getZSTD_DCtx();
    if (!dctx_) {
      throw std::bad_alloc{};
    }
  }
  resetDCtxSessionAndParameters(dctx_.get());
  if (options_.maxWindowSize() != 0) {
    zstdThrowIfError(
        ZSTD_DCtx_setMaxWindowSize(dctx_.get(), options_.maxWindowSize()));
  }
}

bool ZSTDStreamCodec::doUncompressStream(
    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp) {
  if (needReset_) {
    resetDCtx();
    needReset_ = false;
  }
  ZSTD_inBuffer in = {input.data(), input.size(), 0};
  ZSTD_outBuffer out = {output.data(), output.size(), 0};
  SCOPE_EXIT {
    input.uncheckedAdvance(in.pos);
    output.uncheckedAdvance(out.pos);
  };
  size_t const rc =
      zstdThrowIfError(ZSTD_decompressStream(dctx_.get(), &out, &in));
  return rc == 0;
}

} // namespace

Options::Options(int level) : params_(ZSTD_createCCtxParams()), level_(level) {
  if (params_ == nullptr) {
    throw std::bad_alloc{};
  }
#if ZSTD_VERSION_NUMBER >= 10304
  zstdThrowIfError(ZSTD_CCtxParams_init(params_.get(), level));
#else
  zstdThrowIfError(ZSTD_initCCtxParams(params_.get(), level));
  set(ZSTD_c_contentSizeFlag, 1);
#endif
  // zstd-1.3.4 is buggy and only disables Huffman decompression for negative
  // compression levels if this call is present. This call is begign in other
  // versions.
  set(ZSTD_c_compressionLevel, level);
}

void Options::set(ZSTD_cParameter param, unsigned value) {
  zstdThrowIfError(ZSTD_CCtxParams_setParameter(params_.get(), param, value));
  if (param == ZSTD_c_compressionLevel) {
    level_ = static_cast<int>(value);
  }
}

/* static */ void Options::freeCCtxParams(ZSTD_CCtx_params* params) {
  ZSTD_freeCCtxParams(params);
}

std::unique_ptr<Codec> getCodec(Options options) {
  return std::make_unique<ZSTDStreamCodec>(std::move(options));
}

std::unique_ptr<StreamCodec> getStreamCodec(Options options) {
  return std::make_unique<ZSTDStreamCodec>(std::move(options));
}

} // namespace zstd
} // namespace io
} // namespace folly

#endif
<commit_msg>Surrender the ZSTD_{C,D}Ctx on (de)compression end<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/compression/Zstd.h>

#if FOLLY_HAVE_LIBZSTD

#include <stdexcept>
#include <string>

#include <zstd.h>

#include <folly/Conv.h>
#include <folly/Range.h>
#include <folly/ScopeGuard.h>
#include <folly/compression/CompressionContextPoolSingletons.h>
#include <folly/compression/Utils.h>

static_assert(
    ZSTD_VERSION_NUMBER >= 10302,
    "zstd-1.3.2 is the minimum supported zstd version.");

using folly::io::compression::detail::dataStartsWithLE;
using folly::io::compression::detail::prefixToStringLE;

using namespace folly::compression::contexts;

namespace folly {
namespace io {
namespace zstd {
namespace {

// Compatibility helpers for zstd versions < 1.4.0
#if ZSTD_VERSION_NUMBER < 10400

#define ZSTD_CCtxParams_setParameter ZSTD_CCtxParam_setParameter

#endif

// Compatibility helpers for zstd versions < 1.3.8.
#if ZSTD_VERSION_NUMBER < 10308

#define ZSTD_compressStream2 ZSTD_compress_generic
#define ZSTD_c_compressionLevel ZSTD_p_compressionLevel
#define ZSTD_c_contentSizeFlag ZSTD_p_contentSizeFlag

void resetCCtxSessionAndParameters(ZSTD_CCtx* cctx) {
  ZSTD_CCtx_reset(cctx);
}

void resetDCtxSessionAndParameters(ZSTD_DCtx* dctx) {
  ZSTD_DCtx_reset(dctx);
}

#else

void resetCCtxSessionAndParameters(ZSTD_CCtx* cctx) {
  ZSTD_CCtx_reset(cctx, ZSTD_reset_session_and_parameters);
}

void resetDCtxSessionAndParameters(ZSTD_DCtx* dctx) {
  ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters);
}

#endif

size_t zstdThrowIfError(size_t rc) {
  if (!ZSTD_isError(rc)) {
    return rc;
  }
  throw std::runtime_error(
      to<std::string>("ZSTD returned an error: ", ZSTD_getErrorName(rc)));
}

ZSTD_EndDirective zstdTranslateFlush(StreamCodec::FlushOp flush) {
  switch (flush) {
    case StreamCodec::FlushOp::NONE:
      return ZSTD_e_continue;
    case StreamCodec::FlushOp::FLUSH:
      return ZSTD_e_flush;
    case StreamCodec::FlushOp::END:
      return ZSTD_e_end;
    default:
      throw std::invalid_argument("ZSTDStreamCodec: Invalid flush");
  }
}

class ZSTDStreamCodec final : public StreamCodec {
 public:
  explicit ZSTDStreamCodec(Options options);

  std::vector<std::string> validPrefixes() const override;
  bool canUncompress(
      const IOBuf* data, Optional<uint64_t> uncompressedLength) const override;

 private:
  bool doNeedsUncompressedLength() const override;
  uint64_t doMaxCompressedLength(uint64_t uncompressedLength) const override;
  Optional<uint64_t> doGetUncompressedLength(
      IOBuf const* data, Optional<uint64_t> uncompressedLength) const override;

  void doResetStream() override;
  bool doCompressStream(
      ByteRange& input,
      MutableByteRange& output,
      StreamCodec::FlushOp flushOp) override;
  bool doUncompressStream(
      ByteRange& input,
      MutableByteRange& output,
      StreamCodec::FlushOp flushOp) override;

  void resetCCtx();
  void resetDCtx();

  Options options_;
  ZSTD_CCtx_Pool::Ref cctx_{getNULL_ZSTD_CCtx()};
  ZSTD_DCtx_Pool::Ref dctx_{getNULL_ZSTD_DCtx()};
};

constexpr uint32_t kZSTDMagicLE = 0xFD2FB528;

std::vector<std::string> ZSTDStreamCodec::validPrefixes() const {
  return {prefixToStringLE(kZSTDMagicLE)};
}

bool ZSTDStreamCodec::canUncompress(
    const IOBuf* data, Optional<uint64_t>) const {
  return dataStartsWithLE(data, kZSTDMagicLE);
}

CodecType codecType(Options const& options) {
  int const level = options.level();
  DCHECK_NE(level, 0);
  return level > 0 ? CodecType::ZSTD : CodecType::ZSTD_FAST;
}

ZSTDStreamCodec::ZSTDStreamCodec(Options options)
    : StreamCodec(codecType(options), options.level()),
      options_(std::move(options)) {}

bool ZSTDStreamCodec::doNeedsUncompressedLength() const {
  return false;
}

uint64_t ZSTDStreamCodec::doMaxCompressedLength(
    uint64_t uncompressedLength) const {
  return ZSTD_compressBound(uncompressedLength);
}

Optional<uint64_t> ZSTDStreamCodec::doGetUncompressedLength(
    IOBuf const* data, Optional<uint64_t> uncompressedLength) const {
  // Read decompressed size from frame if available in first IOBuf.
  auto const decompressedSize =
      ZSTD_getFrameContentSize(data->data(), data->length());
  if (decompressedSize == ZSTD_CONTENTSIZE_UNKNOWN ||
      decompressedSize == ZSTD_CONTENTSIZE_ERROR) {
    return uncompressedLength;
  }
  if (uncompressedLength && *uncompressedLength != decompressedSize) {
    throw std::runtime_error("ZSTD: invalid uncompressed length");
  }
  return decompressedSize;
}

void ZSTDStreamCodec::doResetStream() {
  cctx_.reset(nullptr);
  dctx_.reset(nullptr);
}

void ZSTDStreamCodec::resetCCtx() {
  DCHECK(cctx_ == nullptr);
  cctx_ = getZSTD_CCtx();
  DCHECK(cctx_ != nullptr);
  resetCCtxSessionAndParameters(cctx_.get());
  zstdThrowIfError(
      ZSTD_CCtx_setParametersUsingCCtxParams(cctx_.get(), options_.params()));
  zstdThrowIfError(ZSTD_CCtx_setPledgedSrcSize(
      cctx_.get(), uncompressedLength().value_or(ZSTD_CONTENTSIZE_UNKNOWN)));
}

bool ZSTDStreamCodec::doCompressStream(
    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp flushOp) {
  if (cctx_ == nullptr) {
    resetCCtx();
  }
  ZSTD_inBuffer in = {input.data(), input.size(), 0};
  ZSTD_outBuffer out = {output.data(), output.size(), 0};
  SCOPE_EXIT {
    input.uncheckedAdvance(in.pos);
    output.uncheckedAdvance(out.pos);
  };
  size_t const rc = zstdThrowIfError(ZSTD_compressStream2(
      cctx_.get(), &out, &in, zstdTranslateFlush(flushOp)));
  switch (flushOp) {
    case StreamCodec::FlushOp::NONE:
      return false;
    case StreamCodec::FlushOp::FLUSH:
      return rc == 0;
    case StreamCodec::FlushOp::END:
      if (rc == 0) {
        // Surrender our cctx_
        doResetStream();
      }
      return rc == 0;
    default:
      throw std::invalid_argument("ZSTD: invalid FlushOp");
  }
}

void ZSTDStreamCodec::resetDCtx() {
  DCHECK(dctx_ == nullptr);
  dctx_ = getZSTD_DCtx();
  DCHECK(dctx_ != nullptr);
  resetDCtxSessionAndParameters(dctx_.get());
  if (options_.maxWindowSize() != 0) {
    zstdThrowIfError(
        ZSTD_DCtx_setMaxWindowSize(dctx_.get(), options_.maxWindowSize()));
  }
}

bool ZSTDStreamCodec::doUncompressStream(
    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp) {
  if (dctx_ == nullptr) {
    resetDCtx();
  }
  ZSTD_inBuffer in = {input.data(), input.size(), 0};
  ZSTD_outBuffer out = {output.data(), output.size(), 0};
  SCOPE_EXIT {
    input.uncheckedAdvance(in.pos);
    output.uncheckedAdvance(out.pos);
  };
  size_t const rc =
      zstdThrowIfError(ZSTD_decompressStream(dctx_.get(), &out, &in));
  if (rc == 0) {
    // Surrender our dctx_
    doResetStream();
  }
  return rc == 0;
}

} // namespace

Options::Options(int level) : params_(ZSTD_createCCtxParams()), level_(level) {
  if (params_ == nullptr) {
    throw std::bad_alloc{};
  }
#if ZSTD_VERSION_NUMBER >= 10304
  zstdThrowIfError(ZSTD_CCtxParams_init(params_.get(), level));
#else
  zstdThrowIfError(ZSTD_initCCtxParams(params_.get(), level));
  set(ZSTD_c_contentSizeFlag, 1);
#endif
  // zstd-1.3.4 is buggy and only disables Huffman decompression for negative
  // compression levels if this call is present. This call is begign in other
  // versions.
  set(ZSTD_c_compressionLevel, level);
}

void Options::set(ZSTD_cParameter param, unsigned value) {
  zstdThrowIfError(ZSTD_CCtxParams_setParameter(params_.get(), param, value));
  if (param == ZSTD_c_compressionLevel) {
    level_ = static_cast<int>(value);
  }
}

/* static */ void Options::freeCCtxParams(ZSTD_CCtx_params* params) {
  ZSTD_freeCCtxParams(params);
}

std::unique_ptr<Codec> getCodec(Options options) {
  return std::make_unique<ZSTDStreamCodec>(std::move(options));
}

std::unique_ptr<StreamCodec> getStreamCodec(Options options) {
  return std::make_unique<ZSTDStreamCodec>(std::move(options));
}

} // namespace zstd
} // namespace io
} // namespace folly

#endif
<|endoftext|>