<commit_before>/// this file exists to remove C++11 from CUDA, to support outdated nvcc compilers
#include "lkt.h"
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
//#include "mergesort.hh"
#include "tbb/tbb.h"
#include "quicksort.hh"

/*
std::ostream& operator<<(std::ostream& s, const lkt_point& p) {
  s << "{" << p.x << ", " << p.y << ", " << p.key << "}";
  return s;
}
*/

/// \todo change these to C++ and use templates. Or Macros. Something.

/// finds a heuristic value, using the given sample rate, splitting on the x-axis
static ord_t lkt_find_splitpoint_x(lkt_point* begin, lkt_point* end, size_t sample_rate) {
  double average = 0.0;
  size_t samples_taken = 0;
  sample_rate = (end - begin) / sample_rate + 1;
  for(lkt_point* i = begin; i < end; i += sample_rate, ++samples_taken) {
    average += i->x;
  }
  average /= samples_taken;
  return average;
}

/// finds a heuristic value, using the given sample rate, splitting on the y-axis
static ord_t lkt_find_splitpoint_y(lkt_point* begin, lkt_point* end, size_t sample_rate) {
  double average = 0.0;
  size_t samples_taken = 0;
  sample_rate = (end - begin) / sample_rate + 1;
  for(lkt_point* i = begin; i < end; i += sample_rate, ++samples_taken) {
    average += i->y;
  }
  average /= samples_taken;
  return average;
}

/// DO NOT change this to use the XOR method. It is slow.
static inline void lkt_swap(lkt_point* a, lkt_point* b) {
  lkt_point old_a = *a;
  *a = *b;
  *b = old_a;
}

static inline size_t get_heap_child_l(const size_t i) {return i * 2 + 1;}
static inline size_t get_heap_child_r(const size_t i) {return i * 2 + 2;}
static inline size_t get_heap_parent(const size_t i) {return (i - 1) / 2;}

static bool point_comparator_x(const lkt_point& a, const lkt_point& b) {
  return a.x < b.x;
}

static bool point_comparator_y(const lkt_point& a, const lkt_point& b) {
  return a.y < b.y;
}

/// \param sample_rate the rate to sample when finding the split point
static void lkt_sort_parallel(lkt_point* points, size_t len,
                              size_t sample_rate, fixlentree<lkt_split_point>& splitpoints, index_t splitpoint_parent, bool splitpoint_thisisleft,
                              bool xaxis,
                              const unsigned short current_depth, const unsigned short max_depth) {

  const size_t PARALLEL_QUICKSORT_THREADS = 8;
/*
  fprintf(stderr, "lkt_sort called for splitpoint %f\n", splitpoint);
  fprintf(stderr, "lkt_sort called for points %p, len %lu\n", (void*)points, len);
  fprintf(stderr, "lkt_sort called for splitpoint_i %lu\n", splitpoint_i);
  fprintf(stderr, "lkt_sort next splitpoint_is: %lu, %lu\n", get_heap_child_l(splitpoint_i), get_heap_child_r(splitpoint_i));

  fprintf(stderr, "sort at splitpoint_i = %lu\n", splitpoint_i);
  fflush(stdout);
*/
  if(len < 2 || current_depth == max_depth)
    return;

  // splitpoint is the value in the points array, by which the points will be partitioned
  // splitpoint_val is the (local) index in the points array, before which values are less than splitpoint.

  typedef ord_t (*splitpoint_finder_func_t)(lkt_point* begin, lkt_point* end, size_t sample_rate);
  typedef bool (*comparator_func_t)(const lkt_point&, const lkt_point&);

  // avoids conditionals.
  const splitpoint_finder_func_t find_split_func = (splitpoint_finder_func_t)((intptr_t)lkt_find_splitpoint_x * xaxis + (intptr_t)lkt_find_splitpoint_y * !xaxis);
  const comparator_func_t        comparator_func = (comparator_func_t)((intptr_t)point_comparator_x * xaxis + (intptr_t)point_comparator_y * !xaxis);

  const ord_t          splitpoint       = find_split_func(points, points + len, sample_rate);
  const lkt_point      splitpoint_point = {splitpoint, splitpoint, 0}; // cheaper to just assign both, than a conditional

//  cout << "debug len:" << len << " splitpoint: " << splitpoint << endl;

//  cout << "debug partition started" << endl;
  const uint_least64_t splitpoint_val   = parallel_quicksort_partition(points, points + len, splitpoint_point, PARALLEL_QUICKSORT_THREADS, comparator_func); ///< \todo fix last (?) block bug
//  cout << "debug partition finished" << endl;

  const lkt_split_point lkt_splitpoint         = {splitpoint, splitpoint_val};

  const index_t         splitpoint_next_parent = splitpoints.insert(splitpoint_parent, splitpoint_thisisleft, lkt_splitpoint);

  if(splitpoint_next_parent == splitpoints.tree_end)
    return;

  if(splitpoint_val == 0 || splitpoint_val == len - 1)
    return;

//  cout << "splitpoint " << (xaxis ? "x" : "y") << " val: " << splitpoint << " location: " << splitpoint_val << endl;

/*
lkt_sort_parallel(points, splitpoint_val, sample_rate,
                  splitpoints, splitpoint_next_parent, true,
                  !xaxis, current_depth + 1, max_depth);
lkt_sort_parallel(points + splitpoint_val, len - splitpoint_val, sample_rate,
                  splitpoints, splitpoint_next_parent, false,
                  !xaxis, current_depth + 1, max_depth);
*/

  tbb::parallel_invoke([&]() {lkt_sort_parallel(points, splitpoint_val, sample_rate,
                                                splitpoints, splitpoint_next_parent, true,
                                                !xaxis, current_depth + 1, max_depth);},
                       [&]() {lkt_sort_parallel(points + splitpoint_val, len - splitpoint_val, sample_rate,
                                                splitpoints, splitpoint_next_parent, false,
                                              !xaxis, current_depth + 1, max_depth);});

}

/// returns a heap
linear_kdtree lkt_create_parallel(lkt_point* points, size_t len) {

  const size_t PARALLEL_QUICKSORT_THREADS = 8;

  fprintf(stderr, "lkt_create called for points %p, true end %p \n", (void*)points, (void*)(points + len));

  if(sizeof(mortoncode_t) != 4) {
    fprintf(stderr, "mortoncode_t NOT 32 BITS! ERROR!ERROR!ERROR!"); /// \todo fix to be static_assert
    exit(1);
  }

  const unsigned short max_depth = sizeof(mortoncode_t) * CHAR_BIT;

  linear_kdtree tree;
  tree.points = points;
  tree.len    = len;

//  fprintf(stderr, "lkt_create depth %lu\n", (size_t)depth);
//  fprintf(stderr, "lkt_create split_points_len %lu\n", (size_t)tree.split_points_len);
//  fprintf(stderr, "lkt_create newing split_points size %lu\n", sizeof(lkt_split_point) * tree.split_points_len);
//  fprintf(stderr, "lkt_create newed split_points\n");

  const size_t sample_rate = 100;

//  fprintf(stderr, "lkt_create sorting\n");

  tree.split_points_len = len;

  fixlentree<lkt_split_point> splitpoints(tree.split_points_len); ///< \todo scope

  const ord_t           splitpoint       = lkt_find_splitpoint_x(points, points + len, sample_rate);
  const lkt_point       splitpoint_point = {splitpoint, splitpoint, 0}; // cheaper to just assign both, than a conditional
  const uint_least64_t  splitpoint_val   = parallel_quicksort_partition(points, &points[len], splitpoint_point, PARALLEL_QUICKSORT_THREADS, point_comparator_x);

  const lkt_split_point lkt_splitpoint = {splitpoint, splitpoint_val};
  const index_t         root           = splitpoints.insert_root(lkt_splitpoint);

//  cout << "first splitpoint x val: " << splitpoint << " location: " << splitpoint_val << endl;

  cout << "debug 0 parallel invoking" << endl;

  tbb::parallel_invoke([&]() {lkt_sort_parallel(points, splitpoint_val, sample_rate,
                                                splitpoints, root, true,
                                                false, 1, max_depth);},
                       [&]() {lkt_sort_parallel(points + splitpoint_val, len - splitpoint_val, sample_rate,
                                                splitpoints, root, false,
                                                false, 1, max_depth);});

/*
  lkt_sort_parallel(points, splitpoint_val, sample_rate,
                    splitpoints, root, true,
                    false, 1, max_depth);
  lkt_sort_parallel(points + splitpoint_val, len - splitpoint_val, sample_rate,
                    splitpoints, root, false,
                    false, 1, max_depth);
*/

  cout << "debug 1 splitpoints releasing" << endl;

  tree.split_points = splitpoints.release();

  fprintf(stderr, "lkt_create coding\n");

  tree.morton_codes = lkt_create_mortoncodes_parallel(tree.points, tree.len, tree.split_points);

//  fprintf(stderr, "lkt_create returning\n");
  return tree;
}

/*
// x value ALONE is used for comparison, to create an xpack
bool operator<(const lkt_point& rhs, const lkt_point& lhs) {
  return rhs.x < lhs.x;
}

bool operator<(const lqt_unified_node& rhs, const lqt_unified_node& lhs) {
  return rhs.location < lhs.location;
}

linear_quadtree_unified tbb_sortify_unified(linear_quadtree_unified lqt, const size_t threads) {
//  auto lowxpack = [](const rtree_point& rhs, const rtree_point& lhs) {
//    return rhs.x < rhs.y;
//  };
  tbb::task_scheduler_init init(threads);
  tbb::parallel_sort(lqt.nodes, lqt.nodes + lqt.length);
  return lqt;
}

/// does not block for GPU memory. Will fail, if GPU memory is insufficient.
linear_quadtree_unified lqt_create_heterogeneous(lkt_point* points, size_t len,
                                                       ord_t xstart, ord_t xend,
                                                       ord_t ystart, ord_t yend,
                                                       size_t* depth, const size_t threads) {
  return tbb_sortify_unified(lqt_nodify_cuda_unified(points, len, xstart, xend, ystart, yend, depth), threads);
}

/// \param threads the number of threads to use when sorting. ONLY used in the 'sort' part of the algorithm
rtree cuda_create_rtree_heterogeneously_mergesort(rtree_point* points, const size_t len, const size_t threads) {
  rtree_leaf* leaves = cuda_create_leaves_together(parallel_mergesort(points, points + len, threads), len);
  const size_t leaves_len = DIV_CEIL(len, RTREE_NODE_SIZE);

  rtree_node* previous_level = (rtree_node*) leaves;
  size_t      previous_len = leaves_len;
  size_t      depth = 1; // leaf level is 0
  while(previous_len > RTREE_NODE_SIZE) {
    previous_level = cuda_create_level(previous_level, previous_len);
    previous_len = DIV_CEIL(previous_len, RTREE_NODE_SIZE);
    ++depth;
  }

  rtree_node* root = (rtree_node*) malloc(sizeof(rtree_node));
  init_boundary(&root->bounding_box);
  root->num = previous_len;
  root->children = previous_level;
  for(size_t i = 0, end = previous_len; i != end; ++i)
    update_boundary(&root->bounding_box, &root->children[i].bounding_box);
  ++depth;

  rtree tree = {depth, root};
  return tree;
}
*/
/*
/// SISD sort via single CPU core (for benchmarks)
rtree cuda_create_rtree_sisd(rtree_point* points, const size_t len) {
  std::sort(points, points + len);
  rtree_leaf* leaves = cuda_create_leaves_together(points, len);
  const size_t leaves_len = DIV_CEIL(len, RTREE_NODE_SIZE);

  rtree_node* previous_level = (rtree_node*) leaves;
  size_t      previous_len = leaves_len;
  size_t      depth = 1; // leaf level is 0
  while(previous_len > RTREE_NODE_SIZE) {
    previous_level = cuda_create_level(previous_level, previous_len);
    previous_len = DIV_CEIL(previous_len, RTREE_NODE_SIZE);
    ++depth;
  }

p  rtree_node* root = (rtree_node*) malloc(sizeof(rtree_node));
  init_boundary(&root->bounding_box);
  root->num = previous_len;
  root->children = previous_level;
  for(size_t i = 0, end = previous_len; i != end; ++i)
    update_boundary(&root->bounding_box, &root->children[i].bounding_box);
  ++depth;

  rtree tree = {depth, root};
  return tree;
}
*/
<commit_msg>removed commented code<commit_after>/// this file exists to remove C++11 from CUDA, to support outdated nvcc compilers
#include "lkt.h"
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
//#include "mergesort.hh"
#include "tbb/tbb.h"
#include "quicksort.hh"

/*
std::ostream& operator<<(std::ostream& s, const lkt_point& p) {
  s << "{" << p.x << ", " << p.y << ", " << p.key << "}";
  return s;
}
*/

/// \todo change these to C++ and use templates. Or Macros. Something.

/// finds a heuristic value, using the given sample rate, splitting on the x-axis
static ord_t lkt_find_splitpoint_x(lkt_point* begin, lkt_point* end, size_t sample_rate) {
  double average = 0.0;
  size_t samples_taken = 0;
  sample_rate = (end - begin) / sample_rate + 1;
  for(lkt_point* i = begin; i < end; i += sample_rate, ++samples_taken) {
    average += i->x;
  }
  average /= samples_taken;
  return average;
}

/// finds a heuristic value, using the given sample rate, splitting on the y-axis
static ord_t lkt_find_splitpoint_y(lkt_point* begin, lkt_point* end, size_t sample_rate) {
  double average = 0.0;
  size_t samples_taken = 0;
  sample_rate = (end - begin) / sample_rate + 1;
  for(lkt_point* i = begin; i < end; i += sample_rate, ++samples_taken) {
    average += i->y;
  }
  average /= samples_taken;
  return average;
}

/// DO NOT change this to use the XOR method. It is slow.
static inline void lkt_swap(lkt_point* a, lkt_point* b) {
  lkt_point old_a = *a;
  *a = *b;
  *b = old_a;
}

static inline size_t get_heap_child_l(const size_t i) {return i * 2 + 1;}
static inline size_t get_heap_child_r(const size_t i) {return i * 2 + 2;}
static inline size_t get_heap_parent(const size_t i) {return (i - 1) / 2;}

static bool point_comparator_x(const lkt_point& a, const lkt_point& b) {
  return a.x < b.x;
}

static bool point_comparator_y(const lkt_point& a, const lkt_point& b) {
  return a.y < b.y;
}

/// \param sample_rate the rate to sample when finding the split point
static void lkt_sort_parallel(lkt_point* points, size_t len,
                              size_t sample_rate, fixlentree<lkt_split_point>& splitpoints, index_t splitpoint_parent, bool splitpoint_thisisleft,
                              bool xaxis,
                              const unsigned short current_depth, const unsigned short max_depth) {

  const size_t PARALLEL_QUICKSORT_THREADS = 8;
/*
  fprintf(stderr, "lkt_sort called for splitpoint %f\n", splitpoint);
  fprintf(stderr, "lkt_sort called for points %p, len %lu\n", (void*)points, len);
  fprintf(stderr, "lkt_sort called for splitpoint_i %lu\n", splitpoint_i);
  fprintf(stderr, "lkt_sort next splitpoint_is: %lu, %lu\n", get_heap_child_l(splitpoint_i), get_heap_child_r(splitpoint_i));

  fprintf(stderr, "sort at splitpoint_i = %lu\n", splitpoint_i);
  fflush(stdout);
*/
  if(len < 2 || current_depth == max_depth)
    return;

  // splitpoint is the value in the points array, by which the points will be partitioned
  // splitpoint_val is the (local) index in the points array, before which values are less than splitpoint.

  typedef ord_t (*splitpoint_finder_func_t)(lkt_point* begin, lkt_point* end, size_t sample_rate);
  typedef bool (*comparator_func_t)(const lkt_point&, const lkt_point&);

  // avoids conditionals.
  const splitpoint_finder_func_t find_split_func = (splitpoint_finder_func_t)((intptr_t)lkt_find_splitpoint_x * xaxis + (intptr_t)lkt_find_splitpoint_y * !xaxis);
  const comparator_func_t        comparator_func = (comparator_func_t)((intptr_t)point_comparator_x * xaxis + (intptr_t)point_comparator_y * !xaxis);

  const ord_t          splitpoint       = find_split_func(points, points + len, sample_rate);
  const lkt_point      splitpoint_point = {splitpoint, splitpoint, 0}; // cheaper to just assign both, than a conditional

//  cout << "debug len:" << len << " splitpoint: " << splitpoint << endl;

//  cout << "debug partition started" << endl;
  const uint_least64_t splitpoint_val   = parallel_quicksort_partition(points, points + len, splitpoint_point, PARALLEL_QUICKSORT_THREADS, comparator_func); ///< \todo fix last (?) block bug
//  cout << "debug partition finished" << endl;

  const lkt_split_point lkt_splitpoint         = {splitpoint, splitpoint_val};

  const index_t         splitpoint_next_parent = splitpoints.insert(splitpoint_parent, splitpoint_thisisleft, lkt_splitpoint);

  if(splitpoint_next_parent == splitpoints.tree_end)
    return;

  if(splitpoint_val == 0 || splitpoint_val == len - 1)
    return;

//  cout << "splitpoint " << (xaxis ? "x" : "y") << " val: " << splitpoint << " location: " << splitpoint_val << endl;

/*
lkt_sort_parallel(points, splitpoint_val, sample_rate,
                  splitpoints, splitpoint_next_parent, true,
                  !xaxis, current_depth + 1, max_depth);
lkt_sort_parallel(points + splitpoint_val, len - splitpoint_val, sample_rate,
                  splitpoints, splitpoint_next_parent, false,
                  !xaxis, current_depth + 1, max_depth);
*/

  tbb::parallel_invoke([&]() {lkt_sort_parallel(points, splitpoint_val, sample_rate,
                                                splitpoints, splitpoint_next_parent, true,
                                                !xaxis, current_depth + 1, max_depth);},
                       [&]() {lkt_sort_parallel(points + splitpoint_val, len - splitpoint_val, sample_rate,
                                                splitpoints, splitpoint_next_parent, false,
                                              !xaxis, current_depth + 1, max_depth);});

}

/// returns a heap
linear_kdtree lkt_create_parallel(lkt_point* points, size_t len) {

  const size_t PARALLEL_QUICKSORT_THREADS = 8;

  fprintf(stderr, "lkt_create called for points %p, true end %p \n", (void*)points, (void*)(points + len));

  if(sizeof(mortoncode_t) != 4) {
    fprintf(stderr, "mortoncode_t NOT 32 BITS! ERROR!ERROR!ERROR!"); /// \todo fix to be static_assert
    exit(1);
  }

  const unsigned short max_depth = sizeof(mortoncode_t) * CHAR_BIT;

  linear_kdtree tree;
  tree.points = points;
  tree.len    = len;

//  fprintf(stderr, "lkt_create depth %lu\n", (size_t)depth);
//  fprintf(stderr, "lkt_create split_points_len %lu\n", (size_t)tree.split_points_len);
//  fprintf(stderr, "lkt_create newing split_points size %lu\n", sizeof(lkt_split_point) * tree.split_points_len);
//  fprintf(stderr, "lkt_create newed split_points\n");

  const size_t sample_rate = 100;

//  fprintf(stderr, "lkt_create sorting\n");

  tree.split_points_len = len;

  fixlentree<lkt_split_point> splitpoints(tree.split_points_len); ///< \todo scope

  const ord_t           splitpoint       = lkt_find_splitpoint_x(points, points + len, sample_rate);
  const lkt_point       splitpoint_point = {splitpoint, splitpoint, 0}; // cheaper to just assign both, than a conditional
  const uint_least64_t  splitpoint_val   = parallel_quicksort_partition(points, &points[len], splitpoint_point, PARALLEL_QUICKSORT_THREADS, point_comparator_x);

  const lkt_split_point lkt_splitpoint = {splitpoint, splitpoint_val};
  const index_t         root           = splitpoints.insert_root(lkt_splitpoint);

//  cout << "first splitpoint x val: " << splitpoint << " location: " << splitpoint_val << endl;

  cout << "debug 0 parallel invoking" << endl;

  tbb::parallel_invoke([&]() {lkt_sort_parallel(points, splitpoint_val, sample_rate,
                                                splitpoints, root, true,
                                                false, 1, max_depth);},
                       [&]() {lkt_sort_parallel(points + splitpoint_val, len - splitpoint_val, sample_rate,
                                                splitpoints, root, false,
                                                false, 1, max_depth);});

/*
  lkt_sort_parallel(points, splitpoint_val, sample_rate,
                    splitpoints, root, true,
                    false, 1, max_depth);
  lkt_sort_parallel(points + splitpoint_val, len - splitpoint_val, sample_rate,
                    splitpoints, root, false,
                    false, 1, max_depth);
*/

  cout << "debug 1 splitpoints releasing" << endl;

  tree.split_points = splitpoints.release();

  fprintf(stderr, "lkt_create coding\n");

  tree.morton_codes = lkt_create_mortoncodes_parallel(tree.points, tree.len, tree.split_points);

//  fprintf(stderr, "lkt_create returning\n");
  return tree;
}
<|endoftext|>


<commit_before><commit_msg>stop uninitialized memory leaking into resource files.<commit_after><|endoftext|>


<commit_before>// Time:  O(max(r, c) * wlogw)
// Space: O(w^2)

class Solution {
public:
    int shortestDistance(vector<vector<int>>& maze, vector<int>& start, vector<int>& destination) {
        static const vector<vector<int>> dirs = {{-1, 0}, {0, 1}, {0, -1}, {1, 0}};
        priority_queue<node, vector<node>, greater<node>> heap;
        unordered_set<int> visited;
        heap.emplace(0, start);

        while (!heap.empty()) {
            int dist = 0;
            vector<int> node;
            tie(dist, node) = heap.top();
            heap.pop();
            if (visited.count(hash(maze, node))) {
                continue;
            }
            if (node[0] == destination[0] &&
                node[1] == destination[1]) {
                return dist;
            }

            visited.emplace(hash(maze, node));
            for (const auto& dir : dirs) {
                int neighbor_dist = 0;
                vector<int> neighbor;
                tie(neighbor_dist, neighbor) = findNeighbor(maze, node, dir);
                heap.emplace(dist + neighbor_dist, neighbor);
            }
        }

        return -1;
    }

private:
    using node = pair<int, vector<int>>;

    node findNeighbor(const vector<vector<int>>& maze,
                      const vector<int>& node, const vector<int>& dir) {
        vector<int> cur_node = node;
        int dist = 0;

        while (0 <= cur_node[0] + dir[0] && cur_node[0] + dir[0] < maze.size() &&
               0 <= cur_node[1] + dir[1] && cur_node[1] + dir[1] < maze[0].size() &&
               !maze[cur_node[0] + dir[0]][cur_node[1] + dir[1]]) {
            cur_node[0] += dir[0];
            cur_node[1] += dir[1];
            ++dist;
        }
        return {dist, cur_node};
    }

    int hash(const vector<vector<int>>& maze, const vector<int>& node) {
        return node[0] * maze[0].size() + node[1];
    }
};
<commit_msg>Update the-maze-ii.cpp<commit_after>// Time:  O(max(r, c) * wlogw)
// Space: O(w)

class Solution {
public:
    int shortestDistance(vector<vector<int>>& maze, vector<int>& start, vector<int>& destination) {
        static const vector<vector<int>> dirs = {{-1, 0}, {0, 1}, {0, -1}, {1, 0}};
        priority_queue<node, vector<node>, greater<node>> heap;
        unordered_set<int> visited;
        heap.emplace(0, start);

        while (!heap.empty()) {
            int dist = 0;
            vector<int> node;
            tie(dist, node) = heap.top();
            heap.pop();
            if (visited.count(hash(maze, node))) {
                continue;
            }
            if (node[0] == destination[0] &&
                node[1] == destination[1]) {
                return dist;
            }

            visited.emplace(hash(maze, node));
            for (const auto& dir : dirs) {
                int neighbor_dist = 0;
                vector<int> neighbor;
                tie(neighbor_dist, neighbor) = findNeighbor(maze, node, dir);
                heap.emplace(dist + neighbor_dist, neighbor);
            }
        }

        return -1;
    }

private:
    using node = pair<int, vector<int>>;

    node findNeighbor(const vector<vector<int>>& maze,
                      const vector<int>& node, const vector<int>& dir) {
        vector<int> cur_node = node;
        int dist = 0;

        while (0 <= cur_node[0] + dir[0] && cur_node[0] + dir[0] < maze.size() &&
               0 <= cur_node[1] + dir[1] && cur_node[1] + dir[1] < maze[0].size() &&
               !maze[cur_node[0] + dir[0]][cur_node[1] + dir[1]]) {
            cur_node[0] += dir[0];
            cur_node[1] += dir[1];
            ++dist;
        }
        return {dist, cur_node};
    }

    int hash(const vector<vector<int>>& maze, const vector<int>& node) {
        return node[0] * maze[0].size() + node[1];
    }
};
<|endoftext|>


<commit_before>#include <bits/stdc++.h>
#define MAXN 100002
#define X first
#define Y second
#define PB push_back
#define MP make_pair
#define INF -1
using namespace std;
typedef pair<int, int> P;

int N, M, K;
vector<P> E[MAXN];
vector<int> S;
bool isStorage[MAXN];
int ans = -1;

int main() {
	int i, j, u, v, l, n;

	cin >> N >> M >> K;

	for (i = 0; i < M; ++i) {
		cin >> u >> v >> l;
		E[u].PB(MP(v, l));
		E[v].PB(MP(u, l));
	}

	for (i = 0; i < K; ++i) {
		cin >> u;

		S.PB(u);
		isStorage[u] = true;
	}

	for (i = 0; i < K; ++i) {
		n = E[S[i]].size();

		for (j = 0; j < n; ++j) {
			if (!isStorage[E[S[i]][j].X]) {
				if (ans == INF) {
					ans = E[S[i]][j].Y;
					continue;
				}

				ans = min(ans, E[S[i]][j].Y);
			}
		}
	}

	cout << ans << "\n";
	return 0;
}
<commit_msg>Codeforces - 707-B - Bakery - SOLVED - UPDATED - BEST SOLUTION<commit_after>#include <bits/stdc++.h>
#define optimize_ios ios_base::sync_with_stdio(0);cin.tie(0);
#define MAXN 100002
#define X first
#define Y second
#define PB push_back
#define MP make_pair
#define INF -1
using namespace std;
typedef pair<int, int> P;
typedef pair<P, int> T;

int N, M, K;
T E[MAXN];
bool isStorage[MAXN];
int ans = -1;

int main() {
	optimize_ios

	int i, j, u, v, l, n;

	cin >> N >> M >> K;

	for (i = 0; i < M; ++i) {
		cin >> u >> v >> l;
		E[i].X.X = u;
		E[i].X.Y = v;
		E[i].Y = l;
	}

	for (i = 0; i < K; ++i) {
		cin >> u;
		isStorage[u] = true;
	}

	for (i = 0; i < M; ++i) {
		if (isStorage[E[i].X.X] ^ isStorage[E[i].X.Y]) {
				if (ans == INF) {
					ans = E[i].Y;
					continue;
				}

				ans = min(ans, E[i].Y);
		}
	}

	cout << ans << "\n";
	return 0;
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "call/version.h"

namespace webrtc {

// The timestamp is always in UTC.
const char* const kSourceTimestamp = "WebRTC source stamp 2021-01-05T04:04:14";

void LoadWebRTCVersionInRegister() {
  // Using volatile to instruct the compiler to not optimize `p` away even
  // if it looks unused.
  const char* volatile p = kSourceTimestamp;
  static_cast<void>(p);
}

}  // namespace webrtc
<commit_msg>Update WebRTC code version (2021-01-06T04:02:29).<commit_after>/*
 *  Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "call/version.h"

namespace webrtc {

// The timestamp is always in UTC.
const char* const kSourceTimestamp = "WebRTC source stamp 2021-01-06T04:02:29";

void LoadWebRTCVersionInRegister() {
  // Using volatile to instruct the compiler to not optimize `p` away even
  // if it looks unused.
  const char* volatile p = kSourceTimestamp;
  static_cast<void>(p);
}

}  // namespace webrtc
<|endoftext|>


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

#include "fstream.hh"
#include "align.hh"
#include "circular_buffer.hh"
#include "semaphore.hh"
#include "reactor.hh"
#include <malloc.h>
#include <string.h>

class file_data_source_impl : public data_source_impl {
    file _file;
    file_input_stream_options _options;
    uint64_t _pos;
    uint64_t _remain;
    circular_buffer<future<temporary_buffer<char>>> _read_buffers;
    unsigned _reads_in_progress = 0;
    std::experimental::optional<promise<>> _done;
public:
    file_data_source_impl(file f, uint64_t offset, uint64_t len, file_input_stream_options options)
            : _file(std::move(f)), _options(options), _pos(offset), _remain(len) {
        // prevent wraparounds
        _remain = std::min(std::numeric_limits<uint64_t>::max() - _pos, _remain);
    }
    virtual future<temporary_buffer<char>> get() override {
        if (_read_buffers.empty()) {
            issue_read_aheads(1);
        }
        auto ret = std::move(_read_buffers.front());
        _read_buffers.pop_front();
        return ret;
    }
    virtual future<> close() {
        _done.emplace();
        if (!_reads_in_progress) {
            _done->set_value();
        }
        return _done->get_future().then([this] {
            for (auto&& c : _read_buffers) {
                c.ignore_ready_future();
            }
        });
    }
private:
    void issue_read_aheads(unsigned min_ra = 0) {
        if (_done) {
            return;
        }
        auto ra = std::max(min_ra, _options.read_ahead);
        _read_buffers.reserve(ra); // prevent push_back() failure
        while (_read_buffers.size() < ra) {
            if (!_remain) {
                if (_read_buffers.size() >= min_ra) {
                    return;
                }
                _read_buffers.push_back(make_ready_future<temporary_buffer<char>>());
                continue;
            }
            ++_reads_in_progress;
            // if _pos is not dma-aligned, we'll get a short read.  Account for that.
            // Also avoid reading beyond _remain.
            uint64_t align = _file.disk_read_dma_alignment();
            auto start = align_down(_pos, align);
            auto end = align_up(std::min(start + _options.buffer_size, _pos + _remain), align);
            auto len = end - start;
            _read_buffers.push_back(futurize<future<temporary_buffer<char>>>::apply([&] {
                    return _file.dma_read_bulk<char>(start, len, _options.io_priority_class);
            }).then_wrapped(
                    [this, start, end, pos = _pos, remain = _remain] (future<temporary_buffer<char>> ret) {
                issue_read_aheads();
                --_reads_in_progress;
                if (_done && !_reads_in_progress) {
                    _done->set_value();
                }
                if ((pos == start && end <= pos + remain) || ret.failed()) {
                    // no games needed
                    return ret;
                } else {
                    // first or last buffer, need trimming
                    auto tmp = ret.get0();
                    auto real_end = start + tmp.size();
                    if (real_end <= pos) {
                        return make_ready_future<temporary_buffer<char>>();
                    }
                    if (real_end > pos + remain) {
                        tmp.trim(pos + remain - start);
                    }
                    if (start < pos) {
                        tmp.trim_front(pos - start);
                    }
                    return make_ready_future<temporary_buffer<char>>(std::move(tmp));
                }
            }));
            auto old_pos = _pos;
            _pos = end;
            _remain = std::max(_pos, old_pos + _remain) - _pos;
        };
    }
};

class file_data_source : public data_source {
public:
    file_data_source(file f, uint64_t offset, uint64_t len, file_input_stream_options options)
        : data_source(std::make_unique<file_data_source_impl>(
                std::move(f), offset, len, options)) {}
};

input_stream<char> make_file_input_stream(
        file f, uint64_t offset, uint64_t len, file_input_stream_options options) {
    return input_stream<char>(file_data_source(std::move(f), offset, len, std::move(options)));
}

input_stream<char> make_file_input_stream(
        file f, uint64_t offset, file_input_stream_options options) {
    return make_file_input_stream(std::move(f), offset, std::numeric_limits<uint64_t>::max(), std::move(options));
}

input_stream<char> make_file_input_stream(
        file f, file_input_stream_options options) {
    return make_file_input_stream(std::move(f), 0, std::move(options));
}

class file_data_sink_impl : public data_sink_impl {
    file _file;
    file_output_stream_options _options;
    uint64_t _pos = 0;
    semaphore _write_behind_sem = { _options.write_behind };
    future<> _background_writes_done = make_ready_future<>();
    bool _failed = false;
public:
    file_data_sink_impl(file f, file_output_stream_options options)
            : _file(std::move(f)), _options(options) {}
    future<> put(net::packet data) { abort(); }
    virtual temporary_buffer<char> allocate_buffer(size_t size) override {
        return temporary_buffer<char>::aligned(_file.memory_dma_alignment(), size);
    }
    virtual future<> put(temporary_buffer<char> buf) override {
        uint64_t pos = _pos;
        _pos += buf.size();
        if (!_options.write_behind) {
            return do_put(pos, std::move(buf));
        }
        // Write behind strategy:
        //
        // 1. Issue N writes in parallel, using a semaphore to limit to N
        // 2. Collect results in _background_writes_done, merging exception futures
        // 3. If we've already seen a failure, don't issue more writes.
        return _write_behind_sem.wait().then([this, pos, buf = std::move(buf)] () mutable {
            if (_failed) {
                _write_behind_sem.signal();
                auto ret = std::move(_background_writes_done);
                _background_writes_done = make_ready_future<>();
                return ret;
            }
            auto this_write_done = do_put(pos, std::move(buf)).finally([this] {
                _write_behind_sem.signal();
            });
            _background_writes_done = when_all(std::move(_background_writes_done), std::move(this_write_done))
                    .then([this] (std::tuple<future<>, future<>> possible_errors) {
                // merge the two errors, preferring the first
                auto& e1 = std::get<0>(possible_errors);
                auto& e2 = std::get<1>(possible_errors);
                if (e1.failed()) {
                    e2.ignore_ready_future();
                    return std::move(e1);
                } else {
                    if (e2.failed()) {
                        _failed = true;
                    }
                    return std::move(e2);
                }
            });
            return make_ready_future<>();
        });
    }
public:
    future<> do_put(uint64_t pos, temporary_buffer<char> buf) noexcept {
      try {
        // put() must usually be of chunks multiple of file::dma_alignment.
        // Only the last part can have an unaligned length. If put() was
        // called again with an unaligned pos, we have a bug in the caller.
        assert(!(pos & (_file.disk_write_dma_alignment() - 1)));
        bool truncate = false;
        auto p = static_cast<const char*>(buf.get());
        size_t buf_size = buf.size();

        if ((buf.size() & (_file.disk_write_dma_alignment() - 1)) != 0) {
            // If buf size isn't aligned, copy its content into a new aligned buf.
            // This should only happen when the user calls output_stream::flush().
            auto tmp = allocate_buffer(align_up(buf.size(), _file.disk_write_dma_alignment()));
            ::memcpy(tmp.get_write(), buf.get(), buf.size());
            buf = std::move(tmp);
            p = buf.get();
            buf_size = buf.size();
            truncate = true;
        }

        return _file.dma_write(pos, p, buf_size, _options.io_priority_class).then(
                [this, buf = std::move(buf), truncate] (size_t size) {
            if (truncate) {
                return _file.truncate(_pos);
            }
            return make_ready_future<>();
        });
      } catch (...) {
          return make_exception_future<>(std::current_exception());
      }
    }
    future<> wait() {
        return _write_behind_sem.wait(_options.write_behind).then([this] {
            return _background_writes_done.then([this] {
                // restore to pristine state; for flush() + close() sequence
                // (we allow either flush, or close, or both)
                _write_behind_sem.signal(_options.write_behind);
                _background_writes_done = make_ready_future<>();
            });
        });
    }
public:
    virtual future<> flush() override {
        return wait().then([this] {
            return _file.flush();
        });
    }
    virtual future<> close() {
        return wait().then([this] {
            return _file.close();
        });
    }
};

class file_data_sink : public data_sink {
public:
    file_data_sink(file f, file_output_stream_options options)
        : data_sink(std::make_unique<file_data_sink_impl>(
                std::move(f), options)) {}
};

output_stream<char> make_file_output_stream(file f, size_t buffer_size) {
    file_output_stream_options options;
    options.buffer_size = buffer_size;
    return make_file_output_stream(std::move(f), options);
}

output_stream<char> make_file_output_stream(file f, file_output_stream_options options) {
    return output_stream<char>(file_data_sink(std::move(f), options), options.buffer_size, true);
}

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

#include "fstream.hh"
#include "align.hh"
#include "circular_buffer.hh"
#include "semaphore.hh"
#include "reactor.hh"
#include <malloc.h>
#include <string.h>

class file_data_source_impl : public data_source_impl {
    file _file;
    file_input_stream_options _options;
    uint64_t _pos;
    uint64_t _remain;
    circular_buffer<future<temporary_buffer<char>>> _read_buffers;
    unsigned _reads_in_progress = 0;
    std::experimental::optional<promise<>> _done;
public:
    file_data_source_impl(file f, uint64_t offset, uint64_t len, file_input_stream_options options)
            : _file(std::move(f)), _options(options), _pos(offset), _remain(len) {
        // prevent wraparounds
        _remain = std::min(std::numeric_limits<uint64_t>::max() - _pos, _remain);
    }
    virtual future<temporary_buffer<char>> get() override {
        if (_read_buffers.empty()) {
            issue_read_aheads(1);
        }
        auto ret = std::move(_read_buffers.front());
        _read_buffers.pop_front();
        return ret;
    }
    virtual future<> close() {
        _done.emplace();
        if (!_reads_in_progress) {
            _done->set_value();
        }
        return _done->get_future().then([this] {
            for (auto&& c : _read_buffers) {
                c.ignore_ready_future();
            }
        });
    }
private:
    void issue_read_aheads(unsigned min_ra = 0) {
        if (_done) {
            return;
        }
        auto ra = std::max(min_ra, _options.read_ahead);
        _read_buffers.reserve(ra); // prevent push_back() failure
        while (_read_buffers.size() < ra) {
            if (!_remain) {
                if (_read_buffers.size() >= min_ra) {
                    return;
                }
                _read_buffers.push_back(make_ready_future<temporary_buffer<char>>());
                continue;
            }
            ++_reads_in_progress;
            // if _pos is not dma-aligned, we'll get a short read.  Account for that.
            // Also avoid reading beyond _remain.
            uint64_t align = _file.disk_read_dma_alignment();
            auto start = align_down(_pos, align);
            auto end = align_up(std::min(start + _options.buffer_size, _pos + _remain), align);
            auto len = end - start;
            _read_buffers.push_back(futurize<future<temporary_buffer<char>>>::apply([&] {
                    return _file.dma_read_bulk<char>(start, len, _options.io_priority_class);
            }).then_wrapped(
                    [this, start, end, pos = _pos, remain = _remain] (future<temporary_buffer<char>> ret) {
                issue_read_aheads();
                --_reads_in_progress;
                if (_done && !_reads_in_progress) {
                    _done->set_value();
                }
                if ((pos == start && end <= pos + remain) || ret.failed()) {
                    // no games needed
                    return ret;
                } else {
                    // first or last buffer, need trimming
                    auto tmp = ret.get0();
                    auto real_end = start + tmp.size();
                    if (real_end <= pos) {
                        return make_ready_future<temporary_buffer<char>>();
                    }
                    if (real_end > pos + remain) {
                        tmp.trim(pos + remain - start);
                    }
                    if (start < pos) {
                        tmp.trim_front(pos - start);
                    }
                    return make_ready_future<temporary_buffer<char>>(std::move(tmp));
                }
            }));
            auto old_pos = _pos;
            _pos = end;
            _remain = std::max(_pos, old_pos + _remain) - _pos;
        };
    }
};

class file_data_source : public data_source {
public:
    file_data_source(file f, uint64_t offset, uint64_t len, file_input_stream_options options)
        : data_source(std::make_unique<file_data_source_impl>(
                std::move(f), offset, len, options)) {}
};

input_stream<char> make_file_input_stream(
        file f, uint64_t offset, uint64_t len, file_input_stream_options options) {
    return input_stream<char>(file_data_source(std::move(f), offset, len, std::move(options)));
}

input_stream<char> make_file_input_stream(
        file f, uint64_t offset, file_input_stream_options options) {
    return make_file_input_stream(std::move(f), offset, std::numeric_limits<uint64_t>::max(), std::move(options));
}

input_stream<char> make_file_input_stream(
        file f, file_input_stream_options options) {
    return make_file_input_stream(std::move(f), 0, std::move(options));
}

class file_data_sink_impl : public data_sink_impl {
    file _file;
    file_output_stream_options _options;
    uint64_t _pos = 0;
    semaphore _write_behind_sem = { _options.write_behind };
    future<> _background_writes_done = make_ready_future<>();
    bool _failed = false;
public:
    file_data_sink_impl(file f, file_output_stream_options options)
            : _file(std::move(f)), _options(options) {}
    future<> put(net::packet data) { abort(); }
    virtual temporary_buffer<char> allocate_buffer(size_t size) override {
        return temporary_buffer<char>::aligned(_file.memory_dma_alignment(), size);
    }
    virtual future<> put(temporary_buffer<char> buf) override {
        uint64_t pos = _pos;
        _pos += buf.size();
        if (!_options.write_behind) {
            return do_put(pos, std::move(buf));
        }
        // Write behind strategy:
        //
        // 1. Issue N writes in parallel, using a semaphore to limit to N
        // 2. Collect results in _background_writes_done, merging exception futures
        // 3. If we've already seen a failure, don't issue more writes.
        return _write_behind_sem.wait().then([this, pos, buf = std::move(buf)] () mutable {
            if (_failed) {
                _write_behind_sem.signal();
                auto ret = std::move(_background_writes_done);
                _background_writes_done = make_ready_future<>();
                return ret;
            }
            auto this_write_done = do_put(pos, std::move(buf)).finally([this] {
                _write_behind_sem.signal();
            });
            _background_writes_done = when_all(std::move(_background_writes_done), std::move(this_write_done))
                    .then([this] (std::tuple<future<>, future<>> possible_errors) {
                // merge the two errors, preferring the first
                auto& e1 = std::get<0>(possible_errors);
                auto& e2 = std::get<1>(possible_errors);
                if (e1.failed()) {
                    e2.ignore_ready_future();
                    return std::move(e1);
                } else {
                    if (e2.failed()) {
                        _failed = true;
                    }
                    return std::move(e2);
                }
            });
            return make_ready_future<>();
        });
    }
public:
    future<> do_put(uint64_t pos, temporary_buffer<char> buf) noexcept {
      try {
        // put() must usually be of chunks multiple of file::dma_alignment.
        // Only the last part can have an unaligned length. If put() was
        // called again with an unaligned pos, we have a bug in the caller.
        assert(!(pos & (_file.disk_write_dma_alignment() - 1)));
        bool truncate = false;
        auto p = static_cast<const char*>(buf.get());
        size_t buf_size = buf.size();

        if ((buf.size() & (_file.disk_write_dma_alignment() - 1)) != 0) {
            // If buf size isn't aligned, copy its content into a new aligned buf.
            // This should only happen when the user calls output_stream::flush().
            auto tmp = allocate_buffer(align_up(buf.size(), _file.disk_write_dma_alignment()));
            ::memcpy(tmp.get_write(), buf.get(), buf.size());
            buf = std::move(tmp);
            p = buf.get();
            buf_size = buf.size();
            truncate = true;
        }

        return _file.dma_write(pos, p, buf_size, _options.io_priority_class).then(
                [this, buf = std::move(buf), truncate] (size_t size) {
            if (truncate) {
                return _file.truncate(_pos);
            }
            return make_ready_future<>();
        });
      } catch (...) {
          return make_exception_future<>(std::current_exception());
      }
    }
    future<> wait() {
        // restore to pristine state; for flush() + close() sequence
        // (we allow either flush, or close, or both)
        return _write_behind_sem.wait(_options.write_behind).then([this] {
            return std::exchange(_background_writes_done, make_ready_future<>());
        }).finally([this] {
            _write_behind_sem.signal(_options.write_behind);
        });
    }
public:
    virtual future<> flush() override {
        return wait().then([this] {
            return _file.flush();
        });
    }
    virtual future<> close() {
        return wait().then([this] {
            return _file.close();
        });
    }
};

class file_data_sink : public data_sink {
public:
    file_data_sink(file f, file_output_stream_options options)
        : data_sink(std::make_unique<file_data_sink_impl>(
                std::move(f), options)) {}
};

output_stream<char> make_file_output_stream(file f, size_t buffer_size) {
    file_output_stream_options options;
    options.buffer_size = buffer_size;
    return make_file_output_stream(std::move(f), options);
}

output_stream<char> make_file_output_stream(file f, file_output_stream_options options) {
    return output_stream<char>(file_data_sink(std::move(f), options), options.buffer_size, true);
}

<|endoftext|>


<commit_before>/**
 * \file
 * \brief Semaphore C-API test cases
 *
 * \author Copyright (C) 2017 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "distortos/Semaphore.hpp"
#include "distortos/C-API/Semaphore.h"

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local functions
+---------------------------------------------------------------------------------------------------------------------*/

void testCommon(distortos_Semaphore& semaphore, const unsigned int value, const unsigned int maxValue = UINT_MAX)
{
	REQUIRE(semaphore.value == std::min(value, maxValue));
	REQUIRE(semaphore.maxValue == maxValue);
	distortos_Semaphore constructed;
	memcpy(&constructed, &semaphore, sizeof(semaphore));
	REQUIRE(distortos_Semaphore_destruct(&semaphore) == 0);
	struct distortos_Semaphore destructed;
	memcpy(&destructed, &semaphore, sizeof(semaphore));

	memset(&semaphore, 0, sizeof(semaphore));

	const auto realSemaphore = new (&semaphore) distortos::Semaphore {value, maxValue};
	REQUIRE(realSemaphore->getValue() == std::min(value, maxValue));
	REQUIRE(memcmp(&constructed, &semaphore, sizeof(semaphore)) == 0);
	realSemaphore->~Semaphore();
	REQUIRE(memcmp(&destructed, &semaphore, sizeof(semaphore)) == 0);
}

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| global test cases
+---------------------------------------------------------------------------------------------------------------------*/

TEST_CASE("Testing DISTORTOS_SEMAPHORE_INITIALIZER()", "[initializer]")
{
	constexpr unsigned int lowRandomValue {0x2c380555};
	constexpr unsigned int highRandomValue {0x8d87de63};

	{
		distortos_Semaphore semaphore = DISTORTOS_SEMAPHORE_INITIALIZER(semaphore, lowRandomValue, highRandomValue);
		testCommon(semaphore, lowRandomValue, highRandomValue);
	}
	{
		distortos_Semaphore semaphore = DISTORTOS_SEMAPHORE_INITIALIZER(semaphore, highRandomValue, lowRandomValue);
		testCommon(semaphore, highRandomValue, lowRandomValue);
	}
}

TEST_CASE("Testing DISTORTOS_SEMAPHORE_CONSTRUCT_1()", "[construct]")
{
	constexpr unsigned int lowRandomValue {0x0dc449b6};
	constexpr unsigned int highRandomValue {0x726030cd};

	{
		DISTORTOS_SEMAPHORE_CONSTRUCT_1(semaphore, lowRandomValue, highRandomValue);
		testCommon(semaphore, lowRandomValue, highRandomValue);
	}
	{
		DISTORTOS_SEMAPHORE_CONSTRUCT_1(semaphore, highRandomValue, lowRandomValue);
		testCommon(semaphore, highRandomValue, lowRandomValue);
	}
}

TEST_CASE("Testing DISTORTOS_SEMAPHORE_CONSTRUCT()", "[construct]")
{
	constexpr unsigned int randomValue {0x5d0051dc};

	{
		DISTORTOS_SEMAPHORE_CONSTRUCT(semaphore, randomValue);
		testCommon(semaphore, randomValue);
	}
}

TEST_CASE("Testing distortos_Semaphore_construct_1()", "[construct]")
{
	constexpr unsigned int lowRandomValue {0x7a78032b};
	constexpr unsigned int highRandomValue {0xb24e4367};

	{
		distortos_Semaphore semaphore;
		REQUIRE(distortos_Semaphore_construct_1(&semaphore, lowRandomValue, highRandomValue) == 0);
		testCommon(semaphore, lowRandomValue, highRandomValue);
	}
	{
		distortos_Semaphore semaphore;
		REQUIRE(distortos_Semaphore_construct_1(&semaphore, highRandomValue, lowRandomValue) == 0);
		testCommon(semaphore, highRandomValue, lowRandomValue);
	}
}

TEST_CASE("Testing distortos_Semaphore_construct()", "[construct]")
{
	constexpr unsigned int randomValue {0xb86c251b};

	{
		distortos_Semaphore semaphore;
		REQUIRE(distortos_Semaphore_construct(&semaphore, randomValue) == 0);
		testCommon(semaphore, randomValue);
	}
}
<commit_msg>Add file comment explaining purpose of C-API-Semaphore-unit-test-1<commit_after>/**
 * \file
 * \brief Semaphore C-API test cases
 *
 * This test checks whether semaphore objects instantiated with C-API macros and functions are binary identical to
 * constructed distortos::Semaphore objects.
 *
 * \author Copyright (C) 2017 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "distortos/Semaphore.hpp"
#include "distortos/C-API/Semaphore.h"

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local functions
+---------------------------------------------------------------------------------------------------------------------*/

void testCommon(distortos_Semaphore& semaphore, const unsigned int value, const unsigned int maxValue = UINT_MAX)
{
	REQUIRE(semaphore.value == std::min(value, maxValue));
	REQUIRE(semaphore.maxValue == maxValue);
	distortos_Semaphore constructed;
	memcpy(&constructed, &semaphore, sizeof(semaphore));
	REQUIRE(distortos_Semaphore_destruct(&semaphore) == 0);
	struct distortos_Semaphore destructed;
	memcpy(&destructed, &semaphore, sizeof(semaphore));

	memset(&semaphore, 0, sizeof(semaphore));

	const auto realSemaphore = new (&semaphore) distortos::Semaphore {value, maxValue};
	REQUIRE(realSemaphore->getValue() == std::min(value, maxValue));
	REQUIRE(memcmp(&constructed, &semaphore, sizeof(semaphore)) == 0);
	realSemaphore->~Semaphore();
	REQUIRE(memcmp(&destructed, &semaphore, sizeof(semaphore)) == 0);
}

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| global test cases
+---------------------------------------------------------------------------------------------------------------------*/

TEST_CASE("Testing DISTORTOS_SEMAPHORE_INITIALIZER()", "[initializer]")
{
	constexpr unsigned int lowRandomValue {0x2c380555};
	constexpr unsigned int highRandomValue {0x8d87de63};

	{
		distortos_Semaphore semaphore = DISTORTOS_SEMAPHORE_INITIALIZER(semaphore, lowRandomValue, highRandomValue);
		testCommon(semaphore, lowRandomValue, highRandomValue);
	}
	{
		distortos_Semaphore semaphore = DISTORTOS_SEMAPHORE_INITIALIZER(semaphore, highRandomValue, lowRandomValue);
		testCommon(semaphore, highRandomValue, lowRandomValue);
	}
}

TEST_CASE("Testing DISTORTOS_SEMAPHORE_CONSTRUCT_1()", "[construct]")
{
	constexpr unsigned int lowRandomValue {0x0dc449b6};
	constexpr unsigned int highRandomValue {0x726030cd};

	{
		DISTORTOS_SEMAPHORE_CONSTRUCT_1(semaphore, lowRandomValue, highRandomValue);
		testCommon(semaphore, lowRandomValue, highRandomValue);
	}
	{
		DISTORTOS_SEMAPHORE_CONSTRUCT_1(semaphore, highRandomValue, lowRandomValue);
		testCommon(semaphore, highRandomValue, lowRandomValue);
	}
}

TEST_CASE("Testing DISTORTOS_SEMAPHORE_CONSTRUCT()", "[construct]")
{
	constexpr unsigned int randomValue {0x5d0051dc};

	{
		DISTORTOS_SEMAPHORE_CONSTRUCT(semaphore, randomValue);
		testCommon(semaphore, randomValue);
	}
}

TEST_CASE("Testing distortos_Semaphore_construct_1()", "[construct]")
{
	constexpr unsigned int lowRandomValue {0x7a78032b};
	constexpr unsigned int highRandomValue {0xb24e4367};

	{
		distortos_Semaphore semaphore;
		REQUIRE(distortos_Semaphore_construct_1(&semaphore, lowRandomValue, highRandomValue) == 0);
		testCommon(semaphore, lowRandomValue, highRandomValue);
	}
	{
		distortos_Semaphore semaphore;
		REQUIRE(distortos_Semaphore_construct_1(&semaphore, highRandomValue, lowRandomValue) == 0);
		testCommon(semaphore, highRandomValue, lowRandomValue);
	}
}

TEST_CASE("Testing distortos_Semaphore_construct()", "[construct]")
{
	constexpr unsigned int randomValue {0xb86c251b};

	{
		distortos_Semaphore semaphore;
		REQUIRE(distortos_Semaphore_construct(&semaphore, randomValue) == 0);
		testCommon(semaphore, randomValue);
	}
}
<|endoftext|>


<commit_before>#pragma once

#include <stdexcept>
#include <string>
#include <vector>
#include <initializer_list>
#include <memory>

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

class argc_argv
{
 public:
    class option_not_found : public std::runtime_error { public: using std::runtime_error::runtime_error; };
    class argument_not_found : public std::runtime_error { public: using std::runtime_error::runtime_error; };
    class option_has_no_value : public std::runtime_error { public: using std::runtime_error::runtime_error; };
    class option_value_conversion_failed : public std::runtime_error { public: using std::runtime_error::runtime_error; };

    class option
    {
     public:
        virtual ~option();

          // if option was present in the command line
        virtual operator bool() const;

          // argument extraction, throws option_not_found, option_has_no_value, option_value_conversion_failed
        virtual operator const char*() const;
          // virtual operator std::string() const;
        virtual operator double() const;
        virtual operator int() const;

     protected:
        inline option(std::string name, const char* value = nullptr) : mName{name}, mValue{value} {}
        friend class argc_argv;

        inline std::string name() const { return mName; }
        inline void set_value(const char* aValue) { mValue = aValue; }
        virtual bool really_present() const;

     private:
        std::string mName;
        const char* mValue;

    }; // class option

      // options_with_value: list of option names that have values, e.g. {"-o", "--output"}
      // may throw option_has_no_value if last value in argv is option from options_with_value
    argc_argv(int argc, const char* const argv[], std::initializer_list<const char*> options_with_value, bool split_single_dash = true);

      // returns option by name, if option was not in argv, returns ref to special option object
    const option& operator[](std::string aName) const;

      // returns option by name, if option was not in argv, returns ref to an option object having aDefault as a value
    const option& get(std::string aName, std::string aDefault) const;
    const option& get(std::string aName, double aDefault) const;
    const option& get(std::string aName, int aDefault) const;

      // returns number of arguments in the command line that are neither options nor option values
    inline size_t number_of_arguments() const { return mArguments.size(); }

      // returns argument (neither option nor option value) by index, throws argument_not_found if aIndex >= number_of_arguments()
    const char* operator[](size_t aIndex) const;

      // returns argv[0]
    inline const char* program() const { return mProgram; }

 private:
    const std::vector<const char*> mOptionsWithValue;
    const char* mProgram;
    std::vector<const char*> mArguments;
    mutable std::vector<std::unique_ptr<option>> mOptions;

    const option& get(std::string aName) const;

}; // class argc_argv

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>minor improvement<commit_after>#pragma once

#include <stdexcept>
#include <string>
#include <vector>
#include <initializer_list>
#include <memory>

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

class argc_argv
{
 public:
    class option_not_found : public std::runtime_error { public: using std::runtime_error::runtime_error; };
    class argument_not_found : public std::runtime_error { public: using std::runtime_error::runtime_error; };
    class option_has_no_value : public std::runtime_error { public: using std::runtime_error::runtime_error; };
    class option_value_conversion_failed : public std::runtime_error { public: using std::runtime_error::runtime_error; };

    class option
    {
     public:
        virtual ~option();

          // if option was present in the command line
        virtual operator bool() const;

          // argument extraction, throws option_not_found, option_has_no_value, option_value_conversion_failed
        virtual operator const char*() const;
        inline operator std::string() const { return operator const char*(); }
        virtual operator double() const;
        virtual operator int() const;

     protected:
        inline option(std::string name, const char* value = nullptr) : mName{name}, mValue{value} {}
        friend class argc_argv;

        inline std::string name() const { return mName; }
        inline void set_value(const char* aValue) { mValue = aValue; }
        virtual bool really_present() const;

     private:
        std::string mName;
        const char* mValue;

    }; // class option

      // options_with_value: list of option names that have values, e.g. {"-o", "--output"}
      // may throw option_has_no_value if last value in argv is option from options_with_value
    argc_argv(int argc, const char* const argv[], std::initializer_list<const char*> options_with_value, bool split_single_dash = true);

      // returns option by name, if option was not in argv, returns ref to special option object
    const option& operator[](std::string aName) const;

      // returns option by name, if option was not in argv, returns ref to an option object having aDefault as a value
    const option& get(std::string aName, std::string aDefault) const;
    const option& get(std::string aName, double aDefault) const;
    const option& get(std::string aName, int aDefault) const;

      // returns number of arguments in the command line that are neither options nor option values
    inline size_t number_of_arguments() const { return mArguments.size(); }

      // returns argument (neither option nor option value) by index, throws argument_not_found if aIndex >= number_of_arguments()
    const char* operator[](size_t aIndex) const;

      // returns argv[0]
    inline const char* program() const { return mProgram; }

 private:
    const std::vector<const char*> mOptionsWithValue;
    const char* mProgram;
    std::vector<const char*> mArguments;
    mutable std::vector<std::unique_ptr<option>> mOptions;

    const option& get(std::string aName) const;

}; // class argc_argv

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>#include "ProcessEvent.h"

#include <specialized/eventbackend.h>

#if defined(__linux__)
#include <linux/version.h>
#elif !defined(KERNEL_VERSION)
#define LINUX_VERSION_CODE 0
#define KERNEL_VERSION(a, b, c) 0
#endif

#if defined(__linux__) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) /* Linux 2.6.15+ */

// Linux
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>

// PDTK
#include <cxxutils/posix_helpers.h>
#include <cxxutils/socket_helpers.h>
#include <cxxutils/vterm.h>
#include <specialized/procstat.h>

enum {
  Read = 0,
  Write = 1,
};

struct message_t
{
  union {
    ProcessEvent::Flags action;
    uint32_t : 0;
  };
  pid_t pid;
};
static_assert(sizeof(message_t) == sizeof(uint64_t), "unexpected struct size");

// process flags
static constexpr uint8_t from_native_flags(const native_flags_t flags) noexcept
{
  return
      (flags & proc_event::PROC_EVENT_EXEC ? ProcessEvent::Exec : 0) |
      (flags & proc_event::PROC_EVENT_EXIT ? ProcessEvent::Exit : 0) |
      (flags & proc_event::PROC_EVENT_FORK ? ProcessEvent::Fork : 0) ;
}
/*
static constexpr native_flags_t to_native_flags(const uint8_t flags) noexcept
{
  return
      (flags & ProcessEvent::Exec ? native_flags_t(proc_event::PROC_EVENT_EXEC) : 0) | // Process called exec*()
      (flags & ProcessEvent::Exit ? native_flags_t(proc_event::PROC_EVENT_EXIT) : 0) | // Process exited
      (flags & ProcessEvent::Fork ? native_flags_t(proc_event::PROC_EVENT_FORK) : 0) ; // Process forked
}
*/

struct ProcessEvent::platform_dependant // process notification (process events connector)
{
  posix::fd_t fd;
  struct eventinfo_t
  {
    posix::fd_t fd[2]; // two fds for pipe based communication
    ProcessEvent::Flags_t flags;
  };

  std::unordered_map<pid_t, eventinfo_t> events;

  platform_dependant(void) noexcept
  {
    fd = posix::socket(EDomain::netlink, EType::datagram, EProtocol::connector);
    flaw(fd == posix::invalid_descriptor,
         terminal::warning,,,
         "Unable to open a netlink socket for Process Events Connector: %s", std::strerror(errno))

    sockaddr_nl sa_nl;
    sa_nl.nl_family = PF_NETLINK;
    sa_nl.nl_groups = CN_IDX_PROC;
    sa_nl.nl_pid = uint32_t(getpid());

    flaw(!posix::bind(fd, reinterpret_cast<struct sockaddr *>(&sa_nl), sizeof(sa_nl)),
         terminal::warning,,,
         "Process Events Connector requires root level access: %s", std::strerror(errno));

#pragma pack(push, 1)
    struct alignas(NLMSG_ALIGNTO) procconn_t // 32-bit alignment
    {
      nlmsghdr header; // 16 bytes
      cn_msg message;
      proc_cn_mcast_op operation;
    } procconn;
#pragma pack(pop)
    static_assert(sizeof(nlmsghdr) + sizeof(cn_msg) + sizeof(proc_cn_mcast_op) == sizeof(procconn_t), "compiler needs to pack struct");

    std::memset(&procconn, 0, sizeof(procconn));
    procconn.header.nlmsg_len = sizeof(procconn);
    procconn.header.nlmsg_pid = uint32_t(getpid());
    procconn.header.nlmsg_type = NLMSG_DONE;
    procconn.message.id.idx = CN_IDX_PROC;
    procconn.message.id.val = CN_VAL_PROC;
    procconn.message.len = sizeof(proc_cn_mcast_op);
    procconn.operation = PROC_CN_MCAST_LISTEN;

    flaw(posix::send(fd, &procconn, sizeof(procconn)) == posix::error_response,
         terminal::warning,,,
         "Failed to enable Process Events Connector notifications: %s", std::strerror(errno));

    EventBackend::add(fd, EventBackend::SimplePollReadFlags,
                      [this](posix::fd_t lambda_fd, native_flags_t) noexcept { read(lambda_fd); });

    std::fprintf(stderr, "%s%s\n", terminal::information, "Process Events Connector active");
  }

  ~platform_dependant(void) noexcept
  {
    EventBackend::remove(fd, EventBackend::SimplePollReadFlags);
    posix::close(fd);
    fd = posix::invalid_descriptor;
  }

  posix::fd_t add(pid_t pid, ProcessEvent::Flags_t flags) noexcept
  {
    eventinfo_t event;
    event.flags = flags;
    if(!posix::pipe(event.fd))
      return posix::invalid_descriptor;

    auto iter = events.emplace(pid, event);

    // add filter installation code here

    return event.fd[Read];
  }

  bool remove(pid_t pid) noexcept
  {
    auto iter = events.find(pid);
    if(iter == events.end())
      return false;

    // add filter removal code here

    posix::close(iter->second.fd[Read]);
    posix::close(iter->second.fd[Write]);
    events.erase(iter);
    return true;
  }

  void read(posix::fd_t procfd) noexcept
  {
#pragma pack(push, 1)
    struct alignas(NLMSG_ALIGNTO) procmsg_t // 32-bit alignment
    {
      nlmsghdr header; // 16 bytes
      cn_msg message;
      proc_event event;
    } procmsg;
#pragma pack(pop)
    static_assert(sizeof(nlmsghdr) + sizeof(cn_msg) + sizeof(proc_event) == sizeof(procmsg_t), "compiler needs to pack struct");

    pollfd fds = { procfd, POLLIN, 0 };
    while(posix::poll(&fds, 1, 0) > 0 && // while there are messages AND
          posix::recv(procfd, reinterpret_cast<void*>(&procmsg), sizeof(procmsg_t), 0) > 0) // read process event message
    {
      auto iter = events.find(procmsg.event.event_data.id.process_pid); // find event info for this PID
      if(iter != events.end()) // if found...
        posix::write(iter->second.fd[Write], &procmsg.event, sizeof(procmsg.event)); // write process event info into the communications pipe
    }
  }
} ProcessEvent::s_platform;

ProcessEvent::ProcessEvent(pid_t _pid, Flags_t _flags) noexcept
  : m_pid(_pid), m_flags(_flags), m_fd(posix::invalid_descriptor)
{
  m_fd = s_platform.add(m_pid, m_flags); // add PID to monitor and return communications pipe

  EventBackend::add(m_fd, EventBackend::SimplePollReadFlags, // connect communications pipe to a lambda function
                    [this](posix::fd_t lambda_fd, native_flags_t) noexcept
                    {
                      proc_event data;
                      pollfd fds = { lambda_fd, POLLIN, 0 };
                      while(posix::poll(&fds, 1, 0) > 0 && // while there is another event to be read
                            posix::read(lambda_fd, &data, sizeof(data)) > 0) // read the event
                        switch(from_native_flags(data.what)) // find the type of event
                        {
                          case Flags::Exec: // queue exec signal with PID
                            Object::enqueue(execed,
                                            data.event_data.exec.process_pid);
                            break;
                          case Flags::Exit: // queue exit signal with PID and exit code
                            if(data.event_data.exit.exit_signal) // if killed by a signal
                              Object::enqueue(killed,
                                              data.event_data.exit.process_pid,
                                              *reinterpret_cast<posix::signal::EId*>(&data.event_data.exit.exit_signal));
                            else // else exited by itself
                              Object::enqueue(exited,
                                              data.event_data.exit.process_pid,
                                              *reinterpret_cast<posix::error_t*>(&data.event_data.exit.exit_code));
                            break;
                          case Flags::Fork: // queue fork signal with PID and child PID
                            Object::enqueue(forked,
                                            data.event_data.fork.parent_pid,
                                            data.event_data.fork.child_pid);
                            break;
                        }
                    });
}

ProcessEvent::~ProcessEvent(void) noexcept
{
  EventBackend::remove(m_fd, EventBackend::SimplePollReadFlags);
  s_platform.remove(m_pid);
}
#elif defined(__linux__)
//&& LINUX_VERSION_CODE >= KERNEL_VERSION(X,X,X) /* Linux X.X.X+ */
# error No process event backend code exists in PDTK for Linux before version 2.6.15!  Please submit a patch!

#elif (defined(__APPLE__) && defined(__MACH__)) /* Darwin 7+     */ || \
      defined(__FreeBSD__)                      /* FreeBSD 4.1+  */ || \
      defined(__DragonFly__)                    /* DragonFly BSD */ || \
      defined(__OpenBSD__)                      /* OpenBSD 2.9+  */ || \
      defined(__NetBSD__)                       /* NetBSD 2+     */

#include <sys/event.h> // kqueue

static constexpr native_flags_t composite_flag(uint16_t actions, int16_t filters, uint32_t flags) noexcept
  { return native_flags_t(actions) | (native_flags_t(uint16_t(filters)) << 16) | (native_flags_t(flags) << 32); }

static constexpr bool flag_subset(native_flags_t flags, native_flags_t subset)
  { return (flags & subset) == subset; }

static constexpr native_flags_t to_native_flags(const uint8_t flags) noexcept
{
  return
      (flags & ProcessEvent::Exec ? composite_flag(0, EVFILT_PROC, NOTE_EXEC) : 0) |
      (flags & ProcessEvent::Exit ? composite_flag(0, EVFILT_PROC, NOTE_EXIT) : 0) |
      (flags & ProcessEvent::Fork ? composite_flag(0, EVFILT_PROC, NOTE_FORK) : 0) ;
}

static constexpr ushort extract_filter(native_flags_t flags) noexcept
  { return (flags >> 16) & 0xFFFF; }

static constexpr uint32_t extract_flags(native_flags_t flags) noexcept
  { return flags >> 32; }

ProcessEvent::ProcessEvent(pid_t _pid, Flags_t _flags) noexcept
  : m_pid(_pid), m_flags(_flags), m_fd(posix::invalid_descriptor)
{
  EventBackend::add(m_pid, to_native_flags(m_flags), // connect PID event to lambda function
                    [this](posix::fd_t lambda_fd, native_flags_t lambda_flags) noexcept
                    {
                      uint32_t data = extract_flags(lambda_fd); // get return value (if any)
                      switch(extract_filter(lambda_fd)) // switch by filtered event type
                      {
                        case Flags::Exec: // queue exec signal with PID
                          Object::enqueue(execed, m_pid);
                          break;
                        case Flags::Exit: // queue exit signal with PID and exit code
                          Object::enqueue(exited, m_pid,
                                          *reinterpret_cast<posix::error_t*>(&data));
                          break;
                        case Flags::Fork: // queue fork signal with PID and child PID
                          Object::enqueue(forked, m_pid,
                                          *reinterpret_cast<pid_t*>(&data));
                          break;
                      }
                    });
}

ProcessEvent::~ProcessEvent(void) noexcept
{
  EventBackend::remove(m_pid, to_native_flags(m_flags)); // disconnect PID with flags
}

#elif defined(__sun) && defined(__SVR4) // Solaris / OpenSolaris / OpenIndiana / illumos
# error No process event backend code exists in PDTK for Solaris / OpenSolaris / OpenIndiana / illumos!  Please submit a patch!

#elif defined(__minix) // MINIX
# error No process event backend code exists in PDTK for MINIX!  Please submit a patch!

#elif defined(__QNX__) // QNX
// QNX docs: http://www.qnx.com/developers/docs/7.0.0/index.html#com.qnx.doc.neutrino.devctl/topic/about.html
# error No process event backend code exists in PDTK for QNX!  Please submit a patch!

#elif defined(__hpux) // HP-UX
# error No process event backend code exists in PDTK for HP-UX!  Please submit a patch!

#elif defined(_AIX) // IBM AIX
# error No process event backend code exists in PDTK for IBM AIX!  Please submit a patch!

#elif defined(BSD)
# error Unrecognized BSD derivative!

#elif defined(__unix__) || defined(__unix)
# error Unrecognized UNIX variant!

#else
# error This platform is not supported.
#endif
<commit_msg>use copies for simple types<commit_after>#include "ProcessEvent.h"

#include <specialized/eventbackend.h>

#if defined(__linux__)
#include <linux/version.h>
#elif !defined(KERNEL_VERSION)
#define LINUX_VERSION_CODE 0
#define KERNEL_VERSION(a, b, c) 0
#endif

#if defined(__linux__) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) /* Linux 2.6.15+ */

// Linux
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>

// PDTK
#include <cxxutils/posix_helpers.h>
#include <cxxutils/socket_helpers.h>
#include <cxxutils/vterm.h>
#include <specialized/procstat.h>

enum {
  Read = 0,
  Write = 1,
};

struct message_t
{
  union {
    ProcessEvent::Flags action;
    uint32_t : 0;
  };
  pid_t pid;
};
static_assert(sizeof(message_t) == sizeof(uint64_t), "unexpected struct size");

// process flags
static constexpr uint8_t from_native_flags(const native_flags_t flags) noexcept
{
  return
      (flags & proc_event::PROC_EVENT_EXEC ? ProcessEvent::Exec : 0) |
      (flags & proc_event::PROC_EVENT_EXIT ? ProcessEvent::Exit : 0) |
      (flags & proc_event::PROC_EVENT_FORK ? ProcessEvent::Fork : 0) ;
}
/*
static constexpr native_flags_t to_native_flags(const uint8_t flags) noexcept
{
  return
      (flags & ProcessEvent::Exec ? native_flags_t(proc_event::PROC_EVENT_EXEC) : 0) | // Process called exec*()
      (flags & ProcessEvent::Exit ? native_flags_t(proc_event::PROC_EVENT_EXIT) : 0) | // Process exited
      (flags & ProcessEvent::Fork ? native_flags_t(proc_event::PROC_EVENT_FORK) : 0) ; // Process forked
}
*/

struct ProcessEvent::platform_dependant // process notification (process events connector)
{
  posix::fd_t fd;
  struct eventinfo_t
  {
    posix::fd_t fd[2]; // two fds for pipe based communication
    ProcessEvent::Flags_t flags;
  };

  std::unordered_map<pid_t, eventinfo_t> events;

  platform_dependant(void) noexcept
  {
    fd = posix::socket(EDomain::netlink, EType::datagram, EProtocol::connector);
    flaw(fd == posix::invalid_descriptor,
         terminal::warning,,,
         "Unable to open a netlink socket for Process Events Connector: %s", std::strerror(errno))

    sockaddr_nl sa_nl;
    sa_nl.nl_family = PF_NETLINK;
    sa_nl.nl_groups = CN_IDX_PROC;
    sa_nl.nl_pid = uint32_t(getpid());

    flaw(!posix::bind(fd, reinterpret_cast<struct sockaddr *>(&sa_nl), sizeof(sa_nl)),
         terminal::warning,,,
         "Process Events Connector requires root level access: %s", std::strerror(errno));

#pragma pack(push, 1)
    struct alignas(NLMSG_ALIGNTO) procconn_t // 32-bit alignment
    {
      nlmsghdr header; // 16 bytes
      cn_msg message;
      proc_cn_mcast_op operation;
    } procconn;
#pragma pack(pop)
    static_assert(sizeof(nlmsghdr) + sizeof(cn_msg) + sizeof(proc_cn_mcast_op) == sizeof(procconn_t), "compiler needs to pack struct");

    std::memset(&procconn, 0, sizeof(procconn));
    procconn.header.nlmsg_len = sizeof(procconn);
    procconn.header.nlmsg_pid = uint32_t(getpid());
    procconn.header.nlmsg_type = NLMSG_DONE;
    procconn.message.id.idx = CN_IDX_PROC;
    procconn.message.id.val = CN_VAL_PROC;
    procconn.message.len = sizeof(proc_cn_mcast_op);
    procconn.operation = PROC_CN_MCAST_LISTEN;

    flaw(posix::send(fd, &procconn, sizeof(procconn)) == posix::error_response,
         terminal::warning,,,
         "Failed to enable Process Events Connector notifications: %s", std::strerror(errno));

    EventBackend::add(fd, EventBackend::SimplePollReadFlags,
                      [this](posix::fd_t lambda_fd, native_flags_t) noexcept { read(lambda_fd); });

    std::fprintf(stderr, "%s%s\n", terminal::information, "Process Events Connector active");
  }

  ~platform_dependant(void) noexcept
  {
    EventBackend::remove(fd, EventBackend::SimplePollReadFlags);
    posix::close(fd);
    fd = posix::invalid_descriptor;
  }

  posix::fd_t add(pid_t pid, ProcessEvent::Flags_t flags) noexcept
  {
    eventinfo_t event;
    event.flags = flags;
    if(!posix::pipe(event.fd))
      return posix::invalid_descriptor;

    auto iter = events.emplace(pid, event);

    // add filter installation code here

    return event.fd[Read];
  }

  bool remove(pid_t pid) noexcept
  {
    auto iter = events.find(pid);
    if(iter == events.end())
      return false;

    // add filter removal code here

    posix::close(iter->second.fd[Read]);
    posix::close(iter->second.fd[Write]);
    events.erase(iter);
    return true;
  }

  void read(posix::fd_t procfd) noexcept
  {
#pragma pack(push, 1)
    struct alignas(NLMSG_ALIGNTO) procmsg_t // 32-bit alignment
    {
      nlmsghdr header; // 16 bytes
      cn_msg message;
      proc_event event;
    } procmsg;
#pragma pack(pop)
    static_assert(sizeof(nlmsghdr) + sizeof(cn_msg) + sizeof(proc_event) == sizeof(procmsg_t), "compiler needs to pack struct");

    pollfd fds = { procfd, POLLIN, 0 };
    while(posix::poll(&fds, 1, 0) > 0 && // while there are messages AND
          posix::recv(procfd, reinterpret_cast<void*>(&procmsg), sizeof(procmsg_t), 0) > 0) // read process event message
    {
      auto iter = events.find(procmsg.event.event_data.id.process_pid); // find event info for this PID
      if(iter != events.end()) // if found...
        posix::write(iter->second.fd[Write], &procmsg.event, sizeof(procmsg.event)); // write process event info into the communications pipe
    }
  }
} ProcessEvent::s_platform;

ProcessEvent::ProcessEvent(pid_t _pid, Flags_t _flags) noexcept
  : m_pid(_pid), m_flags(_flags), m_fd(posix::invalid_descriptor)
{
  m_fd = s_platform.add(m_pid, m_flags); // add PID to monitor and return communications pipe

  EventBackend::add(m_fd, EventBackend::SimplePollReadFlags, // connect communications pipe to a lambda function
                    [this](posix::fd_t lambda_fd, native_flags_t) noexcept
                    {
                      proc_event data;
                      pollfd fds = { lambda_fd, POLLIN, 0 };
                      while(posix::poll(&fds, 1, 0) > 0 && // while there is another event to be read
                            posix::read(lambda_fd, &data, sizeof(data)) > 0) // read the event
                        switch(from_native_flags(data.what)) // find the type of event
                        {
                          case Flags::Exec: // queue exec signal with PID
                            Object::enqueue(execed,
                                            data.event_data.exec.process_pid);
                            break;
                          case Flags::Exit: // queue exit signal with PID and exit code
                            if(data.event_data.exit.exit_signal) // if killed by a signal
                              Object::enqueue(killed,
                                              data.event_data.exit.process_pid,
                                              *reinterpret_cast<posix::signal::EId*>(&data.event_data.exit.exit_signal));
                            else // else exited by itself
                              Object::enqueue(exited,
                                              data.event_data.exit.process_pid,
                                              *reinterpret_cast<posix::error_t*>(&data.event_data.exit.exit_code));
                            break;
                          case Flags::Fork: // queue fork signal with PID and child PID
                            Object::enqueue(forked,
                                            data.event_data.fork.parent_pid,
                                            data.event_data.fork.child_pid);
                            break;
                        }
                    });
}

ProcessEvent::~ProcessEvent(void) noexcept
{
  EventBackend::remove(m_fd, EventBackend::SimplePollReadFlags);
  s_platform.remove(m_pid);
}
#elif defined(__linux__)
//&& LINUX_VERSION_CODE >= KERNEL_VERSION(X,X,X) /* Linux X.X.X+ */
# error No process event backend code exists in PDTK for Linux before version 2.6.15!  Please submit a patch!

#elif (defined(__APPLE__) && defined(__MACH__)) /* Darwin 7+     */ || \
      defined(__FreeBSD__)                      /* FreeBSD 4.1+  */ || \
      defined(__DragonFly__)                    /* DragonFly BSD */ || \
      defined(__OpenBSD__)                      /* OpenBSD 2.9+  */ || \
      defined(__NetBSD__)                       /* NetBSD 2+     */

#include <sys/event.h> // kqueue

static constexpr native_flags_t composite_flag(uint16_t actions, int16_t filters, uint32_t flags) noexcept
  { return native_flags_t(actions) | (native_flags_t(uint16_t(filters)) << 16) | (native_flags_t(flags) << 32); }

static constexpr bool flag_subset(native_flags_t flags, native_flags_t subset)
  { return (flags & subset) == subset; }

static constexpr native_flags_t to_native_flags(const uint8_t flags) noexcept
{
  return
      (flags & ProcessEvent::Exec ? composite_flag(0, EVFILT_PROC, NOTE_EXEC) : 0) |
      (flags & ProcessEvent::Exit ? composite_flag(0, EVFILT_PROC, NOTE_EXIT) : 0) |
      (flags & ProcessEvent::Fork ? composite_flag(0, EVFILT_PROC, NOTE_FORK) : 0) ;
}

static constexpr ushort extract_filter(native_flags_t flags) noexcept
  { return (flags >> 16) & 0xFFFF; }

template<typename rtype>
static constexpr rtype extract_flags(native_flags_t flags) noexcept
  { return flags >> 32; }

ProcessEvent::ProcessEvent(pid_t _pid, Flags_t _flags) noexcept
  : m_pid(_pid), m_flags(_flags), m_fd(posix::invalid_descriptor)
{
  EventBackend::add(m_pid, to_native_flags(m_flags), // connect PID event to lambda function
                    [this](posix::fd_t lambda_fd, native_flags_t lambda_flags) noexcept
                    {
                      switch(extract_filter(lambda_fd)) // switch by filtered event type
                      {
                        case Flags::Exec: // queue exec signal with PID
                          Object::enqueue_copy(execed, m_pid);
                          break;
                        case Flags::Exit: // queue exit signal with PID and exit code
                          Object::enqueue_copy(exited, m_pid, extract_flags<posix::error_t>(lambda_fd));
                          break;
                        case Flags::Fork: // queue fork signal with PID and child PID
                          Object::enqueue_copy(forked, m_pid, extract_flags<pid_t>(lambda_fd));
                          break;
                      }
                    });
}

ProcessEvent::~ProcessEvent(void) noexcept
{
  EventBackend::remove(m_pid, to_native_flags(m_flags)); // disconnect PID with flags
}

#elif defined(__sun) && defined(__SVR4) // Solaris / OpenSolaris / OpenIndiana / illumos
# error No process event backend code exists in PDTK for Solaris / OpenSolaris / OpenIndiana / illumos!  Please submit a patch!

#elif defined(__minix) // MINIX
# error No process event backend code exists in PDTK for MINIX!  Please submit a patch!

#elif defined(__QNX__) // QNX
// QNX docs: http://www.qnx.com/developers/docs/7.0.0/index.html#com.qnx.doc.neutrino.devctl/topic/about.html
# error No process event backend code exists in PDTK for QNX!  Please submit a patch!

#elif defined(__hpux) // HP-UX
# error No process event backend code exists in PDTK for HP-UX!  Please submit a patch!

#elif defined(_AIX) // IBM AIX
# error No process event backend code exists in PDTK for IBM AIX!  Please submit a patch!

#elif defined(BSD)
# error Unrecognized BSD derivative!

#elif defined(__unix__) || defined(__unix)
# error Unrecognized UNIX variant!

#else
# error This platform is not supported.
#endif
<|endoftext|>


<commit_before>#include <chrono>

#include "ParallelAndNaive.h"
#include "SequentialAnd.h"

using Clock = std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::microseconds;

void test(const char* info, MultipleAndInterface& ma, const std::vector<bitvector*> input) {

    assert(input.size() >= 2);

    printf("%-20s (%lu bitmaps): ", info, input.size());
    fflush(stdout);

    const auto t1 = Clock::now();
    auto res = ma.calculate();
    const auto t2 = Clock::now();

    const auto t_us = duration_cast<microseconds>(t2 - t1).count();
    printf("%luus [cardinality=%lu]\n", t_us, res->cardinality());
}

int main() {

    const size_t bitmap_size = 10000000;
    const size_t count = 20;

    std::vector<bitvector*> input;
    srand(0);
    for (size_t i=0; i < count; i++) {
        bitvector* bv = new bitvector(bitmap_size);
        bv->fill_random(50);

        input.push_back(bv);
    }

    SequentialAnd seq(input);
    test("SequentialAnd", seq, input);

    ParallelAndNaive par1(input, 8);
    test("ParallelAndNaive", par1, input);
}
<commit_msg>Change parameters<commit_after>#include <chrono>

#include "ParallelAndNaive.h"
#include "SequentialAnd.h"

using Clock = std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::microseconds;

void test(const char* info, MultipleAndInterface& ma, const std::vector<bitvector*> input) {

    assert(input.size() >= 2);

    printf("%-20s (%lu bitmaps): ", info, input.size());
    fflush(stdout);

    const auto t1 = Clock::now();
    auto res = ma.calculate();
    const auto t2 = Clock::now();

    const auto t_us = duration_cast<microseconds>(t2 - t1).count();
    printf("%luus [cardinality=%lu]\n", t_us, res->cardinality());
}

int main() {

    const size_t bitmap_size = 2000000;
    const size_t count = 100;

    std::vector<bitvector*> input;
    srand(0);
    for (size_t i=0; i < count; i++) {
        bitvector* bv = new bitvector(bitmap_size);
        bv->fill_random(50);

        input.push_back(bv);
    }

    SequentialAnd seq(input);
    test("SequentialAnd", seq, input);

    ParallelAndNaive par1(input, 4);
    test("ParallelAndNaive", par1, input);
}
<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Ruben Van Boxem
 *
 * 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.
 **/

/*
 * Signal
 * Registers observers.
 * Notifies registered observers.
 */

#include <functional>
#include <mutex>
#include <vector>

namespace skui
{
  namespace core
  {
    namespace implementation
    {
      template<typename... ArgTypes>
      class signal_base
      {
      public:
        signal_base() = default;

        signal_base(const signal_base& other) : slots(other.slots) {}
        signal_base(signal_base&& other) : slots(std::move(other.slots)) {}
        signal_base& operator=(signal_base other) { swap(other); return *this; }

        template<typename Callable>
        void connect(Callable&& slot)
        {
          std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          slots.emplace_back(slot);
        }

      protected:
        // mutable here allows to connect to a const object's signals
        mutable std::vector<std::function<void(ArgTypes...)>> slots;
        mutable std::mutex slots_mutex;

      private:
        void swap(signal_base& other) { std::swap(slots, other.slots); }
      };
    }
    template<typename... ArgTypes>
    class signal : public implementation::signal_base<ArgTypes...>
    {
    public:
      signal() = default;

      void emit(ArgTypes... arguments) const
      {
        std::lock_guard<decltype(this->slots_mutex)> lock(this->slots_mutex);
        for(auto&& slot : this->slots)
        {
          slot(arguments...);
        }
      }
    };

    // Parameterless specialization
    template<> class signal<> : public implementation::signal_base<>
    {
    public:
      signal() = default;

      void emit() const
      {
        std::lock_guard<decltype(this->slots_mutex)> lock(this->slots_mutex);
        for(auto&& slot : this->slots)
        {
          slot();
        }
      }
    };
  }
}

<commit_msg>Make connect a normal function taking a std::function&&.<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Ruben Van Boxem
 *
 * 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.
 **/

/*
 * Signal
 * Registers observers.
 * Notifies registered observers.
 */

#include <functional>
#include <mutex>
#include <vector>

namespace skui
{
  namespace core
  {
    namespace implementation
    {
      template<typename... ArgTypes>
      class signal_base
      {
      public:
        using slot_type = std::function<void(ArgTypes...)>;

        signal_base() = default;

        signal_base(const signal_base& other) : slots(other.slots) {}
        signal_base(signal_base&& other) : slots(std::move(other.slots)) {}
        signal_base& operator=(signal_base other) { swap(other); return *this; }

        void connect(slot_type&& slot)
        {
          std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          slots.emplace_back(slot);
        }

      protected:
        // mutable here allows to connect to a const object's signals
        mutable std::vector<slot_type> slots;
        mutable std::mutex slots_mutex;

      private:
        void swap(signal_base& other) { std::swap(slots, other.slots); }
      };
    }
    template<typename... ArgTypes>
    class signal : public implementation::signal_base<ArgTypes...>
    {
    public:
      signal() = default;

      void emit(ArgTypes... arguments) const
      {
        std::lock_guard<decltype(this->slots_mutex)> lock(this->slots_mutex);
        for(auto&& slot : this->slots)
        {
          slot(arguments...);
        }
      }
    };

    // Parameterless specialization
    template<> class signal<> : public implementation::signal_base<>
    {
    public:
      signal() = default;

      void emit() const
      {
        std::lock_guard<decltype(this->slots_mutex)> lock(this->slots_mutex);
        for(auto&& slot : this->slots)
        {
          slot();
        }
      }
    };
  }
}

<|endoftext|>