<commit_before>//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "SourceKit/Core/Context.h"
#include "SourceKit/Core/LangSupport.h"
#include "SourceKit/Core/NotificationCenter.h"
#include "SourceKit/Support/Concurrency.h"
#include "SourceKit/SwiftLang/Factory.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TargetSelect.h"
#include "gtest/gtest.h"

using namespace SourceKit;
using namespace llvm;

static StringRef getRuntimeLibPath() {
  return sys::path::parent_path(SWIFTLIB_DIR);
}

namespace {

class NullEditorConsumer : public EditorConsumer {
  bool needsSemanticInfo() override { return needsSema; }

  void handleRequestError(const char *Description) override {
    llvm_unreachable("unexpected error");
  }

  bool syntaxMapEnabled() override { return true; }

  void handleSyntaxMap(unsigned Offset, unsigned Length, UIdent Kind) override {
  }

  void handleSemanticAnnotation(unsigned Offset, unsigned Length, UIdent Kind,
                                bool isSystem) override {}

  bool documentStructureEnabled() override { return false; }

  void beginDocumentSubStructure(unsigned Offset, unsigned Length,
                                 UIdent Kind, UIdent AccessLevel,
                                 UIdent SetterAccessLevel,
                                 unsigned NameOffset,
                                 unsigned NameLength,
                                 unsigned BodyOffset,
                                 unsigned BodyLength,
                                 unsigned DocOffset,
                                 unsigned DocLength,
                                 StringRef DisplayName,
                                 StringRef TypeName,
                                 StringRef RuntimeName,
                                 StringRef SelectorName,
                                 ArrayRef<StringRef> InheritedTypes,
                                 ArrayRef<std::tuple<UIdent, unsigned, unsigned>> Attrs) override {
  }

  void endDocumentSubStructure() override {}

  void handleDocumentSubStructureElement(UIdent Kind, unsigned Offset,
                                         unsigned Length) override {}

  void recordAffectedRange(unsigned Offset, unsigned Length) override {}

  void recordAffectedLineRange(unsigned Line, unsigned Length) override {}

  bool diagnosticsEnabled() override { return false; }

  void setDiagnosticStage(UIdent DiagStage) override {}
  void handleDiagnostic(const DiagnosticEntryInfo &Info,
                        UIdent DiagStage) override {}
  void recordFormattedText(StringRef Text) override {}

  void handleSourceText(StringRef Text) override {}
  void handleSyntaxTree(const swift::syntax::SourceFileSyntax &SyntaxTree) override {}

  SyntaxTreeTransferMode syntaxTreeTransferMode() override {
    return SyntaxTreeTransferMode::Off;
  }

public:
  bool needsSema = false;
};

struct TestCursorInfo {
  // Empty if no error.
  std::string Error;
  std::string Name;
  std::string Typename;
  std::string Filename;
  unsigned Offset;
  unsigned Length;
};

class CursorInfoTest : public ::testing::Test {
  SourceKit::Context &Ctx;
  std::atomic<int> NumTasks;
  NullEditorConsumer Consumer;

public:
  LangSupport &getLang() { return Ctx.getSwiftLangSupport(); }

  void SetUp() override {
    llvm::InitializeAllTargets();
    llvm::InitializeAllTargetMCs();
    llvm::InitializeAllAsmPrinters();
    llvm::InitializeAllAsmParsers();
    NumTasks = 0;
  }

  CursorInfoTest()
      : Ctx(*new SourceKit::Context(getRuntimeLibPath(),
                                    /*diagnosticDocumentationPath*/ "",
                                    SourceKit::createSwiftLangSupport,
                                    /*dispatchOnMain=*/false)) {
    // This is avoiding destroying \p SourceKit::Context because another
    // thread may be active trying to use it to post notifications.
    // FIXME: Use shared_ptr ownership to avoid such issues.
  }

  void addNotificationReceiver(DocumentUpdateNotificationReceiver Receiver) {
    Ctx.getNotificationCenter()->addDocumentUpdateNotificationReceiver(Receiver);
  }

  void open(const char *DocName, StringRef Text,
            Optional<ArrayRef<const char *>> CArgs = llvm::None) {
    auto Args = CArgs.hasValue() ? makeArgs(DocName, *CArgs)
                                 : std::vector<const char *>{};
    auto Buf = MemoryBuffer::getMemBufferCopy(Text, DocName);
    getLang().editorOpen(DocName, Buf.get(), Consumer, Args, None);
  }

  void replaceText(StringRef DocName, unsigned Offset, unsigned Length,
                   StringRef Text) {
    auto Buf = MemoryBuffer::getMemBufferCopy(Text, DocName);
    getLang().editorReplaceText(DocName, Buf.get(), Offset, Length, Consumer);
  }

  TestCursorInfo getCursor(const char *DocName, unsigned Offset,
                           ArrayRef<const char *> CArgs) {
    auto Args = makeArgs(DocName, CArgs);
    Semaphore sema(0);

    TestCursorInfo TestInfo;
    getLang().getCursorInfo(DocName, Offset, 0, false, false, false, Args, None,
      [&](const RequestResult<CursorInfoData> &Result) {
        assert(!Result.isCancelled());
        if (Result.isError()) {
          TestInfo.Error = Result.getError().str();
          sema.signal();
          return;
        }
        const CursorInfoData &Info = Result.value();
        if (!Info.Symbols.empty()) {
          const CursorSymbolInfo &MainSymbol = Info.Symbols[0];
          TestInfo.Name = std::string(MainSymbol.Name.str());
          TestInfo.Typename = MainSymbol.TypeName.str();
          TestInfo.Filename = MainSymbol.Location.Filename.str();
          TestInfo.Offset = MainSymbol.Location.Offset;
          TestInfo.Length = MainSymbol.Location.Length;
        }
        sema.signal();
      });

    bool expired = sema.wait(60 * 1000);
    if (expired)
      llvm::report_fatal_error("check took too long");
    return TestInfo;
  }

  unsigned findOffset(StringRef Val, StringRef Text) {
    auto pos = Text.find(Val);
    assert(pos != StringRef::npos);
    return pos;
  }

  void setNeedsSema(bool needsSema) { Consumer.needsSema = needsSema; }

private:
  std::vector<const char *> makeArgs(const char *DocName,
                                     ArrayRef<const char *> CArgs) {
    std::vector<const char *> Args = CArgs;
    Args.push_back(DocName);
    return Args;
  }
};

} // anonymous namespace

TEST_F(CursorInfoTest, FileNotExist) {
  const char *DocName = "test.swift";
  const char *Contents =
    "let foo = 0\n";
  const char *Args[] = { "<not-existent-file>" };

  open(DocName, Contents);
  auto FooOffs = findOffset("foo =", Contents);
  auto Info = getCursor(DocName, FooOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());
}

static const char *ExpensiveInit =
    "[0:0,0:0,0:0,0:0,0:0,0:0,0:0]";

TEST_F(CursorInfoTest, EditAfter) {
  const char *DocName = "test.swift";
  const char *Contents =
    "let value = foo\n"
    "let foo = 0\n";
  const char *Args[] = { "-parse-as-library" };

  open(DocName, Contents);
  auto FooRefOffs = findOffset("foo", Contents);
  auto FooOffs = findOffset("foo =", Contents);
  auto Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());
  EXPECT_STREQ(DocName, Info.Filename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("foo"), Info.Length);

  StringRef TextToReplace = "0";
  replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
              ExpensiveInit);
  // Insert a space in front of 'foo' decl.
  replaceText(DocName, FooOffs, 0, " ");
  ++FooOffs;

  // Should not wait for the new AST, it should give the previous answer.
  Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());
  EXPECT_STREQ(DocName, Info.Filename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("foo"), Info.Length);
}

TEST_F(CursorInfoTest, EditBefore) {
  const char *DocName = "test.swift";
  const char *Contents =
    "let foo = 0\n"
    "let value = foo;\n";
  const char *Args[] = { "-parse-as-library" };

  open(DocName, Contents);
  auto FooRefOffs = findOffset("foo;", Contents);
  auto FooOffs = findOffset("foo =", Contents);
  auto Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());
  EXPECT_STREQ(DocName, Info.Filename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("foo"), Info.Length);

  StringRef TextToReplace = "0";
  replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
              ExpensiveInit);
  FooRefOffs += StringRef(ExpensiveInit).size() - TextToReplace.size();
  // Insert a space in front of 'foo' decl.
  replaceText(DocName, FooOffs, 0, " ");
  ++FooOffs;
  ++FooRefOffs;

  // Should not wait for the new AST, it should give the previous answer.
  Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());
  EXPECT_STREQ(DocName, Info.Filename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("foo"), Info.Length);
}

TEST_F(CursorInfoTest, CursorInfoMustWaitDueDeclLoc) {
  const char *DocName = "test.swift";
  const char *Contents =
    "let value = foo\n"
    "let foo = 0\n";
  const char *Args[] = { "-parse-as-library" };

  open(DocName, Contents);
  auto FooRefOffs = findOffset("foo", Contents);
  auto FooOffs = findOffset("foo =", Contents);
  auto Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());

  StringRef TextToReplace = "0";
  replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
              ExpensiveInit);
  // Edit over the 'foo' decl.
  replaceText(DocName, FooOffs, strlen("foo"), "foo");

  // Should wait for the new AST, because the declaration location for the 'foo'
  // reference has been edited out.
  Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("[Int : Int]", Info.Typename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("foo"), Info.Length);
}

TEST_F(CursorInfoTest, CursorInfoMustWaitDueOffset) {
  const char *DocName = "test.swift";
  const char *Contents =
    "let value = foo\n"
    "let foo = 0\n";
  const char *Args[] = { "-parse-as-library" };

  open(DocName, Contents);
  auto FooRefOffs = findOffset("foo", Contents);
  auto FooOffs = findOffset("foo =", Contents);
  auto Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());

  StringRef TextToReplace = "0";
  replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
              ExpensiveInit);
  // Edit over the 'foo' reference.
  replaceText(DocName, FooRefOffs, strlen("foo"), "foo");

  // Should wait for the new AST, because the cursor location has been edited
  // out.
  Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("[Int : Int]", Info.Typename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("foo"), Info.Length);
}

TEST_F(CursorInfoTest, CursorInfoMustWaitDueToken) {
  const char *DocName = "test.swift";
  const char *Contents =
    "let value = foo\n"
    "let foo = 0\n";
  const char *Args[] = { "-parse-as-library" };

  open(DocName, Contents);
  auto FooRefOffs = findOffset("foo", Contents);
  auto FooOffs = findOffset("foo =", Contents);
  auto Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());

  StringRef TextToReplace = "0";
  replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
              ExpensiveInit);
  // Change 'foo' to 'fog' by replacing the last character.
  replaceText(DocName, FooOffs+2, 1, "g");
  replaceText(DocName, FooRefOffs+2, 1, "g");

  // Should wait for the new AST, because the cursor location points to a
  // different token.
  Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("fog", Info.Name.c_str());
  EXPECT_STREQ("[Int : Int]", Info.Typename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("fog"), Info.Length);
}

TEST_F(CursorInfoTest, CursorInfoMustWaitDueTokenRace) {
  const char *DocName = "test.swift";
  const char *Contents = "let value = foo\n"
                         "let foo = 0\n";
  const char *Args[] = {"-parse-as-library"};

  auto FooRefOffs = findOffset("foo", Contents);
  auto FooOffs = findOffset("foo =", Contents);

  // Open with args, kicking off an ast build. The hope of this tests is for
  // this AST to still be in the process of building when we start the cursor
  // info, to ensure the ASTManager doesn't try to handle this cursor info with
  // the wrong AST.
  setNeedsSema(true);
  open(DocName, Contents, llvm::makeArrayRef(Args));
  // Change 'foo' to 'fog' by replacing the last character.
  replaceText(DocName, FooOffs + 2, 1, "g");
  replaceText(DocName, FooRefOffs + 2, 1, "g");

  // Should wait for the new AST, because the cursor location points to a
  // different token.
  auto Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("fog", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("fog"), Info.Length);
}
<commit_msg>[test] Disable CursorInfoTest.CursorInfoMustWaitDueTokenRace<commit_after>//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "SourceKit/Core/Context.h"
#include "SourceKit/Core/LangSupport.h"
#include "SourceKit/Core/NotificationCenter.h"
#include "SourceKit/Support/Concurrency.h"
#include "SourceKit/SwiftLang/Factory.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TargetSelect.h"
#include "gtest/gtest.h"

using namespace SourceKit;
using namespace llvm;

static StringRef getRuntimeLibPath() {
  return sys::path::parent_path(SWIFTLIB_DIR);
}

namespace {

class NullEditorConsumer : public EditorConsumer {
  bool needsSemanticInfo() override { return needsSema; }

  void handleRequestError(const char *Description) override {
    llvm_unreachable("unexpected error");
  }

  bool syntaxMapEnabled() override { return true; }

  void handleSyntaxMap(unsigned Offset, unsigned Length, UIdent Kind) override {
  }

  void handleSemanticAnnotation(unsigned Offset, unsigned Length, UIdent Kind,
                                bool isSystem) override {}

  bool documentStructureEnabled() override { return false; }

  void beginDocumentSubStructure(unsigned Offset, unsigned Length,
                                 UIdent Kind, UIdent AccessLevel,
                                 UIdent SetterAccessLevel,
                                 unsigned NameOffset,
                                 unsigned NameLength,
                                 unsigned BodyOffset,
                                 unsigned BodyLength,
                                 unsigned DocOffset,
                                 unsigned DocLength,
                                 StringRef DisplayName,
                                 StringRef TypeName,
                                 StringRef RuntimeName,
                                 StringRef SelectorName,
                                 ArrayRef<StringRef> InheritedTypes,
                                 ArrayRef<std::tuple<UIdent, unsigned, unsigned>> Attrs) override {
  }

  void endDocumentSubStructure() override {}

  void handleDocumentSubStructureElement(UIdent Kind, unsigned Offset,
                                         unsigned Length) override {}

  void recordAffectedRange(unsigned Offset, unsigned Length) override {}

  void recordAffectedLineRange(unsigned Line, unsigned Length) override {}

  bool diagnosticsEnabled() override { return false; }

  void setDiagnosticStage(UIdent DiagStage) override {}
  void handleDiagnostic(const DiagnosticEntryInfo &Info,
                        UIdent DiagStage) override {}
  void recordFormattedText(StringRef Text) override {}

  void handleSourceText(StringRef Text) override {}
  void handleSyntaxTree(const swift::syntax::SourceFileSyntax &SyntaxTree) override {}

  SyntaxTreeTransferMode syntaxTreeTransferMode() override {
    return SyntaxTreeTransferMode::Off;
  }

public:
  bool needsSema = false;
};

struct TestCursorInfo {
  // Empty if no error.
  std::string Error;
  std::string Name;
  std::string Typename;
  std::string Filename;
  unsigned Offset;
  unsigned Length;
};

class CursorInfoTest : public ::testing::Test {
  SourceKit::Context &Ctx;
  std::atomic<int> NumTasks;
  NullEditorConsumer Consumer;

public:
  LangSupport &getLang() { return Ctx.getSwiftLangSupport(); }

  void SetUp() override {
    llvm::InitializeAllTargets();
    llvm::InitializeAllTargetMCs();
    llvm::InitializeAllAsmPrinters();
    llvm::InitializeAllAsmParsers();
    NumTasks = 0;
  }

  CursorInfoTest()
      : Ctx(*new SourceKit::Context(getRuntimeLibPath(),
                                    /*diagnosticDocumentationPath*/ "",
                                    SourceKit::createSwiftLangSupport,
                                    /*dispatchOnMain=*/false)) {
    // This is avoiding destroying \p SourceKit::Context because another
    // thread may be active trying to use it to post notifications.
    // FIXME: Use shared_ptr ownership to avoid such issues.
  }

  void addNotificationReceiver(DocumentUpdateNotificationReceiver Receiver) {
    Ctx.getNotificationCenter()->addDocumentUpdateNotificationReceiver(Receiver);
  }

  void open(const char *DocName, StringRef Text,
            Optional<ArrayRef<const char *>> CArgs = llvm::None) {
    auto Args = CArgs.hasValue() ? makeArgs(DocName, *CArgs)
                                 : std::vector<const char *>{};
    auto Buf = MemoryBuffer::getMemBufferCopy(Text, DocName);
    getLang().editorOpen(DocName, Buf.get(), Consumer, Args, None);
  }

  void replaceText(StringRef DocName, unsigned Offset, unsigned Length,
                   StringRef Text) {
    auto Buf = MemoryBuffer::getMemBufferCopy(Text, DocName);
    getLang().editorReplaceText(DocName, Buf.get(), Offset, Length, Consumer);
  }

  TestCursorInfo getCursor(const char *DocName, unsigned Offset,
                           ArrayRef<const char *> CArgs) {
    auto Args = makeArgs(DocName, CArgs);
    Semaphore sema(0);

    TestCursorInfo TestInfo;
    getLang().getCursorInfo(DocName, Offset, 0, false, false, false, Args, None,
      [&](const RequestResult<CursorInfoData> &Result) {
        assert(!Result.isCancelled());
        if (Result.isError()) {
          TestInfo.Error = Result.getError().str();
          sema.signal();
          return;
        }
        const CursorInfoData &Info = Result.value();
        if (!Info.Symbols.empty()) {
          const CursorSymbolInfo &MainSymbol = Info.Symbols[0];
          TestInfo.Name = std::string(MainSymbol.Name.str());
          TestInfo.Typename = MainSymbol.TypeName.str();
          TestInfo.Filename = MainSymbol.Location.Filename.str();
          TestInfo.Offset = MainSymbol.Location.Offset;
          TestInfo.Length = MainSymbol.Location.Length;
        }
        sema.signal();
      });

    bool expired = sema.wait(60 * 1000);
    if (expired)
      llvm::report_fatal_error("check took too long");
    return TestInfo;
  }

  unsigned findOffset(StringRef Val, StringRef Text) {
    auto pos = Text.find(Val);
    assert(pos != StringRef::npos);
    return pos;
  }

  void setNeedsSema(bool needsSema) { Consumer.needsSema = needsSema; }

private:
  std::vector<const char *> makeArgs(const char *DocName,
                                     ArrayRef<const char *> CArgs) {
    std::vector<const char *> Args = CArgs;
    Args.push_back(DocName);
    return Args;
  }
};

} // anonymous namespace

TEST_F(CursorInfoTest, FileNotExist) {
  const char *DocName = "test.swift";
  const char *Contents =
    "let foo = 0\n";
  const char *Args[] = { "<not-existent-file>" };

  open(DocName, Contents);
  auto FooOffs = findOffset("foo =", Contents);
  auto Info = getCursor(DocName, FooOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());
}

static const char *ExpensiveInit =
    "[0:0,0:0,0:0,0:0,0:0,0:0,0:0]";

TEST_F(CursorInfoTest, EditAfter) {
  const char *DocName = "test.swift";
  const char *Contents =
    "let value = foo\n"
    "let foo = 0\n";
  const char *Args[] = { "-parse-as-library" };

  open(DocName, Contents);
  auto FooRefOffs = findOffset("foo", Contents);
  auto FooOffs = findOffset("foo =", Contents);
  auto Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());
  EXPECT_STREQ(DocName, Info.Filename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("foo"), Info.Length);

  StringRef TextToReplace = "0";
  replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
              ExpensiveInit);
  // Insert a space in front of 'foo' decl.
  replaceText(DocName, FooOffs, 0, " ");
  ++FooOffs;

  // Should not wait for the new AST, it should give the previous answer.
  Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());
  EXPECT_STREQ(DocName, Info.Filename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("foo"), Info.Length);
}

TEST_F(CursorInfoTest, EditBefore) {
  const char *DocName = "test.swift";
  const char *Contents =
    "let foo = 0\n"
    "let value = foo;\n";
  const char *Args[] = { "-parse-as-library" };

  open(DocName, Contents);
  auto FooRefOffs = findOffset("foo;", Contents);
  auto FooOffs = findOffset("foo =", Contents);
  auto Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());
  EXPECT_STREQ(DocName, Info.Filename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("foo"), Info.Length);

  StringRef TextToReplace = "0";
  replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
              ExpensiveInit);
  FooRefOffs += StringRef(ExpensiveInit).size() - TextToReplace.size();
  // Insert a space in front of 'foo' decl.
  replaceText(DocName, FooOffs, 0, " ");
  ++FooOffs;
  ++FooRefOffs;

  // Should not wait for the new AST, it should give the previous answer.
  Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());
  EXPECT_STREQ(DocName, Info.Filename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("foo"), Info.Length);
}

TEST_F(CursorInfoTest, CursorInfoMustWaitDueDeclLoc) {
  const char *DocName = "test.swift";
  const char *Contents =
    "let value = foo\n"
    "let foo = 0\n";
  const char *Args[] = { "-parse-as-library" };

  open(DocName, Contents);
  auto FooRefOffs = findOffset("foo", Contents);
  auto FooOffs = findOffset("foo =", Contents);
  auto Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());

  StringRef TextToReplace = "0";
  replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
              ExpensiveInit);
  // Edit over the 'foo' decl.
  replaceText(DocName, FooOffs, strlen("foo"), "foo");

  // Should wait for the new AST, because the declaration location for the 'foo'
  // reference has been edited out.
  Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("[Int : Int]", Info.Typename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("foo"), Info.Length);
}

TEST_F(CursorInfoTest, CursorInfoMustWaitDueOffset) {
  const char *DocName = "test.swift";
  const char *Contents =
    "let value = foo\n"
    "let foo = 0\n";
  const char *Args[] = { "-parse-as-library" };

  open(DocName, Contents);
  auto FooRefOffs = findOffset("foo", Contents);
  auto FooOffs = findOffset("foo =", Contents);
  auto Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());

  StringRef TextToReplace = "0";
  replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
              ExpensiveInit);
  // Edit over the 'foo' reference.
  replaceText(DocName, FooRefOffs, strlen("foo"), "foo");

  // Should wait for the new AST, because the cursor location has been edited
  // out.
  Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("[Int : Int]", Info.Typename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("foo"), Info.Length);
}

TEST_F(CursorInfoTest, CursorInfoMustWaitDueToken) {
  const char *DocName = "test.swift";
  const char *Contents =
    "let value = foo\n"
    "let foo = 0\n";
  const char *Args[] = { "-parse-as-library" };

  open(DocName, Contents);
  auto FooRefOffs = findOffset("foo", Contents);
  auto FooOffs = findOffset("foo =", Contents);
  auto Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("foo", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());

  StringRef TextToReplace = "0";
  replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
              ExpensiveInit);
  // Change 'foo' to 'fog' by replacing the last character.
  replaceText(DocName, FooOffs+2, 1, "g");
  replaceText(DocName, FooRefOffs+2, 1, "g");

  // Should wait for the new AST, because the cursor location points to a
  // different token.
  Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("fog", Info.Name.c_str());
  EXPECT_STREQ("[Int : Int]", Info.Typename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("fog"), Info.Length);
}

// This test is failing occassionally in CI: rdar://55314062
TEST_F(CursorInfoTest, DISABLED_CursorInfoMustWaitDueTokenRace) {
  const char *DocName = "test.swift";
  const char *Contents = "let value = foo\n"
                         "let foo = 0\n";
  const char *Args[] = {"-parse-as-library"};

  auto FooRefOffs = findOffset("foo", Contents);
  auto FooOffs = findOffset("foo =", Contents);

  // Open with args, kicking off an ast build. The hope of this tests is for
  // this AST to still be in the process of building when we start the cursor
  // info, to ensure the ASTManager doesn't try to handle this cursor info with
  // the wrong AST.
  setNeedsSema(true);
  open(DocName, Contents, llvm::makeArrayRef(Args));
  // Change 'foo' to 'fog' by replacing the last character.
  replaceText(DocName, FooOffs + 2, 1, "g");
  replaceText(DocName, FooRefOffs + 2, 1, "g");

  // Should wait for the new AST, because the cursor location points to a
  // different token.
  auto Info = getCursor(DocName, FooRefOffs, Args);
  EXPECT_STREQ("fog", Info.Name.c_str());
  EXPECT_STREQ("Int", Info.Typename.c_str());
  EXPECT_EQ(FooOffs, Info.Offset);
  EXPECT_EQ(strlen("fog"), Info.Length);
}
<|endoftext|>


<commit_before>#include "game.h"

const char *_ch_piece_csa[16] =
    { "--", "FU", "--", "--", "GI", "KI", "KA", "HI",
    "OU", "TO", "--", "--", "NG", "--", "UM", "RY" };

Game::Game(std::string bp): binPath(bp){
  _board = new Board();
  _search = new Search(_board);
}

Game::~Game() {
  delete _board;
  delete _search;
}

Board* Game::get_board(){
  return _board;
}

Search* Game::get_search(){
  return _search;
}

void Game::game_initialize(){
  if ( _board->zobrist_init(binPath) < 0 ) {
    std::cout << "could not load Zobrist" << std::endl;
    exit(1);
  }
  //_board->printZobristHashed();
  _board->clear_game();
  if ( _board->starting_initialize(binPath) < 0 ) {
    std::cout << "could not load BB_Attack" << std::endl;
    exit(1);
  }
  if ( !_board->zobrist_check() ) {
    _board->printZobristHashed();
    std::cout << "zobrist is duplicate" << std::endl;
    exit(1);
  }
  if ( _search->useTpt) {
    _board->read_tpt(binPath);
  }
}

void Game::game_finalize(){
  if ( _search->useTpt ) {
    _board->write_tpt(binPath);
  }
}

unsigned int Game::move(double *_fromX, double *_fromY, double *_toX, double *_toY, const char *_piece, double *_color, double *_promote, std::string* _cap, double *_isAttack){
  int move;
  int from, to, type, type_c, prom, cap, cap_type;
  int fromX, fromY, toX, toY, i;
  unsigned int attack_pieces;
  const char *buf;

  fromX = int(*_fromX);
  fromY = int(*_fromY);
  toX   = int(*_toX);
  toY   = int(*_toY);
  buf   = _piece;

  from = 5 - fromX + ( fromY -1 )*5;
  to   = 5 - toX   + ( toY   -1 )*5;

  if( fromX == 0 && fromY == 0 )
    {
      from = move_drop;
      for( i=0; i < 16; i++ )
        {
          if( !strcmp( buf, _ch_piece_csa[ i ] ) )
            {
              type_c = _board->get_turn() ? i+16 : i;
              break;
            }
        }
      if( i >= 16 )
        type_c = 0;
      prom = 0;
      cap = 0;
      cap_type = 0;
    }
  else
    {
      if( from < 0 || from >= 25 || to < 0 || to >= 25 )
        { return MOVE_NULL; }

      type_c = _board->get_turn() ? _board->get_piece_on_sq_b( from ) : _board->get_piece_on_sq_w( from );
      type   = ( type_c & ~mask_piece_color );

      if( !strcmp( buf, _ch_piece_csa[ 0 ] ) )
        { return MOVE_NULL; }
      if( ( type < m_promote ) && !strcmp( buf, _ch_piece_csa[ type + m_promote ] ) )
        { prom = 1; }
      else if( !strcmp( buf, _ch_piece_csa[ type ] ) )
        { prom = 0; }
      else
        { return MOVE_NULL; }
      cap_type = _board->get_turn() ? _board->get_piece_on_sq_w( to ) : _board->get_piece_on_sq_b( to );
      if( cap_type == no_piece )
        {
          cap = 0;
          cap_type = 0;
        }
      else
        {
          cap = 1;
        }
    }

  *_promote = (double)prom;
  if (cap_type == 0) {
    *_cap = "";
  } else {
    *_cap = _ch_piece_csa[cap_type & ~mask_piece_color];
  }

  if (_board->is_attack(&attack_pieces)) {
    *_isAttack = 1;
  } else {
    *_isAttack = 0;
  }
  *_color = _board->turn();

  move = MOVE_C( type_c, from, to, prom, cap, cap_type );
  _board->make_move( move );
  return move;

}

double Game::search(double *_fromX, double *_fromY, double *_toX, double *_toY, std::string *_piece, double *_color, double *_promote, std::string* _capture, double *_isAttack){
  double search_time = _search->search_root();
  unsigned int move = _board->history[ _board->get_nply() - 1].move;
  unsigned int attack_pieces;
  int type_c = MOVE_TYPE( move );
  int type   = type_c & ~mask_piece_color;
  int from   = MOVE_FROM( move );
  int to     = MOVE_TO( move );
  int prom   = MOVE_PROMOTE( move );
  int cap    = MOVE_CAPTURE( move );
  int cap_type = MOVE_CAPTURED_TYPE( move );

  if(cap_type > 16){
    cap_type -= 16;
  }

  if( from == move_drop ) {
    *_fromX = 0;
    *_fromY = 0;
    *_toX = 5-( to%5 );
    *_toY = ( to/5 +1);
  } else {
    *_fromX = 5-( from%5 );
    *_fromY = ( from/5 +1);
    *_toX = 5-( to%5 );
    *_toY = ( to/5 +1);
  }

  *_promote = (double)prom;

  if( prom ) {
    *_piece = _ch_piece_csa[ type + m_promote ];
  } else {
    *_piece = _ch_piece_csa[ type ];
  }

  if (cap) {
    *_capture = _ch_piece_csa[ cap_type ];
  }else {
    *_capture = "";
  }
  if (_board->is_attack(&attack_pieces)) {
    *_isAttack = 1;
  } else {
    *_isAttack = 0;
  }

  *_color = _board->turn();
  return search_time;
}

void Game::legal(const unsigned int& lmove, double *_fromX, double *_fromY, double *_toX, double *_toY, std::string *_piece) {
  int type_c = MOVE_TYPE( lmove );
  int type   = type_c & ~mask_piece_color;
  int from   = MOVE_FROM( lmove );
  int to     = MOVE_TO( lmove );
  int prom   = MOVE_PROMOTE( lmove );

  if( from == move_drop ) {
    *_fromX = 0;
    *_fromY = 0;
    *_toX = 5-( to%5 );
    *_toY = ( to/5 +1);
  } else {
    *_fromX = 5-( from%5 );
    *_fromY = ( from/5 +1);
    *_toX = 5-( to%5 );
    *_toY = ( to/5 +1);
  }

  if (prom) {
    *_piece = _ch_piece_csa[ type + m_promote ];
  }else {
    *_piece = _ch_piece_csa[ type ];
  }
}
<commit_msg>show search sum time<commit_after>#include "game.h"

const char *_ch_piece_csa[16] =
    { "--", "FU", "--", "--", "GI", "KI", "KA", "HI",
    "OU", "TO", "--", "--", "NG", "--", "UM", "RY" };

Game::Game(std::string bp): binPath(bp){
  _board = new Board();
  _search = new Search(_board);
}

Game::~Game() {
  delete _board;
  delete _search;
}

Board* Game::get_board(){
  return _board;
}

Search* Game::get_search(){
  return _search;
}

void Game::game_initialize(){
  if ( _board->zobrist_init(binPath) < 0 ) {
    std::cout << "could not load Zobrist" << std::endl;
    exit(1);
  }
  //_board->printZobristHashed();
  _board->clear_game();
  if ( _board->starting_initialize(binPath) < 0 ) {
    std::cout << "could not load BB_Attack" << std::endl;
    exit(1);
  }
  if ( !_board->zobrist_check() ) {
    _board->printZobristHashed();
    std::cout << "zobrist is duplicate" << std::endl;
    exit(1);
  }
  if ( _search->useTpt) {
    _board->read_tpt(binPath);
  }
}

void Game::game_finalize(){
  if ( _search->useTpt ) {
    _board->write_tpt(binPath);
  }
  std::cout << "searchALL: " << _search->searchSumTime << std::endl;
}

unsigned int Game::move(double *_fromX, double *_fromY, double *_toX, double *_toY, const char *_piece, double *_color, double *_promote, std::string* _cap, double *_isAttack){
  int move;
  int from, to, type, type_c, prom, cap, cap_type;
  int fromX, fromY, toX, toY, i;
  unsigned int attack_pieces;
  const char *buf;

  fromX = int(*_fromX);
  fromY = int(*_fromY);
  toX   = int(*_toX);
  toY   = int(*_toY);
  buf   = _piece;

  from = 5 - fromX + ( fromY -1 )*5;
  to   = 5 - toX   + ( toY   -1 )*5;

  if( fromX == 0 && fromY == 0 )
    {
      from = move_drop;
      for( i=0; i < 16; i++ )
        {
          if( !strcmp( buf, _ch_piece_csa[ i ] ) )
            {
              type_c = _board->get_turn() ? i+16 : i;
              break;
            }
        }
      if( i >= 16 )
        type_c = 0;
      prom = 0;
      cap = 0;
      cap_type = 0;
    }
  else
    {
      if( from < 0 || from >= 25 || to < 0 || to >= 25 )
        { return MOVE_NULL; }

      type_c = _board->get_turn() ? _board->get_piece_on_sq_b( from ) : _board->get_piece_on_sq_w( from );
      type   = ( type_c & ~mask_piece_color );

      if( !strcmp( buf, _ch_piece_csa[ 0 ] ) )
        { return MOVE_NULL; }
      if( ( type < m_promote ) && !strcmp( buf, _ch_piece_csa[ type + m_promote ] ) )
        { prom = 1; }
      else if( !strcmp( buf, _ch_piece_csa[ type ] ) )
        { prom = 0; }
      else
        { return MOVE_NULL; }
      cap_type = _board->get_turn() ? _board->get_piece_on_sq_w( to ) : _board->get_piece_on_sq_b( to );
      if( cap_type == no_piece )
        {
          cap = 0;
          cap_type = 0;
        }
      else
        {
          cap = 1;
        }
    }

  *_promote = (double)prom;
  if (cap_type == 0) {
    *_cap = "";
  } else {
    *_cap = _ch_piece_csa[cap_type & ~mask_piece_color];
  }

  if (_board->is_attack(&attack_pieces)) {
    *_isAttack = 1;
  } else {
    *_isAttack = 0;
  }
  *_color = _board->turn();

  move = MOVE_C( type_c, from, to, prom, cap, cap_type );
  _board->make_move( move );
  return move;

}

double Game::search(double *_fromX, double *_fromY, double *_toX, double *_toY, std::string *_piece, double *_color, double *_promote, std::string* _capture, double *_isAttack){
  double search_time = _search->search_root();
  unsigned int move = _board->history[ _board->get_nply() - 1].move;
  unsigned int attack_pieces;
  int type_c = MOVE_TYPE( move );
  int type   = type_c & ~mask_piece_color;
  int from   = MOVE_FROM( move );
  int to     = MOVE_TO( move );
  int prom   = MOVE_PROMOTE( move );
  int cap    = MOVE_CAPTURE( move );
  int cap_type = MOVE_CAPTURED_TYPE( move );

  if(cap_type > 16){
    cap_type -= 16;
  }

  if( from == move_drop ) {
    *_fromX = 0;
    *_fromY = 0;
    *_toX = 5-( to%5 );
    *_toY = ( to/5 +1);
  } else {
    *_fromX = 5-( from%5 );
    *_fromY = ( from/5 +1);
    *_toX = 5-( to%5 );
    *_toY = ( to/5 +1);
  }

  *_promote = (double)prom;

  if( prom ) {
    *_piece = _ch_piece_csa[ type + m_promote ];
  } else {
    *_piece = _ch_piece_csa[ type ];
  }

  if (cap) {
    *_capture = _ch_piece_csa[ cap_type ];
  }else {
    *_capture = "";
  }
  if (_board->is_attack(&attack_pieces)) {
    *_isAttack = 1;
  } else {
    *_isAttack = 0;
  }

  *_color = _board->turn();
  return search_time;
}

void Game::legal(const unsigned int& lmove, double *_fromX, double *_fromY, double *_toX, double *_toY, std::string *_piece) {
  int type_c = MOVE_TYPE( lmove );
  int type   = type_c & ~mask_piece_color;
  int from   = MOVE_FROM( lmove );
  int to     = MOVE_TO( lmove );
  int prom   = MOVE_PROMOTE( lmove );

  if( from == move_drop ) {
    *_fromX = 0;
    *_fromY = 0;
    *_toX = 5-( to%5 );
    *_toY = ( to/5 +1);
  } else {
    *_fromX = 5-( from%5 );
    *_fromY = ( from/5 +1);
    *_toX = 5-( to%5 );
    *_toY = ( to/5 +1);
  }

  if (prom) {
    *_piece = _ch_piece_csa[ type + m_promote ];
  }else {
    *_piece = _ch_piece_csa[ type ];
  }
}
<|endoftext|>


<commit_before>/*
Copyright 2013-present Barefoot Networks, 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.
*/

#include "bitvec.h"
#include "hex.h"
#include "lib/exceptions.h"

std::ostream &operator<<(std::ostream &os, const bitvec &bv) {
    if (bv.size == 1) {
        os << hex(bv.data);
    } else {
        bool first = true;
        for (int i = bv.size-1; i >= 0; i--) {
            if (first) {
                if (!bv.ptr[i]) continue;
                os << hex(bv.ptr[i]);
                first = false;
            } else {
                os << hex(bv.ptr[i], sizeof(bv.data)*2, '0'); } }
        if (first)
            os << '0';
    }
    return os;
}

bitvec &bitvec::operator>>=(size_t count) {
    if (size == 1) {
        if (count >= bits_per_unit)
            data = 0;
        else
            data >>= count;
        return *this; }
    int off = count / bits_per_unit;
    count %= bits_per_unit;
    for (size_t i = 0; i < size; i++)
        if (i + off < size) {
            ptr[i] = ptr[i+off] >> count;
            if (count && i + off + 1 < size)
                ptr[i] |= ptr[i+off+1] << (bits_per_unit - count);
        } else {
            ptr[i] = 0; }
    while (size > 1 && !ptr[size-1]) size--;
    if (size == 1) {
        auto tmp = ptr[0];
        delete [] ptr;
        data = tmp; }
    return *this;
}

bitvec &bitvec::operator<<=(size_t count) {
    size_t needsize = (max().index() + count + bits_per_unit)/bits_per_unit;
    if (needsize > size) expand(needsize);
    if (size == 1) {
        data <<= count;
        return *this; }
    int off = count / bits_per_unit;
    count %= bits_per_unit;
    for (int i = size-1; i >= 0; i--)
        if (i >= off) {
            ptr[i] = ptr[i-off] << count;
            if (count && i > off)
                ptr[i] |= ptr[i-off-1] >> (bits_per_unit - count);
        } else {
            ptr[i] = 0; }
    return *this;
}

bitvec bitvec::getslice(size_t idx, size_t sz) const {
    if (sz == 0) return bitvec();
    if (idx >= size * bits_per_unit) return bitvec();
    if (idx + sz > size * bits_per_unit)
        sz = size * bits_per_unit - idx;
    if (size > 1) {
        bitvec rv;
        unsigned shift = idx % bits_per_unit;
        idx /= bits_per_unit;
        if (sz > bits_per_unit) {
            rv.expand((sz-1)/bits_per_unit + 1);
            for (size_t i = 0; i < rv.size; i++) {
                if (shift != 0 && i != 0)
                    rv.ptr[i-1] |= ptr[idx + i] << (bits_per_unit - shift);
                rv.ptr[i] = ptr[idx + i] >> shift; }
            if ((sz %= bits_per_unit))
                rv.ptr[rv.size-1] &= ~(~static_cast<uintptr_t>(1) << (sz-1));
        } else {
            rv.data = ptr[idx] >> shift;
            if (shift != 0 && idx + 1 < size)
                rv.data |= ptr[idx + 1] << (bits_per_unit - shift);
            rv.data &= ~(~static_cast<uintptr_t>(1) << (sz-1)); }
        return rv;
    } else {
        return bitvec((data >> idx) & ~(~static_cast<uintptr_t>(1) << (sz-1))); }
}

int bitvec::ffs(unsigned start) const {
    uintptr_t val = ~static_cast<uintptr_t>(0);
    unsigned idx = start / bits_per_unit;
    val <<= (start % bits_per_unit);
    while (idx < size && !(val &= word(idx))) {
        ++idx;
        val = ~static_cast<uintptr_t>(0); }
    if (idx >= size) return -1;
    unsigned rv = idx * bits_per_unit;
#if defined(__GNUC__) || defined(__clang__)
    rv += builtin_ctz(val);
#else
    while ((val & 0xff) == 0) { rv += 8; val >>= 8; }
    while ((val & 1) == 0) { rv++; val >>= 1; }
#endif
    return rv;
}

unsigned bitvec::ffz(unsigned start) const {
    uintptr_t val = static_cast<uintptr_t>(0);
    unsigned idx = start / bits_per_unit;
    val = ~(~val << (start % bits_per_unit));
    while (!~(val |= word(idx))) {
        ++idx;
        val = 0; }
    unsigned rv = idx * bits_per_unit;
#if defined(__GNUC__) || defined(__clang__)
    rv += builtin_ctz(~val);
#else
    while ((val & 0xff) == 0xff) { rv += 8; val >>= 8; }
    while (val & 1) { rv++; val >>= 1; }
#endif
    return rv;
}

bool bitvec::is_contiguous() const {
    // Empty bitvec is not contiguous
    if (empty())
        return false;
    return max().index() - min().index() + 1 == popcount();
}

bitvec bitvec::rotate_right_helper(size_t start_bit, size_t rotation_idx, size_t end_bit) const {
    BUG_CHECK(start_bit <= rotation_idx && rotation_idx < end_bit, "Invalid rotation on bitvec, as "
              "rotation_idx does not fall between start_bit and end_bit");
    bitvec rot_mask(start_bit, end_bit - start_bit);
    bitvec rotation_section = *this & rot_mask;
    int down_shift = rotation_idx - start_bit;
    int up_shift = (end_bit - start_bit) - down_shift;
    bitvec rv;
    rv = (rotation_section >> down_shift) | (rotation_section << up_shift);
    return rv & rot_mask;
}

/**
 * Designed to imitate the std::rotate/std::rotate_copy function for vectors.  Return a bitvec
 * which has the bit at rotation_idx appear at start_bit, and the corresponding data
 * between start_bit and end_bit rotated.  Similar to std::rotate/std::rotate_copy, end_bit is
 * exclusive
 */
void bitvec::rotate_right(size_t start_bit, size_t rotation_idx, size_t end_bit) {
    bitvec rot_section = rotate_right_helper(start_bit, rotation_idx, end_bit);
    clrrange(start_bit, end_bit - start_bit);
    *this |= rot_section;
}

bitvec bitvec::rotate_right_copy(size_t start_bit, size_t rotation_idx, size_t end_bit) const {
    bitvec rot_section = rotate_right_helper(start_bit, rotation_idx, end_bit);
    bitvec rot_mask(start_bit, end_bit - start_bit);
    bitvec rv = rot_section | (*this - rot_mask);
    return rv;
}
<commit_msg>Move a message to assert (#1680)<commit_after>/*
Copyright 2013-present Barefoot Networks, 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.
*/

#include "bitvec.h"
#include "hex.h"

std::ostream &operator<<(std::ostream &os, const bitvec &bv) {
    if (bv.size == 1) {
        os << hex(bv.data);
    } else {
        bool first = true;
        for (int i = bv.size-1; i >= 0; i--) {
            if (first) {
                if (!bv.ptr[i]) continue;
                os << hex(bv.ptr[i]);
                first = false;
            } else {
                os << hex(bv.ptr[i], sizeof(bv.data)*2, '0'); } }
        if (first)
            os << '0';
    }
    return os;
}

bitvec &bitvec::operator>>=(size_t count) {
    if (size == 1) {
        if (count >= bits_per_unit)
            data = 0;
        else
            data >>= count;
        return *this; }
    int off = count / bits_per_unit;
    count %= bits_per_unit;
    for (size_t i = 0; i < size; i++)
        if (i + off < size) {
            ptr[i] = ptr[i+off] >> count;
            if (count && i + off + 1 < size)
                ptr[i] |= ptr[i+off+1] << (bits_per_unit - count);
        } else {
            ptr[i] = 0; }
    while (size > 1 && !ptr[size-1]) size--;
    if (size == 1) {
        auto tmp = ptr[0];
        delete [] ptr;
        data = tmp; }
    return *this;
}

bitvec &bitvec::operator<<=(size_t count) {
    size_t needsize = (max().index() + count + bits_per_unit)/bits_per_unit;
    if (needsize > size) expand(needsize);
    if (size == 1) {
        data <<= count;
        return *this; }
    int off = count / bits_per_unit;
    count %= bits_per_unit;
    for (int i = size-1; i >= 0; i--)
        if (i >= off) {
            ptr[i] = ptr[i-off] << count;
            if (count && i > off)
                ptr[i] |= ptr[i-off-1] >> (bits_per_unit - count);
        } else {
            ptr[i] = 0; }
    return *this;
}

bitvec bitvec::getslice(size_t idx, size_t sz) const {
    if (sz == 0) return bitvec();
    if (idx >= size * bits_per_unit) return bitvec();
    if (idx + sz > size * bits_per_unit)
        sz = size * bits_per_unit - idx;
    if (size > 1) {
        bitvec rv;
        unsigned shift = idx % bits_per_unit;
        idx /= bits_per_unit;
        if (sz > bits_per_unit) {
            rv.expand((sz-1)/bits_per_unit + 1);
            for (size_t i = 0; i < rv.size; i++) {
                if (shift != 0 && i != 0)
                    rv.ptr[i-1] |= ptr[idx + i] << (bits_per_unit - shift);
                rv.ptr[i] = ptr[idx + i] >> shift; }
            if ((sz %= bits_per_unit))
                rv.ptr[rv.size-1] &= ~(~static_cast<uintptr_t>(1) << (sz-1));
        } else {
            rv.data = ptr[idx] >> shift;
            if (shift != 0 && idx + 1 < size)
                rv.data |= ptr[idx + 1] << (bits_per_unit - shift);
            rv.data &= ~(~static_cast<uintptr_t>(1) << (sz-1)); }
        return rv;
    } else {
        return bitvec((data >> idx) & ~(~static_cast<uintptr_t>(1) << (sz-1))); }
}

int bitvec::ffs(unsigned start) const {
    uintptr_t val = ~static_cast<uintptr_t>(0);
    unsigned idx = start / bits_per_unit;
    val <<= (start % bits_per_unit);
    while (idx < size && !(val &= word(idx))) {
        ++idx;
        val = ~static_cast<uintptr_t>(0); }
    if (idx >= size) return -1;
    unsigned rv = idx * bits_per_unit;
#if defined(__GNUC__) || defined(__clang__)
    rv += builtin_ctz(val);
#else
    while ((val & 0xff) == 0) { rv += 8; val >>= 8; }
    while ((val & 1) == 0) { rv++; val >>= 1; }
#endif
    return rv;
}

unsigned bitvec::ffz(unsigned start) const {
    uintptr_t val = static_cast<uintptr_t>(0);
    unsigned idx = start / bits_per_unit;
    val = ~(~val << (start % bits_per_unit));
    while (!~(val |= word(idx))) {
        ++idx;
        val = 0; }
    unsigned rv = idx * bits_per_unit;
#if defined(__GNUC__) || defined(__clang__)
    rv += builtin_ctz(~val);
#else
    while ((val & 0xff) == 0xff) { rv += 8; val >>= 8; }
    while (val & 1) { rv++; val >>= 1; }
#endif
    return rv;
}

bool bitvec::is_contiguous() const {
    // Empty bitvec is not contiguous
    if (empty())
        return false;
    return max().index() - min().index() + 1 == popcount();
}

bitvec bitvec::rotate_right_helper(size_t start_bit, size_t rotation_idx, size_t end_bit) const {
    assert(start_bit <= rotation_idx && rotation_idx < end_bit);
    bitvec rot_mask(start_bit, end_bit - start_bit);
    bitvec rotation_section = *this & rot_mask;
    int down_shift = rotation_idx - start_bit;
    int up_shift = (end_bit - start_bit) - down_shift;
    bitvec rv;
    rv = (rotation_section >> down_shift) | (rotation_section << up_shift);
    return rv & rot_mask;
}

/**
 * Designed to imitate the std::rotate/std::rotate_copy function for vectors.  Return a bitvec
 * which has the bit at rotation_idx appear at start_bit, and the corresponding data
 * between start_bit and end_bit rotated.  Similar to std::rotate/std::rotate_copy, end_bit is
 * exclusive
 */
void bitvec::rotate_right(size_t start_bit, size_t rotation_idx, size_t end_bit) {
    bitvec rot_section = rotate_right_helper(start_bit, rotation_idx, end_bit);
    clrrange(start_bit, end_bit - start_bit);
    *this |= rot_section;
}

bitvec bitvec::rotate_right_copy(size_t start_bit, size_t rotation_idx, size_t end_bit) const {
    bitvec rot_section = rotate_right_helper(start_bit, rotation_idx, end_bit);
    bitvec rot_mask(start_bit, end_bit - start_bit);
    bitvec rv = rot_section | (*this - rot_mask);
    return rv;
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2018 Georg Ziegler
//
// 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 <QtWidgets>

#include "Kitsune.hpp"
#include "KitsuneTab.hpp"
#include "KitsuneImage.hpp"
#include "ui_kitsuneui.h"

Kitsune::Kitsune(QWidget *parent) :
    QMainWindow(parent),
    tileData(new KitsuneTileData),      // helper class
    ui(new Ui::KitsuneUi)
{
    // set up UI. duh.
    ui->setupUi(this);
    this->setCentralWidget(ui->centralWidget);  // set central widget
    ui->centralWidget->hide();

    connectActions();

    resize(QGuiApplication::primaryScreen()->availableSize() * 3/5);
}

Kitsune::~Kitsune()
{
    delete ui;
}

//------------------------------------------------------------------------------

static void initializeImageFileDialog(QFileDialog &dialog, QFileDialog::AcceptMode acceptMode)
{
    static bool firstDialog = true;

    // set standard path to pictures location, else to execution path
    if (firstDialog) {
        firstDialog = false;
        const QStringList picturesLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
        dialog.setDirectory(picturesLocations.isEmpty() ? QDir::currentPath() : picturesLocations.last());
    }

    QStringList mimeTypeFilters;
    const QByteArrayList supportedMimeTypes =
        acceptMode == QFileDialog::AcceptOpen
        ? QImageReader::supportedMimeTypes() : QImageWriter::supportedMimeTypes();

    foreach (const QByteArray &mimeTypeName, supportedMimeTypes)
        mimeTypeFilters.append(mimeTypeName);

    mimeTypeFilters.sort();
    dialog.setMimeTypeFilters(mimeTypeFilters);
    dialog.selectMimeTypeFilter("image/png");
    if (acceptMode == QFileDialog::AcceptSave)
        dialog.setDefaultSuffix("png");
}

//------------------------------------------------------------------------------
// private slot members
//------------------------------------------------------------------------------
/*!
 *  This method opens a new file in a new tab. It will create a new KitsuneTab
 *  object and adds it to the tabList, that keeps track of all tabs currently
 *  opened.
 */
void Kitsune::openImage()
{
    // create new Tab
    tabList << new KitsuneTab(this);
    // get file name and open it
    QFileDialog dialog(this, tr("Open File"));
    initializeImageFileDialog(dialog, QFileDialog::AcceptOpen);

    while (dialog.exec() == QDialog::Accepted && !tabList.last()->loadTabContent(dialog.selectedFiles().first())) {}

    // make latest tab visible, change title
    ui->imageTabs->insertTab(ui->imageTabs->currentIndex() + 1, // insert after current tab
                             tabList.last(),                    // add newly opened tab
                             tabList.last()->getFileName());    // tab text is file name
    ui->imageTabs->setCurrentWidget(tabList.last());            // change focus to new tab
    ui->centralWidget->show();                                  // make tab widget visible
}

//------------------------------------------------------------------------------
/*!
 *  This method will save the image in the current tab in any supported file
 *  format.
 */
void Kitsune::saveAsImage()
{
    // kitImage->saveFile(QFileDialog::getSaveFileName(this, tr("Save File As"), ""));
}

//------------------------------------------------------------------------------
/*!
 *  This method will open a dialog to export the color palette in BGR555 format
 *  in a .pal file that can be used in a SNES program. The export itseld is
 *  handled by KitsuneTileData.
 */
void Kitsune::exportPalette()
{
    // BUG: check for empty tabList
    // Get the current tab's image and export its palette
    KitsuneImage *image = tabList[ui->imageTabs->currentIndex()]->getTabContent();
    tileData->exportPalette(image->getImage().colorTable());
}

//------------------------------------------------------------------------------
/*!
 *  This method opens a dialog to export a tile set of the current tab. The
 *  export itself is handled by KitsuneTileData.
 */
void Kitsune::exportTileSet()
{
    // BUG: check for empty tabList
    // Get the current tab's image and export its tile set
    KitsuneImage *image = tabList[ui->imageTabs->currentIndex()]->getTabContent();
    tileData->exportTileSet(image->getImage());
}

//------------------------------------------------------------------------------
/*!
 *  Changes the bit format for the current tab. This controls what format
 *  the export functions of KitsuneTileData will use.
 */
void Kitsune::setBitFormat()
{
    if(QObject::sender()->objectName() == "_2bppAct")
    {
        ui->_2bppAct->setChecked(true);
        ui->_4bppAct->setChecked(false);
        tileData->setBitFormat(BitFormats::_2bpp);
    }

    if(QObject::sender()->objectName() == "_4bppAct")
    {
        ui->_2bppAct->setChecked(false);
        ui->_4bppAct->setChecked(true);
        tileData->setBitFormat(BitFormats::_4bpp);
    }
}

//------------------------------------------------------------------------------
/*!
 *  Resets the scale factor of the current tab.
 */
void Kitsune::zoomReset()
{
    // reset scale factor of current tab to 1.0
    KitsuneTab *currentTab = tabList[ui->imageTabs->currentIndex()];
    currentTab->setScaleFactor(1.0);
}

//------------------------------------------------------------------------------
/*!
 *  Increases the scale factor of the current tab.
 */
void Kitsune::zoomIn()
{
    // increase scale factor for current tab by 0.1
    KitsuneTab *currentTab = tabList[ui->imageTabs->currentIndex()];
    currentTab->setScaleFactor(currentTab->getScaleFactor() + 0.1);
}

//------------------------------------------------------------------------------
/*!
 *  Decreases the scale factor of the current tab.
 */
void Kitsune::zoomOut()
{
    // decrease scale factor for current tab by 0.1
    // tabList[ui->imageTabs->currentIndex()]->scaleContent(1.0);
    KitsuneTab *currentTab = tabList[ui->imageTabs->currentIndex()];
    currentTab->setScaleFactor(currentTab->getScaleFactor() - 0.1);
}

//------------------------------------------------------------------------------
/*!
 *  Displays information about the application in a dialog.
 */
void Kitsune::about()
{
    QMessageBox::about(this, tr("About Kitsune"),
            tr("
Kitsune 0.2.4-preAlpha
 "
               "
Pixel Graphic and Tilemap Tool for SNES Developers
"
               "
License: 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.
"
               "
Copyright (C) 2018 Georg Ziegler
"));
}

//------------------------------------------------------------------------------
/*!
 *  Invoked whenever a tab is closed.
 */
void Kitsune::closeTab(int tabIndex)
{
    ui->imageTabs->removeTab(tabIndex);
}

//------------------------------------------------------------------------------
/*!
 *  Connects all actions of KitsuneUi to members of the Kitsune class.
 */
void Kitsune::connectActions()
{
    // File menu
    connect(ui->openAct, &QAction::triggered, this, &Kitsune::openImage);
    connect(ui->saveAsAct, &QAction::triggered, this, &Kitsune::saveAsImage);
    connect(ui->exitAct, &QAction::triggered, this, &QWidget::close);
    // Image menu
    connect(ui->expPaletteAct, &QAction::triggered, this, &Kitsune::exportPalette);
    connect(ui->expTileSetAct, &QAction::triggered, this, &Kitsune::exportTileSet);
    // Settings menu
    connect(ui->_2bppAct, &QAction::triggered, this, &Kitsune::setBitFormat);
    connect(ui->_4bppAct, &QAction::triggered, this, &Kitsune::setBitFormat);
    // View menu
    connect(ui->zoomResetAct, &QAction::triggered, this, &Kitsune::zoomReset);
    connect(ui->zoomInAct, &QAction::triggered, this, &Kitsune::zoomIn);
    connect(ui->zoomOutAct, &QAction::triggered, this, &Kitsune::zoomOut);
    // About menu
    connect(ui->aboutAct, &QAction::triggered, this, &Kitsune::about);
    connect(ui->aboutQtAct, &QAction::triggered, this, &QApplication::aboutQt);
    // Tab functions
    connect(ui->imageTabs, &QTabWidget::tabCloseRequested, this, &Kitsune::closeTab);
}
<commit_msg>Fixes #1<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2018 Georg Ziegler
//
// 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 <QtWidgets>

#include "Kitsune.hpp"
#include "KitsuneTab.hpp"
#include "KitsuneImage.hpp"
#include "ui_kitsuneui.h"

Kitsune::Kitsune(QWidget *parent) :
    QMainWindow(parent),
    tileData(new KitsuneTileData),      // helper class
    ui(new Ui::KitsuneUi)
{
    // set up UI. duh.
    ui->setupUi(this);
    this->setCentralWidget(ui->centralWidget);  // set central widget
    ui->centralWidget->hide();

    connectActions();

    resize(QGuiApplication::primaryScreen()->availableSize() * 3/5);
}

Kitsune::~Kitsune()
{
    delete ui;
}

//------------------------------------------------------------------------------

static void initializeImageFileDialog(QFileDialog &dialog, QFileDialog::AcceptMode acceptMode)
{
    static bool firstDialog = true;

    // set standard path to pictures location, else to execution path
    if (firstDialog) {
        firstDialog = false;
        const QStringList picturesLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
        dialog.setDirectory(picturesLocations.isEmpty() ? QDir::currentPath() : picturesLocations.last());
    }

    QStringList mimeTypeFilters;
    const QByteArrayList supportedMimeTypes =
        acceptMode == QFileDialog::AcceptOpen
        ? QImageReader::supportedMimeTypes() : QImageWriter::supportedMimeTypes();

    foreach (const QByteArray &mimeTypeName, supportedMimeTypes)
        mimeTypeFilters.append(mimeTypeName);

    mimeTypeFilters.sort();
    dialog.setMimeTypeFilters(mimeTypeFilters);
    dialog.selectMimeTypeFilter("image/png");
    if (acceptMode == QFileDialog::AcceptSave)
        dialog.setDefaultSuffix("png");
}

//------------------------------------------------------------------------------
// private slot members
//------------------------------------------------------------------------------
/*!
 *  This method opens a new file in a new tab. It will create a new KitsuneTab
 *  object and adds it to the tabList, that keeps track of all tabs currently
 *  opened.
 */
void Kitsune::openImage()
{
    // create new Tab
    tabList << new KitsuneTab(this);
    // get file name and open it
    QFileDialog dialog(this, tr("Open File"));
    initializeImageFileDialog(dialog, QFileDialog::AcceptOpen);
    QString fileName;
    if(dialog.exec() == QDialog::Accepted)
    {
        fileName = dialog.selectedFiles().first();
    }
    else
    {
        if(!tabList.isEmpty())       // sanity check
            tabList.removeLast();    // remove newly add tab
        return;                      // return to caller
    }

    if(tabList.last()->loadTabContent(fileName))    // check for errors while loading file
    {
        // make latest tab visible, change title
        ui->imageTabs->insertTab(ui->imageTabs->currentIndex() + 1, // insert after current tab
                                tabList.last(),                     // add newly opened tab
                                tabList.last()->getFileName());     // tab text is file name
        ui->imageTabs->setCurrentWidget(tabList.last());            // change focus to new tab
        ui->centralWidget->show();                                  // make tab widget visible
    }
}

//------------------------------------------------------------------------------
/*!
 *  This method will save the image in the current tab in any supported file
 *  format.
 */
void Kitsune::saveAsImage()
{
    // kitImage->saveFile(QFileDialog::getSaveFileName(this, tr("Save File As"), ""));
}

//------------------------------------------------------------------------------
/*!
 *  This method will open a dialog to export the color palette in BGR555 format
 *  in a .pal file that can be used in a SNES program. The export itseld is
 *  handled by KitsuneTileData.
 */
void Kitsune::exportPalette()
{
    // BUG: check for empty tabList
    // Get the current tab's image and export its palette
    KitsuneImage *image = tabList[ui->imageTabs->currentIndex()]->getTabContent();
    tileData->exportPalette(image->getImage().colorTable());
}

//------------------------------------------------------------------------------
/*!
 *  This method opens a dialog to export a tile set of the current tab. The
 *  export itself is handled by KitsuneTileData.
 */
void Kitsune::exportTileSet()
{
    // BUG: check for empty tabList
    // Get the current tab's image and export its tile set
    KitsuneImage *image = tabList[ui->imageTabs->currentIndex()]->getTabContent();
    tileData->exportTileSet(image->getImage());
}

//------------------------------------------------------------------------------
/*!
 *  Changes the bit format for the current tab. This controls what format
 *  the export functions of KitsuneTileData will use.
 */
void Kitsune::setBitFormat()
{
    if(QObject::sender()->objectName() == "_2bppAct")
    {
        ui->_2bppAct->setChecked(true);
        ui->_4bppAct->setChecked(false);
        tileData->setBitFormat(BitFormats::_2bpp);
    }

    if(QObject::sender()->objectName() == "_4bppAct")
    {
        ui->_2bppAct->setChecked(false);
        ui->_4bppAct->setChecked(true);
        tileData->setBitFormat(BitFormats::_4bpp);
    }
}

//------------------------------------------------------------------------------
/*!
 *  Resets the scale factor of the current tab.
 */
void Kitsune::zoomReset()
{
    // reset scale factor of current tab to 1.0
    KitsuneTab *currentTab = tabList[ui->imageTabs->currentIndex()];
    currentTab->setScaleFactor(1.0);
}

//------------------------------------------------------------------------------
/*!
 *  Increases the scale factor of the current tab.
 */
void Kitsune::zoomIn()
{
    // increase scale factor for current tab by 0.1
    KitsuneTab *currentTab = tabList[ui->imageTabs->currentIndex()];
    currentTab->setScaleFactor(currentTab->getScaleFactor() + 0.1);
}

//------------------------------------------------------------------------------
/*!
 *  Decreases the scale factor of the current tab.
 */
void Kitsune::zoomOut()
{
    // decrease scale factor for current tab by 0.1
    // tabList[ui->imageTabs->currentIndex()]->scaleContent(1.0);
    KitsuneTab *currentTab = tabList[ui->imageTabs->currentIndex()];
    currentTab->setScaleFactor(currentTab->getScaleFactor() - 0.1);
}

//------------------------------------------------------------------------------
/*!
 *  Displays information about the application in a dialog.
 */
void Kitsune::about()
{
    QMessageBox::about(this, tr("About Kitsune"),
            tr("
Kitsune 0.2.4-preAlpha
 "
               "
Pixel Graphic and Tilemap Tool for SNES Developers
"
               "
License: 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.
"
               "
Copyright (C) 2018 Georg Ziegler
"));
}

//------------------------------------------------------------------------------
/*!
 *  Invoked whenever a tab is closed.
 */
void Kitsune::closeTab(int tabIndex)
{
    ui->imageTabs->removeTab(tabIndex);
}

//------------------------------------------------------------------------------
/*!
 *  Connects all actions of KitsuneUi to members of the Kitsune class.
 */
void Kitsune::connectActions()
{
    // File menu
    connect(ui->openAct, &QAction::triggered, this, &Kitsune::openImage);
    connect(ui->saveAsAct, &QAction::triggered, this, &Kitsune::saveAsImage);
    connect(ui->exitAct, &QAction::triggered, this, &QWidget::close);
    // Image menu
    connect(ui->expPaletteAct, &QAction::triggered, this, &Kitsune::exportPalette);
    connect(ui->expTileSetAct, &QAction::triggered, this, &Kitsune::exportTileSet);
    // Settings menu
    connect(ui->_2bppAct, &QAction::triggered, this, &Kitsune::setBitFormat);
    connect(ui->_4bppAct, &QAction::triggered, this, &Kitsune::setBitFormat);
    // View menu
    connect(ui->zoomResetAct, &QAction::triggered, this, &Kitsune::zoomReset);
    connect(ui->zoomInAct, &QAction::triggered, this, &Kitsune::zoomIn);
    connect(ui->zoomOutAct, &QAction::triggered, this, &Kitsune::zoomOut);
    // About menu
    connect(ui->aboutAct, &QAction::triggered, this, &Kitsune::about);
    connect(ui->aboutQtAct, &QAction::triggered, this, &QApplication::aboutQt);
    // Tab functions
    connect(ui->imageTabs, &QTabWidget::tabCloseRequested, this, &Kitsune::closeTab);
}
<|endoftext|>


<commit_before>// Scintilla source code edit control
/** @file LexBaan.cxx
 ** Lexer for Baan.
 ** Based heavily on LexCPP.cxx
 **/
// Copyright 2001- by Vamsi Potluru <vamsi@who.net> & Praveen Ambekar <ambekarpraveen@yahoo.com>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"

static inline bool IsAWordChar(const int  ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch == '$' || ch == ':');
}

static inline bool IsAWordStart(const int ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '_');
}

static void ColouriseBaanDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
                            Accessor &styler) {

	WordList &keywords = *keywordlists[0];
	WordList &keywords2 = *keywordlists[1];
	bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0;

	if (initStyle == SCE_BAAN_STRINGEOL)	// Does not leak onto next line
		initStyle = SCE_BAAN_DEFAULT;

	int visibleChars = 0;

	StyleContext sc(startPos, length, initStyle, styler);

	for (; sc.More(); sc.Forward()) {

		if (sc.state == SCE_BAAN_OPERATOR) {
			sc.SetState(SCE_BAAN_DEFAULT);
		} else if (sc.state == SCE_BAAN_NUMBER) {
			if (!IsAWordChar(sc.ch)) {
				sc.SetState(SCE_BAAN_DEFAULT);
			}
		} else if (sc.state == SCE_BAAN_IDENTIFIER) {
			if (!IsAWordChar(sc.ch)) {
				char s[100];
				sc.GetCurrentLowered(s, sizeof(s));
				if (keywords.InList(s)) {
					sc.ChangeState(SCE_BAAN_WORD);
				} else if (keywords2.InList(s)) {
					sc.ChangeState(SCE_BAAN_WORD2);
				}
				sc.SetState(SCE_BAAN_DEFAULT);
			}
		} else if (sc.state == SCE_BAAN_PREPROCESSOR) {
			if (stylingWithinPreprocessor) {
				if (IsASpace(sc.ch)) {
					sc.SetState(SCE_BAAN_DEFAULT);
				}
			} else {
				if (sc.atLineEnd && (sc.chNext != '^')) {
					sc.SetState(SCE_BAAN_DEFAULT);
				}
			}
		} else if (sc.state == SCE_BAAN_COMMENT) {
			if (sc.atLineEnd) {
				sc.SetState(SCE_BAAN_DEFAULT);
			}
		} else if (sc.state == SCE_BAAN_COMMENTDOC) {
			if (sc.MatchIgnoreCase("enddllusage")) {
				for (unsigned int i = 0; i < 10; i++){
					sc.Forward();
				}
				sc.ForwardSetState(SCE_BAAN_DEFAULT);
			}
		} else if (sc.state == SCE_BAAN_STRING) {
			if (sc.ch == '\"') {
				sc.ForwardSetState(SCE_BAAN_DEFAULT);
			} else if ((sc.atLineEnd) && (sc.chNext != '^')) {
				sc.ChangeState(SCE_BAAN_STRINGEOL);
				sc.ForwardSetState(SCE_C_DEFAULT);
				visibleChars = 0;
			}
		}

		if (sc.state == SCE_BAAN_DEFAULT) {
			if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
				sc.SetState(SCE_BAAN_NUMBER);
			} else if (sc.MatchIgnoreCase("dllusage")){
					sc.SetState(SCE_BAAN_COMMENTDOC);
					do {
						sc.Forward();
					} while ((!sc.atLineEnd) && sc.More());
			} else if (IsAWordStart(sc.ch)) {
					sc.SetState(SCE_BAAN_IDENTIFIER);
			} else if (sc.Match('|')){
					sc.SetState(SCE_BAAN_COMMENT);
			} else if (sc.ch == '\"') {
				sc.SetState(SCE_BAAN_STRING);
			} else if (sc.ch == '#' && visibleChars == 0) {
				// Preprocessor commands are alone on their line
				sc.SetState(SCE_BAAN_PREPROCESSOR);
				// Skip whitespace between # and preprocessor word
				do {
					sc.Forward();
				} while (IsASpace(sc.ch) && sc.More());
			} else if (isoperator(static_cast<char>(sc.ch))) {
				sc.SetState(SCE_BAAN_OPERATOR);
			}
		}
		if (sc.atLineEnd) {
			// Reset states to begining of colourise so no surprises
			// if different sets of lines lexed.
			visibleChars = 0;
		}
		if (!IsASpace(sc.ch)) {
			visibleChars++;
		}
	}
	sc.Complete();
}

static void FoldBaanDoc(unsigned int startPos, int length, int initStyle, WordList *[],
                            Accessor &styler) {
	bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
	unsigned int endPos = startPos + length;
	int visibleChars = 0;
	int lineCurrent = styler.GetLine(startPos);
	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
	int levelCurrent = levelPrev;
	char chNext = styler[startPos];
	int styleNext = styler.StyleAt(startPos);
	int style = initStyle;
	for (unsigned int i = startPos; i < endPos; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);
		int stylePrev = style;
		style = styleNext;
		styleNext = styler.StyleAt(i + 1);
		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
		if (foldComment &&
			(style == SCE_BAAN_COMMENT || style == SCE_BAAN_COMMENTDOC)) {
			if (style != stylePrev) {
				levelCurrent++;
			} else if ((style != styleNext) && !atEOL) {
				// Comments don't end at end of line and the next character may be unstyled.
				levelCurrent--;
			}
		}
		if (style == SCE_BAAN_OPERATOR) {
			if (ch == '{') {
				levelCurrent++;
			} else if (ch == '}') {
				levelCurrent--;
			}
		}
		if (atEOL) {
			int lev = levelPrev;
			if (visibleChars == 0 && foldCompact)
				lev |= SC_FOLDLEVELWHITEFLAG;
			if ((levelCurrent > levelPrev) && (visibleChars > 0))
				lev |= SC_FOLDLEVELHEADERFLAG;
			if (lev != styler.LevelAt(lineCurrent)) {
				styler.SetLevel(lineCurrent, lev);
			}
			lineCurrent++;
			levelPrev = levelCurrent;
			visibleChars = 0;
		}
		if (!isspacechar(ch))
			visibleChars++;
	}
	// Fill in the real level of the next line, keeping the current flags as they will be filled in later
	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
	styler.SetLevel(lineCurrent, levelPrev | flagsNext);
}

const LexerModule lmBaan(SCLEX_BAAN, ColouriseBaanDoc, "baan", FoldBaanDoc);
<commit_msg>Made LexerModule objects not be const again as that was impairing static linking.<commit_after>// Scintilla source code edit control
/** @file LexBaan.cxx
 ** Lexer for Baan.
 ** Based heavily on LexCPP.cxx
 **/
// Copyright 2001- by Vamsi Potluru <vamsi@who.net> & Praveen Ambekar <ambekarpraveen@yahoo.com>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"

static inline bool IsAWordChar(const int  ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch == '$' || ch == ':');
}

static inline bool IsAWordStart(const int ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '_');
}

static void ColouriseBaanDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
                            Accessor &styler) {

	WordList &keywords = *keywordlists[0];
	WordList &keywords2 = *keywordlists[1];
	bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0;

	if (initStyle == SCE_BAAN_STRINGEOL)	// Does not leak onto next line
		initStyle = SCE_BAAN_DEFAULT;

	int visibleChars = 0;

	StyleContext sc(startPos, length, initStyle, styler);

	for (; sc.More(); sc.Forward()) {

		if (sc.state == SCE_BAAN_OPERATOR) {
			sc.SetState(SCE_BAAN_DEFAULT);
		} else if (sc.state == SCE_BAAN_NUMBER) {
			if (!IsAWordChar(sc.ch)) {
				sc.SetState(SCE_BAAN_DEFAULT);
			}
		} else if (sc.state == SCE_BAAN_IDENTIFIER) {
			if (!IsAWordChar(sc.ch)) {
				char s[100];
				sc.GetCurrentLowered(s, sizeof(s));
				if (keywords.InList(s)) {
					sc.ChangeState(SCE_BAAN_WORD);
				} else if (keywords2.InList(s)) {
					sc.ChangeState(SCE_BAAN_WORD2);
				}
				sc.SetState(SCE_BAAN_DEFAULT);
			}
		} else if (sc.state == SCE_BAAN_PREPROCESSOR) {
			if (stylingWithinPreprocessor) {
				if (IsASpace(sc.ch)) {
					sc.SetState(SCE_BAAN_DEFAULT);
				}
			} else {
				if (sc.atLineEnd && (sc.chNext != '^')) {
					sc.SetState(SCE_BAAN_DEFAULT);
				}
			}
		} else if (sc.state == SCE_BAAN_COMMENT) {
			if (sc.atLineEnd) {
				sc.SetState(SCE_BAAN_DEFAULT);
			}
		} else if (sc.state == SCE_BAAN_COMMENTDOC) {
			if (sc.MatchIgnoreCase("enddllusage")) {
				for (unsigned int i = 0; i < 10; i++){
					sc.Forward();
				}
				sc.ForwardSetState(SCE_BAAN_DEFAULT);
			}
		} else if (sc.state == SCE_BAAN_STRING) {
			if (sc.ch == '\"') {
				sc.ForwardSetState(SCE_BAAN_DEFAULT);
			} else if ((sc.atLineEnd) && (sc.chNext != '^')) {
				sc.ChangeState(SCE_BAAN_STRINGEOL);
				sc.ForwardSetState(SCE_C_DEFAULT);
				visibleChars = 0;
			}
		}

		if (sc.state == SCE_BAAN_DEFAULT) {
			if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
				sc.SetState(SCE_BAAN_NUMBER);
			} else if (sc.MatchIgnoreCase("dllusage")){
					sc.SetState(SCE_BAAN_COMMENTDOC);
					do {
						sc.Forward();
					} while ((!sc.atLineEnd) && sc.More());
			} else if (IsAWordStart(sc.ch)) {
					sc.SetState(SCE_BAAN_IDENTIFIER);
			} else if (sc.Match('|')){
					sc.SetState(SCE_BAAN_COMMENT);
			} else if (sc.ch == '\"') {
				sc.SetState(SCE_BAAN_STRING);
			} else if (sc.ch == '#' && visibleChars == 0) {
				// Preprocessor commands are alone on their line
				sc.SetState(SCE_BAAN_PREPROCESSOR);
				// Skip whitespace between # and preprocessor word
				do {
					sc.Forward();
				} while (IsASpace(sc.ch) && sc.More());
			} else if (isoperator(static_cast<char>(sc.ch))) {
				sc.SetState(SCE_BAAN_OPERATOR);
			}
		}
		if (sc.atLineEnd) {
			// Reset states to begining of colourise so no surprises
			// if different sets of lines lexed.
			visibleChars = 0;
		}
		if (!IsASpace(sc.ch)) {
			visibleChars++;
		}
	}
	sc.Complete();
}

static void FoldBaanDoc(unsigned int startPos, int length, int initStyle, WordList *[],
                            Accessor &styler) {
	bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
	unsigned int endPos = startPos + length;
	int visibleChars = 0;
	int lineCurrent = styler.GetLine(startPos);
	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
	int levelCurrent = levelPrev;
	char chNext = styler[startPos];
	int styleNext = styler.StyleAt(startPos);
	int style = initStyle;
	for (unsigned int i = startPos; i < endPos; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);
		int stylePrev = style;
		style = styleNext;
		styleNext = styler.StyleAt(i + 1);
		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
		if (foldComment &&
			(style == SCE_BAAN_COMMENT || style == SCE_BAAN_COMMENTDOC)) {
			if (style != stylePrev) {
				levelCurrent++;
			} else if ((style != styleNext) && !atEOL) {
				// Comments don't end at end of line and the next character may be unstyled.
				levelCurrent--;
			}
		}
		if (style == SCE_BAAN_OPERATOR) {
			if (ch == '{') {
				levelCurrent++;
			} else if (ch == '}') {
				levelCurrent--;
			}
		}
		if (atEOL) {
			int lev = levelPrev;
			if (visibleChars == 0 && foldCompact)
				lev |= SC_FOLDLEVELWHITEFLAG;
			if ((levelCurrent > levelPrev) && (visibleChars > 0))
				lev |= SC_FOLDLEVELHEADERFLAG;
			if (lev != styler.LevelAt(lineCurrent)) {
				styler.SetLevel(lineCurrent, lev);
			}
			lineCurrent++;
			levelPrev = levelCurrent;
			visibleChars = 0;
		}
		if (!isspacechar(ch))
			visibleChars++;
	}
	// Fill in the real level of the next line, keeping the current flags as they will be filled in later
	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
	styler.SetLevel(lineCurrent, levelPrev | flagsNext);
}

LexerModule lmBaan(SCLEX_BAAN, ColouriseBaanDoc, "baan", FoldBaanDoc);
<|endoftext|>