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

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

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

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

        signal_base() = default;

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

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

      protected:
        // mutable here allows to connect to a const object's signals
        mutable std::vector<slot_type> slots;
        mutable std::mutex slots_mutex;
      };
    }
    template<typename... ArgTypes>
    class signal : public implementation::signal_base<ArgTypes...>
    {
    public:
      signal() = default;

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

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

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

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

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

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

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

        signal_base() = default;

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

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

      protected:
        // mutable here allows to connect to a const object's signals
        mutable std::vector<slot_type> slots;
        mutable std::mutex slots_mutex;
      };
    }
    template<typename... ArgTypes>
    class signal : public implementation::signal_base<ArgTypes...>
    {
    public:
      signal() = default;

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

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

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

<|endoftext|>


<commit_before>#include "schedulers/shortest_path_switch_scheduler.h"

DEFINE_bool(overtake, true, "Should car overtake others?");
DECLARE_bool(log_overtaking);

using game::Position;
using game::Switch;

namespace schedulers {

ShortestPathSwitchScheduler::ShortestPathSwitchScheduler(
    const game::Race& race,
    game::RaceTracker& race_tracker,
    game::CarTracker& car_tracker)
  : race_(race), car_tracker_(car_tracker), race_tracker_(race_tracker),
    should_switch_now_(false), waiting_for_switch_(false),
    target_switch_(-1) {

  path_optimizer_.reset(new game::GreedyPathOptimizer(race, car_tracker_));
}

bool ShortestPathSwitchScheduler::WaitingToReachIssuedSwitch(const game::CarState& state) {
  if (state.position().piece() == target_switch_) {
    waiting_for_switch_ = false;
    target_switch_ = -1;
  }
  return waiting_for_switch_;
}

// TODO IMPORTANT
// If we switch our decision last second, throttle scheduler may not
// be able to slow down to match switch curvature. We should check it!
//
// Updates and calculates next state
void ShortestPathSwitchScheduler::Schedule(const game::CarState& state) {
  if (WaitingToReachIssuedSwitch(state))
    return;

  // [0 - inf] = time loss for each decision compared to optimum
  auto time_loss = path_optimizer_->Score(state.position());

  // [-10 - 10] = (-)overtake, (0)neutral, (+) bump competitive
  auto obstacle_scores = race_tracker_.ScoreLanes(state);

  Switch best_direction = Switch::kStay;
  int best_score = -10000000;

  for (auto& el : time_loss) {
    Switch dir = el.first;

    // TODO improve weights on scores
    int score =
      - time_loss[dir]                       // ticks
      - std::min(0, obstacle_scores[dir]) * 1000  // overtaking strictly more important
      + std::max(0, obstacle_scores[dir]) * 1;    // bumping just a bit better

    if (score > best_score) {
      best_score = score;
      best_direction = dir;
    }
  }

  if (best_direction == Switch::kStay) {
    should_switch_now_ = false;
    target_switch_ = -1;
  } else {
    should_switch_now_ = true;
    target_switch_ = race_.track().NextSwitch(state.position().piece());
    direction_ = best_direction;
  }
}

void ShortestPathSwitchScheduler::Switched() {
  should_switch_now_ = false;
  waiting_for_switch_ = true;
}

bool ShortestPathSwitchScheduler::ShouldSwitch() {
  if (should_switch_now_ && !waiting_for_switch_) {
    auto s = car_tracker_.current_state();
    s = car_tracker_.Predict(s, game::Command(1));
    s = car_tracker_.Predict(s, game::Command(1));
    if (s.position().piece() != target_switch_)
      return false;

    if (car_tracker_.current_state().velocity() > 0 &&
        car_tracker_.IsSafe(car_tracker_.current_state(), game::Command(direction_)))
      return true;
  }
  return false;
}

game::Switch ShortestPathSwitchScheduler::ExpectedSwitch() {
  if (should_switch_now_ && !waiting_for_switch_) {
    return direction_;
  } else {
    return Switch::kStay;
  }
}

}  // namespace schedulers
<commit_msg>Fix sign bug in scoring enemies<commit_after>#include "schedulers/shortest_path_switch_scheduler.h"

DEFINE_bool(overtake, true, "Should car overtake others?");
DECLARE_bool(log_overtaking);

using game::Position;
using game::Switch;

namespace schedulers {

ShortestPathSwitchScheduler::ShortestPathSwitchScheduler(
    const game::Race& race,
    game::RaceTracker& race_tracker,
    game::CarTracker& car_tracker)
  : race_(race), car_tracker_(car_tracker), race_tracker_(race_tracker),
    should_switch_now_(false), waiting_for_switch_(false),
    target_switch_(-1) {

  path_optimizer_.reset(new game::GreedyPathOptimizer(race, car_tracker_));
}

bool ShortestPathSwitchScheduler::WaitingToReachIssuedSwitch(const game::CarState& state) {
  if (state.position().piece() == target_switch_) {
    waiting_for_switch_ = false;
    target_switch_ = -1;
  }
  return waiting_for_switch_;
}

// TODO IMPORTANT
// If we switch our decision last second, throttle scheduler may not
// be able to slow down to match switch curvature. We should check it!
//
// Updates and calculates next state
void ShortestPathSwitchScheduler::Schedule(const game::CarState& state) {
  if (WaitingToReachIssuedSwitch(state))
    return;

  // [0 - inf] = time loss for each decision compared to optimum
  auto time_loss = path_optimizer_->Score(state.position());

  // [-10 - 10] = (-)overtake, (0)neutral, (+) bump competitive
  auto obstacle_scores = race_tracker_.ScoreLanes(state);

  Switch best_direction = Switch::kStay;
  int best_score = -10000000;

  for (auto& el : time_loss) {
    Switch dir = el.first;

    // TODO improve weights on scores
    int score =
      - time_loss[dir]                       // ticks
      + std::min(0, obstacle_scores[dir]) * 1000  // overtaking strictly more important
      + std::max(0, obstacle_scores[dir]) * 1;    // bumping just a bit better

    if (score > best_score) {
      best_score = score;
      best_direction = dir;
    }
  }

  if (best_direction == Switch::kStay) {
    should_switch_now_ = false;
    target_switch_ = -1;
  } else {
    should_switch_now_ = true;
    target_switch_ = race_.track().NextSwitch(state.position().piece());
    direction_ = best_direction;
  }
}

void ShortestPathSwitchScheduler::Switched() {
  should_switch_now_ = false;
  waiting_for_switch_ = true;
}

bool ShortestPathSwitchScheduler::ShouldSwitch() {
  if (should_switch_now_ && !waiting_for_switch_) {
    auto s = car_tracker_.current_state();
    s = car_tracker_.Predict(s, game::Command(1));
    s = car_tracker_.Predict(s, game::Command(1));
    if (s.position().piece() != target_switch_)
      return false;

    if (car_tracker_.current_state().velocity() > 0 &&
        car_tracker_.IsSafe(car_tracker_.current_state(), game::Command(direction_)))
      return true;
  }
  return false;
}

game::Switch ShortestPathSwitchScheduler::ExpectedSwitch() {
  if (should_switch_now_ && !waiting_for_switch_) {
    return direction_;
  } else {
    return Switch::kStay;
  }
}

}  // namespace schedulers
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 by Matthias Noack, Zuse Institute Berlin
 *
 * Licensed under the BSD License, see LICENSE file for details.
 *
 */

#include <gtest/gtest.h>

#include <algorithm>
#include <vector>

#include "common/test_environment.h"
#include "common/test_rpc_server_dir.h"
#include "common/test_rpc_server_mrc.h"
#include "common/test_rpc_server_osd.h"
#include "xtreemfs/OSDServiceConstants.h"
#include "libxtreemfs/client.h"
#include "libxtreemfs/file_handle.h"
#include "libxtreemfs/options.h"
#include "libxtreemfs/volume.h"
#include "libxtreemfs/xtreemfs_exception.h"
#include "rpc/client.h"

using namespace std;
using namespace xtreemfs::pbrpc;
using namespace xtreemfs::util;

namespace xtreemfs {
namespace rpc {

class AsyncWriteHandlerTest : public ::testing::Test {
 protected:
  enum {
    BLOCK_SIZE = 1024 * 128
  };

  virtual void SetUp() {
    initialize_logger(LEVEL_DEBUG);
    test_env.options.connect_timeout_s = 15;
    test_env.options.request_timeout_s = 5;
    test_env.options.retry_delay_s = 5;
    test_env.options.max_writeahead = 1024 * 128 * 8;
    test_env.options.max_writeahead_requests = 8;

    test_env.options.periodic_xcap_renewal_interval_s = 2;
    test_env.Start();

    // Open a volume
    volume = test_env.client->OpenVolume(
        test_env.volume_name_,
        NULL,  // No SSL options.
        test_env.options);

    // Open a file.
    file = volume->OpenFile(
        test_env.user_credentials,
        "/test_file",
        static_cast<xtreemfs::pbrpc::SYSTEM_V_FCNTL>(
            xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_CREAT |
            xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_TRUNC |
            xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_RDWR));

  }

  virtual void TearDown() {
    //volume->Close();
    test_env.Stop();
  }

  TestEnvironment test_env;
  Volume* volume;
  FileHandle* file;
};

/** A normal async write with nothing special */
TEST_F(AsyncWriteHandlerTest, NormalWrite) {
  size_t blocks = 5;
  size_t buffer_size = BLOCK_SIZE * blocks;
  boost::scoped_array<char> write_buf(new char[buffer_size]);

  vector<WriteEntry> expected(blocks);
  for (int i = 0; i < blocks; ++i) {
    expected[i] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  file->Write(test_env.user_credentials,
              write_buf.get(),
              buffer_size,
              0);

  boost::this_thread::sleep(boost::posix_time::seconds(
      2 * test_env.options.request_timeout_s));

  EXPECT_TRUE(equal(expected.begin(),
                    expected.end(),
                    test_env.osds[0]->GetReceivedWrites().end() - blocks));

  ASSERT_NO_THROW(file->Close());
}

/** Let the first write request fail. The write should be retried and finally
 *  succeed. */
TEST_F(AsyncWriteHandlerTest, FirstWriteFail) {
  size_t blocks = 5;
  size_t buffer_size = BLOCK_SIZE * blocks;
  boost::scoped_array<char> write_buf(new char[buffer_size]);

  vector<WriteEntry> expected_tail(blocks);
  for (int i = 0; i < blocks; ++i) {
    expected_tail[i] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  test_env.osds[0]->AddDropRule(
      new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE, new DropNRule(1)));

  file->Write(test_env.user_credentials, write_buf.get(), buffer_size, 0);

  boost::this_thread::sleep(boost::posix_time::seconds(
      test_env.options.connect_timeout_s +
      2 * test_env.options.request_timeout_s +
      2 * test_env.options.retry_delay_s));

  EXPECT_TRUE(equal(expected_tail.begin(), expected_tail.end(),
      test_env.osds[0]->GetReceivedWrites().end() - expected_tail.size()));

  ASSERT_NO_THROW(file->Close());
}

/** Let the last write request fail. The write should be retried and finally
 *  succeed. */
TEST_F(AsyncWriteHandlerTest, LastWriteFail) {
  size_t blocks = 5;
  size_t buffer_size = BLOCK_SIZE * blocks;
  boost::scoped_array<char> write_buf(new char[buffer_size]);

  vector<WriteEntry> expected_front(blocks - 1);
  vector<WriteEntry> expected_tail(1);
  for (int i = 0; i < blocks - 1; ++i) {
    expected_front[i] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  expected_tail[0] = WriteEntry(blocks - 1, 0, BLOCK_SIZE);

  test_env.osds[0]->AddDropRule(
      new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE,
                           new SkipMDropNRule(blocks - 1, 1)));

  file->Write(test_env.user_credentials, write_buf.get(), buffer_size, 0);

  boost::this_thread::sleep(boost::posix_time::seconds(
      test_env.options.connect_timeout_s +
      2 * test_env.options.request_timeout_s +
      2 * test_env.options.retry_delay_s));

  EXPECT_TRUE(equal(expected_front.begin(),
                    expected_front.end(),
                    test_env.osds[0]->GetReceivedWrites().begin()));
  EXPECT_TRUE(equal(expected_tail.begin(),
                    expected_tail.end(),
                    test_env.osds[0]->GetReceivedWrites().end() -
                        expected_tail.size()));

  ASSERT_NO_THROW(file->Close());
}

/** Let the intermediate write request fail. The write should be retried and
 *  finally succeed. */
TEST_F(AsyncWriteHandlerTest, IntermediateWriteFail) {
  size_t blocks = 5;
  size_t buffer_size = BLOCK_SIZE * blocks;
  size_t middle = blocks / 2;
  boost::scoped_array<char> write_buf(new char[buffer_size]);

  vector<WriteEntry> expected_front(middle);
  vector<WriteEntry> expected_tail(blocks - middle);
  for (int i = 0; i < middle; ++i) {
    expected_front[i] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  for (int i = middle; i < blocks; ++i) {
    expected_tail[i - middle] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  test_env.osds[0]->AddDropRule(
      new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE,
                           new SkipMDropNRule(middle, 1)));

  file->Write(test_env.user_credentials, write_buf.get(), buffer_size, 0);

  boost::this_thread::sleep(boost::posix_time::seconds(
      test_env.options.connect_timeout_s +
      2 * test_env.options.request_timeout_s +
      2 * test_env.options.retry_delay_s));

  EXPECT_TRUE(equal(expected_front.begin(),
                    expected_front.end(),
                    test_env.osds[0]->GetReceivedWrites().begin()));
  EXPECT_TRUE(equal(expected_tail.begin(),
                    expected_tail.end(),
                    test_env.osds[0]->GetReceivedWrites().end() -
                        expected_tail.size()));

  ASSERT_NO_THROW(file->Close());
}

/** Let the all writes request fail. The write should be retried and finally
 *  succeed. */
TEST_F(AsyncWriteHandlerTest, AllWritesFail) {
  size_t blocks = 5;
  size_t buffer_size = BLOCK_SIZE * blocks;
  boost::scoped_array<char> write_buf(new char[buffer_size]);

  vector<WriteEntry> expected_tail(blocks);
  for (int i = 0; i < blocks; ++i) {
    expected_tail[i] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  test_env.osds[0]->AddDropRule(
      new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE,
                           new DropNRule(blocks)));

  file->Write(test_env.user_credentials, write_buf.get(), buffer_size, 0);

  boost::this_thread::sleep(boost::posix_time::seconds(
      test_env.options.connect_timeout_s +
      2 * test_env.options.request_timeout_s +
      2 * test_env.options.retry_delay_s));

  EXPECT_TRUE(equal(expected_tail.begin(),
                    expected_tail.end(),
                    test_env.osds[0]->GetReceivedWrites().end() -
                        expected_tail.size()));

  ASSERT_NO_THROW(file->Close());
}

/** Let the first write request fail when there are more writes than
 *  writeahead allows.  The write should be retried and finally succeed. */
TEST_F(AsyncWriteHandlerTest, FirstWriteFailLong) {
  size_t blocks = 2 * test_env.options.max_writeahead_requests;
  size_t buffer_size = BLOCK_SIZE * blocks;
  boost::scoped_array<char> write_buf(new char[buffer_size]);

  vector<WriteEntry> expected_tail(blocks);
  for (int i = 0; i < blocks; ++i) {
    expected_tail[i] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  test_env.osds[0]->AddDropRule(
      new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE, new DropNRule(1)));

  file->Write(test_env.user_credentials, write_buf.get(), buffer_size, 0);

  boost::this_thread::sleep(boost::posix_time::seconds(
      test_env.options.connect_timeout_s +
      2 * test_env.options.request_timeout_s +
      2 * test_env.options.retry_delay_s));

  EXPECT_TRUE(equal(expected_tail.begin(),
                    expected_tail.end(),
                    test_env.osds[0]->GetReceivedWrites().end() -
                        expected_tail.size()));

  ASSERT_NO_THROW(file->Close());
}

/** TODO(mno): Maybe let all future requests fail to test the retry count. */

}  // namespace rpc
}  // namespace xtreemfs
<commit_msg>client: Modified async_write_handler_test.cpp according to the review for r3306.<commit_after>/*
 * Copyright (c) 2012 by Matthias Noack, Zuse Institute Berlin
 *
 * Licensed under the BSD License, see LICENSE file for details.
 *
 */

#include <gtest/gtest.h>

#include <algorithm>
#include <vector>

#include "common/test_environment.h"
#include "common/test_rpc_server_dir.h"
#include "common/test_rpc_server_mrc.h"
#include "common/test_rpc_server_osd.h"
#include "libxtreemfs/client.h"
#include "libxtreemfs/file_handle.h"
#include "libxtreemfs/options.h"
#include "libxtreemfs/volume.h"
#include "libxtreemfs/xtreemfs_exception.h"
#include "rpc/client.h"
#include "xtreemfs/OSDServiceConstants.h"

using namespace std;
using namespace xtreemfs::pbrpc;
using namespace xtreemfs::util;

namespace xtreemfs {
namespace rpc {

class AsyncWriteHandlerTest : public ::testing::Test {
 protected:
  enum {
    BLOCK_SIZE = 1024 * 128
  };

  virtual void SetUp() {
    initialize_logger(LEVEL_DEBUG);
    test_env.options.connect_timeout_s = 15;
    test_env.options.request_timeout_s = 5;
    test_env.options.retry_delay_s = 5;
    test_env.options.max_writeahead = 1024 * 128 * 8;
    test_env.options.max_writeahead_requests = 8;

    test_env.options.periodic_xcap_renewal_interval_s = 2;
    test_env.Start();

    // Open a volume
    volume = test_env.client->OpenVolume(
        test_env.volume_name_,
        NULL,  // No SSL options.
        test_env.options);

    // Open a file.
    file = volume->OpenFile(
        test_env.user_credentials,
        "/test_file",
        static_cast<xtreemfs::pbrpc::SYSTEM_V_FCNTL>(
            xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_CREAT |
            xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_TRUNC |
            xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_RDWR));

  }

  virtual void TearDown() {
    //volume->Close();
    test_env.Stop();
  }

  TestEnvironment test_env;
  Volume* volume;
  FileHandle* file;
};

/** A normal async write with nothing special */
TEST_F(AsyncWriteHandlerTest, NormalWrite) {
  size_t blocks = 5;
  size_t buffer_size = BLOCK_SIZE * blocks;
  boost::scoped_array<char> write_buf(new char[buffer_size]);

  vector<WriteEntry> expected(blocks);
  for (int i = 0; i < blocks; ++i) {
    expected[i] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  file->Write(test_env.user_credentials,
              write_buf.get(),
              buffer_size,
              0);

  boost::this_thread::sleep(boost::posix_time::seconds(
      2 * test_env.options.request_timeout_s));

  EXPECT_TRUE(equal(expected.begin(),
                    expected.end(),
                    test_env.osds[0]->GetReceivedWrites().end() - blocks));

  ASSERT_NO_THROW(file->Close());
}

/** Let the first write request fail. The write should be retried and finally
 *  succeed. */
TEST_F(AsyncWriteHandlerTest, FirstWriteFail) {
  size_t blocks = 5;
  size_t buffer_size = BLOCK_SIZE * blocks;
  boost::scoped_array<char> write_buf(new char[buffer_size]);

  vector<WriteEntry> expected_tail(blocks);
  for (int i = 0; i < blocks; ++i) {
    expected_tail[i] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  test_env.osds[0]->AddDropRule(
      new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE, new DropNRule(1)));

  file->Write(test_env.user_credentials, write_buf.get(), buffer_size, 0);

  boost::this_thread::sleep(boost::posix_time::seconds(
      test_env.options.connect_timeout_s +
      2 * test_env.options.request_timeout_s +
      2 * test_env.options.retry_delay_s));

  EXPECT_TRUE(equal(expected_tail.begin(), expected_tail.end(),
      test_env.osds[0]->GetReceivedWrites().end() - expected_tail.size()));

  ASSERT_NO_THROW(file->Close());
}

/** Let the last write request fail. The write should be retried and finally
 *  succeed. */
TEST_F(AsyncWriteHandlerTest, LastWriteFail) {
  size_t blocks = 5;
  size_t buffer_size = BLOCK_SIZE * blocks;
  boost::scoped_array<char> write_buf(new char[buffer_size]);

  vector<WriteEntry> expected_front(blocks - 1);
  vector<WriteEntry> expected_tail(1);
  for (int i = 0; i < blocks - 1; ++i) {
    expected_front[i] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  expected_tail[0] = WriteEntry(blocks - 1, 0, BLOCK_SIZE);

  test_env.osds[0]->AddDropRule(
      new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE,
                           new SkipMDropNRule(blocks - 1, 1)));

  file->Write(test_env.user_credentials, write_buf.get(), buffer_size, 0);

  boost::this_thread::sleep(boost::posix_time::seconds(
      test_env.options.connect_timeout_s +
      2 * test_env.options.request_timeout_s +
      2 * test_env.options.retry_delay_s));

  EXPECT_TRUE(equal(expected_front.begin(),
                    expected_front.end(),
                    test_env.osds[0]->GetReceivedWrites().begin()));
  EXPECT_TRUE(equal(expected_tail.begin(),
                    expected_tail.end(),
                    test_env.osds[0]->GetReceivedWrites().end() -
                        expected_tail.size()));

  ASSERT_NO_THROW(file->Close());
}

/** Let the intermediate write request fail. The write should be retried and
 *  finally succeed. */
TEST_F(AsyncWriteHandlerTest, IntermediateWriteFail) {
  size_t blocks = 5;
  size_t buffer_size = BLOCK_SIZE * blocks;
  size_t middle = blocks / 2;
  boost::scoped_array<char> write_buf(new char[buffer_size]);

  vector<WriteEntry> expected_front(middle);
  vector<WriteEntry> expected_tail(blocks - middle);
  for (int i = 0; i < middle; ++i) {
    expected_front[i] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  for (int i = middle; i < blocks; ++i) {
    expected_tail[i - middle] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  test_env.osds[0]->AddDropRule(
      new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE,
                           new SkipMDropNRule(middle, 1)));

  file->Write(test_env.user_credentials, write_buf.get(), buffer_size, 0);

  boost::this_thread::sleep(boost::posix_time::seconds(
      test_env.options.connect_timeout_s +
      2 * test_env.options.request_timeout_s +
      2 * test_env.options.retry_delay_s));

  EXPECT_TRUE(equal(expected_front.begin(),
                    expected_front.end(),
                    test_env.osds[0]->GetReceivedWrites().begin()));
  EXPECT_TRUE(equal(expected_tail.begin(),
                    expected_tail.end(),
                    test_env.osds[0]->GetReceivedWrites().end() -
                        expected_tail.size()));

  ASSERT_NO_THROW(file->Close());
}

/** Let the all writes request fail. The write should be retried and finally
 *  succeed. */
TEST_F(AsyncWriteHandlerTest, AllWritesFail) {
  size_t blocks = 5;
  size_t buffer_size = BLOCK_SIZE * blocks;
  boost::scoped_array<char> write_buf(new char[buffer_size]);

  vector<WriteEntry> expected_tail(blocks);
  for (int i = 0; i < blocks; ++i) {
    expected_tail[i] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  test_env.osds[0]->AddDropRule(
      new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE,
                           new DropNRule(blocks)));

  file->Write(test_env.user_credentials, write_buf.get(), buffer_size, 0);

  boost::this_thread::sleep(boost::posix_time::seconds(
      test_env.options.connect_timeout_s +
      2 * test_env.options.request_timeout_s +
      2 * test_env.options.retry_delay_s));

  EXPECT_TRUE(equal(expected_tail.begin(),
                    expected_tail.end(),
                    test_env.osds[0]->GetReceivedWrites().end() -
                        expected_tail.size()));

  ASSERT_NO_THROW(file->Close());
}

/** Let the first write request fail when there are more writes than
 *  writeahead allows.  The write should be retried and finally succeed. */
TEST_F(AsyncWriteHandlerTest, FirstWriteFailLong) {
  size_t blocks = 2 * test_env.options.max_writeahead_requests;
  size_t buffer_size = BLOCK_SIZE * blocks;
  boost::scoped_array<char> write_buf(new char[buffer_size]);

  vector<WriteEntry> expected_tail(blocks);
  for (int i = 0; i < blocks; ++i) {
    expected_tail[i] = WriteEntry(i, 0, BLOCK_SIZE);
  }

  test_env.osds[0]->AddDropRule(
      new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE, new DropNRule(1)));

  file->Write(test_env.user_credentials, write_buf.get(), buffer_size, 0);

  boost::this_thread::sleep(boost::posix_time::seconds(
      test_env.options.connect_timeout_s +
      2 * test_env.options.request_timeout_s +
      2 * test_env.options.retry_delay_s));

  EXPECT_TRUE(equal(expected_tail.begin(),
                    expected_tail.end(),
                    test_env.osds[0]->GetReceivedWrites().end() -
                        expected_tail.size()));

  ASSERT_NO_THROW(file->Close());
}

/** TODO(mno): Maybe let all future requests fail to test the retry count. */

}  // namespace rpc
}  // namespace xtreemfs
<|endoftext|>


<commit_before>// @(#)root/gfal:$Name:  $:$Id: TGFALFile.cxx,v 1.33 2005/12/09 09:35:07 rdm Exp $
// Author: Fons Rademakers   8/12/2005

/*************************************************************************
 * Copyright (C) 1995-2005, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGFALFile                                                            //
//                                                                      //
// A TGFALFile is like a normal TFile except that it reads and writes   //
// its data via the underlaying Grid access mechanism.                  //
// TGFALFile file names are either a logical file name, a guid, an      //
// SURL or a TURL, like:                                                //
//                                                                      //
//    gfal:/lfn/user/r/rdm/galice.root                                  //
//                                                                      //
// Grid storage interactions today require using several existing       //
// software components:                                                 //
//  - The replica catalog services to locate valid replicas of          //
//    files.                                                            //
//  - The SRM software to ensure:                                       //
//     - files  exist on disk (they are recalled from mass              //
//       storage if necessary) or                                       //
//     - space is allocated on disk for new files (they are possibly    //
//       migrated to mass storage later)                                //
//  - A file access mechanism to access files from the storage          //
//    system on the worker node.                                        //
//                                                                      //
// The GFAL library hides these interactions and presents a Posix       //
// interface for the I/O operations. The currently supported protocols  //
// are: file for local access, dcap, gsidcap and kdcap (dCache access   //
// protocol) and rfio (CASTOR access protocol).                         //
//                                                                      //
// File naming convention:                                              //
// A file name can be a Logical File Name (LFN), a Grid Unique          //
// IDentifier (GUID), a file replica (SURL) or a Transport file         //
// name (TURL):                                                         //
//                                                                      //
//     an LFN starts with lfn:                                          //
//        for example lfn:baud/testgfal15                               //
//                                                                      //
//     a GUID starts with guid:                                         //
//        for example guid:2cd59291-7ae7-4778-af6d-b1f423719441         //
//                                                                      //
//     an SURL starts with srm://                                       //
//         for example srm://wacdr002d.cern.ch:8443/castor/             //
//                    cern.ch/user/b/baud/testgfal15                    //
//                                                                      //
//      a TURL starts with a protocol name:                             //
//          for example rfio:////castor/cern.ch/user/b/baud/testgfal15  //
//                                                                      //
// Note that for the TGFALFile plugin to work, all these pathnames      //
// should be prepended by gfal:.                                        //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TGFALFile.h"
#include "TROOT.h"

#include <gfal_api.h>

ClassImp(TGFALFile)
ClassImp(TGFALSystem)

//______________________________________________________________________________
TGFALFile::TGFALFile(const char *url, Option_t *option, const char *ftitle,
                     Int_t compress)
         : TFile(url, "NET", ftitle, compress), fUrl(url)
{
   // Create a GFAL file object. A GFAL file is the same as a TFile
   // except that it is being accessed via the underlaying Grid access
   // mechanism. The url argument must be of the form: gfal:/lfn/file.root
   // If the file specified in the URL does not exist, is not accessable
   // or can not be created the kZombie bit will be set in the TGFALFile
   // object. Use IsZombie() to see if the file is accessable.
   // For a description of the option and other arguments see the TFile ctor.
   // The preferred interface to this constructor is via TFile::Open().

   fOption = option;
   fOption.ToUpper();

   if (fOption == "NEW")
      fOption = "CREATE";

   Bool_t create   = (fOption == "CREATE") ? kTRUE : kFALSE;
   Bool_t recreate = (fOption == "RECREATE") ? kTRUE : kFALSE;
   Bool_t update   = (fOption == "UPDATE") ? kTRUE : kFALSE;
   Bool_t read     = (fOption == "READ") ? kTRUE : kFALSE;
   if (!create && !recreate && !update && !read) {
      read    = kTRUE;
      fOption = "READ";
   }

   TString stmp;
   char *fname;
   if ((fname = gSystem->ExpandPathName(fUrl.GetFile()))) {
      stmp = fname;
      delete [] fname;
      fname = (char *)stmp.Data();
   } else {
      Error("TGFALFile", "error expanding path %s", fUrl.GetFile());
      goto zombie;
   }

   if (recreate) {
      if (::gfal_access(fname, kFileExists) == 0)
         ::gfal_unlink(fname);
      recreate = kFALSE;
      create   = kTRUE;
      fOption  = "CREATE";
   }
   if (create && ::gfal_access(fname, kFileExists) == 0) {
      Error("TGFALFile", "file %s already exists", fname);
      goto zombie;
   }
   if (update) {
      if (::gfal_access(fname, kFileExists) != 0) {
         update = kFALSE;
         create = kTRUE;
      }
      if (update && ::gfal_access(fname, kWritePermission) != 0) {
         Error("TGFALFile", "no write permission, could not open file %s", fname);
         goto zombie;
      }
   }
   if (read) {
      if (::gfal_access(fname, kFileExists) != 0) {
         Error("TGFALFile", "file %s does not exist", fname);
         goto zombie;
      }
      if (::gfal_access(fname, kReadPermission) != 0) {
         Error("TGFALFile", "no read permission, could not open file %s", fname);
         goto zombie;
      }
   }

   // Connect to file system stream
   fRealName = fname;

   if (create || update) {
#ifndef WIN32
      fD = SysOpen(fname, O_RDWR | O_CREAT, 0644);
#else
      fD = SysOpen(fname, O_RDWR | O_CREAT | O_BINARY, S_IREAD | S_IWRITE);
#endif
      if (fD == -1) {
         SysError("TGFALFile", "file %s can not be opened", fname);
         goto zombie;
      }
      fWritable = kTRUE;
   } else {
#ifndef WIN32
      fD = SysOpen(fname, O_RDONLY, 0644);
#else
      fD = SysOpen(fname, O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
#endif
      if (fD == -1) {
         SysError("TGFALFile", "file %s can not be opened for reading", fname);
         goto zombie;
      }
      fWritable = kFALSE;
   }

   Init(create);

   return;

zombie:
   // error in file opening occured, make this object a zombie
   MakeZombie();
   gDirectory = gROOT;
}

//______________________________________________________________________________
TGFALFile::~TGFALFile()
{
   // GFAL file dtor. Close and flush directory structure.

   Close();
}

//______________________________________________________________________________
Int_t TGFALFile::SysOpen(const char *pathname, Int_t flags, UInt_t mode)
{
   // Interface to system open. All arguments like in POSIX open.

   Int_t ret = ::gfal_open64(pathname, flags, (Int_t) mode);

   return ret;
}

//______________________________________________________________________________
Int_t TGFALFile::SysClose(Int_t fd)
{
   // Interface to system close. All arguments like in POSIX close.

   Int_t ret = ::gfal_close(fd);

   return ret;
}

//______________________________________________________________________________
Int_t TGFALFile::SysRead(Int_t fd, void *buf, Int_t len)
{
   // Interface to system read. All arguments like in POSIX read.

   fOffset += len;
   Int_t ret = ::gfal_read(fd, buf, len);

   return ret;
}

//______________________________________________________________________________
Int_t TGFALFile::SysWrite(Int_t fd, const void *buf, Int_t len)
{
   // Interface to system write. All arguments like in POSIX write.

   fOffset += len;
   Int_t ret = ::gfal_write(fd, buf, len);

   return ret;
}

//______________________________________________________________________________
Long64_t TGFALFile::SysSeek(Int_t fd, Long64_t offset, Int_t whence)
{
   // Interface to system lseek. All arguments like in POSIX lseek
   // except that the offset and return value are Long_t to be able to
   // handle 64 bit file systems.

   if (whence == SEEK_SET && offset == fOffset) return offset;

   Long64_t ret = ::gfal_lseek64(fd, offset, whence);

   if (ret >= 0)
      fOffset = ret;

   return ret;
}

//______________________________________________________________________________
Int_t TGFALFile::SysStat(Int_t fd, Long_t *id, Long64_t *size, Long_t *flags,
                         Long_t *modtime)
{
   // Interface to TSystem:GetPathInfo(). Generally implemented via
   // stat() or fstat().

   struct stat64 &statbuf = fStatBuffer;

   if (fOption != "READ" || !fStatCached) {
      // We are not in read mode, or the file status information is not yet
      // in the cache. Update or read the status information with gfal_stat().

      if (::gfal_stat64(fRealName, &statbuf) >= 0)
         fStatCached = kTRUE;
   }

   if (fStatCached) {
      if (id)
         *id = (statbuf.st_dev << 24) + statbuf.st_ino;
      if (size)
         *size = statbuf.st_size;
      if (modtime)
         *modtime = statbuf.st_mtime;
      if (flags) {
         *flags = 0;
         if (statbuf.st_mode & ((S_IEXEC)|(S_IEXEC>>3)|(S_IEXEC>>6)))
            *flags |= 1;
         if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
            *flags |= 2;
         if ((statbuf.st_mode & S_IFMT) != S_IFREG &&
             (statbuf.st_mode & S_IFMT) != S_IFDIR)
            *flags |= 4;
      }
      return 0;
   }

   return 1;
}

//______________________________________________________________________________
Bool_t TGFALFile::ReadBuffer(char *buf, Int_t len)
{
   // Read specified byte range from remote file via GFAL.
   // Returns kTRUE in case of error.

   Int_t st;
   if ((st = ReadBufferViaCache(buf, len))) {
      if (st == 2)
         return kTRUE;
      return kFALSE;
   }

   return TFile::ReadBuffer(buf, len);
}

//______________________________________________________________________________
Bool_t TGFALFile::WriteBuffer(const char *buf, Int_t len)
{
   // Write specified byte range to remote file via GFAL.
   // Returns kTRUE in case of error.

   if (!IsOpen() || !fWritable) return kTRUE;

   Int_t st;
   if ((st = WriteBufferViaCache(buf, len))) {
      if (st == 2)
         return kTRUE;
      return kFALSE;
   }

   return TFile::WriteBuffer(buf, len);
}

//______________________________________________________________________________
TGFALSystem::TGFALSystem() : TSystem("-gfal", "GFAL Helper System")
{
   // Create helper class that allows directory access via GFAL.

   // name must start with '-' to bypass the TSystem singleton check
   SetName("gfal");

   fDirp = 0;
}

//______________________________________________________________________________
Int_t TGFALSystem::MakeDirectory(const char *dir)
{
   // Make a directory via GFAL.

   TUrl url(dir);

   Int_t ret = ::gfal_mkdir(url.GetFile(), 0755);

   return ret;
}

//______________________________________________________________________________
void *TGFALSystem::OpenDirectory(const char *dir)
{
   // Open a directory via GFAL. Returns an opaque pointer to a dir
   // structure. Returns 0 in case of error.

   if (fDirp) {
      Error("OpenDirectory", "invalid directory pointer (should never happen)");
      fDirp = 0;
   }

   TUrl url(dir);

   struct stat64 finfo;

   if (::gfal_stat64(url.GetFile(), &finfo) < 0)
      return 0;

   if ((finfo.st_mode & S_IFMT) != S_IFDIR)
      return 0;

   fDirp = (void*) ::gfal_opendir(url.GetFile());

   return fDirp;
}

//______________________________________________________________________________
void TGFALSystem::FreeDirectory(void *dirp)
{
   // Free directory via GFAL.

   if (dirp != fDirp) {
      Error("FreeDirectory", "invalid directory pointer (should never happen)");
      return;
   }

   if (dirp)
      ::gfal_closedir((DIR*)dirp);

   fDirp = 0;
}

//______________________________________________________________________________
const char *TGFALSystem::GetDirEntry(void *dirp)
{
   // Get directory entry via GFAL. Returns 0 in case no more entries.

   if (dirp != fDirp) {
      Error("GetDirEntry", "invalid directory pointer (should never happen)");
      return 0;
   }

   struct dirent64 *dp;

   if (dirp) {
      dp = ::gfal_readdir64((DIR*)dirp);
      if (!dp)
         return 0;
      return dp->d_name;
   }
   return 0;
}

//______________________________________________________________________________
Int_t TGFALSystem::GetPathInfo(const char *path, FileStat_t &buf)
{
   // Get info about a file. Info is returned in the form of a FileStat_t
   // structure (see TSystem.h).
   // The function returns 0 in case of success and 1 if the file could
   // not be stat'ed.

   TUrl url(path);

   struct stat64 sbuf;

   if (path && ::gfal_stat64(url.GetFile(), &sbuf) >= 0) {

      buf.fDev    = sbuf.st_dev;
      buf.fIno    = sbuf.st_ino;
      buf.fMode   = sbuf.st_mode;
      buf.fUid    = sbuf.st_uid;
      buf.fGid    = sbuf.st_gid;
      buf.fSize   = sbuf.st_size;
      buf.fMtime  = sbuf.st_mtime;
      buf.fIsLink = kFALSE;

      return 0;
   }
   return 1;
}

//______________________________________________________________________________
Bool_t TGFALSystem::AccessPathName(const char *path, EAccessMode mode)
{
   // Returns FALSE if one can access a file using the specified access mode.
   // Mode is the same as for the Unix access(2) function.
   // Attention, bizarre convention of return value!!

   TUrl url(path);

   if (::gfal_access(url.GetFile(), mode) == 0)
      return kFALSE;

   return kTRUE;
}
<commit_msg>use extern "C" around gfal header file.<commit_after>// @(#)root/gfal:$Name:  $:$Id: TGFALFile.cxx,v 1.1 2005/12/10 01:19:52 rdm Exp $
// Author: Fons Rademakers   8/12/2005

/*************************************************************************
 * Copyright (C) 1995-2005, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGFALFile                                                            //
//                                                                      //
// A TGFALFile is like a normal TFile except that it reads and writes   //
// its data via the underlaying Grid access mechanism.                  //
// TGFALFile file names are either a logical file name, a guid, an      //
// SURL or a TURL, like:                                                //
//                                                                      //
//    gfal:/lfn/user/r/rdm/galice.root                                  //
//                                                                      //
// Grid storage interactions today require using several existing       //
// software components:                                                 //
//  - The replica catalog services to locate valid replicas of          //
//    files.                                                            //
//  - The SRM software to ensure:                                       //
//     - files  exist on disk (they are recalled from mass              //
//       storage if necessary) or                                       //
//     - space is allocated on disk for new files (they are possibly    //
//       migrated to mass storage later)                                //
//  - A file access mechanism to access files from the storage          //
//    system on the worker node.                                        //
//                                                                      //
// The GFAL library hides these interactions and presents a Posix       //
// interface for the I/O operations. The currently supported protocols  //
// are: file for local access, dcap, gsidcap and kdcap (dCache access   //
// protocol) and rfio (CASTOR access protocol).                         //
//                                                                      //
// File naming convention:                                              //
// A file name can be a Logical File Name (LFN), a Grid Unique          //
// IDentifier (GUID), a file replica (SURL) or a Transport file         //
// name (TURL):                                                         //
//                                                                      //
//     an LFN starts with lfn:                                          //
//        for example lfn:baud/testgfal15                               //
//                                                                      //
//     a GUID starts with guid:                                         //
//        for example guid:2cd59291-7ae7-4778-af6d-b1f423719441         //
//                                                                      //
//     an SURL starts with srm://                                       //
//         for example srm://wacdr002d.cern.ch:8443/castor/             //
//                    cern.ch/user/b/baud/testgfal15                    //
//                                                                      //
//      a TURL starts with a protocol name:                             //
//          for example rfio:////castor/cern.ch/user/b/baud/testgfal15  //
//                                                                      //
// Note that for the TGFALFile plugin to work, all these pathnames      //
// should be prepended by gfal:.                                        //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TGFALFile.h"
#include "TROOT.h"

extern "C" {
#include <gfal_api.h>
}

ClassImp(TGFALFile)
ClassImp(TGFALSystem)

//______________________________________________________________________________
TGFALFile::TGFALFile(const char *url, Option_t *option, const char *ftitle,
                     Int_t compress)
         : TFile(url, "NET", ftitle, compress), fUrl(url)
{
   // Create a GFAL file object. A GFAL file is the same as a TFile
   // except that it is being accessed via the underlaying Grid access
   // mechanism. The url argument must be of the form: gfal:/lfn/file.root
   // If the file specified in the URL does not exist, is not accessable
   // or can not be created the kZombie bit will be set in the TGFALFile
   // object. Use IsZombie() to see if the file is accessable.
   // For a description of the option and other arguments see the TFile ctor.
   // The preferred interface to this constructor is via TFile::Open().

   fOption = option;
   fOption.ToUpper();

   if (fOption == "NEW")
      fOption = "CREATE";

   Bool_t create   = (fOption == "CREATE") ? kTRUE : kFALSE;
   Bool_t recreate = (fOption == "RECREATE") ? kTRUE : kFALSE;
   Bool_t update   = (fOption == "UPDATE") ? kTRUE : kFALSE;
   Bool_t read     = (fOption == "READ") ? kTRUE : kFALSE;
   if (!create && !recreate && !update && !read) {
      read    = kTRUE;
      fOption = "READ";
   }

   TString stmp;
   char *fname;
   if ((fname = gSystem->ExpandPathName(fUrl.GetFile()))) {
      stmp = fname;
      delete [] fname;
      fname = (char *)stmp.Data();
   } else {
      Error("TGFALFile", "error expanding path %s", fUrl.GetFile());
      goto zombie;
   }

   if (recreate) {
      if (::gfal_access(fname, kFileExists) == 0)
         ::gfal_unlink(fname);
      recreate = kFALSE;
      create   = kTRUE;
      fOption  = "CREATE";
   }
   if (create && ::gfal_access(fname, kFileExists) == 0) {
      Error("TGFALFile", "file %s already exists", fname);
      goto zombie;
   }
   if (update) {
      if (::gfal_access(fname, kFileExists) != 0) {
         update = kFALSE;
         create = kTRUE;
      }
      if (update && ::gfal_access(fname, kWritePermission) != 0) {
         Error("TGFALFile", "no write permission, could not open file %s", fname);
         goto zombie;
      }
   }
   if (read) {
      if (::gfal_access(fname, kFileExists) != 0) {
         Error("TGFALFile", "file %s does not exist", fname);
         goto zombie;
      }
      if (::gfal_access(fname, kReadPermission) != 0) {
         Error("TGFALFile", "no read permission, could not open file %s", fname);
         goto zombie;
      }
   }

   // Connect to file system stream
   fRealName = fname;

   if (create || update) {
#ifndef WIN32
      fD = SysOpen(fname, O_RDWR | O_CREAT, 0644);
#else
      fD = SysOpen(fname, O_RDWR | O_CREAT | O_BINARY, S_IREAD | S_IWRITE);
#endif
      if (fD == -1) {
         SysError("TGFALFile", "file %s can not be opened", fname);
         goto zombie;
      }
      fWritable = kTRUE;
   } else {
#ifndef WIN32
      fD = SysOpen(fname, O_RDONLY, 0644);
#else
      fD = SysOpen(fname, O_RDONLY | O_BINARY, S_IREAD | S_IWRITE);
#endif
      if (fD == -1) {
         SysError("TGFALFile", "file %s can not be opened for reading", fname);
         goto zombie;
      }
      fWritable = kFALSE;
   }

   Init(create);

   return;

zombie:
   // error in file opening occured, make this object a zombie
   MakeZombie();
   gDirectory = gROOT;
}

//______________________________________________________________________________
TGFALFile::~TGFALFile()
{
   // GFAL file dtor. Close and flush directory structure.

   Close();
}

//______________________________________________________________________________
Int_t TGFALFile::SysOpen(const char *pathname, Int_t flags, UInt_t mode)
{
   // Interface to system open. All arguments like in POSIX open.

   Int_t ret = ::gfal_open64(pathname, flags, (Int_t) mode);

   return ret;
}

//______________________________________________________________________________
Int_t TGFALFile::SysClose(Int_t fd)
{
   // Interface to system close. All arguments like in POSIX close.

   Int_t ret = ::gfal_close(fd);

   return ret;
}

//______________________________________________________________________________
Int_t TGFALFile::SysRead(Int_t fd, void *buf, Int_t len)
{
   // Interface to system read. All arguments like in POSIX read.

   fOffset += len;
   Int_t ret = ::gfal_read(fd, buf, len);

   return ret;
}

//______________________________________________________________________________
Int_t TGFALFile::SysWrite(Int_t fd, const void *buf, Int_t len)
{
   // Interface to system write. All arguments like in POSIX write.

   fOffset += len;
   Int_t ret = ::gfal_write(fd, buf, len);

   return ret;
}

//______________________________________________________________________________
Long64_t TGFALFile::SysSeek(Int_t fd, Long64_t offset, Int_t whence)
{
   // Interface to system lseek. All arguments like in POSIX lseek
   // except that the offset and return value are Long_t to be able to
   // handle 64 bit file systems.

   if (whence == SEEK_SET && offset == fOffset) return offset;

   Long64_t ret = ::gfal_lseek64(fd, offset, whence);

   if (ret >= 0)
      fOffset = ret;

   return ret;
}

//______________________________________________________________________________
Int_t TGFALFile::SysStat(Int_t fd, Long_t *id, Long64_t *size, Long_t *flags,
                         Long_t *modtime)
{
   // Interface to TSystem:GetPathInfo(). Generally implemented via
   // stat() or fstat().

   struct stat64 &statbuf = fStatBuffer;

   if (fOption != "READ" || !fStatCached) {
      // We are not in read mode, or the file status information is not yet
      // in the cache. Update or read the status information with gfal_stat().

      if (::gfal_stat64(fRealName, &statbuf) >= 0)
         fStatCached = kTRUE;
   }

   if (fStatCached) {
      if (id)
         *id = (statbuf.st_dev << 24) + statbuf.st_ino;
      if (size)
         *size = statbuf.st_size;
      if (modtime)
         *modtime = statbuf.st_mtime;
      if (flags) {
         *flags = 0;
         if (statbuf.st_mode & ((S_IEXEC)|(S_IEXEC>>3)|(S_IEXEC>>6)))
            *flags |= 1;
         if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
            *flags |= 2;
         if ((statbuf.st_mode & S_IFMT) != S_IFREG &&
             (statbuf.st_mode & S_IFMT) != S_IFDIR)
            *flags |= 4;
      }
      return 0;
   }

   return 1;
}

//______________________________________________________________________________
Bool_t TGFALFile::ReadBuffer(char *buf, Int_t len)
{
   // Read specified byte range from remote file via GFAL.
   // Returns kTRUE in case of error.

   Int_t st;
   if ((st = ReadBufferViaCache(buf, len))) {
      if (st == 2)
         return kTRUE;
      return kFALSE;
   }

   return TFile::ReadBuffer(buf, len);
}

//______________________________________________________________________________
Bool_t TGFALFile::WriteBuffer(const char *buf, Int_t len)
{
   // Write specified byte range to remote file via GFAL.
   // Returns kTRUE in case of error.

   if (!IsOpen() || !fWritable) return kTRUE;

   Int_t st;
   if ((st = WriteBufferViaCache(buf, len))) {
      if (st == 2)
         return kTRUE;
      return kFALSE;
   }

   return TFile::WriteBuffer(buf, len);
}

//______________________________________________________________________________
TGFALSystem::TGFALSystem() : TSystem("-gfal", "GFAL Helper System")
{
   // Create helper class that allows directory access via GFAL.

   // name must start with '-' to bypass the TSystem singleton check
   SetName("gfal");

   fDirp = 0;
}

//______________________________________________________________________________
Int_t TGFALSystem::MakeDirectory(const char *dir)
{
   // Make a directory via GFAL.

   TUrl url(dir);

   Int_t ret = ::gfal_mkdir(url.GetFile(), 0755);

   return ret;
}

//______________________________________________________________________________
void *TGFALSystem::OpenDirectory(const char *dir)
{
   // Open a directory via GFAL. Returns an opaque pointer to a dir
   // structure. Returns 0 in case of error.

   if (fDirp) {
      Error("OpenDirectory", "invalid directory pointer (should never happen)");
      fDirp = 0;
   }

   TUrl url(dir);

   struct stat64 finfo;

   if (::gfal_stat64(url.GetFile(), &finfo) < 0)
      return 0;

   if ((finfo.st_mode & S_IFMT) != S_IFDIR)
      return 0;

   fDirp = (void*) ::gfal_opendir(url.GetFile());

   return fDirp;
}

//______________________________________________________________________________
void TGFALSystem::FreeDirectory(void *dirp)
{
   // Free directory via GFAL.

   if (dirp != fDirp) {
      Error("FreeDirectory", "invalid directory pointer (should never happen)");
      return;
   }

   if (dirp)
      ::gfal_closedir((DIR*)dirp);

   fDirp = 0;
}

//______________________________________________________________________________
const char *TGFALSystem::GetDirEntry(void *dirp)
{
   // Get directory entry via GFAL. Returns 0 in case no more entries.

   if (dirp != fDirp) {
      Error("GetDirEntry", "invalid directory pointer (should never happen)");
      return 0;
   }

   struct dirent64 *dp;

   if (dirp) {
      dp = ::gfal_readdir64((DIR*)dirp);
      if (!dp)
         return 0;
      return dp->d_name;
   }
   return 0;
}

//______________________________________________________________________________
Int_t TGFALSystem::GetPathInfo(const char *path, FileStat_t &buf)
{
   // Get info about a file. Info is returned in the form of a FileStat_t
   // structure (see TSystem.h).
   // The function returns 0 in case of success and 1 if the file could
   // not be stat'ed.

   TUrl url(path);

   struct stat64 sbuf;

   if (path && ::gfal_stat64(url.GetFile(), &sbuf) >= 0) {

      buf.fDev    = sbuf.st_dev;
      buf.fIno    = sbuf.st_ino;
      buf.fMode   = sbuf.st_mode;
      buf.fUid    = sbuf.st_uid;
      buf.fGid    = sbuf.st_gid;
      buf.fSize   = sbuf.st_size;
      buf.fMtime  = sbuf.st_mtime;
      buf.fIsLink = kFALSE;

      return 0;
   }
   return 1;
}

//______________________________________________________________________________
Bool_t TGFALSystem::AccessPathName(const char *path, EAccessMode mode)
{
   // Returns FALSE if one can access a file using the specified access mode.
   // Mode is the same as for the Unix access(2) function.
   // Attention, bizarre convention of return value!!

   TUrl url(path);

   if (::gfal_access(url.GetFile(), mode) == 0)
      return kFALSE;

   return kTRUE;
}
<|endoftext|>


<commit_before>#include "stdafx.h"

TexMan texMan;

#ifndef _MSC_VER
static GLuint LoadTextureViaOS(const char* name, ivec2& size)
{
	jclass myview = jniEnv->FindClass(boundJavaClass);
	jmethodID method = method = jniEnv->GetStaticMethodID(myview, "loadTexture", "(Ljava/lang/String;)I");
	if (method == 0) {
		return 0;
	}
	GLuint id = jniEnv->CallStaticIntMethod(myview, method, jniEnv->NewStringUTF(name));

	glBindTexture(GL_TEXTURE_2D, id);
	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &size.x);
	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &size.y);
	glBindTexture(GL_TEXTURE_2D, 0);

	return id;
}
#endif

#ifdef _MSC_VER
namespace Gdiplus {
	using std::min;
	using std::max;
}
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")

static GLuint LoadTextureViaOS(const char* name, ivec2& size)
{
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	ULONG_PTR gdiplusToken;
	Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
	WCHAR wc[MAX_PATH];
	MultiByteToWideChar(CP_ACP, 0, name, -1, wc, dimof(wc));
	Gdiplus::Bitmap* image = new Gdiplus::Bitmap(wc);

	int w = (int)image->GetWidth();
	int h = (int)image->GetHeight();
	size.x = w;
	size.y = h;
	Gdiplus::Rect rc(0, 0, w, h);

	Gdiplus::BitmapData* bitmapData = new Gdiplus::BitmapData;
	image->LockBits(&rc, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, bitmapData);

	std::vector<uint32_t> col;
	col.resize(w * h);
	for (int y = 0; y < h; y++) {
		memcpy(&col[y * w], (char*)bitmapData->Scan0 + bitmapData->Stride * y, w * 4);
		for (int x = 0; x < w; x++) {
			uint32_t& c = col[y * w + x];
			c = (c & 0xff00ff00) | ((c & 0xff) << 16) | ((c & 0xff0000) >> 16);
		}
	}
	image->UnlockBits(bitmapData);
	delete bitmapData;
	delete image;
	Gdiplus::GdiplusShutdown(gdiplusToken);
	if (!w || !h) {
		return 0;
	}

	GLuint texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, &col[0]);
	glGenerateMipmap(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, 0);
	return texture;
}
#endif

struct DDSHeader {
	uint32_t h3[3];
	int h, w;
	uint32_t h2[2];
	int mipCnt;
	uint32_t h13[13];
	uint32_t fourcc, bitsPerPixel, rMask, gMask, bMask, aMask, caps1, caps2;
	bool IsCubeMap() const { return caps2 == 0xFE00; }
	int GetArraySize() const { return IsCubeMap() ? 6 : 1; }
	int GetMipCnt() const { return std::max(mipCnt, 1); }
};

static void bitScanForward(uint32_t* result, uint32_t mask)
{
	//	DWORD dwd;
	//	_BitScanForward(&dwd, mask);
	//	*result = dwd;

	for (int i = 0; i < 32; i++) {
		if (mask & (1 << i)) {
			*result = i;
			return;
		}
	}
	*result = 0;
}

static void ArrangeRawDDS(void* img, int size)
{
	const DDSHeader* hdr = (DDSHeader*)img;
	uint32_t rShift, gShift, bShift, aShift;
	bitScanForward(&rShift, hdr->rMask);
	bitScanForward(&gShift, hdr->gMask);
	bitScanForward(&bShift, hdr->bMask);
	bitScanForward(&aShift, hdr->aMask);
	std::for_each((uint32_t*)img + 128 / 4, (uint32_t*)img + size / 4, [&](uint32_t& im) {
		im = ((hdr->aMask & im) >> aShift << 24) + ((hdr->bMask & im) >> bShift << 16) + ((hdr->gMask & im) >> gShift << 8) + ((hdr->rMask & im) >> rShift);
	});
}

static GLuint LoadDDSTexture(const char* name, ivec2& texSize)
{
	int size;
	GLuint texture = 0;
	void* img = LoadFile(name, &size);
	if (!img) {
		aflog("LoadDDSTexture failed! %s", name);
		return 0;
	}
	const DDSHeader* hdr = (DDSHeader*)img;

	GLenum format;
	int(*pitchCalcurator)(int, int) = nullptr;
	switch (hdr->fourcc) {
	case 0x31545844: //'1TXD':
		format = 0x83F1;	// GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
		pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 8; };
		break;
	case 0x33545844: //'3TXD':
		format = 0x83F2;	// GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
		pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 16; };
		break;
	case 0x35545844: //'5TXD':
		format = 0x83F3;	// GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
		pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 16; };
		break;
	default:
		ArrangeRawDDS(img, size);
		format = GL_RGBA;
		pitchCalcurator = [](int w, int h) { return w * h * 4; };
		break;
	}
	texSize.x = hdr->w;
	texSize.y = hdr->h;

	GLenum target = hdr->IsCubeMap() ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
	GLenum targetFace = hdr->IsCubeMap() ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : GL_TEXTURE_2D;

	glGenTextures(1, &texture);
	glBindTexture(target, texture);
	glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);

	int arraySize = hdr->GetArraySize();
	int mipCnt = hdr->GetMipCnt();
	int offset = 128;
	for (int a = 0; a < arraySize; a++) {
		for (int m = 0; m < mipCnt; m++) {
			int w = std::max(1, hdr->w >> m);
			int h = std::max(1, hdr->h >> m);
			if (format == GL_RGBA) {
				afHandleGLError(glTexImage2D(targetFace + a, m, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (char*)img + offset));
			} else {
				int texSize = pitchCalcurator(w, h);
				afHandleGLError(glCompressedTexImage2D(targetFace + a, m, format, w, h, 0, texSize, (char*)img + offset));
			}
			offset += pitchCalcurator(w, h);
		}
	}
	if (mipCnt == 1) {
		if (format == GL_RGBA) {
			afHandleGLError(glGenerateMipmap(target));
		} else {
			glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);	// temporary disable mipmap
		}
	}

	glBindTexture(target, 0);
	free(img);
	return texture;
}

static GLuint LoadTexture(const char* name, ivec2& size)
{
	size = ivec2();
	int len = strlen(name);
	if (len > 4 && !stricmp(name + len - 4, ".dds")) {
		return LoadDDSTexture(name, size);
	} else {
		return LoadTextureViaOS(name, size);
	}
}

TexMan::TMID TexMan::CreateDynamicTexture(const char* name, int w, int h)
{
	auto it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	TMID id = nameToId[name] = afCreateDynamicTexture(w, h, AFDT_R8G8B8A8_UINT);
	ivec2 size(w, h);
	StoreTexState(id, size);
	return id;
}

TexMan::TMID TexMan::Create(const char *name)
{
	NameToId::iterator it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	ivec2 size;
	TMID id = nameToId[name] = LoadTexture(name, size);
	StoreTexState(id, size);
	return id;
}

TexMan::TMID TexMan::CreateWhiteTexture()
{
	const std::string name = "$WHITE";
	NameToId::iterator it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	TMID id = nameToId[name] = afCreateWhiteTexture();
	ivec2 size(1, 1);
	StoreTexState(id, size);
	return id;
}

void TexMan::Destroy()
{
	for (NameToId::iterator it = nameToId.begin(); it != nameToId.end(); ++it)
	{
		GLuint id[1] = { it->second };
		glDeleteTextures(1, id);
	}
	nameToId.clear();
}

void TexMan::StoreTexState(TMID id, const ivec2& v2)
{
	if (id >= texStates.size() ) {
		texStates.resize(id + 1);
	}
	texStates[id] = v2;
}

ivec2 TexMan::GetSize(TMID id)
{
	if (id >= texStates.size()) {
		return ivec2();
	}
	return texStates[id];
}

void TexMan::Write(TMID id, const void* buf)
{
	ivec2 v = GetSize(id);
	glBindTexture(GL_TEXTURE_2D, id);
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v.x, v.y, GL_RGBA, GL_UNSIGNED_BYTE, buf);
	glBindTexture(GL_TEXTURE_2D, 0);
}
<commit_msg>Fill alpha for "X8R8G8B8" dds format<commit_after>#include "stdafx.h"

TexMan texMan;

#ifndef _MSC_VER
static GLuint LoadTextureViaOS(const char* name, ivec2& size)
{
	jclass myview = jniEnv->FindClass(boundJavaClass);
	jmethodID method = method = jniEnv->GetStaticMethodID(myview, "loadTexture", "(Ljava/lang/String;)I");
	if (method == 0) {
		return 0;
	}
	GLuint id = jniEnv->CallStaticIntMethod(myview, method, jniEnv->NewStringUTF(name));

	glBindTexture(GL_TEXTURE_2D, id);
	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &size.x);
	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &size.y);
	glBindTexture(GL_TEXTURE_2D, 0);

	return id;
}
#endif

#ifdef _MSC_VER
namespace Gdiplus {
	using std::min;
	using std::max;
}
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")

static GLuint LoadTextureViaOS(const char* name, ivec2& size)
{
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	ULONG_PTR gdiplusToken;
	Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
	WCHAR wc[MAX_PATH];
	MultiByteToWideChar(CP_ACP, 0, name, -1, wc, dimof(wc));
	Gdiplus::Bitmap* image = new Gdiplus::Bitmap(wc);

	int w = (int)image->GetWidth();
	int h = (int)image->GetHeight();
	size.x = w;
	size.y = h;
	Gdiplus::Rect rc(0, 0, w, h);

	Gdiplus::BitmapData* bitmapData = new Gdiplus::BitmapData;
	image->LockBits(&rc, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, bitmapData);

	std::vector<uint32_t> col;
	col.resize(w * h);
	for (int y = 0; y < h; y++) {
		memcpy(&col[y * w], (char*)bitmapData->Scan0 + bitmapData->Stride * y, w * 4);
		for (int x = 0; x < w; x++) {
			uint32_t& c = col[y * w + x];
			c = (c & 0xff00ff00) | ((c & 0xff) << 16) | ((c & 0xff0000) >> 16);
		}
	}
	image->UnlockBits(bitmapData);
	delete bitmapData;
	delete image;
	Gdiplus::GdiplusShutdown(gdiplusToken);
	if (!w || !h) {
		return 0;
	}

	GLuint texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, &col[0]);
	glGenerateMipmap(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, 0);
	return texture;
}
#endif

struct DDSHeader {
	uint32_t h3[3];
	int h, w;
	uint32_t h2[2];
	int mipCnt;
	uint32_t h13[13];
	uint32_t fourcc, bitsPerPixel, rMask, gMask, bMask, aMask, caps1, caps2;
	bool IsCubeMap() const { return caps2 == 0xFE00; }
	int GetArraySize() const { return IsCubeMap() ? 6 : 1; }
	int GetMipCnt() const { return std::max(mipCnt, 1); }
};

static void bitScanForward(uint32_t* result, uint32_t mask)
{
	//	DWORD dwd;
	//	_BitScanForward(&dwd, mask);
	//	*result = dwd;

	for (int i = 0; i < 32; i++) {
		if (mask & (1 << i)) {
			*result = i;
			return;
		}
	}
	*result = 0;
}

static void ArrangeRawDDS(void* img, int size)
{
	const DDSHeader* hdr = (DDSHeader*)img;
	uint32_t rShift, gShift, bShift, aShift;
	bitScanForward(&rShift, hdr->rMask);
	bitScanForward(&gShift, hdr->gMask);
	bitScanForward(&bShift, hdr->bMask);
	bitScanForward(&aShift, hdr->aMask);
	std::for_each((uint32_t*)img + 128 / 4, (uint32_t*)img + size / 4, [&](uint32_t& im) {
		im = ((hdr->aMask & im) >> aShift << 24) + ((hdr->bMask & im) >> bShift << 16) + ((hdr->gMask & im) >> gShift << 8) + ((hdr->rMask & im) >> rShift);
		if (hdr->aMask == 0) {
			im |= 0xff000000;
		}
	});
}

static GLuint LoadDDSTexture(const char* name, ivec2& texSize)
{
	int size;
	GLuint texture = 0;
	void* img = LoadFile(name, &size);
	if (!img) {
		aflog("LoadDDSTexture failed! %s", name);
		return 0;
	}
	const DDSHeader* hdr = (DDSHeader*)img;

	GLenum format;
	int(*pitchCalcurator)(int, int) = nullptr;
	switch (hdr->fourcc) {
	case 0x31545844: //'1TXD':
		format = 0x83F1;	// GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
		pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 8; };
		break;
	case 0x33545844: //'3TXD':
		format = 0x83F2;	// GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
		pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 16; };
		break;
	case 0x35545844: //'5TXD':
		format = 0x83F3;	// GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
		pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 16; };
		break;
	default:
		ArrangeRawDDS(img, size);
		format = GL_RGBA;
		pitchCalcurator = [](int w, int h) { return w * h * 4; };
		break;
	}
	texSize.x = hdr->w;
	texSize.y = hdr->h;

	GLenum target = hdr->IsCubeMap() ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
	GLenum targetFace = hdr->IsCubeMap() ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : GL_TEXTURE_2D;

	glGenTextures(1, &texture);
	glBindTexture(target, texture);
	glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);

	int arraySize = hdr->GetArraySize();
	int mipCnt = hdr->GetMipCnt();
	int offset = 128;
	for (int a = 0; a < arraySize; a++) {
		for (int m = 0; m < mipCnt; m++) {
			int w = std::max(1, hdr->w >> m);
			int h = std::max(1, hdr->h >> m);
			if (format == GL_RGBA) {
				afHandleGLError(glTexImage2D(targetFace + a, m, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (char*)img + offset));
			} else {
				int texSize = pitchCalcurator(w, h);
				afHandleGLError(glCompressedTexImage2D(targetFace + a, m, format, w, h, 0, texSize, (char*)img + offset));
			}
			offset += pitchCalcurator(w, h);
		}
	}
	if (mipCnt == 1) {
		if (format == GL_RGBA) {
			afHandleGLError(glGenerateMipmap(target));
		} else {
			glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);	// temporary disable mipmap
		}
	}

	glBindTexture(target, 0);
	free(img);
	return texture;
}

static GLuint LoadTexture(const char* name, ivec2& size)
{
	size = ivec2();
	int len = strlen(name);
	if (len > 4 && !stricmp(name + len - 4, ".dds")) {
		return LoadDDSTexture(name, size);
	} else {
		return LoadTextureViaOS(name, size);
	}
}

TexMan::TMID TexMan::CreateDynamicTexture(const char* name, int w, int h)
{
	auto it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	TMID id = nameToId[name] = afCreateDynamicTexture(w, h, AFDT_R8G8B8A8_UINT);
	ivec2 size(w, h);
	StoreTexState(id, size);
	return id;
}

TexMan::TMID TexMan::Create(const char *name)
{
	NameToId::iterator it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	ivec2 size;
	TMID id = nameToId[name] = LoadTexture(name, size);
	StoreTexState(id, size);
	return id;
}

TexMan::TMID TexMan::CreateWhiteTexture()
{
	const std::string name = "$WHITE";
	NameToId::iterator it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	TMID id = nameToId[name] = afCreateWhiteTexture();
	ivec2 size(1, 1);
	StoreTexState(id, size);
	return id;
}

void TexMan::Destroy()
{
	for (NameToId::iterator it = nameToId.begin(); it != nameToId.end(); ++it)
	{
		GLuint id[1] = { it->second };
		glDeleteTextures(1, id);
	}
	nameToId.clear();
}

void TexMan::StoreTexState(TMID id, const ivec2& v2)
{
	if (id >= texStates.size() ) {
		texStates.resize(id + 1);
	}
	texStates[id] = v2;
}

ivec2 TexMan::GetSize(TMID id)
{
	if (id >= texStates.size()) {
		return ivec2();
	}
	return texStates[id];
}

void TexMan::Write(TMID id, const void* buf)
{
	ivec2 v = GetSize(id);
	glBindTexture(GL_TEXTURE_2D, id);
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v.x, v.y, GL_RGBA, GL_UNSIGNED_BYTE, buf);
	glBindTexture(GL_TEXTURE_2D, 0);
}
<|endoftext|>


<commit_before>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SkBenchmark.h"
#include "SkBitmap.h"
#include "SkPaint.h"
#include "SkCanvas.h"
#include "SkColorPriv.h"
#include "SkRandom.h"
#include "SkString.h"

static void drawIntoBitmap(const SkBitmap& bm) {
    const int w = bm.width();
    const int h = bm.height();

    SkCanvas canvas(bm);
    SkPaint p;
    p.setAntiAlias(true);
    p.setColor(SK_ColorRED);
    canvas.drawCircle(SkIntToScalar(w)/2, SkIntToScalar(h)/2,
                      SkIntToScalar(SkMin32(w, h))*3/8, p);

    SkRect r;
    r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SkIntToScalar(4));
    p.setColor(SK_ColorBLUE);
    canvas.drawRect(r, p);
}

/*  Variants for bitmaprect
    src : entire bitmap, subset, fractional subset
    dst : same size as src, diff size
    paint : filter-p
 */

class BitmapRectBench : public SkBenchmark {
    SkBitmap    fBitmap;
    bool        fDoFilter;
    uint8_t     fAlpha;
    SkString    fName;
    SkRect      fSrcR, fDstR;
    enum { N = SkBENCHLOOP(300) };
public:
    BitmapRectBench(void* param, U8CPU alpha, bool doFilter) : INHERITED(param) {
        fAlpha = SkToU8(alpha);
        fDoFilter = doFilter;

        const int w = 128;
        const int h = 128;

        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
        fBitmap.allocPixels();
        fBitmap.setIsOpaque(true);
        fBitmap.eraseColor(SK_ColorBLACK);
        drawIntoBitmap(fBitmap);

        fSrcR.iset(0, 0, w, h);
        fDstR.iset(0, 0, w, h);
    }

protected:
    virtual const char* onGetName() {
        fName.printf("bitmaprect_%02X_%sfilter", fAlpha, fDoFilter ? "" : "no");
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) {
        SkRandom rand;

        SkPaint paint;
        this->setupPaint(&paint);
        paint.setFilterBitmap(fDoFilter);
        paint.setAlpha(fAlpha);

        for (int i = 0; i < N; i++) {
            canvas->drawBitmapRectToRect(fBitmap, &fSrcR, fDstR, &paint);
        }
    }

private:
    typedef SkBenchmark INHERITED;
};

DEF_BENCH(return new BitmapRectBench(p, 0xFF, false))
DEF_BENCH(return new BitmapRectBench(p, 0x80, false))
DEF_BENCH(return new BitmapRectBench(p, 0xFF, true))
DEF_BENCH(return new BitmapRectBench(p, 0x80, true))

<commit_msg>add benches for drawBitmapRectToRect with slight scale/translate<commit_after>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SkBenchmark.h"
#include "SkBitmap.h"
#include "SkPaint.h"
#include "SkCanvas.h"
#include "SkColorPriv.h"
#include "SkRandom.h"
#include "SkString.h"

static void drawIntoBitmap(const SkBitmap& bm) {
    const int w = bm.width();
    const int h = bm.height();

    SkCanvas canvas(bm);
    SkPaint p;
    p.setAntiAlias(true);
    p.setColor(SK_ColorRED);
    canvas.drawCircle(SkIntToScalar(w)/2, SkIntToScalar(h)/2,
                      SkIntToScalar(SkMin32(w, h))*3/8, p);

    SkRect r;
    r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SkIntToScalar(4));
    p.setColor(SK_ColorBLUE);
    canvas.drawRect(r, p);
}

/*  Variants for bitmaprect
    src : entire bitmap, subset, fractional subset
    dst : same size as src, diff size
    paint : filter-p
 */

class BitmapRectBench : public SkBenchmark {
    SkBitmap    fBitmap;
    bool        fDoFilter;
    bool        fSlightMatrix;
    uint8_t     fAlpha;
    SkString    fName;
    SkRect      fSrcR, fDstR;
    enum { N = SkBENCHLOOP(300) };
public:
    BitmapRectBench(void* param, U8CPU alpha, bool doFilter, bool slightMatrix) : INHERITED(param) {
        fAlpha = SkToU8(alpha);
        fDoFilter = doFilter;
        fSlightMatrix = slightMatrix;

        const int w = 128;
        const int h = 128;

        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
        fBitmap.allocPixels();
        fBitmap.setIsOpaque(true);
        fBitmap.eraseColor(SK_ColorBLACK);
        drawIntoBitmap(fBitmap);

        fSrcR.iset(0, 0, w, h);
        fDstR.iset(0, 0, w, h);

        if (slightMatrix) {
            // want fractional translate
            fDstR.offset(SK_Scalar1 / 3, SK_Scalar1 * 5 / 7);
            // want enough to create a scale matrix, but not enough to scare
            // off our sniffer which tries to see if the matrix is "effectively"
            // translate-only.
            fDstR.fRight += SK_Scalar1 / (w * 60);
        }
    }

protected:
    virtual const char* onGetName() {
        fName.printf("bitmaprect_%02X_%sfilter_%s",
                     fAlpha, fDoFilter ? "" : "no",
                     fSlightMatrix ? "trans" : "identity");
        return fName.c_str();
    }

    virtual void onDraw(SkCanvas* canvas) {
        SkRandom rand;

        SkPaint paint;
        this->setupPaint(&paint);
        paint.setFilterBitmap(fDoFilter);
        paint.setAlpha(fAlpha);

        for (int i = 0; i < N; i++) {
            canvas->drawBitmapRectToRect(fBitmap, &fSrcR, fDstR, &paint);
        }
    }

private:
    typedef SkBenchmark INHERITED;
};

DEF_BENCH(return new BitmapRectBench(p, 0xFF, false, false))
DEF_BENCH(return new BitmapRectBench(p, 0x80, false, false))
DEF_BENCH(return new BitmapRectBench(p, 0xFF, true, false))
DEF_BENCH(return new BitmapRectBench(p, 0x80, true, false))

DEF_BENCH(return new BitmapRectBench(p, 0xFF, false, true))
DEF_BENCH(return new BitmapRectBench(p, 0xFF, true, true))

<|endoftext|>


<commit_before>// Copyright (C) 2014 Călin Cruceru <calin@rosedu.org>
//
// This program is free software licenced under MIT Licence. You can
// find a copy of this licence in LICENCE.txt in the top directory of
// source code.
//

// Project specific
#include "Board.h"

// libc++ and libc
#include <cassert>
#include <cstdlib>
#include <cmath>

namespace Puzzle15
{

void swap(Board &first, Board &second) noexcept
{
    using std::swap;
    swap(first.m_blocks, second.m_blocks);
}

Board::Board()
    : m_size(0)
{
    // nothing to do
}

Board::Board(const Board &other)
    : m_blocks(other.m_blocks),
      m_size(other.m_size)
{
    // nothing to do
}

Board::Board(Board &&other)
    : m_blocks(std::move(other.m_blocks)),
      m_size(other.m_size)
{
    // nothing to do
}

Board::Board(const std::vector<int> &initBlocks, int dim)
    : m_blocks(initBlocks),
      m_size(dim)
{
    // nothing to do
}

Board::~Board()
{
    // nothing to do
}

Board &Board::operator=(Board other)
{
    swap(*this, other);
    return *this;
}

bool Board::operator==(const Board &other) const
{
    assert(m_blocks.size() == other.m_blocks.size());
    return m_blocks == other.m_blocks && m_size == other.m_size;
}

bool Board::operator!=(const Board &other) const
{
    return !this->operator==(other);
}

int Board::dimension() const
{
    return m_size;
}

int Board::hamming() const
{
    auto count = 0;
    for (auto it = m_blocks.cbegin(); it != m_blocks.cend(); ++it)
        count += (*it == it - m_blocks.cbegin() + 1 ? 1 : 0);

    return count;
}

int Board::manhattan() const
{
    auto count = 0;
    for (auto it = m_blocks.cbegin(); it != m_blocks.cend(); ++it) {
        int i = (it - m_blocks.cbegin())/m_size;
        int j = it - m_blocks.cbegin() - i*m_size;
        int goalRow = (*it - 1)/m_size;
        int goalColumn = *it - goalRow*m_size - 1;

        count += (abs(goalRow - i) + abs(goalColumn - j));
    }

    return count;
}

bool Board::isGoal() const
{
    for (auto it = m_blocks.cbegin(); it != m_blocks.cend() - 1; ++it)
        if (*it != it - m_blocks.cbegin() + 1)
            return false;

    assert(m_blocks.back() == 0);
    return true;
}

Board Board::twin() const
{
    // more intelligent choice of the twin?
    auto twinBoard = *this;
    if (!m_blocks.front()) {
        auto tmp = m_blocks[2];
        twinBoard.m_blocks[2] = m_blocks[1];
        twinBoard.m_blocks[1] = tmp;
    } else if (!m_blocks[1]) {
        auto tmp = m_blocks[m_size];
        twinBoard.m_blocks[m_size] = m_blocks[m_size+1];
        twinBoard.m_blocks[m_size+1] = tmp;
    } else {
        auto tmp = m_blocks.front();
        twinBoard.m_blocks.front() = m_blocks[1];
        twinBoard.m_blocks[1] = tmp;
    }

    return twinBoard;
}

Queue<Board> Board::neighbours() const
{
    Queue<Board> neighList;
    int emptyRow, emptyColumn;
    emptyPosition(emptyRow, emptyColumn);

    if (emptyRow > 0)
        neighList.enqueue(upperNeighbour(emptyRow, emptyColumn));
    if (emptyRow < m_size - 1)
        neighList.enqueue(lowerNeighbour(emptyRow, emptyColumn));
    if (emptyColumn > 0)
        neighList.enqueue(leftNeighbour(emptyRow, emptyColumn));
    if (emptyColumn < m_size - 1)
        neighList.enqueue(rightNeighbour(emptyRow, emptyColumn));

    return neighList;
}

std::string Board::toString() const
{
    std::string result;
    for (auto it = m_blocks.cbegin(); it != m_blocks.cend(); ++it) {
        auto i = (it - m_blocks.cbegin())/m_size;
        auto j = it - m_blocks.cbegin() - i*m_size;

        result += std::to_string(*it);
        if (j < m_size - 1)
            result += " ";
        else
            result += "\n";
    }

    return result;
}

void Board::emptyPosition(int &emptyRow, int &emptyColumn) const
{
    for (auto it = m_blocks.cbegin(); it != m_blocks.cend(); ++it)
        if (!*it) {
            emptyRow = (it - m_blocks.cbegin())/m_size;
            emptyColumn = it - m_blocks.cbegin() - emptyRow*m_size;
            return;
        }

    assert(0);
}

Board Board::upperNeighbour(int emptyRow, int emptyColumn) const
{
    assert(emptyRow > 0);
    assert(emptyColumn >= 0 && emptyColumn < m_size);

    auto newBoard = *this;
    auto prevValue = m_blocks[(emptyRow-1)*m_size + emptyColumn];
    newBoard.m_blocks[(emptyRow-1)*m_size + emptyColumn] = 0;
    newBoard.m_blocks[emptyRow*m_size + emptyColumn] = prevValue;
    return newBoard;
}

Board Board::lowerNeighbour(int emptyRow, int emptyColumn) const
{
    assert(emptyRow < m_size - 1);
    assert(emptyColumn >= 0 && emptyColumn < m_size);

    auto newBoard = *this;
    auto prevValue = m_blocks[(emptyRow+1)*m_size + emptyColumn];
    newBoard.m_blocks[(emptyRow+1)*m_size + emptyColumn] = 0;
    newBoard.m_blocks[emptyRow*m_size + emptyColumn] = prevValue;
    return newBoard;
}

Board Board::leftNeighbour(int emptyRow, int emptyColumn) const
{
    assert(emptyRow >= 0 && emptyRow < m_size);
    assert(emptyColumn > 0);

    auto newBoard = *this;
    auto prevValue = m_blocks[emptyRow*m_size + emptyColumn - 1];
    newBoard.m_blocks[emptyRow*m_size + emptyColumn - 1] = 0;
    newBoard.m_blocks[emptyRow*m_size + emptyColumn] = prevValue;
    return newBoard;
}

Board Board::rightNeighbour(int emptyRow, int emptyColumn) const
{
    assert(emptyRow >= 0 && emptyRow < m_size);
    assert(emptyColumn < m_size - 1);

    auto newBoard = *this;
    auto prevValue = m_blocks[emptyRow*m_size + emptyColumn + 1];
    newBoard.m_blocks[emptyRow*m_size + emptyColumn + 1] = 0;
    newBoard.m_blocks[emptyRow*m_size + emptyColumn] = prevValue;
    return newBoard;
}

}
<commit_msg>Apply correctly the copy and swap idiom in case of move construction.<commit_after>// Copyright (C) 2014 Călin Cruceru <calin@rosedu.org>
//
// This program is free software licenced under MIT Licence. You can
// find a copy of this licence in LICENCE.txt in the top directory of
// source code.
//

// Project specific
#include "Board.h"

// libc++ and libc
#include <cassert>
#include <cstdlib>
#include <cmath>

namespace Puzzle15
{

void swap(Board &first, Board &second) noexcept
{
    using std::swap;
    swap(first.m_blocks, second.m_blocks);
    swap(first.m_size, second.m_size);
}

Board::Board()
    : m_size(0)
{
    // nothing to do
}

Board::Board(const Board &other)
    : m_blocks(other.m_blocks),
      m_size(other.m_size)
{
    // nothing to do
}

Board::Board(Board &&other)
    : Board()
{
    swap(*this, other);
}

Board::Board(const std::vector<int> &initBlocks, int dim)
    : m_blocks(initBlocks),
      m_size(dim)
{
    // nothing to do
}

Board::~Board()
{
    // nothing to do
}

Board &Board::operator=(Board other)
{
    swap(*this, other);
    return *this;
}

bool Board::operator==(const Board &other) const
{
    assert(m_blocks.size() == other.m_blocks.size());
    return m_blocks == other.m_blocks && m_size == other.m_size;
}

bool Board::operator!=(const Board &other) const
{
    return !this->operator==(other);
}

int Board::dimension() const
{
    return m_size;
}

int Board::hamming() const
{
    auto count = 0;
    for (auto it = m_blocks.cbegin(); it != m_blocks.cend(); ++it)
        count += (*it == it - m_blocks.cbegin() + 1 ? 1 : 0);

    return count;
}

int Board::manhattan() const
{
    auto count = 0;
    for (auto it = m_blocks.cbegin(); it != m_blocks.cend(); ++it) {
        int i = (it - m_blocks.cbegin())/m_size;
        int j = it - m_blocks.cbegin() - i*m_size;
        int goalRow = (*it - 1)/m_size;
        int goalColumn = *it - goalRow*m_size - 1;

        count += (abs(goalRow - i) + abs(goalColumn - j));
    }

    return count;
}

bool Board::isGoal() const
{
    for (auto it = m_blocks.cbegin(); it != m_blocks.cend() - 1; ++it)
        if (*it != it - m_blocks.cbegin() + 1)
            return false;

    assert(m_blocks.back() == 0);
    return true;
}

Board Board::twin() const
{
    // more intelligent choice of the twin?
    auto twinBoard = *this;
    if (!m_blocks.front()) {
        auto tmp = m_blocks[2];
        twinBoard.m_blocks[2] = m_blocks[1];
        twinBoard.m_blocks[1] = tmp;
    } else if (!m_blocks[1]) {
        auto tmp = m_blocks[m_size];
        twinBoard.m_blocks[m_size] = m_blocks[m_size+1];
        twinBoard.m_blocks[m_size+1] = tmp;
    } else {
        auto tmp = m_blocks.front();
        twinBoard.m_blocks.front() = m_blocks[1];
        twinBoard.m_blocks[1] = tmp;
    }

    return twinBoard;
}

Queue<Board> Board::neighbours() const
{
    Queue<Board> neighList;
    int emptyRow, emptyColumn;
    emptyPosition(emptyRow, emptyColumn);

    if (emptyRow > 0)
        neighList.enqueue(upperNeighbour(emptyRow, emptyColumn));
    if (emptyRow < m_size - 1)
        neighList.enqueue(lowerNeighbour(emptyRow, emptyColumn));
    if (emptyColumn > 0)
        neighList.enqueue(leftNeighbour(emptyRow, emptyColumn));
    if (emptyColumn < m_size - 1)
        neighList.enqueue(rightNeighbour(emptyRow, emptyColumn));

    return neighList;
}

std::string Board::toString() const
{
    std::string result;
    for (auto it = m_blocks.cbegin(); it != m_blocks.cend(); ++it) {
        auto i = (it - m_blocks.cbegin())/m_size;
        auto j = it - m_blocks.cbegin() - i*m_size;

        result += std::to_string(*it);
        if (j < m_size - 1)
            result += " ";
        else
            result += "\n";
    }

    return result;
}

void Board::emptyPosition(int &emptyRow, int &emptyColumn) const
{
    for (auto it = m_blocks.cbegin(); it != m_blocks.cend(); ++it)
        if (!*it) {
            emptyRow = (it - m_blocks.cbegin())/m_size;
            emptyColumn = it - m_blocks.cbegin() - emptyRow*m_size;
            return;
        }

    assert(0);
}

Board Board::upperNeighbour(int emptyRow, int emptyColumn) const
{
    assert(emptyRow > 0);
    assert(emptyColumn >= 0 && emptyColumn < m_size);

    auto newBoard = *this;
    auto prevValue = m_blocks[(emptyRow-1)*m_size + emptyColumn];
    newBoard.m_blocks[(emptyRow-1)*m_size + emptyColumn] = 0;
    newBoard.m_blocks[emptyRow*m_size + emptyColumn] = prevValue;
    return newBoard;
}

Board Board::lowerNeighbour(int emptyRow, int emptyColumn) const
{
    assert(emptyRow < m_size - 1);
    assert(emptyColumn >= 0 && emptyColumn < m_size);

    auto newBoard = *this;
    auto prevValue = m_blocks[(emptyRow+1)*m_size + emptyColumn];
    newBoard.m_blocks[(emptyRow+1)*m_size + emptyColumn] = 0;
    newBoard.m_blocks[emptyRow*m_size + emptyColumn] = prevValue;
    return newBoard;
}

Board Board::leftNeighbour(int emptyRow, int emptyColumn) const
{
    assert(emptyRow >= 0 && emptyRow < m_size);
    assert(emptyColumn > 0);

    auto newBoard = *this;
    auto prevValue = m_blocks[emptyRow*m_size + emptyColumn - 1];
    newBoard.m_blocks[emptyRow*m_size + emptyColumn - 1] = 0;
    newBoard.m_blocks[emptyRow*m_size + emptyColumn] = prevValue;
    return newBoard;
}

Board Board::rightNeighbour(int emptyRow, int emptyColumn) const
{
    assert(emptyRow >= 0 && emptyRow < m_size);
    assert(emptyColumn < m_size - 1);

    auto newBoard = *this;
    auto prevValue = m_blocks[emptyRow*m_size + emptyColumn + 1];
    newBoard.m_blocks[emptyRow*m_size + emptyColumn + 1] = 0;
    newBoard.m_blocks[emptyRow*m_size + emptyColumn] = prevValue;
    return newBoard;
}

}
<|endoftext|>