<commit_before>// Copyright 2014-present Bill Fisher. All rights reserved.

#include "ofp/unittest.h"
#include "ofp/matchpacket.h"

using namespace ofp;

static void testPacket(const char *hex, const char *outHex) {
  std::string pkt = HexToRawData(hex);

  // Make sure that packet is aligned to a byte boundary as expected.
  // (Bypass Short-String Optimization)
  if (!IsPtrAligned(pkt.data(), 8))
    pkt.reserve(256);

  ASSERT_TRUE(IsPtrAligned(pkt.data(), 8));

  // Insert two zero bytes at the beginning of the data. MatchPacket expects
  // packet data to be aligned at 2 bytes *past* the 8-byte alignment. The
  // actual value of the two bytes is irrelevant.
  pkt.insert(0, "xx", 2);

  MatchPacket match{ByteRange{pkt.data() + 2, pkt.size() - 2}};
  EXPECT_HEX(outHex, match.data(), match.size());
}

TEST(matchpacket, ethernet) {
  testPacket("FFFFFFFFFFFF0000000000010806",
             "80000606:FFFFFFFFFFFF 80000806:000000000001 80000A02:0806");
}

TEST(matchpacket, arp) {
  testPacket(
      "FFFFFFFFFFFF000000000001080600010800060400010000000000010A00000100000000"
      "00000A000002",
      "80000606:FFFFFFFFFFFF 80000806:000000000001 80000A02:0806 80002A02:0001 "
      "80002C04:0A000001 80002E04:0A000002 80003006:000000000001 "
      "80003206:000000000000");
}

TEST(matchpacket, tcpv4) {
  testPacket(
      "1111111111112222222222220800 "
      "45334444555500007706888899999999AAAAAAAA "
      "BBBBCCCCDDDDDDDDDDDDDDDDEEEEFFFFFFFFFFFF",
      "80000606:111111111111 80000806:222222222222 80000A02:0800 80001001:0C "
      "80001201:03 80001401:06 80001604:99999999 80001804:AAAAAAAA 00013A01:77 "
      "80001A02:BBBB 80001C02:CCCC 00014402:0EEE");
}

TEST(matchpacket, udpv4) {
  testPacket(
      "1111111111112222222222220800 "
      "45334444555500007711888899999999AAAAAAAA BBBBCCCCDDDDEEEE",
      "80000606:111111111111 80000806:222222222222 80000A02:0800 80001001:0C "
      "80001201:03 80001401:11 80001604:99999999 80001804:AAAAAAAA 00013A01:77 "
      "80001E02:BBBB 80002002:CCCC");
}

TEST(matchpacket, icmpv4) {
  testPacket(
      "1111111111112222222222220800 "
      "45334444555500007701888899999999AAAAAAAA BBCCDDDDDDDD",
      "80000606:111111111111 80000806:222222222222 80000A02:0800 80001001:0C "
      "80001201:03 80001401:01 80001604:99999999 80001804:AAAAAAAA 00013A01:77 "
      "80002601:BB 80002801:CC");
}

TEST(matchpacket, icmpv6) {
  testPacket(
      "3333FF718C4C32FA25D4234186DD6000000000183AFF0000000000000000000000000000"
      "0000FF0200000000000000000001FF718C4C87009A9E00000000FE80000000000000FC0C"
      "CDFFFE718C4C",
      "80000606:3333FF718C4C 80000806:32FA25D42341 80000A02:86DD 80001001:00 "
      "80001201:00 80001401:3A 80003410:00000000000000000000000000000000 "
      "80003610:FF0200000000000000000001FF718C4C 80003804:00000000 80003A01:87 "
      "80003C01:00 80004E02:0001");
}

TEST(matchpacket, udpv6) {
  testPacket(
      "11111111111122222222222286DD "
      "633444445555116677777777777777777777777777777777888888888888888888888888"
      "88888888 BBBBCCCCDDDDEEEE",
      "80000606:111111111111 80000806:222222222222 80000A02:86DD 80001001:0C "
      "80001201:03 80001401:11 80003410:77777777777777777777777777777777 "
      "80003610:88888888888888888888888888888888 80003804:00044444 "
      "80001E02:BBBB 80002002:CCCC 80004E02:0001");
}

TEST(matchpacket, lldp) {
  testPacket(
      "0180C200000E00000000000188CC0207040000000000010403072D32060200780A1A4F46"
      "7C30303A30303A30303A30303A30303A30303A30303A3031FE240026E1004F467C2D3240"
      "4F467C30303A30303A30303A30303A30303A30303A30303A30310000",
      "800006060180C200000E8000080600000000000180000A0288CCFFFF024400FFFFFF0704"
      "000000000001000000000000000000000000000000000000000000000000000000000000"
      "0000000000000000000000000000000000000000000000000000FFFF044400FFFFFF0307"
      "2D3200000000000000000000000000000000000000000000000000000000000000000000"
      "0000000000000000000000000000000000000000000000000000FFFF060600FFFFFF007"
      "8");
}
<commit_msg>Fix 32-bit compile of testPacket. [ci skip]<commit_after>// Copyright 2014-present Bill Fisher. All rights reserved.

#include "ofp/unittest.h"
#include "ofp/matchpacket.h"

using namespace ofp;

static void testPacket(const char *hex, const char *outHex) {
  ByteList buf{HexToRawData(hex)};

  // Insert two zero bytes at the beginning. MatchPacket expects packet data
  // to be aligned at 2 bytes past the 8-byte alignment.
  buf.insertZeros(buf.begin(), 2);

  ASSERT_TRUE(IsPtrAligned(buf.data(), 8));

  MatchPacket match{ByteRange{buf.data() + 2, buf.size() - 2}};
  EXPECT_HEX(outHex, match.data(), match.size());
}

TEST(matchpacket, ethernet) {
  testPacket("FFFFFFFFFFFF0000000000010806",
             "80000606:FFFFFFFFFFFF 80000806:000000000001 80000A02:0806");
}

TEST(matchpacket, arp) {
  testPacket(
      "FFFFFFFFFFFF000000000001080600010800060400010000000000010A00000100000000"
      "00000A000002",
      "80000606:FFFFFFFFFFFF 80000806:000000000001 80000A02:0806 80002A02:0001 "
      "80002C04:0A000001 80002E04:0A000002 80003006:000000000001 "
      "80003206:000000000000");
}

TEST(matchpacket, tcpv4) {
  testPacket(
      "1111111111112222222222220800 "
      "45334444555500007706888899999999AAAAAAAA "
      "BBBBCCCCDDDDDDDDDDDDDDDDEEEEFFFFFFFFFFFF",
      "80000606:111111111111 80000806:222222222222 80000A02:0800 80001001:0C "
      "80001201:03 80001401:06 80001604:99999999 80001804:AAAAAAAA 00013A01:77 "
      "80001A02:BBBB 80001C02:CCCC 00014402:0EEE");
}

TEST(matchpacket, udpv4) {
  testPacket(
      "1111111111112222222222220800 "
      "45334444555500007711888899999999AAAAAAAA BBBBCCCCDDDDEEEE",
      "80000606:111111111111 80000806:222222222222 80000A02:0800 80001001:0C "
      "80001201:03 80001401:11 80001604:99999999 80001804:AAAAAAAA 00013A01:77 "
      "80001E02:BBBB 80002002:CCCC");
}

TEST(matchpacket, icmpv4) {
  testPacket(
      "1111111111112222222222220800 "
      "45334444555500007701888899999999AAAAAAAA BBCCDDDDDDDD",
      "80000606:111111111111 80000806:222222222222 80000A02:0800 80001001:0C "
      "80001201:03 80001401:01 80001604:99999999 80001804:AAAAAAAA 00013A01:77 "
      "80002601:BB 80002801:CC");
}

TEST(matchpacket, icmpv6) {
  testPacket(
      "3333FF718C4C32FA25D4234186DD6000000000183AFF0000000000000000000000000000"
      "0000FF0200000000000000000001FF718C4C87009A9E00000000FE80000000000000FC0C"
      "CDFFFE718C4C",
      "80000606:3333FF718C4C 80000806:32FA25D42341 80000A02:86DD 80001001:00 "
      "80001201:00 80001401:3A 80003410:00000000000000000000000000000000 "
      "80003610:FF0200000000000000000001FF718C4C 80003804:00000000 80003A01:87 "
      "80003C01:00 80004E02:0001");
}

TEST(matchpacket, udpv6) {
  testPacket(
      "11111111111122222222222286DD "
      "633444445555116677777777777777777777777777777777888888888888888888888888"
      "88888888 BBBBCCCCDDDDEEEE",
      "80000606:111111111111 80000806:222222222222 80000A02:86DD 80001001:0C "
      "80001201:03 80001401:11 80003410:77777777777777777777777777777777 "
      "80003610:88888888888888888888888888888888 80003804:00044444 "
      "80001E02:BBBB 80002002:CCCC 80004E02:0001");
}

TEST(matchpacket, lldp) {
  testPacket(
      "0180C200000E00000000000188CC0207040000000000010403072D32060200780A1A4F46"
      "7C30303A30303A30303A30303A30303A30303A30303A3031FE240026E1004F467C2D3240"
      "4F467C30303A30303A30303A30303A30303A30303A30303A30310000",
      "800006060180C200000E8000080600000000000180000A0288CCFFFF024400FFFFFF0704"
      "000000000001000000000000000000000000000000000000000000000000000000000000"
      "0000000000000000000000000000000000000000000000000000FFFF044400FFFFFF0307"
      "2D3200000000000000000000000000000000000000000000000000000000000000000000"
      "0000000000000000000000000000000000000000000000000000FFFF060600FFFFFF007"
      "8");
}
<|endoftext|>


<commit_before>#include <cassert>
#include <algorithm>
#include <memory>
#include <cmath>
#include <vector>
#include <array>
#include "bvh.h"

namespace pt {

BVHTraversalState::BVHTraversalState() : current(0), bitstack(0) {}

// Bucket used for SAH split method
struct SAHBucket {
	size_t count;
	BBox bounds;

	SAHBucket() : count(0) {}
};

BVH::GeomInfo::GeomInfo(size_t i, const BBox &b) : geom_idx(i), center(b.center()), bounds(b){}

BVH::BuildNode::BuildNode(size_t geom_offset, size_t ngeom, const BBox &bounds)
	: children({nullptr, nullptr}), geom_offset(geom_offset), ngeom(ngeom), bounds(bounds)
{}
BVH::BuildNode::BuildNode(int split, std::unique_ptr<BuildNode> a, std::unique_ptr<BuildNode> b)
	: children({std::move(a), std::move(b)}), geom_offset(0), ngeom(0), split(split)
{
	bounds = children[0]->bounds;
	bounds.box_union(children[1]->bounds);
}

BVH::BVH(const std::vector<const DistributedRegion*> &regions) : max_geom(1), geometry(regions) {
	// If we didn't get any geometry to partition then we don't have to do anything
	if (geometry.empty()) {
		return;
	}
	// Get bounds and index info together for the geometry we're storing
	std::vector<GeomInfo> build_geom;
	build_geom.reserve(geometry.size());
	for (size_t i = 0; i < geometry.size(); ++i){
		build_geom.emplace_back(i, regions[i]->bounds);
	}

	std::vector<const DistributedRegion*> ordered_geom;
	ordered_geom.reserve(geometry.size());
	size_t total_nodes = 0;
	std::unique_ptr<BuildNode> root = build(build_geom, ordered_geom, 0, geometry.size(), total_nodes);
	// Our BVH structure refers to the ordered geometry so swap out our unordered list for
	// the correctly ordered one
	geometry.swap(ordered_geom);

	// Recursively flatten the tree for faster traversal
	flat_nodes.resize(total_nodes);
	size_t offset = 0;
	flatten_tree(root, offset);
}
BBox BVH::bounds() const {
	return !flat_nodes.empty() ? flat_nodes[0].bounds : BBox{};
}
const DistributedRegion* BVH::intersect(ActiveRay &r) const {
	const glm::vec3 inv_dir = glm::vec3(1.f) / r.ray.dir;
	const std::array<int, 3> neg_dir = {inv_dir.x < 0, inv_dir.y < 0, inv_dir.z < 0};
	BVHTraversalState &state = r.traversal;
	while (true) {
		const FlatNode &fnode = flat_nodes[state.current];
		// If it's a leaf node we need to send the ray to the owner of the region
		if (fnode.ngeom > 0) {
			if (fnode.bounds.fast_intersect(r.ray, inv_dir, neg_dir)) {
				return geometry[fnode.geom_offset];
			}
		} else {
			// Check which (if any) children we hit
			const std::array<bool, 2> child_hits{
				flat_nodes[state.current + 1].bounds.fast_intersect(r.ray, inv_dir, neg_dir),
				flat_nodes[fnode.second_child].bounds.fast_intersect(r.ray, inv_dir, neg_dir)
			};
			if (child_hits[0] || child_hits[1]) {
				state.bitstack = state.bitstack << 1;
				// If we hit both children enter the nearer one and push a 1 onto the stack
				if (child_hits[0] && child_hits[1]) {
					state.bitstack = state.bitstack | 1;
					if (neg_dir[fnode.axis]) {
						state.current = fnode.second_child;
					} else {
						++state.current;
					}
				} else if (child_hits[0]) {
					++state.current;
				} else {
					state.current = fnode.second_child;
				}
				continue;
			}
		}
		if (!backtrack(r)) {
			return nullptr;
		}
	}
	return nullptr;
}
bool BVH::backtrack(ActiveRay &ray) const {
	BVHTraversalState &state = ray.traversal;
	while ((state.bitstack & 1) == 0) {
		if (!state.bitstack) {
			return false;
		}
		state.current = flat_nodes[state.current].parent;
		state.bitstack = state.bitstack >> 1;
	}
	state.current = flat_nodes[state.current].sibling;
	state.bitstack = state.bitstack ^ 1;
	return true;
}
std::unique_ptr<BVH::BuildNode> BVH::build(std::vector<GeomInfo> &build_geom,
		std::vector<const DistributedRegion*> &ordered_geom, size_t start,
		size_t end, size_t &total_nodes)
{
	++total_nodes;
	// Find total bounds for the geometry we're trying to store
	BBox box;
	for (size_t i = start; i < end; ++i){
		box.box_union(build_geom[i].bounds);
	}

	const size_t ngeom = end - start;
	if (ngeom == 1) {
		return build_leaf(build_geom, ordered_geom, start, end, box);
	}

	// Need to build an interior node, figure out which axis to split on by
	// bounding the various geometry's centroids and picking the axis
	// with the most variation
	BBox centroids;
	for (size_t i = start; i < end; ++i){
		centroids.extend(build_geom[i].center);
	}
	const int axis = centroids.max_extent();
	size_t mid = (start + end) / 2;

	// If all the geometry's centers are on the same point we can't partition
	if (centroids.max[axis] == centroids.min[axis]){
		// Check that we can fit all the geometry into a single leaf node, if not we need
		// to force a split
		if (ngeom <= max_geom) {
			return build_leaf(build_geom, ordered_geom, start, end, box);
		} else {
			return std::make_unique<BuildNode>(axis,
				build(build_geom, ordered_geom, start, mid, total_nodes),
				build(build_geom, ordered_geom, mid, end, total_nodes));
		}
	}
	// Partition the primitives based on the surface area heuristic
	// If there's only a few primitives just use EQUAL and break
	if (ngeom < 5) {
		std::nth_element(build_geom.begin() + start, build_geom.begin() + mid,
				build_geom.begin() + end,
				[axis](const GeomInfo &a, const GeomInfo &b){
					return a.center[axis] < b.center[axis];
				});
	} else {
		// We're just going to consider the possibilities of splitting along 12 bucket boundaries
		std::array<SAHBucket, 12> buckets;
		// Place the various geometry we're partitioning into the appropriate bucket, scale
		// the position along the axis into a bucket index
		for (size_t i = start; i < end; ++i){
			size_t b = (build_geom[i].center[axis] - centroids.min[axis])
				/ (centroids.max[axis] - centroids.min[axis]) * buckets.size();
			b = b == buckets.size() ? b - 1 : b;
			++buckets[b].count;
			buckets[b].bounds.box_union(build_geom[i].bounds);
		}
		// Use the SAH to compute the costs of splitting at each bucket except the last
		std::array<float, 11> cost;
		for (size_t i = 0; i < cost.size(); ++i){
			SAHBucket left, right;
			for (size_t j = 0; j <= i; ++j){
				left.bounds.box_union(buckets[j].bounds);
				left.count += buckets[j].count;
			}
			for (size_t j = i + 1; j < buckets.size(); ++j){
				right.bounds.box_union(buckets[j].bounds);
				right.count += buckets[j].count;
			}
			// cost of traversel + (cost of hitting in left + cost of hitting in right) / total area of node
			cost[i] = .125f + (left.count * left.bounds.surface_area()
					+ right.count * right.bounds.surface_area()) / box.surface_area();
		}
		// Find the lowest cost split we can make here
		const auto min_cost = std::min_element(cost.begin(), cost.end());
		const size_t min_cost_idx = std::distance(cost.begin(), min_cost);
		// If we're forced to split by the amount of geometry here or it's cheaper to split then do so
		if (ngeom > max_geom || *min_cost < ngeom) {
			// Partition the geometry about the splitting bucket
			auto mid_ptr = std::partition(build_geom.begin() + start, build_geom.begin() + end,
					[min_cost_idx, axis, centroids](const GeomInfo &g){
						size_t b = (g.center[axis] - centroids.min[axis])
							/ (centroids.max[axis] - centroids.min[axis]) * 12;
						b = b == 12 ? b - 1 : b;
						return b <= min_cost_idx;
					});
			mid = std::distance(build_geom.begin(), mid_ptr);
		} else {
			return build_leaf(build_geom, ordered_geom, start, end, box);
		}
	}

	assert(start != mid && mid != end);
	return std::make_unique<BuildNode>(axis,
		build(build_geom, ordered_geom, start, mid, total_nodes),
		build(build_geom, ordered_geom, mid, end, total_nodes));
}
std::unique_ptr<BVH::BuildNode> BVH::build_leaf(std::vector<GeomInfo> &build_geom,
		std::vector<const DistributedRegion*> &ordered_geom,
		size_t start, size_t end, const BBox &box)
{
	const size_t ngeom = end - start;
	// Store the offset to this leaf's geometry then push it on
	const size_t geom_offset = ordered_geom.size();
	for (size_t i = start; i < end; ++i){
		ordered_geom.push_back(geometry[build_geom[i].geom_idx]);
	}
	return std::make_unique<BuildNode>(geom_offset, ngeom, box);
}
size_t BVH::flatten_tree(const std::unique_ptr<BuildNode> &node, size_t &offset){
	FlatNode &fnode = flat_nodes[offset];
	fnode.bounds = node->bounds;
	const size_t node_offset = offset++;
	// If the node has geometry we're creating a leaf, otherwise it's an interior node
	if (node->ngeom > 0) {
		fnode.geom_offset = node->geom_offset;
		fnode.ngeom = node->ngeom;
	} else {
		fnode.axis = node->split;
		fnode.ngeom = 0;
		const size_t first_child = flatten_tree(node->children[0], offset);
		fnode.second_child = flatten_tree(node->children[1], offset);
		flat_nodes[first_child].sibling = fnode.second_child;
		flat_nodes[fnode.second_child].sibling = first_child;
		flat_nodes[first_child].parent = node_offset;
		flat_nodes[fnode.second_child].parent = node_offset;
	}
	return node_offset;
}

}

<commit_msg>Move BVHTraversalState ctor to matching .cpp file<commit_after>#include <cassert>
#include <algorithm>
#include <memory>
#include <cmath>
#include <vector>
#include <array>
#include "bvh.h"

namespace pt {

// Bucket used for SAH split method
struct SAHBucket {
	size_t count;
	BBox bounds;

	SAHBucket() : count(0) {}
};

BVH::GeomInfo::GeomInfo(size_t i, const BBox &b) : geom_idx(i), center(b.center()), bounds(b){}

BVH::BuildNode::BuildNode(size_t geom_offset, size_t ngeom, const BBox &bounds)
	: children({nullptr, nullptr}), geom_offset(geom_offset), ngeom(ngeom), bounds(bounds)
{}
BVH::BuildNode::BuildNode(int split, std::unique_ptr<BuildNode> a, std::unique_ptr<BuildNode> b)
	: children({std::move(a), std::move(b)}), geom_offset(0), ngeom(0), split(split)
{
	bounds = children[0]->bounds;
	bounds.box_union(children[1]->bounds);
}

BVH::BVH(const std::vector<const DistributedRegion*> &regions) : max_geom(1), geometry(regions) {
	// If we didn't get any geometry to partition then we don't have to do anything
	if (geometry.empty()) {
		return;
	}
	// Get bounds and index info together for the geometry we're storing
	std::vector<GeomInfo> build_geom;
	build_geom.reserve(geometry.size());
	for (size_t i = 0; i < geometry.size(); ++i){
		build_geom.emplace_back(i, regions[i]->bounds);
	}

	std::vector<const DistributedRegion*> ordered_geom;
	ordered_geom.reserve(geometry.size());
	size_t total_nodes = 0;
	std::unique_ptr<BuildNode> root = build(build_geom, ordered_geom, 0, geometry.size(), total_nodes);
	// Our BVH structure refers to the ordered geometry so swap out our unordered list for
	// the correctly ordered one
	geometry.swap(ordered_geom);

	// Recursively flatten the tree for faster traversal
	flat_nodes.resize(total_nodes);
	size_t offset = 0;
	flatten_tree(root, offset);
}
BBox BVH::bounds() const {
	return !flat_nodes.empty() ? flat_nodes[0].bounds : BBox{};
}
const DistributedRegion* BVH::intersect(ActiveRay &r) const {
	const glm::vec3 inv_dir = glm::vec3(1.f) / r.ray.dir;
	const std::array<int, 3> neg_dir = {inv_dir.x < 0, inv_dir.y < 0, inv_dir.z < 0};
	BVHTraversalState &state = r.traversal;
	while (true) {
		const FlatNode &fnode = flat_nodes[state.current];
		// If it's a leaf node we need to send the ray to the owner of the region
		if (fnode.ngeom > 0) {
			if (fnode.bounds.fast_intersect(r.ray, inv_dir, neg_dir)) {
				return geometry[fnode.geom_offset];
			}
		} else {
			// Check which (if any) children we hit
			const std::array<bool, 2> child_hits{
				flat_nodes[state.current + 1].bounds.fast_intersect(r.ray, inv_dir, neg_dir),
				flat_nodes[fnode.second_child].bounds.fast_intersect(r.ray, inv_dir, neg_dir)
			};
			if (child_hits[0] || child_hits[1]) {
				state.bitstack = state.bitstack << 1;
				// If we hit both children enter the nearer one and push a 1 onto the stack
				if (child_hits[0] && child_hits[1]) {
					state.bitstack = state.bitstack | 1;
					if (neg_dir[fnode.axis]) {
						state.current = fnode.second_child;
					} else {
						++state.current;
					}
				} else if (child_hits[0]) {
					++state.current;
				} else {
					state.current = fnode.second_child;
				}
				continue;
			}
		}
		if (!backtrack(r)) {
			return nullptr;
		}
	}
	return nullptr;
}
bool BVH::backtrack(ActiveRay &ray) const {
	BVHTraversalState &state = ray.traversal;
	while ((state.bitstack & 1) == 0) {
		if (!state.bitstack) {
			return false;
		}
		state.current = flat_nodes[state.current].parent;
		state.bitstack = state.bitstack >> 1;
	}
	state.current = flat_nodes[state.current].sibling;
	state.bitstack = state.bitstack ^ 1;
	return true;
}
std::unique_ptr<BVH::BuildNode> BVH::build(std::vector<GeomInfo> &build_geom,
		std::vector<const DistributedRegion*> &ordered_geom, size_t start,
		size_t end, size_t &total_nodes)
{
	++total_nodes;
	// Find total bounds for the geometry we're trying to store
	BBox box;
	for (size_t i = start; i < end; ++i){
		box.box_union(build_geom[i].bounds);
	}

	const size_t ngeom = end - start;
	if (ngeom == 1) {
		return build_leaf(build_geom, ordered_geom, start, end, box);
	}

	// Need to build an interior node, figure out which axis to split on by
	// bounding the various geometry's centroids and picking the axis
	// with the most variation
	BBox centroids;
	for (size_t i = start; i < end; ++i){
		centroids.extend(build_geom[i].center);
	}
	const int axis = centroids.max_extent();
	size_t mid = (start + end) / 2;

	// If all the geometry's centers are on the same point we can't partition
	if (centroids.max[axis] == centroids.min[axis]){
		// Check that we can fit all the geometry into a single leaf node, if not we need
		// to force a split
		if (ngeom <= max_geom) {
			return build_leaf(build_geom, ordered_geom, start, end, box);
		} else {
			return std::make_unique<BuildNode>(axis,
				build(build_geom, ordered_geom, start, mid, total_nodes),
				build(build_geom, ordered_geom, mid, end, total_nodes));
		}
	}
	// Partition the primitives based on the surface area heuristic
	// If there's only a few primitives just use EQUAL and break
	if (ngeom < 5) {
		std::nth_element(build_geom.begin() + start, build_geom.begin() + mid,
				build_geom.begin() + end,
				[axis](const GeomInfo &a, const GeomInfo &b){
					return a.center[axis] < b.center[axis];
				});
	} else {
		// We're just going to consider the possibilities of splitting along 12 bucket boundaries
		std::array<SAHBucket, 12> buckets;
		// Place the various geometry we're partitioning into the appropriate bucket, scale
		// the position along the axis into a bucket index
		for (size_t i = start; i < end; ++i){
			size_t b = (build_geom[i].center[axis] - centroids.min[axis])
				/ (centroids.max[axis] - centroids.min[axis]) * buckets.size();
			b = b == buckets.size() ? b - 1 : b;
			++buckets[b].count;
			buckets[b].bounds.box_union(build_geom[i].bounds);
		}
		// Use the SAH to compute the costs of splitting at each bucket except the last
		std::array<float, 11> cost;
		for (size_t i = 0; i < cost.size(); ++i){
			SAHBucket left, right;
			for (size_t j = 0; j <= i; ++j){
				left.bounds.box_union(buckets[j].bounds);
				left.count += buckets[j].count;
			}
			for (size_t j = i + 1; j < buckets.size(); ++j){
				right.bounds.box_union(buckets[j].bounds);
				right.count += buckets[j].count;
			}
			// cost of traversel + (cost of hitting in left + cost of hitting in right) / total area of node
			cost[i] = .125f + (left.count * left.bounds.surface_area()
					+ right.count * right.bounds.surface_area()) / box.surface_area();
		}
		// Find the lowest cost split we can make here
		const auto min_cost = std::min_element(cost.begin(), cost.end());
		const size_t min_cost_idx = std::distance(cost.begin(), min_cost);
		// If we're forced to split by the amount of geometry here or it's cheaper to split then do so
		if (ngeom > max_geom || *min_cost < ngeom) {
			// Partition the geometry about the splitting bucket
			auto mid_ptr = std::partition(build_geom.begin() + start, build_geom.begin() + end,
					[min_cost_idx, axis, centroids](const GeomInfo &g){
						size_t b = (g.center[axis] - centroids.min[axis])
							/ (centroids.max[axis] - centroids.min[axis]) * 12;
						b = b == 12 ? b - 1 : b;
						return b <= min_cost_idx;
					});
			mid = std::distance(build_geom.begin(), mid_ptr);
		} else {
			return build_leaf(build_geom, ordered_geom, start, end, box);
		}
	}

	assert(start != mid && mid != end);
	return std::make_unique<BuildNode>(axis,
		build(build_geom, ordered_geom, start, mid, total_nodes),
		build(build_geom, ordered_geom, mid, end, total_nodes));
}
std::unique_ptr<BVH::BuildNode> BVH::build_leaf(std::vector<GeomInfo> &build_geom,
		std::vector<const DistributedRegion*> &ordered_geom,
		size_t start, size_t end, const BBox &box)
{
	const size_t ngeom = end - start;
	// Store the offset to this leaf's geometry then push it on
	const size_t geom_offset = ordered_geom.size();
	for (size_t i = start; i < end; ++i){
		ordered_geom.push_back(geometry[build_geom[i].geom_idx]);
	}
	return std::make_unique<BuildNode>(geom_offset, ngeom, box);
}
size_t BVH::flatten_tree(const std::unique_ptr<BuildNode> &node, size_t &offset){
	FlatNode &fnode = flat_nodes[offset];
	fnode.bounds = node->bounds;
	const size_t node_offset = offset++;
	// If the node has geometry we're creating a leaf, otherwise it's an interior node
	if (node->ngeom > 0) {
		fnode.geom_offset = node->geom_offset;
		fnode.ngeom = node->ngeom;
	} else {
		fnode.axis = node->split;
		fnode.ngeom = 0;
		const size_t first_child = flatten_tree(node->children[0], offset);
		fnode.second_child = flatten_tree(node->children[1], offset);
		flat_nodes[first_child].sibling = fnode.second_child;
		flat_nodes[fnode.second_child].sibling = first_child;
		flat_nodes[first_child].parent = node_offset;
		flat_nodes[fnode.second_child].parent = node_offset;
	}
	return node_offset;
}

}

<|endoftext|>


<commit_before>//===- LTO.cpp ------------------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "LTO.h"
#include "Config.h"
#include "Driver.h"
#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/LoopPassManager.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/ParallelCG.h"
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Linker/IRMover.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
#include <llvm/IR/Verifier.h>

using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;

using namespace lld;
using namespace lld::elf;

// This is for use when debugging LTO.
static void saveLtoObjectFile(StringRef Buffer, unsigned I, bool Many) {
  SmallString<128> Filename = Config->OutputFile;
  if (Many)
    Filename += utostr(I);
  Filename += ".lto.o";
  std::error_code EC;
  raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::F_None);
  check(EC);
  OS << Buffer;
}

// This is for use when debugging LTO.
static void saveBCFile(Module &M, StringRef Suffix) {
  std::error_code EC;
  raw_fd_ostream OS(Config->OutputFile.str() + Suffix.str(), EC,
                    sys::fs::OpenFlags::F_None);
  check(EC);
  WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ true);
}

static void runNewCustomLtoPasses(Module &M, TargetMachine &TM) {
  PassBuilder PB(&TM);

  AAManager AA;
  LoopAnalysisManager LAM;
  FunctionAnalysisManager FAM;
  CGSCCAnalysisManager CGAM;
  ModuleAnalysisManager MAM;

  // Register the AA manager first so that our version is the one used.
  FAM.registerPass([&] { return std::move(AA); });

  // Register all the basic analyses with the managers.
  PB.registerModuleAnalyses(MAM);
  PB.registerCGSCCAnalyses(CGAM);
  PB.registerFunctionAnalyses(FAM);
  PB.registerLoopAnalyses(LAM);
  PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);

  ModulePassManager MPM;
  if (!Config->DisableVerify)
    MPM.addPass(VerifierPass());

  // Now, add all the passes we've been requested to.
  if (!PB.parsePassPipeline(MPM, Config->LtoNewPmPasses)) {
    error("unable to parse pass pipeline description: " +
          Config->LtoNewPmPasses);
    return;
  }

  if (!Config->DisableVerify)
    MPM.addPass(VerifierPass());
  MPM.run(M, MAM);
}

static void runOldLtoPasses(Module &M, TargetMachine &TM) {
  // Note that the gold plugin has a similar piece of code, so
  // it is probably better to move this code to a common place.
  legacy::PassManager LtoPasses;
  LtoPasses.add(createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis()));
  PassManagerBuilder PMB;
  PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM.getTargetTriple()));
  PMB.Inliner = createFunctionInliningPass();
  PMB.VerifyInput = PMB.VerifyOutput = !Config->DisableVerify;
  PMB.LoopVectorize = true;
  PMB.SLPVectorize = true;
  PMB.OptLevel = Config->LtoO;
  PMB.populateLTOPassManager(LtoPasses);
  LtoPasses.run(M);
}

static void runLTOPasses(Module &M, TargetMachine &TM) {
  if (!Config->LtoNewPmPasses.empty()) {
    // The user explicitly asked for a set of passes to be run.
    // This needs the new PM to work as there's no clean way to
    // pass a set of passes to run in the legacy PM.
    runNewCustomLtoPasses(M, TM);
    if (HasError)
      return;
  } else {
    // Run the 'default' set of LTO passes. This code still uses
    // the legacy PM as the new one is not the default.
    runOldLtoPasses(M, TM);
  }

  if (Config->SaveTemps)
    saveBCFile(M, ".lto.opt.bc");
}

static bool shouldInternalize(const SmallPtrSet<GlobalValue *, 8> &Used,
                              Symbol *S, GlobalValue *GV) {
  if (S->IsUsedInRegularObj)
    return false;

  if (Used.count(GV))
    return false;

  return !S->includeInDynsym();
}

BitcodeCompiler::BitcodeCompiler()
    : Combined(new llvm::Module("ld-temp.o", Driver->Context)),
      Mover(*Combined) {}

static void undefine(Symbol *S) {
  replaceBody<Undefined>(S, S->body()->getName(), STV_DEFAULT, 0);
}

void BitcodeCompiler::add(BitcodeFile &F) {
  std::unique_ptr<IRObjectFile> Obj = std::move(F.Obj);
  std::vector<GlobalValue *> Keep;
  unsigned BodyIndex = 0;
  ArrayRef<Symbol *> Syms = F.getSymbols();

  Module &M = Obj->getModule();
  if (M.getDataLayoutStr().empty())
    fatal("invalid bitcode file: " + F.getName() + " has no datalayout");

  // Discard non-compatible debug infos if necessary.
  M.materializeMetadata();
  UpgradeDebugInfo(M);

  // If a symbol appears in @llvm.used, the linker is required
  // to treat the symbol as there is a reference to the symbol
  // that it cannot see. Therefore, we can't internalize.
  SmallPtrSet<GlobalValue *, 8> Used;
  collectUsedGlobalVariables(M, Used, /* CompilerUsed */ false);

  for (const BasicSymbolRef &Sym : Obj->symbols()) {
    uint32_t Flags = Sym.getFlags();
    GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl());
    if (GV && GV->hasAppendingLinkage())
      Keep.push_back(GV);
    if (BitcodeFile::shouldSkip(Flags))
      continue;
    Symbol *S = Syms[BodyIndex++];
    if (Flags & BasicSymbolRef::SF_Undefined)
      continue;
    auto *B = dyn_cast<DefinedBitcode>(S->body());
    if (!B || B->File != &F)
      continue;

    // We collect the set of symbols we want to internalize here
    // and change the linkage after the IRMover executed, i.e. after
    // we imported the symbols and satisfied undefined references
    // to it. We can't just change linkage here because otherwise
    // the IRMover will just rename the symbol.
    if (GV && shouldInternalize(Used, S, GV))
      InternalizedSyms.insert(GV->getName());

    // At this point we know that either the combined LTO object will provide a
    // definition of a symbol, or we will internalize it. In either case, we
    // need to undefine the symbol. In the former case, the real definition
    // needs to be able to replace the original definition without conflicting.
    // In the latter case, we need to allow the combined LTO object to provide a
    // definition with the same name, for example when doing parallel codegen.
    undefine(S);

    if (!GV)
      // Module asm symbol.
      continue;

    switch (GV->getLinkage()) {
    default:
      break;
    case llvm::GlobalValue::LinkOnceAnyLinkage:
      GV->setLinkage(GlobalValue::WeakAnyLinkage);
      break;
    case llvm::GlobalValue::LinkOnceODRLinkage:
      GV->setLinkage(GlobalValue::WeakODRLinkage);
      break;
    }

    Keep.push_back(GV);
  }

  Mover.move(Obj->takeModule(), Keep,
             [](GlobalValue &, IRMover::ValueAdder) {});
}

static void internalize(GlobalValue &GV) {
  assert(!GV.hasLocalLinkage() &&
         "Trying to internalize a symbol with local linkage!");
  GV.setLinkage(GlobalValue::InternalLinkage);
}

std::vector<std::unique_ptr<InputFile>> BitcodeCompiler::runSplitCodegen(
    const std::function<std::unique_ptr<TargetMachine>()> &TMFactory) {
  unsigned NumThreads = Config->LtoJobs;
  OwningData.resize(NumThreads);

  std::list<raw_svector_ostream> OSs;
  std::vector<raw_pwrite_stream *> OSPtrs;
  for (SmallString<0> &Obj : OwningData) {
    OSs.emplace_back(Obj);
    OSPtrs.push_back(&OSs.back());
  }

  splitCodeGen(std::move(Combined), OSPtrs, {}, TMFactory);

  std::vector<std::unique_ptr<InputFile>> ObjFiles;
  for (SmallString<0> &Obj : OwningData)
    ObjFiles.push_back(createObjectFile(
        MemoryBufferRef(Obj, "LLD-INTERNAL-combined-lto-object")));

  if (Config->SaveTemps)
    for (unsigned I = 0; I < NumThreads; ++I)
      saveLtoObjectFile(OwningData[I], I, NumThreads > 1);

  return ObjFiles;
}

// Merge all the bitcode files we have seen, codegen the result
// and return the resulting ObjectFile.
std::vector<std::unique_ptr<InputFile>> BitcodeCompiler::compile() {
  TheTriple = Combined->getTargetTriple();
  for (const auto &Name : InternalizedSyms) {
    GlobalValue *GV = Combined->getNamedValue(Name.first());
    assert(GV);
    internalize(*GV);
  }

  if (Config->SaveTemps)
    saveBCFile(*Combined, ".lto.bc");

  std::string Msg;
  const Target *T = TargetRegistry::lookupTarget(TheTriple, Msg);
  if (!T)
    fatal("target not found: " + Msg);
  TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
  Reloc::Model R = Config->Pic ? Reloc::PIC_ : Reloc::Static;

  auto CreateTargetMachine = [&]() {
    return std::unique_ptr<TargetMachine>(
        T->createTargetMachine(TheTriple, "", "", Options, R));
  };

  std::unique_ptr<TargetMachine> TM = CreateTargetMachine();
  runLTOPasses(*Combined, *TM);
  if (HasError)
    return {};

  return runSplitCodegen(CreateTargetMachine);
}
<commit_msg>Sort includes, and don't look into system headers.<commit_after>//===- LTO.cpp ------------------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "LTO.h"
#include "Config.h"
#include "Driver.h"
#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/LoopPassManager.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/ParallelCG.h"
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Linker/IRMover.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"

using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;

using namespace lld;
using namespace lld::elf;

// This is for use when debugging LTO.
static void saveLtoObjectFile(StringRef Buffer, unsigned I, bool Many) {
  SmallString<128> Filename = Config->OutputFile;
  if (Many)
    Filename += utostr(I);
  Filename += ".lto.o";
  std::error_code EC;
  raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::F_None);
  check(EC);
  OS << Buffer;
}

// This is for use when debugging LTO.
static void saveBCFile(Module &M, StringRef Suffix) {
  std::error_code EC;
  raw_fd_ostream OS(Config->OutputFile.str() + Suffix.str(), EC,
                    sys::fs::OpenFlags::F_None);
  check(EC);
  WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ true);
}

static void runNewCustomLtoPasses(Module &M, TargetMachine &TM) {
  PassBuilder PB(&TM);

  AAManager AA;
  LoopAnalysisManager LAM;
  FunctionAnalysisManager FAM;
  CGSCCAnalysisManager CGAM;
  ModuleAnalysisManager MAM;

  // Register the AA manager first so that our version is the one used.
  FAM.registerPass([&] { return std::move(AA); });

  // Register all the basic analyses with the managers.
  PB.registerModuleAnalyses(MAM);
  PB.registerCGSCCAnalyses(CGAM);
  PB.registerFunctionAnalyses(FAM);
  PB.registerLoopAnalyses(LAM);
  PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);

  ModulePassManager MPM;
  if (!Config->DisableVerify)
    MPM.addPass(VerifierPass());

  // Now, add all the passes we've been requested to.
  if (!PB.parsePassPipeline(MPM, Config->LtoNewPmPasses)) {
    error("unable to parse pass pipeline description: " +
          Config->LtoNewPmPasses);
    return;
  }

  if (!Config->DisableVerify)
    MPM.addPass(VerifierPass());
  MPM.run(M, MAM);
}

static void runOldLtoPasses(Module &M, TargetMachine &TM) {
  // Note that the gold plugin has a similar piece of code, so
  // it is probably better to move this code to a common place.
  legacy::PassManager LtoPasses;
  LtoPasses.add(createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis()));
  PassManagerBuilder PMB;
  PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM.getTargetTriple()));
  PMB.Inliner = createFunctionInliningPass();
  PMB.VerifyInput = PMB.VerifyOutput = !Config->DisableVerify;
  PMB.LoopVectorize = true;
  PMB.SLPVectorize = true;
  PMB.OptLevel = Config->LtoO;
  PMB.populateLTOPassManager(LtoPasses);
  LtoPasses.run(M);
}

static void runLTOPasses(Module &M, TargetMachine &TM) {
  if (!Config->LtoNewPmPasses.empty()) {
    // The user explicitly asked for a set of passes to be run.
    // This needs the new PM to work as there's no clean way to
    // pass a set of passes to run in the legacy PM.
    runNewCustomLtoPasses(M, TM);
    if (HasError)
      return;
  } else {
    // Run the 'default' set of LTO passes. This code still uses
    // the legacy PM as the new one is not the default.
    runOldLtoPasses(M, TM);
  }

  if (Config->SaveTemps)
    saveBCFile(M, ".lto.opt.bc");
}

static bool shouldInternalize(const SmallPtrSet<GlobalValue *, 8> &Used,
                              Symbol *S, GlobalValue *GV) {
  if (S->IsUsedInRegularObj)
    return false;

  if (Used.count(GV))
    return false;

  return !S->includeInDynsym();
}

BitcodeCompiler::BitcodeCompiler()
    : Combined(new llvm::Module("ld-temp.o", Driver->Context)),
      Mover(*Combined) {}

static void undefine(Symbol *S) {
  replaceBody<Undefined>(S, S->body()->getName(), STV_DEFAULT, 0);
}

void BitcodeCompiler::add(BitcodeFile &F) {
  std::unique_ptr<IRObjectFile> Obj = std::move(F.Obj);
  std::vector<GlobalValue *> Keep;
  unsigned BodyIndex = 0;
  ArrayRef<Symbol *> Syms = F.getSymbols();

  Module &M = Obj->getModule();
  if (M.getDataLayoutStr().empty())
    fatal("invalid bitcode file: " + F.getName() + " has no datalayout");

  // Discard non-compatible debug infos if necessary.
  M.materializeMetadata();
  UpgradeDebugInfo(M);

  // If a symbol appears in @llvm.used, the linker is required
  // to treat the symbol as there is a reference to the symbol
  // that it cannot see. Therefore, we can't internalize.
  SmallPtrSet<GlobalValue *, 8> Used;
  collectUsedGlobalVariables(M, Used, /* CompilerUsed */ false);

  for (const BasicSymbolRef &Sym : Obj->symbols()) {
    uint32_t Flags = Sym.getFlags();
    GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl());
    if (GV && GV->hasAppendingLinkage())
      Keep.push_back(GV);
    if (BitcodeFile::shouldSkip(Flags))
      continue;
    Symbol *S = Syms[BodyIndex++];
    if (Flags & BasicSymbolRef::SF_Undefined)
      continue;
    auto *B = dyn_cast<DefinedBitcode>(S->body());
    if (!B || B->File != &F)
      continue;

    // We collect the set of symbols we want to internalize here
    // and change the linkage after the IRMover executed, i.e. after
    // we imported the symbols and satisfied undefined references
    // to it. We can't just change linkage here because otherwise
    // the IRMover will just rename the symbol.
    if (GV && shouldInternalize(Used, S, GV))
      InternalizedSyms.insert(GV->getName());

    // At this point we know that either the combined LTO object will provide a
    // definition of a symbol, or we will internalize it. In either case, we
    // need to undefine the symbol. In the former case, the real definition
    // needs to be able to replace the original definition without conflicting.
    // In the latter case, we need to allow the combined LTO object to provide a
    // definition with the same name, for example when doing parallel codegen.
    undefine(S);

    if (!GV)
      // Module asm symbol.
      continue;

    switch (GV->getLinkage()) {
    default:
      break;
    case llvm::GlobalValue::LinkOnceAnyLinkage:
      GV->setLinkage(GlobalValue::WeakAnyLinkage);
      break;
    case llvm::GlobalValue::LinkOnceODRLinkage:
      GV->setLinkage(GlobalValue::WeakODRLinkage);
      break;
    }

    Keep.push_back(GV);
  }

  Mover.move(Obj->takeModule(), Keep,
             [](GlobalValue &, IRMover::ValueAdder) {});
}

static void internalize(GlobalValue &GV) {
  assert(!GV.hasLocalLinkage() &&
         "Trying to internalize a symbol with local linkage!");
  GV.setLinkage(GlobalValue::InternalLinkage);
}

std::vector<std::unique_ptr<InputFile>> BitcodeCompiler::runSplitCodegen(
    const std::function<std::unique_ptr<TargetMachine>()> &TMFactory) {
  unsigned NumThreads = Config->LtoJobs;
  OwningData.resize(NumThreads);

  std::list<raw_svector_ostream> OSs;
  std::vector<raw_pwrite_stream *> OSPtrs;
  for (SmallString<0> &Obj : OwningData) {
    OSs.emplace_back(Obj);
    OSPtrs.push_back(&OSs.back());
  }

  splitCodeGen(std::move(Combined), OSPtrs, {}, TMFactory);

  std::vector<std::unique_ptr<InputFile>> ObjFiles;
  for (SmallString<0> &Obj : OwningData)
    ObjFiles.push_back(createObjectFile(
        MemoryBufferRef(Obj, "LLD-INTERNAL-combined-lto-object")));

  if (Config->SaveTemps)
    for (unsigned I = 0; I < NumThreads; ++I)
      saveLtoObjectFile(OwningData[I], I, NumThreads > 1);

  return ObjFiles;
}

// Merge all the bitcode files we have seen, codegen the result
// and return the resulting ObjectFile.
std::vector<std::unique_ptr<InputFile>> BitcodeCompiler::compile() {
  TheTriple = Combined->getTargetTriple();
  for (const auto &Name : InternalizedSyms) {
    GlobalValue *GV = Combined->getNamedValue(Name.first());
    assert(GV);
    internalize(*GV);
  }

  if (Config->SaveTemps)
    saveBCFile(*Combined, ".lto.bc");

  std::string Msg;
  const Target *T = TargetRegistry::lookupTarget(TheTriple, Msg);
  if (!T)
    fatal("target not found: " + Msg);
  TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
  Reloc::Model R = Config->Pic ? Reloc::PIC_ : Reloc::Static;

  auto CreateTargetMachine = [&]() {
    return std::unique_ptr<TargetMachine>(
        T->createTargetMachine(TheTriple, "", "", Options, R));
  };

  std::unique_ptr<TargetMachine> TM = CreateTargetMachine();
  runLTOPasses(*Combined, *TM);
  if (HasError)
    return {};

  return runSplitCodegen(CreateTargetMachine);
}
<|endoftext|>


<commit_before>//===- LTO.cpp ------------------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "LTO.h"
#include "Config.h"
#include "Driver.h"
#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/LoopPassManager.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/ParallelCG.h"
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Verifier.h"
#include "llvm/LTO/LTO.h"
#include "llvm/LTO/legacy/UpdateCompilerUsed.h"
#include "llvm/Linker/IRMover.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"

using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;

using namespace lld;
using namespace lld::elf;

// This is for use when debugging LTO.
static void saveBuffer(StringRef Buffer, const Twine &Path) {
  std::error_code EC;
  raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
  if (EC)
    error(EC, "cannot create " + Path);
  OS << Buffer;
}

static void diagnosticHandler(const DiagnosticInfo &DI) {
  SmallString<128> ErrStorage;
  raw_svector_ostream OS(ErrStorage);
  DiagnosticPrinterRawOStream DP(OS);
  DI.print(DP);
  warning(ErrStorage);
}

static std::unique_ptr<lto::LTO> createLTO() {
  lto::Config Conf;
  lto::ThinBackend Backend;

  // LLD supports the new relocations.
  Conf.Options = InitTargetOptionsFromCodeGenFlags();
  Conf.Options.RelaxELFRelocations = true;

  Conf.RelocModel = Config->Pic ? Reloc::PIC_ : Reloc::Static;
  Conf.DisableVerify = Config->DisableVerify;
  Conf.DiagHandler = diagnosticHandler;
  Conf.OptLevel = Config->LtoO;

  // Set up a custom pipeline if we've been asked to.
  Conf.OptPipeline = Config->LtoNewPmPasses;
  Conf.AAPipeline = Config->LtoAAPipeline;

  if (Config->SaveTemps)
    check(Conf.addSaveTemps(std::string(Config->OutputFile) + ".",
                            /*UseInputModulePath*/ true));

  return make_unique<lto::LTO>(std::move(Conf), Backend, Config->LtoJobs);
}

BitcodeCompiler::BitcodeCompiler() : LtoObj(createLTO()) {}

BitcodeCompiler::~BitcodeCompiler() {}

static void undefine(Symbol *S) {
  replaceBody<Undefined>(S, S->body()->getName(), STV_DEFAULT, S->body()->Type,
                         nullptr);
}

void BitcodeCompiler::add(BitcodeFile &F) {
  lto::InputFile &Obj = *F.Obj;
  if (Obj.getDataLayoutStr().empty())
    fatal("invalid bitcode file: " + F.getName() + " has no datalayout");

  unsigned SymNum = 0;
  std::vector<Symbol *> Syms = F.getSymbols();
  std::vector<lto::SymbolResolution> Resols(Syms.size());

  // Provide a resolution to the LTO API for each symbol.
  for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
    Symbol *Sym = Syms[SymNum];
    lto::SymbolResolution &R = Resols[SymNum];
    ++SymNum;
    SymbolBody *B = Sym->body();

    // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
    // reports two symbols for module ASM defined. Without this check, lld
    // flags an undefined in IR with a definition in ASM as prevailing.
    // Once IRObjectFile is fixed to report only one symbol this hack can
    // be removed.
    R.Prevailing =
        !(ObjSym.getFlags() & object::BasicSymbolRef::SF_Undefined) &&
        B->File == &F;

    R.VisibleToRegularObj =
        Sym->IsUsedInRegularObj || (R.Prevailing && Sym->includeInDynsym());
    if (R.Prevailing)
      undefine(Sym);
  }
  check(LtoObj->add(std::move(F.Obj), Resols));
}

// Merge all the bitcode files we have seen, codegen the result
// and return the resulting ObjectFile(s).
std::vector<InputFile *> BitcodeCompiler::compile() {
  std::vector<InputFile *> Ret;
  unsigned MaxTasks = LtoObj->getMaxTasks();
  Buff.resize(MaxTasks);

  auto AddStream =
      [&](size_t Task) -> std::unique_ptr<lto::NativeObjectStream> {
    return llvm::make_unique<lto::NativeObjectStream>(
        llvm::make_unique<llvm::raw_svector_ostream>(Buff[Task]));
  };

  check(LtoObj->run(AddStream));
  if (HasError)
    return Ret;

  for (unsigned I = 0; I != MaxTasks; ++I) {
    if (Buff[I].empty())
      continue;
    if (Config->SaveTemps) {
      if (MaxTasks == 1)
        saveBuffer(Buff[I], Config->OutputFile + ".lto.o");
      else
        saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.o");
    }
    MemoryBufferRef CompiledObjRef(Buff[I], "lto.tmp");
    InputFile *Obj = createObjectFile(CompiledObjRef);
    Ret.push_back(Obj);
  }
  return Ret;
}
<commit_msg>[LTO] Fully qualify make_unique to make MSVC happy.<commit_after>//===- LTO.cpp ------------------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "LTO.h"
#include "Config.h"
#include "Driver.h"
#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/LoopPassManager.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/ParallelCG.h"
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Verifier.h"
#include "llvm/LTO/LTO.h"
#include "llvm/LTO/legacy/UpdateCompilerUsed.h"
#include "llvm/Linker/IRMover.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"

using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;

using namespace lld;
using namespace lld::elf;

// This is for use when debugging LTO.
static void saveBuffer(StringRef Buffer, const Twine &Path) {
  std::error_code EC;
  raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
  if (EC)
    error(EC, "cannot create " + Path);
  OS << Buffer;
}

static void diagnosticHandler(const DiagnosticInfo &DI) {
  SmallString<128> ErrStorage;
  raw_svector_ostream OS(ErrStorage);
  DiagnosticPrinterRawOStream DP(OS);
  DI.print(DP);
  warning(ErrStorage);
}

static std::unique_ptr<lto::LTO> createLTO() {
  lto::Config Conf;
  lto::ThinBackend Backend;

  // LLD supports the new relocations.
  Conf.Options = InitTargetOptionsFromCodeGenFlags();
  Conf.Options.RelaxELFRelocations = true;

  Conf.RelocModel = Config->Pic ? Reloc::PIC_ : Reloc::Static;
  Conf.DisableVerify = Config->DisableVerify;
  Conf.DiagHandler = diagnosticHandler;
  Conf.OptLevel = Config->LtoO;

  // Set up a custom pipeline if we've been asked to.
  Conf.OptPipeline = Config->LtoNewPmPasses;
  Conf.AAPipeline = Config->LtoAAPipeline;

  if (Config->SaveTemps)
    check(Conf.addSaveTemps(std::string(Config->OutputFile) + ".",
                            /*UseInputModulePath*/ true));

  return llvm::make_unique<lto::LTO>(std::move(Conf), Backend, Config->LtoJobs);
}

BitcodeCompiler::BitcodeCompiler() : LtoObj(createLTO()) {}

BitcodeCompiler::~BitcodeCompiler() {}

static void undefine(Symbol *S) {
  replaceBody<Undefined>(S, S->body()->getName(), STV_DEFAULT, S->body()->Type,
                         nullptr);
}

void BitcodeCompiler::add(BitcodeFile &F) {
  lto::InputFile &Obj = *F.Obj;
  if (Obj.getDataLayoutStr().empty())
    fatal("invalid bitcode file: " + F.getName() + " has no datalayout");

  unsigned SymNum = 0;
  std::vector<Symbol *> Syms = F.getSymbols();
  std::vector<lto::SymbolResolution> Resols(Syms.size());

  // Provide a resolution to the LTO API for each symbol.
  for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
    Symbol *Sym = Syms[SymNum];
    lto::SymbolResolution &R = Resols[SymNum];
    ++SymNum;
    SymbolBody *B = Sym->body();

    // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
    // reports two symbols for module ASM defined. Without this check, lld
    // flags an undefined in IR with a definition in ASM as prevailing.
    // Once IRObjectFile is fixed to report only one symbol this hack can
    // be removed.
    R.Prevailing =
        !(ObjSym.getFlags() & object::BasicSymbolRef::SF_Undefined) &&
        B->File == &F;

    R.VisibleToRegularObj =
        Sym->IsUsedInRegularObj || (R.Prevailing && Sym->includeInDynsym());
    if (R.Prevailing)
      undefine(Sym);
  }
  check(LtoObj->add(std::move(F.Obj), Resols));
}

// Merge all the bitcode files we have seen, codegen the result
// and return the resulting ObjectFile(s).
std::vector<InputFile *> BitcodeCompiler::compile() {
  std::vector<InputFile *> Ret;
  unsigned MaxTasks = LtoObj->getMaxTasks();
  Buff.resize(MaxTasks);

  auto AddStream =
      [&](size_t Task) -> std::unique_ptr<lto::NativeObjectStream> {
    return llvm::make_unique<lto::NativeObjectStream>(
        llvm::make_unique<llvm::raw_svector_ostream>(Buff[Task]));
  };

  check(LtoObj->run(AddStream));
  if (HasError)
    return Ret;

  for (unsigned I = 0; I != MaxTasks; ++I) {
    if (Buff[I].empty())
      continue;
    if (Config->SaveTemps) {
      if (MaxTasks == 1)
        saveBuffer(Buff[I], Config->OutputFile + ".lto.o");
      else
        saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.o");
    }
    MemoryBufferRef CompiledObjRef(Buff[I], "lto.tmp");
    InputFile *Obj = createObjectFile(CompiledObjRef);
    Ret.push_back(Obj);
  }
  return Ret;
}
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#include "caf/io/middleman_actor.hpp"

#include <stdexcept>
#include <tuple>
#include <utility>

#include "caf/actor.hpp"
#include "caf/actor_proxy.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/logger.hpp"
#include "caf/node_id.hpp"
#include "caf/sec.hpp"
#include "caf/send.hpp"
#include "caf/typed_event_based_actor.hpp"

#include "caf/io/basp_broker.hpp"
#include "caf/io/middleman_actor_impl.hpp"
#include "caf/io/system_messages.hpp"

#include "caf/io/network/default_multiplexer.hpp"
#include "caf/io/network/doorman_impl.hpp"
#include "caf/io/network/interfaces.hpp"
#include "caf/io/network/stream_impl.hpp"

#include "caf/openssl/session.hpp"

#ifdef CAF_WINDOWS
#  include <winsock2.h>
#  include <ws2tcpip.h> // socket_size_type, etc. (MSVC20xx)
#else
#  include <sys/socket.h>
#  include <sys/types.h>
#endif

namespace caf::openssl {

namespace {

using native_socket = io::network::native_socket;
using default_mpx = io::network::default_multiplexer;

struct ssl_policy {
  ssl_policy(session_ptr session) : session_(std::move(session)) {
    // nop
  }

  rw_state read_some(size_t& result, native_socket fd, void* buf, size_t len) {
    CAF_LOG_TRACE(CAF_ARG(fd) << CAF_ARG(len));
    return session_->read_some(result, fd, buf, len);
  }

  rw_state
  write_some(size_t& result, native_socket fd, const void* buf, size_t len) {
    CAF_LOG_TRACE(CAF_ARG(fd) << CAF_ARG(len));
    return session_->write_some(result, fd, buf, len);
  }

  bool try_accept(native_socket& result, native_socket fd) {
    CAF_LOG_TRACE(CAF_ARG(fd));
    sockaddr_storage addr;
    memset(&addr, 0, sizeof(addr));
    caf::io::network::socket_size_type addrlen = sizeof(addr);
    result = accept(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen);
    // note accept4 is better to avoid races in setting CLOEXEC (but not posix)
    if (result == io::network::invalid_native_socket) {
      auto err = io::network::last_socket_error();
      if (!io::network::would_block_or_temporarily_unavailable(err))
        CAF_LOG_ERROR(
          "accept failed:" << io::network::socket_error_as_string(err));
      return false;
    }
    io::network::child_process_inherit(result, false);
    CAF_LOG_DEBUG(CAF_ARG(fd) << CAF_ARG(result));
    return session_->try_accept(result);
  }

  bool must_read_more(native_socket fd, size_t threshold) {
    return session_->must_read_more(fd, threshold);
  }

private:
  session_ptr session_;
};

class scribe_impl : public io::scribe {
public:
  scribe_impl(default_mpx& mpx, native_socket sockfd, session_ptr sptr)
    : scribe(io::network::conn_hdl_from_socket(sockfd)),
      launched_(false),
      stream_(mpx, sockfd, std::move(sptr)) {
    // nop
  }

  ~scribe_impl() override {
    CAF_LOG_TRACE("");
  }

  void configure_read(io::receive_policy::config config) override {
    CAF_LOG_TRACE(CAF_ARG(config));
    stream_.configure_read(config);
    if (!launched_)
      launch();
  }

  void ack_writes(bool enable) override {
    CAF_LOG_TRACE(CAF_ARG(enable));
    stream_.ack_writes(enable);
  }

  byte_buffer& wr_buf() override {
    return stream_.wr_buf();
  }

  byte_buffer& rd_buf() override {
    return stream_.rd_buf();
  }

  void graceful_shutdown() override {
    CAF_LOG_TRACE("");
    stream_.graceful_shutdown();
    detach(&stream_.backend(), false);
  }

  void flush() override {
    CAF_LOG_TRACE("");
    stream_.flush(this);
  }

  std::string addr() const override {
    auto x = io::network::remote_addr_of_fd(stream_.fd());
    if (!x)
      return "";
    return *x;
  }

  uint16_t port() const override {
    auto x = io::network::remote_port_of_fd(stream_.fd());
    if (!x)
      return 0;
    return *x;
  }

  void launch() {
    CAF_LOG_TRACE("");
    CAF_ASSERT(!launched_);
    launched_ = true;
    stream_.start(this);
    // This schedules the scribe in case SSL still needs to call SSL_connect
    // or SSL_accept. Otherwise, the backend simply removes the socket for
    // write operations after the first "nop write".
    stream_.force_empty_write(this);
  }

  void add_to_loop() override {
    CAF_LOG_TRACE("");
    stream_.activate(this);
  }

  void remove_from_loop() override {
    CAF_LOG_TRACE("");
    stream_.passivate();
  }

private:
  bool launched_;
  io::network::stream_impl<ssl_policy> stream_;
};

class doorman_impl : public io::network::doorman_impl {
public:
  doorman_impl(default_mpx& mx, native_socket sockfd)
    : io::network::doorman_impl(mx, sockfd) {
    // nop
  }

  bool new_connection() override {
    CAF_LOG_TRACE("");
    if (detached())
      // we are already disconnected from the broker while the multiplexer
      // did not yet remove the socket, this can happen if an I/O event causes
      // the broker to call close_all() while the pollset contained
      // further activities for the broker
      return false;
    auto& dm = acceptor_.backend();
    auto fd = acceptor_.accepted_socket();
    io::network::nonblocking(fd, true);
    auto sssn = make_session(parent()->system(), fd, true);
    if (sssn == nullptr) {
      CAF_LOG_ERROR("Unable to create SSL session for accepted socket");
      return false;
    }
    auto scrb = make_counted<scribe_impl>(dm, fd, std::move(sssn));
    auto hdl = scrb->hdl();
    parent()->add_scribe(std::move(scrb));
    return doorman::new_connection(&dm, hdl);
  }
};

class middleman_actor_impl : public io::middleman_actor_impl {
public:
  middleman_actor_impl(actor_config& cfg, actor default_broker)
    : io::middleman_actor_impl(cfg, std::move(default_broker)) {
    // nop
  }

  const char* name() const override {
    return "openssl::middleman_actor";
  }

protected:
  expected<io::scribe_ptr>
  connect(const std::string& host, uint16_t port) override {
    CAF_LOG_TRACE(CAF_ARG(host) << CAF_ARG(port));
    auto fd = io::network::new_tcp_connection(host, port);
    if (!fd)
      return std::move(fd.error());
    io::network::nonblocking(*fd, true);
    auto sssn = make_session(system(), *fd, false);
    if (!sssn) {
      CAF_LOG_ERROR("Unable to create SSL session for connection");
      return sec::cannot_connect_to_node;
    }
    CAF_LOG_DEBUG("successfully created an SSL session for:" << CAF_ARG(host)
                                                             << CAF_ARG(port));
    return make_counted<scribe_impl>(mpx(), *fd, std::move(sssn));
  }

  expected<io::doorman_ptr>
  open(uint16_t port, const char* addr, bool reuse) override {
    CAF_LOG_TRACE(CAF_ARG(port) << CAF_ARG(reuse));
    auto fd = io::network::new_tcp_acceptor_impl(port, addr, reuse);
    if (!fd)
      return std::move(fd.error());
    return make_counted<doorman_impl>(mpx(), *fd);
  }

private:
  default_mpx& mpx() {
    return static_cast<default_mpx&>(system().middleman().backend());
  }
};

} // namespace

io::middleman_actor make_middleman_actor(actor_system& sys, actor db) {
  return !get_or(sys.config(), "middleman.attach-utility-actors", false)
           ? sys.spawn<middleman_actor_impl, detached + hidden>(std::move(db))
           : sys.spawn<middleman_actor_impl, hidden>(std::move(db));
}

} // namespace caf::openssl
<commit_msg>fixes compile error in libcaf_openssl/middleman_actor.cpp on windows<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#include "caf/io/middleman_actor.hpp"

#ifdef CAF_WINDOWS
#  include <winsock2.h>
#  include <ws2tcpip.h> // socket_size_type, etc. (MSVC20xx)
#else
#  include <sys/socket.h>
#  include <sys/types.h>
#endif

#include <stdexcept>
#include <tuple>
#include <utility>

#include "caf/actor.hpp"
#include "caf/actor_proxy.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/logger.hpp"
#include "caf/node_id.hpp"
#include "caf/sec.hpp"
#include "caf/send.hpp"
#include "caf/typed_event_based_actor.hpp"

#include "caf/io/basp_broker.hpp"
#include "caf/io/middleman_actor_impl.hpp"
#include "caf/io/system_messages.hpp"

#include "caf/io/network/default_multiplexer.hpp"
#include "caf/io/network/doorman_impl.hpp"
#include "caf/io/network/interfaces.hpp"
#include "caf/io/network/stream_impl.hpp"

#include "caf/openssl/session.hpp"

namespace caf::openssl {

namespace {

using native_socket = io::network::native_socket;
using default_mpx = io::network::default_multiplexer;

struct ssl_policy {
  ssl_policy(session_ptr session) : session_(std::move(session)) {
    // nop
  }

  rw_state read_some(size_t& result, native_socket fd, void* buf, size_t len) {
    CAF_LOG_TRACE(CAF_ARG(fd) << CAF_ARG(len));
    return session_->read_some(result, fd, buf, len);
  }

  rw_state
  write_some(size_t& result, native_socket fd, const void* buf, size_t len) {
    CAF_LOG_TRACE(CAF_ARG(fd) << CAF_ARG(len));
    return session_->write_some(result, fd, buf, len);
  }

  bool try_accept(native_socket& result, native_socket fd) {
    CAF_LOG_TRACE(CAF_ARG(fd));
    sockaddr_storage addr;
    memset(&addr, 0, sizeof(addr));
    caf::io::network::socket_size_type addrlen = sizeof(addr);
    result = accept(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen);
    // note accept4 is better to avoid races in setting CLOEXEC (but not posix)
    if (result == io::network::invalid_native_socket) {
      auto err = io::network::last_socket_error();
      if (!io::network::would_block_or_temporarily_unavailable(err))
        CAF_LOG_ERROR(
          "accept failed:" << io::network::socket_error_as_string(err));
      return false;
    }
    io::network::child_process_inherit(result, false);
    CAF_LOG_DEBUG(CAF_ARG(fd) << CAF_ARG(result));
    return session_->try_accept(result);
  }

  bool must_read_more(native_socket fd, size_t threshold) {
    return session_->must_read_more(fd, threshold);
  }

private:
  session_ptr session_;
};

class scribe_impl : public io::scribe {
public:
  scribe_impl(default_mpx& mpx, native_socket sockfd, session_ptr sptr)
    : scribe(io::network::conn_hdl_from_socket(sockfd)),
      launched_(false),
      stream_(mpx, sockfd, std::move(sptr)) {
    // nop
  }

  ~scribe_impl() override {
    CAF_LOG_TRACE("");
  }

  void configure_read(io::receive_policy::config config) override {
    CAF_LOG_TRACE(CAF_ARG(config));
    stream_.configure_read(config);
    if (!launched_)
      launch();
  }

  void ack_writes(bool enable) override {
    CAF_LOG_TRACE(CAF_ARG(enable));
    stream_.ack_writes(enable);
  }

  byte_buffer& wr_buf() override {
    return stream_.wr_buf();
  }

  byte_buffer& rd_buf() override {
    return stream_.rd_buf();
  }

  void graceful_shutdown() override {
    CAF_LOG_TRACE("");
    stream_.graceful_shutdown();
    detach(&stream_.backend(), false);
  }

  void flush() override {
    CAF_LOG_TRACE("");
    stream_.flush(this);
  }

  std::string addr() const override {
    auto x = io::network::remote_addr_of_fd(stream_.fd());
    if (!x)
      return "";
    return *x;
  }

  uint16_t port() const override {
    auto x = io::network::remote_port_of_fd(stream_.fd());
    if (!x)
      return 0;
    return *x;
  }

  void launch() {
    CAF_LOG_TRACE("");
    CAF_ASSERT(!launched_);
    launched_ = true;
    stream_.start(this);
    // This schedules the scribe in case SSL still needs to call SSL_connect
    // or SSL_accept. Otherwise, the backend simply removes the socket for
    // write operations after the first "nop write".
    stream_.force_empty_write(this);
  }

  void add_to_loop() override {
    CAF_LOG_TRACE("");
    stream_.activate(this);
  }

  void remove_from_loop() override {
    CAF_LOG_TRACE("");
    stream_.passivate();
  }

private:
  bool launched_;
  io::network::stream_impl<ssl_policy> stream_;
};

class doorman_impl : public io::network::doorman_impl {
public:
  doorman_impl(default_mpx& mx, native_socket sockfd)
    : io::network::doorman_impl(mx, sockfd) {
    // nop
  }

  bool new_connection() override {
    CAF_LOG_TRACE("");
    if (detached())
      // we are already disconnected from the broker while the multiplexer
      // did not yet remove the socket, this can happen if an I/O event causes
      // the broker to call close_all() while the pollset contained
      // further activities for the broker
      return false;
    auto& dm = acceptor_.backend();
    auto fd = acceptor_.accepted_socket();
    io::network::nonblocking(fd, true);
    auto sssn = make_session(parent()->system(), fd, true);
    if (sssn == nullptr) {
      CAF_LOG_ERROR("Unable to create SSL session for accepted socket");
      return false;
    }
    auto scrb = make_counted<scribe_impl>(dm, fd, std::move(sssn));
    auto hdl = scrb->hdl();
    parent()->add_scribe(std::move(scrb));
    return doorman::new_connection(&dm, hdl);
  }
};

class middleman_actor_impl : public io::middleman_actor_impl {
public:
  middleman_actor_impl(actor_config& cfg, actor default_broker)
    : io::middleman_actor_impl(cfg, std::move(default_broker)) {
    // nop
  }

  const char* name() const override {
    return "openssl::middleman_actor";
  }

protected:
  expected<io::scribe_ptr>
  connect(const std::string& host, uint16_t port) override {
    CAF_LOG_TRACE(CAF_ARG(host) << CAF_ARG(port));
    auto fd = io::network::new_tcp_connection(host, port);
    if (!fd)
      return std::move(fd.error());
    io::network::nonblocking(*fd, true);
    auto sssn = make_session(system(), *fd, false);
    if (!sssn) {
      CAF_LOG_ERROR("Unable to create SSL session for connection");
      return sec::cannot_connect_to_node;
    }
    CAF_LOG_DEBUG("successfully created an SSL session for:" << CAF_ARG(host)
                                                             << CAF_ARG(port));
    return make_counted<scribe_impl>(mpx(), *fd, std::move(sssn));
  }

  expected<io::doorman_ptr>
  open(uint16_t port, const char* addr, bool reuse) override {
    CAF_LOG_TRACE(CAF_ARG(port) << CAF_ARG(reuse));
    auto fd = io::network::new_tcp_acceptor_impl(port, addr, reuse);
    if (!fd)
      return std::move(fd.error());
    return make_counted<doorman_impl>(mpx(), *fd);
  }

private:
  default_mpx& mpx() {
    return static_cast<default_mpx&>(system().middleman().backend());
  }
};

} // namespace

io::middleman_actor make_middleman_actor(actor_system& sys, actor db) {
  return !get_or(sys.config(), "middleman.attach-utility-actors", false)
           ? sys.spawn<middleman_actor_impl, detached + hidden>(std::move(db))
           : sys.spawn<middleman_actor_impl, hidden>(std::move(db));
}

} // namespace caf::openssl
<|endoftext|>


<commit_before>/********************************************************************************
    LibVideoGfx - video processing library
    Copyright (C) 2002  Dirk Farin

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

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

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 ********************************************************************************/

#include "libvideogfx/graphics/fileio/yuv.hh"

namespace videogfx {
  using namespace std;

  FileReader_YUV1::FileReader_YUV1()
    : d_yuvstr(NULL), d_alphastr(NULL),
      d_interleavedUV(false),
      d_greyscale_input(false),
      d_initialized(false)
  {
  }

  void FileReader_YUV1::Init()
  {
    if (d_initialized)
      return;

    // Get file length to calculate number of frames in file.

    assert(d_yuvstr);

    if (d_yuvstr==&cin)
      d_nFrames=999999;
    else
      {
	d_yuvstr->seekg(0,ios::end);
	long length = d_yuvstr->tellg();
	d_yuvstr->seekg(0,ios::beg);

	// Calculate the size of one frame.

	switch (d_spec.chroma)
	  {
	  case Chroma_420:  d_Framesize = d_spec.width * d_spec.height *3/2; break;
	  case Chroma_422:  d_Framesize = d_spec.width * d_spec.height *2;   break;
	  case Chroma_444:  d_Framesize = d_spec.width * d_spec.height *3;   break;
	  default: assert(0); break;
	  }

	d_nFrames = length/d_Framesize;
	if (d_nFrames * d_Framesize != length)
	  {
	    cerr << "Input file has strange file size, continuing anyway.\n";
	    // TOOD: Put exceptionhandling here.
	  }

	//cout << d_nFrames << " frames\n";
      }

    d_nextFrame=0;
    d_initialized=true;
  }

  int FileReader_YUV1::AskNFrames() const
  {
    if (d_initialized)
      return d_nFrames;

    (const_cast<FileReader_YUV1*>(this))->Init();
    return d_nFrames;
  }

  bool FileReader_YUV1::IsEOF() const
  {
    (const_cast<FileReader_YUV1*>(this))->Init();
    if (d_yuvstr->eof())
      return true;

    return d_nextFrame >= d_nFrames;
  }

  void FileReader_YUV1::SkipToImage(int nr)
  {
    if (!d_initialized)
      Init();

    assert(nr>=0);
    assert(nr<d_nFrames);

    d_yuvstr->seekg(nr*d_Framesize,ios::beg);
    if (d_alphastr) d_alphastr->seekg(nr * d_spec.width * d_spec.height , ios::beg);
    d_nextFrame=nr;
  }

  void FileReader_YUV1::ReadImage(Image<Pixel>& img)
  {
    if (!d_initialized)
      Init();

    d_spec.has_alpha = (d_alphastr != NULL);
    d_spec.colorspace = Colorspace_YUV;

    img.Create(d_spec);

    Pixel*const* yp = img.AskFrameY();
    Pixel*const* up = (d_greyscale_input ? NULL : img.AskFrameU());
    Pixel*const* vp = (d_greyscale_input ? NULL : img.AskFrameV());

    // Y
    for (int y=0;y<d_spec.height;y++)
      d_yuvstr->read((char*)yp[y],d_spec.width);

    // color

    if (!d_greyscale_input)
      {
	int ch,cw;
	d_spec.AskChromaSizes(cw,ch);

	if (d_interleavedUV)
	  {
	    uint8* buf = new uint8[cw*2];

	    for (int y=0;y<ch;y++)
	      {
		d_yuvstr->read((char*)buf,cw*2);

		// demangle U,V components

		for (int x=0;x<cw;x++)
		  {
		    up[y][x] = buf[x*2  ];
		    vp[y][x] = buf[x*2+1];
		  }
	      }

	    delete[] buf;
	  }
	else
	  {
	    // U
	    for (int y=0;y<ch;y++)
	      d_yuvstr->read((char*)up[y],cw);

	    // V
	    for (int y=0;y<ch;y++)
	      d_yuvstr->read((char*)vp[y],cw);
	  }
      }

    // Alpha mask

    if (d_alphastr)
      {
	assert(!img.AskBitmap(Bitmap_Alpha).IsEmpty());
	Pixel*const* aa = img.AskFrameA();

	for (int y=0;y<d_spec.height;y++)
	  d_alphastr->read((char*)aa[y],d_spec.width);
      }

    d_nextFrame++;
  }

  FileWriter_YUV1::FileWriter_YUV1()
    : d_yuvstr(NULL),
      d_alphastr(NULL),
      d_write_greyscale_as_color(false),
      d_write_color_as_greyscale(false),
      d_write_interleaved(false)
  {
  }

  void FileWriter_YUV1::WriteImage(const Image<Pixel>& img)
  {
    ImageParam param = img.AskParam();

    bool input_is_greyscale = (param.colorspace == Colorspace_Greyscale);

    const Pixel*const* Y = img.AskFrameY();
    const Pixel*const* U = (input_is_greyscale ? NULL : img.AskFrameU());
    const Pixel*const* V = (input_is_greyscale ? NULL : img.AskFrameV());

    // write Y

    for (int y=0;y<param.height;y++)
      d_yuvstr->write((char*)Y[y],param.width);

    // write chrominance

    int cw,ch;
    param.AskChromaSizes(cw,ch);

    if (d_write_greyscale_as_color && input_is_greyscale)
      {
	uint8* buf;
	buf = new uint8[2*cw];

	for (int i=0;i<2*cw;i++)
	  buf[i]=128;

	for (int y=0;y<ch;y++)
	  d_yuvstr->write((char*)buf,2*cw);

	delete[] buf;
      }
    else if (input_is_greyscale || (!input_is_greyscale && d_write_color_as_greyscale))
      {
	// write no color information
      }
    else
      {
	assert(!input_is_greyscale);
	assert(!d_write_color_as_greyscale);

	if (d_write_interleaved)
	  {
	    uint8* buf;
	    buf = new uint8[2*cw];

	    for (int y=0;y<ch;y++)
	      {
		for (int i=0;i<cw;i++)
		  {
		    buf[2*i  ] = U[y][i];
		    buf[2*i+1] = V[y][i];
		  }

		d_yuvstr->write((char*)buf,2*cw);
	      }

	    delete[] buf;
	  }
	else
	  {
	    for (int y=0;y<ch;y++)
	      d_yuvstr->write((char*)U[y],cw);
	    for (int y=0;y<ch;y++)
	      d_yuvstr->write((char*)V[y],cw);
	  }
      }

    // alpha

    if (d_alphastr && param.has_alpha)
      {
	const Pixel*const* A = img.AskFrameA();

	for (int y=0;y<param.height;y++)
	  d_alphastr->write((char*)A[y],param.width);
      }
  }

}
<commit_msg>Bugfix: number of input frames was calculated erroneous when input         is a greyscale stream.<commit_after>/********************************************************************************
    LibVideoGfx - video processing library
    Copyright (C) 2002  Dirk Farin

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

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

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 ********************************************************************************/

#include "libvideogfx/graphics/fileio/yuv.hh"

namespace videogfx {
  using namespace std;

  FileReader_YUV1::FileReader_YUV1()
    : d_yuvstr(NULL), d_alphastr(NULL),
      d_interleavedUV(false),
      d_greyscale_input(false),
      d_initialized(false)
  {
  }

  void FileReader_YUV1::Init()
  {
    if (d_initialized)
      return;

    // Get file length to calculate number of frames in file.

    assert(d_yuvstr);

    if (d_yuvstr==&cin)
      d_nFrames=999999;
    else
      {
	d_yuvstr->seekg(0,ios::end);
	long length = d_yuvstr->tellg();
	d_yuvstr->seekg(0,ios::beg);

	// Calculate the size of one frame.

	if (d_greyscale_input)
	  d_Framesize = d_spec.width * d_spec.height;
	else
	  {
	    switch (d_spec.chroma)
	      {
	      case Chroma_420:  d_Framesize = d_spec.width * d_spec.height *3/2; break;
	      case Chroma_422:  d_Framesize = d_spec.width * d_spec.height *2;   break;
	      case Chroma_444:  d_Framesize = d_spec.width * d_spec.height *3;   break;
	      default: assert(0); break;
	      }
	  }

	d_nFrames = length/d_Framesize;
	if (d_nFrames * d_Framesize != length)
	  {
	    cerr << "Input file has strange file size, continuing anyway.\n";
	    // TOOD: Put exceptionhandling here.
	  }

	//cout << d_nFrames << " frames\n";
      }

    d_nextFrame=0;
    d_initialized=true;
  }

  int FileReader_YUV1::AskNFrames() const
  {
    if (d_initialized)
      return d_nFrames;

    (const_cast<FileReader_YUV1*>(this))->Init();
    return d_nFrames;
  }

  bool FileReader_YUV1::IsEOF() const
  {
    (const_cast<FileReader_YUV1*>(this))->Init();
    if (d_yuvstr->eof())
      return true;

    return d_nextFrame >= d_nFrames;
  }

  void FileReader_YUV1::SkipToImage(int nr)
  {
    if (!d_initialized)
      Init();

    assert(nr>=0);
    assert(nr<d_nFrames);

    d_yuvstr->seekg(nr*d_Framesize,ios::beg);
    if (d_alphastr) d_alphastr->seekg(nr * d_spec.width * d_spec.height , ios::beg);
    d_nextFrame=nr;
  }

  void FileReader_YUV1::ReadImage(Image<Pixel>& img)
  {
    if (!d_initialized)
      Init();

    d_spec.has_alpha = (d_alphastr != NULL);
    d_spec.colorspace = Colorspace_YUV;

    img.Create(d_spec);

    Pixel*const* yp = img.AskFrameY();
    Pixel*const* up = (d_greyscale_input ? NULL : img.AskFrameU());
    Pixel*const* vp = (d_greyscale_input ? NULL : img.AskFrameV());

    // Y
    for (int y=0;y<d_spec.height;y++)
      d_yuvstr->read((char*)yp[y],d_spec.width);

    // color

    if (!d_greyscale_input)
      {
	int ch,cw;
	d_spec.AskChromaSizes(cw,ch);

	if (d_interleavedUV)
	  {
	    uint8* buf = new uint8[cw*2];

	    for (int y=0;y<ch;y++)
	      {
		d_yuvstr->read((char*)buf,cw*2);

		// demangle U,V components

		for (int x=0;x<cw;x++)
		  {
		    up[y][x] = buf[x*2  ];
		    vp[y][x] = buf[x*2+1];
		  }
	      }

	    delete[] buf;
	  }
	else
	  {
	    // U
	    for (int y=0;y<ch;y++)
	      d_yuvstr->read((char*)up[y],cw);

	    // V
	    for (int y=0;y<ch;y++)
	      d_yuvstr->read((char*)vp[y],cw);
	  }
      }

    // Alpha mask

    if (d_alphastr)
      {
	assert(!img.AskBitmap(Bitmap_Alpha).IsEmpty());
	Pixel*const* aa = img.AskFrameA();

	for (int y=0;y<d_spec.height;y++)
	  d_alphastr->read((char*)aa[y],d_spec.width);
      }

    d_nextFrame++;
  }

  FileWriter_YUV1::FileWriter_YUV1()
    : d_yuvstr(NULL),
      d_alphastr(NULL),
      d_write_greyscale_as_color(false),
      d_write_color_as_greyscale(false),
      d_write_interleaved(false)
  {
  }

  void FileWriter_YUV1::WriteImage(const Image<Pixel>& img)
  {
    ImageParam param = img.AskParam();

    bool input_is_greyscale = (param.colorspace == Colorspace_Greyscale);

    const Pixel*const* Y = img.AskFrameY();
    const Pixel*const* U = (input_is_greyscale ? NULL : img.AskFrameU());
    const Pixel*const* V = (input_is_greyscale ? NULL : img.AskFrameV());

    // write Y

    for (int y=0;y<param.height;y++)
      d_yuvstr->write((char*)Y[y],param.width);

    // write chrominance

    int cw,ch;
    param.AskChromaSizes(cw,ch);

    if (d_write_greyscale_as_color && input_is_greyscale)
      {
	uint8* buf;
	buf = new uint8[2*cw];

	for (int i=0;i<2*cw;i++)
	  buf[i]=128;

	for (int y=0;y<ch;y++)
	  d_yuvstr->write((char*)buf,2*cw);

	delete[] buf;
      }
    else if (input_is_greyscale || (!input_is_greyscale && d_write_color_as_greyscale))
      {
	// write no color information
      }
    else
      {
	assert(!input_is_greyscale);
	assert(!d_write_color_as_greyscale);

	if (d_write_interleaved)
	  {
	    uint8* buf;
	    buf = new uint8[2*cw];

	    for (int y=0;y<ch;y++)
	      {
		for (int i=0;i<cw;i++)
		  {
		    buf[2*i  ] = U[y][i];
		    buf[2*i+1] = V[y][i];
		  }

		d_yuvstr->write((char*)buf,2*cw);
	      }

	    delete[] buf;
	  }
	else
	  {
	    for (int y=0;y<ch;y++)
	      d_yuvstr->write((char*)U[y],cw);
	    for (int y=0;y<ch;y++)
	      d_yuvstr->write((char*)V[y],cw);
	  }
      }

    // alpha

    if (d_alphastr && param.has_alpha)
      {
	const Pixel*const* A = img.AskFrameA();

	for (int y=0;y<param.height;y++)
	  d_alphastr->write((char*)A[y],param.width);
      }
  }

}
<|endoftext|>