<commit_before>
#include <vector>

#include "geometry/permutation.hpp"
#include "geometry/orthogonal_map.hpp"
#include "geometry/r3_element.hpp"
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "quantities/si.hpp"
#include "testing_utilities/almost_equals.hpp"

using principia::quantities::Length;
using principia::si::Metre;
using principia::testing_utilities::AlmostEquals;
using testing::Eq;

namespace principia {
namespace geometry {

class PermutationTest : public testing::Test {
 protected:
  struct World;
  using Orth = OrthogonalMap<World, World>;
  using Perm = Permutation<World, World>;
  using R3 = R3Element<quantities::Length>;

  void SetUp() override {
    vector_ = Vector<quantities::Length, World>(
        R3(1.0 * Metre, 2.0 * Metre, 3.0 * Metre));
    bivector_ = Bivector<quantities::Length, World>(
        R3(1.0 * Metre, 2.0 * Metre, 3.0 * Metre));
    trivector_ = Trivector<quantities::Length, World>(4.0 * Metre);
  }

  Vector<quantities::Length, World> vector_;
  Bivector<quantities::Length, World> bivector_;
  Trivector<quantities::Length, World> trivector_;
};

TEST_F(PermutationTest, Identity) {
  EXPECT_THAT(Perm::Identity()(vector_.coordinates()),
              Eq<R3>({1.0 * Metre, 2.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, XYZ) {
  EXPECT_THAT(Perm(Perm::XYZ)(vector_.coordinates()),
              Eq<R3>({1.0 * Metre, 2.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, YZX) {
  EXPECT_THAT(Perm(Perm::YZX)(vector_.coordinates()),
              Eq<R3>({2.0 * Metre, 3.0 * Metre, 1.0 * Metre}));
}

TEST_F(PermutationTest, ZXY) {
  EXPECT_THAT(Perm(Perm::ZXY)(vector_.coordinates()),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
}

TEST_F(PermutationTest, XZY) {
  EXPECT_THAT(Perm(Perm::XZY)(vector_.coordinates()),
              Eq<R3>({1.0 * Metre, 3.0 * Metre, 2.0 * Metre}));
}

TEST_F(PermutationTest, ZYX) {
  EXPECT_THAT(Perm(Perm::ZYX)(vector_.coordinates()),
              Eq<R3>({3.0 * Metre, 2.0 * Metre, 1.0 * Metre}));
}

TEST_F(PermutationTest, YXZ) {
  EXPECT_THAT(Perm(Perm::YXZ)(vector_.coordinates()),
              Eq<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, Determinant) {
  Perm xyz(Perm::XYZ);
  Perm yzx(Perm::YZX);
  Perm zxy(Perm::ZXY);
  Perm xzy(Perm::XZY);
  Perm zyx(Perm::ZYX);
  Perm yxz(Perm::YXZ);
  EXPECT_TRUE(xyz.Determinant().Positive());
  EXPECT_TRUE(yzx.Determinant().Positive());
  EXPECT_TRUE(zxy.Determinant().Positive());
  EXPECT_TRUE(xzy.Determinant().Negative());
  EXPECT_TRUE(zyx.Determinant().Negative());
  EXPECT_TRUE(yxz.Determinant().Negative());
}

TEST_F(PermutationTest, AppliedToVector) {
  EXPECT_THAT(Perm(Perm::YZX)(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 3.0 * Metre, 1.0 * Metre}));
  EXPECT_THAT(Perm(Perm::YXZ)(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, AppliedToBivector) {
  EXPECT_THAT(Perm(Perm::ZXY)(bivector_).coordinates(),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
  EXPECT_THAT(Perm(Perm::ZYX)(bivector_).coordinates(),
              Eq<R3>({-3.0 * Metre, -2.0 * Metre, -1.0 * Metre}));
}

TEST_F(PermutationTest, AppliedToTrivector) {
  EXPECT_THAT(Perm(Perm::XYZ)(trivector_).coordinates(),
              Eq(4.0 * Metre));
  EXPECT_THAT(Perm(Perm::XZY)(trivector_).coordinates(),
              Eq(-4.0 * Metre));
}

TEST_F(PermutationTest, Inverse) {
  EXPECT_THAT(Perm(Perm::YZX).Inverse()(vector_).coordinates(),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
  EXPECT_THAT(Perm(Perm::YXZ).Inverse()(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}));

  std::vector<Perm> const all =
      {Perm(Perm::XYZ), Perm(Perm::YZX), Perm(Perm::ZXY),
       Perm(Perm::XZY), Perm(Perm::ZYX), Perm(Perm::YXZ)};
  for (const Perm& p : all) {
    Perm const identity = p * p.Inverse();
    EXPECT_THAT(identity(vector_), Eq(vector_));
  }
}

TEST_F(PermutationTest, Forget) {
  EXPECT_THAT(Perm(Perm::XYZ).Forget()(vector_).coordinates(),
              Eq<R3>({1.0 * Metre, 2.0 * Metre, 3.0 * Metre}));
  EXPECT_THAT(Perm(Perm::YZX).Forget()(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 3.0 * Metre, 1.0 * Metre}));
  EXPECT_THAT(Perm(Perm::ZXY).Forget()(vector_).coordinates(),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
  EXPECT_THAT(Perm(Perm::XZY).Forget()(vector_).coordinates(),
              AlmostEquals<R3>({1.0 * Metre, 3.0 * Metre, 2.0 * Metre}, 2));
  EXPECT_THAT(Perm(Perm::ZYX).Forget()(vector_).coordinates(),
              AlmostEquals<R3>({3.0 * Metre, 2.0 * Metre, 1.0 * Metre}, 4));
  EXPECT_THAT(Perm(Perm::YXZ).Forget()(vector_).coordinates(),
              AlmostEquals<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}, 2));
}

TEST_F(PermutationTest, Compose) {
  std::vector<Perm> const all =
      {Perm(Perm::XYZ), Perm(Perm::YZX), Perm(Perm::ZXY),
       Perm(Perm::XZY), Perm(Perm::ZYX), Perm(Perm::YXZ)};
  for (Perm const& p1 : all) {
    Orth const o1 = p1.Forget();
    for (Perm const& p2 : all) {
      Orth const o2 = p2.Forget();
      Perm const p12 = p1 * p2;
      Orth const o12 = o1 * o2;
      for (Length l = 1 * Metre; l < 4 * Metre; l += 1 * Metre) {
        vector_.coordinates().x = l;
        EXPECT_THAT(p12(vector_), AlmostEquals(o12(vector_), 20));
      }
    }
  }
}

}  // namespace geometry
}  // namespace principia
<commit_msg>Better testing, notably composition.<commit_after>
#include <vector>

#include "geometry/permutation.hpp"
#include "geometry/orthogonal_map.hpp"
#include "geometry/r3_element.hpp"
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "quantities/si.hpp"
#include "testing_utilities/almost_equals.hpp"

using principia::quantities::Length;
using principia::si::Metre;
using principia::testing_utilities::AlmostEquals;
using testing::Eq;

namespace principia {
namespace geometry {

class PermutationTest : public testing::Test {
 protected:
  struct World1;
  struct World2;
  using Orth = OrthogonalMap<World1, World2>;
  using Perm = Permutation<World1, World2>;
  using R3 = R3Element<quantities::Length>;

  void SetUp() override {
    vector_ = Vector<quantities::Length, World1>(
        R3(1.0 * Metre, 2.0 * Metre, 3.0 * Metre));
    bivector_ = Bivector<quantities::Length, World1>(
        R3(1.0 * Metre, 2.0 * Metre, 3.0 * Metre));
    trivector_ = Trivector<quantities::Length, World1>(4.0 * Metre);
  }

  Vector<quantities::Length, World1> vector_;
  Bivector<quantities::Length, World1> bivector_;
  Trivector<quantities::Length, World1> trivector_;
};

TEST_F(PermutationTest, Identity) {
  EXPECT_THAT(Perm::Identity()(vector_.coordinates()),
              Eq<R3>({1.0 * Metre, 2.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, XYZ) {
  EXPECT_THAT(Perm(Perm::XYZ)(vector_.coordinates()),
              Eq<R3>({1.0 * Metre, 2.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, YZX) {
  EXPECT_THAT(Perm(Perm::YZX)(vector_.coordinates()),
              Eq<R3>({2.0 * Metre, 3.0 * Metre, 1.0 * Metre}));
}

TEST_F(PermutationTest, ZXY) {
  EXPECT_THAT(Perm(Perm::ZXY)(vector_.coordinates()),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
}

TEST_F(PermutationTest, XZY) {
  EXPECT_THAT(Perm(Perm::XZY)(vector_.coordinates()),
              Eq<R3>({1.0 * Metre, 3.0 * Metre, 2.0 * Metre}));
}

TEST_F(PermutationTest, ZYX) {
  EXPECT_THAT(Perm(Perm::ZYX)(vector_.coordinates()),
              Eq<R3>({3.0 * Metre, 2.0 * Metre, 1.0 * Metre}));
}

TEST_F(PermutationTest, YXZ) {
  EXPECT_THAT(Perm(Perm::YXZ)(vector_.coordinates()),
              Eq<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, Determinant) {
  Perm xyz(Perm::XYZ);
  Perm yzx(Perm::YZX);
  Perm zxy(Perm::ZXY);
  Perm xzy(Perm::XZY);
  Perm zyx(Perm::ZYX);
  Perm yxz(Perm::YXZ);
  EXPECT_TRUE(xyz.Determinant().Positive());
  EXPECT_TRUE(yzx.Determinant().Positive());
  EXPECT_TRUE(zxy.Determinant().Positive());
  EXPECT_TRUE(xzy.Determinant().Negative());
  EXPECT_TRUE(zyx.Determinant().Negative());
  EXPECT_TRUE(yxz.Determinant().Negative());
}

TEST_F(PermutationTest, AppliedToVector) {
  EXPECT_THAT(Perm(Perm::YZX)(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 3.0 * Metre, 1.0 * Metre}));
  EXPECT_THAT(Perm(Perm::YXZ)(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}));
}

TEST_F(PermutationTest, AppliedToBivector) {
  EXPECT_THAT(Perm(Perm::ZXY)(bivector_).coordinates(),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
  EXPECT_THAT(Perm(Perm::ZYX)(bivector_).coordinates(),
              Eq<R3>({-3.0 * Metre, -2.0 * Metre, -1.0 * Metre}));
}

TEST_F(PermutationTest, AppliedToTrivector) {
  EXPECT_THAT(Perm(Perm::XYZ)(trivector_).coordinates(),
              Eq(4.0 * Metre));
  EXPECT_THAT(Perm(Perm::XZY)(trivector_).coordinates(),
              Eq(-4.0 * Metre));
}

TEST_F(PermutationTest, Inverse) {
  EXPECT_THAT(Perm(Perm::YZX).Inverse()(vector_).coordinates(),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
  EXPECT_THAT(Perm(Perm::YXZ).Inverse()(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}));

  std::vector<Perm> const all =
      {Perm(Perm::XYZ), Perm(Perm::YZX), Perm(Perm::ZXY),
       Perm(Perm::XZY), Perm(Perm::ZYX), Perm(Perm::YXZ)};
  for (const Perm& p : all) {
    Perm const identity = p * p.Inverse();
    EXPECT_THAT(identity(vector_), Eq(vector_));
  }
}

TEST_F(PermutationTest, Forget) {
  EXPECT_THAT(Perm(Perm::XYZ).Forget()(vector_).coordinates(),
              Eq<R3>({1.0 * Metre, 2.0 * Metre, 3.0 * Metre}));
  EXPECT_THAT(Perm(Perm::YZX).Forget()(vector_).coordinates(),
              Eq<R3>({2.0 * Metre, 3.0 * Metre, 1.0 * Metre}));
  EXPECT_THAT(Perm(Perm::ZXY).Forget()(vector_).coordinates(),
              Eq<R3>({3.0 * Metre, 1.0 * Metre, 2.0 * Metre}));
  EXPECT_THAT(Perm(Perm::XZY).Forget()(vector_).coordinates(),
              AlmostEquals<R3>({1.0 * Metre, 3.0 * Metre, 2.0 * Metre}, 2));
  EXPECT_THAT(Perm(Perm::ZYX).Forget()(vector_).coordinates(),
              AlmostEquals<R3>({3.0 * Metre, 2.0 * Metre, 1.0 * Metre}, 4));
  EXPECT_THAT(Perm(Perm::YXZ).Forget()(vector_).coordinates(),
              AlmostEquals<R3>({2.0 * Metre, 1.0 * Metre, 3.0 * Metre}, 2));
}

TEST_F(PermutationTest, Compose) {
  struct World3;
  using Orth12 = OrthogonalMap<World1, World2>;
  using Orth13 = OrthogonalMap<World1, World3>;
  using Orth23 = OrthogonalMap<World2, World3>;
  using Perm12 = Permutation<World1, World2>;
  using Perm13 = Permutation<World1, World3>;
  using Perm23 = Permutation<World2, World3>;
  std::vector<Perm12> const all12 =
      {Perm12(Perm12::XYZ), Perm12(Perm12::YZX), Perm12(Perm12::ZXY),
       Perm12(Perm12::XZY), Perm12(Perm12::ZYX), Perm12(Perm12::YXZ)};
  std::vector<Perm23> const all23 =
      {Perm23(Perm23::XYZ), Perm23(Perm23::YZX), Perm23(Perm23::ZXY),
       Perm23(Perm23::XZY), Perm23(Perm23::ZYX), Perm23(Perm23::YXZ)};
  for (Perm12 const& p12 : all12) {
    Orth12 const o12 = p12.Forget();
    for (Perm23 const& p23 : all23) {
      Orth23 const o23 = p23.Forget();
      Perm13 const p13 = p23 * p12;
      Orth13 const o13 = o23 * o12;
      for (Length l = 1 * Metre; l < 4 * Metre; l += 1 * Metre) {
        vector_.coordinates().x = l;
        EXPECT_THAT(p13(vector_), AlmostEquals(o13(vector_), 20));
      }
    }
  }
}

}  // namespace geometry
}  // namespace principia
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2018 https://www.thecoderscorner.com (Nutricherry LTD).
 * This product is licensed under an Apache license, see the LICENSE file in the top-level directory.
 */

#include <inttypes.h>
#include <Arduino.h>
#include "TaskManager.h"

TaskManager taskManager;

TimerTask::TimerTask() {
	next = NULL;
	clear();
}

void TimerTask::initialise(uint16_t executionInfo, TimerFn execCallback) {
	this->executionInfo = executionInfo;
	this->callback = execCallback;

	this->scheduledAt = (executionInfo & TASK_MICROS) ? micros() : millis();
}

bool TimerTask::isReady() {
	if (!isInUse() || isRunning()) return false;

	if ((executionInfo & TASK_MICROS) != 0) {
		uint16_t delay = (executionInfo & TIMER_MASK);
		return (micros() - scheduledAt) >= delay;
	}
	else if((executionInfo & TASK_SECONDS) != 0) {
		uint32_t delay = (executionInfo & TIMER_MASK) * 1000L;
		return (millis() - scheduledAt) >= delay;
	}
	else {
		uint16_t delay = (executionInfo & TIMER_MASK);
		return (millis() - scheduledAt) >= delay;
	}
}

unsigned long TimerTask::microsFromNow() {
	uint32_t microsFromNow;
	if ((executionInfo & TASK_MICROS) != 0) {
		uint16_t delay = (executionInfo & TIMER_MASK);
		microsFromNow =  delay - (micros() - scheduledAt);
	}
	else {
		uint32_t startTm = (executionInfo & TIMER_MASK);
		if ((executionInfo & TASK_SECONDS) != 0) {
			startTm *= 1000;
		}
		microsFromNow = (startTm - (millis() - scheduledAt)) * 1000;
	}
	return microsFromNow;
}

inline void TimerTask::execute() {
	if (callback == NULL) return; // failsafe, always exit with null callback.

	// handle repeating tasks - reschedule.
	if (isRepeating()) {
		markRunning();
		callback();
		this->scheduledAt = (executionInfo & TASK_MICROS) ? micros() : millis();
		clearRunning();
	}
	else {
		TimerFn savedCallback = callback;
		clear();
		savedCallback();
	}
}

void TimerTask::clear() {
	executionInfo = 0;
	callback = NULL;
}

void TaskManager::markInterrupted(uint8_t interruptNo) {
	taskManager.lastInterruptTrigger = interruptNo;
	taskManager.interrupted = true;
}

TaskManager::TaskManager(uint8_t taskSlots) {
	this->numberOfSlots = taskSlots;
	this->tasks = new TimerTask[taskSlots];
	interrupted = false;
	first = NULL;
	interruptCallback = NULL;
}

int TaskManager::findFreeTask() {
	for (uint8_t i = 0; i < numberOfSlots; ++i) {
		if (!tasks[i].isInUse()) {
			return i;
		}
	}
	return TASKMGR_INVALIDID;
}

inline int toTimerValue(int v, TimerUnit unit) {
	if (unit == TIME_MILLIS && v > TIMER_MASK) {
		unit = TIME_SECONDS;
		v = v / 1000;
	}
	v = min(v, TIMER_MASK);
	return v | (((uint16_t)unit) << 12);
}

uint8_t TaskManager::scheduleOnce(int millis, TimerFn timerFunction, TimerUnit timeUnit) {
	uint8_t taskId = findFreeTask();
	if (taskId != TASKMGR_INVALIDID) {
		tasks[taskId].initialise(toTimerValue(millis, timeUnit) | TASK_IN_USE, timerFunction);
		putItemIntoQueue(&tasks[taskId]);
	}
	return taskId;
}

uint8_t TaskManager::scheduleFixedRate(int millis, TimerFn timerFunction, TimerUnit timeUnit) {
	uint8_t taskId = findFreeTask();
	if (taskId != TASKMGR_INVALIDID) {
		tasks[taskId].initialise(toTimerValue(millis, timeUnit) | TASK_IN_USE | TASK_REPEATING, timerFunction);
		putItemIntoQueue(&tasks[taskId]);
	}
	return taskId;
}

void TaskManager::cancelTask(uint8_t task) {
	if (task < numberOfSlots) {
		tasks[task].clear();
		removeFromQueue(&tasks[task]);
	}
}

char* TaskManager::checkAvailableSlots(char* data) {
	uint8_t i;
	for (i = 0; i < numberOfSlots; ++i) {
		data[i] = tasks[i].isRepeating() ? 'R' : (tasks[i].isInUse() ? 'U' : 'F');
		if (tasks[i].isRunning()) data[i] = tolower(data[i]);
	}
	data[i] = 0;
	return data;
}

void TaskManager::yieldForMicros(uint16_t microsToWait) {
	unsigned long microsEnd = micros() + microsToWait;

	while(micros() < microsEnd) {
		runLoop();
	}
}

void TaskManager::runLoop() {
	if (interrupted) {
		interrupted = false;
		interruptCallback(lastInterruptTrigger);
	}

	TimerTask* tm = first;
	while(tm != NULL) {
		if (tm->isReady()) {
			removeFromQueue(tm);
			tm->execute();
			if (tm->isRepeating()) {
				putItemIntoQueue(tm);
			}
		}
		else {
			break;
		}

		tm = tm->getNext();
	}

	// TODO, if not interrupted, and no task for a while sleep and set timer - go into low power.
}

void TaskManager::putItemIntoQueue(TimerTask* tm) {

	// shortcut, no first yet, so we are at the top!
	if (first == NULL) {
		first = tm;
		tm->setNext(NULL);
		return;
	}

	// if we are the new first..
	if (first->microsFromNow() > tm->microsFromNow()) {
		tm->setNext(first);
		first = tm;
		return;
	}

	// otherwise we have to find the place in the queue for this item by time
	TimerTask* current = first->getNext();
	TimerTask* previous = first;

	while (current != NULL) {
		if (current->microsFromNow() > tm->microsFromNow()) {
			previous->setNext(tm);
			tm->setNext(current);
			return;
		}
		previous = current;
		current = current->getNext();
	}

	// we are at the end of the queue
	previous->setNext(tm);
	tm->setNext(NULL);
}

void TaskManager::removeFromQueue(TimerTask* tm) {

	// shortcut, if we are first, just remove us by getting the next and setting first.
	if (first == tm) {
		first = tm->getNext();
		return;
	}

	// otherwise, we have a single linked list, so we need to keep previous and current and
	// then iterate through each item
	TimerTask* current = first->getNext();
	TimerTask* previous = first;

	while (current != NULL) {

		// we've found the item, unlink it from the queue and nullify its next.
		if (current == tm) {
			previous->setNext(current->getNext());
			current->setNext(NULL);
			break;
		}

		previous = current;
		current = current->getNext();
	}
}

typedef void ArdunioIntFn(void);

void interruptHandler1() {
	taskManager.markInterrupted(1);
}
void interruptHandler2() {
	taskManager.markInterrupted(2);
}
void interruptHandler3() {
	taskManager.markInterrupted(3);
}
void interruptHandler5() {
	taskManager.markInterrupted(5);
}
void interruptHandler18() {
	taskManager.markInterrupted(18);
}
void interruptHandlerOther() {
	taskManager.markInterrupted(0xff);
}

void TaskManager::addInterrupt(uint8_t pin, uint8_t mode) {
	if (interruptCallback == NULL) return;
	int interruptNo = digitalPinToInterrupt(pin);

	switch (pin) {
	case 1:  attachInterrupt(interruptNo, interruptHandler1, mode); break;
	case 2: attachInterrupt(interruptNo, interruptHandler2, mode); break;
	case 3: attachInterrupt(interruptNo, interruptHandler3, mode); break;
	case 5: attachInterrupt(interruptNo, interruptHandler5, mode); break;
	case 18: attachInterrupt(interruptNo, interruptHandler18, mode); break;
	default: attachInterrupt(interruptNo, interruptHandlerOther, mode); break;
	}
}

void TaskManager::setInterruptCallback(InterruptFn handler) {
	interruptCallback = handler;
}

<commit_msg>Ensure system yield is called during waitForMicros call<commit_after>/*
 * Copyright (c) 2018 https://www.thecoderscorner.com (Nutricherry LTD).
 * This product is licensed under an Apache license, see the LICENSE file in the top-level directory.
 */

#include <inttypes.h>
#include <Arduino.h>
#include "TaskManager.h"

TaskManager taskManager;

TimerTask::TimerTask() {
	next = NULL;
	clear();
}

void TimerTask::initialise(uint16_t executionInfo, TimerFn execCallback) {
	this->executionInfo = executionInfo;
	this->callback = execCallback;

	this->scheduledAt = (executionInfo & TASK_MICROS) ? micros() : millis();
}

bool TimerTask::isReady() {
	if (!isInUse() || isRunning()) return false;

	if ((executionInfo & TASK_MICROS) != 0) {
		uint16_t delay = (executionInfo & TIMER_MASK);
		return (micros() - scheduledAt) >= delay;
	}
	else if((executionInfo & TASK_SECONDS) != 0) {
		uint32_t delay = (executionInfo & TIMER_MASK) * 1000L;
		return (millis() - scheduledAt) >= delay;
	}
	else {
		uint16_t delay = (executionInfo & TIMER_MASK);
		return (millis() - scheduledAt) >= delay;
	}
}

unsigned long TimerTask::microsFromNow() {
	uint32_t microsFromNow;
	if ((executionInfo & TASK_MICROS) != 0) {
		uint16_t delay = (executionInfo & TIMER_MASK);
		microsFromNow =  delay - (micros() - scheduledAt);
	}
	else {
		uint32_t startTm = (executionInfo & TIMER_MASK);
		if ((executionInfo & TASK_SECONDS) != 0) {
			startTm *= 1000;
		}
		microsFromNow = (startTm - (millis() - scheduledAt)) * 1000;
	}
	return microsFromNow;
}

inline void TimerTask::execute() {
	if (callback == NULL) return; // failsafe, always exit with null callback.

	// handle repeating tasks - reschedule.
	if (isRepeating()) {
		markRunning();
		callback();
		this->scheduledAt = (executionInfo & TASK_MICROS) ? micros() : millis();
		clearRunning();
	}
	else {
		TimerFn savedCallback = callback;
		clear();
		savedCallback();
	}
}

void TimerTask::clear() {
	executionInfo = 0;
	callback = NULL;
}

void TaskManager::markInterrupted(uint8_t interruptNo) {
	taskManager.lastInterruptTrigger = interruptNo;
	taskManager.interrupted = true;
}

TaskManager::TaskManager(uint8_t taskSlots) {
	this->numberOfSlots = taskSlots;
	this->tasks = new TimerTask[taskSlots];
	interrupted = false;
	first = NULL;
	interruptCallback = NULL;
}

int TaskManager::findFreeTask() {
	for (uint8_t i = 0; i < numberOfSlots; ++i) {
		if (!tasks[i].isInUse()) {
			return i;
		}
	}
	return TASKMGR_INVALIDID;
}

inline int toTimerValue(int v, TimerUnit unit) {
	if (unit == TIME_MILLIS && v > TIMER_MASK) {
		unit = TIME_SECONDS;
		v = v / 1000;
	}
	v = min(v, TIMER_MASK);
	return v | (((uint16_t)unit) << 12);
}

uint8_t TaskManager::scheduleOnce(int millis, TimerFn timerFunction, TimerUnit timeUnit) {
	uint8_t taskId = findFreeTask();
	if (taskId != TASKMGR_INVALIDID) {
		tasks[taskId].initialise(toTimerValue(millis, timeUnit) | TASK_IN_USE, timerFunction);
		putItemIntoQueue(&tasks[taskId]);
	}
	return taskId;
}

uint8_t TaskManager::scheduleFixedRate(int millis, TimerFn timerFunction, TimerUnit timeUnit) {
	uint8_t taskId = findFreeTask();
	if (taskId != TASKMGR_INVALIDID) {
		tasks[taskId].initialise(toTimerValue(millis, timeUnit) | TASK_IN_USE | TASK_REPEATING, timerFunction);
		putItemIntoQueue(&tasks[taskId]);
	}
	return taskId;
}

void TaskManager::cancelTask(uint8_t task) {
	if (task < numberOfSlots) {
		tasks[task].clear();
		removeFromQueue(&tasks[task]);
	}
}

char* TaskManager::checkAvailableSlots(char* data) {
	uint8_t i;
	for (i = 0; i < numberOfSlots; ++i) {
		data[i] = tasks[i].isRepeating() ? 'R' : (tasks[i].isInUse() ? 'U' : 'F');
		if (tasks[i].isRunning()) data[i] = tolower(data[i]);
	}
	data[i] = 0;
	return data;
}

void TaskManager::yieldForMicros(uint16_t microsToWait) {
	yield();

	unsigned long microsEnd = micros() + microsToWait;
	while(micros() < microsEnd) {
		runLoop();
	}
}

void TaskManager::runLoop() {
	if (interrupted) {
		interrupted = false;
		interruptCallback(lastInterruptTrigger);
	}

	TimerTask* tm = first;
	while(tm != NULL) {
		if (tm->isReady()) {
			removeFromQueue(tm);
			tm->execute();
			if (tm->isRepeating()) {
				putItemIntoQueue(tm);
			}
		}
		else {
			break;
		}

		tm = tm->getNext();
	}

	// TODO, if not interrupted, and no task for a while sleep and set timer - go into low power.
}

void TaskManager::putItemIntoQueue(TimerTask* tm) {

	// shortcut, no first yet, so we are at the top!
	if (first == NULL) {
		first = tm;
		tm->setNext(NULL);
		return;
	}

	// if we are the new first..
	if (first->microsFromNow() > tm->microsFromNow()) {
		tm->setNext(first);
		first = tm;
		return;
	}

	// otherwise we have to find the place in the queue for this item by time
	TimerTask* current = first->getNext();
	TimerTask* previous = first;

	while (current != NULL) {
		if (current->microsFromNow() > tm->microsFromNow()) {
			previous->setNext(tm);
			tm->setNext(current);
			return;
		}
		previous = current;
		current = current->getNext();
	}

	// we are at the end of the queue
	previous->setNext(tm);
	tm->setNext(NULL);
}

void TaskManager::removeFromQueue(TimerTask* tm) {

	// shortcut, if we are first, just remove us by getting the next and setting first.
	if (first == tm) {
		first = tm->getNext();
		return;
	}

	// otherwise, we have a single linked list, so we need to keep previous and current and
	// then iterate through each item
	TimerTask* current = first->getNext();
	TimerTask* previous = first;

	while (current != NULL) {

		// we've found the item, unlink it from the queue and nullify its next.
		if (current == tm) {
			previous->setNext(current->getNext());
			current->setNext(NULL);
			break;
		}

		previous = current;
		current = current->getNext();
	}
}

typedef void ArdunioIntFn(void);

void interruptHandler1() {
	taskManager.markInterrupted(1);
}
void interruptHandler2() {
	taskManager.markInterrupted(2);
}
void interruptHandler3() {
	taskManager.markInterrupted(3);
}
void interruptHandler5() {
	taskManager.markInterrupted(5);
}
void interruptHandler18() {
	taskManager.markInterrupted(18);
}
void interruptHandlerOther() {
	taskManager.markInterrupted(0xff);
}

void TaskManager::addInterrupt(uint8_t pin, uint8_t mode) {
	if (interruptCallback == NULL) return;
	int interruptNo = digitalPinToInterrupt(pin);

	switch (pin) {
	case 1:  attachInterrupt(interruptNo, interruptHandler1, mode); break;
	case 2: attachInterrupt(interruptNo, interruptHandler2, mode); break;
	case 3: attachInterrupt(interruptNo, interruptHandler3, mode); break;
	case 5: attachInterrupt(interruptNo, interruptHandler5, mode); break;
	case 18: attachInterrupt(interruptNo, interruptHandler18, mode); break;
	default: attachInterrupt(interruptNo, interruptHandlerOther, mode); break;
	}
}

void TaskManager::setInterruptCallback(InterruptFn handler) {
	interruptCallback = handler;
}

<|endoftext|>


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

#include "buffer_pool.hpp"
#include "device.hpp"
#include <utility>

using namespace std;

namespace Vulkan
{
void BufferPool::init(Device *device_, VkDeviceSize block_size_,
                      VkDeviceSize alignment_, VkBufferUsageFlags usage_,
                      bool need_device_local_)
{
	device = device_;
	block_size = block_size_;
	alignment = alignment_;
	usage = usage_;
	need_device_local = need_device_local_;
}

void BufferPool::set_spill_region_size(VkDeviceSize spill_size_)
{
	spill_size = spill_size_;
}

BufferBlock::~BufferBlock()
{
}

void BufferPool::reset()
{
	blocks.clear();
}

BufferBlock BufferPool::allocate_block(VkDeviceSize size)
{
	BufferDomain ideal_domain = need_device_local ? BufferDomain::Device : BufferDomain::Host;
	VkBufferUsageFlags extra_usage = ideal_domain == BufferDomain::Device ? VK_BUFFER_USAGE_TRANSFER_DST_BIT : 0;

	BufferBlock block;

	BufferCreateInfo info;
	info.domain = ideal_domain;
	info.size = size;
	info.usage = usage | extra_usage;

	block.gpu = device->create_buffer(info, nullptr);
	device->set_name(*block.gpu, "chain-allocated-block-gpu");
	block.gpu->set_internal_sync_object();

	// Try to map it, will fail unless the memory is host visible.
	block.mapped = static_cast<uint8_t *>(device->map_host_buffer(*block.gpu, MEMORY_ACCESS_WRITE_BIT));
	if (!block.mapped)
	{
		// Fall back to host memory, and remember to sync to gpu on submission time using DMA queue. :)
		BufferCreateInfo cpu_info;
		cpu_info.domain = BufferDomain::Host;
		cpu_info.size = size;
		cpu_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;

		block.cpu = device->create_buffer(cpu_info, nullptr);
		block.cpu->set_internal_sync_object();
		device->set_name(*block.cpu, "chain-allocated-block-cpu");
		block.mapped = static_cast<uint8_t *>(device->map_host_buffer(*block.cpu, MEMORY_ACCESS_WRITE_BIT));
	}
	else
		block.cpu = block.gpu;

	block.offset = 0;
	block.alignment = alignment;
	block.size = size;
	block.spill_size = spill_size;
	return block;
}

BufferBlock BufferPool::request_block(VkDeviceSize minimum_size)
{
	if ((minimum_size > block_size) || blocks.empty())
	{
		return allocate_block(max(block_size, minimum_size));
	}
	else
	{
		auto back = move(blocks.back());
		blocks.pop_back();

		back.mapped = static_cast<uint8_t *>(device->map_host_buffer(*back.cpu, MEMORY_ACCESS_WRITE_BIT));
		back.offset = 0;
		return back;
	}
}

void BufferPool::recycle_block(BufferBlock &&block)
{
	VK_ASSERT(block.size == block_size);
	blocks.push_back(move(block));
}

BufferPool::~BufferPool()
{
	VK_ASSERT(blocks.empty());
}

}
<commit_msg>Prefer LinkedDeviceHost domain for streamed VBO/IBO/UBO.<commit_after>/* Copyright (c) 2017-2020 Hans-Kristian Arntzen
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "buffer_pool.hpp"
#include "device.hpp"
#include <utility>

using namespace std;

namespace Vulkan
{
void BufferPool::init(Device *device_, VkDeviceSize block_size_,
                      VkDeviceSize alignment_, VkBufferUsageFlags usage_,
                      bool need_device_local_)
{
	device = device_;
	block_size = block_size_;
	alignment = alignment_;
	usage = usage_;
	need_device_local = need_device_local_;
}

void BufferPool::set_spill_region_size(VkDeviceSize spill_size_)
{
	spill_size = spill_size_;
}

BufferBlock::~BufferBlock()
{
}

void BufferPool::reset()
{
	blocks.clear();
}

BufferBlock BufferPool::allocate_block(VkDeviceSize size)
{
	BufferDomain ideal_domain = need_device_local ?
	                            BufferDomain::Device :
	                            ((usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT) != 0) ? BufferDomain::Host : BufferDomain::LinkedDeviceHost;

	VkBufferUsageFlags extra_usage = ideal_domain == BufferDomain::Device ? VK_BUFFER_USAGE_TRANSFER_DST_BIT : 0;

	BufferBlock block;

	BufferCreateInfo info;
	info.domain = ideal_domain;
	info.size = size;
	info.usage = usage | extra_usage;

	block.gpu = device->create_buffer(info, nullptr);
	device->set_name(*block.gpu, "chain-allocated-block-gpu");
	block.gpu->set_internal_sync_object();

	// Try to map it, will fail unless the memory is host visible.
	block.mapped = static_cast<uint8_t *>(device->map_host_buffer(*block.gpu, MEMORY_ACCESS_WRITE_BIT));
	if (!block.mapped)
	{
		// Fall back to host memory, and remember to sync to gpu on submission time using DMA queue. :)
		BufferCreateInfo cpu_info;
		cpu_info.domain = BufferDomain::Host;
		cpu_info.size = size;
		cpu_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;

		block.cpu = device->create_buffer(cpu_info, nullptr);
		block.cpu->set_internal_sync_object();
		device->set_name(*block.cpu, "chain-allocated-block-cpu");
		block.mapped = static_cast<uint8_t *>(device->map_host_buffer(*block.cpu, MEMORY_ACCESS_WRITE_BIT));
	}
	else
		block.cpu = block.gpu;

	block.offset = 0;
	block.alignment = alignment;
	block.size = size;
	block.spill_size = spill_size;
	return block;
}

BufferBlock BufferPool::request_block(VkDeviceSize minimum_size)
{
	if ((minimum_size > block_size) || blocks.empty())
	{
		return allocate_block(max(block_size, minimum_size));
	}
	else
	{
		auto back = move(blocks.back());
		blocks.pop_back();

		back.mapped = static_cast<uint8_t *>(device->map_host_buffer(*back.cpu, MEMORY_ACCESS_WRITE_BIT));
		back.offset = 0;
		return back;
	}
}

void BufferPool::recycle_block(BufferBlock &&block)
{
	VK_ASSERT(block.size == block_size);
	blocks.push_back(move(block));
}

BufferPool::~BufferPool()
{
	VK_ASSERT(blocks.empty());
}

}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <string>
#include <RakPeer.h>
#include <set>
#include <thread>
#include <map>
#include <vector>
#include <random>
#include <algorithm>
#undef REGISTERED
#include "../Core/common.cpp"
using namespace gameplay;
#include "../Core/Message.h"
using namespace std::literals;
#undef max
#undef min

class AI final {
private:
    enum class Type {
        attack, defense, discover
    };
    std::map<std::string, Type> mUnits;
    using SendFunc = std::function<void(const RakNet::BitStream&, PacketPriority, PacketReliability)>;
    SendFunc mSend;
    std::vector<Vector2> mKeyPoint;
    std::map<uint32_t, UnitSyncInfo> mMine;
    std::map<uint32_t, UnitSyncInfo> mArmies;
    uint32_t mGroup;
    float mValue;
    Type mStep = Type::defense;
    struct TeamInfo final {
        std::set<uint32_t> current;
        uint32_t size;
        Vector2 object;
    };
    std::vector<TeamInfo> mTeams;
    std::vector<uint32_t> mFree;
    Vector2 mRoot;
public:
    AI() :mValue(0.0f) {
        std::vector<std::string> units;
        listDirs("res/units", units);
        for (auto&& x : units) {
            uniqueRAII<Properties> info = Properties::create(("res/units/" + x + "/unit.info").c_str());
            auto tname = info->getString("AIType", "discover");
            if (tname == "attack")mUnits[x] = Type::attack;
            else if (tname == "defense")mUnits[x] = Type::defense;
            else mUnits[x] = Type::discover;
        }
    }

    void connect(const SendFunc& send, const std::string& map, uint8_t group) {
        mSend = send;
        uniqueRAII<Properties> info = Properties::create(("res/maps/" + map + "/map.info").c_str());
        const char* id;
        Vector2 tmp;
        while ((id = info->getNextProperty()) && info->getVector2(id, &tmp))
            mKeyPoint.emplace_back(tmp / 512.0f* 10000.0f - Vector2{ 5000.0f, 5000.0f });
        mGroup = group;
    }

    void setRoot(const Vector2 root) {
        mRoot = root;
    }

    void updateUnit(const UnitSyncInfo& info) {
        if (info.group == mGroup)
            mMine[info.id] = info;
        else
            mArmies[info.id] = info;
    }

    void newUnit(const UnitSyncInfo& info) {
        updateUnit(info);
        mValue += (info.group == mGroup) ? 0.5f : -1.0f;
        if(info.group==mGroup)
            mFree.emplace_back(info.id);
    }

    void deleteUnit(uint32_t id) {
        if (mMine.find(id) != mMine.cend())
            mMine.erase(id), --mValue;
        else
            mArmies.erase(id), mValue += 0.5f;
    }

    void send(const TeamInfo& info) {
        RakNet::BitStream data;
        data.Write(ClientMessage::setMoveTarget);
        data.Write(info.object);
        for (auto&& x : info.current)
            data.Write(x);
        mSend(data, PacketPriority::HIGH_PRIORITY, PacketReliability::RELIABLE_ORDERED);
    }

    void send(uint16_t id, uint16_t weight) {
        RakNet::BitStream data;
        data.Write(ClientMessage::changeWeight);
        data.Write(id);
        data.Write(weight);
        mSend(data, PacketPriority::MEDIUM_PRIORITY, PacketReliability::RELIABLE);
    }

    void clearTeams(){
        for (auto&& x : mTeams)
            for (auto&& u : x.current)
                if (mMine.find(u) != mMine.cend())
                    mFree.emplace_back(u);
        mTeams.clear();
    }

    void update() {

        auto old = mStep;
        //discover
        if (mValue > -20.0f || mArmies.empty())mStep = Type::discover;
        //attack
        if (mValue > 100.0f || (mArmies.size() &&
            mArmies.size() * 5 < mMine.size())) mStep = Type::attack;
        //defense
        if (mValue<-10.0f || mArmies.size()>0.2*mMine.size())mStep = Type::defense;

        if (old != mStep) {
            std::cout << "Switch state : ";
            switch (mStep) {
            case Type::attack:std::cout << "attack";
                break;
            case Type::defense:std::cout << "defense";
                break;
            case Type::discover:std::cout << "discover";
                break;
            default:
                break;
            }
            std::cout << std::endl;
            uint16_t id=0;
            for (auto&& x : mUnits) {
                if (x.second == mStep)
                    send(id, 1000);
                ++id;
            }
            clearTeams();

            switch (mStep) {
            case Type::attack:
            {
            }
            break;
            case Type::defense:
            {
                TeamInfo team;
                team.object = mRoot;
                team.size = std::numeric_limits<uint32_t>::max();
                send(team);
                mTeams.emplace_back(team);
                uint16_t id = 0;
                for (auto&& x : mUnits) {
                    if (x.second != Type::defense)
                        send(id, 1);
                    ++id;
                }
            }
            break;
            case Type::discover:
            {
                for (auto&& x : mKeyPoint) {
                    TeamInfo team;
                    team.size = 7;
                    team.object = x;
                    mTeams.emplace_back(team);
                }
            }
            break;
            default:
                break;
            }
        }

        if (mStep == Type::attack) {
            clearTeams();

            std::map<Vector2, uint32_t> find;
            for (auto&& x : mArmies) {
                auto&& info = x.second;
                if (info.group != mGroup) {
                    Vector2 p{ info.pos.x,info.pos.z };
                    bool flag = true;
                    for (auto&& x : find) {
                        if (x.first.distanceSquared(p) < 90000.0f) {
                            ++x.second, flag = false;
                            break;
                        }
                    }
                    if (flag)find[p] = 1;
                }
            }

            for (auto&& x : find) {
                TeamInfo team;
                team.size = x.second*1.5;
                team.object = x.first;
            }
        }

        std::remove_if(mFree.begin(), mFree.end(),
            [this](uint32_t id) {return mMine.find(id) == mMine.cend(); });

        if (mStep == Type::discover && mFree.size() > 5) {
            TeamInfo team;
            team.size = 3;
            team.object = { mt() % 10000 - 5000.0f,mt() % 10000 - 5000.0f };
        }

        for (auto&& x : mTeams) {
            if (mFree.empty())continue;
            std::set<uint32_t> deferred;
            for (auto&& id : x.current)
                if (mMine.find(id) == mMine.cend())
                    deferred.insert(id);
            for (auto&& y : deferred)
                x.current.erase(y);
            uint32_t size = std::min(mFree.size(), x.size - x.current.size());
            std::partial_sort(mFree.begin(), mFree.begin() + size, mFree.end(),
                [this,&x](uint32_t a,uint32_t b) {
                return x.object.distanceSquared({ mMine[a].pos.x,mMine[a].pos.z }) >
                    x.object.distanceSquared({ mMine[b].pos.x,mMine[b].pos.z });
            });
            for (auto i = 0; i < size; ++i)
                x.current.insert(*(mFree.rbegin() + i));
            mFree.resize(mFree.size() - size);
            send(x);
        }

        std::map<uint32_t, uint32_t> attackMap;
        for (auto&& x : mMine) {
            float md = std::numeric_limits<float>::max();
            uint32_t maxwell=0;
            for (auto&& y : mArmies) {
                auto dis = y.second.pos.distanceSquared(x.second.pos);
                if (dis < md)md = dis, maxwell = y.first;
            }
            if (maxwell) attackMap[x.first] = maxwell;
        }
        if (attackMap.size()) {
            RakNet::BitStream data;
            data.Write(ClientMessage::setAttackTarget);
            for (auto&& x : attackMap){
                data.Write(x.first);
                data.Write(x.second);
            }
            mSend(data, PacketPriority::HIGH_PRIORITY, PacketReliability::RELIABLE);
        }

    }
} tinyAI;

#define FORNET for (auto packet = peer.Receive(); packet; peer.DeallocatePacket(packet), packet = peer.Receive())

int main() {
    RakNet::RakPeer peer;
    RakNet::SocketDescriptor SD;
    peer.Startup(1, &SD, 1);
    std::cout << "Input group:" << std::endl;
    uint16_t group;
    std::cin >> group;
    std::cout << "Scanning servers..." << std::endl;
    std::string str;
    std::set<RakNet::SystemAddress> ban;
    RakNet::SystemAddress server;
    while (true) {
        peer.Ping("255.255.255.255", 23333, false);
        FORNET{
            if (packet->data[0] == ID_UNCONNECTED_PONG &&
            ban.find(packet->systemAddress) == ban.cend()) {
                std::cout << "Find server " << packet->systemAddress.ToString() << std::endl;
                std::cout << "Shall we connect it?(y/n)" << std::endl;
                char c;
                std::cin >> c;
                if (c == 'y') {
                    server = packet->systemAddress;
                    auto res = peer.Connect(server.ToString(false), 23333, nullptr,0);
                    if (res == RakNet::CONNECTION_ATTEMPT_STARTED) {
                        while (peer.NumberOfConnections() != 1)
                            std::this_thread::sleep_for(1ms);
                        std::cout << "OK!" << std::endl;
                        peer.DeallocatePacket(packet);
                        goto p1;
                    }
                }
                else ban.insert(packet->systemAddress);
            }
        }
        std::this_thread::sleep_for(1ms);
    }
p1:
    {
        RakNet::BitStream data;
        data.Write(ClientMessage::changeGroup);
        data.Write(static_cast<uint8_t>(group));
        peer.Send(&data, PacketPriority::IMMEDIATE_PRIORITY,
            PacketReliability::RELIABLE_ORDERED, 0, server, false);
    }

    while (true) {
        FORNET{
            CheckBegin;
            CheckHeader(ServerMessage::info) {
                RakNet::BitStream data(packet->data, packet->length,false);
                data.IgnoreBytes(1 + sizeof(uint64_t));
                RakNet::RakString str;
                data.Read(str);
                tinyAI.connect([&](const RakNet::BitStream& data, PacketPriority pp, PacketReliability pr) {
                    peer.Send(&data, pp, pr, 0, server, false);
                },str.C_String(),group);
            }
            CheckHeader(ServerMessage::go) {
                RakNet::BitStream data(packet->data, packet->length,false);
                data.IgnoreBytes(1);
                Vector2 p;
                data.Read(p);
                tinyAI.setRoot(p);
                std::cout << "Go!" << std::endl;
                goto p2;
            }
        }
        std::this_thread::sleep_for(1ms);
    }
p2:
    struct UnitInfo final {
        uint32_t id;
        Vector3 pos;
        bool operator<(const UnitInfo& rhs) const {
            return id < rhs.id;
        }
    };

    bool isStop = false;
    std::set<uint32_t> old;

    auto begin = std::chrono::system_clock::now();
    while (true) {
        std::vector<unsigned char> latestData;
        FORNET{
            if (isStop)continue;
            RakNet::BitStream data(packet->data, packet->length, false);
            data.IgnoreBytes(1);
            CheckBegin;
            CheckHeader(ServerMessage::out) {
                std::cout << "What a pity!" << std::endl;
                isStop = true;
                continue;
            }
            CheckHeader(ServerMessage::stop) {
                std::cout << "The game stopped." << std::endl;
                isStop = true;
                continue;
            }
            CheckHeader(ServerMessage::win) {
                std::cout << "We are winner!" << std::endl;
                isStop = true;
                continue;
            }
            CheckHeader(ServerMessage::updateUnit) {
                latestData = std::vector<unsigned char>(packet->data, packet->data + packet->length);
            }
        }
            if (isStop ||
                peer.GetConnectionState(server) != RakNet::ConnectionState::IS_CONNECTED)break;
        if (latestData.empty())continue;

        uint32_t mine = 0, armies = 0;
        std::set<uint32_t> copy = old;

        RakNet::BitStream latest(latestData.data(), latestData.size(), false);
        latest.IgnoreBytes(1);
        uint32_t size;
        latest.Read(size);
        for (uint32_t i = 0; i < size; ++i) {
            UnitSyncInfo u;
            latest.Read(u);
            auto iter = copy.find(u.id);
            if (iter == copy.cend()) {
                old.insert(u.id);
                tinyAI.newUnit(u);
            }
            else {
                copy.erase(iter);
                tinyAI.updateUnit(u);
            }
            if (u.group == group)
                ++mine;
            else
                ++armies;
        }

        for (auto&& x : copy)
            tinyAI.deleteUnit(x);

        tinyAI.update();

        std::cout << "mine:" << mine << " armies:" << armies << std::endl;
        std::this_thread::sleep_for(1s);
    }

    auto second = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - begin).count();
    std::cout << "time:" << second / 60 << ":" << second % 60 << std::endl;

    peer.Shutdown(500, 0, IMMEDIATE_PRIORITY);
    std::cin.get();
    std::cin.get();
    return 0;
}

#undef FORNET
<commit_msg>fix bugs<commit_after>#include <iostream>
#include <fstream>
#include <string>
#include <RakPeer.h>
#include <set>
#include <thread>
#include <map>
#include <vector>
#include <random>
#include <algorithm>
#undef REGISTERED
#include "../Core/common.cpp"
using namespace gameplay;
#include "../Core/Message.h"
using namespace std::literals;
#undef max
#undef min

class AI final {
private:
    enum class Type {
        attack, defense, discover
    };
    std::map<std::string, Type> mUnits;
    using SendFunc = std::function<void(const RakNet::BitStream&, PacketPriority, PacketReliability)>;
    SendFunc mSend;
    std::vector<Vector2> mKeyPoint;
    std::map<uint32_t, UnitSyncInfo> mMine;
    std::map<uint32_t, UnitSyncInfo> mArmies;
    uint32_t mGroup;
    float mValue;
    Type mStep = Type::defense;
    struct TeamInfo final {
        std::set<uint32_t> current;
        uint32_t size;
        Vector2 object;
    };
    std::vector<TeamInfo> mTeams;
    std::vector<uint32_t> mFree;
    Vector2 mRoot;
public:
    AI() :mValue(0.0f) {
        std::vector<std::string> units;
        listDirs("res/units", units);
        for (auto&& x : units) {
            uniqueRAII<Properties> info = Properties::create(("res/units/" + x + "/unit.info").c_str());
            auto tname = info->getString("AIType", "discover");
            if (tname == "attack")mUnits[x] = Type::attack;
            else if (tname == "defense")mUnits[x] = Type::defense;
            else mUnits[x] = Type::discover;
        }
    }

    void connect(const SendFunc& send, const std::string& map, uint8_t group) {
        mSend = send;
        uniqueRAII<Properties> info = Properties::create(("res/maps/" + map + "/map.info").c_str());
        const char* id;
        Vector2 tmp;
        while ((id = info->getNextProperty()) && info->getVector2(id, &tmp))
            mKeyPoint.emplace_back(tmp / 512.0f* 10000.0f - Vector2{ 5000.0f, 5000.0f });
        mGroup = group;
    }

    void setRoot(const Vector2 root) {
        mRoot = root;
    }

    void updateUnit(const UnitSyncInfo& info) {
        if (info.group == mGroup)
            mMine[info.id] = info;
        else
            mArmies[info.id] = info;
    }

    void newUnit(const UnitSyncInfo& info) {
        updateUnit(info);
        mValue += (info.group == mGroup) ? 0.5f : -1.0f;
        if(info.group==mGroup)
            mFree.emplace_back(info.id);
    }

    void deleteUnit(uint32_t id) {
        if (mMine.find(id) != mMine.cend())
            mMine.erase(id), --mValue;
        else
            mArmies.erase(id), mValue += 0.5f;
    }

    void send(const TeamInfo& info) {
        RakNet::BitStream data;
        data.Write(ClientMessage::setMoveTarget);
        data.Write(info.object);
        for (auto&& x : info.current)
            data.Write(x);
        mSend(data, PacketPriority::HIGH_PRIORITY, PacketReliability::RELIABLE_ORDERED);
    }

    void send(uint16_t id, uint16_t weight) {
        RakNet::BitStream data;
        data.Write(ClientMessage::changeWeight);
        data.Write(id);
        data.Write(weight);
        mSend(data, PacketPriority::MEDIUM_PRIORITY, PacketReliability::RELIABLE);
    }

    void clearTeams(){
        for (auto&& x : mTeams)
            for (auto&& u : x.current)
                if (mMine.find(u) != mMine.cend())
                    mFree.emplace_back(u);
        mTeams.clear();
    }

    void update() {

        auto old = mStep;
        //discover
        if (mValue > -20.0f || mArmies.empty())mStep = Type::discover;
        //attack
        if (mValue > 100.0f || (mArmies.size() &&
            mArmies.size() * 5 < mMine.size())) mStep = Type::attack;
        //defense
        if (mValue<-10.0f || mArmies.size()>0.2*mMine.size())mStep = Type::defense;

        if (old != mStep) {
            std::cout << "Switch state : ";
            switch (mStep) {
            case Type::attack:std::cout << "attack";
                break;
            case Type::defense:std::cout << "defense";
                break;
            case Type::discover:std::cout << "discover";
                break;
            default:
                break;
            }
            std::cout << std::endl;
            uint16_t id=0;
            for (auto&& x : mUnits) {
                if (x.second == mStep)
                    send(id, 1000);
                ++id;
            }
            clearTeams();

            switch (mStep) {
            case Type::attack:
            {
            }
            break;
            case Type::defense:
            {
                TeamInfo team;
                team.object = mRoot;
                team.size = std::numeric_limits<uint32_t>::max();
                send(team);
                mTeams.emplace_back(team);
                uint16_t id = 0;
                for (auto&& x : mUnits) {
                    if (x.second != Type::defense)
                        send(id, 1);
                    ++id;
                }
            }
            break;
            case Type::discover:
            {
                for (auto&& x : mKeyPoint) {
                    TeamInfo team;
                    team.size = 3;
                    team.object = x;
                    mTeams.emplace_back(team);
                }
            }
            break;
            default:
                break;
            }
        }

        if (mStep == Type::attack) {
            clearTeams();

            std::map<Vector2, uint32_t> find;
            for (auto&& x : mArmies) {
                auto&& info = x.second;
                if (info.group != mGroup) {
                    Vector2 p{ info.pos.x,info.pos.z };
                    bool flag = true;
                    for (auto&& x : find) {
                        if (x.first.distanceSquared(p) < 90000.0f) {
                            ++x.second, flag = false;
                            break;
                        }
                    }
                    if (flag)find[p] = 1;
                }
            }

            for (auto&& x : find) {
                TeamInfo team;
                team.size = x.second*1.5;
                team.object = x.first;
                mTeams.emplace_back(team);
            }
        }

        std::remove_if(mFree.begin(), mFree.end(),
            [this](uint32_t id) {return mMine.find(id) == mMine.cend(); });

        if (mStep == Type::discover && mFree.size() > 5) {
            TeamInfo team;
            team.size = 3;
            team.object = { mt() % 10000 - 5000.0f,mt() % 10000 - 5000.0f };
            mTeams.emplace_back(team);
        }

        for (auto&& x : mTeams) {
            if (mFree.empty())continue;
            std::set<uint32_t> deferred;
            for (auto&& id : x.current)
                if (mMine.find(id) == mMine.cend())
                    deferred.insert(id);
            for (auto&& y : deferred)
                x.current.erase(y);
            uint32_t size = std::min(mFree.size(), x.size - x.current.size());
            std::partial_sort(mFree.begin(), mFree.begin() + size, mFree.end(),
                [this,&x](uint32_t a,uint32_t b) {
                return x.object.distanceSquared({ mMine[a].pos.x,mMine[a].pos.z }) >
                    x.object.distanceSquared({ mMine[b].pos.x,mMine[b].pos.z });
            });
            for (auto i = 0; i < size; ++i)
                x.current.insert(*(mFree.rbegin() + i));
            mFree.resize(mFree.size() - size);
            send(x);
        }

        std::map<uint32_t, uint32_t> attackMap;
        for (auto&& x : mMine) {
            float md = std::numeric_limits<float>::max();
            uint32_t maxwell=0;
            for (auto&& y : mArmies) {
                auto dis = y.second.pos.distanceSquared(x.second.pos);
                if (dis < md)md = dis, maxwell = y.first;
            }
            if (maxwell) attackMap[x.first] = maxwell;
        }
        if (attackMap.size()) {
            RakNet::BitStream data;
            data.Write(ClientMessage::setAttackTarget);
            for (auto&& x : attackMap){
                data.Write(x.first);
                data.Write(x.second);
            }
            mSend(data, PacketPriority::HIGH_PRIORITY, PacketReliability::RELIABLE);
        }

    }
} tinyAI;

#define FORNET for (auto packet = peer.Receive(); packet; peer.DeallocatePacket(packet), packet = peer.Receive())

int main() {
    RakNet::RakPeer peer;
    RakNet::SocketDescriptor SD;
    peer.Startup(1, &SD, 1);
    std::cout << "Input group:" << std::endl;
    uint16_t group;
    std::cin >> group;
    std::cout << "Scanning servers..." << std::endl;
    std::string str;
    std::set<RakNet::SystemAddress> ban;
    RakNet::SystemAddress server;
    while (true) {
        peer.Ping("255.255.255.255", 23333, false);
        FORNET{
            if (packet->data[0] == ID_UNCONNECTED_PONG &&
            ban.find(packet->systemAddress) == ban.cend()) {
                std::cout << "Find server " << packet->systemAddress.ToString() << std::endl;
                std::cout << "Shall we connect it?(y/n)" << std::endl;
                char c;
                std::cin >> c;
                if (c == 'y') {
                    server = packet->systemAddress;
                    auto res = peer.Connect(server.ToString(false), 23333, nullptr,0);
                    if (res == RakNet::CONNECTION_ATTEMPT_STARTED) {
                        while (peer.NumberOfConnections() != 1)
                            std::this_thread::sleep_for(1ms);
                        std::cout << "OK!" << std::endl;
                        peer.DeallocatePacket(packet);
                        goto p1;
                    }
                }
                else ban.insert(packet->systemAddress);
            }
        }
        std::this_thread::sleep_for(1ms);
    }
p1:
    {
        RakNet::BitStream data;
        data.Write(ClientMessage::changeGroup);
        data.Write(static_cast<uint8_t>(group));
        peer.Send(&data, PacketPriority::IMMEDIATE_PRIORITY,
            PacketReliability::RELIABLE_ORDERED, 0, server, false);
    }

    while (true) {
        FORNET{
            CheckBegin;
            CheckHeader(ServerMessage::info) {
                RakNet::BitStream data(packet->data, packet->length,false);
                data.IgnoreBytes(1 + sizeof(uint64_t));
                RakNet::RakString str;
                data.Read(str);
                tinyAI.connect([&](const RakNet::BitStream& data, PacketPriority pp, PacketReliability pr) {
                    peer.Send(&data, pp, pr, 0, server, false);
                },str.C_String(),group);
            }
            CheckHeader(ServerMessage::go) {
                RakNet::BitStream data(packet->data, packet->length,false);
                data.IgnoreBytes(1);
                Vector2 p;
                data.Read(p);
                tinyAI.setRoot(p);
                std::cout << "Go!" << std::endl;
                goto p2;
            }
        }
        std::this_thread::sleep_for(1ms);
    }
p2:
    struct UnitInfo final {
        uint32_t id;
        Vector3 pos;
        bool operator<(const UnitInfo& rhs) const {
            return id < rhs.id;
        }
    };

    bool isStop = false;
    std::set<uint32_t> old;

    auto begin = std::chrono::system_clock::now();
    while (true) {
        std::vector<unsigned char> latestData;
        FORNET{
            if (isStop)continue;
            RakNet::BitStream data(packet->data, packet->length, false);
            data.IgnoreBytes(1);
            CheckBegin;
            CheckHeader(ServerMessage::out) {
                std::cout << "What a pity!" << std::endl;
                isStop = true;
                continue;
            }
            CheckHeader(ServerMessage::stop) {
                std::cout << "The game stopped." << std::endl;
                isStop = true;
                continue;
            }
            CheckHeader(ServerMessage::win) {
                std::cout << "We are winner!" << std::endl;
                isStop = true;
                continue;
            }
            CheckHeader(ServerMessage::updateUnit) {
                latestData = std::vector<unsigned char>(packet->data, packet->data + packet->length);
            }
        }
            if (isStop ||
                peer.GetConnectionState(server) != RakNet::ConnectionState::IS_CONNECTED)break;
        if (latestData.empty())continue;

        uint32_t mine = 0, armies = 0;
        std::set<uint32_t> copy = old;

        RakNet::BitStream latest(latestData.data(), latestData.size(), false);
        latest.IgnoreBytes(1);
        uint32_t size;
        latest.Read(size);
        for (uint32_t i = 0; i < size; ++i) {
            UnitSyncInfo u;
            latest.Read(u);
            auto iter = copy.find(u.id);
            if (iter == copy.cend()) {
                old.insert(u.id);
                tinyAI.newUnit(u);
            }
            else {
                copy.erase(iter);
                tinyAI.updateUnit(u);
            }
            if (u.group == group)
                ++mine;
            else
                ++armies;
        }

        for (auto&& x : copy)
            tinyAI.deleteUnit(x);

        tinyAI.update();

        std::cout << "mine:" << mine << " armies:" << armies << std::endl;
        std::this_thread::sleep_for(1s);
    }

    auto second = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - begin).count();
    std::cout << "time:" << second / 60 << ":" << second % 60 << std::endl;

    peer.Shutdown(500, 0, IMMEDIATE_PRIORITY);
    std::cin.get();
    std::cin.get();
    return 0;
}

#undef FORNET
<|endoftext|>


<commit_before>#include "global_cached_private.h"

static void __complete_req(io_request *orig, thread_safe_page *p)
{
	int page_off = orig->get_offset() - ROUND_PAGE(orig->get_offset());

	p->lock();
	if (orig->get_access_method() == WRITE) {
		unsigned long *l = (unsigned long *) ((char *) p->get_data()
				+ page_off);
		unsigned long start = orig->get_offset() / sizeof(long);
		if (*l != start)
			printf("write: start: %ld, l: %ld, offset: %ld\n",
					start, *l, p->get_offset() / sizeof(long));
		memcpy((char *) p->get_data() + page_off, orig->get_buf(),
				orig->get_size());
		p->set_dirty(true);
	}
	else
		/* I assume the data I read never crosses the page boundary */
		memcpy(orig->get_buf(), (char *) p->get_data() + page_off,
				orig->get_size());
	p->unlock();
	p->dec_ref();
}

class read_page_callback: public callback
{
public:
	int invoke(io_request *request) {
		io_request *orig = (io_request *) request->get_priv();
		thread_safe_page *p = (thread_safe_page *) orig->get_priv();
		std::vector<io_request> reqs;

		p->lock();
		p->set_data_ready(true);
		p->set_io_pending(false);
		io_request *req = p->get_io_req();
		assert(req);
		// TODO vector may allocate memory and block the thread.
		// I should be careful of that.
		while (req) {
			reqs.push_back(*req);
			req = req->get_next_req();
		}
		p->unlock();
		// At this point, data is ready, so any threads that want to add
		// requests to the page will fail.
		p->reset_reqs();

		for (unsigned i = 0; i < reqs.size(); i++) {
			__complete_req(&reqs[i], p);
			io_interface *io = reqs[i].get_io();
			if (io->get_callback())
				io->get_callback()->invoke(&reqs[i]);
		}
		return 0;
	}
};

global_cached_io::global_cached_io(io_interface *underlying,
		long cache_size, int cache_type) {
	cb = NULL;
	cache_hits = 0;
	this->underlying = underlying;
	underlying->set_callback(new read_page_callback());
	num_waits = 0;
	this->cache_size = cache_size;
	if (global_cache == NULL) {
		global_cache = create_cache(cache_type, cache_size);
	}
}

ssize_t global_cached_io::access(io_request *requests, int num)
{
	for (int i = 0; i < num; i++) {
		ssize_t ret;
		off_t old_off = -1;
		off_t offset = requests[i].get_offset();
		thread_safe_page *p = (thread_safe_page *) (get_global_cache()
				->search(ROUND_PAGE(offset), old_off));

		/*
		 * the page isn't in the cache,
		 * so the cache evict a page and return it to us.
		 */
		if (old_off != ROUND_PAGE(offset) && old_off != -1) {
			// TODO handle dirty pages later.
		}

		p->lock();
		if (!p->data_ready()) {
			if(!p->is_io_pending()) {
				p->set_io_pending(true);
				assert(!p->is_dirty());
				p->unlock();

				// If the thread can't add the request as the first IO request
				// of the page, it doesn't need to issue a IO request to the file.
				int status;
				io_request *orig = p->add_first_io_req(requests[i], status);
				if (status == ADD_REQ_SUCCESS) {
					io_request req((char *) p->get_data(),
							ROUND_PAGE(offset), PAGE_SIZE, READ,
							/*
							 * it will notify the underlying IO,
							 * which then notifies global_cached_io.
							 */
							underlying, (void *) orig);
					ret = underlying->access(&req, 1);
					if (ret < 0) {
						perror("read");
						exit(1);
					}
				}
				// If data is ready, the request won't be added to the page.
				// so just serve the data.
				else if (status == ADD_REQ_DATA_READY)
					goto serve_data;
			}
			else {
				p->unlock();
				// An IO request can be added only when the page is still
				// in IO pending state. Otherwise, it returns NULL.
				io_request *orig = p->add_io_req(requests[i]);
				// the page isn't in IO pending state any more.
				if (orig == NULL)
					goto serve_data;
			}
		}
		else {
			// If the data in the page is ready, we don't need to change any state
			// of the page and just read data.
			p->unlock();
serve_data:
			__complete_req(&requests[i], p);
			if (get_callback())
				get_callback()->invoke(&requests[i]);
#ifdef STATISTICS
			cache_hits++;
#endif
		}
	}
	return 0;
}

ssize_t global_cached_io::access(char *buf, off_t offset,
		ssize_t size, int access_method)
{
	ssize_t ret;
	off_t old_off = -1;
	thread_safe_page *p = (thread_safe_page *) (get_global_cache()
			->search(ROUND_PAGE(offset), old_off));

	/*
	 * the page isn't in the cache,
	 * so the cache evict a page and return it to us.
	 */
	if (old_off != ROUND_PAGE(offset) && old_off != -1) {
		/*
		 * if the new page we get is dirty,
		 * we need to write its data back to the file
		 * before we can put data in the page.
		 * Therefore, the data ready flag is definitely
		 * not set yet.
		 */
		if (p->is_dirty()) {
			unsigned long *l = (unsigned long *) p->get_data();
			unsigned long start = old_off / sizeof(long);
			if (*l != start)
				printf("start: %ld, l: %ld\n", start, *l);
			underlying->access((char *) p->get_data(),
					old_off, PAGE_SIZE, WRITE);
			p->set_dirty(false);
		}
	}

	if (!p->data_ready()) {
		/* if the page isn't io pending, set it io pending, and return
		 * original result. otherwise, just return the original value.
		 *
		 * This is an ugly hack, but with this atomic operation,
		 * I can avoid using locks.
		 */
		if(!p->test_and_set_io_pending()) {
			/*
			 * Because of the atomic operation, it's guaranteed
			 * that only one thread can enter here.
			 * If other threads have reference to the page,
			 * they must be waiting for its data to be ready.
			 */
			/*
			 * It's possible that two threads go through here
			 * sequentially. For example, the second thread already
			 * sees the data isn't ready, but find io pending isn't
			 * set when the first thread resets io pending.
			 *
			 * However, in any case, when the second thread comes here,
			 * the data is already ready and the second thread should
			 * be able to see the data is ready when it comes here.
			 */
			if (!p->data_ready()) {
				/*
				 * No other threads set the page dirty at this moment,
				 * because if a thread can set the page dirty,
				 * it means the page isn't dirty and already has data
				 * ready at the first place.
				 */
				if (p->is_dirty())
					p->wait_cleaned();
				ret = underlying->access((char *) p->get_data(),
						ROUND_PAGE(offset), PAGE_SIZE, READ);
				if (ret < 0) {
					perror("read");
					exit(1);
				}
			}
			p->set_data_ready(true);
			p->set_io_pending(false);
		}
		else {
			num_waits++;
			// TODO this is a problem. It takes a long time to get data
			// from real storage devices. If we use busy waiting,
			// we just waste computation time.
			p->wait_ready();
		}
	}
	else {
#ifdef STATISTICS
		cache_hits++;
#endif
	}
	int page_off = offset - ROUND_PAGE(offset);
	p->lock();
	if (access_method == WRITE) {
		unsigned long *l = (unsigned long *) ((char *) p->get_data() + page_off);
		unsigned long start = offset / sizeof(long);
		if (*l != start)
			printf("write: start: %ld, l: %ld, offset: %ld\n",
					start, *l, p->get_offset() / sizeof(long));
		memcpy((char *) p->get_data() + page_off, buf, size);
		p->set_dirty(true);
	}
	else
		/* I assume the data I read never crosses the page boundary */
		memcpy(buf, (char *) p->get_data() + page_off, size);
	p->unlock();
	p->dec_ref();
	ret = size;
	return ret;
}

int global_cached_io::preload(off_t start, long size) {
	if (size > cache_size) {
		fprintf(stderr, "we can't preload data larger than the cache size\n");
		exit(1);
	}

	/* open the file. It's a hack, but it works for now. */
	underlying->init();

	assert(ROUND_PAGE(start) == start);
	for (long offset = start; offset < start + size; offset += PAGE_SIZE) {
		off_t old_off = -1;
		thread_safe_page *p = (thread_safe_page *) (get_global_cache()->search(ROUND_PAGE(offset), old_off));
		if (!p->data_ready()) {
			ssize_t ret = underlying->access((char *) p->get_data(),
					ROUND_PAGE(offset), PAGE_SIZE, READ);
			if (ret < 0) {
				perror("read");
				return ret;
			}
			p->set_io_pending(false);
			p->set_data_ready(true);
		}
	}
	/* close the file as it will be opened again in the real workload. */
	underlying->cleanup();
	return 0;
}

page_cache *global_cached_io::global_cache;
<commit_msg>calculate cache hit rate correctly in global cache IO.<commit_after>#include "global_cached_private.h"

static void __complete_req(io_request *orig, thread_safe_page *p)
{
	int page_off = orig->get_offset() - ROUND_PAGE(orig->get_offset());

	p->lock();
	if (orig->get_access_method() == WRITE) {
		unsigned long *l = (unsigned long *) ((char *) p->get_data()
				+ page_off);
		unsigned long start = orig->get_offset() / sizeof(long);
		if (*l != start)
			printf("write: start: %ld, l: %ld, offset: %ld\n",
					start, *l, p->get_offset() / sizeof(long));
		memcpy((char *) p->get_data() + page_off, orig->get_buf(),
				orig->get_size());
		p->set_dirty(true);
	}
	else
		/* I assume the data I read never crosses the page boundary */
		memcpy(orig->get_buf(), (char *) p->get_data() + page_off,
				orig->get_size());
	p->unlock();
	p->dec_ref();
}

class read_page_callback: public callback
{
public:
	int invoke(io_request *request) {
		io_request *orig = (io_request *) request->get_priv();
		thread_safe_page *p = (thread_safe_page *) orig->get_priv();
		std::vector<io_request> reqs;

		p->lock();
		p->set_data_ready(true);
		p->set_io_pending(false);
		io_request *req = p->get_io_req();
		assert(req);
		// TODO vector may allocate memory and block the thread.
		// I should be careful of that.
		while (req) {
			reqs.push_back(*req);
			req = req->get_next_req();
		}
		p->unlock();
		// At this point, data is ready, so any threads that want to add
		// requests to the page will fail.
		p->reset_reqs();

		for (unsigned i = 0; i < reqs.size(); i++) {
			__complete_req(&reqs[i], p);
			io_interface *io = reqs[i].get_io();
			if (io->get_callback())
				io->get_callback()->invoke(&reqs[i]);
		}
		return 0;
	}
};

global_cached_io::global_cached_io(io_interface *underlying,
		long cache_size, int cache_type) {
	cb = NULL;
	cache_hits = 0;
	this->underlying = underlying;
	underlying->set_callback(new read_page_callback());
	num_waits = 0;
	this->cache_size = cache_size;
	if (global_cache == NULL) {
		global_cache = create_cache(cache_type, cache_size);
	}
}

ssize_t global_cached_io::access(io_request *requests, int num)
{
	for (int i = 0; i < num; i++) {
		ssize_t ret;
		off_t old_off = -1;
		off_t offset = requests[i].get_offset();
		thread_safe_page *p = (thread_safe_page *) (get_global_cache()
				->search(ROUND_PAGE(offset), old_off));

		/*
		 * the page isn't in the cache,
		 * so the cache evict a page and return it to us.
		 */
		if (old_off != ROUND_PAGE(offset) && old_off != -1) {
			// TODO handle dirty pages later.
		}

		p->lock();
		if (!p->data_ready()) {
			if(!p->is_io_pending()) {
				p->set_io_pending(true);
				assert(!p->is_dirty());
				p->unlock();

				// If the thread can't add the request as the first IO request
				// of the page, it doesn't need to issue a IO request to the file.
				int status;
				io_request *orig = p->add_first_io_req(requests[i], status);
				if (status == ADD_REQ_SUCCESS) {
					io_request req((char *) p->get_data(),
							ROUND_PAGE(offset), PAGE_SIZE, READ,
							/*
							 * it will notify the underlying IO,
							 * which then notifies global_cached_io.
							 */
							underlying, (void *) orig);
					ret = underlying->access(&req, 1);
					if (ret < 0) {
						perror("read");
						exit(1);
					}
				}
				else {
#ifdef STATISTICS
					cache_hits++;
#endif
					// If data is ready, the request won't be added to the page.
					// so just serve the data.
					if (status == ADD_REQ_DATA_READY)
						goto serve_data;
				}
			}
			else {
				p->unlock();
#ifdef STATISTICS
				cache_hits++;
#endif
				// An IO request can be added only when the page is still
				// in IO pending state. Otherwise, it returns NULL.
				io_request *orig = p->add_io_req(requests[i]);
				// the page isn't in IO pending state any more.
				if (orig == NULL)
					goto serve_data;
			}
		}
		else {
			// If the data in the page is ready, we don't need to change any state
			// of the page and just read data.
			p->unlock();
#ifdef STATISTICS
			cache_hits++;
#endif
serve_data:
			__complete_req(&requests[i], p);
			if (get_callback())
				get_callback()->invoke(&requests[i]);
		}
	}
	return 0;
}

ssize_t global_cached_io::access(char *buf, off_t offset,
		ssize_t size, int access_method)
{
	ssize_t ret;
	off_t old_off = -1;
	thread_safe_page *p = (thread_safe_page *) (get_global_cache()
			->search(ROUND_PAGE(offset), old_off));

	/*
	 * the page isn't in the cache,
	 * so the cache evict a page and return it to us.
	 */
	if (old_off != ROUND_PAGE(offset) && old_off != -1) {
		/*
		 * if the new page we get is dirty,
		 * we need to write its data back to the file
		 * before we can put data in the page.
		 * Therefore, the data ready flag is definitely
		 * not set yet.
		 */
		if (p->is_dirty()) {
			unsigned long *l = (unsigned long *) p->get_data();
			unsigned long start = old_off / sizeof(long);
			if (*l != start)
				printf("start: %ld, l: %ld\n", start, *l);
			underlying->access((char *) p->get_data(),
					old_off, PAGE_SIZE, WRITE);
			p->set_dirty(false);
		}
	}

	if (!p->data_ready()) {
		/* if the page isn't io pending, set it io pending, and return
		 * original result. otherwise, just return the original value.
		 *
		 * This is an ugly hack, but with this atomic operation,
		 * I can avoid using locks.
		 */
		if(!p->test_and_set_io_pending()) {
			/*
			 * Because of the atomic operation, it's guaranteed
			 * that only one thread can enter here.
			 * If other threads have reference to the page,
			 * they must be waiting for its data to be ready.
			 */
			/*
			 * It's possible that two threads go through here
			 * sequentially. For example, the second thread already
			 * sees the data isn't ready, but find io pending isn't
			 * set when the first thread resets io pending.
			 *
			 * However, in any case, when the second thread comes here,
			 * the data is already ready and the second thread should
			 * be able to see the data is ready when it comes here.
			 */
			if (!p->data_ready()) {
				/*
				 * No other threads set the page dirty at this moment,
				 * because if a thread can set the page dirty,
				 * it means the page isn't dirty and already has data
				 * ready at the first place.
				 */
				if (p->is_dirty())
					p->wait_cleaned();
				ret = underlying->access((char *) p->get_data(),
						ROUND_PAGE(offset), PAGE_SIZE, READ);
				if (ret < 0) {
					perror("read");
					exit(1);
				}
			}
			p->set_data_ready(true);
			p->set_io_pending(false);
		}
		else {
			num_waits++;
			// TODO this is a problem. It takes a long time to get data
			// from real storage devices. If we use busy waiting,
			// we just waste computation time.
			p->wait_ready();
		}
	}
	else {
#ifdef STATISTICS
		cache_hits++;
#endif
	}
	int page_off = offset - ROUND_PAGE(offset);
	p->lock();
	if (access_method == WRITE) {
		unsigned long *l = (unsigned long *) ((char *) p->get_data() + page_off);
		unsigned long start = offset / sizeof(long);
		if (*l != start)
			printf("write: start: %ld, l: %ld, offset: %ld\n",
					start, *l, p->get_offset() / sizeof(long));
		memcpy((char *) p->get_data() + page_off, buf, size);
		p->set_dirty(true);
	}
	else
		/* I assume the data I read never crosses the page boundary */
		memcpy(buf, (char *) p->get_data() + page_off, size);
	p->unlock();
	p->dec_ref();
	ret = size;
	return ret;
}

int global_cached_io::preload(off_t start, long size) {
	if (size > cache_size) {
		fprintf(stderr, "we can't preload data larger than the cache size\n");
		exit(1);
	}

	/* open the file. It's a hack, but it works for now. */
	underlying->init();

	assert(ROUND_PAGE(start) == start);
	for (long offset = start; offset < start + size; offset += PAGE_SIZE) {
		off_t old_off = -1;
		thread_safe_page *p = (thread_safe_page *) (get_global_cache()->search(ROUND_PAGE(offset), old_off));
		if (!p->data_ready()) {
			ssize_t ret = underlying->access((char *) p->get_data(),
					ROUND_PAGE(offset), PAGE_SIZE, READ);
			if (ret < 0) {
				perror("read");
				return ret;
			}
			p->set_io_pending(false);
			p->set_data_ready(true);
		}
	}
	/* close the file as it will be opened again in the real workload. */
	underlying->cleanup();
	return 0;
}

page_cache *global_cached_io::global_cache;
<|endoftext|>


<commit_before>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRect.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkTypes.h"
#include "src/core/SkGeometry.h"

static constexpr float kStrokeWidth = 40;
static constexpr int kCellSize = 200;

static const SkPoint kCubics[][4] = {
    {{122, 737}, {348, 553}, {403, 761}, {400, 760}},
    {{244, 520}, {244, 518}, {1141, 634}, {394, 688}},
    {{550, 194}, {138, 130}, {1035, 246}, {288, 300}},
    {{226, 733}, {556, 779}, {-43, 471}, {348, 683}},
    {{268, 204}, {492, 304}, {352, 23}, {433, 412}},
    {{172, 480}, {396, 580}, {256, 299}, {338, 677}},
    {{731, 340}, {318, 252}, {1026, -64}, {367, 265}},
    {{475, 708}, {62, 620}, {770, 304}, {220, 659}},
};

static SkRect calc_tight_cubic_bounds(const SkPoint P[4], int depth=5) {
    if (0 == depth) {
        SkRect bounds;
        bounds.fLeft = std::min(std::min(P[0].x(), P[1].x()), std::min(P[2].x(), P[3].x()));
        bounds.fTop = std::min(std::min(P[0].y(), P[1].y()), std::min(P[2].y(), P[3].y()));
        bounds.fRight = std::max(std::max(P[0].x(), P[1].x()), std::max(P[2].x(), P[3].x()));
        bounds.fBottom = std::max(std::max(P[0].y(), P[1].y()), std::max(P[2].y(), P[3].y()));
        return bounds;
    }

    SkPoint chopped[7];
    SkChopCubicAt(P, chopped, .5f);
    SkRect bounds = calc_tight_cubic_bounds(chopped, depth - 1);
    bounds.join(calc_tight_cubic_bounds(chopped+3, depth - 1));
    return bounds;
}

// This is a compilation of cubics that have given strokers grief. Feel free to add more.
class TrickyCubicStrokesGM : public skiagm::GM {
public:
    TrickyCubicStrokesGM() {}

protected:

    SkString onShortName() override {
        return SkString("trickycubicstrokes");
    }

    SkISize onISize() override {
        return SkISize::Make(3*kCellSize, 3*kCellSize);
    }

    void onOnceBeforeDraw() override {
        fStrokePaint.setAntiAlias(true);
        fStrokePaint.setStrokeWidth(kStrokeWidth);
        fStrokePaint.setColor(SK_ColorGREEN);
        fStrokePaint.setStyle(SkPaint::kStroke_Style);
    }

    void onDraw(SkCanvas* canvas) override {
        canvas->clear(SK_ColorBLACK);

        for (size_t i = 0; i < SK_ARRAY_COUNT(kCubics); ++i) {
            this->drawStroke(canvas, kCubics[i],
                             SkRect::MakeXYWH((i%3) * kCellSize, (i/3) * kCellSize, kCellSize,
                                              kCellSize));
        }
    }

    void drawStroke(SkCanvas* canvas, const SkPoint P[4], const SkRect& location) {
        SkRect strokeBounds = calc_tight_cubic_bounds(P);
        strokeBounds.outset(kStrokeWidth, kStrokeWidth);

        SkMatrix matrix;
        matrix.setRectToRect(strokeBounds, location, SkMatrix::kCenter_ScaleToFit);

        SkPath path;
        path.moveTo(P[0]);
        path.cubicTo(P[1], P[2], P[3]);

        SkAutoCanvasRestore acr(canvas, true);
        canvas->concat(matrix);
        canvas->drawPath(path, fStrokePaint);
    }

private:
    SkPaint fStrokePaint;
    typedef GM INHERITED;
};

DEF_GM( return new TrickyCubicStrokesGM; )
<commit_msg>Add new tests to trickycubicstrokes<commit_after>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRect.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkTypes.h"
#include "src/core/SkGeometry.h"

static constexpr float kStrokeWidth = 30;
static constexpr int kCellSize = 200;
static constexpr int kNumCols = 4;
static constexpr int kNumRows = 4;

enum class CellFillMode {
    kStretch,
    kCenter
};

struct TrickyCubic {
    SkPoint fPoints[4];
    CellFillMode fFillMode;
    float fScale = 1;
};

static const TrickyCubic kTrickyCubics[] = {
    {{{122, 737}, {348, 553}, {403, 761}, {400, 760}}, CellFillMode::kStretch},
    {{{244, 520}, {244, 518}, {1141, 634}, {394, 688}}, CellFillMode::kStretch},
    {{{550, 194}, {138, 130}, {1035, 246}, {288, 300}}, CellFillMode::kStretch},
    {{{226, 733}, {556, 779}, {-43, 471}, {348, 683}}, CellFillMode::kStretch},
    {{{268, 204}, {492, 304}, {352, 23}, {433, 412}}, CellFillMode::kStretch},
    {{{172, 480}, {396, 580}, {256, 299}, {338, 677}}, CellFillMode::kStretch},
    {{{731, 340}, {318, 252}, {1026, -64}, {367, 265}}, CellFillMode::kStretch},
    {{{475, 708}, {62, 620}, {770, 304}, {220, 659}}, CellFillMode::kStretch},
    {{{0, 0}, {128, 128}, {128, 0}, {0, 128}}, CellFillMode::kCenter},  // Perfect cusp
    {{{0,.01f}, {128,127.999f}, {128,.01f}, {0,127.99f}}, CellFillMode::kCenter},  // Near-cusp
    {{{0,-.01f}, {128,128.001f}, {128,-.01f}, {0,128.001f}}, CellFillMode::kCenter},  // Near-cusp
    {{{0,0}, {0,-10}, {0,-10}, {0,10}}, CellFillMode::kCenter, 1.098283f},  // Flat line with 180
    {{{10,0}, {0,0}, {20,0}, {10,0}}, CellFillMode::kStretch},  // Flat line with 2 180s
    {{{39,-39}, {40,-40}, {40,-40}, {0,0}}, CellFillMode::kStretch},  // Flat diagonal with 180
    {{{40, 40}, {0, 0}, {200, 200}, {0, 0}}, CellFillMode::kStretch},  // Diag with an internal 180
    {{{0,0}, {1e-2f,0}, {-1e-2f,0}, {0,0}}, CellFillMode::kCenter},  // Circle
};

static SkRect calc_tight_cubic_bounds(const SkPoint P[4], int depth=5) {
    if (0 == depth) {
        SkRect bounds;
        bounds.fLeft = std::min(std::min(P[0].x(), P[1].x()), std::min(P[2].x(), P[3].x()));
        bounds.fTop = std::min(std::min(P[0].y(), P[1].y()), std::min(P[2].y(), P[3].y()));
        bounds.fRight = std::max(std::max(P[0].x(), P[1].x()), std::max(P[2].x(), P[3].x()));
        bounds.fBottom = std::max(std::max(P[0].y(), P[1].y()), std::max(P[2].y(), P[3].y()));
        return bounds;
    }

    SkPoint chopped[7];
    SkChopCubicAt(P, chopped, .5f);
    SkRect bounds = calc_tight_cubic_bounds(chopped, depth - 1);
    bounds.join(calc_tight_cubic_bounds(chopped+3, depth - 1));
    return bounds;
}

enum class FillMode {
    kCenter,
    kScale
};

// This is a compilation of cubics that have given strokers grief. Feel free to add more.
class TrickyCubicStrokesGM : public skiagm::GM {
public:
    TrickyCubicStrokesGM() {}

protected:

    SkString onShortName() override {
        return SkString("trickycubicstrokes");
    }

    SkISize onISize() override {
        return SkISize::Make(kNumCols * kCellSize, kNumRows * kCellSize);
    }

    void onOnceBeforeDraw() override {
        fStrokePaint.setAntiAlias(true);
        fStrokePaint.setStrokeWidth(kStrokeWidth);
        fStrokePaint.setColor(SK_ColorGREEN);
        fStrokePaint.setStyle(SkPaint::kStroke_Style);
    }

    void onDraw(SkCanvas* canvas) override {
        canvas->clear(SK_ColorBLACK);

        for (size_t i = 0; i < SK_ARRAY_COUNT(kTrickyCubics); ++i) {
            const auto& trickyCubic = kTrickyCubics[i];
            SkPoint p[7];
            memcpy(p, trickyCubic.fPoints, sizeof(SkPoint) * 4);
            for (int j = 0; j < 4; ++j) {
                p[j] *= trickyCubic.fScale;
            }
            this->drawStroke(canvas, p, i, trickyCubic.fFillMode);
        }
    }

    void drawStroke(SkCanvas* canvas, const SkPoint p[4], int cellID, CellFillMode fillMode) {
        auto cellRect = SkRect::MakeXYWH((cellID % kNumCols) * kCellSize,
                                         (cellID / kNumCols) * kCellSize,
                                         kCellSize, kCellSize);

        SkRect strokeBounds = calc_tight_cubic_bounds(p);
        strokeBounds.outset(kStrokeWidth, kStrokeWidth);

        SkMatrix matrix;
        if (fillMode == CellFillMode::kStretch) {
            matrix.setRectToRect(strokeBounds, cellRect, SkMatrix::kCenter_ScaleToFit);
        } else {
            matrix.setTranslate(cellRect.x() + kStrokeWidth +
                                (cellRect.width() - strokeBounds.width()) / 2,
                                cellRect.y() + kStrokeWidth +
                                (cellRect.height() - strokeBounds.height()) / 2);
        }

        SkAutoCanvasRestore acr(canvas, true);
        canvas->concat(matrix);
        fStrokePaint.setStrokeWidth(kStrokeWidth / matrix.getMaxScale());
        canvas->drawPath(SkPath().moveTo(p[0]).cubicTo(p[1], p[2], p[3]), fStrokePaint);
    }

private:
    SkPaint fStrokePaint;
    typedef GM INHERITED;
};

DEF_GM( return new TrickyCubicStrokesGM; )
<|endoftext|>