<commit_before>/*************************************************************************
 *
 * TIGHTDB CONFIDENTIAL
 * __________________
 *
 *  [2011] - [2012] TightDB Inc
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of TightDB Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to TightDB Incorporated
 * and its suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from TightDB Incorporated.
 *
 **************************************************************************/
#ifndef TIGHTDB_QUERY_ENGINE_HPP
#define TIGHTDB_QUERY_ENGINE_HPP

#include <string>
#include "table.hpp"
#include "column_string.hpp"
#include "column_string_enum.hpp"

#include "utf8.hpp"
#include "query_conditions.hpp"

namespace tightdb {

class ParentNode {
public:
    ParentNode() : m_table(NULL) {}
    virtual ~ParentNode() {}
    virtual void Init(const Table& table) {m_table = &table; if (m_child) m_child->Init(table);}
    virtual size_t find_first(size_t start, size_t end) = 0;

    virtual std::string Verify(void)
    {
        if(error_code != "")
            return error_code;
        if(m_child == 0)
            return "";
        else
            return m_child->Verify();
    };

    ParentNode* m_child;

protected:
    const Table* m_table;
    std::string error_code;
};

/*
template <class T, class C, class F> class NODE: public ParentNode {
public:
    NODE(T v, size_t column) : m_value(v), m_column(column)  {m_child = 0;}
    ~NODE() {delete m_child; }

    size_t find_first(size_t start, size_t end, const Table& table)
    {
        const C& column = (C&)(table.GetColumnBase(m_column));
        const F function = {};
        for (size_t s = start; s < end; ++s) {
            const T t = column.Get(s);
            if (function(t, m_value)) {
                if (m_child == 0)
                    return s;
                else {
                    const size_t a = m_child->find_first(s, end, table);
                    if (s == a)
                        return s;
                    else
                        s = a - 1;
                }
            }
        }
        return end;
    }

protected:
    T m_value;
    size_t m_column;
};
*/

// Not finished
class SUBTABLE: public ParentNode {
public:
    SUBTABLE(size_t column): m_column(column) {m_child = 0; m_child2 = 0;}
    SUBTABLE() {};

    void Init(const Table& table)
    {
        m_table = &table;

        if (m_child) m_child->Init(table);
        if (m_child2) m_child2->Init(table);
    }

    size_t find_first(size_t start, size_t end)
    {
        assert(m_table);
        assert(m_child);

        for (size_t s = start; s < end; ++s) {
            const TableRef subtable = ((Table*)m_table)->get_subtable(m_column, s);

            m_child->Init(*subtable);
            const size_t subsize = subtable->size();
            const size_t sub = m_child->find_first(0, subsize);

            if(sub != subsize) {
                if (m_child2 == 0)
                    return s;
                else {
                    const size_t a = m_child2->find_first(s, end);
                    if (s == a)
                        return s;
                    else
                        s = a - 1;
                }
            }
        }
        return end;
    }
//protected:
    ParentNode* m_child2;
    size_t m_column;
};

template <class T, class C, class F> class NODE: public ParentNode {
public:
    NODE(T v, size_t column) : m_leaf_start(0), m_leaf_end(0), m_local_end(0), m_value(v), m_column_id(column) {m_child = 0;}
    ~NODE() {delete m_child; }

    void Init(const Table& table)
    {
        m_table = &table;
        m_column = (C*)&table.GetColumnBase(m_column_id);
        m_leaf_end = 0;

        if (m_child)m_child->Init(table);
    }

    size_t find_first(size_t start, size_t end)
    {
        assert(m_table);

        for (size_t s = start; s < end; ++s) {
            // Cache internal leafs
            if (s >= m_leaf_end) {
                m_column->GetBlock(s, m_array, m_leaf_start);
                const size_t leaf_size = m_array.Size();
                m_leaf_end = m_leaf_start + leaf_size;
                const size_t e = end - m_leaf_start;
                m_local_end = leaf_size < e ? leaf_size : e;
            }

            // Do search directly on cached leaf array
            s = m_array.Query<F>(m_value, s - m_leaf_start, m_local_end);

            if (s == (size_t)-1) {
                s = m_leaf_end-1;
                continue;
            }
            else
                s += m_leaf_start;

            if (m_child == 0)
                return s;
            else {
                const size_t a = m_child->find_first(s, end);
                if (s == a)
                    return s;
                else
                    s = a - 1;
            }
        }
        return end;
    }

protected:
    C* m_column;
    Array m_array;
    size_t m_leaf_start;
    size_t m_leaf_end;
    size_t m_local_end;
    T m_value;
    size_t m_column_id;
};

template <class F> class STRINGNODE: public ParentNode {
public:
    STRINGNODE(const char* v, size_t column): m_column_id(column)
    {
        m_child = 0;

        m_value = (char *)malloc(strlen(v)*6);
        memcpy(m_value, v, strlen(v) + 1);
        m_ucase = (char *)malloc(strlen(v)*6);
        m_lcase = (char *)malloc(strlen(v)*6);

        const bool b1 = utf8case(v, m_lcase, false);
        const bool b2 = utf8case(v, m_ucase, true);
        if (!b1 || !b2)
            error_code = "Malformed UTF-8: " + std::string(m_value);
    }
    ~STRINGNODE() {delete m_child; free((void*)m_value); free((void*)m_ucase); free((void*)m_lcase); }

    void Init(const Table& table)
    {
        m_table = &table;
        m_column = &table.GetColumnBase(m_column_id);
        m_column_type = table.GetRealColumnType(m_column_id);

        if (m_child) m_child->Init(table);
    }

    size_t find_first(size_t start, size_t end)
    {
        F function;// = {};

        for (size_t s = start; s < end; ++s) {
            const char* t;

            // todo, can be optimized by placing outside loop
            if (m_column_type == COLUMN_TYPE_STRING)
                t = ((const AdaptiveStringColumn*)m_column)->Get(s);
            else {
                //TODO: First check if string is in key list
                t = ((const ColumnStringEnum*)m_column)->Get(s);
            }

            if (function(m_value, m_ucase, m_lcase, t)) {
                if (m_child == 0)
                    return s;
                else {
                    const size_t a = m_child->find_first(s, end);
                    if (s == a)
                        return s;
                    else
                        s = a - 1;
                }
            }
        }
        return end;
    }

protected:
    char* m_value;
    char* m_lcase;
    char* m_ucase;
    size_t m_column_id;
    const ColumnBase* m_column;
    ColumnType m_column_type;
};

template <> class STRINGNODE<EQUAL>: public ParentNode {
public:
    STRINGNODE(const char* v, size_t column): m_column_id(column), m_key_ndx((size_t)-1) {
        m_child = 0;
        m_value = (char *)malloc(strlen(v)*6);
        memcpy(m_value, v, strlen(v) + 1);
    }
    ~STRINGNODE() {delete m_child; free((void*)m_value); }

    void Init(const Table& table)
    {
        m_table = &table;
        m_column = &table.GetColumnBase(m_column_id);
        m_column_type = table.GetRealColumnType(m_column_id);

        if (m_column_type == COLUMN_TYPE_STRING_ENUM) {
            m_key_ndx =  ((const ColumnStringEnum*)m_column)->GetKeyNdx(m_value);
        }

        if (m_child) m_child->Init(table);
    }

    size_t find_first(size_t start, size_t end)
    {
        assert(m_table);

        for (size_t s = start; s < end; ++s) {
            // todo, can be optimized by placing outside loop
            if (m_column_type == COLUMN_TYPE_STRING)
                s = ((const AdaptiveStringColumn*)m_column)->find_first(m_value, s, end);
            else {
                if (m_key_ndx == size_t(-1)) s = end; // not in key set
                else {
                    const ColumnStringEnum* const cse = (const ColumnStringEnum*)m_column;
                    s = cse->find_first(m_key_ndx, s, end);
                }
            }

            if (s == (size_t)-1)
                s = end;

            if (m_child == 0)
                return s;
            else {
                const size_t a = m_child->find_first(s, end);
                if (s == a)
                    return s;
                else
                    s = a - 1;
            }
        }
        return end;
    }

protected:
    char*  m_value;
    size_t m_column_id;

private:
    const ColumnBase* m_column;
    ColumnType m_column_type;
    size_t m_key_ndx;
};

class OR_NODE: public ParentNode {
public:
    OR_NODE(ParentNode* p1) : m_table(NULL) {m_child = NULL; m_cond1 = p1; m_cond2 = NULL;};
    ~OR_NODE()
    {
        delete m_cond1;
        delete m_cond2;
        delete m_child;
    }

    void Init(const Table& table)
    {
        m_cond1->Init(table);
        m_cond2->Init(table);

        if(m_child)
            m_child->Init(table);

        m_last1 = -1;
        m_last2 = -1;

        m_table = &table;
    }

// Keep old un-optimized or code until new has been sufficiently tested
#if 0
    size_t find_first(size_t start, size_t end)
    {
        for (size_t s = start; s < end; ++s) {
            // Todo, redundant searches can occur
            // We have to init here to reset nodes internal
            // leaf cache (since we may go backwards)
            m_cond1->Init(*m_table);
            m_cond2->Init(*m_table);
            const size_t f1 = m_cond1->find_first(s, end);
            const size_t f2 = m_cond2->find_first(s, end);
            s = f1 < f2 ? f1 : f2;

            if (m_child == 0)
                return s;
            else {
                const size_t a = m_child->find_first(s, end);
                if (s == a)
                    return s;
                else
                    s = a - 1;
            }
        }
        return end;
    }
#else
    size_t find_first(size_t start, size_t end)
    {
        for (size_t s = start; s < end; ++s) {
            size_t f1;
            size_t f2;

            if (m_last1 >= s && m_last1 != (size_t)-1)
                f1 = m_last1;
            else {
                f1 = m_cond1->find_first(s, end);
                m_last1 = f1;
            }

            if (m_last2 >= s && m_last2 != (size_t)-1)
                f2 = m_last2;
            else {
                f2 = m_cond2->find_first(s, end);
                m_last2 = f2;
            }
            s = f1 < f2 ? f1 : f2;

            if (m_child == 0)
                return s;
            else {
                const size_t a = m_child->find_first(s, end);
                if (s == a)
                    return s;
                else
                    s = a - 1;
            }
        }
        return end;
    }
#endif

    virtual std::string Verify(void)
    {
        if(error_code != "")
            return error_code;
        if(m_cond1 == 0)
            return "Missing left-hand side of OR";
        if(m_cond2 == 0)
            return "Missing right-hand side of OR";
        std::string s;
        if(m_child != 0)
            s = m_child->Verify();
        if(s != "")
            return s;
        s = m_cond1->Verify();
        if(s != "")
            return s;
        s = m_cond2->Verify();
        if(s != "")
            return s;
        return "";
    }

    ParentNode* m_cond1;
    ParentNode* m_cond2;
private:
    size_t m_last1;
    size_t m_last2;
    const Table* m_table;
};

} // namespace tightdb

#endif // TIGHTDB_QUERY_ENGINE_HPP
<commit_msg>Fixed minor memory leak in query<commit_after>/*************************************************************************
 *
 * TIGHTDB CONFIDENTIAL
 * __________________
 *
 *  [2011] - [2012] TightDB Inc
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of TightDB Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to TightDB Incorporated
 * and its suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from TightDB Incorporated.
 *
 **************************************************************************/
#ifndef TIGHTDB_QUERY_ENGINE_HPP
#define TIGHTDB_QUERY_ENGINE_HPP

#include <string>
#include "table.hpp"
#include "column_string.hpp"
#include "column_string_enum.hpp"

#include "utf8.hpp"
#include "query_conditions.hpp"

namespace tightdb {

class ParentNode {
public:
    ParentNode() : m_table(NULL) {}
    virtual ~ParentNode() {}
    virtual void Init(const Table& table) {m_table = &table; if (m_child) m_child->Init(table);}
    virtual size_t find_first(size_t start, size_t end) = 0;

    virtual std::string Verify(void)
    {
        if(error_code != "")
            return error_code;
        if(m_child == 0)
            return "";
        else
            return m_child->Verify();
    };

    ParentNode* m_child;

protected:
    const Table* m_table;
    std::string error_code;
};

/*
template <class T, class C, class F> class NODE: public ParentNode {
public:
    NODE(T v, size_t column) : m_value(v), m_column(column)  {m_child = 0;}
    ~NODE() {delete m_child; }

    size_t find_first(size_t start, size_t end, const Table& table)
    {
        const C& column = (C&)(table.GetColumnBase(m_column));
        const F function = {};
        for (size_t s = start; s < end; ++s) {
            const T t = column.Get(s);
            if (function(t, m_value)) {
                if (m_child == 0)
                    return s;
                else {
                    const size_t a = m_child->find_first(s, end, table);
                    if (s == a)
                        return s;
                    else
                        s = a - 1;
                }
            }
        }
        return end;
    }

protected:
    T m_value;
    size_t m_column;
};
*/

// Not finished
class SUBTABLE: public ParentNode {
public:
    SUBTABLE(size_t column): m_column(column) {m_child = 0; m_child2 = 0;}
    SUBTABLE() {};

    void Init(const Table& table)
    {
        m_table = &table;

        if (m_child) m_child->Init(table);
        if (m_child2) m_child2->Init(table);
    }

    size_t find_first(size_t start, size_t end)
    {
        assert(m_table);
        assert(m_child);

        for (size_t s = start; s < end; ++s) {
            const TableRef subtable = ((Table*)m_table)->get_subtable(m_column, s);

            m_child->Init(*subtable);
            const size_t subsize = subtable->size();
            const size_t sub = m_child->find_first(0, subsize);

            if(sub != subsize) {
                if (m_child2 == 0)
                    return s;
                else {
                    const size_t a = m_child2->find_first(s, end);
                    if (s == a)
                        return s;
                    else
                        s = a - 1;
                }
            }
        }
        return end;
    }
//protected:
    ParentNode* m_child2;
    size_t m_column;
};

template <class T, class C, class F> class NODE: public ParentNode {
public:
    NODE(T v, size_t column) : m_array(GetDefaultAllocator()), m_leaf_start(0), m_leaf_end(0), m_local_end(0), m_value(v), m_column_id(column) {m_child = 0;}
    ~NODE() {delete m_child; }

    void Init(const Table& table)
    {
        m_table = &table;
        m_column = (C*)&table.GetColumnBase(m_column_id);
        m_leaf_end = 0;

        if (m_child)m_child->Init(table);
    }

    size_t find_first(size_t start, size_t end)
    {
        assert(m_table);

        for (size_t s = start; s < end; ++s) {
            // Cache internal leafs
            if (s >= m_leaf_end) {
                m_column->GetBlock(s, m_array, m_leaf_start);
                const size_t leaf_size = m_array.Size();
                m_leaf_end = m_leaf_start + leaf_size;
                const size_t e = end - m_leaf_start;
                m_local_end = leaf_size < e ? leaf_size : e;
            }

            // Do search directly on cached leaf array
            s = m_array.Query<F>(m_value, s - m_leaf_start, m_local_end);

            if (s == (size_t)-1) {
                s = m_leaf_end-1;
                continue;
            }
            else
                s += m_leaf_start;

            if (m_child == 0)
                return s;
            else {
                const size_t a = m_child->find_first(s, end);
                if (s == a)
                    return s;
                else
                    s = a - 1;
            }
        }
        return end;
    }

protected:
    C* m_column;
    Array m_array;
    size_t m_leaf_start;
    size_t m_leaf_end;
    size_t m_local_end;
    T m_value;
    size_t m_column_id;
};

template <class F> class STRINGNODE: public ParentNode {
public:
    STRINGNODE(const char* v, size_t column): m_column_id(column)
    {
        m_child = 0;

        m_value = (char *)malloc(strlen(v)*6);
        memcpy(m_value, v, strlen(v) + 1);
        m_ucase = (char *)malloc(strlen(v)*6);
        m_lcase = (char *)malloc(strlen(v)*6);

        const bool b1 = utf8case(v, m_lcase, false);
        const bool b2 = utf8case(v, m_ucase, true);
        if (!b1 || !b2)
            error_code = "Malformed UTF-8: " + std::string(m_value);
    }
    ~STRINGNODE() {delete m_child; free((void*)m_value); free((void*)m_ucase); free((void*)m_lcase); }

    void Init(const Table& table)
    {
        m_table = &table;
        m_column = &table.GetColumnBase(m_column_id);
        m_column_type = table.GetRealColumnType(m_column_id);

        if (m_child) m_child->Init(table);
    }

    size_t find_first(size_t start, size_t end)
    {
        F function;// = {};

        for (size_t s = start; s < end; ++s) {
            const char* t;

            // todo, can be optimized by placing outside loop
            if (m_column_type == COLUMN_TYPE_STRING)
                t = ((const AdaptiveStringColumn*)m_column)->Get(s);
            else {
                //TODO: First check if string is in key list
                t = ((const ColumnStringEnum*)m_column)->Get(s);
            }

            if (function(m_value, m_ucase, m_lcase, t)) {
                if (m_child == 0)
                    return s;
                else {
                    const size_t a = m_child->find_first(s, end);
                    if (s == a)
                        return s;
                    else
                        s = a - 1;
                }
            }
        }
        return end;
    }

protected:
    char* m_value;
    char* m_lcase;
    char* m_ucase;
    size_t m_column_id;
    const ColumnBase* m_column;
    ColumnType m_column_type;
};

template <> class STRINGNODE<EQUAL>: public ParentNode {
public:
    STRINGNODE(const char* v, size_t column): m_column_id(column), m_key_ndx((size_t)-1) {
        m_child = 0;
        m_value = (char *)malloc(strlen(v)*6);
        memcpy(m_value, v, strlen(v) + 1);
    }
    ~STRINGNODE() {delete m_child; free((void*)m_value); }

    void Init(const Table& table)
    {
        m_table = &table;
        m_column = &table.GetColumnBase(m_column_id);
        m_column_type = table.GetRealColumnType(m_column_id);

        if (m_column_type == COLUMN_TYPE_STRING_ENUM) {
            m_key_ndx =  ((const ColumnStringEnum*)m_column)->GetKeyNdx(m_value);
        }

        if (m_child) m_child->Init(table);
    }

    size_t find_first(size_t start, size_t end)
    {
        assert(m_table);

        for (size_t s = start; s < end; ++s) {
            // todo, can be optimized by placing outside loop
            if (m_column_type == COLUMN_TYPE_STRING)
                s = ((const AdaptiveStringColumn*)m_column)->find_first(m_value, s, end);
            else {
                if (m_key_ndx == size_t(-1)) s = end; // not in key set
                else {
                    const ColumnStringEnum* const cse = (const ColumnStringEnum*)m_column;
                    s = cse->find_first(m_key_ndx, s, end);
                }
            }

            if (s == (size_t)-1)
                s = end;

            if (m_child == 0)
                return s;
            else {
                const size_t a = m_child->find_first(s, end);
                if (s == a)
                    return s;
                else
                    s = a - 1;
            }
        }
        return end;
    }

protected:
    char*  m_value;
    size_t m_column_id;

private:
    const ColumnBase* m_column;
    ColumnType m_column_type;
    size_t m_key_ndx;
};

class OR_NODE: public ParentNode {
public:
    OR_NODE(ParentNode* p1) : m_table(NULL) {m_child = NULL; m_cond1 = p1; m_cond2 = NULL;};
    ~OR_NODE()
    {
        delete m_cond1;
        delete m_cond2;
        delete m_child;
    }

    void Init(const Table& table)
    {
        m_cond1->Init(table);
        m_cond2->Init(table);

        if(m_child)
            m_child->Init(table);

        m_last1 = -1;
        m_last2 = -1;

        m_table = &table;
    }

// Keep old un-optimized or code until new has been sufficiently tested
#if 0
    size_t find_first(size_t start, size_t end)
    {
        for (size_t s = start; s < end; ++s) {
            // Todo, redundant searches can occur
            // We have to init here to reset nodes internal
            // leaf cache (since we may go backwards)
            m_cond1->Init(*m_table);
            m_cond2->Init(*m_table);
            const size_t f1 = m_cond1->find_first(s, end);
            const size_t f2 = m_cond2->find_first(s, end);
            s = f1 < f2 ? f1 : f2;

            if (m_child == 0)
                return s;
            else {
                const size_t a = m_child->find_first(s, end);
                if (s == a)
                    return s;
                else
                    s = a - 1;
            }
        }
        return end;
    }
#else
    size_t find_first(size_t start, size_t end)
    {
        for (size_t s = start; s < end; ++s) {
            size_t f1;
            size_t f2;

            if (m_last1 >= s && m_last1 != (size_t)-1)
                f1 = m_last1;
            else {
                f1 = m_cond1->find_first(s, end);
                m_last1 = f1;
            }

            if (m_last2 >= s && m_last2 != (size_t)-1)
                f2 = m_last2;
            else {
                f2 = m_cond2->find_first(s, end);
                m_last2 = f2;
            }
            s = f1 < f2 ? f1 : f2;

            if (m_child == 0)
                return s;
            else {
                const size_t a = m_child->find_first(s, end);
                if (s == a)
                    return s;
                else
                    s = a - 1;
            }
        }
        return end;
    }
#endif

    virtual std::string Verify(void)
    {
        if(error_code != "")
            return error_code;
        if(m_cond1 == 0)
            return "Missing left-hand side of OR";
        if(m_cond2 == 0)
            return "Missing right-hand side of OR";
        std::string s;
        if(m_child != 0)
            s = m_child->Verify();
        if(s != "")
            return s;
        s = m_cond1->Verify();
        if(s != "")
            return s;
        s = m_cond2->Verify();
        if(s != "")
            return s;
        return "";
    }

    ParentNode* m_cond1;
    ParentNode* m_cond2;
private:
    size_t m_last1;
    size_t m_last2;
    const Table* m_table;
};

} // namespace tightdb

#endif // TIGHTDB_QUERY_ENGINE_HPP
<|endoftext|>


<commit_before>#include "regexmanager.h"

#include <cstring>

#include "config.h"
#include "exceptions.h"
#include "logger.h"
#include "strprintf.h"
#include "utils.h"

namespace newsboat {

RegexManager::RegexManager()
{
	// this creates the entries in the map. we need them there to have the
	// "all" location work.
	locations["article"];
	locations["articlelist"];
	locations["feedlist"];
}

RegexManager::~RegexManager()
{
	for (const auto& location : locations) {
		if (location.second.first.size() > 0) {
			for (const auto& regex : location.second.first) {
				delete regex;
			}
		}
	}
}

void RegexManager::dump_config(std::vector<std::string>& config_output)
{
	for (const auto& foo : cheat_store_for_dump_config) {
		config_output.push_back(foo);
	}
}

void RegexManager::handle_action(const std::string& action,
	const std::vector<std::string>& params)
{
	if (action == "highlight") {
		if (params.size() < 3)
			throw ConfigHandlerException(
				ActionHandlerStatus::TOO_FEW_PARAMS);

		std::string location = params[0];
		if (location != "all" && location != "article" &&
			location != "articlelist" && location != "feedlist")
			throw ConfigHandlerException(strprintf::fmt(
				_("`%s' is an invalid dialog type"), location));

		regex_t* rx = new regex_t;
		int err;
		if ((err = regcomp(rx,
			     params[1].c_str(),
			     REG_EXTENDED | REG_ICASE)) != 0) {
			char buf[1024];
			regerror(err, rx, buf, sizeof(buf));
			delete rx;
			throw ConfigHandlerException(strprintf::fmt(
				_("`%s' is not a valid regular expression: %s"),
				params[1],
				buf));
		}
		std::string colorstr;
		if (params[2] != "default") {
			colorstr.append("fg=");
			if (!utils::is_valid_color(params[2]))
				throw ConfigHandlerException(strprintf::fmt(
					_("`%s' is not a valid color"),
					params[2]));
			colorstr.append(params[2]);
		}
		if (params.size() > 3) {
			if (params[3] != "default") {
				if (colorstr.length() > 0)
					colorstr.append(",");
				colorstr.append("bg=");
				if (!utils::is_valid_color(params[3]))
					throw ConfigHandlerException(
						strprintf::fmt(
							_("`%s' is not a valid "
							  "color"),
							params[3]));
				colorstr.append(params[3]);
			}
			for (unsigned int i = 4; i < params.size(); ++i) {
				if (params[i] != "default") {
					if (colorstr.length() > 0)
						colorstr.append(",");
					colorstr.append("attr=");
					if (!utils::is_valid_attribute(
						    params[i]))
						throw ConfigHandlerException(
							strprintf::fmt(
								_("`%s' is not "
								  "a valid "
								  "attribute"),
								params[i]));
					colorstr.append(params[i]);
				}
			}
		}
		if (location != "all") {
			LOG(Level::DEBUG,
				"RegexManager::handle_action: adding rx = %s "
				"colorstr = %s to location %s",
				params[1],
				colorstr,
				location);
			locations[location].first.push_back(rx);
			locations[location].second.push_back(colorstr);
		} else {
			delete rx;
			for (auto& location : locations) {
				LOG(Level::DEBUG,
					"RegexManager::handle_action: adding "
					"rx = "
					"%s colorstr = %s to location %s",
					params[1],
					colorstr,
					location.first);
				rx = new regex_t;
				// we need to create a new one for each
				// push_back, otherwise we'd have double frees.
				regcomp(rx,
					params[1].c_str(),
					REG_EXTENDED | REG_ICASE);
				location.second.first.push_back(rx);
				location.second.second.push_back(colorstr);
			}
		}
		std::string line = "highlight";
		for (const auto& param : params) {
			line.append(" ");
			line.append(utils::quote(param));
		}
		cheat_store_for_dump_config.push_back(line);
	} else if (action == "highlight-article") {
		if (params.size() < 3)
			throw ConfigHandlerException(
				ActionHandlerStatus::TOO_FEW_PARAMS);

		std::string expr = params[0];
		std::string fgcolor = params[1];
		std::string bgcolor = params[2];

		std::string colorstr;
		if (fgcolor != "default") {
			colorstr.append("fg=");
			if (!utils::is_valid_color(fgcolor))
				throw ConfigHandlerException(strprintf::fmt(
					_("`%s' is not a valid color"),
					fgcolor));
			colorstr.append(fgcolor);
		}
		if (bgcolor != "default") {
			if (colorstr.length() > 0)
				colorstr.append(",");
			colorstr.append("bg=");
			if (!utils::is_valid_color(bgcolor))
				throw ConfigHandlerException(strprintf::fmt(
					_("`%s' is not a valid color"),
					bgcolor));
			colorstr.append(bgcolor);
		}

		for (unsigned int i = 3; i < params.size(); i++) {
			if (params[i] != "default") {
				if (colorstr.length() > 0)
					colorstr.append(",");
				colorstr.append("attr=");
				if (!utils::is_valid_attribute(params[i]))
					throw ConfigHandlerException(
						strprintf::fmt(
							_("`%s' is not a valid "
							  "attribute"),
							params[i]));
				colorstr.append(params[i]);
			}
		}

		std::shared_ptr<Matcher> m(new Matcher());
		if (!m->parse(params[0])) {
			throw ConfigHandlerException(strprintf::fmt(
				_("couldn't parse filter expression `%s': %s"),
				params[0],
				m->get_parse_error()));
		}

		int pos = locations["articlelist"].first.size();

		locations["articlelist"].first.push_back(nullptr);
		locations["articlelist"].second.push_back(colorstr);

		matchers.push_back(
			std::pair<std::shared_ptr<Matcher>, int>(m, pos));

	} else
		throw ConfigHandlerException(
			ActionHandlerStatus::INVALID_COMMAND);
}

int RegexManager::article_matches(Matchable* item)
{
	for (const auto& Matcher : matchers) {
		if (Matcher.first->matches(item)) {
			return Matcher.second;
		}
	}
	return -1;
}

void RegexManager::remove_last_regex(const std::string& location)
{
	std::vector<regex_t*>& regexes = locations[location].first;

	auto it = regexes.begin() + regexes.size() - 1;
	delete *it;
	regexes.erase(it);
}

std::string RegexManager::extract_initial_marker(const std::string& str)
{
	if (str.length() == 0)
		return "";

	if (str[0] == '<') {
		std::string::size_type pos = str.find_first_of(">", 0);
		if (pos != std::string::npos) {
			return str.substr(0, pos + 1);
		}
	}
	return "";
}

void RegexManager::quote_and_highlight(std::string& str,
	const std::string& location)
{
	std::vector<regex_t*>& regexes = locations[location].first;

	unsigned int i = 0;
	for (const auto& regex : regexes) {
		if (!regex)
			continue;
		std::string initial_marker = extract_initial_marker(str);
		regmatch_t pmatch;
		unsigned int offset = 0;
		int err = regexec(regex, str.c_str(), 1, &pmatch, 0);
		while (err == 0) {
			if (pmatch.rm_so != pmatch.rm_eo) {
				const std::string marker = strprintf::fmt("<%u>", i);
				str.insert(offset + pmatch.rm_eo,
						std::string("</>") + initial_marker);
				str.insert(offset + pmatch.rm_so, marker);
				offset += pmatch.rm_eo + marker.length() +
					strlen("</>") + initial_marker.length();
			}
			else
			{
				offset++;
			}
			if (offset >= str.length())
				break;
			err = regexec(
				regex, str.c_str() + offset, 1, &pmatch, 0);
		}
		i++;
	}
}

} // namespace newsboat
<commit_msg>Make requested changes<commit_after>#include "regexmanager.h"

#include <cstring>

#include "config.h"
#include "exceptions.h"
#include "logger.h"
#include "strprintf.h"
#include "utils.h"

namespace newsboat {

RegexManager::RegexManager()
{
	// this creates the entries in the map. we need them there to have the
	// "all" location work.
	locations["article"];
	locations["articlelist"];
	locations["feedlist"];
}

RegexManager::~RegexManager()
{
	for (const auto& location : locations) {
		if (location.second.first.size() > 0) {
			for (const auto& regex : location.second.first) {
				delete regex;
			}
		}
	}
}

void RegexManager::dump_config(std::vector<std::string>& config_output)
{
	for (const auto& foo : cheat_store_for_dump_config) {
		config_output.push_back(foo);
	}
}

void RegexManager::handle_action(const std::string& action,
	const std::vector<std::string>& params)
{
	if (action == "highlight") {
		if (params.size() < 3)
			throw ConfigHandlerException(
				ActionHandlerStatus::TOO_FEW_PARAMS);

		std::string location = params[0];
		if (location != "all" && location != "article" &&
			location != "articlelist" && location != "feedlist")
			throw ConfigHandlerException(strprintf::fmt(
				_("`%s' is an invalid dialog type"), location));

		regex_t* rx = new regex_t;
		int err;
		if ((err = regcomp(rx,
			     params[1].c_str(),
			     REG_EXTENDED | REG_ICASE)) != 0) {
			char buf[1024];
			regerror(err, rx, buf, sizeof(buf));
			delete rx;
			throw ConfigHandlerException(strprintf::fmt(
				_("`%s' is not a valid regular expression: %s"),
				params[1],
				buf));
		}
		std::string colorstr;
		if (params[2] != "default") {
			colorstr.append("fg=");
			if (!utils::is_valid_color(params[2]))
				throw ConfigHandlerException(strprintf::fmt(
					_("`%s' is not a valid color"),
					params[2]));
			colorstr.append(params[2]);
		}
		if (params.size() > 3) {
			if (params[3] != "default") {
				if (colorstr.length() > 0)
					colorstr.append(",");
				colorstr.append("bg=");
				if (!utils::is_valid_color(params[3]))
					throw ConfigHandlerException(
						strprintf::fmt(
							_("`%s' is not a valid "
							  "color"),
							params[3]));
				colorstr.append(params[3]);
			}
			for (unsigned int i = 4; i < params.size(); ++i) {
				if (params[i] != "default") {
					if (colorstr.length() > 0)
						colorstr.append(",");
					colorstr.append("attr=");
					if (!utils::is_valid_attribute(
						    params[i]))
						throw ConfigHandlerException(
							strprintf::fmt(
								_("`%s' is not "
								  "a valid "
								  "attribute"),
								params[i]));
					colorstr.append(params[i]);
				}
			}
		}
		if (location != "all") {
			LOG(Level::DEBUG,
				"RegexManager::handle_action: adding rx = %s "
				"colorstr = %s to location %s",
				params[1],
				colorstr,
				location);
			locations[location].first.push_back(rx);
			locations[location].second.push_back(colorstr);
		} else {
			delete rx;
			for (auto& location : locations) {
				LOG(Level::DEBUG,
					"RegexManager::handle_action: adding "
					"rx = "
					"%s colorstr = %s to location %s",
					params[1],
					colorstr,
					location.first);
				rx = new regex_t;
				// we need to create a new one for each
				// push_back, otherwise we'd have double frees.
				regcomp(rx,
					params[1].c_str(),
					REG_EXTENDED | REG_ICASE);
				location.second.first.push_back(rx);
				location.second.second.push_back(colorstr);
			}
		}
		std::string line = "highlight";
		for (const auto& param : params) {
			line.append(" ");
			line.append(utils::quote(param));
		}
		cheat_store_for_dump_config.push_back(line);
	} else if (action == "highlight-article") {
		if (params.size() < 3)
			throw ConfigHandlerException(
				ActionHandlerStatus::TOO_FEW_PARAMS);

		std::string expr = params[0];
		std::string fgcolor = params[1];
		std::string bgcolor = params[2];

		std::string colorstr;
		if (fgcolor != "default") {
			colorstr.append("fg=");
			if (!utils::is_valid_color(fgcolor))
				throw ConfigHandlerException(strprintf::fmt(
					_("`%s' is not a valid color"),
					fgcolor));
			colorstr.append(fgcolor);
		}
		if (bgcolor != "default") {
			if (colorstr.length() > 0)
				colorstr.append(",");
			colorstr.append("bg=");
			if (!utils::is_valid_color(bgcolor))
				throw ConfigHandlerException(strprintf::fmt(
					_("`%s' is not a valid color"),
					bgcolor));
			colorstr.append(bgcolor);
		}

		for (unsigned int i = 3; i < params.size(); i++) {
			if (params[i] != "default") {
				if (colorstr.length() > 0)
					colorstr.append(",");
				colorstr.append("attr=");
				if (!utils::is_valid_attribute(params[i]))
					throw ConfigHandlerException(
						strprintf::fmt(
							_("`%s' is not a valid "
							  "attribute"),
							params[i]));
				colorstr.append(params[i]);
			}
		}

		std::shared_ptr<Matcher> m(new Matcher());
		if (!m->parse(params[0])) {
			throw ConfigHandlerException(strprintf::fmt(
				_("couldn't parse filter expression `%s': %s"),
				params[0],
				m->get_parse_error()));
		}

		int pos = locations["articlelist"].first.size();

		locations["articlelist"].first.push_back(nullptr);
		locations["articlelist"].second.push_back(colorstr);

		matchers.push_back(
			std::pair<std::shared_ptr<Matcher>, int>(m, pos));

	} else
		throw ConfigHandlerException(
			ActionHandlerStatus::INVALID_COMMAND);
}

int RegexManager::article_matches(Matchable* item)
{
	for (const auto& Matcher : matchers) {
		if (Matcher.first->matches(item)) {
			return Matcher.second;
		}
	}
	return -1;
}

void RegexManager::remove_last_regex(const std::string& location)
{
	std::vector<regex_t*>& regexes = locations[location].first;

	auto it = regexes.begin() + regexes.size() - 1;
	delete *it;
	regexes.erase(it);
}

std::string RegexManager::extract_initial_marker(const std::string& str)
{
	if (str.length() == 0)
		return "";

	if (str[0] == '<') {
		std::string::size_type pos = str.find_first_of(">", 0);
		if (pos != std::string::npos) {
			return str.substr(0, pos + 1);
		}
	}
	return "";
}

void RegexManager::quote_and_highlight(std::string& str,
	const std::string& location)
{
	std::vector<regex_t*>& regexes = locations[location].first;

	unsigned int i = 0;
	for (const auto& regex : regexes) {
		if (!regex) {
			continue;
		}
		std::string initial_marker = extract_initial_marker(str);
		regmatch_t pmatch;
		unsigned int offset = 0;
		int err = regexec(regex, str.c_str(), 1, &pmatch, 0);
		while (err == 0) {
			if (pmatch.rm_so != pmatch.rm_eo) {
				const std::string marker = strprintf::fmt("<%u>", i);
				str.insert(offset + pmatch.rm_eo,
						std::string("</>") + initial_marker);
				str.insert(offset + pmatch.rm_so, marker);
				offset += pmatch.rm_eo + marker.length() +
					strlen("</>") + initial_marker.length();
			}
			else {
				offset++;
			}
			if (offset >= str.length()) {
				break;
			}
			err = regexec(
				regex, str.c_str() + offset, 1, &pmatch, 0);
		}
		i++;
	}
}

} // namespace newsboat
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Benny Bobaganoosh
 *
 * 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 "mesh.h"
#include "profiling.h"
#include <GL/glew.h>
#include <iostream>

#include <vector>
#include <cassert>

#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>

std::map<std::string, MeshData*> Mesh::s_resourceMap;

bool IndexedModel::IsValid() const
{
	return m_positions.size() == m_texCoords.size()
		&& m_texCoords.size() == m_normals.size()
		&& m_normals.size() == m_tangents.size();
}

void IndexedModel::AddVertex(const Vector3f& vert)
{
	m_positions.push_back(vert);
}

void IndexedModel::AddTexCoord(const Vector2f& texCoord)
{
	m_texCoords.push_back(texCoord);
}

void IndexedModel::AddNormal(const Vector3f& normal)
{
	m_normals.push_back(normal);
}

void IndexedModel::AddTangent(const Vector3f& tangent)
{
	m_tangents.push_back(tangent);
}

IndexedModel IndexedModel::Finalize()
{
	if(IsValid())
	{
		return *this;
	}

	if(m_texCoords.size() == 0)
	{
		for(unsigned int i = m_texCoords.size(); i < m_positions.size(); i++)
		{
			m_texCoords.push_back(Vector2f(0.0f, 0.0f));
		}
	}

	if(m_normals.size() == 0)
	{
		CalcNormals();
	}

	if(m_tangents.size() == 0)
	{
		CalcTangents();
	}

	return *this;
}

void IndexedModel::AddFace(unsigned int vertIndex0, unsigned int vertIndex1, unsigned int vertIndex2)
{
	m_indices.push_back(vertIndex0);
	m_indices.push_back(vertIndex1);
	m_indices.push_back(vertIndex2);
}

void IndexedModel::CalcNormals()
{
	m_normals.clear();
	m_normals.reserve(m_positions.size());

	for(unsigned int i = 0; i < m_positions.size(); i++)
		m_normals.push_back(Vector3f(0,0,0));

	for(unsigned int i = 0; i < m_indices.size(); i += 3)
	{
		int i0 = m_indices[i];
		int i1 = m_indices[i + 1];
		int i2 = m_indices[i + 2];

		Vector3f v1 = m_positions[i1] - m_positions[i0];
		Vector3f v2 = m_positions[i2] - m_positions[i0];

		Vector3f normal = v1.Cross(v2).Normalized();

		m_normals[i0] = m_normals[i0] + normal;
		m_normals[i1] = m_normals[i1] + normal;
		m_normals[i2] = m_normals[i2] + normal;
	}

	for(unsigned int i = 0; i < m_normals.size(); i++)
		m_normals[i] = m_normals[i].Normalized();
}

void IndexedModel::CalcTangents()
{
	m_tangents.clear();
	m_tangents.reserve(m_positions.size());

	for(unsigned int i = 0; i < m_positions.size(); i++)
		m_tangents.push_back(Vector3f(0,0,0));

	for(unsigned int i = 0; i < m_indices.size(); i += 3)
    {
		int i0 = m_indices[i];
		int i1 = m_indices[i + 1];
		int i2 = m_indices[i + 2];

        Vector3f edge1 = m_positions[i1] - m_positions[i0];
        Vector3f edge2 = m_positions[i2] - m_positions[i0];

        float deltaU1 = m_texCoords[i1].GetX() - m_texCoords[i0].GetX();
        float deltaU2 = m_texCoords[i2].GetX() - m_texCoords[i0].GetX();
        float deltaV1 = m_texCoords[i1].GetY() - m_texCoords[i0].GetY();
        float deltaV2 = m_texCoords[i2].GetY() - m_texCoords[i0].GetY();

        float dividend = (deltaU1 * deltaV2 - deltaU2 * deltaV1);
        float f = dividend == 0.0f ? 0.0f : 1.0f/dividend;

        Vector3f tangent = Vector3f(0,0,0);

        tangent.SetX(f * (deltaV2 * edge1.GetX() - deltaV1 * edge2.GetX()));
        tangent.SetY(f * (deltaV2 * edge1.GetY() - deltaV1 * edge2.GetY()));
        tangent.SetZ(f * (deltaV2 * edge1.GetZ() - deltaV1 * edge2.GetZ()));

//Bitangent example, in Java
//		Vector3f bitangent = new Vector3f(0,0,0);
//
//		bitangent.setX(f * (-deltaU2 * edge1.getX() - deltaU1 * edge2.getX()));
//		bitangent.setX(f * (-deltaU2 * edge1.getY() - deltaU1 * edge2.getY()));
//		bitangent.setX(f * (-deltaU2 * edge1.getZ() - deltaU1 * edge2.getZ()));

		m_tangents[i0] += tangent;
		m_tangents[i1] += tangent;
		m_tangents[i2] += tangent;
    }

    for(unsigned int i = 0; i < m_tangents.size(); i++)
		m_tangents[i] = m_tangents[i].Normalized();
}

MeshData::MeshData(const IndexedModel& model) :
	ReferenceCounter(),
	m_drawCount(model.GetIndices().size())
{
	if(!model.IsValid())
	{
		std::cout << "Error: Invalid mesh! Must have same number of positions, texCoords, normals, and tangents! "
			<< "(Maybe you forgot to Finalize() your IndexedModel?)" << std::endl;
		assert(0 != 0);
	}
	glGenVertexArrays(1, &m_vertexArrayObject);
	glBindVertexArray(m_vertexArrayObject);

	glGenBuffers(NUM_BUFFERS, m_vertexArrayBuffers);
	glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[POSITION_VB]);
	glBufferData(GL_ARRAY_BUFFER, model.GetPositions().size() * sizeof(model.GetPositions()[0]), &model.GetPositions()[0], GL_STATIC_DRAW);

	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

	glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[TEXCOORD_VB]);
	glBufferData(GL_ARRAY_BUFFER, model.GetTexCoords().size() * sizeof(model.GetTexCoords()[0]), &model.GetTexCoords()[0], GL_STATIC_DRAW);

	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);

	glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[NORMAL_VB]);
	glBufferData(GL_ARRAY_BUFFER, model.GetNormals().size() * sizeof(model.GetNormals()[0]), &model.GetNormals()[0], GL_STATIC_DRAW);

	glEnableVertexAttribArray(2);
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);

	glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[TANGENT_VB]);
	glBufferData(GL_ARRAY_BUFFER, model.GetTangents().size() * sizeof(model.GetTangents()[0]), &model.GetTangents()[0], GL_STATIC_DRAW);

	glEnableVertexAttribArray(3);
	glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, 0);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vertexArrayBuffers[INDEX_VB]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, model.GetIndices().size() * sizeof(model.GetIndices()[0]), &model.GetIndices()[0], GL_STATIC_DRAW);
}

MeshData::~MeshData()
{
	glDeleteVertexArrays(1, &m_vertexArrayObject);
}

void MeshData::Draw() const
{
	glBindVertexArray(m_vertexArrayObject);

	#if PROFILING_DISABLE_MESH_DRAWING == 0
		glDrawElements(GL_TRIANGLES, m_drawCount, GL_UNSIGNED_INT, 0);
	#endif
}

Mesh::Mesh(const std::string& meshName, const IndexedModel& model) :
	m_fileName(meshName)
{
	std::map<std::string, MeshData*>::const_iterator it = s_resourceMap.find(meshName);
	if(it != s_resourceMap.end())
	{
		std::cout << "Error adding mesh " << meshName << ": Mesh already exists by the same name!" << std::endl;
		assert(0 != 0);
	}
	else
	{
		m_meshData = new MeshData(model);
		s_resourceMap.insert(std::pair<std::string, MeshData*>(meshName, m_meshData));
	}
}

Mesh::Mesh(const std::string& fileName) :
	m_fileName(fileName),
	m_meshData(0)
{
	std::map<std::string, MeshData*>::const_iterator it = s_resourceMap.find(fileName);
	if(it != s_resourceMap.end())
	{
		m_meshData = it->second;
		m_meshData->AddReference();
	}
	else
	{
		Assimp::Importer importer;

		const aiScene* scene = importer.ReadFile(("./res/models/" + fileName).c_str(),
		                                         aiProcess_Triangulate |
		                                         aiProcess_GenSmoothNormals |
		                                         aiProcess_FlipUVs |
		                                         aiProcess_CalcTangentSpace);

		if(!scene)
		{
			std::cout << "Mesh load failed!: " << fileName << std::endl;
			assert(0 == 0);
		}

		const aiMesh* model = scene->mMeshes[0];

		std::vector<Vector3f> positions;
		std::vector<Vector2f> texCoords;
		std::vector<Vector3f> normals;
		std::vector<Vector3f> tangents;
		std::vector<unsigned int> indices;

		const aiVector3D aiZeroVector(0.0f, 0.0f, 0.0f);
		for(unsigned int i = 0; i < model->mNumVertices; i++)
		{
			const aiVector3D pos = model->mVertices[i];
			const aiVector3D normal = model->mNormals[i];
			const aiVector3D texCoord = model->HasTextureCoords(0) ? model->mTextureCoords[0][i] : aiZeroVector;
			const aiVector3D tangent = model->mTangents[i];

			positions.push_back(Vector3f(pos.x, pos.y, pos.z));
			texCoords.push_back(Vector2f(texCoord.x, texCoord.y));
			normals.push_back(Vector3f(normal.x, normal.y, normal.z));
			tangents.push_back(Vector3f(tangent.x, tangent.y, tangent.z));
		}

		for(unsigned int i = 0; i < model->mNumFaces; i++)
		{
			const aiFace& face = model->mFaces[i];
			assert(face.mNumIndices == 3);
			indices.push_back(face.mIndices[0]);
			indices.push_back(face.mIndices[1]);
			indices.push_back(face.mIndices[2]);
		}

		m_meshData = new MeshData(IndexedModel(indices, positions, texCoords, normals, tangents));
		s_resourceMap.insert(std::pair<std::string, MeshData*>(fileName, m_meshData));
	}
}

Mesh::Mesh(const Mesh& mesh) :
	m_fileName(mesh.m_fileName),
	m_meshData(mesh.m_meshData)
{
	m_meshData->AddReference();
}

Mesh::~Mesh()
{
	if(m_meshData && m_meshData->RemoveReference())
	{
		if(m_fileName.length() > 0)
		{
			s_resourceMap.erase(m_fileName);
		}

		delete m_meshData;
	}
}

void Mesh::Draw() const
{
	m_meshData->Draw();
}
<commit_msg>Added missing delete in MeshData destructor<commit_after>/*
 * Copyright (C) 2014 Benny Bobaganoosh
 *
 * 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 "mesh.h"
#include "profiling.h"
#include <GL/glew.h>
#include <iostream>

#include <vector>
#include <cassert>

#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>

std::map<std::string, MeshData*> Mesh::s_resourceMap;

bool IndexedModel::IsValid() const
{
	return m_positions.size() == m_texCoords.size()
		&& m_texCoords.size() == m_normals.size()
		&& m_normals.size() == m_tangents.size();
}

void IndexedModel::AddVertex(const Vector3f& vert)
{
	m_positions.push_back(vert);
}

void IndexedModel::AddTexCoord(const Vector2f& texCoord)
{
	m_texCoords.push_back(texCoord);
}

void IndexedModel::AddNormal(const Vector3f& normal)
{
	m_normals.push_back(normal);
}

void IndexedModel::AddTangent(const Vector3f& tangent)
{
	m_tangents.push_back(tangent);
}

IndexedModel IndexedModel::Finalize()
{
	if(IsValid())
	{
		return *this;
	}

	if(m_texCoords.size() == 0)
	{
		for(unsigned int i = m_texCoords.size(); i < m_positions.size(); i++)
		{
			m_texCoords.push_back(Vector2f(0.0f, 0.0f));
		}
	}

	if(m_normals.size() == 0)
	{
		CalcNormals();
	}

	if(m_tangents.size() == 0)
	{
		CalcTangents();
	}

	return *this;
}

void IndexedModel::AddFace(unsigned int vertIndex0, unsigned int vertIndex1, unsigned int vertIndex2)
{
	m_indices.push_back(vertIndex0);
	m_indices.push_back(vertIndex1);
	m_indices.push_back(vertIndex2);
}

void IndexedModel::CalcNormals()
{
	m_normals.clear();
	m_normals.reserve(m_positions.size());

	for(unsigned int i = 0; i < m_positions.size(); i++)
		m_normals.push_back(Vector3f(0,0,0));

	for(unsigned int i = 0; i < m_indices.size(); i += 3)
	{
		int i0 = m_indices[i];
		int i1 = m_indices[i + 1];
		int i2 = m_indices[i + 2];

		Vector3f v1 = m_positions[i1] - m_positions[i0];
		Vector3f v2 = m_positions[i2] - m_positions[i0];

		Vector3f normal = v1.Cross(v2).Normalized();

		m_normals[i0] = m_normals[i0] + normal;
		m_normals[i1] = m_normals[i1] + normal;
		m_normals[i2] = m_normals[i2] + normal;
	}

	for(unsigned int i = 0; i < m_normals.size(); i++)
		m_normals[i] = m_normals[i].Normalized();
}

void IndexedModel::CalcTangents()
{
	m_tangents.clear();
	m_tangents.reserve(m_positions.size());

	for(unsigned int i = 0; i < m_positions.size(); i++)
		m_tangents.push_back(Vector3f(0,0,0));

	for(unsigned int i = 0; i < m_indices.size(); i += 3)
    {
		int i0 = m_indices[i];
		int i1 = m_indices[i + 1];
		int i2 = m_indices[i + 2];

        Vector3f edge1 = m_positions[i1] - m_positions[i0];
        Vector3f edge2 = m_positions[i2] - m_positions[i0];

        float deltaU1 = m_texCoords[i1].GetX() - m_texCoords[i0].GetX();
        float deltaU2 = m_texCoords[i2].GetX() - m_texCoords[i0].GetX();
        float deltaV1 = m_texCoords[i1].GetY() - m_texCoords[i0].GetY();
        float deltaV2 = m_texCoords[i2].GetY() - m_texCoords[i0].GetY();

        float dividend = (deltaU1 * deltaV2 - deltaU2 * deltaV1);
        float f = dividend == 0.0f ? 0.0f : 1.0f/dividend;

        Vector3f tangent = Vector3f(0,0,0);

        tangent.SetX(f * (deltaV2 * edge1.GetX() - deltaV1 * edge2.GetX()));
        tangent.SetY(f * (deltaV2 * edge1.GetY() - deltaV1 * edge2.GetY()));
        tangent.SetZ(f * (deltaV2 * edge1.GetZ() - deltaV1 * edge2.GetZ()));

//Bitangent example, in Java
//		Vector3f bitangent = new Vector3f(0,0,0);
//
//		bitangent.setX(f * (-deltaU2 * edge1.getX() - deltaU1 * edge2.getX()));
//		bitangent.setX(f * (-deltaU2 * edge1.getY() - deltaU1 * edge2.getY()));
//		bitangent.setX(f * (-deltaU2 * edge1.getZ() - deltaU1 * edge2.getZ()));

		m_tangents[i0] += tangent;
		m_tangents[i1] += tangent;
		m_tangents[i2] += tangent;
    }

    for(unsigned int i = 0; i < m_tangents.size(); i++)
		m_tangents[i] = m_tangents[i].Normalized();
}

MeshData::MeshData(const IndexedModel& model) :
	ReferenceCounter(),
	m_drawCount(model.GetIndices().size())
{
	if(!model.IsValid())
	{
		std::cout << "Error: Invalid mesh! Must have same number of positions, texCoords, normals, and tangents! "
			<< "(Maybe you forgot to Finalize() your IndexedModel?)" << std::endl;
		assert(0 != 0);
	}
	glGenVertexArrays(1, &m_vertexArrayObject);
	glBindVertexArray(m_vertexArrayObject);

	glGenBuffers(NUM_BUFFERS, m_vertexArrayBuffers);
	glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[POSITION_VB]);
	glBufferData(GL_ARRAY_BUFFER, model.GetPositions().size() * sizeof(model.GetPositions()[0]), &model.GetPositions()[0], GL_STATIC_DRAW);

	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

	glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[TEXCOORD_VB]);
	glBufferData(GL_ARRAY_BUFFER, model.GetTexCoords().size() * sizeof(model.GetTexCoords()[0]), &model.GetTexCoords()[0], GL_STATIC_DRAW);

	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);

	glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[NORMAL_VB]);
	glBufferData(GL_ARRAY_BUFFER, model.GetNormals().size() * sizeof(model.GetNormals()[0]), &model.GetNormals()[0], GL_STATIC_DRAW);

	glEnableVertexAttribArray(2);
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);

	glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[TANGENT_VB]);
	glBufferData(GL_ARRAY_BUFFER, model.GetTangents().size() * sizeof(model.GetTangents()[0]), &model.GetTangents()[0], GL_STATIC_DRAW);

	glEnableVertexAttribArray(3);
	glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, 0);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vertexArrayBuffers[INDEX_VB]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, model.GetIndices().size() * sizeof(model.GetIndices()[0]), &model.GetIndices()[0], GL_STATIC_DRAW);
}

MeshData::~MeshData()
{
	glDeleteBuffers(NUM_BUFFERS, m_vertexArrayBuffers);
	glDeleteVertexArrays(1, &m_vertexArrayObject);
}

void MeshData::Draw() const
{
	glBindVertexArray(m_vertexArrayObject);

	#if PROFILING_DISABLE_MESH_DRAWING == 0
		glDrawElements(GL_TRIANGLES, m_drawCount, GL_UNSIGNED_INT, 0);
	#endif
}

Mesh::Mesh(const std::string& meshName, const IndexedModel& model) :
	m_fileName(meshName)
{
	std::map<std::string, MeshData*>::const_iterator it = s_resourceMap.find(meshName);
	if(it != s_resourceMap.end())
	{
		std::cout << "Error adding mesh " << meshName << ": Mesh already exists by the same name!" << std::endl;
		assert(0 != 0);
	}
	else
	{
		m_meshData = new MeshData(model);
		s_resourceMap.insert(std::pair<std::string, MeshData*>(meshName, m_meshData));
	}
}

Mesh::Mesh(const std::string& fileName) :
	m_fileName(fileName),
	m_meshData(0)
{
	std::map<std::string, MeshData*>::const_iterator it = s_resourceMap.find(fileName);
	if(it != s_resourceMap.end())
	{
		m_meshData = it->second;
		m_meshData->AddReference();
	}
	else
	{
		Assimp::Importer importer;

		const aiScene* scene = importer.ReadFile(("./res/models/" + fileName).c_str(),
		                                         aiProcess_Triangulate |
		                                         aiProcess_GenSmoothNormals |
		                                         aiProcess_FlipUVs |
		                                         aiProcess_CalcTangentSpace);

		if(!scene)
		{
			std::cout << "Mesh load failed!: " << fileName << std::endl;
			assert(0 == 0);
		}

		const aiMesh* model = scene->mMeshes[0];

		std::vector<Vector3f> positions;
		std::vector<Vector2f> texCoords;
		std::vector<Vector3f> normals;
		std::vector<Vector3f> tangents;
		std::vector<unsigned int> indices;

		const aiVector3D aiZeroVector(0.0f, 0.0f, 0.0f);
		for(unsigned int i = 0; i < model->mNumVertices; i++)
		{
			const aiVector3D pos = model->mVertices[i];
			const aiVector3D normal = model->mNormals[i];
			const aiVector3D texCoord = model->HasTextureCoords(0) ? model->mTextureCoords[0][i] : aiZeroVector;
			const aiVector3D tangent = model->mTangents[i];

			positions.push_back(Vector3f(pos.x, pos.y, pos.z));
			texCoords.push_back(Vector2f(texCoord.x, texCoord.y));
			normals.push_back(Vector3f(normal.x, normal.y, normal.z));
			tangents.push_back(Vector3f(tangent.x, tangent.y, tangent.z));
		}

		for(unsigned int i = 0; i < model->mNumFaces; i++)
		{
			const aiFace& face = model->mFaces[i];
			assert(face.mNumIndices == 3);
			indices.push_back(face.mIndices[0]);
			indices.push_back(face.mIndices[1]);
			indices.push_back(face.mIndices[2]);
		}

		m_meshData = new MeshData(IndexedModel(indices, positions, texCoords, normals, tangents));
		s_resourceMap.insert(std::pair<std::string, MeshData*>(fileName, m_meshData));
	}
}

Mesh::Mesh(const Mesh& mesh) :
	m_fileName(mesh.m_fileName),
	m_meshData(mesh.m_meshData)
{
	m_meshData->AddReference();
}

Mesh::~Mesh()
{
	if(m_meshData && m_meshData->RemoveReference())
	{
		if(m_fileName.length() > 0)
		{
			s_resourceMap.erase(m_fileName);
		}

		delete m_meshData;
	}
}

void Mesh::Draw() const
{
	m_meshData->Draw();
}
<|endoftext|>


<commit_before>#ifndef RAISE_HXX
# define RAISE_HXX

# include <scheduler/scheduler.hh>
# include <kernel/userver.hh>
# include <object/global.hh>
# include <object/string.hh>
# include <runner/runner.hh>
# include <runner/call.hh>

namespace runner
{
  LIBPORT_SPEED_INLINE
  void
  raise(const object::rObject& exn)
  {
    ::urbiserver->getCurrentRunner().raise(exn);
    pabort("Unreachable");
  }

  LIBPORT_SPEED_INLINE
  void
  raise_lookup_error(libport::Symbol msg, const object::rObject& obj)
  {
    Runner& r = ::urbiserver->getCurrentRunner();
    object::rObject exn =
      urbi_call(r,
                object::global_class->slot_get(SYMBOL(LookupError)),
                SYMBOL(new), new object::String(msg), obj);
    r.raise(exn);
    pabort("Unreachable");
  }

  LIBPORT_SPEED_INLINE
  void
  raise_arity_error(unsigned effective,
                    unsigned expected)
  {
    Runner& r = ::urbiserver->getCurrentRunner();
    object::rObject exn =
      urbi_call(r, object::global_class->slot_get(SYMBOL(ArityError)),
                SYMBOL(new),
                new object::String(r.innermost_call_get()),
                new object::Float(effective),
                new object::Float(expected));
    r.raise(exn, true);
    pabort("Unreachable");
  }

  LIBPORT_SPEED_INLINE
  void
  raise_arity_error(unsigned effective,
                    unsigned minimum,
                    unsigned maximum)
  {
    Runner& r = ::urbiserver->getCurrentRunner();
    object::rObject exn =
      urbi_call(r, object::global_class->slot_get(SYMBOL(ArityError)),
                SYMBOL(new),
                new object::String(r.innermost_call_get()),
                new object::Float(effective),
                new object::Float(minimum),
                new object::Float(maximum));
    r.raise(exn, true);
    pabort("Unreachable");
  }

  LIBPORT_SPEED_INLINE
  void
  raise_argument_type_error(unsigned idx,
                            object::rObject effective,
                            object::rObject expected)
  {
    Runner& r = ::urbiserver->getCurrentRunner();
    r.raise(
      urbi_call(r, object::global_class->slot_get(SYMBOL(ArgumentTypeError)),
                SYMBOL(new),
                new object::String(r.innermost_call_get()),
                new object::Float(idx),
                expected,
                effective),
      true);
    pabort("Unreachable");
  }

}

#endif
<commit_msg>Import "urbi_call" from "object" namespace.<commit_after>#ifndef RAISE_HXX
# define RAISE_HXX

# include <scheduler/scheduler.hh>
# include <kernel/userver.hh>
# include <object/global.hh>
# include <object/string.hh>
# include <runner/runner.hh>
# include <runner/call.hh>

namespace runner
{
  LIBPORT_SPEED_INLINE
  void
  raise(const object::rObject& exn)
  {
    ::urbiserver->getCurrentRunner().raise(exn);
    pabort("Unreachable");
  }

  LIBPORT_SPEED_INLINE
  void
  raise_lookup_error(libport::Symbol msg, const object::rObject& obj)
  {
    Runner& r = ::urbiserver->getCurrentRunner();
    object::rObject exn =
      object::urbi_call(r,
                        object::global_class->slot_get(SYMBOL(LookupError)),
                        SYMBOL(new), new object::String(msg), obj);
    r.raise(exn);
    pabort("Unreachable");
  }

  LIBPORT_SPEED_INLINE
  void
  raise_arity_error(unsigned effective,
                    unsigned expected)
  {
    Runner& r = ::urbiserver->getCurrentRunner();
    object::rObject exn =
      object::urbi_call(r, object::global_class->slot_get(SYMBOL(ArityError)),
                        SYMBOL(new),
                        new object::String(r.innermost_call_get()),
                        new object::Float(effective),
                        new object::Float(expected));
    r.raise(exn, true);
    pabort("Unreachable");
  }

  LIBPORT_SPEED_INLINE
  void
  raise_arity_error(unsigned effective,
                    unsigned minimum,
                    unsigned maximum)
  {
    Runner& r = ::urbiserver->getCurrentRunner();
    object::rObject exn =
      object::urbi_call(r, object::global_class->slot_get(SYMBOL(ArityError)),
                        SYMBOL(new),
                        new object::String(r.innermost_call_get()),
                        new object::Float(effective),
                        new object::Float(minimum),
                        new object::Float(maximum));
    r.raise(exn, true);
    pabort("Unreachable");
  }

  LIBPORT_SPEED_INLINE
  void
  raise_argument_type_error(unsigned idx,
                            object::rObject effective,
                            object::rObject expected)
  {
    Runner& r = ::urbiserver->getCurrentRunner();
    r.raise(
      object::urbi_call(r, object::global_class->slot_get(SYMBOL(ArgumentTypeError)),
                        SYMBOL(new),
                        new object::String(r.innermost_call_get()),
                        new object::Float(idx),
                        expected,
                        effective),
      true);
    pabort("Unreachable");
  }

}

#endif
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2014 Lantern
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "natty.h"

#include <utility>
#include <iostream>
#include <fstream>
#include <streambuf>
#include <string>

#include "talk/app/webrtc/videosourceinterface.h"
#include "webrtc/base/common.h"
#include "talk/p2p/base/sessiondescription.h"
#include "talk/app/webrtc/test/fakeconstraints.h"
#include "talk/app/webrtc/peerconnectioninterface.h"
#include "talk/app/webrtc/portallocatorfactory.h"
#include "talk/session/media/mediasession.h"
#include "talk/p2p/base/constants.h"
#include "talk/app/webrtc/datachannelinterface.h"
#include "webrtc/base/json.h"
#include "webrtc/base/logging.h"
#include "talk/media/devices/devicemanager.h"

using namespace std;

#define DEBUG(fmt, ...) printf("%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__);

// Names used for a IceCandidate JSON object.
const char kCandidateSdpMidName[] = "sdpMid";
const char kCandidateSdpMlineIndexName[] = "sdpMLineIndex";
const char kCandidateSdpName[] = "candidate";

typedef webrtc::PeerConnectionInterface::IceServers IceServers;
typedef webrtc::PeerConnectionInterface::IceServer IceServer;;

// Names used for a SessionDescription JSON object.
const char kSessionDescriptionTypeName[] = "type";
const char kSessionDescriptionSdpName[] = "sdp";

void Natty::InputStream::read(Natty* natty) {
  //std::ifstream filein("cand-input.txt");
  while (getline(std::cin, input)) {
    /* need to remove new lines or the SDP won't be valid */
    if (input == "exit") {
      natty->Shutdown();
      break;
    }

    if (input.empty()) {
      /* terminate input on empty line */
      //std::cout << "\n";
      continue;
    }
    natty->ReadMessage(input);
  }

}

string Natty::InputStream::build() const {
  string str = ss.str();
  str.erase(
      std::remove(str.begin(), str.end(), '\n'), str.end()
      );
  return str;
}

class NattySessionObserver
: public webrtc::SetSessionDescriptionObserver {
  public:
    static NattySessionObserver* Create() {
      return
        new rtc::RefCountedObject<NattySessionObserver>();
    }
    virtual void OnSuccess() {
      LOG(INFO) << __FUNCTION__;
    }
    virtual void OnFailure(const std::string& error) {
      LOG(INFO) << __FUNCTION__ << " " << error;
    }

  protected:
    NattySessionObserver() {}
    ~NattySessionObserver() {}
};

bool NattySocket::Wait(int cms, bool process_io) {
  return rtc::PhysicalSocketServer::Wait(-1,
        process_io);
}

MessageClient::~MessageClient() {
  delete socket_;
}

void MessageClient::OnMessage(rtc::Message *pmsg) {
  NattyMessage* msg = static_cast<NattyMessage*>(pmsg->pdata);

  delete msg;
}

Natty::Natty(PeerConnectionClient* client,
    rtc::Thread* thread
    )
: peer_id_(-1),
  highestPrioritySeen(0),
  thread_(thread) {
  }

Natty::~Natty() {
  if (outfile != NULL) {
    outfile.close();
  }
  ASSERT(peer_connection_.get() == NULL);
}

bool Natty::connection_active() const {
  return peer_connection_.get() != NULL;
}

PeerConnectionClient* Natty::GetClient() {
  return client_;
}

std::string GetEnvVarOrDefault(const char* env_var_name,
    const char* default_value) {
  std::string value;
  const char* env_var = getenv(env_var_name);
  if (env_var)
    value = env_var;

  if (value.empty())
    value = default_value;

  return value;
}

std::string GetPeerConnectionString() {
  return GetEnvVarOrDefault("WEBRTC_CONNECT", "stun:stun.l.google.com:19302");
}

void Natty::AddStreams() {
  const char kStreamLabel[] = "stream_label";
  const uint16 kDefaultServerPort = 8888;
  const char kAudioLabel[] = "audio_label";
  if (active_streams_.find(kStreamLabel) != active_streams_.end())
    return;  // Already added.

  rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
      peer_connection_factory_->CreateAudioTrack(
          kAudioLabel, peer_connection_factory_->CreateAudioSource(NULL)));
  stream =
      peer_connection_factory_->CreateLocalMediaStream(kStreamLabel);

  stream->AddTrack(audio_track);
  if (!peer_connection_->AddStream(stream, NULL)) {
    LOG(LS_ERROR) << "Adding stream to PeerConnection failed";
  }
  typedef std::pair<std::string,
                    rtc::scoped_refptr<webrtc::MediaStreamInterface> >
      MediaStreamPair;
  active_streams_.insert(MediaStreamPair(stream->label(), stream));
}

bool Natty::InitializePeerConnection() {

  IceServers servers;
  IceServer server;
  webrtc::FakeConstraints constraints;
  constraints.SetAllowRtpDataChannels();

  ASSERT(peer_connection_factory_.get() == NULL);
  ASSERT(peer_connection_.get() == NULL);

  peer_connection_factory_  = webrtc::CreatePeerConnectionFactory();

  if (!peer_connection_factory_.get()) {
    LOG(INFO) << "Failed to initialize peer connection factory";
    Shutdown();
    return false;
  }
  LOG(INFO) << "Created peer connection factory";

  server.uri = GetPeerConnectionString();
  servers.push_back(server);

  peer_connection_ = peer_connection_factory_->CreatePeerConnection(servers, &constraints, allocator_factory_.get(), NULL, this);

  if (!peer_connection_.get()) {
    LOG(INFO) << "Create peer connection failed";
    Shutdown();
  }
  AddStreams();
  allocator = peer_connection_->GetAllocator();
  ASSERT(allocator != NULL);

  LOG(INFO) << "Created peer connection";

  return peer_connection_.get() != NULL;
}

void Natty::Shutdown() {
  LOG(INFO) << "Deleting peer connection";
  peer_connection_ = NULL;
  peer_connection_factory_ = NULL;
  peer_id_ = -1;
  //active_streams_.clear();
  rtc::CleanupSSL();
  thread_->Stop();
}

//
// PeerConnectionObserver implementation.
//

void Natty::OnError() {
  LOG(INFO) << __FUNCTION__;
}

void Natty::OnAddStream(webrtc::MediaStreamInterface* stream) {
  LOG(INFO) << "Successfully added stream";
}

/* the answerer removes his media stream before disconnecting
 * this is triggered when that happens and means we
 * were able to communicate
 */
void Natty::OnRemoveStream(webrtc::MediaStreamInterface* stream) {
  PickFinalCandidate();
}

void Natty::OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) {
}

void Natty::OnStateChange(
    webrtc::PeerConnectionObserver::StateType state_changed) {
}

void Natty::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) {
  Json::FastWriter writer;
  Json::Value jmessage;

  jmessage[kCandidateSdpMidName] = candidate->sdp_mid();
  jmessage[kCandidateSdpMlineIndexName] = candidate->sdp_mline_index();
  std::string sdp;
  if (!candidate->ToString(&sdp)) {
    LOG(LS_ERROR) << "Failed to serialize candidate";
    return;
  }

  jmessage[kCandidateSdpName] = sdp;
  outfile << writer.write(jmessage);
  outfile.flush();
}

void Natty::OnRenegotiationNeeded() {
  LOG(INFO) << "Renegotiation needed";
}

void Natty::ReadMessage(const std::string& message) {
  Json::Reader reader;
  Json::Value jmessage;
  std::string type;
  std::string json_object;

  if (!reader.parse(message, jmessage)) {
    LOG(INFO) << "Received an unknown message.";
    return;
  }
  GetStringFromJsonObject(jmessage, kSessionDescriptionTypeName, &type);

  if (!type.empty()) {
    std::string sdp;
    if (!GetStringFromJsonObject(jmessage, kSessionDescriptionSdpName, &sdp)) {
      LOG(INFO) << "Can't parse received session description message.";
      return;
    }
    session_description = webrtc::CreateSessionDescription(type, sdp);
    if (!session_description) {
      LOG(INFO) << "Can't parse SDP message";
      return;
    }
    LOG(INFO) << "Received session description " << message << " sending answer back";

    peer_connection_->SetRemoteDescription(
        NattySessionObserver::Create(), session_description);
    sleep(1);
    if (session_description->type() ==
        webrtc::SessionDescriptionInterface::kOffer) {
      peer_connection_->CreateAnswer(this, NULL);
      sleep(1);
    }
    return;
  }
  else {
    std::string sdp_mid;
    sdp_mlineindex = 0;
    std::string sdp;
    if (!GetStringFromJsonObject(jmessage, kCandidateSdpMidName, &sdp_mid) ||
        !GetIntFromJsonObject(jmessage, kCandidateSdpMlineIndexName,
          &sdp_mlineindex) ||
        !GetStringFromJsonObject(jmessage, kCandidateSdpName, &sdp)) {
      LOG(INFO) << "Can't parse received message";
      return;
    }
    rtc::scoped_ptr<webrtc::IceCandidateInterface> candidate(
        webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, sdp));
    LOG(INFO) << "Remote candidate information";

    if (!candidate.get()) {
      LOG(WARNING) << "Can't parse received candidate message.";
      return;
    }
    if (!peer_connection_->AddIceCandidate(candidate.get())) {
      LOG(WARNING) << "Failed to apply the received candidate";
      return;
    }
    LOG(INFO) << candidate.get()->candidate().ToString();
    LOG(INFO) << " Received candidate :" << message;
    PickFinalCandidate();
    return;
  }
};

void Natty::Output5Tuple(const cricket::Candidate *cand) {
   const char kStreamLabel[] = "stream_label";
   Json::FastWriter writer;
   Json::Value jmessage;
   jmessage["remote"] = cand->address().ToString();
   jmessage["local"] = cand->related_address().ToString();
   jmessage["type"] = cand->protocol();
   outfile << writer.write(jmessage);
   outfile.flush();
   peer_connection_->RemoveStream(stream);
   Shutdown();
}

void Natty::PickFinalCandidate() {
  const webrtc::IceCandidateCollection* candidates =
      session_description->candidates(sdp_mlineindex);

  for (size_t i = 0; i < candidates->count(); ++i) {
    const cricket::Candidate *cand = &candidates->at(i)->candidate();
    if (cand->type() == "stun") {
      Output5Tuple(cand);
      return;
    }
  }
}

void Natty::OnDataChannel(webrtc::DataChannelInterface* data_channel) {
  LOG(INFO) << "New data channel created " << data_channel;
}

void Natty::OnIceComplete() {
  LOG(INFO) << "ICE finished gathering candidates!";
}

// PeerConnectionObserver implementation.
void Natty::OnServerConnectionFailure() {

}

std::string GetPeerName() {
  char computer_name[256];
  if (gethostname(computer_name, ARRAY_SIZE(computer_name)) != 0)
    strcpy(computer_name, "host");
  std::string ret(GetEnvVarOrDefault("USERNAME", "user"));
  ret += '@';
  ret += computer_name;
  return ret;
}

void Natty::setMode(Natty::Mode m) {
  mode = m;
}

void Natty::Init(bool offer) {
  InitializePeerConnection();
  if (offer) {
    Natty::setMode(Natty::OFFER);
    webrtc::FakeConstraints constraints;
    constraints.SetAllowRtpDataChannels();
    peer_connection_->CreateOffer(this, &constraints);
    sleep(1);
  }
}

void Natty::ProcessInput() {
  Natty::InputStream is;
  is.read(this);

}

/* need to update this to accept stdout when the stdout
 * option is blank
 *
 */
void Natty::OpenDumpFile(const std::string& filename) {
  if (!filename.empty()) {
    outfile.open(filename.c_str());
  }
  else {
    outfile.copyfmt(std::cout);
    outfile.clear(std::cout.rdstate());
    outfile.basic_ios<char>::rdbuf(std::cout.rdbuf());
  }
}

void Natty::OnSuccess(webrtc::SessionDescriptionInterface* desc) {
  LOG(INFO) << "Setting local description";
  peer_connection_->SetLocalDescription(
      NattySessionObserver::Create(), desc);
  Json::FastWriter writer;
  Json::Value jmessage;
  jmessage[kSessionDescriptionTypeName] = desc->type();

  std::string sdp;
  desc->ToString(&sdp);
  jmessage[kSessionDescriptionSdpName] = sdp;
  outfile << writer.write(jmessage);
  outfile.flush();
}

void Natty::OnFailure(const std::string& error) {
  LOG(LERROR) << error;
}

void Natty::SendMessage(const std::string& json_object) {

}

<commit_msg>add peer connection close check<commit_after>/**
 * Copyright (C) 2014 Lantern
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "natty.h"

#include <utility>
#include <iostream>
#include <fstream>
#include <streambuf>
#include <string>

#include "talk/app/webrtc/videosourceinterface.h"
#include "webrtc/base/common.h"
#include "talk/p2p/base/sessiondescription.h"
#include "talk/app/webrtc/test/fakeconstraints.h"
#include "talk/app/webrtc/peerconnectioninterface.h"
#include "talk/app/webrtc/portallocatorfactory.h"
#include "talk/session/media/mediasession.h"
#include "talk/p2p/base/constants.h"
#include "talk/app/webrtc/datachannelinterface.h"
#include "webrtc/base/json.h"
#include "webrtc/base/logging.h"
#include "talk/media/devices/devicemanager.h"

using namespace std;

#define DEBUG(fmt, ...) printf("%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__);

// Names used for a IceCandidate JSON object.
const char kCandidateSdpMidName[] = "sdpMid";
const char kCandidateSdpMlineIndexName[] = "sdpMLineIndex";
const char kCandidateSdpName[] = "candidate";

typedef webrtc::PeerConnectionInterface::IceServers IceServers;
typedef webrtc::PeerConnectionInterface::IceServer IceServer;;

// Names used for a SessionDescription JSON object.
const char kSessionDescriptionTypeName[] = "type";
const char kSessionDescriptionSdpName[] = "sdp";

void Natty::InputStream::read(Natty* natty) {
  //std::ifstream filein("cand-input.txt");
  while (getline(std::cin, input)) {
    /* need to remove new lines or the SDP won't be valid */
    if (input == "exit") {
      natty->Shutdown();
      break;
    }

    if (input.empty()) {
      /* terminate input on empty line */
      //std::cout << "\n";
      continue;
    }
    natty->ReadMessage(input);
  }

}

string Natty::InputStream::build() const {
  string str = ss.str();
  str.erase(
      std::remove(str.begin(), str.end(), '\n'), str.end()
      );
  return str;
}

class NattySessionObserver
: public webrtc::SetSessionDescriptionObserver {
  public:
    static NattySessionObserver* Create() {
      return
        new rtc::RefCountedObject<NattySessionObserver>();
    }
    virtual void OnSuccess() {
      LOG(INFO) << __FUNCTION__;
    }
    virtual void OnFailure(const std::string& error) {
      LOG(INFO) << __FUNCTION__ << " " << error;
    }

  protected:
    NattySessionObserver() {}
    ~NattySessionObserver() {}
};

bool NattySocket::Wait(int cms, bool process_io) {
  return rtc::PhysicalSocketServer::Wait(-1,
        process_io);
}

MessageClient::~MessageClient() {
  delete socket_;
}

void MessageClient::OnMessage(rtc::Message *pmsg) {
  NattyMessage* msg = static_cast<NattyMessage*>(pmsg->pdata);

  delete msg;
}

Natty::Natty(PeerConnectionClient* client,
    rtc::Thread* thread
    )
: peer_id_(-1),
  highestPrioritySeen(0),
  thread_(thread) {
  }

Natty::~Natty() {
  if (outfile != NULL) {
    outfile.close();
  }
  ASSERT(peer_connection_.get() == NULL);
}

bool Natty::connection_active() const {
  return peer_connection_.get() != NULL;
}

PeerConnectionClient* Natty::GetClient() {
  return client_;
}

std::string GetEnvVarOrDefault(const char* env_var_name,
    const char* default_value) {
  std::string value;
  const char* env_var = getenv(env_var_name);
  if (env_var)
    value = env_var;

  if (value.empty())
    value = default_value;

  return value;
}

std::string GetPeerConnectionString() {
  return GetEnvVarOrDefault("WEBRTC_CONNECT", "stun:stun.l.google.com:19302");
}

void Natty::AddStreams() {
  const char kStreamLabel[] = "stream_label";
  const uint16 kDefaultServerPort = 8888;
  const char kAudioLabel[] = "audio_label";
  if (active_streams_.find(kStreamLabel) != active_streams_.end())
    return;  // Already added.

  rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
      peer_connection_factory_->CreateAudioTrack(
          kAudioLabel, peer_connection_factory_->CreateAudioSource(NULL)));
  stream =
      peer_connection_factory_->CreateLocalMediaStream(kStreamLabel);

  stream->AddTrack(audio_track);
  if (!peer_connection_->AddStream(stream, NULL)) {
    LOG(LS_ERROR) << "Adding stream to PeerConnection failed";
  }
  typedef std::pair<std::string,
                    rtc::scoped_refptr<webrtc::MediaStreamInterface> >
      MediaStreamPair;
  active_streams_.insert(MediaStreamPair(stream->label(), stream));
}

bool Natty::InitializePeerConnection() {

  IceServers servers;
  IceServer server;
  webrtc::FakeConstraints constraints;
  constraints.SetAllowRtpDataChannels();

  ASSERT(peer_connection_factory_.get() == NULL);
  ASSERT(peer_connection_.get() == NULL);

  peer_connection_factory_  = webrtc::CreatePeerConnectionFactory();

  if (!peer_connection_factory_.get()) {
    LOG(INFO) << "Failed to initialize peer connection factory";
    Shutdown();
    return false;
  }
  LOG(INFO) << "Created peer connection factory";

  server.uri = GetPeerConnectionString();
  servers.push_back(server);

  peer_connection_ = peer_connection_factory_->CreatePeerConnection(servers, &constraints, allocator_factory_.get(), NULL, this);

  if (!peer_connection_.get()) {
    LOG(INFO) << "Create peer connection failed";
    Shutdown();
  }
  AddStreams();
  allocator = peer_connection_->GetAllocator();
  ASSERT(allocator != NULL);

  LOG(INFO) << "Created peer connection";

  return peer_connection_.get() != NULL;
}

void Natty::Shutdown() {
  LOG(INFO) << "Deleting peer connection";
  peer_connection_ = NULL;
  peer_connection_factory_ = NULL;
  peer_id_ = -1;
  //active_streams_.clear();
  rtc::CleanupSSL();
  thread_->Stop();
}

//
// PeerConnectionObserver implementation.
//

void Natty::OnError() {
  LOG(INFO) << __FUNCTION__;
}

void Natty::OnAddStream(webrtc::MediaStreamInterface* stream) {
  LOG(INFO) << "Successfully added stream";
}

/* the answerer removes his media stream before disconnecting
 * this is triggered when that happens and means we
 * were able to communicate
 */
void Natty::OnRemoveStream(webrtc::MediaStreamInterface* stream) {
  PickFinalCandidate();
}

void Natty::OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) {
  if (new_state == webrtc::PeerConnectionInterface::SignalingState::kClosed ||
      new_state == webrtc::PeerConnectionInterface::SignalingState::kHaveRemotePrAnswer
      ) {
    PickFinalCandidate();
  }
}

void Natty::OnStateChange(
    webrtc::PeerConnectionObserver::StateType state_changed) {
}

void Natty::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) {
  Json::FastWriter writer;
  Json::Value jmessage;

  jmessage[kCandidateSdpMidName] = candidate->sdp_mid();
  jmessage[kCandidateSdpMlineIndexName] = candidate->sdp_mline_index();
  std::string sdp;
  if (!candidate->ToString(&sdp)) {
    LOG(LS_ERROR) << "Failed to serialize candidate";
    return;
  }

  jmessage[kCandidateSdpName] = sdp;
  outfile << writer.write(jmessage);
  outfile.flush();
}

void Natty::OnRenegotiationNeeded() {
  LOG(INFO) << "Renegotiation needed";
}

void Natty::ReadMessage(const std::string& message) {
  Json::Reader reader;
  Json::Value jmessage;
  std::string type;
  std::string json_object;

  if (!reader.parse(message, jmessage)) {
    LOG(INFO) << "Received an unknown message.";
    return;
  }
  GetStringFromJsonObject(jmessage, kSessionDescriptionTypeName, &type);

  if (!type.empty()) {
    std::string sdp;
    if (!GetStringFromJsonObject(jmessage, kSessionDescriptionSdpName, &sdp)) {
      LOG(INFO) << "Can't parse received session description message.";
      return;
    }
    session_description = webrtc::CreateSessionDescription(type, sdp);
    if (!session_description) {
      LOG(INFO) << "Can't parse SDP message";
      return;
    }
    LOG(INFO) << "Received session description " << message << " sending answer back";

    peer_connection_->SetRemoteDescription(
        NattySessionObserver::Create(), session_description);
    sleep(1);
    if (session_description->type() ==
        webrtc::SessionDescriptionInterface::kOffer) {
      peer_connection_->CreateAnswer(this, NULL);
      sleep(1);
    }
    return;
  }
  else {
    std::string sdp_mid;
    sdp_mlineindex = 0;
    std::string sdp;
    if (!GetStringFromJsonObject(jmessage, kCandidateSdpMidName, &sdp_mid) ||
        !GetIntFromJsonObject(jmessage, kCandidateSdpMlineIndexName,
          &sdp_mlineindex) ||
        !GetStringFromJsonObject(jmessage, kCandidateSdpName, &sdp)) {
      LOG(INFO) << "Can't parse received message";
      return;
    }
    rtc::scoped_ptr<webrtc::IceCandidateInterface> candidate(
        webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, sdp));
    LOG(INFO) << "Remote candidate information";

    if (!candidate.get()) {
      LOG(WARNING) << "Can't parse received candidate message.";
      return;
    }
    if (!peer_connection_->AddIceCandidate(candidate.get())) {
      LOG(WARNING) << "Failed to apply the received candidate";
      return;
    }
    LOG(INFO) << candidate.get()->candidate().ToString();
    LOG(INFO) << " Received candidate :" << message;
    PickFinalCandidate();
    return;
  }
};

void Natty::Output5Tuple(const cricket::Candidate *cand) {
   const char kStreamLabel[] = "stream_label";
   Json::FastWriter writer;
   Json::Value jmessage;
   jmessage["remote"] = cand->address().ToString();
   jmessage["local"] = cand->related_address().ToString();
   jmessage["type"] = cand->protocol();
   outfile << writer.write(jmessage);
   outfile.flush();
   peer_connection_->RemoveStream(stream);
   Shutdown();
}

void Natty::PickFinalCandidate() {
  const webrtc::IceCandidateCollection* candidates =
      session_description->candidates(sdp_mlineindex);

  for (size_t i = 0; i < candidates->count(); ++i) {
    const cricket::Candidate *cand = &candidates->at(i)->candidate();
    if (cand->type() == "stun") {
      Output5Tuple(cand);
      return;
    }
  }
}

void Natty::OnDataChannel(webrtc::DataChannelInterface* data_channel) {
  LOG(INFO) << "New data channel created " << data_channel;
}

void Natty::OnIceComplete() {
  LOG(INFO) << "ICE finished gathering candidates!";
}

// PeerConnectionObserver implementation.
void Natty::OnServerConnectionFailure() {

}

std::string GetPeerName() {
  char computer_name[256];
  if (gethostname(computer_name, ARRAY_SIZE(computer_name)) != 0)
    strcpy(computer_name, "host");
  std::string ret(GetEnvVarOrDefault("USERNAME", "user"));
  ret += '@';
  ret += computer_name;
  return ret;
}

void Natty::setMode(Natty::Mode m) {
  mode = m;
}

void Natty::Init(bool offer) {
  InitializePeerConnection();
  if (offer) {
    Natty::setMode(Natty::OFFER);
    webrtc::FakeConstraints constraints;
    constraints.SetAllowRtpDataChannels();
    peer_connection_->CreateOffer(this, &constraints);
    sleep(1);
  }
}

void Natty::ProcessInput() {
  Natty::InputStream is;
  is.read(this);

}

/* need to update this to accept stdout when the stdout
 * option is blank
 *
 */
void Natty::OpenDumpFile(const std::string& filename) {
  if (!filename.empty()) {
    outfile.open(filename.c_str());
  }
  else {
    outfile.copyfmt(std::cout);
    outfile.clear(std::cout.rdstate());
    outfile.basic_ios<char>::rdbuf(std::cout.rdbuf());
  }
}

void Natty::OnSuccess(webrtc::SessionDescriptionInterface* desc) {
  LOG(INFO) << "Setting local description";
  peer_connection_->SetLocalDescription(
      NattySessionObserver::Create(), desc);
  Json::FastWriter writer;
  Json::Value jmessage;
  jmessage[kSessionDescriptionTypeName] = desc->type();

  std::string sdp;
  desc->ToString(&sdp);
  jmessage[kSessionDescriptionSdpName] = sdp;
  outfile << writer.write(jmessage);
  outfile.flush();
}

void Natty::OnFailure(const std::string& error) {
  LOG(LERROR) << error;
}

void Natty::SendMessage(const std::string& json_object) {

}

<|endoftext|>


<commit_before>#include "av500.hpp"

struct p0wr : Module {
	enum ParamIds {
		SWITCH_PARAM,
		NUM_PARAMS
	};
	enum InputIds {
		NUM_INPUTS
	};
	enum OutputIds {
		NUM_OUTPUTS
	};

	p0wr() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
	}
	void step() override;

	float LEDs[3] = {};

};

void p0wr::step()
{
	switch ((int)roundf(params[SWITCH_PARAM].value)) {
		case 0:
			LEDs[0] = 0;
			LEDs[1] = 0;
			LEDs[2] = 0;
			break;
		case 1:
			LEDs[0] = 1;
			LEDs[1] = 2;
			LEDs[2] = 3;
			break;
	}
}

struct p0wrModeLight : ModeValueLight {
	p0wrModeLight() {
		addColor(COLOR_BLACK_TRANSPARENT);
		addColor(COLOR_RED);
		addColor(COLOR_YELLOW);
		addColor(COLOR_BLUE);
	}
};

p0wrWidget::p0wrWidget()
{
	p0wr *module = new p0wr();
	setModule(module);
	box.size = Vec(15 * 4, 380);

	{
		Panel *panel = new LightPanel();
		panel->backgroundImage = Image::load("plugins/av500/res/p0wr.png");
		panel->box.size = box.size;
		addChild(panel);
	}

	addChild(createScrew<ScrewSilver>(Vec(15,   0)));
	addChild(createScrew<ScrewSilver>(Vec(15, 365)));

	addParam(createParam<NKK>(Vec(14, 129), module, p0wr::SWITCH_PARAM, 0.0, 1.0, 0.0));

	addChild(createValueLight<LargeLight<p0wrModeLight>>(Vec(20,  51), &module->LEDs[0]));
	addChild(createValueLight<LargeLight<p0wrModeLight>>(Vec(20,  75), &module->LEDs[1]));
	addChild(createValueLight<LargeLight<p0wrModeLight>>(Vec(20, 100), &module->LEDs[2]));
}
<commit_msg>adapt to latest API changes for LEDs<commit_after>#include "av500.hpp"

struct p0wr : Module {
	enum ParamIds {
		SWITCH_PARAM,
		NUM_PARAMS
	};
	enum InputIds {
		NUM_INPUTS
	};
	enum OutputIds {
		NUM_OUTPUTS
	};

	enum LightIds {
		RED_LIGHT,
		YEL_LIGHT,
		BLU_LIGHT,
		NUM_LIGHTS
	};

	p0wr() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
	}
	void step() override;
};

void p0wr::step()
{
	switch ((int)roundf(params[SWITCH_PARAM].value)) {
		case 0:
			lights[RED_LIGHT].value = 0;
			lights[YEL_LIGHT].value = 0;
			lights[BLU_LIGHT].value = 0;
			break;
		case 1:
			lights[RED_LIGHT].value = 1;
			lights[YEL_LIGHT].value = 1;
			lights[BLU_LIGHT].value = 1;
			break;
	}
}

p0wrWidget::p0wrWidget()
{
	p0wr *module = new p0wr();
	setModule(module);
	box.size = Vec(15 * 4, 380);

	{
		Panel *panel = new LightPanel();
		panel->backgroundImage = Image::load("plugins/av500/res/p0wr.png");
		panel->box.size = box.size;
		addChild(panel);
	}

	addChild(createScrew<ScrewSilver>(Vec(15,   0)));
	addChild(createScrew<ScrewSilver>(Vec(15, 365)));

	addParam(createParam<NKK>(Vec(14, 129), module, p0wr::SWITCH_PARAM, 0.0, 1.0, 0.0));

	addChild(createLight<LargeLight<RedLight>>   (Vec(20,  51), module, p0wr::RED_LIGHT));
	addChild(createLight<LargeLight<YellowLight>>(Vec(20,  75), module, p0wr::YEL_LIGHT));
	addChild(createLight<LargeLight<BlueLight>>  (Vec(20, 100), module, p0wr::BLU_LIGHT));
}
<|endoftext|>