<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//

#include "base/logging.h"
#include "base/sha2.h"
#include "chrome/browser/safe_browsing/safe_browsing_util.h"
#include "googleurl/src/gurl.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {
  bool VectorContains(const std::vector<std::string>& data,
                      const std::string& str) {
    for (size_t i = 0; i < data.size(); ++i) {
      if (data[i] == str)
        return true;
    }

    return false;
  }

SBFullHash CreateFullHash(SBPrefix prefix) {
  SBFullHash result;
  memset(&result, 0, sizeof(result));
  memcpy(&result, &prefix, sizeof(result));
  return result;
}
}

// Tests that we generate the required host/path combinations for testing
// according to the Safe Browsing spec.
// See section 6.2 in
// http://code.google.com/p/google-safe-browsing/wiki/Protocolv2Spec.
TEST(SafeBrowsingUtilTest, UrlParsing) {
  std::vector<std::string> hosts, paths;

  GURL url("http://a.b.c/1/2.html?param=1");
  safe_browsing_util::GenerateHostsToCheck(url, &hosts);
  safe_browsing_util::GeneratePathsToCheck(url, &paths);
  EXPECT_EQ(hosts.size(), 2);
  EXPECT_EQ(paths.size(), 4);
  EXPECT_EQ(hosts[0], "b.c");
  EXPECT_EQ(hosts[1], "a.b.c");

  EXPECT_TRUE(VectorContains(paths, "/1/2.html?param=1"));
  EXPECT_TRUE(VectorContains(paths, "/1/2.html"));
  EXPECT_TRUE(VectorContains(paths, "/1/"));
  EXPECT_TRUE(VectorContains(paths, "/"));

  url = GURL("http://a.b.c.d.e.f.g/1.html");
  safe_browsing_util::GenerateHostsToCheck(url, &hosts);
  safe_browsing_util::GeneratePathsToCheck(url, &paths);
  EXPECT_EQ(hosts.size(), 5);
  EXPECT_EQ(paths.size(), 2);
  EXPECT_EQ(hosts[0], "f.g");
  EXPECT_EQ(hosts[1], "e.f.g");
  EXPECT_EQ(hosts[2], "d.e.f.g");
  EXPECT_EQ(hosts[3], "c.d.e.f.g");
  EXPECT_EQ(hosts[4], "a.b.c.d.e.f.g");
  EXPECT_TRUE(VectorContains(paths, "/1.html"));
  EXPECT_TRUE(VectorContains(paths, "/"));

  url = GURL("http://a.b/saw-cgi/eBayISAPI.dll/");
  safe_browsing_util::GeneratePathsToCheck(url, &paths);
  EXPECT_EQ(paths.size(), 3);
  EXPECT_TRUE(VectorContains(paths, "/saw-cgi/eBayISAPI.dll/"));
  EXPECT_TRUE(VectorContains(paths, "/saw-cgi/"));
  EXPECT_TRUE(VectorContains(paths, "/"));
}

TEST(SafeBrowsingUtilTest, FullHashCompare) {
  GURL url("http://www.evil.com/phish.html");
  SBFullHashResult full_hash;
  base::SHA256HashString(url.host() + url.path(),
                         &full_hash.hash,
                         sizeof(SBFullHash));
  std::vector<SBFullHashResult> full_hashes;
  full_hashes.push_back(full_hash);

  EXPECT_EQ(safe_browsing_util::CompareFullHashes(url, full_hashes), 0);

  url = GURL("http://www.evil.com/okay_path.html");
  EXPECT_EQ(safe_browsing_util::CompareFullHashes(url, full_hashes), -1);
}

// Checks the reading/writing code of the database information for a hostkey.
TEST(SafeBrowsing, HostInfo) {
  // Test a simple case of adding a prefix from scratch.
  SBEntry* entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1);
  entry->SetPrefixAt(0, 0x01000000);
  entry->set_list_id(1);
  entry->set_chunk_id(1);

  SBHostInfo info;
  info.AddPrefixes(entry);
  entry->Destroy();

  int list_id;
  std::vector<SBFullHash> full_hashes;
  full_hashes.push_back(CreateFullHash(0x01000000));
  std::vector<SBPrefix> prefix_hits;
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Test appending prefixes to an existing entry.
  entry = SBEntry::Create(SBEntry::ADD_PREFIX, 2);
  entry->SetPrefixAt(0, 0x02000000);
  entry->SetPrefixAt(1, 0x02000001);
  entry->set_list_id(1);
  entry->set_chunk_id(2);
  info.AddPrefixes(entry);
  entry->Destroy();

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x01000000));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x02000000));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x02000001));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Test removing the entire first entry.
  entry = SBEntry::Create(SBEntry::SUB_PREFIX, 0);
  entry->set_list_id(1);
  entry->set_chunk_id(1);
  info.RemovePrefixes(entry, false);
  entry->Destroy();

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x01000000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x02000000));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x02000001));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Test removing one prefix from the second entry.
  entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1);
  entry->SetPrefixAt(0,0x02000000);
  entry->SetChunkIdAtPrefix(0, 2);
  entry->set_list_id(1);
  info.RemovePrefixes(entry, false);
  entry->Destroy();

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x02000000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x02000001));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Test adding a sub that specifies a prefix before the add.
  entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1);
  entry->SetPrefixAt(0, 0x1000);
  entry->SetChunkIdAtPrefix(0, 100);
  entry->set_list_id(1);
  info.RemovePrefixes(entry, true);
  entry->Destroy();

  // Make sure we don't get a match from a sub.
  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x1000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Now add the prefixes.
  entry = SBEntry::Create(SBEntry::ADD_PREFIX, 3);
  entry->SetPrefixAt(0, 0x10000);
  entry->SetPrefixAt(1, 0x1000);
  entry->SetPrefixAt(2, 0x100000);
  entry->set_list_id(1);
  entry->set_chunk_id(100);
  info.AddPrefixes(entry);
  entry->Destroy();

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x10000));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x1000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x100000));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Now try adding a sub that deletes all prefixes from the chunk.
  entry = SBEntry::Create(SBEntry::SUB_PREFIX, 0);
  entry->set_list_id(1);
  entry->set_chunk_id(100);
  info.RemovePrefixes(entry, true);
  entry->Destroy();

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x10000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x100000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Add a sub for all prefixes before the add comes.
  entry = SBEntry::Create(SBEntry::SUB_PREFIX, 0);
  entry->set_list_id(1);
  entry->set_chunk_id(200);
  info.RemovePrefixes(entry, true);
  entry->Destroy();

  // Now add the prefixes.
  entry = SBEntry::Create(SBEntry::ADD_PREFIX, 3);
  entry->SetPrefixAt(0, 0x2000);
  entry->SetPrefixAt(1, 0x20000);
  entry->SetPrefixAt(2, 0x200000);
  entry->set_list_id(1);
  entry->set_chunk_id(200);
  info.AddPrefixes(entry);
  entry->Destroy();

  // None of the prefixes should be found.
  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x2000));
  full_hashes.push_back(CreateFullHash(0x20000));
  full_hashes.push_back(CreateFullHash(0x200000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));
}

// Checks that if we have a hostname blacklisted and we get a sub prefix, the
// hostname remains blacklisted.
TEST(SafeBrowsing, HostInfo2) {
  // Blacklist the entire hostname.
  SBEntry* entry = SBEntry::Create(SBEntry::ADD_PREFIX, 0);
  entry->set_list_id(1);
  entry->set_chunk_id(1);

  SBHostInfo info;
  info.AddPrefixes(entry);
  entry->Destroy();

  int list_id;
  std::vector<SBFullHash> full_hashes;
  full_hashes.push_back(CreateFullHash(0x01000000));
  std::vector<SBPrefix> prefix_hits;
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Now add a sub prefix.
  entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1);
  entry->SetPrefixAt(0, 0x02000000);
  entry->SetChunkIdAtPrefix(0, 2);
  entry->set_list_id(1);
  info.RemovePrefixes(entry, true);
  entry->Destroy();

  // Any prefix except the one removed should still be blocked.
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));
}
<commit_msg>Take out uneeded comment (and test public Rietveld instance!).<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/logging.h"
#include "base/sha2.h"
#include "chrome/browser/safe_browsing/safe_browsing_util.h"
#include "googleurl/src/gurl.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {
  bool VectorContains(const std::vector<std::string>& data,
                      const std::string& str) {
    for (size_t i = 0; i < data.size(); ++i) {
      if (data[i] == str)
        return true;
    }

    return false;
  }

SBFullHash CreateFullHash(SBPrefix prefix) {
  SBFullHash result;
  memset(&result, 0, sizeof(result));
  memcpy(&result, &prefix, sizeof(result));
  return result;
}
}

// Tests that we generate the required host/path combinations for testing
// according to the Safe Browsing spec.
// See section 6.2 in
// http://code.google.com/p/google-safe-browsing/wiki/Protocolv2Spec.
TEST(SafeBrowsingUtilTest, UrlParsing) {
  std::vector<std::string> hosts, paths;

  GURL url("http://a.b.c/1/2.html?param=1");
  safe_browsing_util::GenerateHostsToCheck(url, &hosts);
  safe_browsing_util::GeneratePathsToCheck(url, &paths);
  EXPECT_EQ(hosts.size(), 2);
  EXPECT_EQ(paths.size(), 4);
  EXPECT_EQ(hosts[0], "b.c");
  EXPECT_EQ(hosts[1], "a.b.c");

  EXPECT_TRUE(VectorContains(paths, "/1/2.html?param=1"));
  EXPECT_TRUE(VectorContains(paths, "/1/2.html"));
  EXPECT_TRUE(VectorContains(paths, "/1/"));
  EXPECT_TRUE(VectorContains(paths, "/"));

  url = GURL("http://a.b.c.d.e.f.g/1.html");
  safe_browsing_util::GenerateHostsToCheck(url, &hosts);
  safe_browsing_util::GeneratePathsToCheck(url, &paths);
  EXPECT_EQ(hosts.size(), 5);
  EXPECT_EQ(paths.size(), 2);
  EXPECT_EQ(hosts[0], "f.g");
  EXPECT_EQ(hosts[1], "e.f.g");
  EXPECT_EQ(hosts[2], "d.e.f.g");
  EXPECT_EQ(hosts[3], "c.d.e.f.g");
  EXPECT_EQ(hosts[4], "a.b.c.d.e.f.g");
  EXPECT_TRUE(VectorContains(paths, "/1.html"));
  EXPECT_TRUE(VectorContains(paths, "/"));

  url = GURL("http://a.b/saw-cgi/eBayISAPI.dll/");
  safe_browsing_util::GeneratePathsToCheck(url, &paths);
  EXPECT_EQ(paths.size(), 3);
  EXPECT_TRUE(VectorContains(paths, "/saw-cgi/eBayISAPI.dll/"));
  EXPECT_TRUE(VectorContains(paths, "/saw-cgi/"));
  EXPECT_TRUE(VectorContains(paths, "/"));
}

TEST(SafeBrowsingUtilTest, FullHashCompare) {
  GURL url("http://www.evil.com/phish.html");
  SBFullHashResult full_hash;
  base::SHA256HashString(url.host() + url.path(),
                         &full_hash.hash,
                         sizeof(SBFullHash));
  std::vector<SBFullHashResult> full_hashes;
  full_hashes.push_back(full_hash);

  EXPECT_EQ(safe_browsing_util::CompareFullHashes(url, full_hashes), 0);

  url = GURL("http://www.evil.com/okay_path.html");
  EXPECT_EQ(safe_browsing_util::CompareFullHashes(url, full_hashes), -1);
}

// Checks the reading/writing code of the database information for a hostkey.
TEST(SafeBrowsing, HostInfo) {
  // Test a simple case of adding a prefix from scratch.
  SBEntry* entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1);
  entry->SetPrefixAt(0, 0x01000000);
  entry->set_list_id(1);
  entry->set_chunk_id(1);

  SBHostInfo info;
  info.AddPrefixes(entry);
  entry->Destroy();

  int list_id;
  std::vector<SBFullHash> full_hashes;
  full_hashes.push_back(CreateFullHash(0x01000000));
  std::vector<SBPrefix> prefix_hits;
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Test appending prefixes to an existing entry.
  entry = SBEntry::Create(SBEntry::ADD_PREFIX, 2);
  entry->SetPrefixAt(0, 0x02000000);
  entry->SetPrefixAt(1, 0x02000001);
  entry->set_list_id(1);
  entry->set_chunk_id(2);
  info.AddPrefixes(entry);
  entry->Destroy();

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x01000000));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x02000000));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x02000001));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Test removing the entire first entry.
  entry = SBEntry::Create(SBEntry::SUB_PREFIX, 0);
  entry->set_list_id(1);
  entry->set_chunk_id(1);
  info.RemovePrefixes(entry, false);
  entry->Destroy();

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x01000000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x02000000));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x02000001));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Test removing one prefix from the second entry.
  entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1);
  entry->SetPrefixAt(0,0x02000000);
  entry->SetChunkIdAtPrefix(0, 2);
  entry->set_list_id(1);
  info.RemovePrefixes(entry, false);
  entry->Destroy();

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x02000000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x02000001));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Test adding a sub that specifies a prefix before the add.
  entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1);
  entry->SetPrefixAt(0, 0x1000);
  entry->SetChunkIdAtPrefix(0, 100);
  entry->set_list_id(1);
  info.RemovePrefixes(entry, true);
  entry->Destroy();

  // Make sure we don't get a match from a sub.
  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x1000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Now add the prefixes.
  entry = SBEntry::Create(SBEntry::ADD_PREFIX, 3);
  entry->SetPrefixAt(0, 0x10000);
  entry->SetPrefixAt(1, 0x1000);
  entry->SetPrefixAt(2, 0x100000);
  entry->set_list_id(1);
  entry->set_chunk_id(100);
  info.AddPrefixes(entry);
  entry->Destroy();

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x10000));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x1000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x100000));
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Now try adding a sub that deletes all prefixes from the chunk.
  entry = SBEntry::Create(SBEntry::SUB_PREFIX, 0);
  entry->set_list_id(1);
  entry->set_chunk_id(100);
  info.RemovePrefixes(entry, true);
  entry->Destroy();

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x10000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));

  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x100000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Add a sub for all prefixes before the add comes.
  entry = SBEntry::Create(SBEntry::SUB_PREFIX, 0);
  entry->set_list_id(1);
  entry->set_chunk_id(200);
  info.RemovePrefixes(entry, true);
  entry->Destroy();

  // Now add the prefixes.
  entry = SBEntry::Create(SBEntry::ADD_PREFIX, 3);
  entry->SetPrefixAt(0, 0x2000);
  entry->SetPrefixAt(1, 0x20000);
  entry->SetPrefixAt(2, 0x200000);
  entry->set_list_id(1);
  entry->set_chunk_id(200);
  info.AddPrefixes(entry);
  entry->Destroy();

  // None of the prefixes should be found.
  full_hashes.clear();
  full_hashes.push_back(CreateFullHash(0x2000));
  full_hashes.push_back(CreateFullHash(0x20000));
  full_hashes.push_back(CreateFullHash(0x200000));
  EXPECT_FALSE(info.Contains(full_hashes, &list_id, &prefix_hits));
}

// Checks that if we have a hostname blacklisted and we get a sub prefix, the
// hostname remains blacklisted.
TEST(SafeBrowsing, HostInfo2) {
  // Blacklist the entire hostname.
  SBEntry* entry = SBEntry::Create(SBEntry::ADD_PREFIX, 0);
  entry->set_list_id(1);
  entry->set_chunk_id(1);

  SBHostInfo info;
  info.AddPrefixes(entry);
  entry->Destroy();

  int list_id;
  std::vector<SBFullHash> full_hashes;
  full_hashes.push_back(CreateFullHash(0x01000000));
  std::vector<SBPrefix> prefix_hits;
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));

  // Now add a sub prefix.
  entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1);
  entry->SetPrefixAt(0, 0x02000000);
  entry->SetChunkIdAtPrefix(0, 2);
  entry->set_list_id(1);
  info.RemovePrefixes(entry, true);
  entry->Destroy();

  // Any prefix except the one removed should still be blocked.
  EXPECT_TRUE(info.Contains(full_hashes, &list_id, &prefix_hits));
}
<|endoftext|>


<commit_before>// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.

#include "UnrealEnginePythonPrivatePCH.h"

void unreal_engine_init_py_module();

#if defined(UNREAL_ENGINE_PYTHON_ON_LINUX)
const char *ue4_module_options = "linux_global_symbols";
#endif

#if PY_MAJOR_VERSION < 3
char *PyUnicode_AsUTF8(PyObject *py_str) {
	if (PyUnicode_Check(py_str)) {
		PyObject *unicode = PyUnicode_AsUTF8String(py_str);
		if (unicode) {
			return PyString_AsString(unicode);
		}
		// just a hack to avoid crashes
		return (char *)"<invalid_string>";
	}
	return PyString_AsString(py_str);
}

int PyGILState_Check() {
	PyThreadState * tstate = _PyThreadState_Current;
	return tstate && (tstate == PyGILState_GetThisThreadState());
}
#endif

bool PyUnicodeOrString_Check(PyObject *py_obj) {
	if (PyUnicode_Check(py_obj)) {
		return true;
	}
#if PY_MAJOR_VERSION < 3
	else if (PyString_Check(py_obj)) {
		return true;
	}
#endif
	return false;
}

#define LOCTEXT_NAMESPACE "FUnrealEnginePythonModule"

void FUnrealEnginePythonModule::PythonGILRelease() {
#if defined(UEPY_THREADING)
	if (PyGILState_Check() == 1) {
		ue_python_gil = PyEval_SaveThread();
	}
#endif
}

bool FUnrealEnginePythonModule::PythonGILAcquire() {
#if defined(UEPY_THREADING)
	if (PyGILState_Check() == 0) {
		PyEval_RestoreThread((PyThreadState *)ue_python_gil);
		return true;
	}
	return false;
#endif
	return true;
}

static void UESetupPythonInterpeter(bool verbose) {
	unreal_engine_init_py_module();

	PyObject *py_sys = PyImport_ImportModule("sys");
	PyObject *py_sys_dict = PyModule_GetDict(py_sys);

	PyObject *py_path = PyDict_GetItemString(py_sys_dict, "path");

	char *zip_path = TCHAR_TO_UTF8(*FPaths::Combine(*FPaths::GameContentDir(), UTF8_TO_TCHAR("ue_python.zip")));
	PyObject *py_zip_path = PyUnicode_FromString(zip_path);
	PyList_Insert(py_path, 0, py_zip_path);

	char *scripts_path = TCHAR_TO_UTF8(*FPaths::Combine(*FPaths::GameContentDir(), UTF8_TO_TCHAR("Scripts")));
	PyObject *py_scripts_path = PyUnicode_FromString(scripts_path);
	PyList_Insert(py_path, 0, py_scripts_path);

	if (verbose) {
		UE_LOG(LogPython, Log, TEXT("Python VM initialized: %s"), UTF8_TO_TCHAR(Py_GetVersion()));
		UE_LOG(LogPython, Log, TEXT("Python Scripts search path: %s"), UTF8_TO_TCHAR(scripts_path));
	}
}

void FUnrealEnginePythonModule::StartupModule()
{
	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module

	Py_Initialize();
#if PY_MAJOR_VERSION >= 3
	wchar_t *argv[] = { UTF8_TO_TCHAR("UnrealEngine"), NULL };
#else
	char *argv[] = { (char *)"UnrealEngine", NULL };
#endif
	PySys_SetArgv(1, argv);

	PyEval_InitThreads();

	UESetupPythonInterpeter(true);

	PyObject *main_module = PyImport_AddModule("__main__");
	main_dict = PyModule_GetDict(main_module);
	local_dict = main_dict;// PyDict_New();

	if (PyImport_ImportModule("ue_site")) {
		UE_LOG(LogPython, Log, TEXT("ue_site Python module successfully imported"));
	}
	else {
		// TODO gracefully manage the error
		unreal_engine_py_log_error();
	}

#if WITH_EDITOR
	// register commands (after importing ue_site)
	FPythonSlateCommands::Register();
	// apply extenders
	FPythonSlateCommands::ApplyPythonExtenders();
#endif

	// release the GIL
	PythonGILRelease();

}

void FUnrealEnginePythonModule::ShutdownModule()
{
	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
	// we call this function before unloading the module.

	UE_LOG(LogPython, Log, TEXT("Goodbye Python"));
	PythonGILAcquire();
	Py_Finalize();
}

void FUnrealEnginePythonModule::RunString(char *str) {
	FScopePythonGIL gil;
	PyObject *eval_ret = PyRun_String(str, Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict);
	if (!eval_ret) {
		unreal_engine_py_log_error();
		return;
	}
	Py_DECREF(eval_ret);
}

void FUnrealEnginePythonModule::RunFile(char *filename) {
	FScopePythonGIL gil;
	char *full_path = filename;
	if (!FPaths::FileExists(filename))
	{
		full_path = TCHAR_TO_UTF8(*FPaths::Combine(*FPaths::GameContentDir(), UTF8_TO_TCHAR("Scripts"), *FString("/"), UTF8_TO_TCHAR(filename)));
	}
#if PY_MAJOR_VERSION >= 3
	FILE *fd = nullptr;

#if PLATFORM_WINDOWS
	if (fopen_s(&fd, full_path, "r") != 0) {
		UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path));
		return;
	}
#else
	fd = fopen(full_path, "r");
	if (!fd) {
		UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path));
		return;
	}
#endif

	PyObject *eval_ret = PyRun_File(fd, full_path, Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict);
	fclose(fd);
	if (!eval_ret) {
		unreal_engine_py_log_error();
		return;
	}
	Py_DECREF(eval_ret);
#else
	// damn, this is horrible, but it is the only way i found to avoid the CRT error :(
	FString command = FString::Printf(TEXT("execfile(\"%s\")"), UTF8_TO_TCHAR(full_path));
	PyObject *eval_ret = PyRun_String(TCHAR_TO_UTF8(*command), Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict);
	if (!eval_ret) {
		unreal_engine_py_log_error();
		return;
	}
#endif

}

// run a python script in a new sub interpreter (useful for unit tests)
void FUnrealEnginePythonModule::RunFileSandboxed(char *filename) {
	FScopePythonGIL gil;
	char *full_path = filename;
	if (!FPaths::FileExists(filename))
	{
		full_path = TCHAR_TO_UTF8(*FPaths::Combine(*FPaths::GameContentDir(), UTF8_TO_TCHAR("Scripts"), *FString("/"), UTF8_TO_TCHAR(filename)));
	}
#if PY_MAJOR_VERSION >= 3
	FILE *fd = nullptr;

#if PLATFORM_WINDOWS
	if (fopen_s(&fd, full_path, "r") != 0) {
		UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path));
		return;
	}
#else
	fd = fopen(full_path, "r");
	if (!fd) {
		UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path));
		return;
	}
#endif

	PyThreadState *_main = PyThreadState_Get();

	PyThreadState *py_new_state = Py_NewInterpreter();
	if (!py_new_state) {
		UE_LOG(LogPython, Error, TEXT("Unable to create new Python interpreter"));
		return;
	}
	PyThreadState_Swap(nullptr);
	PyThreadState_Swap(py_new_state);

	UESetupPythonInterpeter(false);

	PyObject *m = PyImport_AddModule("__main__");
	if (m == NULL) {
		UE_LOG(LogPython, Error, TEXT("Unable to create new global dict"));
		Py_EndInterpreter(py_new_state);
		PyThreadState_Swap(_main);
		return;
	}
	PyObject *global_dict = PyModule_GetDict(m);

	PyObject *eval_ret = PyRun_File(fd, full_path, Py_file_input, global_dict, global_dict);
	fclose(fd);
	if (!eval_ret) {
		unreal_engine_py_log_error();
		Py_EndInterpreter(py_new_state);
		PyThreadState_Swap(_main);
		return;
	}
	Py_DECREF(eval_ret);
#else
	// damn, this is horrible, but it is the only way i found to avoid the CRT error :(
	FString command = FString::Printf(TEXT("execfile(\"%s\")"), UTF8_TO_TCHAR(full_path));
	PyObject *eval_ret = PyRun_String(TCHAR_TO_UTF8(*command), Py_file_input, global_dict, global_dict);
	if (!eval_ret) {
		unreal_engine_py_log_error();
		Py_EndInterpreter(py_new_state);
		PyThreadState_Swap(_main);
		return;
	}
#endif
	Py_EndInterpreter(py_new_state);
	PyThreadState_Swap(_main);
}

#undef LOCTEXT_NAMESPACE

IMPLEMENT_MODULE(FUnrealEnginePythonModule, UnrealEnginePython)

<commit_msg>Fixed the build on linux python2.7<commit_after>// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.

#include "UnrealEnginePythonPrivatePCH.h"

void unreal_engine_init_py_module();

#if defined(UNREAL_ENGINE_PYTHON_ON_LINUX)
const char *ue4_module_options = "linux_global_symbols";
#endif

#if PY_MAJOR_VERSION < 3
char *PyUnicode_AsUTF8(PyObject *py_str) {
	if (PyUnicode_Check(py_str)) {
		PyObject *unicode = PyUnicode_AsUTF8String(py_str);
		if (unicode) {
			return PyString_AsString(unicode);
		}
		// just a hack to avoid crashes
		return (char *)"<invalid_string>";
	}
	return PyString_AsString(py_str);
}

int PyGILState_Check() {
	PyThreadState * tstate = _PyThreadState_Current;
	return tstate && (tstate == PyGILState_GetThisThreadState());
}
#endif

bool PyUnicodeOrString_Check(PyObject *py_obj) {
	if (PyUnicode_Check(py_obj)) {
		return true;
	}
#if PY_MAJOR_VERSION < 3
	else if (PyString_Check(py_obj)) {
		return true;
	}
#endif
	return false;
}

#define LOCTEXT_NAMESPACE "FUnrealEnginePythonModule"

void FUnrealEnginePythonModule::PythonGILRelease() {
#if defined(UEPY_THREADING)
	if (PyGILState_Check() == 1) {
		ue_python_gil = PyEval_SaveThread();
	}
#endif
}

bool FUnrealEnginePythonModule::PythonGILAcquire() {
#if defined(UEPY_THREADING)
	if (PyGILState_Check() == 0) {
		PyEval_RestoreThread((PyThreadState *)ue_python_gil);
		return true;
	}
	return false;
#endif
	return true;
}

static void UESetupPythonInterpeter(bool verbose) {
	unreal_engine_init_py_module();

	PyObject *py_sys = PyImport_ImportModule("sys");
	PyObject *py_sys_dict = PyModule_GetDict(py_sys);

	PyObject *py_path = PyDict_GetItemString(py_sys_dict, "path");

	char *zip_path = TCHAR_TO_UTF8(*FPaths::Combine(*FPaths::GameContentDir(), UTF8_TO_TCHAR("ue_python.zip")));
	PyObject *py_zip_path = PyUnicode_FromString(zip_path);
	PyList_Insert(py_path, 0, py_zip_path);

	char *scripts_path = TCHAR_TO_UTF8(*FPaths::Combine(*FPaths::GameContentDir(), UTF8_TO_TCHAR("Scripts")));
	PyObject *py_scripts_path = PyUnicode_FromString(scripts_path);
	PyList_Insert(py_path, 0, py_scripts_path);

	if (verbose) {
		UE_LOG(LogPython, Log, TEXT("Python VM initialized: %s"), UTF8_TO_TCHAR(Py_GetVersion()));
		UE_LOG(LogPython, Log, TEXT("Python Scripts search path: %s"), UTF8_TO_TCHAR(scripts_path));
	}
}

void FUnrealEnginePythonModule::StartupModule()
{
	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module

	Py_Initialize();
#if PY_MAJOR_VERSION >= 3
	wchar_t *argv[] = { UTF8_TO_TCHAR("UnrealEngine"), NULL };
#else
	char *argv[] = { (char *)"UnrealEngine", NULL };
#endif
	PySys_SetArgv(1, argv);

	PyEval_InitThreads();

	UESetupPythonInterpeter(true);

	PyObject *main_module = PyImport_AddModule("__main__");
	main_dict = PyModule_GetDict(main_module);
	local_dict = main_dict;// PyDict_New();

	if (PyImport_ImportModule("ue_site")) {
		UE_LOG(LogPython, Log, TEXT("ue_site Python module successfully imported"));
	}
	else {
		// TODO gracefully manage the error
		unreal_engine_py_log_error();
	}

#if WITH_EDITOR
	// register commands (after importing ue_site)
	FPythonSlateCommands::Register();
	// apply extenders
	FPythonSlateCommands::ApplyPythonExtenders();
#endif

	// release the GIL
	PythonGILRelease();

}

void FUnrealEnginePythonModule::ShutdownModule()
{
	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
	// we call this function before unloading the module.

	UE_LOG(LogPython, Log, TEXT("Goodbye Python"));
	PythonGILAcquire();
	Py_Finalize();
}

void FUnrealEnginePythonModule::RunString(char *str) {
	FScopePythonGIL gil;
	PyObject *eval_ret = PyRun_String(str, Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict);
	if (!eval_ret) {
		unreal_engine_py_log_error();
		return;
	}
	Py_DECREF(eval_ret);
}

void FUnrealEnginePythonModule::RunFile(char *filename) {
	FScopePythonGIL gil;
	char *full_path = filename;
	if (!FPaths::FileExists(filename))
	{
		full_path = TCHAR_TO_UTF8(*FPaths::Combine(*FPaths::GameContentDir(), UTF8_TO_TCHAR("Scripts"), *FString("/"), UTF8_TO_TCHAR(filename)));
	}
#if PY_MAJOR_VERSION >= 3
	FILE *fd = nullptr;

#if PLATFORM_WINDOWS
	if (fopen_s(&fd, full_path, "r") != 0) {
		UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path));
		return;
	}
#else
	fd = fopen(full_path, "r");
	if (!fd) {
		UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path));
		return;
	}
#endif

	PyObject *eval_ret = PyRun_File(fd, full_path, Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict);
	fclose(fd);
	if (!eval_ret) {
		unreal_engine_py_log_error();
		return;
	}
	Py_DECREF(eval_ret);
#else
	// damn, this is horrible, but it is the only way i found to avoid the CRT error :(
	FString command = FString::Printf(TEXT("execfile(\"%s\")"), UTF8_TO_TCHAR(full_path));
	PyObject *eval_ret = PyRun_String(TCHAR_TO_UTF8(*command), Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict);
	if (!eval_ret) {
		unreal_engine_py_log_error();
		return;
	}
#endif

}

// run a python script in a new sub interpreter (useful for unit tests)
void FUnrealEnginePythonModule::RunFileSandboxed(char *filename) {
	FScopePythonGIL gil;
	char *full_path = filename;
	if (!FPaths::FileExists(filename))
	{
		full_path = TCHAR_TO_UTF8(*FPaths::Combine(*FPaths::GameContentDir(), UTF8_TO_TCHAR("Scripts"), *FString("/"), UTF8_TO_TCHAR(filename)));
	}

	PyThreadState *_main = PyThreadState_Get();

	PyThreadState *py_new_state = Py_NewInterpreter();
	if (!py_new_state) {
		UE_LOG(LogPython, Error, TEXT("Unable to create new Python interpreter"));
		return;
	}
	PyThreadState_Swap(nullptr);
	PyThreadState_Swap(py_new_state);

	UESetupPythonInterpeter(false);

	PyObject *m = PyImport_AddModule("__main__");
	if (m == NULL) {
		UE_LOG(LogPython, Error, TEXT("Unable to create new global dict"));
		Py_EndInterpreter(py_new_state);
		PyThreadState_Swap(_main);
		return;
	}
	PyObject *global_dict = PyModule_GetDict(m);

#if PY_MAJOR_VERSION >= 3
	FILE *fd = nullptr;

#if PLATFORM_WINDOWS
	if (fopen_s(&fd, full_path, "r") != 0) {
		UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path));
		return;
	}
#else
	fd = fopen(full_path, "r");
	if (!fd) {
		UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path));
		return;
	}
#endif
	PyObject *eval_ret = PyRun_File(fd, full_path, Py_file_input, global_dict, global_dict);
	fclose(fd);
	if (!eval_ret) {
		unreal_engine_py_log_error();
		Py_EndInterpreter(py_new_state);
		PyThreadState_Swap(_main);
		return;
	}
	Py_DECREF(eval_ret);
#else
	// damn, this is horrible, but it is the only way i found to avoid the CRT error :(
	FString command = FString::Printf(TEXT("execfile(\"%s\")"), UTF8_TO_TCHAR(full_path));
	PyObject *eval_ret = PyRun_String(TCHAR_TO_UTF8(*command), Py_file_input, global_dict, global_dict);
	if (!eval_ret) {
		unreal_engine_py_log_error();
		Py_EndInterpreter(py_new_state);
		PyThreadState_Swap(_main);
		return;
	}
#endif
	Py_EndInterpreter(py_new_state);
	PyThreadState_Swap(_main);
}

#undef LOCTEXT_NAMESPACE

IMPLEMENT_MODULE(FUnrealEnginePythonModule, UnrealEnginePython)

<|endoftext|>


<commit_before>#include <chain.hpp>

#include <vector>
#include <list>
#include <string>
#include <vector>
#include <iterator>
#include <utility>

#include "catch.hpp"

using iter::chain;
using Vec = const std::vector<char>;

TEST_CASE("chain three strings", "[chain]") {
    std::string s1{"abc"};
    std::string s2{"mno"};
    std::string s3{"xyz"};
    auto ch = chain(s1, s2, s3);

    Vec v(std::begin(ch), std::end(ch));
    Vec vc{'a','b','c','m','n','o','x','y','z'};

    REQUIRE( v == vc );
}

TEST_CASE("chain with different container types", "[chain]") {
    std::string s1{"abc"};
    std::list<char> li{'m', 'n', 'o'};
    std::vector<char> vec{'x', 'y', 'z'};
    auto ch = chain(s1, li, vec);

    Vec v(std::begin(ch), std::end(ch));
    Vec vc{'a','b','c','m','n','o','x','y','z'};

    REQUIRE( v == vc );
}

TEST_CASE("chain handles empty containers", "[chain]") {
    std::string emp;
    std::string a{"a"};
    std::string b{"b"};
    std::string c{"c"};
    Vec vc{'a', 'b', 'c'};

    SECTION("Empty container at front") {
        auto ch = chain(emp, a, b, c);
        Vec v(std::begin(ch), std::end(ch));

        REQUIRE( v == vc );
    }

    SECTION("Empty container at back") {
        auto ch = chain(a, b, c, emp);
        Vec v(std::begin(ch), std::end(ch));

        REQUIRE( v == vc );
    }

    SECTION("Empty container in middle") {
        auto ch = chain(a, emp, b, emp, c);
        Vec v(std::begin(ch), std::end(ch));

        REQUIRE( v == vc );
    }

    SECTION("Consecutive empty containers at front") {
        auto ch = chain(emp, emp, a, b, c);
        Vec v(std::begin(ch), std::end(ch));

        REQUIRE( v == vc );
    }

    SECTION("Consecutive empty containers at back") {
        auto ch = chain(a, b, c, emp, emp);
        Vec v(std::begin(ch), std::end(ch));

        REQUIRE( v == vc );
    }

    SECTION("Consecutive empty containers in middle") {
        auto ch = chain(a, emp, emp, b, emp, emp, c);
        Vec v(std::begin(ch), std::end(ch));

        REQUIRE( v == vc );
    }
}
<commit_msg>adds chain tests with only empty containers<commit_after>#include <chain.hpp>

#include <vector>
#include <list>
#include <string>
#include <vector>
#include <iterator>
#include <utility>

#include "catch.hpp"

using iter::chain;
using Vec = const std::vector<char>;

TEST_CASE("chain three strings", "[chain]") {
    std::string s1{"abc"};
    std::string s2{"mno"};
    std::string s3{"xyz"};
    auto ch = chain(s1, s2, s3);

    Vec v(std::begin(ch), std::end(ch));
    Vec vc{'a','b','c','m','n','o','x','y','z'};

    REQUIRE( v == vc );
}

TEST_CASE("chain with different container types", "[chain]") {
    std::string s1{"abc"};
    std::list<char> li{'m', 'n', 'o'};
    std::vector<char> vec{'x', 'y', 'z'};
    auto ch = chain(s1, li, vec);

    Vec v(std::begin(ch), std::end(ch));
    Vec vc{'a','b','c','m','n','o','x','y','z'};

    REQUIRE( v == vc );
}

TEST_CASE("chain handles empty containers", "[chain]") {
    std::string emp;
    std::string a{"a"};
    std::string b{"b"};
    std::string c{"c"};
    Vec vc{'a', 'b', 'c'};

    SECTION("Empty container at front") {
        auto ch = chain(emp, a, b, c);
        Vec v(std::begin(ch), std::end(ch));

        REQUIRE( v == vc );
    }

    SECTION("Empty container at back") {
        auto ch = chain(a, b, c, emp);
        Vec v(std::begin(ch), std::end(ch));

        REQUIRE( v == vc );
    }

    SECTION("Empty container in middle") {
        auto ch = chain(a, emp, b, emp, c);
        Vec v(std::begin(ch), std::end(ch));

        REQUIRE( v == vc );
    }

    SECTION("Consecutive empty containers at front") {
        auto ch = chain(emp, emp, a, b, c);
        Vec v(std::begin(ch), std::end(ch));

        REQUIRE( v == vc );
    }

    SECTION("Consecutive empty containers at back") {
        auto ch = chain(a, b, c, emp, emp);
        Vec v(std::begin(ch), std::end(ch));

        REQUIRE( v == vc );
    }

    SECTION("Consecutive empty containers in middle") {
        auto ch = chain(a, emp, emp, b, emp, emp, c);
        Vec v(std::begin(ch), std::end(ch));

        REQUIRE( v == vc );
    }
}

TEST_CASE("chain with only empty containers", "[chain]") {
    std::string emp{};
    SECTION("one empty container") {
        auto ch = chain(emp);
        REQUIRE_FALSE( std::begin(ch) != std::end(ch) );
    }

    SECTION("two empty containers") {
        auto ch = chain(emp, emp);
        REQUIRE_FALSE( std::begin(ch) != std::end(ch) );
    }

    SECTION("three empty containers") {
        auto ch = chain(emp, emp, emp);
        REQUIRE_FALSE( std::begin(ch) != std::end(ch) );
    }
}
<|endoftext|>


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

#include <unistd.h>

#include "postgres/include/postgres.h"

#include "postgres/include/bootstrap/bootstrap.h"
#include "postgres/include/common/username.h"
#include "postgres/include/postmaster/postmaster.h"
#include "postgres/include/storage/barrier.h"
#include "postgres/include/storage/s_lock.h"
#include "postgres/include/storage/spin.h"
#include "postgres/include/tcop/tcopprot.h"
#include "postgres/include/utils/help_config.h"
#include "postgres/include/utils/memutils.h"
#include "postgres/include/utils/pg_locale.h"
#include "postgres/include/utils/ps_status.h"

const char *progname;

static void startup_hacks(const char *progname);
static void init_locale(int category, const char *locale);
static void help(const char *progname);
static void check_root(const char *progname);

/*
 * Any Postgres server process begins execution here.
 */
int main(int argc, char *argv[]) {
  bool do_check_root = true;

  progname = "peloton";
  // progname = get_progname(argv[0]);

  /*
   * Platform-specific startup hacks
   */
  startup_hacks(progname);

  /*
   * Remember the physical location of the initially given argv[] array for
   * possible use by ps display.  On some platforms, the argv[] storage must
   * be overwritten in order to set the process title for ps. In such cases
   * save_ps_display_args makes and returns a new copy of the argv[] array.
   *
   * save_ps_display_args may also move the environment strings to make
   * extra room. Therefore this should be done as early as possible during
   * startup, to avoid entanglements with code that might save a getenv()
   * result pointer.
   */
  argv = save_ps_display_args(argc, argv);

/*
 * If supported on the current platform, set up a handler to be called if
 * the backend/postmaster crashes with a fatal signal or exception.
 */
#if defined(WIN32) && defined(HAVE_MINIDUMP_TYPE)
  pgwin32_install_crashdump_handler();
#endif

  /*
   * Fire up essential subsystems: error and memory management
   *
   * Code after this point is allowed to use elog/ereport, though
   * localization of messages may not work right away, and messages won't go
   * anywhere but stderr until GUC settings get loaded.
   */
  MemoryContextInit();

  /*
   * Set up locale information from environment.  Note that LC_CTYPE and
   * LC_COLLATE will be overridden later from pg_control if we are in an
   * already-initialized database.  We set them here so that they will be
   * available to fill pg_control during initdb.  LC_MESSAGES will get set
   * later during GUC option processing, but we set it here to allow startup
   * error messages to be localized.
   */

  set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("postgres"));

  init_locale(LC_COLLATE, "");
  init_locale(LC_CTYPE, "");

#ifdef LC_MESSAGES
  init_locale(LC_MESSAGES, "");
#endif

  /*
   * We keep these set to "C" always, except transiently in pg_locale.c; see
   * that file for explanations.
   */
  init_locale(LC_MONETARY, "C");
  init_locale(LC_NUMERIC, "C");
  init_locale(LC_TIME, "C");

  /*
   * Now that we have absorbed as much as we wish to from the locale
   * environment, remove any LC_ALL setting, so that the environment
   * variables installed by pg_perm_setlocale have force.
   */
  unsetenv("LC_ALL");

  /*
   * Catch standard options before doing much else, in particular before we
   * insist on not being root.
   */
  if (argc > 1) {
    if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) {
      help(progname);
      exit(0);
    }
    if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) {
      puts("postgres (PostgreSQL) " PG_VERSION);
      exit(0);
    }

    /*
     * In addition to the above, we allow "--describe-config" and "-C var"
     * to be called by root.  This is reasonably safe since these are
     * read-only activities.  The -C case is important because pg_ctl may
     * try to invoke it while still holding administrator privileges on
     * Windows.  Note that while -C can normally be in any argv position,
     * if you wanna bypass the root check you gotta put it first.  This
     * reduces the risk that we might misinterpret some other mode's -C
     * switch as being the postmaster/postgres one.
     */
    if (strcmp(argv[1], "--describe-config") == 0)
      do_check_root = false;
    else if (argc > 2 && strcmp(argv[1], "-C") == 0)
      do_check_root = false;
  }

  /*
   * Make sure we are not running as root, unless it's safe for the selected
   * option.
   */
  if (do_check_root) check_root(progname);

/*
 * Dispatch to one of various subprograms depending on first argument.
 */

#ifdef EXEC_BACKEND
  if (argc > 1 && strncmp(argv[1], "--fork", 6) == 0)
    SubPostmasterMain(argc, argv); /* does not return */
#endif

  if (argc > 1 && strcmp(argv[1], "--boot") == 0)
    AuxiliaryProcessMain(argc, argv); /* does not return */
  else if (argc > 1 && strcmp(argv[1], "--describe-config") == 0)
    GucInfoMain(); /* does not return */
  else if (argc > 1 && strcmp(argv[1], "--single") == 0)
    PostgresMain(argc, argv, NULL,                         /* no dbname */
                 strdup(get_user_name_or_exit(progname))); /* does not return */
  else
    PostmasterMain(argc, argv); /* does not return */
  abort();                      /* should not get here */
}

/*
 * Place platform-specific startup hacks here.  This is the right
 * place to put code that must be executed early in the launch of any new
 * server process.  Note that this code will NOT be executed when a backend
 * or sub-bootstrap process is forked, unless we are in a fork/exec
 * environment (ie EXEC_BACKEND is defined).
 *
 * XXX The need for code here is proof that the platform in question
 * is too brain-dead to provide a standard C execution environment
 * without help.  Avoid adding more here, if you can.
 */
static void startup_hacks(const char *progname __attribute__((unused))) {
  /*
   * Initialize dummy_spinlock, in case we are on a platform where we have
   * to use the fallback implementation of pg_memory_barrier().
   */
  SpinLockInit(&dummy_spinlock);
}

/*
 * Make the initial permanent setting for a locale category.  If that fails,
 * perhaps due to LC_foo=invalid in the environment, use locale C.  If even
 * that fails, perhaps due to out-of-memory, the entire startup fails with it.
 * When this returns, we are guaranteed to have a setting for the given
 * category's environment variable.
 */
static void init_locale(int category, const char *locale) {
  if (pg_perm_setlocale(category, locale) == NULL &&
      pg_perm_setlocale(category, "C") == NULL)
    elog(FATAL, "could not adopt C locale");
}

/*
 * Help display should match the options accepted by PostmasterMain()
 * and PostgresMain().
 *
 * XXX On Windows, non-ASCII localizations of these messages only display
 * correctly if the console output code page covers the necessary characters.
 * Messages emitted in write_console() do not exhibit this problem.
 */
static void help(const char *progname) {
  std::string message = std::string(progname) + " is the PostgreSQL server.\n\n";
  message +="Usage:\n  " + std::string(progname) + " [OPTION]...\n\n";
  message +="Options:\n";
  message +="  -B NBUFFERS        number of shared buffers\n";
  message +="  -c NAME=VALUE      set run-time parameter\n";
  message +="  -C NAME            print value of run-time parameter, then exit\n";
  message +="  -d 1-5             debugging level\n";
  message +="  -D DATADIR         database directory\n";
  message +="  -e                 use European date input format (DMY)\n";
  message +="  -F                 turn fsync off\n";
  message +="  -h HOSTNAME        host name or IP address to listen on\n";
  message +="  -i                 enable TCP/IP connections\n";
  message +="  -k DIRECTORY       Unix-domain socket location\n";
#ifdef USE_SSL
  message +="  -l                 enable SSL connections\n";
#endif
  message +="  -N MAX-CONNECT     maximum number of allowed connections\n";
  message +="  -o OPTIONS         pass \"OPTIONS\" to each server process "
        "(obsolete)\n";
  message +="  -p PORT            port number to listen on\n";
  message +="  -s                 show statistics after each query\n";
  message +="  -S WORK-MEM        set amount of memory for sorts (in kB)\n";
  message +="  -V, --version      output version information, then exit\n";
  message +="  --NAME=VALUE       set run-time parameter\n";
  message +="  --describe-config  describe configuration parameters, then exit\n";
  message +="  -?, --help         show this help, then exit\n";

  message +="\nDeveloper options:\n";
  message +="  -f s|i|n|m|h       forbid use of some plan types\n";
  message +="  -n                 do not reinitialize shared memory after abnormal "
        "exit\n";
  message +="  -O                 allow system table structure changes\n";
  message +="  -P                 disable system indexes\n";
  message +="  -t pa|pl|ex        show timings after each query\n";
  message +="  -T                 send SIGSTOP to all backend processes if one "
        "dies\n";
  message +="  -W NUM             wait NUM seconds to allow attach from a "
        "debugger\n";

  message +="\nOptions for single-user mode:\n";
  message +="  --single           selects single-user mode (must be first "
        "argument)\n";
  message +="  DBNAME             database name (defaults to user name)\n";
  message +="  -d 0-5             override debugging level\n";
  message +="  -E                 echo statement before execution\n";
  message +="  -j                 do not use newline as interactive query "
        "delimiter\n";
  message +="  -r FILENAME        send stdout and stderr to given file\n";

  message +="\nOptions for bootstrapping mode:\n";
  message +="  --boot             selects bootstrapping mode (must be first "
        "argument)\n";
  message +="  DBNAME             database name (mandatory argument in "
        "bootstrapping mode)\n";
  message +="  -r FILENAME        send stdout and stderr to given file\n";
  message +="  -x NUM             internal use\n";

  message +="\nPlease read the documentation for the complete list of run-time\n"
        "configuration settings and how to set them on the command line or in\n"
        "the configuration file.\n\n"
        "Report bugs to <pgsql-bugs@postgresql.org>.\n";
  fprintf(stderr, "%s", message.c_str());
}

static void check_root(const char *progname) {
  if (geteuid() == 0) {
    write_stderr(
        "\"root\" execution of the PostgreSQL server is not permitted.\n"
        "The server must be started under an unprivileged user ID to prevent\n"
        "possible system security compromise.  See the documentation for\n"
        "more information on how to properly start the server.\n");
    exit(1);
  }

  /*
   * Also make sure that real and effective uids are the same. Executing as
   * a setuid program from a root shell is a security hole, since on many
   * platforms a nefarious subroutine could setuid back to root if real uid
   * is root.  (Since nobody actually uses postgres as a setuid program,
   * trying to actively fix this situation seems more trouble than it's
   * worth; we'll just expend the effort to check for it.)
   */
  if (getuid() != geteuid()) {
    write_stderr("%s: real and effective user IDs must match\n", progname);
    exit(1);
  }
}
<commit_msg>Fix main<commit_after>//===----------------------------------------------------------------------===//
//
//                         Peloton
//
// main.cpp
//
// Identification: src/backend/main/main.cpp
//
// Copyright (c) 2015-16, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <unistd.h>

#include "postgres/include/postgres.h"

#include "postgres/include/bootstrap/bootstrap.h"
#include "postgres/include/common/username.h"
#include "postgres/include/postmaster/postmaster.h"
#include "postgres/include/storage/barrier.h"
#include "postgres/include/storage/s_lock.h"
#include "postgres/include/storage/spin.h"
#include "postgres/include/tcop/tcopprot.h"
#include "postgres/include/utils/help_config.h"
#include "postgres/include/utils/memutils.h"
#include "postgres/include/utils/pg_locale.h"
#include "postgres/include/utils/ps_status.h"

const char *progname;

static void startup_hacks(const char *progname);
static void init_locale(int category, const char *locale);
static void help(const char *progname);
static void check_root(const char *progname);

/*
 * Any Postgres server process begins execution here.
 */
int main(int argc, char *argv[]) {
  bool do_check_root = true;

  progname = "peloton";
  // progname = get_progname(argv[0]);

  /*
   * Platform-specific startup hacks
   */
  startup_hacks(progname);

  /*
   * Remember the physical location of the initially given argv[] array for
   * possible use by ps display.  On some platforms, the argv[] storage must
   * be overwritten in order to set the process title for ps. In such cases
   * save_ps_display_args makes and returns a new copy of the argv[] array.
   *
   * save_ps_display_args may also move the environment strings to make
   * extra room. Therefore this should be done as early as possible during
   * startup, to avoid entanglements with code that might save a getenv()
   * result pointer.
   */
  argv = save_ps_display_args(argc, argv);

/*
 * If supported on the current platform, set up a handler to be called if
 * the backend/postmaster crashes with a fatal signal or exception.
 */
#if defined(WIN32) && defined(HAVE_MINIDUMP_TYPE)
  pgwin32_install_crashdump_handler();
#endif

  /*
   * Fire up essential subsystems: error and memory management
   *
   * Code after this point is allowed to use elog/ereport, though
   * localization of messages may not work right away, and messages won't go
   * anywhere but stderr until GUC settings get loaded.
   */
  MemoryContextInit();

  /*
   * Set up locale information from environment.  Note that LC_CTYPE and
   * LC_COLLATE will be overridden later from pg_control if we are in an
   * already-initialized database.  We set them here so that they will be
   * available to fill pg_control during initdb.  LC_MESSAGES will get set
   * later during GUC option processing, but we set it here to allow startup
   * error messages to be localized.
   */

  set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("postgres"));

  init_locale(LC_COLLATE, "");
  init_locale(LC_CTYPE, "");

#ifdef LC_MESSAGES
  init_locale(LC_MESSAGES, "");
#endif

  /*
   * We keep these set to "C" always, except transiently in pg_locale.c; see
   * that file for explanations.
   */
  init_locale(LC_MONETARY, "C");
  init_locale(LC_NUMERIC, "C");
  init_locale(LC_TIME, "C");

  /*
   * Now that we have absorbed as much as we wish to from the locale
   * environment, remove any LC_ALL setting, so that the environment
   * variables installed by pg_perm_setlocale have force.
   */
  unsetenv("LC_ALL");

  /*
   * Catch standard options before doing much else, in particular before we
   * insist on not being root.
   */
  if (argc > 1) {
    if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) {
      help(progname);
      exit(0);
    }
    if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) {
      puts("postgres (PostgreSQL) " PG_VERSION);
      exit(0);
    }

    /*
     * In addition to the above, we allow "--describe-config" and "-C var"
     * to be called by root.  This is reasonably safe since these are
     * read-only activities.  The -C case is important because pg_ctl may
     * try to invoke it while still holding administrator privileges on
     * Windows.  Note that while -C can normally be in any argv position,
     * if you wanna bypass the root check you gotta put it first.  This
     * reduces the risk that we might misinterpret some other mode's -C
     * switch as being the postmaster/postgres one.
     */
    if (strcmp(argv[1], "--describe-config") == 0)
      do_check_root = false;
    else if (argc > 2 && strcmp(argv[1], "-C") == 0)
      do_check_root = false;
  }

  /*
   * Make sure we are not running as root, unless it's safe for the selected
   * option.
   */
  if (do_check_root) check_root(progname);

/*
 * Dispatch to one of various subprograms depending on first argument.
 */

#ifdef EXEC_BACKEND
  if (argc > 1 && strncmp(argv[1], "--fork", 6) == 0)
    SubPostmasterMain(argc, argv); /* does not return */
#endif

  if (argc > 1 && strcmp(argv[1], "--boot") == 0)
    AuxiliaryProcessMain(argc, argv); /* does not return */
  else if (argc > 1 && strcmp(argv[1], "--describe-config") == 0)
    GucInfoMain(); /* does not return */
  else if (argc > 1 && strcmp(argv[1], "--single") == 0)
    PostgresMain(argc, argv, NULL,                         /* no dbname */
                 strdup(get_user_name_or_exit(progname))); /* does not return */
  else
    PostmasterMain(argc, argv); /* does not return */
  abort();                      /* should not get here */
}

/*
 * Place platform-specific startup hacks here.  This is the right
 * place to put code that must be executed early in the launch of any new
 * server process.  Note that this code will NOT be executed when a backend
 * or sub-bootstrap process is forked, unless we are in a fork/exec
 * environment (ie EXEC_BACKEND is defined).
 *
 * XXX The need for code here is proof that the platform in question
 * is too brain-dead to provide a standard C execution environment
 * without help.  Avoid adding more here, if you can.
 */
static void startup_hacks(const char *progname __attribute__((unused))) {
  /*
   * Initialize dummy_spinlock, in case we are on a platform where we have
   * to use the fallback implementation of pg_memory_barrier().
   */
  SpinLockInit(&dummy_spinlock);
}

/*
 * Make the initial permanent setting for a locale category.  If that fails,
 * perhaps due to LC_foo=invalid in the environment, use locale C.  If even
 * that fails, perhaps due to out-of-memory, the entire startup fails with it.
 * When this returns, we are guaranteed to have a setting for the given
 * category's environment variable.
 */
static void init_locale(int category, const char *locale) {
  if (pg_perm_setlocale(category, locale) == NULL &&
      pg_perm_setlocale(category, "C") == NULL)
    elog(FATAL, "could not adopt C locale");
}

/*
 * Help display should match the options accepted by PostmasterMain()
 * and PostgresMain().
 *
 * XXX On Windows, non-ASCII localizations of these messages only display
 * correctly if the console output code page covers the necessary characters.
 * Messages emitted in write_console() do not exhibit this problem.
 */
static void help(const char *progname) {
  fprintf(stderr,"%s is the PostgreSQL server.\n\n", progname);
  fprintf(stderr,"Usage:\n  %s [OPTION]...\n\n", progname);
  fprintf(stderr,"Options:\n");
  fprintf(stderr,"  -B NBUFFERS        number of shared buffers\n");
  fprintf(stderr,"  -c NAME=VALUE      set run-time parameter\n");
  fprintf(stderr,"  -C NAME            print value of run-time parameter, then exit\n");
  fprintf(stderr,"  -d 1-5             debugging level\n");
  fprintf(stderr,"  -D DATADIR         database directory\n");
  fprintf(stderr,"  -e                 use European date input format (DMY)\n");
  fprintf(stderr,"  -F                 turn fsync off\n");
  fprintf(stderr,"  -h HOSTNAME        host name or IP address to listen on\n");
  fprintf(stderr,"  -i                 enable TCP/IP connections\n");
  fprintf(stderr,"  -k DIRECTORY       Unix-domain socket location\n");
#ifdef USE_SSL
  fprintf(stderr,"  -l                 enable SSL connections\n");
#endif
  fprintf(stderr,"  -N MAX-CONNECT     maximum number of allowed connections\n");
  fprintf(stderr,"  -o OPTIONS         pass \"OPTIONS\" to each server process "
        "(obsolete)\n");
  fprintf(stderr,"  -p PORT            port number to listen on\n");
  fprintf(stderr,"  -s                 show statistics after each query\n");
  fprintf(stderr,"  -S WORK-MEM        set amount of memory for sorts (in kB)\n");
  fprintf(stderr,"  -V, --version      output version information, then exit\n");
  fprintf(stderr,"  --NAME=VALUE       set run-time parameter\n");
  fprintf(stderr,"  --describe-config  describe configuration parameters, then exit\n");
  fprintf(stderr,"  -?, --help         show this help, then exit\n");

  fprintf(stderr,"\nDeveloper options:\n");
  fprintf(stderr,"  -f s|i|n|m|h       forbid use of some plan types\n");
  fprintf(stderr,"  -n                 do not reinitialize shared memory after abnormal "
        "exit\n");
  fprintf(stderr,"  -O                 allow system table structure changes\n");
  fprintf(stderr,"  -P                 disable system indexes\n");
  fprintf(stderr,"  -t pa|pl|ex        show timings after each query\n");
  fprintf(stderr,"  -T                 send SIGSTOP to all backend processes if one "
        "dies\n");
  fprintf(stderr,"  -W NUM             wait NUM seconds to allow attach from a "
        "debugger\n");

  fprintf(stderr,"\nOptions for single-user mode:\n");
  fprintf(stderr,"  --single           selects single-user mode (must be first "
        "argument)\n");
  fprintf(stderr,"  DBNAME             database name (defaults to user name)\n");
  fprintf(stderr,"  -d 0-5             override debugging level\n");
  fprintf(stderr,"  -E                 echo statement before execution\n");
  fprintf(stderr,"  -j                 do not use newline as interactive query "
        "delimiter\n");
  fprintf(stderr,"  -r FILENAME        send stdout and stderr to given file\n");

  fprintf(stderr,"\nOptions for bootstrapping mode:\n");
  fprintf(stderr,"  --boot             selects bootstrapping mode (must be first "
        "argument)\n");
  fprintf(stderr,"  DBNAME             database name (mandatory argument in "
        "bootstrapping mode)\n");
  fprintf(stderr,"  -r FILENAME        send stdout and stderr to given file\n");
  fprintf(stderr,"  -x NUM             internal use\n");

  fprintf(stderr,"\nPlease read the documentation for the complete list of run-time\n"
        "configuration settings and how to set them on the command line or in\n"
        "the configuration file.\n\n"
        "Report bugs to <pgsql-bugs@postgresql.org>.\n");
}

static void check_root(const char *progname) {
  if (geteuid() == 0) {
    write_stderr(
        "\"root\" execution of the PostgreSQL server is not permitted.\n"
        "The server must be started under an unprivileged user ID to prevent\n"
        "possible system security compromise.  See the documentation for\n"
        "more information on how to properly start the server.\n");
    exit(1);
  }

  /*
   * Also make sure that real and effective uids are the same. Executing as
   * a setuid program from a root shell is a security hole, since on many
   * platforms a nefarious subroutine could setuid back to root if real uid
   * is root.  (Since nobody actually uses postgres as a setuid program,
   * trying to actively fix this situation seems more trouble than it's
   * worth; we'll just expend the effort to check for it.)
   */
  if (getuid() != geteuid()) {
    write_stderr("%s: real and effective user IDs must match\n", progname);
    exit(1);
  }
}
<|endoftext|>


<commit_before>#include "pch.h"
#include "carousel.h"

Carousel::Carousel()
    : _currentItemIndex(INVALID_ITEM_INDEX)
    , _currentItemBeforeTouch(INVALID_ITEM_INDEX)
    , _startScrollingPosition(0.0f, 0.0f)
    , _itemScrollingClip(NULL)
    , _rawScrollPosition(0, 0)
    , _freeSliding(false)
{
}

Carousel::~Carousel()
{
}

const char * Carousel::getTypeName() const
{
    return "Carousel";
}

gameplay::Control * Carousel::create(gameplay::Theme::Style* style, gameplay::Properties* properties)
{
    Carousel * res = new Carousel();
    res->initialize(res->getTypeName(), style, properties);
    return res;
}

void Carousel::initialize(const char* typeName, gameplay::Theme::Style* style, gameplay::Properties* properties)
{
    gameplay::Container::initialize(typeName, style, properties);

    _freeSliding = properties->getBool("freeSliding");

    setReceiveInputEvents(true);

    for (gameplay::Control * child : getControls())
        unsetConsumeInputEvents(child);
}

bool Carousel::touchEventScroll(gameplay::Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
{
    if (evt != gameplay::Touch::TOUCH_PRESS && _currentItemBeforeTouch == INVALID_ITEM_INDEX)
        return false;

    // using _currentItemBeforeTouch also as a flag that touch is still pressed (INVALID_ITEM_INDEX)
    // remap scroll position only when touch is pressed
    if (_currentItemBeforeTouch == INVALID_ITEM_INDEX)
        _rawScrollPosition = _scrollPosition;

    _scrollPosition = _rawScrollPosition;
    bool res = gameplay::Container::touchEventScroll(evt, x, y, contactIndex) || _currentItemBeforeTouch != INVALID_ITEM_INDEX;
    _rawScrollPosition = _scrollPosition;

    // reset velocity
    _scrollingVelocity.set(0.0f, 0.0f);

    if (getControlCount() > 0)
    {
        unsigned closestControlIndex = findClosestControlIndex(-_scrollPosition.x, false);

        if (!_freeSliding)
        {
            // remap scrollPosition so it feels like there is a spring inside button
            // that help items to stick to borders when rotating a dial
            gameplay::Control * closestControl = getControl(closestControlIndex);
            float distance = 0.5f * (closestControl->getWidth() + closestControl->getMargin().left + closestControl->getMargin().bottom);
            float relativeOffsetToClosestItem = (closestControl->getX() + closestControl->getWidth() * 0.5f + _scrollPosition.x) / distance - 1.0f;
            relativeOffsetToClosestItem = std::min(1.0f, std::max(-1.0f, relativeOffsetToClosestItem));

            relativeOffsetToClosestItem *= relativeOffsetToClosestItem * relativeOffsetToClosestItem;
            _scrollPosition.x = (relativeOffsetToClosestItem + 1.0f) * distance - closestControl->getX() - closestControl->getWidth() * 0.5f;

            closestControlIndex = findClosestControlIndex(-_scrollPosition.x, false);
        }

        switch (evt)
        {
        case gameplay::Touch::TOUCH_MOVE:
            if (_currentItemBeforeTouch != INVALID_ITEM_INDEX && _currentItemIndex != closestControlIndex)
            {
                _currentItemIndex = closestControlIndex;
            }
            break;
        case gameplay::Touch::TOUCH_RELEASE:
            // scroll to nearest item
            if (_currentItemBeforeTouch != INVALID_ITEM_INDEX)
            {
                _currentItemIndex = closestControlIndex;
                scrollToItem(closestControlIndex);
                if (_currentItemBeforeTouch != _currentItemIndex)
                    notifyListeners(gameplay::Control::Listener::VALUE_CHANGED);
                _currentItemBeforeTouch = INVALID_ITEM_INDEX;
            }
            break;
        case gameplay::Touch::TOUCH_PRESS:
            if (getControl(closestControlIndex)->getBounds().height > y)
                if (_currentItemIndex != INVALID_ITEM_INDEX)
                    _currentItemBeforeTouch = _currentItemIndex;
            break;
        }
    }

    return res;
}

unsigned Carousel::findClosestControlIndex(float localX, bool exitOnPositiveOffset) const
{
    float minDistance = FLT_MAX;
    unsigned closestControlIndex = 0;
    unsigned index = 0;
    for (gameplay::Control * control : getControls())
    {
        if (control->isVisible())
        {
            float distance = control->getX() - control->getMargin().left - localX;
            if (exitOnPositiveOffset && distance > -control->getHeight())
                return index;

            if (fabs(distance) < minDistance)
            {
                minDistance = fabs(distance);
                closestControlIndex = index;
                if (distance > 0.0f)
                    return closestControlIndex;
            }
        }
        index++;
    }

    return closestControlIndex < getControlCount() ? closestControlIndex : INVALID_ITEM_INDEX;
}

void Carousel::scrollToItem(unsigned itemIndex, bool immediately)
{
    if (itemIndex >= getControlCount())
        return;

    if (_itemScrollingClip && _itemScrollingClip->isPlaying())
    {
        _itemScrollingClip->stop();
        _itemScrollingClip = NULL;
    }

    unsigned int lastItem = _currentItemIndex;
    if (_currentItemIndex != itemIndex)
    {
        _currentItemIndex = itemIndex;
        notifyListeners(gameplay::Control::Listener::VALUE_CHANGED);

        // controls count may change after notifying listeners
        if (_currentItemIndex >= getControlCount())
        {
            _currentItemIndex = INVALID_ITEM_INDEX;
            notifyListeners(gameplay::Control::Listener::VALUE_CHANGED);
            return;
        }
    }

    if (!immediately && lastItem < getControlCount())
    {
        float from = 0.0f;
        float to = 1.0f;

        _startScrollingPosition = _scrollPosition;

        gameplay::Animation * animation = createAnimationFromTo("scrollbar-scroll-to-item", ANIMATE_SCROLL_TO_ITEM, &from, &to,
            gameplay::Curve::QUADRATIC_IN, 200);
        _itemScrollingClip = animation->getClip();
        _itemScrollingClip->play();
    }
    else
    {
        updateChildBounds();
        updateBounds();
        gameplay::Control * itemToScrollTo = getControl(itemIndex);
        gameplay::Vector2 desiredScrollPosition(-(itemToScrollTo->getX() - itemToScrollTo->getMargin().left), 0.0f);
        setScrollPosition(desiredScrollPosition);
    }
}

unsigned int Carousel::getAnimationPropertyComponentCount(int propertyId) const
{
    switch (propertyId)
    {
    case ANIMATE_SCROLL_TO_ITEM:
        return 1;
    default:
        return Container::getAnimationPropertyComponentCount(propertyId);
    }
}

void Carousel::getAnimationPropertyValue(int propertyId, gameplay::AnimationValue* value)
{
    GP_ASSERT(value);

    switch (propertyId)
    {
    case ANIMATE_SCROLL_TO_ITEM:
        {
            GP_ASSERT(_currentItemIndex < getControlCount());
            gameplay::Control * itemToScrollTo = getControl(_currentItemIndex);
            gameplay::Vector2 desiredScrollPosition(-(itemToScrollTo->getX() - itemToScrollTo->getMargin().left), 0.0f);
            value->setFloat(0, (_scrollPosition - _startScrollingPosition).length() / (desiredScrollPosition - _startScrollingPosition).length());
        }
        break;
    default:
        Container::getAnimationPropertyValue(propertyId, value);
        break;
    }
}

void Carousel::setAnimationPropertyValue(int propertyId, gameplay::AnimationValue* value, float blendWeight)
{
    GP_ASSERT(value);

    switch (propertyId)
    {
    case ANIMATE_SCROLL_TO_ITEM:
        {
            GP_ASSERT(_currentItemIndex < getControlCount());
            gameplay::Control * itemToScrollTo = getControl(_currentItemIndex);
            gameplay::Vector2 desiredScrollPosition(-(itemToScrollTo->getX() - itemToScrollTo->getMargin().left), 0.0f);

            float scrollFactor = value->getFloat(0);
            _scrollPosition = _startScrollingPosition + (desiredScrollPosition - _startScrollingPosition) * scrollFactor * blendWeight;

            setDirty(DIRTY_BOUNDS);
            setChildrenDirty(DIRTY_BOUNDS, true);
        }
        break;
    default:
        Container::setAnimationPropertyValue(propertyId, value, blendWeight);
        break;
    }
}

void Carousel::removeControl(unsigned int index)
{
    gameplay::Container::removeControl(index);
    if (_currentItemIndex != INVALID_ITEM_INDEX && _currentItemIndex >= getControlCount())
        _currentItemIndex--;

    if (_currentItemIndex >= getControlCount())
    {
        _currentItemIndex = INVALID_ITEM_INDEX;
        notifyListeners(gameplay::Control::Listener::VALUE_CHANGED);
    }
}

unsigned int Carousel::addControl(gameplay::Control * control)
{
    unsigned int res = gameplay::Container::addControl(control);
    unsetConsumeInputEvents(control);
    return res;
}

void Carousel::insertControl(gameplay::Control * control, unsigned int index)
{
    gameplay::Container::insertControl(control, index);
    unsetConsumeInputEvents(control);
}

void Carousel::setEnabled(bool enabled)
{
    gameplay::Container::setEnabled(enabled);

    if (!enabled)
    {
        // reset any behavior related to user input
        stopScrolling();
        if (_currentItemBeforeTouch != INVALID_ITEM_INDEX)
            _currentItemBeforeTouch = INVALID_ITEM_INDEX;
    }
}

void Carousel::unsetConsumeInputEvents(gameplay::Control * control)
{
    control->setConsumeInputEvents(false);

    if (control->isContainer())
        for (gameplay::Control * child : static_cast<gameplay::Container*>(control)->getControls())
            unsetConsumeInputEvents(child);
}<commit_msg>using up-to-date children bounds for scrolling over carousel items<commit_after>#include "pch.h"
#include "carousel.h"

Carousel::Carousel()
    : _currentItemIndex(INVALID_ITEM_INDEX)
    , _currentItemBeforeTouch(INVALID_ITEM_INDEX)
    , _startScrollingPosition(0.0f, 0.0f)
    , _itemScrollingClip(NULL)
    , _rawScrollPosition(0, 0)
    , _freeSliding(false)
{
}

Carousel::~Carousel()
{
}

const char * Carousel::getTypeName() const
{
    return "Carousel";
}

gameplay::Control * Carousel::create(gameplay::Theme::Style* style, gameplay::Properties* properties)
{
    Carousel * res = new Carousel();
    res->initialize(res->getTypeName(), style, properties);
    return res;
}

void Carousel::initialize(const char* typeName, gameplay::Theme::Style* style, gameplay::Properties* properties)
{
    gameplay::Container::initialize(typeName, style, properties);

    _freeSliding = properties->getBool("freeSliding");

    setReceiveInputEvents(true);

    for (gameplay::Control * child : getControls())
        unsetConsumeInputEvents(child);
}

bool Carousel::touchEventScroll(gameplay::Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
{
    if (evt != gameplay::Touch::TOUCH_PRESS && _currentItemBeforeTouch == INVALID_ITEM_INDEX)
        return false;

    // using _currentItemBeforeTouch also as a flag that touch is still pressed (INVALID_ITEM_INDEX)
    // remap scroll position only when touch is pressed
    if (_currentItemBeforeTouch == INVALID_ITEM_INDEX)
        _rawScrollPosition = _scrollPosition;

    _scrollPosition = _rawScrollPosition;
    bool res = gameplay::Container::touchEventScroll(evt, x, y, contactIndex) || _currentItemBeforeTouch != INVALID_ITEM_INDEX;
    _rawScrollPosition = _scrollPosition;

    // reset velocity
    _scrollingVelocity.set(0.0f, 0.0f);

    if (getControlCount() > 0)
    {
        unsigned closestControlIndex = findClosestControlIndex(-_scrollPosition.x, false);

        if (!_freeSliding)
        {
            // remap scrollPosition so it feels like there is a spring inside button
            // that help items to stick to borders when rotating a dial
            gameplay::Control * closestControl = getControl(closestControlIndex);
            float distance = 0.5f * (closestControl->getWidth() + closestControl->getMargin().left + closestControl->getMargin().bottom);
            float relativeOffsetToClosestItem = (closestControl->getX() + closestControl->getWidth() * 0.5f + _scrollPosition.x) / distance - 1.0f;
            relativeOffsetToClosestItem = std::min(1.0f, std::max(-1.0f, relativeOffsetToClosestItem));

            relativeOffsetToClosestItem *= relativeOffsetToClosestItem * relativeOffsetToClosestItem;
            _scrollPosition.x = (relativeOffsetToClosestItem + 1.0f) * distance - closestControl->getX() - closestControl->getWidth() * 0.5f;

            closestControlIndex = findClosestControlIndex(-_scrollPosition.x, false);
        }

        switch (evt)
        {
        case gameplay::Touch::TOUCH_MOVE:
            if (_currentItemBeforeTouch != INVALID_ITEM_INDEX && _currentItemIndex != closestControlIndex)
            {
                _currentItemIndex = closestControlIndex;
            }
            break;
        case gameplay::Touch::TOUCH_RELEASE:
            // scroll to nearest item
            if (_currentItemBeforeTouch != INVALID_ITEM_INDEX)
            {
                _currentItemIndex = closestControlIndex;
                scrollToItem(closestControlIndex);
                if (_currentItemBeforeTouch != _currentItemIndex)
                    notifyListeners(gameplay::Control::Listener::VALUE_CHANGED);
                _currentItemBeforeTouch = INVALID_ITEM_INDEX;
            }
            break;
        case gameplay::Touch::TOUCH_PRESS:
            if (getControl(closestControlIndex)->getBounds().height > y)
                if (_currentItemIndex != INVALID_ITEM_INDEX)
                    _currentItemBeforeTouch = _currentItemIndex;
            break;
        }
    }

    return res;
}

unsigned Carousel::findClosestControlIndex(float localX, bool exitOnPositiveOffset) const
{
    float minDistance = FLT_MAX;
    unsigned closestControlIndex = 0;
    unsigned index = 0;
    for (gameplay::Control * control : getControls())
    {
        if (control->isVisible())
        {
            float distance = control->getX() - control->getMargin().left - localX;
            if (exitOnPositiveOffset && distance > -control->getHeight())
                return index;

            if (fabs(distance) < minDistance)
            {
                minDistance = fabs(distance);
                closestControlIndex = index;
                if (distance > 0.0f)
                    return closestControlIndex;
            }
        }
        index++;
    }

    return closestControlIndex < getControlCount() ? closestControlIndex : INVALID_ITEM_INDEX;
}

void Carousel::scrollToItem(unsigned itemIndex, bool immediately)
{
    if (itemIndex >= getControlCount())
        return;

    if (_itemScrollingClip && _itemScrollingClip->isPlaying())
    {
        _itemScrollingClip->stop();
        _itemScrollingClip = NULL;
    }

    unsigned int lastItem = _currentItemIndex;
    if (_currentItemIndex != itemIndex)
    {
        _currentItemIndex = itemIndex;
        notifyListeners(gameplay::Control::Listener::VALUE_CHANGED);

        // controls count may change after notifying listeners
        if (_currentItemIndex >= getControlCount())
        {
            _currentItemIndex = INVALID_ITEM_INDEX;
            notifyListeners(gameplay::Control::Listener::VALUE_CHANGED);
            return;
        }
    }

    if (!immediately && lastItem < getControlCount())
    {
        float from = 0.0f;
        float to = 1.0f;

        _startScrollingPosition = _scrollPosition;

        gameplay::Animation * animation = createAnimationFromTo("scrollbar-scroll-to-item", ANIMATE_SCROLL_TO_ITEM, &from, &to,
            gameplay::Curve::QUADRATIC_IN, 200);
        _itemScrollingClip = animation->getClip();
        _itemScrollingClip->play();
    }
    else
    {
        updateChildBounds();
        updateBounds();
        gameplay::Control * itemToScrollTo = getControl(itemIndex);
        gameplay::Vector2 desiredScrollPosition(-(itemToScrollTo->getX() - itemToScrollTo->getMargin().left), 0.0f);
        setScrollPosition(desiredScrollPosition);
    }
}

unsigned int Carousel::getAnimationPropertyComponentCount(int propertyId) const
{
    switch (propertyId)
    {
    case ANIMATE_SCROLL_TO_ITEM:
        return 1;
    default:
        return Container::getAnimationPropertyComponentCount(propertyId);
    }
}

void Carousel::getAnimationPropertyValue(int propertyId, gameplay::AnimationValue* value)
{
    GP_ASSERT(value);

    switch (propertyId)
    {
    case ANIMATE_SCROLL_TO_ITEM:
        {
            GP_ASSERT(_currentItemIndex < getControlCount());
            gameplay::Control * itemToScrollTo = getControl(_currentItemIndex);
            gameplay::Vector2 desiredScrollPosition(-(itemToScrollTo->getX() - itemToScrollTo->getMargin().left), 0.0f);
            value->setFloat(0, (_scrollPosition - _startScrollingPosition).length() / (desiredScrollPosition - _startScrollingPosition).length());
        }
        break;
    default:
        Container::getAnimationPropertyValue(propertyId, value);
        break;
    }
}

void Carousel::setAnimationPropertyValue(int propertyId, gameplay::AnimationValue* value, float blendWeight)
{
    GP_ASSERT(value);

    switch (propertyId)
    {
    case ANIMATE_SCROLL_TO_ITEM:
        if (_currentItemIndex < getControlCount())
        {
            updateChildBounds();
            updateBounds();
            gameplay::Control * itemToScrollTo = getControl(_currentItemIndex);
            gameplay::Vector2 desiredScrollPosition(-(itemToScrollTo->getX() - itemToScrollTo->getMargin().left), 0.0f);

            float scrollFactor = value->getFloat(0);
            _scrollPosition = _startScrollingPosition + (desiredScrollPosition - _startScrollingPosition) * scrollFactor * blendWeight;

            setDirty(DIRTY_BOUNDS);
            setChildrenDirty(DIRTY_BOUNDS, true);
        }
        break;
    default:
        Container::setAnimationPropertyValue(propertyId, value, blendWeight);
        break;
    }
}

void Carousel::removeControl(unsigned int index)
{
    gameplay::Container::removeControl(index);
    if (_currentItemIndex != INVALID_ITEM_INDEX && _currentItemIndex >= getControlCount())
        _currentItemIndex--;

    if (_currentItemIndex >= getControlCount())
    {
        _currentItemIndex = INVALID_ITEM_INDEX;
        notifyListeners(gameplay::Control::Listener::VALUE_CHANGED);
    }
}

unsigned int Carousel::addControl(gameplay::Control * control)
{
    unsigned int res = gameplay::Container::addControl(control);
    unsetConsumeInputEvents(control);
    return res;
}

void Carousel::insertControl(gameplay::Control * control, unsigned int index)
{
    gameplay::Container::insertControl(control, index);
    unsetConsumeInputEvents(control);
}

void Carousel::setEnabled(bool enabled)
{
    gameplay::Container::setEnabled(enabled);

    if (!enabled)
    {
        // reset any behavior related to user input
        stopScrolling();
        if (_currentItemBeforeTouch != INVALID_ITEM_INDEX)
            _currentItemBeforeTouch = INVALID_ITEM_INDEX;
    }
}

void Carousel::unsetConsumeInputEvents(gameplay::Control * control)
{
    control->setConsumeInputEvents(false);

    if (control->isContainer())
        for (gameplay::Control * child : static_cast<gameplay::Container*>(control)->getControls())
            unsetConsumeInputEvents(child);
}<|endoftext|>