<commit_before>#include <algorithm>
#include <iostream>
#include <vector>
#include <cuddCdd.h>
#include <cudd/cuddInt.h>

using std::cout;
using std::cerr;
using std::endl;
using std::vector;
using std::pair;
using std::make_pair;

namespace BDDImpl {

class BDDConfig {
public:
  /// Pointer to the Bdd manager
  static DdManager *dd;
  /// Constant true
  static DdNode *one;
  /// Constant false
  static DdNode *zero;
  static int BBV;
  static int  BA;
  static int references(void) {
    return Cudd_CheckZeroRef(dd);
  }
};

// Initializate the manager
DdManager* BDDConfig::dd = Cudd_Init(0,0,CUDD_UNIQUE_SLOTS,CUDD_CACHE_SLOTS,0);
DdNode* BDDConfig::one = CDD_ONE(BDDConfig::dd);
DdNode* BDDConfig::zero = CDD_ZERO(BDDConfig::dd);
int BDDConfig::BBV = 5;
int BDDConfig::BA = 3;

  DdNode* encode(int p, int a) {
    DdNode *f = BDDConfig::one;
    DdNode *v, *tmp;
    Cudd_Ref(f);
    for (int i = (1 << BDDConfig::BBV); i--;) {
      v = Cudd_bddIthVar(BDDConfig::dd,(i << BDDConfig::BA)+a);
      tmp = Cudd_bddAnd(BDDConfig::dd, p&1 ? v : Cudd_Not(v),f);
      Cudd_Ref(tmp);
      Cudd_RecursiveDeref(BDDConfig::dd,f);
      f = tmp;
      p >>= 1;
    }
    return f;
  }
  DdNode* encode(const vector<int>& tuple) {
    DdNode *f = BDDConfig::one;
    DdNode *t, *tmp;
    Cudd_Ref(f);
    for (unsigned int i = 0; i < tuple.size(); i++) {
      t = encode(tuple[i],i);
      tmp = Cudd_bddAnd(BDDConfig::dd,f,t);
      Cudd_Ref(tmp);
      Cudd_RecursiveDeref(BDDConfig::dd,t);
      Cudd_RecursiveDeref(BDDConfig::dd,f);
      f = tmp;
    }
    return f;
  }

  /// Iterator on a Bdd representation
  class BddIter {
  private:
    /// The represented relation
    DdNode *relation_;
    /// Arity of the encoded relation
    int arity_;
    /// Undefined default constructor
    BddIter(void);
  public:
    BddIter(DdNode *relation, int arity)
      : relation_(relation), arity_(arity) {
      assert(relation != NULL);
      if (relation_ == BDDConfig::one ||
	  relation_ == BDDConfig::zero) {
	cout << "Creating iterator on empty or full" << endl;
      } else {
	Cudd_Ref(relation_);
      }
    }
    /// Copy constructor
    BddIter(const BddIter& i) : relation_(i.relation_), arity_(i.arity_) {}
    ~BddIter(void) {
      assert(relation_ != NULL);
      Cudd_RecursiveDeref(BDDConfig::dd, relation_);
    }
    bool operator()(void) const {
      return relation_ != BDDConfig::zero;
    }
    /// This is asingle pass iterator
    vector<int> val(void) {
      DdGen* gen;
      const int cube_size = 1<<(BDDConfig::BBV+BDDConfig::BA);
      const int tuple_size = 1<<BDDConfig::BA;
      int *cube = (int*)malloc(sizeof(int) * cube_size);
      int tuple[tuple_size];
      CUDD_VALUE_TYPE val;

      for(int k = 0; k < tuple_size; k++) tuple[k] = 0;

      gen = Cudd_FirstCube(BDDConfig::dd,relation_,&cube,&val);
      //Cudd_ForeachCube(BDDConfig::dd,relation_,gen,cube,val){
      for(int i = cube_size -1; i>=0; i--){
	if( (i & (tuple_size-1))<arity_){
	  tuple[i&(tuple_size-1)] &=
	    ~(1<<((1<<BDDConfig::BBV)-1-(i>>BDDConfig::BA)));
	  tuple[i&(tuple_size-1)] |=
	    (cube[i]&1)<<((1<<BDDConfig::BBV)-1-(i>>BDDConfig::BA));
	}
      }
      /// Prepare the output
      vector<int> out;
      out.resize(arity_);
      copy(tuple,tuple+arity_,out.begin());

      /// print out
      for(unsigned int j = 0; j < out.size(); j++) {
	cerr << tuple[j] << ",";
      }
      /// Affect the state of the iterator
      DdNode *i = Cudd_Not(encode(out));
      DdNode *tmp = Cudd_bddAnd(BDDConfig::dd,relation_,i);
      Cudd_Ref(tmp);
      Cudd_RecursiveDeref(BDDConfig::dd,i);
      Cudd_RecursiveDeref(BDDConfig::dd,relation_);
      relation_ = tmp;

      /// return t
      return out;
    }
    void operator++(void) {
      assert(false);
    }
  };

/// Class for Binary Decision Diagrams
class Bdd {
private:
  friend class BddIter;
  enum BddTerm { Bdd_Zero, Bdd_One, Bdd_Unk};
  /// Root node of the representation
  DdNode *node_;
public:
  /// Constructor from an existing node
  explicit Bdd(DdNode *n) : node_(n) {
    if (node_ != NULL) Cudd_Ref(node_);
  }
private:
  /// Construcotr for terminal \a t
  explicit Bdd(BddTerm t) {
    // This initialization cannot be done with the previous constructor because
    // when terminal are created they cannot be referenced, otherwise it will
    // report referenced nodes before finishing the program.
    switch(t) {
    case Bdd_Zero: node_ = BDDConfig::zero; break;
    case Bdd_One:  node_ = BDDConfig::one; break;
    default:
      assert(false); break;
    }
  }

public:
  /// Default constructor that contains no representation
  Bdd(void) : node_(NULL) {}
  /// Constructor from an existing representation
  Bdd(const Bdd& r) : node_(r.node_) {
    if (node_ != NULL) Cudd_Ref(node_);
  }
  /// Creates a representation with the ith node with value \a b
  Bdd(int i, bool b) {
    if (b) {
      node_ = Cudd_bddIthVar(BDDConfig::dd,i);
    } else {
      node_ = Cudd_Not(Cudd_bddIthVar(BDDConfig::dd,i));
    }
    assert(node_ != NULL);
    Cudd_Ref(node_);
  }
  /// Destructor
  ~Bdd(void) {
    if (node_ != NULL)
      Cudd_RecursiveDeref(BDDConfig::dd, node_);
  }
  /// Constant true
  static Bdd one;
  /// Constant false
  static Bdd zero;
  /// Tests if this representation is equal to \a r
  bool eq(const Bdd& r) const { return node_ == r.node_; }
  /// Tests if this representation is different from \a r
  bool diff(const Bdd& r) { return !eq(r); }
  /// Compute the complement of this representation
  Bdd complement(void) const {
    // TODO: this has to change for CDDs
    return Bdd(Cudd_Not(node_));
  }
  /// Assignement operator
  Bdd& operator = (const Bdd& right) {
    if (this == &right) return *this;
    if (right.node_ != NULL) Cudd_Ref(right.node_);
    if (node_ != NULL) Cudd_RecursiveDeref(BDDConfig::dd,node_);
    node_ = right.node_;
    return *this;
  }
  ///Computes the logic and between this representation and \a r.
  void intersectAssign(const Bdd& r) {
    Bdd tmp = intersect(r);
    this->operator=(tmp);
  }
  Bdd intersect(const Bdd& r) const {
    DdNode *i = Cudd_bddAnd(BDDConfig::dd,node_,r.node_);
    assert(i);
    return Bdd(i);
  }
  Bdd Union(const Bdd& r) const {
    DdNode *i = Cudd_bddOr(BDDConfig::dd,node_,r.node_);
    assert(i);
    return Bdd(i);
  }
  ///Computes the logic or between this representation and \a r.
  void unionAssign(const Bdd& r) {
    Bdd tmp = Union(r);
    this->operator=(tmp);
  }
  /// Computes the difference between this and \a r
  Bdd difference(const Bdd& r) const {
    return intersect(r.complement());
  }
  /// Computes the difference between this representation and \a r
  void differenceAssign(const Bdd& r) {
    Bdd tmp = difference(r);
    this->operator=(tmp);
  }
  /// Return the number of minterms represented
  int minterms(int a) const {
    return
      Cudd_CountMinterm(BDDConfig::dd,node_,a<<BDDConfig::BBV);
  }
  BddIter tuples(int a) {
    return BddIter(node_,a);
  }
  // Print: temporal
  void print(int a) const {
    if (node_ == CDD_ONE(BDDConfig::dd)) {
      cerr << "Universe";
      return;
    }
    if (node_ == CDD_ZERO(BDDConfig::dd)) {
      cerr << "Empty";
      return;
    }

    DdGen* gen;
    int *cube = (int*)malloc(sizeof(int)*(1<<(BDDConfig::BBV+BDDConfig::BA)));
    int tuple[1<<BDDConfig::BA];
    CUDD_VALUE_TYPE val;
    int done;
    int i,k,j;
    cerr << "#("
	 << Cudd_CountMinterm(BDDConfig::dd,node_,a<<BDDConfig::BBV)
	 << "){";
    //printf("Cardinality: %f\n", Cudd_CountMinterm(Cudd::dd,node,a<<BBV));
    for(k=0;k<1<<BDDConfig::BA;k++)tuple[k]=0;
    Cudd_ForeachCube(BDDConfig::dd,node_,gen,cube,val){
      done=0;
      while(!done){
        done=1;
        for(i=(1<<(BDDConfig::BBV+BDDConfig::BA))-1;i>=0;i--){
          if((i&((1<<BDDConfig::BA)-1))<a){
            tuple[i&((1<<BDDConfig::BA)-1)] &=
	      ~(1<<((1<<BDDConfig::BBV)-1-(i>>BDDConfig::BA)));
            tuple[i&((1<<BDDConfig::BA)-1)] |=
	      (cube[i]&1)<<((1<<BDDConfig::BBV)-1-(i>>BDDConfig::BA));
            if((cube[i]&2)&&done){
              done&=cube[i]&1;
              cube[i]^=1;
            }
          }
        }
        cerr << "<";
        for(j = 0; j < a; j++) {
          cerr << tuple[j] << ",";//printf("%d,",tuple[j]);
        }
        //printf(">\n");
        cerr << ">, ";
      }
    }
    cerr << "}";
  }
};
  Bdd Bdd::one = Bdd(Bdd::Bdd_One);
  Bdd Bdd::zero = Bdd(Bdd::Bdd_Zero);

  /**
   * \brief Path creation in BDD
   *
   * Creates a BDD to represent \a p (encoded as sequence of bits)
   * for a column \a a.
   */
  Bdd createPath(int p, int a) {
    Bdd f(Bdd::one);
    for (int i = (1 << BDDConfig::BBV); i--;) {
      f.intersectAssign(Bdd((i << BDDConfig::BA)+a, p&1));
      p >>= 1;
    }
    return f;
  }

  /// Definition for ground relations
  class GRelation {
  private:
    /// Relation storage
    Bdd representation_;
    /// Arity
    int arity_;
    /// Maximum element of the relation
    int maximum_;
    /// Add the pair \a p to the relation
    Bdd addPair(const pair<int,int>& p) const {
      Bdd r(Bdd::one);
      r.intersectAssign(createPath(p.first,0));
      r.intersectAssign(createPath(p.second,1));
      return r;
    }
  public:
    /// Constructor from a representation
    GRelation(Bdd& representation, int arity, int maximum)
      : representation_(representation), arity_(arity)
      , maximum_(maximum){}
    /// Constructor of a ground relation from a set of tuples
    GRelation(int arity,const vector<pair<int,int> >& data)
      : representation_(Bdd::zero), arity_(arity)
    {
      int max = 0;
      for (unsigned int i = 0; i < data.size(); i++) {
	assert(data[i].first >= 0 && data[i].second >= 0);
	representation_.unionAssign(addPair(data[i]));
	if (data[i].first > max) max = data[i].first;
	if (data[i].second > max) max = data[i].second;
      }
      maximum_ = max;
    }
    /// Constructor for a full ground relation of arity \a arity and
    /// maximum element \a maximum
    GRelation(int arity, int maximum)
      : representation_(Bdd::zero), arity_(arity), maximum_(maximum)
    {
      // TODO: for now only binary (arity = 2) is supported
      assert(arity == 2);
      pair<int,int> x;
      for (int i = 0; i <= maximum_; i++)
	for (int j = 0; j <= maximum_; j++) {
	  x = make_pair(i,j);
	  addPair(x);
	}
    }
    /// Copy constructor
    GRelation(const GRelation& r)
      : representation_(r.representation_), arity_(r.arity_)
      , maximum_(r.maximum_){}
    /// Destructor
    ~GRelation(void) {}
    /// Iterator
    BddIter tuples(void) {
      return representation_.tuples(arity_);
    }
    /// Tests if this represents the empty relation
    bool empty(void) const {
      return representation_.eq(Bdd::zero);
    }
    bool full(void) const {
      return representation_.eq(Bdd::one);
    }
    /// Complement
    GRelation complement(void) const {
      Bdd c = representation_.complement();
      return GRelation(c,arity_,maximum_);
    }
    /// Relation intersection
    GRelation intersect(const GRelation& r) const {
      assert(arity_ == r.arity_);
      assert(maximum_ == r.maximum_);
      Bdd t(representation_.intersect(r.representation_));
      return
	GRelation(t,arity_,maximum_);
    }
    /// Tests if tuple \a t is present in the relation
    bool in(const pair<int,int>& t) const {
      Bdd r(addPair(t));
      Bdd inter(r.intersect(representation_));
      return !inter.eq(Bdd::zero);
    }
    /// Print a relation as a set of tuples
    void print(void) const {
      representation_.print(arity_);
    }
  };
}

int main(void) {
  using namespace BDDImpl;
  {
    vector<pair<int,int> > data(4);
    data.push_back(make_pair(0,0));
    data.push_back(make_pair(0,1));
    data.push_back(make_pair(1,0));
    data.push_back(make_pair(1,1));

    GRelation rr(2,data);
    BddIter it(rr.tuples());
    it.val();
    it.val();
    it.val();
    it.val();
    cout << "Valid? " << it() << endl;
  }
  cout << "References before exit: " << BDDConfig::references() << endl;
  //Cudd_Quit(BDDConfig::dd);
  return 0;
}
<commit_msg>Iterator improvements<commit_after>#include <algorithm>
#include <iostream>
#include <vector>
#include <cuddCdd.h>
#include <cudd/cuddInt.h>

using std::cout;
using std::cerr;
using std::endl;
using std::vector;
using std::pair;
using std::make_pair;

namespace BDDImpl {

class BDDConfig {
public:
  /// Pointer to the Bdd manager
  static DdManager *dd;
  /// Constant true
  static DdNode *one;
  /// Constant false
  static DdNode *zero;
  static int BBV;
  static int  BA;
  static int references(void) {
    return Cudd_CheckZeroRef(dd);
  }
};

// Initializate the manager
DdManager* BDDConfig::dd = Cudd_Init(0,0,CUDD_UNIQUE_SLOTS,CUDD_CACHE_SLOTS,0);
DdNode* BDDConfig::one = CDD_ONE(BDDConfig::dd);
DdNode* BDDConfig::zero = CDD_ZERO(BDDConfig::dd);
int BDDConfig::BBV = 5;
int BDDConfig::BA = 3;

  DdNode* encode(int p, int a) {
    DdNode *f = BDDConfig::one;
    DdNode *v, *tmp;
    Cudd_Ref(f);
    for (int i = (1 << BDDConfig::BBV); i--;) {
      v = Cudd_bddIthVar(BDDConfig::dd,(i << BDDConfig::BA)+a);
      tmp = Cudd_bddAnd(BDDConfig::dd, p&1 ? v : Cudd_Not(v),f);
      Cudd_Ref(tmp);
      Cudd_RecursiveDeref(BDDConfig::dd,f);
      f = tmp;
      p >>= 1;
    }
    return f;
  }
  DdNode* encode(const vector<int>& tuple) {
    DdNode *f = BDDConfig::one;
    DdNode *t, *tmp;
    Cudd_Ref(f);
    for (unsigned int i = 0; i < tuple.size(); i++) {
      t = encode(tuple[i],i);
      tmp = Cudd_bddAnd(BDDConfig::dd,f,t);
      Cudd_Ref(tmp);
      Cudd_RecursiveDeref(BDDConfig::dd,t);
      Cudd_RecursiveDeref(BDDConfig::dd,f);
      f = tmp;
    }
    return f;
  }

  /// Iterator on a Bdd representation
  class BddIter {
  private:
    /// The represented relation
    DdNode *relation_;
    /// Arity of the encoded relation
    int arity_;
    /// Undefined default constructor
    BddIter(void);
    /// Removes a tuple from the iterated relation
    void remove(const vector<int>& tuple) {
      DdNode *i = Cudd_Not(encode(tuple));
      DdNode *tmp = Cudd_bddAnd(BDDConfig::dd,relation_,i);
      Cudd_Ref(tmp);
      Cudd_RecursiveDeref(BDDConfig::dd,i);
      Cudd_RecursiveDeref(BDDConfig::dd,relation_);
      relation_ = tmp;
    }
  public:
    BddIter(DdNode *relation, int arity)
      : relation_(relation), arity_(arity) {
      assert(relation != NULL);
      if (relation_ == BDDConfig::one ||
	  relation_ == BDDConfig::zero) {
	cout << "Creating iterator on empty or full" << endl;
      } else {
	Cudd_Ref(relation_);
      }
    }
    /// Copy constructor
    BddIter(const BddIter& i) : relation_(i.relation_), arity_(i.arity_) {}
    ~BddIter(void) {
      assert(relation_ != NULL);
      Cudd_RecursiveDeref(BDDConfig::dd, relation_);
    }
    bool operator()(void) const {
      return relation_ != BDDConfig::zero;
    }
    /// This is asingle pass iterator
    vector<int> val(void) {
      DdGen* gen;
      const int cube_size = 1<<(BDDConfig::BBV+BDDConfig::BA);
      const int tuple_size = 1<<BDDConfig::BA;
      int *cube = (int*)malloc(sizeof(int) * cube_size);
      int tuple[tuple_size];
      CUDD_VALUE_TYPE val;

      for(int k = 0; k < tuple_size; k++) tuple[k] = 0;

      gen = Cudd_FirstCube(BDDConfig::dd,relation_,&cube,&val);
      //Cudd_ForeachCube(BDDConfig::dd,relation_,gen,cube,val){
      for(int i = cube_size -1; i>=0; i--){
	if( (i & (tuple_size-1))<arity_){
	  tuple[i&(tuple_size-1)] &=
	    ~(1<<((1<<BDDConfig::BBV)-1-(i>>BDDConfig::BA)));
	  tuple[i&(tuple_size-1)] |=
	    (cube[i]&1)<<((1<<BDDConfig::BBV)-1-(i>>BDDConfig::BA));
	}
      }
      /// Prepare the output
      vector<int> out;
      out.resize(arity_);
      copy(tuple,tuple+arity_,out.begin());

      /// print out
      for(unsigned int j = 0; j < out.size(); j++) {
	cerr << tuple[j] << ",";
      }
      /// Affect the state of the iterator
      remove(out);
      /// return t
      return out;
    }
    void operator++(void) {
      assert(false);
    }
  };

/// Class for Binary Decision Diagrams
class Bdd {
private:
  friend class BddIter;
  enum BddTerm { Bdd_Zero, Bdd_One, Bdd_Unk};
  /// Root node of the representation
  DdNode *node_;
  /// Constructor from an existing node
  explicit Bdd(DdNode *n) : node_(n) {
    if (node_ != NULL) Cudd_Ref(node_);
  }
private:
  /// Construcotr for terminal \a t
  explicit Bdd(BddTerm t) {
    // This initialization cannot be done with the previous constructor because
    // when terminal are created they cannot be referenced, otherwise it will
    // report referenced nodes before finishing the program.
    switch(t) {
    case Bdd_Zero: node_ = BDDConfig::zero; break;
    case Bdd_One:  node_ = BDDConfig::one; break;
    default:
      assert(false); break;
    }
    //Cudd_Ref(node_);
  }

public:
  /// Default constructor that contains no representation
  Bdd(void) : node_(NULL) {}
  /// Constructor from an existing representation
  Bdd(const Bdd& r) : node_(r.node_) {
    if (node_ != NULL) Cudd_Ref(node_);
  }
  /// Creates a representation with the ith node with value \a b
  Bdd(int i, bool b) {
    if (b) {
      node_ = Cudd_bddIthVar(BDDConfig::dd,i);
    } else {
      node_ = Cudd_Not(Cudd_bddIthVar(BDDConfig::dd,i));
    }
    assert(node_ != NULL);
    Cudd_Ref(node_);
  }
  /// Destructor
  ~Bdd(void) {
    if (node_ != NULL)
      Cudd_RecursiveDeref(BDDConfig::dd, node_);
  }
  /// Constant true
  static Bdd one;
  /// Constant false
  static Bdd zero;
  /// Tests if this representation is equal to \a r
  bool eq(const Bdd& r) const { return node_ == r.node_; }
  /// Tests if this representation is different from \a r
  bool diff(const Bdd& r) { return !eq(r); }
  /// Compute the complement of this representation
  Bdd complement(void) const {
    // TODO: this has to change for CDDs
    return Bdd(Cudd_Not(node_));
  }
  /// Assignement operator
  Bdd& operator = (const Bdd& right) {
    if (this == &right) return *this;
    if (right.node_ != NULL) Cudd_Ref(right.node_);
    if (node_ != NULL) Cudd_RecursiveDeref(BDDConfig::dd,node_);
    node_ = right.node_;
    return *this;
  }
  ///Computes the logic and between this representation and \a r.
  void intersectAssign(const Bdd& r) {
    Bdd tmp = intersect(r);
    this->operator=(tmp);
  }
  Bdd intersect(const Bdd& r) const {
    DdNode *i = Cudd_bddAnd(BDDConfig::dd,node_,r.node_);
    assert(i);
    return Bdd(i);
  }
  Bdd Union(const Bdd& r) const {
    DdNode *i = Cudd_bddOr(BDDConfig::dd,node_,r.node_);
    assert(i);
    return Bdd(i);
  }
  ///Computes the logic or between this representation and \a r.
  void unionAssign(const Bdd& r) {
    Bdd tmp = Union(r);
    this->operator=(tmp);
  }
  /// Computes the difference between this and \a r
  Bdd difference(const Bdd& r) const {
    return intersect(r.complement());
  }
  /// Computes the difference between this representation and \a r
  void differenceAssign(const Bdd& r) {
    Bdd tmp = difference(r);
    this->operator=(tmp);
  }
  /// Return the number of minterms represented
  int minterms(int a) const {
    return
      Cudd_CountMinterm(BDDConfig::dd,node_,a<<BDDConfig::BBV);
  }
  BddIter tuples(int a) {
    return BddIter(node_,a);
  }
  // Print: temporal
  void print(int a) const {
    if (node_ == CDD_ONE(BDDConfig::dd)) {
      cerr << "Universe";
      return;
    }
    if (node_ == CDD_ZERO(BDDConfig::dd)) {
      cerr << "Empty";
      return;
    }

    DdGen* gen;
    int *cube = (int*)malloc(sizeof(int)*(1<<(BDDConfig::BBV+BDDConfig::BA)));
    int tuple[1<<BDDConfig::BA];
    CUDD_VALUE_TYPE val;
    int done;
    int i,k,j;
    cerr << "#("
	 << Cudd_CountMinterm(BDDConfig::dd,node_,a<<BDDConfig::BBV)
	 << "){";
    //printf("Cardinality: %f\n", Cudd_CountMinterm(Cudd::dd,node,a<<BBV));
    for(k=0;k<1<<BDDConfig::BA;k++)tuple[k]=0;
    Cudd_ForeachCube(BDDConfig::dd,node_,gen,cube,val){
      done=0;
      while(!done){
        done=1;
        for(i=(1<<(BDDConfig::BBV+BDDConfig::BA))-1;i>=0;i--){
          if((i&((1<<BDDConfig::BA)-1))<a){
            tuple[i&((1<<BDDConfig::BA)-1)] &=
	      ~(1<<((1<<BDDConfig::BBV)-1-(i>>BDDConfig::BA)));
            tuple[i&((1<<BDDConfig::BA)-1)] |=
	      (cube[i]&1)<<((1<<BDDConfig::BBV)-1-(i>>BDDConfig::BA));
            if((cube[i]&2)&&done){
              done&=cube[i]&1;
              cube[i]^=1;
            }
          }
        }
        cerr << "<";
        for(j = 0; j < a; j++) {
          cerr << tuple[j] << ",";//printf("%d,",tuple[j]);
        }
        //printf(">\n");
        cerr << ">, ";
      }
    }
    cerr << "}";
  }
};
  Bdd Bdd::one = Bdd(Bdd::Bdd_One);
  Bdd Bdd::zero = Bdd(Bdd::Bdd_Zero);

  /**
   * \brief Path creation in BDD
   *
   * Creates a BDD to represent \a p (encoded as sequence of bits)
   * for a column \a a.
   */
  Bdd createPath(int p, int a) {
    Bdd f(Bdd::one);
    for (int i = (1 << BDDConfig::BBV); i--;) {
      f.intersectAssign(Bdd((i << BDDConfig::BA)+a, p&1));
      p >>= 1;
    }
    return f;
  }

  /// Definition for ground relations
  class GRelation {
  private:
    /// Relation storage
    Bdd representation_;
    /// Arity
    int arity_;
    /// Maximum element of the relation
    int maximum_;
    /// Add the pair \a p to the relation
    Bdd addPair(const pair<int,int>& p) const {
      Bdd r(Bdd::one);
      r.intersectAssign(createPath(p.first,0));
      r.intersectAssign(createPath(p.second,1));
      return r;
    }
  public:
    /// Constructor from a representation
    GRelation(Bdd& representation, int arity, int maximum)
      : representation_(representation), arity_(arity)
      , maximum_(maximum){}
    /// Constructor of a ground relation from a set of tuples
    GRelation(int arity,const vector<pair<int,int> >& data)
      : representation_(Bdd::zero), arity_(arity)
    {
      int max = 0;
      for (unsigned int i = 0; i < data.size(); i++) {
	assert(data[i].first >= 0 && data[i].second >= 0);
	representation_.unionAssign(addPair(data[i]));
	if (data[i].first > max) max = data[i].first;
	if (data[i].second > max) max = data[i].second;
      }
      maximum_ = max;
    }
    /// Constructor for a full ground relation of arity \a arity and
    /// maximum element \a maximum
    GRelation(int arity, int maximum)
      : representation_(Bdd::zero), arity_(arity), maximum_(maximum)
    {
      // TODO: for now only binary (arity = 2) is supported
      assert(arity == 2);
      pair<int,int> x;
      for (int i = 0; i <= maximum_; i++)
	for (int j = 0; j <= maximum_; j++) {
	  x = make_pair(i,j);
	  addPair(x);
	}
    }
    /// Copy constructor
    GRelation(const GRelation& r)
      : representation_(r.representation_), arity_(r.arity_)
      , maximum_(r.maximum_){}
    /// Destructor
    ~GRelation(void) {}
    /// Iterator
    BddIter tuples(void) {
      return representation_.tuples(arity_);
    }
    /// Tests if this represents the empty relation
    bool empty(void) const {
      return representation_.eq(Bdd::zero);
    }
    bool full(void) const {
      return representation_.eq(Bdd::one);
    }
    /// Complement
    GRelation complement(void) const {
      Bdd c = representation_.complement();
      return GRelation(c,arity_,maximum_);
    }
    /// Relation intersection
    GRelation intersect(const GRelation& r) const {
      assert(arity_ == r.arity_);
      assert(maximum_ == r.maximum_);
      Bdd t(representation_.intersect(r.representation_));
      return
	GRelation(t,arity_,maximum_);
    }
    /// Tests if tuple \a t is present in the relation
    bool in(const pair<int,int>& t) const {
      Bdd r(addPair(t));
      Bdd inter(r.intersect(representation_));
      return !inter.eq(Bdd::zero);
    }
    /// Print a relation as a set of tuples
    void print(void) const {
      representation_.print(arity_);
    }
  };
}

int main(void) {
  using namespace BDDImpl;
  {
    vector<pair<int,int> > data(4);
    data.push_back(make_pair(0,0));
    data.push_back(make_pair(0,1));
    data.push_back(make_pair(1,0));
    data.push_back(make_pair(1,1));

    GRelation rr(2,data);
    BddIter it(rr.tuples());
    BddIter it2(rr.tuples());

    it.val();
    it.val();
    it.val();
    it.val();
    cout << "Valid? " << it() << endl;
    it2.val();
    cout << "Valid? " << it2() << endl;
  }
  cout << "References before exit: " << BDDConfig::references() << endl;
  //Cudd_Quit(BDDConfig::dd);
  return 0;
}
<|endoftext|>


<commit_before>#include "http_nostl.h"
#include <cstdlib>

namespace http
{
	namespace nostl
	{

		inline size_t tri_min(size_t a, size_t b, size_t c)
		{
			size_t temp = a < b ? a : b;
			return (temp < c ? temp : c);
		}

		inline void safe_free(void *s)
		{
			if(s != nullptr) {
				free(s);
			}
		}

		inline void safe_delete(void *s)
		{
			if(s != nullptr) {
				delete s;
			}
		}

		inline void safe_array_delete(void *s)
		{
			if(s != nullptr) {
				delete[] s;
			}
		}

		wchar_t *alloc_wide_string(const char *s)
		{
			int length = lstrlenA(s);
			wchar_t *ws = new wchar_t[length + 1];
			MultiByteToWideChar(CP_UTF8, 0, s, length + 1, ws, length + 1);
			return ws;
		}

		char *format_last_error(const char *msg)
		{
			LPSTR buffer = nullptr;
			DWORD error_code = GetLastError();
			if(!FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
				GetModuleHandleA("winhttp.dll"), error_code, 0, (LPSTR)&buffer, 0, nullptr)) {

				const char *tail = " (Failed to format error)";
				char *ret = new char[lstrlenA(msg) + lstrlenA(tail) + 1];
				lstrcpyA(ret, msg);
				lstrcatA(ret, tail);
				return ret;
			}

			const char *sep = ": ";
			char *ret = new char[lstrlenA(msg) + lstrlenA(sep) + lstrlenA(buffer) + 1];
			lstrcpyA(ret, msg);
			lstrcatA(ret, sep);
			lstrcatA(ret, buffer);
			return ret;
		}

		handle_manager::handle_manager() : handle_(nullptr) {}
		handle_manager::handle_manager(HINTERNET h) : handle_(h) {}
		handle_manager::~handle_manager() { if(handle_ != nullptr) WinHttpCloseHandle(handle_); }

		session::session()
		{
			handle_ = WinHttpOpen(L"", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
			if(handle_ == nullptr) {
				set_error("WinHttpOpen() failed");
				return;
			}
		}

		session::~session()
		{
		}

		connection::connection(const session &sess, const char *host)
			: flags_(0),
			timeout_(30),
			host_(alloc_wide_string(host))
		{
			memset(&components_, 0, sizeof(components_));
			components_.dwStructSize = sizeof(components_);
			components_.dwSchemeLength = -1;
			components_.dwHostNameLength = -1;

			if(!WinHttpCrackUrl(host_, 0, 0, &components_)) {
				set_error("WinHttpCrackUrl() failed");
				return;
			}

			size_t buffer_bytes = (components_.dwHostNameLength + 1) * sizeof(wchar_t);
			wchar_t *host_only = new wchar_t[buffer_bytes];
			wcsncpy_s(host_only, buffer_bytes, components_.lpszHostName, components_.dwHostNameLength);
			handle_ = WinHttpConnect(sess.handle(), host_only, components_.nPort, 0);
			safe_array_delete(host_only);

			if(handle_ == nullptr) {
				set_error("WinHttpConnect() failed");
				return;
			}
		}

		connection::~connection()
		{
			safe_array_delete(host_);
		}

		void connection::set_option(option opt, bool on)
		{
			if(on) {
				flags_ |= (1u << opt);
			}
			else {
				flags_ &= ~(1u << opt);
			}
		}

		response connection::send(const request &req)
		{
			const wchar_t *path = req.url_;

			URL_COMPONENTS url_comps;
			memset(&url_comps, 0, sizeof(url_comps));
			url_comps.dwStructSize = sizeof(url_comps);
			url_comps.dwSchemeLength = -1;
			url_comps.dwHostNameLength = -1;
			url_comps.dwUrlPathLength = -1;
			if(WinHttpCrackUrl(req.url_, 0, 0, &url_comps)) {
				// If we managed to parse it, then it's an absolute url
				// Validate the scheme, domain, port
				if(url_comps.dwSchemeLength > 0 && url_comps.nScheme != components_.nScheme) {
					set_error("Request url used a different scheme than the connection was initialized with");
					return response(nullptr);
				}

				if(url_comps.dwHostNameLength > 0 && _wcsnicmp(url_comps.lpszHostName, components_.lpszHostName, url_comps.dwHostNameLength) != 0) {
					set_error("Request url used a different host name than the connection was initialized with");
					return response(nullptr);
				}

				if(url_comps.nPort != components_.nPort) {
					set_error("Request url used a different port than the connection was initialized with");
					return response(nullptr);
				}

				// Use only the path part for making the request
				path = url_comps.lpszUrlPath;
			}

			DWORD open_request_flags = 0;
			if(components_.nScheme == INTERNET_SCHEME_HTTPS) {
				open_request_flags |= WINHTTP_FLAG_SECURE;
			}

			handle_manager request(WinHttpOpenRequest(handle_, req.method_, path, nullptr, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, open_request_flags));
			if(request == nullptr) {
				set_error("WinHttpOpenRequest() failed");
				return response(nullptr);
			}

			unsigned int option_flags = flags_ | req.flags_;
			DWORD security_flags = 0;
			if((option_flags & option_allow_unknown_cert_authority) != 0) {
				security_flags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
			}
			if((option_flags & option_allow_invalid_cert_name) != 0) {
				security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
			}
			if((option_flags & option_allow_invalid_cert_date) != 0) {
				security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
			}

			if(!WinHttpSetOption(request, WINHTTP_OPTION_SECURITY_FLAGS, (LPVOID)&security_flags, sizeof(DWORD))) {
				set_error("WinHttpSetOption(WINHTTP_OPTION_SECURITY_FLAGS) on request handle failed");
				return response(nullptr);
			}

			DWORD timeout = timeout_;
			if(!WinHttpSetOption(request, WINHTTP_OPTION_SEND_TIMEOUT, (LPVOID)&timeout, sizeof(DWORD))) {
				set_error("WinHttpSetOption(WINHTTP_OPTION_SEND_TIMEOUT) on request handle failed");
				return response(nullptr);
			}

			const request::header_line *entry = req.headers_head_;
			while(entry != nullptr) {
				if(!WinHttpAddRequestHeaders(request, entry->line_, wcslen(entry->line_), WINHTTP_ADDREQ_FLAG_ADD|WINHTTP_ADDREQ_FLAG_REPLACE)) {
					set_error("WinHttpAddRequestHeaders() failed");
					return response(nullptr);
				}
				entry = entry->next_;
			}

			DWORD total_request_length = req.body_length_;
			if(!WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, total_request_length, 0)) {
				set_error("WinHttpSendRequest() failed");
				return response(nullptr);
			}

			if(total_request_length > 0) {
				DWORD bytes_written = 0;
				if(!WinHttpWriteData(request, req.body_, req.body_length_, &bytes_written)) {
					set_error("WinHttpWriteData() failed");
					return response(nullptr);
				}
				if(bytes_written != req.body_length_) {
					set_error("WinHttpWriteData did not send entire request body");
					return response(nullptr);
				}
			}

			HINTERNET h = request.handle();
			request.set_handle(nullptr);
			return response(h);
		}

		request::header_line::header_line(wchar_t *line)
			: line_(line),
			next_(nullptr)
		{
		}

		request::header_line::~header_line()
		{
			safe_delete(line_);
			safe_delete(next_);
		}

		request::request(const char *method, const char *url)
			: method_(alloc_wide_string(method)),
			url_(alloc_wide_string(url)),
			body_(nullptr),
			body_length_(0),
			flags_(0),
			headers_head_(nullptr),
			headers_tail_(nullptr)
		{
		}

		request::~request()
		{
			safe_array_delete(method_);
			safe_array_delete(url_);
			safe_array_delete(body_);
			safe_delete(headers_head_);
		}

		void request::add_header(const char *line)
		{
			header_line *entry = new header_line(alloc_wide_string(line));
			if(headers_head_ == nullptr) {
				headers_head_ = headers_tail_ = entry;
			}
			else {
				headers_tail_->next_ = entry;
				headers_tail_ = entry;
			}
		}

		void request::set_body(const char *data, size_t length)
		{
			safe_array_delete(body_);
			body_length_ = length;
			body_ = new char[length];
			memcpy(body_, data, length);
		}

		void request::set_option(option opt, bool on)
		{
			if(on) {
				flags_ |= (1u << opt);
			} else {
				flags_ &= ~(1u << opt);
			}
		}

		response::response(HINTERNET request)
			: handle_manager(request),
			status_(-1)
		{
			if(handle_ != nullptr) {
				if(!WinHttpReceiveResponse(request, nullptr)) {
					set_error("WinHttpReceiveResponse() failed");
					return;
				}

				DWORD status_code = 0;
				DWORD status_code_size = sizeof(status_code);
				if(!WinHttpQueryHeaders(request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &status_code, &status_code_size, WINHTTP_NO_HEADER_INDEX)) {
					set_error("WinHttpWriteData did not send entire request body");
					return;
				}
				status_ = (int)status_code;
			}
		}

		response::response(response &&other)
			: handle_manager(other.handle_),
			status_(other.status_)
		{
			other.handle_ = nullptr;
		}

		response::~response()
		{
		}

		bool response::read(char *buffer, size_t count, size_t *bytes_read)
		{
			if(handle_ == nullptr) {
				return false;
			}

			size_t remaining = count;
			char *p = buffer;

			while(remaining > 0) {

				DWORD data_available;
				if(!WinHttpQueryDataAvailable(handle_, &data_available)) {
					set_error("WinHttpQueryDataAvailable() failed");
					return false;
				}

				if(data_available == 0) {
					break;
				}

				while(data_available > 0 && remaining > 0) {
					DWORD copied;
					DWORD chunk_size = data_available < remaining ? data_available : remaining;

					if(!WinHttpReadData(handle_, p, chunk_size, &copied)) {
						set_error("WinHttpReadData() failed");
						return false;
					}

					p += copied;
					data_available -= copied;
					remaining -= copied;
				}
			}

			if(bytes_read != nullptr) {
				*bytes_read = count - remaining;
			}

			return true;
		}

	} // namespace nostl

} // namespace http
<commit_msg>Use lstrlenW for consistency<commit_after>#include "http_nostl.h"
#include <cstdlib>

namespace http
{
	namespace nostl
	{

		inline size_t tri_min(size_t a, size_t b, size_t c)
		{
			size_t temp = a < b ? a : b;
			return (temp < c ? temp : c);
		}

		inline void safe_free(void *s)
		{
			if(s != nullptr) {
				free(s);
			}
		}

		inline void safe_delete(void *s)
		{
			if(s != nullptr) {
				delete s;
			}
		}

		inline void safe_array_delete(void *s)
		{
			if(s != nullptr) {
				delete[] s;
			}
		}

		wchar_t *alloc_wide_string(const char *s)
		{
			int length = lstrlenA(s);
			wchar_t *ws = new wchar_t[length + 1];
			MultiByteToWideChar(CP_UTF8, 0, s, length + 1, ws, length + 1);
			return ws;
		}

		char *format_last_error(const char *msg)
		{
			LPSTR buffer = nullptr;
			DWORD error_code = GetLastError();
			if(!FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
				GetModuleHandleA("winhttp.dll"), error_code, 0, (LPSTR)&buffer, 0, nullptr)) {

				const char *tail = " (Failed to format error)";
				char *ret = new char[lstrlenA(msg) + lstrlenA(tail) + 1];
				lstrcpyA(ret, msg);
				lstrcatA(ret, tail);
				return ret;
			}

			const char *sep = ": ";
			char *ret = new char[lstrlenA(msg) + lstrlenA(sep) + lstrlenA(buffer) + 1];
			lstrcpyA(ret, msg);
			lstrcatA(ret, sep);
			lstrcatA(ret, buffer);
			return ret;
		}

		handle_manager::handle_manager() : handle_(nullptr) {}
		handle_manager::handle_manager(HINTERNET h) : handle_(h) {}
		handle_manager::~handle_manager() { if(handle_ != nullptr) WinHttpCloseHandle(handle_); }

		session::session()
		{
			handle_ = WinHttpOpen(L"", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
			if(handle_ == nullptr) {
				set_error("WinHttpOpen() failed");
				return;
			}
		}

		session::~session()
		{
		}

		connection::connection(const session &sess, const char *host)
			: flags_(0),
			timeout_(30),
			host_(alloc_wide_string(host))
		{
			memset(&components_, 0, sizeof(components_));
			components_.dwStructSize = sizeof(components_);
			components_.dwSchemeLength = -1;
			components_.dwHostNameLength = -1;

			if(!WinHttpCrackUrl(host_, 0, 0, &components_)) {
				set_error("WinHttpCrackUrl() failed");
				return;
			}

			size_t buffer_bytes = (components_.dwHostNameLength + 1) * sizeof(wchar_t);
			wchar_t *host_only = new wchar_t[buffer_bytes];
			wcsncpy_s(host_only, buffer_bytes, components_.lpszHostName, components_.dwHostNameLength);
			handle_ = WinHttpConnect(sess.handle(), host_only, components_.nPort, 0);
			safe_array_delete(host_only);

			if(handle_ == nullptr) {
				set_error("WinHttpConnect() failed");
				return;
			}
		}

		connection::~connection()
		{
			safe_array_delete(host_);
		}

		void connection::set_option(option opt, bool on)
		{
			if(on) {
				flags_ |= (1u << opt);
			}
			else {
				flags_ &= ~(1u << opt);
			}
		}

		response connection::send(const request &req)
		{
			const wchar_t *path = req.url_;

			URL_COMPONENTS url_comps;
			memset(&url_comps, 0, sizeof(url_comps));
			url_comps.dwStructSize = sizeof(url_comps);
			url_comps.dwSchemeLength = -1;
			url_comps.dwHostNameLength = -1;
			url_comps.dwUrlPathLength = -1;
			if(WinHttpCrackUrl(req.url_, 0, 0, &url_comps)) {
				// If we managed to parse it, then it's an absolute url
				// Validate the scheme, domain, port
				if(url_comps.dwSchemeLength > 0 && url_comps.nScheme != components_.nScheme) {
					set_error("Request url used a different scheme than the connection was initialized with");
					return response(nullptr);
				}

				if(url_comps.dwHostNameLength > 0 && _wcsnicmp(url_comps.lpszHostName, components_.lpszHostName, url_comps.dwHostNameLength) != 0) {
					set_error("Request url used a different host name than the connection was initialized with");
					return response(nullptr);
				}

				if(url_comps.nPort != components_.nPort) {
					set_error("Request url used a different port than the connection was initialized with");
					return response(nullptr);
				}

				// Use only the path part for making the request
				path = url_comps.lpszUrlPath;
			}

			DWORD open_request_flags = 0;
			if(components_.nScheme == INTERNET_SCHEME_HTTPS) {
				open_request_flags |= WINHTTP_FLAG_SECURE;
			}

			handle_manager request(WinHttpOpenRequest(handle_, req.method_, path, nullptr, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, open_request_flags));
			if(request == nullptr) {
				set_error("WinHttpOpenRequest() failed");
				return response(nullptr);
			}

			unsigned int option_flags = flags_ | req.flags_;
			DWORD security_flags = 0;
			if((option_flags & option_allow_unknown_cert_authority) != 0) {
				security_flags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
			}
			if((option_flags & option_allow_invalid_cert_name) != 0) {
				security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
			}
			if((option_flags & option_allow_invalid_cert_date) != 0) {
				security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
			}

			if(!WinHttpSetOption(request, WINHTTP_OPTION_SECURITY_FLAGS, (LPVOID)&security_flags, sizeof(DWORD))) {
				set_error("WinHttpSetOption(WINHTTP_OPTION_SECURITY_FLAGS) on request handle failed");
				return response(nullptr);
			}

			DWORD timeout = timeout_;
			if(!WinHttpSetOption(request, WINHTTP_OPTION_SEND_TIMEOUT, (LPVOID)&timeout, sizeof(DWORD))) {
				set_error("WinHttpSetOption(WINHTTP_OPTION_SEND_TIMEOUT) on request handle failed");
				return response(nullptr);
			}

			const request::header_line *entry = req.headers_head_;
			while(entry != nullptr) {
				if(!WinHttpAddRequestHeaders(request, entry->line_, lstrlenW(entry->line_), WINHTTP_ADDREQ_FLAG_ADD|WINHTTP_ADDREQ_FLAG_REPLACE)) {
					set_error("WinHttpAddRequestHeaders() failed");
					return response(nullptr);
				}
				entry = entry->next_;
			}

			DWORD total_request_length = req.body_length_;
			if(!WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, total_request_length, 0)) {
				set_error("WinHttpSendRequest() failed");
				return response(nullptr);
			}

			if(total_request_length > 0) {
				DWORD bytes_written = 0;
				if(!WinHttpWriteData(request, req.body_, req.body_length_, &bytes_written)) {
					set_error("WinHttpWriteData() failed");
					return response(nullptr);
				}
				if(bytes_written != req.body_length_) {
					set_error("WinHttpWriteData did not send entire request body");
					return response(nullptr);
				}
			}

			HINTERNET h = request.handle();
			request.set_handle(nullptr);
			return response(h);
		}

		request::header_line::header_line(wchar_t *line)
			: line_(line),
			next_(nullptr)
		{
		}

		request::header_line::~header_line()
		{
			safe_delete(line_);
			safe_delete(next_);
		}

		request::request(const char *method, const char *url)
			: method_(alloc_wide_string(method)),
			url_(alloc_wide_string(url)),
			body_(nullptr),
			body_length_(0),
			flags_(0),
			headers_head_(nullptr),
			headers_tail_(nullptr)
		{
		}

		request::~request()
		{
			safe_array_delete(method_);
			safe_array_delete(url_);
			safe_array_delete(body_);
			safe_delete(headers_head_);
		}

		void request::add_header(const char *line)
		{
			header_line *entry = new header_line(alloc_wide_string(line));
			if(headers_head_ == nullptr) {
				headers_head_ = headers_tail_ = entry;
			}
			else {
				headers_tail_->next_ = entry;
				headers_tail_ = entry;
			}
		}

		void request::set_body(const char *data, size_t length)
		{
			safe_array_delete(body_);
			body_length_ = length;
			body_ = new char[length];
			memcpy(body_, data, length);
		}

		void request::set_option(option opt, bool on)
		{
			if(on) {
				flags_ |= (1u << opt);
			} else {
				flags_ &= ~(1u << opt);
			}
		}

		response::response(HINTERNET request)
			: handle_manager(request),
			status_(-1)
		{
			if(handle_ != nullptr) {
				if(!WinHttpReceiveResponse(request, nullptr)) {
					set_error("WinHttpReceiveResponse() failed");
					return;
				}

				DWORD status_code = 0;
				DWORD status_code_size = sizeof(status_code);
				if(!WinHttpQueryHeaders(request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &status_code, &status_code_size, WINHTTP_NO_HEADER_INDEX)) {
					set_error("WinHttpWriteData did not send entire request body");
					return;
				}
				status_ = (int)status_code;
			}
		}

		response::response(response &&other)
			: handle_manager(other.handle_),
			status_(other.status_)
		{
			other.handle_ = nullptr;
		}

		response::~response()
		{
		}

		bool response::read(char *buffer, size_t count, size_t *bytes_read)
		{
			if(handle_ == nullptr) {
				return false;
			}

			size_t remaining = count;
			char *p = buffer;

			while(remaining > 0) {

				DWORD data_available;
				if(!WinHttpQueryDataAvailable(handle_, &data_available)) {
					set_error("WinHttpQueryDataAvailable() failed");
					return false;
				}

				if(data_available == 0) {
					break;
				}

				while(data_available > 0 && remaining > 0) {
					DWORD copied;
					DWORD chunk_size = data_available < remaining ? data_available : remaining;

					if(!WinHttpReadData(handle_, p, chunk_size, &copied)) {
						set_error("WinHttpReadData() failed");
						return false;
					}

					p += copied;
					data_available -= copied;
					remaining -= copied;
				}
			}

			if(bytes_read != nullptr) {
				*bytes_read = count - remaining;
			}

			return true;
		}

	} // namespace nostl

} // namespace http
<|endoftext|>


<commit_before>/** @date 2015

@author
Sensics, Inc.
<http://sensics.com/osvr>
*/

// Copyright 2015 SMI (SensoMotoric Instruments).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Internal Includes
#include <SMITrackerDevice.h>

// Anonymous namespace to avoid symbol collision
namespace {

OSVR_MessageType eyeTrackerMessage;
SMITrackerDevice *SMITrackerDevice::m_instance;

static void CALLBACK
myCallback(smi_CallbackDataStruct *result /*, void* userData  */) {

    /** We need a void* userData parameter to use here
    auto self = static_cast<SMITrackerDevice*>(result->result);
    */

    smi_SampleHMDStruct *m_eyeData = (smi_SampleHMDStruct *)result->result;

    SMITrackerDevice::getInstance()->reportData(m_eyeData);
}

/// helper function for SMI API calls
static void apiCallRC(int rc) {
    std::cout << std::endl
              << smi_rcToString(rc) << std::endl;
}

SMITrackerDevice *SMITrackerDevice::createInstance(OSVR_PluginRegContext ctx) {
    if (m_instance == nullptr) {
        m_instance = new SMITrackerDevice(ctx);
    }
    return m_instance;
}

// assuming that m_instance is not null
SMITrackerDevice *SMITrackerDevice::getInstance() { return m_instance; }

// default update callback, in this case, it shouldn't do anything
OSVR_ReturnCode SMITrackerDevice::update() { return OSVR_RETURN_SUCCESS; }

void SMITrackerDevice::reportData(smi_SampleHMDStruct *m_eyeData) {

    OSVR_TimeValue times;
    osvrTimeValueGetNow(&times);

    OSVR_EyeGazePosition2DState por;
    por.data[0] = m_eyeData->por.x;
    por.data[1] = m_eyeData->por.y;
    osvrDeviceEyeTrackerReport2DGaze(m_dev, m_eyetracker, por, 0, &times);

    /** @todo, uncomment the following section to report other gaze data
    such as 3D gaze direction (see EyeTrackerInterfaceC.h for description)

    OSVR_EyeGazeDirectionState left_dir;
    left_dir.data[0] = m_eyeData->left.gazeDirection.x;
    left_dir.data[1] = m_eyeData->left.gazeDirection.y;
    left_dir.data[2] = m_eyeData->left.gazeDirection.z;
    osvrDeviceEyeTrackerReport3DGazeDirection(m_dev, m_eyetracker, left_dir,
    0, &times);

    OSVR_EyeGazeDirectionState right_dir;
    right_dir.data[0] = m_eyeData->right.gazeDirection.x;
    right_dir.data[1] = m_eyeData->right.gazeDirection.y;
    right_dir.data[2] = m_eyeData->right.gazeDirection.z;
    osvrDeviceEyeTrackerReport3DGazeDirection(m_dev, m_eyetracker,
    right_dir, 1, &times);

    OSVR_EyeGazeBasePoint3DState left_basePoint;
    left_basePoint.data[0] = m_eyeData->left.gazeBasePoint.x;
    left_basePoint.data[1] = m_eyeData->left.gazeBasePoint.y;
    left_basePoint.data[2] = m_eyeData->left.gazeBasePoint.z;
    osvrDeviceEyeTrackerReport3DGaze(m_dev, m_eyetracker, left_dir,
    left_basePoint, 0, &times);

    OSVR_EyeGazeBasePoint3DState right_basePoint;
    right_basePoint.data[0] = m_eyeData->right.gazeBasePoint.x;
    right_basePoint.data[1] = m_eyeData->right.gazeBasePoint.y;
    right_basePoint.data[2] = m_eyeData->right.gazeBasePoint.z;
    osvrDeviceEyeTrackerReport3DGaze(m_dev, m_eyetracker, right_dir,
    right_basePoint, 1, &times);
    */
}

SMITrackerDevice::SMITrackerDevice(OSVR_PluginRegContext ctx) {
    /// Create the initialization options
    OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);

    /// @todo specify number of sensors (1, 2)
    osvrDeviceEyeTrackerConfigure(opts, &m_eyetracker, 2);

    /// Create the sync device token with the options
    m_dev.initSync(ctx, "SMI", opts);

    /// Send JSON descriptor
    m_dev.sendJsonDescriptor(com_osvr_SMI_EyeTracker_json);

    /* SMI part */

    int rc;

    /// @todo re-use existing callback or create a new one, but you will
    /// need
    /// access to class members
    apiCallRC(rc = smi_setCallback(myCallback));

    smi_TrackingParameterStruct params;
    memset(&params, 0, sizeof(smi_TrackingParameterStruct));
    params.mappingDistance = 1500; // map to vergence distance

    /// @todo this is simulating data for now
    bool simulateData = true;

    apiCallRC(rc = smi_startStreaming(simulateData, &params));

    /* END SMI part */

    /// Register update callback
    m_dev.registerUpdateCallback(this);
}

class HardwareDetection {
  public:
    HardwareDetection() : m_found(false) {}
    OSVR_ReturnCode operator()(OSVR_PluginRegContext ctx) {

        // if device was already detected, we don't need to go thru hardware
        // detection again
        if (m_found) {
            return OSVR_RETURN_SUCCESS;
        }

        std::cout << "PLUGIN: Got a hardware detection request" << std::endl;

        /** @todo add proper hardware detection to make sure that device is
        connected
        If device is not detected, it should return OSVR_RETURN_FAILURE
        */
        if (0 == 1) {
            std::cout << "PLUGIN: We have NOT detected Eye Tracker "
                      << std::endl;
            return OSVR_RETURN_FAILURE;
        }

        m_found = true;

        std::cout << "PLUGIN: We have detected SMI HMD! " << std::endl;
        /// Create our device object
        osvr::pluginkit::registerObjectForDeletion(
            ctx, SMITrackerDevice::createInstance(ctx));

        return OSVR_RETURN_SUCCESS;
    }

  private:
    bool m_found;
};
} // namespace

OSVR_PLUGIN(com_osvr_SMI_EyeTracker) {

    /// don't modify this part

    osvrDeviceRegisterMessageType(ctx, "EyeTrackerMessage", &eyeTrackerMessage);

    osvr::pluginkit::PluginContext context(ctx);

    /// Register a detection callback function object.
    context.registerHardwareDetectCallback(new HardwareDetection());

    return OSVR_RETURN_SUCCESS;
}<commit_msg>add condition to skip reporting data to avoid server crash which occurred when callback fn is initially set<commit_after>/** @date 2015

@author
Sensics, Inc.
<http://sensics.com/osvr>
*/

// Copyright 2015 SMI (SensoMotoric Instruments).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Internal Includes
#include <SMITrackerDevice.h>

// Anonymous namespace to avoid symbol collision
namespace {

OSVR_MessageType eyeTrackerMessage;
SMITrackerDevice *SMITrackerDevice::m_instance;

static void CALLBACK
myCallback(smi_CallbackDataStruct *result /*, void* userData  */) {

    /** We need a void* userData parameter to use here
    auto self = static_cast<SMITrackerDevice*>(result->result);
    */

    smi_SampleHMDStruct *m_eyeData = (smi_SampleHMDStruct *)result->result;

    if (SMITrackerDevice::getInstance() == nullptr) {
        // skip this report, occurs once when mycallback is registered
    } else {
        SMITrackerDevice::getInstance()->reportData(m_eyeData);
    }
}

/// helper function for SMI API calls
static void apiCallRC(int rc) {
    std::cout << std::endl
              << smi_rcToString(rc) << std::endl;
}

SMITrackerDevice *SMITrackerDevice::createInstance(OSVR_PluginRegContext ctx) {
    if (m_instance == nullptr) {
        m_instance = new SMITrackerDevice(ctx);
    }
    return m_instance;
}

// assuming that m_instance is not null
SMITrackerDevice *SMITrackerDevice::getInstance() { return m_instance; }

// default update callback, in this case, it shouldn't do anything
OSVR_ReturnCode SMITrackerDevice::update() { return OSVR_RETURN_SUCCESS; }

/// Report data from this function
void SMITrackerDevice::reportData(smi_SampleHMDStruct *m_eyeData) {

    OSVR_TimeValue times;
    osvrTimeValueGetNow(&times);

    OSVR_EyeGazePosition2DState por;
    por.data[0] = m_eyeData->por.x;
    por.data[1] = m_eyeData->por.y;
    osvrDeviceEyeTrackerReport2DGaze(m_dev, m_eyetracker, por, 0, &times);

    /** @todo, uncomment the following section to report other gaze data
    such as 3D gaze direction (see EyeTrackerInterfaceC.h for description)

    OSVR_EyeGazeDirectionState left_dir;
    left_dir.data[0] = m_eyeData->left.gazeDirection.x;
    left_dir.data[1] = m_eyeData->left.gazeDirection.y;
    left_dir.data[2] = m_eyeData->left.gazeDirection.z;
    osvrDeviceEyeTrackerReport3DGazeDirection(m_dev, m_eyetracker, left_dir,
    0, &times);

    OSVR_EyeGazeDirectionState right_dir;
    right_dir.data[0] = m_eyeData->right.gazeDirection.x;
    right_dir.data[1] = m_eyeData->right.gazeDirection.y;
    right_dir.data[2] = m_eyeData->right.gazeDirection.z;
    osvrDeviceEyeTrackerReport3DGazeDirection(m_dev, m_eyetracker,
    right_dir, 1, &times);

    OSVR_EyeGazeBasePoint3DState left_basePoint;
    left_basePoint.data[0] = m_eyeData->left.gazeBasePoint.x;
    left_basePoint.data[1] = m_eyeData->left.gazeBasePoint.y;
    left_basePoint.data[2] = m_eyeData->left.gazeBasePoint.z;
    osvrDeviceEyeTrackerReport3DGaze(m_dev, m_eyetracker, left_dir,
    left_basePoint, 0, &times);

    OSVR_EyeGazeBasePoint3DState right_basePoint;
    right_basePoint.data[0] = m_eyeData->right.gazeBasePoint.x;
    right_basePoint.data[1] = m_eyeData->right.gazeBasePoint.y;
    right_basePoint.data[2] = m_eyeData->right.gazeBasePoint.z;
    osvrDeviceEyeTrackerReport3DGaze(m_dev, m_eyetracker, right_dir,
    right_basePoint, 1, &times);
    */
}

SMITrackerDevice::SMITrackerDevice(OSVR_PluginRegContext ctx) {

    /// Create the initialization options
    OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);

    /// @todo specify number of sensors (1, 2)
    osvrDeviceEyeTrackerConfigure(opts, &m_eyetracker, 2);

    /// Create the sync device token with the options
    m_dev.initSync(ctx, "SMI", opts);

    /// Send JSON descriptor
    m_dev.sendJsonDescriptor(com_osvr_SMI_EyeTracker_json);

    /* SMI part */

    int rc;

    /// @todo re-use existing callback or create a new one, but you will
    /// need access to class members
    apiCallRC(rc = smi_setCallback(myCallback));

    smi_TrackingParameterStruct params;
    memset(&params, 0, sizeof(smi_TrackingParameterStruct));
    params.mappingDistance = 1500; // map to vergence distance

    /// @todo this is simulating data for now
    bool simulateData = true;

    apiCallRC(rc = smi_startStreaming(simulateData, &params));

    /* END SMI part */

    /// Register update callback
    m_dev.registerUpdateCallback(this);
}

class HardwareDetection {
  public:
    HardwareDetection() : m_found(false) {}
    OSVR_ReturnCode operator()(OSVR_PluginRegContext ctx) {

        // if device was already detected, we don't need to go thru hardware
        // detection again
        if (m_found) {
            return OSVR_RETURN_SUCCESS;
        }

        std::cout << "PLUGIN: Got a hardware detection request" << std::endl;

        /** @todo add proper hardware detection to make sure that device is
        connected
        If device is not detected, it should return OSVR_RETURN_FAILURE
        */
        if (0 == 1) {
            std::cout << "PLUGIN: We have NOT detected Eye Tracker "
                      << std::endl;
            return OSVR_RETURN_FAILURE;
        }

        m_found = true;

        std::cout << "PLUGIN: We have detected SMI HMD! " << std::endl;
        /// Create our device object
        osvr::pluginkit::registerObjectForDeletion(
            ctx, SMITrackerDevice::createInstance(ctx));

        return OSVR_RETURN_SUCCESS;
    }

  private:
    bool m_found;
};
} // namespace

OSVR_PLUGIN(com_osvr_SMI_EyeTracker) {

    /// don't modify this part

    osvrDeviceRegisterMessageType(ctx, "EyeTrackerMessage", &eyeTrackerMessage);

    osvr::pluginkit::PluginContext context(ctx);

    /// Register a detection callback function object.
    context.registerHardwareDetectCallback(new HardwareDetection());

    return OSVR_RETURN_SUCCESS;
}<|endoftext|>


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

# ifdef __TITLE__
# undef __TITLE__
# endif
# define __TITLE__ "replica.learn"

namespace dsn { namespace replication {

void replica::init_learn(uint64_t signature)
{
    check_hashed_access();

    dassert (status() == PS_POTENTIAL_SECONDARY, "");

    // at most one learning task running
    if (_potential_secondary_states.learning_round_is_running || !signature)
        return;

    if (signature != _potential_secondary_states.learning_signature)
    {
        _potential_secondary_states.cleanup(true);
        _potential_secondary_states.learning_signature = signature;
        _potential_secondary_states.learning_status = LearningWithoutPrepare;
        _prepare_list->reset(_app->last_committed_decree());
    }
    else
    {
        switch (_potential_secondary_states.learning_status)
        {
        case LearningSucceeded:
            notify_learn_completion();
            return;
        case LearningFailed:
            break;
        case LearningWithPrepare:
            if (_app->last_durable_decree() >= last_committed_decree())
            {
                _potential_secondary_states.learning_status = LearningSucceeded;
                notify_learn_completion();
                return;
            }
            break;
        case LearningWithoutPrepare:
            break;
        default:
            dassert (false, "");
        }
    }

    _potential_secondary_states.learning_round_is_running = true;

    std::shared_ptr<learn_request> request(new learn_request);
    request->gpid = get_gpid();
    request->last_committed_decree_in_app = _app->last_committed_decree();
    request->last_committed_decree_in_prepare_list = _prepare_list->last_committed_decree();
    request->learner = primary_address();
    request->signature = _potential_secondary_states.learning_signature;
    _app->prepare_learning_request(request->app_specific_learn_request);

    _potential_secondary_states.learning_task = rpc::call_typed(
        _config.primary,
        RPC_LEARN,
        request,
        this,
        &replica::on_learn_reply,
        gpid_to_hash(get_gpid())
        );

    ddebug(
        "%s: init_learn with lastAppC/DDecree = <%llu,%llu>, lastCDecree = %llu, learnState = %s",
        name(),
        _app->last_committed_decree(),
        _app->last_durable_decree(),
        last_committed_decree(),
        enum_to_string(_potential_secondary_states.learning_status)
        );
}

void replica::on_learn(const learn_request& request, __out_param learn_response& response)
{
    check_hashed_access();

    if (PS_PRIMARY  != status())
    {
        response.err = ERR_INVALID_STATE;
        return;
    }

    _primary_states.get_replica_config(request.learner, response.config);

    auto it = _primary_states.learners.find(request.learner);
    if (it == _primary_states.learners.end())
    {
        response.err = (response.config.status == PS_SECONDARY ? ERR_OK : ERR_OBJECT_NOT_FOUND);
        return;
    }
    else if (it->second.signature != request.signature)
    {
        response.err = ERR_OBJECT_NOT_FOUND;
        return;
    }

    // prepare learnStartDecree
    decree localCommittedDecree = last_committed_decree();
    decree learnStartDecree = request.last_committed_decree_in_app + 1;
    if (request.last_committed_decree_in_app > localCommittedDecree)
    {
        ddebug(
            "%s: on_learn %s:%d, learner state is lost due to DDD, "
            "with its appCommittedDecree = %llu vs localCommittedDecree = %llu, "
            "so we learn from scratch by setting learnStartDecree = 0",
            name(), request.learner.name.c_str(), static_cast<int>(request.learner.port),
            request.last_committed_decree_in_app, localCommittedDecree
            );
        learnStartDecree = 0; // 0 means learn from scratch
    }

    ddebug(
        "%s: on_learn %s:%d, with localCommittedDecree = %llu and learnStartDecree = %llu",
        name(), request.learner.name.c_str(), static_cast<int>(request.learner.port),
        localCommittedDecree, learnStartDecree
        );

    response.prepare_start_decree = invalid_decree;
    response.commit_decree = localCommittedDecree;
    response.err = ERR_OK;

    // set prepare_start_decree if need
    if (learnStartDecree + _options.staleness_for_start_prepare_for_potential_secondary > localCommittedDecree)
    {
        if (it->second.prepare_start_decree == invalid_decree)
        {
            // start from (last_committed_decree + 1)
            it->second.prepare_start_decree = localCommittedDecree + 1;

            // TODO(qinzuoyan) replay before response on_learn, learner may reject prepare request?
            cleanup_preparing_mutations(true);
            replay_prepare_list();

            ddebug(
                "%s: on_learn %s:%d, set prepareStartDecree = %llu",
                name(), request.learner.name.c_str(), static_cast<int>(request.learner.port),
                localCommittedDecree + 1
            );
        }

        response.prepare_start_decree = it->second.prepare_start_decree;
    }
    else
    {
        it->second.prepare_start_decree = invalid_decree;
    }

    int lerr = _app->get_learn_state(learnStartDecree, request.app_specific_learn_request, response.state);
    if (lerr != 0)
    {
        response.err = ERR_GET_LEARN_STATE_FALED;
        derror("%s get learn state failed, error = %d", dir().c_str(), lerr);
    }
    else
    {
        response.base_local_dir = _app->data_dir();
        for (auto itr = response.state.files.begin(); itr != response.state.files.end(); ++itr)
            *itr = itr->substr(_app->data_dir().length());
    }
}

void replica::on_learn_reply(error_code err, std::shared_ptr<learn_request>& req, std::shared_ptr<learn_response>& resp)
{
    check_hashed_access();

    dassert(PS_POTENTIAL_SECONDARY == status(), "");
    dassert(req->signature == _potential_secondary_states.learning_signature, "");

    if (err != ERR_OK)
    {
        handle_learning_error(err);
        return;
    }

    ddebug(
        "%s: on_learn_reply with err = %s, prepare_start_decree = %llu, current learnState = %s",
        name(), resp->err.to_string(), resp->prepare_start_decree, enum_to_string(_potential_secondary_states.learning_status)
        );

    if (resp->err != ERR_OK)
    {
        handle_learning_error(resp->err);
        return;
    }

    if (resp->config.ballot > get_ballot())
    {
        update_local_configuration(resp->config);
    }

    if (status() != PS_POTENTIAL_SECONDARY)
    {
        return;
    }

    if (resp->prepare_start_decree != invalid_decree && _potential_secondary_states.learning_status == LearningWithoutPrepare)
    {
        _potential_secondary_states.learning_status = LearningWithPrepare;
        _prepare_list->reset(resp->prepare_start_decree - 1);
    }

    if (resp->state.files.size() > 0)
    {
        _potential_secondary_states.learn_remote_files_task =
            file::copy_remote_files(resp->config.primary,
                resp->base_local_dir,
                resp->state.files,
                _app->learn_dir(),
                true,
                LPC_COPY_REMOTE_DELTA_FILES,
                this,
                std::bind(&replica::on_copy_remote_state_completed, this,
                std::placeholders::_1,
                std::placeholders::_2,
                resp)
                );
    }
    else
    {
        _potential_secondary_states.learn_remote_files_task = tasking::enqueue(
            LPC_LEARN_REMOTE_DELTA_FILES,
            this,
            std::bind(&replica::on_copy_remote_state_completed, this, ERR_OK, 0, resp)
            );
    }
}

void replica::on_copy_remote_state_completed(error_code err2, int size, std::shared_ptr<learn_response> resp)
{
    learn_state localState;
    localState.meta = resp->state.meta;
    if (err2 == ERR_OK)
    {
        for (auto itr = resp->state.files.begin(); itr != resp->state.files.end(); ++itr)
        {
            std::string file;
            if (dir().back() == '/' || itr->front() == '/')
                file = dir() + *itr;
            else
                file = dir() + '/' + *itr;

            localState.files.push_back(file);
        }

        decree oldDecree = _app->last_committed_decree();

        // the only place where there is non-in-partition-thread update
        int err = _app->apply_learn_state(resp->state);
        if (err == 0)
        {
            dassert(_app->last_committed_decree() >= _app->last_durable_decree(), "");
            // because if the original _app->last_committed_decree > resp->commit_decree,
            // the learnStartDecree will be set to 0, which makes learner to learn from scratch
            dassert(_app->last_committed_decree() <= resp->commit_decree, "");
        }

        ddebug(
                "%s: learning %d files to %s, err = %x, "
                "appCommit(%llu => %llu), durable(%llu), "
                "remoteC(%llu), prepStart(%llu), state(%s)",
                name(),
                resp->state.files.size(), _dir.c_str(), err,
                oldDecree, _app->last_committed_decree(),
                _app->last_durable_decree(),
                resp->commit_decree,
                resp->prepare_start_decree,
                enum_to_string(_potential_secondary_states.learning_status)
              );

        // if catch-up done, do flush to force data in memtables to the ground
        if (err == 0 && _app->last_committed_decree() == resp->commit_decree)
        {
            err = _app->flush(true);
            if (err == 0)
            {
                dassert (_app->last_committed_decree() == _app->last_durable_decree(), "");
            }
        }

        // translate to general error code
        if (err != 0)
        {
            err2 = ERR_LOCAL_APP_FAILURE;
        }
    }
    else
    {
        derror(
                "%s: transfer %d files to %s failed, err = %s",
                name(),
                static_cast<int>(resp->state.files.size()),
                _dir.c_str(),
                err2.to_string()
                );
    }

    _potential_secondary_states.learn_remote_files_completed_task = tasking::enqueue(
        LPC_LEARN_REMOTE_DELTA_FILES_COMPLETED,
        this,
        std::bind(&replica::on_learn_remote_state_completed, this, err2),
        gpid_to_hash(get_gpid())
        );
}

void replica::on_learn_remote_state_completed(error_code err)
{
    check_hashed_access();

    if (PS_POTENTIAL_SECONDARY != status())
        return;

    _potential_secondary_states.learning_round_is_running = false;

    if (err != ERR_OK)
    {
        handle_learning_error(err);
    }
    else
    {
        // continue
        init_learn(_potential_secondary_states.learning_signature);
    }
}

void replica::handle_learning_error(error_code err)
{
    check_hashed_access();

    dwarn(
        "%s: learning failed with err = %s, LastCommitted = %lld",
        name(),
        err.to_string(),
        _app->last_committed_decree()
        );

    _potential_secondary_states.cleanup(true);
    _potential_secondary_states.learning_status = LearningFailed;

    update_local_configuration_with_no_ballot_change(PS_ERROR);
}

void replica::handle_learning_succeeded_on_primary(const end_point& node, uint64_t learnSignature)
{
    auto it = _primary_states.learners.find(node);
    if (it != _primary_states.learners.end() && it->second.signature == learnSignature)
        upgrade_to_secondary_on_primary(node);
}

void replica::notify_learn_completion()
{
    group_check_response report;
    report.gpid = get_gpid();
    report.err = ERR_OK;
    report.last_committed_decree_in_app = _app->last_committed_decree();
    report.last_committed_decree_in_prepare_list = last_committed_decree();
    report.learner_signature = _potential_secondary_states.learning_signature;
    report.learner_status_ = _potential_secondary_states.learning_status;
    report.node = primary_address();

    rpc::call_one_way_typed(_config.primary, RPC_LEARN_COMPLETION_NOTIFY, report, gpid_to_hash(get_gpid()));
}

void replica::on_learn_completion_notification(const group_check_response& report)
{
    check_hashed_access();
    report.err.end_tracking();
    if (status() != PS_PRIMARY)
        return;

    if (report.learner_status_ == LearningSucceeded)
    {
        handle_learning_succeeded_on_primary(report.node, report.learner_signature);
    }
}

void replica::on_add_learner(const group_check_request& request)
{
    if (request.config.ballot < get_ballot())
        return;

    if (request.config.ballot > get_ballot()
        || is_same_ballot_status_change_allowed(status(), request.config.status))
    {
        update_local_configuration(request.config, true);
        dassert(PS_POTENTIAL_SECONDARY == status(), "");
        init_learn(request.learner_signature);
    }
}

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

# ifdef __TITLE__
# undef __TITLE__
# endif
# define __TITLE__ "replica.learn"

namespace dsn { namespace replication {

void replica::init_learn(uint64_t signature)
{
    check_hashed_access();

    dassert (status() == PS_POTENTIAL_SECONDARY, "");

    // at most one learning task running
    if (_potential_secondary_states.learning_round_is_running || !signature)
        return;

    if (signature != _potential_secondary_states.learning_signature)
    {
        _potential_secondary_states.cleanup(true);
        _potential_secondary_states.learning_signature = signature;
        _potential_secondary_states.learning_status = LearningWithoutPrepare;
        _prepare_list->reset(_app->last_committed_decree());
    }
    else
    {
        switch (_potential_secondary_states.learning_status)
        {
        case LearningSucceeded:
            notify_learn_completion();
            return;
        case LearningFailed:
            break;
        case LearningWithPrepare:
            if (_app->last_durable_decree() >= last_committed_decree())
            {
                _potential_secondary_states.learning_status = LearningSucceeded;
                notify_learn_completion();
                return;
            }
            break;
        case LearningWithoutPrepare:
            break;
        default:
            dassert (false, "");
        }
    }

    _potential_secondary_states.learning_round_is_running = true;

    std::shared_ptr<learn_request> request(new learn_request);
    request->gpid = get_gpid();
    request->last_committed_decree_in_app = _app->last_committed_decree();
    request->last_committed_decree_in_prepare_list = _prepare_list->last_committed_decree();
    request->learner = primary_address();
    request->signature = _potential_secondary_states.learning_signature;
    _app->prepare_learning_request(request->app_specific_learn_request);

    _potential_secondary_states.learning_task = rpc::call_typed(
        _config.primary,
        RPC_LEARN,
        request,
        this,
        &replica::on_learn_reply,
        gpid_to_hash(get_gpid())
        );

    ddebug(
        "%s: init_learn with lastAppC/DDecree = <%llu,%llu>, lastCDecree = %llu, learnState = %s",
        name(),
        _app->last_committed_decree(),
        _app->last_durable_decree(),
        last_committed_decree(),
        enum_to_string(_potential_secondary_states.learning_status)
        );
}

void replica::on_learn(const learn_request& request, __out_param learn_response& response)
{
    check_hashed_access();

    if (PS_PRIMARY  != status())
    {
        response.err = ERR_INVALID_STATE;
        return;
    }

    _primary_states.get_replica_config(request.learner, response.config);

    auto it = _primary_states.learners.find(request.learner);
    if (it == _primary_states.learners.end())
    {
        response.err = (response.config.status == PS_SECONDARY ? ERR_OK : ERR_OBJECT_NOT_FOUND);
        return;
    }
    else if (it->second.signature != request.signature)
    {
        response.err = ERR_OBJECT_NOT_FOUND;
        return;
    }

    // prepare learnStartDecree
    decree localCommittedDecree = last_committed_decree();
    decree learnStartDecree = request.last_committed_decree_in_app + 1;
    if (request.last_committed_decree_in_app > localCommittedDecree)
    {
        ddebug(
            "%s: on_learn %s:%d, learner state is lost due to DDD, "
            "with its appCommittedDecree = %llu vs localCommittedDecree = %llu, "
            "so we learn from scratch by setting learnStartDecree = 0",
            name(), request.learner.name.c_str(), static_cast<int>(request.learner.port),
            request.last_committed_decree_in_app, localCommittedDecree
            );
        learnStartDecree = 0; // 0 means learn from scratch
    }

    ddebug(
        "%s: on_learn %s:%d, with localCommittedDecree = %llu, "
        "localAppC/DDecree = <%llu, %llu>, learnStartDecree = %llu",
        name(), request.learner.name.c_str(), static_cast<int>(request.learner.port),
        localCommittedDecree, _app->last_committed_decree(), _app->last_durable_decree(),
        learnStartDecree
        );

    response.prepare_start_decree = invalid_decree;
    response.commit_decree = localCommittedDecree;
    response.err = ERR_OK;

    // set prepare_start_decree if need
    if (learnStartDecree + _options.staleness_for_start_prepare_for_potential_secondary > localCommittedDecree)
    {
        if (it->second.prepare_start_decree == invalid_decree)
        {
            // start from (last_committed_decree + 1)
            it->second.prepare_start_decree = localCommittedDecree + 1;

            // TODO(qinzuoyan) replay before response on_learn, learner may reject prepare request?
            cleanup_preparing_mutations(true);
            replay_prepare_list();

            ddebug(
                "%s: on_learn %s:%d, set prepareStartDecree = %llu",
                name(), request.learner.name.c_str(), static_cast<int>(request.learner.port),
                localCommittedDecree + 1
            );
        }

        response.prepare_start_decree = it->second.prepare_start_decree;
    }
    else
    {
        it->second.prepare_start_decree = invalid_decree;
    }

    int lerr = _app->get_learn_state(learnStartDecree, request.app_specific_learn_request, response.state);
    if (lerr != 0)
    {
        response.err = ERR_GET_LEARN_STATE_FALED;
        derror("%s get learn state failed, error = %d", dir().c_str(), lerr);
    }
    else
    {
        response.base_local_dir = _app->data_dir();
        for (auto itr = response.state.files.begin(); itr != response.state.files.end(); ++itr)
            *itr = itr->substr(_app->data_dir().length());
    }
}

void replica::on_learn_reply(error_code err, std::shared_ptr<learn_request>& req, std::shared_ptr<learn_response>& resp)
{
    check_hashed_access();

    dassert(PS_POTENTIAL_SECONDARY == status(), "");
    dassert(req->signature == _potential_secondary_states.learning_signature, "");

    if (err != ERR_OK)
    {
        handle_learning_error(err);
        return;
    }

    ddebug(
        "%s: on_learn_reply with err = %s, remoteCommit = %llu, prepareStart = %llu, currentState = %s",
        name(), resp->err.to_string(), resp->commit_decree, resp->prepare_start_decree,
        enum_to_string(_potential_secondary_states.learning_status)
        );

    if (resp->err != ERR_OK)
    {
        handle_learning_error(resp->err);
        return;
    }

    if (resp->config.ballot > get_ballot())
    {
        update_local_configuration(resp->config);
    }

    if (status() != PS_POTENTIAL_SECONDARY)
    {
        return;
    }

    if (resp->prepare_start_decree != invalid_decree && _potential_secondary_states.learning_status == LearningWithoutPrepare)
    {
        _potential_secondary_states.learning_status = LearningWithPrepare;
        _prepare_list->reset(resp->prepare_start_decree - 1);
        ddebug(
            "%s: resetPrepareList = %llu, currentState = %s",
            name(), resp->prepare_start_decree - 1,
            enum_to_string(_potential_secondary_states.learning_status)
            );
    }

    if (resp->state.files.size() > 0)
    {
        _potential_secondary_states.learn_remote_files_task =
            file::copy_remote_files(resp->config.primary,
                resp->base_local_dir,
                resp->state.files,
                _app->learn_dir(),
                true,
                LPC_COPY_REMOTE_DELTA_FILES,
                this,
                std::bind(&replica::on_copy_remote_state_completed, this,
                std::placeholders::_1,
                std::placeholders::_2,
                resp)
                );
    }
    else
    {
        _potential_secondary_states.learn_remote_files_task = tasking::enqueue(
            LPC_LEARN_REMOTE_DELTA_FILES,
            this,
            std::bind(&replica::on_copy_remote_state_completed, this, ERR_OK, 0, resp)
            );
    }
}

void replica::on_copy_remote_state_completed(error_code err2, int size, std::shared_ptr<learn_response> resp)
{
    learn_state localState;
    localState.meta = resp->state.meta;
    if (err2 == ERR_OK)
    {
        for (auto itr = resp->state.files.begin(); itr != resp->state.files.end(); ++itr)
        {
            std::string file;
            if (dir().back() == '/' || itr->front() == '/')
                file = dir() + *itr;
            else
                file = dir() + '/' + *itr;

            localState.files.push_back(file);
        }

        decree oldDecree = _app->last_committed_decree();

        // the only place where there is non-in-partition-thread update
        int err = _app->apply_learn_state(resp->state);
        if (err == 0)
        {
            dassert(_app->last_committed_decree() >= _app->last_durable_decree(), "");
            // because if the original _app->last_committed_decree > resp->commit_decree,
            // the learnStartDecree will be set to 0, which makes learner to learn from scratch
            dassert(_app->last_committed_decree() <= resp->commit_decree, "");
        }

        ddebug(
                "%s: learning %d files to %s, err = %x, "
                "appCommit(%llu => %llu), durable(%llu), "
                "remoteCommit(%llu), prepareStart(%llu), currentState(%s)",
                name(),
                resp->state.files.size(), _dir.c_str(), err,
                oldDecree, _app->last_committed_decree(),
                _app->last_durable_decree(),
                resp->commit_decree,
                resp->prepare_start_decree,
                enum_to_string(_potential_secondary_states.learning_status)
              );

        // if catch-up done, do flush to force data in memtables to the ground
        if (err == 0 && _app->last_committed_decree() == resp->commit_decree)
        {
            err = _app->flush(true);
            ddebug(
                "%s: flush done, err = %x, lastC/DDecree = <%llu, %llu>",
                name(), err, _app->last_committed_decree(), _app->last_durable_decree()
                );
            if (err == 0)
            {
                dassert (_app->last_committed_decree() == _app->last_durable_decree(), "");
            }
        }

        // translate to general error code
        if (err != 0)
        {
            err2 = ERR_LOCAL_APP_FAILURE;
        }
    }
    else
    {
        derror(
                "%s: transfer %d files to %s failed, err = %s",
                name(),
                static_cast<int>(resp->state.files.size()),
                _dir.c_str(),
                err2.to_string()
                );
    }

    _potential_secondary_states.learn_remote_files_completed_task = tasking::enqueue(
        LPC_LEARN_REMOTE_DELTA_FILES_COMPLETED,
        this,
        std::bind(&replica::on_learn_remote_state_completed, this, err2),
        gpid_to_hash(get_gpid())
        );
}

void replica::on_learn_remote_state_completed(error_code err)
{
    check_hashed_access();

    if (PS_POTENTIAL_SECONDARY != status())
        return;

    _potential_secondary_states.learning_round_is_running = false;

    if (err != ERR_OK)
    {
        handle_learning_error(err);
    }
    else
    {
        // continue
        init_learn(_potential_secondary_states.learning_signature);
    }
}

void replica::handle_learning_error(error_code err)
{
    check_hashed_access();

    dwarn(
        "%s: learning failed with err = %s, LastCommitted = %lld",
        name(),
        err.to_string(),
        _app->last_committed_decree()
        );

    _potential_secondary_states.cleanup(true);
    _potential_secondary_states.learning_status = LearningFailed;

    update_local_configuration_with_no_ballot_change(PS_ERROR);
}

void replica::handle_learning_succeeded_on_primary(const end_point& node, uint64_t learnSignature)
{
    auto it = _primary_states.learners.find(node);
    if (it != _primary_states.learners.end() && it->second.signature == learnSignature)
        upgrade_to_secondary_on_primary(node);
}

void replica::notify_learn_completion()
{
    group_check_response report;
    report.gpid = get_gpid();
    report.err = ERR_OK;
    report.last_committed_decree_in_app = _app->last_committed_decree();
    report.last_committed_decree_in_prepare_list = last_committed_decree();
    report.learner_signature = _potential_secondary_states.learning_signature;
    report.learner_status_ = _potential_secondary_states.learning_status;
    report.node = primary_address();

    ddebug(
        "%s: notify_learn_completion with lastAppC/DDecree = <%llu,%llu>, lastCDecree = %llu, learnState = %s",
        name(),
        _app->last_committed_decree(),
        _app->last_durable_decree(),
        last_committed_decree(),
        enum_to_string(_potential_secondary_states.learning_status)
        );

    rpc::call_one_way_typed(_config.primary, RPC_LEARN_COMPLETION_NOTIFY, report, gpid_to_hash(get_gpid()));
}

void replica::on_learn_completion_notification(const group_check_response& report)
{
    check_hashed_access();
    report.err.end_tracking();
    if (status() != PS_PRIMARY)
        return;

    if (report.learner_status_ == LearningSucceeded)
    {
        handle_learning_succeeded_on_primary(report.node, report.learner_signature);
    }
}

void replica::on_add_learner(const group_check_request& request)
{
    if (request.config.ballot < get_ballot())
        return;

    if (request.config.ballot > get_ballot()
        || is_same_ballot_status_change_allowed(status(), request.config.status))
    {
        update_local_configuration(request.config, true);
        dassert(PS_POTENTIAL_SECONDARY == status(), "");
        init_learn(request.learner_signature);
    }
}

}} // namespace
<|endoftext|>


<commit_before>#ifndef __SNOW__GL_ERROR_HH__
#define __SNOW__GL_ERROR_HH__

#include "../config.hh"
#include "sgl.hh"
#include <stdexcept>

namespace snow {

struct S_EXPORT gl_error_t : public std::runtime_error
{
  gl_error_t(const std::string &what);
  gl_error_t(const std::string &what, GLenum error);
  virtual ~gl_error_t();
};

std::string gl_error_string(GLenum error);
void sn_assert_gl__(const char *msg, size_t line, const char *file, const char *func);

} // namespace snow

#ifdef NDEBUG
#define assert_gl(MSGLIT) { GLenum sgl__temp_error__ = glGetError(); if (sgl__temp_error__ != GL_NO_ERROR) { s_log_error("GL Error[%x %s]: %s", sgl__temp_error__, gl_error_string(error).c_str() ,MSGLIT); }}
#else
#define assert_gl(MSGLIT) snow::sn_assert_gl__(MSGLIT, __LINE__, __FILE__, __FUNCTION__)
#endif

#endif /* end __SNOW__GL_ERROR_HH__ include guard */
<commit_msg>Fix error in release build assert_gl.<commit_after>#ifndef __SNOW__GL_ERROR_HH__
#define __SNOW__GL_ERROR_HH__

#include "../config.hh"
#include "sgl.hh"
#include <stdexcept>

namespace snow {

struct S_EXPORT gl_error_t : public std::runtime_error
{
  gl_error_t(const std::string &what);
  gl_error_t(const std::string &what, GLenum error);
  virtual ~gl_error_t();
};

std::string gl_error_string(GLenum error);
void sn_assert_gl__(const char *msg, size_t line, const char *file, const char *func);

} // namespace snow

#ifdef NDEBUG
#define assert_gl(MSGLIT) { GLenum sgl__temp_error__ = glGetError(); if (sgl__temp_error__ != GL_NO_ERROR) { s_log_error("GL Error[%x %s]: %s", sgl__temp_error__, gl_error_string(sgl__temp_error__).c_str() ,MSGLIT); }}
#else
#define assert_gl(MSGLIT) snow::sn_assert_gl__(MSGLIT, __LINE__, __FILE__, __FUNCTION__)
#endif

#endif /* end __SNOW__GL_ERROR_HH__ include guard */
<|endoftext|>