<commit_before>/**
Copyright (c) 2013, Philip Deegan.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
    * Neither the name of Philip Deegan nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _MAIKEN_APP_HPP_
#define _MAIKEN_APP_HPP_

#include "kul/os.hpp"
#include "kul/cli.hpp"
#include "kul/log.hpp"
#include "kul/scm.hpp"
#include "kul/proc.hpp"
#include "kul/threads.hpp"
#include "kul/code/compilers.hpp"

#include "maiken/project.hpp"

namespace maiken{

class Exception : public kul::Exception{
    public:
        Exception(const char*f, const uint16_t& l, const std::string& s) : kul::Exception(f, l, s){}
};

class AppVars{
    private:
        bool b = 0, c = 0, d = 0, f = 0, g = 0, l = 0, p = 0, r = 0, s = 0, sh = 0, st = 0, t = 0, u = 0;
        uint16_t dl = 0;
        uint16_t ts = 1;
        std::string aa;
        std::string la;
        kul::hash::map::S2S jas, pks;
        AppVars(){
            pks["OS"]   = KTOSTRING(__KUL_OS__);
            pks["HOME"] = kul::user::home().path();
            if(Settings::INSTANCE().root()[LOCAL] && Settings::INSTANCE().root()[LOCAL][REPO])
                pks["MKN_REPO"] = Settings::INSTANCE().root()[LOCAL][REPO].Scalar();
            else
                pks["MKN_REPO"] = kul::user::home(MAIKEN).path();

        }
    public:
        const std::string& args() const { return aa;}
        void args(const std::string& aa)    { this->aa = aa;}

        const kul::hash::map::S2S& jargs() const { return jas;}
        void jargs(const std::string& a, const std::string& b) { this->jas.insert(a, b);}

        const std::string& linker() const  { return la;}
        void linker(const std::string& la) { this->la = la;}

        const bool& build() const { return this->b;}
        void build(const bool& b) { this->b = b;}

        const bool& clean() const { return this->c;}
        void clean(const bool& c) { this->c = c;}

        const bool& compile() const { return this->p;}
        void compile(const bool& p) { this->p = p;}

        const bool& dbg() const { return this->g;}
        void dbg(const bool& g)   { this->g = g;}

        const bool& debug() const { return this->d;}
        void debug(const bool& d)   { this->d = d;}

        const bool& fupdate() const { return this->f;}
        void fupdate(const bool& f)   { this->f = f;}

        const bool& link() const { return this->l;}
        void link(const bool& l) { this->l = l;}

        const bool& run() const { return this->r;}
        void run(const bool& r) { this->r = r;}

        const bool& show() const { return this->s;}
        void show(const bool& s){ this->s = s;}

        const bool& shar() const { return this->sh;}
        void shar(const bool& sh){ this->sh = sh;}

        const bool& trim() const { return this->t;}
        void trim(const bool& t) { this->t = t;}

        const bool& update() const { return this->u;}
        void update(const bool& u)   { this->u = u;}

        const bool& stat() const { return this->st;}
        void stat(const bool& st){ this->st = st;}

        const uint16_t& dependencyLevel()  const { return dl;}
        void dependencyLevel(const uint16_t& dl)  { this->dl = dl;}

        const uint16_t& threads() const { return ts;}
        void threads(const uint16_t& t) { this->ts = t;}

        const kul::hash::map::S2S& properkeys() const { return pks;}

        static AppVars& INSTANCE(){
            static AppVars instance;
            return instance;
        }
};

class ThreadingCompiler;
class Application : public Constants{
    protected:
        bool ig = 1;
        const Application* par = 0;
        kul::code::Mode m;
        std::string arg, main, lang;
        const std::string p;
        kul::Dir bd, inst;
        maiken::Project proj;
        kul::hash::map::S2T<kul::hash::map::S2S> fs;
        std::vector<std::string> libs;
        std::vector<std::pair<std::string, bool> > srcs;
        std::vector<std::pair<std::string, bool> > incs;
        std::vector<std::string> paths;
        kul::hash::map::S2S ps;
        kul::hash::map::S2T<kul::hash::set::String> args;
        kul::hash::map::S2T<uint16_t> stss;
        kul::hash::map::S2S itss;
        kul::hash::map::S2S includeStamps;
        std::vector<kul::cli::EnvVar> evs;
        std::vector<Application> deps;
        std::string scr;
        const kul::SCM* scm = 0;

        Application(const maiken::Project& proj, const std::string profile) : m(kul::code::Mode::NONE), p(profile), proj(proj){}
        Application(const maiken::Project& proj) : m(kul::code::Mode::NONE), proj(proj){}
        void buildDepVec(const std::string* depVal);
        void buildDepVecRec(std::vector<Application*>& dePs, int16_t i, const kul::hash::set::String& inc);
        void buildExecutable(const std::vector<std::string>& objects);
        void buildLibrary(const std::vector<std::string>& objects);
        void checkErrors(const kul::code::CompilerProcessCapture& cpc) throw(kul::Exception);
        void populateMaps(const YAML::Node& n);
        void populateMapsFromDependencies();
        void populateDependencies(const YAML::Node& n) throw(kul::Exception);
        void preSetupValidation() throw(Exception);
        void postSetupValidation() throw(Exception);
        void resolveProperties();
        void build() throw(kul::Exception);
        void link()  throw(kul::Exception);
        void run(bool dbg);
        void trim();
        void trim(const kul::File& f);
        void scmStatus(const bool& deps = false) throw(kul::scm::Exception);
        void scmUpdate(const bool& f) throw(kul::scm::Exception);
        void scmUpdate(const bool& f, const kul::SCM* scm, const std::string& repo) throw(kul::scm::Exception);
        void setup();
        void showConfig(bool force = 0);
        void cyclicCheck(const std::vector<std::pair<std::string, std::string>>& apps) const throw(kul::Exception);
        void showProfiles();
        void loadTimeStamps() throw (kul::StringException);
        bool incSrc(const kul::File& f);
        std::string resolveFromProperties(const std::string& s) const;
        kul::Dir    resolveDependencyDirectory(const YAML::Node& d);
        kul::hash::map::S2T<kul::hash::map::S2T<kul::hash::set::String> > sourceMap();
        std::vector<std::string> compile() throw(kul::Exception);
        kul::hash::set::String   inactiveMains();

        void addSourceLine (const std::string& o) throw (kul::StringException);
        void addIncludeLine(const std::string& o) throw (kul::StringException);

        static void showHelp();
    public:
        static Application CREATE(int16_t argc, char *argv[]) throw(kul::Exception);
        virtual void                                       process()   throw(kul::Exception);
        const kul::Dir&                                    buildDir()      const { return bd; }
        const std::string&                                 profile()       const { return p; }
        const maiken::Project&                             project()       const { return proj;}
        const std::vector<Application>&                    dependencies()  const { return deps; }
        const std::vector<kul::cli::EnvVar>&               envVars()       const { return evs; }
        const kul::hash::map::S2T<kul::hash::map::S2S>&    files()         const { return fs; }
        const std::vector<std::string>&                    libraries()     const { return libs;}
        const std::vector<std::pair<std::string, bool> >&  sources()       const { return srcs;}
        const std::vector<std::pair<std::string, bool> >&  includes()      const { return incs;}
        const std::vector<std::string>&                    libraryPaths()  const { return paths;}
        const kul::hash::map::S2S&                         properties()    const { return ps;}
        const kul::hash::map::S2T<kul::hash::set::String>& arguments()     const { return args; }

        friend class ThreadingCompiler;
};

class ThreadingCompiler : public Constants{
    private:
        bool f;
        kul::Mutex compile;
        kul::Mutex push;
        maiken::Application& app;
        std::queue<std::pair<std::string, std::string> >& sources;
        std::vector<kul::code::CompilerProcessCapture> cpcs;
        std::vector<std::string> incs;
    public:
        ThreadingCompiler(maiken::Application& app, std::queue<std::pair<std::string, std::string> >& sources)
            : f(0), app(app), sources(sources){
                for(const auto& s : app.includes()){
                    kul::Dir d(s.first);
                    const std::string& m(d.escm());
                    if(!m.empty()) incs.push_back(m);
                    else           incs.push_back(".");
                }
            }
        void operator()() throw(kul::Exception){
            std::pair<std::string, std::string> p;
            {
                kul::ScopeLock lock(compile);
                p = sources.front();
                sources.pop();
            }
            const std::string src(p.first);
            const std::string obj(p.second);
            if(!f){
                const std::string& fileType = src.substr(src.rfind(".") + 1);
                const std::string& compiler = (*(*app.files().find(fileType)).second.find(COMPILER)).second;
                std::vector<std::string> args;
                if(app.arguments().count(fileType) > 0)
                    for(const std::string& o : (*app.arguments().find(fileType)).second)
                        for(const auto& s : kul::cli::asArgs(o))
                            args.push_back(s);
                for(const auto& s : kul::cli::asArgs(app.arg)) args.push_back(s);
                std::string cmd = compiler + " " + AppVars::INSTANCE().args();
                if(AppVars::INSTANCE().jargs().count(fileType) > 0)
                    cmd += " " + (*AppVars::INSTANCE().jargs().find(fileType)).second;
                // WE CHECK BEFORE USING THIS THAT A COMPILER EXISTS FOR EVERY FILE
                if(kul::LogMan::INSTANCE().inf() && !kul::LogMan::INSTANCE().dbg())
                    KOUT(NON) << compiler << " : " << src;
                const kul::code::CompilerProcessCapture& cpc = kul::code::Compilers::INSTANCE().get(compiler)->compileSource(cmd, args, incs, src, obj, app.m);
                kul::ScopeLock lock(push);
                cpcs.push_back(cpc);
                if(cpc.exception()) f = 1;
            }
        }
        const std::vector<kul::code::CompilerProcessCapture>& processCaptures(){return cpcs;}
};

class SCMGetter{
    private:
        kul::hash::map::S2S valids;
        static bool IS_SOLID(const std::string& r){
            return r.find("://") != std::string::npos || r.find("@") != std::string::npos;
        }
        static SCMGetter& INSTANCE(){
            static SCMGetter s;
            return s;
        }
        static const kul::SCM* GET_SCM(const kul::Dir& d, const std::string& r){
            std::vector<std::string> repos;
            if(IS_SOLID(r)) repos.push_back(r);
            else
                for(const std::string& s : Settings::INSTANCE().remoteRepos()) repos.push_back(s + r);
            for(const auto& repo : repos){
                try{
                    kul::Process g("git");
                    kul::ProcessCapture gp(g);
                    std::string r1(repo);
                    if(repo.find("http") != std::string::npos && repo.find("@") == std::string::npos)
                        r1 = repo.substr(0, repo.find("//") + 2) + "u:p@" + repo.substr(repo.find("//") + 2);
                    g.arg("ls-remote").arg(r1).start();
                    if(!gp.errs().size()) {
                        INSTANCE().valids.insert(d.path(), repo);
                        return &kul::scm::Manager::INSTANCE().get("git");
                    }
                }catch(const kul::proc::ExitException& e){}
                try{
                    kul::Process s("svn");
                    kul::ProcessCapture sp(s);
                    s.arg("ls").arg(repo).start();
                    if(!sp.errs().size()) {
                        INSTANCE().valids.insert(d.path(), repo);
                        return &kul::scm::Manager::INSTANCE().get("svn");
                    }
                }catch(const kul::proc::ExitException& e){}
            }
            std::stringstream ss;
            for(const auto& s : repos) ss << s << "\n";
            KEXCEPT(Exception, "SCM not found or not supported type(git/svn) for repo(s)\n"+ss.str()+"project:"+d.path());
        }
    public:
        static const std::string REPO(const kul::Dir& d, const std::string& r){
            if(INSTANCE().valids.count(d.path())) return (*INSTANCE().valids.find(d.path())).second;
            if(IS_SOLID(r)) INSTANCE().valids.insert(d.path(), r);
            else            GET_SCM(d, r);
            if(INSTANCE().valids.count(d.path())) return (*INSTANCE().valids.find(d.path())).second;
            KEXCEPT(Exception, "SCM not discovered for project: "+d.path());
        }
        static bool HAS(const kul::Dir& d){
            return (kul::Dir(d.join(".git")) || kul::Dir(d.join(".svn")));
        }
        static const kul::SCM* GET(const kul::Dir& d, const std::string& r){
            if(IS_SOLID(r)) INSTANCE().valids.insert(d.path(), r);
            if(kul::Dir(d.join(".git"))) return &kul::scm::Manager::INSTANCE().get("git");
            if(kul::Dir(d.join(".svn"))) return &kul::scm::Manager::INSTANCE().get("svn");
            return r.size() ? GET_SCM(d, r) : 0;
        }
};

}
#endif /* _MAIKEN_APP_HPP_ */

<commit_msg>constants inherited<commit_after>/**
Copyright (c) 2013, Philip Deegan.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
    * Neither the name of Philip Deegan nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _MAIKEN_APP_HPP_
#define _MAIKEN_APP_HPP_

#include "kul/os.hpp"
#include "kul/cli.hpp"
#include "kul/log.hpp"
#include "kul/scm.hpp"
#include "kul/proc.hpp"
#include "kul/threads.hpp"
#include "kul/code/compilers.hpp"

#include "maiken/project.hpp"

namespace maiken{

class Exception : public kul::Exception{
    public:
        Exception(const char*f, const uint16_t& l, const std::string& s) : kul::Exception(f, l, s){}
};

class AppVars : public Constants{
    private:
        bool b = 0, c = 0, d = 0, f = 0, g = 0, l = 0, p = 0, r = 0, s = 0, sh = 0, st = 0, t = 0, u = 0;
        uint16_t dl = 0;
        uint16_t ts = 1;
        std::string aa;
        std::string la;
        kul::hash::map::S2S jas, pks;
        AppVars(){
            pks["OS"]   = KTOSTRING(__KUL_OS__);
            pks["HOME"] = kul::user::home().path();
            if(Settings::INSTANCE().root()[LOCAL] && Settings::INSTANCE().root()[LOCAL][REPO])
                pks["MKN_REPO"] = Settings::INSTANCE().root()[LOCAL][REPO].Scalar();
            else
                pks["MKN_REPO"] = kul::user::home(MAIKEN).path();

        }
    public:
        const std::string& args() const { return aa;}
        void args(const std::string& aa)    { this->aa = aa;}

        const kul::hash::map::S2S& jargs() const { return jas;}
        void jargs(const std::string& a, const std::string& b) { this->jas.insert(a, b);}

        const std::string& linker() const  { return la;}
        void linker(const std::string& la) { this->la = la;}

        const bool& build() const { return this->b;}
        void build(const bool& b) { this->b = b;}

        const bool& clean() const { return this->c;}
        void clean(const bool& c) { this->c = c;}

        const bool& compile() const { return this->p;}
        void compile(const bool& p) { this->p = p;}

        const bool& dbg() const { return this->g;}
        void dbg(const bool& g)   { this->g = g;}

        const bool& debug() const { return this->d;}
        void debug(const bool& d)   { this->d = d;}

        const bool& fupdate() const { return this->f;}
        void fupdate(const bool& f)   { this->f = f;}

        const bool& link() const { return this->l;}
        void link(const bool& l) { this->l = l;}

        const bool& run() const { return this->r;}
        void run(const bool& r) { this->r = r;}

        const bool& show() const { return this->s;}
        void show(const bool& s){ this->s = s;}

        const bool& shar() const { return this->sh;}
        void shar(const bool& sh){ this->sh = sh;}

        const bool& trim() const { return this->t;}
        void trim(const bool& t) { this->t = t;}

        const bool& update() const { return this->u;}
        void update(const bool& u)   { this->u = u;}

        const bool& stat() const { return this->st;}
        void stat(const bool& st){ this->st = st;}

        const uint16_t& dependencyLevel()  const { return dl;}
        void dependencyLevel(const uint16_t& dl)  { this->dl = dl;}

        const uint16_t& threads() const { return ts;}
        void threads(const uint16_t& t) { this->ts = t;}

        const kul::hash::map::S2S& properkeys() const { return pks;}

        static AppVars& INSTANCE(){
            static AppVars instance;
            return instance;
        }
};

class ThreadingCompiler;
class Application : public Constants{
    protected:
        bool ig = 1;
        const Application* par = 0;
        kul::code::Mode m;
        std::string arg, main, lang;
        const std::string p;
        kul::Dir bd, inst;
        maiken::Project proj;
        kul::hash::map::S2T<kul::hash::map::S2S> fs;
        std::vector<std::string> libs;
        std::vector<std::pair<std::string, bool> > srcs;
        std::vector<std::pair<std::string, bool> > incs;
        std::vector<std::string> paths;
        kul::hash::map::S2S ps;
        kul::hash::map::S2T<kul::hash::set::String> args;
        kul::hash::map::S2T<uint16_t> stss;
        kul::hash::map::S2S itss;
        kul::hash::map::S2S includeStamps;
        std::vector<kul::cli::EnvVar> evs;
        std::vector<Application> deps;
        std::string scr;
        const kul::SCM* scm = 0;

        Application(const maiken::Project& proj, const std::string profile) : m(kul::code::Mode::NONE), p(profile), proj(proj){}
        Application(const maiken::Project& proj) : m(kul::code::Mode::NONE), proj(proj){}
        void buildDepVec(const std::string* depVal);
        void buildDepVecRec(std::vector<Application*>& dePs, int16_t i, const kul::hash::set::String& inc);
        void buildExecutable(const std::vector<std::string>& objects);
        void buildLibrary(const std::vector<std::string>& objects);
        void checkErrors(const kul::code::CompilerProcessCapture& cpc) throw(kul::Exception);
        void populateMaps(const YAML::Node& n);
        void populateMapsFromDependencies();
        void populateDependencies(const YAML::Node& n) throw(kul::Exception);
        void preSetupValidation() throw(Exception);
        void postSetupValidation() throw(Exception);
        void resolveProperties();
        void build() throw(kul::Exception);
        void link()  throw(kul::Exception);
        void run(bool dbg);
        void trim();
        void trim(const kul::File& f);
        void scmStatus(const bool& deps = false) throw(kul::scm::Exception);
        void scmUpdate(const bool& f) throw(kul::scm::Exception);
        void scmUpdate(const bool& f, const kul::SCM* scm, const std::string& repo) throw(kul::scm::Exception);
        void setup();
        void showConfig(bool force = 0);
        void cyclicCheck(const std::vector<std::pair<std::string, std::string>>& apps) const throw(kul::Exception);
        void showProfiles();
        void loadTimeStamps() throw (kul::StringException);
        bool incSrc(const kul::File& f);
        std::string resolveFromProperties(const std::string& s) const;
        kul::Dir    resolveDependencyDirectory(const YAML::Node& d);
        kul::hash::map::S2T<kul::hash::map::S2T<kul::hash::set::String> > sourceMap();
        std::vector<std::string> compile() throw(kul::Exception);
        kul::hash::set::String   inactiveMains();

        void addSourceLine (const std::string& o) throw (kul::StringException);
        void addIncludeLine(const std::string& o) throw (kul::StringException);

        static void showHelp();
    public:
        static Application CREATE(int16_t argc, char *argv[]) throw(kul::Exception);
        virtual void                                       process()   throw(kul::Exception);
        const kul::Dir&                                    buildDir()      const { return bd; }
        const std::string&                                 profile()       const { return p; }
        const maiken::Project&                             project()       const { return proj;}
        const std::vector<Application>&                    dependencies()  const { return deps; }
        const std::vector<kul::cli::EnvVar>&               envVars()       const { return evs; }
        const kul::hash::map::S2T<kul::hash::map::S2S>&    files()         const { return fs; }
        const std::vector<std::string>&                    libraries()     const { return libs;}
        const std::vector<std::pair<std::string, bool> >&  sources()       const { return srcs;}
        const std::vector<std::pair<std::string, bool> >&  includes()      const { return incs;}
        const std::vector<std::string>&                    libraryPaths()  const { return paths;}
        const kul::hash::map::S2S&                         properties()    const { return ps;}
        const kul::hash::map::S2T<kul::hash::set::String>& arguments()     const { return args; }

        friend class ThreadingCompiler;
};

class ThreadingCompiler : public Constants{
    private:
        bool f;
        kul::Mutex compile;
        kul::Mutex push;
        maiken::Application& app;
        std::queue<std::pair<std::string, std::string> >& sources;
        std::vector<kul::code::CompilerProcessCapture> cpcs;
        std::vector<std::string> incs;
    public:
        ThreadingCompiler(maiken::Application& app, std::queue<std::pair<std::string, std::string> >& sources)
            : f(0), app(app), sources(sources){
                for(const auto& s : app.includes()){
                    kul::Dir d(s.first);
                    const std::string& m(d.escm());
                    if(!m.empty()) incs.push_back(m);
                    else           incs.push_back(".");
                }
            }
        void operator()() throw(kul::Exception){
            std::pair<std::string, std::string> p;
            {
                kul::ScopeLock lock(compile);
                p = sources.front();
                sources.pop();
            }
            const std::string src(p.first);
            const std::string obj(p.second);
            if(!f){
                const std::string& fileType = src.substr(src.rfind(".") + 1);
                const std::string& compiler = (*(*app.files().find(fileType)).second.find(COMPILER)).second;
                std::vector<std::string> args;
                if(app.arguments().count(fileType) > 0)
                    for(const std::string& o : (*app.arguments().find(fileType)).second)
                        for(const auto& s : kul::cli::asArgs(o))
                            args.push_back(s);
                for(const auto& s : kul::cli::asArgs(app.arg)) args.push_back(s);
                std::string cmd = compiler + " " + AppVars::INSTANCE().args();
                if(AppVars::INSTANCE().jargs().count(fileType) > 0)
                    cmd += " " + (*AppVars::INSTANCE().jargs().find(fileType)).second;
                // WE CHECK BEFORE USING THIS THAT A COMPILER EXISTS FOR EVERY FILE
                if(kul::LogMan::INSTANCE().inf() && !kul::LogMan::INSTANCE().dbg())
                    KOUT(NON) << compiler << " : " << src;
                const kul::code::CompilerProcessCapture& cpc = kul::code::Compilers::INSTANCE().get(compiler)->compileSource(cmd, args, incs, src, obj, app.m);
                kul::ScopeLock lock(push);
                cpcs.push_back(cpc);
                if(cpc.exception()) f = 1;
            }
        }
        const std::vector<kul::code::CompilerProcessCapture>& processCaptures(){return cpcs;}
};

class SCMGetter{
    private:
        kul::hash::map::S2S valids;
        static bool IS_SOLID(const std::string& r){
            return r.find("://") != std::string::npos || r.find("@") != std::string::npos;
        }
        static SCMGetter& INSTANCE(){
            static SCMGetter s;
            return s;
        }
        static const kul::SCM* GET_SCM(const kul::Dir& d, const std::string& r){
            std::vector<std::string> repos;
            if(IS_SOLID(r)) repos.push_back(r);
            else
                for(const std::string& s : Settings::INSTANCE().remoteRepos()) repos.push_back(s + r);
            for(const auto& repo : repos){
                try{
                    kul::Process g("git");
                    kul::ProcessCapture gp(g);
                    std::string r1(repo);
                    if(repo.find("http") != std::string::npos && repo.find("@") == std::string::npos)
                        r1 = repo.substr(0, repo.find("//") + 2) + "u:p@" + repo.substr(repo.find("//") + 2);
                    g.arg("ls-remote").arg(r1).start();
                    if(!gp.errs().size()) {
                        INSTANCE().valids.insert(d.path(), repo);
                        return &kul::scm::Manager::INSTANCE().get("git");
                    }
                }catch(const kul::proc::ExitException& e){}
                try{
                    kul::Process s("svn");
                    kul::ProcessCapture sp(s);
                    s.arg("ls").arg(repo).start();
                    if(!sp.errs().size()) {
                        INSTANCE().valids.insert(d.path(), repo);
                        return &kul::scm::Manager::INSTANCE().get("svn");
                    }
                }catch(const kul::proc::ExitException& e){}
            }
            std::stringstream ss;
            for(const auto& s : repos) ss << s << "\n";
            KEXCEPT(Exception, "SCM not found or not supported type(git/svn) for repo(s)\n"+ss.str()+"project:"+d.path());
        }
    public:
        static const std::string REPO(const kul::Dir& d, const std::string& r){
            if(INSTANCE().valids.count(d.path())) return (*INSTANCE().valids.find(d.path())).second;
            if(IS_SOLID(r)) INSTANCE().valids.insert(d.path(), r);
            else            GET_SCM(d, r);
            if(INSTANCE().valids.count(d.path())) return (*INSTANCE().valids.find(d.path())).second;
            KEXCEPT(Exception, "SCM not discovered for project: "+d.path());
        }
        static bool HAS(const kul::Dir& d){
            return (kul::Dir(d.join(".git")) || kul::Dir(d.join(".svn")));
        }
        static const kul::SCM* GET(const kul::Dir& d, const std::string& r){
            if(IS_SOLID(r)) INSTANCE().valids.insert(d.path(), r);
            if(kul::Dir(d.join(".git"))) return &kul::scm::Manager::INSTANCE().get("git");
            if(kul::Dir(d.join(".svn"))) return &kul::scm::Manager::INSTANCE().get("svn");
            return r.size() ? GET_SCM(d, r) : 0;
        }
};

}
#endif /* _MAIKEN_APP_HPP_ */

<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/session_manager.h"

#include <algorithm>

#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "base/stl_util-inl.h"
#include "media/base/data_buffer.h"
#include "remoting/base/protocol_decoder.h"
#include "remoting/host/client_connection.h"
#include "remoting/host/encoder.h"

namespace remoting {

// By default we capture 20 times a second. This number is obtained by
// experiment to provide good latency.
static const double kDefaultCaptureRate = 20.0;

// Interval that we perform rate regulation.
static const base::TimeDelta kRateControlInterval =
    base::TimeDelta::FromSeconds(1);

// We divide the pending update stream number by this value to determine the
// rate divider.
static const int kSlowDownFactor = 10;

// A list of dividers used to divide the max rate to determine the current
// capture rate.
static const int kRateDividers[] = {1, 2, 4, 8, 16};

SessionManager::SessionManager(
    MessageLoop* capture_loop,
    MessageLoop* encode_loop,
    MessageLoop* network_loop,
    Capturer* capturer,
    Encoder* encoder)
    : capture_loop_(capture_loop),
      encode_loop_(encode_loop),
      network_loop_(network_loop),
      capturer_(capturer),
      encoder_(encoder),
      rate_(kDefaultCaptureRate),
      started_(false),
      recordings_(0),
      max_rate_(kDefaultCaptureRate),
      rate_control_started_(false) {
  DCHECK(capture_loop_);
  DCHECK(encode_loop_);
  DCHECK(network_loop_);
}

SessionManager::~SessionManager() {
  clients_.clear();
}

void SessionManager::Start() {
  capture_loop_->PostTask(
      FROM_HERE, NewRunnableMethod(this, &SessionManager::DoStart));
}

void SessionManager::DoStart() {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  if (started_) {
    NOTREACHED() << "Record session already started";
    return;
  }

  started_ = true;
  DoCapture();

  // Starts the rate regulation.
  network_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoStartRateControl));
}

void SessionManager::DoStartRateControl() {
  DCHECK_EQ(network_loop_, MessageLoop::current());

  if (rate_control_started_) {
    NOTREACHED() << "Rate regulation already started";
    return;
  }
  rate_control_started_ = true;
  ScheduleNextRateControl();
}

void SessionManager::Pause() {
  capture_loop_->PostTask(
      FROM_HERE, NewRunnableMethod(this, &SessionManager::DoPause));
}

void SessionManager::DoPause() {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  if (!started_) {
    NOTREACHED() << "Record session not started";
    return;
  }

  started_ = false;

  // Pause the rate regulation.
  network_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoPauseRateControl));
}

void SessionManager::DoPauseRateControl() {
  DCHECK_EQ(network_loop_, MessageLoop::current());

  if (!rate_control_started_) {
    NOTREACHED() << "Rate regulation not started";
    return;
  }
  rate_control_started_ = false;
}

void SessionManager::SetMaxRate(double rate) {
  capture_loop_->PostTask(
      FROM_HERE, NewRunnableMethod(this, &SessionManager::DoSetMaxRate, rate));
}

void SessionManager::AddClient(scoped_refptr<ClientConnection> client) {
  // Gets the init information for the client.
  capture_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoGetInitInfo, client));
}

void SessionManager::RemoveClient(scoped_refptr<ClientConnection> client) {
  network_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoRemoveClient, client));
}

void SessionManager::DoCapture() {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  // Make sure we have at most two oustanding recordings. We can simply return
  // if we can't make a capture now, the next capture will be started by the
  // end of an encode operation.
  if (recordings_ >= 2 || !started_)
    return;

  base::Time now = base::Time::Now();
  base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
      static_cast<int>(base::Time::kMillisecondsPerSecond / rate_));
  base::TimeDelta elapsed = now - last_capture_time_;

  // If this method is called sonner than the required interval we return
  // immediately
  if (elapsed < interval)
    return;

  // At this point we are going to perform one capture so save the current time.
  last_capture_time_ = now;
  ++recordings_;

  // Before we actually do a capture, schedule the next one.
  ScheduleNextCapture();

  // And finally perform one capture.
  DCHECK(capturer_.get());
  capturer_->CaptureDirtyRects(
      NewRunnableMethod(this, &SessionManager::CaptureDoneTask));
}

void SessionManager::DoFinishEncode() {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  // Decrement the number of recording in process since we have completed
  // one cycle.
  --recordings_;

  // Try to do a capture again. Note that the following method may do nothing
  // if it is too early to perform a capture.
  if (rate_ > 0)
    DoCapture();
}

void SessionManager::DoEncode(const CaptureData *capture_data) {
  // Take ownership of capture_data.
  scoped_ptr<const CaptureData> capture_data_owner(capture_data);

  DCHECK_EQ(encode_loop_, MessageLoop::current());

  DCHECK(encoder_.get());

  // TODO(hclam): Enable |force_refresh| if a new client was
  // added.
  encoder_->SetSize(capture_data->width_, capture_data->height_);
  encoder_->SetPixelFormat(capture_data->pixel_format_);
  encoder_->Encode(
      capture_data->dirty_rects_,
      capture_data->data_,
      capture_data->data_strides_,
      false,
      NewCallback(this, &SessionManager::EncodeDataAvailableTask));
}

void SessionManager::DoSendUpdate(const UpdateStreamPacketHeader* header,
                                  const scoped_refptr<media::DataBuffer> data,
                                  Encoder::EncodingState state) {
  // Take ownership of header.
  scoped_ptr<const UpdateStreamPacketHeader> header_owner(header);
  DCHECK_EQ(network_loop_, MessageLoop::current());

  for (size_t i = 0; i < clients_.size(); ++i) {
    if (state & Encoder::EncodingStarting) {
      clients_[i]->SendBeginUpdateStreamMessage();
    }

    clients_[i]->SendUpdateStreamPacketMessage(header, data);

    if (state & Encoder::EncodingEnded) {
      clients_[i]->SendEndUpdateStreamMessage();
    }
  }
  delete header;
}

void SessionManager::DoSendInit(scoped_refptr<ClientConnection> client,
                                int width, int height) {
  DCHECK_EQ(network_loop_, MessageLoop::current());

  // Sends the client init information.
  client->SendInitClientMessage(width, height);
}

void SessionManager::DoGetInitInfo(scoped_refptr<ClientConnection> client) {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  // Sends the init message to the cleint.
  network_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoSendInit, client,
                        capturer_->GetWidth(), capturer_->GetHeight()));

  // And then add the client to the list so it can receive update stream.
  // It is important we do so in such order or the client will receive
  // update stream before init message.
  network_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoAddClient, client));
}

void SessionManager::DoSetRate(double rate) {
  DCHECK_EQ(capture_loop_, MessageLoop::current());
  if (rate == rate_)
    return;

  // Change the current capture rate.
  rate_ = rate;

  // If we have already started then schedule the next capture with the new
  // rate.
  if (started_)
    ScheduleNextCapture();
}

void SessionManager::DoSetMaxRate(double max_rate) {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  // TODO(hclam): Should also check for small epsilon.
  if (max_rate != 0) {
    max_rate_ = max_rate;
    DoSetRate(max_rate);
  } else {
    NOTREACHED() << "Rate is too small.";
  }
}

void SessionManager::DoAddClient(scoped_refptr<ClientConnection> client) {
  DCHECK_EQ(network_loop_, MessageLoop::current());

  // TODO(hclam): Force a full frame for next encode.
  clients_.push_back(client);
}

void SessionManager::DoRemoveClient(scoped_refptr<ClientConnection> client) {
  DCHECK_EQ(network_loop_, MessageLoop::current());

  // TODO(hclam): Is it correct to do to a scoped_refptr?
  ClientConnectionList::iterator it
      = std::find(clients_.begin(), clients_.end(), client);
  if (it != clients_.end())
    clients_.erase(it);
}

void SessionManager::DoRateControl() {
  DCHECK_EQ(network_loop_, MessageLoop::current());

  // If we have been paused then shutdown the rate regulation loop.
  if (!rate_control_started_)
    return;

  int max_pending_update_streams = 0;
  for (size_t i = 0; i < clients_.size(); ++i) {
    max_pending_update_streams =
        std::max(max_pending_update_streams,
                 clients_[i]->GetPendingUpdateStreamMessages());
  }

  // If |slow_down| equals zero, we have no slow down.
  size_t slow_down = max_pending_update_streams / kSlowDownFactor;
  // Set new_rate to -1 for checking later.
  double new_rate = -1;
  // If the slow down is too large.
  if (slow_down >= arraysize(kRateDividers)) {
    // Then we stop the capture completely.
    new_rate = 0;
  } else {
    // Slow down the capture rate using the divider.
    new_rate = max_rate_ / kRateDividers[slow_down];
  }
  DCHECK_NE(new_rate, -1.0);

  // Then set the rate.
  capture_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoSetRate, new_rate));
  ScheduleNextRateControl();
}

void SessionManager::ScheduleNextCapture() {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  if (rate_ == 0)
    return;

  base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
      static_cast<int>(base::Time::kMillisecondsPerSecond / rate_));
  capture_loop_->PostDelayedTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoCapture),
      interval.InMilliseconds());
}

void SessionManager::ScheduleNextRateControl() {
  network_loop_->PostDelayedTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoRateControl),
      kRateControlInterval.InMilliseconds());
}

void SessionManager::CaptureDoneTask() {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  scoped_ptr<CaptureData> data(new CaptureData);

  // Save results of the capture.
  capturer_->GetData(data->data_);
  capturer_->GetDataStride(data->data_strides_);
  capturer_->GetDirtyRects(&data->dirty_rects_);
  data->pixel_format_ = capturer_->GetPixelFormat();
  data->width_ = capturer_->GetWidth();
  data->height_ = capturer_->GetHeight();

  encode_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoEncode, data.release()));
}

void SessionManager::EncodeDataAvailableTask(
    const UpdateStreamPacketHeader *header,
    const scoped_refptr<media::DataBuffer>& data,
    Encoder::EncodingState state) {
  DCHECK_EQ(encode_loop_, MessageLoop::current());

  // Before a new encode task starts, notify clients a new update
  // stream is coming.
  // Notify this will keep a reference to the DataBuffer in the
  // task. The ownership will eventually pass to the ClientConnections.
  network_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this,
                        &SessionManager::DoSendUpdate,
                        header,
                        data,
                        state));

  if (state == Encoder::EncodingEnded) {
    capture_loop_->PostTask(
        FROM_HERE, NewRunnableMethod(this, &SessionManager::DoFinishEncode));
  }
}

}  // namespace remoting
<commit_msg>Fix double deletion in SessionManager<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/session_manager.h"

#include <algorithm>

#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "base/stl_util-inl.h"
#include "media/base/data_buffer.h"
#include "remoting/base/protocol_decoder.h"
#include "remoting/host/client_connection.h"
#include "remoting/host/encoder.h"

namespace remoting {

// By default we capture 20 times a second. This number is obtained by
// experiment to provide good latency.
static const double kDefaultCaptureRate = 20.0;

// Interval that we perform rate regulation.
static const base::TimeDelta kRateControlInterval =
    base::TimeDelta::FromSeconds(1);

// We divide the pending update stream number by this value to determine the
// rate divider.
static const int kSlowDownFactor = 10;

// A list of dividers used to divide the max rate to determine the current
// capture rate.
static const int kRateDividers[] = {1, 2, 4, 8, 16};

SessionManager::SessionManager(
    MessageLoop* capture_loop,
    MessageLoop* encode_loop,
    MessageLoop* network_loop,
    Capturer* capturer,
    Encoder* encoder)
    : capture_loop_(capture_loop),
      encode_loop_(encode_loop),
      network_loop_(network_loop),
      capturer_(capturer),
      encoder_(encoder),
      rate_(kDefaultCaptureRate),
      started_(false),
      recordings_(0),
      max_rate_(kDefaultCaptureRate),
      rate_control_started_(false) {
  DCHECK(capture_loop_);
  DCHECK(encode_loop_);
  DCHECK(network_loop_);
}

SessionManager::~SessionManager() {
  clients_.clear();
}

void SessionManager::Start() {
  capture_loop_->PostTask(
      FROM_HERE, NewRunnableMethod(this, &SessionManager::DoStart));
}

void SessionManager::DoStart() {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  if (started_) {
    NOTREACHED() << "Record session already started";
    return;
  }

  started_ = true;
  DoCapture();

  // Starts the rate regulation.
  network_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoStartRateControl));
}

void SessionManager::DoStartRateControl() {
  DCHECK_EQ(network_loop_, MessageLoop::current());

  if (rate_control_started_) {
    NOTREACHED() << "Rate regulation already started";
    return;
  }
  rate_control_started_ = true;
  ScheduleNextRateControl();
}

void SessionManager::Pause() {
  capture_loop_->PostTask(
      FROM_HERE, NewRunnableMethod(this, &SessionManager::DoPause));
}

void SessionManager::DoPause() {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  if (!started_) {
    NOTREACHED() << "Record session not started";
    return;
  }

  started_ = false;

  // Pause the rate regulation.
  network_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoPauseRateControl));
}

void SessionManager::DoPauseRateControl() {
  DCHECK_EQ(network_loop_, MessageLoop::current());

  if (!rate_control_started_) {
    NOTREACHED() << "Rate regulation not started";
    return;
  }
  rate_control_started_ = false;
}

void SessionManager::SetMaxRate(double rate) {
  capture_loop_->PostTask(
      FROM_HERE, NewRunnableMethod(this, &SessionManager::DoSetMaxRate, rate));
}

void SessionManager::AddClient(scoped_refptr<ClientConnection> client) {
  // Gets the init information for the client.
  capture_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoGetInitInfo, client));
}

void SessionManager::RemoveClient(scoped_refptr<ClientConnection> client) {
  network_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoRemoveClient, client));
}

void SessionManager::DoCapture() {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  // Make sure we have at most two oustanding recordings. We can simply return
  // if we can't make a capture now, the next capture will be started by the
  // end of an encode operation.
  if (recordings_ >= 2 || !started_)
    return;

  base::Time now = base::Time::Now();
  base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
      static_cast<int>(base::Time::kMillisecondsPerSecond / rate_));
  base::TimeDelta elapsed = now - last_capture_time_;

  // If this method is called sonner than the required interval we return
  // immediately
  if (elapsed < interval)
    return;

  // At this point we are going to perform one capture so save the current time.
  last_capture_time_ = now;
  ++recordings_;

  // Before we actually do a capture, schedule the next one.
  ScheduleNextCapture();

  // And finally perform one capture.
  DCHECK(capturer_.get());
  capturer_->CaptureDirtyRects(
      NewRunnableMethod(this, &SessionManager::CaptureDoneTask));
}

void SessionManager::DoFinishEncode() {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  // Decrement the number of recording in process since we have completed
  // one cycle.
  --recordings_;

  // Try to do a capture again. Note that the following method may do nothing
  // if it is too early to perform a capture.
  if (rate_ > 0)
    DoCapture();
}

void SessionManager::DoEncode(const CaptureData *capture_data) {
  // Take ownership of capture_data.
  scoped_ptr<const CaptureData> capture_data_owner(capture_data);

  DCHECK_EQ(encode_loop_, MessageLoop::current());

  DCHECK(encoder_.get());

  // TODO(hclam): Enable |force_refresh| if a new client was
  // added.
  encoder_->SetSize(capture_data->width_, capture_data->height_);
  encoder_->SetPixelFormat(capture_data->pixel_format_);
  encoder_->Encode(
      capture_data->dirty_rects_,
      capture_data->data_,
      capture_data->data_strides_,
      false,
      NewCallback(this, &SessionManager::EncodeDataAvailableTask));
}

void SessionManager::DoSendUpdate(const UpdateStreamPacketHeader* header,
                                  const scoped_refptr<media::DataBuffer> data,
                                  Encoder::EncodingState state) {
  // Take ownership of header.
  scoped_ptr<const UpdateStreamPacketHeader> header_owner(header);
  DCHECK_EQ(network_loop_, MessageLoop::current());

  for (size_t i = 0; i < clients_.size(); ++i) {
    if (state & Encoder::EncodingStarting) {
      clients_[i]->SendBeginUpdateStreamMessage();
    }

    clients_[i]->SendUpdateStreamPacketMessage(header, data);

    if (state & Encoder::EncodingEnded) {
      clients_[i]->SendEndUpdateStreamMessage();
    }
  }
}

void SessionManager::DoSendInit(scoped_refptr<ClientConnection> client,
                                int width, int height) {
  DCHECK_EQ(network_loop_, MessageLoop::current());

  // Sends the client init information.
  client->SendInitClientMessage(width, height);
}

void SessionManager::DoGetInitInfo(scoped_refptr<ClientConnection> client) {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  // Sends the init message to the cleint.
  network_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoSendInit, client,
                        capturer_->GetWidth(), capturer_->GetHeight()));

  // And then add the client to the list so it can receive update stream.
  // It is important we do so in such order or the client will receive
  // update stream before init message.
  network_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoAddClient, client));
}

void SessionManager::DoSetRate(double rate) {
  DCHECK_EQ(capture_loop_, MessageLoop::current());
  if (rate == rate_)
    return;

  // Change the current capture rate.
  rate_ = rate;

  // If we have already started then schedule the next capture with the new
  // rate.
  if (started_)
    ScheduleNextCapture();
}

void SessionManager::DoSetMaxRate(double max_rate) {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  // TODO(hclam): Should also check for small epsilon.
  if (max_rate != 0) {
    max_rate_ = max_rate;
    DoSetRate(max_rate);
  } else {
    NOTREACHED() << "Rate is too small.";
  }
}

void SessionManager::DoAddClient(scoped_refptr<ClientConnection> client) {
  DCHECK_EQ(network_loop_, MessageLoop::current());

  // TODO(hclam): Force a full frame for next encode.
  clients_.push_back(client);
}

void SessionManager::DoRemoveClient(scoped_refptr<ClientConnection> client) {
  DCHECK_EQ(network_loop_, MessageLoop::current());

  // TODO(hclam): Is it correct to do to a scoped_refptr?
  ClientConnectionList::iterator it
      = std::find(clients_.begin(), clients_.end(), client);
  if (it != clients_.end())
    clients_.erase(it);
}

void SessionManager::DoRateControl() {
  DCHECK_EQ(network_loop_, MessageLoop::current());

  // If we have been paused then shutdown the rate regulation loop.
  if (!rate_control_started_)
    return;

  int max_pending_update_streams = 0;
  for (size_t i = 0; i < clients_.size(); ++i) {
    max_pending_update_streams =
        std::max(max_pending_update_streams,
                 clients_[i]->GetPendingUpdateStreamMessages());
  }

  // If |slow_down| equals zero, we have no slow down.
  size_t slow_down = max_pending_update_streams / kSlowDownFactor;
  // Set new_rate to -1 for checking later.
  double new_rate = -1;
  // If the slow down is too large.
  if (slow_down >= arraysize(kRateDividers)) {
    // Then we stop the capture completely.
    new_rate = 0;
  } else {
    // Slow down the capture rate using the divider.
    new_rate = max_rate_ / kRateDividers[slow_down];
  }
  DCHECK_NE(new_rate, -1.0);

  // Then set the rate.
  capture_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoSetRate, new_rate));
  ScheduleNextRateControl();
}

void SessionManager::ScheduleNextCapture() {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  if (rate_ == 0)
    return;

  base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
      static_cast<int>(base::Time::kMillisecondsPerSecond / rate_));
  capture_loop_->PostDelayedTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoCapture),
      interval.InMilliseconds());
}

void SessionManager::ScheduleNextRateControl() {
  network_loop_->PostDelayedTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoRateControl),
      kRateControlInterval.InMilliseconds());
}

void SessionManager::CaptureDoneTask() {
  DCHECK_EQ(capture_loop_, MessageLoop::current());

  scoped_ptr<CaptureData> data(new CaptureData);

  // Save results of the capture.
  capturer_->GetData(data->data_);
  capturer_->GetDataStride(data->data_strides_);
  capturer_->GetDirtyRects(&data->dirty_rects_);
  data->pixel_format_ = capturer_->GetPixelFormat();
  data->width_ = capturer_->GetWidth();
  data->height_ = capturer_->GetHeight();

  encode_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &SessionManager::DoEncode, data.release()));
}

void SessionManager::EncodeDataAvailableTask(
    const UpdateStreamPacketHeader *header,
    const scoped_refptr<media::DataBuffer>& data,
    Encoder::EncodingState state) {
  DCHECK_EQ(encode_loop_, MessageLoop::current());

  // Before a new encode task starts, notify clients a new update
  // stream is coming.
  // Notify this will keep a reference to the DataBuffer in the
  // task. The ownership will eventually pass to the ClientConnections.
  network_loop_->PostTask(
      FROM_HERE,
      NewRunnableMethod(this,
                        &SessionManager::DoSendUpdate,
                        header,
                        data,
                        state));

  if (state == Encoder::EncodingEnded) {
    capture_loop_->PostTask(
        FROM_HERE, NewRunnableMethod(this, &SessionManager::DoFinishEncode));
  }
}

}  // namespace remoting
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h"

#include <string.h>

#include "webrtc/base/checks.h"

namespace webrtc {

AudioEncoderCopyRed::AudioEncoderCopyRed(const Config& config)
    : speech_encoder_(config.speech_encoder),
      red_payload_type_(config.payload_type) {
  CHECK(speech_encoder_) << "Speech encoder not provided.";
}

AudioEncoderCopyRed::~AudioEncoderCopyRed() {
}

int AudioEncoderCopyRed::SampleRateHz() const {
  return speech_encoder_->SampleRateHz();
}

int AudioEncoderCopyRed::RtpTimestampRateHz() const {
  return speech_encoder_->RtpTimestampRateHz();
}

int AudioEncoderCopyRed::NumChannels() const {
  return speech_encoder_->NumChannels();
}

size_t AudioEncoderCopyRed::MaxEncodedBytes() const {
  return 2 * speech_encoder_->MaxEncodedBytes();
}

size_t AudioEncoderCopyRed::Num10MsFramesInNextPacket() const {
  return speech_encoder_->Num10MsFramesInNextPacket();
}

size_t AudioEncoderCopyRed::Max10MsFramesInAPacket() const {
  return speech_encoder_->Max10MsFramesInAPacket();
}

int AudioEncoderCopyRed::GetTargetBitrate() const {
  return speech_encoder_->GetTargetBitrate();
}

void AudioEncoderCopyRed::SetTargetBitrate(int bits_per_second) {
  speech_encoder_->SetTargetBitrate(bits_per_second);
}

void AudioEncoderCopyRed::SetProjectedPacketLossRate(double fraction) {
  DCHECK_GE(fraction, 0.0);
  DCHECK_LE(fraction, 1.0);
  speech_encoder_->SetProjectedPacketLossRate(fraction);
}

AudioEncoder::EncodedInfo AudioEncoderCopyRed::EncodeInternal(
    uint32_t rtp_timestamp,
    const int16_t* audio,
    size_t max_encoded_bytes,
    uint8_t* encoded) {
  EncodedInfo info = speech_encoder_->Encode(
      rtp_timestamp, audio, static_cast<size_t>(SampleRateHz() / 100),
      max_encoded_bytes, encoded);
  CHECK_GE(max_encoded_bytes,
           info.encoded_bytes + secondary_info_.encoded_bytes);
  CHECK(info.redundant.empty()) << "Cannot use nested redundant encoders.";

  if (info.encoded_bytes > 0) {
    // |info| will be implicitly cast to an EncodedInfoLeaf struct, effectively
    // discarding the (empty) vector of redundant information. This is
    // intentional.
    info.redundant.push_back(info);
    DCHECK_EQ(info.redundant.size(), 1u);
    if (secondary_info_.encoded_bytes > 0) {
      memcpy(&encoded[info.encoded_bytes], secondary_encoded_.data(),
             secondary_info_.encoded_bytes);
      info.redundant.push_back(secondary_info_);
      DCHECK_EQ(info.redundant.size(), 2u);
    }
    // Save primary to secondary.
    secondary_encoded_.SetSize(info.encoded_bytes);
    memcpy(secondary_encoded_.data(), encoded, info.encoded_bytes);
    secondary_info_ = info;
    DCHECK_EQ(info.speech, info.redundant[0].speech);
  }
  // Update main EncodedInfo.
  info.payload_type = red_payload_type_;
  info.encoded_bytes = 0;
  for (std::vector<EncodedInfoLeaf>::const_iterator it = info.redundant.begin();
       it != info.redundant.end(); ++it) {
    info.encoded_bytes += it->encoded_bytes;
  }
  return info;
}

}  // namespace webrtc
<commit_msg>copy-red: Fill an rtc::Buffer with bytes the easy way<commit_after>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h"

#include <string.h>

#include "webrtc/base/checks.h"

namespace webrtc {

AudioEncoderCopyRed::AudioEncoderCopyRed(const Config& config)
    : speech_encoder_(config.speech_encoder),
      red_payload_type_(config.payload_type) {
  CHECK(speech_encoder_) << "Speech encoder not provided.";
}

AudioEncoderCopyRed::~AudioEncoderCopyRed() {
}

int AudioEncoderCopyRed::SampleRateHz() const {
  return speech_encoder_->SampleRateHz();
}

int AudioEncoderCopyRed::RtpTimestampRateHz() const {
  return speech_encoder_->RtpTimestampRateHz();
}

int AudioEncoderCopyRed::NumChannels() const {
  return speech_encoder_->NumChannels();
}

size_t AudioEncoderCopyRed::MaxEncodedBytes() const {
  return 2 * speech_encoder_->MaxEncodedBytes();
}

size_t AudioEncoderCopyRed::Num10MsFramesInNextPacket() const {
  return speech_encoder_->Num10MsFramesInNextPacket();
}

size_t AudioEncoderCopyRed::Max10MsFramesInAPacket() const {
  return speech_encoder_->Max10MsFramesInAPacket();
}

int AudioEncoderCopyRed::GetTargetBitrate() const {
  return speech_encoder_->GetTargetBitrate();
}

void AudioEncoderCopyRed::SetTargetBitrate(int bits_per_second) {
  speech_encoder_->SetTargetBitrate(bits_per_second);
}

void AudioEncoderCopyRed::SetProjectedPacketLossRate(double fraction) {
  DCHECK_GE(fraction, 0.0);
  DCHECK_LE(fraction, 1.0);
  speech_encoder_->SetProjectedPacketLossRate(fraction);
}

AudioEncoder::EncodedInfo AudioEncoderCopyRed::EncodeInternal(
    uint32_t rtp_timestamp,
    const int16_t* audio,
    size_t max_encoded_bytes,
    uint8_t* encoded) {
  EncodedInfo info = speech_encoder_->Encode(
      rtp_timestamp, audio, static_cast<size_t>(SampleRateHz() / 100),
      max_encoded_bytes, encoded);
  CHECK_GE(max_encoded_bytes,
           info.encoded_bytes + secondary_info_.encoded_bytes);
  CHECK(info.redundant.empty()) << "Cannot use nested redundant encoders.";

  if (info.encoded_bytes > 0) {
    // |info| will be implicitly cast to an EncodedInfoLeaf struct, effectively
    // discarding the (empty) vector of redundant information. This is
    // intentional.
    info.redundant.push_back(info);
    DCHECK_EQ(info.redundant.size(), 1u);
    if (secondary_info_.encoded_bytes > 0) {
      memcpy(&encoded[info.encoded_bytes], secondary_encoded_.data(),
             secondary_info_.encoded_bytes);
      info.redundant.push_back(secondary_info_);
      DCHECK_EQ(info.redundant.size(), 2u);
    }
    // Save primary to secondary.
    secondary_encoded_.SetData(encoded, info.encoded_bytes);
    secondary_info_ = info;
    DCHECK_EQ(info.speech, info.redundant[0].speech);
  }
  // Update main EncodedInfo.
  info.payload_type = red_payload_type_;
  info.encoded_bytes = 0;
  for (std::vector<EncodedInfoLeaf>::const_iterator it = info.redundant.begin();
       it != info.redundant.end(); ++it) {
    info.encoded_bytes += it->encoded_bytes;
  }
  return info;
}

}  // namespace webrtc
<|endoftext|>


<commit_before>/*
    Copyright (c) 2016 VOCA AS / Harald Nkland

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to
    deal in the Software without restriction, including without limitation the
    rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    sell copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    IN THE SOFTWARE.
*/

#ifndef __ZMQ_ADDON_HPP_INCLUDED__
#define __ZMQ_ADDON_HPP_INCLUDED__

#include <zmq.hpp>
#include <deque>
#include <iomanip>
#include <sstream>

namespace zmq {

#ifdef ZMQ_HAS_RVALUE_REFS

/*
    This class handles multipart messaging. It is the C++ equivalent of zmsg.h,
    which is part of CZMQ (the high-level C binding). Furthermore, it is a major
    improvement compared to zmsg.hpp, which is part of the examples in the MQ
    Guide. Unnecessary copying is avoided by using move semantics to efficiently
    add/remove parts.
*/
class multipart_t
{
private:
    std::deque<message_t> m_parts;

public:
    multipart_t()
    {}

    multipart_t(socket_t& socket)
    {
        recv(socket);
    }

    multipart_t(const void *src, size_t size)
    {
        addmem(src, size);
    }

    multipart_t(const std::string& string)
    {
        addstr(string);
    }

    multipart_t(message_t&& message)
    {
        add(std::move(message));
    }

    multipart_t(multipart_t&& other)
    {
        std::swap(m_parts, other.m_parts);
    }

    multipart_t& operator=(multipart_t&& other)
    {
        std::swap(m_parts, other.m_parts);
        return *this;
    }

    virtual ~multipart_t()
    {
        clear();
    }

    void clear()
    {
        m_parts.clear();
    }

    size_t size() const
    {
        return m_parts.size();
    }

    bool empty() const
    {
        return m_parts.empty();
    }

    bool recv(socket_t& socket)
    {
        clear();
        bool more = true;
        while (more)
        {
            message_t message;
            if (!socket.recv(&message))
                return false;
            more = message.more();
            add(std::move(message));
        }
        return true;
    }

    bool send(socket_t& socket)
    {
        bool more = size() > 0;
        while (more)
        {
            message_t message = pop();
            more = size() > 0;
            if (!socket.send(message, more ? ZMQ_SNDMORE : 0))
                return false;
        }
        clear();
        return true;
    }

    void prepend(multipart_t&& other)
    {
        while (!other.empty())
            push(other.remove());
    }

    void append(multipart_t&& other)
    {
        while (!other.empty())
            add(other.pop());
    }

    void pushmem(const void *src, size_t size)
    {
        m_parts.push_front(message_t(src, size));
    }

    void addmem(const void *src, size_t size)
    {
        m_parts.push_back(message_t(src, size));
    }

    void pushstr(const std::string& string)
    {
        m_parts.push_front(message_t(string.data(), string.size()));
    }

    void addstr(const std::string& string)
    {
        m_parts.push_back(message_t(string.data(), string.size()));
    }

    template<typename T>
    void pushtyp(const T& type)
    {
        m_parts.push_front(message_t(&type, sizeof(type)));
    }

    template<typename T>
    void addtyp(const T& type)
    {
        m_parts.push_back(message_t(&type, sizeof(type)));
    }

    void push(message_t&& message)
    {
        m_parts.push_front(std::move(message));
    }

    void add(message_t&& message)
    {
        m_parts.push_back(std::move(message));
    }

    std::string popstr()
    {
        if (m_parts.empty())
            return "";
        std::string string(m_parts.front().data<char>(), m_parts.front().size());
        m_parts.pop_front();
        return string;
    }

    template<typename T>
    T poptyp()
    {
        if (m_parts.empty())
            return T();
        T type = *m_parts.front().data<T>();
        m_parts.pop_front();
        return type;
    }

    message_t pop()
    {
        if (m_parts.empty())
            return message_t(0);
        message_t message = std::move(m_parts.front());
        m_parts.pop_front();
        return message;
    }

    message_t remove()
    {
        if (m_parts.empty())
            return message_t(0);
        message_t message = std::move(m_parts.back());
        m_parts.pop_back();
        return message;
    }

    const message_t* peek(size_t index) const
    {
        if (index >= size())
            return nullptr;
        return &m_parts[index];
    }

    template<typename T>
    static multipart_t create(const T& type)
    {
        multipart_t multipart;
        multipart.addtyp(type);
        return multipart;
    }

    multipart_t clone() const
    {
        multipart_t multipart;
        for (size_t i = 0; i < size(); i++)
            multipart.addmem(m_parts[i].data(), m_parts[i].size());
        return multipart;
    }

    std::string str() const
    {
        std::stringstream ss;
        for (size_t i = 0; i < m_parts.size(); i++)
        {
            const unsigned char* data = m_parts[i].data<unsigned char>();
            size_t size = m_parts[i].size();

            // Dump the message as text or binary
            bool isText = true;
            for (size_t j = 0; j < size; j++)
            {
                if (data[j] < 32 || data[j] > 127)
                {
                    isText = false;
                    break;
                }
            }
            ss << "\n[" << std::dec << std::setw(3) << std::setfill('0') << size << "] ";
            if (size >= 1000)
            {
                ss << "... (to big to print)";
                continue;
            }
            for (size_t j = 0; j < size; j++)
            {
                if (isText)
                    ss << static_cast<char>(data[j]);
                else
                    ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<short>(data[j]);
            }
        }
        return ss.str();
    }

    bool equal(const multipart_t* other) const
    {
        if (size() != other->size())
            return false;
        for (size_t i = 0; i < size(); i++)
            if (!peek(i)->equal(other->peek(i)))
                return false;
        return true;
    }

    static int test()
    {
        bool ok = true;
        float num = 0;
        std::string str = "";
        message_t msg;

        // Create two PAIR sockets and connect over inproc
        context_t context(1);
        socket_t output(context, ZMQ_PAIR);
        socket_t input(context, ZMQ_PAIR);
        output.bind("inproc://multipart.test");
        input.connect("inproc://multipart.test");

        // Test send and receive of single-frame message
        multipart_t multipart;
        assert(multipart.empty());

        multipart.push(message_t("Hello", 5));
        assert(multipart.size() == 1);

        ok = multipart.send(output);
        assert(multipart.empty());
        assert(ok);

        ok = multipart.recv(input);
        assert(multipart.size() == 1);
        assert(ok);

        msg = multipart.pop();
        assert(multipart.empty());
        assert(std::string(msg.data<char>(), msg.size()) == "Hello");

        // Test send and receive of multi-frame message
        multipart.addstr("A");
        multipart.addstr("BB");
        multipart.addstr("CCC");
        assert(multipart.size() == 3);

        multipart_t copy = multipart.clone();
        assert(copy.size() == 3);

        ok = copy.send(output);
        assert(copy.empty());
        assert(ok);

        ok = copy.recv(input);
        assert(copy.size() == 3);
        assert(ok);
        assert(copy.equal(&multipart));

        multipart.clear();
        assert(multipart.empty());

        // Test message frame manipulation
        multipart.add(message_t("Frame5", 6));
        multipart.addstr("Frame6");
        multipart.addstr("Frame7");
        multipart.addtyp(8.0f);
        multipart.addmem("Frame9", 6);
        multipart.push(message_t("Frame4", 6));
        multipart.pushstr("Frame3");
        multipart.pushstr("Frame2");
        multipart.pushtyp(1.0f);
        multipart.pushmem("Frame0", 6);
        assert(multipart.size() == 10);

        msg = multipart.remove();
        assert(multipart.size() == 9);
        assert(std::string(msg.data<char>(), msg.size()) == "Frame9");

        msg = multipart.pop();
        assert(multipart.size() == 8);
        assert(std::string(msg.data<char>(), msg.size()) == "Frame0");

        num = multipart.poptyp<float>();
        assert(multipart.size() == 7);
        assert(num == 1.0f);

        str = multipart.popstr();
        assert(multipart.size() == 6);
        assert(str == "Frame2");

        str = multipart.popstr();
        assert(multipart.size() == 5);
        assert(str == "Frame3");

        str = multipart.popstr();
        assert(multipart.size() == 4);
        assert(str == "Frame4");

        str = multipart.popstr();
        assert(multipart.size() == 3);
        assert(str == "Frame5");

        str = multipart.popstr();
        assert(multipart.size() == 2);
        assert(str == "Frame6");

        str = multipart.popstr();
        assert(multipart.size() == 1);
        assert(str == "Frame7");

        num = multipart.poptyp<float>();
        assert(multipart.empty());
        assert(num == 8.0f);

        // Test message constructors and concatenation
        multipart_t head("One", 3);
        head.addstr("Two");
        assert(head.size() == 2);

        multipart_t tail("One-hundred");
        tail.pushstr("Ninety-nine");
        assert(tail.size() == 2);

        multipart_t tmp(message_t("Fifty", 5));
        assert(tmp.size() == 1);

        multipart_t mid = multipart_t::create(49.0f);
        mid.append(std::move(tmp));
        assert(mid.size() == 2);
        assert(tmp.empty());

        multipart_t merged(std::move(mid));
        merged.prepend(std::move(head));
        merged.append(std::move(tail));
        assert(merged.size() == 6);
        assert(head.empty());
        assert(tail.empty());

        ok = merged.send(output);
        assert(merged.empty());
        assert(ok);

        multipart_t received(input);
        assert(received.size() == 6);

        str = received.popstr();
        assert(received.size() == 5);
        assert(str == "One");

        str = received.popstr();
        assert(received.size() == 4);
        assert(str == "Two");

        num = received.poptyp<float>();
        assert(received.size() == 3);
        assert(num == 49.0f);

        str = received.popstr();
        assert(received.size() == 2);
        assert(str == "Fifty");

        str = received.popstr();
        assert(received.size() == 1);
        assert(str == "Ninety-nine");

        str = received.popstr();
        assert(received.empty());
        assert(str == "One-hundred");

        return 0;
    }

private:
    // Disable implicit copying (moving is more efficient)
    multipart_t(const multipart_t& other) ZMQ_DELETED_FUNCTION;
    void operator=(const multipart_t& other) ZMQ_DELETED_FUNCTION;
};

#endif

}

#endif
<commit_msg>Enable passing flags in to `send()` and `recv()`<commit_after>/*
    Copyright (c) 2016 VOCA AS / Harald Nkland

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to
    deal in the Software without restriction, including without limitation the
    rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    sell copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    IN THE SOFTWARE.
*/

#ifndef __ZMQ_ADDON_HPP_INCLUDED__
#define __ZMQ_ADDON_HPP_INCLUDED__

#include <zmq.hpp>
#include <deque>
#include <iomanip>
#include <sstream>

namespace zmq {

#ifdef ZMQ_HAS_RVALUE_REFS

/*
    This class handles multipart messaging. It is the C++ equivalent of zmsg.h,
    which is part of CZMQ (the high-level C binding). Furthermore, it is a major
    improvement compared to zmsg.hpp, which is part of the examples in the MQ
    Guide. Unnecessary copying is avoided by using move semantics to efficiently
    add/remove parts.
*/
class multipart_t
{
private:
    std::deque<message_t> m_parts;

public:
    multipart_t()
    {}

    multipart_t(socket_t& socket)
    {
        recv(socket);
    }

    multipart_t(const void *src, size_t size)
    {
        addmem(src, size);
    }

    multipart_t(const std::string& string)
    {
        addstr(string);
    }

    multipart_t(message_t&& message)
    {
        add(std::move(message));
    }

    multipart_t(multipart_t&& other)
    {
        std::swap(m_parts, other.m_parts);
    }

    multipart_t& operator=(multipart_t&& other)
    {
        std::swap(m_parts, other.m_parts);
        return *this;
    }

    virtual ~multipart_t()
    {
        clear();
    }

    void clear()
    {
        m_parts.clear();
    }

    size_t size() const
    {
        return m_parts.size();
    }

    bool empty() const
    {
        return m_parts.empty();
    }

    bool recv(socket_t& socket, int flags = 0)
    {
        clear();
        bool more = true;
        while (more)
        {
            message_t message;
            if (!socket.recv(&message, flags))
                return false;
            more = message.more();
            add(std::move(message));
        }
        return true;
    }

    bool send(socket_t& socket, int flags = 0)
    {
        flags &= ~(ZMQ_SNDMORE);
        bool more = size() > 0;
        while (more)
        {
            message_t message = pop();
            more = size() > 0;
            if (!socket.send(message, (more ? ZMQ_SNDMORE : 0) | flags))
                return false;
        }
        clear();
        return true;
    }

    void prepend(multipart_t&& other)
    {
        while (!other.empty())
            push(other.remove());
    }

    void append(multipart_t&& other)
    {
        while (!other.empty())
            add(other.pop());
    }

    void pushmem(const void *src, size_t size)
    {
        m_parts.push_front(message_t(src, size));
    }

    void addmem(const void *src, size_t size)
    {
        m_parts.push_back(message_t(src, size));
    }

    void pushstr(const std::string& string)
    {
        m_parts.push_front(message_t(string.data(), string.size()));
    }

    void addstr(const std::string& string)
    {
        m_parts.push_back(message_t(string.data(), string.size()));
    }

    template<typename T>
    void pushtyp(const T& type)
    {
        m_parts.push_front(message_t(&type, sizeof(type)));
    }

    template<typename T>
    void addtyp(const T& type)
    {
        m_parts.push_back(message_t(&type, sizeof(type)));
    }

    void push(message_t&& message)
    {
        m_parts.push_front(std::move(message));
    }

    void add(message_t&& message)
    {
        m_parts.push_back(std::move(message));
    }

    std::string popstr()
    {
        if (m_parts.empty())
            return "";
        std::string string(m_parts.front().data<char>(), m_parts.front().size());
        m_parts.pop_front();
        return string;
    }

    template<typename T>
    T poptyp()
    {
        if (m_parts.empty())
            return T();
        T type = *m_parts.front().data<T>();
        m_parts.pop_front();
        return type;
    }

    message_t pop()
    {
        if (m_parts.empty())
            return message_t(0);
        message_t message = std::move(m_parts.front());
        m_parts.pop_front();
        return message;
    }

    message_t remove()
    {
        if (m_parts.empty())
            return message_t(0);
        message_t message = std::move(m_parts.back());
        m_parts.pop_back();
        return message;
    }

    const message_t* peek(size_t index) const
    {
        if (index >= size())
            return nullptr;
        return &m_parts[index];
    }

    template<typename T>
    static multipart_t create(const T& type)
    {
        multipart_t multipart;
        multipart.addtyp(type);
        return multipart;
    }

    multipart_t clone() const
    {
        multipart_t multipart;
        for (size_t i = 0; i < size(); i++)
            multipart.addmem(m_parts[i].data(), m_parts[i].size());
        return multipart;
    }

    std::string str() const
    {
        std::stringstream ss;
        for (size_t i = 0; i < m_parts.size(); i++)
        {
            const unsigned char* data = m_parts[i].data<unsigned char>();
            size_t size = m_parts[i].size();

            // Dump the message as text or binary
            bool isText = true;
            for (size_t j = 0; j < size; j++)
            {
                if (data[j] < 32 || data[j] > 127)
                {
                    isText = false;
                    break;
                }
            }
            ss << "\n[" << std::dec << std::setw(3) << std::setfill('0') << size << "] ";
            if (size >= 1000)
            {
                ss << "... (to big to print)";
                continue;
            }
            for (size_t j = 0; j < size; j++)
            {
                if (isText)
                    ss << static_cast<char>(data[j]);
                else
                    ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<short>(data[j]);
            }
        }
        return ss.str();
    }

    bool equal(const multipart_t* other) const
    {
        if (size() != other->size())
            return false;
        for (size_t i = 0; i < size(); i++)
            if (!peek(i)->equal(other->peek(i)))
                return false;
        return true;
    }

    static int test()
    {
        bool ok = true;
        float num = 0;
        std::string str = "";
        message_t msg;

        // Create two PAIR sockets and connect over inproc
        context_t context(1);
        socket_t output(context, ZMQ_PAIR);
        socket_t input(context, ZMQ_PAIR);
        output.bind("inproc://multipart.test");
        input.connect("inproc://multipart.test");

        // Test send and receive of single-frame message
        multipart_t multipart;
        assert(multipart.empty());

        multipart.push(message_t("Hello", 5));
        assert(multipart.size() == 1);

        ok = multipart.send(output);
        assert(multipart.empty());
        assert(ok);

        ok = multipart.recv(input);
        assert(multipart.size() == 1);
        assert(ok);

        msg = multipart.pop();
        assert(multipart.empty());
        assert(std::string(msg.data<char>(), msg.size()) == "Hello");

        // Test send and receive of multi-frame message
        multipart.addstr("A");
        multipart.addstr("BB");
        multipart.addstr("CCC");
        assert(multipart.size() == 3);

        multipart_t copy = multipart.clone();
        assert(copy.size() == 3);

        ok = copy.send(output);
        assert(copy.empty());
        assert(ok);

        ok = copy.recv(input);
        assert(copy.size() == 3);
        assert(ok);
        assert(copy.equal(&multipart));

        multipart.clear();
        assert(multipart.empty());

        // Test message frame manipulation
        multipart.add(message_t("Frame5", 6));
        multipart.addstr("Frame6");
        multipart.addstr("Frame7");
        multipart.addtyp(8.0f);
        multipart.addmem("Frame9", 6);
        multipart.push(message_t("Frame4", 6));
        multipart.pushstr("Frame3");
        multipart.pushstr("Frame2");
        multipart.pushtyp(1.0f);
        multipart.pushmem("Frame0", 6);
        assert(multipart.size() == 10);

        msg = multipart.remove();
        assert(multipart.size() == 9);
        assert(std::string(msg.data<char>(), msg.size()) == "Frame9");

        msg = multipart.pop();
        assert(multipart.size() == 8);
        assert(std::string(msg.data<char>(), msg.size()) == "Frame0");

        num = multipart.poptyp<float>();
        assert(multipart.size() == 7);
        assert(num == 1.0f);

        str = multipart.popstr();
        assert(multipart.size() == 6);
        assert(str == "Frame2");

        str = multipart.popstr();
        assert(multipart.size() == 5);
        assert(str == "Frame3");

        str = multipart.popstr();
        assert(multipart.size() == 4);
        assert(str == "Frame4");

        str = multipart.popstr();
        assert(multipart.size() == 3);
        assert(str == "Frame5");

        str = multipart.popstr();
        assert(multipart.size() == 2);
        assert(str == "Frame6");

        str = multipart.popstr();
        assert(multipart.size() == 1);
        assert(str == "Frame7");

        num = multipart.poptyp<float>();
        assert(multipart.empty());
        assert(num == 8.0f);

        // Test message constructors and concatenation
        multipart_t head("One", 3);
        head.addstr("Two");
        assert(head.size() == 2);

        multipart_t tail("One-hundred");
        tail.pushstr("Ninety-nine");
        assert(tail.size() == 2);

        multipart_t tmp(message_t("Fifty", 5));
        assert(tmp.size() == 1);

        multipart_t mid = multipart_t::create(49.0f);
        mid.append(std::move(tmp));
        assert(mid.size() == 2);
        assert(tmp.empty());

        multipart_t merged(std::move(mid));
        merged.prepend(std::move(head));
        merged.append(std::move(tail));
        assert(merged.size() == 6);
        assert(head.empty());
        assert(tail.empty());

        ok = merged.send(output);
        assert(merged.empty());
        assert(ok);

        multipart_t received(input);
        assert(received.size() == 6);

        str = received.popstr();
        assert(received.size() == 5);
        assert(str == "One");

        str = received.popstr();
        assert(received.size() == 4);
        assert(str == "Two");

        num = received.poptyp<float>();
        assert(received.size() == 3);
        assert(num == 49.0f);

        str = received.popstr();
        assert(received.size() == 2);
        assert(str == "Fifty");

        str = received.popstr();
        assert(received.size() == 1);
        assert(str == "Ninety-nine");

        str = received.popstr();
        assert(received.empty());
        assert(str == "One-hundred");

        return 0;
    }

private:
    // Disable implicit copying (moving is more efficient)
    multipart_t(const multipart_t& other) ZMQ_DELETED_FUNCTION;
    void operator=(const multipart_t& other) ZMQ_DELETED_FUNCTION;
};

#endif

}

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2014-2018, NetApp, Inc.
// 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.
//
// 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 <net/if.h>

#include <benchmark/benchmark.h>
#include <quant/quant.h>
#include <warpcore/warpcore.h>

extern "C" {
#include "pkt.h"
#include "quic.h"
#include "tls.h" // IWYU pragma: keep
}

static struct q_conn * c;
static struct w_engine * w;

static void BM_quic_encryption(benchmark::State & state)
{
    const auto len = uint16_t(state.range(0));
    const auto pne = uint16_t(state.range(1));
    struct w_iov * const v = q_alloc_iov(w, len, 0);
    struct w_iov * const x = q_alloc_iov(w, MAX_PKT_LEN, 0);

    meta(v).hdr.type = F_LH_INIT;
    meta(v).hdr.flags = F_LONG_HDR | meta(v).hdr.type;
    meta(v).hdr.hdr_len = 16;

    for (auto _ : state)
        benchmark::DoNotOptimize(enc_aead(c, v, x, pne * 16));
    state.SetBytesProcessed(int64_t(state.iterations() * len)); // NOLINT

    q_free_iov(x);
    q_free_iov(v);
}

BENCHMARK(BM_quic_encryption)
    ->RangeMultiplier(2)
    ->Ranges({{16, MAX_PKT_LEN}, {0, 1}})
    ->Complexity();

// BENCHMARK_MAIN()

int main(int argc, char ** argv)
{
    char i[IFNAMSIZ] = "lo"
#ifndef __linux__
                       "0"
#endif
        ;

    benchmark::Initialize(&argc, argv);
#ifndef NDEBUG
    util_dlevel = INF;
#endif
    w = q_init(i, nullptr, nullptr, nullptr, nullptr); // NOLINT
    c = q_bind(w, 55555);
    init_tls(c);
    init_hshk_prot(c);
    benchmark::RunSpecifiedBenchmarks();

    q_cleanup(w);
}
<commit_msg>Randomize buffer before benchmark<commit_after>// Copyright (c) 2014-2018, NetApp, Inc.
// 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.
//
// 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 <net/if.h>

#include <benchmark/benchmark.h>
#include <quant/quant.h>
#include <warpcore/warpcore.h>

extern "C" {
#include "pkt.h"
#include "quic.h"
#include "tls.h" // IWYU pragma: keep
}

static struct q_conn * c;
static struct w_engine * w;

static void BM_quic_encryption(benchmark::State & state)
{
    const auto len = uint16_t(state.range(0));
    const auto pne = uint16_t(state.range(1));
    struct w_iov * const v = q_alloc_iov(w, len, 0);
    struct w_iov * const x = q_alloc_iov(w, MAX_PKT_LEN, 0);

    arc4random_buf(v->buf, len);
    meta(v).hdr.type = F_LH_INIT;
    meta(v).hdr.flags = F_LONG_HDR | meta(v).hdr.type;
    meta(v).hdr.hdr_len = 16;

    for (auto _ : state)
        benchmark::DoNotOptimize(enc_aead(c, v, x, pne * 16));
    state.SetBytesProcessed(int64_t(state.iterations() * len)); // NOLINT

    q_free_iov(x);
    q_free_iov(v);
}

BENCHMARK(BM_quic_encryption)
    ->RangeMultiplier(2)
    ->Ranges({{16, MAX_PKT_LEN}, {0, 1}})
    // ->MinTime(3)
    // ->UseRealTime()
    ;

// BENCHMARK_MAIN()

int main(int argc, char ** argv)
{
    char i[IFNAMSIZ] = "lo"
#ifndef __linux__
                       "0"
#endif
        ;

    benchmark::Initialize(&argc, argv);
#ifndef NDEBUG
    util_dlevel = INF;
#endif
    w = q_init(i, nullptr, nullptr, nullptr, nullptr); // NOLINT
    c = q_bind(w, 55555);
    init_tls(c);
    init_hshk_prot(c);
    benchmark::RunSpecifiedBenchmarks();

    q_cleanup(w);
}
<|endoftext|>