text:
/*
 * Copyright (c) 2019 Opticks Team. All Rights Reserved.
 *
 * This file is part of Opticks
 * (see https://bitbucket.org/simoncblyth/opticks).
 *
 * 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 "SSys.hh"
#include "BFile.hh"
#include "BStr.hh"
#include "BResource.hh"

#include <csignal>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <vector>

#include <boost/algorithm/string.hpp>
#include <boost/regex.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/operations.hpp>

namespace fs = boost::filesystem;

#include "PLOG.hh"

const plog::Severity BFile::LEVEL = PLOG::EnvLevel("BFile", "DEBUG");

char* BFile::OPTICKS_PATH_PREFIX = NULL ;

/**
Formerly uses the below prefix on a windows build:

case $(uname -s) in
MINGW*) export OPTICKS_PATH_PREFIX="C:\\msys64" ;;
esac
**/

void BFile::setOpticksPathPrefix(const char* prefix)
{
    OPTICKS_PATH_PREFIX = prefix ? strdup(prefix) : NULL ;
}
void BFile::dumpOpticksPathPrefix(const char* msg)
{
     std::cout << msg
               << " OPTICKS_PATH_PREFIX " << ( OPTICKS_PATH_PREFIX ? OPTICKS_PATH_PREFIX : "NULL" )
               << std::endl ;
}

void BFile::setOpticksPathPrefixFromEnv(const char* envvar)
{
    const char* prefix = SSys::getenvvar(envvar);
    if(prefix)
    {
        setOpticksPathPrefix(prefix);
        dumpOpticksPathPrefix("BFile::setOpticksPathPrefixFromEnv envvar ");
    }
}

void dump(boost::cmatch& m)
{
    std::cout << " prefix " << m.prefix() << std::endl ;
    for(unsigned int i=0 ; i < m.size() ; i++)
    {
       std::string sm = m[i] ;
       std::cout << std::setw(3) << i
                 << " sm " << sm
                 << " first [" << m[i].first << "]"
                 << " second [" << m[i].second << "]"
                 << " matched [" << m[i].matched << "]"
                 << std::endl ;
    }
}

/**
BFile::UserTmpDir
-----------------

When TMP envvar is defined return that, otherwise
defaults to /tmp/<username>/opticks

**/

std::string BFile::UserTmpDir()
{
    const char* tmp = SSys::getenvvar(OPTICKS_USER_TMP_KEY) ;
    if(tmp) return tmp ;
    return usertmpdir("/tmp", "opticks", NULL ) ;
}

/**
BFile::UserTmpPath
--------------------

Returns a string suitable for temporary paths including
random hex higits to avoid clashes.

**/

std::string BFile::UserTmpPath_(const char* pfx_)
{
    fs::path tmpl = boost::filesystem::path(UserTmpDir()) ;

    std::stringstream ss ;
    ss << pfx_ << "_%%%%-%%%%-%%%%-%%%%" ;
    std::string pfx = ss.str();
    tmpl /= pfx ;

    fs::path p = boost::filesystem::unique_path(tmpl);
    const std::string tmp = p.native();
    return tmp ;
}

const char* BFile::UserTmpPath(const char* pfx)
{
    std::string path = UserTmpPath_(pfx);
    return strdup(path.c_str());
}

std::string BFile::usertmpdir(const char* base, const char* sub, const char* rel )
{
    fs::path p(base) ;

    const char* user = SSys::username();

    if(user) p /= user ;
    if(sub) p /= sub ;
    if(rel) p /= rel ;

    std::string x = p.string() ;
    return x ;
}

/**
BFile::expandvar
------------------

Expands dollar tokens in strings according
to envvars or internal BResource vars

**/

std::string BFile::expandvar(const char* s)
{
    fs::path p ;

    std::string dollar("$");
    boost::regex e("(\\$)(\\w+)(.*?)"); // eg $HOME/.opticks/hello
    boost::cmatch m ;

    if(boost::regex_match(s,m,e))
    {
        //dump(m);

        unsigned int size = m.size();

        if(size == 4 && dollar.compare(m[1]) == 0)
        {
           std::string key = m[2] ;

           std::string evalue = ResolveKey(key.c_str());

           p /= evalue ;

           std::string tail = m[3] ;

           p /= tail ;
        }
    }
    else
    {
        p /= s ;
    }

    p.make_preferred();

    std::string x = p.string() ;
    return x ;
}

const std::vector<std::string> BFile::envvars = {
   "TMP",
   "HOME",
   "OPTICKS_INSTALL_PREFIX",    // needed for OpticksFlags to find the enum header
   "OPTICKS_PREFIX",
   "OPTICKS_GEOCACHE_PREFIX",
   "OPTICKS_EVENT_BASE",
   "OPTICKS_HOME",              // needed by OInterpolationTest to find a python script
   "OPTICKS_USER_HOME",         // overrides HOME within Opticks
   "IDPATH"
} ;

/**
BFile::IsAllowedEnvvar
-----------------------

Envvars listed as allowed will be resolved from strings such as $OPTICKS_GEOCACHE_PREFIX
by BFile path handling

**/

bool BFile::IsAllowedEnvvar(const char* key_)
{
    std::string key(key_);
    return std::find( envvars.begin(), envvars.end(), key ) != envvars.end() ;
}

/**
BFile::ResolveKey
---------------------

NB only allowed envvars are replaced, for clarity and control

The HOME key is special cased, it is replaced by OPTICKS_USER_HOME
if that envvar is defined.  This is convenient to internally
change the HOME directory for Opticks executables on clusters
where HOME is prone to permissions problems, eg from expiring AFS tokens.

::

   OPTICKS_USER_HOME=/hpcfs/juno/junogpu/blyth BFileTest

**/

const char* BFile::OPTICKS_USER_HOME_KEY = "OPTICKS_USER_HOME" ;
const char* BFile::OPTICKS_USER_TMP_KEY  = "TMP" ;

std::string BFile::ResolveKey( const char* _key )
{
    bool is_home = strcmp(_key, "HOME") == 0  ;
    bool has_home_override = SSys::getenvvar(OPTICKS_USER_HOME_KEY) != NULL ;
    const char* key = is_home && has_home_override ? OPTICKS_USER_HOME_KEY  : _key ;

    const char* envvar = SSys::getenvvar(key) ;
    std::string evalue ;

    if( IsAllowedEnvvar(key) )
    {
        if( envvar != NULL )
        {
            evalue = envvar ;
            LOG(verbose) << "replacing allowed envvar token " << key << " with value of the envvar " << evalue ;
        }
        else
        {
            evalue = UserTmpDir();
            LOG(LEVEL) << "replacing allowed envvar token " << key << " with default value " << evalue << " as envvar not defined " ;
        }
    }
    else if(strcmp(key,"KEYDIR")==0 )
    {
        const char* idpath = BResource::GetDir("idpath") ;
        assert( idpath );
        evalue = idpath ;
        LOG(error) << "replacing $IDPATH with " << evalue ;
    }
    else if(strcmp(key,"PREFIX")==0 )
    {
        const char* prefix = BResource::GetDir("install_prefix") ;
        assert( prefix );
        evalue = prefix ;
        LOG(LEVEL) << "replacing $PREFIX with " << evalue ;
    }
    else if(strcmp(key,"DATADIR")==0 )
    {
        const char* datadir = BResource::GetDir("opticksdata_dir") ;
        assert( datadir );
        evalue = datadir ;
        LOG(error) << "replacing $DATADIR with " << evalue ;
    }
    else if(strcmp(key,"OPTICKS_EVENT_BASE")==0)
    {
        const char* evtbase = BResource::GetDir("evtbase") ;   // <-- from BOpticksResource
        if( evtbase != NULL )
        {
            evalue = evtbase ;
        }
        else if( envvar )   // if no internal BResource set allow use of external envvar
        {
            evalue = envvar ;
        }
        else
        {
            evalue = UserTmpDir();
        }
        LOG(LEVEL)
           << "replacing $OPTICKS_EVENT_BASE  "
           << " evalue " << evalue
           << " evtbase " << evtbase
           ;

       if(evalue.empty()) LOG(fatal) << "Failed to resolve OPTICKS_EVENT_BASE " ;
       assert(!evalue.empty());

    }
    else if(strcmp(key,"INSTALLCACHE_DIR")==0)
    {
        const char* installcache_dir = BResource::GetDir("installcache_dir") ;
        evalue = installcache_dir ;
    }
    else
    {
        evalue = key ;
    }
    return evalue ;
}

std::string BFile::CWD()
{
    fs::path p = fs::current_path();
    return p.string();
}

const char* BFile::CurrentDirectoryName()
{
    fs::path p = fs::current_path();
    fs::path s = p.stem();

    std::string cdn = s.string();
    return strdup(cdn.c_str());
}

std::string BFile::Absolute(const char* rela, const char* relb, const char* relc)
{
    fs::path r(rela);
    if(relb) r /= relb ;
    if(relc) r /= relc ;

    const fs::path base = fs::current_path() ;
    fs::path a = fs::absolute(r, base);
    return a.string();
}

std::string BFile::AbsoluteCanonical(const char* relpath)
{
    fs::path r(relpath);
    const fs::path base = fs::current_path() ;
    fs::path a = fs::absolute(r, base);

    try
    {
        a = fs::canonical(a);
    }
    catch( fs::filesystem_error& e  )
    {
        LOG(error) << e.what() ;
    }

    return a.string();
}

std::string BFile::expandhome(const char* s)
{
    assert(strcmp(s,"~")==0);
#ifdef _WIN32
    const char* home = "$USERPROFILE" ;
#else
    const char* home = "$HOME" ;
#endif
    return expandvar(home);
}

std::string BFile::Stem(const char* path)
{
    fs::path fsp(path);
    std::string stem = fsp.stem().string() ;
    return stem ;
}

std::string BFile::Name(const char* path)
{
    fs::path fsp(path);
    std::string name = fsp.filename().string() ;
    return name ;
}

std::string BFile::ParentDir(const char* path)
{
    fs::path fsp(path);
    std::string fold = fsp.parent_path().string() ;
    return fold ;
}

std::string BFile::ParentParentDir(const char* path)
{
    fs::path fsp(path);
    std::string fold = fsp.parent_path().parent_path().string() ;
    return fold ;
}

std::string BFile::ParentName(const char* path)
{
    if(!path) return "" ;
    fs::path fsp(path);
    std::string fold = fsp.parent_path().string() ;
    std::string name = BFile::Name(fold.c_str());
    return name ;
}

bool BFile::ExistsDir(const char* path, const char* sub, const char* name)
{
    std::string p = FormPath(path, sub, name) ;
    assert(!p.empty());
    fs::path fsp(p);
    return fs::exists(fsp) && fs::is_directory(fsp) ;
}
bool BFile::ExistsNativeDir(const std::string& native)
{
    fs::path fsp(native);
    return fs::exists(fsp) && fs::is_directory(fsp) ;
}

std::time_t* BFile::LastWriteTime(const char* path, const char* sub, const char* name)
{
    std::string psn = FormPath(path, sub, name) ;

    std::time_t* t = NULL ;
    fs::path p(psn);
    if(fs::exists(p))
    {
        t = new std::time_t( boost::filesystem::last_write_time( p ) );
    }
    return t ;
}

std::time_t* BFile::SinceLastWriteTime(const char* path, const char* sub, const char* name)
{
    std::time_t age(0) ;
    std::time_t* lwt = BFile::LastWriteTime(path, sub, name);
    if(lwt)
    {
        std::time_t  now = std::time(NULL) ;
        age = (now - *lwt);
    }
    return new std::time_t(age) ;
}

bool BFile::LooksLikePath(const char* path)
{
    if(!path) return false ;
    if(strlen(path) < 2) return false ;
    return path[0] == '/' || path[0] == '$' ;
}

bool BFile::ExistsFile(const char* path, const char* sub, const char* name)
{
    std::string p = FormPath(path, sub, name) ;

    if(p.empty())
    {
        LOG(error) << "BFile::ExistsFile BAD PATH"
                     << " path " << ( path ? path : "NULL" )
                     << " sub " << ( sub ? sub : "NULL" )
                     << " name " << ( name ? name : "NULL" )
                     ;
         return false ;
    }

    //assert(!p.empty());
    fs::path fsp(p);
    return fs::exists(fsp) && fs::is_regular_file(fsp) ;
}
bool BFile::ExistsNativeFile(const std::string& native)
{
    fs::path fsp(native);
    return fs::exists(fsp) && fs::is_regular_file(fsp) ;
}

std::string BFile::FindFile(const char* dirlist, const char* sub, const char* name, const char* dirlist_delim)
{
    std::vector<std::string> dirs ;
    boost::split(dirs,dirlist,boost::is_any_of(dirlist_delim));

    LOG(info) << "BFile::FindFile"
              << " dirlist " << dirlist
              << " sub " << sub
              << " name " << name
              << " dirlist_delim " << dirlist_delim
              << " elems " << dirs.size()
              ;

    std::string path ;
    for(unsigned int i=0 ; i < dirs.size() ; i++)
    {
        std::string candidate = BFile::FormPath(dirs[i].c_str(), sub, name );
        if(BFile::ExistsNativeFile(candidate))
        {
            path = candidate ;
            break ;
        }
    }
    return path ;
}

std::string BFile::FormPath(const std::vector<std::string>& elem, unsigned i0, unsigned i1)
{
   assert( i0 < elem.size() );
   assert( i1 <= elem.size() );

   fs::path p ;
   for(unsigned i=i0 ; i < i1 ; i++)
   {
       if(!elem[i].empty()) p /= elem[i]  ;
   }
   p.make_preferred();
   std::string preferred = p.string();  // platform native
   return preferred ;
}

std::string BFile::FormRelativePath(const char* a, const char* b, const char* c, const char* d, const char* e, const char* f)
{
    fs::path pp ;

    if(a) pp /= a ;
    if(b) pp /= b ;
    if(c) pp /= c ;
    if(d) pp /= d ;
    if(e) pp /= e ;
    if(f) pp /= f ;

    return pp.string();  // platform native
}

std::string BFile::FormPath(const char* path, const char* sub, const char* name, const char* extra1, const char* extra2)
{

   bool prepare = false ;
   LOG(LEVEL)
       << " path " << path
       << " sub " << sub
       << " name " << name
       << " prepare " << prepare
       ;

/*
   std::string empty ;
   if(!path)
   {
       LOG(debug) << "BFile::FormPath return empty "
                  << " path " << ( path ? path : "NULL" )
                  << " sub " << ( sub ? sub : "NULL" )
                  << " name " << ( name ? name : "NULL" )
                  ;
       return empty ;
   }

   if(strlen(path)<1)
   {
       LOG(debug) << "BFile::FormPath return empty "
                  << " strlen(path) " << strlen(path)
                  << " path " << ( path ? path : "NULL" )
                  << " sub " << ( sub ? sub : "NULL" )
                  << " name " << ( name ? name : "NULL" )
                  ;
       return empty ;
   }
*/

#ifdef MSC_VER_
    // hmm this is getting called on every path, so restrict to windows where its needed
   if(!OPTICKS_PATH_PREFIX)
   {
      setOpticksPathPrefixFromEnv();
   }
#endif

   fs::path p ;

   bool dbg(false);
   std::string xpath ;

   if(path != nullptr)
   {
       if(path[0] == '$')
       {
           LOG(LEVEL) << "expandvar for dollar " << path ;
           xpath.assign(expandvar(path));
       }
       else if(path[0] == '~')
       {
           xpath.assign(expandhome(path));
       }
       else if(OPTICKS_PATH_PREFIX)
       {
           //  eg windows prefix C:\msys64
           if(strlen(path) > 1 && path[1] == ':')
           {
               if(dbg) std::cerr << "BFile::FormPath path is already prefixed " << path << std::endl ;
           }
           else
           {
               p /= OPTICKS_PATH_PREFIX ;
           }
       }
       p /= xpath.empty() ? path : xpath ;
   }

   if(sub)   p /= sub ;
   if(name)  p /= name ;
   if(extra1)  p /= extra1 ;
   if(extra2)  p /= extra2 ;

   p.make_preferred();

   std::string preferred = p.string();  // platform native

   if(prepare)
   {
       preparePath(preferred.c_str(), true);
   }

   std::string check ;
   //check="/tmp/blyth/opticks/evt/dayabay/machinery/1/gs.npy" ;

   if(!check.empty() && check.compare(preferred) == 0)
   {
       LOG(fatal) << "forming a checked path " << preferred ;
       assert(0);
   }

   return preferred ;
}

void BFile::CreateFile(const char* path, const char* sub, const char* name)
{
    std::string p = FormPath(path, sub, name) ;
    assert(!p.empty());

    if( p.size() < 2 )
    {
        LOG(error) << " path sanity check fail " << p  ;
        return ;
    }

    preparePath( p.c_str() ) ;

    fs::path fsp(p);
    if( fs::exists(fsp) )
    {
        LOG(error) << " path exists already " << p ;
        return ;
    }

    std::ofstream ofs(p.c_str()) ;
    //fs::ofstream ofs{fsp};

}

void BFile::RemoveFile(const char* path, const char* sub, const char* name)
{
    std::string p = FormPath(path, sub, name) ;
    assert(!p.empty());

    if( p.size() < 2 )
    {
        LOG(error) << " path sanity check fail " << p  ;
        return ;
    }

    fs::path fsp(p);

    if( !fs::exists(fsp) )
    {
        LOG(error) << " path does not exist " << p ;
        return ;
    }
    if( !fs::is_regular_file(fsp) )
    {
        LOG(error) << " path is not a regular file " << p  ;
        return ;
    }

    bool ret = fs::remove(fsp) ;
    assert( ret == true );
}

void BFile::RemoveDir(const char* path, const char* sub, const char* name)
{
    std::string p = FormPath(path, sub, name) ;
    assert(!p.empty());
    fs::path dir(p);
    bool exists = fs::exists(dir) ;

    if(!exists)
    {
         std::cout << "BFile::RemoveDir"
                   << " path does not exist " << p
                   << std::endl
                  ;
    }
    else
    {
         LOG(debug) << "BFile::RemoveDir"
                   << " deleting path " << p
                  ;
        unsigned long long nrm = fs::remove_all(dir);
        LOG(debug) << "BFile::RemoveDir removed " << nrm ;
    }
}

std::string BFile::CreateDir(const char* base, const char* asub, const char* bsub)
{
    std::string ppath = FormPath(base, asub, bsub) ;

    LOG(LEVEL)
        << " base " << ( base ? base : "NULL" )
        << " asub " << ( asub ? asub : "NULL" )
        << " bsub " << ( bsub ? bsub : "NULL" )
        << " ppath [" << ppath << "]"
        ;

    if(!ppath.empty())
    {
        fs::path dir(ppath);
        bool exists = fs::exists(dir) ;
        if(!exists && fs::create_directories(dir))
        {
            LOG(LEVEL) << " created ppath [" << ppath << "]" ;
        }
    }
    return ppath ;
}

std::string BFile::preparePath(const char* dir_, const char* reldir_, const char* name, bool create )
{
    fs::path fpath(dir_);
    if(reldir_) fpath /= reldir_ ;
    std::string p = preparePath(fpath.string().c_str(), name, create);
    LOG(LEVEL) << " dir_ " << dir_ << " reldir_ " << reldir_ << " name " << name << " create " << create << " -> " << p ;
    return p ;
}

std::string BFile::preparePath(const char* path_, bool create )
{
    std::string dir = BFile::ParentDir(path_);
    std::string name = BFile::Name(path_);
    return preparePath(dir.c_str(), name.c_str(), create);
}

std::string BFile::preparePath(const char* dir_, const char* name, bool create )
{

    std::string dir = BFile::FormPath(dir_) ;
    fs::path fdir(dir.c_str());
    if(!fs::exists(fdir) && create)
    {
        if (fs::create_directories(fdir))
        {
            LOG(info)<< "created directory " << dir ;
        }
    }
    if(fs::exists(fdir) && fs::is_directory(fdir))
    {
        fs::path fpath(dir);
        fpath /= name ;
        return fpath.string();
    }
    else
    {
        LOG(error)
            << " FAILED "
            << " dir " << dir
            << " dir_ " << dir_
            << " name " << name
            ;
         //std::raise(SIGINT);
    }
    std::string empty ;
    return empty ;
}

std::string BFile::prefixShorten( const char* path, const char* prefix_)
{
    std::string prefix = BFile::FormPath(prefix_);
    if(strncmp(path, prefix.c_str(), strlen(prefix.c_str()))==0)
        return path + strlen(prefix.c_str()) ;
    else
        return path  ;
}

std::string BFile::ChangeExt( const char* path, const char* ext)
{
    std::string dir = BFile::ParentDir(path);
    std::string name = BFile::Stem(path);
    name += ext ;
    return FormPath(dir.c_str(), name.c_str());
}

bool BFile::pathEndsWithInt(const char* path)
{
    if(!path) return false ;
    int fallback = -666 ;
    std::string name = BFile::Name(path) ;
    int check = BStr::atoi(name.c_str(), fallback);
    return check != fallback ;
}

void BFile::SplitPath(std::vector<std::string>& elem, const char* path )
{
    fs::path fpath(path);
    for (fs::path::iterator it=fpath.begin() ; it != fpath.end(); ++it)
    {
        std::string s = it->filename().string() ;
        elem.push_back(s);
    }
}

std::size_t BFile::FileSize( const char* path_ )
{
    std::string path = BFile::FormPath(path_ );
    const char* xpath = path.c_str() ;
    fs::path fsp(xpath);
    bool exists = fs::exists(fsp) && fs::is_regular_file(fsp) ;
    return exists ? fs::file_size(fsp) : 0 ;
}

const char* BFile::ResolveScript(const char* script_name, const char* fallback_directory)
{
    std::string path = SSys::Which(script_name);
    if(path.empty() && fallback_directory != NULL)
    {
        path = FormPath(fallback_directory, script_name);
    }
    return path.empty() ? NULL : strdup(path.c_str()) ;
}

std::string BFile::MakeIndex(int index, unsigned width) // static
{
    std::stringstream ss ;
    ss
        << std::setw(width)
        << std::setfill('0')
        << index
        ;
    return ss.str();
}

/**
BFile::MakeName
-----------------

Using index -1 returns a format string of form "%0.5d" rather than  the index

**/

std::string BFile::MakeName(int index, unsigned width, const char* prefix, const char* ext ) // static
{
    std::stringstream ff ;
    ff << "%0." << width << "d" ;
    std::string fmt = ff.str();

    std::stringstream ss ;
    ss
       << prefix
       << ( index == -1 ? fmt : MakeIndex(index, width) )
       << ext
       ;

    return ss.str();
}

ext: cc

max_stars_repo_name: hanswenzel/opticks

max_issues_repo_name: hanswenzel/opticks

max_forks_repo_name: hanswenzel/opticks


text:
#pragma once
#include "lcv.hpp"
#include "lubee/src/meta/check_macro.hpp"
#include "lubee/src/meta/enable_if.hpp"
#include "lubee/src/error.hpp"
#include <boost/preprocessor.hpp>

namespace rev {
	DEF_HASMETHOD(LuaExport)
	class LuaState;
	// LuaExportというメソッドを持っていればそれを呼ぶ
	template <class T, ENABLE_IF(HasMethod_LuaExport_t<T>{})>
	void CallLuaExport(LuaState& lsc) {
		T::LuaExport(lsc);
	}
	template <class T, ENABLE_IF(!HasMethod_LuaExport_t<T>{})>
	void CallLuaExport(LuaState&) {}
}
#define DEF_LUAIMPLEMENT_DERIVED(clazz, base) \
	namespace rev { \
		namespace lua { \
			template <> \
			const char* LuaName(clazz*) { return LuaName((base*)nullptr); } \
			template <> \
			void LuaExport(LuaState& lsc, clazz*) { LuaExport(lsc, (base*)nullptr); } \
		} \
	}

namespace rev {
	template <class Dummy_t, class C, class... Ts>
	int MakeObject(lua_State* ls) {
		FuncCall<Ts...>::callCB(
			[ls](auto&&... args) {
				LCV<C>()(ls, C{std::forward<decltype(args)>(args)...});
			},
			ls,
			static_cast<int>(-sizeof...(Ts))
		);
		return 1;
	}
	template <class Dummy_t, class C, class... Ts>
	int MakeSPObject(lua_State* ls) {
		auto obj = FuncCall<Ts...>::callCB(
			[](auto&&... args) {
				return std::make_shared<C>(std::forward<decltype(args)>(args)...);
			},
			ls,
			static_cast<int>(-sizeof...(Ts))
		);
		LCV<decltype(obj)>()(ls, obj);
		return 1;
	}
	//! (LuaのClassT::New()から呼ばれる)オブジェクトのリソースハンドルを作成
	template <class Mgr_t, class TF, class... Ts>
	int MakeHandle(lua_State* ls) {
		auto obj = FuncCall<Ts...>::callCB(
			[](auto&&... args) {
				return Mgr_t::ref().template emplace<TF>(std::forward<decltype(args)>(args)...);
			},
			ls,
			static_cast<int>(-sizeof...(Ts))
		);
		LCV<decltype(obj)>()(ls, obj);
		return 1;
	}
	//! デフォルトコンストラクタを持たないオブジェクト用
	template <class... Ts>
	int MakeFake(lua_State* /*ls*/) {
		AssertF("no constructor defined.(can't construct this type of object)");
		return 0;
	}
}

namespace rev {
	namespace lua {
		struct Dummy {};
		template <
			class C, class Getter,
			class CBSMethod, class CBMember, class CBMethod, class Maker
		>
		void LuaExportImpl(LuaState& lsc,
				const char* class_name,
				const std::string& base_name,
				const CBSMethod& cbSMethod,
				const CBMember& cbMember,
				const CBMethod& cbMethod,
				Maker* maker
		) {
			// 既に登録されていたら警告だけ表示して何もせずリターン
			if(LuaImport::IsObjectRegistered(lsc, class_name)) {
				Expect(false, "class \"%s\" is already registered", class_name);
				return;
			}

			LuaImport::BeginImportBlock(class_name);
			lsc.getGlobal(::rev::luaNS::DefineObject);
			lsc.getGlobal(base_name);
			lsc.push(class_name);
			lsc.newTable();
			lsc.push(false);
			lsc.call(4,1);

			lsc.push(::rev::luaNS::objBase::New);
			lsc.push(maker);
			lsc.rawSet(-3);

			lsc.setField(-1, luaNS::objBase::Name, class_name);

			LuaImport::BeginImportBlock("StaticFunctions");
			cbSMethod();
			LuaImport::EndImportBlock();

			LuaImport::BeginImportBlock("Values");
			lsc.rawGetField(-1, ::rev::luaNS::objBase::ValueR);
			lsc.rawGetField(-2, ::rev::luaNS::objBase::ValueW);
			cbMember();
			lsc.pop(2);
			LuaImport::EndImportBlock();

			LuaImport::BeginImportBlock("Functions");
			lsc.rawGetField(-1, ::rev::luaNS::objBase::Func);
			cbMethod();
			lsc.pop(1);
			LuaImport::EndImportBlock();

			CallLuaExport<C>(lsc);
			lsc.setGlobal(class_name);

			LuaImport::EndImportBlock();
		}
	}
}

// param = (clazz)(class_raw)
#define DEF_LUAMEMBER(n, param, elem) LuaImport::RegisterMember<::rev::LI_GetPtr<BOOST_PP_SEQ_ELEM(0,param)>, BOOST_PP_SEQ_ELEM(0,param)>(lsc, BOOST_PP_STRINGIZE(elem), &BOOST_PP_SEQ_ELEM(0,param)::elem);
#define DEF_LUASMEMBER(n, clazz, elem) LuaImport::RegisterStatic(lsc, BOOST_PP_STRINGIZE(elem), &clazz::elem);
#define DEF_LUAIMPLEMENT_IMPL(mgr, clazz, class_raw, base, seq_smethod, seq_member, seq_method, seq_ctor, makeobj) \
namespace rev { \
	namespace lua { \
		template <> \
		const char* LuaName(clazz*) { return #class_raw; } \
		template <> \
		void LuaExport(LuaState& lsc, clazz*) { \
			LuaExportImpl<clazz, ::rev::LI_GetPtr<clazz>>( \
				lsc, \
				#class_raw, \
				base, \
				[&](){ BOOST_PP_SEQ_FOR_EACH(DEF_LUASMEMBER, clazz, seq_smethod) }, \
				[&](){ BOOST_PP_SEQ_FOR_EACH(DEF_LUAMEMBER, (clazz)(class_raw), seq_member) }, \
				[&](){ BOOST_PP_SEQ_FOR_EACH(DEF_LUAMEMBER, (clazz)(class_raw), seq_method) }, \
				&makeobj<BOOST_PP_SEQ_ENUM((mgr)(clazz) seq_ctor)> \
			); \
		} \
	} \
}

#define LUAIMPLEMENT_BASE luaNS::Object
#define DEF_LUAIMPLEMENT_SPTR(mgr, clazz, class_raw, base, seq_smethod, seq_member, seq_method, seq_ctor) \
	DEF_LUAIMPLEMENT_IMPL(mgr, clazz, class_raw, base, seq_smethod, seq_member, seq_method, seq_ctor, ::rev::MakeHandle)
#define DEF_LUAIMPLEMENT_SPOBJ(clazz, class_raw, base, seq_smethod, seq_member, seq_method, seq_ctor) \
	DEF_LUAIMPLEMENT_IMPL(::rev::lua::Dummy, clazz, class_raw, base, seq_smethod, seq_member, seq_method, seq_ctor, ::rev::MakeSPObject)
#define DEF_LUAIMPLEMENT_PTR(clazz, class_raw, base, seq_smethod, seq_member, seq_method, seq_ctor) \
	DEF_LUAIMPLEMENT_IMPL(::rev::lua::Dummy, clazz, class_raw, base, seq_smethod, seq_member, seq_method, seq_ctor, ::rev::MakeObject)
#define DEF_LUAIMPLEMENT_PTR_NOCTOR(clazz, class_raw, base, seq_smethod, seq_member, seq_method) \
	DEF_LUAIMPLEMENT_IMPL(::rev::lua::Dummy, clazz, class_raw, base, seq_smethod, seq_member, seq_method, NOTHING, ::rev::MakeFake)

ext: hpp

max_stars_repo_name: degarashi/revenant

max_issues_repo_name: degarashi/revenant

max_forks_repo_name: degarashi/revenant


text:
//
//  SendFundsFormSubmissionController.cpp
//  MyMonero
//
//  Copyright (c) 2014-2019, MyMonero.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:
//
//  1. Redistributions of source code must retain the above copyright notice, this list of
//	conditions and the following disclaimer.
//
//  2. 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.
//
//  3. Neither the name of the copyright holder 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
//  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
//  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
//  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//
#include "SendFundsFormSubmissionController.hpp"
#include <iostream>
#include "../../OpenAlias/OpenAlias.hpp"
#include "wallet_errors.h"
#include "monero_address_utils.hpp"
#include "monero_paymentID_utils.hpp"
#include "monero_send_routine.hpp"
using namespace monero_send_routine;
using namespace monero_transfer_utils;
using namespace SendFunds;

#include <boost/optional/optional_io.hpp>
//
// Imperatives - Initialization
void FormSubmissionController::set__get_unspent_outs_fn(std::function<void(LightwalletAPI_Req_GetUnspentOuts req_params)> fn)
{
	this->get_unspent_outs = fn;
}
void FormSubmissionController::set__get_random_outs_fn(std::function<void(LightwalletAPI_Req_GetRandomOuts req_params)> fn)
{
	this->get_random_outs = fn;
}
void FormSubmissionController::set__submit_raw_tx_fn(std::function<void(LightwalletAPI_Req_SubmitRawTx req_params)> fn)
{
	this->submit_raw_tx = fn;
}
void FormSubmissionController::set__authenticate_fn(std::function<void(void)> fn)
{
	this->authenticate_fn = fn;
}
//
// Imperatives - Controller
void FormSubmissionController::handle()
{
	using namespace std;
	using namespace boost;
	//
	THROW_WALLET_EXCEPTION_IF(this->valsState != WAIT_FOR_HANDLE, error::wallet_internal_error, "Expected valsState of WAIT_FOR_HANDLE");
	this->valsState = WAIT_FOR_STEP1;
	//
	if (this->parameters.fromWallet_didFailToInitialize) {
		this->parameters.failure_fn(unableToLoadWallet, boost::none, boost::none, boost::none, boost::none);
		return;
	}
	if (this->parameters.fromWallet_didFailToBoot) {
		this->parameters.failure_fn(unableToLogIntoWallet, boost::none, boost::none, boost::none, boost::none);
		return;
	}
	if (this->parameters.fromWallet_needsImport) {
		this->parameters.failure_fn(walletMustBeImported, boost::none, boost::none, boost::none, boost::none);
		return;
	}
	if (this->parameters.is_sweeping) {
		this->sending_amount = 0;
	} else {
		if (this->parameters.send_amount_double_string == boost::none || this->parameters.send_amount_double_string->empty()) {
			if (this->parameters.is_sweeping == false) {
				this->parameters.failure_fn(amountTooLow, boost::none, boost::none, boost::none, boost::none);
				return;
			}
		}
		bool ok = cryptonote::parse_amount(this->sending_amount, this->parameters.send_amount_double_string.get());
		if (!ok) {
			this->parameters.failure_fn(cannotParseAmount, boost::none, boost::none, boost::none, boost::none);
			return;
		}
		if (this->sending_amount <= 0) {
			this->parameters.failure_fn(amountTooLow, boost::none, boost::none, boost::none, boost::none);
			return;
		}
	}
	bool enteredAddressValue_exists = this->parameters.enteredAddressValue != boost::none && !this->parameters.enteredAddressValue->empty(); // it will be valid if it exists
//	let notPickedContactBut_enteredAddressValue = !hasPickedAContact && enteredAddressValue_exists ? true : false
	//
	bool resolvedAddress_exists = this->parameters.resolvedAddress != boost::none && !this->parameters.resolvedAddress->empty(); // NOTE: it might be hidden, though!
	//
	bool resolvedPaymentID_exists = this->parameters.resolvedPaymentID != boost::none && !this->parameters.resolvedPaymentID->empty(); // NOTE: it might be hidden, though!
	if (this->parameters.resolvedPaymentID_fieldIsVisible) {
		THROW_WALLET_EXCEPTION_IF(!resolvedPaymentID_exists, error::wallet_internal_error, "Expected resolvedPaymentID_exists && resolvedPaymentID_fieldIsVisible");
	}
	bool manuallyEnteredPaymentID_exists = this->parameters.manuallyEnteredPaymentID != boost::none && !this->parameters.manuallyEnteredPaymentID->empty();
	bool canUseManualPaymentID = manuallyEnteredPaymentID_exists
		&& this->parameters.manuallyEnteredPaymentID_fieldIsVisible
		&& !this->parameters.resolvedPaymentID_fieldIsVisible; // but not if we have a resolved one!
	if (canUseManualPaymentID && this->parameters.hasPickedAContact) {
		cerr << "canUseManualPaymentID shouldn't be true at same time as hasPickedAContact" << endl;
		// NOTE: That this is an exception will also be the case even if we are using the payment ID from a Funds Request QR code / URI because we set the request URI as a 'resolved' / "detected" payment id. So the `hasPickedAContact` usage above yields slight ambiguity in code and could be improved to encompass request uri pid "forcing"
		this->parameters.failure_fn(codeFault_manualPaymentID_while_hasPickedAContact, boost::none, boost::none, boost::none, boost::none);
		return;
	}
	optional<string> xmrAddress_toDecode = boost::none; // just initializing it to none here; may be integrated
	optional<string> paymentID_toUseOrToNilIfIntegrated = boost::none; // may be nil
	if (this->parameters.hasPickedAContact) { // we have already re-resolved the payment_id
		if (this->parameters.contact_payment_id != boost::none) {
			paymentID_toUseOrToNilIfIntegrated = this->parameters.contact_payment_id.get();
		}
		THROW_WALLET_EXCEPTION_IF(this->parameters.contact_hasOpenAliasAddress == boost::none, error::wallet_internal_error, "Expected non-none parameters.contact_hasOpenAliasAddress");
		if (*(this->parameters.contact_hasOpenAliasAddress) == true) {
			if (this->parameters.cached_OAResolved_address == boost::none || this->parameters.cached_OAResolved_address->empty()) { // ensure address indeed was able to be resolved - but UI should preclude it not having been by here
				cerr << "Code fault? Unable to find a recipient address for this transfer." << endl;
				this->parameters.failure_fn(codeFault_unableToFindResolvedAddrOnOAContact, boost::none, boost::none, boost::none, boost::none);
				return;
			}
			xmrAddress_toDecode = this->parameters.cached_OAResolved_address.get(); // We can just use the cached_OAResolved_XMR_address because in order to have picked this contact and for the user to hit send, we'd need to have gone through an OA resolve (_didPickContact)
		} else {
			xmrAddress_toDecode = this->parameters.contact_address.get();
		}
	} else {
		if (enteredAddressValue_exists == false) {
			this->parameters.failure_fn(pleaseSpecifyRecipient, boost::none, boost::none, boost::none, boost::none);
			return;
		}
		// address input via text input…
		bool is_enteredAddressValue_OAAddress = OpenAlias::looksLikeOpenAliasAndNotCryptoCurrencyAddress(this->parameters.enteredAddressValue.get());
		if (is_enteredAddressValue_OAAddress) {
			if (this->parameters.resolvedAddress_fieldIsVisible == false || resolvedAddress_exists == false) {
				this->parameters.failure_fn(couldntResolveThisOAAddress, boost::none, boost::none, boost::none, boost::none);
				return;
			}
			xmrAddress_toDecode = this->parameters.resolvedAddress.get();
			if (this->parameters.resolvedPaymentID_fieldIsVisible) {
				paymentID_toUseOrToNilIfIntegrated = this->parameters.resolvedPaymentID.get();
			} else if (canUseManualPaymentID) {
				paymentID_toUseOrToNilIfIntegrated = this->parameters.manuallyEnteredPaymentID.get();
			} else {
				paymentID_toUseOrToNilIfIntegrated = boost::none;
			}
		} else { // then it's an XMR address
			xmrAddress_toDecode = this->parameters.enteredAddressValue.get();
			// we don't care whether it's an integrated address or not here since we're not going to use its payment id
			if (canUseManualPaymentID) {
				if (this->parameters.resolvedPaymentID_fieldIsVisible) {
					cerr << "Code fault? Detected payment ID unexpectedly visible while manual input field visible." << endl;
					this->parameters.failure_fn(codeFault_detectedPIDVisibleWhileManualInputVisible, boost::none, boost::none, boost::none, boost::none);
					return;
				}
				paymentID_toUseOrToNilIfIntegrated = this->parameters.manuallyEnteredPaymentID.get();
			} else if (this->parameters.resolvedPaymentID_fieldIsVisible) {
				paymentID_toUseOrToNilIfIntegrated = this->parameters.resolvedPaymentID.get();
			}
		}
	}
	THROW_WALLET_EXCEPTION_IF(xmrAddress_toDecode == boost::none, error::wallet_internal_error, "Expected xmrAddress_toDecode");
	//
	auto decode_retVals = monero::address_utils::decodedAddress(xmrAddress_toDecode.get(), this->parameters.nettype);
	if (decode_retVals.did_error) {
		this->parameters.failure_fn(couldntValidateDestAddress, boost::none, boost::none, boost::none, boost::none);
		return;
	}
	if (decode_retVals.paymentID_string != boost::none) { // is integrated address!
		this->to_address_string = *xmrAddress_toDecode; // for integrated addrs, we don't want to extract the payment id and then use the integrated addr as well (TODO: unless we use fluffy's patch?)
		this->payment_id_string = boost::none;
		this->isXMRAddressIntegrated = true;
		this->integratedAddressPIDForDisplay = *decode_retVals.paymentID_string;
		this->_proceedTo_authOrSendTransaction();
		return;
	}
	// since we may have a payment ID here (which may also have been entered manually), validate
	if (monero_paymentID_utils::is_a_valid_or_not_a_payment_id(paymentID_toUseOrToNilIfIntegrated) == false) { // convenience function - will be true if nil pid
		this->parameters.failure_fn(enterValidPID, boost::none, boost::none, boost::none, boost::none);
		return;
	}
	if (paymentID_toUseOrToNilIfIntegrated != boost::none && paymentID_toUseOrToNilIfIntegrated->empty() == false) { // short pid / integrated address coersion
		if (decode_retVals.isSubaddress != true) { // this is critical or funds will be lost!!
			if (paymentID_toUseOrToNilIfIntegrated->size() == monero_paymentID_utils::payment_id_length__short) { // a short one
				THROW_WALLET_EXCEPTION_IF(decode_retVals.isSubaddress, error::wallet_internal_error, "Expected !decode_retVals.isSubaddress"); // just an extra safety measure
				optional<string> fabricated_integratedAddress_orNone = monero::address_utils::new_integratedAddrFromStdAddr( // construct integrated address
					*xmrAddress_toDecode, // the monero one
					*paymentID_toUseOrToNilIfIntegrated, // short pid
					this->parameters.nettype
				);
				if (fabricated_integratedAddress_orNone == boost::none) {
					this->parameters.failure_fn(couldntConstructIntAddrWithShortPid, boost::none, boost::none, boost::none, boost::none);
					return;
				}
				this->to_address_string = *fabricated_integratedAddress_orNone;
				this->payment_id_string = boost::none; // must now zero this or Send will throw a "pid must be blank with integrated addr"
				this->isXMRAddressIntegrated = true;
				this->integratedAddressPIDForDisplay = *paymentID_toUseOrToNilIfIntegrated; // a short pid
				this->_proceedTo_authOrSendTransaction();
				return; // return early to prevent fall-through to non-short or zero pid case
			}
		}
	}
	this->to_address_string = *xmrAddress_toDecode; // therefore, non-integrated normal XMR address
	this->payment_id_string = paymentID_toUseOrToNilIfIntegrated; // may still be nil
	this->isXMRAddressIntegrated = false;
	this->integratedAddressPIDForDisplay = boost::none;
	this->_proceedTo_authOrSendTransaction();
}
void FormSubmissionController::_proceedTo_authOrSendTransaction()
{
	if (this->parameters.requireAuthentication) {
		this->authenticate_fn(); // this will wait for authentication
	} else {
		this->_proceedTo_generateSendTransaction();
	}
}
void FormSubmissionController::_proceedTo_generateSendTransaction()
{
	// verify we were left with a good state
	THROW_WALLET_EXCEPTION_IF(this->to_address_string.size() == 0, error::wallet_internal_error, "Expected non-zero this->to_address_string");
	//
	this->parameters.preSuccess_passedValidation_willBeginSending();
	this->parameters.preSuccess_nonTerminal_validationMessageUpdate_fn(initiatingSend); // start with just prefix
	// ^--- TODO: this may be unnecessary .. there is no longer such a delay
	this->parameters.preSuccess_nonTerminal_validationMessageUpdate_fn(fetchingLatestBalance);
	auto req_params = new__req_params__get_unspent_outs(
		this->parameters.from_address_string,
		this->parameters.sec_viewKey_string
	);
	this->get_unspent_outs(req_params); // wait for cb I
}
void FormSubmissionController::cb__authentication(bool did_pass)
{
	if (did_pass == false) {
		this->parameters.canceled_fn();
		return;
	}
	this->_proceedTo_generateSendTransaction();
}
void FormSubmissionController::cb_I__got_unspent_outs(optional<string> err_msg, const optional<property_tree::ptree> &res)
{
	if (err_msg != boost::none && err_msg->empty() == false) {
		this->parameters.failure_fn(errInServerResponse_withMsg, err_msg.get(), boost::none, boost::none, boost::none);
		return;
	}
	crypto::secret_key sec_viewKey{};
	crypto::secret_key sec_spendKey{};
	crypto::public_key pub_spendKey{};
	{
		bool r = false;
		r = epee::string_tools::hex_to_pod(this->parameters.sec_viewKey_string, sec_viewKey);
		if (!r) {
			this->parameters.failure_fn(codeFault_invalidSecViewKey, boost::none, boost::none, boost::none, boost::none);
			return;
		}
		r = epee::string_tools::hex_to_pod(this->parameters.sec_spendKey_string, sec_spendKey);
		if (!r) {
			this->parameters.failure_fn(codeFault_invalidSecSpendKey, boost::none, boost::none, boost::none, boost::none);
			return;
		}
		r = epee::string_tools::hex_to_pod(this->parameters.pub_spendKey_string, pub_spendKey);
		if (!r) {
			this->parameters.failure_fn(codeFault_invalidPubSpendKey, boost::none, boost::none, boost::none, boost::none);
			return;
		}
	}
	auto parsed_res = new__parsed_res__get_unspent_outs(
		res.get(),
		sec_viewKey,
		sec_spendKey,
		pub_spendKey
	);
	if (parsed_res.err_msg != boost::none) {
		this->parameters.failure_fn(msgProvided, std::move(*(parsed_res.err_msg)), boost::none, boost::none, boost::none);
		return;
	}
	this->unspent_outs = std::move(*(parsed_res.unspent_outs));
	this->fee_per_b = *(parsed_res.per_byte_fee);
	this->fee_mask = *(parsed_res.fee_mask);
	this->use_fork_rules = monero_fork_rules::make_use_fork_rules_fn(parsed_res.fork_version);
	//
	this->passedIn_attemptAt_fee = boost::none;
	this->constructionAttempt = 0;
	//
	_reenterable_construct_and_send_tx();
}
void FormSubmissionController::_reenterable_construct_and_send_tx()
{
	this->parameters.preSuccess_nonTerminal_validationMessageUpdate_fn(calculatingFee);
	//
	Send_Step1_RetVals step1_retVals;
	monero_transfer_utils::send_step1__prepare_params_for_get_decoys(
		step1_retVals,
		//
		this->payment_id_string,
		this->sending_amount,
		this->parameters.is_sweeping,
		this->parameters.priority,
		this->use_fork_rules,
		this->unspent_outs,
		this->fee_per_b,
		this->fee_mask,
		//
		this->passedIn_attemptAt_fee // use this for passing step2 "must-reconstruct" return values back in, i.e. re-entry; when none, defaults to attempt at network min
		// ^- and this will be 'none' as initial value
	);
	if (step1_retVals.errCode != noError) {
		this->parameters.failure_fn(createTransactionCode_balancesProvided, boost::none, step1_retVals.errCode, step1_retVals.spendable_balance, step1_retVals.required_balance);
		return;
	}
	THROW_WALLET_EXCEPTION_IF(this->valsState != WAIT_FOR_STEP1, error::wallet_internal_error, "Expected valsState of WAIT_FOR_STEP1"); // for addtl safety
	// now store step1_retVals for step2
	this->step1_retVals__final_total_wo_fee = step1_retVals.final_total_wo_fee;
	this->step1_retVals__using_fee = step1_retVals.using_fee;
	this->step1_retVals__change_amount = step1_retVals.change_amount;
	this->step1_retVals__mixin = step1_retVals.mixin;
	THROW_WALLET_EXCEPTION_IF(this->step1_retVals__using_outs.size() != 0, error::wallet_internal_error, "Expected 0 using_outs");
	this->step1_retVals__using_outs = std::move(step1_retVals.using_outs); // move structs from stack's vector to heap's vector
	this->valsState = WAIT_FOR_STEP2;
	//
	this->parameters.preSuccess_nonTerminal_validationMessageUpdate_fn(fetchingDecoyOutputs);
	//
	auto req_params = new__req_params__get_random_outs(this->step1_retVals__using_outs); // use the one on the heap, since we've moved the one from step1_retVals
	this->get_random_outs(req_params); // wait for cb II
}
void FormSubmissionController::cb_II__got_random_outs(
	optional<string> err_msg,
	const optional<property_tree::ptree> &res
) {
	if (err_msg != boost::none && err_msg->empty() == false) {
		this->parameters.failure_fn(errInServerResponse_withMsg, err_msg.get(), boost::none, boost::none, boost::none);
		return;
	}
	auto parsed_res = new__parsed_res__get_random_outs(res.get());
	if (parsed_res.err_msg != boost::none) {
		this->parameters.failure_fn(msgProvided, std::move(*(parsed_res.err_msg)), boost::none, boost::none, boost::none);
		return;
	}
	THROW_WALLET_EXCEPTION_IF(this->step1_retVals__using_outs.size() == 0, error::wallet_internal_error, "Expected non-0 using_outs");
	Send_Step2_RetVals step2_retVals;
	uint64_t unlock_time = 0; // hard-coded for now since we don't ever expose it, presently
	monero_transfer_utils::send_step2__try_create_transaction(
		step2_retVals,
		//
		this->parameters.from_address_string,
		this->parameters.sec_viewKey_string,
		this->parameters.sec_spendKey_string,
		this->to_address_string,
		this->payment_id_string,
		*(this->step1_retVals__final_total_wo_fee),
		*(this->step1_retVals__change_amount),
		*(this->step1_retVals__using_fee),
		this->parameters.priority,
		this->step1_retVals__using_outs,
		this->fee_per_b,
		this->fee_mask,
		*(parsed_res.mix_outs),
		this->use_fork_rules,
		unlock_time,
		this->parameters.nettype
	);
	if (step2_retVals.errCode != noError) {
		this->parameters.failure_fn(createTranasctionCode_noBalances, boost::none, step2_retVals.errCode, boost::none, boost::none);
		return;
	}
	if (step2_retVals.tx_must_be_reconstructed) {
		// this will update status back to .calculatingFee
		if (this->constructionAttempt > 15) { // just going to avoid an infinite loop here or particularly long stack
			this->parameters.failure_fn(exceededConstructionAttempts, boost::none, boost::none, boost::none, boost::none); // Unable to construct a transaction with sufficient fee for unknown reason
			return;
		}
		this->valsState = WAIT_FOR_STEP1; // must reset this
		//
		this->constructionAttempt += 1; // increment for re-entry
		this->passedIn_attemptAt_fee = step2_retVals.fee_actually_needed; // -> reconstruction attempt's step1's passedIn_attemptAt_fee
		// reset step1 vals for correctness: (otherwise we end up, for example, with duplicate outs added)
		this->step1_retVals__final_total_wo_fee = none;
		this->step1_retVals__change_amount = none;
		this->step1_retVals__using_fee = none;
		this->step1_retVals__mixin = none;
		this->step1_retVals__using_outs.clear(); // critical!
		// and let's reset step2 just for clarity/explicitness, though we don't expect them to have values yet:
		this->step2_retVals__signed_serialized_tx_string = boost::none;
		this->step2_retVals__tx_hash_string = boost::none;
		this->step2_retVals__tx_key_string = boost::none;
		this->step2_retVals__tx_pub_key_string = boost::none;
		//
		_reenterable_construct_and_send_tx();
		return;
	}
	THROW_WALLET_EXCEPTION_IF(this->valsState != WAIT_FOR_STEP2, error::wallet_internal_error, "Expected valsState of WAIT_FOR_STEP2"); // just for addtl safety
	// move step2 vals onto heap for later:
	this->step2_retVals__signed_serialized_tx_string = *(step2_retVals.signed_serialized_tx_string);
	this->step2_retVals__tx_hash_string = *(step2_retVals.tx_hash_string);
	this->step2_retVals__tx_key_string = *(step2_retVals.tx_key_string);
	this->step2_retVals__tx_pub_key_string = *(step2_retVals.tx_pub_key_string);
	//
	this->valsState = WAIT_FOR_FINISH;
	//
	this->parameters.preSuccess_nonTerminal_validationMessageUpdate_fn(submittingTransaction);
	//
	auto req_params = LightwalletAPI_Req_SubmitRawTx{
		this->parameters.from_address_string,
		this->parameters.sec_viewKey_string,
		*(step2_retVals.signed_serialized_tx_string)
	};
	this->submit_raw_tx(req_params); // wait for cb III
}
void FormSubmissionController::cb_III__submitted_tx(optional<string> err_msg)
{
	if (err_msg != boost::none && err_msg->empty() == false) {
		this->parameters.failure_fn(errInServerResponse_withMsg, err_msg.get(), boost::none, boost::none, boost::none);
		return;
	}
	THROW_WALLET_EXCEPTION_IF(this->valsState != WAIT_FOR_FINISH, error::wallet_internal_error, "Expected valsState of WAIT_FOR_FINISH"); // just for addtl safety
	// not actually expecting anything in a success response, so no need to parse it
	//
	Success_RetVals success_retVals;
	success_retVals.used_fee = *(this->step1_retVals__using_fee); // NOTE: not the same thing as step2_retVals.fee_actually_needed
	success_retVals.total_sent = *(this->step1_retVals__final_total_wo_fee) + *(this->step1_retVals__using_fee);
	success_retVals.mixin = *(this->step1_retVals__mixin);
	{
		optional<string> returning__payment_id = this->payment_id_string; // separated from submit_raw_tx_fn so that it can be captured w/o capturing all of args
		if (returning__payment_id == boost::none) {
			auto decoded = monero::address_utils::decodedAddress(this->to_address_string, this->parameters.nettype);
			if (decoded.did_error) { // would be very strange...
				this->parameters.failure_fn(couldntValidateDestAddress, boost::none, boost::none, boost::none, boost::none);
				return;
			}
			if (decoded.paymentID_string != boost::none) {
				returning__payment_id = std::move(*(decoded.paymentID_string)); // just preserving this as an original return value - this can probably eventually be removed
			}
		}
		success_retVals.final_payment_id = returning__payment_id;
	}
	success_retVals.signed_serialized_tx_string = *(this->step2_retVals__signed_serialized_tx_string);
	success_retVals.tx_hash_string = *(this->step2_retVals__tx_hash_string);
	success_retVals.tx_key_string = *(this->step2_retVals__tx_key_string);
	success_retVals.tx_pub_key_string = *(this->step2_retVals__tx_pub_key_string);
	success_retVals.final_total_wo_fee = *(this->step1_retVals__final_total_wo_fee);
	success_retVals.isXMRAddressIntegrated = this-isXMRAddressIntegrated;
	success_retVals.integratedAddressPIDForDisplay = this->integratedAddressPIDForDisplay;
	success_retVals.target_address = this->to_address_string;
	//
	this->parameters.success_fn(success_retVals);
}

ext: cpp

max_stars_repo_name: Toklio-project/mytokl-libapp-cpp

max_issues_repo_name: Toklio-project/mytokl-libapp-cpp

max_forks_repo_name: Toklio-project/mytokl-libapp-cpp


text:
/**
 * @file
 * @copyright This code is licensed under the 3-clause BSD license.\n
 *            Copyright ETH Zurich, Laboratory of Physical Chemistry, Reiher Group.\n
 *            See LICENSE.txt for details.
 */

#include "Files/tests_file_location.h"
#include <Swoose/MolecularMechanics/SFAM/SfamCalculatorSettings.h>
#include <Swoose/MolecularMechanics/SFAM/SfamMolecularMechanicsCalculator.h>
#include <Swoose/Utilities/TopologyUtils.h>
#include <Utils/GeometricDerivatives/NumericalHessianCalculator.h>
#include <Utils/IO/ChemicalFileFormats/ChemicalFileHandler.h>
#include <Utils/IO/FilesystemHelpers.h>
#include <gmock/gmock.h>
#include <boost/filesystem/operations.hpp>

using namespace testing;
namespace Scine {
using namespace MolecularMechanics;
using namespace SwooseUtilities;
namespace Tests {

/**
 * @class ASfamMolecularMechanicsTest SfamMolecularMechanicsTest.cpp
 * @brief Tests concerning SFAM's molecular mechanics method in SCINE.
 * @test
 */
class ASfamMolecularMechanicsTest : public Test {
 public:
  SfamMolecularMechanicsCalculator calculator;
  Core::Log silentLogger = Core::Log::silent();
  void SetUp() override {
    calculator.setLog(silentLogger);
  }
};

TEST_F(ASfamMolecularMechanicsTest, SettingsAreSetCorrectly) {
  using namespace SwooseUtilities::SettingsNames;
  calculator.settings().modifyBool(onlyCalculateBondedContribution, true);
  calculator.settings().modifyBool(printContributionsMolecularMechanics, true);
  calculator.settings().modifyString(parameterFilePath, "pfp");
  calculator.settings().modifyString(connectivityFilePath, "cfp");
  calculator.settings().modifyString(sfamAtomTypeLevel, "unique");
  calculator.settings().modifyBool(detectBondsWithCovalentRadii, false);
  calculator.settings().modifyDouble(nonCovalentCutoffRadius, 4.2);
  calculator.settings().modifyBool(hydrogenBondCorrection, false);

  ASSERT_THAT(calculator.settings().getBool(onlyCalculateBondedContribution), Eq(true));
  ASSERT_THAT(calculator.settings().getBool(printContributionsMolecularMechanics), Eq(true));
  ASSERT_THAT(calculator.settings().getString(parameterFilePath), Eq("pfp"));
  ASSERT_THAT(calculator.settings().getString(connectivityFilePath), Eq("cfp"));
  ASSERT_THAT(calculator.settings().getString(sfamAtomTypeLevel), Eq("unique"));
  ASSERT_THAT(calculator.settings().getBool(detectBondsWithCovalentRadii), Eq(false));
  ASSERT_THAT(calculator.settings().getDouble(nonCovalentCutoffRadius), Eq(4.2));
  ASSERT_THAT(calculator.settings().getBool(hydrogenBondCorrection), Eq(false));
}

// The energy of the test structure that is calculated here contains contributions of all force field interaction types.
TEST_F(ASfamMolecularMechanicsTest, EnergyAndGradientsAreCalculatedCorrectlyForMelatonin) {
  Utils::AtomCollection testStructure = Utils::ChemicalFileHandler::read(melatonin_xyz_file).first;

  calculator.settings().modifyString(SwooseUtilities::SettingsNames::parameterFilePath, melatonin_param_file);
  calculator.settings().modifyString(SwooseUtilities::SettingsNames::connectivityFilePath, melatonin_connectivity_file);
  calculator.settings().modifyDouble(SwooseUtilities::SettingsNames::nonCovalentCutoffRadius, 20.0); // basically no
                                                                                // cutoff radius

  calculator.setRequiredProperties(Utils::Property::Energy | Utils::Property::Gradients | Utils::Property::Hessian);

  calculator.setStructure(testStructure);

  // Clone calculator to test clone interface as well
  auto clonedCalculator = calculator.clone();

  // Calculate and gather results
  Utils::Results results = clonedCalculator->calculate("test calculation 1");
  double energy = results.get<Utils::Property::Energy>() * Utils::Constants::kCalPerMol_per_hartree;
  Utils::GradientCollection gradients = results.get<Utils::Property::Gradients>() * Utils::Constants::kCalPerMol_per_hartree;
  Utils::HessianMatrix hessian = results.get<Utils::Property::Hessian>() * Utils::Constants::kCalPerMol_per_hartree;

  // Numerical gradients
  Utils::PositionCollection positions = testStructure.getPositions();
  Utils::GradientCollection numGrad(positions.rows(), 3);

  double stepsize = 1e-5;

  for (int i = 0; i < positions.rows(); ++i) {
    Eigen::RowVector3d pos = positions.row(i);
    Eigen::RowVector3d tmpPos = pos;
    for (int j = 0; j < 3; ++j) {
      pos[j] += stepsize;
      positions.row(i) = pos;

      clonedCalculator->setRequiredProperties(Utils::Property::Energy); // This is needed, but why? Bug?
      clonedCalculator->modifyPositions(positions);
      results = clonedCalculator->calculate("");
      double energyPlus = results.get<Utils::Property::Energy>() * Utils::Constants::kCalPerMol_per_hartree;

      pos = tmpPos;
      pos[j] -= stepsize;
      positions.row(i) = pos;

      clonedCalculator->modifyPositions(positions);
      results = clonedCalculator->calculate("");
      double energyMinus = results.get<Utils::Property::Energy>() * Utils::Constants::kCalPerMol_per_hartree;

      pos = tmpPos;
      positions.row(i) = pos;

      numGrad(i, j) = (energyPlus - energyMinus) / (2.0 * stepsize);
    }
  }

  // Assertions
  ASSERT_THAT(energy, DoubleNear(-4.009964844, 1e-6));
  ASSERT_THAT(hessian(0, 0), DoubleNear(92.95206847, 1e-3));
  ASSERT_THAT(hessian(2, 7), DoubleNear(0.697864568, 1e-3));
  ASSERT_THAT(hessian(11, 11), DoubleNear(381.965531, 1e-3));

  for (int k = 0; k < gradients.rows(); ++k) {
    for (int l = 0; l < 3; ++l) {
      ASSERT_THAT(gradients(k, l), DoubleNear(numGrad(k, l), 1e-3));
    }
  }

  /*
   * The same calculation with the original calculator and no connectivity file given.
   */
  calculator.settings().modifyString(SwooseUtilities::SettingsNames::connectivityFilePath, "");
  Utils::Results newResults = calculator.calculate("test calculation 2");
  double newEnergy = newResults.get<Utils::Property::Energy>() * Utils::Constants::kCalPerMol_per_hartree;
  auto newGradients = newResults.get<Utils::Property::Gradients>() * Utils::Constants::kCalPerMol_per_hartree;
  auto newHessian = newResults.get<Utils::Property::Hessian>() * Utils::Constants::kCalPerMol_per_hartree;
  auto success = results.get<Utils::Property::SuccessfulCalculation>();

  // Assertions
  ASSERT_THAT(success, Eq(true));
  ASSERT_THAT(energy, DoubleNear(newEnergy, 1e-6));
  ASSERT_THAT(hessian(0, 0), DoubleNear(newHessian(0, 0), 1e-3));
  ASSERT_THAT(hessian(2, 7), DoubleNear(newHessian(2, 7), 1e-3));
  ASSERT_THAT(hessian(11, 11), DoubleNear(newHessian(11, 11), 1e-3));
  for (int k = 0; k < gradients.rows(); ++k) {
    for (int l = 0; l < 3; ++l) {
      ASSERT_THAT(gradients(k, l), DoubleNear(newGradients(k, l), 1e-3));
    }
  }

  /*
   * The same calculation with a parameter file that is missing C6 coefficients.
   * The energy should be similar, but not quite the same as the C6 now deviate slightly
   * from the other calculation.
   */
  constexpr const char* tmpFile = "tmp_parameter_file.dat";
  auto silentLogger = Core::Log::silent();
  calculator.setLog(silentLogger);
  Utils::FilesystemHelpers::copyFile(melatonin_param_file_no_c6, tmpFile);
  calculator.settings().modifyString(SwooseUtilities::SettingsNames::parameterFilePath, tmpFile);
  calculator.setRequiredProperties(Utils::Property::Energy);
  calculator.setStructure(testStructure); // new set structure because re-initialization is necessary
  newResults = calculator.calculate("test calculation 3");

  // Clean-up
  boost::filesystem::remove_all(tmpFile);

  // Assertions
  double otherC6Energy = newResults.get<Utils::Property::Energy>() * Utils::Constants::kCalPerMol_per_hartree;
  ASSERT_TRUE(std::abs(energy - otherC6Energy) > 1e-3);
  ASSERT_TRUE(std::abs(energy - otherC6Energy) < 1e-1);
}

// Tests that the Hessian is identical to a fully numerical one (Hessian is half numerical and half analytical).
TEST_F(ASfamMolecularMechanicsTest, HessianIsIdenticalToTheFullyNumericalHessianForMelatonin) {
  Utils::AtomCollection testStructure = Utils::ChemicalFileHandler::read(melatonin_xyz_file).first;

  calculator.settings().modifyString(SwooseUtilities::SettingsNames::parameterFilePath, melatonin_param_file);
  calculator.settings().modifyString(SwooseUtilities::SettingsNames::connectivityFilePath, melatonin_connectivity_file);
  calculator.settings().modifyDouble(SwooseUtilities::SettingsNames::nonCovalentCutoffRadius, 20.0); // basically no
                                                                                // cutoff radius

  calculator.setRequiredProperties(Utils::Property::Hessian);
  calculator.setStructure(testStructure);

  // Calculate and gather results
  Utils::Results results = calculator.calculate("test calculation 3");
  const Utils::HessianMatrix& hessian = results.get<Utils::Property::Hessian>();

  // Calculated Hessian fully numerically
  calculator.setRequiredProperties(Utils::Property::Energy | Utils::Property::Gradients);
  Utils::NumericalHessianCalculator numericalHessianCalculator(calculator);
  Utils::Results numericalResult = numericalHessianCalculator.calculate();
  const Utils::HessianMatrix& numericalHessian = numericalResult.get<Utils::Property::Hessian>();

  // Assertions
  ASSERT_THAT(hessian.rows(), Eq(numericalHessian.rows()));
  ASSERT_THAT(hessian.cols(), Eq(numericalHessian.cols()));
  for (int i = 0; i < hessian.rows(); ++i) {
    for (int j = 0; j < hessian.cols(); ++j) {
      ASSERT_THAT(hessian(i, j), DoubleNear(numericalHessian(i, j), 1e-6));
    }
  }
}

TEST_F(ASfamMolecularMechanicsTest, TopologyUtilsDividesAStructureCorrectly) {
  // Does it correctly divide two bonded atoms?
  auto bo1 = Utils::BondOrderCollection(2);
  bo1.setZero();
  bo1.setOrder(1, 0, 1.0);

  bool ok1;
  auto r1 = TopologyUtils::divideStructureAtBond(0, 1, bo1, ok1);
  ASSERT_TRUE(ok1);
  ASSERT_THAT(r1[0], Eq(1));
  ASSERT_THAT(r1[1], Eq(2));

  // Does it correctly divide three bonded atoms?
  auto bo2 = Utils::BondOrderCollection(3);
  bo2.setZero();
  bo2.setOrder(1, 0, 1.0);
  bo2.setOrder(2, 1, 1.0);

  bool ok2;
  auto r2 = TopologyUtils::divideStructureAtBond(0, 1, bo2, ok2);
  ASSERT_TRUE(ok2);
  ASSERT_THAT(r2[0], Eq(1));
  ASSERT_THAT(r2[1], Eq(2));
  ASSERT_THAT(r2[2], Eq(2));

  // Does it detect a cycle?
  auto bo3 = Utils::BondOrderCollection(3);
  bo3.setZero();
  bo3.setOrder(1, 0, 1.0);
  bo3.setOrder(2, 0, 1.0);
  bo3.setOrder(2, 1, 1.0);

  bool ok3;
  auto r3 = TopologyUtils::divideStructureAtBond(0, 1, bo3, ok3);
  ASSERT_FALSE(ok3);
}

TEST_F(ASfamMolecularMechanicsTest, IncorrectConnectivityFileCausesException) {
  Utils::AtomCollection testStructure = Utils::ChemicalFileHandler::read(melatonin_xyz_file).first;
  calculator.settings().modifyString(SwooseUtilities::SettingsNames::parameterFilePath, melatonin_param_file);
  calculator.settings().modifyString(SwooseUtilities::SettingsNames::connectivityFilePath,
                                     melatonin_connectivity_file_with_error);
  calculator.setRequiredProperties(Utils::Property::Energy | Utils::Property::Gradients | Utils::Property::Hessian);

  std::string exceptionString = "";
  try {
    calculator.setStructure(testStructure);
  }
  catch (const std::runtime_error& e) {
    exceptionString = e.what();
  }

  ASSERT_STREQ(exceptionString.c_str(), "Connectivity file is invalid! Error during check of atom with index: 32");
}

} // namespace Tests
} // namespace Scine

max_stars_repo_name: qcscine/swoose

max_issues_repo_name: qcscine/swoose

max_forks_repo_name: qcscine/swoose


text: #include <boost/mpl/vector/aux_/iterator.hpp>

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
/*
	This file is part of cpp-ethereum.

	cpp-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	cpp-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Client.cpp
 * @author Gav Wood <i@gavwood.com>
 * @date 2014
 */

#include "Client.h"
#include <chrono>
#include <memory>
#include <thread>
#include <boost/filesystem.hpp>
#include <libdevcore/Log.h>
#include <libp2p/Host.h>
#include "Defaults.h"
#include "Executive.h"
#include "EthereumHost.h"
#include "Block.h"
#include "TransactionQueue.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace p2p;

static_assert(BOOST_VERSION == 106300, "Wrong boost headers version");

std::ostream& dev::eth::operator<<(std::ostream& _out, ActivityReport const& _r)
{
	_out << "Since " << toString(_r.since) << " (" << std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - _r.since).count();
	_out << "): " << _r.ticks << "ticks";
	return _out;
}

#if defined(_WIN32)
const char* ClientNote::name() { return EthTeal "^" EthBlue " i"; }
const char* ClientChat::name() { return EthTeal "^" EthWhite " o"; }
const char* ClientTrace::name() { return EthTeal "^" EthGray " O"; }
const char* ClientDetail::name() { return EthTeal "^" EthCoal " 0"; }
#else
const char* ClientNote::name() { return EthTeal "⧫" EthBlue " ℹ"; }
const char* ClientChat::name() { return EthTeal "⧫" EthWhite " ◌"; }
const char* ClientTrace::name() { return EthTeal "⧫" EthGray " ◎"; }
const char* ClientDetail::name() { return EthTeal "⧫" EthCoal " ●"; }
#endif

Client::Client(
	ChainParams const& _params,
	int _networkID,
	p2p::Host* _host,
	std::shared_ptr<GasPricer> _gpForAdoption,
	std::string const& _dbPath,
	WithExisting _forceAction,
	TransactionQueue::Limits const& _l
):
	ClientBase(_l),
	Worker("eth", 0),
	m_bc(_params, _dbPath, _forceAction, [](unsigned d, unsigned t){ std::cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }),
	m_gp(_gpForAdoption ? _gpForAdoption : make_shared<TrivialGasPricer>()),
	m_preSeal(chainParams().accountStartNonce),
	m_postSeal(chainParams().accountStartNonce),
	m_working(chainParams().accountStartNonce)
{
	init(_host, _dbPath, _forceAction, _networkID);
}

Client::~Client()
{
	stopWorking();
}

void Client::init(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId)
{
	DEV_TIMED_FUNCTION_ABOVE(500);

	// Cannot be opened until after blockchain is open, since BlockChain may upgrade the database.
	// TODO: consider returning the upgrade mechanism here. will delaying the opening of the blockchain database
	// until after the construction.
	m_stateDB = State::openDB(_dbPath, bc().genesisHash(), _forceAction);
	// LAZY. TODO: move genesis state construction/commiting to stateDB openning and have this just take the root from the genesis block.
	m_preSeal = bc().genesisBlock(m_stateDB);
	m_postSeal = m_preSeal;

	m_bq.setChain(bc());

	m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30);
	m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); });	// TODO: should read m_tq->onReady(thisThread, syncTransactionQueue);
	m_tqReplaced = m_tq.onReplaced([=](h256 const&){ m_needStateReset = true; });
	m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); });			// TODO: should read m_bq->onReady(thisThread, syncBlockQueue);
	m_bq.setOnBad([=](Exception& ex){ this->onBadBlock(ex); });
	bc().setOnBad([=](Exception& ex){ this->onBadBlock(ex); });
	bc().setOnBlockImport([=](BlockHeader const& _info){
		if (auto h = m_host.lock())
			h->onBlockImported(_info);
	});

	if (_forceAction == WithExisting::Rescue)
		bc().rescue(m_stateDB);

	m_gp->update(bc());

	auto host = _extNet->registerCapability(make_shared<EthereumHost>(bc(), m_stateDB, m_tq, m_bq, _networkId));
	m_host = host;

	_extNet->addCapability(host, EthereumHost::staticName(), EthereumHost::c_oldProtocolVersion); //TODO: remove this once v61+ protocol is common

	if (_dbPath.size())
		Defaults::setDBPath(_dbPath);
	doWork(false);
	startWorking();
}

ImportResult Client::queueBlock(bytes const& _block, bool _isSafe)
{
	if (m_bq.status().verified + m_bq.status().verifying + m_bq.status().unverified > 10000)
		this_thread::sleep_for(std::chrono::milliseconds(500));
	return m_bq.import(&_block, _isSafe);
}

tuple<ImportRoute, bool, unsigned> Client::syncQueue(unsigned _max)
{
	stopWorking();
	return bc().sync(m_bq, m_stateDB, _max);
}

void Client::onBadBlock(Exception& _ex) const
{
	// BAD BLOCK!!!
	bytes const* block = boost::get_error_info<errinfo_block>(_ex);
	if (!block)
	{
		cwarn << "ODD: onBadBlock called but exception (" << _ex.what() << ") has no block in it.";
		cwarn << boost::diagnostic_information(_ex);
		return;
	}

	badBlock(*block, _ex.what());
}

void Client::callQueuedFunctions()
{
	while (true)
	{
		function<void()> f;
		DEV_WRITE_GUARDED(x_functionQueue)
			if (!m_functionQueue.empty())
			{
				f = m_functionQueue.front();
				m_functionQueue.pop();
			}
		if (f)
			f();
		else
			break;
	}
}

u256 Client::networkId() const
{
	if (auto h = m_host.lock())
		return h->networkId();
	return 0;
}

void Client::setNetworkId(u256 const& _n)
{
	if (auto h = m_host.lock())
		h->setNetworkId(_n);
}

bool Client::isSyncing() const
{
	if (auto h = m_host.lock())
		return h->isSyncing();
	return false;
}

bool Client::isMajorSyncing() const
{
	if (auto h = m_host.lock())
	{
		SyncState state = h->status().state;
		return (state != SyncState::Idle && state != SyncState::NewBlocks) || h->bq().items().first > 10;
	}
	return false;
}

void Client::startedWorking()
{
	// Synchronise the state according to the head of the block chain.
	// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
	clog(ClientTrace) << "startedWorking()";

	DEV_WRITE_GUARDED(x_preSeal)
		m_preSeal.sync(bc());
	DEV_READ_GUARDED(x_preSeal)
	{
		DEV_WRITE_GUARDED(x_working)
			m_working = m_preSeal;
		DEV_WRITE_GUARDED(x_postSeal)
			m_postSeal = m_preSeal;
	}
}

void Client::doneWorking()
{
	// Synchronise the state according to the head of the block chain.
	// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
	DEV_WRITE_GUARDED(x_preSeal)
		m_preSeal.sync(bc());
	DEV_READ_GUARDED(x_preSeal)
	{
		DEV_WRITE_GUARDED(x_working)
			m_working = m_preSeal;
		DEV_WRITE_GUARDED(x_postSeal)
			m_postSeal = m_preSeal;
	}
}

void Client::reopenChain(WithExisting _we)
{
	reopenChain(bc().chainParams(), _we);
}

void Client::reopenChain(ChainParams const& _p, WithExisting _we)
{
	bool wasSealing = wouldSeal();
	if (wasSealing)
		stopSealing();
	stopWorking();

	m_tq.clear();
	m_bq.clear();
	sealEngine()->cancelGeneration();

	{
		WriteGuard l(x_postSeal);
		WriteGuard l2(x_preSeal);
		WriteGuard l3(x_working);

		auto author = m_preSeal.author();	// backup and restore author.
		m_preSeal = Block(chainParams().accountStartNonce);
		m_postSeal = Block(chainParams().accountStartNonce);
		m_working = Block(chainParams().accountStartNonce);

		m_stateDB = OverlayDB();
		bc().reopen(_p, _we);
		m_stateDB = State::openDB(Defaults::dbPath(), bc().genesisHash(), _we);

		m_preSeal = bc().genesisBlock(m_stateDB);
		m_preSeal.setAuthor(author);
		m_postSeal = m_preSeal;
		m_working = Block(chainParams().accountStartNonce);
	}

	if (auto h = m_host.lock())
		h->reset();

	startedWorking();
	doWork();

	startWorking();
	if (wasSealing)
		startSealing();
}

void Client::executeInMainThread(function<void ()> const& _function)
{
	DEV_WRITE_GUARDED(x_functionQueue)
		m_functionQueue.push(_function);
	m_signalled.notify_all();
}

void Client::clearPending()
{
	DEV_WRITE_GUARDED(x_postSeal)
	{
		if (!m_postSeal.pending().size())
			return;
		m_tq.clear();
		DEV_READ_GUARDED(x_preSeal)
			m_postSeal = m_preSeal;
	}

	startSealing();
	h256Hash changeds;
	noteChanged(changeds);
}

template <class S, class T>
static S& filtersStreamOut(S& _out, T const& _fs)
{
	_out << "{";
	unsigned i = 0;
	for (h256 const& f: _fs)
	{
		_out << (i++ ? ", " : "");
		if (f == PendingChangedFilter)
			_out << LogTag::Special << "pending";
		else if (f == ChainChangedFilter)
			_out << LogTag::Special << "chain";
		else
			_out << f;
	}
	_out << "}";
	return _out;
}

void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Hash& io_changed, h256 _sha3)
{
	Guard l(x_filtersWatches);
	io_changed.insert(PendingChangedFilter);
	m_specialFilters.at(PendingChangedFilter).push_back(_sha3);
	for (pair<h256 const, InstalledFilter>& i: m_filters)
	{
		// acceptable number.
		auto m = i.second.filter.matches(_receipt);
		if (m.size())
		{
			// filter catches them
			for (LogEntry const& l: m)
				i.second.changes.push_back(LocalisedLogEntry(l));
			io_changed.insert(i.first);
		}
	}
}

void Client::appendFromBlock(h256 const& _block, BlockPolarity _polarity, h256Hash& io_changed)
{
	// TODO: more precise check on whether the txs match.
	auto receipts = bc().receipts(_block).receipts;

	Guard l(x_filtersWatches);
	io_changed.insert(ChainChangedFilter);
	m_specialFilters.at(ChainChangedFilter).push_back(_block);
	for (pair<h256 const, InstalledFilter>& i: m_filters)
	{
		// acceptable number & looks like block may contain a matching log entry.
		for (size_t j = 0; j < receipts.size(); j++)
		{
			auto tr = receipts[j];
			auto m = i.second.filter.matches(tr);
			if (m.size())
			{
				auto transactionHash = transaction(_block, j).sha3();
				// filter catches them
				for (LogEntry const& l: m)
					i.second.changes.push_back(LocalisedLogEntry(l, _block, (BlockNumber)bc().number(_block), transactionHash, j, 0, _polarity));
				io_changed.insert(i.first);
			}
		}
	}
}

ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 _value, u256 _gasPrice, Address const& _from)
{
	ExecutionResult ret;
	try
	{
		Block temp(chainParams().accountStartNonce);
		clog(ClientDetail) << "Nonce at " << _dest << " pre:" << m_preSeal.transactionsFrom(_dest) << " post:" << m_postSeal.transactionsFrom(_dest);
		DEV_READ_GUARDED(x_postSeal)
			temp = m_postSeal;
		temp.mutableState().addBalance(_from, _value + _gasPrice * _gas);
		Executive e(temp);
		e.setResultRecipient(ret);
		if (!e.call(_dest, _from, _value, _gasPrice, &_data, _gas))
			e.go();
		e.finalize();
	}
	catch (...)
	{
		cwarn << "Client::call failed: " << boost::current_exception_diagnostic_information();
	}
	return ret;
}

unsigned static const c_syncMin = 1;
unsigned static const c_syncMax = 1000;
double static const c_targetDuration = 1;

void Client::syncBlockQueue()
{
//	cdebug << "syncBlockQueue()";

	ImportRoute ir;
	unsigned count;
	Timer t;
	tie(ir, m_syncBlockQueue, count) = bc().sync(m_bq, m_stateDB, m_syncAmount);
	double elapsed = t.elapsed();

	if (count)
	{
		clog(ClientNote) << count << "blocks imported in" << unsigned(elapsed * 1000) << "ms (" << (count / elapsed) << "blocks/s) in #" << bc().number();
	}

	if (elapsed > c_targetDuration * 1.1 && count > c_syncMin)
		m_syncAmount = max(c_syncMin, count * 9 / 10);
	else if (count == m_syncAmount && elapsed < c_targetDuration * 0.9 && m_syncAmount < c_syncMax)
		m_syncAmount = min(c_syncMax, m_syncAmount * 11 / 10 + 1);
	if (ir.liveBlocks.empty())
		return;
	onChainChanged(ir);
}

void Client::syncTransactionQueue()
{
	Timer timer;

	h256Hash changeds;
	TransactionReceipts newPendingReceipts;

	DEV_WRITE_GUARDED(x_working)
	{
		if (m_working.isSealed())
		{
			ctrace << "Skipping txq sync for a sealed block.";
			return;
		}

		tie(newPendingReceipts, m_syncTransactionQueue) = m_working.sync(bc(), m_tq, *m_gp);
	}

	if (newPendingReceipts.empty())
	{
		auto s = m_tq.status();
		ctrace << "No transactions to process. " << m_working.pending().size() << " pending, " << s.current << " queued, " << s.future << " future, " << s.unverified << " unverified";
		return;
	}

	DEV_READ_GUARDED(x_working)
		DEV_WRITE_GUARDED(x_postSeal)
			m_postSeal = m_working;

	DEV_READ_GUARDED(x_postSeal)
		for (size_t i = 0; i < newPendingReceipts.size(); i++)
			appendFromNewPending(newPendingReceipts[i], changeds, m_postSeal.pending()[i].sha3());

	// Tell farm about new transaction (i.e. restart mining).
	onPostStateChanged();

	// Tell watches about the new transactions.
	noteChanged(changeds);

	// Tell network about the new transactions.
	if (auto h = m_host.lock())
		h->noteNewTransactions();

	ctrace << "Processed " << newPendingReceipts.size() << " transactions in" << (timer.elapsed() * 1000) << "(" << (bool)m_syncTransactionQueue << ")";
}

void Client::onDeadBlocks(h256s const& _blocks, h256Hash& io_changed)
{
	// insert transactions that we are declaring the dead part of the chain
	for (auto const& h: _blocks)
	{
		clog(ClientTrace) << "Dead block:" << h;
		for (auto const& t: bc().transactions(h))
		{
			clog(ClientTrace) << "Resubmitting dead-block transaction " << Transaction(t, CheckTransaction::None);
			ctrace << "Resubmitting dead-block transaction " << Transaction(t, CheckTransaction::None);
			m_tq.import(t, IfDropped::Retry);
		}
	}

	for (auto const& h: _blocks)
		appendFromBlock(h, BlockPolarity::Dead, io_changed);
}

void Client::onNewBlocks(h256s const& _blocks, h256Hash& io_changed)
{
	// remove transactions from m_tq nicely rather than relying on out of date nonce later on.
	for (auto const& h: _blocks)
		clog(ClientTrace) << "Live block:" << h;

	if (auto h = m_host.lock())
		h->noteNewBlocks();

	for (auto const& h: _blocks)
		appendFromBlock(h, BlockPolarity::Live, io_changed);
}

void Client::resyncStateFromChain()
{
	// RESTART MINING

//	ctrace << "resyncStateFromChain()";

	if (!isMajorSyncing())
	{
		bool preChanged = false;
		Block newPreMine(chainParams().accountStartNonce);
		DEV_READ_GUARDED(x_preSeal)
			newPreMine = m_preSeal;

		// TODO: use m_postSeal to avoid re-evaluating our own blocks.
		preChanged = newPreMine.sync(bc());

		if (preChanged || m_postSeal.author() != m_preSeal.author())
		{
			DEV_WRITE_GUARDED(x_preSeal)
				m_preSeal = newPreMine;
			DEV_WRITE_GUARDED(x_working)
				m_working = newPreMine;
			DEV_READ_GUARDED(x_postSeal)
				if (!m_postSeal.isSealed() || m_postSeal.info().hash() != newPreMine.info().parentHash())
					for (auto const& t: m_postSeal.pending())
					{
						clog(ClientTrace) << "Resubmitting post-seal transaction " << t;
//						ctrace << "Resubmitting post-seal transaction " << t;
						auto ir = m_tq.import(t, IfDropped::Retry);
						if (ir != ImportResult::Success)
							onTransactionQueueReady();
					}
			DEV_READ_GUARDED(x_working) DEV_WRITE_GUARDED(x_postSeal)
				m_postSeal = m_working;

			onPostStateChanged();
		}

		// Quick hack for now - the TQ at this point already has the prior pending transactions in it;
		// we should resync with it manually until we are stricter about what constitutes "knowing".
		onTransactionQueueReady();
	}
}

void Client::resetState()
{
	Block newPreMine(chainParams().accountStartNonce);
	DEV_READ_GUARDED(x_preSeal)
		newPreMine = m_preSeal;

	DEV_WRITE_GUARDED(x_working)
		m_working = newPreMine;
	DEV_READ_GUARDED(x_working) DEV_WRITE_GUARDED(x_postSeal)
		m_postSeal = m_working;

	onPostStateChanged();
	onTransactionQueueReady();
}

void Client::onChainChanged(ImportRoute const& _ir)
{
//	ctrace << "onChainChanged()";
	h256Hash changeds;
	onDeadBlocks(_ir.deadBlocks, changeds);
	for (auto const& t: _ir.goodTranactions)
	{
		clog(ClientTrace) << "Safely dropping transaction " << t.sha3();
		m_tq.dropGood(t);
	}
	onNewBlocks(_ir.liveBlocks, changeds);
	resyncStateFromChain();
	noteChanged(changeds);
}

bool Client::remoteActive() const
{
	return chrono::system_clock::now() - m_lastGetWork < chrono::seconds(30);
}

void Client::onPostStateChanged()
{
	clog(ClientTrace) << "Post state changed.";
	m_signalled.notify_all();
	m_remoteWorking = false;
}

void Client::startSealing()
{
	if (m_wouldSeal == true)
		return;
	clog(ClientNote) << "Mining Beneficiary: " << author();
	if (author())
	{
		m_wouldSeal = true;
		m_signalled.notify_all();
	}
	else
		clog(ClientNote) << "You need to set an author in order to seal!";
}

void Client::rejigSealing()
{
	if ((wouldSeal() || remoteActive()) && !isMajorSyncing())
	{
		if (sealEngine()->shouldSeal(this))
		{
			m_wouldButShouldnot = false;

			clog(ClientTrace) << "Rejigging seal engine...";
			DEV_WRITE_GUARDED(x_working)
			{
				if (m_working.isSealed())
				{
					clog(ClientNote) << "Tried to seal sealed block...";
					return;
				}
				m_working.commitToSeal(bc(), m_extraData);
			}
			DEV_READ_GUARDED(x_working)
			{
				DEV_WRITE_GUARDED(x_postSeal)
					m_postSeal = m_working;
				m_sealingInfo = m_working.info();
			}

			if (wouldSeal())
			{
				sealEngine()->onSealGenerated([=](bytes const& header){
					if (!this->submitSealed(header))
						clog(ClientNote) << "Submitting block failed...";
				});
				ctrace << "Generating seal on" << m_sealingInfo.hash(WithoutSeal) << "#" << m_sealingInfo.number();
				sealEngine()->generateSeal(m_sealingInfo);
			}
		}
		else
			m_wouldButShouldnot = true;
	}
	if (!m_wouldSeal)
		sealEngine()->cancelGeneration();
}

void Client::noteChanged(h256Hash const& _filters)
{
	Guard l(x_filtersWatches);
	if (_filters.size())
		filtersStreamOut(cwatch << "noteChanged:", _filters);
	// accrue all changes left in each filter into the watches.
	for (auto& w: m_watches)
		if (_filters.count(w.second.id))
		{
			if (m_filters.count(w.second.id))
			{
				cwatch << "!!!" << w.first << w.second.id.abridged();
				w.second.changes += m_filters.at(w.second.id).changes;
			}
			else if (m_specialFilters.count(w.second.id))
				for (h256 const& hash: m_specialFilters.at(w.second.id))
				{
					cwatch << "!!!" << w.first << LogTag::Special << (w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???");
					w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, hash));
				}
		}
	// clear the filters now.
	for (auto& i: m_filters)
		i.second.changes.clear();
	for (auto& i: m_specialFilters)
		i.second.clear();
}

void Client::doWork(bool _doWait)
{
	bool t = true;
	if (m_syncBlockQueue.compare_exchange_strong(t, false))
		syncBlockQueue();

	if (m_needStateReset)
	{
		resetState();
		m_needStateReset = false;
	}

	t = true;
	bool isSealed = false;
	DEV_READ_GUARDED(x_working)
		isSealed = m_working.isSealed();
	if (!isSealed && !isSyncing() && !m_remoteWorking && m_syncTransactionQueue.compare_exchange_strong(t, false))
		syncTransactionQueue();

	tick();

	rejigSealing();

	callQueuedFunctions();

	DEV_READ_GUARDED(x_working)
		isSealed = m_working.isSealed();
	// If the block is sealed, we have to wait for it to tickle through the block queue
	// (which only signals as wanting to be synced if it is ready).
	if (!m_syncBlockQueue && !m_syncTransactionQueue && (_doWait || isSealed))
	{
		std::unique_lock<std::mutex> l(x_signalled);
		m_signalled.wait_for(l, chrono::seconds(1));
	}
}

void Client::tick()
{
	if (chrono::system_clock::now() - m_lastTick > chrono::seconds(1))
	{
		m_report.ticks++;
		checkWatchGarbage();
		m_bq.tick();
		m_lastTick = chrono::system_clock::now();
		if (m_report.ticks == 15)
			clog(ClientTrace) << activityReport();
	}
}

void Client::checkWatchGarbage()
{
	if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5))
	{
		// watches garbage collection
		vector<unsigned> toUninstall;
		DEV_GUARDED(x_filtersWatches)
			for (auto key: keysOf(m_watches))
				if (m_watches[key].lastPoll != chrono::system_clock::time_point::max() && chrono::system_clock::now() - m_watches[key].lastPoll > chrono::seconds(20))
				{
					toUninstall.push_back(key);
					clog(ClientTrace) << "GC: Uninstall" << key << "(" << chrono::duration_cast<chrono::seconds>(chrono::system_clock::now() - m_watches[key].lastPoll).count() << "s old)";
				}
		for (auto i: toUninstall)
			uninstallWatch(i);

		// blockchain GC
		bc().garbageCollect();

		m_lastGarbageCollection = chrono::system_clock::now();
	}
}

void Client::prepareForTransaction()
{
	startWorking();
}

Block Client::block(h256 const& _block) const
{
	try
	{
		Block ret(bc(), m_stateDB);
		ret.populateFromChain(bc(), _block);
		return ret;
	}
	catch (Exception& ex)
	{
		ex << errinfo_block(bc().block(_block));
		onBadBlock(ex);
		return Block(bc());
	}
}

Block Client::block(h256 const& _blockHash, PopulationStatistics* o_stats) const
{
	try
	{
		Block ret(bc(), m_stateDB);
		PopulationStatistics s = ret.populateFromChain(bc(), _blockHash);
		if (o_stats)
			swap(s, *o_stats);
		return ret;
	}
	catch (Exception& ex)
	{
		ex << errinfo_block(bc().block(_blockHash));
		onBadBlock(ex);
		return Block(bc());
	}
}

State Client::state(unsigned _txi, h256 const& _blockHash) const
{
	try
	{
		return block(_blockHash).fromPending(_txi);
	}
	catch (Exception& ex)
	{
		ex << errinfo_block(bc().block(_blockHash));
		onBadBlock(ex);
		return State(chainParams().accountStartNonce);
	}
}

eth::State Client::state(unsigned _txi) const
{
	DEV_READ_GUARDED(x_postSeal)
		return m_postSeal.fromPending(_txi);
	assert(false);
	return State(chainParams().accountStartNonce);
}

void Client::flushTransactions()
{
	doWork();
}

SyncStatus Client::syncStatus() const
{
	auto h = m_host.lock();
	if (!h)
		return SyncStatus();
	SyncStatus status = h->status();
	status.majorSyncing = isMajorSyncing();
	return status;
}

bool Client::submitSealed(bytes const& _header)
{
	bytes newBlock;
	{
		UpgradableGuard l(x_working);
		{
			UpgradeGuard l2(l);
			if (!m_working.sealBlock(_header))
				return false;
		}
		DEV_WRITE_GUARDED(x_postSeal)
			m_postSeal = m_working;
		newBlock = m_working.blockData();
	}

	// OPTIMISE: very inefficient to not utilise the existing OverlayDB in m_postSeal that contains all trie changes.
	return m_bq.import(&newBlock, true) == ImportResult::Success;
}

void Client::rewind(unsigned _n)
{
	executeInMainThread([=]() {
		bc().rewind(_n);
		onChainChanged(ImportRoute());
	});

	for (unsigned i = 0; i < 10; ++i)
	{
		u256 n;
		DEV_READ_GUARDED(x_working)
			n = m_working.info().number();
		if (n == _n + 1)
			break;
		this_thread::sleep_for(std::chrono::milliseconds(50));
	}
	auto h = m_host.lock();
	if (h)
		h->reset();
}

max_stars_repo_name: XianlinGong/fabcoinsc-dev

max_issues_repo_name: XianlinGong/fabcoinsc-dev

max_forks_repo_name: XianlinGong/fabcoinsc-dev


text:
// Warning! This file is autogenerated.
#ifndef BOOST_TEXT_DATA_CHR_HPP
#define BOOST_TEXT_DATA_CHR_HPP

#include <boost/text/string_view.hpp>

namespace boost { namespace text { namespace data { namespace chr {

inline string_view standard_collation_tailoring()
{
    return string_view(
u8R"([reorder Cher])");
}

}}}}

#endif

max_stars_repo_name: eightysquirrels/text

max_issues_repo_name: eightysquirrels/text

max_forks_repo_name: eightysquirrels/text


text:
#include <boost/asio.hpp>
#include <iostream>
#include <memory>
#include "stat.hpp"

using namespace boost::asio;
using namespace boost::asio::ip;

// socket type
typedef tcp::socket socket_t;
typedef std::shared_ptr<socket_t> socket_ptr;
typedef std::shared_ptr<streambuf> streambuf_ptr;

void onReceive(socket_ptr socket, streambuf_ptr sb, streambuf_ptr wsb, boost::system::error_code ec, size_t bytes) {
    if (ec) {
        std::cout << "disconnected, reason:" << ec.message() << std::endl;
        return ;
    }

//    std::cout << "onReceive" << std::endl;
    stats::instance().inc(stats::qps, 1);
    stats::instance().inc(stats::bytes, bytes);

    // ps:此处假设不会被拆包, 仅用于测试原生asio的性能做对比
    sb->commit(bytes);

    wsb->consume(wsb->size());
    buffer_copy(wsb->prepare(bytes), sb->data());
    wsb->commit(bytes);

    // ping-pong
    socket->async_write_some(wsb->data(), [wsb](boost::system::error_code ec, size_t bytes){});

    sb->consume(bytes);

    socket->async_read_some(sb->prepare(4096), std::bind(&onReceive, socket, sb, wsb,
                std::placeholders::_1,
                std::placeholders::_2));
}

void onAccept(tcp::acceptor* acceptor, socket_ptr socket, boost::system::error_code ec) {
    if (ec) {
        std::cout << "accept error:" << ec.message() << std::endl;
        return ;
    }

    std::cout << "accept success" << std::endl;

    // 2.连接成功, 发起读操作
    streambuf_ptr sb(new streambuf);
    streambuf_ptr wsb(new streambuf);
    socket->async_read_some(sb->prepare(4096), std::bind(&onReceive, socket, sb, wsb,
                std::placeholders::_1,
                std::placeholders::_2));

    // accept接力
    socket_ptr new_socket(new socket_t(acceptor->get_io_context()));
    acceptor->async_accept(*new_socket, std::bind(&onAccept, acceptor, new_socket, std::placeholders::_1));
}

int main() {

    io_context ioc;

    tcp::endpoint addr(address::from_string("127.0.0.1"), 1234);
    tcp::acceptor acceptor(ioc, addr);

    socket_ptr socket(new socket_t(ioc));
    acceptor.async_accept(*socket, std::bind(&onAccept, &acceptor, socket, std::placeholders::_1));

    ioc.run();
}

max_stars_repo_name: brinkqiang/dmlibgo

max_issues_repo_name: brinkqiang/dmlibgo

max_forks_repo_name: brinkqiang/dmlibgo


text:
#include <cinatra_http/http_server.h>

#include <boost/bind.hpp>
#include <iostream>
#include <memory>

namespace cinatra
{
	http_server::http_server(std::size_t io_service_pool_size)
		: io_service_pool_(io_service_pool_size)
	{
	}

	cinatra::http_server& http_server::listen(const std::string& address, const std::string& port)
	{
		auto acceprot = std::make_shared<boost::asio::ip::tcp::acceptor>(io_service_pool_.get_io_service());
		do_listen(acceprot, address, port);
		start_accept(acceprot);
		return *this;
	}

	cinatra::http_server& http_server::listen(const std::string& address, const std::string& port, ssl_method_t ssl_method,
		const std::string& private_key, const std::string& certificate_chain, bool is_file /*= true*/)
	{
		return listen(address, port, ssl_config_t{ ssl_method, private_key, certificate_chain, is_file });
	}

	cinatra::http_server& http_server::listen(const std::string& address, const std::string& port, const ssl_config_t& ssl_cfg)
	{
		auto ssl_ctx = std::make_shared<boost::asio::ssl::context>(static_cast<boost::asio::ssl::context::method>(ssl_cfg.ssl_method));
		if (ssl_cfg.is_file)
		{
			ssl_ctx->use_private_key_file(ssl_cfg.private_key, boost::asio::ssl::context::pem);
			ssl_ctx->use_certificate_chain_file(ssl_cfg.certificate_chain);
		}
		else
		{
			ssl_ctx->use_private_key(boost::asio::buffer(ssl_cfg.private_key), boost::asio::ssl::context::pem);
			ssl_ctx->use_certificate_chain(boost::asio::buffer(ssl_cfg.certificate_chain));
		}

		//HTTP2???
		//configure_tls_context_easy(ec, tls);
		auto acceprot = std::make_shared<boost::asio::ip::tcp::acceptor>(io_service_pool_.get_io_service());
		do_listen(acceprot, address, port);
		start_accept(acceprot, ssl_ctx);
		return *this;
	}

	void http_server::run()
	{
		io_service_pool_.run();
	}

	void http_server::start_accept(std::shared_ptr<boost::asio::ip::tcp::acceptor> const& acceptor)
	{
		auto new_conn = std::make_shared<connection<boost::asio::ip::tcp::socket>>(
			io_service_pool_.get_io_service(), request_handler_, max_req_size_, keep_alive_timeout_);
		acceptor->async_accept(new_conn->socket(), [this, new_conn, acceptor](const boost::system::error_code& e)
		{
			if (!e)
			{
				new_conn->socket().set_option(boost::asio::ip::tcp::no_delay(true));
				new_conn->start();
			}
			else
			{
				std::cout << "server::handle_accept: " << e.message() << std::endl;
			}

			start_accept(acceptor);
		});
	}

	void http_server::start_accept(std::shared_ptr<boost::asio::ip::tcp::acceptor> const& acceptor,
		std::shared_ptr<boost::asio::ssl::context> const& ssl_ctx)
	{
		auto new_conn = std::make_shared<connection<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>>(
			io_service_pool_.get_io_service(), request_handler_, *ssl_ctx, max_req_size_, keep_alive_timeout_);
		acceptor->async_accept(new_conn->socket().lowest_layer(), [this, new_conn, acceptor, ssl_ctx](const boost::system::error_code& e)
		{
			if (!e)
			{
				new_conn->socket().lowest_layer().set_option(boost::asio::ip::tcp::no_delay(true));
				new_conn->reset_timer();
				new_conn->socket().async_handshake(boost::asio::ssl::stream_base::server,
					[new_conn](const boost::system::error_code &e)
				{
					if (e)
					{
						return;
					}
					//HTTP2???
// 					if (!tls_h2_negotiated(new_conn->socket()))
// 					{
// 						return;
// 					}
					new_conn->start();
				});
			}
			else
			{
				std::cout << "server::handle_accept: " << e.message() << std::endl;
			}

			start_accept(acceptor, ssl_ctx);
		});
	}

	void http_server::do_listen(std::shared_ptr<boost::asio::ip::tcp::acceptor> const& acceptor,
		const std::string& address, const std::string& port)
	{
		boost::asio::ip::tcp::resolver resolver(acceptor->get_io_service());
		boost::asio::ip::tcp::resolver::query query(address, port);
		boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
		acceptor->open(endpoint.protocol());
		acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
		acceptor->bind(endpoint);
		acceptor->listen();
	}

	void http_server::stop()
	{
		io_service_pool_.stop();
	}

}

max_issues_repo_name: InvisibleLeVen/cinatra

max_forks_repo_name: InvisibleLeVen/cinatra


text:
/*
 * ElevationVisualization.cpp
 *
 *  Created on: Jun 18, 2014
 *      Author: Péter Fankhauser, Christian Forster
 *   Institute: ETH Zurich, Autonomous Systems Lab
 */

#include "grid_map/GridMap.hpp"
#include "grid_map_visualization/visualizations/ElevationVisualization.hpp"
#include "grid_map_lib/GridMapMath.hpp"

// Eigen
#include <Eigen/Core>

namespace grid_map_visualization {

ElevationVisualization::ElevationVisualization(ros::NodeHandle& nodeHandle)
  : nodeHandle_(nodeHandle)
  , markerHeight_(1.0)
  , lowerColorValue_(255)
  , upperColorValue_(16711680)
  , lowerColor_(grid_map_visualization::color_utils::colorFromColorValue(lowerColorValue_))
  , upperColor_(grid_map_visualization::color_utils::colorFromColorValue(upperColorValue_))
{
  markerPublisher_ =
      nodeHandle_.advertise<visualization_msgs::Marker>("elevation", 1, true);
}

ElevationVisualization::~ElevationVisualization()
{}

bool ElevationVisualization::readParameters()
{
  nodeHandle_.param("elevation/marker_height", markerHeight_, markerHeight_);
  return true;
}

bool ElevationVisualization::initialize()
{
  marker_.ns = "elevation";
  marker_.lifetime = ros::Duration();
  marker_.action = visualization_msgs::Marker::ADD;
  marker_.type = visualization_msgs::Marker::CUBE_LIST;
  marker_.scale.z = markerHeight_;
  return true;
}

void ElevationVisualization::visualize(
    const grid_map::GridMap& map,
    const std::string& typeNameElevation,
    const std::string& typeNameColor,
    const float lowerValueBound,
    const float upperValueBound)
{
  // Set marker info.
  marker_.header.frame_id = map.getFrameId();
  marker_.header.stamp.fromNSec(map.getTimestamp());
  marker_.scale.x = map.getResolution();
  marker_.scale.y = map.getResolution();

  // Clear points.
  marker_.points.clear();
  marker_.colors.clear();

  float markerHeightOffset = static_cast<float>(markerHeight_/2.0);

  const Eigen::Array2i buffSize = map.getBufferSize();
  const Eigen::Array2i buffStartIndex = map.getBufferStartIndex();
  const bool haveColor = map.isType("color");
  for (unsigned int i = 0; i < buffSize(0); ++i)
  {
    for (unsigned int j = 0; j < buffSize(1); ++j)
    {
      // Getting map data.
      const Eigen::Array2i cellIndex(i, j);
      const Eigen::Array2i buffIndex =
          grid_map_lib::getBufferIndexFromIndex(cellIndex, buffSize, buffStartIndex);
      const float& elevation = map.at(typeNameElevation, buffIndex);
      if(std::isnan(elevation))
        continue;
      const float color = haveColor ? map.at(typeNameColor, buffIndex) : lowerValueBound;

      // Add marker point.
      Eigen::Vector2d position;
      map.getPosition(buffIndex, position);
      geometry_msgs::Point point;
      point.x = position.x();
      point.y = position.y();
      point.z = elevation - markerHeightOffset;
      marker_.points.push_back(point);

      // Add marker color.
      if(haveColor)
      {
        std_msgs::ColorRGBA markerColor =
            grid_map_visualization::color_utils::interpolateBetweenColors(
              color, lowerValueBound, upperValueBound, lowerColor_, upperColor_);
        marker_.colors.push_back(markerColor);
      }
    }
  }

  markerPublisher_.publish(marker_);
}

namespace color_utils {

Eigen::Vector3f colorValueToRGB(
    const unsigned long& colorValue)
{
  Eigen::Vector3i rgb;
  rgb(0) = (colorValue >> 16) & 0x0000ff;
  rgb(1) = (colorValue >> 8) & 0x0000ff;
  rgb(2) = (colorValue) & 0x0000ff;
  return rgb.cast<float>()/255.0;
}

std_msgs::ColorRGBA colorFromColorValue(
    const unsigned long& colorValue)
{
  Eigen::Vector3f colorVector = colorValueToRGB(colorValue);
  std_msgs::ColorRGBA color;
  color.r = colorVector(0);
  color.g = colorVector(1);
  color.b = colorVector(2);
  color.a = 1.0;
  return color;
}

float computeLinearMapping(
    const float& sourceValue,
    const float& sourceLowerValue,
    const float& sourceUpperValue,
    const float& mapLowerValue,
    const float& mapUpperValue)
{
  float m = (mapLowerValue - mapUpperValue) / (sourceLowerValue - sourceUpperValue);
  float b = mapUpperValue - m * sourceUpperValue;
  float mapValue = m * sourceValue + b;
  if (mapLowerValue < mapUpperValue)
  {
    mapValue = std::max(mapValue, mapLowerValue);
    mapValue = std::min(mapValue, mapUpperValue);
  }
  else
  {
    mapValue = std::min(mapValue, mapLowerValue);
    mapValue = std::max(mapValue, mapUpperValue);
  }
  return mapValue;
}

void setColorChannelFromValue(
    float& colorChannel,
    const float value,
    const float lowerValueBound,
    const float upperValueBound,
    const bool invert,
    const float colorChannelLowerValue,
    const float colorChannelUpperValue)
{
  float tempColorChannelLowerValue = colorChannelLowerValue;
  float tempColorChannelUpperValue = colorChannelUpperValue;
  if (invert)
  {
    tempColorChannelLowerValue = colorChannelUpperValue;
    tempColorChannelUpperValue = colorChannelLowerValue;
  }
  colorChannel =
      computeLinearMapping(
        value, lowerValueBound, upperValueBound, tempColorChannelLowerValue,
        tempColorChannelUpperValue);
}

std_msgs::ColorRGBA interpolateBetweenColors(
    const float value,
    const float lowerValueBound,
    const float upperValueBound,
    const std_msgs::ColorRGBA& colorForLowerValue,
    const std_msgs::ColorRGBA& colorForUpperValue)
{
  std_msgs::ColorRGBA color;
  color.a = 1.0;
  setColorChannelFromValue(
        color.r, value, lowerValueBound, upperValueBound,
        false, colorForLowerValue.r, colorForUpperValue.r);
  setColorChannelFromValue(
        color.g, value, lowerValueBound, upperValueBound,
        false, colorForLowerValue.g, colorForUpperValue.g);
  setColorChannelFromValue(
        color.b, value, lowerValueBound, upperValueBound,
        false, colorForLowerValue.b, colorForUpperValue.b);
  return color;
}

} /* namespace color_utils */
} /* namespace grid_map_visualization */