<commit_before>/**
 * @file    labelProp.hpp
 * @ingroup group
 * @author  Chirag Jain <cjain7@gatech.edu>
 * @brief   Connected component labeling using label propagation (or coloring) approach
 *
 * Copyright (c) 2015 Georgia Institute of Technology. All Rights Reserved.
 */

#ifndef LABEL_PROPAGATION_HPP
#define LABEL_PROPAGATION_HPP

//Includes
#include <iostream>

//Own includes
#include "coloring/tupleComp.hpp"
#include "coloring/labelProp_utils.hpp"
#include "utils/commonfuncs.hpp"
#include "utils/logging.hpp"
#include "utils/prettyprint.hpp"

#include "mxx/sort.hpp"
#include "mxx_extra/sort.hpp"
#include "mxx/timer.hpp"
#include "mxx/comm.hpp"

namespace conn
{
  namespace coloring
  {

    /**
     * @class                     conn::coloring::ccl
     * @brief                     supports parallel connected component labeling using label propagation technique
     * @tparam[in]  pIdtype       type used for partition ids
     * @tparam[in]  nIdType       type used for node id
     * @tparam[in]  OPTIMIZATION  optimization level for benchmarking, use loadbalanced for the best version
     */
    template<typename pIdtype = uint32_t, typename nIdType = uint64_t, uint8_t OPTIMIZATION = opt_level::loadbalanced>
    class ccl
    {
      public:
        //Type for saving parition ids
        using partitionIdtype = pIdtype;

        //Type for saving node ids
        using nodeIdType = nIdType;

        //This is the communicator which participates for computing the components
        mxx::comm comm;

      private:

        using T = std::tuple<pIdtype, pIdtype, nodeIdType>;
        std::vector<T> tupleVector;

        //Used during initialization of <Pn>
        //Also used to mark partitions as stable
        pIdtype MAX = std::numeric_limits<pIdtype>::max();

        //Used to mark tuples as stable
        //partitions would become stable if all its tuples are stable
        pIdtype MAX2 = std::numeric_limits<pIdtype>::max() -1 ;

      public:
        /**
         * @brief                 public constructor
         * @param[in] edgeList    distributed vector of edges
         * @param[in] c           mpi communicator for the execution
         */
        template <typename edgeListPairsType>
        ccl(edgeListPairsType &edgeList, const mxx::comm &c) : comm(c.copy())
        {
          //Parse the edgeList
          convertEdgeListforCCL(edgeList);

          //Re-distribute the tuples uniformly across the ranks
          mxx::distribute_inplace(tupleVector, comm);
        }

        //Compute the connected component labels
        void compute()
        {
          //Size of vector should be >= 0
          assert(tupleVector.begin() != tupleVector.end());

          runConnectedComponentLabeling();
        }

        /**
         * @brief     count the components in the graph after ccl (useful for debugging/testing)
         * @return    count
         * @note      should be called after computing connected components.
         *            assumes vector is sorted by Pc
         */
        std::size_t getComponentCount()
        {
          //Vector should be sorted by Pc
          if(!mxx::is_sorted(tupleVector.begin(), tupleVector.end(), TpleComp<cclTupleIds::Pc>(), comm))
            mxx::sort(tupleVector.begin(), tupleVector.end(), TpleComp<cclTupleIds::Pc>(), comm);

          //Count unique Pc values
          return mxx::uniqueCount(tupleVector.begin(), tupleVector.end(),  TpleComp<cclTupleIds::Pc>(), comm);
        }

        /**
         * @brief     Free the communicator
         * @note      Its mandatory to call this function
         */
        void free_comm()
        {
          comm.~comm();
        }

      private:

        /**
         * @brief     converts the edgelist to vector of tuples needed for ccl
         * @details   For the bucket in the edgeList ...<(u, v1), (u,v2)>...
         *            we append <(u, ~, u), (u, ~, v1), (u, ~, v2)> to our tupleVector
         *            We ignore the bucket splits across ranks here, because that shouldn't affect the correctness and complexity
         */
        template <typename edgeListPairsType>
        void convertEdgeListforCCL(edgeListPairsType &edgeList)
        {
          mxx::section_timer timer(std::cerr, comm);

          //Sort the edgeList by src id of each edge
          mxx::sort(edgeList.begin(), edgeList.end(), TpleComp<edgeListTIds::src>(), comm);

          //Reserve the approximate required space in our vector
          tupleVector.reserve(edgeList.size());

          for(auto it = edgeList.begin(); it != edgeList.end(); )
          {
            //Range of edges with same source vertex
            auto equalRange = conn::utils::findRange(it, edgeList.end(), *it, TpleComp<edgeListTIds::src>());

            //Range would include atleast 1 element
            assert(std::distance(equalRange.first, equalRange.second) >= 1);

            //Insert the self loop
            tupleVector.emplace_back(std::get<edgeListTIds::src>(*it), MAX, std::get<edgeListTIds::src>(*it));

            //Insert other vertex members in this partition
            for(auto it2 = equalRange.first; it2 != equalRange.second; it2++)
              tupleVector.emplace_back(std::get<edgeListTIds::src>(*it2), MAX, std::get<edgeListTIds::dst>(*it2));;

            it = equalRange.second;
          }

          timer.end_section("vector of tuples initialized for ccl");

          //Log the total count of tuples
          auto totalTupleCount = mxx::reduce(tupleVector.size(), 0, comm);

          LOG_IF(comm.rank() == 0, INFO) << "Total tuple count is " << totalTupleCount;
        }

        /**
         * @brief     run the iterative algorithm for ccl
         */
        void runConnectedComponentLabeling()
        {
          bool converged = false;

          int iterCount = 0;

          mxx::section_timer timer(std::cerr, comm);

          auto begin = tupleVector.begin();
          auto end = tupleVector.end();
          auto mid = begin;   //range mid-end represents active partitions

          while(!converged)
          {
            updatePn(mid, end);

            converged = updatePc(mid, end);

            //parition the dataset into stable and active paritions, if optimization is enabled
            if(!converged && (OPTIMIZATION == opt_level::stable_partition_removed || OPTIMIZATION == opt_level::loadbalanced))
            {
              //use std::partition to move stable tuples to the left
              //After this step, mid-end would represent active tuples
              mid = partitionStableTuples( mid, end );

              if(OPTIMIZATION == opt_level::loadbalanced)
                mid = mxx::block_decompose_partitions(begin, mid, end, comm);
                //Re distributed the tuples to balance the load across the ranks
            }

            iterCount ++;
          }

          timer.end_section("Coloring done");

          LOG_IF(comm.rank() == 0, INFO) << "Iteration count " << iterCount;
        }

        /**
         * @brief             update the Pn layer by sorting the tuples using node ids
         * @param[in] begin   To iterate over the vector of tuples, marks the range of active tuples
         * @param[in] end     end iterator
         */
        template <typename Iterator>
        void updatePn(Iterator begin, Iterator end)
        {
          //Sort by nid,Pc
          mxx::sort(begin, end, TpleComp2Layers<cclTupleIds::nId, cclTupleIds::Pc>(), comm);

          //Resolve last and first bucket's boundary splits

          //First, find the element with max node id and min Pc locally
          //Or in other words, get the min Pc of the last bucket
          auto minPcOfLastBucket = mxx::local_reduce(begin, end, TpleReduce2Layers<cclTupleIds::nId, cclTupleIds::Pc, std::greater, std::less>());

          //Second, do exscan, look for max nodeid and min Pc on previous ranks
          auto prevMinPc = mxx::exscan(minPcOfLastBucket, TpleReduce2Layers<cclTupleIds::nId, cclTupleIds::Pc, std::greater, std::less>(), comm);

          //We also need to know max Pc of the first bucket on the next rank (to check for stability)
          auto maxPcOfFirstBucket = mxx::local_reduce(begin, end, TpleReduce2Layers<cclTupleIds::nId, cclTupleIds::Pc, std::less, std::greater>());

          //reverse exscan, look for min nodeid and max Pc on forward ranks
          auto nextMaxPc = mxx::exscan(maxPcOfFirstBucket,  TpleReduce2Layers<cclTupleIds::nId, cclTupleIds::Pc, std::less, std::greater>(), comm.reverse());

          //Now we can update the Pn layer of all the buckets locally
          for(auto it = begin; it !=  end;)
          {
            //Range of tuples with the same node id
            auto equalRange = conn::utils::findRange(it, end, *it, TpleComp<cclTupleIds::nId>());

            //Range would include atleast 1 element
            assert(std::distance(equalRange.first, equalRange.second) >= 1);

            //Minimum Pc from local bucket
            auto thisBucketsMinPcLocal = mxx::local_reduce(equalRange.first, equalRange.second, TpleReduce<cclTupleIds::Pc>());

            //Maximum Pc from local bucket
            auto thisBucketsMaxPcLocal = mxx::local_reduce(equalRange.first, equalRange.second, TpleReduce<cclTupleIds::Pc, std::greater>());

            //For now, mark global minimum as local
            auto thisBucketsMaxPcGlobal = thisBucketsMaxPcLocal;
            auto thisBucketsMinPcGlobal = thisBucketsMinPcLocal;

            //Treat first, last buckets as special cases
            if(equalRange.first == begin)
            {
              //Use value from previous rank
              thisBucketsMinPcGlobal =  comm.rank() == 0 ? thisBucketsMinPcLocal : TpleReduce2Layers<cclTupleIds::nId, cclTupleIds::Pc, std::greater, std::less>() (prevMinPc, thisBucketsMinPcLocal);
            }

            if(equalRange.second == end)
            {
              //Use value from next rank
              thisBucketsMaxPcGlobal = comm.rank() == comm.size() - 1 ? thisBucketsMaxPcLocal : TpleReduce2Layers<cclTupleIds::nId, cclTupleIds::Pc, std::less, std::greater>() (nextMaxPc, thisBucketsMaxPcLocal);

            }

            //If min Pc < max Pc for this bucket, update Pn or else mark them as stable
            if(TpleComp<cclTupleIds::Pc>()(thisBucketsMinPcGlobal, thisBucketsMaxPcGlobal))
              std::for_each(equalRange.first, equalRange.second, [&](T &e){
                  std::get<cclTupleIds::Pn>(e) = std::get<cclTupleIds::Pc>(thisBucketsMinPcGlobal);
                  });
            else
              std::for_each(equalRange.first, equalRange.second, [&](T &e){
                  std::get<cclTupleIds::Pn>(e) = MAX2;
                  });

            //Advance the loop pointer
            it = equalRange.second;
          }
        }

        /**
         * @brief     update the Pc layer by choosing min Pn
         * @param[in] begin   To iterate over the vector of tuples, marks the range of active tuples
         * @param[in] end     end iterator
         * @return    bool value, true if the algorithm is converged
         */
        template <typename Iterator>
          bool updatePc(Iterator begin, Iterator end)
          {
            //converged yet
            uint8_t converged = 1;    // 1 means true, we will update it below

            //Sort by Pc, Pn
            mxx::sort(begin, end, TpleComp2Layers<cclTupleIds::Pc, cclTupleIds::Pn>(), comm);

            //Resolve last bucket's boundary split

            //First, find the element with max Pc and min Pn locally
            //Or in other words, get the min Pn of the last bucket
            auto minPnOfLastBucket = mxx::local_reduce(begin, end, TpleReduce2Layers<cclTupleIds::Pc, cclTupleIds::Pn, std::greater, std::less>());

            //Result of exscan, again look for max Pc and min Pn on previous ranks
            auto prevMinPn = mxx::exscan(minPnOfLastBucket, TpleReduce2Layers<cclTupleIds::Pc, cclTupleIds::Pn, std::greater, std::less>(), comm);

            //Now we can update the Pc layer of all the buckets locally
            for(auto it = begin; it !=  end;)
            {
              //Range of tuples with the same Pc
              auto equalRange = conn::utils::findRange(it, end, *it, TpleComp<cclTupleIds::Pc>());

              //Range would include atleast 1 element
              assert(std::distance(equalRange.first, equalRange.second) >= 1);

              //Minimum Pn from local bucket
              auto thisBucketsMinPnLocal = mxx::local_reduce(equalRange.first, equalRange.second, TpleReduce<cclTupleIds::Pn>());

              //For now, mark global minimum as local
              auto thisBucketsMinPnGlobal = thisBucketsMinPnLocal;

              //Treat first, last buckets as special cases
              if(equalRange.first == begin)
              {
                //Use value from previous rank
                thisBucketsMinPnGlobal =  comm.rank() == 0 ? thisBucketsMinPnLocal : TpleReduce2Layers<cclTupleIds::Pc, cclTupleIds::Pn, std::greater, std::less>() (prevMinPn, thisBucketsMinPnLocal);
              }

              //If min Pn < MAX2 for this bucket, update the Pc to new value or else mark the partition as stable
              if(std::get<cclTupleIds::Pn>(thisBucketsMinPnGlobal) < MAX2) {
                converged = 0;
                std::for_each(equalRange.first, equalRange.second, [&](T &e){
                    std::get<cclTupleIds::Pc>(e) = std::get<cclTupleIds::Pn>(thisBucketsMinPnGlobal);
                    });
              }
              else
                std::for_each(equalRange.first, equalRange.second, [&](T &e){
                    std::get<cclTupleIds::Pn>(e) = MAX;
                    });

              //Advance the loop pointer
              it = equalRange.second;
            }

            //Know convergence of all the ranks
            uint8_t allConverged;
            mxx::allreduce(&converged, 1, &allConverged, mxx::min<uint8_t>(), comm);

            return (allConverged == 1  ? true : false);
          }

        template <typename Iterator>
          Iterator partitionStableTuples(Iterator begin, Iterator end)
          {
            return std::partition(begin, end, [&](const T &e){
                return std::get<cclTupleIds::Pn>(e) == MAX;
                });
          }
    };

  }
}

#endif
<commit_msg>Minor fix<commit_after>/**
 * @file    labelProp.hpp
 * @ingroup group
 * @author  Chirag Jain <cjain7@gatech.edu>
 * @brief   Connected component labeling using label propagation (or coloring) approach
 *
 * Copyright (c) 2015 Georgia Institute of Technology. All Rights Reserved.
 */

#ifndef LABEL_PROPAGATION_HPP
#define LABEL_PROPAGATION_HPP

//Includes
#include <iostream>

//Own includes
#include "coloring/tupleComp.hpp"
#include "coloring/labelProp_utils.hpp"
#include "utils/commonfuncs.hpp"
#include "utils/logging.hpp"
#include "utils/prettyprint.hpp"

#include "mxx/sort.hpp"
#include "mxx_extra/sort.hpp"
#include "mxx/timer.hpp"
#include "mxx/comm.hpp"

namespace conn
{
  namespace coloring
  {

    /**
     * @class                     conn::coloring::ccl
     * @brief                     supports parallel connected component labeling using label propagation technique
     * @tparam[in]  pIdtype       type used for partition ids
     * @tparam[in]  nIdType       type used for node id
     * @tparam[in]  OPTIMIZATION  optimization level for benchmarking, use loadbalanced for the best version
     */
    template<typename pIdtype = uint32_t, typename nIdType = uint64_t, uint8_t OPTIMIZATION = opt_level::loadbalanced>
    class ccl
    {
      public:
        //Type for saving parition ids
        using partitionIdtype = pIdtype;

        //Type for saving node ids
        using nodeIdType = nIdType;

      private:

        //This is the communicator which participates for computing the components
        mxx::comm comm;

      private:

        using T = std::tuple<pIdtype, pIdtype, nodeIdType>;
        std::vector<T> tupleVector;

        //Used during initialization of <Pn>
        //Also used to mark partitions as stable
        pIdtype MAX = std::numeric_limits<pIdtype>::max();

        //Used to mark tuples as stable
        //partitions would become stable if all its tuples are stable
        pIdtype MAX2 = std::numeric_limits<pIdtype>::max() -1 ;

      public:
        /**
         * @brief                 public constructor
         * @param[in] edgeList    distributed vector of edges
         * @param[in] c           mpi communicator for the execution
         */
        template <typename edgeListPairsType>
        ccl(edgeListPairsType &edgeList, const mxx::comm &c) : comm(c.copy())
        {
          //Parse the edgeList
          convertEdgeListforCCL(edgeList);

          //Re-distribute the tuples uniformly across the ranks
          mxx::distribute_inplace(tupleVector, comm);
        }

        //Compute the connected component labels
        void compute()
        {
          //Size of vector should be >= 0
          assert(tupleVector.begin() != tupleVector.end());

          runConnectedComponentLabeling();
        }

        /**
         * @brief     count the components in the graph after ccl (useful for debugging/testing)
         * @return    count
         * @note      should be called after computing connected components.
         *            assumes vector is sorted by Pc
         */
        std::size_t getComponentCount()
        {
          //Vector should be sorted by Pc
          if(!mxx::is_sorted(tupleVector.begin(), tupleVector.end(), TpleComp<cclTupleIds::Pc>(), comm))
            mxx::sort(tupleVector.begin(), tupleVector.end(), TpleComp<cclTupleIds::Pc>(), comm);

          //Count unique Pc values
          return mxx::uniqueCount(tupleVector.begin(), tupleVector.end(),  TpleComp<cclTupleIds::Pc>(), comm);
        }

        /**
         * @brief     Free the communicator
         * @note      Its mandatory to call this function
         */
        void free_comm()
        {
          comm.~comm();
        }

      private:

        /**
         * @brief     converts the edgelist to vector of tuples needed for ccl
         * @details   For the bucket in the edgeList ...<(u, v1), (u,v2)>...
         *            we append <(u, ~, u), (u, ~, v1), (u, ~, v2)> to our tupleVector
         *            We ignore the bucket splits across ranks here, because that shouldn't affect the correctness and complexity
         */
        template <typename edgeListPairsType>
        void convertEdgeListforCCL(edgeListPairsType &edgeList)
        {
          mxx::section_timer timer(std::cerr, comm);

          //Sort the edgeList by src id of each edge
          mxx::sort(edgeList.begin(), edgeList.end(), TpleComp<edgeListTIds::src>(), comm);

          //Reserve the approximate required space in our vector
          tupleVector.reserve(edgeList.size());

          for(auto it = edgeList.begin(); it != edgeList.end(); )
          {
            //Range of edges with same source vertex
            auto equalRange = conn::utils::findRange(it, edgeList.end(), *it, TpleComp<edgeListTIds::src>());

            //Range would include atleast 1 element
            assert(std::distance(equalRange.first, equalRange.second) >= 1);

            //Insert the self loop
            tupleVector.emplace_back(std::get<edgeListTIds::src>(*it), MAX, std::get<edgeListTIds::src>(*it));

            //Insert other vertex members in this partition
            for(auto it2 = equalRange.first; it2 != equalRange.second; it2++)
              tupleVector.emplace_back(std::get<edgeListTIds::src>(*it2), MAX, std::get<edgeListTIds::dst>(*it2));;

            it = equalRange.second;
          }

          timer.end_section("vector of tuples initialized for ccl");

          //Log the total count of tuples
          auto totalTupleCount = mxx::reduce(tupleVector.size(), 0, comm);

          LOG_IF(comm.rank() == 0, INFO) << "Total tuple count is " << totalTupleCount;
        }

        /**
         * @brief     run the iterative algorithm for ccl
         */
        void runConnectedComponentLabeling()
        {
          bool converged = false;

          int iterCount = 0;

          mxx::section_timer timer(std::cerr, comm);

          auto begin = tupleVector.begin();
          auto end = tupleVector.end();
          auto mid = begin;   //range mid-end represents active partitions

          while(!converged)
          {
            updatePn(mid, end);

            converged = updatePc(mid, end);

            //parition the dataset into stable and active paritions, if optimization is enabled
            if(!converged && (OPTIMIZATION == opt_level::stable_partition_removed || OPTIMIZATION == opt_level::loadbalanced))
            {
              //use std::partition to move stable tuples to the left
              //After this step, mid-end would represent active tuples
              mid = partitionStableTuples( mid, end );

              if(OPTIMIZATION == opt_level::loadbalanced)
                mid = mxx::block_decompose_partitions(begin, mid, end, comm);
                //Re distributed the tuples to balance the load across the ranks
            }

            iterCount ++;
          }

          timer.end_section("Coloring done");

          LOG_IF(comm.rank() == 0, INFO) << "Iteration count " << iterCount;
        }

        /**
         * @brief             update the Pn layer by sorting the tuples using node ids
         * @param[in] begin   To iterate over the vector of tuples, marks the range of active tuples
         * @param[in] end     end iterator
         */
        template <typename Iterator>
        void updatePn(Iterator begin, Iterator end)
        {
          //Sort by nid,Pc
          mxx::sort(begin, end, TpleComp2Layers<cclTupleIds::nId, cclTupleIds::Pc>(), comm);

          //Resolve last and first bucket's boundary splits

          //First, find the element with max node id and min Pc locally
          //Or in other words, get the min Pc of the last bucket
          auto minPcOfLastBucket = mxx::local_reduce(begin, end, TpleReduce2Layers<cclTupleIds::nId, cclTupleIds::Pc, std::greater, std::less>());

          //Second, do exscan, look for max nodeid and min Pc on previous ranks
          auto prevMinPc = mxx::exscan(minPcOfLastBucket, TpleReduce2Layers<cclTupleIds::nId, cclTupleIds::Pc, std::greater, std::less>(), comm);

          //We also need to know max Pc of the first bucket on the next rank (to check for stability)
          auto maxPcOfFirstBucket = mxx::local_reduce(begin, end, TpleReduce2Layers<cclTupleIds::nId, cclTupleIds::Pc, std::less, std::greater>());

          //reverse exscan, look for min nodeid and max Pc on forward ranks
          auto nextMaxPc = mxx::exscan(maxPcOfFirstBucket,  TpleReduce2Layers<cclTupleIds::nId, cclTupleIds::Pc, std::less, std::greater>(), comm.reverse());

          //Now we can update the Pn layer of all the buckets locally
          for(auto it = begin; it !=  end;)
          {
            //Range of tuples with the same node id
            auto equalRange = conn::utils::findRange(it, end, *it, TpleComp<cclTupleIds::nId>());

            //Range would include atleast 1 element
            assert(std::distance(equalRange.first, equalRange.second) >= 1);

            //Minimum Pc from local bucket
            auto thisBucketsMinPcLocal = mxx::local_reduce(equalRange.first, equalRange.second, TpleReduce<cclTupleIds::Pc>());

            //Maximum Pc from local bucket
            auto thisBucketsMaxPcLocal = mxx::local_reduce(equalRange.first, equalRange.second, TpleReduce<cclTupleIds::Pc, std::greater>());

            //For now, mark global minimum as local
            auto thisBucketsMaxPcGlobal = thisBucketsMaxPcLocal;
            auto thisBucketsMinPcGlobal = thisBucketsMinPcLocal;

            //Treat first, last buckets as special cases
            if(equalRange.first == begin)
            {
              //Use value from previous rank
              thisBucketsMinPcGlobal =  comm.rank() == 0 ? thisBucketsMinPcLocal : TpleReduce2Layers<cclTupleIds::nId, cclTupleIds::Pc, std::greater, std::less>() (prevMinPc, thisBucketsMinPcLocal);
            }

            if(equalRange.second == end)
            {
              //Use value from next rank
              thisBucketsMaxPcGlobal = comm.rank() == comm.size() - 1 ? thisBucketsMaxPcLocal : TpleReduce2Layers<cclTupleIds::nId, cclTupleIds::Pc, std::less, std::greater>() (nextMaxPc, thisBucketsMaxPcLocal);

            }

            //If min Pc < max Pc for this bucket, update Pn or else mark them as stable
            if(TpleComp<cclTupleIds::Pc>()(thisBucketsMinPcGlobal, thisBucketsMaxPcGlobal))
              std::for_each(equalRange.first, equalRange.second, [&](T &e){
                  std::get<cclTupleIds::Pn>(e) = std::get<cclTupleIds::Pc>(thisBucketsMinPcGlobal);
                  });
            else
              std::for_each(equalRange.first, equalRange.second, [&](T &e){
                  std::get<cclTupleIds::Pn>(e) = MAX2;
                  });

            //Advance the loop pointer
            it = equalRange.second;
          }
        }

        /**
         * @brief     update the Pc layer by choosing min Pn
         * @param[in] begin   To iterate over the vector of tuples, marks the range of active tuples
         * @param[in] end     end iterator
         * @return    bool value, true if the algorithm is converged
         */
        template <typename Iterator>
          bool updatePc(Iterator begin, Iterator end)
          {
            //converged yet
            uint8_t converged = 1;    // 1 means true, we will update it below

            //Sort by Pc, Pn
            mxx::sort(begin, end, TpleComp2Layers<cclTupleIds::Pc, cclTupleIds::Pn>(), comm);

            //Resolve last bucket's boundary split

            //First, find the element with max Pc and min Pn locally
            //Or in other words, get the min Pn of the last bucket
            auto minPnOfLastBucket = mxx::local_reduce(begin, end, TpleReduce2Layers<cclTupleIds::Pc, cclTupleIds::Pn, std::greater, std::less>());

            //Result of exscan, again look for max Pc and min Pn on previous ranks
            auto prevMinPn = mxx::exscan(minPnOfLastBucket, TpleReduce2Layers<cclTupleIds::Pc, cclTupleIds::Pn, std::greater, std::less>(), comm);

            //Now we can update the Pc layer of all the buckets locally
            for(auto it = begin; it !=  end;)
            {
              //Range of tuples with the same Pc
              auto equalRange = conn::utils::findRange(it, end, *it, TpleComp<cclTupleIds::Pc>());

              //Range would include atleast 1 element
              assert(std::distance(equalRange.first, equalRange.second) >= 1);

              //Minimum Pn from local bucket
              auto thisBucketsMinPnLocal = mxx::local_reduce(equalRange.first, equalRange.second, TpleReduce<cclTupleIds::Pn>());

              //For now, mark global minimum as local
              auto thisBucketsMinPnGlobal = thisBucketsMinPnLocal;

              //Treat first, last buckets as special cases
              if(equalRange.first == begin)
              {
                //Use value from previous rank
                thisBucketsMinPnGlobal =  comm.rank() == 0 ? thisBucketsMinPnLocal : TpleReduce2Layers<cclTupleIds::Pc, cclTupleIds::Pn, std::greater, std::less>() (prevMinPn, thisBucketsMinPnLocal);
              }

              //If min Pn < MAX2 for this bucket, update the Pc to new value or else mark the partition as stable
              if(std::get<cclTupleIds::Pn>(thisBucketsMinPnGlobal) < MAX2) {
                converged = 0;
                std::for_each(equalRange.first, equalRange.second, [&](T &e){
                    std::get<cclTupleIds::Pc>(e) = std::get<cclTupleIds::Pn>(thisBucketsMinPnGlobal);
                    });
              }
              else
                std::for_each(equalRange.first, equalRange.second, [&](T &e){
                    std::get<cclTupleIds::Pn>(e) = MAX;
                    });

              //Advance the loop pointer
              it = equalRange.second;
            }

            //Know convergence of all the ranks
            uint8_t allConverged;
            mxx::allreduce(&converged, 1, &allConverged, mxx::min<uint8_t>(), comm);

            return (allConverged == 1  ? true : false);
          }

        template <typename Iterator>
          Iterator partitionStableTuples(Iterator begin, Iterator end)
          {
            return std::partition(begin, end, [&](const T &e){
                return std::get<cclTupleIds::Pn>(e) == MAX;
                });
          }
    };

  }
}

#endif
<|endoftext|>


<commit_before>// vim : set fileencoding=utf-8 expandtab noai ts=4 sw=4 :
/// @addtogroup ahbmem
/// @{
/// @file ahbmem.cpp
///
/// @date 2010-2014
/// @copyright All rights reserved.
///            Any reproduction, use, distribution or disclosure of this
///            program, without the express, prior written consent of the
///            authors is strictly prohibited.
/// @author Rolf Meyer
///
/// Provide a test bench memory class with AHB slave interface. DMI and
/// streaming width fields are ignored. Delays are not modeled. Address checking
/// is performed in that way, that transactions are only executed if the slave
/// select condition defined in grlib user manual holds. Transactions generating
/// a correct slave select but exceeding the memory region due to their length
/// are reported as warning and executed anyhow.

#include <fstream>

#include "models/ahbmem/ahbmem.h"
#include "common/report.h"
#include "common/verbose.h"

/// Constructor
AHBMem::AHBMem(const sc_core::sc_module_name nm,  // Module name
  uint16_t haddr,                                 // AMBA AHB address (12 bit)
  uint16_t hmask,                                 // AMBA AHB address mask (12 bit)
  amba::amba_layer_ids ambaLayer,                 // abstraction layer
  uint32_t hindex,
  bool cacheable,
  uint32_t wait_states,
  bool pow_mon) :
    AHBSlave<>(nm,
      hindex,
      0x01,                                       // Gaisler
      0x00E,                                      // AHB Mem,
      0,
      0,
      ambaLayer,
      BAR(AHBDevice::AHBMEM, hmask, cacheable, 0, haddr)),
    BaseMemory(BaseMemory::ARRAY, get_size()),
    ahbBaseAddress(static_cast<uint32_t>((hmask) & haddr) << 20),
    ahbSize(~(static_cast<uint32_t>(hmask) << 20) + 1),
    g_conf("conf"),
    g_haddr("haddr", haddr, g_conf),
    g_hmask("hmask", hmask, g_conf),
    g_hindex("hindex", hindex, g_conf),
    g_cacheable("cacheable", cacheable, g_conf),
    g_wait_states("wait_states", wait_states, g_conf),
    g_pow_mon("pow_mon", pow_mon, g_conf),
    sta_power_norm("power.ahbmem.sta_power_norm", 1269.53125, true),                  // Normalized static power input
    int_power_norm("power.ahbmem.int_power_norm", 1.61011e-12, true),                 // Normalized internal power input
    dyn_read_energy_norm("power.ahbmem.dyn_read_energy_norm", 7.57408e-13, true),     // Normalized read energy input
    dyn_write_energy_norm("power.ahbmem.dyn_write_energy_norm", 7.57408e-13, true),   // Normalized write energy iput
    power("power"),
    sta_power("sta_power", 0.0, power),           // Static power output
    int_power("int_power", 0.0, power),           // Internal power of module (dyn. switching independent)
    swi_power("swi_power", 0.0, power),           // Switching power of modules
    power_frame_starting_time("power_frame_starting_time", SC_ZERO_TIME, power),
    dyn_read_energy("dyn_read_energy", 0.0, power),             // Energy per read access
    dyn_write_energy("dyn_write_energy", 0.0, power),           // Energy per write access
    dyn_reads("dyn_reads", 0ull, power),            // Read access counter for power computation
    dyn_writes("dyn_writes", 0ull, power) {         // Write access counter for power computation
  // haddr and hmask must be 12 bit
  assert(!((g_haddr | g_hmask) >> 12));

  // Register power callback functions
  if (g_pow_mon) {
    GC_REGISTER_TYPED_PARAM_CALLBACK(&sta_power, gs::cnf::pre_read, AHBMem, sta_power_cb);
    GC_REGISTER_TYPED_PARAM_CALLBACK(&int_power, gs::cnf::pre_read, AHBMem, int_power_cb);
    GC_REGISTER_TYPED_PARAM_CALLBACK(&swi_power, gs::cnf::pre_read, AHBMem, swi_power_cb);
  }

  // Display AHB slave information
  srInfo("/configuration/ahbmem/ahbslave")
     ("addr", (uint64_t)get_base_addr())
     ("size", (uint64_t)get_size())
     ("AHB Slave Configuration");

  srInfo("/configuration/ahbmem/generics")
    ("haddr", g_haddr)
    ("hmask", g_hmask)
    ("hindex", g_hindex)
    ("cacheable", g_cacheable)
    ("wait_states", g_wait_states)
    ("Create AHB simulation memory");
}

/// Destructor
AHBMem::~AHBMem() {
  // Delete memory contents
  GC_UNREGISTER_CALLBACKS();
}

void AHBMem::init_generics() {
  // set name, type, default, range, hint and description for gs_configs
  g_hindex.add_properties()
    ("name", "Bus Index")
    ("range", "0..15")
    ("Slave index at the AHB bus");

  g_haddr.add_properties()
    ("name", "AHB Address")
    ("range", "0..0xFFF")
    ("The 12bit MSB address at the AHB bus");

  g_hmask.add_properties()
    ("name", "AHB Mask")
    ("range", "0..0xFFF")
    ("The 12bit AHB address mask");

  g_cacheable.add_properties()
    ("name", "Memory Cachability")
    ("If true the AHB Bus will set the cachability flag for all transactions from the memory");

  g_wait_states.add_properties()
    ("name", "Wait States")
    ("Number of wait states for each transaction");

  g_pow_mon.add_properties()
    ("name", "Power Monitoring")
    ("If true enable power monitoring");
}

void AHBMem::dorst() {
  erase(0, get_size()-1);
}

/// Encapsulated functionality
uint32_t AHBMem::exec_func(
    tlm::tlm_generic_payload &trans,  // NOLINT(runtime/references)
    sc_core::sc_time &delay,          // NOLINT(runtime/references)
    bool debug) {
  uint32_t words_transferred;

  // Is the address for me
  if (!((g_haddr ^ (trans.get_address() >> 20)) & g_hmask)) {
    // Warn if access exceeds slave memory region
    if ((trans.get_address() + trans.get_data_length()) >

        (ahbBaseAddress + ahbSize)) {
      srWarn(name())
        ("Transaction exceeds slave memory region");
    }

    if (trans.is_write()) {
      // write simulation memory
      write_block(trans.get_address(), trans.get_data_ptr(), trans.get_data_length());

      // Base delay is one clock cycle per word
      words_transferred = (trans.get_data_length() < 4) ? 1 : (trans.get_data_length() >> 2);

      if (g_pow_mon) {
        dyn_writes += words_transferred;
      }

      // Total delay is base delay + wait states
      delay += clock_cycle * (words_transferred + g_wait_states);
      trans.set_response_status(tlm::TLM_OK_RESPONSE);
    } else {
      // read simulation memory
      read_block(trans.get_address(), trans.get_data_ptr(), trans.get_data_length());

      // Base delay is one clock cycle per word
      words_transferred = (trans.get_data_length() < 4) ? 1 : (trans.get_data_length() >> 2);

      if (g_pow_mon) {
        dyn_reads += words_transferred;
      }

      // Total delay is base delay + wait states
      delay += clock_cycle * (words_transferred + g_wait_states);
      trans.set_response_status(tlm::TLM_OK_RESPONSE);

      // set cacheability
      if (g_cacheable) {
        ahb.validate_extension<amba::amba_cacheable>(trans);
      }
    }

    srDebug(name())
      ("delay", delay)
      ("Delay increment!");
  } else {
    // address not valid
    srError(name())
      ("taddress", (uint64_t)trans.get_address())
      ("taddr", (uint64_t)trans.get_address() >> 20)
      ("haddr", g_haddr)
      ("hmask", g_hmask)
      ("Address not within permissable slave memory space");
    trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
  }

  return trans.get_data_length();
}

// Returns clock cycle time for e.g. use in AHBSlave parent
sc_core::sc_time AHBMem::get_clock() {
  return clock_cycle;
}

void AHBMem::writeByteDBG(const uint32_t address, const uint8_t byte) {
  write_dbg(address, byte);
}

// Automatically called at the beginning of the simulation
void AHBMem::start_of_simulation() {
  // Initialize power model
  if (g_pow_mon) {
    power_model();
  }
}

// Calculate power/energy values from normalized input data
void AHBMem::power_model() {
  // Static power calculation (pW)
  sta_power = sta_power_norm * (get_size() << 3);

  // Cell internal power (uW)
  int_power = int_power_norm * (get_size() << 3) * 1 / (clock_cycle.to_seconds());

  // Energy per read access (uJ)
  dyn_read_energy =  dyn_read_energy_norm * 32 * (get_size() << 3);

  // Energy per write access (uJ)
  dyn_write_energy = dyn_write_energy_norm * 32 * (get_size() << 3);
}

// Static power callback
gs::cnf::callback_return_type AHBMem::sta_power_cb(
    gs::gs_param_base &changed_param,  // NOLINT(runtime/references)
    gs::cnf::callback_type reason) {
  // Nothing to do !!
  // Static power of AHBMem is constant !!
  return GC_RETURN_OK;
}

// Internal power callback
gs::cnf::callback_return_type AHBMem::int_power_cb(
    gs::gs_param_base &changed_param,  // NOLINT(runtime/references)
    gs::cnf::callback_type reason) {
  // Nothing to do !!
  // AHBMem internal power is constant !!
  return GC_RETURN_OK;
}

// Switching power callback
gs::cnf::callback_return_type AHBMem::swi_power_cb(
    gs::gs_param_base &changed_param,  // NOLINT(runtime/references)
    gs::cnf::callback_type reason) {
  swi_power =
    ((dyn_read_energy *
      dyn_reads) + (dyn_write_energy * dyn_writes)) / (sc_time_stamp() - power_frame_starting_time).to_seconds();
  return GC_RETURN_OK;
}

// Automatically called at the end of the simulation
void AHBMem::end_of_simulation() {
  v::report << name() << " **************************************************** " << v::endl;
  v::report << name() << " * AHBMem Statistics: " << v::endl;
  v::report << name() << " * ------------------ " << v::endl;
  print_transport_statistics(name());
  v::report << name() << " * ************************************************** " << v::endl;
}
/// @}
<commit_msg>AHBMem: Adding init_generics to the constructor<commit_after>// vim : set fileencoding=utf-8 expandtab noai ts=4 sw=4 :
/// @addtogroup ahbmem
/// @{
/// @file ahbmem.cpp
///
/// @date 2010-2014
/// @copyright All rights reserved.
///            Any reproduction, use, distribution or disclosure of this
///            program, without the express, prior written consent of the
///            authors is strictly prohibited.
/// @author Rolf Meyer
///
/// Provide a test bench memory class with AHB slave interface. DMI and
/// streaming width fields are ignored. Delays are not modeled. Address checking
/// is performed in that way, that transactions are only executed if the slave
/// select condition defined in grlib user manual holds. Transactions generating
/// a correct slave select but exceeding the memory region due to their length
/// are reported as warning and executed anyhow.

#include <fstream>

#include "models/ahbmem/ahbmem.h"
#include "common/report.h"
#include "common/verbose.h"

/// Constructor
AHBMem::AHBMem(const sc_core::sc_module_name nm,  // Module name
  uint16_t haddr,                                 // AMBA AHB address (12 bit)
  uint16_t hmask,                                 // AMBA AHB address mask (12 bit)
  amba::amba_layer_ids ambaLayer,                 // abstraction layer
  uint32_t hindex,
  bool cacheable,
  uint32_t wait_states,
  bool pow_mon) :
    AHBSlave<>(nm,
      hindex,
      0x01,                                       // Gaisler
      0x00E,                                      // AHB Mem,
      0,
      0,
      ambaLayer,
      BAR(AHBDevice::AHBMEM, hmask, cacheable, 0, haddr)),
    BaseMemory(BaseMemory::ARRAY, get_size()),
    ahbBaseAddress(static_cast<uint32_t>((hmask) & haddr) << 20),
    ahbSize(~(static_cast<uint32_t>(hmask) << 20) + 1),
    g_conf("conf"),
    g_haddr("haddr", haddr, g_conf),
    g_hmask("hmask", hmask, g_conf),
    g_hindex("hindex", hindex, g_conf),
    g_cacheable("cacheable", cacheable, g_conf),
    g_wait_states("wait_states", wait_states, g_conf),
    g_pow_mon("pow_mon", pow_mon, g_conf),
    sta_power_norm("power.ahbmem.sta_power_norm", 1269.53125, true),                  // Normalized static power input
    int_power_norm("power.ahbmem.int_power_norm", 1.61011e-12, true),                 // Normalized internal power input
    dyn_read_energy_norm("power.ahbmem.dyn_read_energy_norm", 7.57408e-13, true),     // Normalized read energy input
    dyn_write_energy_norm("power.ahbmem.dyn_write_energy_norm", 7.57408e-13, true),   // Normalized write energy iput
    power("power"),
    sta_power("sta_power", 0.0, power),           // Static power output
    int_power("int_power", 0.0, power),           // Internal power of module (dyn. switching independent)
    swi_power("swi_power", 0.0, power),           // Switching power of modules
    power_frame_starting_time("power_frame_starting_time", SC_ZERO_TIME, power),
    dyn_read_energy("dyn_read_energy", 0.0, power),             // Energy per read access
    dyn_write_energy("dyn_write_energy", 0.0, power),           // Energy per write access
    dyn_reads("dyn_reads", 0ull, power),            // Read access counter for power computation
    dyn_writes("dyn_writes", 0ull, power) {         // Write access counter for power computation
  init_generics();
  // haddr and hmask must be 12 bit
  assert(!((g_haddr | g_hmask) >> 12));

  // Register power callback functions
  if (g_pow_mon) {
    GC_REGISTER_TYPED_PARAM_CALLBACK(&sta_power, gs::cnf::pre_read, AHBMem, sta_power_cb);
    GC_REGISTER_TYPED_PARAM_CALLBACK(&int_power, gs::cnf::pre_read, AHBMem, int_power_cb);
    GC_REGISTER_TYPED_PARAM_CALLBACK(&swi_power, gs::cnf::pre_read, AHBMem, swi_power_cb);
  }

  // Display AHB slave information
  srInfo("/configuration/ahbmem/ahbslave")
     ("addr", (uint64_t)get_base_addr())
     ("size", (uint64_t)get_size())
     ("AHB Slave Configuration");

  srInfo("/configuration/ahbmem/generics")
    ("haddr", g_haddr)
    ("hmask", g_hmask)
    ("hindex", g_hindex)
    ("cacheable", g_cacheable)
    ("wait_states", g_wait_states)
    ("Create AHB simulation memory");
}

/// Destructor
AHBMem::~AHBMem() {
  // Delete memory contents
  GC_UNREGISTER_CALLBACKS();
}

void AHBMem::init_generics() {
  // set name, type, default, range, hint and description for gs_configs
  g_hindex.add_properties()
    ("name", "Bus Index")
    ("range", "0..15")
    ("Slave index at the AHB bus");

  g_haddr.add_properties()
    ("name", "AHB Address")
    ("range", "0..0xFFF")
    ("The 12bit MSB address at the AHB bus");

  g_hmask.add_properties()
    ("name", "AHB Mask")
    ("range", "0..0xFFF")
    ("The 12bit AHB address mask");

  g_cacheable.add_properties()
    ("name", "Memory Cachability")
    ("If true the AHB Bus will set the cachability flag for all transactions from the memory");

  g_wait_states.add_properties()
    ("name", "Wait States")
    ("Number of wait states for each transaction");

  g_pow_mon.add_properties()
    ("name", "Power Monitoring")
    ("If true enable power monitoring");
}

void AHBMem::dorst() {
  erase(0, get_size()-1);
}

/// Encapsulated functionality
uint32_t AHBMem::exec_func(
    tlm::tlm_generic_payload &trans,  // NOLINT(runtime/references)
    sc_core::sc_time &delay,          // NOLINT(runtime/references)
    bool debug) {
  uint32_t words_transferred;

  // Is the address for me
  if (!((g_haddr ^ (trans.get_address() >> 20)) & g_hmask)) {
    // Warn if access exceeds slave memory region
    if ((trans.get_address() + trans.get_data_length()) >

        (ahbBaseAddress + ahbSize)) {
      srWarn(name())
        ("Transaction exceeds slave memory region");
    }

    if (trans.is_write()) {
      // write simulation memory
      write_block(trans.get_address(), trans.get_data_ptr(), trans.get_data_length());

      // Base delay is one clock cycle per word
      words_transferred = (trans.get_data_length() < 4) ? 1 : (trans.get_data_length() >> 2);

      if (g_pow_mon) {
        dyn_writes += words_transferred;
      }

      // Total delay is base delay + wait states
      delay += clock_cycle * (words_transferred + g_wait_states);
      trans.set_response_status(tlm::TLM_OK_RESPONSE);
    } else {
      // read simulation memory
      read_block(trans.get_address(), trans.get_data_ptr(), trans.get_data_length());

      // Base delay is one clock cycle per word
      words_transferred = (trans.get_data_length() < 4) ? 1 : (trans.get_data_length() >> 2);

      if (g_pow_mon) {
        dyn_reads += words_transferred;
      }

      // Total delay is base delay + wait states
      delay += clock_cycle * (words_transferred + g_wait_states);
      trans.set_response_status(tlm::TLM_OK_RESPONSE);

      // set cacheability
      if (g_cacheable) {
        ahb.validate_extension<amba::amba_cacheable>(trans);
      }
    }

    srDebug(name())
      ("delay", delay)
      ("Delay increment!");
  } else {
    // address not valid
    srError(name())
      ("taddress", (uint64_t)trans.get_address())
      ("taddr", (uint64_t)trans.get_address() >> 20)
      ("haddr", g_haddr)
      ("hmask", g_hmask)
      ("Address not within permissable slave memory space");
    trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
  }

  return trans.get_data_length();
}

// Returns clock cycle time for e.g. use in AHBSlave parent
sc_core::sc_time AHBMem::get_clock() {
  return clock_cycle;
}

void AHBMem::writeByteDBG(const uint32_t address, const uint8_t byte) {
  write_dbg(address, byte);
}

// Automatically called at the beginning of the simulation
void AHBMem::start_of_simulation() {
  // Initialize power model
  if (g_pow_mon) {
    power_model();
  }
}

// Calculate power/energy values from normalized input data
void AHBMem::power_model() {
  // Static power calculation (pW)
  sta_power = sta_power_norm * (get_size() << 3);

  // Cell internal power (uW)
  int_power = int_power_norm * (get_size() << 3) * 1 / (clock_cycle.to_seconds());

  // Energy per read access (uJ)
  dyn_read_energy =  dyn_read_energy_norm * 32 * (get_size() << 3);

  // Energy per write access (uJ)
  dyn_write_energy = dyn_write_energy_norm * 32 * (get_size() << 3);
}

// Static power callback
gs::cnf::callback_return_type AHBMem::sta_power_cb(
    gs::gs_param_base &changed_param,  // NOLINT(runtime/references)
    gs::cnf::callback_type reason) {
  // Nothing to do !!
  // Static power of AHBMem is constant !!
  return GC_RETURN_OK;
}

// Internal power callback
gs::cnf::callback_return_type AHBMem::int_power_cb(
    gs::gs_param_base &changed_param,  // NOLINT(runtime/references)
    gs::cnf::callback_type reason) {
  // Nothing to do !!
  // AHBMem internal power is constant !!
  return GC_RETURN_OK;
}

// Switching power callback
gs::cnf::callback_return_type AHBMem::swi_power_cb(
    gs::gs_param_base &changed_param,  // NOLINT(runtime/references)
    gs::cnf::callback_type reason) {
  swi_power =
    ((dyn_read_energy *
      dyn_reads) + (dyn_write_energy * dyn_writes)) / (sc_time_stamp() - power_frame_starting_time).to_seconds();
  return GC_RETURN_OK;
}

// Automatically called at the end of the simulation
void AHBMem::end_of_simulation() {
  v::report << name() << " **************************************************** " << v::endl;
  v::report << name() << " * AHBMem Statistics: " << v::endl;
  v::report << name() << " * ------------------ " << v::endl;
  print_transport_statistics(name());
  v::report << name() << " * ************************************************** " << v::endl;
}
/// @}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "android-setup.h"

#include <logging/translator.h>
#include <tools/error.h>
#include <tools/hostosinfo.h>
#include <tools/profile.h>
#include <tools/settings.h>
#include <tools/version.h>

#include <QtCore/qcoreapplication.h>
#include <QtCore/qdir.h>
#include <QtCore/qdiriterator.h>
#include <QtCore/qfile.h>
#include <QtCore/qhash.h>
#include <QtCore/qprocess.h>
#include <QtCore/qstring.h>

#include <algorithm>

using namespace qbs;
using qbs::Internal::Tr;

static QString qls(const char *s) { return QLatin1String(s); }

static QStringList expectedArchs()
{
    return QStringList()
            << QStringLiteral("arm64")
            << QStringLiteral("armv5te")
            << QStringLiteral("armv7a")
            << QStringLiteral("mips")
            << QStringLiteral("mips64")
            << QStringLiteral("x86")
            << QStringLiteral("x86_64");
}

static QString subProfileName(const QString &mainProfileName, const QString &arch)
{
    return mainProfileName + QLatin1Char('-') + arch;
}

void setupSdk(qbs::Settings *settings, const QString &profileName, const QString &sdkDirPath)
{
    if (!QDir(sdkDirPath).exists()) {
        throw ErrorInfo(Tr::tr("SDK directory '%1' does not exist.")
                        .arg(QDir::toNativeSeparators(sdkDirPath)));
    }

    Profile profile(profileName, settings);
    profile.removeProfile();
    if (!sdkDirPath.isEmpty())
        profile.setValue(qls("Android.sdk.sdkDir"), QDir::cleanPath(sdkDirPath));
    profile.setValue(qls("qbs.targetPlatform"), qls("android"));
}

static QString mapArch(const QString &androidName)
{
    if (androidName == qls("arm64-v8a"))
        return qls("arm64");
    if (androidName == qls("armeabi"))
        return qls("armv5te");
    if (androidName == qls("armeabi-v7a"))
        return qls("armv7a");
    return androidName;
}

struct QtAndroidInfo {
    bool isValid() const { return !arch.isEmpty(); }

    QString qmakePath;
    QString arch;
    QString platform;
};

static QtAndroidInfo getInfoForQtDir(const QString &qtDir)
{
    QtAndroidInfo info;
    info.qmakePath = qbs::Internal::HostOsInfo::appendExecutableSuffix(qtDir + qls("/bin/qmake"));
    if (!QFile::exists(info.qmakePath))
        return info;
    QFile qdevicepri(qtDir + qls("/mkspecs/qdevice.pri"));
    if (!qdevicepri.open(QIODevice::ReadOnly))
        return info;
    while (!qdevicepri.atEnd()) {
        const QByteArray line = qdevicepri.readLine().simplified();
        const bool isArchLine = line.startsWith("DEFAULT_ANDROID_TARGET_ARCH");
        const bool isPlatformLine = line.startsWith("DEFAULT_ANDROID_PLATFORM");
        if (!isArchLine && !isPlatformLine)
            continue;
        const QList<QByteArray> elems = line.split('=');
        if (elems.size() != 2)
            continue;
        const QString rhs = QString::fromLatin1(elems.at(1).trimmed());
        if (isArchLine)
            info.arch = mapArch(rhs);
        else
            info.platform = rhs;
    }
    return info;
}

using QtInfoPerArch = QHash<QString, QtAndroidInfo>;
static QtInfoPerArch getQtAndroidInfo(const QString &qtSdkDir)
{
    QtInfoPerArch archs;
    if (qtSdkDir.isEmpty())
        return archs;

    QStringList qtDirs(qtSdkDir);
    QDirIterator dit(qtSdkDir, QStringList() << QLatin1String("android_*"), QDir::Dirs);
    while (dit.hasNext())
        qtDirs << dit.next();
    for (auto it = qtDirs.cbegin(); it != qtDirs.cend(); ++it) {
        const QtAndroidInfo info = getInfoForQtDir(*it);
        if (info.isValid())
            archs.insert(info.arch, info);
    }
    return archs;
}

static QString maximumPlatform(const QString &platform1, const QString &platform2)
{
    if (platform1.isEmpty())
        return platform2;
    if (platform2.isEmpty())
        return platform1;
    static const QString prefix = qls("android-");
    const QString numberString1 = platform1.mid(prefix.size());
    const QString numberString2 = platform2.mid(prefix.size());
    bool ok;
    const int value1 = numberString1.toInt(&ok);
    if (!ok) {
        qWarning("Ignoring malformed Android platform string '%s'.", qPrintable(platform1));
        return platform2;
    }
    const int value2 = numberString2.toInt(&ok);
    if (!ok) {
        qWarning("Ignoring malformed Android platform string '%s'.", qPrintable(platform2));
        return platform1;
    }
    return prefix + QString::number(std::max(value1, value2));
}

static void setupNdk(qbs::Settings *settings, const QString &profileName, const QString &ndkDirPath,
                     const QString &qtSdkDirPath)
{
    if (!QDir(ndkDirPath).exists()) {
        throw ErrorInfo(Tr::tr("NDK directory '%1' does not exist.")
                        .arg(QDir::toNativeSeparators(ndkDirPath)));
    }

    Profile mainProfile(profileName, settings);
    if (!ndkDirPath.isEmpty()) {
        mainProfile.setValue(qls("Android.ndk.ndkDir"), QDir::cleanPath(ndkDirPath));
        mainProfile.setValue(qls("Android.sdk.ndkDir"), QDir::cleanPath(ndkDirPath));
    }
    mainProfile.setValue(qls("qbs.toolchain"), QStringList() << qls("gcc"));
    const QStringList archs = expectedArchs();
    const QtInfoPerArch infoPerArch = getQtAndroidInfo(qtSdkDirPath);
    mainProfile.setValue(qls("qbs.architectures"), infoPerArch.empty()
                         ? archs : QStringList(infoPerArch.keys()));
    QStringList searchPaths;
    QString platform;
    for (const QString &arch : archs) {
        const QtAndroidInfo qtAndroidInfo = infoPerArch.value(arch);
        if (!qtAndroidInfo.isValid())
            continue;
        const QString subProName = subProfileName(profileName, arch);
        const QString setupQtPath = qApp->applicationDirPath() + qls("/qbs-setup-qt");
        QProcess setupQt;
        setupQt.start(setupQtPath, QStringList({ qtAndroidInfo.qmakePath, subProName }));
        if (!setupQt.waitForStarted()) {
            throw ErrorInfo(Tr::tr("Setting up Qt profile failed: '%1' "
                                   "could not be started.").arg(setupQtPath));
        }
        if (!setupQt.waitForFinished()) {
            throw ErrorInfo(Tr::tr("Setting up Qt profile failed: Error running '%1' (%2)")
                            .arg(setupQtPath, setupQt.errorString()));
        }
        if (setupQt.exitCode() != 0) {
            throw ErrorInfo(Tr::tr("Setting up Qt profile failed: '%1' returned with "
                                   "exit code %2.").arg(setupQtPath).arg(setupQt.exitCode()));
        }
        settings->sync();
        qbs::Internal::TemporaryProfile p(subProName, settings);
        searchPaths << p.p.value(qls("preferences.qbsSearchPaths")).toStringList();
        platform = maximumPlatform(platform, qtAndroidInfo.platform);
    }
    if (!searchPaths.empty())
        mainProfile.setValue(qls("preferences.qbsSearchPaths"), searchPaths);
    if (!platform.isEmpty())
        mainProfile.setValue(qls("Android.ndk.platform"), platform);
}

void setupAndroid(Settings *settings, const QString &profileName, const QString &sdkDirPath,
                  const QString &ndkDirPath, const QString &qtSdkDirPath)
{
    setupSdk(settings, profileName, sdkDirPath);
    setupNdk(settings, profileName, ndkDirPath, qtSdkDirPath);
}
<commit_msg>setup-android: Fix case where there is only one architecture<commit_after>/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "android-setup.h"

#include <logging/translator.h>
#include <tools/error.h>
#include <tools/hostosinfo.h>
#include <tools/profile.h>
#include <tools/settings.h>
#include <tools/version.h>

#include <QtCore/qcoreapplication.h>
#include <QtCore/qdir.h>
#include <QtCore/qdiriterator.h>
#include <QtCore/qfile.h>
#include <QtCore/qhash.h>
#include <QtCore/qprocess.h>
#include <QtCore/qstring.h>

#include <algorithm>

using namespace qbs;
using qbs::Internal::Tr;

static QString qls(const char *s) { return QLatin1String(s); }

static QStringList expectedArchs()
{
    return QStringList()
            << QStringLiteral("arm64")
            << QStringLiteral("armv5te")
            << QStringLiteral("armv7a")
            << QStringLiteral("mips")
            << QStringLiteral("mips64")
            << QStringLiteral("x86")
            << QStringLiteral("x86_64");
}

static QString subProfileName(const QString &mainProfileName, const QString &arch)
{
    return mainProfileName + QLatin1Char('-') + arch;
}

void setupSdk(qbs::Settings *settings, const QString &profileName, const QString &sdkDirPath)
{
    if (!QDir(sdkDirPath).exists()) {
        throw ErrorInfo(Tr::tr("SDK directory '%1' does not exist.")
                        .arg(QDir::toNativeSeparators(sdkDirPath)));
    }

    Profile profile(profileName, settings);
    profile.removeProfile();
    if (!sdkDirPath.isEmpty())
        profile.setValue(qls("Android.sdk.sdkDir"), QDir::cleanPath(sdkDirPath));
    profile.setValue(qls("qbs.targetPlatform"), qls("android"));
}

static QString mapArch(const QString &androidName)
{
    if (androidName == qls("arm64-v8a"))
        return qls("arm64");
    if (androidName == qls("armeabi"))
        return qls("armv5te");
    if (androidName == qls("armeabi-v7a"))
        return qls("armv7a");
    return androidName;
}

struct QtAndroidInfo {
    bool isValid() const { return !arch.isEmpty(); }

    QString qmakePath;
    QString arch;
    QString platform;
};

static QtAndroidInfo getInfoForQtDir(const QString &qtDir)
{
    QtAndroidInfo info;
    info.qmakePath = qbs::Internal::HostOsInfo::appendExecutableSuffix(qtDir + qls("/bin/qmake"));
    if (!QFile::exists(info.qmakePath))
        return info;
    QFile qdevicepri(qtDir + qls("/mkspecs/qdevice.pri"));
    if (!qdevicepri.open(QIODevice::ReadOnly))
        return info;
    while (!qdevicepri.atEnd()) {
        const QByteArray line = qdevicepri.readLine().simplified();
        const bool isArchLine = line.startsWith("DEFAULT_ANDROID_TARGET_ARCH");
        const bool isPlatformLine = line.startsWith("DEFAULT_ANDROID_PLATFORM");
        if (!isArchLine && !isPlatformLine)
            continue;
        const QList<QByteArray> elems = line.split('=');
        if (elems.size() != 2)
            continue;
        const QString rhs = QString::fromLatin1(elems.at(1).trimmed());
        if (isArchLine)
            info.arch = mapArch(rhs);
        else
            info.platform = rhs;
    }
    return info;
}

using QtInfoPerArch = QHash<QString, QtAndroidInfo>;
static QtInfoPerArch getQtAndroidInfo(const QString &qtSdkDir)
{
    QtInfoPerArch archs;
    if (qtSdkDir.isEmpty())
        return archs;

    QStringList qtDirs(qtSdkDir);
    QDirIterator dit(qtSdkDir, QStringList() << QLatin1String("android_*"), QDir::Dirs);
    while (dit.hasNext())
        qtDirs << dit.next();
    for (auto it = qtDirs.cbegin(); it != qtDirs.cend(); ++it) {
        const QtAndroidInfo info = getInfoForQtDir(*it);
        if (info.isValid())
            archs.insert(info.arch, info);
    }
    return archs;
}

static QString maximumPlatform(const QString &platform1, const QString &platform2)
{
    if (platform1.isEmpty())
        return platform2;
    if (platform2.isEmpty())
        return platform1;
    static const QString prefix = qls("android-");
    const QString numberString1 = platform1.mid(prefix.size());
    const QString numberString2 = platform2.mid(prefix.size());
    bool ok;
    const int value1 = numberString1.toInt(&ok);
    if (!ok) {
        qWarning("Ignoring malformed Android platform string '%s'.", qPrintable(platform1));
        return platform2;
    }
    const int value2 = numberString2.toInt(&ok);
    if (!ok) {
        qWarning("Ignoring malformed Android platform string '%s'.", qPrintable(platform2));
        return platform1;
    }
    return prefix + QString::number(std::max(value1, value2));
}

static void setupNdk(qbs::Settings *settings, const QString &profileName, const QString &ndkDirPath,
                     const QString &qtSdkDirPath)
{
    if (!QDir(ndkDirPath).exists()) {
        throw ErrorInfo(Tr::tr("NDK directory '%1' does not exist.")
                        .arg(QDir::toNativeSeparators(ndkDirPath)));
    }

    Profile mainProfile(profileName, settings);
    if (!ndkDirPath.isEmpty()) {
        mainProfile.setValue(qls("Android.ndk.ndkDir"), QDir::cleanPath(ndkDirPath));
        mainProfile.setValue(qls("Android.sdk.ndkDir"), QDir::cleanPath(ndkDirPath));
    }
    mainProfile.setValue(qls("qbs.toolchain"), QStringList() << qls("gcc"));
    const QStringList archs = expectedArchs();
    const QtInfoPerArch infoPerArch = getQtAndroidInfo(qtSdkDirPath);
    const QStringList archsForProfile = infoPerArch.empty()
            ? archs : QStringList(infoPerArch.keys());
    if (archsForProfile.size() == 1)
        mainProfile.setValue(qls("qbs.architecture"), archsForProfile.front());
    else
        mainProfile.setValue(qls("qbs.architectures"), archsForProfile);
    QStringList searchPaths;
    QString platform;
    for (const QString &arch : archs) {
        const QtAndroidInfo qtAndroidInfo = infoPerArch.value(arch);
        if (!qtAndroidInfo.isValid())
            continue;
        const QString subProName = subProfileName(profileName, arch);
        const QString setupQtPath = qApp->applicationDirPath() + qls("/qbs-setup-qt");
        QProcess setupQt;
        setupQt.start(setupQtPath, QStringList({ qtAndroidInfo.qmakePath, subProName }));
        if (!setupQt.waitForStarted()) {
            throw ErrorInfo(Tr::tr("Setting up Qt profile failed: '%1' "
                                   "could not be started.").arg(setupQtPath));
        }
        if (!setupQt.waitForFinished()) {
            throw ErrorInfo(Tr::tr("Setting up Qt profile failed: Error running '%1' (%2)")
                            .arg(setupQtPath, setupQt.errorString()));
        }
        if (setupQt.exitCode() != 0) {
            throw ErrorInfo(Tr::tr("Setting up Qt profile failed: '%1' returned with "
                                   "exit code %2.").arg(setupQtPath).arg(setupQt.exitCode()));
        }
        settings->sync();
        qbs::Internal::TemporaryProfile p(subProName, settings);
        searchPaths << p.p.value(qls("preferences.qbsSearchPaths")).toStringList();
        platform = maximumPlatform(platform, qtAndroidInfo.platform);
    }
    if (!searchPaths.empty())
        mainProfile.setValue(qls("preferences.qbsSearchPaths"), searchPaths);
    if (!platform.isEmpty())
        mainProfile.setValue(qls("Android.ndk.platform"), platform);
}

void setupAndroid(Settings *settings, const QString &profileName, const QString &sdkDirPath,
                  const QString &ndkDirPath, const QString &qtSdkDirPath)
{
    setupSdk(settings, profileName, sdkDirPath);
    setupNdk(settings, profileName, ndkDirPath, qtSdkDirPath);
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qremoteservicecontrol_p.h"
#include "qremoteservicecontrol_ls_p.h"
#include "ipcendpoint_p.h"
#include "objectendpoint_p.h"

#include <QLocalServer>
#include <QLocalSocket>
#include <QDataStream>
#include <QTimer>
#include <QProcess>

#include <time.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

#ifndef Q_OS_WIN
#include <sys/un.h>
#endif

#ifdef LOCAL_PEERCRED /* from sys/un.h */
#include <sys/ucred.h>
#endif

// Needed for ::Sleep, while we wait for a better solution
#ifdef Q_OS_WIN
#include <Windows.h>
#include <Winbase.h>
#endif

QTM_BEGIN_NAMESPACE

//IPC based on QLocalSocket

class LocalSocketEndPoint : public QServiceIpcEndPoint
{
    Q_OBJECT
public:
    LocalSocketEndPoint(QLocalSocket* s, QObject* parent = 0)
        : QServiceIpcEndPoint(parent), socket(s)
    {
        Q_ASSERT(socket);
        socket->setParent(this);
        connect(s, SIGNAL(readyRead()), this, SLOT(readIncoming()));
        connect(s, SIGNAL(disconnected()), this, SIGNAL(disconnected()));

        if (socket->bytesAvailable())
            QTimer::singleShot(0, this, SLOT(readIncoming()));
    }

    ~LocalSocketEndPoint()
    {
    }

protected:
    void flushPackage(const QServicePackage& package)
    {
        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
        out.setVersion(QDataStream::Qt_4_6);
        out << package;
        socket->write(block);
    }

protected slots:
    void readIncoming()
    {
        QDataStream in(socket);
        in.setVersion(QDataStream::Qt_4_6);

        while(socket->bytesAvailable()) {
            QServicePackage package;
            in >> package;
            incoming.enqueue(package);
        }

        emit readyRead();
    }

private:
    QLocalSocket* socket;
};

QRemoteServiceControlLocalSocketPrivate::QRemoteServiceControlLocalSocketPrivate(QObject* parent)
    : QRemoteServiceControlPrivate(parent)
{
}

void QRemoteServiceControlLocalSocketPrivate::publishServices( const QString& ident)
{
    createServiceEndPoint(ident) ;
}

void QRemoteServiceControlLocalSocketPrivate::processIncoming()
{
    qDebug() << "Processing incoming connect";
    if (localServer->hasPendingConnections()) {
        QLocalSocket* s = localServer->nextPendingConnection();
        //LocalSocketEndPoint owns socket
        int fd = s->socketDescriptor();
        if(getSecurityFilter()){
            QRemoteServiceControlLocalSocketCred qcred;
            memset(&qcred, 0, sizeof(QRemoteServiceControlLocalSocketCred));
            qcred.fd = fd;

#if defined(LOCAL_PEERCRED)
            struct xucred xuc;
            socklen_t len = sizeof(struct xucred);

            if(getsockopt(fd, SOL_SOCKET, LOCAL_PEERCRED, &xuc, &len) == 0) {
                qcred.pid = -1; // No PID on bsd
                qcred.uid = xuc.cr_uid;
                qcred.gid = xuc.cr_gid;

            }

#elif defined(SO_PEERCRED)
            struct ucred uc;
            socklen_t len = sizeof(struct ucred);

            if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) == 0) {
                qcred.pid = uc.pid;
                qcred.uid = uc.uid;
                qcred.gid = uc.gid;
            }
            else {
                s->close();
                perror("Failed to get peer credential");
                return;
            }
#else
            s->close();
            qWarning("Credentials check unsupprted on this platform");
            return;
#endif
            if(!getSecurityFilter()(reinterpret_cast<const void *>(&qcred))){
                s->close();
                return;
            }
        }
        LocalSocketEndPoint* ipcEndPoint = new LocalSocketEndPoint(s);
        ObjectEndPoint* endpoint = new ObjectEndPoint(ObjectEndPoint::Service, ipcEndPoint, this);
        Q_UNUSED(endpoint);
    }
}

/*
    Creates endpoint on service side.
*/
bool QRemoteServiceControlLocalSocketPrivate::createServiceEndPoint(const QString& ident)
{
    //other IPC mechanisms such as dbus may have to publish the
    //meta object definition for all registered service types
    QLocalServer::removeServer(ident);
    qDebug() << "Start listening for incoming connections";
    localServer = new QLocalServer(this);
    if ( !localServer->listen(ident) ) {
        qWarning() << "Cannot create local socket endpoint";
        return false;
    }
    connect(localServer, SIGNAL(newConnection()), this, SLOT(processIncoming()));
    if (localServer->hasPendingConnections())
        QTimer::singleShot(0, this, SLOT(processIncoming()));

    return true;
}

QRemoteServiceControlPrivate* QRemoteServiceControlPrivate::constructPrivateObject(QObject *parent)
{
  return new QRemoteServiceControlLocalSocketPrivate(parent);
}

/*
    Creates endpoint on client side.
*/
QObject* QRemoteServiceControlPrivate::proxyForService(const QRemoteServiceIdentifier& typeIdent, const QString& location)
{
    QLocalSocket* socket = new QLocalSocket();
    socket->connectToServer(location);
    if (!socket->isValid()) {
        qWarning() << "Cannot connect to remote service, trying to start service " << location;
        // try starting the service by hand
        QProcess *service = new QProcess();
        service->start(location);
        service->waitForStarted();
        if(service->error() != QProcess::UnknownError || service->state() != QProcess::Running) {
            qWarning() << "Unable to start service " << location << service->error() << service->errorString() << service->state();
            return false;
        }
        int i;
        socket->connectToServer(location);
        for(i = 0; !socket->isValid() && i < 100; i++){
            if(service->state() != QProcess::Running){
                qWarning() << "Service died on startup" << service->errorString();
                return false;
            }
			// Temporary hack till we can improve startup signaling
#ifdef Q_OS_WIN
			::Sleep(10);
#else
            struct timespec tm;
            tm.tv_sec = 0;
            tm.tv_nsec = 1000000;
            nanosleep(&tm, 0x0);
#endif
            socket->connectToServer(location);
            // keep trying for a while
        }
        qDebug() << "Number of loops: "  << i;
        if(!socket->isValid()){
            qWarning() << "Server failed to start within waiting period";
            return false;
        }
    }
    LocalSocketEndPoint* ipcEndPoint = new LocalSocketEndPoint(socket);
    ObjectEndPoint* endPoint = new ObjectEndPoint(ObjectEndPoint::Client, ipcEndPoint);

    QObject *proxy = endPoint->constructProxy(typeIdent);
    QObject::connect(proxy, SIGNAL(destroyed()), endPoint, SLOT(deleteLater()));
    return proxy;
}

#include "moc_qremoteservicecontrol_ls_p.cpp"
#include "qremoteservicecontrol_ls_p.moc"
QTM_END_NAMESPACE
<commit_msg>Fix for windows not having sys/socket.h<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qremoteservicecontrol_p.h"
#include "qremoteservicecontrol_ls_p.h"
#include "ipcendpoint_p.h"
#include "objectendpoint_p.h"

#include <QLocalServer>
#include <QLocalSocket>
#include <QDataStream>
#include <QTimer>
#include <QProcess>

#include <time.h>
#include <sys/types.h>          /* See NOTES */

#ifndef Q_OS_WIN
#include <sys/un.h>
#include <sys/socket.h>
#endif

#ifdef LOCAL_PEERCRED /* from sys/un.h */
#include <sys/ucred.h>
#endif

// Needed for ::Sleep, while we wait for a better solution
#ifdef Q_OS_WIN
#include <Windows.h>
#include <Winbase.h>
#endif

QTM_BEGIN_NAMESPACE

//IPC based on QLocalSocket

class LocalSocketEndPoint : public QServiceIpcEndPoint
{
    Q_OBJECT
public:
    LocalSocketEndPoint(QLocalSocket* s, QObject* parent = 0)
        : QServiceIpcEndPoint(parent), socket(s)
    {
        Q_ASSERT(socket);
        socket->setParent(this);
        connect(s, SIGNAL(readyRead()), this, SLOT(readIncoming()));
        connect(s, SIGNAL(disconnected()), this, SIGNAL(disconnected()));

        if (socket->bytesAvailable())
            QTimer::singleShot(0, this, SLOT(readIncoming()));
    }

    ~LocalSocketEndPoint()
    {
    }

protected:
    void flushPackage(const QServicePackage& package)
    {
        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
        out.setVersion(QDataStream::Qt_4_6);
        out << package;
        socket->write(block);
    }

protected slots:
    void readIncoming()
    {
        QDataStream in(socket);
        in.setVersion(QDataStream::Qt_4_6);

        while(socket->bytesAvailable()) {
            QServicePackage package;
            in >> package;
            incoming.enqueue(package);
        }

        emit readyRead();
    }

private:
    QLocalSocket* socket;
};

QRemoteServiceControlLocalSocketPrivate::QRemoteServiceControlLocalSocketPrivate(QObject* parent)
    : QRemoteServiceControlPrivate(parent)
{
}

void QRemoteServiceControlLocalSocketPrivate::publishServices( const QString& ident)
{
    createServiceEndPoint(ident) ;
}

void QRemoteServiceControlLocalSocketPrivate::processIncoming()
{
    qDebug() << "Processing incoming connect";
    if (localServer->hasPendingConnections()) {
        QLocalSocket* s = localServer->nextPendingConnection();
        //LocalSocketEndPoint owns socket
        int fd = s->socketDescriptor();
        if(getSecurityFilter()){
            QRemoteServiceControlLocalSocketCred qcred;
            memset(&qcred, 0, sizeof(QRemoteServiceControlLocalSocketCred));
            qcred.fd = fd;

#if defined(LOCAL_PEERCRED)
            struct xucred xuc;
            socklen_t len = sizeof(struct xucred);

            if(getsockopt(fd, SOL_SOCKET, LOCAL_PEERCRED, &xuc, &len) == 0) {
                qcred.pid = -1; // No PID on bsd
                qcred.uid = xuc.cr_uid;
                qcred.gid = xuc.cr_gid;

            }

#elif defined(SO_PEERCRED)
            struct ucred uc;
            socklen_t len = sizeof(struct ucred);

            if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) == 0) {
                qcred.pid = uc.pid;
                qcred.uid = uc.uid;
                qcred.gid = uc.gid;
            }
            else {
                s->close();
                perror("Failed to get peer credential");
                return;
            }
#else
            s->close();
            qWarning("Credentials check unsupprted on this platform");
            return;
#endif
            if(!getSecurityFilter()(reinterpret_cast<const void *>(&qcred))){
                s->close();
                return;
            }
        }
        LocalSocketEndPoint* ipcEndPoint = new LocalSocketEndPoint(s);
        ObjectEndPoint* endpoint = new ObjectEndPoint(ObjectEndPoint::Service, ipcEndPoint, this);
        Q_UNUSED(endpoint);
    }
}

/*
    Creates endpoint on service side.
*/
bool QRemoteServiceControlLocalSocketPrivate::createServiceEndPoint(const QString& ident)
{
    //other IPC mechanisms such as dbus may have to publish the
    //meta object definition for all registered service types
    QLocalServer::removeServer(ident);
    qDebug() << "Start listening for incoming connections";
    localServer = new QLocalServer(this);
    if ( !localServer->listen(ident) ) {
        qWarning() << "Cannot create local socket endpoint";
        return false;
    }
    connect(localServer, SIGNAL(newConnection()), this, SLOT(processIncoming()));
    if (localServer->hasPendingConnections())
        QTimer::singleShot(0, this, SLOT(processIncoming()));

    return true;
}

QRemoteServiceControlPrivate* QRemoteServiceControlPrivate::constructPrivateObject(QObject *parent)
{
  return new QRemoteServiceControlLocalSocketPrivate(parent);
}

/*
    Creates endpoint on client side.
*/
QObject* QRemoteServiceControlPrivate::proxyForService(const QRemoteServiceIdentifier& typeIdent, const QString& location)
{
    QLocalSocket* socket = new QLocalSocket();
    socket->connectToServer(location);
    if (!socket->isValid()) {
        qWarning() << "Cannot connect to remote service, trying to start service " << location;
        // try starting the service by hand
        QProcess *service = new QProcess();
        service->start(location);
        service->waitForStarted();
        if(service->error() != QProcess::UnknownError || service->state() != QProcess::Running) {
            qWarning() << "Unable to start service " << location << service->error() << service->errorString() << service->state();
            return false;
        }
        int i;
        socket->connectToServer(location);
        for(i = 0; !socket->isValid() && i < 100; i++){
            if(service->state() != QProcess::Running){
                qWarning() << "Service died on startup" << service->errorString();
                return false;
            }
			// Temporary hack till we can improve startup signaling
#ifdef Q_OS_WIN
			::Sleep(10);
#else
            struct timespec tm;
            tm.tv_sec = 0;
            tm.tv_nsec = 1000000;
            nanosleep(&tm, 0x0);
#endif
            socket->connectToServer(location);
            // keep trying for a while
        }
        qDebug() << "Number of loops: "  << i;
        if(!socket->isValid()){
            qWarning() << "Server failed to start within waiting period";
            return false;
        }
    }
    LocalSocketEndPoint* ipcEndPoint = new LocalSocketEndPoint(socket);
    ObjectEndPoint* endPoint = new ObjectEndPoint(ObjectEndPoint::Client, ipcEndPoint);

    QObject *proxy = endPoint->constructProxy(typeIdent);
    QObject::connect(proxy, SIGNAL(destroyed()), endPoint, SLOT(deleteLater()));
    return proxy;
}

#include "moc_qremoteservicecontrol_ls_p.cpp"
#include "qremoteservicecontrol_ls_p.moc"
QTM_END_NAMESPACE
<|endoftext|>


<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
// Copyright (c) 2014-2015 Francois Beaune, The appleseedhq Organization
//
// 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.
//

// Interface header.
#include "appleseed.h"

// appleseed.foundation headers.
#include "foundation/core/version.h"

// Standard headers.
#include <cstdio>

using namespace std;

namespace foundation
{

//
// Appleseed class implementation.
//

const char* Appleseed::get_lib_name()
{
// Windows.
#if defined _WIN32

    return "appleseed.dll";

// OS X.
#elif defined __APPLE__

    return "libappleseed.dylib";

// Other platforms.
#else

    return "appleseed.so";

#endif
}

const char* Appleseed::get_lib_version()
{
#ifdef APPLESEED_USE_SSE
    return APPLESEED_VERSION_STRING " (SSE)";
#else
    return APPLESEED_VERSION_STRING;
#endif
}

const char* Appleseed::get_lib_configuration()
{
#ifdef DEBUG
    return "Debug";
#else
    return "Release";
#endif
}

const char* Appleseed::get_lib_compilation_date()
{
    return __DATE__;
}

const char* Appleseed::get_lib_compilation_time()
{
    return __TIME__;
}

namespace
{
    struct SyntheticVersionString
    {
        char m_value[1024];

        SyntheticVersionString()
        {
            sprintf(
                m_value,
                "%s version %s",
                Appleseed::get_lib_name(),
                Appleseed::get_lib_version());
        }
    };

    SyntheticVersionString s_synthetic_version_string;
}

const char* Appleseed::get_synthetic_version_string()
{
    return s_synthetic_version_string.m_value;
}

}   // namespace foundation
<commit_msg>be specific about which version of SSE is enabled in appleseed's version string.<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
// Copyright (c) 2014-2015 Francois Beaune, The appleseedhq Organization
//
// 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.
//

// Interface header.
#include "appleseed.h"

// appleseed.foundation headers.
#include "foundation/core/version.h"

// Standard headers.
#include <cstdio>

using namespace std;

namespace foundation
{

//
// Appleseed class implementation.
//

const char* Appleseed::get_lib_name()
{
// Windows.
#if defined _WIN32

    return "appleseed.dll";

// OS X.
#elif defined __APPLE__

    return "libappleseed.dylib";

// Other platforms.
#else

    return "appleseed.so";

#endif
}

const char* Appleseed::get_lib_version()
{
#if defined APPLESEED_USE_SSE42
    return APPLESEED_VERSION_STRING " (SSE 4.2)";
#elif defined APPLESEED_USE_SSE
    return APPLESEED_VERSION_STRING " (SSE 2)";
#else
    return APPLESEED_VERSION_STRING;
#endif
}

const char* Appleseed::get_lib_configuration()
{
#ifdef DEBUG
    return "Debug";
#else
    return "Release";
#endif
}

const char* Appleseed::get_lib_compilation_date()
{
    return __DATE__;
}

const char* Appleseed::get_lib_compilation_time()
{
    return __TIME__;
}

namespace
{
    struct SyntheticVersionString
    {
        char m_value[1024];

        SyntheticVersionString()
        {
            sprintf(
                m_value,
                "%s version %s",
                Appleseed::get_lib_name(),
                Appleseed::get_lib_version());
        }
    };

    SyntheticVersionString s_synthetic_version_string;
}

const char* Appleseed::get_synthetic_version_string()
{
    return s_synthetic_version_string.m_value;
}

}   // namespace foundation
<|endoftext|>


<commit_before>#include "ModI2CGuest.h"

CModI2CGuest::CModI2CGuest(void)
{
  // nothing
}

void CModI2CGuest::Begin(void) //TODO add method to return entire EEPROM to host
{
  CModTemplate::Begin();
  ModGUID = 9; // GUID of this specific mod

  if (!Global.HasUSB) // if no usb
  {
    isMaster = false;
    SetMaster(true);
  }
}

void CModI2CGuest::OnReceive(int numBytes)
{
  byte errorOffset = 0;

  while (Wire.available())
  {
    byte type = Wire.read();

    if (type == 1) // received templay
    {
      byte input = Wire.read();
      Global.TempLayer = input;
    }
    else if (type == 2) // setEEPROM
    {
      byte byteA = Wire.read();
      byte byteB = Wire.read();
      byte packetSize = (byteA << 8) | byteB; i < MEM_EEPROM_SIZE;
      for (short i = packetSize && Wire.available(); i++)
      {
        PersMem.SetEEPROM(i, Wire.read());
      }
      if (packetSize >= I2C_PACKET_SIZE -2) // if packet size is at max size indicating there's another trailing packet
      {
        pullRate = 1; // then set refresh rate to 1 so that the next packet is retrieved as quickly as possible
      }
      else // if packet size is not at max size, then this is the last packet
      {
        pullRate = DEFAULT_I2C_PULL_RATE; // lowers the pull rate
        PersMem.CommitEEPROM(); // commits EEPROM
        Global.RequiresLoadData = true; // reloads persistent data to SRAM
      }
    }
    else if (type == 3) // return part of the EEPROM
    {
      byte byteA = Wire.read();
      byte byteB = Wire.read();
      SlaveIndex = 0;
      for (short i = (byteA << 8) | byteB; i < MEM_EEPROM_SIZE; i++)
      {
        SlaveArray[SlaveIndex] = PersMem.GetEEPROM(i);
        SlaveIndex++;
      }
    }
    else if (type == 4) // update brightness
    {
      Global.LEDBrightness = Wire.read();
    }
    else if (type == 5) // update refresh rate
    {
      Global.KeyDownDelay = Wire.read();
      Global.KeyUpDelay   = Wire.read();
    }
    else if (type == 6) // erreneous key
    {
      byte x = Wire.read();
      byte y = Wire.read();
      Global.LayerState[x][y] = Global.TempLayer;

      if (32 - SlaveIndex >= 3)
      {
        // We want all erreneous keys to appear before any keypresses that might have
        // occured, in chronological order.
        if (SlaveIndex > 0)
        {
          memcpy(SlaveArray + errorOffset + 3, SlaveArray + errorOffset, SlaveIndex - errorOffset);
        }
        SlaveArray[errorOffset + 0] = PersMem.GetKeyData(x, y, Global.TempLayer);
        SlaveArray[errorOffset + 1] = PersMem.GetKeyType(x, y, Global.TempLayer);
        SlaveArray[errorOffset + 2] = 5;
        SlaveIndex += 3;
        errorOffset += 3;
      }
    }
  }

  SetMaster(true);
  pullTimeout = 0;
}

void CModI2CGuest::LoadData(void)
{
  CModTemplate::LoadData();

  if (Global.HasUSB)
  {

  }
}

void CModI2CGuest::Loop(void)
{
  CModTemplate::Loop();

  if (!Global.HasUSB && isMaster)
  {
    if (SlaveIndex > 0)
    {
      Wire.beginTransmission(I2C_HOST_ADDRESS);
      Wire.write(SlaveArray, SlaveIndex);
      Wire.endTransmission();
      SlaveIndex = 0;
      SetMaster(false);
    }
    else if (Animus.Async1MSDelay())
    {
      pullTimeout++;
      if (pullTimeout >= pullRate) // Send an empty message to pull updates from the host
      {
        Wire.beginTransmission(I2C_HOST_ADDRESS);
        Wire.endTransmission();
        SetMaster(false);
      }
    }
  }
}

void CModI2CGuest::PressCoords(byte x, byte y)
{
  CModTemplate::PressCoords(x, y);
  if (!Global.HasUSB)
  {
    if (SlaveIndex < 32)
    {
      byte Coord = (y << 4) | x; // bitwise format is YYYYXXXX
      SlaveArray[SlaveIndex] = Coord;
      SlaveIndex++;
    }
  }

}

void CModI2CGuest::PrePress(byte val, byte type)
{
  CModTemplate::PrePress(val, type);
  if (Global.HasUSB)
  {

  }
}
void CModI2CGuest::PressKey(byte val, byte type)
{
  CModTemplate::PressKey(val, type);

  if (!Global.HasUSB)
  {
    if (SlaveIndex < 32)
    {
      SlaveArray[SlaveIndex] = val;
      SlaveIndex++;
      SlaveArray[SlaveIndex] = type;
      SlaveIndex++;
      SlaveArray[SlaveIndex] = 5;
      SlaveIndex++;
    }
  }
}
void CModI2CGuest::ReleaseKey(byte val, byte type)
{
  CModTemplate::ReleaseKey(val, type);

  if (!Global.HasUSB)
  {
    if (SlaveIndex < 32)
    {
      SlaveArray[SlaveIndex] = 0;
      SlaveIndex++;
      SlaveArray[SlaveIndex] = val;
      SlaveIndex++;
      SlaveArray[SlaveIndex] = type;
      SlaveIndex++;
      SlaveArray[SlaveIndex] = 1;
      SlaveIndex++;
    }
  }
}
/* TODO: Remove this part when everything is confirmed to work
static void CModI2CGuest::RequestEvent()
{
  ModI2CGuest.OnRequest();
}

static void CModI2CGuest::ReceiveEvent(int numBytes)
{
  ModI2CGuest.OnReceive(numBytes);
}
//*/
void CModI2CGuest::SerialComms(byte mode)
{
  CModTemplate::SerialComms(mode);
}

void CModI2CGuest::SetMaster(bool value)
{
  if (isMaster == value) return;

  isMaster = value;
  Wire.end();

  if (value)
  {
    Wire.begin();
    Wire.onReceive([] (int numBytes) {ModI2CGuest.OnReceive(numBytes);});
  }
  else
  {
    Wire.begin(I2C_GUEST_ADDRESS);
  }
}

CModI2CGuest ModI2CGuest;
<commit_msg>bugfix: wrong variables<commit_after>#include "ModI2CGuest.h"

CModI2CGuest::CModI2CGuest(void)
{
  // nothing
}

void CModI2CGuest::Begin(void) //TODO add method to return entire EEPROM to host
{
  CModTemplate::Begin();
  ModGUID = 9; // GUID of this specific mod

  if (!Global.HasUSB) // if no usb
  {
    isMaster = false;
    SetMaster(true);
  }
}

void CModI2CGuest::OnReceive(int numBytes)
{
  byte errorOffset = 0;

  while (Wire.available())
  {
    byte type = Wire.read();

    if (type == 1) // received templay
    {
      byte input = Wire.read();
      Global.TempLayer = input;
    }
    else if (type == 2) // setEEPROM
    {
      byte byteA = Wire.read();
      byte byteB = Wire.read();
      byte packetSize = (byteA << 8) | byteB; i < MEM_EEPROM_SIZE;
      bool isLastPacket = false;
      if (packetSize >= 200) // last packet size is 2XX where XX is actual size of packet, non-last packets are 0XX
      {
        packetSize = packetSize - 200;
        isLastPacket = true;
      }
      for (short i = packetSize && Wire.available(); i++)
      {
        PersMem.SetEEPROM(i, Wire.read());
      }
      if (isLastPacket)
      {
        pullRate = DEFAULT_I2C_PULL_RATE; // lowers the pull rate
        PersMem.CommitEEPROM(); // commits EEPROM
        Global.RequiresLoadData = true; // reloads persistent data to SRAM
      }
      else
      {
        pullRate = 1;
      }
    }
    else if (type == 3) // return part of the EEPROM
    {
      byte byteA = Wire.read();
      byte byteB = Wire.read();
      SlaveIndex = 0;
      for (short i = (byteA << 8) | byteB; i < MEM_EEPROM_SIZE; i++)
      {
        SlaveArray[SlaveIndex] = PersMem.GetEEPROM(i);
        SlaveIndex++;
      }
    }
    else if (type == 4) // update brightness
    {
      Global.LEDBrightness = Wire.read();
    }
    else if (type == 5) // update refresh rate
    {
      Global.KeyDownDelay = Wire.read();
      Global.KeyUpDelay   = Wire.read();
    }
    else if (type == 6) // erreneous key
    {
      byte x = Wire.read();
      byte y = Wire.read();
      Global.LayerState[x][y] = Global.TempLayer;

      if (32 - SlaveIndex >= 3)
      {
        // We want all erreneous keys to appear before any keypresses that might have
        // occured, in chronological order.
        if (SlaveIndex > 0)
        {
          memcpy(SlaveArray + errorOffset + 3, SlaveArray + errorOffset, SlaveIndex - errorOffset);
        }
        SlaveArray[errorOffset + 0] = PersMem.GetKeyData(x, y, Global.TempLayer);
        SlaveArray[errorOffset + 1] = PersMem.GetKeyType(x, y, Global.TempLayer);
        SlaveArray[errorOffset + 2] = 5;
        SlaveIndex += 3;
        errorOffset += 3;
      }
    }
  }

  SetMaster(true);
  pullTimeout = 0;
}

void CModI2CGuest::LoadData(void)
{
  CModTemplate::LoadData();

  if (Global.HasUSB)
  {

  }
}

void CModI2CGuest::Loop(void)
{
  CModTemplate::Loop();

  if (!Global.HasUSB && isMaster)
  {
    if (SlaveIndex > 0)
    {
      Wire.beginTransmission(I2C_HOST_ADDRESS);
      Wire.write(SlaveArray, SlaveIndex);
      Wire.endTransmission();
      SlaveIndex = 0;
      SetMaster(false);
    }
    else if (Animus.Async1MSDelay())
    {
      pullTimeout++;
      if (pullTimeout >= pullRate) // Send an empty message to pull updates from the host
      {
        Wire.beginTransmission(I2C_HOST_ADDRESS);
        Wire.endTransmission();
        SetMaster(false);
      }
    }
  }
}

void CModI2CGuest::PressCoords(byte x, byte y)
{
  CModTemplate::PressCoords(x, y);
  if (!Global.HasUSB)
  {
    if (SlaveIndex < 32)
    {
      byte Coord = (y << 4) | x; // bitwise format is YYYYXXXX
      SlaveArray[SlaveIndex] = Coord;
      SlaveIndex++;
    }
  }

}

void CModI2CGuest::PrePress(byte val, byte type)
{
  CModTemplate::PrePress(val, type);
  if (Global.HasUSB)
  {

  }
}
void CModI2CGuest::PressKey(byte val, byte type)
{
  CModTemplate::PressKey(val, type);

  if (!Global.HasUSB)
  {
    if (SlaveIndex < 32)
    {
      SlaveArray[SlaveIndex] = val;
      SlaveIndex++;
      SlaveArray[SlaveIndex] = type;
      SlaveIndex++;
      SlaveArray[SlaveIndex] = 5;
      SlaveIndex++;
    }
  }
}
void CModI2CGuest::ReleaseKey(byte val, byte type)
{
  CModTemplate::ReleaseKey(val, type);

  if (!Global.HasUSB)
  {
    if (SlaveIndex < 32)
    {
      SlaveArray[SlaveIndex] = 0;
      SlaveIndex++;
      SlaveArray[SlaveIndex] = val;
      SlaveIndex++;
      SlaveArray[SlaveIndex] = type;
      SlaveIndex++;
      SlaveArray[SlaveIndex] = 1;
      SlaveIndex++;
    }
  }
}
/* TODO: Remove this part when everything is confirmed to work
static void CModI2CGuest::RequestEvent()
{
  ModI2CGuest.OnRequest();
}

static void CModI2CGuest::ReceiveEvent(int numBytes)
{
  ModI2CGuest.OnReceive(numBytes);
}
//*/
void CModI2CGuest::SerialComms(byte mode)
{
  CModTemplate::SerialComms(mode);
}

void CModI2CGuest::SetMaster(bool value)
{
  if (isMaster == value) return;

  isMaster = value;
  Wire.end();

  if (value)
  {
    Wire.begin();
    Wire.onReceive([] (int numBytes) {ModI2CGuest.OnReceive(numBytes);});
  }
  else
  {
    Wire.begin(I2C_GUEST_ADDRESS);
  }
}

CModI2CGuest ModI2CGuest;
<|endoftext|>