<commit_before>/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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 "src/tracing/core/trace_writer_impl.h"

#include <gtest/gtest.h>
#include "perfetto/ext/base/utils.h"
#include "perfetto/ext/tracing/core/commit_data_request.h"
#include "perfetto/ext/tracing/core/trace_writer.h"
#include "perfetto/ext/tracing/core/tracing_service.h"
#include "src/base/test/gtest_test_suite.h"
#include "src/base/test/test_task_runner.h"
#include "src/tracing/core/shared_memory_arbiter_impl.h"
#include "src/tracing/test/aligned_buffer_test.h"
#include "src/tracing/test/fake_producer_endpoint.h"

#include "perfetto/trace/test_event.pbzero.h"
#include "perfetto/trace/trace_packet.pbzero.h"

namespace perfetto {
namespace {

class TraceWriterImplTest : public AlignedBufferTest {
 public:
  void SetUp() override {
    SharedMemoryArbiterImpl::set_default_layout_for_testing(
        SharedMemoryABI::PageLayout::kPageDiv4);
    AlignedBufferTest::SetUp();
    task_runner_.reset(new base::TestTaskRunner());
    arbiter_.reset(new SharedMemoryArbiterImpl(buf(), buf_size(), page_size(),
                                               &fake_producer_endpoint_,
                                               task_runner_.get()));
  }

  void TearDown() override {
    arbiter_.reset();
    task_runner_.reset();
  }

  FakeProducerEndpoint fake_producer_endpoint_;
  std::unique_ptr<base::TestTaskRunner> task_runner_;
  std::unique_ptr<SharedMemoryArbiterImpl> arbiter_;
  std::function<void(const std::vector<uint32_t>&)> on_pages_complete_;
};

size_t const kPageSizes[] = {4096, 65536};
INSTANTIATE_TEST_SUITE_P(PageSize,
                         TraceWriterImplTest,
                         ::testing::ValuesIn(kPageSizes));

TEST_P(TraceWriterImplTest, SingleWriter) {
  const BufferID kBufId = 42;
  std::unique_ptr<TraceWriter> writer = arbiter_->CreateTraceWriter(kBufId);
  const size_t kNumPackets = 32;
  for (size_t i = 0; i < kNumPackets; i++) {
    auto packet = writer->NewTracePacket();
    char str[16];
    sprintf(str, "foobar %zu", i);
    packet->set_for_testing()->set_str(str);
  }

  // Destroying the TraceWriteImpl should cause the last packet to be finalized
  // and the chunk to be put back in the kChunkComplete state.
  writer.reset();

  SharedMemoryABI* abi = arbiter_->shmem_abi_for_testing();
  size_t packets_count = 0;
  for (size_t page_idx = 0; page_idx < kNumPages; page_idx++) {
    uint32_t page_layout = abi->GetPageLayout(page_idx);
    size_t num_chunks = SharedMemoryABI::GetNumChunksForLayout(page_layout);
    for (size_t chunk_idx = 0; chunk_idx < num_chunks; chunk_idx++) {
      auto chunk_state = abi->GetChunkState(page_idx, chunk_idx);
      ASSERT_TRUE(chunk_state == SharedMemoryABI::kChunkFree ||
                  chunk_state == SharedMemoryABI::kChunkComplete);
      auto chunk = abi->TryAcquireChunkForReading(page_idx, chunk_idx);
      if (!chunk.is_valid())
        continue;
      packets_count += chunk.header()->packets.load().count;
    }
  }
  EXPECT_EQ(kNumPackets, packets_count);
  // TODO(primiano): check also the content of the packets decoding the protos.
}

TEST_P(TraceWriterImplTest, FragmentingPacket) {
  const BufferID kBufId = 42;
  std::unique_ptr<TraceWriter> writer = arbiter_->CreateTraceWriter(kBufId);

  // Write a packet that's guaranteed to span more than a single chunk.
  auto packet = writer->NewTracePacket();
  size_t chunk_size = page_size() / 4;
  std::stringstream large_string_writer;
  for (size_t pos = 0; pos < chunk_size; pos++)
    large_string_writer << "x";
  std::string large_string = large_string_writer.str();
  packet->set_for_testing()->set_str(large_string.data(), large_string.size());

  // First chunk should be committed.
  arbiter_->FlushPendingCommitDataRequests();
  const auto& last_commit = fake_producer_endpoint_.last_commit_data_request;
  ASSERT_EQ(1, last_commit.chunks_to_move_size());
  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].page());
  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].chunk());
  EXPECT_EQ(kBufId, last_commit.chunks_to_move()[0].target_buffer());
  EXPECT_EQ(0, last_commit.chunks_to_patch_size());

  SharedMemoryABI* abi = arbiter_->shmem_abi_for_testing();

  // The first allocated chunk should be complete but need patching, since the
  // packet extended past the chunk and no patches for the packet size or string
  // field size were applied yet.
  ASSERT_EQ(SharedMemoryABI::kChunkComplete, abi->GetChunkState(0u, 0u));
  auto chunk = abi->TryAcquireChunkForReading(0u, 0u);
  ASSERT_TRUE(chunk.is_valid());
  ASSERT_EQ(1, chunk.header()->packets.load().count);
  ASSERT_TRUE(chunk.header()->packets.load().flags &
              SharedMemoryABI::ChunkHeader::kChunkNeedsPatching);
  ASSERT_TRUE(chunk.header()->packets.load().flags &
              SharedMemoryABI::ChunkHeader::kLastPacketContinuesOnNextChunk);

  // Starting a new packet should cause patches to be applied.
  packet->Finalize();
  auto packet2 = writer->NewTracePacket();
  arbiter_->FlushPendingCommitDataRequests();
  EXPECT_EQ(0, last_commit.chunks_to_move_size());
  ASSERT_EQ(1, last_commit.chunks_to_patch_size());
  EXPECT_EQ(writer->writer_id(), last_commit.chunks_to_patch()[0].writer_id());
  EXPECT_EQ(kBufId, last_commit.chunks_to_patch()[0].target_buffer());
  EXPECT_EQ(chunk.header()->chunk_id.load(),
            last_commit.chunks_to_patch()[0].chunk_id());
  EXPECT_FALSE(last_commit.chunks_to_patch()[0].has_more_patches());
  ASSERT_EQ(1, last_commit.chunks_to_patch()[0].patches_size());
}

// Sets up a scenario in which the SMB is exhausted and TraceWriter fails to get
// a new chunk while fragmenting a packet. Verifies that data is dropped until
// the SMB is freed up and TraceWriter can get a new chunk.
TEST_P(TraceWriterImplTest, FragmentingPacketWhileBufferExhaused) {
  arbiter_.reset(new SharedMemoryArbiterImpl(buf(), buf_size(), page_size(),
                                             &fake_producer_endpoint_,
                                             task_runner_.get()));

  const BufferID kBufId = 42;
  std::unique_ptr<TraceWriter> writer = arbiter_->CreateTraceWriter(
      kBufId, SharedMemoryArbiter::BufferExhaustedPolicy::kDrop);

  // Write a small first packet, so that |writer| owns a chunk.
  auto packet = writer->NewTracePacket();
  EXPECT_FALSE(reinterpret_cast<TraceWriterImpl*>(writer.get())
                   ->drop_packets_for_testing());
  EXPECT_EQ(packet->Finalize(), 0);

  // Grab all the remaining chunks in the SMB in new writers.
  std::array<std::unique_ptr<TraceWriter>, kNumPages * 4 - 1> other_writers;
  for (size_t i = 0; i < other_writers.size(); i++) {
    other_writers[i] = arbiter_->CreateTraceWriter(
        kBufId, SharedMemoryArbiter::BufferExhaustedPolicy::kDrop);
    auto other_writer_packet = other_writers[i]->NewTracePacket();
    EXPECT_FALSE(reinterpret_cast<TraceWriterImpl*>(other_writers[i].get())
                     ->drop_packets_for_testing());
  }

  // Write a packet that's guaranteed to span more than a single chunk, causing
  // |writer| to attempt to acquire a new chunk but fail to do so.
  auto packet2 = writer->NewTracePacket();
  size_t chunk_size = page_size() / 4;
  std::stringstream large_string_writer;
  for (size_t pos = 0; pos < chunk_size; pos++)
    large_string_writer << "x";
  std::string large_string = large_string_writer.str();
  packet2->set_for_testing()->set_str(large_string.data(), large_string.size());

  EXPECT_TRUE(reinterpret_cast<TraceWriterImpl*>(writer.get())
                  ->drop_packets_for_testing());

  // First chunk should be committed.
  arbiter_->FlushPendingCommitDataRequests();
  const auto& last_commit = fake_producer_endpoint_.last_commit_data_request;
  ASSERT_EQ(1, last_commit.chunks_to_move_size());
  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].page());
  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].chunk());
  EXPECT_EQ(kBufId, last_commit.chunks_to_move()[0].target_buffer());
  EXPECT_EQ(0, last_commit.chunks_to_patch_size());

  // It should not need patching and not have the continuation flag set.
  SharedMemoryABI* abi = arbiter_->shmem_abi_for_testing();
  ASSERT_EQ(SharedMemoryABI::kChunkComplete, abi->GetChunkState(0u, 0u));
  auto chunk = abi->TryAcquireChunkForReading(0u, 0u);
  ASSERT_TRUE(chunk.is_valid());
  ASSERT_EQ(2, chunk.header()->packets.load().count);
  ASSERT_FALSE(chunk.header()->packets.load().flags &
               SharedMemoryABI::ChunkHeader::kChunkNeedsPatching);
  ASSERT_FALSE(chunk.header()->packets.load().flags &
               SharedMemoryABI::ChunkHeader::kLastPacketContinuesOnNextChunk);

  // Writing more data while in garbage mode succeeds. This data is dropped.
  packet2->Finalize();
  auto packet3 = writer->NewTracePacket();
  packet3->set_for_testing()->set_str(large_string.data(), large_string.size());

  // Release the |writer|'s first chunk as free, so that it can grab it again.
  abi->ReleaseChunkAsFree(std::move(chunk));

  // Starting a new packet should cause TraceWriter to attempt to grab a new
  // chunk again, because we wrote enough data to wrap the garbage chunk.
  packet3->Finalize();
  auto packet4 = writer->NewTracePacket();

  // Grabbing the chunk should have succeeded.
  EXPECT_FALSE(reinterpret_cast<TraceWriterImpl*>(writer.get())
                   ->drop_packets_for_testing());

  // The first packet in the chunk should have the previous_packet_dropped flag
  // set, so shouldn't be empty.
  EXPECT_GT(packet4->Finalize(), 0);

  // Flushing the writer causes the chunk to be released again.
  writer->Flush();
  EXPECT_EQ(1, last_commit.chunks_to_move_size());
  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].page());
  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].chunk());
  ASSERT_EQ(0, last_commit.chunks_to_patch_size());

  // Chunk should contain only |packet4| and not have any continuation flag set.
  ASSERT_EQ(SharedMemoryABI::kChunkComplete, abi->GetChunkState(0u, 0u));
  chunk = abi->TryAcquireChunkForReading(0u, 0u);
  ASSERT_TRUE(chunk.is_valid());
  ASSERT_EQ(1, chunk.header()->packets.load().count);
  ASSERT_FALSE(chunk.header()->packets.load().flags &
               SharedMemoryABI::ChunkHeader::kChunkNeedsPatching);
  ASSERT_FALSE(
      chunk.header()->packets.load().flags &
      SharedMemoryABI::ChunkHeader::kFirstPacketContinuesFromPrevChunk);
  ASSERT_FALSE(chunk.header()->packets.load().flags &
               SharedMemoryABI::ChunkHeader::kLastPacketContinuesOnNextChunk);
}

// TODO(primiano): add multi-writer test.
// TODO(primiano): add Flush() test.

}  // namespace
}  // namespace perfetto
<commit_msg>build: Attempt to fix chrometto roll am: ee85c6fe18 am: c39efa40d4 am: 4cf4d9ebec am: cd378ed793<commit_after>/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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 "src/tracing/core/trace_writer_impl.h"

#include <gtest/gtest.h>
#include "perfetto/ext/base/utils.h"
#include "perfetto/ext/tracing/core/commit_data_request.h"
#include "perfetto/ext/tracing/core/trace_writer.h"
#include "perfetto/ext/tracing/core/tracing_service.h"
#include "src/base/test/gtest_test_suite.h"
#include "src/base/test/test_task_runner.h"
#include "src/tracing/core/shared_memory_arbiter_impl.h"
#include "src/tracing/test/aligned_buffer_test.h"
#include "src/tracing/test/fake_producer_endpoint.h"

#include "perfetto/trace/test_event.pbzero.h"
#include "perfetto/trace/trace_packet.pbzero.h"

namespace perfetto {
namespace {

class TraceWriterImplTest : public AlignedBufferTest {
 public:
  void SetUp() override {
    SharedMemoryArbiterImpl::set_default_layout_for_testing(
        SharedMemoryABI::PageLayout::kPageDiv4);
    AlignedBufferTest::SetUp();
    task_runner_.reset(new base::TestTaskRunner());
    arbiter_.reset(new SharedMemoryArbiterImpl(buf(), buf_size(), page_size(),
                                               &fake_producer_endpoint_,
                                               task_runner_.get()));
  }

  void TearDown() override {
    arbiter_.reset();
    task_runner_.reset();
  }

  FakeProducerEndpoint fake_producer_endpoint_;
  std::unique_ptr<base::TestTaskRunner> task_runner_;
  std::unique_ptr<SharedMemoryArbiterImpl> arbiter_;
  std::function<void(const std::vector<uint32_t>&)> on_pages_complete_;
};

size_t const kPageSizes[] = {4096, 65536};
INSTANTIATE_TEST_SUITE_P(PageSize,
                         TraceWriterImplTest,
                         ::testing::ValuesIn(kPageSizes));

TEST_P(TraceWriterImplTest, SingleWriter) {
  const BufferID kBufId = 42;
  std::unique_ptr<TraceWriter> writer = arbiter_->CreateTraceWriter(kBufId);
  const size_t kNumPackets = 32;
  for (size_t i = 0; i < kNumPackets; i++) {
    auto packet = writer->NewTracePacket();
    char str[16];
    sprintf(str, "foobar %zu", i);
    packet->set_for_testing()->set_str(str);
  }

  // Destroying the TraceWriteImpl should cause the last packet to be finalized
  // and the chunk to be put back in the kChunkComplete state.
  writer.reset();

  SharedMemoryABI* abi = arbiter_->shmem_abi_for_testing();
  size_t packets_count = 0;
  for (size_t page_idx = 0; page_idx < kNumPages; page_idx++) {
    uint32_t page_layout = abi->GetPageLayout(page_idx);
    size_t num_chunks = SharedMemoryABI::GetNumChunksForLayout(page_layout);
    for (size_t chunk_idx = 0; chunk_idx < num_chunks; chunk_idx++) {
      auto chunk_state = abi->GetChunkState(page_idx, chunk_idx);
      ASSERT_TRUE(chunk_state == SharedMemoryABI::kChunkFree ||
                  chunk_state == SharedMemoryABI::kChunkComplete);
      auto chunk = abi->TryAcquireChunkForReading(page_idx, chunk_idx);
      if (!chunk.is_valid())
        continue;
      packets_count += chunk.header()->packets.load().count;
    }
  }
  EXPECT_EQ(kNumPackets, packets_count);
  // TODO(primiano): check also the content of the packets decoding the protos.
}

TEST_P(TraceWriterImplTest, FragmentingPacket) {
  const BufferID kBufId = 42;
  std::unique_ptr<TraceWriter> writer = arbiter_->CreateTraceWriter(kBufId);

  // Write a packet that's guaranteed to span more than a single chunk.
  auto packet = writer->NewTracePacket();
  size_t chunk_size = page_size() / 4;
  std::stringstream large_string_writer;
  for (size_t pos = 0; pos < chunk_size; pos++)
    large_string_writer << "x";
  std::string large_string = large_string_writer.str();
  packet->set_for_testing()->set_str(large_string.data(), large_string.size());

  // First chunk should be committed.
  arbiter_->FlushPendingCommitDataRequests();
  const auto& last_commit = fake_producer_endpoint_.last_commit_data_request;
  ASSERT_EQ(1, last_commit.chunks_to_move_size());
  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].page());
  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].chunk());
  EXPECT_EQ(kBufId, last_commit.chunks_to_move()[0].target_buffer());
  EXPECT_EQ(0, last_commit.chunks_to_patch_size());

  SharedMemoryABI* abi = arbiter_->shmem_abi_for_testing();

  // The first allocated chunk should be complete but need patching, since the
  // packet extended past the chunk and no patches for the packet size or string
  // field size were applied yet.
  ASSERT_EQ(SharedMemoryABI::kChunkComplete, abi->GetChunkState(0u, 0u));
  auto chunk = abi->TryAcquireChunkForReading(0u, 0u);
  ASSERT_TRUE(chunk.is_valid());
  ASSERT_EQ(1, chunk.header()->packets.load().count);
  ASSERT_TRUE(chunk.header()->packets.load().flags &
              SharedMemoryABI::ChunkHeader::kChunkNeedsPatching);
  ASSERT_TRUE(chunk.header()->packets.load().flags &
              SharedMemoryABI::ChunkHeader::kLastPacketContinuesOnNextChunk);

  // Starting a new packet should cause patches to be applied.
  packet->Finalize();
  auto packet2 = writer->NewTracePacket();
  arbiter_->FlushPendingCommitDataRequests();
  EXPECT_EQ(0, last_commit.chunks_to_move_size());
  ASSERT_EQ(1, last_commit.chunks_to_patch_size());
  EXPECT_EQ(writer->writer_id(), last_commit.chunks_to_patch()[0].writer_id());
  EXPECT_EQ(kBufId, last_commit.chunks_to_patch()[0].target_buffer());
  EXPECT_EQ(chunk.header()->chunk_id.load(),
            last_commit.chunks_to_patch()[0].chunk_id());
  EXPECT_FALSE(last_commit.chunks_to_patch()[0].has_more_patches());
  ASSERT_EQ(1, last_commit.chunks_to_patch()[0].patches_size());
}

// Sets up a scenario in which the SMB is exhausted and TraceWriter fails to get
// a new chunk while fragmenting a packet. Verifies that data is dropped until
// the SMB is freed up and TraceWriter can get a new chunk.
TEST_P(TraceWriterImplTest, FragmentingPacketWhileBufferExhausted) {
  arbiter_.reset(new SharedMemoryArbiterImpl(buf(), buf_size(), page_size(),
                                             &fake_producer_endpoint_,
                                             task_runner_.get()));

  const BufferID kBufId = 42;
  std::unique_ptr<TraceWriter> writer = arbiter_->CreateTraceWriter(
      kBufId, SharedMemoryArbiter::BufferExhaustedPolicy::kDrop);

  // Write a small first packet, so that |writer| owns a chunk.
  auto packet = writer->NewTracePacket();
  EXPECT_FALSE(reinterpret_cast<TraceWriterImpl*>(writer.get())
                   ->drop_packets_for_testing());
  EXPECT_EQ(packet->Finalize(), 0u);

  // Grab all the remaining chunks in the SMB in new writers.
  std::array<std::unique_ptr<TraceWriter>, kNumPages * 4 - 1> other_writers;
  for (size_t i = 0; i < other_writers.size(); i++) {
    other_writers[i] = arbiter_->CreateTraceWriter(
        kBufId, SharedMemoryArbiter::BufferExhaustedPolicy::kDrop);
    auto other_writer_packet = other_writers[i]->NewTracePacket();
    EXPECT_FALSE(reinterpret_cast<TraceWriterImpl*>(other_writers[i].get())
                     ->drop_packets_for_testing());
  }

  // Write a packet that's guaranteed to span more than a single chunk, causing
  // |writer| to attempt to acquire a new chunk but fail to do so.
  auto packet2 = writer->NewTracePacket();
  size_t chunk_size = page_size() / 4;
  std::stringstream large_string_writer;
  for (size_t pos = 0; pos < chunk_size; pos++)
    large_string_writer << "x";
  std::string large_string = large_string_writer.str();
  packet2->set_for_testing()->set_str(large_string.data(), large_string.size());

  EXPECT_TRUE(reinterpret_cast<TraceWriterImpl*>(writer.get())
                  ->drop_packets_for_testing());

  // First chunk should be committed.
  arbiter_->FlushPendingCommitDataRequests();
  const auto& last_commit = fake_producer_endpoint_.last_commit_data_request;
  ASSERT_EQ(1, last_commit.chunks_to_move_size());
  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].page());
  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].chunk());
  EXPECT_EQ(kBufId, last_commit.chunks_to_move()[0].target_buffer());
  EXPECT_EQ(0, last_commit.chunks_to_patch_size());

  // It should not need patching and not have the continuation flag set.
  SharedMemoryABI* abi = arbiter_->shmem_abi_for_testing();
  ASSERT_EQ(SharedMemoryABI::kChunkComplete, abi->GetChunkState(0u, 0u));
  auto chunk = abi->TryAcquireChunkForReading(0u, 0u);
  ASSERT_TRUE(chunk.is_valid());
  ASSERT_EQ(2, chunk.header()->packets.load().count);
  ASSERT_FALSE(chunk.header()->packets.load().flags &
               SharedMemoryABI::ChunkHeader::kChunkNeedsPatching);
  ASSERT_FALSE(chunk.header()->packets.load().flags &
               SharedMemoryABI::ChunkHeader::kLastPacketContinuesOnNextChunk);

  // Writing more data while in garbage mode succeeds. This data is dropped.
  packet2->Finalize();
  auto packet3 = writer->NewTracePacket();
  packet3->set_for_testing()->set_str(large_string.data(), large_string.size());

  // Release the |writer|'s first chunk as free, so that it can grab it again.
  abi->ReleaseChunkAsFree(std::move(chunk));

  // Starting a new packet should cause TraceWriter to attempt to grab a new
  // chunk again, because we wrote enough data to wrap the garbage chunk.
  packet3->Finalize();
  auto packet4 = writer->NewTracePacket();

  // Grabbing the chunk should have succeeded.
  EXPECT_FALSE(reinterpret_cast<TraceWriterImpl*>(writer.get())
                   ->drop_packets_for_testing());

  // The first packet in the chunk should have the previous_packet_dropped flag
  // set, so shouldn't be empty.
  EXPECT_GT(packet4->Finalize(), 0u);

  // Flushing the writer causes the chunk to be released again.
  writer->Flush();
  EXPECT_EQ(1, last_commit.chunks_to_move_size());
  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].page());
  EXPECT_EQ(0u, last_commit.chunks_to_move()[0].chunk());
  ASSERT_EQ(0, last_commit.chunks_to_patch_size());

  // Chunk should contain only |packet4| and not have any continuation flag set.
  ASSERT_EQ(SharedMemoryABI::kChunkComplete, abi->GetChunkState(0u, 0u));
  chunk = abi->TryAcquireChunkForReading(0u, 0u);
  ASSERT_TRUE(chunk.is_valid());
  ASSERT_EQ(1, chunk.header()->packets.load().count);
  ASSERT_FALSE(chunk.header()->packets.load().flags &
               SharedMemoryABI::ChunkHeader::kChunkNeedsPatching);
  ASSERT_FALSE(
      chunk.header()->packets.load().flags &
      SharedMemoryABI::ChunkHeader::kFirstPacketContinuesFromPrevChunk);
  ASSERT_FALSE(chunk.header()->packets.load().flags &
               SharedMemoryABI::ChunkHeader::kLastPacketContinuesOnNextChunk);
}

// TODO(primiano): add multi-writer test.
// TODO(primiano): add Flush() test.

}  // namespace
}  // namespace perfetto
<|endoftext|>


<commit_before>#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <uv.h>
#include <string>
#include <sstream>
#include <map>
#include <memory>
#include <utility>
#include <vector>

#include "../worker_platform.h"
#include "../worker_thread.h"
#include "../../message.h"
#include "../../log.h"
#include "../../lock.h"

using std::string;
using std::wstring;
using std::ostringstream;
using std::unique_ptr;
using std::shared_ptr;
using std::default_delete;
using std::map;
using std::pair;
using std::make_pair;
using std::vector;
using std::move;
using std::endl;

const DWORD DEFAULT_BUFFER_SIZE = 1024 * 1024;
const DWORD NETWORK_BUFFER_SIZE = 64 * 1024;

static void CALLBACK command_perform_helper(__in ULONG_PTR payload);

static void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped);

static Result<string> to_utf8(const wstring &in);

template < class V = void* >
static Result<V> windows_error_result(const string &prefix);

template < class V = void* >
static Result<V> windows_error_result(const string &prefix, DWORD error_code);

class WindowsWorkerPlatform;

class Subscription {
public:
  Subscription(ChannelID channel, HANDLE root, const string &path, WindowsWorkerPlatform *platform) :
    channel{channel},
    platform{platform},
    path{path},
    root{root},
    buffer_size{DEFAULT_BUFFER_SIZE},
    buffer{new BYTE[buffer_size]},
    written{new BYTE[buffer_size]}
  {
    ZeroMemory(&overlapped, sizeof(OVERLAPPED));
    overlapped.hEvent = this;
  }

  ~Subscription()
  {
    CloseHandle(root);
  }

  Result<> schedule()
  {
    int success = ReadDirectoryChangesW(
      root, // root directory handle
      buffer.get(), // result buffer
      buffer_size, // result buffer size
      TRUE, // recursive
      FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES
        | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS
        | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SECURITY, // change flags
      NULL, // bytes returned
      &overlapped, // overlapped
      &event_helper // completion routine
    );
    if (!success) {
      return windows_error_result<>("Unable to subscribe to filesystem events");
    }

    return ok_result();
  }

  Result<> use_network_size()
  {
    if (buffer_size <= NETWORK_BUFFER_SIZE) {
      ostringstream out("Buffer size of ");
      out
        << buffer_size
        << " is already lower than the network buffer size "
        << NETWORK_BUFFER_SIZE;
      return error_result(out.str());
    }

    buffer_size = NETWORK_BUFFER_SIZE;
    buffer.reset(new BYTE[buffer_size]);
    written.reset(new BYTE[buffer_size]);

    return ok_result();
  }

  ChannelID get_channel() const {
    return channel;
  }

  WindowsWorkerPlatform* get_platform() const {
    return platform;
  }

  BYTE *get_written(DWORD written_size) {
    memcpy(written.get(), buffer.get(), written_size);
    return written.get();
  }

  string make_absolute(const string &sub_path)
  {
    ostringstream out;

    out << path;
    if (path.back() != '\\' && sub_path.front() != '\\') {
      out << '\\';
    }
    out << sub_path;

    return out.str();
  }

private:
  ChannelID channel;
  WindowsWorkerPlatform *platform;

  string path;
  HANDLE root;
  OVERLAPPED overlapped;

  DWORD buffer_size;
  unique_ptr<BYTE[]> buffer;
  unique_ptr<BYTE[]> written;
};

class WindowsWorkerPlatform : public WorkerPlatform {
public:
  WindowsWorkerPlatform(WorkerThread *thread) :
    WorkerPlatform(thread),
    thread_handle{0}
  {
    int err;

    err = uv_mutex_init(&thread_handle_mutex);
    if (err) {
      report_uv_error(err);
    }
  };

  ~WindowsWorkerPlatform() override
  {
    uv_mutex_destroy(&thread_handle_mutex);
  }

  Result<> wake() override
  {
    Lock lock(thread_handle_mutex);

    if (!thread_handle) {
      return ok_result();
    }

    BOOL success = QueueUserAPC(
      command_perform_helper,
      thread_handle,
      reinterpret_cast<ULONG_PTR>(this)
    );
    if (!success) {
      return windows_error_result<>("Unable to queue APC");
    }

    return ok_result();
  }

  Result<> listen() override
  {
    {
      Lock lock(thread_handle_mutex);

      HANDLE pseudo_handle = GetCurrentThread();
      BOOL success = DuplicateHandle(
        GetCurrentProcess(), // Source process
        pseudo_handle, // Source handle
        GetCurrentProcess(), // Destination process
        &thread_handle, // Destination handle
        0, // Desired access
        FALSE, // Inheritable by new processes
        DUPLICATE_SAME_ACCESS // options
      );
      if (!success) {
        Result<> r = windows_error_result<>("Unable to duplicate thread handle");
        report_error("Unable to acquire thread handle");
        return r;
      }
    }

    while (true) {
      SleepEx(INFINITE, true);
    }

    report_error("listen loop ended unexpectedly");
    return health_err_result();
  }

  Result<> handle_add_command(const ChannelID channel, const string &root_path)
  {
    LOGGER << "Watching: " << root_path << endl;

    // Convert the path to a wide-character array
    size_t wlen = MultiByteToWideChar(
      CP_UTF8, // code page
      0, // flags
      root_path.data(), // source string data
      -1, // source string length (null-terminated)
      0, // output buffer
      0 // output buffer size
    );
    if (wlen == 0) {
      return windows_error_result<>("Unable to measure UTF-16 buffer");
    }
    unique_ptr<WCHAR[]> root_path_w{new WCHAR[wlen]};
    size_t conv_success = MultiByteToWideChar(
      CP_UTF8, // code page
      0, // flags,
      root_path.data(), // source string data
      -1, // source string length (null-terminated)
      root_path_w.get(), // output buffer
      wlen // output buffer size
    );
    if (!conv_success) {
      return windows_error_result<>("Unable to convert root path to UTF-16");
    }

    // Open a directory handle
    HANDLE root = CreateFileW(
      root_path_w.get(), // file name
      FILE_LIST_DIRECTORY, // desired access
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // share mode
      NULL, // security attributes
      OPEN_EXISTING, // creation disposition
      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // flags and attributes
      NULL // template file
    );
    if (root == INVALID_HANDLE_VALUE) {
      return windows_error_result<>("Unable to open directory handle");
    }

    // Allocate and persist the subscription
    Subscription *sub = new Subscription(channel, root, root_path, this);
    auto insert_result = subscriptions.insert(make_pair(channel, sub));
    if (!insert_result.second) {
      delete sub;

      ostringstream msg("Channel collision: ");
      msg << channel;
      return error_result(msg.str());
    }

    LOGGER << "Now watching directory " << root_path << "." << endl;

    return sub->schedule();
  }

  Result<> handle_remove_command(const ChannelID channel)
  {
    return ok_result();
  }

  Result<> handle_fs_event(DWORD error_code, DWORD num_bytes, Subscription* sub)
  {
    // Ensure that the subscription is valid.
    ChannelID channel = sub->get_channel();
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end() || it->second != sub) {
      return ok_result();
    }

    // Handle errors.
    if (error_code == ERROR_OPERATION_ABORTED) {
      LOGGER << "Operation aborted." << endl;

      subscriptions.erase(it);
      delete sub;

      return ok_result();
    }

    if (error_code == ERROR_INVALID_PARAMETER) {
      Result<> resize = sub->use_network_size();
      if (resize.is_error()) return resize;

      return sub->schedule();
    }

    if (error_code == ERROR_NOTIFY_ENUM_DIR) {
      LOGGER << "Change buffer overflow. Some events may have been lost." << endl;
      return sub->schedule();
    }

    if (error_code != ERROR_SUCCESS) {
      return windows_error_result<>("Completion callback error", error_code);
    }

    // Schedule the next completion callback.
    BYTE *base = sub->get_written(num_bytes);
    Result<> next = sub->schedule();
    if (next.is_error()) {
      report_error(string(next.get_error()));
    }

    // Process received events.
    vector<Message> messages;
    bool old_path_seen = false;
    string old_path;

    while (true) {
      PFILE_NOTIFY_INFORMATION info = reinterpret_cast<PFILE_NOTIFY_INFORMATION>(base);

      wstring wpath{info->FileName, info->FileNameLength};
      Result<string> u8r = to_utf8(wpath);
      if (u8r.is_error()) {
        LOGGER << "Skipping path: " << u8r << "." << endl;
      } else {
        string path = sub->make_absolute(u8r.get_value());

        switch (info->Action) {
        case FILE_ACTION_ADDED:
          {
            FileSystemPayload payload(channel, ACTION_CREATED, KIND_UNKNOWN, move(path), "");
            Message message(move(payload));

            LOGGER << "Emitting filesystem message " << message << "." << endl;
            messages.push_back(move(message));
          }
          break;
        case FILE_ACTION_MODIFIED:
          {
            FileSystemPayload payload(channel, ACTION_MODIFIED, KIND_UNKNOWN, move(path), "");
            Message message(move(payload));

            LOGGER << "Emitting filesystem message " << message << "." << endl;
            messages.push_back(move(message));
          }
          break;
        case FILE_ACTION_REMOVED:
          {
            FileSystemPayload payload(channel, ACTION_DELETED, KIND_UNKNOWN, move(path), "");
            Message message(move(payload));

            LOGGER << "Emitting filesystem message " << message << "." << endl;
            messages.push_back(move(message));
          }
          break;
        case FILE_ACTION_RENAMED_OLD_NAME:
          old_path_seen = true;
          old_path = move(path);
          break;
        case FILE_ACTION_RENAMED_NEW_NAME:
          if (old_path_seen) {
            // Old name received first
            {
              FileSystemPayload payload(channel, ACTION_RENAMED, KIND_UNKNOWN, move(old_path), move(path));
              Message message(move(payload));

              LOGGER << "Emitting filesystem message " << message << "." << endl;
              messages.push_back(move(message));
            }

            old_path_seen = false;
          } else {
            // No old name. Treat it as a creation
            {
              FileSystemPayload payload(channel, ACTION_CREATED, KIND_UNKNOWN, move(path), "");
              Message message(move(payload));

              LOGGER << "Emitting filesystem message " << message << "." << endl;
              messages.push_back(move(message));
            }
          }
          break;
        default:
          LOGGER << "Skipping unexpected action " << info->Action << "." << endl;
          break;
        }
      }

      if (info->NextEntryOffset == 0) {
        break;
      }
      base += info->NextEntryOffset;
    }

    if (!messages.empty()) {
      Result<> er = emit_all(messages.begin(), messages.end());
      if (er.is_error()) {
        LOGGER << "Unable to emit messages: " << er << "." << endl;
      }
    }

    return next;
  }

private:
  uv_mutex_t thread_handle_mutex;
  HANDLE thread_handle;

  map<ChannelID, Subscription*> subscriptions;
};

unique_ptr<WorkerPlatform> WorkerPlatform::for_worker(WorkerThread *thread)
{
  return unique_ptr<WorkerPlatform>(new WindowsWorkerPlatform(thread));
}

void CALLBACK command_perform_helper(__in ULONG_PTR payload)
{
  WindowsWorkerPlatform *platform = reinterpret_cast<WindowsWorkerPlatform*>(payload);
  platform->handle_commands();
}

static void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped)
{
  Subscription *sub = static_cast<Subscription*>(overlapped->hEvent);
  Result<> r = sub->get_platform()->handle_fs_event(error_code, num_bytes, sub);
  if (r.is_error()) {
    LOGGER << "Unable to handle filesystem events: " << r << "." << endl;
  }
}

Result<string> to_utf8(const wstring &in)
{
  size_t len = WideCharToMultiByte(
    CP_UTF8, // code page
    0, // flags
    in.data(), // source string
    in.size() / sizeof(WCHAR), // source string length
    nullptr, // destination string, null to measure
    0, // destination string length
    nullptr, // default char
    nullptr  // used default char
  );
  if (!len) {
    return windows_error_result<string>("Unable to measure path as UTF-8");
  }

  char *out = new char[len];
  size_t copied = WideCharToMultiByte(
    CP_UTF8, // code page
    0, // flags
    in.data(), // source string
    in.size() / sizeof(WCHAR), // source string length
    out, // destination string
    len, // destination string length
    nullptr, // default char
    nullptr // used default char
  );
  if (!copied) {
    delete [] out;
    return windows_error_result<string>("Unable to convert path to UTF-8");
  }

  return ok_result(string{out, len});
}

template < class V >
Result<V> windows_error_result(const string &prefix)
{
  return windows_error_result<V>(prefix, GetLastError());
}

template < class V >
Result<V> windows_error_result(const string &prefix, DWORD error_code)
{
  LPVOID msg_buffer;

  FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL, // source
    error_code, // message ID
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // language ID
    (LPSTR) &msg_buffer, // output buffer
    0, // size
    NULL // arguments
  );

  ostringstream msg;
  msg << prefix << "\n (" << error_code << ") " << (char*) msg_buffer;
  LocalFree(msg_buffer);

  return Result<V>::make_error(msg.str());
}
<commit_msg>Extract to_wchar() as a counterpoint to to_utf8()<commit_after>#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <uv.h>
#include <string>
#include <sstream>
#include <map>
#include <memory>
#include <utility>
#include <vector>

#include "../worker_platform.h"
#include "../worker_thread.h"
#include "../../message.h"
#include "../../log.h"
#include "../../lock.h"

using std::string;
using std::wstring;
using std::ostringstream;
using std::unique_ptr;
using std::shared_ptr;
using std::default_delete;
using std::map;
using std::pair;
using std::make_pair;
using std::vector;
using std::move;
using std::endl;

const DWORD DEFAULT_BUFFER_SIZE = 1024 * 1024;
const DWORD NETWORK_BUFFER_SIZE = 64 * 1024;

static void CALLBACK command_perform_helper(__in ULONG_PTR payload);

static void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped);

static Result<string> to_utf8(const wstring &in);

static Result<wstring> to_wchar(const string &in);

template < class V = void* >
static Result<V> windows_error_result(const string &prefix);

template < class V = void* >
static Result<V> windows_error_result(const string &prefix, DWORD error_code);

class WindowsWorkerPlatform;

class Subscription {
public:
  Subscription(ChannelID channel, HANDLE root, const string &path, WindowsWorkerPlatform *platform) :
    channel{channel},
    platform{platform},
    path{path},
    root{root},
    buffer_size{DEFAULT_BUFFER_SIZE},
    buffer{new BYTE[buffer_size]},
    written{new BYTE[buffer_size]}
  {
    ZeroMemory(&overlapped, sizeof(OVERLAPPED));
    overlapped.hEvent = this;
  }

  ~Subscription()
  {
    CloseHandle(root);
  }

  Result<> schedule()
  {
    int success = ReadDirectoryChangesW(
      root, // root directory handle
      buffer.get(), // result buffer
      buffer_size, // result buffer size
      TRUE, // recursive
      FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES
        | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS
        | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SECURITY, // change flags
      NULL, // bytes returned
      &overlapped, // overlapped
      &event_helper // completion routine
    );
    if (!success) {
      return windows_error_result<>("Unable to subscribe to filesystem events");
    }

    return ok_result();
  }

  Result<> use_network_size()
  {
    if (buffer_size <= NETWORK_BUFFER_SIZE) {
      ostringstream out("Buffer size of ");
      out
        << buffer_size
        << " is already lower than the network buffer size "
        << NETWORK_BUFFER_SIZE;
      return error_result(out.str());
    }

    buffer_size = NETWORK_BUFFER_SIZE;
    buffer.reset(new BYTE[buffer_size]);
    written.reset(new BYTE[buffer_size]);

    return ok_result();
  }

  ChannelID get_channel() const {
    return channel;
  }

  WindowsWorkerPlatform* get_platform() const {
    return platform;
  }

  BYTE *get_written(DWORD written_size) {
    memcpy(written.get(), buffer.get(), written_size);
    return written.get();
  }

  string make_absolute(const string &sub_path)
  {
    ostringstream out;

    out << path;
    if (path.back() != '\\' && sub_path.front() != '\\') {
      out << '\\';
    }
    out << sub_path;

    return out.str();
  }

private:
  ChannelID channel;
  WindowsWorkerPlatform *platform;

  string path;
  HANDLE root;
  OVERLAPPED overlapped;

  DWORD buffer_size;
  unique_ptr<BYTE[]> buffer;
  unique_ptr<BYTE[]> written;
};

class WindowsWorkerPlatform : public WorkerPlatform {
public:
  WindowsWorkerPlatform(WorkerThread *thread) :
    WorkerPlatform(thread),
    thread_handle{0}
  {
    int err;

    err = uv_mutex_init(&thread_handle_mutex);
    if (err) {
      report_uv_error(err);
    }
  };

  ~WindowsWorkerPlatform() override
  {
    uv_mutex_destroy(&thread_handle_mutex);
  }

  Result<> wake() override
  {
    Lock lock(thread_handle_mutex);

    if (!thread_handle) {
      return ok_result();
    }

    BOOL success = QueueUserAPC(
      command_perform_helper,
      thread_handle,
      reinterpret_cast<ULONG_PTR>(this)
    );
    if (!success) {
      return windows_error_result<>("Unable to queue APC");
    }

    return ok_result();
  }

  Result<> listen() override
  {
    {
      Lock lock(thread_handle_mutex);

      HANDLE pseudo_handle = GetCurrentThread();
      BOOL success = DuplicateHandle(
        GetCurrentProcess(), // Source process
        pseudo_handle, // Source handle
        GetCurrentProcess(), // Destination process
        &thread_handle, // Destination handle
        0, // Desired access
        FALSE, // Inheritable by new processes
        DUPLICATE_SAME_ACCESS // options
      );
      if (!success) {
        Result<> r = windows_error_result<>("Unable to duplicate thread handle");
        report_error("Unable to acquire thread handle");
        return r;
      }
    }

    while (true) {
      SleepEx(INFINITE, true);
    }

    report_error("listen loop ended unexpectedly");
    return health_err_result();
  }

  Result<> handle_add_command(const ChannelID channel, const string &root_path)
  {
    LOGGER << "Watching: " << root_path << endl;

    // Convert the path to a wide-character array
    size_t wlen = MultiByteToWideChar(
      CP_UTF8, // code page
      0, // flags
      root_path.data(), // source string data
      -1, // source string length (null-terminated)
      0, // output buffer
      0 // output buffer size
    );
    if (wlen == 0) {
      return windows_error_result<>("Unable to measure UTF-16 buffer");
    }
    unique_ptr<WCHAR[]> root_path_w{new WCHAR[wlen]};
    size_t conv_success = MultiByteToWideChar(
      CP_UTF8, // code page
      0, // flags,
      root_path.data(), // source string data
      -1, // source string length (null-terminated)
      root_path_w.get(), // output buffer
      wlen // output buffer size
    );
    if (!conv_success) {
      return windows_error_result<>("Unable to convert root path to UTF-16");
    }

    // Open a directory handle
    HANDLE root = CreateFileW(
      root_path_w.get(), // file name
      FILE_LIST_DIRECTORY, // desired access
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // share mode
      NULL, // security attributes
      OPEN_EXISTING, // creation disposition
      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // flags and attributes
      NULL // template file
    );
    if (root == INVALID_HANDLE_VALUE) {
      return windows_error_result<>("Unable to open directory handle");
    }

    // Allocate and persist the subscription
    Subscription *sub = new Subscription(channel, root, root_path, this);
    auto insert_result = subscriptions.insert(make_pair(channel, sub));
    if (!insert_result.second) {
      delete sub;

      ostringstream msg("Channel collision: ");
      msg << channel;
      return error_result(msg.str());
    }

    LOGGER << "Now watching directory " << root_path << "." << endl;

    return sub->schedule();
  }

  Result<> handle_remove_command(const ChannelID channel)
  {
    return ok_result();
  }

  Result<> handle_fs_event(DWORD error_code, DWORD num_bytes, Subscription* sub)
  {
    // Ensure that the subscription is valid.
    ChannelID channel = sub->get_channel();
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end() || it->second != sub) {
      return ok_result();
    }

    // Handle errors.
    if (error_code == ERROR_OPERATION_ABORTED) {
      LOGGER << "Operation aborted." << endl;

      subscriptions.erase(it);
      delete sub;

      return ok_result();
    }

    if (error_code == ERROR_INVALID_PARAMETER) {
      Result<> resize = sub->use_network_size();
      if (resize.is_error()) return resize;

      return sub->schedule();
    }

    if (error_code == ERROR_NOTIFY_ENUM_DIR) {
      LOGGER << "Change buffer overflow. Some events may have been lost." << endl;
      return sub->schedule();
    }

    if (error_code != ERROR_SUCCESS) {
      return windows_error_result<>("Completion callback error", error_code);
    }

    // Schedule the next completion callback.
    BYTE *base = sub->get_written(num_bytes);
    Result<> next = sub->schedule();
    if (next.is_error()) {
      report_error(string(next.get_error()));
    }

    // Process received events.
    vector<Message> messages;
    bool old_path_seen = false;
    string old_path;

    while (true) {
      PFILE_NOTIFY_INFORMATION info = reinterpret_cast<PFILE_NOTIFY_INFORMATION>(base);

      wstring wpath{info->FileName, info->FileNameLength};
      Result<string> u8r = to_utf8(wpath);
      if (u8r.is_error()) {
        LOGGER << "Skipping path: " << u8r << "." << endl;
      } else {
        string path = sub->make_absolute(u8r.get_value());

        switch (info->Action) {
        case FILE_ACTION_ADDED:
          {
            FileSystemPayload payload(channel, ACTION_CREATED, KIND_UNKNOWN, move(path), "");
            Message message(move(payload));

            LOGGER << "Emitting filesystem message " << message << "." << endl;
            messages.push_back(move(message));
          }
          break;
        case FILE_ACTION_MODIFIED:
          {
            FileSystemPayload payload(channel, ACTION_MODIFIED, KIND_UNKNOWN, move(path), "");
            Message message(move(payload));

            LOGGER << "Emitting filesystem message " << message << "." << endl;
            messages.push_back(move(message));
          }
          break;
        case FILE_ACTION_REMOVED:
          {
            FileSystemPayload payload(channel, ACTION_DELETED, KIND_UNKNOWN, move(path), "");
            Message message(move(payload));

            LOGGER << "Emitting filesystem message " << message << "." << endl;
            messages.push_back(move(message));
          }
          break;
        case FILE_ACTION_RENAMED_OLD_NAME:
          old_path_seen = true;
          old_path = move(path);
          break;
        case FILE_ACTION_RENAMED_NEW_NAME:
          if (old_path_seen) {
            // Old name received first
            {
              FileSystemPayload payload(channel, ACTION_RENAMED, KIND_UNKNOWN, move(old_path), move(path));
              Message message(move(payload));

              LOGGER << "Emitting filesystem message " << message << "." << endl;
              messages.push_back(move(message));
            }

            old_path_seen = false;
          } else {
            // No old name. Treat it as a creation
            {
              FileSystemPayload payload(channel, ACTION_CREATED, KIND_UNKNOWN, move(path), "");
              Message message(move(payload));

              LOGGER << "Emitting filesystem message " << message << "." << endl;
              messages.push_back(move(message));
            }
          }
          break;
        default:
          LOGGER << "Skipping unexpected action " << info->Action << "." << endl;
          break;
        }
      }

      if (info->NextEntryOffset == 0) {
        break;
      }
      base += info->NextEntryOffset;
    }

    if (!messages.empty()) {
      Result<> er = emit_all(messages.begin(), messages.end());
      if (er.is_error()) {
        LOGGER << "Unable to emit messages: " << er << "." << endl;
      }
    }

    return next;
  }

private:
  uv_mutex_t thread_handle_mutex;
  HANDLE thread_handle;

  map<ChannelID, Subscription*> subscriptions;
};

unique_ptr<WorkerPlatform> WorkerPlatform::for_worker(WorkerThread *thread)
{
  return unique_ptr<WorkerPlatform>(new WindowsWorkerPlatform(thread));
}

void CALLBACK command_perform_helper(__in ULONG_PTR payload)
{
  WindowsWorkerPlatform *platform = reinterpret_cast<WindowsWorkerPlatform*>(payload);
  platform->handle_commands();
}

static void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped)
{
  Subscription *sub = static_cast<Subscription*>(overlapped->hEvent);
  Result<> r = sub->get_platform()->handle_fs_event(error_code, num_bytes, sub);
  if (r.is_error()) {
    LOGGER << "Unable to handle filesystem events: " << r << "." << endl;
  }
}

Result<string> to_utf8(const wstring &in)
{
  size_t len = WideCharToMultiByte(
    CP_UTF8, // code page
    0, // flags
    in.data(), // source string
    in.size(), // source string length
    nullptr, // destination string, null to measure
    0, // destination string length
    nullptr, // default char
    nullptr  // used default char
  );
  if (!len) {
    return windows_error_result<string>("Unable to measure string as UTF-8");
  }

  unique_ptr<char[]> payload(new char[len]);
  size_t copied = WideCharToMultiByte(
    CP_UTF8, // code page
    0, // flags
    in.data(), // source string
    in.size(), // source string length
    payload.get(), // destination string
    len, // destination string length
    nullptr, // default char
    nullptr // used default char
  );
  if (!copied) {
    return windows_error_result<string>("Unable to convert string to UTF-8");
  }

  return ok_result(string(payload.get(), len));
}

Result<wstring> to_wchar(const string &in)
{
  size_t wlen = MultiByteToWideChar(
    CP_UTF8, // code page
    0, // flags
    in.c_str(), // source string data
    -1, // source string length (null-terminated)
    0, // output buffer
    0 // output buffer size
  );
  if (wlen == 0) {
    return windows_error_result<wstring>("Unable to measure string as wide string");
  }

  unique_ptr<WCHAR[]> payload(new WCHAR[wlen]);
  size_t conv_success = MultiByteToWideChar(
    CP_UTF8, // code page
    0, // flags,
    in.c_str(), // source string data
    -1, // source string length (null-terminated)
    payload.get(), // output buffer
    wlen // output buffer size (in bytes)
  );
  if (!conv_success) {
    return windows_error_result<wstring>("Unable to convert string to wide string");
  }

  return ok_result(wstring(payload.get(), wlen - 1));
}

template < class V >
Result<V> windows_error_result(const string &prefix)
{
  return windows_error_result<V>(prefix, GetLastError());
}

template < class V >
Result<V> windows_error_result(const string &prefix, DWORD error_code)
{
  LPVOID msg_buffer;

  FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL, // source
    error_code, // message ID
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // language ID
    (LPSTR) &msg_buffer, // output buffer
    0, // size
    NULL // arguments
  );

  ostringstream msg;
  msg << prefix << "\n (" << error_code << ") " << (char*) msg_buffer;
  LocalFree(msg_buffer);

  return Result<V>::make_error(msg.str());
}
<|endoftext|>


<commit_before>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2013 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/kernel/fs/devices/host_path_entry.h"

#include "poly/mapped_memory.h"
#include "xenia/kernel/fs/devices/host_path_file.h"

namespace xe {
namespace kernel {
namespace fs {

class HostPathMemoryMapping : public MemoryMapping {
 public:
  HostPathMemoryMapping(std::unique_ptr<poly::MappedMemory> mmap)
      : MemoryMapping(mmap->data(), mmap->size()), mmap_(std::move(mmap)) {}

 private:
  std::unique_ptr<poly::MappedMemory> mmap_;
};

HostPathEntry::HostPathEntry(Device* device, const char* path,
                             const std::wstring& local_path)
    : Entry(device, path),
      local_path_(local_path),
      find_file_(INVALID_HANDLE_VALUE) {}

HostPathEntry::~HostPathEntry() {
  if (find_file_ != INVALID_HANDLE_VALUE) {
    FindClose(find_file_);
  }
}

#define COMBINE_TIME(t) (((uint64_t)t.dwHighDateTime << 32) | t.dwLowDateTime)

X_STATUS HostPathEntry::QueryInfo(XFileInfo* out_info) {
  assert_not_null(out_info);

  WIN32_FILE_ATTRIBUTE_DATA data;
  if (!GetFileAttributesEx(local_path_.c_str(), GetFileExInfoStandard, &data)) {
    return X_STATUS_ACCESS_DENIED;
  }

  out_info->creation_time = COMBINE_TIME(data.ftCreationTime);
  out_info->last_access_time = COMBINE_TIME(data.ftLastAccessTime);
  out_info->last_write_time = COMBINE_TIME(data.ftLastWriteTime);
  out_info->change_time = COMBINE_TIME(data.ftLastWriteTime);
  out_info->allocation_size = 4096;
  out_info->file_length =
      ((uint64_t)data.nFileSizeHigh << 32) | data.nFileSizeLow;
  out_info->attributes = (X_FILE_ATTRIBUTES)data.dwFileAttributes;
  return X_STATUS_SUCCESS;
}

X_STATUS HostPathEntry::QueryDirectory(XDirectoryInfo* out_info, size_t length,
                                       const char* file_name, bool restart) {
  assert_not_null(out_info);

  WIN32_FIND_DATA ffd;

  HANDLE handle = find_file_;

  if (restart == true && handle != INVALID_HANDLE_VALUE) {
    FindClose(find_file_);
    handle = find_file_ = INVALID_HANDLE_VALUE;
  }

  if (handle == INVALID_HANDLE_VALUE) {
    std::wstring target_path = local_path_;
    if (!file_name) {
      target_path = poly::join_paths(target_path, L"*");
    } else {
      target_path = poly::join_paths(target_path, poly::to_wstring(file_name));
    }
    handle = find_file_ = FindFirstFile(target_path.c_str(), &ffd);
    if (handle == INVALID_HANDLE_VALUE) {
      if (GetLastError() == ERROR_FILE_NOT_FOUND) {
        return X_STATUS_NO_MORE_FILES;
      }
      return X_STATUS_UNSUCCESSFUL;
    }
  } else {
    if (FindNextFile(handle, &ffd) == FALSE) {
      FindClose(handle);
      find_file_ = INVALID_HANDLE_VALUE;
      return X_STATUS_NO_MORE_FILES;
    }
  }

  auto end = (uint8_t*)out_info + length;
  size_t entry_name_length = wcslen(ffd.cFileName);
  if (((uint8_t*)&out_info->file_name[0]) + entry_name_length > end) {
    FindClose(handle);
    find_file_ = INVALID_HANDLE_VALUE;
    return X_STATUS_BUFFER_OVERFLOW;
  }

  out_info->next_entry_offset = 0;
  out_info->file_index = 0xCDCDCDCD;
  out_info->creation_time = COMBINE_TIME(ffd.ftCreationTime);
  out_info->last_access_time = COMBINE_TIME(ffd.ftLastAccessTime);
  out_info->last_write_time = COMBINE_TIME(ffd.ftLastWriteTime);
  out_info->change_time = COMBINE_TIME(ffd.ftLastWriteTime);
  out_info->end_of_file =
      ((uint64_t)ffd.nFileSizeHigh << 32) | ffd.nFileSizeLow;
  out_info->allocation_size = 4096;
  out_info->attributes = (X_FILE_ATTRIBUTES)ffd.dwFileAttributes;

  out_info->file_name_length = (uint32_t)entry_name_length;
  for (size_t i = 0; i < entry_name_length; ++i) {
    out_info->file_name[i] =
        ffd.cFileName[i] < 256 ? (char)ffd.cFileName[i] : '?';
  }

  return X_STATUS_SUCCESS;
}

std::unique_ptr<MemoryMapping> HostPathEntry::CreateMemoryMapping(
    Mode map_mode, const size_t offset, const size_t length) {
  auto mmap = poly::MappedMemory::Open(
      local_path_,
      map_mode == Mode::READ ? poly::MappedMemory::Mode::kRead
                             : poly::MappedMemory::Mode::kReadWrite,
      offset, length);
  if (!mmap) {
    return nullptr;
  }

  return std::make_unique<HostPathMemoryMapping>(std::move(mmap));
}

X_STATUS HostPathEntry::Open(KernelState* kernel_state, Mode mode, bool async,
                             XFile** out_file) {
  DWORD desired_access =
      mode == Mode::READ ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE);
  DWORD share_mode = FILE_SHARE_READ;
  DWORD creation_disposition = mode == Mode::READ ? OPEN_EXISTING : OPEN_ALWAYS;
  DWORD flags_and_attributes = async ? FILE_FLAG_OVERLAPPED : 0;
  HANDLE file =
      CreateFile(local_path_.c_str(), desired_access, share_mode, NULL,
                 creation_disposition,
                 flags_and_attributes | FILE_FLAG_BACKUP_SEMANTICS, NULL);
  if (file == INVALID_HANDLE_VALUE) {
    // TODO(benvanik): pick correct response.
    return X_STATUS_ACCESS_DENIED;
  }

  *out_file = new HostPathFile(kernel_state, mode, this, file);
  return X_STATUS_SUCCESS;
}

}  // namespace fs
}  // namespace kernel
}  // namespace xe
<commit_msg>Somewhere the file access mode is messed up...<commit_after>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2013 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/kernel/fs/devices/host_path_entry.h"

#include "poly/mapped_memory.h"
#include "xenia/kernel/fs/devices/host_path_file.h"

namespace xe {
namespace kernel {
namespace fs {

class HostPathMemoryMapping : public MemoryMapping {
 public:
  HostPathMemoryMapping(std::unique_ptr<poly::MappedMemory> mmap)
      : MemoryMapping(mmap->data(), mmap->size()), mmap_(std::move(mmap)) {}

 private:
  std::unique_ptr<poly::MappedMemory> mmap_;
};

HostPathEntry::HostPathEntry(Device* device, const char* path,
                             const std::wstring& local_path)
    : Entry(device, path),
      local_path_(local_path),
      find_file_(INVALID_HANDLE_VALUE) {}

HostPathEntry::~HostPathEntry() {
  if (find_file_ != INVALID_HANDLE_VALUE) {
    FindClose(find_file_);
  }
}

#define COMBINE_TIME(t) (((uint64_t)t.dwHighDateTime << 32) | t.dwLowDateTime)

X_STATUS HostPathEntry::QueryInfo(XFileInfo* out_info) {
  assert_not_null(out_info);

  WIN32_FILE_ATTRIBUTE_DATA data;
  if (!GetFileAttributesEx(local_path_.c_str(), GetFileExInfoStandard, &data)) {
    return X_STATUS_ACCESS_DENIED;
  }

  out_info->creation_time = COMBINE_TIME(data.ftCreationTime);
  out_info->last_access_time = COMBINE_TIME(data.ftLastAccessTime);
  out_info->last_write_time = COMBINE_TIME(data.ftLastWriteTime);
  out_info->change_time = COMBINE_TIME(data.ftLastWriteTime);
  out_info->allocation_size = 4096;
  out_info->file_length =
      ((uint64_t)data.nFileSizeHigh << 32) | data.nFileSizeLow;
  out_info->attributes = (X_FILE_ATTRIBUTES)data.dwFileAttributes;
  return X_STATUS_SUCCESS;
}

X_STATUS HostPathEntry::QueryDirectory(XDirectoryInfo* out_info, size_t length,
                                       const char* file_name, bool restart) {
  assert_not_null(out_info);

  WIN32_FIND_DATA ffd;

  HANDLE handle = find_file_;

  if (restart == true && handle != INVALID_HANDLE_VALUE) {
    FindClose(find_file_);
    handle = find_file_ = INVALID_HANDLE_VALUE;
  }

  if (handle == INVALID_HANDLE_VALUE) {
    std::wstring target_path = local_path_;
    if (!file_name) {
      target_path = poly::join_paths(target_path, L"*");
    } else {
      target_path = poly::join_paths(target_path, poly::to_wstring(file_name));
    }
    handle = find_file_ = FindFirstFile(target_path.c_str(), &ffd);
    if (handle == INVALID_HANDLE_VALUE) {
      if (GetLastError() == ERROR_FILE_NOT_FOUND) {
        return X_STATUS_NO_MORE_FILES;
      }
      return X_STATUS_UNSUCCESSFUL;
    }
  } else {
    if (FindNextFile(handle, &ffd) == FALSE) {
      FindClose(handle);
      find_file_ = INVALID_HANDLE_VALUE;
      return X_STATUS_NO_MORE_FILES;
    }
  }

  auto end = (uint8_t*)out_info + length;
  size_t entry_name_length = wcslen(ffd.cFileName);
  if (((uint8_t*)&out_info->file_name[0]) + entry_name_length > end) {
    FindClose(handle);
    find_file_ = INVALID_HANDLE_VALUE;
    return X_STATUS_BUFFER_OVERFLOW;
  }

  out_info->next_entry_offset = 0;
  out_info->file_index = 0xCDCDCDCD;
  out_info->creation_time = COMBINE_TIME(ffd.ftCreationTime);
  out_info->last_access_time = COMBINE_TIME(ffd.ftLastAccessTime);
  out_info->last_write_time = COMBINE_TIME(ffd.ftLastWriteTime);
  out_info->change_time = COMBINE_TIME(ffd.ftLastWriteTime);
  out_info->end_of_file =
      ((uint64_t)ffd.nFileSizeHigh << 32) | ffd.nFileSizeLow;
  out_info->allocation_size = 4096;
  out_info->attributes = (X_FILE_ATTRIBUTES)ffd.dwFileAttributes;

  out_info->file_name_length = (uint32_t)entry_name_length;
  for (size_t i = 0; i < entry_name_length; ++i) {
    out_info->file_name[i] =
        ffd.cFileName[i] < 256 ? (char)ffd.cFileName[i] : '?';
  }

  return X_STATUS_SUCCESS;
}

std::unique_ptr<MemoryMapping> HostPathEntry::CreateMemoryMapping(
    Mode map_mode, const size_t offset, const size_t length) {
  auto mmap = poly::MappedMemory::Open(
      local_path_,
      map_mode == Mode::READ ? poly::MappedMemory::Mode::kRead
                             : poly::MappedMemory::Mode::kReadWrite,
      offset, length);
  if (!mmap) {
    return nullptr;
  }

  return std::make_unique<HostPathMemoryMapping>(std::move(mmap));
}

X_STATUS HostPathEntry::Open(KernelState* kernel_state, Mode mode, bool async,
                             XFile** out_file) {
  // TODO(benvanik): plumb through proper disposition/access mode.
  DWORD desired_access =
      is_read_only() ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE);
  // mode == Mode::READ ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE);
  DWORD share_mode = FILE_SHARE_READ;
  DWORD creation_disposition = mode == Mode::READ ? OPEN_EXISTING : OPEN_ALWAYS;
  DWORD flags_and_attributes = async ? FILE_FLAG_OVERLAPPED : 0;
  HANDLE file =
      CreateFile(local_path_.c_str(), desired_access, share_mode, NULL,
                 creation_disposition,
                 flags_and_attributes | FILE_FLAG_BACKUP_SEMANTICS, NULL);
  if (file == INVALID_HANDLE_VALUE) {
    // TODO(benvanik): pick correct response.
    return X_STATUS_ACCESS_DENIED;
  }

  *out_file = new HostPathFile(kernel_state, mode, this, file);
  return X_STATUS_SUCCESS;
}

}  // namespace fs
}  // namespace kernel
}  // namespace xe
<|endoftext|>


<commit_before>#ifndef STAN_MATH_GPU_BASIC_MATRIX_KERNELS_HPP
#define STAN_MATH_GPU_BASIC_MATRIX_KERNELS_HPP

#include <string>

/**
 *  @file stan/math/gpu/basic_matrix_kernels.hpp
 *  @brief Kernel sources for basic matrix operations on the gpu:
 *    copy, copy lower/upper triangular, copy triangular transposed,
 *    copy submatrix, init to zeros, init to identity,
 *    add, subtract, transpose
 */

namespace stan {
namespace math {

std::string copy_matrix_kernel
    = "#define A(i,j)  A[j*rows+i] \n"
      "#define B(i,j)  B[j*rows+i] \n"
      " __kernel void copy( \n"
      "      __global double *A, \n"
      "      __global double *B, \n"
      "      unsigned int rows, \n"
      "      unsigned int cols) { \n"
      "  int i = get_global_id(0); \n"
      "  int j = get_global_id(1); \n"
      "  if ( i < rows && j < cols ) { \n"
      "   B(i,j) = A(i,j); \n"
      "  }\n"
      "}\n";
}
}  // namespace stan
#endif
<commit_msg>Add ifdef STAN_OPENCL to basic_matrix_kernels<commit_after>#ifndef STAN_MATH_GPU_BASIC_MATRIX_KERNELS_HPP
#define STAN_MATH_GPU_BASIC_MATRIX_KERNELS_HPP
#ifdef STAN_OPENCL
#include <string>

/**
 *  @file stan/math/gpu/basic_matrix_kernels.hpp
 *  @brief Kernel sources for basic matrix operations on the gpu:
 *    copy, copy lower/upper triangular, copy triangular transposed,
 *    copy submatrix, init to zeros, init to identity,
 *    add, subtract, transpose
 */

namespace stan {
namespace math {

std::string copy_matrix_kernel
    = "#define A(i,j)  A[j*rows+i] \n"
      "#define B(i,j)  B[j*rows+i] \n"
      " __kernel void copy( \n"
      "      __global double *A, \n"
      "      __global double *B, \n"
      "      unsigned int rows, \n"
      "      unsigned int cols) { \n"
      "  int i = get_global_id(0); \n"
      "  int j = get_global_id(1); \n"
      "  if ( i < rows && j < cols ) { \n"
      "   B(i,j) = A(i,j); \n"
      "  }\n"
      "}\n";
}
}  // namespace stan
#endif
#endif
<|endoftext|>


<commit_before>/* Copyright 2015 H4314, MIT License */
#include <iostream>
#include <string>

#define OPT_TRANSFORM "-o"
#define OPT_PRINT "-p"
#define OPT_STATIC "-a"
#define OPT_EXEC "-e"

using std::string;
using std::cout;
using std::cerr;
using std::endl;

int main(int argc, char* argv[]) {
  bool transformEnabled = false;
  bool printEnabled = false;
  bool staticEnabled = false;
  bool execEnabled = false;
  string* fileName = NULL;

  for (int i = 1; i < argc; i++) {
    string* arg = new string(argv[i]);
    if (arg->compare(OPT_TRANSFORM) == 0) {
      transformEnabled = true;
      delete arg;
    } else if (arg->compare(OPT_PRINT) == 0) {
      printEnabled = true;
      delete arg;
    } else if (arg->compare(OPT_STATIC) == 0) {
      staticEnabled = true;
      delete arg;
    } else if (arg->compare(OPT_EXEC) == 0) {
      execEnabled = true;
      delete arg;
    } else {
      fileName = arg;
    }
  }

  if (fileName == NULL) {
      string errorMessage = "Erreur, veuillez specifier des arguments\n\
  Utilisation :\n\
    ../lut [-p] [-a] [-e] [-o] source.lt\n\
      [-p] affiche le code source reconnu\n\
      [-a] analyse le programme de maniere statique\n\
      [-e] execute interactivement le programme\n\
      [-o] optimise les expressions et instructions\n";

    cerr << errorMessage << endl;
    return 1;
  }

  // LEXER
  // PARSER
  if (transformEnabled) {
    cout << "Transform (optimize) input" << endl;
    // TRANSFORM
  }
  if (printEnabled) {
    cout << "Print (transformed?) input" << endl;
    // PRINT
  }
  if (staticEnabled) {
    cout << "Analyze statically" << endl;
    // STATIC
  }
  if (execEnabled) {
    cout << "Execute" << endl;
    // EXEC
  }

  return 0;
}
<commit_msg>cpplint errors fixed<commit_after>/* Copyright 2015 H4314, MIT License */
#include <iostream>
#include <string>

#define OPT_TRANSFORM "-o"
#define OPT_PRINT "-p"
#define OPT_STATIC "-a"
#define OPT_EXEC "-e"

using std::string;
using std::cout;
using std::cerr;
using std::endl;

int main(int argc, char* argv[]) {
  bool transformEnabled = false;
  bool printEnabled = false;
  bool staticEnabled = false;
  bool execEnabled = false;
  string* fileName = NULL;

  for (int i = 1; i < argc; i++) {
    string* arg = new string(argv[i]);
    if (arg->compare(OPT_TRANSFORM) == 0) {
      transformEnabled = true;
      delete arg;
    } else if (arg->compare(OPT_PRINT) == 0) {
      printEnabled = true;
      delete arg;
    } else if (arg->compare(OPT_STATIC) == 0) {
      staticEnabled = true;
      delete arg;
    } else if (arg->compare(OPT_EXEC) == 0) {
      execEnabled = true;
      delete arg;
    } else {
      fileName = arg;
    }
  }

  if (fileName == NULL) {
    string errorMessage = "Erreur, veuillez specifier des arguments\n"
    "  Utilisation :\n"
    "    ../lut [-p] [-a] [-e] [-o] source.lt\n"
    "      [-p] affiche le code source reconnu\n"
    "      [-a] analyse le programme de maniere statique\n"
    "      [-e] execute interactivement le programme\n"
    "      [-o] optimise les expressions et instructions\n";

    cerr << errorMessage << endl;
    return 1;
  }

  // LEXER
  // PARSER
  if (transformEnabled) {
    cout << "Transform (optimize) input" << endl;
    // TRANSFORM
  }
  if (printEnabled) {
    cout << "Print (transformed?) input" << endl;
    // PRINT
  }
  if (staticEnabled) {
    cout << "Analyze statically" << endl;
    // STATIC
  }
  if (execEnabled) {
    cout << "Execute" << endl;
    // EXEC
  }

  return 0;
}
<|endoftext|>


<commit_before>#include <ncurses.h>

// Erase characters which would be left behind
void erase(int row, int col)
{
	mvaddch(row, col, ' ');
}

void move_actor(int row, int col, char symbol)
{
	for(;;)
	{
		int ch = getch();

		if (ch == 'h' || ch == 'H' || ch == KEY_LEFT || ch == '4')
		{
			erase(row, col);
			col = col - 1;
			mvaddch(row, col, symbol);
			refresh();
		}
		if (ch == 'l' || ch == 'L' || ch == KEY_RIGHT || ch == '6')
		{
			erase(row, col);
			col = col + 1;
			mvaddch(row, col, symbol);
			refresh();
		}
		if (ch == 'k' || ch == 'K' || ch == KEY_UP || ch == '8')
		{
			erase(row, col);
			row = row - 1;
			mvaddch(row, col, symbol);
			refresh();
		}
		if (ch == 'j' || ch == 'J' || ch == KEY_DOWN || ch == '2')
		{
			erase(row, col);
			row = row + 1;
			mvaddch(row, col, symbol);
			refresh();
		}
		else if(ch == 'q' || ch == 'Q')
			break;
	}
}

void game_loop(int ch)
{
	// Check if our user wants to play
	if(ch == 'q' || ch =='Q') return;

	// Slap an actor on the screen at (10,10)
	int row = 10, col = 10;
	char player_symbol = '@';

	mvaddch(row, col, player_symbol);

	// Allow movement!
	move_actor(row, col, player_symbol);
}

void new_game()
{
	// Greet the user
	printw("Welcome.\nPress the any key to play.\nPress [q] to quit!\n");

	// Wait for user input then scrub
	int ch = getch();
	clear();

	// Run the game loop
	game_loop(ch);
}

void init_ncurses()
{
	initscr();
	clear();
	noecho();
	cbreak();
	keypad(stdscr, TRUE);
	curs_set(0);
}

int main()
{
	// Initialize the ncurses display
	init_ncurses();

	// Start a new game
	new_game();

	// Abolish ncurses
	endwin();

	return 0;
}
<commit_msg>Allow diagonal movement.<commit_after>#include <ncurses.h>

// Erase characters which would be left behind
void erase(int row, int col)
{
	mvaddch(row, col, ' ');
}

void move_actor(int row, int col, char symbol)
{
	for(;;)
	{
		int ch = getch();

		if (ch == 'h' || ch == 'H' || ch == KEY_LEFT || ch == '4')
		{
			erase(row, col);
			col = col - 1;
			mvaddch(row, col, symbol);
			refresh();
		}
		if (ch == 'l' || ch == 'L' || ch == KEY_RIGHT || ch == '6')
		{
			erase(row, col);
			col = col + 1;
			mvaddch(row, col, symbol);
			refresh();
		}
		if (ch == 'k' || ch == 'K' || ch == KEY_UP || ch == '8')
		{
			erase(row, col);
			row = row - 1;
			mvaddch(row, col, symbol);
			refresh();
		}
		if (ch == 'j' || ch == 'J' || ch == KEY_DOWN || ch == '2')
		{
			erase(row, col);
			row = row + 1;
			mvaddch(row, col, symbol);
			refresh();
		}
		if (ch == 'y' || ch == 'Y' || ch == '7')
		{
			erase(row, col);
			row = row - 1;
			col = col - 1;
			mvaddch(row, col, symbol);
			refresh();
		}
		if (ch == 'u' || ch == 'U' || ch == '9')
		{
			erase(row, col);
			row = row - 1;
			col = col + 1;
			mvaddch(row, col, symbol);
			refresh();
		}
		if (ch == 'b' || ch == 'B' || ch == '1')
		{
			erase(row, col);
			row = row + 1;
			col = col - 1;
			mvaddch(row, col, symbol);
			refresh();
		}
		if (ch == 'n' || ch == 'N' || ch == '3')
		{
			erase(row, col);
			row = row + 1;
			col = col + 1;
			mvaddch(row, col, symbol);
			refresh();
		}
		else if(ch == 'q' || ch == 'Q')
			break;
	}
}

void game_loop(int ch)
{
	// Check if our user wants to play
	if(ch == 'q' || ch =='Q') return;

	// Slap an actor on the screen at (10,10)
	int row = 10, col = 10;
	char player_symbol = '@';

	mvaddch(row, col, player_symbol);

	// Allow movement!
	move_actor(row, col, player_symbol);
}

void new_game()
{
	// Greet the user
	printw("Welcome.\nPress the any key to play.\nPress [q] to quit!\n");

	// Wait for user input then scrub
	int ch = getch();
	clear();

	// Run the game loop
	game_loop(ch);
}

void init_ncurses()
{
	initscr();
	clear();
	noecho();
	cbreak();
	keypad(stdscr, TRUE);
	curs_set(0);
}

int main()
{
	// Initialize the ncurses display
	init_ncurses();

	// Start a new game
	new_game();

	// Abolish ncurses
	endwin();

	return 0;
}
<|endoftext|>


<commit_before>/*
 * ForwardChainer.cc
 *
 * Copyright (C) 2014,2015 Misgana Bayetta
 *
 * Author: Misgana Bayetta <misgana.bayetta@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/util/Logger.h>
#include <opencog/atoms/bind/PatternLink.h>
#include <opencog/atomutils/AtomUtils.h>
#include <opencog/query/DefaultImplicator.h>
#include <opencog/rule-engine/Rule.h>
#include <opencog/atoms/bind/BindLink.h>
#include "ForwardChainer.h"
#include "ForwardChainerCallBack.h"

using namespace opencog;

ForwardChainer::ForwardChainer(AtomSpace& as, Handle rbs) :
	_as(as), _rec(_as), _rbs(rbs), _configReader(as, rbs), _fcmem(&as)
{
    init();
}

void ForwardChainer::init()
{
    _fcmem.set_search_in_af(_configReader.get_attention_allocation());
    _fcmem.set_rules(_configReader.get_rules());
    _fcmem.set_cur_rule(nullptr);

    // Provide a logger
    _log = NULL;
    setLogger(new opencog::Logger("forward_chainer.log", Logger::FINE, true));
}

void ForwardChainer::setLogger(Logger* log)
{
    if (_log)
        delete _log;
    _log = log;
}

Logger* ForwardChainer::getLogger()
{
    return _log;
}

/**
 * Does one step forward chaining
 *
 * @param fcb a concrete implementation of of ForwardChainerCallBack class
 */
UnorderedHandleSet ForwardChainer::do_step(bool search_focus_set/* = false*/)
{

    Handle hsource = choose_next_source(_fcmem);

    _log->debug("[ForwardChainer] Next source %s", hsource->toString().c_str());

    _fcmem.set_source(hsource);

    HandleSeq derived_rhandles;

    //choose a rule that source unifies with one of its premises.
    Rule *rule = choose_rule(hsource, false);
    if (rule) {
        _fcmem.set_cur_rule(rule);
        derived_rhandles = derive_rules(hsource, rule);

    } else {
        //choose rule that unifies that source unifies with sub-atoms of its premises.
        rule = choose_rule(hsource, true);

        if (rule) {
            _fcmem.set_cur_rule(rule);
            derived_rhandles = derive_rules(hsource, rule,
            true);
        }
    }

    _log->debug( "Derived rule size = %d", derived_rhandles.size());

    UnorderedHandleSet products;

    for (Handle rhandle : derived_rhandles) {
        HandleSeq temp_result = apply_rule(rhandle,search_focus_set);

        std::copy(temp_result.begin(), temp_result.end(),
                  std::inserter(products, products.end()));
    }

    return products;
}

void ForwardChainer::do_chain(Handle hsource, HandleSeq focus_set)
{

    validate(hsource,focus_set);

    _fcmem.set_focus_set(focus_set);

    HandleSeq init_sources;
    //Accept set of initial sources wrapped in a SET_LINK
    if(LinkCast(hsource) and hsource->getType() == SET_LINK)
    {
     init_sources = _as.get_outgoing(hsource);

     //Relex2Logic uses this.TODO make a separate class
     //to handle this robustly.
     if(init_sources.empty())
     {
         bool search_in_af = not focus_set.empty();
         apply_all_rules(search_in_af);
         return;
     }

    }
    else
    {
        init_sources.push_back(hsource);
    }

    // Variable fulfillment query.
    UnorderedHandleSet var_nodes = get_outgoing_nodes(hsource,
                                                      { VARIABLE_NODE });
    if (not var_nodes.empty())
        return do_pm(hsource, var_nodes);

    // Default forward chaining
    _fcmem.update_potential_sources(init_sources);

    auto max_iter = _configReader.get_maximum_iterations();

    while (_iteration < max_iter /*OR other termination criteria*/) {

        _log->debug("Iteration %d", _iteration);

        UnorderedHandleSet products;

        if (focus_set.empty())
            products = do_step(false);
        else
            products = do_step(true);

        _fcmem.add_rules_product(_iteration,
                                 HandleSeq(products.begin(), products.end()));
        _fcmem.update_potential_sources(
                HandleSeq(products.begin(), products.end()));

        _iteration++;
    }

    _log->debug("[ForwardChainer] finished forwarch chaining.");
}

/**
 * Does pattern matching for a variable containing query.
 * @param source a variable containing handle passed as an input to the pattern matcher
 * @param var_nodes the VariableNodes in @param hsource
 * @param fcb a forward chainer callback implementation used here only for choosing rules
 * that contain @param hsource in their implicant
 */
void ForwardChainer::do_pm(const Handle& hsource,
                           const UnorderedHandleSet& var_nodes,
                           ForwardChainerCallBack& fcb)
{
    DefaultImplicator impl(&_as);
    impl.implicand = hsource;
    HandleSeq vars;
    for (auto h : var_nodes)
        vars.push_back(h);
    _fcmem.set_source(hsource);
    Handle hvar_list = _as.add_link(VARIABLE_LIST, vars);
    Handle hclause = _as.add_link(AND_LINK, hsource);

    // Run the pattern matcher, find all patterns that satisfy the
    // the clause, with the given variables in it.
    PatternLinkPtr sl(createPatternLink(hvar_list, hclause));
    sl->satisfy(impl);

    // Update result
    _fcmem.add_rules_product(0, impl.get_result_list());

    // Delete the AND_LINK and LIST_LINK
    _as.remove_atom(hvar_list);
    _as.remove_atom(hclause);

    //!Additionally, find applicable rules and apply.
    vector<Rule*> rules = fcb.choose_rules(_fcmem);
    for (Rule* rule : rules) {
        BindLinkPtr bl(BindLinkCast(rule->get_handle()));
        DefaultImplicator impl(&_as);
        impl.implicand = bl->get_implicand();
        bl->imply(impl);
        _fcmem.set_cur_rule(rule);
        _fcmem.add_rules_product(0, impl.get_result_list());
    }
}
/**
 * Invokes pattern matcher using each rule declared in the configuration file.
 */
void ForwardChainer::do_pm()
{
    //! Do pattern matching using the rules declared in the declaration file
    _log->info("Forward chaining on the rule-based system %s "
               "declared in %s", _rbs->toString().c_str());
    vector<Rule*> rules = _fcmem.get_rules();
    for (Rule* rule : rules) {
        _log->info("Applying rule %s on ", rule->get_name().c_str());
        BindLinkPtr bl(BindLinkCast(rule->get_handle()));
        DefaultImplicator impl(&_as);
        impl.implicand = bl->get_implicand();
        bl->imply(impl);
        _fcmem.set_cur_rule(rule);

        _log->info("OUTPUTS");
        for (auto h : impl.get_result_list())
            _log->info("%s", h->toString().c_str());

        _fcmem.add_rules_product(0, impl.get_result_list());
    }

}

HandleSeq ForwardChainer::get_chaining_result()
{
    return _fcmem.get_result();
}

Rule* ForwardChainer::choose_rule(Handle hsource, bool subatom_match)
{
    //TODO move this somewhere else
    std::map<Rule*, float> rule_weight;
    for (Rule* r : _fcmem.get_rules())
        rule_weight[r] = r->get_weight();

    _log->debug("[ForwardChainer] %d rules to be searched",rule_weight.size());

    //Select a rule among the admissible rules in the rule-base via stochastic
    //selection,based on the weights of the rules in the current context.
    Rule* rule = nullptr;
    bool unifiable = false;

    if (subatom_match) {
        _log->debug("[ForwardChainer] Subatom-unifying. %s",(hsource->toShortString()).c_str());

        while (!unifiable and !rule_weight.empty()) {
            Rule* temp = _rec.tournament_select(rule_weight);

            if (subatom_unify(hsource, temp)) {
                unifiable = true;
                rule = temp;
                break;
            }
            rule_weight.erase(temp);
        }

    } else {
        _log->debug("[ForwardChainer] Unifying. %s",(hsource->toShortString()).c_str());

        while (!unifiable and !rule_weight.empty()) {
            Rule *temp = _rec.tournament_select(rule_weight);
            HandleSeq hs = temp->get_implicant_seq();

            for (Handle target : hs) {
                if (unify(hsource, target, temp)) {
                    unifiable = true;
                    rule = temp;
                    break;
                }
            }
            rule_weight.erase(temp);
        }
    }

    if(nullptr != rule)
        _log->debug("[ForwardChainer] Selected rule is %s",
                            (rule->get_handle())->toShortString().c_str());
    else
       _log->debug("[ForwardChainer] No match found.");

    return rule;
};
HandleSeq ForwardChainer::apply_rule(Handle rhandle,bool search_in_focus_set /*=false*/)
{
    HandleSeq result;

    if (search_in_focus_set) {
        //This restricts PM to look only in the focus set
        AtomSpace focus_set_as;

        //Add focus set atoms to focus_set atomspace
        HandleSeq focus_set_atoms = _fcmem.get_focus_set();
        for (Handle h : focus_set_atoms)
            focus_set_as.add_atom(h);

        //Add source atoms to focus_set atomspace
        HandleSeq sources = _fcmem.get_potential_sources();
        for (Handle h : sources)
            focus_set_as.add_atom(h);

        //rhandle may introduce a new atoms that satisfies condition for the output
        //In order to prevent this undesirable effect, lets store rhandle in a child
        //atomspace of parent focus_set_as so that PM will never be able to find this
        //new undesired atom created from partial grounding.
        AtomSpace derived_rule_as(&focus_set_as);
        Handle rhcpy = derived_rule_as.add_atom(rhandle);

        BindLinkPtr bl = BindLinkCast(rhcpy);

        FocusSetPMCB fs_pmcb(&derived_rule_as, &_as);
        fs_pmcb.implicand = bl->get_implicand();

        _log->debug("Applying rule in focus set %s ",(rhcpy->toShortString()).c_str());

        std::cout << "ATOMSPACE:" << derived_rule_as << std::endl;
        bl->imply(fs_pmcb);

        result = fs_pmcb.get_result_list();

        _log->debug(
                "Result is %s ",
                ((_as.add_link(SET_LINK, result))->toShortString()).c_str());

    }
    //Search the whole atomspace
    else {
        AtomSpace derived_rule_as(&_as);

        Handle rhcpy = derived_rule_as.add_atom(rhandle);

        _log->debug("Applying rule on atomspace %s ",(rhcpy->toShortString()).c_str());

        Handle h = bindlink(&derived_rule_as,rhcpy);

        _log->debug("Result is %s ",(h->toShortString()).c_str());

        result = derived_rule_as.get_outgoing(h);
    }

    //add the results back to main atomspace
    for(Handle h:result) _as.add_atom(h);

    return result;
}
<commit_msg>Used for R2L case<commit_after>/*
 * ForwardChainer.cc
 *
 * Copyright (C) 2014,2015 Misgana Bayetta
 *
 * Author: Misgana Bayetta <misgana.bayetta@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/util/Logger.h>
#include <opencog/atoms/bind/PatternLink.h>
#include <opencog/atomutils/AtomUtils.h>
#include <opencog/query/DefaultImplicator.h>
#include <opencog/rule-engine/Rule.h>
#include <opencog/atoms/bind/BindLink.h>
#include "ForwardChainer.h"
#include "ForwardChainerCallBack.h"

using namespace opencog;

ForwardChainer::ForwardChainer(AtomSpace& as, Handle rbs) :
	_as(as), _rec(_as), _rbs(rbs), _configReader(as, rbs), _fcmem(&as)
{
    init();
}

void ForwardChainer::init()
{
    _fcmem.set_search_in_af(_configReader.get_attention_allocation());
    _fcmem.set_rules(_configReader.get_rules());
    _fcmem.set_cur_rule(nullptr);

    // Provide a logger
    _log = NULL;
    setLogger(new opencog::Logger("forward_chainer.log", Logger::FINE, true));
}

void ForwardChainer::setLogger(Logger* log)
{
    if (_log)
        delete _log;
    _log = log;
}

Logger* ForwardChainer::getLogger()
{
    return _log;
}

/**
 * Does one step forward chaining
 *
 * @param fcb a concrete implementation of of ForwardChainerCallBack class
 */
UnorderedHandleSet ForwardChainer::do_step(bool search_focus_set/* = false*/)
{

    Handle hsource = choose_next_source(_fcmem);

    _log->debug("[ForwardChainer] Next source %s", hsource->toString().c_str());

    _fcmem.set_source(hsource);

    HandleSeq derived_rhandles;

    //choose a rule that source unifies with one of its premises.
    Rule *rule = choose_rule(hsource, false);
    if (rule) {
        _fcmem.set_cur_rule(rule);
        derived_rhandles = derive_rules(hsource, rule);

    } else {
        //choose rule that unifies that source unifies with sub-atoms of its premises.
        rule = choose_rule(hsource, true);

        if (rule) {
            _fcmem.set_cur_rule(rule);
            derived_rhandles = derive_rules(hsource, rule,
            true);
        }
    }

    _log->debug( "Derived rule size = %d", derived_rhandles.size());

    UnorderedHandleSet products;

    for (Handle rhandle : derived_rhandles) {
        HandleSeq temp_result = apply_rule(rhandle,search_focus_set);

        std::copy(temp_result.begin(), temp_result.end(),
                  std::inserter(products, products.end()));
    }

    return products;
}

void ForwardChainer::do_chain(Handle hsource, HandleSeq focus_set)
{

    validate(hsource,focus_set);

    _fcmem.set_focus_set(focus_set);

    HandleSeq init_sources;
    //Accept set of initial sources wrapped in a SET_LINK
    if(LinkCast(hsource) and hsource->getType() == SET_LINK)
    {
     init_sources = _as.get_outgoing(hsource);

     //Relex2Logic uses this.TODO make a separate class
     //to handle this robustly.
     if(init_sources.empty())
     {
         bool search_in_af = not focus_set.empty();
         apply_all_rules(search_in_af);
         return;
     }

    }
    else
    {
        init_sources.push_back(hsource);
    }

    // Variable fulfillment query.
    UnorderedHandleSet var_nodes = get_outgoing_nodes(hsource,
                                                      { VARIABLE_NODE });
    if (not var_nodes.empty())
        return do_pm(hsource, var_nodes);

    // Default forward chaining
    _fcmem.update_potential_sources(init_sources);

    auto max_iter = _configReader.get_maximum_iterations();

    while (_iteration < max_iter /*OR other termination criteria*/) {

        _log->debug("Iteration %d", _iteration);

        UnorderedHandleSet products;

        if (focus_set.empty())
            products = do_step(false);
        else
            products = do_step(true);

        _fcmem.add_rules_product(_iteration,
                                 HandleSeq(products.begin(), products.end()));
        _fcmem.update_potential_sources(
                HandleSeq(products.begin(), products.end()));

        _iteration++;
    }

    _log->debug("[ForwardChainer] finished forwarch chaining.");
}

/**
 * Does pattern matching for a variable containing query.
 * @param source a variable containing handle passed as an input to the pattern matcher
 * @param var_nodes the VariableNodes in @param hsource
 * @param fcb a forward chainer callback implementation used here only for choosing rules
 * that contain @param hsource in their implicant
 */
void ForwardChainer::do_pm(const Handle& hsource,
                           const UnorderedHandleSet& var_nodes,
                           ForwardChainerCallBack& fcb)
{
    DefaultImplicator impl(&_as);
    impl.implicand = hsource;
    HandleSeq vars;
    for (auto h : var_nodes)
        vars.push_back(h);
    _fcmem.set_source(hsource);
    Handle hvar_list = _as.add_link(VARIABLE_LIST, vars);
    Handle hclause = _as.add_link(AND_LINK, hsource);

    // Run the pattern matcher, find all patterns that satisfy the
    // the clause, with the given variables in it.
    PatternLinkPtr sl(createPatternLink(hvar_list, hclause));
    sl->satisfy(impl);

    // Update result
    _fcmem.add_rules_product(0, impl.get_result_list());

    // Delete the AND_LINK and LIST_LINK
    _as.remove_atom(hvar_list);
    _as.remove_atom(hclause);

    //!Additionally, find applicable rules and apply.
    vector<Rule*> rules = fcb.choose_rules(_fcmem);
    for (Rule* rule : rules) {
        BindLinkPtr bl(BindLinkCast(rule->get_handle()));
        DefaultImplicator impl(&_as);
        impl.implicand = bl->get_implicand();
        bl->imply(impl);
        _fcmem.set_cur_rule(rule);
        _fcmem.add_rules_product(0, impl.get_result_list());
    }
}

/**
 * Applies all rules in the rule base.
 *
 * @param search_focus_set flag for searching focus set.
 */
void ForwardChainer::apply_all_rules(bool search_focus_set /*= false*/)
{
    vector<Rule*> rules = _fcmem.get_rules();

    for (Rule* rule : rules) {
        _fcmem.set_cur_rule(rule);
        HandleSeq hs = apply_rule(rule->get_handle(), search_focus_set);

        //Update
        _fcmem.add_rules_product(0, hs);
        _fcmem.update_potential_sources(hs);
    }

}

HandleSeq ForwardChainer::get_chaining_result()
{
    return _fcmem.get_result();
}

Rule* ForwardChainer::choose_rule(Handle hsource, bool subatom_match)
{
    //TODO move this somewhere else
    std::map<Rule*, float> rule_weight;
    for (Rule* r : _fcmem.get_rules())
        rule_weight[r] = r->get_weight();

    _log->debug("[ForwardChainer] %d rules to be searched",rule_weight.size());

    //Select a rule among the admissible rules in the rule-base via stochastic
    //selection,based on the weights of the rules in the current context.
    Rule* rule = nullptr;
    bool unifiable = false;

    if (subatom_match) {
        _log->debug("[ForwardChainer] Subatom-unifying. %s",(hsource->toShortString()).c_str());

        while (!unifiable and !rule_weight.empty()) {
            Rule* temp = _rec.tournament_select(rule_weight);

            if (subatom_unify(hsource, temp)) {
                unifiable = true;
                rule = temp;
                break;
            }
            rule_weight.erase(temp);
        }

    } else {
        _log->debug("[ForwardChainer] Unifying. %s",(hsource->toShortString()).c_str());

        while (!unifiable and !rule_weight.empty()) {
            Rule *temp = _rec.tournament_select(rule_weight);
            HandleSeq hs = temp->get_implicant_seq();

            for (Handle target : hs) {
                if (unify(hsource, target, temp)) {
                    unifiable = true;
                    rule = temp;
                    break;
                }
            }
            rule_weight.erase(temp);
        }
    }

    if(nullptr != rule)
        _log->debug("[ForwardChainer] Selected rule is %s",
                            (rule->get_handle())->toShortString().c_str());
    else
       _log->debug("[ForwardChainer] No match found.");

    return rule;
};
HandleSeq ForwardChainer::apply_rule(Handle rhandle,bool search_in_focus_set /*=false*/)
{
    HandleSeq result;

    if (search_in_focus_set) {
        //This restricts PM to look only in the focus set
        AtomSpace focus_set_as;

        //Add focus set atoms to focus_set atomspace
        HandleSeq focus_set_atoms = _fcmem.get_focus_set();
        for (Handle h : focus_set_atoms)
            focus_set_as.add_atom(h);

        //Add source atoms to focus_set atomspace
        HandleSeq sources = _fcmem.get_potential_sources();
        for (Handle h : sources)
            focus_set_as.add_atom(h);

        //rhandle may introduce a new atoms that satisfies condition for the output
        //In order to prevent this undesirable effect, lets store rhandle in a child
        //atomspace of parent focus_set_as so that PM will never be able to find this
        //new undesired atom created from partial grounding.
        AtomSpace derived_rule_as(&focus_set_as);
        Handle rhcpy = derived_rule_as.add_atom(rhandle);

        BindLinkPtr bl = BindLinkCast(rhcpy);

        FocusSetPMCB fs_pmcb(&derived_rule_as, &_as);
        fs_pmcb.implicand = bl->get_implicand();

        _log->debug("Applying rule in focus set %s ",(rhcpy->toShortString()).c_str());

        std::cout << "ATOMSPACE:" << derived_rule_as << std::endl;
        bl->imply(fs_pmcb);

        result = fs_pmcb.get_result_list();

        _log->debug(
                "Result is %s ",
                ((_as.add_link(SET_LINK, result))->toShortString()).c_str());

    }
    //Search the whole atomspace
    else {
        AtomSpace derived_rule_as(&_as);

        Handle rhcpy = derived_rule_as.add_atom(rhandle);

        _log->debug("Applying rule on atomspace %s ",(rhcpy->toShortString()).c_str());

        Handle h = bindlink(&derived_rule_as,rhcpy);

        _log->debug("Result is %s ",(h->toShortString()).c_str());

        result = derived_rule_as.get_outgoing(h);
    }

    //add the results back to main atomspace
    for(Handle h:result) _as.add_atom(h);

    return result;
}
<|endoftext|>