<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include "config.h"
#include <cassert>
#include <iostream>
#include <stdexcept>

#include "sqlite-strategies.hh"
#include "sqlite-eval.hh"
#include "ep.hh"
#include "pathexpand.hh"

static const int CURRENT_SCHEMA_VERSION(2);

SqliteStrategy::SqliteStrategy(const char * const fn,
                               const char * const finit,
                               const char * const pfinit,
                               size_t shards) : db(NULL),
    filename(fn),
    initFile(finit),
    postInitFile(pfinit),
    shardCount(shards),
    ins_vb_stmt(NULL), clear_vb_stmt(NULL), sel_vb_stmt(NULL),
    clear_stats_stmt(NULL), ins_stat_stmt(NULL),
    schema_version(0) {

    assert(filename);
    assert(shardCount > 0);
}

SqliteStrategy::~SqliteStrategy() {
    destroyMetaStatements();
    close();
}

void SqliteStrategy::destroyMetaStatements() {
    delete ins_vb_stmt;
    delete clear_vb_stmt;
    delete sel_vb_stmt;
    delete clear_stats_stmt;
    delete ins_stat_stmt;
}

void SqliteStrategy::initMetaTables() {
    assert(db);
    execute("create table if not exists vbucket_states"
            " (vbid integer primary key on conflict replace,"
            "  vb_version interger,"
            "  state varchar(16),"
            "  last_change datetime)");

    execute("create table if not exists stats_snap"
            " (name varchar(16),"
            "  value varchar(24),"
            "  last_change datetime)");
}

void SqliteStrategy::initMetaStatements(void) {
    const char *ins_query = "insert into vbucket_states"
        " (vbid, vb_version, state, last_change) values (?, ?, ?, current_timestamp)";
    ins_vb_stmt = new PreparedStatement(db, ins_query);

    const char *del_query = "delete from vbucket_states";
    clear_vb_stmt = new PreparedStatement(db, del_query);

    const char *sel_query = "select vbid, vb_version, state from vbucket_states";
    sel_vb_stmt = new PreparedStatement(db, sel_query);

    const char *clear_stats_query = "delete from stats_snap";
    clear_stats_stmt = new PreparedStatement(db, clear_stats_query);

    const char *ins_stat_query = "insert into stats_snap "
        "(name, value, last_change) values (?, ?, current_timestamp)";
    ins_stat_stmt = new PreparedStatement(db, ins_stat_query);
}

void SqliteStrategy::checkSchemaVersion(void) {
    assert(db);
    PreparedStatement uv_get(db, "PRAGMA user_version");
    uv_get.fetch();
    schema_version = uv_get.column_int(0);

    PreparedStatement st(db, "select name from sqlite_master where name='vbucket_states'");
    if (schema_version == 0 && !st.fetch()) {
        std::stringstream ss;
        ss << "PRAGMA user_version=" << CURRENT_SCHEMA_VERSION;
        execute(ss.str().c_str());
        schema_version = CURRENT_SCHEMA_VERSION;
    }

    if (schema_version < CURRENT_SCHEMA_VERSION) {
        std::stringstream ss;
        ss << "Schema version " << schema_version << " is not supported anymore.\n"
           << "Run the script to upgrade the schema to version "
           << CURRENT_SCHEMA_VERSION;
        close();
        throw std::runtime_error(ss.str().c_str());
    }
}

sqlite3 *SqliteStrategy::open(void) {
    if(!db) {
        int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
            | SQLITE_OPEN_PRIVATECACHE;

        if(sqlite3_open_v2(filename, &db, flags, NULL) !=  SQLITE_OK) {
            throw std::runtime_error("Error initializing sqlite3");
        }

        if(sqlite3_extended_result_codes(db, 1) != SQLITE_OK) {
            throw std::runtime_error("Error enabling extended RCs");
        }

        initDB();
        checkSchemaVersion();

        initMetaTables();
        initTables();
        initMetaStatements();
        initStatements();
        doFile(postInitFile);
    }
    return db;
}

void SqliteStrategy::close(void) {
    if(db) {
        destroyStatements();
        sqlite3_close(db);
        db = NULL;
    }
}

void SqliteStrategy::doFile(const char * const fn) {
    if (fn) {
        SqliteEvaluator eval(db);
        getLogger()->log(EXTENSION_LOG_INFO, NULL,
                         "Running db script: %s\n", fn);
        eval.eval(fn);
    }
}

void SqliteStrategy::execute(const char * const query) {
    PreparedStatement st(db, query);
    st.execute();
}

// ----------------------------------------------------------------------

void SingleTableSqliteStrategy::destroyStatements() {
    while (!statements.empty()) {
        Statements *st = statements.back();
        delete st;
        statements.pop_back();
    }
}

void SingleTableSqliteStrategy::initTables(void) {
    assert(db);
    execute("create table if not exists kv"
            " (vbucket integer,"
            "  vb_version integer,"
            "  k varchar(250), "
            "  flags integer,"
            "  exptime integer,"
            "  cas integer,"
            "  v text)");
}

void SingleTableSqliteStrategy::initStatements(void) {
    assert(db);
    Statements *st = new Statements(db, "kv");
    statements.push_back(st);
}

void SingleTableSqliteStrategy::destroyTables(void) {
    execute("drop table if exists kv");
}

//
// ----------------------------------------------------------------------
// Multi DB strategy
// ----------------------------------------------------------------------
//

void MultiDBSingleTableSqliteStrategy::initDB() {
    char buf[1024];
    PathExpander p(filename);

    for (int i = 0; i < numTables; i++) {
        std::string shardname(p.expand(shardpattern, i));
        snprintf(buf, sizeof(buf), "attach database \"%s\" as kv_%d",
                 shardname.c_str(), i);
        execute(buf);
    }
    doFile(initFile);
}

void MultiDBSingleTableSqliteStrategy::initTables() {
    char buf[1024];
    PathExpander p(filename);

    for (int i = 0; i < numTables; i++) {
        snprintf(buf, sizeof(buf),
                 "create table if not exists kv_%d.kv"
                 " (vbucket integer,"
                 "  vb_version integer,"
                 "  k varchar(250),"
                 "  flags integer,"
                 "  exptime integer,"
                 "  cas integer,"
                 "  v text)", i);
        execute(buf);
    }
}

void MultiDBSingleTableSqliteStrategy::initStatements() {
    char buf[64];
    for (int i = 0; i < numTables; i++) {
        snprintf(buf, sizeof(buf), "kv_%d.kv", i);
        statements.push_back(new Statements(db, std::string(buf)));
    }
}

void MultiDBSingleTableSqliteStrategy::destroyTables() {
    char buf[1024];
    for (int i = 0; i < numTables; i++) {
        snprintf(buf, sizeof(buf), "drop table if exists kv_%d.kv", i);
        execute(buf);
    }
}

//
// ----------------------------------------------------------------------
// Table per vbucket
// ----------------------------------------------------------------------
//

void MultiTableSqliteStrategy::initTables() {
    char buf[1024];

    for (size_t i = 0; i < nvbuckets; ++i) {
        snprintf(buf, sizeof(buf),
                 "create table if not exists kv_%d"
                 " (vbucket integer,"
                 "  vb_version integer,"
                 "  k varchar(250),"
                 "  flags integer,"
                 "  exptime integer,"
                 "  cas integer,"
                 "  v text)", static_cast<int>(i));
        execute(buf);
    }
}

void MultiTableSqliteStrategy::initStatements() {
    char buf[64];
    for (size_t i = 0; i < nvbuckets; ++i) {
        snprintf(buf, sizeof(buf), "kv_%d", static_cast<int>(i));
        statements.push_back(new Statements(db, std::string(buf)));
    }
}

void MultiTableSqliteStrategy::destroyTables() {
    char buf[1024];
    for (size_t i = 0; i < nvbuckets; ++i) {
        snprintf(buf, sizeof(buf), "drop table if exists kv_%d",
                 static_cast<int>(i));
        execute(buf);
    }
}

void MultiTableSqliteStrategy::destroyStatements() {
    while (!statements.empty()) {
        Statements *st = statements.back();
        delete st;
        statements.pop_back();
    }
}

//
// ----------------------------------------------------------------------
// Multiple Shards, Table Per Vbucket
// ----------------------------------------------------------------------
//

Statements *ShardedMultiTableSqliteStrategy::getStatements(uint16_t vbid, uint16_t vbver,
                                                           const std::string &key) {
    (void)vbver;
    size_t shard(getDbShardIdForKey(key));
    assert(static_cast<size_t>(shard) < statementsPerShard.size());
    assert(static_cast<size_t>(vbid) < statementsPerShard[shard].size());
    return statementsPerShard[shard][vbid];
}

void ShardedMultiTableSqliteStrategy::destroyStatements() {
    MultiTableSqliteStrategy::destroyStatements();
    statementsPerShard.clear();
}

void ShardedMultiTableSqliteStrategy::destroyTables() {
    char buf[1024];
    for (size_t i = 0; i < shardCount; ++i) {
        for (size_t j = 0; j < nvbuckets; ++j) {
            snprintf(buf, sizeof(buf), "drop table if exists kv_%d.kv_%d",
                     static_cast<int>(i), static_cast<int>(j));
            execute(buf);
        }
    }
}

std::vector<PreparedStatement*> ShardedMultiTableSqliteStrategy::getVBLoader(uint16_t vb) {
    std::vector<PreparedStatement*> rv;
    for (size_t i = 0; i < shardCount; ++i) {
        std::vector<Statements*> st = statementsPerShard[i];
        assert(static_cast<size_t>(vb) < st.size());
        rv.push_back(st.at(vb)->all());
    }
    return rv;
}

void ShardedMultiTableSqliteStrategy::initDB() {
    char buf[1024];
    PathExpander p(filename);

    for (size_t i = 0; i < shardCount; ++i) {
        std::string shardname(p.expand(shardpattern, static_cast<int>(i)));
        snprintf(buf, sizeof(buf), "attach database \"%s\" as kv_%d",
                 shardname.c_str(), static_cast<int>(i));
        execute(buf);
    }
    doFile(initFile);
}

void ShardedMultiTableSqliteStrategy::initTables() {
    char buf[1024];

    for (size_t i = 0; i < shardCount; ++i) {
        for (size_t j = 0; j < nvbuckets; ++j) {
            snprintf(buf, sizeof(buf),
                     "create table if not exists kv_%d.kv_%d"
                     " (vbucket integer,"
                     "  vb_version integer,"
                     "  k varchar(250),"
                     "  flags integer,"
                     "  exptime integer,"
                     "  cas integer,"
                     "  v text)",
                     static_cast<int>(i), static_cast<int>(j));
            execute(buf);
        }
    }
}

void ShardedMultiTableSqliteStrategy::initStatements() {
    char buf[64];
    statementsPerShard.resize(shardCount);
    for (size_t i = 0; i < shardCount; ++i) {
        statementsPerShard[i].resize(nvbuckets);
        for (size_t j = 0; j < nvbuckets; ++j) {
            snprintf(buf, sizeof(buf), "kv_%d.kv_%d",
                     static_cast<int>(i), static_cast<int>(j));
            Statements *s = new Statements(db, std::string(buf));
            statements.push_back(s);
            statementsPerShard[i][j] = s;
        }
    }
}

//
// ----------------------------------------------------------------------
// Sharded by VBucket
// ----------------------------------------------------------------------
//

void ShardedByVBucketSqliteStrategy::destroyTables() {
    char buf[1024];
    for (size_t j = 0; j < nvbuckets; ++j) {
        snprintf(buf, sizeof(buf), "drop table if exists kv_%d.kv_%d",
                 static_cast<int>(getShardForVBucket(static_cast<uint16_t>(j))),
                 static_cast<int>(j));
        execute(buf);
    }
}

void ShardedByVBucketSqliteStrategy::initDB() {
    char buf[1024];
    PathExpander p(filename);

    for (size_t i = 0; i < shardCount; ++i) {
        std::string shardname(p.expand(shardpattern, static_cast<int>(i)));
        snprintf(buf, sizeof(buf), "attach database \"%s\" as kv_%d",
                 shardname.c_str(), static_cast<int>(i));
        execute(buf);
    }
    doFile(initFile);
}

void ShardedByVBucketSqliteStrategy::initTables() {
    char buf[1024];

    for (size_t j = 0; j < nvbuckets; ++j) {
        snprintf(buf, sizeof(buf),
                 "create table if not exists kv_%d.kv_%d"
                 " (vbucket integer,"
                 "  vb_version integer,"
                 "  k varchar(250),"
                 "  flags integer,"
                 "  exptime integer,"
                 "  cas integer,"
                 "  v text)",
                 static_cast<int>(getShardForVBucket(static_cast<uint16_t>(j))),
                 static_cast<int>(j));
        execute(buf);
    }
}

void ShardedByVBucketSqliteStrategy::initStatements() {
    char buf[64];
    statements.resize(nvbuckets);
    for (size_t j = 0; j < nvbuckets; ++j) {
        snprintf(buf, sizeof(buf), "kv_%d.kv_%d",
                 static_cast<int>(getShardForVBucket(static_cast<uint16_t>(j))),
                 static_cast<int>(j));
        statements[j] = new Statements(db, std::string(buf));
    }
}
<commit_msg>MB-3244 - Create all the tables within a single transaction.<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include "config.h"
#include <cassert>
#include <iostream>
#include <stdexcept>

#include "sqlite-strategies.hh"
#include "sqlite-eval.hh"
#include "ep.hh"
#include "pathexpand.hh"

static const int CURRENT_SCHEMA_VERSION(2);

SqliteStrategy::SqliteStrategy(const char * const fn,
                               const char * const finit,
                               const char * const pfinit,
                               size_t shards) : db(NULL),
    filename(fn),
    initFile(finit),
    postInitFile(pfinit),
    shardCount(shards),
    ins_vb_stmt(NULL), clear_vb_stmt(NULL), sel_vb_stmt(NULL),
    clear_stats_stmt(NULL), ins_stat_stmt(NULL),
    schema_version(0) {

    assert(filename);
    assert(shardCount > 0);
}

SqliteStrategy::~SqliteStrategy() {
    destroyMetaStatements();
    close();
}

void SqliteStrategy::destroyMetaStatements() {
    delete ins_vb_stmt;
    delete clear_vb_stmt;
    delete sel_vb_stmt;
    delete clear_stats_stmt;
    delete ins_stat_stmt;
}

void SqliteStrategy::initMetaTables() {
    assert(db);
    execute("create table if not exists vbucket_states"
            " (vbid integer primary key on conflict replace,"
            "  vb_version interger,"
            "  state varchar(16),"
            "  last_change datetime)");

    execute("create table if not exists stats_snap"
            " (name varchar(16),"
            "  value varchar(24),"
            "  last_change datetime)");
}

void SqliteStrategy::initMetaStatements(void) {
    const char *ins_query = "insert into vbucket_states"
        " (vbid, vb_version, state, last_change) values (?, ?, ?, current_timestamp)";
    ins_vb_stmt = new PreparedStatement(db, ins_query);

    const char *del_query = "delete from vbucket_states";
    clear_vb_stmt = new PreparedStatement(db, del_query);

    const char *sel_query = "select vbid, vb_version, state from vbucket_states";
    sel_vb_stmt = new PreparedStatement(db, sel_query);

    const char *clear_stats_query = "delete from stats_snap";
    clear_stats_stmt = new PreparedStatement(db, clear_stats_query);

    const char *ins_stat_query = "insert into stats_snap "
        "(name, value, last_change) values (?, ?, current_timestamp)";
    ins_stat_stmt = new PreparedStatement(db, ins_stat_query);
}

void SqliteStrategy::checkSchemaVersion(void) {
    assert(db);
    PreparedStatement uv_get(db, "PRAGMA user_version");
    uv_get.fetch();
    schema_version = uv_get.column_int(0);

    PreparedStatement st(db, "select name from sqlite_master where name='vbucket_states'");
    if (schema_version == 0 && !st.fetch()) {
        std::stringstream ss;
        ss << "PRAGMA user_version=" << CURRENT_SCHEMA_VERSION;
        execute(ss.str().c_str());
        schema_version = CURRENT_SCHEMA_VERSION;
    }

    if (schema_version < CURRENT_SCHEMA_VERSION) {
        std::stringstream ss;
        ss << "Schema version " << schema_version << " is not supported anymore.\n"
           << "Run the script to upgrade the schema to version "
           << CURRENT_SCHEMA_VERSION;
        close();
        throw std::runtime_error(ss.str().c_str());
    }
}

sqlite3 *SqliteStrategy::open(void) {
    if(!db) {
        int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
            | SQLITE_OPEN_PRIVATECACHE;

        if(sqlite3_open_v2(filename, &db, flags, NULL) !=  SQLITE_OK) {
            throw std::runtime_error("Error initializing sqlite3");
        }

        if(sqlite3_extended_result_codes(db, 1) != SQLITE_OK) {
            throw std::runtime_error("Error enabling extended RCs");
        }

        initDB();
        checkSchemaVersion();

        execute("begin immediate");
        initMetaTables();
        initTables();
        execute("commit");
        initMetaStatements();
        initStatements();
        doFile(postInitFile);
    }
    return db;
}

void SqliteStrategy::close(void) {
    if(db) {
        destroyStatements();
        sqlite3_close(db);
        db = NULL;
    }
}

void SqliteStrategy::doFile(const char * const fn) {
    if (fn) {
        SqliteEvaluator eval(db);
        getLogger()->log(EXTENSION_LOG_INFO, NULL,
                         "Running db script: %s\n", fn);
        eval.eval(fn);
    }
}

void SqliteStrategy::execute(const char * const query) {
    PreparedStatement st(db, query);
    st.execute();
}

// ----------------------------------------------------------------------

void SingleTableSqliteStrategy::destroyStatements() {
    while (!statements.empty()) {
        Statements *st = statements.back();
        delete st;
        statements.pop_back();
    }
}

void SingleTableSqliteStrategy::initTables(void) {
    assert(db);
    execute("create table if not exists kv"
            " (vbucket integer,"
            "  vb_version integer,"
            "  k varchar(250), "
            "  flags integer,"
            "  exptime integer,"
            "  cas integer,"
            "  v text)");
}

void SingleTableSqliteStrategy::initStatements(void) {
    assert(db);
    Statements *st = new Statements(db, "kv");
    statements.push_back(st);
}

void SingleTableSqliteStrategy::destroyTables(void) {
    execute("drop table if exists kv");
}

//
// ----------------------------------------------------------------------
// Multi DB strategy
// ----------------------------------------------------------------------
//

void MultiDBSingleTableSqliteStrategy::initDB() {
    char buf[1024];
    PathExpander p(filename);

    for (int i = 0; i < numTables; i++) {
        std::string shardname(p.expand(shardpattern, i));
        snprintf(buf, sizeof(buf), "attach database \"%s\" as kv_%d",
                 shardname.c_str(), i);
        execute(buf);
    }
    doFile(initFile);
}

void MultiDBSingleTableSqliteStrategy::initTables() {
    char buf[1024];
    PathExpander p(filename);

    for (int i = 0; i < numTables; i++) {
        snprintf(buf, sizeof(buf),
                 "create table if not exists kv_%d.kv"
                 " (vbucket integer,"
                 "  vb_version integer,"
                 "  k varchar(250),"
                 "  flags integer,"
                 "  exptime integer,"
                 "  cas integer,"
                 "  v text)", i);
        execute(buf);
    }
}

void MultiDBSingleTableSqliteStrategy::initStatements() {
    char buf[64];
    for (int i = 0; i < numTables; i++) {
        snprintf(buf, sizeof(buf), "kv_%d.kv", i);
        statements.push_back(new Statements(db, std::string(buf)));
    }
}

void MultiDBSingleTableSqliteStrategy::destroyTables() {
    char buf[1024];
    for (int i = 0; i < numTables; i++) {
        snprintf(buf, sizeof(buf), "drop table if exists kv_%d.kv", i);
        execute(buf);
    }
}

//
// ----------------------------------------------------------------------
// Table per vbucket
// ----------------------------------------------------------------------
//

void MultiTableSqliteStrategy::initTables() {
    char buf[1024];

    for (size_t i = 0; i < nvbuckets; ++i) {
        snprintf(buf, sizeof(buf),
                 "create table if not exists kv_%d"
                 " (vbucket integer,"
                 "  vb_version integer,"
                 "  k varchar(250),"
                 "  flags integer,"
                 "  exptime integer,"
                 "  cas integer,"
                 "  v text)", static_cast<int>(i));
        execute(buf);
    }
}

void MultiTableSqliteStrategy::initStatements() {
    char buf[64];
    for (size_t i = 0; i < nvbuckets; ++i) {
        snprintf(buf, sizeof(buf), "kv_%d", static_cast<int>(i));
        statements.push_back(new Statements(db, std::string(buf)));
    }
}

void MultiTableSqliteStrategy::destroyTables() {
    char buf[1024];
    for (size_t i = 0; i < nvbuckets; ++i) {
        snprintf(buf, sizeof(buf), "drop table if exists kv_%d",
                 static_cast<int>(i));
        execute(buf);
    }
}

void MultiTableSqliteStrategy::destroyStatements() {
    while (!statements.empty()) {
        Statements *st = statements.back();
        delete st;
        statements.pop_back();
    }
}

//
// ----------------------------------------------------------------------
// Multiple Shards, Table Per Vbucket
// ----------------------------------------------------------------------
//

Statements *ShardedMultiTableSqliteStrategy::getStatements(uint16_t vbid, uint16_t vbver,
                                                           const std::string &key) {
    (void)vbver;
    size_t shard(getDbShardIdForKey(key));
    assert(static_cast<size_t>(shard) < statementsPerShard.size());
    assert(static_cast<size_t>(vbid) < statementsPerShard[shard].size());
    return statementsPerShard[shard][vbid];
}

void ShardedMultiTableSqliteStrategy::destroyStatements() {
    MultiTableSqliteStrategy::destroyStatements();
    statementsPerShard.clear();
}

void ShardedMultiTableSqliteStrategy::destroyTables() {
    char buf[1024];
    for (size_t i = 0; i < shardCount; ++i) {
        for (size_t j = 0; j < nvbuckets; ++j) {
            snprintf(buf, sizeof(buf), "drop table if exists kv_%d.kv_%d",
                     static_cast<int>(i), static_cast<int>(j));
            execute(buf);
        }
    }
}

std::vector<PreparedStatement*> ShardedMultiTableSqliteStrategy::getVBLoader(uint16_t vb) {
    std::vector<PreparedStatement*> rv;
    for (size_t i = 0; i < shardCount; ++i) {
        std::vector<Statements*> st = statementsPerShard[i];
        assert(static_cast<size_t>(vb) < st.size());
        rv.push_back(st.at(vb)->all());
    }
    return rv;
}

void ShardedMultiTableSqliteStrategy::initDB() {
    char buf[1024];
    PathExpander p(filename);

    for (size_t i = 0; i < shardCount; ++i) {
        std::string shardname(p.expand(shardpattern, static_cast<int>(i)));
        snprintf(buf, sizeof(buf), "attach database \"%s\" as kv_%d",
                 shardname.c_str(), static_cast<int>(i));
        execute(buf);
    }
    doFile(initFile);
}

void ShardedMultiTableSqliteStrategy::initTables() {
    char buf[1024];

    for (size_t i = 0; i < shardCount; ++i) {
        for (size_t j = 0; j < nvbuckets; ++j) {
            snprintf(buf, sizeof(buf),
                     "create table if not exists kv_%d.kv_%d"
                     " (vbucket integer,"
                     "  vb_version integer,"
                     "  k varchar(250),"
                     "  flags integer,"
                     "  exptime integer,"
                     "  cas integer,"
                     "  v text)",
                     static_cast<int>(i), static_cast<int>(j));
            execute(buf);
        }
    }
}

void ShardedMultiTableSqliteStrategy::initStatements() {
    char buf[64];
    statementsPerShard.resize(shardCount);
    for (size_t i = 0; i < shardCount; ++i) {
        statementsPerShard[i].resize(nvbuckets);
        for (size_t j = 0; j < nvbuckets; ++j) {
            snprintf(buf, sizeof(buf), "kv_%d.kv_%d",
                     static_cast<int>(i), static_cast<int>(j));
            Statements *s = new Statements(db, std::string(buf));
            statements.push_back(s);
            statementsPerShard[i][j] = s;
        }
    }
}

//
// ----------------------------------------------------------------------
// Sharded by VBucket
// ----------------------------------------------------------------------
//

void ShardedByVBucketSqliteStrategy::destroyTables() {
    char buf[1024];
    for (size_t j = 0; j < nvbuckets; ++j) {
        snprintf(buf, sizeof(buf), "drop table if exists kv_%d.kv_%d",
                 static_cast<int>(getShardForVBucket(static_cast<uint16_t>(j))),
                 static_cast<int>(j));
        execute(buf);
    }
}

void ShardedByVBucketSqliteStrategy::initDB() {
    char buf[1024];
    PathExpander p(filename);

    for (size_t i = 0; i < shardCount; ++i) {
        std::string shardname(p.expand(shardpattern, static_cast<int>(i)));
        snprintf(buf, sizeof(buf), "attach database \"%s\" as kv_%d",
                 shardname.c_str(), static_cast<int>(i));
        execute(buf);
    }
    doFile(initFile);
}

void ShardedByVBucketSqliteStrategy::initTables() {
    char buf[1024];

    for (size_t j = 0; j < nvbuckets; ++j) {
        snprintf(buf, sizeof(buf),
                 "create table if not exists kv_%d.kv_%d"
                 " (vbucket integer,"
                 "  vb_version integer,"
                 "  k varchar(250),"
                 "  flags integer,"
                 "  exptime integer,"
                 "  cas integer,"
                 "  v text)",
                 static_cast<int>(getShardForVBucket(static_cast<uint16_t>(j))),
                 static_cast<int>(j));
        execute(buf);
    }
}

void ShardedByVBucketSqliteStrategy::initStatements() {
    char buf[64];
    statements.resize(nvbuckets);
    for (size_t j = 0; j < nvbuckets; ++j) {
        snprintf(buf, sizeof(buf), "kv_%d.kv_%d",
                 static_cast<int>(getShardForVBucket(static_cast<uint16_t>(j))),
                 static_cast<int>(j));
        statements[j] = new Statements(db, std::string(buf));
    }
}
<|endoftext|>


<commit_before>#ifndef MS_RTC_NACK_GENERATOR_HPP
#define MS_RTC_NACK_GENERATOR_HPP

#include "common.hpp"
#include "RTC/RtpPacket.hpp"
#include "RTC/SeqManager.hpp"
#include "handles/Timer.hpp"
#include <map>
#include <vector>

namespace RTC
{
	class NackGenerator : public Timer::Listener
	{
	public:
		class Listener
		{
		public:
			virtual void OnNackGeneratorNackRequired(const std::vector<uint16_t>& seqNumbers) = 0;
			virtual void OnNackGeneratorKeyFrameRequired()                                    = 0;
		};

	private:
		struct NackInfo
		{
			NackInfo(){};
			explicit NackInfo(uint16_t seq, uint16_t sendAtSeq);

			uint16_t seq{ 0 };
			uint16_t sendAtSeq{ 0 };
			uint64_t sentAtTime{ 0 };
			uint8_t retries{ 0 };
		};

		enum class NackFilter
		{
			SEQ,
			TIME
		};

	public:
		explicit NackGenerator(Listener* listener);
		~NackGenerator() override;

		bool ReceivePacket(RTC::RtpPacket* packet);

	private:
		void AddPacketsToNackList(uint16_t seqStart, uint16_t seqEnd);
		void RemoveFromNackListOlderThan(RTC::RtpPacket* packet);
		std::vector<uint16_t> GetNackBatch(NackFilter filter);
		void MayRunTimer() const;

		/* Pure virtual methods inherited from Timer::Listener. */
	public:
		void OnTimer(Timer* timer) override;

	private:
		// Passed by argument.
		Listener* listener{ nullptr };
		// Allocated by this.
		Timer* timer{ nullptr };
		// Others.
		std::map<uint16_t, NackInfo, SeqManager<uint16_t>::SeqLowerThan> nackList;
		bool started{ false };
		uint16_t lastSeq{ 0 }; // Seq number of last valid packet.
		uint32_t rtt{ 0 };     // Round trip time (ms).
	};

	// Inline instance methods.

	inline NackGenerator::NackInfo::NackInfo(uint16_t seq, uint16_t sendAtSeq)
	  : seq(seq), sendAtSeq(sendAtSeq)
	{
	}
} // namespace RTC

#endif
<commit_msg>NackGenerator: add GetNackListLength() method<commit_after>#ifndef MS_RTC_NACK_GENERATOR_HPP
#define MS_RTC_NACK_GENERATOR_HPP

#include "common.hpp"
#include "RTC/RtpPacket.hpp"
#include "RTC/SeqManager.hpp"
#include "handles/Timer.hpp"
#include <map>
#include <vector>

namespace RTC
{
	class NackGenerator : public Timer::Listener
	{
	public:
		class Listener
		{
		public:
			virtual void OnNackGeneratorNackRequired(const std::vector<uint16_t>& seqNumbers) = 0;
			virtual void OnNackGeneratorKeyFrameRequired()                                    = 0;
		};

	private:
		struct NackInfo
		{
			NackInfo(){};
			explicit NackInfo(uint16_t seq, uint16_t sendAtSeq);

			uint16_t seq{ 0 };
			uint16_t sendAtSeq{ 0 };
			uint64_t sentAtTime{ 0 };
			uint8_t retries{ 0 };
		};

		enum class NackFilter
		{
			SEQ,
			TIME
		};

	public:
		explicit NackGenerator(Listener* listener);
		~NackGenerator() override;

		bool ReceivePacket(RTC::RtpPacket* packet);
		size_t GetNackListLength() const;

	private:
		void AddPacketsToNackList(uint16_t seqStart, uint16_t seqEnd);
		void RemoveFromNackListOlderThan(RTC::RtpPacket* packet);
		std::vector<uint16_t> GetNackBatch(NackFilter filter);
		void MayRunTimer() const;

		/* Pure virtual methods inherited from Timer::Listener. */
	public:
		void OnTimer(Timer* timer) override;

	private:
		// Passed by argument.
		Listener* listener{ nullptr };
		// Allocated by this.
		Timer* timer{ nullptr };
		// Others.
		std::map<uint16_t, NackInfo, SeqManager<uint16_t>::SeqLowerThan> nackList;
		bool started{ false };
		uint16_t lastSeq{ 0 }; // Seq number of last valid packet.
		uint32_t rtt{ 0 };     // Round trip time (ms).
	};

	// Inline instance methods.

	inline NackGenerator::NackInfo::NackInfo(uint16_t seq, uint16_t sendAtSeq)
	  : seq(seq), sendAtSeq(sendAtSeq)
	{
	}

	inline size_t NackGenerator::GetNackListLength() const
	{
		return this->nackList.size();
	}
} // namespace RTC

#endif
<|endoftext|>


<commit_before>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  You may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * Copyright 2014 Cloudius Systems
 */

#include <seastar/core/memory.hh>
#include <seastar/core/timer.hh>
#include <random>
#include <cmath>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cassert>
#include <memory>
#include <chrono>
#include <boost/program_options.hpp>

using namespace seastar;

template <size_t N>
void test_aligned_allocator() {
#ifdef __cpp_aligned_new
    using aptr = std::unique_ptr<char[]>;
    std::vector<aptr> v;
    for (unsigned i = 0; i < 1000; ++i) {
        aptr p(new (std::align_val_t(64)) char[N]);
        assert(reinterpret_cast<uintptr_t>(p.get()) % 64 == 0);
        v.push_back(std::move(p));
    }
#endif
}

struct allocation {
    size_t n;
    std::unique_ptr<char[]> data;
    char poison;
    allocation(size_t n, char poison) : n(n), data(new char[n]), poison(poison) {
        std::fill_n(data.get(), n, poison);
    }
    ~allocation() {
        verify();
    }
    allocation(allocation&& x) noexcept = default;
    void verify() {
        if (data) {
            assert(std::find_if(data.get(), data.get() + n, [this] (char c) {
                return c != poison;
            }) == data.get() + n);
        }
    }
    allocation& operator=(allocation&& x) {
        verify();
        if (this != &x) {
            data = std::move(x.data);
            n = x.n;
            poison = x.poison;
        }
        return *this;
    }
};

#ifdef __cpp_aligned_new

template <size_t N>
struct alignas(N) cpp17_allocation final {
    char v;
};

struct test17 {
    struct handle {
        const test17* d;
        void* p;
        handle(const test17* d, void* p) : d(d), p(p) {}
        handle(const handle&) = delete;
        handle(handle&& x) noexcept : d(std::exchange(x.d, nullptr)), p(std::exchange(x.p, nullptr)) {}
        handle& operator=(const handle&) = delete;
        handle& operator=(handle&& x) noexcept {
            std::swap(d, x.d);
            std::swap(p, x.p);
            return *this;
        }
        ~handle() {
            if (d) {
                d->free(p);
            }
        }
    };
    virtual ~test17() {}
    virtual handle alloc() const = 0;
    virtual void free(void* ptr) const = 0;
};

template <size_t N>
struct test17_concrete : test17 {
    using value_type = cpp17_allocation<N>;
    static_assert(sizeof(value_type) == N, "language does not guarantee size >= align");
    virtual handle alloc() const override {
        auto ptr = new value_type();
        assert((reinterpret_cast<uintptr_t>(ptr) & (N - 1)) == 0);
        return handle{this, ptr};
    }
    virtual void free(void* ptr) const override {
        delete static_cast<value_type*>(ptr);
    }
};

void test_cpp17_aligned_allocator() {
    std::vector<std::unique_ptr<test17>> tv;
    tv.push_back(std::make_unique<test17_concrete<1>>());
    tv.push_back(std::make_unique<test17_concrete<2>>());
    tv.push_back(std::make_unique<test17_concrete<4>>());
    tv.push_back(std::make_unique<test17_concrete<8>>());
    tv.push_back(std::make_unique<test17_concrete<16>>());
    tv.push_back(std::make_unique<test17_concrete<64>>());
    tv.push_back(std::make_unique<test17_concrete<128>>());
    tv.push_back(std::make_unique<test17_concrete<2048>>());
    tv.push_back(std::make_unique<test17_concrete<4096>>());
    tv.push_back(std::make_unique<test17_concrete<4096*16>>());
    tv.push_back(std::make_unique<test17_concrete<4096*256>>());

    std::default_random_engine random_engine;
    std::uniform_int_distribution<> type_dist(0, 1);
    std::uniform_int_distribution<size_t> size_dist(0, tv.size() - 1);
    std::uniform_real_distribution<> which_dist(0, 1);

    std::vector<test17::handle> allocs;
    for (unsigned i = 0; i < 10000; ++i) {
        auto type = type_dist(random_engine);
        switch (type) {
        case 0: {
            size_t sz_idx = size_dist(random_engine);
            allocs.push_back(tv[sz_idx]->alloc());
            break;
        }
        case 1:
            if (!allocs.empty()) {
                size_t idx = which_dist(random_engine) * allocs.size();
                std::swap(allocs[idx], allocs.back());
                allocs.pop_back();
            }
            break;
        }
    }
}

#else

void test_cpp17_aligned_allocator() {
}

#endif

int main(int ac, char** av) {
    namespace bpo = boost::program_options;
    bpo::options_description opts("Allowed options");
    opts.add_options()
            ("help", "produce this help message")
            ("iterations", bpo::value<unsigned>(), "run s specified number of iterations")
            ("time", bpo::value<float>()->default_value(5.0), "run for a specified amount of time, in seconds")
            ;
    bpo::variables_map vm;
    bpo::store(bpo::parse_command_line(ac, av, opts), vm);
    bpo::notify(vm);
    test_aligned_allocator<1>();
    test_aligned_allocator<4>();
    test_aligned_allocator<80>();
    test_cpp17_aligned_allocator();
    std::default_random_engine random_engine;
    std::exponential_distribution<> distr(0.2);
    std::uniform_int_distribution<> type(0, 1);
    std::uniform_int_distribution<char> poison(-128, 127);
    std::uniform_real_distribution<> which(0, 1);
    std::vector<allocation> allocations;
    auto iteration = [&] {
        auto typ = type(random_engine);
        switch (typ) {
        case 0: {
            auto n = std::min<size_t>(std::exp(distr(random_engine)), 1 << 25);
            try {
                allocations.emplace_back(n, poison(random_engine));
            } catch (std::bad_alloc&) {

            }
            break;
        }
        case 1: {
            if (allocations.empty()) {
                break;
            }
            size_t i = which(random_engine) * allocations.size();
            allocations[i] = std::move(allocations.back());
            allocations.pop_back();
            break;
        }
        }
    };
    if (vm.count("help")) {
        std::cout << opts << "\n";
        return 1;
    }
    if (vm.count("iterations")) {
        auto iterations = vm["iterations"].as<unsigned>();
        for (unsigned i = 0; i < iterations; ++i) {
            iteration();
        }
    } else {
        auto time = vm["time"].as<float>();
        using clock = steady_clock_type;
        auto end = clock::now() + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1) * time);
        while (clock::now() < end) {
            for (unsigned i = 0; i < 1000; ++i) {
                iteration();
            }
        }
    }
    return 0;
}

<commit_msg>tests/allocator: drop test_aligned_allocator<commit_after>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  You may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * Copyright 2014 Cloudius Systems
 */

#include <seastar/core/memory.hh>
#include <seastar/core/timer.hh>
#include <random>
#include <cmath>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cassert>
#include <memory>
#include <chrono>
#include <boost/program_options.hpp>

using namespace seastar;

struct allocation {
    size_t n;
    std::unique_ptr<char[]> data;
    char poison;
    allocation(size_t n, char poison) : n(n), data(new char[n]), poison(poison) {
        std::fill_n(data.get(), n, poison);
    }
    ~allocation() {
        verify();
    }
    allocation(allocation&& x) noexcept = default;
    void verify() {
        if (data) {
            assert(std::find_if(data.get(), data.get() + n, [this] (char c) {
                return c != poison;
            }) == data.get() + n);
        }
    }
    allocation& operator=(allocation&& x) {
        verify();
        if (this != &x) {
            data = std::move(x.data);
            n = x.n;
            poison = x.poison;
        }
        return *this;
    }
};

#ifdef __cpp_aligned_new

template <size_t N>
struct alignas(N) cpp17_allocation final {
    char v;
};

struct test17 {
    struct handle {
        const test17* d;
        void* p;
        handle(const test17* d, void* p) : d(d), p(p) {}
        handle(const handle&) = delete;
        handle(handle&& x) noexcept : d(std::exchange(x.d, nullptr)), p(std::exchange(x.p, nullptr)) {}
        handle& operator=(const handle&) = delete;
        handle& operator=(handle&& x) noexcept {
            std::swap(d, x.d);
            std::swap(p, x.p);
            return *this;
        }
        ~handle() {
            if (d) {
                d->free(p);
            }
        }
    };
    virtual ~test17() {}
    virtual handle alloc() const = 0;
    virtual void free(void* ptr) const = 0;
};

template <size_t N>
struct test17_concrete : test17 {
    using value_type = cpp17_allocation<N>;
    static_assert(sizeof(value_type) == N, "language does not guarantee size >= align");
    virtual handle alloc() const override {
        auto ptr = new value_type();
        assert((reinterpret_cast<uintptr_t>(ptr) & (N - 1)) == 0);
        return handle{this, ptr};
    }
    virtual void free(void* ptr) const override {
        delete static_cast<value_type*>(ptr);
    }
};

void test_cpp17_aligned_allocator() {
    std::vector<std::unique_ptr<test17>> tv;
    tv.push_back(std::make_unique<test17_concrete<1>>());
    tv.push_back(std::make_unique<test17_concrete<2>>());
    tv.push_back(std::make_unique<test17_concrete<4>>());
    tv.push_back(std::make_unique<test17_concrete<8>>());
    tv.push_back(std::make_unique<test17_concrete<16>>());
    tv.push_back(std::make_unique<test17_concrete<64>>());
    tv.push_back(std::make_unique<test17_concrete<128>>());
    tv.push_back(std::make_unique<test17_concrete<2048>>());
    tv.push_back(std::make_unique<test17_concrete<4096>>());
    tv.push_back(std::make_unique<test17_concrete<4096*16>>());
    tv.push_back(std::make_unique<test17_concrete<4096*256>>());

    std::default_random_engine random_engine;
    std::uniform_int_distribution<> type_dist(0, 1);
    std::uniform_int_distribution<size_t> size_dist(0, tv.size() - 1);
    std::uniform_real_distribution<> which_dist(0, 1);

    std::vector<test17::handle> allocs;
    for (unsigned i = 0; i < 10000; ++i) {
        auto type = type_dist(random_engine);
        switch (type) {
        case 0: {
            size_t sz_idx = size_dist(random_engine);
            allocs.push_back(tv[sz_idx]->alloc());
            break;
        }
        case 1:
            if (!allocs.empty()) {
                size_t idx = which_dist(random_engine) * allocs.size();
                std::swap(allocs[idx], allocs.back());
                allocs.pop_back();
            }
            break;
        }
    }
}

#else

void test_cpp17_aligned_allocator() {
}

#endif

int main(int ac, char** av) {
    namespace bpo = boost::program_options;
    bpo::options_description opts("Allowed options");
    opts.add_options()
            ("help", "produce this help message")
            ("iterations", bpo::value<unsigned>(), "run s specified number of iterations")
            ("time", bpo::value<float>()->default_value(5.0), "run for a specified amount of time, in seconds")
            ;
    bpo::variables_map vm;
    bpo::store(bpo::parse_command_line(ac, av, opts), vm);
    bpo::notify(vm);
    test_cpp17_aligned_allocator();
    std::default_random_engine random_engine;
    std::exponential_distribution<> distr(0.2);
    std::uniform_int_distribution<> type(0, 1);
    std::uniform_int_distribution<char> poison(-128, 127);
    std::uniform_real_distribution<> which(0, 1);
    std::vector<allocation> allocations;
    auto iteration = [&] {
        auto typ = type(random_engine);
        switch (typ) {
        case 0: {
            auto n = std::min<size_t>(std::exp(distr(random_engine)), 1 << 25);
            try {
                allocations.emplace_back(n, poison(random_engine));
            } catch (std::bad_alloc&) {

            }
            break;
        }
        case 1: {
            if (allocations.empty()) {
                break;
            }
            size_t i = which(random_engine) * allocations.size();
            allocations[i] = std::move(allocations.back());
            allocations.pop_back();
            break;
        }
        }
    };
    if (vm.count("help")) {
        std::cout << opts << "\n";
        return 1;
    }
    if (vm.count("iterations")) {
        auto iterations = vm["iterations"].as<unsigned>();
        for (unsigned i = 0; i < iterations; ++i) {
            iteration();
        }
    } else {
        auto time = vm["time"].as<float>();
        using clock = steady_clock_type;
        auto end = clock::now() + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1) * time);
        while (clock::now() < end) {
            for (unsigned i = 0; i < 1000; ++i) {
                iteration();
            }
        }
    }
    return 0;
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
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 author 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.

*/

#if defined TORRENT_DEBUG || defined TORRENT_ASIO_DEBUGGING

#ifdef __APPLE__
#include <AvailabilityMacros.h>
#endif

#include <string>
#include <cstring>
#include <stdlib.h>

// uClibc++ doesn't have cxxabi.h
#if defined __GNUC__ && __GNUC__ >= 3 \
	&& !defined __UCLIBCXX_MAJOR__

#include <cxxabi.h>

std::string demangle(char const* name)
{
// in case this string comes
	// this is needed on linux
	char const* start = strchr(name, '(');
	if (start != 0)
	{
		++start;
	}
	else
	{
		// this is needed on macos x
		start = strstr(name, "0x");
		if (start != 0)
		{
			start = strchr(start, ' ');
			if (start != 0) ++start;
			else start = name;
		}
		else start = name;
	}

	char const* end = strchr(start, '+');
	if (end) while (*(end-1) == ' ') --end;

	std::string in;
	if (end == 0) in.assign(start);
	else in.assign(start, end);

	size_t len;
	int status;
	char* unmangled = ::abi::__cxa_demangle(in.c_str(), 0, &len, &status);
	if (unmangled == 0) return in;
	std::string ret(unmangled);
	free(unmangled);
	return ret;
}

#else
std::string demangle(char const* name) { return name; }
#endif

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include "libtorrent/version.hpp"

// execinfo.h is available in the MacOS X 10.5 SDK.
#if (defined __linux__ || (defined __APPLE__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050))
#include <execinfo.h>

void print_backtrace(char* out, int len)
{
	void* stack[50];
	int size = backtrace(stack, 50);
	char** symbols = backtrace_symbols(stack, size);

	for (int i = 1; i < size && len > 0; ++i)
	{
		int ret = snprintf(out, len, "%d: %s\n", i, demangle(symbols[i]).c_str());
		out += ret;
		len -= ret;
	}

	free(symbols);
}
#else

void print_backtrace(FILE* out, char const* label) {}

#endif

#if TORRENT_PRODUCTION_ASSERTS
char const* libtorrent_assert_log = "asserts.log";
#endif

void assert_fail(char const* expr, int line, char const* file, char const* function, char const* value)
{
#if TORRENT_PRODUCTION_ASSERTS
	FILE* out = fopen(libtorrent_assert_log, "a+");
	if (out == 0) out = stderr;
#else
	FILE* out = stderr;
#endif

	char stack[8192];
	print_backtrace(stack, sizeof(stack));

	fprintf(out, "assertion failed. Please file a bugreport at "
		"http://code.rasterbar.com/libtorrent/newticket\n"
		"Please include the following information:\n\n"
		"version: " LIBTORRENT_VERSION "\n"
		"%s\n"
		"file: '%s'\n"
		"line: %d\n"
		"function: %s\n"
		"expression: %s\n"
		"%s%s\n"
		"stack:\n"
		"%s\n"
		, LIBTORRENT_REVISION, file, line, function, expr
		, value ? value : "", value ? "\n" : ""
		, stack);

	// if production asserts are defined, don't abort, just print the error
#if TORRENT_PRODUCTION_ASSERTS
	if (out != stderr) fclose(out);
#else
 	// send SIGINT to the current process
 	// to break into the debugger
 	raise(SIGINT);
 	abort();
#endif
}

#else

void assert_fail(char const* expr, int line, char const* file, char const* function) {}

#endif

<commit_msg>fix windows build issue (one more try)<commit_after>/*

Copyright (c) 2007, Arvid Norberg
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 author 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.

*/

#if defined TORRENT_DEBUG || defined TORRENT_ASIO_DEBUGGING

#ifdef __APPLE__
#include <AvailabilityMacros.h>
#endif

#include <string>
#include <cstring>
#include <stdlib.h>

// uClibc++ doesn't have cxxabi.h
#if defined __GNUC__ && __GNUC__ >= 3 \
	&& !defined __UCLIBCXX_MAJOR__

#include <cxxabi.h>

std::string demangle(char const* name)
{
// in case this string comes
	// this is needed on linux
	char const* start = strchr(name, '(');
	if (start != 0)
	{
		++start;
	}
	else
	{
		// this is needed on macos x
		start = strstr(name, "0x");
		if (start != 0)
		{
			start = strchr(start, ' ');
			if (start != 0) ++start;
			else start = name;
		}
		else start = name;
	}

	char const* end = strchr(start, '+');
	if (end) while (*(end-1) == ' ') --end;

	std::string in;
	if (end == 0) in.assign(start);
	else in.assign(start, end);

	size_t len;
	int status;
	char* unmangled = ::abi::__cxa_demangle(in.c_str(), 0, &len, &status);
	if (unmangled == 0) return in;
	std::string ret(unmangled);
	free(unmangled);
	return ret;
}

#else
std::string demangle(char const* name) { return name; }
#endif

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include "libtorrent/version.hpp"

// execinfo.h is available in the MacOS X 10.5 SDK.
#if (defined __linux__ || (defined __APPLE__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050))
#include <execinfo.h>

void print_backtrace(char* out, int len)
{
	void* stack[50];
	int size = backtrace(stack, 50);
	char** symbols = backtrace_symbols(stack, size);

	for (int i = 1; i < size && len > 0; ++i)
	{
		int ret = snprintf(out, len, "%d: %s\n", i, demangle(symbols[i]).c_str());
		out += ret;
		len -= ret;
	}

	free(symbols);
}
#else

void print_backtrace(char* out, int len) {}

#endif

#if TORRENT_PRODUCTION_ASSERTS
char const* libtorrent_assert_log = "asserts.log";
#endif

void assert_fail(char const* expr, int line, char const* file, char const* function, char const* value)
{
#if TORRENT_PRODUCTION_ASSERTS
	FILE* out = fopen(libtorrent_assert_log, "a+");
	if (out == 0) out = stderr;
#else
	FILE* out = stderr;
#endif

	char stack[8192];
	print_backtrace(stack, sizeof(stack));

	fprintf(out, "assertion failed. Please file a bugreport at "
		"http://code.rasterbar.com/libtorrent/newticket\n"
		"Please include the following information:\n\n"
		"version: " LIBTORRENT_VERSION "\n"
		"%s\n"
		"file: '%s'\n"
		"line: %d\n"
		"function: %s\n"
		"expression: %s\n"
		"%s%s\n"
		"stack:\n"
		"%s\n"
		, LIBTORRENT_REVISION, file, line, function, expr
		, value ? value : "", value ? "\n" : ""
		, stack);

	// if production asserts are defined, don't abort, just print the error
#if TORRENT_PRODUCTION_ASSERTS
	if (out != stderr) fclose(out);
#else
 	// send SIGINT to the current process
 	// to break into the debugger
 	raise(SIGINT);
 	abort();
#endif
}

#else

void assert_fail(char const* expr, int line, char const* file, char const* function) {}

#endif

<|endoftext|>


<commit_before>/* nobleNote, a note taking application
 * Copyright (C) 2012 Christian Metscher <hakaishi@web.de>,
                      Fabian Deuchler <Taiko000@gmail.com>

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

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

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

 * nobleNote is licensed under the MIT, see `http://copyfree.org/licenses/mit/license.txt'.
 */

#include "backup.h"
#include <QDirIterator>
#include <QMessageBox>
#include <QSettings>
#include <QtConcurrentMap>
#include <QAbstractItemModel>

Backup::Backup(QWidget *parent): QDialog(parent){
     setupUi(this);

     setAttribute(Qt::WA_DeleteOnClose);

     splitter = new QSplitter(groupBox);
     gridLayout_2->addWidget(splitter);
     treeWidget = new QTreeWidget(splitter);
     treeWidget->setAlternatingRowColors(true);
     treeWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
     treeWidget->setSortingEnabled(true);
     treeWidget->sortByColumn(0,Qt::AscendingOrder);
     treeWidget->setHeaderLabel(tr("Backups of deleted notes"));
     treeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
     treeWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
     // TODO flickcharm here

     frame = new QFrame(splitter);
     gridLayout3 = new QGridLayout(frame);
     label = new QLabel(frame);
     label->setText(tr("Preview of the selected backup"));
     gridLayout3->addWidget(label, 0, 0, 1, 1);
     textEdit = new QTextEdit(frame);
     textEdit->setReadOnly(true);
     gridLayout3->addWidget(textEdit, 1, 0, 1, 1);

     getNotes(); //Searches for notes and backups. For the backups with no notes it will create the trees children.

     connect(treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(showPreview()));
     connect(deleteButton, SIGNAL(clicked(bool)), this, SLOT(deleteBackup()));
     connect(restoreButton, SIGNAL(clicked(bool)), this, SLOT(restoreBackup()));
}

void Backup::getNoteUuidList()
{
     QFutureIterator<QString> it(future1->future());
     while(it.hasNext())
       noteUuidList << it.next();
}

void Backup::getNotes()
{
     //get note files
     QDirIterator itFiles(QSettings().value("root_path").toString(),
                                              QDirIterator::Subdirectories);
     QStringList noteFiles;
     while(itFiles.hasNext()){
       QString filePath = itFiles.next();
       if(itFiles.fileInfo().isFile())
         noteFiles << filePath;
     }

     progressReceiver1 = new ProgressReceiver(this);
     progressDialog1 = new QProgressDialog(this);
     progressDialog1->setLabelText(QString(tr("Indexing notes...")));
     getUuid.p = progressReceiver1;
     future1 = new QFutureWatcher<QString>(this);
     future1->setFuture(QtConcurrent::mapped(noteFiles, getUuid));

     QObject::connect(progressReceiver1,SIGNAL(valueChanged(int)),progressDialog1, SLOT(setValue(int)));
     QObject::connect(future1, SIGNAL(finished()), this, SLOT(getNoteUuidList()));
     QObject::connect(future1, SIGNAL(finished()), this, SLOT(setupBackups()));
     QObject::connect(future1, SIGNAL(finished()), progressDialog1, SLOT(reset()));
     QObject::connect(progressDialog1, SIGNAL(canceled()), future1, SLOT(cancel()));

     progressDialog1->exec();
}

void Backup::setupBackups()
{
     //get backup uuids
     QDir backupDir(QSettings().value("backup_dir_path").toString());
     backupFiles = backupDir.entryInfoList(QDir::Files, QDir::Name);

     foreach(QString uuid, noteUuidList)
     {
          if(backupFiles.contains(QFileInfo(QSettings().value("backup_dir_path").toString()
                                  + "/" + uuid.mid(1,36))))
             backupFiles.removeOne(QFileInfo(QSettings().value("backup_dir_path").toString()
                                   + "/" + uuid.mid(1,36)));
          if(backupFiles.contains(QFileInfo(QSettings().value("backup_dir_path").toString()
                                  + "/" + uuid)))
             backupFiles.removeOne(QFileInfo(QSettings().value("backup_dir_path").toString()
                                   + "/" + uuid));
     }

     if(backupFiles.isEmpty())
     {
          textEdit->clear();
          return;
     }

     progressReceiver2 = new ProgressReceiver(this);
     progressDialog2 = new QProgressDialog(this);
     progressDialog2->setLabelText(QString(tr("Indexing backup data...")));
     setupBackup.p = progressReceiver2;
     backupDataHash = new QHash<QString,QStringList>;
     setupBackup.hash = backupDataHash;
     future2 = new QFutureWatcher<void>(this);
     future2->setFuture(QtConcurrent::map(backupFiles, setupBackup));

     QObject::connect(progressReceiver2,SIGNAL(valueChanged(int)),progressDialog2, SLOT(setValue(int)));
     QObject::connect(future2, SIGNAL(finished()), this, SLOT(setupChildren()));
     QObject::connect(future2, SIGNAL(finished()), progressDialog2, SLOT(reset()));
     QObject::connect(progressDialog2, SIGNAL(canceled()), future2, SLOT(cancel()));
}

void Backup::setupChildren()
{
     QHash<QString,QStringList> hash = *backupDataHash;
     foreach(QString key, hash.keys())
     {
          QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
          item->setText(0,hash[key].first()); //title
          item->setData(0,Qt::UserRole,hash[key]); //title, path and content
     }
}

void Backup::showPreview()
{
     if(treeWidget->currentItem() == NULL) //Prevents program crush
     {
          textEdit->clear();
          return;
     }

     if(!treeWidget->currentItem()->isSelected())
     {
          if(treeWidget->selectedItems().count() != 1)
             textEdit->clear();
          else
             textEdit->setText(treeWidget->selectedItems().first()->data(0,Qt::UserRole).toStringList().last());
     }
     else
        textEdit->setText(treeWidget->currentItem()->data(0,Qt::UserRole).toStringList().last());
}

void Backup::restoreBackup()
{
     if(treeWidget->selectedItems().isEmpty())
       return;
     foreach(QTreeWidgetItem *item, treeWidget->selectedItems())
     {
          QStringList dataList = item->data(0,Qt::UserRole).toStringList();
          QString title = dataList.takeFirst();
          if(!QFile(dataList.first()).exists())
            return;
          else
          {
             if(!QDir(QSettings().value("root_path").toString()+"/restored notes").exists())
               QDir().mkpath(QSettings().value("root_path").toString()+"/restored notes");
             QFile(dataList.first()).copy(QSettings().value("root_path").toString()+"/restored notes/"+title);
          }
          delete item;
     }
}

void Backup::deleteBackup()
{
     if(treeWidget->selectedItems().isEmpty())
        return;

     QStringList files;
     QList<QTreeWidgetItem*> itemList = treeWidget->selectedItems();
     foreach(QTreeWidgetItem *item, itemList)
     {
          QStringList dataList = item->data(0,Qt::UserRole).toStringList();
          dataList.takeFirst(); //removing title from the list
          files << dataList.first();
     }

     QString backupsToBeDeleted;
     foreach(QString str, files)
         backupsToBeDeleted += (str+"\n");

     if(QMessageBox::warning(this,tr("Deleting backups and file entries"),
          tr("Do you really want to delete the backups and entries for the "
             "following files?\n\n%1\nYou won't be able to restore them!").arg(
          backupsToBeDeleted),QMessageBox::Yes | QMessageBox::Abort) != QMessageBox::Yes)
        return;

     foreach(QString file, files)
     {
          if(QFile(file).exists())
            QFile(file).remove();

          QString uuid = file;
          uuid.remove(QSettings().value("backup_dir_path").toString() + "/");
          QSettings().remove("Notes/" + QUuid(uuid).toString() + "_size");
          QSettings().remove("Notes/" + QUuid(uuid).toString() + "_cursor_position");
     }

     foreach(QTreeWidgetItem *item, itemList)
          delete item;
}
<commit_msg>minor fix<commit_after>/* nobleNote, a note taking application
 * Copyright (C) 2012 Christian Metscher <hakaishi@web.de>,
                      Fabian Deuchler <Taiko000@gmail.com>

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

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

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

 * nobleNote is licensed under the MIT, see `http://copyfree.org/licenses/mit/license.txt'.
 */

#include "backup.h"
#include <QDirIterator>
#include <QMessageBox>
#include <QSettings>
#include <QtConcurrentMap>
#include <QAbstractItemModel>

Backup::Backup(QWidget *parent): QDialog(parent){
     setupUi(this);

     setAttribute(Qt::WA_DeleteOnClose);

     splitter = new QSplitter(groupBox);
     gridLayout_2->addWidget(splitter);
     treeWidget = new QTreeWidget(splitter);
     treeWidget->setAlternatingRowColors(true);
     treeWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
     treeWidget->setSortingEnabled(true);
     treeWidget->sortByColumn(0,Qt::AscendingOrder);
     treeWidget->setHeaderLabel(tr("Backups of deleted notes"));
     treeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
     treeWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
     // TODO flickcharm here

     frame = new QFrame(splitter);
     gridLayout3 = new QGridLayout(frame);
     label = new QLabel(frame);
     label->setText(tr("Preview of the selected backup"));
     gridLayout3->addWidget(label, 0, 0, 1, 1);
     textEdit = new QTextEdit(frame);
     textEdit->setReadOnly(true);
     gridLayout3->addWidget(textEdit, 1, 0, 1, 1);

     getNotes(); //Searches for notes and backups. For the backups with no notes it will create the trees children.

     connect(treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(showPreview()));
     connect(deleteButton, SIGNAL(clicked(bool)), this, SLOT(deleteBackup()));
     connect(restoreButton, SIGNAL(clicked(bool)), this, SLOT(restoreBackup()));
}

void Backup::getNoteUuidList()
{
     QFutureIterator<QString> it(future1->future());
     while(it.hasNext())
       noteUuidList << it.next();
}

void Backup::getNotes()
{
     //get note files
     QDirIterator itFiles(QSettings().value("root_path").toString(),
                                              QDirIterator::Subdirectories);
     QStringList noteFiles;
     while(itFiles.hasNext()){
       QString filePath = itFiles.next();
       if(itFiles.fileInfo().isFile())
         noteFiles << filePath;
     }

     progressReceiver1 = new ProgressReceiver(this);
     progressDialog1 = new QProgressDialog(this);
     progressDialog1->setLabelText(QString(tr("Indexing notes...")));
     getUuid.p = progressReceiver1;
     future1 = new QFutureWatcher<QString>(this);
     future1->setFuture(QtConcurrent::mapped(noteFiles, getUuid));

     QObject::connect(progressReceiver1,SIGNAL(valueChanged(int)),progressDialog1, SLOT(setValue(int)));
     QObject::connect(future1, SIGNAL(finished()), this, SLOT(getNoteUuidList()));
     QObject::connect(future1, SIGNAL(finished()), this, SLOT(setupBackups()));
     QObject::connect(future1, SIGNAL(finished()), progressDialog1, SLOT(reset()));
     QObject::connect(progressDialog1, SIGNAL(canceled()), future1, SLOT(cancel()));

     progressDialog1->exec();
}

void Backup::setupBackups()
{
     //get backup uuids
     QDir backupDir(QSettings().value("backup_dir_path").toString());
     backupFiles = backupDir.entryInfoList(QDir::Files, QDir::Name);

     foreach(QString uuid, noteUuidList)
     {
          if(backupFiles.contains(QFileInfo(QSettings().value("backup_dir_path").toString()
                                  + "/" + uuid.mid(1,36))))
          {
             backupFiles.removeOne(QFileInfo(QSettings().value("backup_dir_path").toString()
                                   + "/" + uuid.mid(1,36)));
             backupFiles.removeOne(QFileInfo(QSettings().value("backup_dir_path").toString()
                                   + "/" + uuid));
          }
     }

     if(backupFiles.isEmpty())
     {
          textEdit->clear();
          return;
     }

     progressReceiver2 = new ProgressReceiver(this);
     progressDialog2 = new QProgressDialog(this);
     progressDialog2->setLabelText(QString(tr("Indexing backup data...")));
     setupBackup.p = progressReceiver2;
     backupDataHash = new QHash<QString,QStringList>;
     setupBackup.hash = backupDataHash;
     future2 = new QFutureWatcher<void>(this);
     future2->setFuture(QtConcurrent::map(backupFiles, setupBackup));

     QObject::connect(progressReceiver2,SIGNAL(valueChanged(int)),progressDialog2, SLOT(setValue(int)));
     QObject::connect(future2, SIGNAL(finished()), this, SLOT(setupChildren()));
     QObject::connect(future2, SIGNAL(finished()), progressDialog2, SLOT(reset()));
     QObject::connect(progressDialog2, SIGNAL(canceled()), future2, SLOT(cancel()));
}

void Backup::setupChildren()
{
     QHash<QString,QStringList> hash = *backupDataHash;
     foreach(QString key, hash.keys())
     {
          QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
          item->setText(0,hash[key].first()); //title
          item->setData(0,Qt::UserRole,hash[key]); //title, path and content
     }
}

void Backup::showPreview()
{
     if(treeWidget->currentItem() == NULL) //Prevents program crush
     {
          textEdit->clear();
          return;
     }

     if(!treeWidget->currentItem()->isSelected())
     {
          if(treeWidget->selectedItems().count() != 1)
             textEdit->clear();
          else
             textEdit->setText(treeWidget->selectedItems().first()->data(0,Qt::UserRole).toStringList().last());
     }
     else
        textEdit->setText(treeWidget->currentItem()->data(0,Qt::UserRole).toStringList().last());
}

void Backup::restoreBackup()
{
     if(treeWidget->selectedItems().isEmpty())
       return;
     foreach(QTreeWidgetItem *item, treeWidget->selectedItems())
     {
          QStringList dataList = item->data(0,Qt::UserRole).toStringList();
          QString title = dataList.takeFirst();
          if(!QFile(dataList.first()).exists())
            return;
          else
          {
             if(!QDir(QSettings().value("root_path").toString()+"/restored notes").exists())
               QDir().mkpath(QSettings().value("root_path").toString()+"/restored notes");
             QFile(dataList.first()).copy(QSettings().value("root_path").toString()+"/restored notes/"+title);
          }
          delete item;
     }
}

void Backup::deleteBackup()
{
     if(treeWidget->selectedItems().isEmpty())
        return;

     QStringList files;
     QList<QTreeWidgetItem*> itemList = treeWidget->selectedItems();
     foreach(QTreeWidgetItem *item, itemList)
     {
          QStringList dataList = item->data(0,Qt::UserRole).toStringList();
          dataList.takeFirst(); //removing title from the list
          files << dataList.first();
     }

     QString backupsToBeDeleted;
     foreach(QString str, files)
         backupsToBeDeleted += (str+"\n");

     if(QMessageBox::warning(this,tr("Deleting backups and file entries"),
          tr("Do you really want to delete the backups and entries for the "
             "following files?\n\n%1\nYou won't be able to restore them!").arg(
          backupsToBeDeleted),QMessageBox::Yes | QMessageBox::Abort) != QMessageBox::Yes)
        return;

     foreach(QString file, files)
     {
          if(QFile(file).exists())
            QFile(file).remove();

          QString uuid = file;
          uuid.remove(QSettings().value("backup_dir_path").toString() + "/");
          QSettings().remove("Notes/" + QUuid(uuid).toString() + "_size");
          QSettings().remove("Notes/" + QUuid(uuid).toString() + "_cursor_position");
     }

     foreach(QTreeWidgetItem *item, itemList)
          delete item;
}
<|endoftext|>


<commit_before>
#include "pebble-cpp/application.hpp"
#include "pebble-cpp/tick_handler.hpp"
#include "countdown_timer.hpp"
#include "pebble-cpp/vibration.hpp"
#include "pebble-cpp/text_layer.hpp"

struct Stopwatch : pebble::WindowController<Stopwatch, pebble::ClickHandling::On>
{
    TWindow& window_;
    pebble::TextLayerWithBuffer<8> jam_clock_layer_;
    pebble::TextLayerWithBuffer<8> prev_jam_clock_layer_;
    pebble::TextLayerWithBuffer<8> break_clock_layer_;
    pebble::OnTickHandler<Stopwatch> tick_handler_;

    pebble::Vibration start_of_jam_vibration_;
    util::Optional<pebble::CustomVibration<20>> end_of_jam_vibration_;

    enum class Period
    {
        Jam,
        Break
    };

    bool paused_ = true;
    Period period_ = Period::Break;
    CountdownTimer jam_countdown_ = { util::Seconds(10) };
    CountdownTimer prev_jam_countdown_ = { util::Seconds(10) };
    CountdownTimer break_countdown_ = { util::Seconds(30) };

    Stopwatch(TWindow& window);

    static Stopwatch& GetStatic();

    void SetupClickHandlers(TClickHandlerSetup& setup);
    void OnTick(const util::CalendarTimeRef& time, pebble::TimeUnitMask);
    void OnSingleClick(pebble::ClickInfo click_info);
    void OnLongClick(pebble::ClickInfo click_info, pebble::ButtonState state);

    void SwitchPeriod(Period new_period);
    void RedrawClock();
};

struct App : public pebble::Application<App>
{
    Stopwatch::TWindow main_window_;

    App()
    {
        window_stack().Push(main_window_, pebble::Animate::SlideIn);
    }
};

extern "C" int main(void)
{
    App::Run();
}

/*--------------------------------------------------------------------------------------------------------------------*\
 * Stopwatch - Implementation
\*--------------------------------------------------------------------------------------------------------------------*/

Stopwatch::Stopwatch(TWindow& window)
:
    window_(window),
    jam_clock_layer_(0, PBL_IF_ROUND_ELSE(40, 30), window.root_layer().width(), 50),
    prev_jam_clock_layer_(0, PBL_IF_ROUND_ELSE(10, 5), window.root_layer().width(), 30),
    break_clock_layer_(0, 100, window.root_layer().width(), 35),
    tick_handler_(pebble::TimeUnit::Second)
{
    jam_clock_layer_.SetBackgroundColor(GColorClear);
    jam_clock_layer_.SetFont(pebble::Font(FONT_KEY_BITHAM_42_BOLD));
    jam_clock_layer_.SetAlignment(pebble::Alignment::Center);

    prev_jam_clock_layer_.SetBackgroundColor(GColorClear);
    prev_jam_clock_layer_.SetFont(pebble::Font(FONT_KEY_GOTHIC_14));
    prev_jam_clock_layer_.SetAlignment(pebble::Alignment::Center);

    break_clock_layer_.SetBackgroundColor(GColorClear);
    break_clock_layer_.SetFont(pebble::Font(FONT_KEY_BITHAM_34_MEDIUM_NUMBERS));
    break_clock_layer_.SetAlignment(pebble::Alignment::Center);

    SwitchPeriod(Period::Break);

    paused_ = true;
    prev_jam_countdown_.finish();

    RedrawClock();

    window.root_layer().AddChild(jam_clock_layer_);
    window.root_layer().AddChild(prev_jam_clock_layer_);
    window.root_layer().AddChild(break_clock_layer_);
}

Stopwatch& Stopwatch::GetStatic()
{
    return App::GetStatic().main_window_.controller().unsafe_get();
}

void Stopwatch::SetupClickHandlers(TClickHandlerSetup& setup)
{
    setup.SetupSingleClick(pebble::Button::Select);
    setup.SetupLongClick(pebble::Button::Select);

    setup.SetupLongClick(pebble::Button::Up);

    setup.SetupSingleClick(pebble::Button::Down);
    setup.SetupLongClick(pebble::Button::Down);
}

void Stopwatch::OnSingleClick(pebble::ClickInfo click_info)
{
    switch (click_info.button())
    {
        case pebble::Button::Select:
        {
            paused_ = !paused_;
            APP_LOG(APP_LOG_LEVEL_DEBUG, "Pause changed: %d", paused_);
            break;
        }
        case pebble::Button::Down:
        {
            if (period_ == Period::Jam)
            {
                SwitchPeriod(Period::Break);
            }
            else if (period_ == Period::Break)
            {
                if (break_countdown_.remaining() > util::Seconds(5))
                {
                    break_countdown_.set_remaining(util::Seconds(5));
                    RedrawClock();
                }
                else
                {
                    SwitchPeriod(Period::Jam);
                }
            }
            break;
        }
        default: break;
    }
}

void Stopwatch::OnLongClick(pebble::ClickInfo click_info, pebble::ButtonState state)
{
    if (state != pebble::ButtonState::Down)
    {
        return;
    }

    switch (click_info.button())
    {
        case pebble::Button::Up:
        {
            if (period_ == Period::Break && !prev_jam_countdown_.is_finished())
            {
                jam_countdown_.set_remaining(prev_jam_countdown_.remaining());
                SwitchPeriod(Period::Jam);
            }
            break;
        }
        default:
        {
            OnSingleClick(click_info);
            break;
        }
    }
}

void Stopwatch::SwitchPeriod(Period new_period)
{
    switch (new_period)
    {
        case Period::Jam:
        {
            APP_LOG(APP_LOG_LEVEL_DEBUG, "Switch to JAM");
            jam_clock_layer_.SetTextColor(pebble::Color::Black());
            prev_jam_clock_layer_.SetTextColor(pebble::Color::White());
            break_clock_layer_.SetTextColor(pebble::Color::LightGray());

            prev_jam_countdown_.finish();
            break_countdown_.finish();
            break;
        }
        case Period::Break:
        {
            APP_LOG(APP_LOG_LEVEL_DEBUG, "Switch to BREAK");
            jam_clock_layer_.SetTextColor(pebble::Color::LightGray());
            prev_jam_clock_layer_.SetTextColor(pebble::Color::Black());
            break_clock_layer_.SetTextColor(pebble::Color::Black());

            prev_jam_countdown_.set_remaining(jam_countdown_.remaining());
            jam_countdown_.Reset();
            break_countdown_.Reset();
            end_of_jam_vibration_.Clear();
            break;
        }
    }

    paused_ = false;
    period_ = new_period;
    RedrawClock();
}

void Stopwatch::OnTick(const util::CalendarTimeRef& time, pebble::TimeUnitMask)
{
    bool need_redraw = !prev_jam_countdown_.is_finished();
    prev_jam_countdown_.decrement(util::Seconds(1));

    if (paused_)
    {
        if (need_redraw) RedrawClock();
        return;
    }

    switch (period_)
    {
        case Period::Jam:
        {
            need_redraw = !jam_countdown_.is_finished();
            jam_countdown_.decrement(util::Seconds(1));

            if (jam_countdown_.is_finished())
            {
                // Vibration call can take a while to return, so do it early
                if (need_redraw)
                {
                    RedrawClock();
                    need_redraw = false;
                }

                using namespace pebble::vibration;

                if (end_of_jam_vibration_.is_none())
                {
                    end_of_jam_vibration_.GetOrEmplace().StartRepeatingSequence<10>(
                        Pulse(util::Seconds(1)), Wait(util::Seconds(1))
                    );
                }
            }

            break;
        }
        case Period::Break:
        {
            need_redraw = true;
            break_countdown_.decrement(util::Seconds(1));

            if (break_countdown_.is_finished())
            {
                jam_countdown_.Reset();
                start_of_jam_vibration_.ShortPulse();
                SwitchPeriod(Period::Jam);
                need_redraw = false;
            }
            break;
        }
    }

    if (need_redraw)
    {
        RedrawClock();
    }
}

void Stopwatch::RedrawClock()
{
    jam_clock_layer_.SetFromFormat(
        "%u:%02u", jam_countdown_.remaining().minutes(), jam_countdown_.remaining().seconds()
    );

    prev_jam_clock_layer_.SetFromFormat(
        "%u:%02u", prev_jam_countdown_.remaining().minutes(), prev_jam_countdown_.remaining().seconds()
    );

    break_clock_layer_.SetFromFormat(
        "%u:%02u", break_countdown_.remaining().minutes(), break_countdown_.remaining().seconds()
    );
}

<commit_msg>Correct time and layout.<commit_after>
#include "pebble-cpp/application.hpp"
#include "pebble-cpp/tick_handler.hpp"
#include "countdown_timer.hpp"
#include "pebble-cpp/vibration.hpp"
#include "pebble-cpp/text_layer.hpp"

struct Stopwatch : pebble::WindowController<Stopwatch, pebble::ClickHandling::On>
{
    TWindow& window_;
    pebble::TextLayerWithBuffer<8> jam_clock_layer_;
    pebble::TextLayerWithBuffer<8> prev_jam_clock_layer_;
    pebble::TextLayerWithBuffer<8> break_clock_layer_;
    pebble::OnTickHandler<Stopwatch> tick_handler_;

    pebble::Vibration start_of_jam_vibration_;
    util::Optional<pebble::CustomVibration<20>> end_of_jam_vibration_;

    enum class Period
    {
        Jam,
        Break
    };

    bool paused_ = true;
    Period period_ = Period::Break;
    CountdownTimer jam_countdown_ = { util::Minutes(2) };
    CountdownTimer prev_jam_countdown_ = { util::Minutes(2) };
    CountdownTimer break_countdown_ = { util::Seconds(30) };

    Stopwatch(TWindow& window);

    static Stopwatch& GetStatic();

    void SetupClickHandlers(TClickHandlerSetup& setup);
    void OnTick(const util::CalendarTimeRef& time, pebble::TimeUnitMask);
    void OnSingleClick(pebble::ClickInfo click_info);
    void OnLongClick(pebble::ClickInfo click_info, pebble::ButtonState state);

    void SwitchPeriod(Period new_period);
    void RedrawClock();
};

struct App : public pebble::Application<App>
{
    Stopwatch::TWindow main_window_;

    App()
    {
        window_stack().Push(main_window_, pebble::Animate::SlideIn);
    }
};

extern "C" int main(void)
{
    App::Run();
}

/*--------------------------------------------------------------------------------------------------------------------*\
 * Stopwatch - Implementation
\*--------------------------------------------------------------------------------------------------------------------*/

Stopwatch::Stopwatch(TWindow& window)
:
    window_(window),
    jam_clock_layer_(0, 20, window.root_layer().width(), 50),
    prev_jam_clock_layer_(0, 65, window.root_layer().width(), 30),
    break_clock_layer_(0, 100, window.root_layer().width(), 35),
    tick_handler_(pebble::TimeUnit::Second)
{
    jam_clock_layer_.SetBackgroundColor(GColorClear);
    jam_clock_layer_.SetFont(pebble::Font(FONT_KEY_BITHAM_42_BOLD));
    jam_clock_layer_.SetAlignment(pebble::Alignment::Center);

    prev_jam_clock_layer_.SetBackgroundColor(GColorClear);
    prev_jam_clock_layer_.SetFont(pebble::Font(FONT_KEY_GOTHIC_18));
    prev_jam_clock_layer_.SetAlignment(pebble::Alignment::Center);

    break_clock_layer_.SetBackgroundColor(GColorClear);
    break_clock_layer_.SetFont(pebble::Font(FONT_KEY_BITHAM_34_MEDIUM_NUMBERS));
    break_clock_layer_.SetAlignment(pebble::Alignment::Center);

    SwitchPeriod(Period::Break);

    paused_ = true;
    prev_jam_countdown_.finish();

    RedrawClock();

    window.root_layer().AddChild(jam_clock_layer_);
    window.root_layer().AddChild(prev_jam_clock_layer_);
    window.root_layer().AddChild(break_clock_layer_);
}

Stopwatch& Stopwatch::GetStatic()
{
    return App::GetStatic().main_window_.controller().unsafe_get();
}

void Stopwatch::SetupClickHandlers(TClickHandlerSetup& setup)
{
    setup.SetupSingleClick(pebble::Button::Select);
    setup.SetupLongClick(pebble::Button::Select);

    setup.SetupLongClick(pebble::Button::Up);

    setup.SetupSingleClick(pebble::Button::Down);
    setup.SetupLongClick(pebble::Button::Down);
}

void Stopwatch::OnSingleClick(pebble::ClickInfo click_info)
{
    switch (click_info.button())
    {
        case pebble::Button::Select:
        {
            paused_ = !paused_;
            APP_LOG(APP_LOG_LEVEL_DEBUG, "Pause changed: %d", paused_);
            break;
        }
        case pebble::Button::Down:
        {
            if (period_ == Period::Jam)
            {
                SwitchPeriod(Period::Break);
            }
            else if (period_ == Period::Break)
            {
                if (break_countdown_.remaining() > util::Seconds(5))
                {
                    break_countdown_.set_remaining(util::Seconds(5));
                    RedrawClock();
                }
                else
                {
                    SwitchPeriod(Period::Jam);
                }
            }
            break;
        }
        default: break;
    }
}

void Stopwatch::OnLongClick(pebble::ClickInfo click_info, pebble::ButtonState state)
{
    if (state != pebble::ButtonState::Down)
    {
        return;
    }

    switch (click_info.button())
    {
        case pebble::Button::Up:
        {
            if (period_ == Period::Break && !prev_jam_countdown_.is_finished())
            {
                jam_countdown_.set_remaining(prev_jam_countdown_.remaining());
                SwitchPeriod(Period::Jam);
            }
            break;
        }
        default:
        {
            OnSingleClick(click_info);
            break;
        }
    }
}

void Stopwatch::SwitchPeriod(Period new_period)
{
    switch (new_period)
    {
        case Period::Jam:
        {
            APP_LOG(APP_LOG_LEVEL_DEBUG, "Switch to JAM");
            jam_clock_layer_.SetTextColor(pebble::Color::Black());
            prev_jam_clock_layer_.SetTextColor(pebble::Color::White());
            break_clock_layer_.SetTextColor(pebble::Color::LightGray());

            prev_jam_countdown_.finish();
            break_countdown_.finish();
            break;
        }
        case Period::Break:
        {
            APP_LOG(APP_LOG_LEVEL_DEBUG, "Switch to BREAK");
            jam_clock_layer_.SetTextColor(pebble::Color::LightGray());
            prev_jam_clock_layer_.SetTextColor(pebble::Color::Black());
            break_clock_layer_.SetTextColor(pebble::Color::Black());

            prev_jam_countdown_.set_remaining(jam_countdown_.remaining());
            jam_countdown_.Reset();
            break_countdown_.Reset();
            end_of_jam_vibration_.Clear();
            break;
        }
    }

    paused_ = false;
    period_ = new_period;
    RedrawClock();
}

void Stopwatch::OnTick(const util::CalendarTimeRef& time, pebble::TimeUnitMask)
{
    bool need_redraw = !prev_jam_countdown_.is_finished();
    prev_jam_countdown_.decrement(util::Seconds(1));

    if (paused_)
    {
        if (need_redraw) RedrawClock();
        return;
    }

    switch (period_)
    {
        case Period::Jam:
        {
            need_redraw = !jam_countdown_.is_finished();
            jam_countdown_.decrement(util::Seconds(1));

            if (jam_countdown_.is_finished())
            {
                // Vibration call can take a while to return, so do it early
                if (need_redraw)
                {
                    RedrawClock();
                    need_redraw = false;
                }

                using namespace pebble::vibration;

                if (end_of_jam_vibration_.is_none())
                {
                    end_of_jam_vibration_.GetOrEmplace().StartRepeatingSequence<10>(
                        Pulse(util::Seconds(1)), Wait(util::Seconds(1))
                    );
                }
            }

            break;
        }
        case Period::Break:
        {
            need_redraw = true;
            break_countdown_.decrement(util::Seconds(1));

            if (break_countdown_.is_finished())
            {
                jam_countdown_.Reset();
                start_of_jam_vibration_.ShortPulse();
                SwitchPeriod(Period::Jam);
                need_redraw = false;
            }
            break;
        }
    }

    if (need_redraw)
    {
        RedrawClock();
    }
}

void Stopwatch::RedrawClock()
{
    jam_clock_layer_.SetFromFormat(
        "%u:%02u", jam_countdown_.remaining().minutes(), jam_countdown_.remaining().seconds()
    );

    prev_jam_clock_layer_.SetFromFormat(
        "%u:%02u", prev_jam_countdown_.remaining().minutes(), prev_jam_countdown_.remaining().seconds()
    );

    break_clock_layer_.SetFromFormat(
        "%u:%02u", break_countdown_.remaining().minutes(), break_countdown_.remaining().seconds()
    );
}

<|endoftext|>


<commit_before>#include "p0run/interpreter.hpp"
#include "p0i/function.hpp"
#include "p0i/emitter.hpp"
#include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp>
using namespace p0::run;
using namespace p0;

namespace
{
	template <class FunctionCreator, class ResultHandler>
	void run_single_function(
		FunctionCreator const &create,
		std::vector<value> const &arguments,
		ResultHandler const &handle_result
		)
	{
		intermediate::unit::function_vector functions;
		intermediate::emitter::instruction_vector instructions;
		intermediate::emitter emitter(instructions);
		intermediate::unit::string_vector strings;
		create(emitter, strings);
		functions.push_back(intermediate::function(instructions, arguments.size()));
		intermediate::unit program(functions, strings);
		interpreter interpreter(program);
		auto const result = interpreter.call(program.functions()[0], arguments);
		handle_result(result);
	}
}

BOOST_AUTO_TEST_CASE(nothing_operation_test)
{
	for (size_t i = 0; i < 3; ++i)
	{
		run_single_function(
			[i](intermediate::emitter &emitter, intermediate::unit::string_vector &strings)
		{
			for (size_t j = 0; j < i; ++j)
			{
				emitter.nothing();
			}
		},
			std::vector<value>(),
			[](value const &result)
		{
			BOOST_CHECK(result == value());
		});
	}
}

BOOST_AUTO_TEST_CASE(set_from_constant_operation_test)
{
	static std::array<integer, 5> const test_numbers =
	{{
		0,
		1,
		-34,
		std::numeric_limits<integer>::min(),
		std::numeric_limits<integer>::max()
	}};

	BOOST_FOREACH (integer const number, test_numbers)
	{
		run_single_function(
			[number](intermediate::emitter &emitter, intermediate::unit::string_vector &)
		{
			emitter.set_from_constant(0, number);
		},
			std::vector<value>(),
			[number](value const &result)
		{
			BOOST_CHECK(result == value(number));
		});
	}
}

BOOST_AUTO_TEST_CASE(set_null_operation_test)
{
	run_single_function(
		[](intermediate::emitter &emitter, intermediate::unit::string_vector &)
	{
		emitter.set_from_constant(1, 123);

		//overwrite the "123" with null
		emitter.set_null(1);

		//return the null
		emitter.copy(0, 1);
	},
		std::vector<value>(),
		[](value const &result)
	{
		BOOST_CHECK(result == value());
	});
}

BOOST_AUTO_TEST_CASE(copy_operation_test)
{
	value const test_value(static_cast<integer>(6));

	run_single_function(
		[](intermediate::emitter &emitter, intermediate::unit::string_vector &)
	{
		//return the first argument
		emitter.copy(0, 1);
	},
		std::vector<value>(1, test_value),
		[test_value](value const &result)
	{
		BOOST_CHECK(result == test_value);
	});
}

namespace
{
	void integer_arithmetic_test(
		intermediate::instruction_type::Enum op,
		integer left,
		integer right,
		integer expected_result
		)
	{
		std::vector<value> arguments;
		arguments.push_back(value(left));
		arguments.push_back(value(right));

		run_single_function(
			[op](intermediate::emitter &emitter, intermediate::unit::string_vector &)
		{
			//[1] += [2]
			emitter.push_instruction(intermediate::instruction(op, 1, 2));
			//[0] = [1]
			emitter.copy(0, 1);
			//return [0]
			emitter.return_();
		},
			arguments,
			[expected_result](value const &result)
		{
			BOOST_CHECK(result == value(expected_result));
		});
	}
}

BOOST_AUTO_TEST_CASE(add_operation_test)
{
	using namespace intermediate::instruction_type;
	integer_arithmetic_test(add,  60,  70,  130);
	integer_arithmetic_test(add,   0,  70,   70);
	integer_arithmetic_test(add,  60,   0,   60);
	integer_arithmetic_test(add, -60,  70,   10);
	integer_arithmetic_test(add, -60, -70, -130);
}

BOOST_AUTO_TEST_CASE(sub_operation_test)
{
	using namespace intermediate::instruction_type;
	integer_arithmetic_test(sub,  60,  70,  -10);
	integer_arithmetic_test(sub,   0,  70,  -70);
	integer_arithmetic_test(sub,  60,   0,   60);
	integer_arithmetic_test(sub, -60,  70, -130);
	integer_arithmetic_test(sub, -60, -70,   10);
}

BOOST_AUTO_TEST_CASE(mul_operation_test)
{
	using namespace intermediate::instruction_type;
	integer_arithmetic_test(mul,  60,  70,  4200);
	integer_arithmetic_test(mul,   0,  70,     0);
	integer_arithmetic_test(mul,  60,   0,     0);
	integer_arithmetic_test(mul, -60,  70, -4200);
	integer_arithmetic_test(mul, -60, -70,  4200);
	integer_arithmetic_test(mul,   1,  70,    70);
	integer_arithmetic_test(mul,  60,   1,    60);
}

BOOST_AUTO_TEST_CASE(div_operation_test)
{
	using namespace intermediate;
	//VC++ says 'div' is ambiguous
	integer_arithmetic_test(instruction_type::div,  60,  70,   0);
	integer_arithmetic_test(instruction_type::div,   0,  70,   0);
	integer_arithmetic_test(instruction_type::div,  60,   2,  30);
	integer_arithmetic_test(instruction_type::div, -70,  60,  -1);
	integer_arithmetic_test(instruction_type::div, -70, -60,   1);
	integer_arithmetic_test(instruction_type::div,   1,  70,   0);
	integer_arithmetic_test(instruction_type::div,  60,   1,  60);
}
<commit_msg>test mod, and, or, xor<commit_after>#include "p0run/interpreter.hpp"
#include "p0i/function.hpp"
#include "p0i/emitter.hpp"
#include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp>
using namespace p0::run;
using namespace p0;

namespace
{
	template <class FunctionCreator, class ResultHandler>
	void run_single_function(
		FunctionCreator const &create,
		std::vector<value> const &arguments,
		ResultHandler const &handle_result
		)
	{
		intermediate::unit::function_vector functions;
		intermediate::emitter::instruction_vector instructions;
		intermediate::emitter emitter(instructions);
		intermediate::unit::string_vector strings;
		create(emitter, strings);
		functions.push_back(intermediate::function(instructions, arguments.size()));
		intermediate::unit program(functions, strings);
		interpreter interpreter(program);
		auto const result = interpreter.call(program.functions()[0], arguments);
		handle_result(result);
	}
}

BOOST_AUTO_TEST_CASE(nothing_operation_test)
{
	for (size_t i = 0; i < 3; ++i)
	{
		run_single_function(
			[i](intermediate::emitter &emitter, intermediate::unit::string_vector &strings)
		{
			for (size_t j = 0; j < i; ++j)
			{
				emitter.nothing();
			}
		},
			std::vector<value>(),
			[](value const &result)
		{
			BOOST_CHECK(result == value());
		});
	}
}

BOOST_AUTO_TEST_CASE(set_from_constant_operation_test)
{
	static std::array<integer, 5> const test_numbers =
	{{
		0,
		1,
		-34,
		std::numeric_limits<integer>::min(),
		std::numeric_limits<integer>::max()
	}};

	BOOST_FOREACH (integer const number, test_numbers)
	{
		run_single_function(
			[number](intermediate::emitter &emitter, intermediate::unit::string_vector &)
		{
			emitter.set_from_constant(0, number);
		},
			std::vector<value>(),
			[number](value const &result)
		{
			BOOST_CHECK(result == value(number));
		});
	}
}

BOOST_AUTO_TEST_CASE(set_null_operation_test)
{
	run_single_function(
		[](intermediate::emitter &emitter, intermediate::unit::string_vector &)
	{
		emitter.set_from_constant(1, 123);

		//overwrite the "123" with null
		emitter.set_null(1);

		//return the null
		emitter.copy(0, 1);
	},
		std::vector<value>(),
		[](value const &result)
	{
		BOOST_CHECK(result == value());
	});
}

BOOST_AUTO_TEST_CASE(copy_operation_test)
{
	value const test_value(static_cast<integer>(6));

	run_single_function(
		[](intermediate::emitter &emitter, intermediate::unit::string_vector &)
	{
		//return the first argument
		emitter.copy(0, 1);
	},
		std::vector<value>(1, test_value),
		[test_value](value const &result)
	{
		BOOST_CHECK(result == test_value);
	});
}

namespace
{
	void integer_arithmetic_test(
		intermediate::instruction_type::Enum op,
		integer left,
		integer right,
		integer expected_result
		)
	{
		std::vector<value> arguments;
		arguments.push_back(value(left));
		arguments.push_back(value(right));

		run_single_function(
			[op](intermediate::emitter &emitter, intermediate::unit::string_vector &)
		{
			//[1] += [2]
			emitter.push_instruction(intermediate::instruction(op, 1, 2));
			//[0] = [1]
			emitter.copy(0, 1);
			//return [0]
			emitter.return_();
		},
			arguments,
			[expected_result](value const &result)
		{
			BOOST_CHECK(result == value(expected_result));
		});
	}
}

BOOST_AUTO_TEST_CASE(add_operation_test)
{
	using namespace intermediate::instruction_type;
	integer_arithmetic_test(add,  60,  70,  130);
	integer_arithmetic_test(add,   0,  70,   70);
	integer_arithmetic_test(add,  60,   0,   60);
	integer_arithmetic_test(add, -60,  70,   10);
	integer_arithmetic_test(add, -60, -70, -130);
}

BOOST_AUTO_TEST_CASE(sub_operation_test)
{
	using namespace intermediate::instruction_type;
	integer_arithmetic_test(sub,  60,  70,  -10);
	integer_arithmetic_test(sub,   0,  70,  -70);
	integer_arithmetic_test(sub,  60,   0,   60);
	integer_arithmetic_test(sub, -60,  70, -130);
	integer_arithmetic_test(sub, -60, -70,   10);
}

BOOST_AUTO_TEST_CASE(mul_operation_test)
{
	using namespace intermediate::instruction_type;
	integer_arithmetic_test(mul,  60,  70,  4200);
	integer_arithmetic_test(mul,   0,  70,     0);
	integer_arithmetic_test(mul,  60,   0,     0);
	integer_arithmetic_test(mul, -60,  70, -4200);
	integer_arithmetic_test(mul, -60, -70,  4200);
	integer_arithmetic_test(mul,   1,  70,    70);
	integer_arithmetic_test(mul,  60,   1,    60);
}

BOOST_AUTO_TEST_CASE(div_operation_test)
{
	using namespace intermediate;
	//VC++ says 'div' is ambiguous
	integer_arithmetic_test(instruction_type::div,  60,  70,   0);
	integer_arithmetic_test(instruction_type::div,   0,  70,   0);
	integer_arithmetic_test(instruction_type::div,  60,   2,  30);
	integer_arithmetic_test(instruction_type::div, -70,  60,  -1);
	integer_arithmetic_test(instruction_type::div, -70, -60,   1);
	integer_arithmetic_test(instruction_type::div,   1,  70,   0);
	integer_arithmetic_test(instruction_type::div,  60,   1,  60);
}

BOOST_AUTO_TEST_CASE(mod_operation_test)
{
	using namespace intermediate::instruction_type;
	integer_arithmetic_test(mod,  60,  70,  60);
	integer_arithmetic_test(mod,   0,  70,   0);
	integer_arithmetic_test(mod,  60,   2,   0);
	integer_arithmetic_test(mod, -70,  60, -10);
	integer_arithmetic_test(mod,   1,  70,   1);
	integer_arithmetic_test(mod,  60,   1,   0);
}

BOOST_AUTO_TEST_CASE(and_operation_test)
{
	using namespace intermediate::instruction_type;
	integer_arithmetic_test(and_,  60,  70,  60 & 70);
	integer_arithmetic_test(and_,   0,  70,   0 & 70);
	integer_arithmetic_test(and_,  60,   2,  60 &  2);
	integer_arithmetic_test(and_, -70,  60, -70 & 60);
	integer_arithmetic_test(and_,   1,  70,   1 & 70);
	integer_arithmetic_test(and_,  60,   1,  60 &  1);
}

BOOST_AUTO_TEST_CASE(or_operation_test)
{
	using namespace intermediate::instruction_type;
	integer_arithmetic_test(or_,  60,  70,  60 | 70);
	integer_arithmetic_test(or_,   0,  70,   0 | 70);
	integer_arithmetic_test(or_,  60,   2,  60 |  2);
	integer_arithmetic_test(or_, -70,  60, -70 | 60);
	integer_arithmetic_test(or_,   1,  70,   1 | 70);
	integer_arithmetic_test(or_,  60,   1,  60 |  1);
}

BOOST_AUTO_TEST_CASE(xor_operation_test)
{
	using namespace intermediate::instruction_type;
	integer_arithmetic_test(xor_,  60,  70,  60 ^ 70);
	integer_arithmetic_test(xor_,   0,  70,   0 ^ 70);
	integer_arithmetic_test(xor_,  60,   2,  60 ^  2);
	integer_arithmetic_test(xor_, -70,  60, -70 ^ 60);
	integer_arithmetic_test(xor_,   1,  70,   1 ^ 70);
	integer_arithmetic_test(xor_,  60,   1,  60 ^  1);
}
<|endoftext|>