<commit_before>/*!
 * \file main.cc
 *
 * \brief The control component for BPEL2oWFN
 *
 * This file controls the behaviour of BPEL2oWFN and is the interface to the
 * environment.
 *
 * \author
 *          - responsible: Christian Gierds <gierds@informatik.hu-berlin.de>
 *          - last changes of: \$Author: nlohmann $
 *
 * \date
 *          - created: 2005/10/18
 *          - last changed: \$Date: 2005/11/29 10:49:40 $
 *
 * \note    This file is part of the tool BPEL2oWFN and was created during the
 *          project "Tools4BPEL" at the Humboldt-Universitt zu Berlin. See
 *          http://www.informatik.hu-berlin.de/top/forschung/projekte/tools4bpel
 *          for details.
 *
 * \version \$Revision: 1.34 $
 *          - 2005-11-15 (gierds) Moved command line evaluation to helpers.cc.
 *            Added option to created (abstracted) low level nets.
 *            Added option for LoLA output.
 *          - 2005-11-15 (nlohmann) Call Exception::info() to signal error.
 *          - 2005-11-16 (gierds) Use of error() and cleanup() as defined in
 *            helpers.cc
 *
 */

#include "main.h"

/// The Petri Net
PetriNet *TheNet = new PetriNet();

// some file names and pointers

/// Filename of input file.
std::string filename = "<STDIN>";

/// Filename of dot output file
std::string dot_filename = "";
/// Pointer to dot output file
std::ostream * dot_output = &std::cout;

/// Filename of lola output file
std::string lola_filename = "";
/// Pointer to lola output file
std::ostream * lola_output = &std::cout;

/// Filename of PN info file
std::string info_filename = "";
/// Pointer to PN info file
std::ostream * info_output = &std::cout;

// different modes controlled by command line

/// read from file
bool mode_file = false;
/// print the Petri Net
bool mode_petri_net = false;
/// simplify Petri Net
bool mode_simplify_petri_net = false;
/// paint Petri Net with lola
bool mode_lola_petri_net = false;
/// paint Petri Net with lola and output to file
bool mode_lola_2_file = false;
/// paint Petri Net with dot
bool mode_dot_petri_net = false;
/// paint Petri Net with dot and output to file
bool mode_dot_2_file = false;
/// pretty printer
bool mode_pretty_printer = false;
/// print AST
bool mode_ast = false;
/// print the "low level" Petri Net
bool mode_low_level_petri_net = false;

/**
 * Entry point for BPEL2oWFN.
 * Controls the behaviour of input and output.
 *
 * \param argc	number of command line arguments
 * \param argv	array with command line arguments
 *
 * \returns Error code (0 if everything went well)
 */
int main( int argc, char *argv[])
{

  try
  {
    /***
     * Reading command line arguments and triggering appropriate behaviour.
     * In case of false parameters call command line help function and exit.
     */
    parse_command_line(argc, argv);

    trace(TRACE_INFORMATION, "Parsing ...\n");

    // invoke Bison parser
    int error = yyparse();

    if (!error)
    {
      trace(TRACE_INFORMATION, "Parsing complete.\n");

      symMan.printScope();

      if (mode_ast)
      {
        TheProcess->print();
      }

      trace(TRACE_INFORMATION, "Rewriting BPEL...\n");
      TheProcess = TheProcess->rewrite(kc::implicit);
      trace(TRACE_INFORMATION, "Rewriting complete.\n");

      if (mode_pretty_printer)
      {
        TheProcess->unparse(kc::printer, kc::xml);
      }
      if (mode_petri_net)
      {
        TheProcess->unparse(kc::pseudoPrinter, kc::petrinet);
      }
    }

    // create dot output ?
    if (mode_lola_petri_net)
    {
      TheNet->lolaOut();
    }
    // create dot output ?
    if (mode_dot_petri_net)
    {
      TheNet->drawDot();
    }

    // make low level ?
    if (mode_low_level_petri_net)
    {
      TheNet->makeLowLevel();
    }
    // simplify net ?
    if (mode_simplify_petri_net)
    {
      TheNet->simplify();
    }
    // output info file ?
    if (mode_petri_net && mode_file )
    {
      TheNet->printInformation();
    }

    cleanup();

    return error;
  }
  catch (Exception& e)
  {
    error(e);
  }

  return 0;
}
<commit_msg>+ another reordering of the output generators<commit_after>/*!
 * \file main.cc
 *
 * \brief The control component for BPEL2oWFN
 *
 * This file controls the behaviour of BPEL2oWFN and is the interface to the
 * environment.
 *
 * \author
 *          - responsible: Christian Gierds <gierds@informatik.hu-berlin.de>
 *          - last changes of: \$Author: nlohmann $
 *
 * \date
 *          - created: 2005/10/18
 *          - last changed: \$Date: 2005/11/29 11:22:08 $
 *
 * \note    This file is part of the tool BPEL2oWFN and was created during the
 *          project "Tools4BPEL" at the Humboldt-Universitt zu Berlin. See
 *          http://www.informatik.hu-berlin.de/top/forschung/projekte/tools4bpel
 *          for details.
 *
 * \version \$Revision: 1.35 $
 *          - 2005-11-15 (gierds) Moved command line evaluation to helpers.cc.
 *            Added option to created (abstracted) low level nets.
 *            Added option for LoLA output.
 *          - 2005-11-15 (nlohmann) Call Exception::info() to signal error.
 *          - 2005-11-16 (gierds) Use of error() and cleanup() as defined in
 *            helpers.cc
 *
 */

#include "main.h"

/// The Petri Net
PetriNet *TheNet = new PetriNet();

// some file names and pointers

/// Filename of input file.
std::string filename = "<STDIN>";

/// Filename of dot output file
std::string dot_filename = "";
/// Pointer to dot output file
std::ostream * dot_output = &std::cout;

/// Filename of lola output file
std::string lola_filename = "";
/// Pointer to lola output file
std::ostream * lola_output = &std::cout;

/// Filename of PN info file
std::string info_filename = "";
/// Pointer to PN info file
std::ostream * info_output = &std::cout;

// different modes controlled by command line

/// read from file
bool mode_file = false;
/// print the Petri Net
bool mode_petri_net = false;
/// simplify Petri Net
bool mode_simplify_petri_net = false;
/// paint Petri Net with lola
bool mode_lola_petri_net = false;
/// paint Petri Net with lola and output to file
bool mode_lola_2_file = false;
/// paint Petri Net with dot
bool mode_dot_petri_net = false;
/// paint Petri Net with dot and output to file
bool mode_dot_2_file = false;
/// pretty printer
bool mode_pretty_printer = false;
/// print AST
bool mode_ast = false;
/// print the "low level" Petri Net
bool mode_low_level_petri_net = false;

/**
 * Entry point for BPEL2oWFN.
 * Controls the behaviour of input and output.
 *
 * \param argc	number of command line arguments
 * \param argv	array with command line arguments
 *
 * \returns Error code (0 if everything went well)
 */
int main( int argc, char *argv[])
{

  try
  {
    /***
     * Reading command line arguments and triggering appropriate behaviour.
     * In case of false parameters call command line help function and exit.
     */
    parse_command_line(argc, argv);

    trace(TRACE_INFORMATION, "Parsing ...\n");

    // invoke Bison parser
    int error = yyparse();

    if (!error)
    {
      trace(TRACE_INFORMATION, "Parsing complete.\n");

      symMan.printScope();

      if (mode_ast)
      {
        TheProcess->print();
      }

      trace(TRACE_INFORMATION, "Rewriting BPEL...\n");
      TheProcess = TheProcess->rewrite(kc::implicit);
      trace(TRACE_INFORMATION, "Rewriting complete.\n");

      if (mode_pretty_printer)
      {
        TheProcess->unparse(kc::printer, kc::xml);
      }
      if (mode_petri_net)
      {
        TheProcess->unparse(kc::pseudoPrinter, kc::petrinet);
      }
    }

    // make low level ?
    if (mode_low_level_petri_net)
    {
      TheNet->makeLowLevel();
    }
    // create dot output ?
    if (mode_lola_petri_net)
    {
      TheNet->lolaOut();
    }
    // create dot output ?
    if (mode_dot_petri_net)
    {
      TheNet->drawDot();
    }
    // simplify net ?
    if (mode_simplify_petri_net)
    {
      TheNet->simplify();
    }
    // output info file ?
    if (mode_petri_net && mode_file )
    {
      TheNet->printInformation();
    }

    cleanup();

    return error;
  }
  catch (Exception& e)
  {
    error(e);
  }

  return 0;
}
<|endoftext|>


<commit_before>//// Fancy ncurses terminal for calculators
//// Author: David P. Sicilia, June 2016

#include <ncurses.h>
#include <string>
#include <vector>
#include <algorithm>
#include <stdexcept>
#include <iostream>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>

#include "assert.hpp"
#include "scope_exit.hpp"
#include "line_editor.hpp"
#include "input_view.hpp"

#include "icalcterm/icalcterm.h"

using namespace std;

struct Entry
{
    std::string one_line;
    std::vector<std::string> grid;
};

struct Stripe
{
    Entry e;
    bool is_left;
    int y;
    int x;
};

auto render_stripe( int width, Stripe const& s ) -> std::vector<std::string>
{
    ASSERT( s.x >= 0 )
    ASSERT( s.y >= 0 )
    auto out = std::vector<std::string>( s.y );
    //auto out = std::vector<std::string>( s.y+2 );
    int start_x = s.is_left ? 1 : width-1-s.x;
    ASSERT( start_x >= 0 )
    auto pad = std::string( " " ); //start_x, ' ' );
    ASSERT( width-start_x-s.x >= 0 )
    auto end_pad = std::string( " " ); //width-start_x-s.x, ' ' );
    for( int i = 0; i < s.y; ++i )
        out[i] = pad + s.e.grid[i] + end_pad;
        //out[i+1] = pad + s.e.grid[i] + end_pad;
    //out[0]     = pad + std::string( s.x, ' ' ) + end_pad;
    //out[s.y+1] = pad + std::string( s.x, ' ' ) + end_pad;
    return out;
}

auto draw_stripe( int width, int start_y, bool highlight, Stripe const& s ) -> void
{
    auto text = render_stripe( width, s );
    if( highlight ) attroff( A_REVERSE );
    for( int i = text.size(); i > 0; --i ) {
        if( start_y-i < 0 )
            break;
        ASSERT( start_y-int(text.size())+i >= 0 )
        if( s.is_left )
            mvprintw( start_y-text.size()+i, 1, text[i-1].c_str() );
        else
            mvprintw( start_y-text.size()+i, width-s.x-1, text[i-1].c_str() );
    }
    if( highlight ) attron( A_REVERSE );
}

auto draw_stripes( int highlight, std::vector<Stripe> const& v ) -> void
{
    int height = 0, width = 0;
    getmaxyx( stdscr, height, width );
    width -= 2;
    (void)height;
    int start_y = height-5;
    ASSERT( start_y >= 0 )
    int highlight_j = v.size() - highlight;
    ASSERT( highlight_j >= 0 )
    //for( int j = start_y; j >= 0; --j )
    for( int j = start_y; j >= 1; --j )
        mvhline( j, 1, ' ', width );
    for( int j = v.size(); j > 0; --j ) {
        if( start_y < 0 )
            break;
        Stripe const& s = v[j-1];
        bool highlight = (j == highlight_j);
        draw_stripe( width, start_y, highlight, s );
        //start_y -= (s.y+2);
        start_y -= (s.y);
    }
}

std::vector<Stripe> vs = { };

#define GET_FUNC_SYM( sym )                                         \
    sym ## _t sym;                                                  \
    *(void**)(&sym) = dlsym( calc, #sym );                          \
    char const* STRING_JOIN( error__, __LINE__ ) = dlerror();       \
    if( STRING_JOIN( error__, __LINE__) ) {                         \
        fprintf( stderr, "%s\n", STRING_JOIN( error__, __LINE__) ); \
        return 1;                                                   \
    }

#ifdef OS_LINUX
char const* deflibname = "libdefcalc.so";
#else
#ifdef OS_OSX
char const* deflibname = "libdefcalc.dylib";
#endif
#endif

int _main(int argc, char* argv[])
{
    char const* libname;
    if( argc == 1 )
        libname = deflibname;
    else
        libname = argv[1];
    //printf("Loading library: %s\n", libname);
    void* calc = dlopen( libname, RTLD_NOW );
    if( !calc ) {
        fprintf( stderr, "Unable to load library %s:\n%s\n", libname, dlerror() );
        return 1;
    }
    SCOPE_EXIT( dlclose( calc ) )

    GET_FUNC_SYM( CI_init )
    GET_FUNC_SYM( CI_config )
    GET_FUNC_SYM( CI_submit )
    GET_FUNC_SYM( CI_result_free )

    CI_init( NULL );

    initscr();
    SCOPE_EXIT( endwin() )
    raw();
    nonl();
    noecho();
    keypad(stdscr, TRUE);
    int ch;
    int highlight = -1;
    int height = 0, width = 0;
    getmaxyx( stdscr, height, width );
    attron( A_REVERSE );
    for( int i = 0; i < height; ++i )
        mvhline( i, 0, ' ', width );
    draw_stripes( -1, vs );
    LineEditor le;
    InputView in( width-4 );
    bool editing = true, update_stripes = true;
    mvhline( height-4, 1, ACS_HLINE, width-1 );
    //mvprintw(height-4, width/2-9, "~<{ ||||||||| }>~" );
    //mvhline( height-3, 1, ACS_HLINE, width-1 );
    mvhline( height-3, 1, ' ', width-1 );
    mvaddch( height-3, 0, ACS_ULCORNER );
    mvaddch( height-3, width-1, ACS_URCORNER );
    mvaddch( height-2, 0, ACS_VLINE );
    mvaddch( height-2, width-1, ACS_VLINE );
    //mvhline( height-1, 1, ACS_HLINE, width-1 );
    mvaddch( height-1, 0, ACS_LLCORNER );
    mvaddch( height-1, width-1, ACS_LRCORNER );
    //attroff( A_REVERSE );
    //mvhline(        0, 1, ACS_HLINE, width-1 );
    //mvprintw(       0, width/2-9, "~<{ calc-term }>~" );
    //mvaddch(        0, 0, ACS_ULCORNER );
    //mvaddch(        0, width-1, ACS_URCORNER );
    //for( int i = 1; i < (height-4); ++i ) {
    //    mvaddch( i, 0, ACS_VLINE );
    //    mvaddch( i, width-1, ACS_VLINE );
    //}
    mvhline( height-4, 0, ACS_HLINE, width-1 );
    mvprintw( height-4, width/2-8, " [ term calc ] " );
    //mvaddch( height-4, 0, ACS_LLCORNER );
    //mvaddch( height-4, width-1, ACS_LRCORNER );
    move( height-2, 2 );
    while( (ch = getch()) != (int)'q' )
    {
        char const* name = keyname( ch );
        ASSERT( strlen( name ) > 0 )
        bool ctrl = (name[0] == '^' && strlen( name ) > 1);
        //mvprintw( 0, 0, "%x         ", ch );
        //mvprintw( 1, 0, "%s         ", name );
        //mvprintw( 2, 0, "%d         ", width );
        //ASSERT( (char)(ch & 0xff) != 'K' )
        //if( ch == KEY_UP || (ctrl && name[1] == 'K') ) {
        if( ch == KEY_UP || (!ctrl && ch == 'k') ) {
            highlight += 1;
            editing = false;
            update_stripes = true;
        }
        //else if ( ch == KEY_DOWN || (ctrl && name[1] == 'J') ) {
        else if ( ch == KEY_DOWN || (!ctrl && ch == 'j') ) {
            highlight -= 1;
            if( highlight < -1 )
                highlight = -1;
            if( highlight == -1 )
                editing = true;
            update_stripes = true;
        }
        else if( ctrl && name[1] == 'L' ) {
            if( editing ) {
                vs.clear();
                update_stripes = true;
            }
        }
        else if( ch == '\n' || ch == '\r' || ch == '\\' ) {
            if( editing ) {
                bool approx = (ch == '\\');
                std::string const& str = le.get_buffer();
                if( !str.empty() ) {
                    CI_Result* res = CI_submit( str.c_str() );
                    if( res ) {
                        SCOPE_EXIT( CI_result_free( res ) )

                        auto output_grid = []( char* _one_line, char** _grid, int rows, bool left_right ) {
                            ASSERT( rows > 0 )
                            auto one_line = std::string( _one_line );
                            auto grid     = std::vector<std::string>();
                            auto length_0 = strlen( _grid[0] );
                            for( int j = 0; j < rows; j++ ) {
                                ASSERT( strlen( _grid[j] ) == length_0 )
                                grid.push_back( std::string( _grid[j] ) );
                            }
                            Stripe s({ {one_line, grid}, left_right, rows, (int)length_0 });
                            vs.push_back( s );
                        };

                        output_grid( res->input.one_line, res->input.grid, res->input.grid_rows, true );
                        if( approx && res->num_outputs > 1 ) {
                            output_grid( res->outputs[1].one_line, res->outputs[1].grid, res->outputs[1].grid_rows, false );
                        } else if( res->num_outputs > 0 ) {
                            output_grid( res->outputs[0].one_line, res->outputs[0].grid, res->outputs[0].grid_rows, false );
                        }

                        le = LineEditor();
                        update_stripes = true;
                    }
                }
            }
            else {
                std::string to_insert = vs[vs.size() - highlight - 1].e.one_line;

                for( auto c : to_insert )
                    le.input( false, false, int( c ), NULL );

                highlight = -1;
                editing = true;
                update_stripes = true;
            }
        }
        else {
            if( editing ) {
                auto ki_old( le );
                le.input( ctrl, false, ch, name );
                if( le.get_buffer() == ki_old.get_buffer() &&
                    le.get_pos()    == ki_old.get_pos() )
                    beep(); // can also use flash()
            }
        }

        if( update_stripes ) {
            draw_stripes( highlight, vs );
            update_stripes = false;
        }
        mvaddnstr( height-2, 2, in.render( le.get_pos(), le.get_buffer() ).c_str(), in.width );
        if( editing ) {
            curs_set(1);
            move( height-2, 2+in.rel_pos( le.get_pos() ) );
        }
        else {
            curs_set(0);
        }
        refresh();
    }
    return 0;
}

int main( int argc, char* argv[] )
{
    try {
        return _main(argc, argv);
    } catch( std::exception const& e ) {
        std::cerr << "exception:" << e.what() << std::endl;
        return 1;
    }
}
<commit_msg>Changing box character layout<commit_after>//// Fancy ncurses terminal for calculators
//// Author: David P. Sicilia, June 2016

#include <ncurses.h>
#include <string>
#include <vector>
#include <algorithm>
#include <stdexcept>
#include <iostream>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>

#include "assert.hpp"
#include "scope_exit.hpp"
#include "line_editor.hpp"
#include "input_view.hpp"

#include "icalcterm/icalcterm.h"

using namespace std;

struct Entry
{
    std::string one_line;
    std::vector<std::string> grid;
};

struct Stripe
{
    Entry e;
    bool is_left;
    int y;
    int x;
};

auto render_stripe( int width, Stripe const& s ) -> std::vector<std::string>
{
    ASSERT( s.x >= 0 )
    ASSERT( s.y >= 0 )
    auto out = std::vector<std::string>( s.y );
    //auto out = std::vector<std::string>( s.y+2 );
    int start_x = s.is_left ? 1 : width-1-s.x;
    ASSERT( start_x >= 0 )
    auto pad = std::string( " " ); //start_x, ' ' );
    ASSERT( width-start_x-s.x >= 0 )
    auto end_pad = std::string( " " ); //width-start_x-s.x, ' ' );
    for( int i = 0; i < s.y; ++i )
        out[i] = pad + s.e.grid[i] + end_pad;
        //out[i+1] = pad + s.e.grid[i] + end_pad;
    //out[0]     = pad + std::string( s.x, ' ' ) + end_pad;
    //out[s.y+1] = pad + std::string( s.x, ' ' ) + end_pad;
    return out;
}

auto draw_stripe( int width, int start_y, bool highlight, Stripe const& s ) -> void
{
    auto text = render_stripe( width, s );
    if( highlight ) attroff( A_REVERSE );
    for( int i = text.size(); i > 0; --i ) {
        if( start_y-i < 0 )
            break;
        ASSERT( start_y-int(text.size())+i >= 0 )
        if( s.is_left )
            mvprintw( start_y-text.size()+i, 1, text[i-1].c_str() );
        else
            mvprintw( start_y-text.size()+i, width-s.x-1, text[i-1].c_str() );
    }
    if( highlight ) attron( A_REVERSE );
}

auto draw_stripes( int highlight, std::vector<Stripe> const& v ) -> void
{
    int height = 0, width = 0;
    getmaxyx( stdscr, height, width );
    width -= 2;
    (void)height;
    int start_y = height-5;
    ASSERT( start_y >= 0 )
    int highlight_j = v.size() - highlight;
    ASSERT( highlight_j >= 0 )
    //for( int j = start_y; j >= 0; --j )
    for( int j = start_y; j >= 1; --j )
        mvhline( j, 1, ' ', width );
    for( int j = v.size(); j > 0; --j ) {
        if( start_y < 0 )
            break;
        Stripe const& s = v[j-1];
        bool highlight = (j == highlight_j);
        draw_stripe( width, start_y, highlight, s );
        //start_y -= (s.y+2);
        start_y -= (s.y);
    }
}

std::vector<Stripe> vs = { };

#define GET_FUNC_SYM( sym )                                         \
    sym ## _t sym;                                                  \
    *(void**)(&sym) = dlsym( calc, #sym );                          \
    char const* STRING_JOIN( error__, __LINE__ ) = dlerror();       \
    if( STRING_JOIN( error__, __LINE__) ) {                         \
        fprintf( stderr, "%s\n", STRING_JOIN( error__, __LINE__) ); \
        return 1;                                                   \
    }

#ifdef OS_LINUX
char const* deflibname = "libdefcalc.so";
#else
#ifdef OS_OSX
char const* deflibname = "libdefcalc.dylib";
#endif
#endif

int _main(int argc, char* argv[])
{
    char const* libname;
    if( argc == 1 )
        libname = deflibname;
    else
        libname = argv[1];
    //printf("Loading library: %s\n", libname);
    void* calc = dlopen( libname, RTLD_NOW );
    if( !calc ) {
        fprintf( stderr, "Unable to load library %s:\n%s\n", libname, dlerror() );
        return 1;
    }
    SCOPE_EXIT( dlclose( calc ) )

    GET_FUNC_SYM( CI_init )
    GET_FUNC_SYM( CI_config )
    GET_FUNC_SYM( CI_submit )
    GET_FUNC_SYM( CI_result_free )

    CI_init( NULL );

    initscr();
    SCOPE_EXIT( endwin() )
    raw();
    nonl();
    noecho();
    keypad(stdscr, TRUE);
    int ch;
    int highlight = -1;
    int height = 0, width = 0;
    getmaxyx( stdscr, height, width );
    attron( A_REVERSE );
    for( int i = 0; i < height; ++i )
        mvhline( i, 0, ' ', width );
    draw_stripes( -1, vs );
    LineEditor le;
    InputView in( width-4 );
    bool editing = true, update_stripes = true;
    mvhline( height-4, 1, ACS_HLINE, width-1 );
    //mvprintw(height-4, width/2-9, "~<{ ||||||||| }>~" );
    //mvhline( height-3, 1, ACS_HLINE, width-1 );
    mvhline( height-3, 1, ' ', width-1 );
    //mvaddch( height-3, 0, ACS_ULCORNER );
    mvaddch( height-3, 0, ACS_VLINE );
    //mvaddch( height-3, width-1, ACS_URCORNER );
    mvaddch( height-3, width-1, ACS_VLINE );
    mvaddch( height-2, 0, ACS_VLINE );
    mvaddch( height-2, width-1, ACS_VLINE );
    //mvhline( height-1, 1, ACS_HLINE, width-1 );
    mvaddch( height-1, 0, ACS_LLCORNER );
    mvaddch( height-1, width-1, ACS_LRCORNER );
    //attroff( A_REVERSE );
    //mvhline(        0, 1, ACS_HLINE, width-1 );
    //mvprintw(       0, width/2-9, "~<{ calc-term }>~" );
    //mvaddch(        0, 0, ACS_ULCORNER );
    //mvaddch(        0, width-1, ACS_URCORNER );
    //for( int i = 1; i < (height-4); ++i ) {
    //    mvaddch( i, 0, ACS_VLINE );
    //    mvaddch( i, width-1, ACS_VLINE );
    //}
    mvhline( height-4, 1, ACS_HLINE, width-2 );
    mvaddch( height-4, 0, ACS_TTEE );
    mvaddch( height-4, width-1, ACS_TTEE );
    mvprintw( height-4, width/2-8, " [ term calc ] " );
    //mvaddch( height-4, 0, ACS_LLCORNER );
    //mvaddch( height-4, width-1, ACS_LRCORNER );
    move( height-2, 2 );
    while( (ch = getch()) != (int)'q' )
    {
        char const* name = keyname( ch );
        ASSERT( strlen( name ) > 0 )
        bool ctrl = (name[0] == '^' && strlen( name ) > 1);
        //mvprintw( 0, 0, "%x         ", ch );
        //mvprintw( 1, 0, "%s         ", name );
        //mvprintw( 2, 0, "%d         ", width );
        //ASSERT( (char)(ch & 0xff) != 'K' )
        //if( ch == KEY_UP || (ctrl && name[1] == 'K') ) {
        if( ch == KEY_UP || (!ctrl && ch == 'k') ) {
            highlight += 1;
            editing = false;
            update_stripes = true;
        }
        //else if ( ch == KEY_DOWN || (ctrl && name[1] == 'J') ) {
        else if ( ch == KEY_DOWN || (!ctrl && ch == 'j') ) {
            highlight -= 1;
            if( highlight < -1 )
                highlight = -1;
            if( highlight == -1 )
                editing = true;
            update_stripes = true;
        }
        else if( ctrl && name[1] == 'L' ) {
            if( editing ) {
                vs.clear();
                update_stripes = true;
            }
        }
        else if( ch == '\n' || ch == '\r' || ch == '\\' ) {
            if( editing ) {
                bool approx = (ch == '\\');
                std::string const& str = le.get_buffer();
                if( !str.empty() ) {
                    CI_Result* res = CI_submit( str.c_str() );
                    if( res ) {
                        SCOPE_EXIT( CI_result_free( res ) )

                        auto output_grid = []( char* _one_line, char** _grid, int rows, bool left_right ) {
                            ASSERT( rows > 0 )
                            auto one_line = std::string( _one_line );
                            auto grid     = std::vector<std::string>();
                            auto length_0 = strlen( _grid[0] );
                            for( int j = 0; j < rows; j++ ) {
                                ASSERT( strlen( _grid[j] ) == length_0 )
                                grid.push_back( std::string( _grid[j] ) );
                            }
                            Stripe s({ {one_line, grid}, left_right, rows, (int)length_0 });
                            vs.push_back( s );
                        };

                        output_grid( res->input.one_line, res->input.grid, res->input.grid_rows, true );
                        if( approx && res->num_outputs > 1 ) {
                            output_grid( res->outputs[1].one_line, res->outputs[1].grid, res->outputs[1].grid_rows, false );
                        } else if( res->num_outputs > 0 ) {
                            output_grid( res->outputs[0].one_line, res->outputs[0].grid, res->outputs[0].grid_rows, false );
                        }

                        le = LineEditor();
                        update_stripes = true;
                    }
                }
            }
            else {
                std::string to_insert = vs[vs.size() - highlight - 1].e.one_line;

                for( auto c : to_insert )
                    le.input( false, false, int( c ), NULL );

                highlight = -1;
                editing = true;
                update_stripes = true;
            }
        }
        else {
            if( editing ) {
                auto ki_old( le );
                le.input( ctrl, false, ch, name );
                if( le.get_buffer() == ki_old.get_buffer() &&
                    le.get_pos()    == ki_old.get_pos() )
                    beep(); // can also use flash()
            }
        }

        if( update_stripes ) {
            draw_stripes( highlight, vs );
            update_stripes = false;
        }
        mvaddnstr( height-2, 2, in.render( le.get_pos(), le.get_buffer() ).c_str(), in.width );
        if( editing ) {
            curs_set(1);
            move( height-2, 2+in.rel_pos( le.get_pos() ) );
        }
        else {
            curs_set(0);
        }
        refresh();
    }
    return 0;
}

int main( int argc, char* argv[] )
{
    try {
        return _main(argc, argv);
    } catch( std::exception const& e ) {
        std::cerr << "exception:" << e.what() << std::endl;
        return 1;
    }
}
<|endoftext|>


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

/**
 * @file
 *   This file includes definitions for MLE functionality required by the Thread Child, Router, and Leader roles.
 */

#ifndef MLE_CONSTANTS_HPP_
#define MLE_CONSTANTS_HPP_

namespace Thread {
namespace Mle {

/**
 * @addtogroup core-mle-core
 *
 */

enum
{
    kMaxChildren                = OPENTHREAD_CONFIG_MAX_CHILDREN,
};

/**
 * MLE Protocol Constants
 *
 */
enum
{
    kVersion                    = 1,     ///< MLE Version
    kUdpPort                    = 19788, ///< MLE UDP Port
    kParentRequestRouterTimeout = 1000,  ///< Router Request timeout
    kParentRequestChildTimeout  = 2000,  ///< End Device Request timeout
};

enum
{
    kMinChildId                 = 1,     ///< Minimum Child ID
    kMaxChildId                 = 511,   ///< Maximum Child ID
    kRouterIdOffset             = 10,    ///< Bit offset of Router ID in RLOC16
    kRlocPrefixLength           = 14,    ///< Prefix length of RLOC in bytes
};

/**
 * Routing Protocol Constants
 *
 */
enum
{
    kAdvertiseIntervalMin       = 1,    ///< ADVERTISEMENT_I_MIN (seconds)
    kAdvertiseIntervalMax       = 32,   ///< ADVERTISEMENT_I_MAX (seconds)
    kRouterIdReuseDelay         = 100,  ///< ID_REUSE_DELAY (seconds)
    kRouterIdSequencePeriod     = 10,   ///< ID_SEQUENCE_PERIOD (seconds)
    kMaxNeighborAge             = 100,  ///< MAX_NEIGHBOR_AGE (seconds)
    kMaxRouteCost               = 16,   ///< MAX_ROUTE_COST
    kMaxRouterId                = 62,   ///< MAX_ROUTER_ID
    kMaxRouters                 = 32,   ///< MAX_ROUTERS
    kMinDowngradeNeighbors      = 7,    ///< MIN_DOWNGRADE_NEIGHBORS
    kNetworkIdTimeout           = 120,  ///< NETWORK_ID_TIMEOUT (seconds)
    kParentRouteToLeaderTimeout = 20,   ///< PARENT_ROUTE_TO_LEADER_TIMEOUT (seconds)
    kRouterSelectionJitter      = 120,  ///< ROUTER_SELECTION_JITTER (seconds)
    kRouterDowngradeThreshold   = 23,   ///< ROUTER_DOWNGRADE_THRESHOLD (routers)
    kRouterUpgradeThreshold     = 16,   ///< ROUTER_UPGRADE_THRESHOLD (routers)
    kMaxLeaderToRouterTimeout   = 90,   ///< INFINITE_COST_TIMEOUT (seconds)
    kReedAdvertiseInterval      = 570,  ///< REED_ADVERTISEMENT_INTERVAL (seconds)
    kReedAdvertiseJitter        = 60,   ///< REED_ADVERTISEMENT_JITTER (seconds)
};

enum
{
    kLqi3LinkCost               = 1,    ///< Link Cost for LQI 3
    kLqi2LinkCost               = 2,    ///< Link Cost for LQI 2
    kLqi1LinkCost               = 4,    ///< Link Cost for LQI 1
    kLqi0LinkCost               = 16,   ///< Link Cost for LQI 0
};

}  // namespace Mle

/**
 * @}
 *
 */

}  // namespace Thread

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

/**
 * @file
 *   This file includes definitions for MLE functionality required by the Thread Child, Router, and Leader roles.
 */

#ifndef MLE_CONSTANTS_HPP_
#define MLE_CONSTANTS_HPP_

namespace Thread {
namespace Mle {

/**
 * @addtogroup core-mle-core
 *
 */

enum
{
    kMaxChildren                = OPENTHREAD_CONFIG_MAX_CHILDREN,
};

/**
 * MLE Protocol Constants
 *
 */
enum
{
    kVersion                    = 2,     ///< MLE Version
    kUdpPort                    = 19788, ///< MLE UDP Port
    kParentRequestRouterTimeout = 1000,  ///< Router Request timeout
    kParentRequestChildTimeout  = 2000,  ///< End Device Request timeout
};

enum
{
    kMinChildId                 = 1,     ///< Minimum Child ID
    kMaxChildId                 = 511,   ///< Maximum Child ID
    kRouterIdOffset             = 10,    ///< Bit offset of Router ID in RLOC16
    kRlocPrefixLength           = 14,    ///< Prefix length of RLOC in bytes
};

/**
 * Routing Protocol Constants
 *
 */
enum
{
    kAdvertiseIntervalMin       = 1,    ///< ADVERTISEMENT_I_MIN (seconds)
    kAdvertiseIntervalMax       = 32,   ///< ADVERTISEMENT_I_MAX (seconds)
    kRouterIdReuseDelay         = 100,  ///< ID_REUSE_DELAY (seconds)
    kRouterIdSequencePeriod     = 10,   ///< ID_SEQUENCE_PERIOD (seconds)
    kMaxNeighborAge             = 100,  ///< MAX_NEIGHBOR_AGE (seconds)
    kMaxRouteCost               = 16,   ///< MAX_ROUTE_COST
    kMaxRouterId                = 62,   ///< MAX_ROUTER_ID
    kMaxRouters                 = 32,   ///< MAX_ROUTERS
    kMinDowngradeNeighbors      = 7,    ///< MIN_DOWNGRADE_NEIGHBORS
    kNetworkIdTimeout           = 120,  ///< NETWORK_ID_TIMEOUT (seconds)
    kParentRouteToLeaderTimeout = 20,   ///< PARENT_ROUTE_TO_LEADER_TIMEOUT (seconds)
    kRouterSelectionJitter      = 120,  ///< ROUTER_SELECTION_JITTER (seconds)
    kRouterDowngradeThreshold   = 23,   ///< ROUTER_DOWNGRADE_THRESHOLD (routers)
    kRouterUpgradeThreshold     = 16,   ///< ROUTER_UPGRADE_THRESHOLD (routers)
    kMaxLeaderToRouterTimeout   = 90,   ///< INFINITE_COST_TIMEOUT (seconds)
    kReedAdvertiseInterval      = 570,  ///< REED_ADVERTISEMENT_INTERVAL (seconds)
    kReedAdvertiseJitter        = 60,   ///< REED_ADVERTISEMENT_JITTER (seconds)
};

enum
{
    kLqi3LinkCost               = 1,    ///< Link Cost for LQI 3
    kLqi2LinkCost               = 2,    ///< Link Cost for LQI 2
    kLqi1LinkCost               = 4,    ///< Link Cost for LQI 1
    kLqi0LinkCost               = 16,   ///< Link Cost for LQI 0
};

}  // namespace Mle

/**
 * @}
 *
 */

}  // namespace Thread

#endif  // MLE_CONSTANTS_HPP_
<|endoftext|>


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

#include "arch/registers.hh"
#include "config/the_isa.hh"
#include "cpu/o3/thread_context.hh"
#include "cpu/quiesce_event.hh"

#if FULL_SYSTEM
template <class Impl>
VirtualPort *
O3ThreadContext<Impl>::getVirtPort()
{
    return thread->getVirtPort();
}

template <class Impl>
void
O3ThreadContext<Impl>::dumpFuncProfile()
{
    thread->dumpFuncProfile();
}
#endif

template <class Impl>
void
O3ThreadContext<Impl>::takeOverFrom(ThreadContext *old_context)
{
    // some things should already be set up
    assert(getSystemPtr() == old_context->getSystemPtr());
#if !FULL_SYSTEM
    assert(getProcessPtr() == old_context->getProcessPtr());
#endif

    // copy over functional state
    setStatus(old_context->status());
    copyArchRegs(old_context);
    setContextId(old_context->contextId());
    setThreadId(old_context->threadId());

#if !FULL_SYSTEM
    thread->funcExeInst = old_context->readFuncExeInst();
#else
    EndQuiesceEvent *other_quiesce = old_context->getQuiesceEvent();
    if (other_quiesce) {
        // Point the quiesce event's TC at this TC so that it wakes up
        // the proper CPU.
        other_quiesce->tc = this;
    }
    if (thread->quiesceEvent) {
        thread->quiesceEvent->tc = this;
    }

    // Transfer kernel stats from one CPU to the other.
    thread->kernelStats = old_context->getKernelStats();
//    storeCondFailures = 0;
    cpu->lockFlag = false;
#endif

    old_context->setStatus(ThreadContext::Halted);

    thread->inSyscall = false;
    thread->trapPending = false;
}

template <class Impl>
void
O3ThreadContext<Impl>::activate(int delay)
{
    DPRINTF(O3CPU, "Calling activate on Thread Context %d\n",
            threadId());

    if (thread->status() == ThreadContext::Active)
        return;

#if FULL_SYSTEM
    thread->lastActivate = curTick;
#endif

    thread->setStatus(ThreadContext::Active);

    // status() == Suspended
    cpu->activateContext(thread->threadId(), delay);
}

template <class Impl>
void
O3ThreadContext<Impl>::suspend(int delay)
{
    DPRINTF(O3CPU, "Calling suspend on Thread Context %d\n",
            threadId());

    if (thread->status() == ThreadContext::Suspended)
        return;

#if FULL_SYSTEM
    thread->lastActivate = curTick;
    thread->lastSuspend = curTick;
#endif
/*
#if FULL_SYSTEM
    // Don't change the status from active if there are pending interrupts
    if (cpu->checkInterrupts()) {
        assert(status() == ThreadContext::Active);
        return;
    }
#endif
*/
    thread->setStatus(ThreadContext::Suspended);
    cpu->suspendContext(thread->threadId());
}

template <class Impl>
void
O3ThreadContext<Impl>::halt(int delay)
{
    DPRINTF(O3CPU, "Calling halt on Thread Context %d\n",
            threadId());

    if (thread->status() == ThreadContext::Halted)
        return;

    thread->setStatus(ThreadContext::Halted);
    cpu->haltContext(thread->threadId());
}

template <class Impl>
void
O3ThreadContext<Impl>::regStats(const std::string &name)
{
#if FULL_SYSTEM
    thread->kernelStats = new TheISA::Kernel::Statistics(cpu->system);
    thread->kernelStats->regStats(name + ".kern");
#endif
}

template <class Impl>
void
O3ThreadContext<Impl>::serialize(std::ostream &os)
{
#if FULL_SYSTEM
    if (thread->kernelStats)
        thread->kernelStats->serialize(os);
#endif

}

template <class Impl>
void
O3ThreadContext<Impl>::unserialize(Checkpoint *cp, const std::string &section)
{
#if FULL_SYSTEM
    if (thread->kernelStats)
        thread->kernelStats->unserialize(cp, section);
#endif

}

#if FULL_SYSTEM
template <class Impl>
Tick
O3ThreadContext<Impl>::readLastActivate()
{
    return thread->lastActivate;
}

template <class Impl>
Tick
O3ThreadContext<Impl>::readLastSuspend()
{
    return thread->lastSuspend;
}

template <class Impl>
void
O3ThreadContext<Impl>::profileClear()
{
    thread->profileClear();
}

template <class Impl>
void
O3ThreadContext<Impl>::profileSample()
{
    thread->profileSample();
}
#endif

template <class Impl>
TheISA::MachInst
O3ThreadContext<Impl>:: getInst()
{
    return thread->getInst();
}

template <class Impl>
void
O3ThreadContext<Impl>::copyArchRegs(ThreadContext *tc)
{
    // This function will mess things up unless the ROB is empty and
    // there are no instructions in the pipeline.
    ThreadID tid = thread->threadId();
    PhysRegIndex renamed_reg;

    // First loop through the integer registers.
    for (int i = 0; i < TheISA::NumIntRegs; ++i) {
        renamed_reg = cpu->renameMap[tid].lookup(i);

        DPRINTF(O3CPU, "Copying over register %i, had data %lli, "
                "now has data %lli.\n",
                renamed_reg, cpu->readIntReg(renamed_reg),
                tc->readIntReg(i));

        cpu->setIntReg(renamed_reg, tc->readIntReg(i));
    }

    // Then loop through the floating point registers.
    for (int i = 0; i < TheISA::NumFloatRegs; ++i) {
        renamed_reg = cpu->renameMap[tid].lookup(i + TheISA::FP_Base_DepTag);
        cpu->setFloatRegBits(renamed_reg,
                             tc->readFloatRegBits(i));
    }

    // Copy the misc regs.
    TheISA::copyMiscRegs(tc, this);

    // Then finally set the PC, the next PC, the nextNPC, the micropc, and the
    // next micropc.
    cpu->setPC(tc->readPC(), tid);
    cpu->setNextPC(tc->readNextPC(), tid);
    cpu->setNextNPC(tc->readNextNPC(), tid);
    cpu->setMicroPC(tc->readMicroPC(), tid);
    cpu->setNextMicroPC(tc->readNextMicroPC(), tid);
#if !FULL_SYSTEM
    this->thread->funcExeInst = tc->readFuncExeInst();
#endif
}

template <class Impl>
void
O3ThreadContext<Impl>::clearArchRegs()
{}

template <class Impl>
uint64_t
O3ThreadContext<Impl>::readIntReg(int reg_idx)
{
    reg_idx = cpu->isa[thread->threadId()].flattenIntIndex(reg_idx);
    return cpu->readArchIntReg(reg_idx, thread->threadId());
}

template <class Impl>
TheISA::FloatReg
O3ThreadContext<Impl>::readFloatReg(int reg_idx)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    return cpu->readArchFloatReg(reg_idx, thread->threadId());
}

template <class Impl>
TheISA::FloatRegBits
O3ThreadContext<Impl>::readFloatRegBits(int reg_idx)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    return cpu->readArchFloatRegInt(reg_idx, thread->threadId());
}

template <class Impl>
void
O3ThreadContext<Impl>::setIntReg(int reg_idx, uint64_t val)
{
    reg_idx = cpu->isa[thread->threadId()].flattenIntIndex(reg_idx);
    cpu->setArchIntReg(reg_idx, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setFloatReg(int reg_idx, FloatReg val)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    cpu->setArchFloatReg(reg_idx, val, thread->threadId());

    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    cpu->setArchFloatRegInt(reg_idx, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setPC(uint64_t val)
{
    cpu->setPC(val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setNextPC(uint64_t val)
{
    cpu->setNextPC(val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setMicroPC(uint64_t val)
{
    cpu->setMicroPC(val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setNextMicroPC(uint64_t val)
{
    cpu->setNextMicroPC(val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
int
O3ThreadContext<Impl>::flattenIntIndex(int reg)
{
    return cpu->isa[thread->threadId()].flattenIntIndex(reg);
}

template <class Impl>
int
O3ThreadContext<Impl>::flattenFloatIndex(int reg)
{
    return cpu->isa[thread->threadId()].flattenFloatIndex(reg);
}

template <class Impl>
void
O3ThreadContext<Impl>::setMiscRegNoEffect(int misc_reg, const MiscReg &val)
{
    cpu->setMiscRegNoEffect(misc_reg, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setMiscReg(int misc_reg,
                                                const MiscReg &val)
{
    cpu->setMiscReg(misc_reg, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

<commit_msg>O3ThreadContext: When taking over from a previous context, only assert that the system pointers match in Full System mode.<commit_after>/*
 * Copyright (c) 2004-2006 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Kevin Lim
 *          Korey Sewell
 */

#include "arch/registers.hh"
#include "config/the_isa.hh"
#include "cpu/o3/thread_context.hh"
#include "cpu/quiesce_event.hh"

#if FULL_SYSTEM
template <class Impl>
VirtualPort *
O3ThreadContext<Impl>::getVirtPort()
{
    return thread->getVirtPort();
}

template <class Impl>
void
O3ThreadContext<Impl>::dumpFuncProfile()
{
    thread->dumpFuncProfile();
}
#endif

template <class Impl>
void
O3ThreadContext<Impl>::takeOverFrom(ThreadContext *old_context)
{
    // some things should already be set up
#if FULL_SYSTEM
    assert(getSystemPtr() == old_context->getSystemPtr());
#else
    assert(getProcessPtr() == old_context->getProcessPtr());
#endif

    // copy over functional state
    setStatus(old_context->status());
    copyArchRegs(old_context);
    setContextId(old_context->contextId());
    setThreadId(old_context->threadId());

#if !FULL_SYSTEM
    thread->funcExeInst = old_context->readFuncExeInst();
#else
    EndQuiesceEvent *other_quiesce = old_context->getQuiesceEvent();
    if (other_quiesce) {
        // Point the quiesce event's TC at this TC so that it wakes up
        // the proper CPU.
        other_quiesce->tc = this;
    }
    if (thread->quiesceEvent) {
        thread->quiesceEvent->tc = this;
    }

    // Transfer kernel stats from one CPU to the other.
    thread->kernelStats = old_context->getKernelStats();
//    storeCondFailures = 0;
    cpu->lockFlag = false;
#endif

    old_context->setStatus(ThreadContext::Halted);

    thread->inSyscall = false;
    thread->trapPending = false;
}

template <class Impl>
void
O3ThreadContext<Impl>::activate(int delay)
{
    DPRINTF(O3CPU, "Calling activate on Thread Context %d\n",
            threadId());

    if (thread->status() == ThreadContext::Active)
        return;

#if FULL_SYSTEM
    thread->lastActivate = curTick;
#endif

    thread->setStatus(ThreadContext::Active);

    // status() == Suspended
    cpu->activateContext(thread->threadId(), delay);
}

template <class Impl>
void
O3ThreadContext<Impl>::suspend(int delay)
{
    DPRINTF(O3CPU, "Calling suspend on Thread Context %d\n",
            threadId());

    if (thread->status() == ThreadContext::Suspended)
        return;

#if FULL_SYSTEM
    thread->lastActivate = curTick;
    thread->lastSuspend = curTick;
#endif
/*
#if FULL_SYSTEM
    // Don't change the status from active if there are pending interrupts
    if (cpu->checkInterrupts()) {
        assert(status() == ThreadContext::Active);
        return;
    }
#endif
*/
    thread->setStatus(ThreadContext::Suspended);
    cpu->suspendContext(thread->threadId());
}

template <class Impl>
void
O3ThreadContext<Impl>::halt(int delay)
{
    DPRINTF(O3CPU, "Calling halt on Thread Context %d\n",
            threadId());

    if (thread->status() == ThreadContext::Halted)
        return;

    thread->setStatus(ThreadContext::Halted);
    cpu->haltContext(thread->threadId());
}

template <class Impl>
void
O3ThreadContext<Impl>::regStats(const std::string &name)
{
#if FULL_SYSTEM
    thread->kernelStats = new TheISA::Kernel::Statistics(cpu->system);
    thread->kernelStats->regStats(name + ".kern");
#endif
}

template <class Impl>
void
O3ThreadContext<Impl>::serialize(std::ostream &os)
{
#if FULL_SYSTEM
    if (thread->kernelStats)
        thread->kernelStats->serialize(os);
#endif

}

template <class Impl>
void
O3ThreadContext<Impl>::unserialize(Checkpoint *cp, const std::string &section)
{
#if FULL_SYSTEM
    if (thread->kernelStats)
        thread->kernelStats->unserialize(cp, section);
#endif

}

#if FULL_SYSTEM
template <class Impl>
Tick
O3ThreadContext<Impl>::readLastActivate()
{
    return thread->lastActivate;
}

template <class Impl>
Tick
O3ThreadContext<Impl>::readLastSuspend()
{
    return thread->lastSuspend;
}

template <class Impl>
void
O3ThreadContext<Impl>::profileClear()
{
    thread->profileClear();
}

template <class Impl>
void
O3ThreadContext<Impl>::profileSample()
{
    thread->profileSample();
}
#endif

template <class Impl>
TheISA::MachInst
O3ThreadContext<Impl>:: getInst()
{
    return thread->getInst();
}

template <class Impl>
void
O3ThreadContext<Impl>::copyArchRegs(ThreadContext *tc)
{
    // This function will mess things up unless the ROB is empty and
    // there are no instructions in the pipeline.
    ThreadID tid = thread->threadId();
    PhysRegIndex renamed_reg;

    // First loop through the integer registers.
    for (int i = 0; i < TheISA::NumIntRegs; ++i) {
        renamed_reg = cpu->renameMap[tid].lookup(i);

        DPRINTF(O3CPU, "Copying over register %i, had data %lli, "
                "now has data %lli.\n",
                renamed_reg, cpu->readIntReg(renamed_reg),
                tc->readIntReg(i));

        cpu->setIntReg(renamed_reg, tc->readIntReg(i));
    }

    // Then loop through the floating point registers.
    for (int i = 0; i < TheISA::NumFloatRegs; ++i) {
        renamed_reg = cpu->renameMap[tid].lookup(i + TheISA::FP_Base_DepTag);
        cpu->setFloatRegBits(renamed_reg,
                             tc->readFloatRegBits(i));
    }

    // Copy the misc regs.
    TheISA::copyMiscRegs(tc, this);

    // Then finally set the PC, the next PC, the nextNPC, the micropc, and the
    // next micropc.
    cpu->setPC(tc->readPC(), tid);
    cpu->setNextPC(tc->readNextPC(), tid);
    cpu->setNextNPC(tc->readNextNPC(), tid);
    cpu->setMicroPC(tc->readMicroPC(), tid);
    cpu->setNextMicroPC(tc->readNextMicroPC(), tid);
#if !FULL_SYSTEM
    this->thread->funcExeInst = tc->readFuncExeInst();
#endif
}

template <class Impl>
void
O3ThreadContext<Impl>::clearArchRegs()
{}

template <class Impl>
uint64_t
O3ThreadContext<Impl>::readIntReg(int reg_idx)
{
    reg_idx = cpu->isa[thread->threadId()].flattenIntIndex(reg_idx);
    return cpu->readArchIntReg(reg_idx, thread->threadId());
}

template <class Impl>
TheISA::FloatReg
O3ThreadContext<Impl>::readFloatReg(int reg_idx)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    return cpu->readArchFloatReg(reg_idx, thread->threadId());
}

template <class Impl>
TheISA::FloatRegBits
O3ThreadContext<Impl>::readFloatRegBits(int reg_idx)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    return cpu->readArchFloatRegInt(reg_idx, thread->threadId());
}

template <class Impl>
void
O3ThreadContext<Impl>::setIntReg(int reg_idx, uint64_t val)
{
    reg_idx = cpu->isa[thread->threadId()].flattenIntIndex(reg_idx);
    cpu->setArchIntReg(reg_idx, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setFloatReg(int reg_idx, FloatReg val)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    cpu->setArchFloatReg(reg_idx, val, thread->threadId());

    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val)
{
    reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx);
    cpu->setArchFloatRegInt(reg_idx, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setPC(uint64_t val)
{
    cpu->setPC(val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setNextPC(uint64_t val)
{
    cpu->setNextPC(val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setMicroPC(uint64_t val)
{
    cpu->setMicroPC(val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setNextMicroPC(uint64_t val)
{
    cpu->setNextMicroPC(val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
int
O3ThreadContext<Impl>::flattenIntIndex(int reg)
{
    return cpu->isa[thread->threadId()].flattenIntIndex(reg);
}

template <class Impl>
int
O3ThreadContext<Impl>::flattenFloatIndex(int reg)
{
    return cpu->isa[thread->threadId()].flattenFloatIndex(reg);
}

template <class Impl>
void
O3ThreadContext<Impl>::setMiscRegNoEffect(int misc_reg, const MiscReg &val)
{
    cpu->setMiscRegNoEffect(misc_reg, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

template <class Impl>
void
O3ThreadContext<Impl>::setMiscReg(int misc_reg,
                                                const MiscReg &val)
{
    cpu->setMiscReg(misc_reg, val, thread->threadId());

    // Squash if we're not already in a state update mode.
    if (!thread->trapPending && !thread->inSyscall) {
        cpu->squashFromTC(thread->threadId());
    }
}

<|endoftext|>


<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program 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 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "SqliteConnection.h"

#include "database/SqliteTools.h"

namespace medialibrary
{
namespace sqlite
{

Connection::Connection( const std::string &dbPath )
    : m_dbPath( dbPath )
    , m_readLock( m_contextLock )
    , m_writeLock( m_contextLock )
{
    if ( sqlite3_threadsafe() == 0 )
        throw std::runtime_error( "SQLite isn't built with threadsafe mode" );
    if ( sqlite3_config( SQLITE_CONFIG_MULTITHREAD ) == SQLITE_ERROR )
        throw std::runtime_error( "Failed to enable sqlite multithreaded mode" );
}

Connection::~Connection()
{
    sqlite::Statement::FlushStatementCache();
}

Connection::Handle Connection::handle()
{
    /**
     * We need to have a single sqlite connection per thread, but we also need
     * to be able to destroy those when the SqliteConnection wrapper instance
     * gets destroyed (otherwise we can't re-instantiate the media library during
     * the application runtime. That can't work for unit test and can be quite
     * a limitation for the application using the medialib)
     * Being able to refer to all per-thread connection means we can't use
     * thread local directly, however, we need to know if a thread goes away, to
     * avoid re-using an old connection on a new thread with the same id, as this
     * would result in a "Database is locked error"
     * In order to solve this, we use a single map to store all connection,
     * indexed by std::thread::id (well, compat::thread::id). Additionaly, in
     * order to know when a thread gets terminated, we store a thread_local
     * object to signal back when a thread returns, and to remove the now
     * unusable connection.
     * Also note that we need to flush any potentially cached compiled statement
     * when a thread gets terminated, as it would become unusable as well.
     *
     * \sa sqlite::Connection::ThreadSpecificConnection::~ThreadSpecificConnection
     * \sa sqlite::Statement::StatementsCache
     */
    std::unique_lock<compat::Mutex> lock( m_connMutex );
    sqlite3* dbConnection;
    auto it = m_conns.find( compat::this_thread::get_id() );
    if ( it == end( m_conns ) )
    {
        auto res = sqlite3_open( m_dbPath.c_str(), &dbConnection );
        ConnPtr dbConn( dbConnection, &sqlite3_close );
        if ( res != SQLITE_OK )
            throw sqlite::errors::Generic( std::string( "Failed to connect to database: " )
                                           + sqlite3_errstr( res ) );
        sqlite3_extended_result_codes( dbConnection, 1 );
        sqlite3_busy_timeout( dbConnection, 500 );
        // Don't use public wrapper, they need to be able to call getConn, which
        // would result from a recursive call and a deadlock from here.
        setPragmaEnabled( dbConnection, "foreign_keys", true );
        setPragmaEnabled( dbConnection, "recursive_triggers", true );

        m_conns.emplace( compat::this_thread::get_id(), std::move( dbConn ) );
        sqlite3_update_hook( dbConnection, &updateHook, this );
        static thread_local ThreadSpecificConnection tsc( shared_from_this() );
        return dbConnection;
    }
    return it->second.get();
}

std::unique_ptr<sqlite::Transaction> Connection::newTransaction()
{
    return std::unique_ptr<sqlite::Transaction>{ new sqlite::Transaction( this ) };
}

Connection::ReadContext Connection::acquireReadContext()
{
    return ReadContext{ m_readLock };
}

Connection::WriteContext Connection::acquireWriteContext()
{
    return WriteContext{ m_writeLock };
}

void Connection::setPragmaEnabled( Handle conn,
                                         const std::string& pragmaName,
                                         bool value )
{
    std::string reqBase = std::string{ "PRAGMA " } + pragmaName;
    std::string reqSet = reqBase + " = " + ( value ? "ON" : "OFF" );

    sqlite::Statement stmt( conn, reqSet );
    stmt.execute();
    if ( stmt.row() != nullptr )
        throw std::runtime_error( "Failed to enable/disable " + pragmaName );

    sqlite::Statement stmtCheck( conn, reqBase );
    stmtCheck.execute();
    auto resultRow = stmtCheck.row();
    bool resultValue;
    resultRow >> resultValue;
    if( resultValue != value )
        throw std::runtime_error( "PRAGMA " + pragmaName + " value mismatch" );
}

void Connection::setForeignKeyEnabled( bool value )
{
    // Ensure no transaction will be started during the pragma change
    auto ctx = acquireWriteContext();
    // Changing this pragma during a transaction is a no-op (silently ignored by
    // sqlite), so ensure we're doing something usefull here:
    assert( sqlite::Transaction::transactionInProgress() == false );
    setPragmaEnabled( handle(), "foreign_keys", value );
}

void Connection::setRecursiveTriggersEnabled( bool value )
{
    // Ensure no request will run while we change this setting
    auto ctx = acquireWriteContext();

    // Changing the recursive_triggers setting affects the execution of all
    // statements prepared using the database connection, including those
    // prepared before the setting was changed. Any existing statements prepared
    // using the legacy sqlite3_prepare() interface may fail with an
    // SQLITE_SCHEMA error after the recursive_triggers setting is changed.
    // https://sqlite.org/pragma.html#pragma_recursive_triggers
    auto h = handle();
    sqlite::Statement::FlushConnectionStatementCache( h );

    setPragmaEnabled( h, "recursive_triggers", value );
}

void Connection::registerUpdateHook( const std::string& table, Connection::UpdateHookCb cb )
{
    m_hooks.emplace( table, cb );
}

std::shared_ptr<Connection> Connection::connect( const std::string& dbPath )
{
    // Use a wrapper to allow make_shared to use the private Connection ctor
    struct SqliteConnectionWrapper : public Connection
    {
        SqliteConnectionWrapper( const std::string& p ) : Connection( p ) {}
    };
    return std::make_shared<SqliteConnectionWrapper>( dbPath );
}

void Connection::updateHook( void* data, int reason, const char*,
                                   const char* table, sqlite_int64 rowId )
{
    const auto self = reinterpret_cast<Connection*>( data );
    auto it = self->m_hooks.find( table );
    if ( it == end( self->m_hooks ) )
        return;
    switch ( reason )
    {
    case SQLITE_INSERT:
        it->second( HookReason::Insert, rowId );
        break;
    case SQLITE_UPDATE:
        it->second( HookReason::Update, rowId );
        break;
    case SQLITE_DELETE:
        it->second( HookReason::Delete, rowId );
        break;
    }
}

Connection::WeakDbContext::WeakDbContext( Connection* conn )
    : m_conn( conn )
{
    m_conn->setForeignKeyEnabled( false );
    m_conn->setRecursiveTriggersEnabled( false );
}

Connection::WeakDbContext::~WeakDbContext()
{
    m_conn->setForeignKeyEnabled( true );
    m_conn->setRecursiveTriggersEnabled( true );
}

Connection::ThreadSpecificConnection::ThreadSpecificConnection(
        std::shared_ptr<Connection> conn)
    : m_conn( conn )
{
}

Connection::ThreadSpecificConnection::~ThreadSpecificConnection()
{
    std::unique_lock<compat::Mutex> lock( m_conn->m_connMutex );
    auto it = m_conn->m_conns.find( compat::this_thread::get_id() );
    if ( it != end( m_conn->m_conns ) )
    {
        // Ensure those cached statements will not be used if another connection
        // with the same pointer gets created
        sqlite::Statement::FlushConnectionStatementCache( it->second.get() );
        // And ensure we won't use the same connection if a thread with the same
        // ID gets used in the future.
        m_conn->m_conns.erase( it );
    }
}

}

}
<commit_msg>SqliteConnection: Avoid a copy<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program 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 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "SqliteConnection.h"

#include "database/SqliteTools.h"

namespace medialibrary
{
namespace sqlite
{

Connection::Connection( const std::string &dbPath )
    : m_dbPath( dbPath )
    , m_readLock( m_contextLock )
    , m_writeLock( m_contextLock )
{
    if ( sqlite3_threadsafe() == 0 )
        throw std::runtime_error( "SQLite isn't built with threadsafe mode" );
    if ( sqlite3_config( SQLITE_CONFIG_MULTITHREAD ) == SQLITE_ERROR )
        throw std::runtime_error( "Failed to enable sqlite multithreaded mode" );
}

Connection::~Connection()
{
    sqlite::Statement::FlushStatementCache();
}

Connection::Handle Connection::handle()
{
    /**
     * We need to have a single sqlite connection per thread, but we also need
     * to be able to destroy those when the SqliteConnection wrapper instance
     * gets destroyed (otherwise we can't re-instantiate the media library during
     * the application runtime. That can't work for unit test and can be quite
     * a limitation for the application using the medialib)
     * Being able to refer to all per-thread connection means we can't use
     * thread local directly, however, we need to know if a thread goes away, to
     * avoid re-using an old connection on a new thread with the same id, as this
     * would result in a "Database is locked error"
     * In order to solve this, we use a single map to store all connection,
     * indexed by std::thread::id (well, compat::thread::id). Additionaly, in
     * order to know when a thread gets terminated, we store a thread_local
     * object to signal back when a thread returns, and to remove the now
     * unusable connection.
     * Also note that we need to flush any potentially cached compiled statement
     * when a thread gets terminated, as it would become unusable as well.
     *
     * \sa sqlite::Connection::ThreadSpecificConnection::~ThreadSpecificConnection
     * \sa sqlite::Statement::StatementsCache
     */
    std::unique_lock<compat::Mutex> lock( m_connMutex );
    sqlite3* dbConnection;
    auto it = m_conns.find( compat::this_thread::get_id() );
    if ( it == end( m_conns ) )
    {
        auto res = sqlite3_open( m_dbPath.c_str(), &dbConnection );
        ConnPtr dbConn( dbConnection, &sqlite3_close );
        if ( res != SQLITE_OK )
            throw sqlite::errors::Generic( std::string( "Failed to connect to database: " )
                                           + sqlite3_errstr( res ) );
        sqlite3_extended_result_codes( dbConnection, 1 );
        sqlite3_busy_timeout( dbConnection, 500 );
        // Don't use public wrapper, they need to be able to call getConn, which
        // would result from a recursive call and a deadlock from here.
        setPragmaEnabled( dbConnection, "foreign_keys", true );
        setPragmaEnabled( dbConnection, "recursive_triggers", true );

        m_conns.emplace( compat::this_thread::get_id(), std::move( dbConn ) );
        sqlite3_update_hook( dbConnection, &updateHook, this );
        static thread_local ThreadSpecificConnection tsc( shared_from_this() );
        return dbConnection;
    }
    return it->second.get();
}

std::unique_ptr<sqlite::Transaction> Connection::newTransaction()
{
    return std::unique_ptr<sqlite::Transaction>{ new sqlite::Transaction( this ) };
}

Connection::ReadContext Connection::acquireReadContext()
{
    return ReadContext{ m_readLock };
}

Connection::WriteContext Connection::acquireWriteContext()
{
    return WriteContext{ m_writeLock };
}

void Connection::setPragmaEnabled( Handle conn,
                                         const std::string& pragmaName,
                                         bool value )
{
    std::string reqBase = std::string{ "PRAGMA " } + pragmaName;
    std::string reqSet = reqBase + " = " + ( value ? "ON" : "OFF" );

    sqlite::Statement stmt( conn, reqSet );
    stmt.execute();
    if ( stmt.row() != nullptr )
        throw std::runtime_error( "Failed to enable/disable " + pragmaName );

    sqlite::Statement stmtCheck( conn, reqBase );
    stmtCheck.execute();
    auto resultRow = stmtCheck.row();
    bool resultValue;
    resultRow >> resultValue;
    if( resultValue != value )
        throw std::runtime_error( "PRAGMA " + pragmaName + " value mismatch" );
}

void Connection::setForeignKeyEnabled( bool value )
{
    // Ensure no transaction will be started during the pragma change
    auto ctx = acquireWriteContext();
    // Changing this pragma during a transaction is a no-op (silently ignored by
    // sqlite), so ensure we're doing something usefull here:
    assert( sqlite::Transaction::transactionInProgress() == false );
    setPragmaEnabled( handle(), "foreign_keys", value );
}

void Connection::setRecursiveTriggersEnabled( bool value )
{
    // Ensure no request will run while we change this setting
    auto ctx = acquireWriteContext();

    // Changing the recursive_triggers setting affects the execution of all
    // statements prepared using the database connection, including those
    // prepared before the setting was changed. Any existing statements prepared
    // using the legacy sqlite3_prepare() interface may fail with an
    // SQLITE_SCHEMA error after the recursive_triggers setting is changed.
    // https://sqlite.org/pragma.html#pragma_recursive_triggers
    auto h = handle();
    sqlite::Statement::FlushConnectionStatementCache( h );

    setPragmaEnabled( h, "recursive_triggers", value );
}

void Connection::registerUpdateHook( const std::string& table, Connection::UpdateHookCb cb )
{
    m_hooks.emplace( table, cb );
}

std::shared_ptr<Connection> Connection::connect( const std::string& dbPath )
{
    // Use a wrapper to allow make_shared to use the private Connection ctor
    struct SqliteConnectionWrapper : public Connection
    {
        SqliteConnectionWrapper( const std::string& p ) : Connection( p ) {}
    };
    return std::make_shared<SqliteConnectionWrapper>( dbPath );
}

void Connection::updateHook( void* data, int reason, const char*,
                                   const char* table, sqlite_int64 rowId )
{
    const auto self = reinterpret_cast<Connection*>( data );
    auto it = self->m_hooks.find( table );
    if ( it == end( self->m_hooks ) )
        return;
    switch ( reason )
    {
    case SQLITE_INSERT:
        it->second( HookReason::Insert, rowId );
        break;
    case SQLITE_UPDATE:
        it->second( HookReason::Update, rowId );
        break;
    case SQLITE_DELETE:
        it->second( HookReason::Delete, rowId );
        break;
    }
}

Connection::WeakDbContext::WeakDbContext( Connection* conn )
    : m_conn( conn )
{
    m_conn->setForeignKeyEnabled( false );
    m_conn->setRecursiveTriggersEnabled( false );
}

Connection::WeakDbContext::~WeakDbContext()
{
    m_conn->setForeignKeyEnabled( true );
    m_conn->setRecursiveTriggersEnabled( true );
}

Connection::ThreadSpecificConnection::ThreadSpecificConnection(
        std::shared_ptr<Connection> conn )
    : m_conn( std::move( conn ) )
{
}

Connection::ThreadSpecificConnection::~ThreadSpecificConnection()
{
    std::unique_lock<compat::Mutex> lock( m_conn->m_connMutex );
    auto it = m_conn->m_conns.find( compat::this_thread::get_id() );
    if ( it != end( m_conn->m_conns ) )
    {
        // Ensure those cached statements will not be used if another connection
        // with the same pointer gets created
        sqlite::Statement::FlushConnectionStatementCache( it->second.get() );
        // And ensure we won't use the same connection if a thread with the same
        // ID gets used in the future.
        m_conn->m_conns.erase( it );
    }
}

}

}
<|endoftext|>


<commit_before>#include <iostream>
#include <fcntl.h>
#include <vector>

#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

namespace ba = boost::asio;
std::vector<char> data(4096);

void start_read( ba::posix::stream_descriptor &desc );

void read_handler( boost::system::error_code const &e,
                   size_t bytes,
                   ba::posix::stream_descriptor &desc)
{
    if( !e ) {
        std::cout << "read " << bytes << "\n";
        start_read( desc );
    } else {
        std::cout << "read error: " << e.message( ) << "\n";
        throw std::runtime_error( e.message( ) );
    }
}

void start_read( ba::posix::stream_descriptor &desc )
{
    desc.async_read_some( ba::buffer(data),
                          boost::bind( &read_handler,
                                       ba::placeholders::error,
                                       ba::placeholders::bytes_transferred,
                                       boost::ref(desc)) );
}

int main( int argc, const char *argv[] ) try
{

    ba::io_service ios;
    ba::io_service::work w(ios);

    ba::posix::stream_descriptor sd(ios);

    int fd = open( argv[1], O_RDONLY );

    std::cout << "Fd: " << fd << "\n";

    sd.assign( fd );
    start_read( sd );

    while( 1 ) {
        ios.run_one( );
    }

    return 0;
} catch( const std::exception &ex ) {
    std::cerr << ex.what( ) << "\n";
    return 1;
}

<commit_msg>draft<commit_after>#include <iostream>
#include <fcntl.h>
#include <vector>

#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

namespace ba = boost::asio;
std::vector<char> data(4096);

void start_read( ba::posix::stream_descriptor &desc );

void read_handler( boost::system::error_code const &e,
                   size_t bytes,
                   ba::posix::stream_descriptor &desc)
{
    if( !e ) {
        std::cout << "read " << bytes << "\n";
        start_read( desc );
    } else {
        std::cout << "read error: " << e.message( ) << "\n";
        throw std::runtime_error( e.message( ) );
    }
}

void start_read( ba::posix::stream_descriptor &desc )
{
    lseek( desc.native_handle( ), 0, SEEK_SET );
    desc.async_read_some( ba::buffer(data),
                          boost::bind( &read_handler,
                                       ba::placeholders::error,
                                       ba::placeholders::bytes_transferred,
                                       boost::ref(desc)) );
}

int main( int argc, const char *argv[] ) try
{

    ba::io_service ios;
    ba::io_service::work w(ios);

    ba::posix::stream_descriptor sd(ios);

    int fd = open( argv[1], O_RDONLY );

    std::cout << "Fd: " << fd << "\n";

    sd.assign( fd );
    start_read( sd );

    while( 1 ) {
        ios.run_one( );
    }

    return 0;
} catch( const std::exception &ex ) {
    std::cerr << ex.what( ) << "\n";
    return 1;
}

<|endoftext|>


<commit_before>/*************************************************
* Random Number Generator Base Source File       *
* (C) 1999-2008 Jack Lloyd                       *
*************************************************/

#include <botan/rng.h>
#include <botan/lookup.h>
#include <botan/util.h>
#include <botan/parsing.h>
#include <botan/timers.h>

#if defined(BOTAN_HAS_RANDPOOL)
  #include <botan/randpool.h>
#endif

#if defined(BOTAN_HAS_X931_RNG)
  #include <botan/x931_rng.h>
#endif

#if defined(BOTAN_HAS_TIMER_HARDWARE)
  #include <botan/tm_hard.h>
#elif defined(BOTAN_HAS_TIMER_POSIX)
  #include <botan/tm_posix.h>
#elif defined(BOTAN_HAS_TIMER_UNIX)
  #include <botan/tm_unix.h>
#elif defined(BOTAN_HAS_TIMER_WIN32)
  #include <botan/tm_win32.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_DEVICE)
  #include <botan/es_dev.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_EGD)
  #include <botan/es_egd.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_UNIX)
  #include <botan/es_unix.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_BEOS)
  #include <botan/es_beos.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_CAPI)
  #include <botan/es_capi.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_WIN32)
  #include <botan/es_win32.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_FTW)
  #include <botan/es_ftw.h>
#endif

namespace Botan {

/*************************************************
* Default fast poll for EntropySources           *
*************************************************/
u32bit EntropySource::fast_poll(byte buf[], u32bit len)
   {
   return this->slow_poll(buf, len);
   }

/*************************************************
* Get a single random byte                       *
*************************************************/
byte RandomNumberGenerator::next_byte()
   {
   byte out;
   this->randomize(&out, 1);
   return out;
   }

/*************************************************
* Create and seed a new RNG object               *
*************************************************/
RandomNumberGenerator* RandomNumberGenerator::make_rng()
   {
#if !defined(BOTAN_HAS_RANDPOOL)
   return 0;
#else

   RandomNumberGenerator* rng = new Randpool(get_block_cipher("AES-256"),
                                             get_mac("HMAC(SHA-256)"));

#if defined(BOTAN_HAS_X931_RNG)
   rng = new ANSI_X931_RNG(get_block_cipher("AES-256"), rng);
#endif

#if defined(BOTAN_HAS_TIMER_HARDWARE)
   rng->add_entropy_source(new Hardware_Timer);
#elif defined(BOTAN_HAS_TIMER_POSIX)
   rng->add_entropy_source(new POSIX_Timer);
#elif defined(BOTAN_HAS_TIMER_UNIX)
   rng->add_entropy_source(new Unix_Timer);
#elif defined(BOTAN_HAS_TIMER_WIN32)
   rng->add_entropy_source(new Win32_Timer);
#else
   rng->add_entropy_source(new Timer);
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_DEVICE)
   rng->add_entropy_source(
      new Device_EntropySource(
         split_on("/dev/random:/dev/srandom:/dev/urandom", ':')
         )
      );
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_EGD)
   rng->add_entropy_source(
      new EGD_EntropySource(split_on("/var/run/egd-pool:/dev/egd-pool", ':'))
      );
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_CAPI)
   rng->add_entropy_source(new Win32_CAPI_EntropySource);
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_WIN32)
   rng->add_entropy_source(new Win32_EntropySource);
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_UNIX)
   rng->add_entropy_source(
      new Unix_EntropySource(split_on("/bin:/sbin:/usr/bin:/usr/sbin", ':'))
      );
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_BEOS)
   rng->add_entropy_source(new BeOS_EntropySource);
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_FTW)
   rng->add_entropy_source(new FTW_EntropySource("/proc"));
#endif

   return rng;
#endif
   }

}
<commit_msg>Have RNG::make_rng throw an exception rather than return NULL<commit_after>/*************************************************
* Random Number Generator Base Source File       *
* (C) 1999-2008 Jack Lloyd                       *
*************************************************/

#include <botan/rng.h>
#include <botan/lookup.h>
#include <botan/util.h>
#include <botan/parsing.h>
#include <botan/timers.h>

#if defined(BOTAN_HAS_RANDPOOL)
  #include <botan/randpool.h>
#endif

#if defined(BOTAN_HAS_X931_RNG)
  #include <botan/x931_rng.h>
#endif

#if defined(BOTAN_HAS_TIMER_HARDWARE)
  #include <botan/tm_hard.h>
#elif defined(BOTAN_HAS_TIMER_POSIX)
  #include <botan/tm_posix.h>
#elif defined(BOTAN_HAS_TIMER_UNIX)
  #include <botan/tm_unix.h>
#elif defined(BOTAN_HAS_TIMER_WIN32)
  #include <botan/tm_win32.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_DEVICE)
  #include <botan/es_dev.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_EGD)
  #include <botan/es_egd.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_UNIX)
  #include <botan/es_unix.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_BEOS)
  #include <botan/es_beos.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_CAPI)
  #include <botan/es_capi.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_WIN32)
  #include <botan/es_win32.h>
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_FTW)
  #include <botan/es_ftw.h>
#endif

namespace Botan {

/*************************************************
* Default fast poll for EntropySources           *
*************************************************/
u32bit EntropySource::fast_poll(byte buf[], u32bit len)
   {
   return this->slow_poll(buf, len);
   }

/*************************************************
* Get a single random byte                       *
*************************************************/
byte RandomNumberGenerator::next_byte()
   {
   byte out;
   this->randomize(&out, 1);
   return out;
   }

/*************************************************
* Create and seed a new RNG object               *
*************************************************/
RandomNumberGenerator* RandomNumberGenerator::make_rng()
   {
#if defined(BOTAN_HAS_RANDPOOL)

   /* Randpool is required for make_rng to work */
   RandomNumberGenerator* rng = new Randpool(get_block_cipher("AES-256"),
                                             get_mac("HMAC(SHA-256)"));

   /* If X9.31 is available, wrap the Randpool algorithm in it */
#if defined(BOTAN_HAS_X931_RNG)
   rng = new ANSI_X931_RNG(get_block_cipher("AES-256"), rng);
#endif

#if defined(BOTAN_HAS_TIMER_HARDWARE)
   rng->add_entropy_source(new Hardware_Timer);
#elif defined(BOTAN_HAS_TIMER_POSIX)
   rng->add_entropy_source(new POSIX_Timer);
#elif defined(BOTAN_HAS_TIMER_UNIX)
   rng->add_entropy_source(new Unix_Timer);
#elif defined(BOTAN_HAS_TIMER_WIN32)
   rng->add_entropy_source(new Win32_Timer);
#else
   rng->add_entropy_source(new Timer);
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_DEVICE)
   rng->add_entropy_source(
      new Device_EntropySource(
         split_on("/dev/random:/dev/srandom:/dev/urandom", ':')
         )
      );
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_EGD)
   rng->add_entropy_source(
      new EGD_EntropySource(split_on("/var/run/egd-pool:/dev/egd-pool", ':'))
      );
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_CAPI)
   rng->add_entropy_source(new Win32_CAPI_EntropySource);
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_WIN32)
   rng->add_entropy_source(new Win32_EntropySource);
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_UNIX)
   rng->add_entropy_source(
      new Unix_EntropySource(split_on("/bin:/sbin:/usr/bin:/usr/sbin", ':'))
      );
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_BEOS)
   rng->add_entropy_source(new BeOS_EntropySource);
#endif

#if defined(BOTAN_HAS_ENTROPY_SRC_FTW)
   rng->add_entropy_source(new FTW_EntropySource("/proc"));
#endif

   return rng;
#endif

   throw Algorithm_Not_Found("RandomNumberGenerator::make_rng - no RNG object available");
   }

}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2013-2018 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <tuple>
#include <utility>
#include <iostream>

#include "currency.hpp"
#include "server.hpp"
#include "assets.hpp" // For get_default_currency
#include "http.hpp"

namespace {

std::map<std::tuple<std::string, std::string>, double> exchanges;

// V1 is using free.currencyconverterapi.com
double get_rate_v1(const std::string& from, const std::string& to){
    httplib::Client cli("free.currencyconverterapi.com", 80);

    std::string api_complete = "/api/v3/convert?q=" + from + "_" + to + "&compact=ultra";

    auto res = cli.get(api_complete.c_str());

    if (!res) {
        std::cout << "Error accessing exchange rates (no response), setting exchange between " << from << " to " << to << " to 1/1" << std::endl;

        return  1.0;
    } else if (res->status != 200) {
        std::cout << "Error accessing exchange rates (not OK), setting exchange between " << from << " to " << to << " to 1/1" << std::endl;

        return  1.0;
    } else {
        auto& buffer = res->body;

        if (buffer.find(':') == std::string::npos || buffer.find('}') == std::string::npos) {
            std::cout << "Error parsing exchange rates, setting exchange between " << from << " to " << to << " to 1/1" << std::endl;

            return  1.0;
        } else {
            std::string ratio_result(buffer.begin() + buffer.find(':') + 1, buffer.begin() + buffer.find('}'));

            return atof(ratio_result.c_str());
        }
    }
}

// V2 is using api.exchangeratesapi.io
double get_rate_v2(const std::string& from, const std::string& to, const std::string& date = "latest") {
    httplib::SSLClient cli("api.exchangeratesapi.io", 443);

    std::string api_complete = date + "?symbols=" + to + "&base=" + from;

    auto res = cli.get(api_complete.c_str());

    if (!res) {
        std::cout << "ERROR: Currency(v2): No response, setting exchange between " << from << " to " << to << " to 1/1" << std::endl;
        std::cout << "ERROR: Currency(v2): URL is" << api_complete << std::endl;

        return  1.0;
    } else if (res->status != 200) {
        std::cout << "ERROR: Currency(v2): Error response " << res->status << ", setting exchange between " << from << " to " << to << " to 1/1" << std::endl;
        std::cout << "ERROR: Currency(v2): URL is" << api_complete << std::endl;
        std::cout << "ERROR: Currency(v2): Response is" << res->body << std::endl;

        return  1.0;
    } else {
        auto& buffer = res->body;

        if (buffer.find("\"" + to + "\":") == std::string::npos || buffer.find('}') == std::string::npos) {
            std::cout << "ERROR: Currency(v2): Error parsing exchange rates, setting exchange between " << from << " to " << to << " to 1/1" << std::endl;
            std::cout << "ERROR: Currency(v2): URL is" << api_complete << std::endl;
            std::cout << "ERROR: Currency(v2): Response is" << res->body << std::endl;

            return  1.0;
        } else {
            std::string ratio_result(buffer.begin() + buffer.find("\"" + to + "\":") + 1, buffer.begin() + buffer.find('}'));

            return atof(ratio_result.c_str());
        }
    }
}

} // end of anonymous namespace

void budget::invalidate_currency_cache(){
    exchanges.clear();
}

double budget::exchange_rate(const std::string& from){
    return exchange_rate(from, get_default_currency());
}

double budget::exchange_rate(const std::string& from, const std::string& to){
    if(from == to){
        return 1.0;
    } else {
        auto key = std::make_tuple(from, to);
        auto reverse_key = std::make_tuple(to, from);

        if (!exchanges.count(key)) {
            auto rate = get_rate_v2(from, to);

            if (budget::is_server_running()) {
                std::cout << "INFO: Currency: Rate from " << from << " to " << to << " = " << rate << std::endl;
            }

            exchanges[key]         = rate;
            exchanges[reverse_key] = 1.0 / rate;
        }

        return exchanges[key];
    }
}
<commit_msg>Cleanup<commit_after>//=======================================================================
// Copyright (c) 2013-2018 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <tuple>
#include <utility>
#include <iostream>

#include "currency.hpp"
#include "server.hpp"
#include "assets.hpp" // For get_default_currency
#include "http.hpp"

namespace {

std::map<std::tuple<std::string, std::string>, double> exchanges;

// V1 is using free.currencyconverterapi.com
double get_rate_v1(const std::string& from, const std::string& to){
    httplib::Client cli("free.currencyconverterapi.com", 80);

    std::string api_complete = "/api/v3/convert?q=" + from + "_" + to + "&compact=ultra";

    auto res = cli.get(api_complete.c_str());

    if (!res) {
        std::cout << "Error accessing exchange rates (no response), setting exchange between " << from << " to " << to << " to 1/1" << std::endl;

        return  1.0;
    } else if (res->status != 200) {
        std::cout << "Error accessing exchange rates (not OK), setting exchange between " << from << " to " << to << " to 1/1" << std::endl;

        return  1.0;
    } else {
        auto& buffer = res->body;

        if (buffer.find(':') == std::string::npos || buffer.find('}') == std::string::npos) {
            std::cout << "Error parsing exchange rates, setting exchange between " << from << " to " << to << " to 1/1" << std::endl;

            return  1.0;
        } else {
            std::string ratio_result(buffer.begin() + buffer.find(':') + 1, buffer.begin() + buffer.find('}'));

            return atof(ratio_result.c_str());
        }
    }
}

// V2 is using api.exchangeratesapi.io
double get_rate_v2(const std::string& from, const std::string& to, const std::string& date = "latest") {
    httplib::SSLClient cli("api.exchangeratesapi.io", 443);

    std::string api_complete = "/" + date + "?symbols=" + to + "&base=" + from;

    auto res = cli.get(api_complete.c_str());

    if (!res) {
        std::cout << "ERROR: Currency(v2): No response, setting exchange between " << from << " to " << to << " to 1/1" << std::endl;
        std::cout << "ERROR: Currency(v2): URL is " << api_complete << std::endl;

        return  1.0;
    } else if (res->status != 200) {
        std::cout << "ERROR: Currency(v2): Error response " << res->status << ", setting exchange between " << from << " to " << to << " to 1/1" << std::endl;
        std::cout << "ERROR: Currency(v2): URL is " << api_complete << std::endl;
        std::cout << "ERROR: Currency(v2): Response is " << res->body << std::endl;

        return  1.0;
    } else {
        auto& buffer = res->body;

        if (buffer.find("\"" + to + "\":") == std::string::npos || buffer.find('}') == std::string::npos) {
            std::cout << "ERROR: Currency(v2): Error parsing exchange rates, setting exchange between " << from << " to " << to << " to 1/1" << std::endl;
            std::cout << "ERROR: Currency(v2): URL is " << api_complete << std::endl;
            std::cout << "ERROR: Currency(v2): Response is " << res->body << std::endl;

            return  1.0;
        } else {
            std::string ratio_result(buffer.begin() + buffer.find("\"" + to + "\":") + 1, buffer.begin() + buffer.find('}'));

            return atof(ratio_result.c_str());
        }
    }
}

} // end of anonymous namespace

void budget::invalidate_currency_cache(){
    exchanges.clear();
}

double budget::exchange_rate(const std::string& from){
    return exchange_rate(from, get_default_currency());
}

double budget::exchange_rate(const std::string& from, const std::string& to){
    if(from == to){
        return 1.0;
    } else {
        auto key = std::make_tuple(from, to);
        auto reverse_key = std::make_tuple(to, from);

        if (!exchanges.count(key)) {
            auto rate = get_rate_v2(from, to);

            if (budget::is_server_running()) {
                std::cout << "INFO: Currency: Rate from " << from << " to " << to << " = " << rate << std::endl;
            }

            exchanges[key]         = rate;
            exchanges[reverse_key] = 1.0 / rate;
        }

        return exchanges[key];
    }
}
<|endoftext|>