<commit_before>// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved.
#include "stdafx.h"
#include "TestFixtures/Decoration.hpp"
#include <autowiring/autowiring.h>
#include <autowiring/AutoSelfUpdate.h>
#include <autowiring/auto_prev.h>

class AutoFilterSequencing:
  public testing::Test
{
public:
  AutoFilterSequencing(void) {
    AutoCurrentContext()->Initiate();
  }
};

class FilterFirst {
public:
  FilterFirst(void) :
    m_magic(0xDEADBEEF),
    m_called(0)
  {}

  const int m_magic;
  int m_called;

  void AutoFilter(AutoPacket& pkt) {
    ASSERT_EQ(0xDEADBEEF, m_magic) << "Magic value was corrupted, pointer was not adjusted to the correct offset";
    ++m_called;
    pkt.Decorate(Decoration<0>());
  }
};

class FilterFirstValidateInheritance:
  public FilterFirst
{};

static_assert(
  std::is_same<
  FilterFirst,
  Decompose<decltype(&FilterFirstValidateInheritance::AutoFilter)>::type
  >::value,
  "Decomposed type did not correctly name the implementing type of an inherited method"
  );

TEST_F(AutoFilterSequencing, VerifyFirstLastCalls) {
  AutoRequired<AutoPacketFactory> factory;
  AutoRequired<FilterFirst> first;

  {
    auto pkt = factory->NewPacket();
    ASSERT_EQ(1, first->m_called) << "First-call filter was not applied";
  }
  ASSERT_EQ(1, first->m_called) << "First-call filter was applied as final call";
}

class FilterOutDeferred:
  public CoreThread
{
public:
  Deferred AutoFilter(auto_out<Decoration<0>> out) {
    //Default constructor sets i == 0
    return Deferred(this);
  }
};

TEST_F(AutoFilterSequencing, SuccessorAliasRules) {
  AutoRequired<AutoPacketFactory> factory;
  auto packet1 = factory->NewPacket();
  auto packet2 = packet1->Successor();
  ASSERT_TRUE(!!packet2) << "Returned successor packet was null";

  auto expected = factory->NewPacket();

  ASSERT_EQ(expected, packet2) << "Expected that the successor packet would match the next packet returned by the factory";
}

TEST_F(AutoFilterSequencing, SuccessorHoldViolationCheck) {
  AutoRequired<AutoPacketFactory> factory;
  auto packet1 = factory->NewPacket();
  auto packet2 = packet1->Successor();

  ASSERT_TRUE(packet1.unique()) << "Expected that the first issued packet shared pointer not to be aliased anywhere";

  packet1.reset();
  factory->NewPacket();

  ASSERT_TRUE(packet2.unique()) << "Expected that a successor packet would be unique when the principal was destroyed";
}

TEST_F(AutoFilterSequencing, PacketReverseSuccessor) {
  AutoRequired<AutoPacketFactory> factory;

  auto packet1 = factory->NewPacket();
  auto packet2 = factory->NewPacket();

  ASSERT_EQ(packet2, packet1->Successor()) << "Successor packet obtained after generation from the factory did not match as expected";
}

TEST_F(AutoFilterSequencing, ManySuccessors) {
  AutoRequired<AutoPacketFactory> factory;
  {
    auto packetA = factory->NewPacket();
    auto packet5 = packetA->Successor()->Successor()->Successor()->Successor();

    factory->NewPacket();
    factory->NewPacket();
    factory->NewPacket();
    auto packetE = factory->NewPacket();

    ASSERT_EQ(packet5, packetE) << "Successor packet obtained after generation from the factory did not match as expected";
  }

  AutoRequired<FilterFirst> first;
  {
    auto packetA = factory->NewPacket();
    packetA->Successor()->Successor()->Successor()->Successor();
    ASSERT_EQ(1, first->m_called) << "AutoFilter triggered from successor";

    factory->NewPacket();
    ASSERT_EQ(2, first->m_called) << "AutoFilter not triggered from new packet";
    factory->NewPacket();
    ASSERT_EQ(3, first->m_called) << "AutoFilter not triggered from new packet";
    factory->NewPacket();
    ASSERT_EQ(4, first->m_called) << "AutoFilter not triggered from new packet";
    factory->NewPacket();
    ASSERT_EQ(5, first->m_called) << "AutoFilter not triggered from new packet";
    factory->NewPacket();
    ASSERT_EQ(6, first->m_called) << "AutoFilter not triggered from new packet";
  }
}

class PrevFilter {
public:
  PrevFilter(void):
    m_prev_value(-1),
    m_called(0),
    m_num_empty_prev(0)
  {}

  int m_prev_value;
  int m_called;
  int m_num_empty_prev;

  void AutoFilter(int current, auto_prev<int> prev) {
    ++m_called;
    if (!prev) {
      m_num_empty_prev++;
      return;
    }
    EXPECT_EQ(m_prev_value, *prev) << "auto_prev isn't set to the previous value";
    m_prev_value = current;
  }
};

TEST_F(AutoFilterSequencing, SimplePrev) {
  AutoRequired<AutoPacketFactory> factory;
  AutoRequired<PrevFilter> filter;

  for (int i=10; i<20; ++i) {
    auto packet = factory->NewPacket();
    packet->Decorate(i);
  }

  ASSERT_EQ(10, filter->m_called) << "AutoFilter not called";
  ASSERT_EQ(1, filter->m_num_empty_prev) << "Prev should only be null for the first call";
}

class PrevPrevFilter {
public:
  PrevPrevFilter(void):
  m_prev_prev_value(-1),
  m_prev_value(-1),
  m_called(0),
  m_num_empty_prev(0)
  {}

  int m_prev_value;
  int m_prev_prev_value;
  int m_called;
  int m_num_empty_prev;

  void AutoFilter(int current, auto_prev<int, 2> prev) {
    ++m_called;
    if (!prev) {
      m_num_empty_prev++;
      return;
    }
    EXPECT_EQ(m_prev_prev_value, *prev) << "auto_prev isn't set to the previous value";
    m_prev_prev_value = m_prev_value;
    m_prev_value = current;
  }
};

TEST_F(AutoFilterSequencing, DoublePrev) {
  AutoRequired<AutoPacketFactory> factory;
  AutoRequired<PrevPrevFilter> filter;

  for (int i=10; i<20; ++i) {
    auto packet = factory->NewPacket();
    packet->Decorate(i);
  }

  ASSERT_EQ(10, filter->m_called) << "AutoFilter not called";
  ASSERT_EQ(2, filter->m_num_empty_prev) << "Prev should only be null for the first two calls";
}
<commit_msg>The "fixed" test logic still had some minor problems. This time it's actually fixed.<commit_after>// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved.
#include "stdafx.h"
#include "TestFixtures/Decoration.hpp"
#include <autowiring/autowiring.h>
#include <autowiring/AutoSelfUpdate.h>
#include <autowiring/auto_prev.h>

class AutoFilterSequencing:
  public testing::Test
{
public:
  AutoFilterSequencing(void) {
    AutoCurrentContext()->Initiate();
  }
};

class FilterFirst {
public:
  FilterFirst(void) :
    m_magic(0xDEADBEEF),
    m_called(0)
  {}

  const int m_magic;
  int m_called;

  void AutoFilter(AutoPacket& pkt) {
    ASSERT_EQ(0xDEADBEEF, m_magic) << "Magic value was corrupted, pointer was not adjusted to the correct offset";
    ++m_called;
    pkt.Decorate(Decoration<0>());
  }
};

class FilterFirstValidateInheritance:
  public FilterFirst
{};

static_assert(
  std::is_same<
  FilterFirst,
  Decompose<decltype(&FilterFirstValidateInheritance::AutoFilter)>::type
  >::value,
  "Decomposed type did not correctly name the implementing type of an inherited method"
  );

TEST_F(AutoFilterSequencing, VerifyFirstLastCalls) {
  AutoRequired<AutoPacketFactory> factory;
  AutoRequired<FilterFirst> first;

  {
    auto pkt = factory->NewPacket();
    ASSERT_EQ(1, first->m_called) << "First-call filter was not applied";
  }
  ASSERT_EQ(1, first->m_called) << "First-call filter was applied as final call";
}

class FilterOutDeferred:
  public CoreThread
{
public:
  Deferred AutoFilter(auto_out<Decoration<0>> out) {
    //Default constructor sets i == 0
    return Deferred(this);
  }
};

TEST_F(AutoFilterSequencing, SuccessorAliasRules) {
  AutoRequired<AutoPacketFactory> factory;
  auto packet1 = factory->NewPacket();
  auto packet2 = packet1->Successor();
  ASSERT_TRUE(!!packet2) << "Returned successor packet was null";

  auto expected = factory->NewPacket();

  ASSERT_EQ(expected, packet2) << "Expected that the successor packet would match the next packet returned by the factory";
}

TEST_F(AutoFilterSequencing, SuccessorHoldViolationCheck) {
  AutoRequired<AutoPacketFactory> factory;
  auto packet1 = factory->NewPacket();
  auto packet2 = packet1->Successor();

  ASSERT_TRUE(packet1.unique()) << "Expected that the first issued packet shared pointer not to be aliased anywhere";

  packet1.reset();
  factory->NewPacket();

  ASSERT_TRUE(packet2.unique()) << "Expected that a successor packet would be unique when the principal was destroyed";
}

TEST_F(AutoFilterSequencing, PacketReverseSuccessor) {
  AutoRequired<AutoPacketFactory> factory;

  auto packet1 = factory->NewPacket();
  auto packet2 = factory->NewPacket();

  ASSERT_EQ(packet2, packet1->Successor()) << "Successor packet obtained after generation from the factory did not match as expected";
}

TEST_F(AutoFilterSequencing, ManySuccessors) {
  AutoRequired<AutoPacketFactory> factory;
  {
    auto packetA = factory->NewPacket();
    auto packet5 = packetA->Successor()->Successor()->Successor()->Successor();

    factory->NewPacket();
    factory->NewPacket();
    factory->NewPacket();
    auto packetE = factory->NewPacket();

    ASSERT_EQ(packet5, packetE) << "Successor packet obtained after generation from the factory did not match as expected";
  }

  AutoRequired<FilterFirst> first;
  {
    auto packetA = factory->NewPacket();
    packetA->Successor()->Successor()->Successor()->Successor();
    ASSERT_EQ(1, first->m_called) << "AutoFilter triggered from successor";

    factory->NewPacket();
    ASSERT_EQ(2, first->m_called) << "AutoFilter not triggered from new packet";
    factory->NewPacket();
    ASSERT_EQ(3, first->m_called) << "AutoFilter not triggered from new packet";
    factory->NewPacket();
    ASSERT_EQ(4, first->m_called) << "AutoFilter not triggered from new packet";
    factory->NewPacket();
    ASSERT_EQ(5, first->m_called) << "AutoFilter not triggered from new packet";
    factory->NewPacket();
    ASSERT_EQ(6, first->m_called) << "AutoFilter not triggered from new packet";
  }
}

class PrevFilter {
public:
  PrevFilter(void):
    m_prev_value(-1),
    m_called(0),
    m_num_empty_prev(0)
  {}

  int m_prev_value;
  int m_called;
  int m_num_empty_prev;

  void AutoFilter(int current, auto_prev<int> prev) {
    ++m_called;
    if (prev) {
      EXPECT_EQ(m_prev_value, *prev) << "auto_prev isn't set to the previous value";
    } else {
      m_num_empty_prev++;
    }
    m_prev_value = current;
  }
};

TEST_F(AutoFilterSequencing, SimplePrev) {
  AutoRequired<AutoPacketFactory> factory;
  AutoRequired<PrevFilter> filter;

  for (int i=10; i<20; ++i) {
    auto packet = factory->NewPacket();
    packet->Decorate(i);
  }

  ASSERT_EQ(10, filter->m_called) << "AutoFilter not called";
  ASSERT_EQ(1, filter->m_num_empty_prev) << "Prev should only be null for the first call";
}

class PrevPrevFilter {
public:
  PrevPrevFilter(void):
  m_prev_prev_value(-1),
  m_prev_value(-1),
  m_called(0),
  m_num_empty_prev(0)
  {}

  int m_prev_value;
  int m_prev_prev_value;
  int m_called;
  int m_num_empty_prev;

  void AutoFilter(int current, auto_prev<int, 2> prev) {
    ++m_called;
    if (prev) {
      EXPECT_EQ(m_prev_prev_value, *prev) << "auto_prev isn't set to the previous value";
    } else {
      m_num_empty_prev++;
    }
    m_prev_prev_value = m_prev_value;
    m_prev_value = current;
  }
};

TEST_F(AutoFilterSequencing, DoublePrev) {
  AutoRequired<AutoPacketFactory> factory;
  AutoRequired<PrevPrevFilter> filter;

  for (int i=10; i<20; ++i) {
    auto packet = factory->NewPacket();
    packet->Decorate(i);
  }

  ASSERT_EQ(10, filter->m_called) << "AutoFilter not called";
  ASSERT_EQ(2, filter->m_num_empty_prev) << "Prev should only be null for the first two calls";
}
<|endoftext|>


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

#include "ResourceBrowser.h"
#include "ImGuiDock.h"
#include <Urho3D/SystemUI/SystemUI.h>
#include <Urho3D/Core/Context.h>
#include <Urho3D/Resource/ResourceCache.h>
#include <Urho3D/Resource/ResourceEvents.h>
#include <Urho3D/IO/FileSystem.h>
#include <Urho3D/Input/Input.h>
#include <IconFontCppHeaders/IconsFontAwesome5.h>
#include <Urho3D/IO/Log.h>
#include "Widgets.h"
#include "IO/ContentUtilities.h"

namespace Urho3D
{

ResourceBrowserResult ResourceBrowserWidget(String& path, String& selected, ResourceBrowserFlags flags)
{
    struct State
    {
        bool isEditing = false;
        bool wasEditing = false;
        bool deletionPending = false;
        char editBuffer[250]{};
        String editStartItem;
    };

    auto result = RBR_NOOP;
    auto systemUI = (SystemUI*)ui::GetIO().UserData;
    auto fs = systemUI->GetFileSystem();
    auto& state = *ui::GetUIState<State>();

    if (!selected.Empty() && !ui::IsAnyItemActive())
    {
        if (fs->GetInput()->GetKeyPress(KEY_F2) || flags & RBF_RENAME_CURRENT)
        {
            state.isEditing = true;
            state.deletionPending = false;
            state.editStartItem = selected;
            strcpy(state.editBuffer, selected.CString());
        }
        if (fs->GetInput()->GetKeyPress(KEY_DELETE) || flags & RBF_DELETE_CURRENT)
        {
            state.isEditing = false;
            state.deletionPending = true;
            state.editStartItem = selected;
        }
    }
    if (fs->GetInput()->GetKeyPress(KEY_ESCAPE) || state.editStartItem != selected)
    {
        state.isEditing = false;
        state.deletionPending = false;
    }

    if (state.deletionPending)
    {
        if (ui::Begin("Delete?", &state.deletionPending))
        {
            ui::Text("Would you like to delete '%s%s'?", path.CString(), selected.CString());
            ui::TextUnformatted(ICON_FA_EXCLAMATION_TRIANGLE " This action can not be undone!");
            ui::NewLine();

            if (ui::Button("Delete Permanently"))
            {
                using namespace ResourceBrowserDelete;
                fs->SendEvent(E_RESOURCEBROWSERDELETE, P_NAME, path + selected);
                state.deletionPending = false;
            }
        }
        ui::End();
    }

    Vector<String> mergedDirs;
    Vector<String> mergedFiles;

    String cacheDir;
    for (const auto& dir: systemUI->GetCache()->GetResourceDirs())
    {
        if (dir.EndsWith("/EditorData/"))
            continue;

        if (dir.EndsWith("/Cache/"))
        {
            cacheDir = dir;
            continue;
        }

        Vector<String> items;
        fs->ScanDir(items, dir + path, "", SCAN_FILES, false);
        for (const auto& item: items)
        {
            if (!mergedFiles.Contains(item))
                mergedFiles.Push(item);
        }

        items.Clear();
        fs->ScanDir(items, dir + path, "", SCAN_DIRS, false);
        items.Remove(".");
        items.Remove("..");
        for (const auto& item: items)
        {
            if (!mergedDirs.Contains(item))
                mergedDirs.Push(item);
        }
    }

    auto moveFileDropTarget = [&](const String& item) {
        if (ui::BeginDragDropTarget())
        {
            auto dropped = ui::AcceptDragDropVariant("path");
            if (dropped.GetType() == VAR_STRING)
            {
                using namespace ResourceBrowserRename;
                auto newName = AddTrailingSlash(item) + GetFileNameAndExtension(dropped.GetString());
                if (dropped != newName)
                    fs->SendEvent(E_RESOURCEBROWSERRENAME, P_FROM, dropped, P_TO, newName);
            }
            ui::EndDragDropTarget();
        }
    };

    if (!path.Empty())
    {
        switch (ui::DoubleClickSelectable("..", selected == ".."))
        {
        case 1:
            selected = "..";
            break;
        case 2:
            path = GetParentPath(path);
            break;
        default:
            break;
        }

        moveFileDropTarget(GetParentPath(path));
    }

    auto renameWidget = [&](const String& item, const String& icon) {
        if (selected == item && state.isEditing)
        {
            ui::TextUnformatted(icon.CString());
            ui::SameLine();

            ui::PushStyleVar(ImGuiStyleVar_FramePadding, {0, 0});
            ui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0);

            if (ui::InputText("", state.editBuffer, sizeof(state.editBuffer), ImGuiInputTextFlags_EnterReturnsTrue))
            {
                using namespace ResourceBrowserRename;
                auto oldName = path + selected;
                auto newName = path + state.editBuffer;
                if (oldName != newName)
                    fs->SendEvent(E_RESOURCEBROWSERRENAME, P_FROM, oldName, P_TO, newName);
                state.isEditing = false;
            }

            if (!state.wasEditing)
                ui::GetCurrentWindow()->FocusIdxTabRequestNext = ui::GetCurrentContext()->ActiveId;

            ui::PopStyleVar(2);

            return true;
        }
        return false;
    };

    Sort(mergedDirs.Begin(), mergedDirs.End());
    for (const auto& item: mergedDirs)
    {
        if (!renameWidget(item, ICON_FA_FOLDER))
        {
            auto isSelected = selected == item;

            if (flags & RBF_SCROLL_TO_CURRENT && isSelected)
                ui::SetScrollHere();

            switch (ui::DoubleClickSelectable((ICON_FA_FOLDER " " + item).CString(), isSelected))
            {
            case 1:
                selected = item;
                break;
            case 2:
                path += AddTrailingSlash(item);
                selected.Clear();
                break;
            default:
                break;
            }

            if (ui::IsItemActive())
            {
                if (ui::BeginDragDropSource())
                {
                    ui::SetDragDropVariant("path", path + item);

                    // TODO: show actual preview of a resource.
                    ui::Text("%s%s", path.CString(), item.CString());

                    ui::EndDragDropSource();
                }
            }

            moveFileDropTarget(path + item);
        }
    }

    auto renderAssetEntry = [&](const String& item) {
        auto icon = GetFileIcon(item);
        if (!renameWidget(item, icon))
        {
            if (flags & RBF_SCROLL_TO_CURRENT && selected == item)
                ui::SetScrollHere();
            auto title = icon + " " + GetFileNameAndExtension(item);
            switch (ui::DoubleClickSelectable(title.CString(), selected == item))
            {
            case 1:
                selected = item;
                result = RBR_ITEM_SELECTED;
                break;
            case 2:
                result = RBR_ITEM_OPEN;
                break;
            default:
                break;
            }

            if (ui::IsItemActive())
            {
                if (ui::BeginDragDropSource())
                {
                    ui::SetDragDropVariant("path", path + item);

                    // TODO: show actual preview of a resource.
                    ui::Text("%s%s", path.CString(), item.CString());

                    ui::EndDragDropSource();
                }
            }
        }
    };

    Sort(mergedFiles.Begin(), mergedFiles.End());
    for (const auto& item: mergedFiles)
    {
        if (fs->DirExists(cacheDir + path + item))
        {
            // File is converted asset.
            std::function<void(const String&)> renderCacheAssetTree = [&](const String& subPath)
            {
                String targetPath = cacheDir + path + subPath;

                if (fs->DirExists(targetPath))
                {
                    ui::TextUnformatted(ICON_FA_FOLDER_OPEN);
                    ui::SameLine();
                    if (ui::TreeNode(GetFileNameAndExtension(subPath).CString()))
                    {
                        Vector<String> files;
                        Vector<String> dirs;
                        fs->ScanDir(files, targetPath, "", SCAN_FILES, false);
                        fs->ScanDir(dirs, targetPath, "", SCAN_DIRS, false);
                        dirs.Remove(".");
                        dirs.Remove("..");
                        Sort(files.Begin(), files.End());
                        Sort(dirs.Begin(), dirs.End());

                        for (const auto& dir : dirs)
                            renderCacheAssetTree(subPath + "/" + dir);

                        for (const auto& file : files)
                            renderAssetEntry(subPath + "/" + file);

                        ui::TreePop();
                    }
                }
                else
                    renderAssetEntry(subPath);
            };
            renderCacheAssetTree(item);
        }
        else
        {
            // File exists only in data directories.
            renderAssetEntry(item);
        }
    }

    if (ui::IsWindowHovered())
    {
        if (ui::IsMouseClicked(MOUSEB_RIGHT))
            result = RBR_ITEM_CONTEXT_MENU;

        if ((ui::IsMouseClicked(MOUSEB_LEFT) || ui::IsMouseClicked(MOUSEB_RIGHT)) && !ui::IsAnyItemHovered())
            // Clicking empty area unselects item.
            selected.Clear();
    }

    state.wasEditing = state.isEditing;

    return result;
}

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

#include "ResourceBrowser.h"
#include "ImGuiDock.h"
#include <Urho3D/SystemUI/SystemUI.h>
#include <Urho3D/Core/Context.h>
#include <Urho3D/Resource/ResourceCache.h>
#include <Urho3D/Resource/ResourceEvents.h>
#include <Urho3D/IO/FileSystem.h>
#include <Urho3D/Input/Input.h>
#include <IconFontCppHeaders/IconsFontAwesome5.h>
#include <Urho3D/IO/Log.h>
#include "Widgets.h"
#include "IO/ContentUtilities.h"

namespace Urho3D
{

ResourceBrowserResult ResourceBrowserWidget(String& path, String& selected, ResourceBrowserFlags flags)
{
    struct State
    {
        bool isEditing = false;
        bool wasEditing = false;
        bool deletionPending = false;
        char editBuffer[250]{};
        String editStartItem;
    };

    auto result = RBR_NOOP;
    auto systemUI = (SystemUI*)ui::GetIO().UserData;
    auto fs = systemUI->GetFileSystem();
    auto& state = *ui::GetUIState<State>();

    if (!selected.Empty() && !ui::IsAnyItemActive() && ui::IsWindowFocused())
    {
        if (fs->GetInput()->GetKeyPress(KEY_F2) || flags & RBF_RENAME_CURRENT)
        {
            state.isEditing = true;
            state.deletionPending = false;
            state.editStartItem = selected;
            strcpy(state.editBuffer, selected.CString());
        }
        if (fs->GetInput()->GetKeyPress(KEY_DELETE) || flags & RBF_DELETE_CURRENT)
        {
            state.isEditing = false;
            state.deletionPending = true;
            state.editStartItem = selected;
        }
    }
    if (fs->GetInput()->GetKeyPress(KEY_ESCAPE) || state.editStartItem != selected)
    {
        state.isEditing = false;
        state.deletionPending = false;
    }

    if (state.deletionPending)
    {
        if (ui::Begin("Delete?", &state.deletionPending))
        {
            ui::Text("Would you like to delete '%s%s'?", path.CString(), selected.CString());
            ui::TextUnformatted(ICON_FA_EXCLAMATION_TRIANGLE " This action can not be undone!");
            ui::NewLine();

            if (ui::Button("Delete Permanently"))
            {
                using namespace ResourceBrowserDelete;
                fs->SendEvent(E_RESOURCEBROWSERDELETE, P_NAME, path + selected);
                state.deletionPending = false;
            }
        }
        ui::End();
    }

    Vector<String> mergedDirs;
    Vector<String> mergedFiles;

    String cacheDir;
    for (const auto& dir: systemUI->GetCache()->GetResourceDirs())
    {
        if (dir.EndsWith("/EditorData/"))
            continue;

        if (dir.EndsWith("/Cache/"))
        {
            cacheDir = dir;
            continue;
        }

        Vector<String> items;
        fs->ScanDir(items, dir + path, "", SCAN_FILES, false);
        for (const auto& item: items)
        {
            if (!mergedFiles.Contains(item))
                mergedFiles.Push(item);
        }

        items.Clear();
        fs->ScanDir(items, dir + path, "", SCAN_DIRS, false);
        items.Remove(".");
        items.Remove("..");
        for (const auto& item: items)
        {
            if (!mergedDirs.Contains(item))
                mergedDirs.Push(item);
        }
    }

    auto moveFileDropTarget = [&](const String& item) {
        if (ui::BeginDragDropTarget())
        {
            auto dropped = ui::AcceptDragDropVariant("path");
            if (dropped.GetType() == VAR_STRING)
            {
                using namespace ResourceBrowserRename;
                auto newName = AddTrailingSlash(item) + GetFileNameAndExtension(dropped.GetString());
                if (dropped != newName)
                    fs->SendEvent(E_RESOURCEBROWSERRENAME, P_FROM, dropped, P_TO, newName);
            }
            ui::EndDragDropTarget();
        }
    };

    if (!path.Empty())
    {
        switch (ui::DoubleClickSelectable("..", selected == ".."))
        {
        case 1:
            selected = "..";
            break;
        case 2:
            path = GetParentPath(path);
            break;
        default:
            break;
        }

        moveFileDropTarget(GetParentPath(path));
    }

    auto renameWidget = [&](const String& item, const String& icon) {
        if (selected == item && state.isEditing)
        {
            ui::TextUnformatted(icon.CString());
            ui::SameLine();

            ui::PushStyleVar(ImGuiStyleVar_FramePadding, {0, 0});
            ui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0);

            if (ui::InputText("", state.editBuffer, sizeof(state.editBuffer), ImGuiInputTextFlags_EnterReturnsTrue))
            {
                using namespace ResourceBrowserRename;
                auto oldName = path + selected;
                auto newName = path + state.editBuffer;
                if (oldName != newName)
                    fs->SendEvent(E_RESOURCEBROWSERRENAME, P_FROM, oldName, P_TO, newName);
                state.isEditing = false;
            }

            if (!state.wasEditing)
                ui::GetCurrentWindow()->FocusIdxTabRequestNext = ui::GetCurrentContext()->ActiveId;

            ui::PopStyleVar(2);

            return true;
        }
        return false;
    };

    Sort(mergedDirs.Begin(), mergedDirs.End());
    for (const auto& item: mergedDirs)
    {
        if (!renameWidget(item, ICON_FA_FOLDER))
        {
            auto isSelected = selected == item;

            if (flags & RBF_SCROLL_TO_CURRENT && isSelected)
                ui::SetScrollHere();

            switch (ui::DoubleClickSelectable((ICON_FA_FOLDER " " + item).CString(), isSelected))
            {
            case 1:
                selected = item;
                break;
            case 2:
                path += AddTrailingSlash(item);
                selected.Clear();
                break;
            default:
                break;
            }

            if (ui::IsItemActive())
            {
                if (ui::BeginDragDropSource())
                {
                    ui::SetDragDropVariant("path", path + item);

                    // TODO: show actual preview of a resource.
                    ui::Text("%s%s", path.CString(), item.CString());

                    ui::EndDragDropSource();
                }
            }

            moveFileDropTarget(path + item);
        }
    }

    auto renderAssetEntry = [&](const String& item) {
        auto icon = GetFileIcon(item);
        if (!renameWidget(item, icon))
        {
            if (flags & RBF_SCROLL_TO_CURRENT && selected == item)
                ui::SetScrollHere();
            auto title = icon + " " + GetFileNameAndExtension(item);
            switch (ui::DoubleClickSelectable(title.CString(), selected == item))
            {
            case 1:
                selected = item;
                result = RBR_ITEM_SELECTED;
                break;
            case 2:
                result = RBR_ITEM_OPEN;
                break;
            default:
                break;
            }

            if (ui::IsItemActive())
            {
                if (ui::BeginDragDropSource())
                {
                    ui::SetDragDropVariant("path", path + item);

                    // TODO: show actual preview of a resource.
                    ui::Text("%s%s", path.CString(), item.CString());

                    ui::EndDragDropSource();
                }
            }
        }
    };

    Sort(mergedFiles.Begin(), mergedFiles.End());
    for (const auto& item: mergedFiles)
    {
        if (fs->DirExists(cacheDir + path + item))
        {
            // File is converted asset.
            std::function<void(const String&)> renderCacheAssetTree = [&](const String& subPath)
            {
                String targetPath = cacheDir + path + subPath;

                if (fs->DirExists(targetPath))
                {
                    ui::TextUnformatted(ICON_FA_FOLDER_OPEN);
                    ui::SameLine();
                    if (ui::TreeNode(GetFileNameAndExtension(subPath).CString()))
                    {
                        Vector<String> files;
                        Vector<String> dirs;
                        fs->ScanDir(files, targetPath, "", SCAN_FILES, false);
                        fs->ScanDir(dirs, targetPath, "", SCAN_DIRS, false);
                        dirs.Remove(".");
                        dirs.Remove("..");
                        Sort(files.Begin(), files.End());
                        Sort(dirs.Begin(), dirs.End());

                        for (const auto& dir : dirs)
                            renderCacheAssetTree(subPath + "/" + dir);

                        for (const auto& file : files)
                            renderAssetEntry(subPath + "/" + file);

                        ui::TreePop();
                    }
                }
                else
                    renderAssetEntry(subPath);
            };
            renderCacheAssetTree(item);
        }
        else
        {
            // File exists only in data directories.
            renderAssetEntry(item);
        }
    }

    if (ui::IsWindowHovered())
    {
        if (ui::IsMouseClicked(MOUSEB_RIGHT))
            result = RBR_ITEM_CONTEXT_MENU;

        if ((ui::IsMouseClicked(MOUSEB_LEFT) || ui::IsMouseClicked(MOUSEB_RIGHT)) && !ui::IsAnyItemHovered())
            // Clicking empty area unselects item.
            selected.Clear();
    }

    state.wasEditing = state.isEditing;

    return result;
}

}
<|endoftext|>


<commit_before>// $Id: clearShiftProcessor.C,v 1.5 2000/09/20 13:51:17 amoll Exp $

#include<BALL/NMR/clearShiftProcessor.h>

namespace BALL
{

	ClearShiftProcessor::ClearShiftProcessor()
		throw()
	{
	}

	ClearShiftProcessor::~ClearShiftProcessor()
		throw()
	{
	}

	Processor::Result ClearShiftProcessor::operator () (Composite& composite)
		throw()
	{
		// clear the "chemical_shift" property of the atom
		// (if defined)
		Atom* atom = dynamic_cast<Atom*>(&composite);
		if (atom != 0)
		{
			if (atom->hasProperty("chemical_shift"))
			{
				atom->clearProperty("chemical_shift");
			}
		}

		return Processor::CONTINUE;
	}

} //namespace BALL
<commit_msg>added: copy ctor added: documentation concerning the named property<commit_after>// $Id: clearShiftProcessor.C,v 1.6 2000/09/22 11:56:49 oliver Exp $

#include<BALL/NMR/clearShiftProcessor.h>

namespace BALL
{

	ClearShiftProcessor::ClearShiftProcessor()
		throw()
		:	ShiftModule()
	{
		valid_ = true;
	}

	ClearShiftProcessor::~ClearShiftProcessor()
		throw()
	{
	}

	ClearShiftProcessor::ClearShiftProcessor(const ClearShiftProcessor& processor)
		throw()
		:	ShiftModule(processor)
	{
	}

	Processor::Result ClearShiftProcessor::operator () (Composite& composite)
		throw()
	{
		// clear the checmical shift property (ShiftModule::PROPERTY__SHIFT)
		// of the atom(if defined)
		Atom* atom = dynamic_cast<Atom*>(&composite);
		if (atom != 0)
		{
			if (atom->hasProperty(ShiftModule::PROPERTY__SHIFT))
			{
				atom->clearProperty(ShiftModule::PROPERTY__SHIFT);
			}
		}

		return Processor::CONTINUE;
	}

} //namespace BALL
<|endoftext|>


<commit_before>#include <memory>
#include <cassert>

#include "threaded-manager.hxx"
#include "threaded-worker.hxx"

#include "tile.hxx"

#include "utils.hxx"
#include "output.hxx"

namespace game_of_life
{

ThreadedManagerShared::ThreadedManagerShared(ThreadedManager& manager):
    workersWaiting(0),
    stop(0),
    manager(manager)
{}

bool ThreadedManagerShared::wakeWhenNextIterationNeeded(int have)
{
    if (stop != -1 && stop <= have)
    {
        MutexLocker locker(stopMutex);
        incWorkersWaiting();
        while (stop != -1 && stop <= have)
            stopCond.wait(stopMutex);
        decWorkersWaiting();
    }
    return stop != -1;
}

void ThreadedManagerShared::wakeWhenStopIs(int needed)
{
    MutexLocker locker(stopMutex);
    while (getStop() != needed)
        stopCond.wait(stopMutex);
}

int ThreadedManagerShared::getStop() const
{ return stop; }

int ThreadedManagerShared::getWorkersWaiting() const
{
    return workersWaiting;
}

void ThreadedManagerShared::incWorkersWaiting()
{
    MutexLocker locker(workersMutex);
    ++workersWaiting;
    debug() << workersWaiting << "/" << workersCount << " workers waiting";
    if (workersWaiting == workersCount)
        manager.updateState();
}

void ThreadedManagerShared::decWorkersWaiting()
{
    MutexLocker locker(workersMutex);
    --workersWaiting;
    debug() << workersWaiting << "/" << workersCount << " workers waiting";
}

void ThreadedManagerShared::setWorkersCount(int count)
{
    workersCount = count;
}

void ThreadedManagerShared::setStop(int newStop)
{
    MutexLocker locker(stopMutex);
    debug() << "setting iteration stopper to " << newStop;
    stop = newStop;
    stopCond.wakeAll();
}

ThreadedManager::ThreadedManager():
    myShared(*this),
    pauseFlag(false),
    runMore(0),
    shutdownFlag(false),
    updateFlag(false)
{}

ThreadedManager::~ThreadedManager()
{
    assert(getState() == NOT_STARTED || getState() == FINISHED);
}

void ThreadedManager::start(Matrix& t, int concurrency)
{
    this->matrix = &t;
    this->concurrency = concurrency;

    Manager::start();
    wakeWhenStateIsNot(NOT_STARTED);
}

ThreadedManagerShared& ThreadedManager::getShared()
{ return myShared; }

void ThreadedManager::pauseAll()
{
    assert(getState() != NOT_STARTED);
    {
        MutexLocker locker(mutex);
        pauseFlag = true;
        cond.wakeOne();
    }
    wakeWhenStateIs(STOPPED);
}

void ThreadedManager::runForMore(int iterations)
{
    int oldStop;
    {
        MutexLocker locker(mutex);
        oldStop = getShared().getStop();
        pauseFlag = false;
        runMore = iterations;
        cond.wakeOne();
    }
    getShared().wakeWhenStopIs(oldStop + iterations);
}

void ThreadedManager::shutdown()
{
    {
        MutexLocker locker(mutex);
        shutdownFlag = true;
        cond.wakeOne();
    }
    getShared().wakeWhenStopIs(-1);
}

void ThreadedManager::updateState()
{
    MutexLocker locker(mutex);
    updateFlag = true;
    cond.wakeOne();
}

void ThreadedManager::run()
{
    debug("initializing workers");
    getShared().setWorkersCount(concurrency);

    UniqueArray<ThreadedWorker> workers(concurrency);
    UniqueArray<TileView> domains(concurrency);

    TorusView torus(*matrix);

    std::vector<CoordRect> domainRects = chooseDomains(torus, concurrency);
    for (int i = 0; i < concurrency; ++i)
        domains[i] = TileView(torus, domainRects[i]);

    std::vector<std::vector<int> > neigs = makeNeighbors(torus, domainRects);

    for (int i = 0; i < concurrency; ++i)
    {
        std::vector<ThreadedWorkerShared*> shareds(neigs[i].size());
        for (size_t j = 0; j < neigs[i].size(); ++j)
            shareds[j] = &workers[neigs[i][j]].getShared();
        workers[i].start(myShared, domains[i], shareds);
    }

    debug("waiting for workers to initialize themselves");
    {
        MutexLocker locker(mutex);
        while (!this->updateFlag)
            cond.wait(mutex);
        this->updateFlag = false;
    }

    debug("all workers are initialized");
    int stop = 0;
    setState(STOPPED);
    while (true)
    {
        int runMore = 0;
        bool pauseFlag = false;
        bool updateFlag = false;
        {
            MutexLocker locker(mutex);
            while (!this->pauseFlag
                    && !this->runMore
                    && !this->shutdownFlag
                    && !this->updateFlag)
                cond.wait(mutex);

            if (this->shutdownFlag)
            {
                debug("manager received shutdownFlag");
                break;
            }
            else if (this->pauseFlag)
            {
                debug("manager received pauseFlag");
                pauseFlag = true;
            }
            else
            {
                runMore = this->runMore;
                updateFlag = this->updateFlag;
                if (runMore)
                    debug() << "manager received runMore = " << runMore;
                if (updateFlag)
                    debug("manager received updateFlag");
            }
            this->pauseFlag = false;
            this->runMore = 0;
            this->updateFlag = false;
        }

        if (pauseFlag)
        {
            myShared.setStop(0);
            for (int i = 0; i < concurrency; ++i)
            {
                int newStop = std::min(stop,
                        workers[i].getShared().getIterationPublished() + 1);
                if (newStop > myShared.stop)
                    myShared.setStop(newStop);
            }
            for (int i = 0; i < concurrency; ++i)
                workers[i].getShared().wakeWhenPublishes(myShared.stop);
            stop = myShared.stop;
            setState(STOPPED);
        }

        if (runMore)
        {
            stop += runMore;
            myShared.setStop(stop);
            setState(RUNNING);
        }

        if (updateFlag)
        {
            if (getState() == RUNNING
                    && getShared().getWorkersWaiting() == concurrency)
            {
                bool over = true;
                for (int i = 0; i < concurrency; ++i)
                    over &= workers[i].getShared().getIterationPublished() == stop;
                if (over)
                    setState(STOPPED);
            }
        }
    }
    myShared.setStop(-1);

    for (int i = 0; i < concurrency; ++i)
    {
        int id = workers[i].getId();
        workers[i].join();
        debug() << "manager joined worker thread " << id;
    }

    setState(FINISHED);
}

} // namespace game_of_life

<commit_msg>Add try-catch for workers initialization<commit_after>#include <memory>
#include <cassert>
#include <cstdlib>

#include "threaded-manager.hxx"
#include "threaded-worker.hxx"

#include "tile.hxx"

#include "utils.hxx"
#include "output.hxx"
#include "exceptions.hxx"

using namespace std;

namespace game_of_life
{

ThreadedManagerShared::ThreadedManagerShared(ThreadedManager& manager):
    workersWaiting(0),
    stop(0),
    manager(manager)
{}

bool ThreadedManagerShared::wakeWhenNextIterationNeeded(int have)
{
    if (stop != -1 && stop <= have)
    {
        MutexLocker locker(stopMutex);
        incWorkersWaiting();
        while (stop != -1 && stop <= have)
            stopCond.wait(stopMutex);
        decWorkersWaiting();
    }
    return stop != -1;
}

void ThreadedManagerShared::wakeWhenStopIs(int needed)
{
    MutexLocker locker(stopMutex);
    while (getStop() != needed)
        stopCond.wait(stopMutex);
}

int ThreadedManagerShared::getStop() const
{ return stop; }

int ThreadedManagerShared::getWorkersWaiting() const
{
    return workersWaiting;
}

void ThreadedManagerShared::incWorkersWaiting()
{
    MutexLocker locker(workersMutex);
    ++workersWaiting;
    debug() << workersWaiting << "/" << workersCount << " workers waiting";
    if (workersWaiting == workersCount)
        manager.updateState();
}

void ThreadedManagerShared::decWorkersWaiting()
{
    MutexLocker locker(workersMutex);
    --workersWaiting;
    debug() << workersWaiting << "/" << workersCount << " workers waiting";
}

void ThreadedManagerShared::setWorkersCount(int count)
{
    workersCount = count;
}

void ThreadedManagerShared::setStop(int newStop)
{
    MutexLocker locker(stopMutex);
    debug() << "setting iteration stopper to " << newStop;
    stop = newStop;
    stopCond.wakeAll();
}

ThreadedManager::ThreadedManager():
    myShared(*this),
    pauseFlag(false),
    runMore(0),
    shutdownFlag(false),
    updateFlag(false)
{}

ThreadedManager::~ThreadedManager()
{
    assert(getState() == NOT_STARTED || getState() == FINISHED);
}

void ThreadedManager::start(Matrix& t, int concurrency)
{
    this->matrix = &t;
    this->concurrency = concurrency;

    Manager::start();
    wakeWhenStateIsNot(NOT_STARTED);
}

ThreadedManagerShared& ThreadedManager::getShared()
{ return myShared; }

void ThreadedManager::pauseAll()
{
    assert(getState() != NOT_STARTED);
    {
        MutexLocker locker(mutex);
        pauseFlag = true;
        cond.wakeOne();
    }
    wakeWhenStateIs(STOPPED);
}

void ThreadedManager::runForMore(int iterations)
{
    int oldStop;
    {
        MutexLocker locker(mutex);
        oldStop = getShared().getStop();
        pauseFlag = false;
        runMore = iterations;
        cond.wakeOne();
    }
    getShared().wakeWhenStopIs(oldStop + iterations);
}

void ThreadedManager::shutdown()
{
    {
        MutexLocker locker(mutex);
        shutdownFlag = true;
        cond.wakeOne();
    }
    getShared().wakeWhenStopIs(-1);
}

void ThreadedManager::updateState()
{
    MutexLocker locker(mutex);
    updateFlag = true;
    cond.wakeOne();
}

void ThreadedManager::run()
{
    debug("initializing workers");
    getShared().setWorkersCount(concurrency);

    UniqueArray<ThreadedWorker> workers(concurrency);
    TorusView torus(*matrix);
    vector<TileView> domains;
    vector<std::vector<int> > neigs;
    try
    {

        std::vector<CoordRect> domainRects = chooseDomains(torus, concurrency);
        for (int i = 0; i < concurrency; ++i)
            domains.push_back(TileView(torus, domainRects[i]));

        neigs = makeNeighbors(torus, domainRects);

        for (int i = 0; i < concurrency; ++i)
        {
            std::vector<ThreadedWorkerShared*> shareds(neigs[i].size());
            for (size_t j = 0; j < neigs[i].size(); ++j)
                shareds[j] = &workers[neigs[i][j]].getShared();
            workers[i].start(myShared, domains[i], shareds);
        }
    }
    catch (exception& e)
    {
        err() << "Failed to initialize workers: " << e.what() << "\n";
        ::exit(1);
    }

    debug("waiting for workers to initialize themselves");
    {
        MutexLocker locker(mutex);
        while (!this->updateFlag)
            cond.wait(mutex);
        this->updateFlag = false;
    }

    debug("all workers are initialized");
    int stop = 0;
    setState(STOPPED);
    while (true)
    {
        int runMore = 0;
        bool pauseFlag = false;
        bool updateFlag = false;
        {
            MutexLocker locker(mutex);
            while (!this->pauseFlag
                    && !this->runMore
                    && !this->shutdownFlag
                    && !this->updateFlag)
                cond.wait(mutex);

            if (this->shutdownFlag)
            {
                debug("manager received shutdownFlag");
                break;
            }
            else if (this->pauseFlag)
            {
                debug("manager received pauseFlag");
                pauseFlag = true;
            }
            else
            {
                runMore = this->runMore;
                updateFlag = this->updateFlag;
                if (runMore)
                    debug() << "manager received runMore = " << runMore;
                if (updateFlag)
                    debug("manager received updateFlag");
            }
            this->pauseFlag = false;
            this->runMore = 0;
            this->updateFlag = false;
        }

        if (pauseFlag)
        {
            myShared.setStop(0);
            for (int i = 0; i < concurrency; ++i)
            {
                int newStop = std::min(stop,
                        workers[i].getShared().getIterationPublished() + 1);
                if (newStop > myShared.stop)
                    myShared.setStop(newStop);
            }
            for (int i = 0; i < concurrency; ++i)
                workers[i].getShared().wakeWhenPublishes(myShared.stop);
            stop = myShared.stop;
            setState(STOPPED);
        }

        if (runMore)
        {
            stop += runMore;
            myShared.setStop(stop);
            setState(RUNNING);
        }

        if (updateFlag)
        {
            if (getState() == RUNNING
                    && getShared().getWorkersWaiting() == concurrency)
            {
                bool over = true;
                for (int i = 0; i < concurrency; ++i)
                    over &= workers[i].getShared().getIterationPublished() == stop;
                if (over)
                    setState(STOPPED);
            }
        }
    }
    myShared.setStop(-1);

    for (int i = 0; i < concurrency; ++i)
    {
        int id = workers[i].getId();
        workers[i].join();
        debug() << "manager joined worker thread " << id;
    }

    setState(FINISHED);
}

} // namespace game_of_life

<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                         Peloton
//
// index_scan_executor.cpp
//
// Identification: src/backend/executor/index_scan_executor.cpp
//
// Copyright (c) 2015-16, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "backend/executor/index_scan_executor.h"

#include <memory>
#include <utility>
#include <vector>

#include "backend/common/types.h"
#include "backend/executor/logical_tile.h"
#include "backend/executor/logical_tile_factory.h"
#include "backend/executor/executor_context.h"
#include "backend/expression/abstract_expression.h"
#include "backend/expression/container_tuple.h"
#include "backend/index/index.h"
#include "backend/storage/data_table.h"
#include "backend/storage/tile_group.h"
#include "backend/storage/tile_group_header.h"
#include "backend/concurrency/transaction_manager_factory.h"
#include "backend/common/logger.h"
#include "backend/catalog/manager.h"
#include "backend/gc/gc_manager_factory.h"

namespace peloton {
namespace executor {

/**
 * @brief Constructor for indexscan executor.
 * @param node Indexscan node corresponding to this executor.
 */
IndexScanExecutor::IndexScanExecutor(const planner::AbstractPlan *node,
                                     ExecutorContext *executor_context)
    : AbstractScanExecutor(node, executor_context) {}

IndexScanExecutor::~IndexScanExecutor() {
  // Nothing to do here
}

/**
 * @brief Let base class Dinit() first, then do my job.
 * @return true on success, false otherwise.
 */
bool IndexScanExecutor::DInit() {
  auto status = AbstractScanExecutor::DInit();

  if (!status) return false;

  assert(children_.size() == 0);

  // Grab info from plan node and check it
  const planner::IndexScanPlan &node = GetPlanNode<planner::IndexScanPlan>();

  index_ = node.GetIndex();
  assert(index_ != nullptr);

  result_itr_ = START_OID;
  done_ = false;

  column_ids_ = node.GetColumnIds();
  key_column_ids_ = node.GetKeyColumnIds();
  expr_types_ = node.GetExprTypes();
  values_ = node.GetValues();
  runtime_keys_ = node.GetRunTimeKeys();
  predicate_ = node.GetPredicate();

  if (runtime_keys_.size() != 0) {
    assert(runtime_keys_.size() == values_.size());

    if (!key_ready_) {
      values_.clear();

      for (auto expr : runtime_keys_) {
        auto value = expr->Evaluate(nullptr, nullptr, executor_context_);
        LOG_INFO("Evaluated runtime scan key: %s", value.GetInfo().c_str());
        values_.push_back(value);
      }

      key_ready_ = true;
    }
  }

  table_ = node.GetTable();

  if (table_ != nullptr) {
    full_column_ids_.resize(table_->GetSchema()->GetColumnCount());
    std::iota(full_column_ids_.begin(), full_column_ids_.end(), 0);
  }

  return true;
}

/**
 * @brief Creates logical tile(s) after scanning index.
 * @return true on success, false otherwise.
 */
bool IndexScanExecutor::DExecute() {
  LOG_INFO("Index Scan executor :: 0 child");

  if (!done_) {
    if (index_->GetIndexType() == INDEX_CONSTRAINT_TYPE_PRIMARY_KEY) {
      auto status = ExecPrimaryIndexLookup();
      if (status == false) return false;
    } else {
      auto status = ExecSecondaryIndexLookup();
      if (status == false) return false;
    }
  }
  // Already performed the index lookup
  assert(done_);

  while (result_itr_ < result_.size()) {  // Avoid returning empty tiles
    if (result_[result_itr_]->GetTupleCount() == 0) {
      result_itr_++;
      continue;
    } else {
      SetOutput(result_[result_itr_]);
      result_itr_++;
      return true;
    }

  }  // end while

  return false;
}

bool IndexScanExecutor::ExecPrimaryIndexLookup() {
  assert(!done_);

  std::vector<ItemPointer *> tuple_location_ptrs;

  assert(index_->GetIndexType() == INDEX_CONSTRAINT_TYPE_PRIMARY_KEY);

  if (0 == key_column_ids_.size()) {
    index_->ScanAllKeys(tuple_location_ptrs);
  } else {
    index_->Scan(values_, key_column_ids_, expr_types_,
                 SCAN_DIRECTION_TYPE_FORWARD, tuple_location_ptrs);
  }

  LOG_INFO("Tuple_locations.size(): %lu", tuple_location_ptrs.size());

  if (tuple_location_ptrs.size() == 0) return false;

  auto &transaction_manager =
      concurrency::TransactionManagerFactory::GetInstance();

  std::map<oid_t, std::vector<oid_t>> visible_tuples;
  // for every tuple that is found in the index.
  for (auto tuple_location_ptr : tuple_location_ptrs) {

    ItemPointer tuple_location = *tuple_location_ptr;

    auto &manager = catalog::Manager::GetInstance();
    auto tile_group = manager.GetTileGroup(tuple_location.block);
    auto tile_group_header = tile_group.get()->GetHeader();

    size_t chain_length = 0;
    while (true) {

      ++chain_length;

      // if the tuple is visible.
      if (transaction_manager.IsVisible(tile_group_header,
                                        tuple_location.offset)) {

        LOG_INFO("traverse chain length : %lu", chain_length);
        LOG_INFO("perform read: %u, %u", tuple_location.block,
                 tuple_location.offset);

        // perform predicate evaluation.
        if (predicate_ == nullptr) {
          visible_tuples[tuple_location.block].push_back(tuple_location.offset);

          auto res = transaction_manager.PerformRead(tuple_location);
          if (!res) {
            transaction_manager.SetTransactionResult(RESULT_FAILURE);
            return res;
          }
        } else {
          expression::ContainerTuple<storage::TileGroup> tuple(
              tile_group.get(), tuple_location.offset);
          auto eval =
              predicate_->Evaluate(&tuple, nullptr, executor_context_).IsTrue();
          if (eval == true) {
            visible_tuples[tuple_location.block]
                .push_back(tuple_location.offset);

            auto res = transaction_manager.PerformRead(tuple_location);
            if (!res) {
              transaction_manager.SetTransactionResult(RESULT_FAILURE);
              return res;
            }
          }
        }
        break;
      }
      // if the tuple is not visible.
      else {
        ItemPointer old_item = tuple_location;
        cid_t old_end_cid = tile_group_header->GetEndCommitId(old_item.offset);

        tuple_location = tile_group_header->GetNextItemPointer(old_item.offset);
        // there must exist a visible version.
        assert(tuple_location.IsNull() == false);

        cid_t max_committed_cid = transaction_manager.GetMaxCommittedCid();

        // check whether older version is garbage.
        if (old_end_cid < max_committed_cid) {
          assert(tile_group_header->GetTransactionId(old_item.offset) == INITIAL_TXN_ID || tile_group_header->GetTransactionId(old_item.offset) == INVALID_TXN_ID);

          if (tile_group_header->SetAtomicTransactionId(old_item.offset, INVALID_TXN_ID) == true) {

            // atomically swap item pointer held in the index bucket.
            AtomicUpdateItemPointer(tuple_location_ptr, tuple_location);

            // currently, let's assume only primary index exists.
            gc::GCManagerFactory::GetInstance().RecycleTupleSlot(
                table_->GetOid(), old_item.block, old_item.offset,
                transaction_manager.GetNextCommitId());

            tile_group = manager.GetTileGroup(tuple_location.block);
            tile_group_header = tile_group.get()->GetHeader();
            tile_group_header->SetPrevItemPointer(tuple_location.offset, INVALID_ITEMPOINTER);

          } else {

            tile_group = manager.GetTileGroup(tuple_location.block);
            tile_group_header = tile_group.get()->GetHeader();
          }

        } else {
        tile_group = manager.GetTileGroup(tuple_location.block);
        tile_group_header = tile_group.get()->GetHeader();

        }

      }
    }
  }

  // Construct a logical tile for each block
  for (auto tuples : visible_tuples) {
    auto &manager = catalog::Manager::GetInstance();
    auto tile_group = manager.GetTileGroup(tuples.first);

    std::unique_ptr<LogicalTile> logical_tile(LogicalTileFactory::GetTile());
    // Add relevant columns to logical tile
    logical_tile->AddColumns(tile_group, full_column_ids_);
    logical_tile->AddPositionList(std::move(tuples.second));
    if (column_ids_.size() != 0) {
      logical_tile->ProjectColumns(full_column_ids_, column_ids_);
    }

    result_.push_back(logical_tile.release());
  }

  done_ = true;

  LOG_TRACE("Result tiles : %lu", result_.size());

  return true;
}

bool IndexScanExecutor::ExecSecondaryIndexLookup() {
  assert(!done_);

  std::vector<ItemPointer> tuple_locations;

  assert(index_->GetIndexType() != INDEX_CONSTRAINT_TYPE_PRIMARY_KEY);

  if (0 == key_column_ids_.size()) {
    index_->ScanAllKeys(tuple_locations);
  } else {
    index_->Scan(values_, key_column_ids_, expr_types_,
                 SCAN_DIRECTION_TYPE_FORWARD, tuple_locations);
  }

  LOG_INFO("Tuple_locations.size(): %lu", tuple_locations.size());

  if (tuple_locations.size() == 0) return false;

  auto &transaction_manager =
      concurrency::TransactionManagerFactory::GetInstance();

  std::map<oid_t, std::vector<oid_t>> visible_tuples;
  // for every tuple that is found in the index.
  for (auto tuple_location : tuple_locations) {
    auto &manager = catalog::Manager::GetInstance();
    auto tile_group = manager.GetTileGroup(tuple_location.block);
    auto tile_group_header = tile_group.get()->GetHeader();
    auto tile_group_id = tuple_location.block;
    auto tuple_id = tuple_location.offset;

    // if the tuple is visible.
    if (transaction_manager.IsVisible(tile_group_header, tuple_id)) {
      // perform predicate evaluation.
      if (predicate_ == nullptr) {
        visible_tuples[tile_group_id].push_back(tuple_id);
        auto res = transaction_manager.PerformRead(tuple_location);
        if (!res) {
          transaction_manager.SetTransactionResult(RESULT_FAILURE);
          return res;
        }
      } else {
        expression::ContainerTuple<storage::TileGroup> tuple(tile_group.get(),
                                                             tuple_id);
        auto eval =
            predicate_->Evaluate(&tuple, nullptr, executor_context_).IsTrue();
        if (eval == true) {
          visible_tuples[tile_group_id].push_back(tuple_id);
          auto res = transaction_manager.PerformRead(tuple_location);
          if (!res) {
            transaction_manager.SetTransactionResult(RESULT_FAILURE);
            return res;
          }
        }
      }
    }
  }
  // Construct a logical tile for each block
  for (auto tuples : visible_tuples) {
    auto &manager = catalog::Manager::GetInstance();
    auto tile_group = manager.GetTileGroup(tuples.first);

    std::unique_ptr<LogicalTile> logical_tile(LogicalTileFactory::GetTile());
    // Add relevant columns to logical tile
    logical_tile->AddColumns(tile_group, full_column_ids_);
    logical_tile->AddPositionList(std::move(tuples.second));
    if (column_ids_.size() != 0) {
      logical_tile->ProjectColumns(full_column_ids_, column_ids_);
    }

    result_.push_back(logical_tile.release());
  }

  done_ = true;

  LOG_TRACE("Result tiles : %lu", result_.size());

  return true;
}

}  // namespace executor
}  // namespace peloton
<commit_msg>more gc<commit_after>//===----------------------------------------------------------------------===//
//
//                         Peloton
//
// index_scan_executor.cpp
//
// Identification: src/backend/executor/index_scan_executor.cpp
//
// Copyright (c) 2015-16, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "backend/executor/index_scan_executor.h"

#include <memory>
#include <utility>
#include <vector>

#include "backend/common/types.h"
#include "backend/executor/logical_tile.h"
#include "backend/executor/logical_tile_factory.h"
#include "backend/executor/executor_context.h"
#include "backend/expression/abstract_expression.h"
#include "backend/expression/container_tuple.h"
#include "backend/index/index.h"
#include "backend/storage/data_table.h"
#include "backend/storage/tile_group.h"
#include "backend/storage/tile_group_header.h"
#include "backend/concurrency/transaction_manager_factory.h"
#include "backend/common/logger.h"
#include "backend/catalog/manager.h"
#include "backend/gc/gc_manager_factory.h"

namespace peloton {
namespace executor {

/**
 * @brief Constructor for indexscan executor.
 * @param node Indexscan node corresponding to this executor.
 */
IndexScanExecutor::IndexScanExecutor(const planner::AbstractPlan *node,
                                     ExecutorContext *executor_context)
    : AbstractScanExecutor(node, executor_context) {}

IndexScanExecutor::~IndexScanExecutor() {
  // Nothing to do here
}

/**
 * @brief Let base class Dinit() first, then do my job.
 * @return true on success, false otherwise.
 */
bool IndexScanExecutor::DInit() {
  auto status = AbstractScanExecutor::DInit();

  if (!status) return false;

  assert(children_.size() == 0);

  // Grab info from plan node and check it
  const planner::IndexScanPlan &node = GetPlanNode<planner::IndexScanPlan>();

  index_ = node.GetIndex();
  assert(index_ != nullptr);

  result_itr_ = START_OID;
  done_ = false;

  column_ids_ = node.GetColumnIds();
  key_column_ids_ = node.GetKeyColumnIds();
  expr_types_ = node.GetExprTypes();
  values_ = node.GetValues();
  runtime_keys_ = node.GetRunTimeKeys();
  predicate_ = node.GetPredicate();

  if (runtime_keys_.size() != 0) {
    assert(runtime_keys_.size() == values_.size());

    if (!key_ready_) {
      values_.clear();

      for (auto expr : runtime_keys_) {
        auto value = expr->Evaluate(nullptr, nullptr, executor_context_);
        LOG_INFO("Evaluated runtime scan key: %s", value.GetInfo().c_str());
        values_.push_back(value);
      }

      key_ready_ = true;
    }
  }

  table_ = node.GetTable();

  if (table_ != nullptr) {
    full_column_ids_.resize(table_->GetSchema()->GetColumnCount());
    std::iota(full_column_ids_.begin(), full_column_ids_.end(), 0);
  }

  return true;
}

/**
 * @brief Creates logical tile(s) after scanning index.
 * @return true on success, false otherwise.
 */
bool IndexScanExecutor::DExecute() {
  LOG_INFO("Index Scan executor :: 0 child");

  if (!done_) {
    if (index_->GetIndexType() == INDEX_CONSTRAINT_TYPE_PRIMARY_KEY) {
      auto status = ExecPrimaryIndexLookup();
      if (status == false) return false;
    } else {
      auto status = ExecSecondaryIndexLookup();
      if (status == false) return false;
    }
  }
  // Already performed the index lookup
  assert(done_);

  while (result_itr_ < result_.size()) {  // Avoid returning empty tiles
    if (result_[result_itr_]->GetTupleCount() == 0) {
      result_itr_++;
      continue;
    } else {
      SetOutput(result_[result_itr_]);
      result_itr_++;
      return true;
    }

  }  // end while

  return false;
}

bool IndexScanExecutor::ExecPrimaryIndexLookup() {
  assert(!done_);

  std::vector<ItemPointer *> tuple_location_ptrs;

  assert(index_->GetIndexType() == INDEX_CONSTRAINT_TYPE_PRIMARY_KEY);

  if (0 == key_column_ids_.size()) {
    index_->ScanAllKeys(tuple_location_ptrs);
  } else {
    index_->Scan(values_, key_column_ids_, expr_types_,
                 SCAN_DIRECTION_TYPE_FORWARD, tuple_location_ptrs);
  }

  LOG_INFO("Tuple_locations.size(): %lu", tuple_location_ptrs.size());

  if (tuple_location_ptrs.size() == 0) return false;

  auto &transaction_manager =
      concurrency::TransactionManagerFactory::GetInstance();

  std::map<oid_t, std::vector<oid_t>> visible_tuples;
  // for every tuple that is found in the index.
  for (auto tuple_location_ptr : tuple_location_ptrs) {

    ItemPointer tuple_location = *tuple_location_ptr;

    auto &manager = catalog::Manager::GetInstance();
    auto tile_group = manager.GetTileGroup(tuple_location.block);
    auto tile_group_header = tile_group.get()->GetHeader();

    size_t chain_length = 0;
    while (true) {

      ++chain_length;

      // if the tuple is visible.
      if (transaction_manager.IsVisible(tile_group_header,
                                        tuple_location.offset)) {

        LOG_INFO("traverse chain length : %lu", chain_length);
        LOG_INFO("perform read: %u, %u", tuple_location.block,
                 tuple_location.offset);

        // perform predicate evaluation.
        if (predicate_ == nullptr) {
          visible_tuples[tuple_location.block].push_back(tuple_location.offset);

          auto res = transaction_manager.PerformRead(tuple_location);
          if (!res) {
            transaction_manager.SetTransactionResult(RESULT_FAILURE);
            return res;
          }
        } else {
          expression::ContainerTuple<storage::TileGroup> tuple(
              tile_group.get(), tuple_location.offset);
          auto eval =
              predicate_->Evaluate(&tuple, nullptr, executor_context_).IsTrue();
          if (eval == true) {
            visible_tuples[tuple_location.block]
                .push_back(tuple_location.offset);

            auto res = transaction_manager.PerformRead(tuple_location);
            if (!res) {
              transaction_manager.SetTransactionResult(RESULT_FAILURE);
              return res;
            }
          }
        }
        break;
      }
      // if the tuple is not visible.
      else {
        ItemPointer old_item = tuple_location;
        cid_t old_end_cid = tile_group_header->GetEndCommitId(old_item.offset);

        tuple_location = tile_group_header->GetNextItemPointer(old_item.offset);
        // there must exist a visible version.
        assert(tuple_location.IsNull() == false);

        cid_t max_committed_cid = transaction_manager.GetMaxCommittedCid();

        // check whether older version is garbage.
        if (old_end_cid <= max_committed_cid) {
          assert(tile_group_header->GetTransactionId(old_item.offset) == INITIAL_TXN_ID || tile_group_header->GetTransactionId(old_item.offset) == INVALID_TXN_ID);

          if (tile_group_header->SetAtomicTransactionId(old_item.offset, INVALID_TXN_ID) == true) {

            // atomically swap item pointer held in the index bucket.
            AtomicUpdateItemPointer(tuple_location_ptr, tuple_location);

            // currently, let's assume only primary index exists.
            gc::GCManagerFactory::GetInstance().RecycleTupleSlot(
                table_->GetOid(), old_item.block, old_item.offset,
                transaction_manager.GetNextCommitId());

            tile_group = manager.GetTileGroup(tuple_location.block);
            tile_group_header = tile_group.get()->GetHeader();
            tile_group_header->SetPrevItemPointer(tuple_location.offset, INVALID_ITEMPOINTER);

          } else {

            tile_group = manager.GetTileGroup(tuple_location.block);
            tile_group_header = tile_group.get()->GetHeader();
          }

        } else {
        tile_group = manager.GetTileGroup(tuple_location.block);
        tile_group_header = tile_group.get()->GetHeader();

        }

      }
    }
  }

  // Construct a logical tile for each block
  for (auto tuples : visible_tuples) {
    auto &manager = catalog::Manager::GetInstance();
    auto tile_group = manager.GetTileGroup(tuples.first);

    std::unique_ptr<LogicalTile> logical_tile(LogicalTileFactory::GetTile());
    // Add relevant columns to logical tile
    logical_tile->AddColumns(tile_group, full_column_ids_);
    logical_tile->AddPositionList(std::move(tuples.second));
    if (column_ids_.size() != 0) {
      logical_tile->ProjectColumns(full_column_ids_, column_ids_);
    }

    result_.push_back(logical_tile.release());
  }

  done_ = true;

  LOG_TRACE("Result tiles : %lu", result_.size());

  return true;
}

bool IndexScanExecutor::ExecSecondaryIndexLookup() {
  assert(!done_);

  std::vector<ItemPointer> tuple_locations;

  assert(index_->GetIndexType() != INDEX_CONSTRAINT_TYPE_PRIMARY_KEY);

  if (0 == key_column_ids_.size()) {
    index_->ScanAllKeys(tuple_locations);
  } else {
    index_->Scan(values_, key_column_ids_, expr_types_,
                 SCAN_DIRECTION_TYPE_FORWARD, tuple_locations);
  }

  LOG_INFO("Tuple_locations.size(): %lu", tuple_locations.size());

  if (tuple_locations.size() == 0) return false;

  auto &transaction_manager =
      concurrency::TransactionManagerFactory::GetInstance();

  std::map<oid_t, std::vector<oid_t>> visible_tuples;
  // for every tuple that is found in the index.
  for (auto tuple_location : tuple_locations) {
    auto &manager = catalog::Manager::GetInstance();
    auto tile_group = manager.GetTileGroup(tuple_location.block);
    auto tile_group_header = tile_group.get()->GetHeader();
    auto tile_group_id = tuple_location.block;
    auto tuple_id = tuple_location.offset;

    // if the tuple is visible.
    if (transaction_manager.IsVisible(tile_group_header, tuple_id)) {
      // perform predicate evaluation.
      if (predicate_ == nullptr) {
        visible_tuples[tile_group_id].push_back(tuple_id);
        auto res = transaction_manager.PerformRead(tuple_location);
        if (!res) {
          transaction_manager.SetTransactionResult(RESULT_FAILURE);
          return res;
        }
      } else {
        expression::ContainerTuple<storage::TileGroup> tuple(tile_group.get(),
                                                             tuple_id);
        auto eval =
            predicate_->Evaluate(&tuple, nullptr, executor_context_).IsTrue();
        if (eval == true) {
          visible_tuples[tile_group_id].push_back(tuple_id);
          auto res = transaction_manager.PerformRead(tuple_location);
          if (!res) {
            transaction_manager.SetTransactionResult(RESULT_FAILURE);
            return res;
          }
        }
      }
    }
  }
  // Construct a logical tile for each block
  for (auto tuples : visible_tuples) {
    auto &manager = catalog::Manager::GetInstance();
    auto tile_group = manager.GetTileGroup(tuples.first);

    std::unique_ptr<LogicalTile> logical_tile(LogicalTileFactory::GetTile());
    // Add relevant columns to logical tile
    logical_tile->AddColumns(tile_group, full_column_ids_);
    logical_tile->AddPositionList(std::move(tuples.second));
    if (column_ids_.size() != 0) {
      logical_tile->ProjectColumns(full_column_ids_, column_ids_);
    }

    result_.push_back(logical_tile.release());
  }

  done_ = true;

  LOG_TRACE("Result tiles : %lu", result_.size());

  return true;
}

}  // namespace executor
}  // namespace peloton
<|endoftext|>


<commit_before>// Copyright (c) 2015 Baidu, Inc.
//
// 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.

// Authors: Ge,Jun (gejun@baidu.com)

#include <google/protobuf/descriptor.h>         // MethodDescriptor
#include <google/protobuf/message.h>            // Message
#include <gflags/gflags.h>
#include "butil/logging.h"                       // LOG()
#include "butil/time.h"
#include "butil/iobuf.h"                         // butil::IOBuf
#include "butil/sys_byteorder.h"
#include "brpc/controller.h"               // Controller
#include "brpc/details/controller_private_accessor.h"
#include "brpc/socket.h"                   // Socket
#include "brpc/server.h"                   // Server
#include "brpc/details/server_private_accessor.h"
#include "brpc/span.h"
#include "brpc/compress.h"                 // ParseFromCompressedData
#include "brpc/policy/memcache_binary_protocol.h"
#include "brpc/policy/memcache_binary_header.h"
#include "brpc/memcache.h"
#include "brpc/policy/most_common_message.h"
#include "butil/containers/flat_map.h"

namespace brpc {

DECLARE_bool(enable_rpcz);

namespace policy {

BAIDU_CASSERT(sizeof(MemcacheRequestHeader) == 24, must_match);
BAIDU_CASSERT(sizeof(MemcacheResponseHeader) == 24, must_match);

static uint64_t supported_cmd_map[8];
static pthread_once_t supported_cmd_map_once = PTHREAD_ONCE_INIT;

static void InitSupportedCommandMap() {
    butil::bit_array_clear(supported_cmd_map, 256);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_GET);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_SET);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_ADD);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_REPLACE);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_DELETE);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_INCREMENT);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_DECREMENT);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_FLUSH);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_VERSION);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_NOOP);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_APPEND);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_PREPEND);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_STAT);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_TOUCH);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_SASL_AUTH);
}

inline bool IsSupportedCommand(uint8_t command) {
    pthread_once(&supported_cmd_map_once, InitSupportedCommandMap);
    return butil::bit_array_get(supported_cmd_map, command);
}

ParseResult ParseMemcacheMessage(butil::IOBuf* source,
                                 Socket* socket, bool /*read_eof*/, const void */*arg*/) {
    while (1) {
        const uint8_t* p_mcmagic = (const uint8_t*)source->fetch1();
        if (NULL == p_mcmagic) {
            return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA);
        }
        if (*p_mcmagic != (uint8_t)MC_MAGIC_RESPONSE) {
            return MakeParseError(PARSE_ERROR_TRY_OTHERS);
        }
        char buf[24];
        const uint8_t* p = (const uint8_t*)source->fetch(buf, sizeof(buf));
        if (NULL == p) {
            return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA);
        }
        const MemcacheResponseHeader* header = (const MemcacheResponseHeader*)p;
        uint32_t total_body_length = butil::NetToHost32(header->total_body_length);
        if (source->size() < sizeof(*header) + total_body_length) {
            return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA);
        }

        if (!IsSupportedCommand(header->command)) {
            LOG(WARNING) << "Not support command=" << header->command;
            source->pop_front(sizeof(*header) + total_body_length);
            return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA);
        }

        PipelinedInfo pi;
        if (!socket->PopPipelinedInfo(&pi)) {
            LOG(WARNING) << "No corresponding PipelinedInfo in socket, drop";
            source->pop_front(sizeof(*header) + total_body_length);
            return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA);
        }
        MostCommonMessage* msg =
            static_cast<MostCommonMessage*>(socket->parsing_context());
        if (msg == NULL) {
            msg = MostCommonMessage::Get();
            socket->reset_parsing_context(msg);
        }

        // endianness conversions.
        const MemcacheResponseHeader local_header = {
            header->magic,
            header->command,
            butil::NetToHost16(header->key_length),
            header->extras_length,
            header->data_type,
            butil::NetToHost16(header->status),
            total_body_length,
            butil::NetToHost32(header->opaque),
            butil::NetToHost64(header->cas_value),
        };
        msg->meta.append(&local_header, sizeof(local_header));
        source->pop_front(sizeof(*header));
        source->cutn(&msg->meta, total_body_length);
        if (header->command == MC_BINARY_SASL_AUTH) {
            if (header->status != 0) {
                LOG(ERROR) << "Failed to authenticate the couchbase bucket."
                           << "All the following commands will result in auth failure.";
                return MakeParseError(PARSE_ERROR_NO_RESOURCE,
                                      "Fail to authenticate with the couchbase bucket");
            }
            DestroyingPtr<MostCommonMessage> auth_msg(
                 static_cast<MostCommonMessage*>(socket->release_parsing_context()));
            socket->GivebackPipelinedInfo(pi);
        } else {
            if (++msg->pi.count >= pi.count) {
                CHECK_EQ(msg->pi.count, pi.count);
                msg = static_cast<MostCommonMessage*>(socket->release_parsing_context());
                msg->pi = pi;
                return MakeMessage(msg);
            } else {
                socket->GivebackPipelinedInfo(pi);
            }
        }
    }
}

void ProcessMemcacheResponse(InputMessageBase* msg_base) {
    const int64_t start_parse_us = butil::cpuwide_time_us();
    DestroyingPtr<MostCommonMessage> msg(static_cast<MostCommonMessage*>(msg_base));

    const bthread_id_t cid = msg->pi.id_wait;
    Controller* cntl = NULL;
    const int rc = bthread_id_lock(cid, (void**)&cntl);
    if (rc != 0) {
        LOG_IF(ERROR, rc != EINVAL && rc != EPERM)
            << "Fail to lock correlation_id=" << cid << ": " << berror(rc);
        return;
    }

    ControllerPrivateAccessor accessor(cntl);
    Span* span = accessor.span();
    if (span) {
        span->set_base_real_us(msg->base_real_us());
        span->set_received_us(msg->received_us());
        span->set_response_size(msg->meta.length());
        span->set_start_parse_us(start_parse_us);
    }
    const int saved_error = cntl->ErrorCode();
    if (cntl->response() == NULL) {
        cntl->SetFailed(ERESPONSE, "response is NULL!");
    } else if (cntl->response()->GetDescriptor() != MemcacheResponse::descriptor()) {
        cntl->SetFailed(ERESPONSE, "Must be MemcacheResponse");
    } else {
        // We work around ParseFrom of pb which is just a placeholder.
        ((MemcacheResponse*)cntl->response())->raw_buffer() = msg->meta.movable();
        if (msg->pi.count != accessor.pipelined_count()) {
            cntl->SetFailed(ERESPONSE, "pipelined_count=%d of response does "
                                  "not equal request's=%d",
                                  msg->pi.count, accessor.pipelined_count());
        }
    }
    // Unlocks correlation_id inside. Revert controller's
    // error code if it version check of `cid' fails
    msg.reset();  // optional, just release resourse ASAP
    accessor.OnResponse(cid, saved_error);
}

void SerializeMemcacheRequest(butil::IOBuf* buf,
                              Controller* cntl,
                              const google::protobuf::Message* request) {
    if (request == NULL) {
        return cntl->SetFailed(EREQUEST, "request is NULL");
    }
    if (request->GetDescriptor() != MemcacheRequest::descriptor()) {
        return cntl->SetFailed(EREQUEST, "Must be MemcacheRequest");
    }
    const MemcacheRequest* mr = (const MemcacheRequest*)request;
    // We work around SerializeTo of pb which is just a placeholder.
    *buf = mr->raw_buffer();
    ControllerPrivateAccessor(cntl).set_pipelined_count(mr->pipelined_count());
}

void PackMemcacheRequest(butil::IOBuf* buf,
                         SocketMessage**,
                         uint64_t /*correlation_id*/,
                         const google::protobuf::MethodDescriptor*,
                         Controller* cntl,
                         const butil::IOBuf& request,
                         const Authenticator* auth) {
    if (auth) {
        std::string auth_str;
        if (auth->GenerateCredential(&auth_str) != 0) {
            return cntl->SetFailed(EREQUEST, "Fail to generate credential");
        }
        buf->append(auth_str);
    }
    buf->append(request);
}

const std::string& GetMemcacheMethodName(
    const google::protobuf::MethodDescriptor*,
    const Controller*) {
    const static std::string MEMCACHED_STR = "memcached";
    return MEMCACHED_STR;
}

}  // namespace policy
} // namespace brpc
<commit_msg>polish a log in last CI<commit_after>// Copyright (c) 2015 Baidu, Inc.
//
// 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.

// Authors: Ge,Jun (gejun@baidu.com)

#include <google/protobuf/descriptor.h>         // MethodDescriptor
#include <google/protobuf/message.h>            // Message
#include <gflags/gflags.h>
#include "butil/logging.h"                       // LOG()
#include "butil/time.h"
#include "butil/iobuf.h"                         // butil::IOBuf
#include "butil/sys_byteorder.h"
#include "brpc/controller.h"               // Controller
#include "brpc/details/controller_private_accessor.h"
#include "brpc/socket.h"                   // Socket
#include "brpc/server.h"                   // Server
#include "brpc/details/server_private_accessor.h"
#include "brpc/span.h"
#include "brpc/compress.h"                 // ParseFromCompressedData
#include "brpc/policy/memcache_binary_protocol.h"
#include "brpc/policy/memcache_binary_header.h"
#include "brpc/memcache.h"
#include "brpc/policy/most_common_message.h"
#include "butil/containers/flat_map.h"

namespace brpc {

DECLARE_bool(enable_rpcz);

namespace policy {

BAIDU_CASSERT(sizeof(MemcacheRequestHeader) == 24, must_match);
BAIDU_CASSERT(sizeof(MemcacheResponseHeader) == 24, must_match);

static uint64_t supported_cmd_map[8];
static pthread_once_t supported_cmd_map_once = PTHREAD_ONCE_INIT;

static void InitSupportedCommandMap() {
    butil::bit_array_clear(supported_cmd_map, 256);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_GET);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_SET);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_ADD);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_REPLACE);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_DELETE);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_INCREMENT);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_DECREMENT);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_FLUSH);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_VERSION);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_NOOP);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_APPEND);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_PREPEND);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_STAT);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_TOUCH);
    butil::bit_array_set(supported_cmd_map, MC_BINARY_SASL_AUTH);
}

inline bool IsSupportedCommand(uint8_t command) {
    pthread_once(&supported_cmd_map_once, InitSupportedCommandMap);
    return butil::bit_array_get(supported_cmd_map, command);
}

ParseResult ParseMemcacheMessage(butil::IOBuf* source,
                                 Socket* socket, bool /*read_eof*/, const void */*arg*/) {
    while (1) {
        const uint8_t* p_mcmagic = (const uint8_t*)source->fetch1();
        if (NULL == p_mcmagic) {
            return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA);
        }
        if (*p_mcmagic != (uint8_t)MC_MAGIC_RESPONSE) {
            return MakeParseError(PARSE_ERROR_TRY_OTHERS);
        }
        char buf[24];
        const uint8_t* p = (const uint8_t*)source->fetch(buf, sizeof(buf));
        if (NULL == p) {
            return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA);
        }
        const MemcacheResponseHeader* header = (const MemcacheResponseHeader*)p;
        uint32_t total_body_length = butil::NetToHost32(header->total_body_length);
        if (source->size() < sizeof(*header) + total_body_length) {
            return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA);
        }

        if (!IsSupportedCommand(header->command)) {
            LOG(WARNING) << "Not support command=" << header->command;
            source->pop_front(sizeof(*header) + total_body_length);
            return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA);
        }

        PipelinedInfo pi;
        if (!socket->PopPipelinedInfo(&pi)) {
            LOG(WARNING) << "No corresponding PipelinedInfo in socket, drop";
            source->pop_front(sizeof(*header) + total_body_length);
            return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA);
        }
        MostCommonMessage* msg =
            static_cast<MostCommonMessage*>(socket->parsing_context());
        if (msg == NULL) {
            msg = MostCommonMessage::Get();
            socket->reset_parsing_context(msg);
        }

        // endianness conversions.
        const MemcacheResponseHeader local_header = {
            header->magic,
            header->command,
            butil::NetToHost16(header->key_length),
            header->extras_length,
            header->data_type,
            butil::NetToHost16(header->status),
            total_body_length,
            butil::NetToHost32(header->opaque),
            butil::NetToHost64(header->cas_value),
        };
        msg->meta.append(&local_header, sizeof(local_header));
        source->pop_front(sizeof(*header));
        source->cutn(&msg->meta, total_body_length);
        if (header->command == MC_BINARY_SASL_AUTH) {
            if (header->status != 0) {
                LOG(ERROR) << "Failed to authenticate the couchbase bucket.";
                return MakeParseError(PARSE_ERROR_NO_RESOURCE,
                                      "Fail to authenticate with the couchbase bucket");
            }
            DestroyingPtr<MostCommonMessage> auth_msg(
                 static_cast<MostCommonMessage*>(socket->release_parsing_context()));
            socket->GivebackPipelinedInfo(pi);
        } else {
            if (++msg->pi.count >= pi.count) {
                CHECK_EQ(msg->pi.count, pi.count);
                msg = static_cast<MostCommonMessage*>(socket->release_parsing_context());
                msg->pi = pi;
                return MakeMessage(msg);
            } else {
                socket->GivebackPipelinedInfo(pi);
            }
        }
    }
}

void ProcessMemcacheResponse(InputMessageBase* msg_base) {
    const int64_t start_parse_us = butil::cpuwide_time_us();
    DestroyingPtr<MostCommonMessage> msg(static_cast<MostCommonMessage*>(msg_base));

    const bthread_id_t cid = msg->pi.id_wait;
    Controller* cntl = NULL;
    const int rc = bthread_id_lock(cid, (void**)&cntl);
    if (rc != 0) {
        LOG_IF(ERROR, rc != EINVAL && rc != EPERM)
            << "Fail to lock correlation_id=" << cid << ": " << berror(rc);
        return;
    }

    ControllerPrivateAccessor accessor(cntl);
    Span* span = accessor.span();
    if (span) {
        span->set_base_real_us(msg->base_real_us());
        span->set_received_us(msg->received_us());
        span->set_response_size(msg->meta.length());
        span->set_start_parse_us(start_parse_us);
    }
    const int saved_error = cntl->ErrorCode();
    if (cntl->response() == NULL) {
        cntl->SetFailed(ERESPONSE, "response is NULL!");
    } else if (cntl->response()->GetDescriptor() != MemcacheResponse::descriptor()) {
        cntl->SetFailed(ERESPONSE, "Must be MemcacheResponse");
    } else {
        // We work around ParseFrom of pb which is just a placeholder.
        ((MemcacheResponse*)cntl->response())->raw_buffer() = msg->meta.movable();
        if (msg->pi.count != accessor.pipelined_count()) {
            cntl->SetFailed(ERESPONSE, "pipelined_count=%d of response does "
                                  "not equal request's=%d",
                                  msg->pi.count, accessor.pipelined_count());
        }
    }
    // Unlocks correlation_id inside. Revert controller's
    // error code if it version check of `cid' fails
    msg.reset();  // optional, just release resourse ASAP
    accessor.OnResponse(cid, saved_error);
}

void SerializeMemcacheRequest(butil::IOBuf* buf,
                              Controller* cntl,
                              const google::protobuf::Message* request) {
    if (request == NULL) {
        return cntl->SetFailed(EREQUEST, "request is NULL");
    }
    if (request->GetDescriptor() != MemcacheRequest::descriptor()) {
        return cntl->SetFailed(EREQUEST, "Must be MemcacheRequest");
    }
    const MemcacheRequest* mr = (const MemcacheRequest*)request;
    // We work around SerializeTo of pb which is just a placeholder.
    *buf = mr->raw_buffer();
    ControllerPrivateAccessor(cntl).set_pipelined_count(mr->pipelined_count());
}

void PackMemcacheRequest(butil::IOBuf* buf,
                         SocketMessage**,
                         uint64_t /*correlation_id*/,
                         const google::protobuf::MethodDescriptor*,
                         Controller* cntl,
                         const butil::IOBuf& request,
                         const Authenticator* auth) {
    if (auth) {
        std::string auth_str;
        if (auth->GenerateCredential(&auth_str) != 0) {
            return cntl->SetFailed(EREQUEST, "Fail to generate credential");
        }
        buf->append(auth_str);
    }
    buf->append(request);
}

const std::string& GetMemcacheMethodName(
    const google::protobuf::MethodDescriptor*,
    const Controller*) {
    const static std::string MEMCACHED_STR = "memcached";
    return MEMCACHED_STR;
}

}  // namespace policy
} // namespace brpc
<|endoftext|>