<commit_before>// Initialize GUIDs (should be done only and at-least once per DLL/EXE)
#include <initguid.h>
#include "guids.h"

#include "ext-common.h"
#include "ext-utils.h"
#include "commands.h"
#include "log.h"
#include "shell-ext.h"

namespace utils = seafile::utils;

namespace {

const int kWorktreeCacheExpireMSecs = 3 * 1000;

} // namespace

std::unique_ptr<seafile::RepoInfoList> ShellExt::repos_cache_;
uint64_t ShellExt::cache_ts_;

// *********************** ShellExt *************************
ShellExt::ShellExt(seafile::RepoInfo::Status status)
  : main_menu_(0),
    index_(0),
    first_(0),
    last_(0)
{
    m_cRef = 0L;
    InterlockedIncrement(&g_cRefThisDll);

    sub_menu_ = CreateMenu();
    next_active_item_ = 0;
    status_ = status;

    // INITCOMMONCONTROLSEX used = {
    //     sizeof(INITCOMMONCONTROLSEX),
    //         ICC_LISTVIEW_CLASSES | ICC_WIN95_CLASSES | ICC_BAR_CLASSES | ICC_USEREX_CLASSES
    // };
    // InitCommonControlsEx(&used);
}

ShellExt::~ShellExt()
{
    InterlockedDecrement(&g_cRefThisDll);
}

STDMETHODIMP ShellExt::QueryInterface(REFIID riid, LPVOID FAR *ppv)
{
    if (ppv == 0)
        return E_POINTER;

    *ppv = NULL;

    if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown)) {
        *ppv = static_cast<LPSHELLEXTINIT>(this);
    }
    else if (IsEqualIID(riid, IID_IContextMenu)) {
        *ppv = static_cast<LPCONTEXTMENU>(this);
    }
    else if (IsEqualIID(riid, IID_IShellIconOverlayIdentifier)) {
        *ppv = static_cast<IShellIconOverlayIdentifier*>(this);
    }
    // else if (IsEqualIID(riid, IID_IContextMenu3))
    // {
    //     *ppv = static_cast<LPCONTEXTMENU3>(this);
    // }
    else
    {
        return E_NOINTERFACE;
    }

    AddRef();
    return S_OK;
}

STDMETHODIMP_(ULONG) ShellExt::AddRef()
{
    return ++m_cRef;
}

STDMETHODIMP_(ULONG) ShellExt::Release()
{
    if (--m_cRef)
        return m_cRef;

    delete this;

    return 0L;
}

bool ShellExt::getReposList(seafile::RepoInfoList *wts)
{
    seafile::utils::MutexLocker lock(&repos_cache_mutex_);

    uint64_t now = utils::currentMSecsSinceEpoch();
    if (repos_cache_ && now < cache_ts_ + kWorktreeCacheExpireMSecs) {
        *wts = *(repos_cache_.get());
        // seaf_ext_log("use cached repos list!");
        return true;
    }

    // no cached worktree list, send request to seafile client
    seafile::ListReposCommand cmd;
    seafile::RepoInfoList repos;
    if (!cmd.sendAndWait(&repos)) {
        // seaf_ext_log("ListReposCommand returned false!");
        return false;
    }

    cache_ts_ = utils::currentMSecsSinceEpoch();
    repos_cache_.reset(new seafile::RepoInfoList(repos));

    *wts = repos;
    return true;
}

bool ShellExt::pathInRepo(const std::string& path,
                          std::string *path_in_repo,
                          seafile::RepoInfo *repo)
{
    seafile::RepoInfoList repos;
    if (!getReposList(&repos)) {
        return false;
    }
    std::string p = utils::normalizedPath(path);

    for (size_t i = 0; i < repos.size(); i++) {
        std::string wt = repos[i].worktree;
        // seaf_ext_log ("work tree is %s, path is %s\n", wt.c_str(), p.c_str());
        if (p.size() >= wt.size() && p.substr(0, wt.size()) == wt) {
            *path_in_repo = p.substr(wt.size(), p.size() - wt.size());
            if (repo) {
                *repo = repos[i];
            }
            return true;
        }
    }

    return false;
}

bool ShellExt::isRepoTopDir(const std::string& path)
{
    seafile::RepoInfoList repos;
    if (!getReposList(&repos)) {
        return false;
    }

    std::string p = utils::normalizedPath(path);
    for (size_t i = 0; i < repos.size(); i++) {
        std::string wt = repos[i].worktree;
        if (p == wt) {
            return true;
        }
    }

    return false;
}

seafile::RepoInfo ShellExt::getRepoInfoByPath(const std::string& path)
{
    seafile::RepoInfoList repos;
    if (!getReposList(&repos)) {
        return seafile::RepoInfo();
    }

    std::string p = utils::normalizedPath(path);
    for (size_t i = 0; i < repos.size(); i++) {
        std::string wt = repos[i].worktree;
        if (p == wt) {
            return repos[i];
        }
    }

    return seafile::RepoInfo();
}

seafile::RepoInfo::Status
ShellExt::getRepoFileStatus(const std::string& repo_id,
                            const std::string& path_in_repo,
                            bool isdir)
{
    // TODO: get the files under the same folder in a single command to reduce overhead
    seafile::GetFileStatusCommand cmd(repo_id, path_in_repo, isdir);
    seafile::RepoInfo::Status status;
    if (!cmd.sendAndWait(&status)) {
        return seafile::RepoInfo::NoStatus;
    }

    return status;
}
<commit_msg>[ext] fixed a bug when there are repo names with common prefix<commit_after>// Initialize GUIDs (should be done only and at-least once per DLL/EXE)
#include <initguid.h>
#include "guids.h"

#include "ext-common.h"
#include "ext-utils.h"
#include "commands.h"
#include "log.h"
#include "shell-ext.h"

namespace utils = seafile::utils;

namespace {

const int kWorktreeCacheExpireMSecs = 3 * 1000;

} // namespace

std::unique_ptr<seafile::RepoInfoList> ShellExt::repos_cache_;
uint64_t ShellExt::cache_ts_;

// *********************** ShellExt *************************
ShellExt::ShellExt(seafile::RepoInfo::Status status)
  : main_menu_(0),
    index_(0),
    first_(0),
    last_(0)
{
    m_cRef = 0L;
    InterlockedIncrement(&g_cRefThisDll);

    sub_menu_ = CreateMenu();
    next_active_item_ = 0;
    status_ = status;

    // INITCOMMONCONTROLSEX used = {
    //     sizeof(INITCOMMONCONTROLSEX),
    //         ICC_LISTVIEW_CLASSES | ICC_WIN95_CLASSES | ICC_BAR_CLASSES | ICC_USEREX_CLASSES
    // };
    // InitCommonControlsEx(&used);
}

ShellExt::~ShellExt()
{
    InterlockedDecrement(&g_cRefThisDll);
}

STDMETHODIMP ShellExt::QueryInterface(REFIID riid, LPVOID FAR *ppv)
{
    if (ppv == 0)
        return E_POINTER;

    *ppv = NULL;

    if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown)) {
        *ppv = static_cast<LPSHELLEXTINIT>(this);
    }
    else if (IsEqualIID(riid, IID_IContextMenu)) {
        *ppv = static_cast<LPCONTEXTMENU>(this);
    }
    else if (IsEqualIID(riid, IID_IShellIconOverlayIdentifier)) {
        *ppv = static_cast<IShellIconOverlayIdentifier*>(this);
    }
    // else if (IsEqualIID(riid, IID_IContextMenu3))
    // {
    //     *ppv = static_cast<LPCONTEXTMENU3>(this);
    // }
    else
    {
        return E_NOINTERFACE;
    }

    AddRef();
    return S_OK;
}

STDMETHODIMP_(ULONG) ShellExt::AddRef()
{
    return ++m_cRef;
}

STDMETHODIMP_(ULONG) ShellExt::Release()
{
    if (--m_cRef)
        return m_cRef;

    delete this;

    return 0L;
}

bool ShellExt::getReposList(seafile::RepoInfoList *wts)
{
    seafile::utils::MutexLocker lock(&repos_cache_mutex_);

    uint64_t now = utils::currentMSecsSinceEpoch();
    if (repos_cache_ && now < cache_ts_ + kWorktreeCacheExpireMSecs) {
        *wts = *(repos_cache_.get());
        // seaf_ext_log("use cached repos list!");
        return true;
    }

    // no cached worktree list, send request to seafile client
    seafile::ListReposCommand cmd;
    seafile::RepoInfoList repos;
    if (!cmd.sendAndWait(&repos)) {
        // seaf_ext_log("ListReposCommand returned false!");
        return false;
    }

    cache_ts_ = utils::currentMSecsSinceEpoch();
    repos_cache_.reset(new seafile::RepoInfoList(repos));

    *wts = repos;
    return true;
}

bool ShellExt::pathInRepo(const std::string& path,
                          std::string *path_in_repo,
                          seafile::RepoInfo *repo)
{
    seafile::RepoInfoList repos;
    if (!getReposList(&repos)) {
        return false;
    }
    std::string p = utils::normalizedPath(path);

    for (size_t i = 0; i < repos.size(); i++) {
        std::string wt = repos[i].worktree;
        // seaf_ext_log ("work tree is %s, path is %s\n", wt.c_str(), p.c_str());
        if (p.size() >= wt.size() && p.substr(0, wt.size()) == wt) {
            if (p.size() > wt.size() && p[wt.size()] != '/') {
                continue;
            }
            *path_in_repo = p.substr(wt.size(), p.size() - wt.size());
            if (repo) {
                *repo = repos[i];
            }
            return true;
        }
    }

    return false;
}

bool ShellExt::isRepoTopDir(const std::string& path)
{
    seafile::RepoInfoList repos;
    if (!getReposList(&repos)) {
        return false;
    }

    std::string p = utils::normalizedPath(path);
    for (size_t i = 0; i < repos.size(); i++) {
        std::string wt = repos[i].worktree;
        if (p == wt) {
            return true;
        }
    }

    return false;
}

seafile::RepoInfo ShellExt::getRepoInfoByPath(const std::string& path)
{
    seafile::RepoInfoList repos;
    if (!getReposList(&repos)) {
        return seafile::RepoInfo();
    }

    std::string p = utils::normalizedPath(path);
    for (size_t i = 0; i < repos.size(); i++) {
        std::string wt = repos[i].worktree;
        if (p == wt) {
            return repos[i];
        }
    }

    return seafile::RepoInfo();
}

seafile::RepoInfo::Status
ShellExt::getRepoFileStatus(const std::string& repo_id,
                            const std::string& path_in_repo,
                            bool isdir)
{
    // TODO: get the files under the same folder in a single command to reduce overhead
    seafile::GetFileStatusCommand cmd(repo_id, path_in_repo, isdir);
    seafile::RepoInfo::Status status;
    if (!cmd.sendAndWait(&status)) {
        return seafile::RepoInfo::NoStatus;
    }

    return status;
}
<|endoftext|>


<commit_before>/*
 * MRustC - Mutabah's Rust Compiler
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * common.hpp
 * - Compiler-global common header
 */
#ifndef COMMON_HPP_INCLUDED
#define COMMON_HPP_INCLUDED

#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <cassert>
#include <sstream>
#include <memory>

#ifdef _MSC_VER
#define __attribute__(x)    /* no-op */
#endif

#define FMT(ss)    (static_cast<::std::ostringstream&&>(::std::ostringstream() << ss).str())
// XXX: Evil hack - Define 'mv$' to be ::std::move
#define mv$(x)    ::std::move(x)
#define box$(...) ::make_unique_ptr(::std::move(__VA_ARGS__))
#define rc_new$(...) ::make_shared_ptr(::std::move(__VA_ARGS__))

#include "include/debug.hpp"
#include "include/compile_error.hpp"

template<typename T>
::std::unique_ptr<T> make_unique_ptr(T&& v) {
    return ::std::unique_ptr<T>(new T(mv$(v)));
}
template<typename T>
::std::shared_ptr<T> make_shared_ptr(T&& v) {
    return ::std::shared_ptr<T>(new T(mv$(v)));
}
template<typename T>
::std::vector<T> make_vec1(T&& v) {
    ::std::vector<T>    rv;
    rv.push_back( mv$(v) );
    return rv;
}
template<typename T>
::std::vector<T> make_vec2(T v1, T v2) {
    ::std::vector<T>    rv;
    rv.reserve(2);
    rv.push_back( mv$(v1) );
    rv.push_back( mv$(v2) );
    return rv;
}
template<typename T>
::std::vector<T> make_vec3(T v1, T v2, T v3) {
    ::std::vector<T>    rv;
    rv.reserve(3);
    rv.push_back( mv$(v1) );
    rv.push_back( mv$(v2) );
    rv.push_back( mv$(v3) );
    return rv;
}

enum Ordering
{
    OrdLess = -1,
    OrdEqual,
    OrdGreater,
};
static inline Ordering ord(bool l, bool r)
{
    if(l == r)
        return OrdEqual;
    else if( l )
        return OrdGreater;
    else
        return OrdLess;
}
static inline Ordering ord(char l, char r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}

static inline Ordering ord(unsigned char l, unsigned char r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(unsigned short l, unsigned short r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(unsigned l, unsigned r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(unsigned long l, unsigned long r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(unsigned long long l, unsigned long long r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(signed char l, signed char r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(int l, int r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(short l, short r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(long l, long r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(long long l, long long r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(float l, float r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(double l, double r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}

static inline Ordering ord(const ::std::string& l, const ::std::string& r)
{
    if(l == r)
        return OrdEqual;
    else if( l > r )
        return OrdGreater;
    else
        return OrdLess;
}
template<typename T>
Ordering ord(const T& l, const T& r)
{
    return l.ord(r);
}
template<typename T, typename U>
Ordering ord(const ::std::pair<T,U>& l, const ::std::pair<T,U>& r)
{
    Ordering    rv;
    rv = ::ord(l.first, r.first);
    if(rv != OrdEqual)   return rv;
    rv = ::ord(l.second, r.second);
    return rv;
}
template<typename T>
Ordering ord(const ::std::vector<T>& l, const ::std::vector<T>& r)
{
    unsigned int i = 0;
    for(const auto& it : l)
    {
        if( i >= r.size() )
            return OrdGreater;

        auto rv = ::ord( it, r[i] );
        if( rv != OrdEqual )
            return rv;

        i ++;
    }

    if( i < r.size() )
        return OrdLess;
    return OrdEqual;
}
template<typename T, typename U>
Ordering ord(const ::std::map<T,U>& l, const ::std::map<T,U>& r)
{
    auto r_it = r.begin();
    for(const auto& le : l)
    {
        if( r_it == r.end() )
            return OrdGreater;
        auto rv = ::ord( le, *r_it );
        if( rv != OrdEqual )
            return rv;
        ++ r_it;
    }
    return OrdEqual;
}
#define ORD(a,b)    do { Ordering ORD_rv = ::ord(a,b); if( ORD_rv != ::OrdEqual )   return ORD_rv; } while(0)

template <typename T>
struct LList
{
    const LList*  m_prev;
    T   m_item;

    LList():
        m_prev(nullptr)
    {}
    LList(const LList* prev, T item):
        m_prev(prev),
        m_item( ::std::move(item) )
    {
    }

    LList end() const {
        return LList();
    }
    LList begin() const {
        return *this;
    }
    bool operator==(const LList& x) {
        return m_prev == x.m_prev;
    }
    bool operator!=(const LList& x) {
        return m_prev != x.m_prev;
    }
    void operator++() {
        assert(m_prev);
        *this = *m_prev;
    }
    const T& operator*() const {
        return m_item;
    }
};

template<typename T>
struct Join {
    const char *sep;
    const ::std::vector<T>& v;
    friend ::std::ostream& operator<<(::std::ostream& os, const Join& j) {
        if( j.v.size() > 0 )
            os << j.v[0];
        for( unsigned int i = 1; i < j.v.size(); i ++ )
            os << j.sep << j.v[i];
        return os;
    }
};
template<typename T>
inline Join<T> join(const char *sep, const ::std::vector<T> v) {
    return Join<T>({ sep, v });
}

namespace std {

template <typename T>
inline auto operator<<(::std::ostream& os, const T& v) -> decltype(v.fmt(os)) {
    return v.fmt(os);
}

template <typename T>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::vector<T*>& v) {
    if( v.size() > 0 )
    {
        bool is_first = true;
        for( const auto& i : v )
        {
            if(!is_first)
                os << ", ";
            is_first = false;
            os << *i;
        }
    }
    return os;
}

template <typename T>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::vector<T>& v) {
    if( v.size() > 0 )
    {
        bool is_first = true;
        for( const auto& i : v )
        {
            if(!is_first)
                os << ", ";
            is_first = false;
            os << i;
        }
    }
    return os;
}
template <typename T>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::set<T>& v) {
    if( v.size() > 0 )
    {
        bool is_first = true;
        for( const auto& i : v )
        {
            if(!is_first)
                os << ", ";
            is_first = false;
            os << i;
        }
    }
    return os;
}

template <typename T, typename U>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::pair<T,U>& v) {
    os << "(" << v.first << ", " << v.second << ")";
    return os;
}

template <typename T, typename U, class Cmp>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::map<T,U,Cmp>& v) {
    if( v.size() > 0 )
    {
        bool is_first = true;
        for( const auto& i : v )
        {
            if(!is_first)
                os << ", ";
            is_first = false;
            os << i.first << ": " << i.second;
        }
    }
    return os;
}

template <typename T, typename U, class Cmp>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::multimap<T,U,Cmp>& v) {
    if( v.size() > 0 )
    {
        bool is_first = true;
        for( const auto& i : v )
        {
            if(!is_first)
                os << ", ";
            is_first = false;
            os << i.first << ": " << i.second;
        }
    }
    return os;
}

}   // namespace std

struct FmtEscaped {
    const char* s;
    FmtEscaped(const ::std::string& s):
        s(s.c_str())
    {}
    // See main.cpp
    friend ::std::ostream& operator<<(::std::ostream& os, const FmtEscaped& x);
};

// -------------------------------------------------------------------
// --- Reversed iterable
template <typename T>
struct reversion_wrapper { T& iterable; };

template <typename T>
//auto begin (reversion_wrapper<T> w) { return ::std::rbegin(w.iterable); }
auto begin (reversion_wrapper<T> w) { return w.iterable.rbegin(); }

template <typename T>
//auto end (reversion_wrapper<T> w) { return ::std::rend(w.iterable); }
auto end (reversion_wrapper<T> w) { return w.iterable.rend(); }

template <typename T>
reversion_wrapper<T> reverse (T&& iterable) { return { iterable }; }

template<typename T>
struct RunIterable {
    const T& list;
    unsigned int ofs;
    ::std::pair<size_t,size_t> cur;
    RunIterable(const T& list):
        list(list), ofs(0)
    {
        advance();
    }
    void advance() {
        if( ofs < list.size() )
        {
            auto start = ofs;
            while(ofs < list.size() && list[ofs] == list[start])
                ofs ++;
            cur = ::std::make_pair(start, ofs-1);
        }
        else
        {
            ofs = list.size()+1;
        }
    }
    RunIterable<T> begin() { return *this; }
    RunIterable<T> end() { auto rv = *this; rv.ofs = list.size()+1; return rv; }
    bool operator==(const RunIterable<T>& x) {
        return x.ofs == ofs;
    }
    bool operator!=(const RunIterable<T>& x) {
        return !(*this == x);
    }
    void operator++() {
        advance();
    }
    const ::std::pair<size_t,size_t>& operator*() const {
        return this->cur;
    }
    const ::std::pair<size_t,size_t>* operator->() const {
        return &this->cur;
    }
};
template<typename T>
RunIterable<T> runs(const T& x) {
    return RunIterable<T>(x);
}

template<typename T>
class NullOnDrop {
    T*& ptr;
public:
    NullOnDrop(T*& ptr):
        ptr(ptr)
    {}
    ~NullOnDrop() {
        ptr = nullptr;
    }
};

#endif
<commit_msg>common - Change `mv$` to be a name macro to improve IDE usage<commit_after>/*
 * MRustC - Mutabah's Rust Compiler
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * common.hpp
 * - Compiler-global common header
 */
#ifndef COMMON_HPP_INCLUDED
#define COMMON_HPP_INCLUDED

#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <cassert>
#include <sstream>
#include <memory>

#ifdef _MSC_VER
#define __attribute__(x)    /* no-op */
#endif

#define FMT(ss)    (static_cast<::std::ostringstream&&>(::std::ostringstream() << ss).str())
// XXX: Evil hack - Define 'mv$' to be ::std::move
#define mv$ ::std::move
#define box$(...) ::make_unique_ptr(::std::move(__VA_ARGS__))
#define rc_new$(...) ::make_shared_ptr(::std::move(__VA_ARGS__))

#include "include/debug.hpp"
#include "include/compile_error.hpp"

template<typename T>
::std::unique_ptr<T> make_unique_ptr(T&& v) {
    return ::std::unique_ptr<T>(new T(mv$(v)));
}
template<typename T>
::std::shared_ptr<T> make_shared_ptr(T&& v) {
    return ::std::shared_ptr<T>(new T(mv$(v)));
}
template<typename T>
::std::vector<T> make_vec1(T&& v) {
    ::std::vector<T>    rv;
    rv.push_back( mv$(v) );
    return rv;
}
template<typename T>
::std::vector<T> make_vec2(T v1, T v2) {
    ::std::vector<T>    rv;
    rv.reserve(2);
    rv.push_back( mv$(v1) );
    rv.push_back( mv$(v2) );
    return rv;
}
template<typename T>
::std::vector<T> make_vec3(T v1, T v2, T v3) {
    ::std::vector<T>    rv;
    rv.reserve(3);
    rv.push_back( mv$(v1) );
    rv.push_back( mv$(v2) );
    rv.push_back( mv$(v3) );
    return rv;
}

enum Ordering
{
    OrdLess = -1,
    OrdEqual,
    OrdGreater,
};
static inline Ordering ord(bool l, bool r)
{
    if(l == r)
        return OrdEqual;
    else if( l )
        return OrdGreater;
    else
        return OrdLess;
}
static inline Ordering ord(char l, char r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}

static inline Ordering ord(unsigned char l, unsigned char r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(unsigned short l, unsigned short r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(unsigned l, unsigned r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(unsigned long l, unsigned long r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(unsigned long long l, unsigned long long r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(signed char l, signed char r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(int l, int r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(short l, short r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(long l, long r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(long long l, long long r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(float l, float r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}
static inline Ordering ord(double l, double r)
{
    return (l == r ? OrdEqual : (l > r ? OrdGreater : OrdLess));
}

static inline Ordering ord(const ::std::string& l, const ::std::string& r)
{
    if(l == r)
        return OrdEqual;
    else if( l > r )
        return OrdGreater;
    else
        return OrdLess;
}
template<typename T>
Ordering ord(const T& l, const T& r)
{
    return l.ord(r);
}
template<typename T, typename U>
Ordering ord(const ::std::pair<T,U>& l, const ::std::pair<T,U>& r)
{
    Ordering    rv;
    rv = ::ord(l.first, r.first);
    if(rv != OrdEqual)   return rv;
    rv = ::ord(l.second, r.second);
    return rv;
}
template<typename T>
Ordering ord(const ::std::vector<T>& l, const ::std::vector<T>& r)
{
    unsigned int i = 0;
    for(const auto& it : l)
    {
        if( i >= r.size() )
            return OrdGreater;

        auto rv = ::ord( it, r[i] );
        if( rv != OrdEqual )
            return rv;

        i ++;
    }

    if( i < r.size() )
        return OrdLess;
    return OrdEqual;
}
template<typename T, typename U>
Ordering ord(const ::std::map<T,U>& l, const ::std::map<T,U>& r)
{
    auto r_it = r.begin();
    for(const auto& le : l)
    {
        if( r_it == r.end() )
            return OrdGreater;
        auto rv = ::ord( le, *r_it );
        if( rv != OrdEqual )
            return rv;
        ++ r_it;
    }
    return OrdEqual;
}
#define ORD(a,b)    do { Ordering ORD_rv = ::ord(a,b); if( ORD_rv != ::OrdEqual )   return ORD_rv; } while(0)

template <typename T>
struct LList
{
    const LList*  m_prev;
    T   m_item;

    LList():
        m_prev(nullptr)
    {}
    LList(const LList* prev, T item):
        m_prev(prev),
        m_item( ::std::move(item) )
    {
    }

    LList end() const {
        return LList();
    }
    LList begin() const {
        return *this;
    }
    bool operator==(const LList& x) {
        return m_prev == x.m_prev;
    }
    bool operator!=(const LList& x) {
        return m_prev != x.m_prev;
    }
    void operator++() {
        assert(m_prev);
        *this = *m_prev;
    }
    const T& operator*() const {
        return m_item;
    }
};

template<typename T>
struct Join {
    const char *sep;
    const ::std::vector<T>& v;
    friend ::std::ostream& operator<<(::std::ostream& os, const Join& j) {
        if( j.v.size() > 0 )
            os << j.v[0];
        for( unsigned int i = 1; i < j.v.size(); i ++ )
            os << j.sep << j.v[i];
        return os;
    }
};
template<typename T>
inline Join<T> join(const char *sep, const ::std::vector<T> v) {
    return Join<T>({ sep, v });
}

namespace std {

template <typename T>
inline auto operator<<(::std::ostream& os, const T& v) -> decltype(v.fmt(os)) {
    return v.fmt(os);
}

template <typename T>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::vector<T*>& v) {
    if( v.size() > 0 )
    {
        bool is_first = true;
        for( const auto& i : v )
        {
            if(!is_first)
                os << ", ";
            is_first = false;
            os << *i;
        }
    }
    return os;
}

template <typename T>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::vector<T>& v) {
    if( v.size() > 0 )
    {
        bool is_first = true;
        for( const auto& i : v )
        {
            if(!is_first)
                os << ", ";
            is_first = false;
            os << i;
        }
    }
    return os;
}
template <typename T>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::set<T>& v) {
    if( v.size() > 0 )
    {
        bool is_first = true;
        for( const auto& i : v )
        {
            if(!is_first)
                os << ", ";
            is_first = false;
            os << i;
        }
    }
    return os;
}

template <typename T, typename U>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::pair<T,U>& v) {
    os << "(" << v.first << ", " << v.second << ")";
    return os;
}

template <typename T, typename U, class Cmp>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::map<T,U,Cmp>& v) {
    if( v.size() > 0 )
    {
        bool is_first = true;
        for( const auto& i : v )
        {
            if(!is_first)
                os << ", ";
            is_first = false;
            os << i.first << ": " << i.second;
        }
    }
    return os;
}

template <typename T, typename U, class Cmp>
inline ::std::ostream& operator<<(::std::ostream& os, const ::std::multimap<T,U,Cmp>& v) {
    if( v.size() > 0 )
    {
        bool is_first = true;
        for( const auto& i : v )
        {
            if(!is_first)
                os << ", ";
            is_first = false;
            os << i.first << ": " << i.second;
        }
    }
    return os;
}

}   // namespace std

struct FmtEscaped {
    const char* s;
    FmtEscaped(const ::std::string& s):
        s(s.c_str())
    {}
    // See main.cpp
    friend ::std::ostream& operator<<(::std::ostream& os, const FmtEscaped& x);
};

// -------------------------------------------------------------------
// --- Reversed iterable
template <typename T>
struct reversion_wrapper { T& iterable; };

template <typename T>
//auto begin (reversion_wrapper<T> w) { return ::std::rbegin(w.iterable); }
auto begin (reversion_wrapper<T> w) { return w.iterable.rbegin(); }

template <typename T>
//auto end (reversion_wrapper<T> w) { return ::std::rend(w.iterable); }
auto end (reversion_wrapper<T> w) { return w.iterable.rend(); }

template <typename T>
reversion_wrapper<T> reverse (T&& iterable) { return { iterable }; }

template<typename T>
struct RunIterable {
    const T& list;
    unsigned int ofs;
    ::std::pair<size_t,size_t> cur;
    RunIterable(const T& list):
        list(list), ofs(0)
    {
        advance();
    }
    void advance() {
        if( ofs < list.size() )
        {
            auto start = ofs;
            while(ofs < list.size() && list[ofs] == list[start])
                ofs ++;
            cur = ::std::make_pair(start, ofs-1);
        }
        else
        {
            ofs = list.size()+1;
        }
    }
    RunIterable<T> begin() { return *this; }
    RunIterable<T> end() { auto rv = *this; rv.ofs = list.size()+1; return rv; }
    bool operator==(const RunIterable<T>& x) {
        return x.ofs == ofs;
    }
    bool operator!=(const RunIterable<T>& x) {
        return !(*this == x);
    }
    void operator++() {
        advance();
    }
    const ::std::pair<size_t,size_t>& operator*() const {
        return this->cur;
    }
    const ::std::pair<size_t,size_t>* operator->() const {
        return &this->cur;
    }
};
template<typename T>
RunIterable<T> runs(const T& x) {
    return RunIterable<T>(x);
}

template<typename T>
class NullOnDrop {
    T*& ptr;
public:
    NullOnDrop(T*& ptr):
        ptr(ptr)
    {}
    ~NullOnDrop() {
        ptr = nullptr;
    }
};

#endif
<|endoftext|>


<commit_before>#include "config.h"

#include <fstream>
#include <rapidxml.hpp>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>

using namespace Metre;
using namespace rapidxml;

namespace {
  std::string const any_element = "any";
  std::string const xmlns = "http://surevine.com/xmlns/metre/config";
  std::string const root_name = "config";

  bool xmlbool(const char * val) {
    if (!val) return false;
    switch (val[0]) {
    case 't':
    case 'T':
    case 'y':
    case 'Y':
    case '1':
      return true;
    }
    return false;
  }

  std::unique_ptr<Config::Domain> parse_domain(xml_node<> * domain, SESSION_TYPE def) {
    std::string name;
    bool forward = (def == INT || def == COMP);
    SESSION_TYPE sess = def;
    bool tls_required = true;
    bool block = false;
    if (any_element == domain->name()) {
      name = "";
    } else {
      auto name_a = domain->first_attribute("name");
      if (!name_a) {
        throw std::runtime_error("Missing name for domain element");
      }
      name = name_a->value();
    }
    auto forward_a = domain->first_attribute("forward");
    if (forward_a) {
      forward = xmlbool(forward_a->value());
    }
    auto block_a = domain->first_attribute("block");
    if (block_a) {
      block = xmlbool(block_a->value());
    }
    auto transport = domain->first_node("transport");
    if (transport) {
      auto type_a = transport->first_attribute("type");
      if (type_a) {
        std::string type = type_a->value();
        if (type == "s2s") {
          sess = S2S;
        } else if (type == "114") {
          sess = COMP;
        }
      }
      auto tls_a = transport->first_attribute("sec");
      if (tls_a) {
        tls_required = xmlbool(tls_a->value());
      }
    }
    std::unique_ptr<Config::Domain> dom(new Config::Domain(name, sess, forward, tls_required, block));
    auto x509t = domain->first_node("x509");
    if (x509t) {
      auto chain_a = x509t->first_attribute("chain");
      if (chain_a) {
        std::string chain = chain_a->value();
        auto pkey_a = x509t->first_attribute("pkey");
        if (pkey_a) {
          std::string pkey = pkey_a->value();
          dom->x509(chain, pkey);
        }
      }
    }
    return dom;
  }

  Config * s_config = nullptr;

  bool openssl_init = false;
}

Config::Domain::Domain(std::string const & domain, SESSION_TYPE transport_type, bool forward, bool require_tls, bool block)
  : m_domain(domain), m_type(transport_type), m_forward(forward), m_require_tls(require_tls), m_block(block), m_auth(), m_ssl_ctx(nullptr) {
}

Config::Domain::~Domain() {
  if (m_ssl_ctx) {
    SSL_CTX_free(m_ssl_ctx);
    m_ssl_ctx = nullptr;
  }
}

int Config::Domain::verify_callback_cb(int preverify_ok, X509_STORE_CTX *store) {
  return 1;
}

void Config::Domain::x509(std::string const & chain, std::string const & pkey) {
  if (!openssl_init) {
    SSL_library_init();
    ERR_load_crypto_strings();
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();
    if (RAND_poll() == 0) {
      throw std::runtime_error("OpenSSL init failed");
    }
    openssl_init = true;
  }
  if (m_ssl_ctx) {
    SSL_CTX_free(m_ssl_ctx);
    m_ssl_ctx = nullptr;
  }
  m_ssl_ctx = SSL_CTX_new(SSLv23_method());
  SSL_CTX_set_options(m_ssl_ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_ALL);
  SSL_CTX_set_verify(m_ssl_ctx, SSL_VERIFY_PEER, Config::Domain::verify_callback_cb);
  if (SSL_CTX_use_certificate_chain_file(m_ssl_ctx, chain.c_str()) != 1) {
    throw std::runtime_error("Couldn't load chain file");
  }
  if (SSL_CTX_use_PrivateKey_file(m_ssl_ctx, pkey.c_str(), SSL_FILETYPE_PEM) != 1) {
    throw std::runtime_error("Couldn't load keyfile");
  }
}

Config::Config(std::string const & filename) : m_config_str() {
  load(filename);
  s_config = this;
}

void Config::load(std::string const & filename) {
  std::ifstream file(filename);
  std::string str{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
  m_config_str = std::move(str);
  rapidxml::xml_document<> doc;
  doc.parse<parse_full>(const_cast<char *>(m_config_str.c_str()));
  auto root_node = doc.first_node();
  if (xmlns != root_node->xmlns()) {
    throw std::runtime_error("Wrong namespace for config");
  }
  if (root_name != root_node->name()) {
    throw std::runtime_error("Wrong name for config");
  }
  auto globals = root_node->first_node("globals");
  if (globals) {
    auto default_domain = globals->first_node("domain");
    if (default_domain) {
      auto name_a = default_domain->first_attribute("name");
      if (name_a) {
        m_default_domain = name_a->value();
      }
    }
  }
  auto internal = root_node->first_node("local");
  if (internal) {
    for (auto domain = internal->first_node("domain"); domain; domain = domain->next_sibling("domain")) {
      std::unique_ptr<Config::Domain> dom = std::move(parse_domain(domain, INT));
      m_domains[dom->domain()] = std::move(dom);
    }
  }
  auto external = root_node->first_node("remote");
  if (external) {
    for (auto domain = external->first_node(); domain; domain = domain->next_sibling()) {
      if (domain->type() == rapidxml::node_comment) continue;
      std::unique_ptr<Config::Domain> dom = std::move(parse_domain(domain, INT));
      m_domains[dom->domain()] = std::move(dom);
    }
  }
  auto it = m_domains.find("");
  if (it == m_domains.end()) {
    m_domains[""] = std::unique_ptr<Config::Domain>(new Config::Domain("", INT, false, true, true));
  }
}

Config::Domain const & Config::domain(std::string const & dom) const {
  auto it = m_domains.find(dom);
  if (it == m_domains.end()) {
    it = m_domains.find("");
  }
  return *(*it).second;
}

Config const & Config::config() {
  return *s_config;
}
<commit_msg>Bit more TLS, some DANE prep<commit_after>#include "config.h"

#include <fstream>
#include <rapidxml.hpp>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>

using namespace Metre;
using namespace rapidxml;

namespace {
  std::string const any_element = "any";
  std::string const xmlns = "http://surevine.com/xmlns/metre/config";
  std::string const root_name = "config";

  bool xmlbool(const char * val) {
    if (!val) return false;
    switch (val[0]) {
    case 't':
    case 'T':
    case 'y':
    case 'Y':
    case '1':
      return true;
    }
    return false;
  }

  std::unique_ptr<Config::Domain> parse_domain(xml_node<> * domain, SESSION_TYPE def) {
    std::string name;
    bool forward = (def == INT || def == COMP);
    SESSION_TYPE sess = def;
    bool tls_required = true;
    bool block = false;
    if (any_element == domain->name()) {
      name = "";
    } else {
      auto name_a = domain->first_attribute("name");
      if (!name_a) {
        throw std::runtime_error("Missing name for domain element");
      }
      name = name_a->value();
    }
    auto forward_a = domain->first_attribute("forward");
    if (forward_a) {
      forward = xmlbool(forward_a->value());
    }
    auto block_a = domain->first_attribute("block");
    if (block_a) {
      block = xmlbool(block_a->value());
    }
    auto transport = domain->first_node("transport");
    if (transport) {
      auto type_a = transport->first_attribute("type");
      if (type_a) {
        std::string type = type_a->value();
        if (type == "s2s") {
          sess = S2S;
        } else if (type == "114") {
          sess = COMP;
        }
      }
      auto tls_a = transport->first_attribute("sec");
      if (tls_a) {
        tls_required = xmlbool(tls_a->value());
      }
    }
    std::unique_ptr<Config::Domain> dom(new Config::Domain(name, sess, forward, tls_required, block));
    auto x509t = domain->first_node("x509");
    if (x509t) {
      auto chain_a = x509t->first_attribute("chain");
      if (chain_a) {
        std::string chain = chain_a->value();
        auto pkey_a = x509t->first_attribute("pkey");
        if (pkey_a) {
          std::string pkey = pkey_a->value();
          dom->x509(chain, pkey);
        }
      }
    }
    return dom;
  }

  Config * s_config = nullptr;

  bool openssl_init = false;
}

Config::Domain::Domain(std::string const & domain, SESSION_TYPE transport_type, bool forward, bool require_tls, bool block)
  : m_domain(domain), m_type(transport_type), m_forward(forward), m_require_tls(require_tls), m_block(block), m_auth(), m_ssl_ctx(nullptr) {
}

Config::Domain::~Domain() {
  if (m_ssl_ctx) {
    SSL_CTX_free(m_ssl_ctx);
    m_ssl_ctx = nullptr;
  }
}

int Config::Domain::verify_callback_cb(int preverify_ok, X509_STORE_CTX *store) {
  return 1;
}

void Config::Domain::x509(std::string const & chain, std::string const & pkey) {
  if (!openssl_init) {
    SSL_library_init();
    ERR_load_crypto_strings();
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();
    if (RAND_poll() == 0) {
      throw std::runtime_error("OpenSSL init failed");
    }
    openssl_init = true;
  }
  if (m_ssl_ctx) {
    SSL_CTX_free(m_ssl_ctx);
    m_ssl_ctx = nullptr;
  }
  m_ssl_ctx = SSL_CTX_new(SSLv23_method());
  SSL_CTX_set_options(m_ssl_ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_ALL);
  SSL_CTX_set_verify(m_ssl_ctx, SSL_VERIFY_PEER, Config::Domain::verify_callback_cb);
  if (SSL_CTX_use_certificate_chain_file(m_ssl_ctx, chain.c_str()) != 1) {
    throw std::runtime_error("Couldn't load chain file");
  }
  if (SSL_CTX_use_PrivateKey_file(m_ssl_ctx, pkey.c_str(), SSL_FILETYPE_PEM) != 1) {
    throw std::runtime_error("Couldn't load keyfile");
  }
  if (SSL_CTX_check_private_key(m_ssl_ctx)) {
    throw std::runtime_error("Private key mismatch");
  }
  SSL_CTX_set_purpose(m_ssl_ctx, X509_PURPOSE_SSL_SERVER);
}

Config::Config(std::string const & filename) : m_config_str() {
  load(filename);
  s_config = this;
}

void Config::load(std::string const & filename) {
  std::ifstream file(filename);
  std::string str{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
  m_config_str = std::move(str);
  rapidxml::xml_document<> doc;
  doc.parse<parse_full>(const_cast<char *>(m_config_str.c_str()));
  auto root_node = doc.first_node();
  if (xmlns != root_node->xmlns()) {
    throw std::runtime_error("Wrong namespace for config");
  }
  if (root_name != root_node->name()) {
    throw std::runtime_error("Wrong name for config");
  }
  auto globals = root_node->first_node("globals");
  if (globals) {
    auto default_domain = globals->first_node("domain");
    if (default_domain) {
      auto name_a = default_domain->first_attribute("name");
      if (name_a) {
        m_default_domain = name_a->value();
      }
    }
  }
  auto internal = root_node->first_node("local");
  if (internal) {
    for (auto domain = internal->first_node("domain"); domain; domain = domain->next_sibling("domain")) {
      std::unique_ptr<Config::Domain> dom = std::move(parse_domain(domain, INT));
      m_domains[dom->domain()] = std::move(dom);
    }
  }
  auto external = root_node->first_node("remote");
  if (external) {
    for (auto domain = external->first_node(); domain; domain = domain->next_sibling()) {
      if (domain->type() == rapidxml::node_comment) continue;
      std::unique_ptr<Config::Domain> dom = std::move(parse_domain(domain, INT));
      m_domains[dom->domain()] = std::move(dom);
    }
  }
  auto it = m_domains.find("");
  if (it == m_domains.end()) {
    m_domains[""] = std::unique_ptr<Config::Domain>(new Config::Domain("", INT, false, true, true));
  }
}

Config::Domain const & Config::domain(std::string const & dom) const {
  auto it = m_domains.find(dom);
  if (it == m_domains.end()) {
    it = m_domains.find("");
  }
  return *(*it).second;
}

Config const & Config::config() {
  return *s_config;
}
<|endoftext|>


<commit_before>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2019-2020 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "common/formatting.h"
#include "dxil_bytecode.h"

namespace DXIL
{
enum class ShaderTag
{
  ShaderFlags = 0,
  Geometry = 1,
  Domain = 2,
  Hull = 3,
  Compute = 4,
};

void Program::FetchComputeProperties(DXBC::Reflection *reflection)
{
  for(const Function &f : m_Functions)
  {
    if(f.name.beginsWith("dx.op.threadId"))
    {
      SigParameter param;
      param.systemValue = ShaderBuiltin::DispatchThreadIndex;
      param.compCount = 3;
      param.regChannelMask = param.channelUsedMask = 0x7;
      param.semanticIdxName = param.semanticName = "threadId";
      reflection->InputSig.push_back(param);
    }
    else if(f.name.beginsWith("dx.op.groupId"))
    {
      SigParameter param;
      param.systemValue = ShaderBuiltin::GroupIndex;
      param.compCount = 3;
      param.regChannelMask = param.channelUsedMask = 0x7;
      param.semanticIdxName = param.semanticName = "groupID";
      reflection->InputSig.push_back(param);
    }
    else if(f.name.beginsWith("dx.op.threadIdInGroup"))
    {
      SigParameter param;
      param.systemValue = ShaderBuiltin::GroupThreadIndex;
      param.compCount = 3;
      param.regChannelMask = param.channelUsedMask = 0x7;
      param.semanticIdxName = param.semanticName = "threadIdInGroup";
      reflection->InputSig.push_back(param);
    }
    else if(f.name.beginsWith("dx.op.flattenedThreadIdInGroup"))
    {
      SigParameter param;
      param.systemValue = ShaderBuiltin::GroupFlatIndex;
      param.compCount = 1;
      param.regChannelMask = param.channelUsedMask = 0x1;
      param.semanticIdxName = param.semanticName = "flattenedThreadIdInGroup";
      reflection->InputSig.push_back(param);
    }
  }

  for(size_t i = 0; i < m_NamedMeta.size(); i++)
  {
    if(m_NamedMeta[i].name == "dx.entryPoints")
    {
      // expect only one child for this, DX doesn't support multiple entry points for compute
      // shaders
      RDCASSERTEQUAL(m_NamedMeta[i].children.size(), 1);
      Metadata &entry = *m_NamedMeta[i].children[0];
      RDCASSERTEQUAL(entry.children.size(), 5);
      Metadata &tags = *entry.children[4];

      for(size_t t = 0; t < tags.children.size(); t += 2)
      {
        RDCASSERT(tags.children[t]->value);
        if(ShaderTag(tags.children[t]->val->val.uv[0]) == ShaderTag::Compute)
        {
          Metadata &threadDim = *tags.children[t + 1];
          RDCASSERTEQUAL(threadDim.children.size(), 3);
          reflection->DispatchThreadsDimension[0] = threadDim.children[0]->val->val.uv[0];
          reflection->DispatchThreadsDimension[1] = threadDim.children[1]->val->val.uv[0];
          reflection->DispatchThreadsDimension[2] = threadDim.children[2]->val->val.uv[0];
          return;
        }
      }

      break;
    }
  }

  RDCERR("Couldn't find thread dimension tag in shader");

  reflection->DispatchThreadsDimension[0] = 1;
  reflection->DispatchThreadsDimension[1] = 1;
  reflection->DispatchThreadsDimension[2] = 1;
}

DXBC::Reflection *Program::GetReflection()
{
  RDCWARN("Unimplemented DXIL::Program::GetReflection()");
  return new DXBC::Reflection;
}

D3D_PRIMITIVE_TOPOLOGY Program::GetOutputTopology()
{
  if(m_Type != DXBC::ShaderType::Geometry && m_Type != DXBC::ShaderType::Domain)
    return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;

  for(size_t i = 0; i < m_NamedMeta.size(); i++)
  {
    if(m_NamedMeta[i].name == "dx.entryPoints")
    {
      // expect only one child for this, DX doesn't support multiple entry points for compute
      // shaders
      RDCASSERTEQUAL(m_NamedMeta[i].children.size(), 1);
      Metadata &entry = *m_NamedMeta[i].children[0];
      RDCASSERTEQUAL(entry.children.size(), 5);
      Metadata &tags = *entry.children[4];

      for(size_t t = 0; t < tags.children.size(); t += 2)
      {
        RDCASSERT(tags.children[t]->value);
        if(ShaderTag(tags.children[t]->val->val.uv[0]) == ShaderTag::Geometry)
        {
          Metadata &geomData = *tags.children[t + 1];
          RDCASSERTEQUAL(geomData.children.size(), 5);
          return (D3D_PRIMITIVE_TOPOLOGY)geomData.children[3]->val->val.uv[0];
        }
      }

      break;
    }
  }

  RDCERR("Couldn't find topology tag in shader");

  return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
}

};    // namespace DXIL
<commit_msg>Start adding reflection based on the metadata<commit_after>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2019-2020 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "common/formatting.h"
#include "dxil_bytecode.h"

namespace DXIL
{
enum class ShaderTag
{
  ShaderFlags = 0,
  Geometry = 1,
  Domain = 2,
  Hull = 3,
  Compute = 4,
};

enum class ResField
{
  ID = 0,
  VarDecl = 1,
  Name = 2,
  Space = 3,
  RegBase = 4,
  RegCount = 5,

  // SRV
  SRVShape = 6,
  SRVSampleCount = 7,
  SRVTags = 8,

  // UAV
  UAVShape = 6,
  UAVGloballyCoherent = 7,
  UAVHiddenCounter = 8,
  UAVRasterOrder = 9,
  UAVTags = 10,

  // CBuffer
  CBufferByteSize = 6,
  CBufferTags = 7,

  // Sampler
  SamplerType = 6,
  SamplerTags = 7,
};

template <typename T>
T getival(const Metadata *m)
{
  return T(m->val->val.uv[0]);
}

void Program::FetchComputeProperties(DXBC::Reflection *reflection)
{
  for(const Function &f : m_Functions)
  {
    if(f.name.beginsWith("dx.op.threadId"))
    {
      SigParameter param;
      param.systemValue = ShaderBuiltin::DispatchThreadIndex;
      param.compCount = 3;
      param.regChannelMask = param.channelUsedMask = 0x7;
      param.semanticIdxName = param.semanticName = "threadId";
      reflection->InputSig.push_back(param);
    }
    else if(f.name.beginsWith("dx.op.groupId"))
    {
      SigParameter param;
      param.systemValue = ShaderBuiltin::GroupIndex;
      param.compCount = 3;
      param.regChannelMask = param.channelUsedMask = 0x7;
      param.semanticIdxName = param.semanticName = "groupID";
      reflection->InputSig.push_back(param);
    }
    else if(f.name.beginsWith("dx.op.threadIdInGroup"))
    {
      SigParameter param;
      param.systemValue = ShaderBuiltin::GroupThreadIndex;
      param.compCount = 3;
      param.regChannelMask = param.channelUsedMask = 0x7;
      param.semanticIdxName = param.semanticName = "threadIdInGroup";
      reflection->InputSig.push_back(param);
    }
    else if(f.name.beginsWith("dx.op.flattenedThreadIdInGroup"))
    {
      SigParameter param;
      param.systemValue = ShaderBuiltin::GroupFlatIndex;
      param.compCount = 1;
      param.regChannelMask = param.channelUsedMask = 0x1;
      param.semanticIdxName = param.semanticName = "flattenedThreadIdInGroup";
      reflection->InputSig.push_back(param);
    }
  }

  for(size_t i = 0; i < m_NamedMeta.size(); i++)
  {
    if(m_NamedMeta[i].name == "dx.entryPoints")
    {
      // expect only one child for this, DX doesn't support multiple entry points for compute
      // shaders
      RDCASSERTEQUAL(m_NamedMeta[i].children.size(), 1);
      Metadata &entry = *m_NamedMeta[i].children[0];
      RDCASSERTEQUAL(entry.children.size(), 5);
      Metadata &tags = *entry.children[4];

      for(size_t t = 0; t < tags.children.size(); t += 2)
      {
        RDCASSERT(tags.children[t]->value);
        if(getival<ShaderTag>(tags.children[t]) == ShaderTag::Compute)
        {
          Metadata &threadDim = *tags.children[t + 1];
          RDCASSERTEQUAL(threadDim.children.size(), 3);
          reflection->DispatchThreadsDimension[0] = getival<uint32_t>(threadDim.children[0]);
          reflection->DispatchThreadsDimension[1] = getival<uint32_t>(threadDim.children[1]);
          reflection->DispatchThreadsDimension[2] = getival<uint32_t>(threadDim.children[2]);
          return;
        }
      }

      break;
    }
  }

  RDCERR("Couldn't find thread dimension tag in shader");

  reflection->DispatchThreadsDimension[0] = 1;
  reflection->DispatchThreadsDimension[1] = 1;
  reflection->DispatchThreadsDimension[2] = 1;
}

D3D_PRIMITIVE_TOPOLOGY Program::GetOutputTopology()
{
  if(m_Type != DXBC::ShaderType::Geometry && m_Type != DXBC::ShaderType::Domain)
    return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;

  for(size_t i = 0; i < m_NamedMeta.size(); i++)
  {
    if(m_NamedMeta[i].name == "dx.entryPoints")
    {
      // expect only one child for this, DX doesn't support multiple entry points for compute
      // shaders
      RDCASSERTEQUAL(m_NamedMeta[i].children.size(), 1);
      Metadata &entry = *m_NamedMeta[i].children[0];
      RDCASSERTEQUAL(entry.children.size(), 5);
      Metadata &tags = *entry.children[4];

      for(size_t t = 0; t < tags.children.size(); t += 2)
      {
        RDCASSERT(tags.children[t]->value);
        if(getival<ShaderTag>(tags.children[t]) == ShaderTag::Geometry)
        {
          Metadata &geomData = *tags.children[t + 1];
          RDCASSERTEQUAL(geomData.children.size(), 5);
          return getival<D3D_PRIMITIVE_TOPOLOGY>(geomData.children[3]);
        }
      }

      break;
    }
  }

  RDCERR("Couldn't find topology tag in shader");

  return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
}

struct DXMeta
{
  struct
  {
    const Metadata *contents = NULL;
    const Metadata *defines = NULL;
    const Metadata *mainFileName = NULL;
    const Metadata *args = NULL;
  } source;

  const Metadata *version = NULL;
  const Metadata *valver = NULL;
  const Metadata *shaderModel = NULL;
  const Metadata *resources = NULL;
  const Metadata *typeAnnotations = NULL;
  const Metadata *viewIdState = NULL;
  const Metadata *entryPoints = NULL;

  DXMeta(const rdcarray<NamedMetadata> &namedMeta)
  {
    DXMeta &dx = *this;

    for(size_t i = 0; i < namedMeta.size(); i++)
    {
#define GRAB_META(metaname)          \
  if(namedMeta[i].name == #metaname) \
    metaname = &namedMeta[i];

      GRAB_META(dx.source.contents);
      GRAB_META(dx.source.defines);
      GRAB_META(dx.source.mainFileName);
      GRAB_META(dx.source.args);
      GRAB_META(dx.version);
      GRAB_META(dx.valver);
      GRAB_META(dx.shaderModel);
      GRAB_META(dx.resources);
      GRAB_META(dx.typeAnnotations);
      GRAB_META(dx.viewIdState);
      GRAB_META(dx.entryPoints);

#undef GRAB_META
    }
  }
};

DXBC::Reflection *Program::GetReflection()
{
  using namespace DXBC;

  Reflection *refl = new Reflection;

  DXMeta dx(m_NamedMeta);

  if(dx.resources)
  {
    RDCASSERTEQUAL(dx.resources->children.size(), 1);

    const Metadata *resList = dx.resources->children[0];
    RDCASSERTEQUAL(resList->children.size(), 4);

    const Metadata *SRVs = resList->children[0];
    if(SRVs)
    {
      for(const Metadata *r : SRVs->children)
      {
        ShaderInputBind bind;
        bind.name = r->children[(size_t)ResField::Name]->str;
        bind.space = getival<uint32_t>(r->children[(size_t)ResField::Space]);
        bind.reg = getival<uint32_t>(r->children[(size_t)ResField::RegBase]);
        bind.bindCount = getival<uint32_t>(r->children[(size_t)ResField::RegCount]);
        refl->SRVs.push_back(bind);
      }
    }

    const Metadata *UAVs = resList->children[1];
    if(UAVs)
    {
      for(const Metadata *r : UAVs->children)
      {
        ShaderInputBind bind;
        bind.name = r->children[(size_t)ResField::Name]->str;
        bind.space = getival<uint32_t>(r->children[(size_t)ResField::Space]);
        bind.reg = getival<uint32_t>(r->children[(size_t)ResField::RegBase]);
        bind.bindCount = getival<uint32_t>(r->children[(size_t)ResField::RegCount]);
        refl->UAVs.push_back(bind);
      }
    }

    const Metadata *CBVs = resList->children[2];
    if(CBVs)
    {
      for(const Metadata *r : CBVs->children)
      {
        CBuffer bind;
        bind.name = r->children[(size_t)ResField::Name]->str;
        bind.identifier = getival<uint32_t>(r->children[(size_t)ResField::ID]);
        bind.space = getival<uint32_t>(r->children[(size_t)ResField::Space]);
        bind.reg = getival<uint32_t>(r->children[(size_t)ResField::RegBase]);
        bind.bindCount = getival<uint32_t>(r->children[(size_t)ResField::RegCount]);

        bind.descriptor.type = CBuffer::Descriptor::TYPE_CBUFFER;
        bind.descriptor.byteSize = getival<uint32_t>(r->children[(size_t)ResField::CBufferByteSize]);

        const Type *cbufType = r->children[(size_t)ResField::VarDecl]->type;

        // variable should be a pointer to the cbuffer type
        RDCASSERT(cbufType->type == Type::Pointer);
        cbufType = cbufType->inner;

        for(const Type *member : cbufType->members)
        {
          (void)member;
        }

        refl->CBuffers.push_back(bind);
      }
    }

    const Metadata *Samplers = resList->children[3];
    if(Samplers)
    {
      for(const Metadata *r : Samplers->children)
      {
        ShaderInputBind bind;
        bind.name = r->children[(size_t)ResField::Name]->str;
        bind.space = getival<uint32_t>(r->children[(size_t)ResField::Space]);
        bind.reg = getival<uint32_t>(r->children[(size_t)ResField::RegBase]);
        bind.bindCount = getival<uint32_t>(r->children[(size_t)ResField::RegCount]);
        bind.type = ShaderInputBind::TYPE_SAMPLER;
        bind.dimension = ShaderInputBind::DIM_UNKNOWN;
        bind.numComps = 0;
        refl->Samplers.push_back(bind);
      }
    }
  }

  return refl;
}

};    // namespace DXIL
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc. All rights reserved.
//
// 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 <cmath>

#include "examples/libfuzzer/libfuzzer_example.pb.h"
#include "port/protobuf.h"
#include "src/libfuzzer/libfuzzer_macro.h"

protobuf_mutator::protobuf::LogSilencer log_silincer;

DEFINE_BINARY_PROTO_FUZZER(const libfuzzer_example::Msg& message) {
  static PostProcessorRegistration reg = {
      [](libfuzzer_example::Msg* message, unsigned int seed) {
        if (seed % 2) {
          message->set_optional_uint64(
              std::hash<std::string>{}(message->optional_string()));
        }

        if (message->has_any()) {
          auto* any = message->mutable_any();

          // Guide mutator to usefull 'Any' types.
          static const char* const expected_types[] = {
              "type.googleapis.com/google.protobuf.DescriptorProto",
              "type.googleapis.com/google.protobuf.FileDescriptorProto",
          };

          if (!std::count(std::begin(expected_types), std::end(expected_types),
                          any->type_url())) {
            const size_t num =
                (std::end(expected_types) - std::begin(expected_types));
            any->set_type_url(expected_types[seed % num]);
          }
        }
      }};

  protobuf_mutator::protobuf::FileDescriptorProto file;

  // Emulate a bug.
  if (message.optional_uint64() ==
          std::hash<std::string>{}(message.optional_string()) &&
      message.optional_string() == "abcdefghijklmnopqrstuvwxyz" &&
      !std::isnan(message.optional_float()) &&
      std::fabs(message.optional_float()) > 1000 &&
      message.any().UnpackTo(&file) && !file.name().empty()) {
    std::cerr << message.DebugString() << "\n";
    abort();
  }
}
<commit_msg>Add missing #include <iostream><commit_after>// Copyright 2017 Google Inc. All rights reserved.
//
// 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 <cmath>
#include <iostream>

#include "examples/libfuzzer/libfuzzer_example.pb.h"
#include "port/protobuf.h"
#include "src/libfuzzer/libfuzzer_macro.h"

protobuf_mutator::protobuf::LogSilencer log_silincer;

DEFINE_BINARY_PROTO_FUZZER(const libfuzzer_example::Msg& message) {
  static PostProcessorRegistration reg = {
      [](libfuzzer_example::Msg* message, unsigned int seed) {
        if (seed % 2) {
          message->set_optional_uint64(
              std::hash<std::string>{}(message->optional_string()));
        }

        if (message->has_any()) {
          auto* any = message->mutable_any();

          // Guide mutator to usefull 'Any' types.
          static const char* const expected_types[] = {
              "type.googleapis.com/google.protobuf.DescriptorProto",
              "type.googleapis.com/google.protobuf.FileDescriptorProto",
          };

          if (!std::count(std::begin(expected_types), std::end(expected_types),
                          any->type_url())) {
            const size_t num =
                (std::end(expected_types) - std::begin(expected_types));
            any->set_type_url(expected_types[seed % num]);
          }
        }
      }};

  protobuf_mutator::protobuf::FileDescriptorProto file;

  // Emulate a bug.
  if (message.optional_uint64() ==
          std::hash<std::string>{}(message.optional_string()) &&
      message.optional_string() == "abcdefghijklmnopqrstuvwxyz" &&
      !std::isnan(message.optional_float()) &&
      std::fabs(message.optional_float()) > 1000 &&
      message.any().UnpackTo(&file) && !file.name().empty()) {
    std::cerr << message.DebugString() << "\n";
    abort();
  }
}
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2016 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_HARFBUZZ_SHAPER_HPP
#define MAPNIK_HARFBUZZ_SHAPER_HPP

// mapnik
#include <mapnik/text/text_properties.hpp>
#include <mapnik/text/text_line.hpp>
#include <mapnik/text/face.hpp>
#include <mapnik/text/font_feature_settings.hpp>
#include <mapnik/text/itemizer.hpp>
#include <mapnik/safe_cast.hpp>
#include <mapnik/font_engine_freetype.hpp>

// stl
#include <list>
#include <type_traits>

#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <harfbuzz/hb.h>
#include <harfbuzz/hb-ft.h>
#include <unicode/uscript.h>
#pragma GCC diagnostic pop

namespace mapnik
{

static inline hb_script_t _icu_script_to_script(UScriptCode script)
{
    if (script == USCRIPT_INVALID_CODE) return HB_SCRIPT_INVALID;
        return hb_script_from_string(uscript_getShortName(script), -1);
}

static inline const uint16_t * uchar_to_utf16(const UChar* src)
{
   static_assert(sizeof(UChar) == sizeof(uint16_t),"UChar is eq size to uint16_t");
#if defined(_MSC_VER)
   return reinterpret_cast<const uint16_t *>(src);
#else
   return src;
#endif
}

struct harfbuzz_shaper
{
static void shape_text(text_line & line,
                       text_itemizer & itemizer,
                       std::map<unsigned,double> & width_map,
                       face_manager_freetype & font_manager,
                       double scale_factor)
{
    unsigned start = line.first_char();
    unsigned end = line.last_char();
    std::size_t length = end - start;
    if (!length) return;

    std::list<text_item> const& list = itemizer.itemize(start, end);

    line.reserve(length);

    auto hb_buffer_deleter = [](hb_buffer_t * buffer) { hb_buffer_destroy(buffer);};
    const std::unique_ptr<hb_buffer_t, decltype(hb_buffer_deleter)> buffer(hb_buffer_create(), hb_buffer_deleter);
    hb_buffer_pre_allocate(buffer.get(), safe_cast<int>(length));
    mapnik::value_unicode_string const& text = itemizer.text();
    for (auto const& text_item : list)
    {
        face_set_ptr face_set = font_manager.get_face_set(text_item.format_->face_name, text_item.format_->fontset);
        double size = text_item.format_->text_size * scale_factor;
        face_set->set_unscaled_character_sizes();
        std::size_t num_faces = face_set->size();

        font_feature_settings const& ff_settings = text_item.format_->ff_settings;
        int ff_count = safe_cast<int>(ff_settings.count());

        // rendering information for a single glyph
        struct glyph_face_info
        {
            face_ptr face;
            hb_glyph_info_t glyph;
            hb_glyph_position_t position;
        };

        // this table is filled with information for rendering each glyph, so that
        // several font faces can be used in a single text_item
        std::size_t pos = 0;
        std::vector<std::vector<glyph_face_info>> glyphinfos;

        glyphinfos.resize(text.length());
        for (auto const& face : *face_set)
        {
            ++pos;
            hb_buffer_clear_contents(buffer.get());
            hb_buffer_add_utf16(buffer.get(), uchar_to_utf16(text.getBuffer()), text.length(), text_item.start, static_cast<int>(text_item.end - text_item.start));
            hb_buffer_set_direction(buffer.get(), (text_item.dir == UBIDI_RTL) ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
            hb_buffer_set_script(buffer.get(), _icu_script_to_script(text_item.script));
            hb_font_t *font(hb_ft_font_create(face->get_face(), nullptr));
            // https://github.com/mapnik/test-data-visual/pull/25

#if (HB_VERSION_MAJOR > 0 && HB_VERSION_ATLEAST(1, 0, 5))
            hb_ft_font_set_load_flags(font,FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING);
#endif
            hb_shape(font, buffer.get(), ff_settings.get_features(), ff_count);
            hb_font_destroy(font);

            unsigned num_glyphs = hb_buffer_get_length(buffer.get());
            hb_glyph_info_t *glyphs = hb_buffer_get_glyph_infos(buffer.get(), &num_glyphs);
            hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer.get(), &num_glyphs);

            unsigned cluster = 0;
            bool in_cluster = false;
            std::vector<unsigned> clusters;

            for (unsigned i = 0; i < num_glyphs; ++i)
            {
                if (i == 0)
                {
                    cluster = glyphs[0].cluster;
                    clusters.push_back(cluster);
                }
                if (cluster != glyphs[i].cluster)
                {
                    cluster = glyphs[i].cluster;
                    clusters.push_back(cluster);
                    in_cluster = false;
                }
                else if (i != 0)
                {
                    in_cluster = true;
                }
                if (glyphinfos.size() <= cluster)
                {
                    glyphinfos.resize(cluster + 1);
                }
                auto & c = glyphinfos[cluster];
                if (c.empty())
                {
                    c.push_back({face, glyphs[i], positions[i]});
                }
                if (c.front().glyph.codepoint == 0)
                {
                    c.front() = { face, glyphs[i], positions[i] };
                }
                else if (in_cluster)
                {
                    c.push_back({ face, glyphs[i], positions[i] });
                }
            }
            bool all_set = true;
            for (auto c_id : clusters)
            {
                auto const& c = glyphinfos[c_id];
                if (c.empty() || c.front().glyph.codepoint == 0)
                {
                    all_set = false;
                    break;
                }
            }
            if (!all_set && (pos < num_faces))
            {
                //Try next font in fontset
                continue;
            }
            double max_glyph_height = 0;
            for (auto const& c_id : clusters)
            {
                auto const& c = glyphinfos[c_id];
                for (auto const& info : c)
                {
                    face_ptr theface = face;
                    auto & gpos = info.position;
                    auto & glyph = info.glyph;
                    if (info.glyph.codepoint != 0)
                    {
                        theface = info.face;
                    }
                    unsigned char_index = glyph.cluster;
                    glyph_info g(glyph.codepoint,char_index,text_item.format_);
                    if (theface->glyph_dimensions(g))
                    {
                        g.face = theface;
                        g.scale_multiplier = size / theface->get_face()->units_per_EM;
                        //Overwrite default advance with better value provided by HarfBuzz
                        g.unscaled_advance = gpos.x_advance;
                        g.offset.set(gpos.x_offset * g.scale_multiplier, gpos.y_offset * g.scale_multiplier);
                        double tmp_height = g.height();
                        if (tmp_height > max_glyph_height) max_glyph_height = tmp_height;
                        width_map[char_index] += g.advance();
                        line.add_glyph(std::move(g), scale_factor);
                    }
                }
            }
            line.update_max_char_height(max_glyph_height);
            break; //When we reach this point the current font had all glyphs.
        }
    }
}
};
} // namespace mapnik

#endif // MAPNIK_HARFBUZZ_SHAPER_HPP
<commit_msg>use `else if`<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2016 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_HARFBUZZ_SHAPER_HPP
#define MAPNIK_HARFBUZZ_SHAPER_HPP

// mapnik
#include <mapnik/text/text_properties.hpp>
#include <mapnik/text/text_line.hpp>
#include <mapnik/text/face.hpp>
#include <mapnik/text/font_feature_settings.hpp>
#include <mapnik/text/itemizer.hpp>
#include <mapnik/safe_cast.hpp>
#include <mapnik/font_engine_freetype.hpp>

// stl
#include <list>
#include <type_traits>

#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <harfbuzz/hb.h>
#include <harfbuzz/hb-ft.h>
#include <unicode/uscript.h>
#pragma GCC diagnostic pop

namespace mapnik
{

static inline hb_script_t _icu_script_to_script(UScriptCode script)
{
    if (script == USCRIPT_INVALID_CODE) return HB_SCRIPT_INVALID;
        return hb_script_from_string(uscript_getShortName(script), -1);
}

static inline const uint16_t * uchar_to_utf16(const UChar* src)
{
   static_assert(sizeof(UChar) == sizeof(uint16_t),"UChar is eq size to uint16_t");
#if defined(_MSC_VER)
   return reinterpret_cast<const uint16_t *>(src);
#else
   return src;
#endif
}

struct harfbuzz_shaper
{
static void shape_text(text_line & line,
                       text_itemizer & itemizer,
                       std::map<unsigned,double> & width_map,
                       face_manager_freetype & font_manager,
                       double scale_factor)
{
    unsigned start = line.first_char();
    unsigned end = line.last_char();
    std::size_t length = end - start;
    if (!length) return;

    std::list<text_item> const& list = itemizer.itemize(start, end);

    line.reserve(length);

    auto hb_buffer_deleter = [](hb_buffer_t * buffer) { hb_buffer_destroy(buffer);};
    const std::unique_ptr<hb_buffer_t, decltype(hb_buffer_deleter)> buffer(hb_buffer_create(), hb_buffer_deleter);
    hb_buffer_pre_allocate(buffer.get(), safe_cast<int>(length));
    mapnik::value_unicode_string const& text = itemizer.text();
    for (auto const& text_item : list)
    {
        face_set_ptr face_set = font_manager.get_face_set(text_item.format_->face_name, text_item.format_->fontset);
        double size = text_item.format_->text_size * scale_factor;
        face_set->set_unscaled_character_sizes();
        std::size_t num_faces = face_set->size();

        font_feature_settings const& ff_settings = text_item.format_->ff_settings;
        int ff_count = safe_cast<int>(ff_settings.count());

        // rendering information for a single glyph
        struct glyph_face_info
        {
            face_ptr face;
            hb_glyph_info_t glyph;
            hb_glyph_position_t position;
        };

        // this table is filled with information for rendering each glyph, so that
        // several font faces can be used in a single text_item
        std::size_t pos = 0;
        std::vector<std::vector<glyph_face_info>> glyphinfos;

        glyphinfos.resize(text.length());
        for (auto const& face : *face_set)
        {
            ++pos;
            hb_buffer_clear_contents(buffer.get());
            hb_buffer_add_utf16(buffer.get(), uchar_to_utf16(text.getBuffer()), text.length(), text_item.start, static_cast<int>(text_item.end - text_item.start));
            hb_buffer_set_direction(buffer.get(), (text_item.dir == UBIDI_RTL) ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
            hb_buffer_set_script(buffer.get(), _icu_script_to_script(text_item.script));
            hb_font_t *font(hb_ft_font_create(face->get_face(), nullptr));
            // https://github.com/mapnik/test-data-visual/pull/25

#if (HB_VERSION_MAJOR > 0 && HB_VERSION_ATLEAST(1, 0, 5))
            hb_ft_font_set_load_flags(font,FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING);
#endif
            hb_shape(font, buffer.get(), ff_settings.get_features(), ff_count);
            hb_font_destroy(font);

            unsigned num_glyphs = hb_buffer_get_length(buffer.get());
            hb_glyph_info_t *glyphs = hb_buffer_get_glyph_infos(buffer.get(), &num_glyphs);
            hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer.get(), &num_glyphs);

            unsigned cluster = 0;
            bool in_cluster = false;
            std::vector<unsigned> clusters;

            for (unsigned i = 0; i < num_glyphs; ++i)
            {
                if (i == 0)
                {
                    cluster = glyphs[0].cluster;
                    clusters.push_back(cluster);
                }
                if (cluster != glyphs[i].cluster)
                {
                    cluster = glyphs[i].cluster;
                    clusters.push_back(cluster);
                    in_cluster = false;
                }
                else if (i != 0)
                {
                    in_cluster = true;
                }
                if (glyphinfos.size() <= cluster)
                {
                    glyphinfos.resize(cluster + 1);
                }
                auto & c = glyphinfos[cluster];
                if (c.empty())
                {
                    c.push_back({face, glyphs[i], positions[i]});
                }
                else if (c.front().glyph.codepoint == 0)
                {
                    c.front() = { face, glyphs[i], positions[i] };
                }
                else if (in_cluster)
                {
                    c.push_back({ face, glyphs[i], positions[i] });
                }
            }
            bool all_set = true;
            for (auto c_id : clusters)
            {
                auto const& c = glyphinfos[c_id];
                if (c.empty() || c.front().glyph.codepoint == 0)
                {
                    all_set = false;
                    break;
                }
            }
            if (!all_set && (pos < num_faces))
            {
                //Try next font in fontset
                continue;
            }
            double max_glyph_height = 0;
            for (auto const& c_id : clusters)
            {
                auto const& c = glyphinfos[c_id];
                for (auto const& info : c)
                {
                    face_ptr theface = face;
                    auto & gpos = info.position;
                    auto & glyph = info.glyph;
                    if (info.glyph.codepoint != 0)
                    {
                        theface = info.face;
                    }
                    unsigned char_index = glyph.cluster;
                    glyph_info g(glyph.codepoint,char_index,text_item.format_);
                    if (theface->glyph_dimensions(g))
                    {
                        g.face = theface;
                        g.scale_multiplier = size / theface->get_face()->units_per_EM;
                        //Overwrite default advance with better value provided by HarfBuzz
                        g.unscaled_advance = gpos.x_advance;
                        g.offset.set(gpos.x_offset * g.scale_multiplier, gpos.y_offset * g.scale_multiplier);
                        double tmp_height = g.height();
                        if (tmp_height > max_glyph_height) max_glyph_height = tmp_height;
                        width_map[char_index] += g.advance();
                        line.add_glyph(std::move(g), scale_factor);
                    }
                }
            }
            line.update_max_char_height(max_glyph_height);
            break; //When we reach this point the current font had all glyphs.
        }
    }
}
};
} // namespace mapnik

#endif // MAPNIK_HARFBUZZ_SHAPER_HPP
<|endoftext|>


<commit_before><commit_msg>* Fixed compile error.<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved.
 */
#include "base/connection_info.h"

#include <boost/system/error_code.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <boost/foreach.hpp>

#include "base/string_util.h"
#include "base/sandesh/process_info_constants.h"

namespace process {

// ConnectionState
boost::scoped_ptr<ConnectionState> ConnectionState::instance_;

ConnectionState::ConnectionState(SendUveCb send_uve_cb) :
    send_uve_cb_(send_uve_cb) {
}

// CreateInstance should be called from ConnectionStateManager::Init()
void ConnectionState::CreateInstance(SendUveCb send_uve_cb) {
    // The assert is to catch errors where ConnectionState::GetInstance()
    // is called before ConnectionStateManager::Init()
    assert(instance_ == NULL);
    instance_.reset(new ConnectionState(send_uve_cb));
}

ConnectionState* ConnectionState::GetInstance() {
    if (instance_ == NULL) {
        // This is needed to handle unit tests that do not call
        // ConnectionStateManager::Init()
        instance_.reset(new ConnectionState(NULL));
    }
    return instance_.get();
}

void ConnectionState::Update() {
    if (!send_uve_cb_.empty()) {
        send_uve_cb_();
    }
}

void ConnectionState::UpdateInternal(ConnectionType::type ctype,
    const std::string &name, ConnectionStatus::type status,
    const std::vector<Endpoint> &servers, std::string message) {
    // Populate key
    ConnectionInfoKey key(ctype, name);
    // Populate info
    ConnectionInfo info;
    info.set_type(
        g_process_info_constants.ConnectionTypeNames.find(ctype)->second);
    info.set_name(name);
    std::vector<std::string> server_addrs;
    BOOST_FOREACH(const Endpoint &server, servers) {
        boost::system::error_code ec;
        std::string saddr(server.address().to_string(ec));
        int sport(server.port());
        std::string server_address(saddr + ":" + integerToString(sport));
        server_addrs.push_back(server_address);
    }
    info.set_server_addrs(server_addrs);
    info.set_status(
        g_process_info_constants.ConnectionStatusNames.find(status)->second);
    info.set_description(message);
    // Lookup connection info map
    tbb::mutex::scoped_lock lock(mutex_);
    ConnectionInfoMap::iterator it = connection_map_.find(key);
    if (it != connection_map_.end()) {
        // Update
        it->second = info;
    } else {
        // Add
        connection_map_[key] = info;
    }
    if (!send_uve_cb_.empty()) {
        send_uve_cb_();
    }
}

void ConnectionState::Update(ConnectionType::type ctype,
    const std::string &name, ConnectionStatus::type status,
    const std::vector<Endpoint> &servers, std::string message) {
    UpdateInternal(ctype, name, status, servers, message);
}

void ConnectionState::Update(ConnectionType::type ctype,
    const std::string &name, ConnectionStatus::type status,
    Endpoint server, std::string message) {
    UpdateInternal(ctype, name, status, boost::assign::list_of(server),
        message);
}

void ConnectionState::Delete(ConnectionType::type ctype,
    const std::string &name) {
    // Construct key
    ConnectionInfoKey key(ctype, name);
    // Delete
    tbb::mutex::scoped_lock lock(mutex_);
    connection_map_.erase(key);
    if (!send_uve_cb_.empty()) {
        send_uve_cb_();
    }
}

std::vector<ConnectionInfo> ConnectionState::GetInfosUnlocked() const {
    std::vector<ConnectionInfo> infos;
    for (ConnectionInfoMap::const_iterator it = connection_map_.begin();
         it != connection_map_.end(); it++) {
        infos.push_back(it->second);
    }
    return infos;
}

std::vector<ConnectionInfo> ConnectionState::GetInfos() const {
    tbb::mutex::scoped_lock lock(mutex_);
    return GetInfosUnlocked();
}

void GetProcessStateCb(const std::vector<ConnectionInfo> &cinfos,
    ProcessState::type &state, std::string &message,
    size_t expected_connections) {
    // Determine if the number of connections is as expected.
    size_t num_connections(cinfos.size());
    if (num_connections != expected_connections) {
        state = ProcessState::NON_FUNCTIONAL;
        message = "Number of connections:" + integerToString(num_connections) +
                  ", Expected:" + integerToString(expected_connections);
        return;
    }
    std::string cup(g_process_info_constants.ConnectionStatusNames.
                    find(ConnectionStatus::UP)->second);
    bool is_cup = true;
    // Iterate to determine process connectivity status
    for (std::vector<ConnectionInfo>::const_iterator it = cinfos.begin();
         it != cinfos.end(); it++) {
        const ConnectionInfo &cinfo(*it);
        const std::string &conn_status(cinfo.get_status());
        if (conn_status != cup) {
            is_cup = false;
            if (message.empty()) {
                message = cinfo.get_type();
            } else {
                message += ", " + cinfo.get_type();
            }
            const std::string &name(cinfo.get_name());
            if (!name.empty()) {
                message += ":" + name;
            }
        }
    }
    // All critical connections are in good condition.
    if (is_cup) {
        state = ProcessState::FUNCTIONAL;
    } else {
        state = ProcessState::NON_FUNCTIONAL;
        message += " connection down";
    }
    return;
}

} // namespace process
<commit_msg>NodeStatusUVE should be sent only if there is any change<commit_after>/*
 * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved.
 */
#include "base/connection_info.h"

#include <boost/system/error_code.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <boost/foreach.hpp>

#include "base/string_util.h"
#include "base/sandesh/process_info_constants.h"

namespace process {

// ConnectionState
boost::scoped_ptr<ConnectionState> ConnectionState::instance_;

ConnectionState::ConnectionState(SendUveCb send_uve_cb) :
    send_uve_cb_(send_uve_cb) {
}

// CreateInstance should be called from ConnectionStateManager::Init()
void ConnectionState::CreateInstance(SendUveCb send_uve_cb) {
    // The assert is to catch errors where ConnectionState::GetInstance()
    // is called before ConnectionStateManager::Init()
    assert(instance_ == NULL);
    instance_.reset(new ConnectionState(send_uve_cb));
}

ConnectionState* ConnectionState::GetInstance() {
    if (instance_ == NULL) {
        // This is needed to handle unit tests that do not call
        // ConnectionStateManager::Init()
        instance_.reset(new ConnectionState(NULL));
    }
    return instance_.get();
}

void ConnectionState::Update() {
    if (!send_uve_cb_.empty()) {
        send_uve_cb_();
    }
}

void ConnectionState::UpdateInternal(ConnectionType::type ctype,
    const std::string &name, ConnectionStatus::type status,
    const std::vector<Endpoint> &servers, std::string message) {
    // Populate key
    ConnectionInfoKey key(ctype, name);
    // Populate info
    ConnectionInfo info;
    info.set_type(
        g_process_info_constants.ConnectionTypeNames.find(ctype)->second);
    info.set_name(name);
    std::vector<std::string> server_addrs;
    BOOST_FOREACH(const Endpoint &server, servers) {
        boost::system::error_code ec;
        std::string saddr(server.address().to_string(ec));
        int sport(server.port());
        std::string server_address(saddr + ":" + integerToString(sport));
        server_addrs.push_back(server_address);
    }
    info.set_server_addrs(server_addrs);
    info.set_status(
        g_process_info_constants.ConnectionStatusNames.find(status)->second);
    info.set_description(message);
    // Lookup connection info map
    tbb::mutex::scoped_lock lock(mutex_);
    ConnectionInfoMap::iterator it = connection_map_.find(key);
    if (it != connection_map_.end()) {
        // Update
        if (it->second.server_addrs == info.server_addrs &&
            it->second.status == info.status &&
            it->second.description == info.description) {
            // Do not send UVE if there is no change in the server_addrs,
            // status or description
            return;
        }
        it->second = info;
    } else {
        // Add
        connection_map_[key] = info;
    }
    if (!send_uve_cb_.empty()) {
        send_uve_cb_();
    }
}

void ConnectionState::Update(ConnectionType::type ctype,
    const std::string &name, ConnectionStatus::type status,
    const std::vector<Endpoint> &servers, std::string message) {
    UpdateInternal(ctype, name, status, servers, message);
}

void ConnectionState::Update(ConnectionType::type ctype,
    const std::string &name, ConnectionStatus::type status,
    Endpoint server, std::string message) {
    UpdateInternal(ctype, name, status, boost::assign::list_of(server),
        message);
}

void ConnectionState::Delete(ConnectionType::type ctype,
    const std::string &name) {
    // Construct key
    ConnectionInfoKey key(ctype, name);
    // Delete
    tbb::mutex::scoped_lock lock(mutex_);
    connection_map_.erase(key);
    if (!send_uve_cb_.empty()) {
        send_uve_cb_();
    }
}

std::vector<ConnectionInfo> ConnectionState::GetInfosUnlocked() const {
    std::vector<ConnectionInfo> infos;
    for (ConnectionInfoMap::const_iterator it = connection_map_.begin();
         it != connection_map_.end(); it++) {
        infos.push_back(it->second);
    }
    return infos;
}

std::vector<ConnectionInfo> ConnectionState::GetInfos() const {
    tbb::mutex::scoped_lock lock(mutex_);
    return GetInfosUnlocked();
}

void GetProcessStateCb(const std::vector<ConnectionInfo> &cinfos,
    ProcessState::type &state, std::string &message,
    size_t expected_connections) {
    // Determine if the number of connections is as expected.
    size_t num_connections(cinfos.size());
    if (num_connections != expected_connections) {
        state = ProcessState::NON_FUNCTIONAL;
        message = "Number of connections:" + integerToString(num_connections) +
                  ", Expected:" + integerToString(expected_connections);
        return;
    }
    std::string cup(g_process_info_constants.ConnectionStatusNames.
                    find(ConnectionStatus::UP)->second);
    bool is_cup = true;
    // Iterate to determine process connectivity status
    for (std::vector<ConnectionInfo>::const_iterator it = cinfos.begin();
         it != cinfos.end(); it++) {
        const ConnectionInfo &cinfo(*it);
        const std::string &conn_status(cinfo.get_status());
        if (conn_status != cup) {
            is_cup = false;
            if (message.empty()) {
                message = cinfo.get_type();
            } else {
                message += ", " + cinfo.get_type();
            }
            const std::string &name(cinfo.get_name());
            if (!name.empty()) {
                message += ":" + name;
            }
        }
    }
    // All critical connections are in good condition.
    if (is_cup) {
        state = ProcessState::FUNCTIONAL;
    } else {
        state = ProcessState::NON_FUNCTIONAL;
        message += " connection down";
    }
    return;
}

} // namespace process
<|endoftext|>