<commit_before>// @(#)root/tree:$Name:  $:$Id: TLeafC.cxx,v 1.14 2002/12/13 22:12:55 brun Exp $
// Author: Rene Brun   17/03/97

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// A TLeaf for a variable length string.                                //
//////////////////////////////////////////////////////////////////////////

#include "TLeafC.h"
#include "TBranch.h"

ClassImp(TLeafC)

//______________________________________________________________________________
TLeafC::TLeafC(): TLeaf()
{
//*-*-*-*-*-*Default constructor for LeafC*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*        ============================

   fValue = 0;
   fPointer = 0;
}

//______________________________________________________________________________
TLeafC::TLeafC(const char *name, const char *type)
       :TLeaf(name,type)
{
//*-*-*-*-*-*-*-*-*-*-*-*-*Create a LeafC*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                      ==============
//*-*

   fLenType = 1;
   fMinimum = 0;
   fMaximum = 0;
   fValue   = 0;
   fPointer = 0;
}

//______________________________________________________________________________
TLeafC::~TLeafC()
{
//*-*-*-*-*-*Default destructor for a LeafC*-*-*-*-*-*-*-*-*-*-*-*
//*-*        ===============================

   if (ResetAddress(0,kTRUE)) delete [] fValue;
}

//______________________________________________________________________________
void TLeafC::Export(TClonesArray *list, Int_t n)
{
//*-*-*-*-*-*Export element from local leaf buffer to ClonesArray*-*-*-*-*
//*-*        ====================================================

   Int_t j = 0;
   for (Int_t i=0;i<n;i++) {
      memcpy((char*)list->UncheckedAt(i) + fOffset,&fValue[j], 1);
      j += fLen;
   }
}

//______________________________________________________________________________
void TLeafC::FillBasket(TBuffer &b)
{
//*-*-*-*-*-*-*-*-*-*-*Pack leaf elements in Basket output buffer*-*-*-*-*-*-*
//*-*                  ==========================================

   if (fPointer) fValue = *fPointer;
   Int_t len = strlen(fValue);
   if (len >= fMaximum) fMaximum = len+1;
   if (len >= fLen)     fLen = len+1;
   if (len < 255) {
      b << (UChar_t)len;
   } else {
      b << (UChar_t)255;
      b << len;
   }
   if (len) b.WriteFastArray(fValue,len);
}

//______________________________________________________________________________
const char *TLeafC::GetTypeName() const
{
//*-*-*-*-*-*-*-*Returns name of leaf type*-*-*-*-*-*-*-*-*-*-*-*
//*-*            =========================

   if (fIsUnsigned) return "UChar_t";
   return "Char_t";
}

//______________________________________________________________________________
void TLeafC::Import(TClonesArray *list, Int_t n)
{
//*-*-*-*-*-*Import element from ClonesArray into local leaf buffer*-*-*-*-*
//*-*        ======================================================

   Int_t j = 0;
   for (Int_t i=0;i<n;i++) {
      memcpy(&fValue[j],(char*)list->UncheckedAt(i) + fOffset, 1);
      j += fLen;
   }
}

//______________________________________________________________________________
void TLeafC::PrintValue(Int_t) const
{
// Prints leaf value

   char *value = (char*)GetValuePointer();
   printf("%s",value);
}

//______________________________________________________________________________
void TLeafC::ReadBasket(TBuffer &b)
{
//*-*-*-*-*-*-*-*-*-*-*Read leaf elements from Basket input buffer*-*-*-*-*-*
//*-*                  ===========================================

   Int_t len;
   UChar_t lenchar;
   b >> lenchar;
   if (lenchar < 255) {
      len = lenchar;
   } else {
      b >> len;
   }
   if (len) {
      if (len >= fLen) len = fLen-1;
      b.ReadFastArray(fValue,len);
      fValue[len] = 0;
   } else {
      fValue[0] = 0;
   }
}

//______________________________________________________________________________
void TLeafC::ReadBasketExport(TBuffer &b, TClonesArray *list, Int_t n)
{
//*-*-*-*-*-*-*-*-*-*-*Read leaf elements from Basket input buffer*-*-*-*-*-*
//  and export buffer to TClonesArray objects

   UChar_t len;
   b >> len;
   if (len) {
      if (len >= fLen) len = fLen-1;
      b.ReadFastArray(fValue,len);
      fValue[len] = 0;
   } else {
      fValue[0] = 0;
   }

   Int_t j = 0;
   for (Int_t i=0;i<n;i++) {
      memcpy((char*)list->UncheckedAt(i) + fOffset,&fValue[j], 1);
      j += fLen;
   }
}

//______________________________________________________________________________
void TLeafC::ReadValue(ifstream &s)
{
// read a string from ifstream s and store it into the branch buffer
   char *value = (char*)GetValuePointer();
   s >> value;
}

//______________________________________________________________________________
void TLeafC::SetAddress(void *add)
{
//*-*-*-*-*-*-*-*-*-*-*Set leaf buffer data address*-*-*-*-*-*
//*-*                  ============================

   if (ResetAddress(add)) {
      delete [] fValue;
   }
   if (add) {
      if (TestBit(kIndirectAddress)) {
         fPointer = (char**)add;
         Int_t ncountmax = fLen;
         if (fLeafCount) ncountmax = fLen*(fLeafCount->GetMaximum() + 1);
         if (ncountmax > fNdata || *fPointer == 0) {
            if (*fPointer) delete [] *fPointer;
            if (ncountmax > fNdata) fNdata = ncountmax;
            *fPointer = new char[fNdata];
         }
         fValue = *fPointer;
      } else {
         fValue = (char*)add;
      }
   }
   else {
      fValue = new char[fNdata];
      fValue[0] = 0;
   }
}
<commit_msg>factor the string storage into TBuffer<commit_after>// @(#)root/tree:$Name:  $:$Id: TLeafC.cxx,v 1.15 2004/10/18 12:32:12 brun Exp $
// Author: Rene Brun   17/03/97

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// A TLeaf for a variable length string.                                //
//////////////////////////////////////////////////////////////////////////

#include "TLeafC.h"
#include "TBranch.h"

ClassImp(TLeafC)

//______________________________________________________________________________
TLeafC::TLeafC(): TLeaf()
{
//*-*-*-*-*-*Default constructor for LeafC*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*        ============================

   fValue = 0;
   fPointer = 0;
}

//______________________________________________________________________________
TLeafC::TLeafC(const char *name, const char *type)
       :TLeaf(name,type)
{
//*-*-*-*-*-*-*-*-*-*-*-*-*Create a LeafC*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
//*-*                      ==============
//*-*

   fLenType = 1;
   fMinimum = 0;
   fMaximum = 0;
   fValue   = 0;
   fPointer = 0;
}

//______________________________________________________________________________
TLeafC::~TLeafC()
{
//*-*-*-*-*-*Default destructor for a LeafC*-*-*-*-*-*-*-*-*-*-*-*
//*-*        ===============================

   if (ResetAddress(0,kTRUE)) delete [] fValue;
}

//______________________________________________________________________________
void TLeafC::Export(TClonesArray *list, Int_t n)
{
//*-*-*-*-*-*Export element from local leaf buffer to ClonesArray*-*-*-*-*
//*-*        ====================================================

   Int_t j = 0;
   for (Int_t i=0;i<n;i++) {
      memcpy((char*)list->UncheckedAt(i) + fOffset,&fValue[j], 1);
      j += fLen;
   }
}

//______________________________________________________________________________
void TLeafC::FillBasket(TBuffer &b)
{
//*-*-*-*-*-*-*-*-*-*-*Pack leaf elements in Basket output buffer*-*-*-*-*-*-*
//*-*                  ==========================================

   if (fPointer) fValue = *fPointer;
   Int_t len = strlen(fValue);
   if (len >= fMaximum) fMaximum = len+1;
   if (len >= fLen)     fLen = len+1;
   if (len) b.WriteFastArrayString(fValue,len);
}

//______________________________________________________________________________
const char *TLeafC::GetTypeName() const
{
//*-*-*-*-*-*-*-*Returns name of leaf type*-*-*-*-*-*-*-*-*-*-*-*
//*-*            =========================

   if (fIsUnsigned) return "UChar_t";
   return "Char_t";
}

//______________________________________________________________________________
void TLeafC::Import(TClonesArray *list, Int_t n)
{
//*-*-*-*-*-*Import element from ClonesArray into local leaf buffer*-*-*-*-*
//*-*        ======================================================

   Int_t j = 0;
   for (Int_t i=0;i<n;i++) {
      memcpy(&fValue[j],(char*)list->UncheckedAt(i) + fOffset, 1);
      j += fLen;
   }
}

//______________________________________________________________________________
void TLeafC::PrintValue(Int_t) const
{
// Prints leaf value

   char *value = (char*)GetValuePointer();
   printf("%s",value);
}

//______________________________________________________________________________
void TLeafC::ReadBasket(TBuffer &b)
{
//*-*-*-*-*-*-*-*-*-*-*Read leaf elements from Basket input buffer*-*-*-*-*-*
//*-*                  ===========================================

    b.ReadFastArrayString(fValue,fLen);
}

//______________________________________________________________________________
void TLeafC::ReadBasketExport(TBuffer &b, TClonesArray *list, Int_t n)
{
//*-*-*-*-*-*-*-*-*-*-*Read leaf elements from Basket input buffer*-*-*-*-*-*
//  and export buffer to TClonesArray objects

   UChar_t len;
   b >> len;
   if (len) {
      if (len >= fLen) len = fLen-1;
      b.ReadFastArray(fValue,len);
      fValue[len] = 0;
   } else {
      fValue[0] = 0;
   }

   Int_t j = 0;
   for (Int_t i=0;i<n;i++) {
      memcpy((char*)list->UncheckedAt(i) + fOffset,&fValue[j], 1);
      j += fLen;
   }
}

//______________________________________________________________________________
void TLeafC::ReadValue(ifstream &s)
{
// read a string from ifstream s and store it into the branch buffer
   char *value = (char*)GetValuePointer();
   s >> value;
}

//______________________________________________________________________________
void TLeafC::SetAddress(void *add)
{
//*-*-*-*-*-*-*-*-*-*-*Set leaf buffer data address*-*-*-*-*-*
//*-*                  ============================

   if (ResetAddress(add)) {
      delete [] fValue;
   }
   if (add) {
      if (TestBit(kIndirectAddress)) {
         fPointer = (char**)add;
         Int_t ncountmax = fLen;
         if (fLeafCount) ncountmax = fLen*(fLeafCount->GetMaximum() + 1);
         if (ncountmax > fNdata || *fPointer == 0) {
            if (*fPointer) delete [] *fPointer;
            if (ncountmax > fNdata) fNdata = ncountmax;
            *fPointer = new char[fNdata];
         }
         fValue = *fPointer;
      } else {
         fValue = (char*)add;
      }
   }
   else {
      fValue = new char[fNdata];
      fValue[0] = 0;
   }
}
<|endoftext|>


<commit_before>#ifndef __NATIVE_ERROR_HPP__
#define __NATIVE_ERROR_HPP__

#include "base/base_utils.hpp"

namespace native {
class Exception {
public:
  struct CallStack {
    CallStack() = delete;
    CallStack(const std::string &iFile, const int iLine, const std::string &iFunction)
        : _file(iFile), _line(iLine), _function(iFunction) {}
    std::string _file;
    int _line;
    std::string _function;
  };

  Exception(const std::string &message) : _message(message), _uvError(0) {}

  Exception(const std::string &message, const std::string &iFile, const int iLine, const std::string &iFunction)
      : _message(message), _uvError(0) {
    CallStack item(iFile, iLine, iFunction);
    _callStack.push_back(item);
  }

  Exception(const std::string &message,
            const std::string &iFile,
            const int iLine,
            const std::string &iFunction,
            const int iuvError)
      : _message(message), _uvError(iuvError) {
    CallStack item(iFile, iLine, iFunction);
    _callStack.push_back(item);
  }

  Exception(const Exception &iOther, const std::string &iFile, const int iLine, const std::string &iFunction) {
    CallStack item(iFile, iLine, iFunction);
    _callStack.push_back(item);
    for (const CallStack &i : iOther._callStack) {
      _callStack.push_back(i);
    }
    _message = iOther._message;
    _uvError = iOther._uvError;
  }

  virtual ~Exception() {}

  const std::string &message() const { return _message; }

private:
  std::string _message;
  std::list<CallStack> _callStack;
  int _uvError;
};

class Error {
public:
  Error() : _uv_err(0) {}
  Error(int iErrCode) : _uv_err(iErrCode) {}
  Error(const std::string &str) : _uv_err(-1), _str(str) {}
  ~Error() = default;
  bool isError() { return (_uv_err < 0); }
  bool isOK() { return (_uv_err > 0); }
  operator bool() { return isError(); }
  virtual Error &operator=(int iErrCode) {
    setError(iErrCode);
    return *this;
  }

  void setError(int iErrCode) { _uv_err = iErrCode; }
  int code() const { return _uv_err; }

  virtual const char *name() const {
    if (_str.empty() && _uv_err < 0) {
      return uv_err_name(_uv_err);
    }
    return 0;
  }
  virtual const char *str() const {
    if (!_str.empty()) {
      return _str.c_str();
    } else if (_uv_err < 0) {
      return uv_strerror(_uv_err);
    }
    return 0;
  }

private:
  int _uv_err;
  std::string _str;
};
}

#endif /* __NATIVE_ERROR_HPP__ */
<commit_msg>error: resolve isOK method<commit_after>#ifndef __NATIVE_ERROR_HPP__
#define __NATIVE_ERROR_HPP__

#include "base/base_utils.hpp"

namespace native {
class Exception {
public:
  struct CallStack {
    CallStack() = delete;
    CallStack(const std::string &iFile, const int iLine, const std::string &iFunction)
        : _file(iFile), _line(iLine), _function(iFunction) {}
    std::string _file;
    int _line;
    std::string _function;
  };

  Exception(const std::string &message) : _message(message), _uvError(0) {}

  Exception(const std::string &message, const std::string &iFile, const int iLine, const std::string &iFunction)
      : _message(message), _uvError(0) {
    CallStack item(iFile, iLine, iFunction);
    _callStack.push_back(item);
  }

  Exception(const std::string &message,
            const std::string &iFile,
            const int iLine,
            const std::string &iFunction,
            const int iuvError)
      : _message(message), _uvError(iuvError) {
    CallStack item(iFile, iLine, iFunction);
    _callStack.push_back(item);
  }

  Exception(const Exception &iOther, const std::string &iFile, const int iLine, const std::string &iFunction) {
    CallStack item(iFile, iLine, iFunction);
    _callStack.push_back(item);
    for (const CallStack &i : iOther._callStack) {
      _callStack.push_back(i);
    }
    _message = iOther._message;
    _uvError = iOther._uvError;
  }

  virtual ~Exception() {}

  const std::string &message() const { return _message; }

private:
  std::string _message;
  std::list<CallStack> _callStack;
  int _uvError;
};

class Error {
public:
  Error() : _uv_err(0) {}
  Error(int iErrCode) : _uv_err(iErrCode) {}
  Error(const std::string &str) : _uv_err(-1), _str(str) {}
  ~Error() = default;
  bool isError() { return (_uv_err < 0); }
  bool isOK() { return !isError(); }
  operator bool() { return isError(); }
  virtual Error &operator=(int iErrCode) {
    setError(iErrCode);
    return *this;
  }

  void setError(int iErrCode) { _uv_err = iErrCode; }
  int code() const { return _uv_err; }

  virtual const char *name() const {
    if (_str.empty() && _uv_err < 0) {
      return uv_err_name(_uv_err);
    }
    return 0;
  }
  virtual const char *str() const {
    if (!_str.empty()) {
      return _str.c_str();
    } else if (_uv_err < 0) {
      return uv_strerror(_uv_err);
    }
    return 0;
  }

private:
  int _uv_err;
  std::string _str;
};
}

#endif /* __NATIVE_ERROR_HPP__ */
<|endoftext|>


<commit_before>
#ifndef NDHIST_DTYPE_H_INCLUDED
#define NDHIST_DTYPE_H_INCLUDED 1

#include <stdint.h>

namespace ndhist {

/**
 * The dtype_desc class provides a base class for all data type classes
 * usable with ndhist. It stores the size of the particular data type in bytes.
 */
class dtype_desc
{
  public:
    dtype_desc()
      : size_(0)
    {}

    dtype_desc(size_t size)
      : size_(size)
    {}

    inline
    size_t GetSize() const { return size_; }

  protected:
    /// The size of the data type in bytes.
    size_t size_;
};

template <CPPType>
class dtype
  : public dtype_desc
{
  public:
    typedef CPPType cpp_type;

    dtype()
      : dtype_desc(sizeof(cpp_type))
    {}
};

class float64
  : public dtype<double>
{
  public:
    typedef dtype<double> base;
    typedef typename base::cpp_type cpp_type;

    float64()
      : base()
    {}
};

class uint64
  : public dtype<uint64_t>
{
  public:
    typedef dtype<uint64_t> base;
    typedef typename base::cpp_type cpp_type;

    uint64()
      : base()
    {}
};

class dtype_value
{
  public:
    dtype_value(dtype_desc const & dt)
      : dtype_desc_(dt)
      , data_(NULL)
    {}

    template <typename T>
    bool
    set(T value)
    {
        size_t sizeT = sizeof(T);
        // Check if T is compatible with the given dtype_desc.
        dtype<T> dt;
        if(dt.GetSize() != sizeT)
        {
            return false;
        }

        // Allocate memory, if not done so alreay before.
        if(data_ == NULL)
        {
            data_ = malloc(sizeT);
            if(data_ == NULL)
            {
                return false;
            }
        }

        memcpy(data_, &value, sizeT);
        return true;
    }

    template <typename T>
    T&
    get()
    {

    }
  protected:
    char* data_;
    dtype_desc dtype_desc_;
};

}// namespace ndhist

#endif
<commit_msg>Get rid of an old file/idea.<commit_after><|endoftext|>


<commit_before>/******************************************************************************
* Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in
*       the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
*       names of its contributors may be used to endorse or promote
*       products derived from this software without specific prior
*       written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/

#ifndef INCLUDED_OPTIONS_HPP
#define INCLUDED_OPTIONS_HPP

#include <pdal/pdal.hpp>
#include <pdal/exceptions.hpp>
#include <pdal/Bounds.hpp>
#include <boost/property_tree/ptree.hpp>

namespace pdal
{

class PDAL_DLL OptionsOld
{
private:
    boost::property_tree::ptree m_tree;

public:
    OptionsOld()
    {
        m_tree.put("is3d", false);
    }

    OptionsOld(boost::property_tree::ptree const& tree) { m_tree = tree; }
    boost::property_tree::ptree& GetPTree() { return m_tree; }
    boost::property_tree::ptree const& GetPTree() const { return m_tree; }
};

// An Option is just a record with three fields: name, value, and description.
//
// Dumped as XML, it looks like this:
//     <?xml...>
//     <Name>myname</Name>
//     <Description>my descr</Description>
//     <Value>17</Value>
// although of course that's not valid XML, since it has no single root element.

template <typename T>
class PDAL_DLL Option
{
public:
    // construct it manually
    Option(std::string const& name, T value, std::string const& description="")
        : m_name(name)
        , m_description(description)
        , m_value(value)
    {}

    // construct it from an xml stream
    Option(std::istream& istr)
    {
        boost::property_tree::ptree t;
        boost::property_tree::xml_parser::read_xml(istr, t);
        m_name = t.get<std::string>("Name");
        m_value = t.get<T>("Value");
        m_description = t.get<std::string>("Description", "");
    }

    // construct it from a ptree
    Option(const boost::property_tree::ptree t)
    {
        m_name = t.get<std::string>("Name");
        m_value = t.get<T>("Value");
        m_description = t.get<std::string>("Description", "");
    }

    // getters
    std::string const& getName() const { return m_name; }
    std::string const& getDescription() const { return m_description; }
    T const& getValue() const { return m_value; }

    // return a ptree representation
    boost::property_tree::ptree getPTree() const
    {
        boost::property_tree::ptree t;
        t.put("Name", getName());
        t.put("Description", getDescription());
        t.put("Value", getValue());
        return t;
    }

    // return an xml representation
    void write_xml(std::ostream& ostr) const
    {
        const boost::property_tree::ptree tree = getPTree();
        boost::property_tree::xml_parser::write_xml(ostr, tree);
        return;
    }

private:
    std::string m_name;
    std::string m_description;
    T m_value;
};

// An Options object is just a tree of Option objects.
//
// Dumped as XML, an Options object with two Option objects looks like this:
//     <?xml...>
//     <option>
//       <name>myname</name>
//       <description>my descr</description>
//       <value>17</value>
//     </option>
//     <option>
//       <name>myname2</name>
//       <description>my descr2</description>
//       <value>13</value>
//     </option>
// although of course that's not valid XML, since it has no single root element.
class PDAL_DLL Options
{
public:
    // defult ctor, empy options list
    Options() {}

    Options(boost::property_tree::ptree t);

    // read options from an xml stream
    Options(std::istream& istr);

    // add an option
    template<class T> void add(Option<T> const& option)
    {
       boost::property_tree::ptree fields = option.getPTree();
       m_tree.add_child("Option", fields);
    }

    // add an option (shortcut version, bypass need for an Option object)
    template<class T> void add(const std::string& name, T value, const std::string& description="")
    {
        Option<T> opt(name, value, description);
        add(opt);
    }

    // get an option, by name
    // throws pdal::option_not_found if the option name is not valid
    template<typename T> Option<T> getOption(std::string const& name) const
    {
        boost::property_tree::ptree::const_iterator iter = m_tree.begin();
        while (iter != m_tree.end())
        {
            if (iter->first != "Option")
                throw pdal_error("malformed Options ptree");

            const boost::property_tree::ptree& optionTree = iter->second;
            if (optionTree.get_child("Name").get_value<std::string>() == name)
            {
                Option<T> option(optionTree);
                return option;
            }
            ++iter;
        }
        throw option_not_found(name);
    }

    // returns true iff the option name is valid
    template<typename T> bool hasOption(std::string const& name) const
    {
        bool ok = false;

        try
        {
            Option<T> option = getOption<T>(name);
            ok = true;
        }
        catch (option_not_found&)
        {
            ok = false;
        }
        // any other exception will bubble up

        return ok;
    }

    // get the ptree for the whole option block
    const boost::property_tree::ptree& getPTree() const;

    // the empty options list
    // BUG: this should be a member variable, not a function, but doing so causes vs2010 to fail to link
    static const Options& empty();

private:
    // get the ptree for an option
    // throws pdal::option_not_found if the option name is not valid
    const boost::property_tree::ptree getOptionPTree(const std::string& name) const;

    boost::property_tree::ptree m_tree;
};

PDAL_DLL std::ostream& operator<<(std::ostream& ostr, const OptionsOld&);

} // namespace pdal

#endif
<commit_msg>include xml_parser from property_tree<commit_after>/******************************************************************************
* Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in
*       the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
*       names of its contributors may be used to endorse or promote
*       products derived from this software without specific prior
*       written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/

#ifndef INCLUDED_OPTIONS_HPP
#define INCLUDED_OPTIONS_HPP

#include <pdal/pdal.hpp>
#include <pdal/exceptions.hpp>
#include <pdal/Bounds.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

namespace pdal
{

class PDAL_DLL OptionsOld
{
private:
    boost::property_tree::ptree m_tree;

public:
    OptionsOld()
    {
        m_tree.put("is3d", false);
    }

    OptionsOld(boost::property_tree::ptree const& tree) { m_tree = tree; }
    boost::property_tree::ptree& GetPTree() { return m_tree; }
    boost::property_tree::ptree const& GetPTree() const { return m_tree; }
};

// An Option is just a record with three fields: name, value, and description.
//
// Dumped as XML, it looks like this:
//     <?xml...>
//     <Name>myname</Name>
//     <Description>my descr</Description>
//     <Value>17</Value>
// although of course that's not valid XML, since it has no single root element.

template <typename T>
class PDAL_DLL Option
{
public:
    // construct it manually
    Option(std::string const& name, T value, std::string const& description="")
        : m_name(name)
        , m_description(description)
        , m_value(value)
    {}

    // construct it from an xml stream
    Option(std::istream& istr)
    {
        boost::property_tree::ptree t;
        boost::property_tree::xml_parser::read_xml(istr, t);
        m_name = t.get<std::string>("Name");
        m_value = t.get<T>("Value");
        m_description = t.get<std::string>("Description", "");
    }

    // construct it from a ptree
    Option(const boost::property_tree::ptree t)
    {
        m_name = t.get<std::string>("Name");
        m_value = t.get<T>("Value");
        m_description = t.get<std::string>("Description", "");
    }

    // getters
    std::string const& getName() const { return m_name; }
    std::string const& getDescription() const { return m_description; }
    T const& getValue() const { return m_value; }

    // return a ptree representation
    boost::property_tree::ptree getPTree() const
    {
        boost::property_tree::ptree t;
        t.put("Name", getName());
        t.put("Description", getDescription());
        t.put("Value", getValue());
        return t;
    }

    // return an xml representation
    void write_xml(std::ostream& ostr) const
    {
        const boost::property_tree::ptree tree = getPTree();
        boost::property_tree::xml_parser::write_xml(ostr, tree);
        return;
    }

private:
    std::string m_name;
    std::string m_description;
    T m_value;
};

// An Options object is just a tree of Option objects.
//
// Dumped as XML, an Options object with two Option objects looks like this:
//     <?xml...>
//     <option>
//       <name>myname</name>
//       <description>my descr</description>
//       <value>17</value>
//     </option>
//     <option>
//       <name>myname2</name>
//       <description>my descr2</description>
//       <value>13</value>
//     </option>
// although of course that's not valid XML, since it has no single root element.
class PDAL_DLL Options
{
public:
    // defult ctor, empy options list
    Options() {}

    Options(boost::property_tree::ptree t);

    // read options from an xml stream
    Options(std::istream& istr);

    // add an option
    template<class T> void add(Option<T> const& option)
    {
       boost::property_tree::ptree fields = option.getPTree();
       m_tree.add_child("Option", fields);
    }

    // add an option (shortcut version, bypass need for an Option object)
    template<class T> void add(const std::string& name, T value, const std::string& description="")
    {
        Option<T> opt(name, value, description);
        add(opt);
    }

    // get an option, by name
    // throws pdal::option_not_found if the option name is not valid
    template<typename T> Option<T> getOption(std::string const& name) const
    {
        boost::property_tree::ptree::const_iterator iter = m_tree.begin();
        while (iter != m_tree.end())
        {
            if (iter->first != "Option")
                throw pdal_error("malformed Options ptree");

            const boost::property_tree::ptree& optionTree = iter->second;
            if (optionTree.get_child("Name").get_value<std::string>() == name)
            {
                Option<T> option(optionTree);
                return option;
            }
            ++iter;
        }
        throw option_not_found(name);
    }

    // returns true iff the option name is valid
    template<typename T> bool hasOption(std::string const& name) const
    {
        bool ok = false;

        try
        {
            Option<T> option = getOption<T>(name);
            ok = true;
        }
        catch (option_not_found&)
        {
            ok = false;
        }
        // any other exception will bubble up

        return ok;
    }

    // get the ptree for the whole option block
    const boost::property_tree::ptree& getPTree() const;

    // the empty options list
    // BUG: this should be a member variable, not a function, but doing so causes vs2010 to fail to link
    static const Options& empty();

private:
    // get the ptree for an option
    // throws pdal::option_not_found if the option name is not valid
    const boost::property_tree::ptree getOptionPTree(const std::string& name) const;

    boost::property_tree::ptree m_tree;
};

PDAL_DLL std::ostream& operator<<(std::ostream& ostr, const OptionsOld&);

} // namespace pdal

#endif
<|endoftext|>


<commit_before>#include "testing/gtest.hh"

#include "viewer/colors/viridis.hh"
#include "viewer/primitives/box.hh"
#include "viewer/primitives/simple_geometry.hh"
#include "viewer/window_3d.hh"
#include "viewer/window_manager.hh"

#include "geometry/import/read_stl.hh"
#include "geometry/spatial/bounding_volume_hierarchy.hh"
#include "geometry/spatial/sphere_volume.hh"
#include "geometry/spatial/triangle_volume.hh"

#include "eigen_helpers.hh"
#include "util/heap.hh"

#include <map>
#include <set>
#include <stack>

using Vec3 = Eigen::Vector3d;
using Vec4 = Eigen::Vector4d;

void verify_all_leaves_unique(const geometry::spatial::BoundingVolumeHierarchy &bvh) {
  std::set<int> already_used;
  for (const auto &node : bvh.tree()) {
    if (node.is_leaf) {
      for (int k = node.leaf.start; k < node.leaf.end; ++k) {
        EXPECT_EQ(already_used.count(k), 0u);
        already_used.insert(k);
      }
    }
  }
}

void draw_children(const geometry::spatial::BoundingVolumeHierarchy &bvh, int base_node_ind) {
  auto win                    = gl_viewer::get_window3d("Window A");
  auto draw_children_geometry = win->add_primitive<gl_viewer::SimpleGeometry>();

  Heap<int> stack;
  stack.push(base_node_ind);
  while (!stack.empty()) {
    const int   next_index = stack.pop();
    const auto &node       = bvh.tree()[next_index];
    if (node.is_leaf) {
      for (int k = node.leaf.start; k < node.leaf.end; ++k) {
        const auto &              aabb = bvh.aabb()[k];
        gl_viewer::AxisAlignedBox gl_aabb;
        gl_aabb.lower = aabb.bbox.lower();
        gl_aabb.upper = aabb.bbox.upper();

        draw_children_geometry->add_box(gl_aabb);
      }
    } else {
      stack.push(node.node.left_child_index);
      stack.push(node.node.left_child_index + 1);
    }
  }
  win->spin_until_step();
  draw_children_geometry->clear();
}

void climb_tree(const geometry::spatial::BoundingVolumeHierarchy &bvh) {
  auto win                 = gl_viewer::get_window3d("Window A");
  auto tree_climb_geometry = win->add_primitive<gl_viewer::SimpleGeometry>();

  // Explore first branch every time
  struct ExplorationCandidate {
    int index;
    int depth;
  };
  ExplorationCandidate cnd;
  cnd.index = 0;
  cnd.depth = 0;

  Heap<ExplorationCandidate> heap(
      [](const ExplorationCandidate &a, const ExplorationCandidate &b) { return a.depth < b.depth; });
  heap.push(cnd);

  std::map<int, Eigen::Vector4d> colors;
  const auto tree = bvh.tree();
  while (heap.size()) {
    const auto  next = heap.pop();
    const auto &node = tree[next.index];

    if (!node.is_leaf) {
      heap.push({node.node.left_child_index, next.depth + 1});
      heap.push({node.node.left_child_index + 1, next.depth + 1});

      auto bbox = tree[node.node.left_child_index].bounding_box;
      bbox.expand(tree[node.node.left_child_index + 1].bounding_box);

      // if (next.depth != 3) {
      // continue;
      // }

      gl_viewer::AxisAlignedBox aabb;
      aabb.lower = node.bounding_box.lower();
      aabb.upper = node.bounding_box.upper();

      if (colors.count(next.depth) == 0) {
        colors[next.depth] = Eigen::Vector4d::Random();
        const double t     = next.depth * (1.0 / 10.0);
        colors[next.depth] = jcc::augment(gl_viewer::colors::viridis(t), 1.0);
      }
      aabb.color = colors[next.depth];

      tree_climb_geometry->add_box(aabb);
      draw_children(bvh, next.index);

      win->spin_until_step();
    }
  }
}

TEST(BoundingVolumeHierarchyTest, intersection) {
  auto win = gl_viewer::get_window3d("Window A");

  const std::string file_path = "/home/jacob/repos/experiments/data/test_stuff2.stl";
  const auto        tri       = geometry::import::read_stl(file_path);

  gl_viewer::WindowManager::draw();
  auto scene_geometry   = win->add_primitive<gl_viewer::SimpleGeometry>();
  auto visitor_geometry = win->add_primitive<gl_viewer::SimpleGeometry>();

  std::vector<geometry::spatial::Volume *> tri_ptrs;
  tri_ptrs.reserve(tri.triangles.size());
  std::vector<geometry::spatial::TriangleVolume> triangles;
  triangles.reserve(tri.triangles.size());

  for (size_t k = 0; k < tri.triangles.size(); ++k) {
    triangles.emplace_back(tri.triangles[k].vertices);
    tri_ptrs.push_back(&triangles.back());
  }

  for (size_t k = 0; k < tri.triangles.size(); ++k) {
    scene_geometry->add_line({tri.triangles[k].vertices[0], tri.triangles[k].vertices[1]});
    scene_geometry->add_line({tri.triangles[k].vertices[1], tri.triangles[k].vertices[2]});
    scene_geometry->add_line({tri.triangles[k].vertices[2], tri.triangles[k].vertices[0]});
  }

  const auto visitor = [&visitor_geometry, &win](const geometry::spatial::BoundingVolumeHierarchy::TreeElement &element,
                                                 const bool intersected) {
    gl_viewer::AxisAlignedBox aabb;
    aabb.lower = element.bounding_box.lower();
    aabb.upper = element.bounding_box.upper();
    if (element.is_leaf) {
      aabb.color = Eigen::Vector4d(0.0, 0.0, 1.0, 0.8);
    } else {
      aabb.color = Eigen::Vector4d(1.0, 0.0, 0.0, 1.0);
    }

    if (intersected) {
      aabb.color(1) = 1.0;
    }

    visitor_geometry->add_box(aabb);
    win->spin_until_step();
  };
  geometry::spatial::BoundingVolumeHierarchy bvh;
  bvh.build(tri_ptrs);

  {
    geometry::Ray ray;
    ray.origin    = Vec3(0.0, 0.0, 0.0);
    ray.direction = Vec3(1.0, 1.0, 1.0).normalized();

    scene_geometry->add_ray(ray, 10.0, Vec4(1.0, 0.0, 0.0, 1.0));
    const auto intersection = bvh.intersect(ray, visitor);
    EXPECT_FALSE(intersection.intersected);
  }
  {
    visitor_geometry->clear();
    geometry::Ray ray;
    ray.origin    = Vec3(0.0, 0.0, 0.75);
    ray.direction = Vec3(-1.0, -1.0, 0.0).normalized();

    scene_geometry->add_ray(ray, 10.0, Vec4(1.0, 0.0, 0.0, 1.0));
    const auto intersection = bvh.intersect(ray, visitor);
    EXPECT_TRUE(intersection.intersected);
    constexpr double EPS = 1e-4;
    EXPECT_NEAR(intersection.distance, 2.80121, EPS);
  }

  win->spin_until_step();
  visitor_geometry->clear();
}

TEST(BoundingVolumeHierarchyTest, bounding_volumes) { /*
   auto win = gl_viewer::get_window3d("Window A");

   const std::string file_path = "/home/jacob/repos/experiments/data/test_stuff2.stl";
   const auto        tri       = geometry::import::read_stl(file_path);

   gl_viewer::WindowManager::draw();
   auto scene_geometry   = win->add_primitive<gl_viewer::SimpleGeometry>();
   auto visitor_geometry = win->add_primitive<gl_viewer::SimpleGeometry>();

   std::vector<geometry::spatial::Volume *> tri_ptrs;
   tri_ptrs.reserve(tri.triangles.size());
   std::vector<geometry::spatial::TriangleVolume> triangles;
   triangles.reserve(tri.triangles.size());

   for (size_t k = 0; k < tri.triangles.size(); ++k) {
     triangles.emplace_back(tri.triangles[k].vertices);
     tri_ptrs.push_back(&triangles.back());
   }

   win->spin_until_step();

   {
     geometry::spatial::BoundingVolumeHierarchy bvh;
     bvh.build(tri_ptrs);
     verify_all_leaves_unique(bvh);
     climb_tree(bvh);
   }
   return;

   std::map<int, Eigen::Vector4d> colors;
   for (int stop_depth = 0; stop_depth < 10; ++stop_depth) {
     const auto visitor = [&visitor_geometry, &win, &colors, stop_depth](const geometry::spatial::BoundingBox<3> &box,
                                                                         int depth, bool leaf) {
       if ((depth != stop_depth) && !leaf) {
         return;
       }
       gl_viewer::AxisAlignedBox aabb;
       aabb.lower = box.lower();
       aabb.upper = box.upper();

       if (colors.count(depth) == 0) {
         colors[depth] = Eigen::Vector4d::Random();
       }
       if (leaf) {
         aabb.color = Eigen::Vector4d(0.0, 1.0, 0.0, 0.8);
       } else {
         aabb.color = colors[depth];
       }
       aabb.color[3] = 0.6;
       aabb.color[0] = 1.0;

       visitor_geometry->add_box(aabb);
     };
     geometry::spatial::BoundingVolumeHierarchy bvh;
     bvh.build(tri_ptrs, visitor);
     win->spin_until_step();
     visitor_geometry->clear();
   }
 */
}
<commit_msg>Enable all bvh tests<commit_after>#include "testing/gtest.hh"

#include "viewer/colors/viridis.hh"
#include "viewer/primitives/box.hh"
#include "viewer/primitives/simple_geometry.hh"
#include "viewer/window_3d.hh"

#include "geometry/import/read_stl.hh"
#include "geometry/spatial/bounding_volume_hierarchy.hh"
#include "geometry/spatial/sphere_volume.hh"
#include "geometry/spatial/triangle_volume.hh"

#include "eigen_helpers.hh"
#include "util/heap.hh"

#include <map>
#include <set>
#include <stack>

using Vec3 = Eigen::Vector3d;
using Vec4 = Eigen::Vector4d;

void verify_all_leaves_unique(const geometry::spatial::BoundingVolumeHierarchy &bvh) {
  std::set<int> already_used;
  for (const auto &node : bvh.tree()) {
    if (node.is_leaf) {
      for (int k = node.leaf.start; k < node.leaf.end; ++k) {
        EXPECT_EQ(already_used.count(k), 0u);
        already_used.insert(k);
      }
    }
  }
}

void draw_children(const geometry::spatial::BoundingVolumeHierarchy &bvh, int base_node_ind) {
  auto win = gl_viewer::get_window3d("Window A");
  auto draw_children_geometry = win->add_primitive<gl_viewer::SimpleGeometry>();

  Heap<int> stack;
  stack.push(base_node_ind);
  while (!stack.empty()) {
    const int next_index = stack.pop();
    const auto &node = bvh.tree()[next_index];
    if (node.is_leaf) {
      for (int k = node.leaf.start; k < node.leaf.end; ++k) {
        const auto &aabb = bvh.aabb()[k];
        gl_viewer::AxisAlignedBox gl_aabb;
        gl_aabb.lower = aabb.bbox.lower();
        gl_aabb.upper = aabb.bbox.upper();

        draw_children_geometry->add_box(gl_aabb);
      }
    } else {
      stack.push(node.node.left_child_index);
      stack.push(node.node.left_child_index + 1);
    }
  }
  win->spin_until_step();
  draw_children_geometry->clear();
}

void climb_tree(const geometry::spatial::BoundingVolumeHierarchy &bvh) {
  auto win = gl_viewer::get_window3d("Window A");
  auto tree_climb_geometry = win->add_primitive<gl_viewer::SimpleGeometry>();

  // Explore first branch every time
  struct ExplorationCandidate {
    int index;
    int depth;
  };
  ExplorationCandidate cnd;
  cnd.index = 0;
  cnd.depth = 0;

  Heap<ExplorationCandidate> heap(
      [](const ExplorationCandidate &a, const ExplorationCandidate &b) { return a.depth < b.depth; });
  heap.push(cnd);

  std::map<int, Eigen::Vector4d> colors;
  const auto tree = bvh.tree();
  while (heap.size()) {
    const auto next = heap.pop();
    const auto &node = tree[next.index];

    if (!node.is_leaf) {
      heap.push({node.node.left_child_index, next.depth + 1});
      heap.push({node.node.left_child_index + 1, next.depth + 1});

      auto bbox = tree[node.node.left_child_index].bounding_box;
      bbox.expand(tree[node.node.left_child_index + 1].bounding_box);

      // if (next.depth != 3) {
      // continue;
      // }

      gl_viewer::AxisAlignedBox aabb;
      aabb.lower = node.bounding_box.lower();
      aabb.upper = node.bounding_box.upper();

      if (colors.count(next.depth) == 0) {
        colors[next.depth] = Eigen::Vector4d::Random();
        const double t = next.depth * (1.0 / 10.0);
        colors[next.depth] = jcc::augment(gl_viewer::colors::viridis(t), 1.0);
      }
      aabb.color = colors[next.depth];

      tree_climb_geometry->add_box(aabb);
      draw_children(bvh, next.index);

      win->spin_until_step();
    }
  }
}

TEST(BoundingVolumeHierarchyTest, intersection) {
  auto win = gl_viewer::get_window3d("Window A");

  const std::string file_path = "/home/jacob/repos/experiments/data/test_stuff2.stl";
  const auto tri = geometry::import::read_stl(file_path);
  auto scene_geometry = win->add_primitive<gl_viewer::SimpleGeometry>();
  auto visitor_geometry = win->add_primitive<gl_viewer::SimpleGeometry>();

  std::vector<geometry::spatial::Volume *> tri_ptrs;
  tri_ptrs.reserve(tri.triangles.size());
  std::vector<geometry::spatial::TriangleVolume> triangles;
  triangles.reserve(tri.triangles.size());

  for (size_t k = 0; k < tri.triangles.size(); ++k) {
    triangles.emplace_back(tri.triangles[k].vertices);
    tri_ptrs.push_back(&triangles.back());
  }

  for (size_t k = 0; k < tri.triangles.size(); ++k) {
    scene_geometry->add_line({tri.triangles[k].vertices[0], tri.triangles[k].vertices[1]});
    scene_geometry->add_line({tri.triangles[k].vertices[1], tri.triangles[k].vertices[2]});
    scene_geometry->add_line({tri.triangles[k].vertices[2], tri.triangles[k].vertices[0]});
  }

  const auto visitor = [&visitor_geometry, &win](const geometry::spatial::BoundingVolumeHierarchy::TreeElement &element,
                                                 const bool intersected) {
    gl_viewer::AxisAlignedBox aabb;
    aabb.lower = element.bounding_box.lower();
    aabb.upper = element.bounding_box.upper();
    if (element.is_leaf) {
      aabb.color = Eigen::Vector4d(0.0, 0.0, 1.0, 0.8);
    } else {
      aabb.color = Eigen::Vector4d(1.0, 0.0, 0.0, 1.0);
    }

    if (intersected) {
      aabb.color(1) = 1.0;
    }

    visitor_geometry->add_box(aabb);
    win->spin_until_step();
  };
  geometry::spatial::BoundingVolumeHierarchy bvh;
  bvh.build(tri_ptrs);

  {
    geometry::Ray ray;
    ray.origin = Vec3(0.0, 0.0, 0.0);
    ray.direction = Vec3(1.0, 1.0, 1.0).normalized();

    scene_geometry->add_ray(ray, 10.0, Vec4(1.0, 0.0, 0.0, 1.0));
    const auto intersection = bvh.intersect(ray, visitor);
    EXPECT_FALSE(intersection.intersected);
  }
  {
    visitor_geometry->clear();
    geometry::Ray ray;
    ray.origin = Vec3(0.0, 0.0, 0.75);
    ray.direction = Vec3(-1.0, -1.0, 0.0).normalized();

    scene_geometry->add_ray(ray, 10.0, Vec4(1.0, 0.0, 0.0, 1.0));
    const auto intersection = bvh.intersect(ray, visitor);
    EXPECT_TRUE(intersection.intersected);
    constexpr double EPS = 1e-4;
    EXPECT_NEAR(intersection.distance, 2.80121, EPS);
  }

  win->spin_until_step();
  visitor_geometry->clear();
}

TEST(BoundingVolumeHierarchyTest, bounding_volumes) {
  auto win = gl_viewer::get_window3d("Window A");

  const std::string file_path = "/home/jacob/repos/experiments/data/test_stuff2.stl";
  const auto tri = geometry::import::read_stl(file_path);

  auto scene_geometry = win->add_primitive<gl_viewer::SimpleGeometry>();
  auto visitor_geometry = win->add_primitive<gl_viewer::SimpleGeometry>();

  std::vector<geometry::spatial::Volume *> tri_ptrs;
  tri_ptrs.reserve(tri.triangles.size());
  std::vector<geometry::spatial::TriangleVolume> triangles;
  triangles.reserve(tri.triangles.size());

  for (size_t k = 0; k < tri.triangles.size(); ++k) {
    triangles.emplace_back(tri.triangles[k].vertices);
    tri_ptrs.push_back(&triangles.back());
  }

  win->spin_until_step();

  {
    geometry::spatial::BoundingVolumeHierarchy bvh;
    bvh.build(tri_ptrs);
    verify_all_leaves_unique(bvh);
    climb_tree(bvh);
  }
  return;

  std::map<int, Eigen::Vector4d> colors;
  for (int stop_depth = 0; stop_depth < 10; ++stop_depth) {
    const auto visitor = [&visitor_geometry, &win, &colors, stop_depth](const geometry::spatial::BoundingBox<3> &box,
                                                                        int depth, bool leaf) {
      if ((depth != stop_depth) && !leaf) {
        return;
      }
      gl_viewer::AxisAlignedBox aabb;
      aabb.lower = box.lower();
      aabb.upper = box.upper();

      if (colors.count(depth) == 0) {
        colors[depth] = Eigen::Vector4d::Random();
      }
      if (leaf) {
        aabb.color = Eigen::Vector4d(0.0, 1.0, 0.0, 0.8);
      } else {
        aabb.color = colors[depth];
      }
      aabb.color[3] = 0.6;
      aabb.color[0] = 1.0;

      visitor_geometry->add_box(aabb);
    };
    geometry::spatial::BoundingVolumeHierarchy bvh;
    bvh.build(tri_ptrs, visitor);
    win->spin_until_step();
    visitor_geometry->clear();
  }
  win->spin_until_step();
}
<|endoftext|>


<commit_before>#include "backenddata.h"

#include "debug.h"

#include "tasks-tarsnap.h"

BackendData::BackendData()
{
}

QMap<QString, JobPtr> BackendData::jobs()
{
    return _jobMap;
}

QMap<QString, ArchivePtr> BackendData::archives()
{
    return _archiveMap;
}

quint64 BackendData::numArchives()
{
    return static_cast<quint64>(_archiveMap.count());
}

bool BackendData::loadArchives()
{
    _archiveMap.clear();

    // Get data from the store.
    if(!global_store->initialized())
    {
        DEBUG << "PersistentStore was not initialized properly.";
        return false;
    }
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from archives")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    if(!global_store->runQuery(query))
    {
        DEBUG << "loadArchives query failed.";
        return false;
    }

    // Process data from the store.
    const int index = query.record().indexOf("name");
    while(query.next())
    {
        ArchivePtr archive(new Archive);
        archive->setName(query.value(index).toString());
        archive->load();
        _archiveMap[archive->name()] = archive;
    }
    return true;
}

QList<ArchivePtr> BackendData::findMatchingArchives(const QString &jobPrefix)
{
    const QString prefix = jobPrefix + QChar('_');

    // Get all archives beginning with the relevant prefix who do
    // not already belong to a job.
    QList<ArchivePtr> matching;
    for(const ArchivePtr &archive : _archiveMap)
    {
        if(archive->name().startsWith(prefix) && archive->jobRef().isEmpty())
            matching << archive;
    }
    return matching;
}

ArchivePtr BackendData::newArchive(BackupTaskDataPtr backupTaskData,
                                   bool              truncated)
{
    ArchivePtr archive(new Archive);
    archive->setName(backupTaskData->name());
    archive->setCommand(backupTaskData->command());
    archive->setJobRef(backupTaskData->jobRef());

    // Was the archive creation interrupted?
    if(truncated)
    {
        archive->setName(archive->name().append(".part"));
        archive->setTruncated(true);
    }

    // Lose milliseconds precision by converting to Unix timestamp and back.
    // So that a subsequent comparison in getArchiveListFinished won't fail.
    archive->setTimestamp(
        QDateTime::fromTime_t(backupTaskData->timestamp().toTime_t()));

    // Save data and add to the map.
    archive->save();
    backupTaskData->setArchive(archive);
    _archiveMap.insert(archive->name(), archive);

    // Ensure that the archive is attached to the job (if applicable).
    for(const JobPtr &job : _jobMap)
    {
        if(job->objectKey() == archive->jobRef())
            emit job->loadArchives();
    }
    return archive;
}

QList<ArchivePtr>
BackendData::setArchivesFromList(QList<struct archive_list_data> metadatas)
{
    QList<ArchivePtr> newArchives;

    QMap<QString, ArchivePtr> nextArchiveMap;
    for(const struct archive_list_data &metadata : metadatas)
    {
        ArchivePtr archive =
            _archiveMap.value(metadata.archiveName, ArchivePtr(new Archive));
        if(!archive->objectKey().isEmpty()
           && (archive->timestamp() != metadata.timestamp))
        {
            // There is a different archive with the same name on the remote
            archive->purge();
            archive.clear();
            archive = archive.create();
        }
        if(archive->objectKey().isEmpty())
        {
            // New archive
            archive->setName(metadata.archiveName);
            archive->setTimestamp(metadata.timestamp);
            archive->setCommand(metadata.command);
            // Automagically set Job ownership
            for(const JobPtr &job : _jobMap)
            {
                if(archive->name().startsWith(job->archivePrefix()))
                    archive->setJobRef(job->objectKey());
            }
            archive->save();
            newArchives.append(archive);
        }
        nextArchiveMap.insert(archive->name(), archive);
        _archiveMap.remove(archive->name());
    }
    // Purge archives left in old _archiveMap (not mirrored by the remote)
    for(const ArchivePtr &archive : _archiveMap)
    {
        archive->purge();
    }
    _archiveMap.clear();
    _archiveMap = nextArchiveMap;
    for(const JobPtr &job : _jobMap)
    {
        emit job->loadArchives();
    }
    return newArchives;
}

void BackendData::removeArchives(QList<ArchivePtr> archives)
{
    for(const ArchivePtr &archive : archives)
    {
        _archiveMap.remove(archive->name());
        archive->purge();
    }
}

bool BackendData::loadJobs()
{
    _jobMap.clear();

    // Get data from the store.
    if(!global_store->initialized())
    {
        DEBUG << "PersistentStore was not initialized properly.";
        return false;
    }
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from jobs")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    if(!global_store->runQuery(query))
    {
        DEBUG << "loadJobs query failed.";
        return false;
    }

    // Process data from the store.
    const int index = query.record().indexOf("name");
    while(query.next())
    {
        JobPtr job(new Job);
        job->setName(query.value(index).toString());
        connect(job.data(), &Job::loadArchives, this,
                &BackendData::loadJobArchives);
        job->load();
        _jobMap[job->name()] = job;
    }
    return true;
}

void BackendData::deleteJob(JobPtr job)
{
    // Clear JobRef for assigned Archives.
    for(const ArchivePtr &archive : job->archives())
    {
        archive->setJobRef("");
        archive->save();
    }

    job->purge();
    _jobMap.remove(job->name());
}

void BackendData::loadJobArchives()
{
    Job *             job = qobject_cast<Job *>(sender());
    QList<ArchivePtr> archives;
    for(const ArchivePtr &archive : _archiveMap)
    {
        if(archive->jobRef() == job->objectKey())
            archives << archive;
    }
    job->setArchives(archives);
}

void BackendData::addJob(JobPtr job)
{
    _jobMap[job->name()] = job;
    connect(job.data(), &Job::loadArchives, this,
            &BackendData::loadJobArchives);
}
<commit_msg>BackendData: don't search for an empty jobRef()<commit_after>#include "backenddata.h"

#include "debug.h"

#include "tasks-tarsnap.h"

BackendData::BackendData()
{
}

QMap<QString, JobPtr> BackendData::jobs()
{
    return _jobMap;
}

QMap<QString, ArchivePtr> BackendData::archives()
{
    return _archiveMap;
}

quint64 BackendData::numArchives()
{
    return static_cast<quint64>(_archiveMap.count());
}

bool BackendData::loadArchives()
{
    _archiveMap.clear();

    // Get data from the store.
    if(!global_store->initialized())
    {
        DEBUG << "PersistentStore was not initialized properly.";
        return false;
    }
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from archives")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    if(!global_store->runQuery(query))
    {
        DEBUG << "loadArchives query failed.";
        return false;
    }

    // Process data from the store.
    const int index = query.record().indexOf("name");
    while(query.next())
    {
        ArchivePtr archive(new Archive);
        archive->setName(query.value(index).toString());
        archive->load();
        _archiveMap[archive->name()] = archive;
    }
    return true;
}

QList<ArchivePtr> BackendData::findMatchingArchives(const QString &jobPrefix)
{
    const QString prefix = jobPrefix + QChar('_');

    // Get all archives beginning with the relevant prefix who do
    // not already belong to a job.
    QList<ArchivePtr> matching;
    for(const ArchivePtr &archive : _archiveMap)
    {
        if(archive->name().startsWith(prefix) && archive->jobRef().isEmpty())
            matching << archive;
    }
    return matching;
}

ArchivePtr BackendData::newArchive(BackupTaskDataPtr backupTaskData,
                                   bool              truncated)
{
    ArchivePtr archive(new Archive);
    archive->setName(backupTaskData->name());
    archive->setCommand(backupTaskData->command());
    archive->setJobRef(backupTaskData->jobRef());

    // Was the archive creation interrupted?
    if(truncated)
    {
        archive->setName(archive->name().append(".part"));
        archive->setTruncated(true);
    }

    // Lose milliseconds precision by converting to Unix timestamp and back.
    // So that a subsequent comparison in getArchiveListFinished won't fail.
    archive->setTimestamp(
        QDateTime::fromTime_t(backupTaskData->timestamp().toTime_t()));

    // Save data and add to the map.
    archive->save();
    backupTaskData->setArchive(archive);
    _archiveMap.insert(archive->name(), archive);

    // Ensure that the archive is attached to the job (if applicable).
    if(!archive->jobRef().isEmpty())
    {
        for(const JobPtr &job : _jobMap)
        {
            if(job->objectKey() == archive->jobRef())
                emit job->loadArchives();
        }
    }
    return archive;
}

QList<ArchivePtr>
BackendData::setArchivesFromList(QList<struct archive_list_data> metadatas)
{
    QList<ArchivePtr> newArchives;

    QMap<QString, ArchivePtr> nextArchiveMap;
    for(const struct archive_list_data &metadata : metadatas)
    {
        ArchivePtr archive =
            _archiveMap.value(metadata.archiveName, ArchivePtr(new Archive));
        if(!archive->objectKey().isEmpty()
           && (archive->timestamp() != metadata.timestamp))
        {
            // There is a different archive with the same name on the remote
            archive->purge();
            archive.clear();
            archive = archive.create();
        }
        if(archive->objectKey().isEmpty())
        {
            // New archive
            archive->setName(metadata.archiveName);
            archive->setTimestamp(metadata.timestamp);
            archive->setCommand(metadata.command);
            // Automagically set Job ownership
            for(const JobPtr &job : _jobMap)
            {
                if(archive->name().startsWith(job->archivePrefix()))
                    archive->setJobRef(job->objectKey());
            }
            archive->save();
            newArchives.append(archive);
        }
        nextArchiveMap.insert(archive->name(), archive);
        _archiveMap.remove(archive->name());
    }
    // Purge archives left in old _archiveMap (not mirrored by the remote)
    for(const ArchivePtr &archive : _archiveMap)
    {
        archive->purge();
    }
    _archiveMap.clear();
    _archiveMap = nextArchiveMap;
    for(const JobPtr &job : _jobMap)
    {
        emit job->loadArchives();
    }
    return newArchives;
}

void BackendData::removeArchives(QList<ArchivePtr> archives)
{
    for(const ArchivePtr &archive : archives)
    {
        _archiveMap.remove(archive->name());
        archive->purge();
    }
}

bool BackendData::loadJobs()
{
    _jobMap.clear();

    // Get data from the store.
    if(!global_store->initialized())
    {
        DEBUG << "PersistentStore was not initialized properly.";
        return false;
    }
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from jobs")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    if(!global_store->runQuery(query))
    {
        DEBUG << "loadJobs query failed.";
        return false;
    }

    // Process data from the store.
    const int index = query.record().indexOf("name");
    while(query.next())
    {
        JobPtr job(new Job);
        job->setName(query.value(index).toString());
        connect(job.data(), &Job::loadArchives, this,
                &BackendData::loadJobArchives);
        job->load();
        _jobMap[job->name()] = job;
    }
    return true;
}

void BackendData::deleteJob(JobPtr job)
{
    // Clear JobRef for assigned Archives.
    for(const ArchivePtr &archive : job->archives())
    {
        archive->setJobRef("");
        archive->save();
    }

    job->purge();
    _jobMap.remove(job->name());
}

void BackendData::loadJobArchives()
{
    Job *             job = qobject_cast<Job *>(sender());
    QList<ArchivePtr> archives;
    for(const ArchivePtr &archive : _archiveMap)
    {
        if(archive->jobRef() == job->objectKey())
            archives << archive;
    }
    job->setArchives(archives);
}

void BackendData::addJob(JobPtr job)
{
    _jobMap[job->name()] = job;
    connect(job.data(), &Job::loadArchives, this,
            &BackendData::loadJobArchives);
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Luca Zanconato
 *
 * 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 <gtest/gtest.h>
#include <fstream>
#include <saltpack.h>
#include <sodium.h>

TEST(signature, attached) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, false);
    sig->addBlock({'a', ' ', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e'}, true);

    out.flush();
    delete sig;

    // verify message
    std::stringstream in(out.str());
    std::stringstream msg;
    saltpack::MessageReader *dec = new saltpack::MessageReader(in);
    while (dec->hasMoreBlocks()) {

        saltpack::BYTE_ARRAY message = dec->getBlock();
        msg.write(reinterpret_cast<const char *>(message.data()), message.size());
    }
    ASSERT_EQ(signer_publickey, dec->getSender());
    delete dec;

    ASSERT_EQ(msg.str(), "a signed message");
}

TEST(signature, attached_failure) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, false);
    sig->addBlock({'a', ' ', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e'}, true);

    out.flush();
    delete sig;

    try {

        // verify message
        std::string mmsg = out.str();
        mmsg[mmsg.size() - 80] = (char)((int)mmsg[mmsg.size() - 80] + 1);
        std::stringstream in(mmsg);
        std::stringstream msg;
        saltpack::MessageReader dec(in);
        while (dec.hasMoreBlocks()) {

            saltpack::BYTE_ARRAY message = dec.getBlock();
            msg.write(reinterpret_cast<const char *>(message.data()), message.size());
        }

        throw std::bad_exception();

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Signature was forged or corrupt.");
    }
}

TEST(signature, attached_armor) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::ArmoredOutputStream aOut(out, saltpack::MODE_ATTACHED_SIGNATURE);
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(aOut, signer_secretkey, false);
    sig->addBlock({'a', ' ', 's', 'i', 'g', 'n', 'e', 'd', ' '}, false);
    sig->addBlock({'m', 'e', 's', 's', 'a', 'g', 'e'}, true);
    aOut.finalise();

    out.flush();
    delete sig;

    // verify message
    std::stringstream in(out.str());
    saltpack::ArmoredInputStream is(in);
    std::stringstream msg;
    saltpack::MessageReader *dec = new saltpack::MessageReader(is);
    while (dec->hasMoreBlocks()) {

        saltpack::BYTE_ARRAY message = dec->getBlock();
        msg.write(reinterpret_cast<const char *>(message.data()), message.size());
    }
    ASSERT_EQ(signer_publickey, dec->getSender());
    delete dec;

    ASSERT_EQ(msg.str(), "a signed message");
}

TEST(signature, detached) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, true);
    sig->addBlock({'T', 'h', '3', ' ', 'm'}, false);
    sig->addBlock({'E', '$', 's', '4', 'g', '['}, true);

    out.flush();
    delete sig;

    // verify message
    std::stringstream in(out.str());
    std::stringstream msg("Th3 mE$s4g[");
    saltpack::MessageReader *dec = new saltpack::MessageReader(in, msg);
    ASSERT_EQ(signer_publickey, dec->getSender());
    delete dec;

    try {

        std::stringstream in2(out.str());
        std::stringstream msg2("Wrong");
        saltpack::MessageReader(in2, msg2);

        throw std::bad_exception();

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Signature was forged or corrupt.");
    }
}

TEST(signature, detached_armor) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::ArmoredOutputStream aOut(out, saltpack::MODE_DETACHED_SIGNATURE);
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(aOut, signer_secretkey, true);
    sig->addBlock({'T', 'h', '3', ' ', 'm', 'E', '$', 's', '4', 'g', '!'}, true);
    aOut.finalise();

    out.flush();
    delete sig;

    // verify message
    std::stringstream in(out.str());
    std::stringstream msg("Th3 mE$s4g!");
    saltpack::ArmoredInputStream is(in);
    saltpack::MessageReader *dec = new saltpack::MessageReader(is, msg);
    ASSERT_EQ(signer_publickey, dec->getSender());
    delete dec;

    try {

        std::stringstream in2(out.str());
        saltpack::ArmoredInputStream is2(in2);
        std::stringstream msg2("Wrong");
        saltpack::MessageReader(is2, msg2);

        throw std::bad_exception();

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Signature was forged or corrupt.");
    }
}

TEST(signature, attached_message_truncated) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, false);
    sig->addBlock({'a', ' ', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e'}, false);

    out.flush();
    delete sig;

    try {

        // verify message
        std::stringstream in(out.str());
        std::stringstream msg;
        saltpack::MessageReader dec(in);
        while (dec.hasMoreBlocks()) {

            saltpack::BYTE_ARRAY message = dec.getBlock();
            msg.write(reinterpret_cast<const char *>(message.data()), message.size());
        }

        throw std::bad_exception();

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Not enough data found to decode block (message truncated?).");
    }
}

TEST(signature, detached_message_truncated) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, true);
    sig->addBlock({'T', 'h', '3', ' ', 'm'}, false);
    sig->addBlock({'E', '$', 's', '4', 'g', '['}, false);

    out.flush();
    delete sig;

    try {

        // verify message
        std::stringstream in(out.str());
        std::stringstream msg("Th3 mE$s4g[");
        saltpack::MessageReader *dec = new saltpack::MessageReader(in, msg);
        ASSERT_EQ(signer_publickey, dec->getSender());
        delete dec;

        throw std::bad_exception();

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Signature not found.");
    }
}

TEST(signature, attached_final_block) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    try {

        // sign message
        std::stringstream out;
        saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, false);
        sig->addBlock({'a', ' ', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e'}, true);
        sig->addBlock({' ', 'v', '2'}, true);

        out.flush();
        delete sig;

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Final block already added.");
    }
}

TEST(signature, detached_final_block) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    try {

        // sign message
        std::stringstream out;
        saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, true);
        sig->addBlock({'T', 'h', '3', ' ', 'm', 'E', '$', 's', '4', 'g', '!'}, true);
        sig->addBlock({'?'}, true);

        out.flush();
        delete sig;

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Final block already added.");
    }
}
<commit_msg>Added tests for V1 compatibility<commit_after>/*
 * Copyright 2016-2017 Luca Zanconato
 *
 * 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 <gtest/gtest.h>
#include <fstream>
#include <saltpack.h>
#include <sodium.h>

TEST(signature, attached) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, false);
    sig->addBlock({'a', ' ', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e'}, true);

    out.flush();
    delete sig;

    // verify message
    std::stringstream in(out.str());
    std::stringstream msg;
    saltpack::MessageReader *dec = new saltpack::MessageReader(in);
    while (dec->hasMoreBlocks()) {

        saltpack::BYTE_ARRAY message = dec->getBlock();
        msg.write(reinterpret_cast<const char *>(message.data()), message.size());
    }
    ASSERT_EQ(signer_publickey, dec->getSender());
    delete dec;

    ASSERT_EQ(msg.str(), "a signed message");
}

TEST(signature, attached_failure) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, false);
    sig->addBlock({'a', ' ', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e'}, true);

    out.flush();
    delete sig;

    try {

        // verify message
        std::string mmsg = out.str();
        mmsg[mmsg.size() - 80] = (char)((int)mmsg[mmsg.size() - 80] + 1);
        std::stringstream in(mmsg);
        std::stringstream msg;
        saltpack::MessageReader dec(in);
        while (dec.hasMoreBlocks()) {

            saltpack::BYTE_ARRAY message = dec.getBlock();
            msg.write(reinterpret_cast<const char *>(message.data()), message.size());
        }

        throw std::bad_exception();

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Signature was forged or corrupt.");
    }
}

TEST(signature, attached_armor) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::ArmoredOutputStream aOut(out, saltpack::MODE_ATTACHED_SIGNATURE);
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(aOut, signer_secretkey, false);
    sig->addBlock({'a', ' ', 's', 'i', 'g', 'n', 'e', 'd', ' '}, false);
    sig->addBlock({'m', 'e', 's', 's', 'a', 'g', 'e'}, true);
    aOut.finalise();

    out.flush();
    delete sig;

    // verify message
    std::stringstream in(out.str());
    saltpack::ArmoredInputStream is(in);
    std::stringstream msg;
    saltpack::MessageReader *dec = new saltpack::MessageReader(is);
    while (dec->hasMoreBlocks()) {

        saltpack::BYTE_ARRAY message = dec->getBlock();
        msg.write(reinterpret_cast<const char *>(message.data()), message.size());
    }
    ASSERT_EQ(signer_publickey, dec->getSender());
    delete dec;

    ASSERT_EQ(msg.str(), "a signed message");
}

TEST(signature, detached) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, true);
    sig->addBlock({'T', 'h', '3', ' ', 'm'}, false);
    sig->addBlock({'E', '$', 's', '4', 'g', '['}, true);

    out.flush();
    delete sig;

    // verify message
    std::stringstream in(out.str());
    std::stringstream msg("Th3 mE$s4g[");
    saltpack::MessageReader *dec = new saltpack::MessageReader(in, msg);
    ASSERT_EQ(signer_publickey, dec->getSender());
    delete dec;

    try {

        std::stringstream in2(out.str());
        std::stringstream msg2("Wrong");
        saltpack::MessageReader(in2, msg2);

        throw std::bad_exception();

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Signature was forged or corrupt.");
    }
}

TEST(signature, detached_armor) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::ArmoredOutputStream aOut(out, saltpack::MODE_DETACHED_SIGNATURE);
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(aOut, signer_secretkey, true);
    sig->addBlock({'T', 'h', '3', ' ', 'm', 'E', '$', 's', '4', 'g', '!'}, true);
    aOut.finalise();

    out.flush();
    delete sig;

    // verify message
    std::stringstream in(out.str());
    std::stringstream msg("Th3 mE$s4g!");
    saltpack::ArmoredInputStream is(in);
    saltpack::MessageReader *dec = new saltpack::MessageReader(is, msg);
    ASSERT_EQ(signer_publickey, dec->getSender());
    delete dec;

    try {

        std::stringstream in2(out.str());
        saltpack::ArmoredInputStream is2(in2);
        std::stringstream msg2("Wrong");
        saltpack::MessageReader(is2, msg2);

        throw std::bad_exception();

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Signature was forged or corrupt.");
    }
}

TEST(signature, attached_message_truncated) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, false);
    sig->addBlock({'a', ' ', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e'}, false);

    out.flush();
    delete sig;

    try {

        // verify message
        std::stringstream in(out.str());
        std::stringstream msg;
        saltpack::MessageReader dec(in);
        while (dec.hasMoreBlocks()) {

            saltpack::BYTE_ARRAY message = dec.getBlock();
            msg.write(reinterpret_cast<const char *>(message.data()), message.size());
        }

        throw std::bad_exception();

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Not enough data found to decode block (message truncated?).");
    }
}

TEST(signature, detached_message_truncated) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    // sign message
    std::stringstream out;
    saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, true);
    sig->addBlock({'T', 'h', '3', ' ', 'm'}, false);
    sig->addBlock({'E', '$', 's', '4', 'g', '['}, false);

    out.flush();
    delete sig;

    try {

        // verify message
        std::stringstream in(out.str());
        std::stringstream msg("Th3 mE$s4g[");
        saltpack::MessageReader *dec = new saltpack::MessageReader(in, msg);
        ASSERT_EQ(signer_publickey, dec->getSender());
        delete dec;

        throw std::bad_exception();

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Signature not found.");
    }
}

TEST(signature, attached_final_block) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    try {

        // sign message
        std::stringstream out;
        saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, false);
        sig->addBlock({'a', ' ', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e'}, true);
        sig->addBlock({' ', 'v', '2'}, true);

        out.flush();
        delete sig;

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Final block already added.");
    }
}

TEST(signature, detached_final_block) {

    saltpack::BYTE_ARRAY signer_secretkey(crypto_sign_SECRETKEYBYTES);
    saltpack::BYTE_ARRAY signer_publickey(crypto_sign_PUBLICKEYBYTES);
    saltpack::Utils::generateSignKeypair(signer_publickey, signer_secretkey);

    try {

        // sign message
        std::stringstream out;
        saltpack::MessageWriter *sig = new saltpack::MessageWriter(out, signer_secretkey, true);
        sig->addBlock({'T', 'h', '3', ' ', 'm', 'E', '$', 's', '4', 'g', '!'}, true);
        sig->addBlock({'?'}, true);

        out.flush();
        delete sig;

    } catch (const saltpack::SaltpackException ex) {

        ASSERT_STREQ(ex.what(), "Final block already added.");
    }
}

TEST(signature, attached_version_one) {

    saltpack::BYTE_ARRAY signer_secretkey({245, 6, 38, 38, 136, 83, 114, 248, 171, 127, 74, 11, 45, 29, 126, 213, 7,
                                           236, 174, 197, 99, 201, 193, 207, 16, 91, 166, 133, 141, 50, 144, 211, 199,
                                           45, 196, 24, 141, 60, 173, 36, 11, 156, 148, 221, 212, 160, 252, 133, 136,
                                           160, 73, 11, 23, 129, 243, 218, 57, 180, 252, 17, 133, 46, 244, 139});
    saltpack::BYTE_ARRAY signer_publickey({199, 45, 196, 24, 141, 60, 173, 36, 11, 156, 148, 221, 212, 160, 252, 133,
                                           136, 160, 73, 11, 23, 129, 243, 218, 57, 180, 252, 17, 133, 46, 244, 139});

    std::string ciphertext = "BEGIN SALTPACK SIGNED MESSAGE. kYM5h1pg6qz9UMn j6G7KB2OUmwXTFd 8hHAxRyMXKWKOxs "
            "bECTM8qEn3zYPTA s94LWmdVgpRAw9I fxsGWxHAkkzEaL1 PfDAsXLp9Zq5ymY 5dySiZQZ5uC3IKy 9VGvkwoHiY8tLW1 "
            "iF5oHeppoqzIN0N 6ySAuKEqldHH8TL j4z3Q4x5C7Rp1lt 7uQljohrfLUO7qx 5EbIJbUQqM22Geh VFAaePwM5YjWGEg "
            "k2um83NphtgtIZQ fW0Aivnts1DYmJ7 bZHBN0yidHwJ2FY 5kmC0vApVJrJfni PwhFaGfjlMnghwS Y5G2v0olHriQMTV "
            "rEEy. END SALTPACK SIGNED MESSAGE.";

    // verify message
    std::stringstream in(ciphertext);
    saltpack::ArmoredInputStream is(in);
    std::stringstream msg;
    saltpack::MessageReader *dec = new saltpack::MessageReader(is);
    while (dec->hasMoreBlocks()) {

        saltpack::BYTE_ARRAY message = dec->getBlock();
        msg.write(reinterpret_cast<const char *>(message.data()), message.size());
    }
    ASSERT_EQ(signer_publickey, dec->getSender());
    delete dec;

    ASSERT_EQ(msg.str(), "Signed message\n");
}

TEST(signature, detached_version_one) {

    saltpack::BYTE_ARRAY signer_secretkey({245, 6, 38, 38, 136, 83, 114, 248, 171, 127, 74, 11, 45, 29, 126, 213, 7,
                                           236, 174, 197, 99, 201, 193, 207, 16, 91, 166, 133, 141, 50, 144, 211, 199,
                                           45, 196, 24, 141, 60, 173, 36, 11, 156, 148, 221, 212, 160, 252, 133, 136,
                                           160, 73, 11, 23, 129, 243, 218, 57, 180, 252, 17, 133, 46, 244, 139});
    saltpack::BYTE_ARRAY signer_publickey({199, 45, 196, 24, 141, 60, 173, 36, 11, 156, 148, 221, 212, 160, 252, 133,
                                           136, 160, 73, 11, 23, 129, 243, 218, 57, 180, 252, 17, 133, 46, 244, 139});

    std::string ciphertext = "BEGIN SALTPACK DETACHED SIGNATURE. kYM5h1pg6qz9UMn j6G7KBABYp9npL6 oT1KkalFeaDwWxs "
            "bECTM8qEn3zYPTA s94LWmdVgpbwCki T35ZsJvycdnnkp5 xjaos54GAI71l9u lGzcrkDkh1iVWXY j8FY4EefSR9qMdi "
            "p8bqfMDseqX84Y2 5dtmyvwTiGQKs1O B40DzEV9VHZbchf PVh04NGL8rZHdQf 1wzeX5z. END SALTPACK DETACHED SIGNATURE.";

    // verify message
    std::stringstream in(ciphertext);
    std::stringstream msg("Signed message 2\n");
    saltpack::ArmoredInputStream is(in);
    saltpack::MessageReader *dec = new saltpack::MessageReader(is, msg);
    ASSERT_EQ(signer_publickey, dec->getSender());
    delete dec;
}
<|endoftext|>


<commit_before>#ifndef RESOURCE_MANAGER_HPP
#define RESOURCE_MANAGER_HPP

#include <iostream>
#include <SDL2/SDL.h>
#include <string>
#include <map>
#include <iostream>
#include <SDL2/SDL_image.h>
#ifdef __EMSCRIPTEN__
#include <SDL/SDL_mixer.h>
#else
#include <SDL2/SDL_mixer.h>
#endif

class ResourceManager {
public:
	/***/
	~ResourceManager() {
		for(auto e : m_textures) {
			SDL_DestroyTexture(e.second);
		}

		for(auto e : m_sounds) {
			Mix_FreeChunk(e.second);
		}
	}

	/***/
	bool load_surface(const std::string &key,
					  const std::string &path,
					  SDL_Renderer *ren) {
		SDL_Texture *texture;
		texture = IMG_LoadTexture(ren, path.c_str());

		if (!texture) {
   			std::cerr << "IMG_Load:" << IMG_GetError() << std::endl;
   			return false;
		}

		m_textures[key] = texture;
		return true;
	}

	/***/
	bool load_sound(std::string key, std::string &path) {
		Mix_Chunk *new_sound = Mix_LoadWAV(path.c_str());

		if (new_sound == NULL) {
			std::cerr << "Error: could not load sound from" << path << std::endl;
			return false;
		}

		m_sounds[key] = new_sound;
		return true;
	}

	/***/
	bool load_music(std::string key, std::string &path) {
		Mix_Music *new_music = Mix_LoadMUS(path.c_str());

		if (new_music == NULL) {
			std::cerr << "Error: could not load sound from " << path << ": "<< Mix_GetError() << std::endl;
			return false;
		}

		m_music[key] = new_music;
		return true;
	}

	/***/
	SDL_Texture* get_texture(std::string texture_key) {
		return m_textures.at(texture_key);
	}

	/***/
	Mix_Chunk* get_sound(std::string sound_key) {
		return m_sounds.at(sound_key);
	}

	/***/
	Mix_Music* get_music(std::string music_key) {
		return m_music.at(music_key);
	}

private:
	std::map<std::string, SDL_Texture*> m_textures;
	std::map<std::string, Mix_Chunk*> m_sounds;
	std::map<std::string, Mix_Music*> m_music;
};

#endif
<commit_msg>ResourceManager: Rename load_surface to load_texture<commit_after>#ifndef RESOURCE_MANAGER_HPP
#define RESOURCE_MANAGER_HPP

#include <iostream>
#include <SDL2/SDL.h>
#include <string>
#include <map>
#include <iostream>
#include <SDL2/SDL_image.h>
#ifdef __EMSCRIPTEN__
#include <SDL/SDL_mixer.h>
#else
#include <SDL2/SDL_mixer.h>
#endif

class ResourceManager {
public:
	/***/
	~ResourceManager() {
		for(auto e : m_textures) {
			SDL_DestroyTexture(e.second);
		}

		for(auto e : m_sounds) {
			Mix_FreeChunk(e.second);
		}
	}

	/***/
	bool load_texture(const std::string &key,
					  const std::string &path,
					  SDL_Renderer *ren) {
		SDL_Texture *texture;
		texture = IMG_LoadTexture(ren, path.c_str());

		if (!texture) {
   			std::cerr << "IMG_Load:" << IMG_GetError() << std::endl;
   			return false;
		}

		m_textures[key] = texture;
		return true;
	}

	/***/
	bool load_sound(std::string key, std::string &path) {
		Mix_Chunk *new_sound = Mix_LoadWAV(path.c_str());

		if (new_sound == NULL) {
			std::cerr << "Error: could not load sound from" << path << std::endl;
			return false;
		}

		m_sounds[key] = new_sound;
		return true;
	}

	/***/
	bool load_music(std::string key, std::string &path) {
		Mix_Music *new_music = Mix_LoadMUS(path.c_str());

		if (new_music == NULL) {
			std::cerr << "Error: could not load sound from " << path << ": "<< Mix_GetError() << std::endl;
			return false;
		}

		m_music[key] = new_music;
		return true;
	}

	/***/
	SDL_Texture* get_texture(std::string texture_key) {
		return m_textures.at(texture_key);
	}

	/***/
	Mix_Chunk* get_sound(std::string sound_key) {
		return m_sounds.at(sound_key);
	}

	/***/
	Mix_Music* get_music(std::string music_key) {
		return m_music.at(music_key);
	}

private:
	std::map<std::string, SDL_Texture*> m_textures;
	std::map<std::string, Mix_Chunk*> m_sounds;
	std::map<std::string, Mix_Music*> m_music;
};

#endif
<|endoftext|>