<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2013 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Achim Brandt
/// @author Dr. Frank Celler
////////////////////////////////////////////////////////////////////////////////

#include "Logger.h"

#include "Basics/ArangoGlobalContext.h"
#include "Basics/ConditionLocker.h"
#include "Basics/Exceptions.h"
#include "Basics/MutexLocker.h"
#include "Basics/StringUtils.h"
#include "Basics/Thread.h"
#include "Basics/files.h"
#include "Logger/LogAppender.h"

using namespace arangodb;
using namespace arangodb::basics;

Mutex Logger::_initializeMutex;

std::atomic<bool> Logger::_active(false);
std::atomic<LogLevel> Logger::_level(LogLevel::INFO);

bool Logger::_showLineNumber(false);
bool Logger::_shortenFilenames(true);
bool Logger::_showThreadIdentifier(false);
bool Logger::_threaded(false);
bool Logger::_useLocalTime(false);
bool Logger::_keepLogRotate(false);
bool Logger::_useMicrotime(false);
std::string Logger::_outputPrefix("");

std::unique_ptr<LogThread> Logger::_loggingThread(nullptr);

LogLevel Logger::logLevel() { return _level.load(std::memory_order_relaxed); }

std::vector<std::pair<std::string, LogLevel>> Logger::logLevelTopics() {
  return LogTopic::logLevelTopics();
}

void Logger::setLogLevel(LogLevel level) {
  _level.store(level, std::memory_order_relaxed);
}

#include <iostream>

void Logger::setLogLevel(std::string const& levelName) {
  std::string l = StringUtils::tolower(levelName);
  std::vector<std::string> v = StringUtils::split(l, '=');

  if (v.empty() || v.size() > 2) {
    Logger::setLogLevel(LogLevel::INFO);
    LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "strange log level '" << levelName
             << "', using log level 'info'";
    return;
  }

  bool isGeneral = v.size() == 1;

  if (!isGeneral) {
    l = v[1];
  }

  LogLevel level;

  if (l == "fatal") {
    level = LogLevel::FATAL;
  } else if (l == "error" || l == "err") {
    level = LogLevel::ERR;
  } else if (l == "warning" || l == "warn") {
    level = LogLevel::WARN;
  } else if (l == "info") {
    level = LogLevel::INFO;
  } else if (l == "debug") {
    level = LogLevel::DEBUG;
  } else if (l == "trace") {
    level = LogLevel::TRACE;
  } else if (!isGeneral && (l.empty() || l == "default")) {
    level = LogLevel::DEFAULT;
  } else {
    if (isGeneral) {
      Logger::setLogLevel(LogLevel::INFO);
      LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "strange log level '" << levelName
               << "', using log level 'info'";
    } else {
      LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "strange log level '" << levelName << "'";
    }

    return;
  }

  if (isGeneral) {
    Logger::setLogLevel(level);
    // setting the log level for topic "fixme" is required here, too,
    // as "fixme" is the previous general log topic...
    LogTopic::setLogLevel(std::string("fixme"), level);
  } else {
    LogTopic::setLogLevel(v[0], level);
  }
}

void Logger::setLogLevel(std::vector<std::string> const& levels) {
  for (auto level : levels) {
    setLogLevel(level);
  }
}

// NOTE: this function should not be called if the logging is active.
void Logger::setOutputPrefix(std::string const& prefix) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change output prefix if logging is active");
  }

  _outputPrefix = prefix;
}

// NOTE: this function should not be called if the logging is active.
void Logger::setShowLineNumber(bool show) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change show line number if logging is active");
  }

  _showLineNumber = show;
}

// NOTE: this function should not be called if the logging is active.
void Logger::setShortenFilenames(bool shorten) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change shorten filenames if logging is active");
  }

  _shortenFilenames = shorten;
}

// NOTE: this function should not be called if the logging is active.
void Logger::setShowThreadIdentifier(bool show) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change show thread identifier if logging is active");
  }

  _showThreadIdentifier = show;
}

// NOTE: this function should not be called if the logging is active.
void Logger::setUseLocalTime(bool show) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change use local time if logging is active");
  }

  _useLocalTime = show;
}

// NOTE: this function should not be called if the logging is active.
void Logger::setUseMicrotime(bool show) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change use microtime if logging is active");
  }

  _useMicrotime = show;
}

// NOTE: this function should not be called if the logging is active.
void Logger::setKeepLogrotate(bool keep) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change keep log rotate if logging is active");
  }

  _keepLogRotate = keep;
}

std::string const& Logger::translateLogLevel(LogLevel level) {
  static std::string DEFAULT = "DEFAULT";
  static std::string FATAL = "FATAL";
  static std::string ERR = "ERROR";
  static std::string WARN = "WARNING";
  static std::string INFO = "INFO";
  static std::string DEBUG = "DEBUG";
  static std::string TRACE = "TRACE";
  static std::string UNKNOWN = "UNKNOWN";

  switch (level) {
    case LogLevel::DEFAULT:
      return DEFAULT;
    case LogLevel::FATAL:
      return FATAL;
    case LogLevel::ERR:
      return ERR;
    case LogLevel::WARN:
      return WARN;
    case LogLevel::INFO:
      return INFO;
    case LogLevel::DEBUG:
      return DEBUG;
    case LogLevel::TRACE:
      return TRACE;
  }

  return UNKNOWN;
}

void Logger::log(char const* function, char const* file, long int line,
                 LogLevel level, size_t topicId,
                 std::string const& message) {
#ifdef _WIN32
  if (level == LogLevel::FATAL || level == LogLevel::ERR) {
    if (ArangoGlobalContext::CONTEXT != nullptr && ArangoGlobalContext::CONTEXT->useEventLog()) {
      TRI_LogWindowsEventlog(function, file, line, message);
    }

    // additionally log these errors to the debug output window in MSVC so
    // we can see them during development
    OutputDebugString(message.c_str());
  }
#endif

  if (!_active.load(std::memory_order_relaxed)) {
    LogAppender::writeStderr(level, message);
    return;
  }

  std::stringstream out;

  // time prefix
  if (_useMicrotime) {
    char buf[128];
    snprintf(buf, sizeof(buf), "%.6f ", TRI_microtime());
    out << buf;
  } else {
    char timePrefix[32];
    time_t tt = time(0);
    struct tm tb;

    if (!_useLocalTime) {
      // use GMtime
      TRI_gmtime(tt, &tb);
      strftime(timePrefix, sizeof(timePrefix), "%Y-%m-%dT%H:%M:%SZ ", &tb);
    } else {
      // use localtime
      TRI_localtime(tt, &tb);
      strftime(timePrefix, sizeof(timePrefix), "%Y-%m-%dT%H:%M:%S ", &tb);
    }

    out << timePrefix;
  }

  // output prefix
  if (!_outputPrefix.empty()) {
    out << _outputPrefix << " ";
  }

  // append the process / thread identifier
  {
    char processPrefix[48];

    TRI_pid_t processId = Thread::currentProcessId();

    if (_showThreadIdentifier) {
      uint64_t threadNumber = Thread::currentThreadNumber();
      snprintf(processPrefix, sizeof(processPrefix), "[%llu-%llu] ",
               (unsigned long long)processId, (unsigned long long)threadNumber);
    } else {
      snprintf(processPrefix, sizeof(processPrefix), "[%llu] ",
               (unsigned long long)processId);
    }

    out << processPrefix;
  }

  // log level
  out << Logger::translateLogLevel(level) << ' ';

  // check if we must display the line number
  if (_showLineNumber) {
    char const* filename = file;

    if (_shortenFilenames) {
      // shorten file names from `/home/.../file.cpp` to just `file.cpp`
      char const* shortened = strrchr(filename, TRI_DIR_SEPARATOR_CHAR);
      if (shortened != nullptr) {
        filename = shortened + 1;
      }
    }
    out << '[' << filename << ':' << line << "] ";
  }

  // generate the complete message
  out << message;
  size_t offset = out.str().size() - message.size();
  auto msg = std::make_unique<LogMessage>(level, topicId, out.str(), offset);

  bool const isDirectLogLevel = (level == LogLevel::FATAL || level == LogLevel::ERR || level == LogLevel::WARN);

  // now either queue or output the message
  if (_threaded && !isDirectLogLevel) {
    try {
      _loggingThread->log(msg);
      return;
    } catch (...) {
      // fall-through to non-threaded logging
    }
  }

  LogAppender::log(msg.get());
}

////////////////////////////////////////////////////////////////////////////////
/// @brief initializes the logging component
////////////////////////////////////////////////////////////////////////////////

void Logger::initialize(bool threaded) {
  MUTEX_LOCKER(locker, _initializeMutex);

  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "Logger already initialized");
  }

  // logging is now active
  _active.store(true);

  // generate threaded logging?
  _threaded = threaded;

  if (threaded) {
    _loggingThread = std::make_unique<LogThread>("Logging");
    _loggingThread->start();
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief shuts down the logging components
////////////////////////////////////////////////////////////////////////////////

void Logger::shutdown() {
  MUTEX_LOCKER(locker, _initializeMutex);

  if (!_active) {
    // if logging not activated or already shutdown, then we can abort here
    return;
  }

  _active = false;

  // logging is now inactive (this will terminate the logging thread)
  // join with the logging thread
  if (_threaded) {
    // ignore all errors for now as we cannot log them anywhere...
    _loggingThread->beginShutdown();
    _loggingThread.reset();
  }

  // cleanup appenders
  LogAppender::shutdown();
}

////////////////////////////////////////////////////////////////////////////////
/// @brief tries to flush the logging
////////////////////////////////////////////////////////////////////////////////

void Logger::flush() {
  MUTEX_LOCKER(locker, _initializeMutex);

  if (!_active) {
    // logging not (or not yet) initialized
    return;
  }

  if (_threaded) {
    LogThread::flush();
  }
}
<commit_msg>flush log thread buffer when we're logging with fatal|err|warn levels<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2013 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Achim Brandt
/// @author Dr. Frank Celler
////////////////////////////////////////////////////////////////////////////////

#include "Logger.h"

#include "Basics/ArangoGlobalContext.h"
#include "Basics/ConditionLocker.h"
#include "Basics/Exceptions.h"
#include "Basics/MutexLocker.h"
#include "Basics/StringUtils.h"
#include "Basics/Thread.h"
#include "Basics/files.h"
#include "Logger/LogAppender.h"

using namespace arangodb;
using namespace arangodb::basics;

Mutex Logger::_initializeMutex;

std::atomic<bool> Logger::_active(false);
std::atomic<LogLevel> Logger::_level(LogLevel::INFO);

bool Logger::_showLineNumber(false);
bool Logger::_shortenFilenames(true);
bool Logger::_showThreadIdentifier(false);
bool Logger::_threaded(false);
bool Logger::_useLocalTime(false);
bool Logger::_keepLogRotate(false);
bool Logger::_useMicrotime(false);
std::string Logger::_outputPrefix("");

std::unique_ptr<LogThread> Logger::_loggingThread(nullptr);

LogLevel Logger::logLevel() { return _level.load(std::memory_order_relaxed); }

std::vector<std::pair<std::string, LogLevel>> Logger::logLevelTopics() {
  return LogTopic::logLevelTopics();
}

void Logger::setLogLevel(LogLevel level) {
  _level.store(level, std::memory_order_relaxed);
}

#include <iostream>

void Logger::setLogLevel(std::string const& levelName) {
  std::string l = StringUtils::tolower(levelName);
  std::vector<std::string> v = StringUtils::split(l, '=');

  if (v.empty() || v.size() > 2) {
    Logger::setLogLevel(LogLevel::INFO);
    LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "strange log level '" << levelName
             << "', using log level 'info'";
    return;
  }

  bool isGeneral = v.size() == 1;

  if (!isGeneral) {
    l = v[1];
  }

  LogLevel level;

  if (l == "fatal") {
    level = LogLevel::FATAL;
  } else if (l == "error" || l == "err") {
    level = LogLevel::ERR;
  } else if (l == "warning" || l == "warn") {
    level = LogLevel::WARN;
  } else if (l == "info") {
    level = LogLevel::INFO;
  } else if (l == "debug") {
    level = LogLevel::DEBUG;
  } else if (l == "trace") {
    level = LogLevel::TRACE;
  } else if (!isGeneral && (l.empty() || l == "default")) {
    level = LogLevel::DEFAULT;
  } else {
    if (isGeneral) {
      Logger::setLogLevel(LogLevel::INFO);
      LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "strange log level '" << levelName
               << "', using log level 'info'";
    } else {
      LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "strange log level '" << levelName << "'";
    }

    return;
  }

  if (isGeneral) {
    Logger::setLogLevel(level);
    // setting the log level for topic "fixme" is required here, too,
    // as "fixme" is the previous general log topic...
    LogTopic::setLogLevel(std::string("fixme"), level);
  } else {
    LogTopic::setLogLevel(v[0], level);
  }
}

void Logger::setLogLevel(std::vector<std::string> const& levels) {
  for (auto level : levels) {
    setLogLevel(level);
  }
}

// NOTE: this function should not be called if the logging is active.
void Logger::setOutputPrefix(std::string const& prefix) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change output prefix if logging is active");
  }

  _outputPrefix = prefix;
}

// NOTE: this function should not be called if the logging is active.
void Logger::setShowLineNumber(bool show) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change show line number if logging is active");
  }

  _showLineNumber = show;
}

// NOTE: this function should not be called if the logging is active.
void Logger::setShortenFilenames(bool shorten) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change shorten filenames if logging is active");
  }

  _shortenFilenames = shorten;
}

// NOTE: this function should not be called if the logging is active.
void Logger::setShowThreadIdentifier(bool show) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change show thread identifier if logging is active");
  }

  _showThreadIdentifier = show;
}

// NOTE: this function should not be called if the logging is active.
void Logger::setUseLocalTime(bool show) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change use local time if logging is active");
  }

  _useLocalTime = show;
}

// NOTE: this function should not be called if the logging is active.
void Logger::setUseMicrotime(bool show) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change use microtime if logging is active");
  }

  _useMicrotime = show;
}

// NOTE: this function should not be called if the logging is active.
void Logger::setKeepLogrotate(bool keep) {
  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "cannot change keep log rotate if logging is active");
  }

  _keepLogRotate = keep;
}

std::string const& Logger::translateLogLevel(LogLevel level) {
  static std::string DEFAULT = "DEFAULT";
  static std::string FATAL = "FATAL";
  static std::string ERR = "ERROR";
  static std::string WARN = "WARNING";
  static std::string INFO = "INFO";
  static std::string DEBUG = "DEBUG";
  static std::string TRACE = "TRACE";
  static std::string UNKNOWN = "UNKNOWN";

  switch (level) {
    case LogLevel::DEFAULT:
      return DEFAULT;
    case LogLevel::FATAL:
      return FATAL;
    case LogLevel::ERR:
      return ERR;
    case LogLevel::WARN:
      return WARN;
    case LogLevel::INFO:
      return INFO;
    case LogLevel::DEBUG:
      return DEBUG;
    case LogLevel::TRACE:
      return TRACE;
  }

  return UNKNOWN;
}

void Logger::log(char const* function, char const* file, long int line,
                 LogLevel level, size_t topicId,
                 std::string const& message) {
#ifdef _WIN32
  if (level == LogLevel::FATAL || level == LogLevel::ERR) {
    if (ArangoGlobalContext::CONTEXT != nullptr && ArangoGlobalContext::CONTEXT->useEventLog()) {
      TRI_LogWindowsEventlog(function, file, line, message);
    }

    // additionally log these errors to the debug output window in MSVC so
    // we can see them during development
    OutputDebugString(message.c_str());
  }
#endif

  if (!_active.load(std::memory_order_relaxed)) {
    LogAppender::writeStderr(level, message);
    return;
  }

  std::stringstream out;

  // time prefix
  if (_useMicrotime) {
    char buf[128];
    snprintf(buf, sizeof(buf), "%.6f ", TRI_microtime());
    out << buf;
  } else {
    char timePrefix[32];
    time_t tt = time(0);
    struct tm tb;

    if (!_useLocalTime) {
      // use GMtime
      TRI_gmtime(tt, &tb);
      strftime(timePrefix, sizeof(timePrefix), "%Y-%m-%dT%H:%M:%SZ ", &tb);
    } else {
      // use localtime
      TRI_localtime(tt, &tb);
      strftime(timePrefix, sizeof(timePrefix), "%Y-%m-%dT%H:%M:%S ", &tb);
    }

    out << timePrefix;
  }

  // output prefix
  if (!_outputPrefix.empty()) {
    out << _outputPrefix << " ";
  }

  // append the process / thread identifier
  {
    char processPrefix[48];

    TRI_pid_t processId = Thread::currentProcessId();

    if (_showThreadIdentifier) {
      uint64_t threadNumber = Thread::currentThreadNumber();
      snprintf(processPrefix, sizeof(processPrefix), "[%llu-%llu] ",
               (unsigned long long)processId, (unsigned long long)threadNumber);
    } else {
      snprintf(processPrefix, sizeof(processPrefix), "[%llu] ",
               (unsigned long long)processId);
    }

    out << processPrefix;
  }

  // log level
  out << Logger::translateLogLevel(level) << ' ';

  // check if we must display the line number
  if (_showLineNumber) {
    char const* filename = file;

    if (_shortenFilenames) {
      // shorten file names from `/home/.../file.cpp` to just `file.cpp`
      char const* shortened = strrchr(filename, TRI_DIR_SEPARATOR_CHAR);
      if (shortened != nullptr) {
        filename = shortened + 1;
      }
    }
    out << '[' << filename << ':' << line << "] ";
  }

  // generate the complete message
  out << message;
  size_t offset = out.str().size() - message.size();
  auto msg = std::make_unique<LogMessage>(level, topicId, out.str(), offset);

  // now either queue or output the message
  if (_threaded) {
    try {
      _loggingThread->log(msg);
      bool const isDirectLogLevel = (level == LogLevel::FATAL || level == LogLevel::ERR || level == LogLevel::WARN);
      if (isDirectLogLevel) {
        _loggingThread->flush();
      }
      return;
    } catch (...) {
      // fall-through to non-threaded logging
    }
  }

  LogAppender::log(msg.get());
}

////////////////////////////////////////////////////////////////////////////////
/// @brief initializes the logging component
////////////////////////////////////////////////////////////////////////////////

void Logger::initialize(bool threaded) {
  MUTEX_LOCKER(locker, _initializeMutex);

  if (_active) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "Logger already initialized");
  }

  // logging is now active
  _active.store(true);

  // generate threaded logging?
  _threaded = threaded;

  if (threaded) {
    _loggingThread = std::make_unique<LogThread>("Logging");
    _loggingThread->start();
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief shuts down the logging components
////////////////////////////////////////////////////////////////////////////////

void Logger::shutdown() {
  MUTEX_LOCKER(locker, _initializeMutex);

  if (!_active) {
    // if logging not activated or already shutdown, then we can abort here
    return;
  }

  _active = false;

  // logging is now inactive (this will terminate the logging thread)
  // join with the logging thread
  if (_threaded) {
    // ignore all errors for now as we cannot log them anywhere...
    _loggingThread->beginShutdown();
    _loggingThread.reset();
  }

  // cleanup appenders
  LogAppender::shutdown();
}

////////////////////////////////////////////////////////////////////////////////
/// @brief tries to flush the logging
////////////////////////////////////////////////////////////////////////////////

void Logger::flush() {
  MUTEX_LOCKER(locker, _initializeMutex);

  if (!_active) {
    // logging not (or not yet) initialized
    return;
  }

  if (_threaded) {
    LogThread::flush();
  }
}
<|endoftext|>


<commit_before>#include "Local.h"
#include "DungeonManager.h"

gameImports_s* trap;

static gameExports_s exportFns;
static int iLoadingScreen = 0;
static Image* ptLoadScreenImage = NULL;

void Game_Init() {
	trap->printf("--- New Game ---\n");
	ptLoadScreenImage = trap->RegisterImage("ui/images/loading");
	iLoadingScreen = 1;
}

void Game_Shutdown() {
	trap->printf("--- Quit Game ---\n");
	trap->ShutdownMaterials();
	delete thisClient;
	delete ptDungeonManager;
}

void Game_Load() {
	trap->InitMaterials();

	trap->RegisterCvarInt("cg_drawfps", "Draw FPS ingame? (1 = FPS, 2 = MS, 3 = both)", 1, 0);
	trap->RegisterCvarBool("cg_drawxy", "Draw mouse X/Y coordinates?", 1, false);
	trap->RegisterCvarBool("cg_drawworldxy", "Draw world X/Y coordinates?", 1, false);

	thisClient = new Client();

	ptDungeonManager = new DungeonManager();
	ptDungeonManager->StartBuild("Survivor's Camp");
	ptDungeonManager->SpawnPlayer("Survivor's Camp");

	iLoadingScreen = 0;
}

void Game_Frame() {
	if(iLoadingScreen == 1) {
		// Force it to draw the loading screen
		trap->DrawImageAspectCorrection(ptLoadScreenImage, 37.5, 37.5, 25, 25);
		iLoadingScreen++;
	}
	else if(iLoadingScreen == 2) {
		// Now do the actual loading
		Game_Load();
		iLoadingScreen = 0;
	}
	else {
		// In a multiplayer game, we would be looping through all 4 acts and running GetWorld()->Run,
		// but since we're only concerned about singleplayer, we're going to focus on the one that
		// our client is on
		ptDungeonManager->GetWorld(thisClient->ptPlayer->iAct)->Run();	// Server frame
		thisClient->Frame();											// Client frame
	}
}

void Game_OnMouseUp(int x, int y) {
	if(iLoadingScreen == 0) {
		thisClient->PassMouseUp(x, y);
	}
}

void Game_OnMouseDown(int x, int y) {
	if(iLoadingScreen == 0) {
		thisClient->PassMouseDown(x, y);
	}
}

void Game_OnMouseMove(int x, int y) {
	if(iLoadingScreen == 0) {
		thisClient->PassMouseMove(x, y);
	}
}

extern "C" {
	__declspec(dllexport) gameExports_s* GetRefAPI(gameImports_s* import) {
		trap = import;

		exportFns.init = Game_Init;
		exportFns.shutdown = Game_Shutdown;
		exportFns.runactiveframe = Game_Frame;
		exportFns.passmouseup = Game_OnMouseUp;
		exportFns.passmousedown = Game_OnMouseDown;
		exportFns.passmousemove = Game_OnMouseMove;
		return &exportFns;
	}
};<commit_msg>Fixed #21<commit_after>#include "Local.h"
#include "DungeonManager.h"

gameImports_s* trap;

static gameExports_s exportFns;
static int iLoadingScreen = 0;
static Image* ptLoadScreenImage = NULL;

void Game_Init() {
	trap->printf("--- New Game ---\n");
	ptLoadScreenImage = trap->RegisterImage("ui/images/loading");
	iLoadingScreen = 1;
}

void Game_Shutdown() {
	trap->printf("--- Quit Game ---\n");
	trap->ShutdownMaterials();
	delete thisClient;
	delete ptDungeonManager;
}

void Game_Load() {
	trap->InitMaterials();

	trap->RegisterCvarInt("cg_drawfps", "Draw FPS ingame? (1 = FPS, 2 = MS, 3 = both)", 1, 0);
	trap->RegisterCvarBool("cg_drawxy", "Draw mouse X/Y coordinates?", 1, false);
	trap->RegisterCvarBool("cg_drawworldxy", "Draw world X/Y coordinates?", 1, false);

	thisClient = new Client();

	ptDungeonManager = new DungeonManager();
	ptDungeonManager->StartBuild("Survivor's Camp");
	ptDungeonManager->SpawnPlayer("Survivor's Camp");

	iLoadingScreen = 0;
}

void Game_Frame() {
	if(iLoadingScreen == 1) {
		// Force it to draw the loading screen
		trap->DrawImageAspectCorrection(ptLoadScreenImage, 37.5, 37.5, 25, 25);
		iLoadingScreen++;
	}
	else if(iLoadingScreen == 2) {
		// Now do the actual loading
		Game_Load();
		iLoadingScreen = 0;
	}
	else {
		// In a multiplayer game, we would be looping through all 4 acts and running GetWorld()->Run,
		// but since we're only concerned about singleplayer, we're going to focus on the one that
		// our client is on
		ptDungeonManager->GetWorld(thisClient->ptPlayer->iAct)->Run();	// Server frame
		thisClient->Frame();											// Client frame
	}
}

void Game_OnMouseUp(int x, int y) {
	if(iLoadingScreen == 0) {
		thisClient->PassMouseUp(x, y);
	}
}

void Game_OnMouseDown(int x, int y) {
	if(iLoadingScreen == 0) {
		if(!trap->IsConsoleOpen()) {
			thisClient->PassMouseDown(x, y);
		}
	}
}

void Game_OnMouseMove(int x, int y) {
	if(iLoadingScreen == 0) {
		if(!trap->IsConsoleOpen()) {
			thisClient->PassMouseMove(x, y);
		}
	}
}

extern "C" {
	__declspec(dllexport) gameExports_s* GetRefAPI(gameImports_s* import) {
		trap = import;

		exportFns.init = Game_Init;
		exportFns.shutdown = Game_Shutdown;
		exportFns.runactiveframe = Game_Frame;
		exportFns.passmouseup = Game_OnMouseUp;
		exportFns.passmousedown = Game_OnMouseDown;
		exportFns.passmousemove = Game_OnMouseMove;
		return &exportFns;
	}
};<|endoftext|>


<commit_before>//===-- IsInf.cpp ---------------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Platform-independent wrapper around C99 isinf().
//
//===----------------------------------------------------------------------===//

#include "llvm/Config/config.h"
#if HAVE_ISINF_IN_MATH_H
# include <math.h>
#elif HAVE_ISINF_IN_CMATH
# include <cmath>
#elif HAVE_STD_ISINF_IN_CMATH
# include <cmath>
using std::isinf;
#elif HAVE_FINITE_IN_IEEEFP_H
// A handy workaround I found at http://www.unixguide.net/sun/faq ...
// apparently this has been a problem with Solaris for years.
# include <ieeefp.h>
static int isinf(double x) { return !finite(x) && x==x; }
#elif defined(_MSC_VER)
#include <float.h>
#define isinf(X) (!_finite(X))
#else
# error "Don't know how to get isinf()"
#endif

namespace llvm {

int IsInf (float f)  { return isinf (f); }
int IsInf (double d) { return isinf (d); }

}; // end namespace llvm;
<commit_msg>* Fix compilation on AIX: GCC's fixincludes eliminates isinf() declaration * Move file comment to the top of the header where it belongs<commit_after>//===-- IsInf.cpp - Platform-independent wrapper around C99 isinf() -------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/Config/config.h"

#if HAVE_ISINF_IN_MATH_H
# include <math.h>
#elif HAVE_ISINF_IN_CMATH
# include <cmath>
#elif HAVE_STD_ISINF_IN_CMATH
# include <cmath>
using std::isinf;
#elif HAVE_FINITE_IN_IEEEFP_H
// A handy workaround I found at http://www.unixguide.net/sun/faq ...
// apparently this has been a problem with Solaris for years.
# include <ieeefp.h>
static int isinf(double x) { return !finite(x) && x==x; }
#elif defined(_MSC_VER)
#include <float.h>
#define isinf(X) (!_finite(X))
#elif defined(_AIX) && defined(__GNUC__)
// GCC's fixincludes seems to be removing the isinf() declaration from the
// system header /usr/include/math.h
# include <math.h>
static int isinf(double x) { return !finite(x) && x==x; }
#else
# error "Don't know how to get isinf()"
#endif

namespace llvm {

int IsInf (float f)  { return isinf (f); }
int IsInf (double d) { return isinf (d); }

}; // end namespace llvm;
<|endoftext|>


<commit_before>//===-- Atomic.cpp - Atomic Operations --------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This header file implements atomic operations.
//
//===----------------------------------------------------------------------===//

#include "llvm/System/Atomic.h"
#include "llvm/Config/config.h"

using namespace llvm;

#if defined(_MSC_VER)
#include <windows.h>
#undef MemoryFence
#endif

void sys::MemoryFence() {
#if LLVM_MULTITHREADED==0
  return;
#else
#  if defined(__GNUC__)
  __sync_synchronize();
#  elif defined(_MSC_VER)
  MemoryBarrier();
#  else
# error No memory fence implementation for your platform!
#  endif
#endif
}

sys::cas_flag sys::CompareAndSwap(volatile sys::cas_flag* ptr,
                                  sys::cas_flag new_value,
                                  sys::cas_flag old_value) {
#if LLVM_MULTITHREADED==0
  sys::cas_flag result = *ptr;
  if (result == old_value)
    *ptr = new_value;
  return result;
#elif defined(__GNUC__)
  return __sync_val_compare_and_swap(ptr, old_value, new_value);
#elif defined(_MSC_VER)
  return InterlockedCompareExchange(ptr, new_value, old_value);
#else
#  error No compare-and-swap implementation for your platform!
#endif
}<commit_msg>At a newline at the end of this file.<commit_after>//===-- Atomic.cpp - Atomic Operations --------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This header file implements atomic operations.
//
//===----------------------------------------------------------------------===//

#include "llvm/System/Atomic.h"
#include "llvm/Config/config.h"

using namespace llvm;

#if defined(_MSC_VER)
#include <windows.h>
#undef MemoryFence
#endif

void sys::MemoryFence() {
#if LLVM_MULTITHREADED==0
  return;
#else
#  if defined(__GNUC__)
  __sync_synchronize();
#  elif defined(_MSC_VER)
  MemoryBarrier();
#  else
# error No memory fence implementation for your platform!
#  endif
#endif
}

sys::cas_flag sys::CompareAndSwap(volatile sys::cas_flag* ptr,
                                  sys::cas_flag new_value,
                                  sys::cas_flag old_value) {
#if LLVM_MULTITHREADED==0
  sys::cas_flag result = *ptr;
  if (result == old_value)
    *ptr = new_value;
  return result;
#elif defined(__GNUC__)
  return __sync_val_compare_and_swap(ptr, old_value, new_value);
#elif defined(_MSC_VER)
  return InterlockedCompareExchange(ptr, new_value, old_value);
#else
#  error No compare-and-swap implementation for your platform!
#endif
}
<|endoftext|>


<commit_before>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "gm.h"
#include "SkTypeface.h"

namespace skiagm {

class FontScalerGM : public GM {
public:
    FontScalerGM() {
        this->setBGColor(0xFFFFFFFF);
    }

    virtual ~FontScalerGM() {
    }

protected:
    virtual SkString onShortName() {
        return SkString("fontscaler");
    }

    virtual SkISize onISize() {
        return make_isize(1450, 750);
    }

    static void rotate_about(SkCanvas* canvas,
                             SkScalar degrees,
                             SkScalar px, SkScalar py) {
        canvas->translate(px, py);
        canvas->rotate(degrees);
        canvas->translate(-px, -py);
    }

    virtual void onDraw(SkCanvas* canvas) {
        SkPaint paint;

        paint.setAntiAlias(true);
        paint.setLCDRenderText(true);
        //With freetype the default (normal hinting) can be really ugly.
        //Most distros now set slight (vertical hinting only) in any event.
        paint.setHinting(SkPaint::kSlight_Hinting);
        SkSafeUnref(paint.setTypeface(SkTypeface::CreateFromName("Times Roman", SkTypeface::kNormal)));

        const char* text = "Hamburgefons ooo mmm";
        const size_t textLen = strlen(text);

        for (int j = 0; j < 2; ++j) {
            for (int i = 0; i < 6; ++i) {
                SkScalar x = SkIntToScalar(10);
                SkScalar y = SkIntToScalar(20);

                SkAutoCanvasRestore acr(canvas, true);
                canvas->translate(SkIntToScalar(50 + i * 230),
                                  SkIntToScalar(20));
                rotate_about(canvas, SkIntToScalar(i * 5), x, y * 10);

                {
                    SkPaint p;
                    p.setAntiAlias(true);
                    SkRect r;
                    r.set(x - SkIntToScalar(3), SkIntToScalar(15),
                          x - SkIntToScalar(1), SkIntToScalar(280));
                    canvas->drawRect(r, p);
                }

                int index = 0;
                for (int ps = 6; ps <= 22; ps++) {
                    paint.setTextSize(SkIntToScalar(ps));
                    canvas->drawText(text, textLen, x, y, paint);
                    y += paint.getFontMetrics(NULL);
                    index += 1;
                }
            }
            canvas->translate(0, SkIntToScalar(360));
            paint.setSubpixelText(true);
        }
    }

private:
    typedef GM INHERITED;
};

//////////////////////////////////////////////////////////////////////////////
#ifndef SK_BUILD_FOR_ANDROID
static GM* MyFactory(void*) { return new FontScalerGM; }
static GMRegistry reg(MyFactory);
#endif
}

<commit_msg>Re-enable gm fontscaler for Android<commit_after>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "gm.h"
#include "SkTypeface.h"

namespace skiagm {

class FontScalerGM : public GM {
public:
    FontScalerGM() {
        this->setBGColor(0xFFFFFFFF);
    }

    virtual ~FontScalerGM() {
    }

protected:
    virtual SkString onShortName() {
        return SkString("fontscaler");
    }

    virtual SkISize onISize() {
        return make_isize(1450, 750);
    }

    static void rotate_about(SkCanvas* canvas,
                             SkScalar degrees,
                             SkScalar px, SkScalar py) {
        canvas->translate(px, py);
        canvas->rotate(degrees);
        canvas->translate(-px, -py);
    }

    virtual void onDraw(SkCanvas* canvas) {
        SkPaint paint;

        paint.setAntiAlias(true);
        paint.setLCDRenderText(true);
        //With freetype the default (normal hinting) can be really ugly.
        //Most distros now set slight (vertical hinting only) in any event.
        paint.setHinting(SkPaint::kSlight_Hinting);
        SkSafeUnref(paint.setTypeface(SkTypeface::CreateFromName("Times Roman", SkTypeface::kNormal)));

        const char* text = "Hamburgefons ooo mmm";
        const size_t textLen = strlen(text);

        for (int j = 0; j < 2; ++j) {
            for (int i = 0; i < 6; ++i) {
                SkScalar x = SkIntToScalar(10);
                SkScalar y = SkIntToScalar(20);

                SkAutoCanvasRestore acr(canvas, true);
                canvas->translate(SkIntToScalar(50 + i * 230),
                                  SkIntToScalar(20));
                rotate_about(canvas, SkIntToScalar(i * 5), x, y * 10);

                {
                    SkPaint p;
                    p.setAntiAlias(true);
                    SkRect r;
                    r.set(x - SkIntToScalar(3), SkIntToScalar(15),
                          x - SkIntToScalar(1), SkIntToScalar(280));
                    canvas->drawRect(r, p);
                }

                int index = 0;
                for (int ps = 6; ps <= 22; ps++) {
                    paint.setTextSize(SkIntToScalar(ps));
                    canvas->drawText(text, textLen, x, y, paint);
                    y += paint.getFontMetrics(NULL);
                    index += 1;
                }
            }
            canvas->translate(0, SkIntToScalar(360));
            paint.setSubpixelText(true);
        }
    }

private:
    typedef GM INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

static GM* MyFactory(void*) { return new FontScalerGM; }
static GMRegistry reg(MyFactory);

}

<|endoftext|>


<commit_before>/*
 * (C) Copyright 2015 ETH Zurich Systems Group (http://www.systems.ethz.ch/) and others.
 *
 * 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.
 *
 * Contributors:
 *     Markus Pilman <mpilman@inf.ethz.ch>
 *     Simon Loesing <sloesing@inf.ethz.ch>
 *     Thomas Etter <etterth@gmail.com>
 *     Kevin Bocksrocker <kevin.bocksrocker@gmail.com>
 *     Lucas Braun <braunl@inf.ethz.ch>
 */

#include "InsertHash.hpp"

#include <util/Log.hpp>

#include <crossbow/allocator.hpp>
#include <crossbow/logger.hpp>

namespace tell {
namespace store {
namespace deltamain {
namespace {

/**
 * @brief Round up to the next highest power of 2
 *
 * Taken from https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
 */
size_t nextPowerOf2(size_t v) {
    v |= v >> 1;
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;
    v |= v >> 32;
    ++v;
    return v;
}

} // anonymous namespace

InsertTable::InsertTable(size_t capacity)
        : mCapacity(capacity),
          mBuckets(new AtomicEntry[mCapacity]),
          mHash(mCapacity) {
}

const void* InsertTable::get(uint64_t key) const {
    return execOnElement(key, static_cast<const void*>(nullptr), [] (const AtomicEntry& /* entry */, uintptr_t ptr) {
        return reinterpret_cast<const void*>(ptr);
    });
}

bool InsertTable::insert(uint64_t key, void* data, void** actualData /* = nullptr */) {
    LOG_ASSERT(data != nullptr, "Data pointer not allowed to be null");

    auto hash = mHash(key);
    for (auto pos = hash; pos < (hash + mCapacity); ++pos) {
        auto& entry = mBuckets[pos % mCapacity];

        // If the pointer is null then we reached the end of the overflow bucket and the element was not found
        auto ptr = entry.loadValue();
        if (ptr == crossbow::to_underlying(EntryMarker::FREE)) {
            // Try to claim the current bucket
            Entry oldEntry;
            Entry newEntry(key, reinterpret_cast<uintptr_t>(data));
            if (entry.compare_exchange_strong(oldEntry, newEntry)) {
                return true;
            }

            // Move to the next bucket if the current one was claimed by an insert with a different key
            if (oldEntry.key != key) {
                continue;
            }

            // Update the pointer
            ptr = oldEntry.value;
        } else {
            // If the stored key is different than the target key we have to search the next bucket
            auto k = entry.loadKey();
            if (k != key) {
                continue;
            }
        }

        // Try to claim the bucket if the pointer marks a deletion
        if (ptr == crossbow::to_underlying(EntryMarker::DELETED)
                && entry.updateValue(ptr, reinterpret_cast<uintptr_t>(data))) {
            return true;
        }

        // The element was not deleted
        if (actualData) *actualData = reinterpret_cast<void*>(ptr);
        return false;
    }

    LOG_ERROR("Hash table is full");
    return false;
}

bool InsertTable::update(uint64_t key, const void* oldData, void* newData, void** actualData /* = nullptr */) {
    LOG_ASSERT(newData != nullptr, "Data pointer not allowed to be null");
    return internalUpdate(key, oldData, reinterpret_cast<uintptr_t>(newData), actualData);
}

bool InsertTable::remove(uint64_t key, const void* oldData, void** actualData /* = nullptr */) {
    return internalUpdate(key, oldData, crossbow::to_underlying(EntryMarker::DELETED), actualData);
}

bool InsertTable::internalUpdate(uint64_t key, const void* oldData, uintptr_t newData, void** actualData) {
    return execOnElement(key, false, [oldData, newData, actualData] (AtomicEntry& entry, uintptr_t ptr) {
        auto expected = reinterpret_cast<uintptr_t>(oldData);
        if (ptr != expected) {
            if (actualData) *actualData = reinterpret_cast<void*>(ptr);
            return false;
        }

        // Try to update the pointer
        if (entry.updateValue(expected, newData)) {
            return true;
        }

        // Set the actual data only in case its not a deletion
        if (expected != crossbow::to_underlying(EntryMarker::DELETED)) {
            if (actualData) *actualData = reinterpret_cast<void*>(expected);
        }
        return false;
    });
}

template <typename T, typename F>
T InsertTable::execOnElement(uint64_t key, T notFound, F fun) const {
    auto hash = mHash(key);
    for (auto pos = hash; pos < (hash + mCapacity); ++pos) {
        auto& entry = mBuckets[pos % mCapacity];

        // If the pointer is null then we reached the end of the overflow bucket and the element was not found
        auto ptr = entry.loadValue();
        if (ptr == crossbow::to_underlying(EntryMarker::FREE)) {
            return notFound;
        }

        // If the stored key is different than the target key we have to search the next bucket
        auto k = entry.loadKey();
        if (k != key) {
            continue;
        }

        // The entry was marked as deleted
        if (ptr == crossbow::to_underlying(EntryMarker::DELETED)) {
            return notFound;
        }

        // The element was found
        return fun(entry, ptr);
    }

    LOG_ERROR("Hash table is full");
    return notFound;
}

template <typename T, typename F>
T InsertTable::execOnElement(uint64_t key, T notFound, F fun) {
    return const_cast<const InsertTable*>(this)->execOnElement(key, notFound,
            [&fun] (const AtomicEntry& entry, uintptr_t ptr) {
        return fun(const_cast<AtomicEntry&>(entry), ptr);
    });
}

DynamicInsertTable::DynamicInsertTable(size_t minimumCapacity)
        : mMinimumCapacity(minimumCapacity),
          mHeadList(crossbow::allocator::construct<DynamicInsertTableEntry>(nullptr, minimumCapacity)) {
    LOG_ASSERT(minimumCapacity > 1, "Minimum capacity must be larger than 1");
    LOG_ASSERT(isPowerOf2(minimumCapacity), "Minimum capacity must be power of 2");
}

DynamicInsertTable::~DynamicInsertTable() {
    auto headList = mHeadList.exchange(nullptr);
    while (headList != nullptr) {
        auto previousList = headList->nextList.load();
        crossbow::allocator::destroy_now(headList);
        headList = previousList;
    }
}

const void* DynamicInsertTable::get(uint64_t key, DynamicInsertTableEntry** headList) const {
    auto insertList = mHeadList.load();
    if (headList) *headList = insertList;
    LOG_ASSERT(insertList != nullptr, "Head insert list must never be null");

    for (auto currentList = insertList; currentList; currentList = currentList->nextList.load()) {
        if (auto ptr = currentList->table.get(key)) {
            return ptr;
        }
    }
    return nullptr;
}

bool DynamicInsertTable::insert(uint64_t key, void* data) {
    DynamicInsertTableEntry* headList = nullptr;
    if (get(key, &headList)) {
        return false;
    }
    return insert(key, data, headList);
}

bool DynamicInsertTable::insert(uint64_t key, void* data, DynamicInsertTableEntry* headList) {
    while (true) {
        // Try to insert the element in the insert table
        if (!headList->table.insert(key, data)) {
            return false;
        }
        auto currentSize = headList->size.fetch_add(1u) + 1;

        // Check if a new head table was allocated in the meantime
        // The insert has to be repeated in the new head table
        auto newHeadList = mHeadList.load();
        if (newHeadList == headList) {
            // Allocate a new head table and repeat the insert if the current table reached its maximum size
            if (currentSize >= headList->maximumSize) {
                headList = allocateHead(headList, currentSize);
                continue;
            }
            return true;
        }

        // Check if an insert took place that in the older tables (up to the previous insert table)
        for (auto currentList = newHeadList->nextList.load(); currentList && currentList != headList;
                currentList = currentList->nextList.load()) {
            if (currentList->table.get(key)) {
                // The element was found in a newer insert table: Abort the current insertion
                return false;
            }
        }

        // Restart the insert in the newest table
        headList = newHeadList;
    }
}

bool DynamicInsertTable::remove(uint64_t key, const void* oldData, DynamicInsertTableEntry* tailList) {
    auto insertList = mHeadList.load();
    auto endList = tailList->nextList.load();
    LOG_ASSERT(insertList != nullptr, "Head insert list must never be null");

    for (auto currentList = insertList; currentList || currentList == endList;
            currentList = currentList->nextList.load()) {
        // Try to remove the element in the insert table
        void* actualData = nullptr;
        if (currentList->table.remove(key, oldData, &actualData)) {
            return true;
        }

        // Check if the element does not exist in the current table, retry with the next
        if (actualData == nullptr) {
            continue;
        }

        // Element exists and changed in the meantime
        return false;
    }

    // Element was not found
    return false;
}

DynamicInsertTableEntry* DynamicInsertTable::allocateHead() {
    auto headList = mHeadList.load();
    return allocateHead(headList, headList->size.load());
}

void DynamicInsertTable::truncate(DynamicInsertTableEntry* endList) {
    // Truncate the insert hash table and free all tables using the epoch mechanism
    auto oldList = endList->nextList.exchange(nullptr);
    while (oldList != nullptr) {
        auto previousList = oldList->nextList.load();
        crossbow::allocator::destroy(oldList);
        oldList = previousList;
    }
}

DynamicInsertTableEntry* DynamicInsertTable::allocateHead(DynamicInsertTableEntry* headList, size_t headSize) {
    auto capacity = std::max(mMinimumCapacity, nextPowerOf2(headSize));
    LOG_ASSERT(isPowerOf2(capacity), "Capacity must be power of 2");
    auto newHeadList = crossbow::allocator::construct<DynamicInsertTableEntry>(headList, capacity);

    // Set the newly allocated table as new head table
    // If this fails another hash table was allocated in the meantime by somebody else
    if (!mHeadList.compare_exchange_strong(headList, newHeadList)) {
        crossbow::allocator::destroy_now(newHeadList);
        newHeadList = headList;
    }
    return newHeadList;
}

} // namespace deltamain
} // namespace store
} // namespace tell
<commit_msg>Always double the capacity when growing through insertion<commit_after>/*
 * (C) Copyright 2015 ETH Zurich Systems Group (http://www.systems.ethz.ch/) and others.
 *
 * 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.
 *
 * Contributors:
 *     Markus Pilman <mpilman@inf.ethz.ch>
 *     Simon Loesing <sloesing@inf.ethz.ch>
 *     Thomas Etter <etterth@gmail.com>
 *     Kevin Bocksrocker <kevin.bocksrocker@gmail.com>
 *     Lucas Braun <braunl@inf.ethz.ch>
 */

#include "InsertHash.hpp"

#include <util/Log.hpp>

#include <crossbow/allocator.hpp>
#include <crossbow/logger.hpp>

namespace tell {
namespace store {
namespace deltamain {
namespace {

/**
 * @brief Round up to the next highest power of 2
 *
 * Taken from https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
 */
size_t nextPowerOf2(size_t v) {
    v |= v >> 1;
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;
    v |= v >> 32;
    ++v;
    return v;
}

} // anonymous namespace

InsertTable::InsertTable(size_t capacity)
        : mCapacity(capacity),
          mBuckets(new AtomicEntry[mCapacity]),
          mHash(mCapacity) {
}

const void* InsertTable::get(uint64_t key) const {
    return execOnElement(key, static_cast<const void*>(nullptr), [] (const AtomicEntry& /* entry */, uintptr_t ptr) {
        return reinterpret_cast<const void*>(ptr);
    });
}

bool InsertTable::insert(uint64_t key, void* data, void** actualData /* = nullptr */) {
    LOG_ASSERT(data != nullptr, "Data pointer not allowed to be null");

    auto hash = mHash(key);
    for (auto pos = hash; pos < (hash + mCapacity); ++pos) {
        auto& entry = mBuckets[pos % mCapacity];

        // If the pointer is null then we reached the end of the overflow bucket and the element was not found
        auto ptr = entry.loadValue();
        if (ptr == crossbow::to_underlying(EntryMarker::FREE)) {
            // Try to claim the current bucket
            Entry oldEntry;
            Entry newEntry(key, reinterpret_cast<uintptr_t>(data));
            if (entry.compare_exchange_strong(oldEntry, newEntry)) {
                return true;
            }

            // Move to the next bucket if the current one was claimed by an insert with a different key
            if (oldEntry.key != key) {
                continue;
            }

            // Update the pointer
            ptr = oldEntry.value;
        } else {
            // If the stored key is different than the target key we have to search the next bucket
            auto k = entry.loadKey();
            if (k != key) {
                continue;
            }
        }

        // Try to claim the bucket if the pointer marks a deletion
        if (ptr == crossbow::to_underlying(EntryMarker::DELETED)
                && entry.updateValue(ptr, reinterpret_cast<uintptr_t>(data))) {
            return true;
        }

        // The element was not deleted
        if (actualData) *actualData = reinterpret_cast<void*>(ptr);
        return false;
    }

    LOG_ERROR("Hash table is full");
    return false;
}

bool InsertTable::update(uint64_t key, const void* oldData, void* newData, void** actualData /* = nullptr */) {
    LOG_ASSERT(newData != nullptr, "Data pointer not allowed to be null");
    return internalUpdate(key, oldData, reinterpret_cast<uintptr_t>(newData), actualData);
}

bool InsertTable::remove(uint64_t key, const void* oldData, void** actualData /* = nullptr */) {
    return internalUpdate(key, oldData, crossbow::to_underlying(EntryMarker::DELETED), actualData);
}

bool InsertTable::internalUpdate(uint64_t key, const void* oldData, uintptr_t newData, void** actualData) {
    return execOnElement(key, false, [oldData, newData, actualData] (AtomicEntry& entry, uintptr_t ptr) {
        auto expected = reinterpret_cast<uintptr_t>(oldData);
        if (ptr != expected) {
            if (actualData) *actualData = reinterpret_cast<void*>(ptr);
            return false;
        }

        // Try to update the pointer
        if (entry.updateValue(expected, newData)) {
            return true;
        }

        // Set the actual data only in case its not a deletion
        if (expected != crossbow::to_underlying(EntryMarker::DELETED)) {
            if (actualData) *actualData = reinterpret_cast<void*>(expected);
        }
        return false;
    });
}

template <typename T, typename F>
T InsertTable::execOnElement(uint64_t key, T notFound, F fun) const {
    auto hash = mHash(key);
    for (auto pos = hash; pos < (hash + mCapacity); ++pos) {
        auto& entry = mBuckets[pos % mCapacity];

        // If the pointer is null then we reached the end of the overflow bucket and the element was not found
        auto ptr = entry.loadValue();
        if (ptr == crossbow::to_underlying(EntryMarker::FREE)) {
            return notFound;
        }

        // If the stored key is different than the target key we have to search the next bucket
        auto k = entry.loadKey();
        if (k != key) {
            continue;
        }

        // The entry was marked as deleted
        if (ptr == crossbow::to_underlying(EntryMarker::DELETED)) {
            return notFound;
        }

        // The element was found
        return fun(entry, ptr);
    }

    LOG_ERROR("Hash table is full");
    return notFound;
}

template <typename T, typename F>
T InsertTable::execOnElement(uint64_t key, T notFound, F fun) {
    return const_cast<const InsertTable*>(this)->execOnElement(key, notFound,
            [&fun] (const AtomicEntry& entry, uintptr_t ptr) {
        return fun(const_cast<AtomicEntry&>(entry), ptr);
    });
}

DynamicInsertTable::DynamicInsertTable(size_t minimumCapacity)
        : mMinimumCapacity(minimumCapacity),
          mHeadList(crossbow::allocator::construct<DynamicInsertTableEntry>(nullptr, minimumCapacity)) {
    LOG_ASSERT(minimumCapacity > 1, "Minimum capacity must be larger than 1");
    LOG_ASSERT(isPowerOf2(minimumCapacity), "Minimum capacity must be power of 2");
}

DynamicInsertTable::~DynamicInsertTable() {
    auto headList = mHeadList.exchange(nullptr);
    while (headList != nullptr) {
        auto previousList = headList->nextList.load();
        crossbow::allocator::destroy_now(headList);
        headList = previousList;
    }
}

const void* DynamicInsertTable::get(uint64_t key, DynamicInsertTableEntry** headList) const {
    auto insertList = mHeadList.load();
    if (headList) *headList = insertList;
    LOG_ASSERT(insertList != nullptr, "Head insert list must never be null");

    for (auto currentList = insertList; currentList; currentList = currentList->nextList.load()) {
        if (auto ptr = currentList->table.get(key)) {
            return ptr;
        }
    }
    return nullptr;
}

bool DynamicInsertTable::insert(uint64_t key, void* data) {
    DynamicInsertTableEntry* headList = nullptr;
    if (get(key, &headList)) {
        return false;
    }
    return insert(key, data, headList);
}

bool DynamicInsertTable::insert(uint64_t key, void* data, DynamicInsertTableEntry* headList) {
    while (true) {
        // Try to insert the element in the insert table
        if (!headList->table.insert(key, data)) {
            return false;
        }
        auto currentSize = headList->size.fetch_add(1u) + 1;

        // Check if a new head table was allocated in the meantime
        // The insert has to be repeated in the new head table
        auto newHeadList = mHeadList.load();
        if (newHeadList == headList) {
            // Allocate a new head table and repeat the insert if the current table reached its maximum size
            if (currentSize >= headList->maximumSize) {
                headList = allocateHead(headList, headList->table.capacity() * 2u);
                continue;
            }
            return true;
        }

        // Check if an insert took place that in the older tables (up to the previous insert table)
        for (auto currentList = newHeadList->nextList.load(); currentList && currentList != headList;
                currentList = currentList->nextList.load()) {
            if (currentList->table.get(key)) {
                // The element was found in a newer insert table: Abort the current insertion
                return false;
            }
        }

        // Restart the insert in the newest table
        headList = newHeadList;
    }
}

bool DynamicInsertTable::remove(uint64_t key, const void* oldData, DynamicInsertTableEntry* tailList) {
    auto insertList = mHeadList.load();
    auto endList = tailList->nextList.load();
    LOG_ASSERT(insertList != nullptr, "Head insert list must never be null");

    for (auto currentList = insertList; currentList || currentList == endList;
            currentList = currentList->nextList.load()) {
        // Try to remove the element in the insert table
        void* actualData = nullptr;
        if (currentList->table.remove(key, oldData, &actualData)) {
            return true;
        }

        // Check if the element does not exist in the current table, retry with the next
        if (actualData == nullptr) {
            continue;
        }

        // Element exists and changed in the meantime
        return false;
    }

    // Element was not found
    return false;
}

DynamicInsertTableEntry* DynamicInsertTable::allocateHead() {
    auto headList = mHeadList.load();
    auto headSize = headList->size.load();

    // If the new capacity is below the threshold multiply it by two
    auto capacity = std::max(mMinimumCapacity, nextPowerOf2(headSize));
    if (headSize * 4u >= capacity * 3u) {
        capacity *= 2u;
    }
    return allocateHead(headList, capacity);
}

void DynamicInsertTable::truncate(DynamicInsertTableEntry* endList) {
    // Truncate the insert hash table and free all tables using the epoch mechanism
    auto oldList = endList->nextList.exchange(nullptr);
    while (oldList != nullptr) {
        auto previousList = oldList->nextList.load();
        crossbow::allocator::destroy(oldList);
        oldList = previousList;
    }
}

DynamicInsertTableEntry* DynamicInsertTable::allocateHead(DynamicInsertTableEntry* headList, size_t capacity) {
    LOG_ASSERT(isPowerOf2(capacity), "Capacity must be power of 2");
    auto newHeadList = crossbow::allocator::construct<DynamicInsertTableEntry>(headList, capacity);

    // Set the newly allocated table as new head table
    // If this fails another hash table was allocated in the meantime by somebody else
    if (!mHeadList.compare_exchange_strong(headList, newHeadList)) {
        crossbow::allocator::destroy_now(newHeadList);
        newHeadList = headList;
    }
    return newHeadList;
}

} // namespace deltamain
} // namespace store
} // namespace tell
<|endoftext|>


<commit_before>/***************************************************************************
 *  The Kernel-Machine Library                                             *
 *  Copyright (C) 2004, 2005 by Rutger W. ter Borg                         *
 *                                                                         *
 *  This program is free software; you can redistribute it and/or          *
 *  modify it under the terms of the GNU General Public License            *
 *  as published by the Free Software Foundation; either version 2         *
 *  of the License, or (at your option) any later version.                 *
 *                                                                         *
 *  This program is distributed in the hope that it will be useful,        *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                           *
 *                                                                         *
 *  You should have received a copy of the GNU General Public License      *
 *  along with this program; if not, write to the Free Software            *
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307  *
 ***************************************************************************/

#ifndef GAUSSIAN_HPP
#define GAUSSIAN_HPP

#include <boost/call_traits.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/split_member.hpp>
#include <boost/type_traits.hpp>
#include <kml/input_value.hpp>
#include <kml/distance.hpp>
#include <cassert>

namespace kml {

/*!
\brief Gaussian kernel of the form \f$ f(x)=e^{-\gamma\|x - \mu\|} \f$ where
	\f$ \gamma = \frac{1}{\sigma^2} \f$
\param T defines the underlying input data type
	(distance_square<T, T> must be defined, which is done for
	scalar and vector types in distance.hpp)

This is a template class that creates a function for the Gaussian kernel.

\todo
- clean-up
- finish documentation
- complexity guarantees
- loading and saving

\ingroup kernels
*/

template<typename T>
class gaussian: public std::binary_function<T, T, typename input_value<T>::type> {
public:
    BOOST_SERIALIZATION_SPLIT_MEMBER()
    typedef gaussian<T> type;
    friend class boost::serialization::access;
    typedef typename input_value<T>::type scalar_type;
    typedef typename mpl::int_<0>::type derivative_order;
    typedef typename input_value<T>::type return_type;

    /*! Construct an uninitialised Gaussian kernel */
    gaussian() {}

    /*! Refinement of CopyConstructable */
    gaussian( type const &other ) {
       copy( other );
    }

    /*! Refinement of Assignable */
    type &operator=( type const &other ) {
	if (this != &other) { destroy(); copy(other); }
	return *this;
    }

    /*! Construct a Gaussian kernel by providing a value for \f$ \sigma \f$ */
	gaussian( typename boost::call_traits<scalar_type>::param_type sigma ) {
       set_width(sigma);
    }

    /*! Kernel constructor by providing TokenIterators */
	template<typename TokenIterator>
       gaussian( TokenIterator const begin, TokenIterator const end ) {
		if (begin == end ) {
			// default value
			set_width( 1.0 );
		} else {
			set_width( boost::lexical_cast<double>( *begin ) );
		}
	}

    /*! Returns the result of the evaluation of the Gaussian kernel for these points
		\param u input pattern u
        \param v input pattern v
	\return \f$ e^{-\gamma\|u - v\|} \f$
    */
    scalar_type operator()( T const &u, T const &v ) const {
        return std::exp( exp_factor * distance_square( u, v ) );
    }

	/*! Sets \f$ \sigma \f$. */
    void set_width( typename boost::call_traits<scalar_type>::param_type sigma ) {
    	assert( sigma > 0.0 );
	width = sigma;
	exp_factor = -1.0 / (2.0*sigma*sigma);
    }

	/*! Returns \f$ \sigma \f$. */
	scalar_type const get_width() const {
    	return width;
    }

   	/*! Sets \f$ \gamma \f$. */
	void set_gamma( typename boost::call_traits<scalar_type>::param_type gamma ) {
	set_scale_factor( gamma );
    }

    /*! Sets \f$ \gamma \f$. */
	void set_scale_factor( typename boost::call_traits<scalar_type>::param_type gamma ) {
        assert( gamma > 0.0 );
	width = std::sqrt(0.5 / gamma);
	exp_factor = -gamma;
    }

    /*! Returns \f$ \gamma \f$. */
    scalar_type const get_gamma() const {
    	return -exp_factor;
    }

    /*! Returns \f$ \gamma \f$. */
    scalar_type const get_scale_factor() const {
    	return -exp_factor;
    }

    /*! The dimension of the feature space */
    scalar_type const dimension() const {
	return std::numeric_limits<scalar_type>::infinity();
    }

    template<class Archive>
    void load( Archive &archive, unsigned int const version ) {
    	archive & width;
	exp_factor = -1.0 / (2.0*width*width);
    }

    template<class Archive>
    void save( Archive &archive, unsigned int const version ) const {
    	archive & width;
    }

    friend std::ostream& operator<<(std::ostream &os, type const &k) {
	os << "Gaussian kernel (exp(-||u-v||^2/(2*" << k.width << "^2))" << std::endl;
	return os;
    }

private:
    void copy( type const &other ) {
	width = other.width;
	exp_factor = other.exp_factor;
    }
    void destroy() {}

    scalar_type width;
    scalar_type exp_factor;
};

} // namespace kml

#endif

<commit_msg>fixed missing include in gaussian.hpp<commit_after>/***************************************************************************
 *  The Kernel-Machine Library                                             *
 *  Copyright (C) 2004, 2005 by Rutger W. ter Borg                         *
 *                                                                         *
 *  This program is free software; you can redistribute it and/or          *
 *  modify it under the terms of the GNU General Public License            *
 *  as published by the Free Software Foundation; either version 2         *
 *  of the License, or (at your option) any later version.                 *
 *                                                                         *
 *  This program is distributed in the hope that it will be useful,        *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                           *
 *                                                                         *
 *  You should have received a copy of the GNU General Public License      *
 *  along with this program; if not, write to the Free Software            *
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307  *
 ***************************************************************************/

#ifndef GAUSSIAN_HPP
#define GAUSSIAN_HPP

#include <boost/call_traits.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/split_member.hpp>
#include <boost/type_traits.hpp>
#include <kml/input_value.hpp>
#include <kml/distance.hpp>
#include <cassert>

namespace kml {

/*!
\brief Gaussian kernel of the form \f$ f(x)=e^{-\gamma\|x - \mu\|} \f$ where
	\f$ \gamma = \frac{1}{\sigma^2} \f$
\param T defines the underlying input data type
	(distance_square<T, T> must be defined, which is done for
	scalar and vector types in distance.hpp)

This is a template class that creates a function for the Gaussian kernel.

\todo
- clean-up
- finish documentation
- complexity guarantees
- loading and saving

\ingroup kernels
*/

template<typename T>
class gaussian: public std::binary_function<T, T, typename input_value<T>::type> {
public:
    BOOST_SERIALIZATION_SPLIT_MEMBER()
    typedef gaussian<T> type;
    friend class boost::serialization::access;
    typedef typename input_value<T>::type scalar_type;
    typedef typename mpl::int_<0>::type derivative_order;
    typedef typename input_value<T>::type return_type;

    /*! Construct an uninitialised Gaussian kernel */
    gaussian() {}

    /*! Refinement of CopyConstructable */
    gaussian( type const &other ) {
       copy( other );
    }

    /*! Refinement of Assignable */
    type &operator=( type const &other ) {
	if (this != &other) { destroy(); copy(other); }
	return *this;
    }

    /*! Construct a Gaussian kernel by providing a value for \f$ \sigma \f$ */
	gaussian( typename boost::call_traits<scalar_type>::param_type sigma ) {
       set_width(sigma);
    }

    /*! Kernel constructor by providing TokenIterators */
	template<typename TokenIterator>
       gaussian( TokenIterator const begin, TokenIterator const end ) {
		if (begin == end ) {
			// default value
			set_width( 1.0 );
		} else {
			set_width( boost::lexical_cast<double>( *begin ) );
		}
	}

    /*! Returns the result of the evaluation of the Gaussian kernel for these points
		\param u input pattern u
        \param v input pattern v
	\return \f$ e^{-\gamma\|u - v\|} \f$
    */
    scalar_type operator()( T const &u, T const &v ) const {
        return std::exp( exp_factor * distance_square( u, v ) );
    }

	/*! Sets \f$ \sigma \f$. */
    void set_width( typename boost::call_traits<scalar_type>::param_type sigma ) {
    	assert( sigma > 0.0 );
	width = sigma;
	exp_factor = -1.0 / (2.0*sigma*sigma);
    }

	/*! Returns \f$ \sigma \f$. */
	scalar_type const get_width() const {
    	return width;
    }

   	/*! Sets \f$ \gamma \f$. */
	void set_gamma( typename boost::call_traits<scalar_type>::param_type gamma ) {
	set_scale_factor( gamma );
    }

    /*! Sets \f$ \gamma \f$. */
	void set_scale_factor( typename boost::call_traits<scalar_type>::param_type gamma ) {
        assert( gamma > 0.0 );
	width = std::sqrt(0.5 / gamma);
	exp_factor = -gamma;
    }

    /*! Returns \f$ \gamma \f$. */
    scalar_type const get_gamma() const {
    	return -exp_factor;
    }

    /*! Returns \f$ \gamma \f$. */
    scalar_type const get_scale_factor() const {
    	return -exp_factor;
    }

    /*! The dimension of the feature space */
    scalar_type const dimension() const {
	return std::numeric_limits<scalar_type>::infinity();
    }

    template<class Archive>
    void load( Archive &archive, unsigned int const version ) {
    	archive & width;
	exp_factor = -1.0 / (2.0*width*width);
    }

    template<class Archive>
    void save( Archive &archive, unsigned int const version ) const {
    	archive & width;
    }

    friend std::ostream& operator<<(std::ostream &os, type const &k) {
	os << "Gaussian kernel (exp(-||u-v||^2/(2*" << k.width << "^2))" << std::endl;
	return os;
    }

private:
    void copy( type const &other ) {
	width = other.width;
	exp_factor = other.exp_factor;
    }
    void destroy() {}

    scalar_type width;
    scalar_type exp_factor;
};

} // namespace kml

#endif

<|endoftext|>


<commit_before>#ifndef SILICIUM_HTML_HPP
#define SILICIUM_HTML_HPP

#include <silicium/source/source.hpp>
#include <silicium/sink/sink.hpp>

namespace Si
{
	namespace html
	{
		template <class CharSink, class Char>
		void write_char(CharSink &&sink, Char c)
		{
			using Si::append;
			switch (c)
			{
			case '&' : append(sink, "&"); break;
			case '<' : append(sink, "<"); break;
			case '>' : append(sink, ">"); break;
			case '\'': append(sink, "'"); break;
			case '"' : append(sink, """); break;
			default:
				append(sink, c);
				break;
			}
		}

		template <class CharSink>
		void write_string(
			CharSink &&sink,
			std::string const &text)
		{
			for (auto c : text)
			{
				write_char(sink, c);
			}
		}

		template <class CharSink>
		void open_attributed_element(
			CharSink &&sink,
			std::string const &name)
		{
			append(sink, '<');
			write_string(sink, name);
		}

		template <class CharSink>
		void finish_attributes(
			CharSink &&sink)
		{
			append(sink, '>');
		}

		template <class CharSink>
		void add_attribute(
			CharSink &&sink,
			std::string const &key,
			std::string const &value)
		{
			append(sink, key);
			append(sink, "=\"");
			write_escaped(sink, value);
			append(sink, '"');
		}

		template <class CharSink>
		void open_element(
			CharSink &&sink,
			std::string const &name)
		{
			open_attributed_element(sink, name);
			finish_attributes(sink);
		}

		template <class CharSink>
		void close_element(
			CharSink &&sink,
			std::string const &name)
		{
			using Si::append;
			append(sink, '<');
			append(sink, '/');
			append(sink, name);
			append(sink, '>');
		}

		template <class CharSink>
		struct generator
		{
			generator()
			{
			}

			explicit generator(CharSink out)
				: m_out(std::move(out))
			{
			}

			template <class ContentMaker>
			void element(std::string const &name, ContentMaker make_content)
			{
				open_element(m_out, name);
				make_content();
				close_element(m_out, name);
			}

			void write(std::string const &text)
			{
				write_string(m_out, text);
			}

		private:

			CharSink m_out;
		};

		template <class CharSink>
		auto make_generator(CharSink &&sink)
			-> generator<typename std::decay<CharSink>::type>
		{
			return generator<typename std::decay<CharSink>::type>(std::forward<CharSink>(sink));
		}
	}
}

#endif
<commit_msg>html: do not require the potentially expensive std::string as the text type everywhere<commit_after>#ifndef SILICIUM_HTML_HPP
#define SILICIUM_HTML_HPP

#include <silicium/source/source.hpp>
#include <silicium/sink/sink.hpp>
#include <boost/utility/string_ref.hpp>

namespace Si
{
	template <class CharRange>
	auto make_range_from_string_like(CharRange &&range)
	{
		return std::forward<CharRange>(range);
	}

	template <class Char>
	auto make_range_from_string_like(Char const *c_str)
	{
		return make_c_str_range(c_str);
	}

	namespace html
	{
		template <class CharSink, class Char>
		void write_char(CharSink &&sink, Char c)
		{
			using Si::append;
			switch (c)
			{
			case '&' : append(sink, "&"); break;
			case '<' : append(sink, "<"); break;
			case '>' : append(sink, ">"); break;
			case '\'': append(sink, "'"); break;
			case '"' : append(sink, """); break;
			default:
				append(sink, c);
				break;
			}
		}

		template <class CharSink, class StringLike>
		void write_string(
			CharSink &&sink,
			StringLike const &text)
		{
			for (auto c : make_range_from_string_like(text))
			{
				write_char(sink, c);
			}
		}

		template <class CharSink, class StringLike>
		void open_attributed_element(
			CharSink &&sink,
			StringLike const &name)
		{
			append(sink, '<');
			write_string(sink, name);
		}

		template <class CharSink>
		void finish_attributes(
			CharSink &&sink)
		{
			append(sink, '>');
		}

		template <class CharSink, class KeyStringLike, class ValueStringLike>
		void add_attribute(
			CharSink &&sink,
			KeyStringLike const &key,
			ValueStringLike const &value)
		{
			append(sink, key);
			append(sink, "=\"");
			write_escaped(sink, value);
			append(sink, '"');
		}

		template <class CharSink, class StringLike>
		void open_element(
			CharSink &&sink,
			StringLike const &name)
		{
			open_attributed_element(sink, name);
			finish_attributes(sink);
		}

		template <class CharSink, class StringLike>
		void close_element(
			CharSink &&sink,
			StringLike const &name)
		{
			using Si::append;
			append(sink, '<');
			append(sink, '/');
			append(sink, name);
			append(sink, '>');
		}

		template <class CharSink>
		struct generator
		{
			generator()
			{
			}

			explicit generator(CharSink out)
				: m_out(std::move(out))
			{
			}

			template <class ContentMaker>
			void element(boost::string_ref const &name, ContentMaker make_content)
			{
				open_element(m_out, name);
				make_content();
				close_element(m_out, name);
			}

			template <class StringLike>
			void write(StringLike const &text)
			{
				write_string(m_out, text);
			}

		private:

			CharSink m_out;
		};

		template <class CharSink>
		auto make_generator(CharSink &&sink)
			-> generator<typename std::decay<CharSink>::type>
		{
			return generator<typename std::decay<CharSink>::type>(std::forward<CharSink>(sink));
		}
	}
}

#endif
<|endoftext|>


<commit_before>#pragma once
//=====================================================================//
/*!	@file
	@brief	メニュー・クラス
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2017, 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=====================================================================//
#include <cstdint>

namespace graphics {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief	メニュー・クラス
		@param[in]	REND	描画クラス
		@param[in]	MAX		最大メニュー数
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	template <class REND, uint16_t MAX>
	class menu {

	public:
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	オブジェクト・タイプ
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		enum class type : uint8_t {
			TEXT,	///< 等幅フォント
			PROP,	///< プロポーショナル・フォント
			MOBJ,	///< モーション・オブジェクト
		};

	private:
		REND&		rend_;

		uint16_t	size_;

		struct obj_t {
			type		type_;
			uint8_t		w_;
			uint8_t		h_;
			const void* src_;

			obj_t() : type_(type::TEXT), w_(0), h_(0), src_(nullptr) { }
		};
		obj_t	obj_[MAX];

		int16_t		mx_;
		int16_t		my_;

		int8_t		gap_;
		int8_t		space_w_;
		int8_t		space_h_;

		uint16_t	pos_;

		typename REND::value_type	fc_;
		typename REND::value_type	bc_;

		bool		frame_;
		bool		focus_;

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
			@param[in]	rend	描画クラス
		*/
		//-----------------------------------------------------------------//
		menu(REND& rend) noexcept : rend_(rend), size_(0), mx_(0), my_(0),
			gap_(0), space_w_(0), space_h_(0), pos_(0),
			fc_(REND::COLOR::Black), bc_(REND::COLOR::White),
			frame_(true), focus_(true)
		{ }

		//-----------------------------------------------------------------//
		/*!
			@brief	スペースの設定
			@param[in]	w	横幅
			@param[in]	h	高さ
		*/
		//-----------------------------------------------------------------//
		void set_space(int16_t w, int16_t h) noexcept
		{
			space_w_ = w;
			space_h_ = h;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	ギャップの設定
			@param[in]	gap	ギャップ
		*/
		//-----------------------------------------------------------------//
		void set_gap(int16_t gap) noexcept { gap_ = gap; }

		//-----------------------------------------------------------------//
		/*!
			@brief	サイズを返す（登録されているメニュー数）
		*/
		//-----------------------------------------------------------------//
		uint16_t size() const noexcept { return size_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	フォーカスを許可
			@param[in]	f	不許可の場合「false」
		*/
		//-----------------------------------------------------------------//
		void enable_focus(bool f = true) noexcept
		{
			focus_ = f;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	フレームの描画
			@param[in]	f	不許可の場合「false」
		*/
		//-----------------------------------------------------------------//
		void draw_frame(bool f = true) noexcept
		{
			frame_ = f;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	フォーカスを戻す
		*/
		//-----------------------------------------------------------------//
		void focus_prev() noexcept
		{
			if(!focus_) return;

			if(pos_ > 0) {
				pos_--;
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	フォーカスを進める
		*/
		//-----------------------------------------------------------------//
		void focus_next() noexcept
		{
			if(!focus_) return;

			++pos_;
			if(pos_ >= size_) {
				pos_ = size_ - 1;
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	メニュー位置を取得
			@return メニュー位置
		*/
		//-----------------------------------------------------------------//
		uint16_t get_pos() const noexcept { return pos_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	クリア
		*/
		//-----------------------------------------------------------------//
		void clear() noexcept
		{
			size_ = 0;
			mx_ = 0;
			my_ = 0;
			pos_ = 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	オブジェクトを追加
			@param[in]	t	オブジェクト・タイプ
			@param[in]	src	オブジェクト・ソース
		*/
		//-----------------------------------------------------------------//
		void add(type t, const void* src) noexcept {
			if(size_ >= MAX) return;

			if(t == type::TEXT) {
				obj_[size_].w_ = rend_.get_text_length(static_cast<const char*>(src), false);
				obj_[size_].h_ = REND::font_height;
			} else if(t == type::PROP) {
				obj_[size_].w_ = rend_.get_text_length(static_cast<const char*>(src), true);
				obj_[size_].h_ = REND::font_height;
			} else if(t == type::MOBJ) {
				uint8_t w;
				uint8_t h;
				rend_.get_mobj_size(src, w, h);
				obj_[size_].w_ = w;
				obj_[size_].h_ = h;
			} else {
				return;
			}
			obj_[size_].type_ = t;
			obj_[size_].src_  = src;
			++size_;

			int16_t x = 0;
			int16_t y = 0;
			for(auto i = 0; i < size_; ++i) {
				if(x < obj_[i].w_) x = obj_[i].w_;
				y += obj_[i].h_;
				y += gap_;
				y += space_h_ * 2;
			}
			mx_ = x;
			my_ = y;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	レンダリング
			@param[in]	ox	描画オフセットＸ
			@param[in]	oy	描画オフセットＹ
		*/
		//-----------------------------------------------------------------//
		void render(int16_t ox = 0, int16_t oy = 0) const noexcept
		{
			if(size_ == 0) return;

			int16_t x = (REND::width  - ox - mx_) / 2;
			int16_t y = (REND::height - oy - my_) / 2;
			x += ox;
			y += oy;
			auto tmp_fc = rend_.get_fore_color();
			rend_.set_fore_color(fc_);
			for(auto i = 0; i < size_; ++i) {
				y += gap_ / 2;
				uint8_t w;
				uint8_t h;
				if(obj_[i].type_ == type::TEXT) {
					rend_.fill_r(x, y, mx_ + space_w_ * 2, obj_[i].h_ + space_h_ * 2, bc_);
					y += space_h_;
					auto t = static_cast<const char*>(obj_[i].src_);
					rend_.draw_text(x + space_w_, y, t);

				} else if(obj_[i].type_ == type::PROP) {
					rend_.fill_r(x, y, mx_ + space_w_ * 2, obj_[i].h_ + space_h_ * 2, bc_);
					y += space_h_;
					auto t = static_cast<const char*>(obj_[i].src_);
					rend_.draw_text(x + space_w_, y, t, true);
				} else if(obj_[i].type_ == type::MOBJ) {
					y += space_h_;
					rend_.draw_mobj(x, y, obj_[i].src_);
				}
//				if(focus_ && i == pos_) {
//					rend_.reverse(x - 1, y, obj_[i].w_ + 2, obj_[i].h_);
//				}
				y += REND::font_height;
				y += space_h_;
				y += gap_ - (gap_ / 2);
			}
			rend_.set_fore_color(tmp_fc);
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	REND クラスの参照
			@return REND クラス
		*/
		//-----------------------------------------------------------------//
		REND& at() noexcept { return rend_; }
	};
}
<commit_msg>update: manage touch reaction<commit_after>#pragma once
//=====================================================================//
/*!	@file
	@brief	メニュー・クラス
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2017, 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=====================================================================//
#include <cstdint>

namespace graphics {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief	メニュー・クラス
		@param[in]	REND	描画クラス
		@param[in]	BACK	背面の描画クラス
		@param[in]	MAX		最大メニュー数
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	template <class REND, class BACK, uint16_t MAX>
	class menu {

		REND&		rend_;
		BACK&		back_;

		uint16_t	size_;

		struct obj_t {
			uint8_t		w_;
			uint8_t		h_;
			const void* src_;

			obj_t() : w_(0), h_(0), src_(nullptr) { }
		};
		obj_t	obj_[MAX];

		int16_t		mx_;
		int16_t		my_;
		int16_t		ox_;
		int16_t		oy_;

		int8_t		gap_;
		int8_t		space_w_;
		int8_t		space_h_;

		uint16_t	pos_;

		typename REND::value_type	fc_;
		typename REND::value_type	hc_;
		typename REND::value_type	bc_;

		bool		focus_;

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
			@param[in]	rend	描画クラス
		*/
		//-----------------------------------------------------------------//
		menu(REND& rend, BACK& back) noexcept : rend_(rend), back_(back),
			size_(0), mx_(0), my_(0), ox_(0), oy_(0),
			gap_(0), space_w_(0), space_h_(0), pos_(0),
			fc_(REND::COLOR::Black), hc_(REND::COLOR::White), bc_(REND::COLOR::Gray),
			focus_(true)
		{ }

		//-----------------------------------------------------------------//
		/*!
			@brief	描画オフセットの設定
			@param[in]	ox	X オフセット
			@param[in]	oy	Y オフセット
		*/
		//-----------------------------------------------------------------//
		void set_offset(int16_t ox, int16_t oy) noexcept
		{
			ox_ = ox;
			oy_ = oy;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	描画スペースの設定
			@param[in]	w	横幅
			@param[in]	h	高さ
		*/
		//-----------------------------------------------------------------//
		void set_space(int16_t w, int16_t h) noexcept
		{
			space_w_ = w;
			space_h_ = h;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	ギャップの設定（縦）
			@param[in]	gap	ギャップ
		*/
		//-----------------------------------------------------------------//
		void set_gap(int16_t gap) noexcept { gap_ = gap; }

		//-----------------------------------------------------------------//
		/*!
			@brief	メニュー・サイズを返す（登録されているメニュー数）
			@return メニュー・サイズ
		*/
		//-----------------------------------------------------------------//
		uint16_t size() const noexcept { return size_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	フォーカスを戻す
		*/
		//-----------------------------------------------------------------//
		void prev_focus() noexcept
		{
			if(size_ == 0) return;

			if(pos_ > 0) {
				pos_--;
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	フォーカスを進める
		*/
		//-----------------------------------------------------------------//
		void next_focus() noexcept
		{
			if(size_ == 0) return;

			++pos_;
			if(pos_ >= size_) {
				pos_ = size_ - 1;
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	メニュー位置を取得
			@return メニュー位置
		*/
		//-----------------------------------------------------------------//
		uint16_t get_pos() const noexcept { return pos_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	登録メニューを全てクリア
		*/
		//-----------------------------------------------------------------//
		void clear() noexcept
		{
			size_ = 0;
			mx_ = 0;
			my_ = 0;
			pos_ = 0;
			focus_ = false;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	オブジェクトを追加
			@param[in]	src		オブジェクト・ソース
			@param[in]	bitmap	ビットマップの場合「true」
		*/
		//-----------------------------------------------------------------//
		void add(const void* src, bool bitmap = false) noexcept
		{
			if(size_ >= MAX) return;

			if(bitmap) {
				uint8_t w;
				uint8_t h;
				rend_.get_mobj_size(src, w, h);
				obj_[size_].w_ = w;
				obj_[size_].h_ = h;
			} else {
				obj_[size_].w_ = rend_.get_text_length(static_cast<const char*>(src), false);
				obj_[size_].h_ = REND::font_height;
			}
			obj_[size_].src_ = src;
			++size_;

			int16_t x = 0;
			int16_t y = 0;
			for(auto i = 0; i < size_; ++i) {
				auto xx = obj_[i].w_ + space_w_ * 2;
				if(x < xx) x = xx;
				y += obj_[i].h_;
				y += gap_;
				y += space_h_ * 2;
			}
			mx_ = x;
			my_ = y;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	レンダリング
			@param[in]	px	X 位置
			@param[in]	py	Y 位置
			@param[in]	touch	タッチ状態
			@return メニューが選択された場合「true」
		*/
		//-----------------------------------------------------------------//
		bool render(int16_t px = -1, int16_t py = -1, bool touch = false) noexcept
		{
			if(size_ == 0) return false;

			int16_t x = (REND::width  - ox_ - mx_) / 2;
			int16_t y = (REND::height - oy_ - my_) / 2;
			x += ox_;
			y += oy_;
			auto tmp_fc = rend_.get_fore_color();
			rend_.set_fore_color(fc_);
			int16_t idx = -1;
			for(auto i = 0; i < size_; ++i) {
				const auto& obj = obj_[i];
				y += gap_ / 2;
				auto c = bc_;
				if(x <= px && px < (x + mx_) && y <= py && py < (y + obj.h_ + space_h_ * 2)) {
					idx = i;
					if(touch) {
						c = hc_;
					}
				}
				back_(x, y, mx_, obj.h_ + space_h_ * 2, c);
				y += space_h_;
				auto t = static_cast<const char*>(obj_[i].src_);
				rend_.draw_text(x + space_w_, y, t);
				y += obj.h_;
				y += space_h_;
				y += gap_ - (gap_ / 2);
			}
			rend_.set_fore_color(tmp_fc);

			bool focus = focus_;
			focus_ = touch;
			if(idx >= 0) {
				pos_ = idx;
				if(focus && !focus_) {
					return true;
				}
			}
			return false;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	REND クラスの参照
			@return REND クラス
		*/
		//-----------------------------------------------------------------//
		REND& at_render() noexcept { return rend_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	BACK クラスの参照
			@return BACK クラス
		*/
		//-----------------------------------------------------------------//
		BACK& at_back() noexcept { return back_; }
	};
}
<|endoftext|>


<commit_before>#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include "ShowImageModule.hpp"
#include "../framework/types/Matrix.hpp"

using namespace std;
using namespace uipf;

/*

*/
void ShowImageModule::run( DataManager& data) const
{
	using namespace cv;

	const Matrix* oMatrix = data.getInputData<Matrix>("image");
	if (oMatrix) {
		context_->displayImage(data.getParam<std::string>("title","view image"), *oMatrix);
	}
}

MetaData ShowImageModule::getMetaData() const
{
	map<string, DataDescription> input = {
		{"image", DataDescription(MATRIX, "the image to show.") }
	};
	map<string, ParamDescription> params = {
		{"title", ParamDescription("the title of the window.") }
	};

	return MetaData(
		"Show an image by opening a window.",
		input,
		map<string, DataDescription>(), // no outputs
		params
	);
}

<commit_msg>shared pointer + (non-)blocking mode<commit_after>#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include "ShowImageModule.hpp"
#include "../framework/types/Matrix.hpp"

using namespace std;
using namespace uipf;

/*

*/
void ShowImageModule::run( DataManager& data) const
{
	using namespace cv;

	const Matrix::ptr oMatrix = data.getInputData<Matrix>("image");
	if (oMatrix) {
		context_->displayImage(data.getParam<std::string>("title","view image"), *oMatrix,data.getParam<bool>("blocking",true));
	}
}

MetaData ShowImageModule::getMetaData() const
{
	map<string, DataDescription> input = {
		{"image", DataDescription(MATRIX, "the image to show.") },
		{"blocking", DataDescription(BOOL, "'true' or 'false' determines if the processing chain has to wait for this window to close or not.") },
	};
	map<string, ParamDescription> params = {
		{"title", ParamDescription("the title of the window.") }
	};

	return MetaData(
		"Show an image by opening a window.",
		input,
		map<string, DataDescription>(), // no outputs
		params
	);
}

<|endoftext|>


<commit_before>#include "testing/testing.hpp"
#include "coding/uri.hpp"
#include "base/macros.hpp"
#include "std/bind.hpp"
#include "std/queue.hpp"
#include "std/utility.hpp"

using url_scheme::Uri;

namespace
{

class TestUri
{
public:
  TestUri(string const & uri) { m_uri = uri; }
  TestUri & Scheme(string const &scheme) { m_scheme = scheme; return *this; }
  TestUri & Path(string const & path) { m_path = path; return *this; }
  TestUri & KV(string const & key, string const & value)
  {
    m_keyValuePairs.push(make_pair(key, value));
    return *this;
  }

  ~TestUri()
  {
    Uri uri(m_uri);
    TEST_EQUAL(uri.GetScheme(), m_scheme, ());
    TEST_EQUAL(uri.GetPath(), m_path, ());
    TEST(!m_scheme.empty() || !uri.IsValid(), ("Scheme is empty if and only if uri is invalid!"));
    uri.ForEachKeyValue(bind(&TestUri::AddTestValue, this, _1, _2));
  }

private:

  void AddTestValue(string const & key, string const & value)
  {
    TEST(!m_keyValuePairs.empty(), ("Failed for uri = ", m_uri, "Passed KV = ", key, value));
    TEST_EQUAL(m_keyValuePairs.front().first,  key, ());
    TEST_EQUAL(m_keyValuePairs.front().second, value, ());
    m_keyValuePairs.pop();
  }

  string m_uri, m_scheme, m_path;
  queue<pair<string, string> > m_keyValuePairs;
};

}  // unnamed namespace

UNIT_TEST(UriValidScheme)
{
  char const uriS[] = "mapswithme://map?ll=10.3,12.3223&n=Hello%20World";
  Uri uri(uriS, ARRAY_SIZE(uriS) - 1);
  TEST_EQUAL(uri.GetScheme(), "mapswithme", ());
}

UNIT_TEST(UriInvalidSchemeNoColon)
{
  TEST_EQUAL(Uri("mapswithme:").GetScheme(), "mapswithme", ());
}

UNIT_TEST(UriTestValidScheme2)
{
  TestUri("mapswithme://map?ll=10.3,12.3223&n=Hello%20World")
      .Scheme("mapswithme")
      .Path("map")
      .KV("ll", "10.3,12.3223")
      .KV("n", "Hello World");
}

UNIT_TEST(UriComprehensive)
{
  TestUri("");

  TestUri("scheme:").Scheme("scheme");

  TestUri("scheme:/").Scheme("scheme");

  TestUri("scheme://").Scheme("scheme");

  TestUri("sometext");

  TestUri(":noscheme");

  TestUri("://noscheme?");

  TestUri("mwm://?").Scheme("mwm");

  TestUri("http://path/to/something").Scheme("http").Path("path/to/something");

  TestUri("http://path?").Scheme("http").Path("path");

  TestUri("maps://path?&&key=&").Scheme("maps").Path("path").KV("key", "");

  TestUri("mapswithme://map?ll=1.2,3.4&z=15").Scheme("mapswithme").Path("map")
      .KV("ll", "1.2,3.4").KV("z", "15");

  TestUri("nopathnovalues://?key1&key2=val2").Scheme("nopathnovalues").Path("")
      .KV("key1", "").KV("key2", "val2");

  TestUri("s://?key1&key2").Scheme("s").Path("").KV("key1", "").KV("key2", "");

  TestUri("g://p?key1=val1&key2=").Scheme("g").Path("p").KV("key1", "val1").KV("key2", "");

  TestUri("g://p?=val1&key2=").Scheme("g").Path("p").KV("", "val1").KV("key2", "");

  TestUri("g://?k&key2").Scheme("g").KV("k", "").KV("key2", "");

  TestUri("m:?%26Amp%26%3D%26Amp%26&name=%31%20%30").Scheme("m")
      .KV("&Amp&=&Amp&", "").KV("name", "1 0");

  TestUri("s://?key1=value1&key1=value2&key1=value3&key2&key2&key3=value1&key3&key3=value2")
      .Scheme("s")
      .KV("key1", "value1").KV("key1", "value2").KV("key1", "value3")
      .KV("key2", "").KV("key2", "")
      .KV("key3", "value1").KV("key3", "").KV("key3", "value2");
}
<commit_msg>[coding_test] Fixed coding tests.<commit_after>#include "testing/testing.hpp"
#include "coding/uri.hpp"
#include "base/macros.hpp"
#include "std/bind.hpp"
#include "std/queue.hpp"
#include "std/utility.hpp"

using url_scheme::Uri;

namespace
{

class TestUri
{
public:
  TestUri(string const & uri) { m_uri = uri; }
  TestUri & Scheme(string const &scheme) { m_scheme = scheme; return *this; }
  TestUri & Path(string const & path) { m_path = path; return *this; }
  TestUri & KV(string const & key, string const & value)
  {
    m_keyValuePairs.push(make_pair(key, value));
    return *this;
  }

  ~TestUri()
  {
    Uri uri(m_uri);
    TEST_EQUAL(uri.GetScheme(), m_scheme, ());
    TEST_EQUAL(uri.GetPath(), m_path, ());
    TEST(!m_scheme.empty() || !uri.IsValid(), ("Scheme is empty if and only if uri is invalid!"));
    uri.ForEachKeyValue(bind(&TestUri::AddTestValue, this, _1, _2));
  }

private:

  bool AddTestValue(string const & key, string const & value)
  {
    TEST(!m_keyValuePairs.empty(), ("Failed for uri = ", m_uri, "Passed KV = ", key, value));
    TEST_EQUAL(m_keyValuePairs.front().first,  key, ());
    TEST_EQUAL(m_keyValuePairs.front().second, value, ());
    m_keyValuePairs.pop();
    return true;
  }

  string m_uri, m_scheme, m_path;
  queue<pair<string, string> > m_keyValuePairs;
};

}  // unnamed namespace

UNIT_TEST(UriValidScheme)
{
  char const uriS[] = "mapswithme://map?ll=10.3,12.3223&n=Hello%20World";
  Uri uri(uriS, ARRAY_SIZE(uriS) - 1);
  TEST_EQUAL(uri.GetScheme(), "mapswithme", ());
}

UNIT_TEST(UriInvalidSchemeNoColon)
{
  TEST_EQUAL(Uri("mapswithme:").GetScheme(), "mapswithme", ());
}

UNIT_TEST(UriTestValidScheme2)
{
  TestUri("mapswithme://map?ll=10.3,12.3223&n=Hello%20World")
      .Scheme("mapswithme")
      .Path("map")
      .KV("ll", "10.3,12.3223")
      .KV("n", "Hello World");
}

UNIT_TEST(UriComprehensive)
{
  TestUri("");

  TestUri("scheme:").Scheme("scheme");

  TestUri("scheme:/").Scheme("scheme");

  TestUri("scheme://").Scheme("scheme");

  TestUri("sometext");

  TestUri(":noscheme");

  TestUri("://noscheme?");

  TestUri("mwm://?").Scheme("mwm");

  TestUri("http://path/to/something").Scheme("http").Path("path/to/something");

  TestUri("http://path?").Scheme("http").Path("path");

  TestUri("maps://path?&&key=&").Scheme("maps").Path("path").KV("key", "");

  TestUri("mapswithme://map?ll=1.2,3.4&z=15").Scheme("mapswithme").Path("map")
      .KV("ll", "1.2,3.4").KV("z", "15");

  TestUri("nopathnovalues://?key1&key2=val2").Scheme("nopathnovalues").Path("")
      .KV("key1", "").KV("key2", "val2");

  TestUri("s://?key1&key2").Scheme("s").Path("").KV("key1", "").KV("key2", "");

  TestUri("g://p?key1=val1&key2=").Scheme("g").Path("p").KV("key1", "val1").KV("key2", "");

  TestUri("g://p?=val1&key2=").Scheme("g").Path("p").KV("", "val1").KV("key2", "");

  TestUri("g://?k&key2").Scheme("g").KV("k", "").KV("key2", "");

  TestUri("m:?%26Amp%26%3D%26Amp%26&name=%31%20%30").Scheme("m")
      .KV("&Amp&=&Amp&", "").KV("name", "1 0");

  TestUri("s://?key1=value1&key1=value2&key1=value3&key2&key2&key3=value1&key3&key3=value2")
      .Scheme("s")
      .KV("key1", "value1").KV("key1", "value2").KV("key1", "value3")
      .KV("key2", "").KV("key2", "")
      .KV("key3", "value1").KV("key3", "").KV("key3", "value2");
}
<|endoftext|>