<commit_before>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <memory>

#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/tensor_types.h"
#include "tensorflow/core/kernels/cubin_headers/tanh_f16_kernel.h"
#include "tensorflow/core/kernels/cubin_headers/tanh_f32_kernel.h"
#include "tensorflow/core/kernels/cubin_headers/tanh_f64_kernel.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/stream_executor.h"

namespace tensorflow {
namespace {
Status CreateKernel(absl::string_view kernel_name, uint64_t num_args,
                    absl::string_view ptx, absl::Span<const uint8_t> cubin_data,
                    se::StreamExecutor* stream_exec,
                    std::unique_ptr<se::KernelBase>& kernel_base) {
  se::MultiKernelLoaderSpec loader_spec(num_args);

  if (!cubin_data.empty()) {
    loader_spec.AddCudaCubinInMemory(
        reinterpret_cast<const char*>(cubin_data.data()), kernel_name);
  }

  kernel_base.reset(new se::KernelBase(stream_exec));
  return stream_exec->GetKernel(loader_spec, kernel_base.get());
}

class MlirGenerateTanhOp : public OpKernel {
 public:
  explicit MlirGenerateTanhOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}

  void Compute(OpKernelContext* ctx) override {
    auto* stream = ctx->op_device_context()->stream();
    se::KernelBase* kernel;
    {
      std::lock_guard<std::mutex> l(mu_);
      if (!kernel_) {
        OP_REQUIRES_OK(ctx, CreateKernel("tanh_kernel", 10, "", cubin_data_,
                                         stream->parent(), kernel_));
      }
      kernel = kernel_.get();
    }

    const Tensor& inp = ctx->input(0);
    Tensor* out = nullptr;
    OP_REQUIRES_OK(
        ctx, ctx->forward_input_or_allocate_output({0}, 0, inp.shape(), &out));

    if (inp.NumElements() == 0) {
      return;
    }

    se::KernelArgsArray<10> args;

    args.add_device_memory_argument(
        stream_executor::DeviceMemoryBase(inp.data(), inp.TotalBytes()));
    args.add_device_memory_argument(
        stream_executor::DeviceMemoryBase(inp.data(), inp.TotalBytes()));
    args.add_argument<int64_t>(0);
    args.add_argument<int64_t>(inp.NumElements());
    args.add_argument<int64_t>(1);

    args.add_device_memory_argument(
        stream_executor::DeviceMemoryBase(out->data(), out->TotalBytes()));
    args.add_device_memory_argument(
        stream_executor::DeviceMemoryBase(out->data(), out->TotalBytes()));
    args.add_argument<int64_t>(0);
    args.add_argument<int64_t>(inp.NumElements());
    args.add_argument<int64_t>(1);

    // TODO(b/158649746): Choose block size and thread dim according to the
    // number of input elements. For now, this supports at most 1024 elements.
    OP_REQUIRES_OK(
        ctx, stream->parent()->Launch(stream, se::ThreadDim(inp.NumElements()),
                                      se::BlockDim(1), *kernel, args));
  }

 protected:
  absl::Span<const uint8_t> cubin_data_;

 private:
  std::unique_ptr<se::KernelBase> kernel_;
  std::mutex mu_;
};

class MlirGenerateTanhF16Op : public MlirGenerateTanhOp {
 public:
  explicit MlirGenerateTanhF16Op(OpKernelConstruction* ctx)
      : MlirGenerateTanhOp(ctx) {
    cubin_data_ = kTanhF16Kernel;
  }
};

class MlirGenerateTanhF32Op : public MlirGenerateTanhOp {
 public:
  explicit MlirGenerateTanhF32Op(OpKernelConstruction* ctx)
      : MlirGenerateTanhOp(ctx) {
    cubin_data_ = kTanhF32Kernel;
  }
};

class MlirGenerateTanhF64Op : public MlirGenerateTanhOp {
 public:
  explicit MlirGenerateTanhF64Op(OpKernelConstruction* ctx)
      : MlirGenerateTanhOp(ctx) {
    cubin_data_ = kTanhF64Kernel;
  }
};
}  // namespace

REGISTER_KERNEL_BUILDER(
    Name("Tanh").Device(DEVICE_GPU).TypeConstraint<Eigen::half>("T"),
    MlirGenerateTanhF16Op);
REGISTER_KERNEL_BUILDER(
    Name("Tanh").Device(DEVICE_GPU).TypeConstraint<float>("T"),
    MlirGenerateTanhF32Op);
REGISTER_KERNEL_BUILDER(
    Name("Tanh").Device(DEVICE_GPU).TypeConstraint<double>("T"),
    MlirGenerateTanhF64Op);
}  // namespace tensorflow
<commit_msg>Properly configure block and grid dimensions when launching generated kernels.<commit_after>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <memory>
#include <vector>

#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/tensor_types.h"
#include "tensorflow/core/kernels/cubin_headers/tanh_f16_kernel.h"
#include "tensorflow/core/kernels/cubin_headers/tanh_f32_kernel.h"
#include "tensorflow/core/kernels/cubin_headers/tanh_f64_kernel.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/stream_executor.h"

namespace tensorflow {
namespace {
Status CreateKernel(absl::string_view kernel_name, uint64_t num_args,
                    absl::string_view ptx, absl::Span<const uint8_t> cubin_data,
                    se::StreamExecutor* stream_exec,
                    std::unique_ptr<se::KernelBase>& kernel_base) {
  se::MultiKernelLoaderSpec loader_spec(num_args);

  if (!cubin_data.empty()) {
    loader_spec.AddCudaCubinInMemory(
        reinterpret_cast<const char*>(cubin_data.data()), kernel_name);
  }

  kernel_base.reset(new se::KernelBase(stream_exec));
  return stream_exec->GetKernel(loader_spec, kernel_base.get());
}

struct LaunchConfig {
  se::BlockDim blockDim;
  se::ThreadDim threadDim;
};

LaunchConfig GetLaunchConfiguration(std::vector<uint64> tile_sizes,
                                    std::vector<uint64> unrolling_factors,
                                    std::vector<uint64> shape) {
  LaunchConfig result;
  // Ensure the vectors are length 3 and pad with ones.
  tile_sizes.resize(3, 1);
  unrolling_factors.resize(3, 1);
  shape.resize(3, 1);
  // The number of threads is given by the tiling size.
  result.threadDim = se::ThreadDim(tile_sizes[0], tile_sizes[1], tile_sizes[2]);
  // We know that the kernel was generated by mapping the three outer-most
  // dimensions to x,y,z dimensions. So we only need to compute those.
  std::vector<int> block_dims(3);
  for (int i = 0; i < 3; ++i) {
    // Compute the number of grids. We use ceildiv here as we have to allocate
    // an extra thread/block if the division is not even. The kernel contains
    // code to handle the boundaries.
    int number_of_threads =
        (shape[i] + unrolling_factors[i] - 1) / unrolling_factors[i];
    int number_of_grids =
        (number_of_threads + tile_sizes[i] - 1) / tile_sizes[i];
    block_dims[i] = number_of_grids;
  }
  result.blockDim = se::BlockDim(block_dims[0], block_dims[1], block_dims[2]);
  return result;
}

class MlirGeneratedTanhOp : public OpKernel {
 public:
  explicit MlirGeneratedTanhOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}

  void Compute(OpKernelContext* ctx) override {
    auto* stream = ctx->op_device_context()->stream();
    se::KernelBase* kernel;
    {
      std::lock_guard<std::mutex> l(mu_);
      if (!kernel_) {
        OP_REQUIRES_OK(ctx, CreateKernel("tanh_kernel", 10, "", cubin_data_,
                                         stream->parent(), kernel_));
      }
      kernel = kernel_.get();
    }

    const Tensor& inp = ctx->input(0);
    Tensor* out = nullptr;
    OP_REQUIRES_OK(
        ctx, ctx->forward_input_or_allocate_output({0}, 0, inp.shape(), &out));

    if (inp.NumElements() == 0) {
      return;
    }

    se::KernelArgsArray<10> args;

    args.add_device_memory_argument(
        stream_executor::DeviceMemoryBase(inp.data(), inp.TotalBytes()));
    args.add_device_memory_argument(
        stream_executor::DeviceMemoryBase(inp.data(), inp.TotalBytes()));
    args.add_argument<int64_t>(0);
    args.add_argument<int64_t>(inp.NumElements());
    args.add_argument<int64_t>(1);

    args.add_device_memory_argument(
        stream_executor::DeviceMemoryBase(out->data(), out->TotalBytes()));
    args.add_device_memory_argument(
        stream_executor::DeviceMemoryBase(out->data(), out->TotalBytes()));
    args.add_argument<int64_t>(0);
    args.add_argument<int64_t>(inp.NumElements());
    args.add_argument<int64_t>(1);

    // This has to be aligned with the configuration that was used when building
    // the kernels. See the corresponding build rules in `cubin_headers/BUILD`.
    LaunchConfig config = GetLaunchConfiguration(
        {256}, {}, {static_cast<uint64>(inp.NumElements())});
    OP_REQUIRES_OK(
        ctx, stream->parent()->Launch(stream, config.threadDim, config.blockDim,
                                      *kernel, args));
  }

 protected:
  absl::Span<const uint8_t> cubin_data_;

 private:
  std::unique_ptr<se::KernelBase> kernel_;
  std::mutex mu_;
};

class MlirGeneratedTanhF16Op : public MlirGeneratedTanhOp {
 public:
  explicit MlirGeneratedTanhF16Op(OpKernelConstruction* ctx)
      : MlirGeneratedTanhOp(ctx) {
    cubin_data_ = kTanhF16Kernel;
  }
};

class MlirGeneratedTanhF32Op : public MlirGeneratedTanhOp {
 public:
  explicit MlirGeneratedTanhF32Op(OpKernelConstruction* ctx)
      : MlirGeneratedTanhOp(ctx) {
    cubin_data_ = kTanhF32Kernel;
  }
};

class MlirGeneratedTanhF64Op : public MlirGeneratedTanhOp {
 public:
  explicit MlirGeneratedTanhF64Op(OpKernelConstruction* ctx)
      : MlirGeneratedTanhOp(ctx) {
    cubin_data_ = kTanhF64Kernel;
  }
};
}  // namespace

REGISTER_KERNEL_BUILDER(
    Name("Tanh").Device(DEVICE_GPU).TypeConstraint<Eigen::half>("T"),
    MlirGeneratedTanhF16Op);
REGISTER_KERNEL_BUILDER(
    Name("Tanh").Device(DEVICE_GPU).TypeConstraint<float>("T"),
    MlirGeneratedTanhF32Op);
REGISTER_KERNEL_BUILDER(
    Name("Tanh").Device(DEVICE_GPU).TypeConstraint<double>("T"),
    MlirGeneratedTanhF64Op);
}  // namespace tensorflow
<|endoftext|>


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

#include "ConditionVariableOperationsTestCase.hpp"

#include "waitForNextTick.hpp"
#include "Mutex/mutexTestTryLockWhenLocked.hpp"

#include "distortos/ConditionVariable.hpp"
#include "distortos/DynamicThread.hpp"
#include "distortos/Mutex.hpp"
#include "distortos/ThisThread.hpp"
#include "distortos/StaticSoftwareTimer.hpp"
#include "distortos/statistics.hpp"

namespace distortos
{

namespace test
{

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local constants
+---------------------------------------------------------------------------------------------------------------------*/

/// single duration used in tests
constexpr auto singleDuration = TickClock::duration{1};

/// long duration used in tests
constexpr auto longDuration = singleDuration * 10;

/// expected number of context switches in waitForNextTick(): main -> idle -> main
constexpr decltype(statistics::getContextSwitchCount()) waitForNextTickContextSwitchCount {2};

/// expected number of context switches in testMutexAndUnlock(): main -> idle -> main
constexpr decltype(statistics::getContextSwitchCount()) testMutexAndUnlockContextSwitchCount
{
		waitForNextTickContextSwitchCount + 2
};

/// expected number of context switches in phase1 block involving waitFor() or waitUntil() (excluding
/// waitForNextTick() and testMutexAndUnlock()): 1 - main thread blocks on condition variable (main -> idle), 2 - main
/// thread wakes up (idle -> main)
constexpr decltype(statistics::getContextSwitchCount()) phase1WaitForUntilContextSwitchCount {2};

/// expected number of context switches in phase2 block involving test thread (excluding waitForNextTick() and
/// testMutexAndUnlock()): 1 - test thread starts (main -> test), 2 - test thread goes to sleep (test -> main), 3 - main
/// thread blocks on condition variable (main -> idle), 4 - test thread wakes (idle -> test), 5 - test thread terminates
/// (test -> main)
constexpr decltype(statistics::getContextSwitchCount()) phase2ThreadContextSwitchCount {5};

/// expected number of context switches in phase3 block involving software timer (excluding waitForNextTick() and
/// testMutexAndUnlock()): 1 - main thread blocks on condition variable (main -> idle), 2 - main thread is unblocked by
/// interrupt (idle -> main)
constexpr decltype(statistics::getContextSwitchCount()) phase3SoftwareTimerContextSwitchCount {2};

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

/**
 * \brief Tests whether supplied mutex is properly locked and unlocks it.
 *
 * \param [in] mutex is a reference to mutex that will be tested and unlocked
 *
 * \return true if test succeeded and mutex was sucessfully unlocked, false otherwise
 */

bool testMutexAndUnlock(Mutex& mutex)
{
	const auto testRet = mutexTestTryLockWhenLocked(mutex, UINT8_MAX);
	const auto unlockRet = mutex.unlock();

	return testRet == true && unlockRet == 0;
}

/**
 * \brief Phase 1 of test case.
 *
 * Tests whether both waitFor() and waitUntil() functions properly return some error and properly handle mutex when
 * timing out without notification.
 *
 * \param [in] mutex is a reference to mutex used with condition variable, must be unlocked
 *
 * \return true if test succeeded, false otherwise
 */

bool phase1(Mutex& mutex)
{
	ConditionVariable conditionVariable;

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		// there will be no notification, so waitFor() should time-out at expected time
		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto start = TickClock::now();
		const auto ret = conditionVariable.waitFor(mutex, singleDuration);
		const auto realDuration = TickClock::now() - start;
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != ETIMEDOUT || realDuration != singleDuration + decltype(singleDuration){1} || mutexTest != true ||
				contextSwitches != phase1WaitForUntilContextSwitchCount)
			return false;
	}

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		// there will be no notification, so waitUntil() should time-out at exact expected time
		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + singleDuration;
		const auto ret = conditionVariable.waitUntil(mutex, wakeUpTimePoint);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != ETIMEDOUT || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase1WaitForUntilContextSwitchCount)
			return false;
	}

	return true;
}

/**
 * \brief Phase 2 of test case.
 *
 * Tests thread-thread signaling scenario. Main (current) thread waits for condition variable notification (using
 * wait(), waitFor() and waitUntil()). Test thread notifies one waiter at specified time point, main thread is expected
 * to wake up and acquire ownership of the mutex in the same moment.
 *
 * \param [in] mutex is a reference to mutex used with condition variable, must be unlocked
 *
 * \return true if test succeeded, false otherwise
 */

bool phase2(Mutex& mutex)
{
	constexpr size_t testThreadStackSize {384};

	ConditionVariable conditionVariable;

	const auto sleepUntilFunctor = [&conditionVariable](const TickClock::time_point timePoint)
			{
				ThisThread::sleepUntil(timePoint);
				conditionVariable.notifyOne();
			};

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + longDuration;
		auto thread = makeDynamicThread({testThreadStackSize, UINT8_MAX}, sleepUntilFunctor, wakeUpTimePoint);

		thread.start();
		ThisThread::yield();

		// wait() should succeed at expected time
		const auto ret = conditionVariable.wait(mutex);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		thread.join();
		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != 0 || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase2ThreadContextSwitchCount)
			return false;
	}

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + longDuration;
		auto thread = makeDynamicThread({testThreadStackSize, UINT8_MAX}, sleepUntilFunctor, wakeUpTimePoint);

		thread.start();
		ThisThread::yield();

		// waitFor() should succeed at expected time
		const auto ret = conditionVariable.waitFor(mutex, wakeUpTimePoint - TickClock::now() + longDuration);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		thread.join();
		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != 0 || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase2ThreadContextSwitchCount)
			return false;
	}

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + longDuration;
		auto thread = makeDynamicThread({testThreadStackSize, UINT8_MAX}, sleepUntilFunctor, wakeUpTimePoint);

		thread.start();
		ThisThread::yield();

		// waitUntil() should succeed at expected time
		const auto ret = conditionVariable.waitUntil(mutex, wakeUpTimePoint + longDuration);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		thread.join();
		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != 0 || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase2ThreadContextSwitchCount)
			return false;
	}

	return true;
}

/**
 * \brief Phase 3 of test case.
 *
 * Tests interrupt-thread signaling scenario. Main (current) thread waits for condition variable notification (using
 * wait(), waitFor() and waitUntil()). Software timer is used to notify one waiter at specified time point from
 * interrupt context, main thread is expected to wake up and acquire ownership of the mutex in the same moment.
 *
 * \param [in] mutex is a reference to mutex used with condition variable, must be unlocked
 *
 * \return true if test succeeded, false otherwise
 */

bool phase3(Mutex& mutex)
{
	ConditionVariable conditionVariable;

	auto softwareTimer = makeStaticSoftwareTimer(&ConditionVariable::notifyOne, std::ref(conditionVariable));

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + longDuration;

		softwareTimer.start(wakeUpTimePoint);

		// wait() should succeed at expected time
		const auto ret = conditionVariable.wait(mutex);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != 0 || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase3SoftwareTimerContextSwitchCount)
			return false;
	}

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + longDuration;

		softwareTimer.start(wakeUpTimePoint);

		// waitFor() should succeed at expected time
		const auto ret = conditionVariable.waitFor(mutex, wakeUpTimePoint - TickClock::now() + longDuration);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != 0 || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase3SoftwareTimerContextSwitchCount)
			return false;
	}

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + longDuration;

		softwareTimer.start(wakeUpTimePoint);

		// waitUntil() should succeed at expected time
		const auto ret = conditionVariable.waitUntil(mutex, wakeUpTimePoint + longDuration);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != 0 || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase3SoftwareTimerContextSwitchCount)
			return false;
	}

	return true;
}

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

bool ConditionVariableOperationsTestCase::run_() const
{
	using Parameters = std::tuple<Mutex::Type, Mutex::Protocol, uint8_t>;
	static const std::array<Parameters, 9> parametersArray
	{{
			Parameters{Mutex::Type::Normal, Mutex::Protocol::None, {}},
			Parameters{Mutex::Type::Normal, Mutex::Protocol::PriorityProtect, UINT8_MAX},
			Parameters{Mutex::Type::Normal, Mutex::Protocol::PriorityInheritance, {}},
			Parameters{Mutex::Type::ErrorChecking, Mutex::Protocol::None, {}},
			Parameters{Mutex::Type::ErrorChecking, Mutex::Protocol::PriorityProtect, UINT8_MAX},
			Parameters{Mutex::Type::ErrorChecking, Mutex::Protocol::PriorityInheritance, {}},
			Parameters{Mutex::Type::Recursive, Mutex::Protocol::None, {}},
			Parameters{Mutex::Type::Recursive, Mutex::Protocol::PriorityProtect, UINT8_MAX},
			Parameters{Mutex::Type::Recursive, Mutex::Protocol::PriorityInheritance, {}},
	}};

	constexpr auto phase1ExpectedContextSwitchCount = 2 * (waitForNextTickContextSwitchCount +
			phase1WaitForUntilContextSwitchCount + testMutexAndUnlockContextSwitchCount);
	constexpr auto phase2ExpectedContextSwitchCount = 3 * (waitForNextTickContextSwitchCount +
			phase2ThreadContextSwitchCount + testMutexAndUnlockContextSwitchCount);
	constexpr auto phase3ExpectedContextSwitchCount = 3 * (waitForNextTickContextSwitchCount +
			phase3SoftwareTimerContextSwitchCount + testMutexAndUnlockContextSwitchCount);
	constexpr auto expectedContextSwitchCount = parametersArray.size() * (phase1ExpectedContextSwitchCount +
			phase2ExpectedContextSwitchCount + phase3ExpectedContextSwitchCount);

	const auto contextSwitchCount = statistics::getContextSwitchCount();

	for (const auto& parameters : parametersArray)
		for (const auto& function : {phase1, phase2, phase3})
		{
			Mutex mutex {std::get<0>(parameters), std::get<1>(parameters), std::get<2>(parameters)};
			const auto ret = function(mutex);
			if (ret != true)
				return ret;
		}

	if (statistics::getContextSwitchCount() - contextSwitchCount != expectedContextSwitchCount)
		return false;

	return true;
}

}	// namespace test

}	// namespace distortos
<commit_msg>test: use makeAndStart*Thread() in ConditionVariableOperationsTestCase<commit_after>/**
 * \file
 * \brief ConditionVariableOperationsTestCase class implementation
 *
 * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "ConditionVariableOperationsTestCase.hpp"

#include "waitForNextTick.hpp"
#include "Mutex/mutexTestTryLockWhenLocked.hpp"

#include "distortos/ConditionVariable.hpp"
#include "distortos/DynamicThread.hpp"
#include "distortos/Mutex.hpp"
#include "distortos/ThisThread.hpp"
#include "distortos/StaticSoftwareTimer.hpp"
#include "distortos/statistics.hpp"

namespace distortos
{

namespace test
{

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local constants
+---------------------------------------------------------------------------------------------------------------------*/

/// single duration used in tests
constexpr auto singleDuration = TickClock::duration{1};

/// long duration used in tests
constexpr auto longDuration = singleDuration * 10;

/// expected number of context switches in waitForNextTick(): main -> idle -> main
constexpr decltype(statistics::getContextSwitchCount()) waitForNextTickContextSwitchCount {2};

/// expected number of context switches in testMutexAndUnlock(): main -> idle -> main
constexpr decltype(statistics::getContextSwitchCount()) testMutexAndUnlockContextSwitchCount
{
		waitForNextTickContextSwitchCount + 2
};

/// expected number of context switches in phase1 block involving waitFor() or waitUntil() (excluding
/// waitForNextTick() and testMutexAndUnlock()): 1 - main thread blocks on condition variable (main -> idle), 2 - main
/// thread wakes up (idle -> main)
constexpr decltype(statistics::getContextSwitchCount()) phase1WaitForUntilContextSwitchCount {2};

/// expected number of context switches in phase2 block involving test thread (excluding waitForNextTick() and
/// testMutexAndUnlock()): 1 - test thread starts (main -> test), 2 - test thread goes to sleep (test -> main), 3 - main
/// thread blocks on condition variable (main -> idle), 4 - test thread wakes (idle -> test), 5 - test thread terminates
/// (test -> main)
constexpr decltype(statistics::getContextSwitchCount()) phase2ThreadContextSwitchCount {5};

/// expected number of context switches in phase3 block involving software timer (excluding waitForNextTick() and
/// testMutexAndUnlock()): 1 - main thread blocks on condition variable (main -> idle), 2 - main thread is unblocked by
/// interrupt (idle -> main)
constexpr decltype(statistics::getContextSwitchCount()) phase3SoftwareTimerContextSwitchCount {2};

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

/**
 * \brief Tests whether supplied mutex is properly locked and unlocks it.
 *
 * \param [in] mutex is a reference to mutex that will be tested and unlocked
 *
 * \return true if test succeeded and mutex was sucessfully unlocked, false otherwise
 */

bool testMutexAndUnlock(Mutex& mutex)
{
	const auto testRet = mutexTestTryLockWhenLocked(mutex, UINT8_MAX);
	const auto unlockRet = mutex.unlock();

	return testRet == true && unlockRet == 0;
}

/**
 * \brief Phase 1 of test case.
 *
 * Tests whether both waitFor() and waitUntil() functions properly return some error and properly handle mutex when
 * timing out without notification.
 *
 * \param [in] mutex is a reference to mutex used with condition variable, must be unlocked
 *
 * \return true if test succeeded, false otherwise
 */

bool phase1(Mutex& mutex)
{
	ConditionVariable conditionVariable;

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		// there will be no notification, so waitFor() should time-out at expected time
		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto start = TickClock::now();
		const auto ret = conditionVariable.waitFor(mutex, singleDuration);
		const auto realDuration = TickClock::now() - start;
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != ETIMEDOUT || realDuration != singleDuration + decltype(singleDuration){1} || mutexTest != true ||
				contextSwitches != phase1WaitForUntilContextSwitchCount)
			return false;
	}

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		// there will be no notification, so waitUntil() should time-out at exact expected time
		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + singleDuration;
		const auto ret = conditionVariable.waitUntil(mutex, wakeUpTimePoint);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != ETIMEDOUT || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase1WaitForUntilContextSwitchCount)
			return false;
	}

	return true;
}

/**
 * \brief Phase 2 of test case.
 *
 * Tests thread-thread signaling scenario. Main (current) thread waits for condition variable notification (using
 * wait(), waitFor() and waitUntil()). Test thread notifies one waiter at specified time point, main thread is expected
 * to wake up and acquire ownership of the mutex in the same moment.
 *
 * \param [in] mutex is a reference to mutex used with condition variable, must be unlocked
 *
 * \return true if test succeeded, false otherwise
 */

bool phase2(Mutex& mutex)
{
	constexpr size_t testThreadStackSize {384};

	ConditionVariable conditionVariable;

	const auto sleepUntilFunctor = [&conditionVariable](const TickClock::time_point timePoint)
			{
				ThisThread::sleepUntil(timePoint);
				conditionVariable.notifyOne();
			};

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + longDuration;
		auto thread = makeAndStartDynamicThread({testThreadStackSize, UINT8_MAX}, sleepUntilFunctor, wakeUpTimePoint);

		ThisThread::yield();

		// wait() should succeed at expected time
		const auto ret = conditionVariable.wait(mutex);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		thread.join();
		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != 0 || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase2ThreadContextSwitchCount)
			return false;
	}

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + longDuration;
		auto thread = makeAndStartDynamicThread({testThreadStackSize, UINT8_MAX}, sleepUntilFunctor, wakeUpTimePoint);

		ThisThread::yield();

		// waitFor() should succeed at expected time
		const auto ret = conditionVariable.waitFor(mutex, wakeUpTimePoint - TickClock::now() + longDuration);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		thread.join();
		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != 0 || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase2ThreadContextSwitchCount)
			return false;
	}

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + longDuration;
		auto thread = makeAndStartDynamicThread({testThreadStackSize, UINT8_MAX}, sleepUntilFunctor, wakeUpTimePoint);

		ThisThread::yield();

		// waitUntil() should succeed at expected time
		const auto ret = conditionVariable.waitUntil(mutex, wakeUpTimePoint + longDuration);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		thread.join();
		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != 0 || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase2ThreadContextSwitchCount)
			return false;
	}

	return true;
}

/**
 * \brief Phase 3 of test case.
 *
 * Tests interrupt-thread signaling scenario. Main (current) thread waits for condition variable notification (using
 * wait(), waitFor() and waitUntil()). Software timer is used to notify one waiter at specified time point from
 * interrupt context, main thread is expected to wake up and acquire ownership of the mutex in the same moment.
 *
 * \param [in] mutex is a reference to mutex used with condition variable, must be unlocked
 *
 * \return true if test succeeded, false otherwise
 */

bool phase3(Mutex& mutex)
{
	ConditionVariable conditionVariable;

	auto softwareTimer = makeStaticSoftwareTimer(&ConditionVariable::notifyOne, std::ref(conditionVariable));

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + longDuration;

		softwareTimer.start(wakeUpTimePoint);

		// wait() should succeed at expected time
		const auto ret = conditionVariable.wait(mutex);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != 0 || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase3SoftwareTimerContextSwitchCount)
			return false;
	}

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + longDuration;

		softwareTimer.start(wakeUpTimePoint);

		// waitFor() should succeed at expected time
		const auto ret = conditionVariable.waitFor(mutex, wakeUpTimePoint - TickClock::now() + longDuration);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != 0 || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase3SoftwareTimerContextSwitchCount)
			return false;
	}

	{
		const auto ret = mutex.lock();
		if (ret != 0)
			return false;
	}

	{
		waitForNextTick();

		const auto contextSwitchCount = statistics::getContextSwitchCount();
		const auto wakeUpTimePoint = TickClock::now() + longDuration;

		softwareTimer.start(wakeUpTimePoint);

		// waitUntil() should succeed at expected time
		const auto ret = conditionVariable.waitUntil(mutex, wakeUpTimePoint + longDuration);
		const auto wokenUpTimePoint = TickClock::now();
		const auto contextSwitches = statistics::getContextSwitchCount() - contextSwitchCount;

		const auto mutexTest = testMutexAndUnlock(mutex);
		if (ret != 0 || wakeUpTimePoint != wokenUpTimePoint || mutexTest != true ||
				contextSwitches != phase3SoftwareTimerContextSwitchCount)
			return false;
	}

	return true;
}

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

bool ConditionVariableOperationsTestCase::run_() const
{
	using Parameters = std::tuple<Mutex::Type, Mutex::Protocol, uint8_t>;
	static const std::array<Parameters, 9> parametersArray
	{{
			Parameters{Mutex::Type::Normal, Mutex::Protocol::None, {}},
			Parameters{Mutex::Type::Normal, Mutex::Protocol::PriorityProtect, UINT8_MAX},
			Parameters{Mutex::Type::Normal, Mutex::Protocol::PriorityInheritance, {}},
			Parameters{Mutex::Type::ErrorChecking, Mutex::Protocol::None, {}},
			Parameters{Mutex::Type::ErrorChecking, Mutex::Protocol::PriorityProtect, UINT8_MAX},
			Parameters{Mutex::Type::ErrorChecking, Mutex::Protocol::PriorityInheritance, {}},
			Parameters{Mutex::Type::Recursive, Mutex::Protocol::None, {}},
			Parameters{Mutex::Type::Recursive, Mutex::Protocol::PriorityProtect, UINT8_MAX},
			Parameters{Mutex::Type::Recursive, Mutex::Protocol::PriorityInheritance, {}},
	}};

	constexpr auto phase1ExpectedContextSwitchCount = 2 * (waitForNextTickContextSwitchCount +
			phase1WaitForUntilContextSwitchCount + testMutexAndUnlockContextSwitchCount);
	constexpr auto phase2ExpectedContextSwitchCount = 3 * (waitForNextTickContextSwitchCount +
			phase2ThreadContextSwitchCount + testMutexAndUnlockContextSwitchCount);
	constexpr auto phase3ExpectedContextSwitchCount = 3 * (waitForNextTickContextSwitchCount +
			phase3SoftwareTimerContextSwitchCount + testMutexAndUnlockContextSwitchCount);
	constexpr auto expectedContextSwitchCount = parametersArray.size() * (phase1ExpectedContextSwitchCount +
			phase2ExpectedContextSwitchCount + phase3ExpectedContextSwitchCount);

	const auto contextSwitchCount = statistics::getContextSwitchCount();

	for (const auto& parameters : parametersArray)
		for (const auto& function : {phase1, phase2, phase3})
		{
			Mutex mutex {std::get<0>(parameters), std::get<1>(parameters), std::get<2>(parameters)};
			const auto ret = function(mutex);
			if (ret != true)
				return ret;
		}

	if (statistics::getContextSwitchCount() - contextSwitchCount != expectedContextSwitchCount)
		return false;

	return true;
}

}	// namespace test

}	// namespace distortos
<|endoftext|>


<commit_before>// Copyright (c) 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "config.h"
#include "ScrollbarThemeChromium.h"

#include "AffineTransform.h"
#include "NotImplemented.h"
#include "PlatformContextSkia.h"
#include "PlatformMouseEvent.h"
#include "Scrollbar.h"

#include "gtkdrawing.h"
#include "GdkSkia.h"
#include <gtk/gtk.h>

namespace WebCore {

int ScrollbarThemeChromium::scrollbarThickness(ScrollbarControlSize controlSize)
{
    static int size = 0;
    if (!size) {
        MozGtkScrollbarMetrics metrics;
        moz_gtk_get_scrollbar_metrics(&metrics);
        size = metrics.slider_width;
    }
    return size;
}

bool ScrollbarThemeChromium::invalidateOnMouseEnterExit()
{
    notImplemented();
    return false;
}

// -----------------------------------------------------------------------------
// Given an uninitialised widget state object, set the members such that it's
// sane for drawing scrollbars
// -----------------------------------------------------------------------------
static void initMozState(GtkWidgetState* mozState)
{
    mozState->active = true;
    mozState->focused = false;
    mozState->inHover = false;
    mozState->disabled = false;
    mozState->isDefault = false;
    mozState->canDefault = false;
    mozState->depressed = false;
    mozState->curpos = 0;
    mozState->maxpos = 0;
}

// -----------------------------------------------------------------------------
// Paint a GTK widget
//   gc: context to draw onto
//   rect: the area of the widget
//   widget_type: the type of widget to draw
//   flags: widget dependent flags (e.g. direction of scrollbar arrows etc)
//
// See paintMozWiget in RenderThemeGtk.cpp for an explanation of the clipping.
// -----------------------------------------------------------------------------
static void paintScrollbarWidget(GraphicsContext* gc, const IntRect& rect,
                                 GtkThemeWidgetType widget_type, gint flags)
{
    PlatformContextSkia* pcs = gc->platformContext();

    GdkRectangle gdkRect = { rect.x(), rect.y(), rect.width(), rect.height() };

    const SkIRect clip_region = pcs->canvas()->getTotalClip().getBounds();
    AffineTransform ctm = gc->getCTM().inverse();
    IntPoint pos = ctm.mapPoint(
        IntPoint(SkScalarRound(clip_region.fLeft), SkScalarRound(clip_region.fTop)));
    GdkRectangle gdkClipRect;
    gdkClipRect.x = pos.x();
    gdkClipRect.y = pos.y();
    gdkClipRect.width = clip_region.width();
    gdkClipRect.height = clip_region.height();

    gdk_rectangle_intersect(&gdkRect, &gdkClipRect, &gdkClipRect);

    GtkWidgetState mozState;
    initMozState(&mozState);

    moz_gtk_widget_paint(widget_type, pcs->gdk_skia(), &gdkRect, &gdkClipRect,
                         &mozState, flags, GTK_TEXT_DIR_LTR);
}

void ScrollbarThemeChromium::paintTrackPiece(GraphicsContext* gc, Scrollbar* scrollbar,
                                             const IntRect& rect, ScrollbarPart partType)
{
    const bool horz = scrollbar->orientation() == HorizontalScrollbar;
    const GtkThemeWidgetType track_type =
        horz ? MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL : MOZ_GTK_SCROLLBAR_TRACK_VERTICAL;
    paintScrollbarWidget(gc, rect, track_type, 0);

    return;
}

void ScrollbarThemeChromium::paintButton(GraphicsContext* gc, Scrollbar* scrollbar,
                                    const IntRect& rect, ScrollbarPart part)
{
    // TODO(port): It appears the either we're upsetting GTK by forcing WebKit
    // sizes on it, or the buttons expect the track to be drawn under them.
    // Either way, we end up with unpainted pixels which are upsetting the
    // pixel tests. Thus we paint green under the buttons to, at least, make
    // the pixel output the same between debug and opt builds.
    SkPaint paint;
    paint.setARGB(255, 0, 255, 128);
    SkRect skrect;
    skrect.set(rect.x(), rect.y(), rect.x() + rect.width() - 1, rect.y() + rect.height() + 1);
    gc->platformContext()->canvas()->drawRect(skrect, paint);

    const bool horz = scrollbar->orientation() == HorizontalScrollbar;
    gint flags = horz ? 0 : MOZ_GTK_STEPPER_VERTICAL;
    flags |= ForwardButtonEndPart == part ? MOZ_GTK_STEPPER_DOWN : 0;
    paintScrollbarWidget(gc, rect, MOZ_GTK_SCROLLBAR_BUTTON, flags);
}

void ScrollbarThemeChromium::paintThumb(GraphicsContext* gc, Scrollbar* scrollbar, const IntRect& rect)
{
    const bool horz = scrollbar->orientation() == HorizontalScrollbar;
    const GtkThemeWidgetType thumb_type =
        horz ? MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL : MOZ_GTK_SCROLLBAR_THUMB_VERTICAL;
    paintScrollbarWidget(gc, rect, thumb_type, 0);
}

}  // namespace WebCore
<commit_msg>I bow down before Dean and his "I don't need sleep, I'll just read every line of code" ways.<commit_after>// Copyright (c) 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "config.h"
#include "ScrollbarThemeChromium.h"

#include "AffineTransform.h"
#include "NotImplemented.h"
#include "PlatformContextSkia.h"
#include "PlatformMouseEvent.h"
#include "Scrollbar.h"

#include "gtkdrawing.h"
#include "GdkSkia.h"
#include <gtk/gtk.h>

namespace WebCore {

int ScrollbarThemeChromium::scrollbarThickness(ScrollbarControlSize controlSize)
{
    static int size = 0;
    if (!size) {
        MozGtkScrollbarMetrics metrics;
        moz_gtk_get_scrollbar_metrics(&metrics);
        size = metrics.slider_width;
    }
    return size;
}

bool ScrollbarThemeChromium::invalidateOnMouseEnterExit()
{
    notImplemented();
    return false;
}

// -----------------------------------------------------------------------------
// Given an uninitialised widget state object, set the members such that it's
// sane for drawing scrollbars
// -----------------------------------------------------------------------------
static void initMozState(GtkWidgetState* mozState)
{
    mozState->active = true;
    mozState->focused = false;
    mozState->inHover = false;
    mozState->disabled = false;
    mozState->isDefault = false;
    mozState->canDefault = false;
    mozState->depressed = false;
    mozState->curpos = 0;
    mozState->maxpos = 0;
}

// -----------------------------------------------------------------------------
// Paint a GTK widget
//   gc: context to draw onto
//   rect: the area of the widget
//   widget_type: the type of widget to draw
//   flags: widget dependent flags (e.g. direction of scrollbar arrows etc)
//
// See paintMozWiget in RenderThemeGtk.cpp for an explanation of the clipping.
// -----------------------------------------------------------------------------
static void paintScrollbarWidget(GraphicsContext* gc, const IntRect& rect,
                                 GtkThemeWidgetType widget_type, gint flags)
{
    PlatformContextSkia* pcs = gc->platformContext();

    GdkRectangle gdkRect = { rect.x(), rect.y(), rect.width(), rect.height() };

    const SkIRect clip_region = pcs->canvas()->getTotalClip().getBounds();
    AffineTransform ctm = gc->getCTM().inverse();
    IntPoint pos = ctm.mapPoint(
        IntPoint(SkScalarRound(clip_region.fLeft), SkScalarRound(clip_region.fTop)));
    GdkRectangle gdkClipRect;
    gdkClipRect.x = pos.x();
    gdkClipRect.y = pos.y();
    gdkClipRect.width = clip_region.width();
    gdkClipRect.height = clip_region.height();

    gdk_rectangle_intersect(&gdkRect, &gdkClipRect, &gdkClipRect);

    GtkWidgetState mozState;
    initMozState(&mozState);

    moz_gtk_widget_paint(widget_type, pcs->gdk_skia(), &gdkRect, &gdkClipRect,
                         &mozState, flags, GTK_TEXT_DIR_LTR);
}

void ScrollbarThemeChromium::paintTrackPiece(GraphicsContext* gc, Scrollbar* scrollbar,
                                             const IntRect& rect, ScrollbarPart partType)
{
    const bool horz = scrollbar->orientation() == HorizontalScrollbar;
    const GtkThemeWidgetType track_type =
        horz ? MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL : MOZ_GTK_SCROLLBAR_TRACK_VERTICAL;
    paintScrollbarWidget(gc, rect, track_type, 0);

    return;
}

void ScrollbarThemeChromium::paintButton(GraphicsContext* gc, Scrollbar* scrollbar,
                                    const IntRect& rect, ScrollbarPart part)
{
    // TODO(port): It appears the either we're upsetting GTK by forcing WebKit
    // sizes on it, or the buttons expect the track to be drawn under them.
    // Either way, we end up with unpainted pixels which are upsetting the
    // pixel tests. Thus we paint green under the buttons to, at least, make
    // the pixel output the same between debug and opt builds.
    SkPaint paint;
    paint.setARGB(255, 0, 255, 128);
    SkRect skrect;
    skrect.set(rect.x(), rect.y(), rect.x() + rect.width() - 1, rect.y() + rect.height() - 1);
    gc->platformContext()->canvas()->drawRect(skrect, paint);

    const bool horz = scrollbar->orientation() == HorizontalScrollbar;
    gint flags = horz ? 0 : MOZ_GTK_STEPPER_VERTICAL;
    flags |= ForwardButtonEndPart == part ? MOZ_GTK_STEPPER_DOWN : 0;
    paintScrollbarWidget(gc, rect, MOZ_GTK_SCROLLBAR_BUTTON, flags);
}

void ScrollbarThemeChromium::paintThumb(GraphicsContext* gc, Scrollbar* scrollbar, const IntRect& rect)
{
    const bool horz = scrollbar->orientation() == HorizontalScrollbar;
    const GtkThemeWidgetType thumb_type =
        horz ? MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL : MOZ_GTK_SCROLLBAR_THUMB_VERTICAL;
    paintScrollbarWidget(gc, rect, thumb_type, 0);
}

}  // namespace WebCore
<|endoftext|>


<commit_before>using namespace std;
void selection_sort(int array[], int n)
	{
		int cloc1 = clock();

		int min, index, temp;
		for ( int i = 0 ; i < n - 1 ; i++ )
			{
				min = array[i];

				for ( int j = i+1 ; j < n ; j++)
					{
						if ( array[j] < min )
							{
								index = j;
								min = array[j];
							}
					}
				temp = array[i];
				array[i] = min;
				array[index] = temp;
			}
		int cloc2 = clock();
		int diff = cloc2 - cloc1;
		cout << "Time taken: " << diff <<" seconds";
		//printArray(array, n);
	}<commit_msg>No message, sorry<commit_after>using namespace std;
void selection_sort(int array[], int n)
	{
		int cloc1 = clock();

		int min, index, temp;
		for ( int i = 0 ; i < n - 1 ; i++ )
			{
				min = array[i];
				index = i;
				for ( int j = i+1 ; j < n ; j++)
					{
						if ( array[j] < min )
							{
								index = j;
								min = array[j];
							}
					}
				temp = array[i];
				array[i] = min;
				array[index] = temp;
			}
		int cloc2 = clock();
		int diff = cloc2 - cloc1;
		cout << "Time taken on array: " << diff <<" seconds";
		//printArray(array, n);
	}
void selection_sort(node* head)
	{
		node* ptr, *ptr2, *index, *temp;
		ptr = ptr -> next;
		while ( ptr -> next != NULL && ptr -> next -> next != NULL )
			{
				ptr2 = ptr -> next; // initially supposed as minimum element
				index = ptr;
				int min = ptr -> data
				while ( ptr2 != NULL && ptr2 -> next != NULL )
					{
						if ( min > ptr2 -> data )
							{
								index = ptr2;
								min = ptr2 -> data;
							}
						// SWAP index and ptr

						ptr2 = ptr2 -> next;
					}

			}
	}<|endoftext|>


<commit_before>/**************************************************************************************/
/*                                                                                */
/*  Visualization Library                                                             */
/*  http://www.visualizationlibrary.com                                               */
/*                                                                                */
/*  Copyright (c) 2005-2010, Michele Bosi                                             */
/*  All rights reserved.                                                              */
/*                                                                                */
/*  Redistribution and use in source and binary forms, with or without modification,  */
/*  are permitted provided that the following conditions are met:                     */
/*                                                                                */
/*  - Redistributions of source code must retain the above copyright notice, this     */
/*  list of conditions and the following disclaimer.                                  */
/*                                                                                */
/*  - Redistributions in binary form must reproduce the above copyright notice, this  */
/*  list of conditions and the following disclaimer in the documentation and/or       */
/*  other materials provided with the distribution.                                   */
/*                                                                                */
/*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND   */
/*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED     */
/*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE            */
/*  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR  */
/*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    */
/*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      */
/*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON    */
/*  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT           */
/*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS     */
/*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                      */
/*                                                                                */
/**************************************************************************************/

#ifndef Tessellator_INCLUDE_ONCE
#define Tessellator_INCLUDE_ONCE

#include <vlGraphics/OpenGL.hpp>
#include <vlGraphics/Geometry.hpp>
#include <vlCore/Vector3.hpp>
#include <vector>

#ifndef CALLBACK
#define CALLBACK
#endif

namespace vl
{
  /**
   * Tessellates a complex polygon defined by a set of outlines into a set of triangles that can be rendered by Visualization Library.
   * For more information see the OpenGL Programmer's Guide chapter #11 "Tessellators and Quadrics".
   */
  class Tessellator: public Object
  {
    typedef void (CALLBACK *callback_type)(void);
  public:
    //! Constructor.
    Tessellator();
    ~Tessellator();

    //! The contours that specify the complex polygon to be tessellated.
    const std::vector<dvec3>& contourVerts() const { return mContourVerts; }
    //! The contours that specify the complex polygon to be tessellated.
    std::vector<dvec3>& contourVerts() { return mContourVerts; }

    //! The contours that specify the complex polygon to be tessellated.
    const std::vector<int>& contours() const { return mContours; }
    //! The contours that specify the complex polygon to be tessellated.
    std::vector<int>& contours() { return mContours; }

    //! A set of triangles representing the tessellated polygon.
    const std::vector<fvec3>& tessellatedTris() const { return mTessellatedTris; }
    //! A set of triangles representing the tessellated polygon.
    std::vector<fvec3>& tessellatedTris() { return mTessellatedTris; }

    //! See gluTessNormal documentation.
    void setTessNormal(const fvec3& normal) { mTessNormal = normal; }
    //! See gluTessNormal documentation.
    const fvec3& tessNormal() const { return mTessNormal; }

    //! See gluTessProperty documentation (GLU_TESS_BOUNDARY_ONLY)
    void setBoundaryOnly(bool on) { mBoundaryOnly = on; }
    //! See gluTessProperty documentation (GLU_TESS_BOUNDARY_ONLY)
    bool boundaryOnly() const { return mBoundaryOnly; }

    //! See gluTessProperty documentation (GLU_TESS_TOLERANCE)
    double tolerance() const { return mTolerance; }
    //! See gluTessProperty documentation (GLU_TESS_TOLERANCE)
    void setTolerance(double tolerance) { mTolerance = tolerance; }

    //! See gluTessProperty documentation (GLU_TESS_WINDING_RULE)
    ETessellationWinding windingRule() const { return mWindingRule; }
    //! See gluTessProperty documentation (GLU_TESS_WINDING_RULE)
    void setWindingRule(ETessellationWinding rule) { mWindingRule = rule; }

    /* Tessellates the specified polygon.
     * If \p append_tessellated_tris equals \p true then the previously tessellated triangles are kept and the newly
     * generated triangles are appended to them. This is useful when one has to tessellate several triangles and
     * the result should be accumulated in a single triangle set.
     *
     * After the function is called the contours() and contourVerts() are cleared.
     */
    bool tessellate(bool append_tessellated_tris=false);

    //! Utility function that calls tessellate() and creates a Geometry with the tessellated triangles.
    ref<Geometry> tessellateGeometry(bool append_tessellated_tris=false);

    bool tessellateIntoSinglePolygon() const { return mTessellateIntoSinglePolygon; }
    void setTessellateIntoSinglePolygon(bool on) { mTessellateIntoSinglePolygon = on; }

  protected:
    static void CALLBACK tessBeginData( GLenum type, Tessellator* tessellator );
    static void CALLBACK tessVertexData( dvec3* vec, Tessellator* tessellator );
    static void CALLBACK tessCombineData( GLdouble coords[3], dvec3 *d[4], GLfloat w[4], dvec3 **dataOut, Tessellator* tessellator );
    static void CALLBACK tessEnd(void);
    static void CALLBACK tessError( GLenum errno );
    void freeCombinedVertices();

  protected:
    // input
    std::vector<int> mContours;
    std::vector<dvec3> mContourVerts;
    // output
    std::vector<fvec3> mTessellatedTris;
    // intermediate data
    std::vector< std::vector<fvec3> > mFans;
    std::vector< std::vector<fvec3> > mTriStrips;
    std::vector< std::vector<fvec3> > mLineLoops;
    std::vector< dvec3* > mCombinedVertices;
    GLenum mPrimitiveType;
    // see gluTessNorml()
    fvec3 mTessNormal;
    // see GLU_TESS_BOUNDARY_ONLY
    bool mBoundaryOnly;
    // see GLU_TESS_TOLERANCE
    double mTolerance;
    // see GLU_TESS_WINDING_RULE
    ETessellationWinding mWindingRule;
    // tessellate into a single polygon
    bool mTessellateIntoSinglePolygon;
  };

}

#endif
<commit_msg>minor docs tweaks<commit_after>/**************************************************************************************/
/*                                                                                */
/*  Visualization Library                                                             */
/*  http://www.visualizationlibrary.com                                               */
/*                                                                                */
/*  Copyright (c) 2005-2010, Michele Bosi                                             */
/*  All rights reserved.                                                              */
/*                                                                                */
/*  Redistribution and use in source and binary forms, with or without modification,  */
/*  are permitted provided that the following conditions are met:                     */
/*                                                                                */
/*  - Redistributions of source code must retain the above copyright notice, this     */
/*  list of conditions and the following disclaimer.                                  */
/*                                                                                */
/*  - Redistributions in binary form must reproduce the above copyright notice, this  */
/*  list of conditions and the following disclaimer in the documentation and/or       */
/*  other materials provided with the distribution.                                   */
/*                                                                                */
/*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND   */
/*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED     */
/*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE            */
/*  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR  */
/*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    */
/*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      */
/*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON    */
/*  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT           */
/*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS     */
/*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                      */
/*                                                                                */
/**************************************************************************************/

#ifndef Tessellator_INCLUDE_ONCE
#define Tessellator_INCLUDE_ONCE

#include <vlGraphics/OpenGL.hpp>
#include <vlGraphics/Geometry.hpp>
#include <vlCore/Vector3.hpp>
#include <vector>

#ifndef CALLBACK
#define CALLBACK
#endif

namespace vl
{
  /**
   * Tessellates a complex polygon defined by a set of outlines into a set of triangles that can be rendered by Visualization Library.
   * For more information see the OpenGL Programmer's Guide chapter #11 "Tessellators and Quadrics".
   */
  class Tessellator: public Object
  {
    typedef void (CALLBACK *callback_type)(void);
  public:
    //! Constructor.
    Tessellator();

    //! Destructor
    ~Tessellator();

    //! The contours that specify the complex polygon to be tessellated.
    const std::vector<dvec3>& contourVerts() const { return mContourVerts; }

    //! The contours that specify the complex polygon to be tessellated.
    std::vector<dvec3>& contourVerts() { return mContourVerts; }

    //! The contours that specify the complex polygon to be tessellated.
    const std::vector<int>& contours() const { return mContours; }

    //! The contours that specify the complex polygon to be tessellated.
    std::vector<int>& contours() { return mContours; }

    //! A set of triangles representing the tessellated polygon.
    const std::vector<fvec3>& tessellatedTris() const { return mTessellatedTris; }

    //! A set of triangles representing the tessellated polygon.
    std::vector<fvec3>& tessellatedTris() { return mTessellatedTris; }

    //! See gluTessNormal documentation.
    void setTessNormal(const fvec3& normal) { mTessNormal = normal; }

    //! See gluTessNormal documentation.
    const fvec3& tessNormal() const { return mTessNormal; }

    //! See gluTessProperty documentation (GLU_TESS_BOUNDARY_ONLY)
    void setBoundaryOnly(bool on) { mBoundaryOnly = on; }

    //! See gluTessProperty documentation (GLU_TESS_BOUNDARY_ONLY)
    bool boundaryOnly() const { return mBoundaryOnly; }

    //! See gluTessProperty documentation (GLU_TESS_TOLERANCE)
    double tolerance() const { return mTolerance; }

    //! See gluTessProperty documentation (GLU_TESS_TOLERANCE)
    void setTolerance(double tolerance) { mTolerance = tolerance; }

    //! See gluTessProperty documentation (GLU_TESS_WINDING_RULE)
    ETessellationWinding windingRule() const { return mWindingRule; }

    //! See gluTessProperty documentation (GLU_TESS_WINDING_RULE)
    void setWindingRule(ETessellationWinding rule) { mWindingRule = rule; }

    /*
     * Tessellates the specified polygon.
     * If \p append_tessellated_tris equals \p true then the previously tessellated triangles are kept and the newly
     * generated triangles are appended to them. This is useful when one has to tessellate several triangles and
     * the result should be accumulated in a single triangle set.
     *
     * After the function is called the contours() and contourVerts() are cleared.
     */
    bool tessellate(bool append_tessellated_tris=false);

    //! Utility function that calls tessellate() and creates a Geometry with the tessellated triangles.
    ref<Geometry> tessellateGeometry(bool append_tessellated_tris=false);

    void setTessellateIntoSinglePolygon(bool on) { mTessellateIntoSinglePolygon = on; }

    bool tessellateIntoSinglePolygon() const { return mTessellateIntoSinglePolygon; }

  protected:
    static void CALLBACK tessBeginData( GLenum type, Tessellator* tessellator );
    static void CALLBACK tessVertexData( dvec3* vec, Tessellator* tessellator );
    static void CALLBACK tessCombineData( GLdouble coords[3], dvec3 *d[4], GLfloat w[4], dvec3 **dataOut, Tessellator* tessellator );
    static void CALLBACK tessEnd(void);
    static void CALLBACK tessError( GLenum errno );
    void freeCombinedVertices();

  protected:
    // input
    std::vector<int> mContours;
    std::vector<dvec3> mContourVerts;
    // output
    std::vector<fvec3> mTessellatedTris;
    // intermediate data
    std::vector< std::vector<fvec3> > mFans;
    std::vector< std::vector<fvec3> > mTriStrips;
    std::vector< std::vector<fvec3> > mLineLoops;
    std::vector< dvec3* > mCombinedVertices;
    GLenum mPrimitiveType;
    // see gluTessNorml()
    fvec3 mTessNormal;
    // see GLU_TESS_BOUNDARY_ONLY
    bool mBoundaryOnly;
    // see GLU_TESS_TOLERANCE
    double mTolerance;
    // see GLU_TESS_WINDING_RULE
    ETessellationWinding mWindingRule;
    // tessellate into a single polygon
    bool mTessellateIntoSinglePolygon;
  };

}

#endif
<|endoftext|>


<commit_before>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>

#include "allocore/ui/al_Preset.hpp"
#include "allocore/ui/al_File.hpp"

using namespace al;

// PresetHandler --------------------------------------------------------------

PresetHandler::PresetHandler(std::string rootDirectory, bool verbose) :
    mRootDir(rootDirectory), mVerbose(verbose)
{
	if (!File::exists(rootDirectory)) {
		if (!Dir::make(rootDirectory, true)) {
			std::cout << "Error creating directory: " << rootDirectory << std::endl;
		}
	}
	loadPresetMap();
}

PresetHandler &PresetHandler::registerParameter(Parameter &parameter)
{
	mParameters.push_back(&parameter);
	return *this;
}

void PresetHandler::setSubDirectory(std::string directory)
{
	mSubDir = directory;
	std::string path = getCurrentPath();
	if (!File::exists(path)) {
		if (!Dir::make(path, true)) {
			std::cout << "Error creating directory: " << mRootDir << std::endl;
		}
	}
	loadPresetMap();
}

std::vector<std::string> PresetHandler::availableSubDirectories()
{
	std::vector<std::string> subDirList;
	Dir presetDir(mRootDir);
	while(presetDir.read()) {
		FileInfo info = presetDir.entry();
		if (info.type() == FileInfo::DIR) {
			subDirList.push_back(info.name());
		}
	}
	return subDirList;
}

void PresetHandler::registerPresetCallback(PresetHandler::PresetChangeCallback cb, void *userData)
{
	mCallbacks.push_back(cb);
	mCallbackUdata.push_back(userData);
}

void PresetHandler::storePreset(std::string name)
{
	int index = -1;
	for (auto preset: mPresetsMap) {
		if (preset.second == name) {
			index = preset.first;
			break;
		}
	}
	if (index < 0) {
		for (auto preset: mPresetsMap) {
			if (index <= preset.first) {
				index = preset.first + 1;
			}
		} // FIXME this should look for the first "empty" preset.
	}
	storePreset(index, name);
}

void PresetHandler::storePreset(int index, std::string name)
{
	// TODO need to check if name contains ':' and remove as this causes
	// issues with the text format for saving
	std::lock_guard<std::mutex> lock(mFileLock);
	std::string path = getCurrentPath();
	if (path.back() != '/') {
		path += "/";
	}
	std::ofstream f(path + name + ".preset");
	if (!f.is_open()) {
		if (mVerbose) {
			std::cout << "Error while opening preset file: " << mFileName << std::endl;
		}
		return;
	}
	f << "::" + name << std::endl;
	for(Parameter *parameter: mParameters) {
		std::string line = parameter->getFullAddress() + " f " + std::to_string(parameter->get());
		f << line << std::endl;
	}
	f << "::" << std::endl;
	if (f.bad()) {
		if (mVerbose) {
			std::cout << "Error while writing preset file: " << mFileName << std::endl;
		}
	}
	f.close();
	mPresetsMap[index] = name;
	storePresetMap();
}

void PresetHandler::recallPreset(std::string name)
{
	std::lock_guard<std::mutex> lock(mFileLock);
	std::string path = getCurrentPath();
	if (path.back() != '/') {
		path += "/";
	}
	std::string line;
	std::ifstream f(path + name + ".preset");
	if (!f.is_open()) {
		if (mVerbose) {
			std::cout << "Error while opening preset file: " << mFileName << std::endl;
		}
	}
	while(getline(f, line)) {
		if (line.substr(0, 2) == "::") {
			if (mVerbose) {
				std::cout << "Found preset : " << line << std::endl;
			}
			while (getline(f, line)) {
				if (line.substr(0, 2) == "::") {
					if (mVerbose) {
						std::cout << "End preset."<< std::endl;
					}
					break;
				}
				bool parameterFound = false;
				std::stringstream ss(line);
				std::string address, type, value;
				std::getline(ss, address, ' ');
				std::getline(ss, type, ' ');
				std::getline(ss, value, ' ');
				for(Parameter *param: mParameters) {
					if (param->getFullAddress() == address) {
						if (type == "f") {
							param->set(std::stof(value));
							parameterFound = true;
							break;
						}
					}
				}
				if (!parameterFound && mVerbose) {
					std::cout << "Preset in parameter not present: " << address;
				}
			}
		}
	}
	if (f.bad()) {
		if (mVerbose) {
			std::cout << "Error while writing preset file: " << mFileName << std::endl;
		}
	}
	f.close();
	int index = -1;
	for (auto preset: mPresetsMap) {
		if (preset.second == name) {
			index = preset.first;
			break;
		}
	}
	for(int i = 0; i < mCallbacks.size(); ++i) {
		if (mCallbacks[i]) {
			mCallbacks[i](index, this, mCallbackUdata[i]);
		}
	}
}

std::string PresetHandler::recallPreset(int index)
{
	auto presetNameIt = mPresetsMap.find(index);
	if (presetNameIt != mPresetsMap.end()) {
		recallPreset(presetNameIt->second);
		return presetNameIt->second;
	} else {
		std::cout << "No preset index " << index << std::endl;
	}
	return "";
}

std::map<int, std::string> PresetHandler::availablePresets()
{
	return mPresetsMap;
}

std::string PresetHandler::getCurrentPath()
{
	std::string relPath = mRootDir;
	if (relPath.back() != '/') {
		relPath += "/";
	}
	relPath += mSubDir;
	return relPath;
}

void PresetHandler::loadPresetMap()
{
	std::string mapFileName = getCurrentPath();
	if (mapFileName.back() != '/') {
		mapFileName += "/";
	}
	mapFileName += "_presetMap.txt";
	mPresetsMap.clear();
	if (!File::exists(mapFileName)) {
		std::cout << "No preset map. Creating default." << std::endl;
		std::vector<std::string> presets;
		Dir presetDir(getCurrentPath());
		while(presetDir.read()) {
			FileInfo info = presetDir.entry();
			if (info.type() == FileInfo::REG) {
				std::string name = info.name();
				if (name.substr(name.size()-7) == ".preset") {
					presets.push_back(info.name().substr(0, name.size()-7));
				}
			}
		}
		for(int i = 0; i < presets.size(); ++i) {
			mPresetsMap[i] = presets[i];
		}
		storePresetMap();
	} else {
		std::ifstream f(mapFileName);
		if (!f.is_open()) {
			if (mVerbose) {
				std::cout << "Error while opening preset map file for reading: " << mapFileName << std::endl;
			}
			return;
		}
		std::string line;
		while (getline(f, line)) {
			if (line.substr(0, 2) == "::") {
				if (mVerbose) {
					std::cout << "End preset map."<< std::endl;
				}
				break;
			}
			std::stringstream ss(line);
			std::string index, name;
			std::getline(ss, index, ':');
			std::getline(ss, name, ':');
			mPresetsMap[std::stoi(index)] = name;
//			std::cout << index << ":" << name << std::endl;
		}
		if (f.bad()) {
			if (mVerbose) {
				std::cout << "Error while writing preset map file for reading: " << mFileName << std::endl;
			}
		}

		std::cout << "Reading preset map." << std::endl;
	}
}

void PresetHandler::storePresetMap()
{
	std::string mapFileName = getCurrentPath();
	if (mapFileName.back() != '/') {
		mapFileName += "/";
	}
	mapFileName += "_presetMap.txt";
	std::ofstream f(mapFileName);
	if (!f.is_open()) {
		if (mVerbose) {
			std::cout << "Error while opening preset map file: " << mapFileName << std::endl;
		}
		return;
	}
	for(auto const &preset : mPresetsMap) {
		std::string line = std::to_string(preset.first) + ":" + preset.second;
		f << line << std::endl;
	}
	f << "::" << std::endl;
	if (f.bad()) {
		if (mVerbose) {
			std::cout << "Error while writing preset map file: " << mFileName << std::endl;
		}
	}
	f.close();
}

// PresetServer ----------------------------------------------------------------

PresetServer::PresetServer(std::string oscAddress, int oscPort) :
    mServer(nullptr), mPresetHandler(nullptr), mOSCpath("/preset")
{
	mServer = new osc::Recv(oscPort, oscAddress.c_str());
	if (mServer) {
		mServer->handler(*this);
		mServer->start();
	} else {
		std::cout << "Error starting OSC server." << std::endl;
	}
}

PresetServer::~PresetServer()
{
	if (mServer) {
		mServer->stop();
		delete mServer;
	}
}

void PresetServer::onMessage(osc::Message &m)
{
	if(m.addressPattern() == mOSCpath && m.typeTags() == "f"){
		float val;
		m >> val;
		mPresetHandler->recallPreset((int) val);
		//			std::cout << "ParameterServer::onMessage" << val << std::endl;
	} else if(m.addressPattern() == mOSCpath && m.typeTags() == "i"){
		int val;
		m >> val;
		mPresetHandler->recallPreset(val);
	}
}

void PresetServer::print()
{
	std::cout << "Preset server listening on: " << mServer->address() << ":" << mServer->port() << std::endl;
	std::cout << "Communicating on path: " << mOSCpath << std::endl;
	std::cout << "Registered listeners: " << std::endl;
	for (auto sender:mOSCSenders) {
		std::cout << sender->address() << ":" << sender->port() << std::endl;
	}
}

void PresetServer::changeCallback(int value, void *sender, void *userData)
{
	PresetServer *server = static_cast<PresetServer *>(userData);
	Parameter *parameter = static_cast<Parameter *>(sender);
	server->notifyListeners(server->mOSCpath, value);
}
<commit_msg>Fixed header path<commit_after>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>

#include "allocore/ui/al_Preset.hpp"
#include "allocore/io/al_File.hpp"

using namespace al;

// PresetHandler --------------------------------------------------------------

PresetHandler::PresetHandler(std::string rootDirectory, bool verbose) :
    mRootDir(rootDirectory), mVerbose(verbose)
{
	if (!File::exists(rootDirectory)) {
		if (!Dir::make(rootDirectory, true)) {
			std::cout << "Error creating directory: " << rootDirectory << std::endl;
		}
	}
	loadPresetMap();
}

PresetHandler &PresetHandler::registerParameter(Parameter &parameter)
{
	mParameters.push_back(&parameter);
	return *this;
}

void PresetHandler::setSubDirectory(std::string directory)
{
	mSubDir = directory;
	std::string path = getCurrentPath();
	if (!File::exists(path)) {
		if (!Dir::make(path, true)) {
			std::cout << "Error creating directory: " << mRootDir << std::endl;
		}
	}
	loadPresetMap();
}

std::vector<std::string> PresetHandler::availableSubDirectories()
{
	std::vector<std::string> subDirList;
	Dir presetDir(mRootDir);
	while(presetDir.read()) {
		FileInfo info = presetDir.entry();
		if (info.type() == FileInfo::DIR) {
			subDirList.push_back(info.name());
		}
	}
	return subDirList;
}

void PresetHandler::registerPresetCallback(PresetHandler::PresetChangeCallback cb, void *userData)
{
	mCallbacks.push_back(cb);
	mCallbackUdata.push_back(userData);
}

void PresetHandler::storePreset(std::string name)
{
	int index = -1;
	for (auto preset: mPresetsMap) {
		if (preset.second == name) {
			index = preset.first;
			break;
		}
	}
	if (index < 0) {
		for (auto preset: mPresetsMap) {
			if (index <= preset.first) {
				index = preset.first + 1;
			}
		} // FIXME this should look for the first "empty" preset.
	}
	storePreset(index, name);
}

void PresetHandler::storePreset(int index, std::string name)
{
	// TODO need to check if name contains ':' and remove as this causes
	// issues with the text format for saving
	std::lock_guard<std::mutex> lock(mFileLock);
	std::string path = getCurrentPath();
	if (path.back() != '/') {
		path += "/";
	}
	std::ofstream f(path + name + ".preset");
	if (!f.is_open()) {
		if (mVerbose) {
			std::cout << "Error while opening preset file: " << mFileName << std::endl;
		}
		return;
	}
	f << "::" + name << std::endl;
	for(Parameter *parameter: mParameters) {
		std::string line = parameter->getFullAddress() + " f " + std::to_string(parameter->get());
		f << line << std::endl;
	}
	f << "::" << std::endl;
	if (f.bad()) {
		if (mVerbose) {
			std::cout << "Error while writing preset file: " << mFileName << std::endl;
		}
	}
	f.close();
	mPresetsMap[index] = name;
	storePresetMap();
}

void PresetHandler::recallPreset(std::string name)
{
	std::lock_guard<std::mutex> lock(mFileLock);
	std::string path = getCurrentPath();
	if (path.back() != '/') {
		path += "/";
	}
	std::string line;
	std::ifstream f(path + name + ".preset");
	if (!f.is_open()) {
		if (mVerbose) {
			std::cout << "Error while opening preset file: " << mFileName << std::endl;
		}
	}
	while(getline(f, line)) {
		if (line.substr(0, 2) == "::") {
			if (mVerbose) {
				std::cout << "Found preset : " << line << std::endl;
			}
			while (getline(f, line)) {
				if (line.substr(0, 2) == "::") {
					if (mVerbose) {
						std::cout << "End preset."<< std::endl;
					}
					break;
				}
				bool parameterFound = false;
				std::stringstream ss(line);
				std::string address, type, value;
				std::getline(ss, address, ' ');
				std::getline(ss, type, ' ');
				std::getline(ss, value, ' ');
				for(Parameter *param: mParameters) {
					if (param->getFullAddress() == address) {
						if (type == "f") {
							param->set(std::stof(value));
							parameterFound = true;
							break;
						}
					}
				}
				if (!parameterFound && mVerbose) {
					std::cout << "Preset in parameter not present: " << address;
				}
			}
		}
	}
	if (f.bad()) {
		if (mVerbose) {
			std::cout << "Error while writing preset file: " << mFileName << std::endl;
		}
	}
	f.close();
	int index = -1;
	for (auto preset: mPresetsMap) {
		if (preset.second == name) {
			index = preset.first;
			break;
		}
	}
	for(int i = 0; i < mCallbacks.size(); ++i) {
		if (mCallbacks[i]) {
			mCallbacks[i](index, this, mCallbackUdata[i]);
		}
	}
}

std::string PresetHandler::recallPreset(int index)
{
	auto presetNameIt = mPresetsMap.find(index);
	if (presetNameIt != mPresetsMap.end()) {
		recallPreset(presetNameIt->second);
		return presetNameIt->second;
	} else {
		std::cout << "No preset index " << index << std::endl;
	}
	return "";
}

std::map<int, std::string> PresetHandler::availablePresets()
{
	return mPresetsMap;
}

std::string PresetHandler::getCurrentPath()
{
	std::string relPath = mRootDir;
	if (relPath.back() != '/') {
		relPath += "/";
	}
	relPath += mSubDir;
	return relPath;
}

void PresetHandler::loadPresetMap()
{
	std::string mapFileName = getCurrentPath();
	if (mapFileName.back() != '/') {
		mapFileName += "/";
	}
	mapFileName += "_presetMap.txt";
	mPresetsMap.clear();
	if (!File::exists(mapFileName)) {
		std::cout << "No preset map. Creating default." << std::endl;
		std::vector<std::string> presets;
		Dir presetDir(getCurrentPath());
		while(presetDir.read()) {
			FileInfo info = presetDir.entry();
			if (info.type() == FileInfo::REG) {
				std::string name = info.name();
				if (name.substr(name.size()-7) == ".preset") {
					presets.push_back(info.name().substr(0, name.size()-7));
				}
			}
		}
		for(int i = 0; i < presets.size(); ++i) {
			mPresetsMap[i] = presets[i];
		}
		storePresetMap();
	} else {
		std::ifstream f(mapFileName);
		if (!f.is_open()) {
			if (mVerbose) {
				std::cout << "Error while opening preset map file for reading: " << mapFileName << std::endl;
			}
			return;
		}
		std::string line;
		while (getline(f, line)) {
			if (line.substr(0, 2) == "::") {
				if (mVerbose) {
					std::cout << "End preset map."<< std::endl;
				}
				break;
			}
			std::stringstream ss(line);
			std::string index, name;
			std::getline(ss, index, ':');
			std::getline(ss, name, ':');
			mPresetsMap[std::stoi(index)] = name;
//			std::cout << index << ":" << name << std::endl;
		}
		if (f.bad()) {
			if (mVerbose) {
				std::cout << "Error while writing preset map file for reading: " << mFileName << std::endl;
			}
		}

		std::cout << "Reading preset map." << std::endl;
	}
}

void PresetHandler::storePresetMap()
{
	std::string mapFileName = getCurrentPath();
	if (mapFileName.back() != '/') {
		mapFileName += "/";
	}
	mapFileName += "_presetMap.txt";
	std::ofstream f(mapFileName);
	if (!f.is_open()) {
		if (mVerbose) {
			std::cout << "Error while opening preset map file: " << mapFileName << std::endl;
		}
		return;
	}
	for(auto const &preset : mPresetsMap) {
		std::string line = std::to_string(preset.first) + ":" + preset.second;
		f << line << std::endl;
	}
	f << "::" << std::endl;
	if (f.bad()) {
		if (mVerbose) {
			std::cout << "Error while writing preset map file: " << mFileName << std::endl;
		}
	}
	f.close();
}

// PresetServer ----------------------------------------------------------------

PresetServer::PresetServer(std::string oscAddress, int oscPort) :
    mServer(nullptr), mPresetHandler(nullptr), mOSCpath("/preset")
{
	mServer = new osc::Recv(oscPort, oscAddress.c_str());
	if (mServer) {
		mServer->handler(*this);
		mServer->start();
	} else {
		std::cout << "Error starting OSC server." << std::endl;
	}
}

PresetServer::~PresetServer()
{
	if (mServer) {
		mServer->stop();
		delete mServer;
	}
}

void PresetServer::onMessage(osc::Message &m)
{
	if(m.addressPattern() == mOSCpath && m.typeTags() == "f"){
		float val;
		m >> val;
		mPresetHandler->recallPreset((int) val);
		//			std::cout << "ParameterServer::onMessage" << val << std::endl;
	} else if(m.addressPattern() == mOSCpath && m.typeTags() == "i"){
		int val;
		m >> val;
		mPresetHandler->recallPreset(val);
	}
}

void PresetServer::print()
{
	std::cout << "Preset server listening on: " << mServer->address() << ":" << mServer->port() << std::endl;
	std::cout << "Communicating on path: " << mOSCpath << std::endl;
	std::cout << "Registered listeners: " << std::endl;
	for (auto sender:mOSCSenders) {
		std::cout << sender->address() << ":" << sender->port() << std::endl;
	}
}

void PresetServer::changeCallback(int value, void *sender, void *userData)
{
	PresetServer *server = static_cast<PresetServer *>(userData);
	Parameter *parameter = static_cast<Parameter *>(sender);
	server->notifyListeners(server->mOSCpath, value);
}
<|endoftext|>