<commit_before>#include "logstore.h"
#include "merger.h"

#include <stasis/transactional.h>
#undef try
#undef end

static inline double tv_to_double(struct timeval tv)
{
  return static_cast<double>(tv.tv_sec) +
      (static_cast<double>(tv.tv_usec) / 1000000.0);
}

/////////////////////////////////////////////////////////////////
// LOG TABLE IMPLEMENTATION
/////////////////////////////////////////////////////////////////

template<class TUPLE>
logtable<TUPLE>::logtable(pageid_t internal_region_size, pageid_t datapage_region_size, pageid_t datapage_size)
{

    tree_c0 = NULL;
    tree_c0_mergeable = NULL;
    tree_c1 = NULL;
    tree_c1_mergeable = NULL;
    tree_c2 = NULL;
    this->still_running_ = true;
    this->mergedata = 0;
    //tmerger = new tuplemerger(&append_merger);
    tmerger = new tuplemerger(&replace_merger);

    header_lock = initlock();

    tsize = 0;
    tree_bytes = 0;

    epoch = 0;

    this->internal_region_size = internal_region_size;
    this->datapage_region_size = datapage_region_size;
    this->datapage_size = datapage_size;
}

template<class TUPLE>
logtable<TUPLE>::~logtable()
{
    if(tree_c1 != NULL)
        delete tree_c1;
    if(tree_c2 != NULL)
        delete tree_c2;

    if(tree_c0 != NULL)
    {
      memTreeComponent<datatuple>::tearDownTree(tree_c0);
    }

    deletelock(header_lock);
    delete tmerger;
}

template<class TUPLE>
recordid logtable<TUPLE>::allocTable(int xid)
{

    table_rec = Talloc(xid, sizeof(tbl_header));

    //create the big tree
    tree_c2 = new diskTreeComponent(xid, internal_region_size, datapage_region_size, datapage_size);

    //create the small tree
    tree_c1 = new diskTreeComponent(xid, internal_region_size, datapage_region_size, datapage_size);

    update_persistent_header(xid);

    return table_rec;
}
template<class TUPLE>
void logtable<TUPLE>::openTable(int xid, recordid rid) {
  table_rec = rid;
  Tread(xid, table_rec, &tbl_header);
  tree_c2 = new diskTreeComponent(xid, tbl_header.c2_root, tbl_header.c2_state, tbl_header.c2_dp_state);
  tree_c1 = new diskTreeComponent(xid, tbl_header.c1_root, tbl_header.c1_state, tbl_header.c1_dp_state);
}
template<class TUPLE>
void logtable<TUPLE>::update_persistent_header(int xid) {

	tbl_header.c2_root = tree_c2->get_root_rid();
    tbl_header.c2_dp_state = tree_c2->get_datapage_allocator_rid();
    tbl_header.c2_state = tree_c2->get_internal_node_allocator_rid();
    tbl_header.c1_root = tree_c1->get_root_rid();
    tbl_header.c1_dp_state = tree_c1->get_datapage_allocator_rid();
    tbl_header.c1_state = tree_c1->get_internal_node_allocator_rid();

    Tset(xid, table_rec, &tbl_header);
}

template<class TUPLE>
void logtable<TUPLE>::setMergeData(logtable_mergedata * mdata){
  this->mergedata = mdata;
  mdata->internal_region_size = internal_region_size;
  mdata->datapage_region_size = datapage_region_size;
  mdata->datapage_size = datapage_size;

  bump_epoch();
}

template<class TUPLE>
void logtable<TUPLE>::flushTable()
{
    struct timeval start_tv, stop_tv;
    double start, stop;

    static double last_start;
    static bool first = 1;
    static int merge_count = 0;

    gettimeofday(&start_tv,0);
    start = tv_to_double(start_tv);

    writelock(header_lock,0);
    pthread_mutex_lock(mergedata->rbtree_mut);

    int expmcount = merge_count;

    //this is for waiting the previous merger of the mem-tree
    //hopefullly this wont happen

    while(get_tree_c0_mergeable()) {
        unlock(header_lock);
        if(tree_bytes >= max_c0_size)
            pthread_cond_wait(mergedata->input_needed_cond, mergedata->rbtree_mut);
        else
        {
            pthread_mutex_unlock(mergedata->rbtree_mut);
            return;
        }

        pthread_mutex_unlock(mergedata->rbtree_mut);

        writelock(header_lock,0);
        pthread_mutex_lock(mergedata->rbtree_mut);

        if(expmcount != merge_count)
        {
            unlock(header_lock);
            pthread_mutex_unlock(mergedata->rbtree_mut);
            return;
        }

    }

    gettimeofday(&stop_tv,0);
    stop = tv_to_double(stop_tv);

    set_tree_c0_mergeable(get_tree_c0());

    pthread_cond_signal(mergedata->input_ready_cond);

    merge_count ++;
    set_tree_c0(new memTreeComponent<datatuple>::rbtree_t);

    tsize = 0;
    tree_bytes = 0;

    pthread_mutex_unlock(mergedata->rbtree_mut);
    unlock(header_lock);
    if(first)
    {
        printf("Blocked writes for %f sec\n", stop-start);
        first = 0;
    }
    else
    {
        printf("Blocked writes for %f sec (serviced writes for %f sec)\n",
               stop-start, start-last_start);
    }
    last_start = stop;

}

template<class TUPLE>
datatuple * logtable<TUPLE>::findTuple(int xid, const datatuple::key_t key, size_t keySize)
{
    //prepare a search tuple
	datatuple *search_tuple = datatuple::create(key, keySize);

    readlock(header_lock,0);
    pthread_mutex_lock(mergedata->rbtree_mut);

    datatuple *ret_tuple=0;

    //step 1: look in tree_c0
    memTreeComponent<datatuple>::rbtree_t::iterator rbitr = get_tree_c0()->find(search_tuple);
    if(rbitr != get_tree_c0()->end())
    {
        DEBUG("tree_c0 size %d\n", get_tree_c0()->size());
        ret_tuple = (*rbitr)->create_copy();
    }

    bool done = false;
    //step: 2 look into first in tree if exists (a first level merge going on)
    if(get_tree_c0_mergeable() != 0)
    {
        DEBUG("old mem tree not null %d\n", (*(mergedata->old_c0))->size());
        rbitr = get_tree_c0_mergeable()->find(search_tuple);
        if(rbitr != get_tree_c0_mergeable()->end())
        {
            datatuple *tuple = *rbitr;

            if(tuple->isDelete())  //tuple deleted
                done = true;  //return ret_tuple
            else if(ret_tuple != 0)  //merge the two
            {
                datatuple *mtuple = tmerger->merge(tuple, ret_tuple);  //merge the two
                datatuple::freetuple(ret_tuple); //free tuple from current tree
                ret_tuple = mtuple; //set return tuple to merge result
            }
            else //key first found in old mem tree
            {
            	ret_tuple = tuple->create_copy();
            }
            //we cannot free tuple from old-tree 'cos it is not a copy
        }
    }

    //release the memtree lock
    pthread_mutex_unlock(mergedata->rbtree_mut);

    //step 3: check c1
    if(!done)
    {
        datatuple *tuple_c1 = get_tree_c1()->findTuple(xid, key, keySize);
        if(tuple_c1 != NULL)
        {
            bool use_copy = false;
            if(tuple_c1->isDelete()) //tuple deleted
                done = true;
            else if(ret_tuple != 0) //merge the two
            {
                datatuple *mtuple = tmerger->merge(tuple_c1, ret_tuple);  //merge the two
                datatuple::freetuple(ret_tuple);  //free tuple from before
                ret_tuple = mtuple; //set return tuple to merge result
            }
            else //found for the first time
            {
                use_copy = true;
                ret_tuple = tuple_c1;
            }

            if(!use_copy)
            {
                datatuple::freetuple(tuple_c1); //free tuple from tree c1
            }
        }
    }

    //step 4: check old c1 if exists
    if(!done && get_tree_c1_mergeable() != 0)
    {
        DEBUG("old c1 tree not null\n");
        datatuple *tuple_oc1 = get_tree_c1_mergeable()->findTuple(xid, key, keySize);

        if(tuple_oc1 != NULL)
        {
            bool use_copy = false;
            if(tuple_oc1->isDelete())
                done = true;
            else if(ret_tuple != 0) //merge the two
            {
                datatuple *mtuple = tmerger->merge(tuple_oc1, ret_tuple);  //merge the two
                datatuple::freetuple(ret_tuple); //free tuple from before
                ret_tuple = mtuple; //set return tuple to merge result
            }
            else //found for the first time
            {
                use_copy = true;
                ret_tuple = tuple_oc1;
            }

            if(!use_copy)
            {
            	datatuple::freetuple(tuple_oc1); //free tuple from tree old c1
            }
        }
    }

    //step 5: check c2
    if(!done)
    {
        DEBUG("Not in old first disk tree\n");
        datatuple *tuple_c2 = get_tree_c2()->findTuple(xid, key, keySize);

        if(tuple_c2 != NULL)
        {
            bool use_copy = false;
            if(tuple_c2->isDelete())
                done = true;
            else if(ret_tuple != 0)
            {
                datatuple *mtuple = tmerger->merge(tuple_c2, ret_tuple);  //merge the two
                datatuple::freetuple(ret_tuple); //free tuple from before
                ret_tuple = mtuple; //set return tuple to merge result
            }
            else //found for the first time
            {
                use_copy = true;
                ret_tuple = tuple_c2;
            }

            if(!use_copy)
            {
            	datatuple::freetuple(tuple_c2);  //free tuple from tree c2
            }
        }
    }

    //pthread_mutex_unlock(mergedata->rbtree_mut);
    unlock(header_lock);
    datatuple::freetuple(search_tuple);
    return ret_tuple;

}

/*
 * returns the first record found with the matching key
 * (not to be used together with diffs)
 **/
template<class TUPLE>
datatuple * logtable<TUPLE>::findTuple_first(int xid, datatuple::key_t key, size_t keySize)
{
    //prepare a search tuple
    datatuple * search_tuple = datatuple::create(key, keySize);

    pthread_mutex_lock(mergedata->rbtree_mut);

    datatuple *ret_tuple=0;
    //step 1: look in tree_c0

    memTreeComponent<datatuple>::rbtree_t::iterator rbitr = get_tree_c0()->find(search_tuple);
    if(rbitr != get_tree_c0()->end())
    {
        DEBUG("tree_c0 size %d\n", tree_c0->size());
        ret_tuple = (*rbitr)->create_copy();

    }
    else
    {
        DEBUG("Not in mem tree %d\n", tree_c0->size());
        //step: 2 look into first in tree if exists (a first level merge going on)
        if(get_tree_c0_mergeable() != NULL)
        {
            DEBUG("old mem tree not null %d\n", (*(mergedata->old_c0))->size());
            rbitr = get_tree_c0_mergeable()->find(search_tuple);
            if(rbitr != get_tree_c0_mergeable()->end())
            {
                ret_tuple = (*rbitr)->create_copy();
            }
        }

        if(ret_tuple == 0)
        {
            DEBUG("Not in old mem tree\n");

            //step 3: check c1
            ret_tuple = get_tree_c1()->findTuple(xid, key, keySize);
        }

        if(ret_tuple == 0)
        {
            DEBUG("Not in first disk tree\n");

            //step 4: check old c1 if exists
            if( get_tree_c1_mergeable() != 0)
            {
              DEBUG("old c1 tree not null\n");
              ret_tuple = get_tree_c1_mergeable()->findTuple(xid, key, keySize);
            }

        }

        if(ret_tuple == 0)
        {
            DEBUG("Not in old first disk tree\n");

            //step 5: check c2
            ret_tuple = get_tree_c2()->findTuple(xid, key, keySize);
        }
    }

    pthread_mutex_unlock(mergedata->rbtree_mut);
    datatuple::freetuple(search_tuple);

    return ret_tuple;

}

template<class TUPLE>
void logtable<TUPLE>::insertTuple(datatuple *tuple)
{
    //lock the red-black tree
    readlock(header_lock,0);
    pthread_mutex_lock(mergedata->rbtree_mut);
    //find the previous tuple with same key in the memtree if exists
    memTreeComponent<datatuple>::rbtree_t::iterator rbitr = tree_c0->find(tuple);
    if(rbitr != tree_c0->end())
    {
        datatuple *pre_t = *rbitr;
        //do the merging
        datatuple *new_t = tmerger->merge(pre_t, tuple);
        tree_c0->erase(pre_t); //remove the previous tuple

        tree_c0->insert(new_t); //insert the new tuple

        //update the tree size (+ new_t size - pre_t size)
        tree_bytes += (new_t->byte_length() - pre_t->byte_length());

        datatuple::freetuple(pre_t); //free the previous tuple
    }
    else //no tuple with same key exists in mem-tree
    {

    	datatuple *t = tuple->create_copy();

        //insert tuple into the rbtree
        tree_c0->insert(t);
        tsize++;
        tree_bytes += t->byte_length() + RB_TREE_OVERHEAD;

    }

    //flushing logic
    if(tree_bytes >= max_c0_size )
    {
        DEBUG("tree size before merge %d tuples %lld bytes.\n", tsize, tree_bytes);
        pthread_mutex_unlock(mergedata->rbtree_mut);
        unlock(header_lock);
        flushTable();

        readlock(header_lock,0);
        pthread_mutex_lock(mergedata->rbtree_mut);
    }

    //unlock
    pthread_mutex_unlock(mergedata->rbtree_mut);
    unlock(header_lock);

    DEBUG("tree size %d tuples %lld bytes.\n", tsize, tree_bytes);
}

template<class TUPLE>
void logtable<TUPLE>::registerIterator(iterator * it) {
  its.push_back(it);
}
template<class TUPLE>
void logtable<TUPLE>::forgetIterator(iterator * it) {
  for(unsigned int i = 0; i < its.size(); i++) {
    if(its[i] == it) {
      its.erase(its.begin()+i);
      break;
    }
  }
}
template<class TUPLE>
void logtable<TUPLE>::bump_epoch() {
  assert(!trywritelock(header_lock,0));
  epoch++;
  for(unsigned int i = 0; i < its.size(); i++) {
    its[i]->invalidate();
  }
}
template class logtable<datatuple>;
<commit_msg>fix integer arithmetic bug that caused C0 and C1 to merge arbitrarily often<commit_after>#include "logstore.h"
#include "merger.h"

#include <stasis/transactional.h>
#undef try
#undef end

static inline double tv_to_double(struct timeval tv)
{
  return static_cast<double>(tv.tv_sec) +
      (static_cast<double>(tv.tv_usec) / 1000000.0);
}

/////////////////////////////////////////////////////////////////
// LOG TABLE IMPLEMENTATION
/////////////////////////////////////////////////////////////////

template<class TUPLE>
logtable<TUPLE>::logtable(pageid_t internal_region_size, pageid_t datapage_region_size, pageid_t datapage_size)
{

    tree_c0 = NULL;
    tree_c0_mergeable = NULL;
    tree_c1 = NULL;
    tree_c1_mergeable = NULL;
    tree_c2 = NULL;
    this->still_running_ = true;
    this->mergedata = 0;
    //tmerger = new tuplemerger(&append_merger);
    tmerger = new tuplemerger(&replace_merger);

    header_lock = initlock();

    tsize = 0;
    tree_bytes = 0;

    epoch = 0;

    this->internal_region_size = internal_region_size;
    this->datapage_region_size = datapage_region_size;
    this->datapage_size = datapage_size;
}

template<class TUPLE>
logtable<TUPLE>::~logtable()
{
    if(tree_c1 != NULL)
        delete tree_c1;
    if(tree_c2 != NULL)
        delete tree_c2;

    if(tree_c0 != NULL)
    {
      memTreeComponent<datatuple>::tearDownTree(tree_c0);
    }

    deletelock(header_lock);
    delete tmerger;
}

template<class TUPLE>
recordid logtable<TUPLE>::allocTable(int xid)
{

    table_rec = Talloc(xid, sizeof(tbl_header));

    //create the big tree
    tree_c2 = new diskTreeComponent(xid, internal_region_size, datapage_region_size, datapage_size);

    //create the small tree
    tree_c1 = new diskTreeComponent(xid, internal_region_size, datapage_region_size, datapage_size);

    update_persistent_header(xid);

    return table_rec;
}
template<class TUPLE>
void logtable<TUPLE>::openTable(int xid, recordid rid) {
  table_rec = rid;
  Tread(xid, table_rec, &tbl_header);
  tree_c2 = new diskTreeComponent(xid, tbl_header.c2_root, tbl_header.c2_state, tbl_header.c2_dp_state);
  tree_c1 = new diskTreeComponent(xid, tbl_header.c1_root, tbl_header.c1_state, tbl_header.c1_dp_state);
}
template<class TUPLE>
void logtable<TUPLE>::update_persistent_header(int xid) {

	tbl_header.c2_root = tree_c2->get_root_rid();
    tbl_header.c2_dp_state = tree_c2->get_datapage_allocator_rid();
    tbl_header.c2_state = tree_c2->get_internal_node_allocator_rid();
    tbl_header.c1_root = tree_c1->get_root_rid();
    tbl_header.c1_dp_state = tree_c1->get_datapage_allocator_rid();
    tbl_header.c1_state = tree_c1->get_internal_node_allocator_rid();

    Tset(xid, table_rec, &tbl_header);
}

template<class TUPLE>
void logtable<TUPLE>::setMergeData(logtable_mergedata * mdata){
  this->mergedata = mdata;
  mdata->internal_region_size = internal_region_size;
  mdata->datapage_region_size = datapage_region_size;
  mdata->datapage_size = datapage_size;

  bump_epoch();
}

template<class TUPLE>
void logtable<TUPLE>::flushTable()
{
    struct timeval start_tv, stop_tv;
    double start, stop;

    static double last_start;
    static bool first = 1;
    static int merge_count = 0;

    gettimeofday(&start_tv,0);
    start = tv_to_double(start_tv);

    writelock(header_lock,0);
    pthread_mutex_lock(mergedata->rbtree_mut);

    int expmcount = merge_count;

    //this is for waiting the previous merger of the mem-tree
    //hopefullly this wont happen

    while(get_tree_c0_mergeable()) {
        unlock(header_lock);
        if(tree_bytes >= max_c0_size)
            pthread_cond_wait(mergedata->input_needed_cond, mergedata->rbtree_mut);
        else
        {
            pthread_mutex_unlock(mergedata->rbtree_mut);
            return;
        }

        pthread_mutex_unlock(mergedata->rbtree_mut);

        writelock(header_lock,0);
        pthread_mutex_lock(mergedata->rbtree_mut);

        if(expmcount != merge_count)
        {
            unlock(header_lock);
            pthread_mutex_unlock(mergedata->rbtree_mut);
            return;
        }

    }

    gettimeofday(&stop_tv,0);
    stop = tv_to_double(stop_tv);

    set_tree_c0_mergeable(get_tree_c0());

    pthread_cond_signal(mergedata->input_ready_cond);

    merge_count ++;
    set_tree_c0(new memTreeComponent<datatuple>::rbtree_t);

    tsize = 0;
    tree_bytes = 0;

    pthread_mutex_unlock(mergedata->rbtree_mut);
    unlock(header_lock);
    if(first)
    {
        printf("Blocked writes for %f sec\n", stop-start);
        first = 0;
    }
    else
    {
        printf("Blocked writes for %f sec (serviced writes for %f sec)\n",
               stop-start, start-last_start);
    }
    last_start = stop;

}

template<class TUPLE>
datatuple * logtable<TUPLE>::findTuple(int xid, const datatuple::key_t key, size_t keySize)
{
    //prepare a search tuple
	datatuple *search_tuple = datatuple::create(key, keySize);

    readlock(header_lock,0);
    pthread_mutex_lock(mergedata->rbtree_mut);

    datatuple *ret_tuple=0;

    //step 1: look in tree_c0
    memTreeComponent<datatuple>::rbtree_t::iterator rbitr = get_tree_c0()->find(search_tuple);
    if(rbitr != get_tree_c0()->end())
    {
        DEBUG("tree_c0 size %d\n", get_tree_c0()->size());
        ret_tuple = (*rbitr)->create_copy();
    }

    bool done = false;
    //step: 2 look into first in tree if exists (a first level merge going on)
    if(get_tree_c0_mergeable() != 0)
    {
        DEBUG("old mem tree not null %d\n", (*(mergedata->old_c0))->size());
        rbitr = get_tree_c0_mergeable()->find(search_tuple);
        if(rbitr != get_tree_c0_mergeable()->end())
        {
            datatuple *tuple = *rbitr;

            if(tuple->isDelete())  //tuple deleted
                done = true;  //return ret_tuple
            else if(ret_tuple != 0)  //merge the two
            {
                datatuple *mtuple = tmerger->merge(tuple, ret_tuple);  //merge the two
                datatuple::freetuple(ret_tuple); //free tuple from current tree
                ret_tuple = mtuple; //set return tuple to merge result
            }
            else //key first found in old mem tree
            {
            	ret_tuple = tuple->create_copy();
            }
            //we cannot free tuple from old-tree 'cos it is not a copy
        }
    }

    //release the memtree lock
    pthread_mutex_unlock(mergedata->rbtree_mut);

    //step 3: check c1
    if(!done)
    {
        datatuple *tuple_c1 = get_tree_c1()->findTuple(xid, key, keySize);
        if(tuple_c1 != NULL)
        {
            bool use_copy = false;
            if(tuple_c1->isDelete()) //tuple deleted
                done = true;
            else if(ret_tuple != 0) //merge the two
            {
                datatuple *mtuple = tmerger->merge(tuple_c1, ret_tuple);  //merge the two
                datatuple::freetuple(ret_tuple);  //free tuple from before
                ret_tuple = mtuple; //set return tuple to merge result
            }
            else //found for the first time
            {
                use_copy = true;
                ret_tuple = tuple_c1;
            }

            if(!use_copy)
            {
                datatuple::freetuple(tuple_c1); //free tuple from tree c1
            }
        }
    }

    //step 4: check old c1 if exists
    if(!done && get_tree_c1_mergeable() != 0)
    {
        DEBUG("old c1 tree not null\n");
        datatuple *tuple_oc1 = get_tree_c1_mergeable()->findTuple(xid, key, keySize);

        if(tuple_oc1 != NULL)
        {
            bool use_copy = false;
            if(tuple_oc1->isDelete())
                done = true;
            else if(ret_tuple != 0) //merge the two
            {
                datatuple *mtuple = tmerger->merge(tuple_oc1, ret_tuple);  //merge the two
                datatuple::freetuple(ret_tuple); //free tuple from before
                ret_tuple = mtuple; //set return tuple to merge result
            }
            else //found for the first time
            {
                use_copy = true;
                ret_tuple = tuple_oc1;
            }

            if(!use_copy)
            {
            	datatuple::freetuple(tuple_oc1); //free tuple from tree old c1
            }
        }
    }

    //step 5: check c2
    if(!done)
    {
        DEBUG("Not in old first disk tree\n");
        datatuple *tuple_c2 = get_tree_c2()->findTuple(xid, key, keySize);

        if(tuple_c2 != NULL)
        {
            bool use_copy = false;
            if(tuple_c2->isDelete())
                done = true;
            else if(ret_tuple != 0)
            {
                datatuple *mtuple = tmerger->merge(tuple_c2, ret_tuple);  //merge the two
                datatuple::freetuple(ret_tuple); //free tuple from before
                ret_tuple = mtuple; //set return tuple to merge result
            }
            else //found for the first time
            {
                use_copy = true;
                ret_tuple = tuple_c2;
            }

            if(!use_copy)
            {
            	datatuple::freetuple(tuple_c2);  //free tuple from tree c2
            }
        }
    }

    //pthread_mutex_unlock(mergedata->rbtree_mut);
    unlock(header_lock);
    datatuple::freetuple(search_tuple);
    return ret_tuple;

}

/*
 * returns the first record found with the matching key
 * (not to be used together with diffs)
 **/
template<class TUPLE>
datatuple * logtable<TUPLE>::findTuple_first(int xid, datatuple::key_t key, size_t keySize)
{
    //prepare a search tuple
    datatuple * search_tuple = datatuple::create(key, keySize);

    pthread_mutex_lock(mergedata->rbtree_mut);

    datatuple *ret_tuple=0;
    //step 1: look in tree_c0

    memTreeComponent<datatuple>::rbtree_t::iterator rbitr = get_tree_c0()->find(search_tuple);
    if(rbitr != get_tree_c0()->end())
    {
        DEBUG("tree_c0 size %d\n", tree_c0->size());
        ret_tuple = (*rbitr)->create_copy();

    }
    else
    {
        DEBUG("Not in mem tree %d\n", tree_c0->size());
        //step: 2 look into first in tree if exists (a first level merge going on)
        if(get_tree_c0_mergeable() != NULL)
        {
            DEBUG("old mem tree not null %d\n", (*(mergedata->old_c0))->size());
            rbitr = get_tree_c0_mergeable()->find(search_tuple);
            if(rbitr != get_tree_c0_mergeable()->end())
            {
                ret_tuple = (*rbitr)->create_copy();
            }
        }

        if(ret_tuple == 0)
        {
            DEBUG("Not in old mem tree\n");

            //step 3: check c1
            ret_tuple = get_tree_c1()->findTuple(xid, key, keySize);
        }

        if(ret_tuple == 0)
        {
            DEBUG("Not in first disk tree\n");

            //step 4: check old c1 if exists
            if( get_tree_c1_mergeable() != 0)
            {
              DEBUG("old c1 tree not null\n");
              ret_tuple = get_tree_c1_mergeable()->findTuple(xid, key, keySize);
            }

        }

        if(ret_tuple == 0)
        {
            DEBUG("Not in old first disk tree\n");

            //step 5: check c2
            ret_tuple = get_tree_c2()->findTuple(xid, key, keySize);
        }
    }

    pthread_mutex_unlock(mergedata->rbtree_mut);
    datatuple::freetuple(search_tuple);

    return ret_tuple;

}

template<class TUPLE>
void logtable<TUPLE>::insertTuple(datatuple *tuple)
{
    //lock the red-black tree
    readlock(header_lock,0);
    pthread_mutex_lock(mergedata->rbtree_mut);
    //find the previous tuple with same key in the memtree if exists
    memTreeComponent<datatuple>::rbtree_t::iterator rbitr = tree_c0->find(tuple);
    if(rbitr != tree_c0->end())
    {
        datatuple *pre_t = *rbitr;
        //do the merging
        datatuple *new_t = tmerger->merge(pre_t, tuple);
        tree_c0->erase(pre_t); //remove the previous tuple

        tree_c0->insert(new_t); //insert the new tuple

        //update the tree size (+ new_t size - pre_t size)
        tree_bytes += ((int64_t)new_t->byte_length() - (int64_t)pre_t->byte_length());

        datatuple::freetuple(pre_t); //free the previous tuple
    }
    else //no tuple with same key exists in mem-tree
    {

    	datatuple *t = tuple->create_copy();

        //insert tuple into the rbtree
        tree_c0->insert(t);
        tsize++;
        tree_bytes += t->byte_length() + RB_TREE_OVERHEAD;

    }

    //flushing logic
    if(tree_bytes >= max_c0_size )
    {
        DEBUG("tree size before merge %d tuples %lld bytes.\n", tsize, tree_bytes);
        pthread_mutex_unlock(mergedata->rbtree_mut);
        unlock(header_lock);
        flushTable();

        readlock(header_lock,0);
        pthread_mutex_lock(mergedata->rbtree_mut);
    }

    //unlock
    pthread_mutex_unlock(mergedata->rbtree_mut);
    unlock(header_lock);

    DEBUG("tree size %d tuples %lld bytes.\n", tsize, tree_bytes);
}

template<class TUPLE>
void logtable<TUPLE>::registerIterator(iterator * it) {
  its.push_back(it);
}
template<class TUPLE>
void logtable<TUPLE>::forgetIterator(iterator * it) {
  for(unsigned int i = 0; i < its.size(); i++) {
    if(its[i] == it) {
      its.erase(its.begin()+i);
      break;
    }
  }
}
template<class TUPLE>
void logtable<TUPLE>::bump_epoch() {
  assert(!trywritelock(header_lock,0));
  epoch++;
  for(unsigned int i = 0; i < its.size(); i++) {
    its[i]->invalidate();
  }
}
template class logtable<datatuple>;
<|endoftext|>


<commit_before>/*
   Copyright (C) 2017 Alexandr Akulich <akulichalexander@gmail.com>

   This file is a part of TelegramQt library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

 */

#include <QObject>

#include "AccountStorage.hpp"
#include "ClientConnection.hpp"
#include "ClientDhLayer.hpp"
#include "ClientRpcLayer.hpp"
#include "ClientSettings.hpp"
#include "CClientTcpTransport.hpp"
#include "Utils.hpp"
#include "SendPackageHelper.hpp"
#include "TelegramNamespace.hpp"
#include "TelegramUtils.hpp"
#include "CAppInformation.hpp"

#include <QTest>
#include <QSignalSpy>
#include <QLoggingCategory>

using namespace Telegram;

CAppInformation *getAppInfo()
{
    static CAppInformation *appInfo = nullptr;
    if (!appInfo) {
        appInfo = new CAppInformation();
        appInfo->setAppId(14617);
        appInfo->setAppHash(QLatin1String("e17ac360fd072f83d5d08db45ce9a121"));
        appInfo->setAppVersion(QLatin1String("0.1"));
        appInfo->setDeviceInfo(QLatin1String("pc"));
        appInfo->setOsInfo(QLatin1String("GNU/Linux"));
        appInfo->setLanguageCode(QLatin1String("en"));
    }
    return appInfo;
}

class tst_toOfficial : public QObject
{
    Q_OBJECT
public:
    explicit tst_toOfficial(QObject *parent = nullptr);

private slots:
    void testClientDhLayer();
};

class TestSendPackageHelper : public BaseSendPackageHelper
{
public:
    explicit TestSendPackageHelper(CTelegramTransport *transport) :
        BaseSendPackageHelper(),
        m_transport(transport)
    {
    }

    quint64 newMessageId(bool isReply) override
    {
        Q_UNUSED(isReply)
        quint64 ts = TelegramUtils::formatTimeStamp(QDateTime::currentMSecsSinceEpoch());
        ts &= ~quint64(3);
        return m_transport->getNewMessageId(ts);
    }

    void sendPackage(const QByteArray &package) override
    {
        return m_transport->sendPackage(package);
    }

protected:
    CTelegramTransport *m_transport;
};

tst_toOfficial::tst_toOfficial(QObject *parent) :
    QObject(parent)
{
    QLoggingCategory::setFilterRules(QStringLiteral("telegram.*.debug=true"));
}

void tst_toOfficial::testClientDhLayer()
{
    const auto serverConfig = Client::Settings::defaultServerConfiguration();
    QVERIFY(!serverConfig.isEmpty());
    const DcOption firstServer = serverConfig.first();
    Client::TcpTransport *transport = new Client::TcpTransport(this);

    TestSendPackageHelper *sendHelper = new TestSendPackageHelper(transport);
    Client::DhLayer *dhLayer = new Client::DhLayer(this);
    dhLayer->setSendPackageHelper(sendHelper);
    dhLayer->setServerRsaKey(Utils::loadHardcodedKey());

    connect(transport, &Client::TcpTransport::packageReceived, [dhLayer](const QByteArray &package) {
        if (package.size() < 8) {
            qWarning() << "Received package is too small to process:" << package.toHex();
            return;
        }
        const quint64 *authKeyIdBytes = reinterpret_cast<const quint64*>(package.constData());
        if (*authKeyIdBytes) {
            qDebug() << "Received a package for RPC Layer";
//            rpcLayer->processPackage(package);
        } else {
            qDebug() << "Received a plain package for DH Layer";
            dhLayer->processPlainPackage(package);
        }
    });

    transport->connectToHost(firstServer.address, firstServer.port);
    QTRY_VERIFY(transport->state() == QAbstractSocket::ConnectedState);

    dhLayer->init();

    PendingRpcOperation *operationReqPq = dhLayer->requestPqAuthorization();
    QTRY_VERIFY(operationReqPq->isFinished());
    QVERIFY(operationReqPq->isSucceeded());
    QVERIFY(dhLayer->acceptPqAuthorization(operationReqPq->replyData()));

    PendingRpcOperation *operationReqDhParams = dhLayer->requestDhParameters();
    QTRY_VERIFY(operationReqDhParams->isFinished());
    QVERIFY(operationReqDhParams->isSucceeded());
    QVERIFY(dhLayer->acceptDhAnswer(operationReqDhParams->replyData()));

    PendingRpcOperation *operationSetClientDHParams = dhLayer->requestDhGenerationResult();
    QTRY_VERIFY(operationSetClientDHParams->isFinished());
    QVERIFY(operationSetClientDHParams->isSucceeded());
    QVERIFY(dhLayer->processServerDhAnswer(operationSetClientDHParams->replyData()));
}

QTEST_GUILESS_MAIN(tst_toOfficial)

#include "tst_toOfficial.moc"
<commit_msg>tst_toOfficial: Add a warning on a new kind of error received<commit_after>/*
   Copyright (C) 2017 Alexandr Akulich <akulichalexander@gmail.com>

   This file is a part of TelegramQt library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

 */

#include <QObject>

#include "AccountStorage.hpp"
#include "ClientConnection.hpp"
#include "ClientDhLayer.hpp"
#include "ClientRpcLayer.hpp"
#include "ClientSettings.hpp"
#include "CClientTcpTransport.hpp"
#include "Utils.hpp"
#include "SendPackageHelper.hpp"
#include "TelegramNamespace.hpp"
#include "TelegramUtils.hpp"
#include "CAppInformation.hpp"

#include <QTest>
#include <QSignalSpy>
#include <QLoggingCategory>

using namespace Telegram;

CAppInformation *getAppInfo()
{
    static CAppInformation *appInfo = nullptr;
    if (!appInfo) {
        appInfo = new CAppInformation();
        appInfo->setAppId(14617);
        appInfo->setAppHash(QLatin1String("e17ac360fd072f83d5d08db45ce9a121"));
        appInfo->setAppVersion(QLatin1String("0.1"));
        appInfo->setDeviceInfo(QLatin1String("pc"));
        appInfo->setOsInfo(QLatin1String("GNU/Linux"));
        appInfo->setLanguageCode(QLatin1String("en"));
    }
    return appInfo;
}

class tst_toOfficial : public QObject
{
    Q_OBJECT
public:
    explicit tst_toOfficial(QObject *parent = nullptr);

private slots:
    void testClientDhLayer();
};

class TestSendPackageHelper : public BaseSendPackageHelper
{
public:
    explicit TestSendPackageHelper(CTelegramTransport *transport) :
        BaseSendPackageHelper(),
        m_transport(transport)
    {
    }

    quint64 newMessageId(bool isReply) override
    {
        Q_UNUSED(isReply)
        quint64 ts = TelegramUtils::formatTimeStamp(QDateTime::currentMSecsSinceEpoch());
        ts &= ~quint64(3);
        return m_transport->getNewMessageId(ts);
    }

    void sendPackage(const QByteArray &package) override
    {
        return m_transport->sendPackage(package);
    }

protected:
    CTelegramTransport *m_transport;
};

tst_toOfficial::tst_toOfficial(QObject *parent) :
    QObject(parent)
{
    QLoggingCategory::setFilterRules(QStringLiteral("telegram.*.debug=true"));
}

void tst_toOfficial::testClientDhLayer()
{
    const auto serverConfig = Client::Settings::defaultServerConfiguration();
    QVERIFY(!serverConfig.isEmpty());
    const DcOption firstServer = serverConfig.first();
    Client::TcpTransport *transport = new Client::TcpTransport(this);

    TestSendPackageHelper *sendHelper = new TestSendPackageHelper(transport);
    Client::DhLayer *dhLayer = new Client::DhLayer(this);
    dhLayer->setSendPackageHelper(sendHelper);
    dhLayer->setServerRsaKey(Utils::loadHardcodedKey());

    connect(transport, &Client::TcpTransport::packageReceived, [dhLayer](const QByteArray &package) {
        if (package.size() == sizeof(quint32)) {
            qint32 errorCode = *(reinterpret_cast<const qint32 *>(package.constData()));
            qWarning() << "Error:" << errorCode;
            return;
        }
        if (package.size() < 8) {
            qWarning() << "Received package is too small to process:" << package.toHex();
            return;
        }
        const quint64 *authKeyIdBytes = reinterpret_cast<const quint64*>(package.constData());
        if (*authKeyIdBytes) {
            qDebug() << "Received a package for RPC Layer";
//            rpcLayer->processPackage(package);
        } else {
            qDebug() << "Received a plain package for DH Layer";
            dhLayer->processPlainPackage(package);
        }
    });

    transport->connectToHost(firstServer.address, firstServer.port);
    QTRY_VERIFY(transport->state() == QAbstractSocket::ConnectedState);

    dhLayer->init();

    PendingRpcOperation *operationReqPq = dhLayer->requestPqAuthorization();
    QTRY_VERIFY(operationReqPq->isFinished());
    QVERIFY(operationReqPq->isSucceeded());
    QVERIFY(dhLayer->acceptPqAuthorization(operationReqPq->replyData()));

    PendingRpcOperation *operationReqDhParams = dhLayer->requestDhParameters();
    QTRY_VERIFY(operationReqDhParams->isFinished());
    QVERIFY(operationReqDhParams->isSucceeded());
    QVERIFY(dhLayer->acceptDhAnswer(operationReqDhParams->replyData()));

    PendingRpcOperation *operationSetClientDHParams = dhLayer->requestDhGenerationResult();
    QTRY_VERIFY(operationSetClientDHParams->isFinished());
    QVERIFY(operationSetClientDHParams->isSucceeded());
    QVERIFY(dhLayer->processServerDhAnswer(operationSetClientDHParams->replyData()));
}

QTEST_GUILESS_MAIN(tst_toOfficial)

#include "tst_toOfficial.moc"
<|endoftext|>


<commit_before>#include "matrix.hpp"

matrix_t::matrix_t() noexcept : rows_{0}, columns_{0}, elements_{nullptr}
{
}

auto matrix_t::rows() -> unsigned int
{
    return rows_;
}

auto matrix_t::columns() -> unsigned int
{
    return columns_;
}
<commit_msg>Update matrix.cpp<commit_after>Matrix::Matrix()
{
	st = cl = 2;
	matr= new int*[st];
	for (int i = 0; i < st; i++)
		matr[i] = new int[cl]();
}

Matrix::Matrix(int m, int n) : st(m), cl(n)
{
	matr = new int *[m];
	for (int i = 0; i < m; i++)
		matr[i] = new int[n]();
}

Matrix::Matrix(Matrix &matrc)
{
	matr = new int*[matrc.st];
	for (int i = 0; i < matrc.st; i++)
		matr[i] = new int[matrc.cl];
	st = matrc.st;
	cl = matrc.cl;
	for (int i = 0; i < matrc.st; i++)
	for (int j = 0; j < matrc.cl; j++)
		matr[i][j] = matrc.matr[i][j];
}

Matrix::~Matrix()
{
	for (int i = 0; i < st; i++)
		delete[] matr[i];
	delete[] matr;
}

Matrix Matrix::operator+ (const Matrix &mat_2) const
{
	if (st != mat_2.st || cl != mat_2.cl)
	{
		cout << "Add error\n";
		system("pause");
		exit(1);
	}
	Matrix result(st, cl);
	for (int i = 0; i < st; i++)
	{
		for (int j = 0; j < cl; j++)
			result.matr[i][j] = matr[i][j] + mat_2.matr[i][j];
	}
	return result;
}
Matrix Matrix::operator*(const Matrix &mat_2) const
{
	if (st != mat_2.cl)
	{
		cout << "Mult error\n";
		system("pause");
		exit(1);
	}
	Matrix result(st, mat_2.cl);
	for (int i = 0; i < st; i++)
	for (int j = 0; j < mat_2.cl; j++)
	{
		result.matr[i][j] = 0;
		for (int k = 0; k < cl; k++)
			result.matr[i][j] += matr[i][k] * mat_2.matr[k][j];
	}
	return result;
}

Matrix& Matrix::operator =(Matrix &matrc)
{
	for (int i = 0; i < st; i++)
	for (int j = 0; j < cl; j++)
		matr[i][j] = matrc.matr[i][j];
	return *this;
}

bool Matrix::operator==(const Matrix& com)const
{
	bool res = true;
	if (st != com.st || cl != com.cl)
	{
		cout << "Comp error" << endl;
		system("pause");
		exit(1);
	}
	else
	{
		for (int i = 0; i < st; i++)
		for (int j = 0; j < cl; j++)
		if (matr[i][j] != com.matr[i][j])
			res = false;
	}
	return res;
}

istream& operator >> (istream& in, const Matrix& mat)
{
	for (int i = 0; i < mat.st; i++)
	for (int j = 0; j < mat.cl; j++)
		in >> mat.matr[i][j];
	return in;
}

ostream& operator << (ostream& out, const Matrix& mat)
{
	for (int i = 0; i < mat.st; i++)
	{
		for (int j = 0; j < mat.cl; j++)
			out << " " << mat.matr[i][j];
		out << endl;
	}
	return out;
}
<|endoftext|>


<commit_before>// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#include "catch.hpp"
#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace mfem;

namespace ea_kernels
{

void velocity_function(const Vector &x, Vector &v)
{
   int dim = x.Size();
   switch (dim)
   {
      case 1: v(0) = 1.0; break;
      case 2: v(0) = x(1); v(1) = -x(0); break;
      case 3: v(0) = x(1); v(1) = -x(0); v(2) = x(0); break;
   }
}

void AddConvectionIntegrators(BilinearForm &k, VectorCoefficient &velocity,
                              bool dg)
{
   k.AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));

   if (dg)
   {
      k.AddInteriorFaceIntegrator(
         new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
      k.AddBdrFaceIntegrator(
         new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
   }
}

void test_assembly_level(Mesh &&mesh, int order, bool dg, const int pb,
                         const AssemblyLevel assembly)
{
   mesh.EnsureNodes();
   mesh.SetCurvature(mesh.GetNodalFESpace()->GetOrder(0),
                     mesh.GetNodalFESpace()->IsDGSpace());
   int dim = mesh.Dimension();

   FiniteElementCollection *fec;
   if (dg)
   {
      fec = new L2_FECollection(order, dim, BasisType::GaussLobatto);
   }
   else
   {
      fec = new H1_FECollection(order, dim);
   }

   FiniteElementSpace fespace(&mesh, fec);

   BilinearForm k_test(&fespace);
   BilinearForm k_ref(&fespace);

   ConstantCoefficient one(1.0);
   VectorFunctionCoefficient vel_coeff(dim, velocity_function);

   if (pb==0) // Mass
   {
      k_ref.AddDomainIntegrator(new MassIntegrator(one));
      k_test.AddDomainIntegrator(new MassIntegrator(one));
   }
   else if (pb==1) // Convection
   {
      AddConvectionIntegrators(k_ref, vel_coeff, dg);
      AddConvectionIntegrators(k_test, vel_coeff, dg);
   }
   else if (pb==2) // Diffusion
   {
      k_ref.AddDomainIntegrator(new DiffusionIntegrator(one));
      k_test.AddDomainIntegrator(new DiffusionIntegrator(one));
   }

   k_ref.Assemble();
   k_ref.Finalize();

   k_test.SetAssemblyLevel(assembly);
   k_test.Assemble();

   GridFunction x(&fespace), y_ref(&fespace), y_test(&fespace);

   x.Randomize(1);

   k_ref.Mult(x,y_ref);
   k_test.Mult(x,y_test);

   y_test -= y_ref;

   REQUIRE(y_test.Norml2() < 1.e-12);

   delete fec;
}

TEST_CASE("Assembly Levels", "[AssemblyLevel]")
{
   SECTION("Continuous Galerkin")
   {
      const bool dg = false;
      SECTION("2D")
      {
         for (AssemblyLevel assembly : {AssemblyLevel::PARTIAL,AssemblyLevel::ELEMENT,AssemblyLevel::FULL})
         {
            for (int pb : {0, 1, 2})
            {
               for (int order : {2, 3, 4})
               {
                  test_assembly_level(Mesh("../../data/inline-quad.mesh", 1, 1),
                                       order, dg, pb, assembly);
                  test_assembly_level(Mesh("../../data/periodic-hexagon.mesh", 1, 1),
                                       order, dg, pb, assembly);
                  test_assembly_level(Mesh("../../data/star-q3.mesh", 1, 1),
                                       order, dg, pb, assembly);
               }
            }
         }
      }
      SECTION("3D")
      {
         for (AssemblyLevel assembly : {AssemblyLevel::PARTIAL,AssemblyLevel::ELEMENT,AssemblyLevel::FULL})
         {
            for (int pb : {0, 1, 2})
            {
               int order = 2;
               test_assembly_level(Mesh("../../data/inline-hex.mesh", 1, 1),
                                    order, dg, pb, assembly);
               test_assembly_level(Mesh("../../data/fichera-q3.mesh", 1, 1),
                                    order, dg, pb, assembly);
            }
         }
      }
      SECTION("AMR 2D")
      {
         for (AssemblyLevel assembly : {AssemblyLevel::PARTIAL,AssemblyLevel::ELEMENT,AssemblyLevel::FULL})
         {
            for (int pb : {0, 1, 2})
            {
               for (int order : {2, 3, 4})
               {
                  test_assembly_level(Mesh("../../data/amr-quad.mesh", 1, 1),
                                       order, false, 0, assembly);
               }
            }
         }
      }
      SECTION("AMR 3D")
      {
         for (AssemblyLevel assembly : {AssemblyLevel::PARTIAL,AssemblyLevel::ELEMENT,AssemblyLevel::FULL})
         {
            for (int pb : {0, 1, 2})
            {
               int order = 2;
               test_assembly_level(Mesh("../../data/fichera-amr.mesh", 1, 1),
                                    order, false, 0, assembly);
            }
         }
      }
   }

   SECTION("Discontinuous Galerkin")
   {
      const bool dg = true;
      SECTION("2D")
      {
         for (AssemblyLevel assembly : {AssemblyLevel::PARTIAL,AssemblyLevel::ELEMENT,AssemblyLevel::FULL})
         {
            for (int pb : {0, 1})
            {
               for (int order : {2, 3, 4})
               {
                  test_assembly_level(Mesh("../../data/periodic-square.mesh", 1, 1),
                                       order, dg, pb, assembly);
                  test_assembly_level(Mesh("../../data/periodic-hexagon.mesh", 1, 1),
                                       order, dg, pb, assembly);
                  test_assembly_level(Mesh("../../data/star-q3.mesh", 1, 1),
                                       order, dg, pb, assembly);
               }
            }
         }
      }
      SECTION("3D")
      {
         for (AssemblyLevel assembly : {AssemblyLevel::PARTIAL,AssemblyLevel::ELEMENT,AssemblyLevel::FULL})
         {
            for (int pb : {0, 1})
            {
               for (bool dg : {true, false})
               {
                  int order = 2;
                  test_assembly_level(Mesh("../../data/periodic-cube.mesh", 1, 1),
                                       order, dg, pb, assembly);
                  test_assembly_level(Mesh("../../data/fichera-q3.mesh", 1, 1),
                                       order, dg, pb, assembly);
               }
            }
         }
      }
   }
} // test case

} // namespace pa_kernels
<commit_msg>Remove unnecessary SetCurvature.<commit_after>// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#include "catch.hpp"
#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace mfem;

namespace ea_kernels
{

void velocity_function(const Vector &x, Vector &v)
{
   int dim = x.Size();
   switch (dim)
   {
      case 1: v(0) = 1.0; break;
      case 2: v(0) = x(1); v(1) = -x(0); break;
      case 3: v(0) = x(1); v(1) = -x(0); v(2) = x(0); break;
   }
}

void AddConvectionIntegrators(BilinearForm &k, VectorCoefficient &velocity,
                              bool dg)
{
   k.AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));

   if (dg)
   {
      k.AddInteriorFaceIntegrator(
         new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
      k.AddBdrFaceIntegrator(
         new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
   }
}

void test_assembly_level(Mesh &&mesh, int order, bool dg, const int pb,
                         const AssemblyLevel assembly)
{
   mesh.EnsureNodes();
   int dim = mesh.Dimension();

   FiniteElementCollection *fec;
   if (dg)
   {
      fec = new L2_FECollection(order, dim, BasisType::GaussLobatto);
   }
   else
   {
      fec = new H1_FECollection(order, dim);
   }

   FiniteElementSpace fespace(&mesh, fec);

   BilinearForm k_test(&fespace);
   BilinearForm k_ref(&fespace);

   ConstantCoefficient one(1.0);
   VectorFunctionCoefficient vel_coeff(dim, velocity_function);

   if (pb==0) // Mass
   {
      k_ref.AddDomainIntegrator(new MassIntegrator(one));
      k_test.AddDomainIntegrator(new MassIntegrator(one));
   }
   else if (pb==1) // Convection
   {
      AddConvectionIntegrators(k_ref, vel_coeff, dg);
      AddConvectionIntegrators(k_test, vel_coeff, dg);
   }
   else if (pb==2) // Diffusion
   {
      k_ref.AddDomainIntegrator(new DiffusionIntegrator(one));
      k_test.AddDomainIntegrator(new DiffusionIntegrator(one));
   }

   k_ref.Assemble();
   k_ref.Finalize();

   k_test.SetAssemblyLevel(assembly);
   k_test.Assemble();

   GridFunction x(&fespace), y_ref(&fespace), y_test(&fespace);

   x.Randomize(1);

   k_ref.Mult(x,y_ref);
   k_test.Mult(x,y_test);

   y_test -= y_ref;

   REQUIRE(y_test.Norml2() < 1.e-12);

   delete fec;
}

TEST_CASE("Assembly Levels", "[AssemblyLevel]")
{
   SECTION("Continuous Galerkin")
   {
      const bool dg = false;
      SECTION("2D")
      {
         for (AssemblyLevel assembly : {AssemblyLevel::PARTIAL,AssemblyLevel::ELEMENT,AssemblyLevel::FULL})
         {
            for (int pb : {0, 1, 2})
            {
               for (int order : {2, 3, 4})
               {
                  test_assembly_level(Mesh("../../data/inline-quad.mesh", 1, 1),
                                       order, dg, pb, assembly);
                  test_assembly_level(Mesh("../../data/periodic-hexagon.mesh", 1, 1),
                                       order, dg, pb, assembly);
                  test_assembly_level(Mesh("../../data/star-q3.mesh", 1, 1),
                                       order, dg, pb, assembly);
               }
            }
         }
      }
      SECTION("3D")
      {
         for (AssemblyLevel assembly : {AssemblyLevel::PARTIAL,AssemblyLevel::ELEMENT,AssemblyLevel::FULL})
         {
            for (int pb : {0, 1, 2})
            {
               int order = 2;
               test_assembly_level(Mesh("../../data/inline-hex.mesh", 1, 1),
                                    order, dg, pb, assembly);
               test_assembly_level(Mesh("../../data/fichera-q3.mesh", 1, 1),
                                    order, dg, pb, assembly);
            }
         }
      }
      SECTION("AMR 2D")
      {
         for (AssemblyLevel assembly : {AssemblyLevel::PARTIAL,AssemblyLevel::ELEMENT,AssemblyLevel::FULL})
         {
            for (int pb : {0, 1, 2})
            {
               for (int order : {2, 3, 4})
               {
                  test_assembly_level(Mesh("../../data/amr-quad.mesh", 1, 1),
                                       order, false, 0, assembly);
               }
            }
         }
      }
      SECTION("AMR 3D")
      {
         for (AssemblyLevel assembly : {AssemblyLevel::PARTIAL,AssemblyLevel::ELEMENT,AssemblyLevel::FULL})
         {
            for (int pb : {0, 1, 2})
            {
               int order = 2;
               test_assembly_level(Mesh("../../data/fichera-amr.mesh", 1, 1),
                                    order, false, 0, assembly);
            }
         }
      }
   }

   SECTION("Discontinuous Galerkin")
   {
      const bool dg = true;
      SECTION("2D")
      {
         for (AssemblyLevel assembly : {AssemblyLevel::PARTIAL,AssemblyLevel::ELEMENT,AssemblyLevel::FULL})
         {
            for (int pb : {0, 1})
            {
               for (int order : {2, 3, 4})
               {
                  test_assembly_level(Mesh("../../data/periodic-square.mesh", 1, 1),
                                       order, dg, pb, assembly);
                  test_assembly_level(Mesh("../../data/periodic-hexagon.mesh", 1, 1),
                                       order, dg, pb, assembly);
                  test_assembly_level(Mesh("../../data/star-q3.mesh", 1, 1),
                                       order, dg, pb, assembly);
               }
            }
         }
      }
      SECTION("3D")
      {
         for (AssemblyLevel assembly : {AssemblyLevel::PARTIAL,AssemblyLevel::ELEMENT,AssemblyLevel::FULL})
         {
            for (int pb : {0, 1})
            {
               for (bool dg : {true, false})
               {
                  int order = 2;
                  test_assembly_level(Mesh("../../data/periodic-cube.mesh", 1, 1),
                                       order, dg, pb, assembly);
                  test_assembly_level(Mesh("../../data/fichera-q3.mesh", 1, 1),
                                       order, dg, pb, assembly);
               }
            }
         }
      }
   }
} // test case

} // namespace pa_kernels
<|endoftext|>


<commit_before>//===-- LLVMSymbolize.cpp -------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implementation for LLVM symbolization library.
//
//===----------------------------------------------------------------------===//

#include "LLVMSymbolize.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"

#include <sstream>

namespace llvm {
namespace symbolize {

static bool error(error_code ec) {
  if (!ec)
    return false;
  errs() << "LLVMSymbolizer: error reading file: " << ec.message() << ".\n";
  return true;
}

static uint32_t
getDILineInfoSpecifierFlags(const LLVMSymbolizer::Options &Opts) {
  uint32_t Flags = llvm::DILineInfoSpecifier::FileLineInfo |
                   llvm::DILineInfoSpecifier::AbsoluteFilePath;
  if (Opts.PrintFunctions)
    Flags |= llvm::DILineInfoSpecifier::FunctionName;
  return Flags;
}

static void patchFunctionNameInDILineInfo(const std::string &NewFunctionName,
                                          DILineInfo &LineInfo) {
  std::string FileName = LineInfo.getFileName();
  LineInfo = DILineInfo(StringRef(FileName), StringRef(NewFunctionName),
                        LineInfo.getLine(), LineInfo.getColumn());
}

ModuleInfo::ModuleInfo(ObjectFile *Obj, DIContext *DICtx)
    : Module(Obj), DebugInfoContext(DICtx) {
  error_code ec;
  for (symbol_iterator si = Module->begin_symbols(), se = Module->end_symbols();
       si != se; si.increment(ec)) {
    if (error(ec))
      return;
    SymbolRef::Type SymbolType;
    if (error(si->getType(SymbolType)))
      continue;
    if (SymbolType != SymbolRef::ST_Function &&
        SymbolType != SymbolRef::ST_Data)
      continue;
    uint64_t SymbolAddress;
    if (error(si->getAddress(SymbolAddress)) ||
        SymbolAddress == UnknownAddressOrSize)
      continue;
    uint64_t SymbolSize;
    // Getting symbol size is linear for Mach-O files, so assume that symbol
    // occupies the memory range up to the following symbol.
    if (isa<MachOObjectFile>(Obj))
      SymbolSize = 0;
    else if (error(si->getSize(SymbolSize)) ||
             SymbolSize == UnknownAddressOrSize)
      continue;
    StringRef SymbolName;
    if (error(si->getName(SymbolName)))
      continue;
    // Mach-O symbol table names have leading underscore, skip it.
    if (Module->isMachO() && SymbolName.size() > 0 && SymbolName[0] == '_')
      SymbolName = SymbolName.drop_front();
    // FIXME: If a function has alias, there are two entries in symbol table
    // with same address size. Make sure we choose the correct one.
    SymbolMapTy &M = SymbolType == SymbolRef::ST_Function ? Functions : Objects;
    SymbolDesc SD = { SymbolAddress, SymbolSize };
    M.insert(std::make_pair(SD, SymbolName));
  }
}

bool ModuleInfo::getNameFromSymbolTable(SymbolRef::Type Type, uint64_t Address,
                                        std::string &Name, uint64_t &Addr,
                                        uint64_t &Size) const {
  const SymbolMapTy &M = Type == SymbolRef::ST_Function ? Functions : Objects;
  if (M.empty())
    return false;
  SymbolDesc SD = { Address, Address };
  SymbolMapTy::const_iterator it = M.upper_bound(SD);
  if (it == M.begin())
    return false;
  --it;
  if (it->first.Size != 0 && it->first.Addr + it->first.Size <= Address)
    return false;
  Name = it->second.str();
  Addr = it->first.Addr;
  Size = it->first.Size;
  return true;
}

DILineInfo ModuleInfo::symbolizeCode(
    uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const {
  DILineInfo LineInfo;
  if (DebugInfoContext) {
    LineInfo = DebugInfoContext->getLineInfoForAddress(
        ModuleOffset, getDILineInfoSpecifierFlags(Opts));
  }
  // Override function name from symbol table if necessary.
  if (Opts.PrintFunctions && Opts.UseSymbolTable) {
    std::string FunctionName;
    uint64_t Start, Size;
    if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset,
                               FunctionName, Start, Size)) {
      patchFunctionNameInDILineInfo(FunctionName, LineInfo);
    }
  }
  return LineInfo;
}

DIInliningInfo ModuleInfo::symbolizeInlinedCode(
    uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const {
  DIInliningInfo InlinedContext;
  if (DebugInfoContext) {
    InlinedContext = DebugInfoContext->getInliningInfoForAddress(
        ModuleOffset, getDILineInfoSpecifierFlags(Opts));
  }
  // Make sure there is at least one frame in context.
  if (InlinedContext.getNumberOfFrames() == 0) {
    InlinedContext.addFrame(DILineInfo());
  }
  // Override the function name in lower frame with name from symbol table.
  if (Opts.PrintFunctions && Opts.UseSymbolTable) {
    DIInliningInfo PatchedInlinedContext;
    for (uint32_t i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) {
      DILineInfo LineInfo = InlinedContext.getFrame(i);
      if (i == n - 1) {
        std::string FunctionName;
        uint64_t Start, Size;
        if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset,
                                   FunctionName, Start, Size)) {
          patchFunctionNameInDILineInfo(FunctionName, LineInfo);
        }
      }
      PatchedInlinedContext.addFrame(LineInfo);
    }
    InlinedContext = PatchedInlinedContext;
  }
  return InlinedContext;
}

bool ModuleInfo::symbolizeData(uint64_t ModuleOffset, std::string &Name,
                               uint64_t &Start, uint64_t &Size) const {
  return getNameFromSymbolTable(SymbolRef::ST_Data, ModuleOffset, Name, Start,
                                Size);
}

const char LLVMSymbolizer::kBadString[] = "??";

std::string LLVMSymbolizer::symbolizeCode(const std::string &ModuleName,
                                          uint64_t ModuleOffset) {
  ModuleInfo *Info = getOrCreateModuleInfo(ModuleName);
  if (Info == 0)
    return printDILineInfo(DILineInfo());
  if (Opts.PrintInlining) {
    DIInliningInfo InlinedContext =
        Info->symbolizeInlinedCode(ModuleOffset, Opts);
    uint32_t FramesNum = InlinedContext.getNumberOfFrames();
    assert(FramesNum > 0);
    std::string Result;
    for (uint32_t i = 0; i < FramesNum; i++) {
      DILineInfo LineInfo = InlinedContext.getFrame(i);
      Result += printDILineInfo(LineInfo);
    }
    return Result;
  }
  DILineInfo LineInfo = Info->symbolizeCode(ModuleOffset, Opts);
  return printDILineInfo(LineInfo);
}

std::string LLVMSymbolizer::symbolizeData(const std::string &ModuleName,
                                          uint64_t ModuleOffset) {
  std::string Name = kBadString;
  uint64_t Start = 0;
  uint64_t Size = 0;
  if (Opts.UseSymbolTable) {
    if (ModuleInfo *Info = getOrCreateModuleInfo(ModuleName)) {
      if (Info->symbolizeData(ModuleOffset, Name, Start, Size) && Opts.Demangle)
        Name = DemangleName(Name);
    }
  }
  std::stringstream ss;
  ss << Name << "\n" << Start << " " << Size << "\n";
  return ss.str();
}

void LLVMSymbolizer::flush() {
  DeleteContainerSeconds(Modules);
  DeleteContainerPointers(ParsedBinariesAndObjects);
  BinaryForPath.clear();
  ObjectFileForArch.clear();
}

static std::string getDarwinDWARFResourceForPath(const std::string &Path) {
  StringRef Basename = sys::path::filename(Path);
  const std::string &DSymDirectory = Path + ".dSYM";
  SmallString<16> ResourceName = StringRef(DSymDirectory);
  sys::path::append(ResourceName, "Contents", "Resources", "DWARF");
  sys::path::append(ResourceName, Basename);
  return ResourceName.str();
}

LLVMSymbolizer::BinaryPair
LLVMSymbolizer::getOrCreateBinary(const std::string &Path) {
  BinaryMapTy::iterator I = BinaryForPath.find(Path);
  if (I != BinaryForPath.end())
    return I->second;
  Binary *Bin = 0;
  Binary *DbgBin = 0;
  OwningPtr<Binary> ParsedBinary;
  OwningPtr<Binary> ParsedDbgBinary;
  if (!error(createBinary(Path, ParsedBinary))) {
    // Check if it's a universal binary.
    Bin = ParsedBinary.take();
    ParsedBinariesAndObjects.push_back(Bin);
    if (Bin->isMachO() || Bin->isMachOUniversalBinary()) {
      // On Darwin we may find DWARF in separate object file in
      // resource directory.
      const std::string &ResourcePath =
          getDarwinDWARFResourceForPath(Path);
      bool ResourceFileExists = false;
      if (!sys::fs::exists(ResourcePath, ResourceFileExists) &&
          ResourceFileExists &&
          !error(createBinary(ResourcePath, ParsedDbgBinary))) {
        DbgBin = ParsedDbgBinary.take();
        ParsedBinariesAndObjects.push_back(DbgBin);
      }
    }
  }
  if (DbgBin == 0)
    DbgBin = Bin;
  BinaryPair Res = std::make_pair(Bin, DbgBin);
  BinaryForPath[Path] = Res;
  return Res;
}

ObjectFile *
LLVMSymbolizer::getObjectFileFromBinary(Binary *Bin, const std::string &ArchName) {
  if (Bin == 0)
    return 0;
  ObjectFile *Res = 0;
  if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(Bin)) {
    ObjectFileForArchMapTy::iterator I = ObjectFileForArch.find(
        std::make_pair(UB, ArchName));
    if (I != ObjectFileForArch.end())
      return I->second;
    OwningPtr<ObjectFile> ParsedObj;
    if (!UB->getObjectForArch(Triple(ArchName).getArch(), ParsedObj)) {
      Res = ParsedObj.take();
      ParsedBinariesAndObjects.push_back(Res);
    }
    ObjectFileForArch[std::make_pair(UB, ArchName)] = Res;
  } else if (Bin->isObject()) {
    Res = cast<ObjectFile>(Bin);
  }
  return Res;
}

ModuleInfo *
LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) {
  ModuleMapTy::iterator I = Modules.find(ModuleName);
  if (I != Modules.end())
    return I->second;
  std::string BinaryName = ModuleName;
  std::string ArchName = Opts.DefaultArch;
  size_t ColonPos = ModuleName.find(':');
#if defined(_WIN32)
  // Recognize a drive letter on win32.
  if (ColonPos == 1 && isalpha(ModuleName[0]))
    ColonPos = ModuleName.find(':', 2);
#endif
  if (ColonPos != std::string::npos) {
    BinaryName = ModuleName.substr(0, ColonPos);
    ArchName = ModuleName.substr(ColonPos + 1);
  }
  BinaryPair Binaries = getOrCreateBinary(BinaryName);
  ObjectFile *Obj = getObjectFileFromBinary(Binaries.first, ArchName);
  ObjectFile *DbgObj = getObjectFileFromBinary(Binaries.second, ArchName);

  if (Obj == 0) {
    // Failed to find valid object file.
    Modules.insert(make_pair(ModuleName, (ModuleInfo *)0));
    return 0;
  }
  DIContext *Context = DIContext::getDWARFContext(DbgObj);
  assert(Context);
  ModuleInfo *Info = new ModuleInfo(Obj, Context);
  Modules.insert(make_pair(ModuleName, Info));
  return Info;
}

std::string LLVMSymbolizer::printDILineInfo(DILineInfo LineInfo) const {
  // By default, DILineInfo contains "<invalid>" for function/filename it
  // cannot fetch. We replace it to "??" to make our output closer to addr2line.
  static const std::string kDILineInfoBadString = "<invalid>";
  std::stringstream Result;
  if (Opts.PrintFunctions) {
    std::string FunctionName = LineInfo.getFunctionName();
    if (FunctionName == kDILineInfoBadString)
      FunctionName = kBadString;
    else if (Opts.Demangle)
      FunctionName = DemangleName(FunctionName);
    Result << FunctionName << "\n";
  }
  std::string Filename = LineInfo.getFileName();
  if (Filename == kDILineInfoBadString)
    Filename = kBadString;
  Result << Filename << ":" << LineInfo.getLine() << ":" << LineInfo.getColumn()
         << "\n";
  return Result.str();
}

#if !defined(_MSC_VER)
// Assume that __cxa_demangle is provided by libcxxabi (except for Windows).
extern "C" char *__cxa_demangle(const char *mangled_name, char *output_buffer,
                                size_t *length, int *status);
#endif

std::string LLVMSymbolizer::DemangleName(const std::string &Name) {
#if !defined(_MSC_VER)
  int status = 0;
  char *DemangledName = __cxa_demangle(Name.c_str(), 0, 0, &status);
  if (status != 0)
    return Name;
  std::string Result = DemangledName;
  free(DemangledName);
  return Result;
#else
  return Name;
#endif
}

} // namespace symbolize
} // namespace llvm
<commit_msg>llvm-symbolizer: be more careful with colons in file names<commit_after>//===-- LLVMSymbolize.cpp -------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implementation for LLVM symbolization library.
//
//===----------------------------------------------------------------------===//

#include "LLVMSymbolize.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"

#include <sstream>

namespace llvm {
namespace symbolize {

static bool error(error_code ec) {
  if (!ec)
    return false;
  errs() << "LLVMSymbolizer: error reading file: " << ec.message() << ".\n";
  return true;
}

static uint32_t
getDILineInfoSpecifierFlags(const LLVMSymbolizer::Options &Opts) {
  uint32_t Flags = llvm::DILineInfoSpecifier::FileLineInfo |
                   llvm::DILineInfoSpecifier::AbsoluteFilePath;
  if (Opts.PrintFunctions)
    Flags |= llvm::DILineInfoSpecifier::FunctionName;
  return Flags;
}

static void patchFunctionNameInDILineInfo(const std::string &NewFunctionName,
                                          DILineInfo &LineInfo) {
  std::string FileName = LineInfo.getFileName();
  LineInfo = DILineInfo(StringRef(FileName), StringRef(NewFunctionName),
                        LineInfo.getLine(), LineInfo.getColumn());
}

ModuleInfo::ModuleInfo(ObjectFile *Obj, DIContext *DICtx)
    : Module(Obj), DebugInfoContext(DICtx) {
  error_code ec;
  for (symbol_iterator si = Module->begin_symbols(), se = Module->end_symbols();
       si != se; si.increment(ec)) {
    if (error(ec))
      return;
    SymbolRef::Type SymbolType;
    if (error(si->getType(SymbolType)))
      continue;
    if (SymbolType != SymbolRef::ST_Function &&
        SymbolType != SymbolRef::ST_Data)
      continue;
    uint64_t SymbolAddress;
    if (error(si->getAddress(SymbolAddress)) ||
        SymbolAddress == UnknownAddressOrSize)
      continue;
    uint64_t SymbolSize;
    // Getting symbol size is linear for Mach-O files, so assume that symbol
    // occupies the memory range up to the following symbol.
    if (isa<MachOObjectFile>(Obj))
      SymbolSize = 0;
    else if (error(si->getSize(SymbolSize)) ||
             SymbolSize == UnknownAddressOrSize)
      continue;
    StringRef SymbolName;
    if (error(si->getName(SymbolName)))
      continue;
    // Mach-O symbol table names have leading underscore, skip it.
    if (Module->isMachO() && SymbolName.size() > 0 && SymbolName[0] == '_')
      SymbolName = SymbolName.drop_front();
    // FIXME: If a function has alias, there are two entries in symbol table
    // with same address size. Make sure we choose the correct one.
    SymbolMapTy &M = SymbolType == SymbolRef::ST_Function ? Functions : Objects;
    SymbolDesc SD = { SymbolAddress, SymbolSize };
    M.insert(std::make_pair(SD, SymbolName));
  }
}

bool ModuleInfo::getNameFromSymbolTable(SymbolRef::Type Type, uint64_t Address,
                                        std::string &Name, uint64_t &Addr,
                                        uint64_t &Size) const {
  const SymbolMapTy &M = Type == SymbolRef::ST_Function ? Functions : Objects;
  if (M.empty())
    return false;
  SymbolDesc SD = { Address, Address };
  SymbolMapTy::const_iterator it = M.upper_bound(SD);
  if (it == M.begin())
    return false;
  --it;
  if (it->first.Size != 0 && it->first.Addr + it->first.Size <= Address)
    return false;
  Name = it->second.str();
  Addr = it->first.Addr;
  Size = it->first.Size;
  return true;
}

DILineInfo ModuleInfo::symbolizeCode(
    uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const {
  DILineInfo LineInfo;
  if (DebugInfoContext) {
    LineInfo = DebugInfoContext->getLineInfoForAddress(
        ModuleOffset, getDILineInfoSpecifierFlags(Opts));
  }
  // Override function name from symbol table if necessary.
  if (Opts.PrintFunctions && Opts.UseSymbolTable) {
    std::string FunctionName;
    uint64_t Start, Size;
    if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset,
                               FunctionName, Start, Size)) {
      patchFunctionNameInDILineInfo(FunctionName, LineInfo);
    }
  }
  return LineInfo;
}

DIInliningInfo ModuleInfo::symbolizeInlinedCode(
    uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const {
  DIInliningInfo InlinedContext;
  if (DebugInfoContext) {
    InlinedContext = DebugInfoContext->getInliningInfoForAddress(
        ModuleOffset, getDILineInfoSpecifierFlags(Opts));
  }
  // Make sure there is at least one frame in context.
  if (InlinedContext.getNumberOfFrames() == 0) {
    InlinedContext.addFrame(DILineInfo());
  }
  // Override the function name in lower frame with name from symbol table.
  if (Opts.PrintFunctions && Opts.UseSymbolTable) {
    DIInliningInfo PatchedInlinedContext;
    for (uint32_t i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) {
      DILineInfo LineInfo = InlinedContext.getFrame(i);
      if (i == n - 1) {
        std::string FunctionName;
        uint64_t Start, Size;
        if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset,
                                   FunctionName, Start, Size)) {
          patchFunctionNameInDILineInfo(FunctionName, LineInfo);
        }
      }
      PatchedInlinedContext.addFrame(LineInfo);
    }
    InlinedContext = PatchedInlinedContext;
  }
  return InlinedContext;
}

bool ModuleInfo::symbolizeData(uint64_t ModuleOffset, std::string &Name,
                               uint64_t &Start, uint64_t &Size) const {
  return getNameFromSymbolTable(SymbolRef::ST_Data, ModuleOffset, Name, Start,
                                Size);
}

const char LLVMSymbolizer::kBadString[] = "??";

std::string LLVMSymbolizer::symbolizeCode(const std::string &ModuleName,
                                          uint64_t ModuleOffset) {
  ModuleInfo *Info = getOrCreateModuleInfo(ModuleName);
  if (Info == 0)
    return printDILineInfo(DILineInfo());
  if (Opts.PrintInlining) {
    DIInliningInfo InlinedContext =
        Info->symbolizeInlinedCode(ModuleOffset, Opts);
    uint32_t FramesNum = InlinedContext.getNumberOfFrames();
    assert(FramesNum > 0);
    std::string Result;
    for (uint32_t i = 0; i < FramesNum; i++) {
      DILineInfo LineInfo = InlinedContext.getFrame(i);
      Result += printDILineInfo(LineInfo);
    }
    return Result;
  }
  DILineInfo LineInfo = Info->symbolizeCode(ModuleOffset, Opts);
  return printDILineInfo(LineInfo);
}

std::string LLVMSymbolizer::symbolizeData(const std::string &ModuleName,
                                          uint64_t ModuleOffset) {
  std::string Name = kBadString;
  uint64_t Start = 0;
  uint64_t Size = 0;
  if (Opts.UseSymbolTable) {
    if (ModuleInfo *Info = getOrCreateModuleInfo(ModuleName)) {
      if (Info->symbolizeData(ModuleOffset, Name, Start, Size) && Opts.Demangle)
        Name = DemangleName(Name);
    }
  }
  std::stringstream ss;
  ss << Name << "\n" << Start << " " << Size << "\n";
  return ss.str();
}

void LLVMSymbolizer::flush() {
  DeleteContainerSeconds(Modules);
  DeleteContainerPointers(ParsedBinariesAndObjects);
  BinaryForPath.clear();
  ObjectFileForArch.clear();
}

static std::string getDarwinDWARFResourceForPath(const std::string &Path) {
  StringRef Basename = sys::path::filename(Path);
  const std::string &DSymDirectory = Path + ".dSYM";
  SmallString<16> ResourceName = StringRef(DSymDirectory);
  sys::path::append(ResourceName, "Contents", "Resources", "DWARF");
  sys::path::append(ResourceName, Basename);
  return ResourceName.str();
}

LLVMSymbolizer::BinaryPair
LLVMSymbolizer::getOrCreateBinary(const std::string &Path) {
  BinaryMapTy::iterator I = BinaryForPath.find(Path);
  if (I != BinaryForPath.end())
    return I->second;
  Binary *Bin = 0;
  Binary *DbgBin = 0;
  OwningPtr<Binary> ParsedBinary;
  OwningPtr<Binary> ParsedDbgBinary;
  if (!error(createBinary(Path, ParsedBinary))) {
    // Check if it's a universal binary.
    Bin = ParsedBinary.take();
    ParsedBinariesAndObjects.push_back(Bin);
    if (Bin->isMachO() || Bin->isMachOUniversalBinary()) {
      // On Darwin we may find DWARF in separate object file in
      // resource directory.
      const std::string &ResourcePath =
          getDarwinDWARFResourceForPath(Path);
      bool ResourceFileExists = false;
      if (!sys::fs::exists(ResourcePath, ResourceFileExists) &&
          ResourceFileExists &&
          !error(createBinary(ResourcePath, ParsedDbgBinary))) {
        DbgBin = ParsedDbgBinary.take();
        ParsedBinariesAndObjects.push_back(DbgBin);
      }
    }
  }
  if (DbgBin == 0)
    DbgBin = Bin;
  BinaryPair Res = std::make_pair(Bin, DbgBin);
  BinaryForPath[Path] = Res;
  return Res;
}

ObjectFile *
LLVMSymbolizer::getObjectFileFromBinary(Binary *Bin, const std::string &ArchName) {
  if (Bin == 0)
    return 0;
  ObjectFile *Res = 0;
  if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(Bin)) {
    ObjectFileForArchMapTy::iterator I = ObjectFileForArch.find(
        std::make_pair(UB, ArchName));
    if (I != ObjectFileForArch.end())
      return I->second;
    OwningPtr<ObjectFile> ParsedObj;
    if (!UB->getObjectForArch(Triple(ArchName).getArch(), ParsedObj)) {
      Res = ParsedObj.take();
      ParsedBinariesAndObjects.push_back(Res);
    }
    ObjectFileForArch[std::make_pair(UB, ArchName)] = Res;
  } else if (Bin->isObject()) {
    Res = cast<ObjectFile>(Bin);
  }
  return Res;
}

ModuleInfo *
LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) {
  ModuleMapTy::iterator I = Modules.find(ModuleName);
  if (I != Modules.end())
    return I->second;
  std::string BinaryName = ModuleName;
  std::string ArchName = Opts.DefaultArch;
  size_t ColonPos = ModuleName.find_last_of(':');
  // Verify that substring after colon form a valid arch name.
  if (ColonPos != std::string::npos) {
    std::string ArchStr = ModuleName.substr(ColonPos + 1);
    if (Triple(ArchStr).getArch() != Triple::ArchType::UnknownArch) {
      BinaryName = ModuleName.substr(0, ColonPos);
      ArchName = ArchStr;
    }
  }
  BinaryPair Binaries = getOrCreateBinary(BinaryName);
  ObjectFile *Obj = getObjectFileFromBinary(Binaries.first, ArchName);
  ObjectFile *DbgObj = getObjectFileFromBinary(Binaries.second, ArchName);

  if (Obj == 0) {
    // Failed to find valid object file.
    Modules.insert(make_pair(ModuleName, (ModuleInfo *)0));
    return 0;
  }
  DIContext *Context = DIContext::getDWARFContext(DbgObj);
  assert(Context);
  ModuleInfo *Info = new ModuleInfo(Obj, Context);
  Modules.insert(make_pair(ModuleName, Info));
  return Info;
}

std::string LLVMSymbolizer::printDILineInfo(DILineInfo LineInfo) const {
  // By default, DILineInfo contains "<invalid>" for function/filename it
  // cannot fetch. We replace it to "??" to make our output closer to addr2line.
  static const std::string kDILineInfoBadString = "<invalid>";
  std::stringstream Result;
  if (Opts.PrintFunctions) {
    std::string FunctionName = LineInfo.getFunctionName();
    if (FunctionName == kDILineInfoBadString)
      FunctionName = kBadString;
    else if (Opts.Demangle)
      FunctionName = DemangleName(FunctionName);
    Result << FunctionName << "\n";
  }
  std::string Filename = LineInfo.getFileName();
  if (Filename == kDILineInfoBadString)
    Filename = kBadString;
  Result << Filename << ":" << LineInfo.getLine() << ":" << LineInfo.getColumn()
         << "\n";
  return Result.str();
}

#if !defined(_MSC_VER)
// Assume that __cxa_demangle is provided by libcxxabi (except for Windows).
extern "C" char *__cxa_demangle(const char *mangled_name, char *output_buffer,
                                size_t *length, int *status);
#endif

std::string LLVMSymbolizer::DemangleName(const std::string &Name) {
#if !defined(_MSC_VER)
  int status = 0;
  char *DemangledName = __cxa_demangle(Name.c_str(), 0, 0, &status);
  if (status != 0)
    return Name;
  std::string Result = DemangledName;
  free(DemangledName);
  return Result;
#else
  return Name;
#endif
}

} // namespace symbolize
} // namespace llvm
<|endoftext|>


<commit_before>// Author: Enrico Guiraud, Danilo Piparo CERN  09/2017

/*************************************************************************
 * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT_RDATASOURCE
#define ROOT_RDATASOURCE

#include "ROOT/RStringView.hxx"
#include "RtypesCore.h" // ULong64_t
#include "TString.h"

#include <algorithm> // std::transform
#include <string>
#include <typeinfo>
#include <vector>

namespace ROOT {
namespace RDF {
class RDataSource;
}
}

/// Print a RDataSource at the prompt
namespace cling {
std::string printValue(ROOT::RDF::RDataSource *ds);
} // namespace cling

namespace ROOT {

namespace Internal {
namespace TDS {

/// Mother class of TTypedPointerHolder. The instances
/// of this class can be put in a container. Upon destruction,
/// the correct deletion of the pointer is performed in the
/// derived class.
class TPointerHolder {
protected:
   void *fPointer{nullptr};

public:
   TPointerHolder(void *ptr) : fPointer(ptr) {}
   void *GetPointer() { return fPointer; }
   void *GetPointerAddr() { return &fPointer; }
   virtual TPointerHolder *GetDeepCopy() = 0;
   virtual ~TPointerHolder(){};
};

/// Class to wrap a pointer and delete the memory associated to it
/// correctly
template <typename T>
class TTypedPointerHolder final : public TPointerHolder {
public:
   TTypedPointerHolder(T *ptr) : TPointerHolder((void *)ptr) {}

   virtual TPointerHolder *GetDeepCopy()
   {
      const auto typedPtr = static_cast<T *>(fPointer);
      return new TTypedPointerHolder(new T(*typedPtr));
   }

   ~TTypedPointerHolder() { delete static_cast<T *>(fPointer); }
};

} // ns TDS
} // ns Internal

namespace RDF {

// clang-format off
/**
\class ROOT::RDF::RDataSource
\ingroup dataframe
\brief RDataSource defines an API that RDataFrame can use to read arbitrary data formats.

A concrete RDataSource implementation (i.e. a class that inherits from RDataSource and implements all of its pure
methods) provides an adaptor that RDataFrame can leverage to read any kind of tabular data formats.
RDataFrame calls into RDataSource to retrieve information about the data, retrieve (thread-local) readers or "cursors"
for selected columns and to advance the readers to the desired data entry.

The sequence of calls that RDataFrame (or any other client of a RDataSource) performs is the following:

 - SetNSlots() : inform RDataSource of the desired level of parallelism
 - GetColumnReaders() : retrieve from RDataSource per-thread readers for the desired columns
 - Initialise() : inform RDataSource that an event-loop is about to start
 - GetEntryRanges() : retrieve from RDataSource a set of ranges of entries that can be processed concurrently
 - InitSlot() : inform RDataSource that a certain thread is about to start working on a certain range of entries
 - SetEntry() : inform RDataSource that a certain thread is about to start working on a certain entry
 - FinaliseSlot() : inform RDataSource that a certain thread finished working on a certain range of entries
 - Finalise() : inform RDataSource that an event-loop finished

RDataSource implementations must support running multiple event-loops consecutively (although sequentially) on the same dataset.
 - \b SetNSlots() is called once per RDataSource object, typically when it is associated to a RDataFrame.
 - \b GetColumnReaders() can be called several times, potentially with the same arguments, also in-between event-loops, but not during an event-loop.
 - \b GetEntryRanges() will be called several times, including during an event loop, as additional ranges are needed.  It will not be called concurrently.
 - \b Initialise() and \b Finalise() are called once per event-loop,  right before starting and right after finishing.
 - \b InitSlot(), \b SetEntry(), and \b FinaliseSlot() can be called concurrently from multiple threads, multiple times per event-loop.
*/
class RDataSource {
   // clang-format on
protected:
   using Record_t = std::vector<void *>;
   friend std::string cling::printValue(::ROOT::RDF::RDataSource *);

   virtual std::string AsString() { return "generic data source"; };

public:
   virtual ~RDataSource() = default;

   // clang-format off
   /// \brief Inform RDataSource of the number of processing slots (i.e. worker threads) used by the associated RDataFrame.
   /// Slots numbers are used to simplify parallel execution: RDataFrame guarantees that different threads will always
   /// pass different slot values when calling methods concurrently.
   // clang-format on
   virtual void SetNSlots(unsigned int nSlots) = 0;

   // clang-format off
   /// \brief Returns a reference to the collection of the dataset's column names
   // clang-format on
   virtual const std::vector<std::string> &GetColumnNames() const = 0;

   /// \brief Checks if the dataset has a certain column
   /// \param[in] columnName The name of the column
   virtual bool HasColumn(std::string_view) const = 0;

   // clang-format off
   /// \brief Type of a column as a string, e.g. `GetTypeName("x") == "double"`. Required for jitting e.g. `df.Filter("x>0")`.
   /// \param[in] columnName The name of the column
   // clang-format on
   virtual std::string GetTypeName(std::string_view) const = 0;

   // clang-format off
   /// Called at most once per column by RDF. Return vector of pointers to pointers to column values - one per slot.
   /// \tparam T The type of the data stored in the column
   /// \param[in] columnName The name of the column
   ///
   /// These pointers are veritable cursors: it's a responsibility of the RDataSource implementation that they point to
   /// the "right" memory region.
   // clang-format on
   template <typename T>
   std::vector<T **> GetColumnReaders(std::string_view columnName)
   {
      auto typeErasedVec = GetColumnReadersImpl(columnName, typeid(T));
      std::vector<T **> typedVec(typeErasedVec.size());
      std::transform(typeErasedVec.begin(), typeErasedVec.end(), typedVec.begin(),
                     [](void *p) { return static_cast<T **>(p); });
      return typedVec;
   }

   // clang-format off
   /// \brief Return ranges of entries to distribute to tasks.
   /// They are required to be contiguous intervals with no entries skipped. Supposing a dataset with nEntries, the
   /// intervals must start at 0 and end at nEntries, e.g. [0-5],[5-10] for 10 entries.
   /// This function will be invoked repeatedly by RDataFrame as it needs additional entries to process.
   /// The same entry range should not be returned more than once.
   /// Returning an empty collection of ranges signals to RDataFrame that the processing can stop.
   // clang-format on
   virtual std::vector<std::pair<ULong64_t, ULong64_t>> GetEntryRanges() = 0;

   // clang-format off
   /// \brief Advance the "cursors" returned by GetColumnReaders to the selected entry for a particular slot.
   /// \param[in] slot The data processing slot that needs to be considered
   /// \param[in] entry The entry which needs to be pointed to by the reader pointers
   /// Slots are adopted to accommodate parallel data processing.
   /// Different workers will loop over different ranges and
   /// will be labelled by different "slot" values.
   /// Returns *true* if the entry has to be processed, *false* otherwise.
   // clang-format on
   virtual bool SetEntry(unsigned int slot, ULong64_t entry) = 0;

   // clang-format off
   /// \brief Convenience method called before starting an event-loop.
   /// This method might be called multiple times over the lifetime of a RDataSource, since
   /// users can run multiple event-loops with the same RDataFrame.
   /// Ideally, `Initialise` should set the state of the RDataSource so that multiple identical event-loops
   /// will produce identical results.
   // clang-format on
   virtual void Initialise() {}

   // clang-format off
   /// \brief Convenience method called at the start of the data processing associated to a slot.
   /// \param[in] slot The data processing slot wihch needs to be initialised
   /// \param[in] firstEntry The first entry of the range that the task will process.
   /// This method might be called multiple times per thread per event-loop.
   // clang-format on
   virtual void InitSlot(unsigned int /*slot*/, ULong64_t /*firstEntry*/) {}

   // clang-format off
   /// \brief Convenience method called at the end of the data processing associated to a slot.
   /// \param[in] slot The data processing slot wihch needs to be finalised
   /// This method might be called multiple times per thread per event-loop.
   // clang-format on
   virtual void FinaliseSlot(unsigned int /*slot*/) {}

   // clang-format off
   /// \brief Convenience method called after concluding an event-loop.
   /// See Initialise for more details.
   // clang-format on
   virtual void Finalise() {}

   /// \brief Return a string representation of the datasource type.
   /// The returned string will be used by ROOT::RDF::SaveGraph() to represent
   /// the datasource in the visualization of the computation graph.
   /// Concrete datasources can override the default implementation.
   virtual std::string GetLabel() { return "custom datasource"; }

protected:
   /// type-erased vector of pointers to pointers to column values - one per slot
   virtual Record_t GetColumnReadersImpl(std::string_view name, const std::type_info &) = 0;
};

} // ns RDF

} // ns ROOT

/// Print a RDataSource at the prompt
namespace cling {
inline std::string printValue(ROOT::RDF::RDataSource *ds)
{
   return ds->AsString();
}
} // namespace cling

#endif // ROOT_TDATASOURCE
<commit_msg>[DF][NFC] Better formatting for default datasource label<commit_after>// Author: Enrico Guiraud, Danilo Piparo CERN  09/2017

/*************************************************************************
 * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT_RDATASOURCE
#define ROOT_RDATASOURCE

#include "ROOT/RStringView.hxx"
#include "RtypesCore.h" // ULong64_t
#include "TString.h"

#include <algorithm> // std::transform
#include <string>
#include <typeinfo>
#include <vector>

namespace ROOT {
namespace RDF {
class RDataSource;
}
}

/// Print a RDataSource at the prompt
namespace cling {
std::string printValue(ROOT::RDF::RDataSource *ds);
} // namespace cling

namespace ROOT {

namespace Internal {
namespace TDS {

/// Mother class of TTypedPointerHolder. The instances
/// of this class can be put in a container. Upon destruction,
/// the correct deletion of the pointer is performed in the
/// derived class.
class TPointerHolder {
protected:
   void *fPointer{nullptr};

public:
   TPointerHolder(void *ptr) : fPointer(ptr) {}
   void *GetPointer() { return fPointer; }
   void *GetPointerAddr() { return &fPointer; }
   virtual TPointerHolder *GetDeepCopy() = 0;
   virtual ~TPointerHolder(){};
};

/// Class to wrap a pointer and delete the memory associated to it
/// correctly
template <typename T>
class TTypedPointerHolder final : public TPointerHolder {
public:
   TTypedPointerHolder(T *ptr) : TPointerHolder((void *)ptr) {}

   virtual TPointerHolder *GetDeepCopy()
   {
      const auto typedPtr = static_cast<T *>(fPointer);
      return new TTypedPointerHolder(new T(*typedPtr));
   }

   ~TTypedPointerHolder() { delete static_cast<T *>(fPointer); }
};

} // ns TDS
} // ns Internal

namespace RDF {

// clang-format off
/**
\class ROOT::RDF::RDataSource
\ingroup dataframe
\brief RDataSource defines an API that RDataFrame can use to read arbitrary data formats.

A concrete RDataSource implementation (i.e. a class that inherits from RDataSource and implements all of its pure
methods) provides an adaptor that RDataFrame can leverage to read any kind of tabular data formats.
RDataFrame calls into RDataSource to retrieve information about the data, retrieve (thread-local) readers or "cursors"
for selected columns and to advance the readers to the desired data entry.

The sequence of calls that RDataFrame (or any other client of a RDataSource) performs is the following:

 - SetNSlots() : inform RDataSource of the desired level of parallelism
 - GetColumnReaders() : retrieve from RDataSource per-thread readers for the desired columns
 - Initialise() : inform RDataSource that an event-loop is about to start
 - GetEntryRanges() : retrieve from RDataSource a set of ranges of entries that can be processed concurrently
 - InitSlot() : inform RDataSource that a certain thread is about to start working on a certain range of entries
 - SetEntry() : inform RDataSource that a certain thread is about to start working on a certain entry
 - FinaliseSlot() : inform RDataSource that a certain thread finished working on a certain range of entries
 - Finalise() : inform RDataSource that an event-loop finished

RDataSource implementations must support running multiple event-loops consecutively (although sequentially) on the same dataset.
 - \b SetNSlots() is called once per RDataSource object, typically when it is associated to a RDataFrame.
 - \b GetColumnReaders() can be called several times, potentially with the same arguments, also in-between event-loops, but not during an event-loop.
 - \b GetEntryRanges() will be called several times, including during an event loop, as additional ranges are needed.  It will not be called concurrently.
 - \b Initialise() and \b Finalise() are called once per event-loop,  right before starting and right after finishing.
 - \b InitSlot(), \b SetEntry(), and \b FinaliseSlot() can be called concurrently from multiple threads, multiple times per event-loop.
*/
class RDataSource {
   // clang-format on
protected:
   using Record_t = std::vector<void *>;
   friend std::string cling::printValue(::ROOT::RDF::RDataSource *);

   virtual std::string AsString() { return "generic data source"; };

public:
   virtual ~RDataSource() = default;

   // clang-format off
   /// \brief Inform RDataSource of the number of processing slots (i.e. worker threads) used by the associated RDataFrame.
   /// Slots numbers are used to simplify parallel execution: RDataFrame guarantees that different threads will always
   /// pass different slot values when calling methods concurrently.
   // clang-format on
   virtual void SetNSlots(unsigned int nSlots) = 0;

   // clang-format off
   /// \brief Returns a reference to the collection of the dataset's column names
   // clang-format on
   virtual const std::vector<std::string> &GetColumnNames() const = 0;

   /// \brief Checks if the dataset has a certain column
   /// \param[in] columnName The name of the column
   virtual bool HasColumn(std::string_view) const = 0;

   // clang-format off
   /// \brief Type of a column as a string, e.g. `GetTypeName("x") == "double"`. Required for jitting e.g. `df.Filter("x>0")`.
   /// \param[in] columnName The name of the column
   // clang-format on
   virtual std::string GetTypeName(std::string_view) const = 0;

   // clang-format off
   /// Called at most once per column by RDF. Return vector of pointers to pointers to column values - one per slot.
   /// \tparam T The type of the data stored in the column
   /// \param[in] columnName The name of the column
   ///
   /// These pointers are veritable cursors: it's a responsibility of the RDataSource implementation that they point to
   /// the "right" memory region.
   // clang-format on
   template <typename T>
   std::vector<T **> GetColumnReaders(std::string_view columnName)
   {
      auto typeErasedVec = GetColumnReadersImpl(columnName, typeid(T));
      std::vector<T **> typedVec(typeErasedVec.size());
      std::transform(typeErasedVec.begin(), typeErasedVec.end(), typedVec.begin(),
                     [](void *p) { return static_cast<T **>(p); });
      return typedVec;
   }

   // clang-format off
   /// \brief Return ranges of entries to distribute to tasks.
   /// They are required to be contiguous intervals with no entries skipped. Supposing a dataset with nEntries, the
   /// intervals must start at 0 and end at nEntries, e.g. [0-5],[5-10] for 10 entries.
   /// This function will be invoked repeatedly by RDataFrame as it needs additional entries to process.
   /// The same entry range should not be returned more than once.
   /// Returning an empty collection of ranges signals to RDataFrame that the processing can stop.
   // clang-format on
   virtual std::vector<std::pair<ULong64_t, ULong64_t>> GetEntryRanges() = 0;

   // clang-format off
   /// \brief Advance the "cursors" returned by GetColumnReaders to the selected entry for a particular slot.
   /// \param[in] slot The data processing slot that needs to be considered
   /// \param[in] entry The entry which needs to be pointed to by the reader pointers
   /// Slots are adopted to accommodate parallel data processing.
   /// Different workers will loop over different ranges and
   /// will be labelled by different "slot" values.
   /// Returns *true* if the entry has to be processed, *false* otherwise.
   // clang-format on
   virtual bool SetEntry(unsigned int slot, ULong64_t entry) = 0;

   // clang-format off
   /// \brief Convenience method called before starting an event-loop.
   /// This method might be called multiple times over the lifetime of a RDataSource, since
   /// users can run multiple event-loops with the same RDataFrame.
   /// Ideally, `Initialise` should set the state of the RDataSource so that multiple identical event-loops
   /// will produce identical results.
   // clang-format on
   virtual void Initialise() {}

   // clang-format off
   /// \brief Convenience method called at the start of the data processing associated to a slot.
   /// \param[in] slot The data processing slot wihch needs to be initialised
   /// \param[in] firstEntry The first entry of the range that the task will process.
   /// This method might be called multiple times per thread per event-loop.
   // clang-format on
   virtual void InitSlot(unsigned int /*slot*/, ULong64_t /*firstEntry*/) {}

   // clang-format off
   /// \brief Convenience method called at the end of the data processing associated to a slot.
   /// \param[in] slot The data processing slot wihch needs to be finalised
   /// This method might be called multiple times per thread per event-loop.
   // clang-format on
   virtual void FinaliseSlot(unsigned int /*slot*/) {}

   // clang-format off
   /// \brief Convenience method called after concluding an event-loop.
   /// See Initialise for more details.
   // clang-format on
   virtual void Finalise() {}

   /// \brief Return a string representation of the datasource type.
   /// The returned string will be used by ROOT::RDF::SaveGraph() to represent
   /// the datasource in the visualization of the computation graph.
   /// Concrete datasources can override the default implementation.
   virtual std::string GetLabel() { return "Custom Datasource"; }

protected:
   /// type-erased vector of pointers to pointers to column values - one per slot
   virtual Record_t GetColumnReadersImpl(std::string_view name, const std::type_info &) = 0;
};

} // ns RDF

} // ns ROOT

/// Print a RDataSource at the prompt
namespace cling {
inline std::string printValue(ROOT::RDF::RDataSource *ds)
{
   return ds->AsString();
}
} // namespace cling

#endif // ROOT_TDATASOURCE
<|endoftext|>