<commit_before>/*
 * Copyright 2016 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "app/src/include/firebase/util.h"

#include <assert.h>

#include <map>
#include <string>
#include <vector>

#include "app/src/include/firebase/internal/platform.h"

#if FIREBASE_PLATFORM_ANDROID
#include "app/src/include/google_play_services/availability.h"
#endif  // FIREBASE_PLATFORM_ANDROID
#include "app/src/include/firebase/internal/mutex.h"
#include "app/src/log.h"
#include "app/src/reference_counted_future_impl.h"
#include "app/src/util.h"

namespace firebase {

enum ModuleInitializerFn {
  kModuleInitializerInitialize,
  kModuleInitializerCount
};

struct ModuleInitializerData {
  ModuleInitializerData()
      : future_impl(kModuleInitializerCount),
        app(nullptr),
        context(nullptr),
        init_fn_idx(0) {}
  // Futures implementation.
  ReferenceCountedFutureImpl future_impl;
  // Handle to the Initialize() future.
  SafeFutureHandle<void> future_handle_init;

  // Data we will pass to the user's callbacks.
  App* app;
  void* context;

  // Initialization callbacks. These will each be called in order, but if any
  // of them returns kInitResultFailedMissingDependency, we'll stop and update,
  // then resume where we left off.
  std::vector<ModuleInitializer::InitializerFn> init_fns;

  // Where we are in the initializer function list.
  int init_fn_idx;
};

ModuleInitializer::ModuleInitializer() { data_ = new ModuleInitializerData; }

ModuleInitializer::~ModuleInitializer() {
  delete data_;
  data_ = nullptr;
}

static void PerformInitialize(ModuleInitializerData* data) {
  while (data->init_fn_idx < data->init_fns.size()) {
    InitResult init_result;
    init_result = data->init_fns[data->init_fn_idx](data->app, data->context);

#if FIREBASE_PLATFORM_ANDROID
    if (init_result == kInitResultFailedMissingDependency) {
      // On Android, we need to update or activate Google Play services
      // before we can initialize this Firebase module.
      LogWarning("Google Play services unavailable, trying to fix.");

      Future<void> make_available = google_play_services::MakeAvailable(
          data->app->GetJNIEnv(), data->app->activity());

      make_available.OnCompletion(
          [](const Future<void>& result, void* ptr) {
            ModuleInitializerData* data =
                reinterpret_cast<ModuleInitializerData*>(ptr);
            if (result.status() == kFutureStatusComplete) {
              if (result.error() == 0) {
                LogInfo("Google Play services now available, continuing.");
                PerformInitialize(data);
              } else {
                LogError("Google Play services still unavailable.");
                int num_remaining = data->init_fns.size() - data->init_fn_idx;
                data->future_impl.Complete(
                    data->future_handle_init, num_remaining,
                    "Unable to initialize due to missing Google Play services "
                    "dependency.");
              }
            }
          },
          data);
    }
#else   // !FIREBASE_PLATFORM_ANDROID
    // Outside of Android, we shouldn't get kInitResultFailedMissingDependency.
    FIREBASE_ASSERT(init_result != kInitResultFailedMissingDependency);
#endif  // FIREBASE_PLATFORM_ANDROID
    if (init_result == kInitResultSuccess) {
      data->init_fn_idx++;  // This function succeeded, move on to the next one.
    } else {
      return;  // If initialization failed, we will be trying again later, after
      // the MakeAvailable() Future completes. (or, if that future fails, then
      // we will just abort and return an error.)
    }
  }
  data->future_impl.Complete(data->future_handle_init, 0);
}

Future<void> ModuleInitializer::Initialize(
    App* app, void* context, ModuleInitializer::InitializerFn init_fn) {
  FIREBASE_ASSERT(app != nullptr);
  FIREBASE_ASSERT(init_fn != nullptr);

  return Initialize(app, context, &init_fn, 1);
}

Future<void> ModuleInitializer::Initialize(
    App* app, void* context, const ModuleInitializer::InitializerFn* init_fns,
    size_t init_fns_count) {
  FIREBASE_ASSERT(app != nullptr);
  FIREBASE_ASSERT(init_fns != nullptr);

  if (!data_->future_impl.ValidFuture(data_->future_handle_init)) {
    data_->future_handle_init =
        data_->future_impl.SafeAlloc<void>(kModuleInitializerInitialize);
    data_->app = app;
    data_->init_fn_idx = 0;
    data_->init_fns.clear();
    for (size_t i = 0; i < init_fns_count; i++) {
      data_->init_fns.push_back(init_fns[i]);
    }
    data_->context = context;
    PerformInitialize(data_);
  }
  return InitializeLastResult();
}

Future<void> ModuleInitializer::InitializeLastResult() {
  return static_cast<const Future<void>&>(
      data_->future_impl.LastResult(kModuleInitializerInitialize));
}

std::map<std::string, AppCallback*>* AppCallback::callbacks_;
Mutex* AppCallback::callbacks_mutex_ = new Mutex();

void AppCallback::NotifyAllAppCreated(
    firebase::App* app, std::map<std::string, InitResult>* results) {
  if (results) results->clear();
  MutexLock lock(*callbacks_mutex_);
  if (!callbacks_) return;
  for (std::map<std::string, AppCallback*>::const_iterator it =
           callbacks_->begin();
       it != callbacks_->end(); ++it) {
    const AppCallback* callback = it->second;
    if (callback->enabled()) {
      InitResult result = callback->NotifyAppCreated(app);
      if (results) (*results)[it->first] = result;
    }
  }
}

void AppCallback::NotifyAllAppDestroyed(firebase::App* app) {
  MutexLock lock(*callbacks_mutex_);
  if (!callbacks_) return;
  for (std::map<std::string, AppCallback*>::const_iterator it =
           callbacks_->begin();
       it != callbacks_->end(); ++it) {
    const AppCallback* callback = it->second;
    if (callback->enabled()) callback->NotifyAppDestroyed(app);
  }
}

// Determine whether a module callback is enabled, by name.
bool AppCallback::GetEnabledByName(const char* name) {
  MutexLock lock(*callbacks_mutex_);
  if (!callbacks_) return false;
  std::map<std::string, AppCallback*>::const_iterator it =
      callbacks_->find(std::string(name));
  if (it == callbacks_->end()) {
    return false;
  }
  return it->second->enabled();
}

// Enable a module callback by name.
void AppCallback::SetEnabledByName(const char* name, bool enable) {
  MutexLock lock(*callbacks_mutex_);
  if (!callbacks_) return;
  std::map<std::string, AppCallback*>::const_iterator it =
      callbacks_->find(std::string(name));
  if (it == callbacks_->end()) {
    LogDebug("App initializer %s not found, failed to enable.", name);
    return;
  }
  LogDebug("%s app initializer %s", name, enable ? "Enabling" : "Disabling");
  it->second->set_enabled(enable);
}

void AppCallback::SetEnabledAll(bool enable) {
  MutexLock lock(*callbacks_mutex_);
  if (!callbacks_) return;
  LogDebug("%s all app initializers", enable ? "Enabling" : "Disabling");
  for (std::map<std::string, AppCallback*>::const_iterator it =
           callbacks_->begin();
       it != callbacks_->end(); ++it) {
    LogDebug("%s %s", enable ? "Enable" : "Disable", it->second->module_name());
    it->second->set_enabled(enable);
  }
}

void AppCallback::AddCallback(AppCallback* callback) {
  if (!callbacks_) {
    callbacks_ = new std::map<std::string, AppCallback*>();
  }
  std::string name = callback->module_name();
  if (callbacks_->find(name) != callbacks_->end()) {
    LogWarning(
        "%s is already registered for callbacks on app initialization,  "
        "ignoring.",
        name.c_str());
  } else {
    LogDebug("Registered app initializer %s (enabled: %d)", name.c_str(),
             callback->enabled() ? 1 : 0);
    (*callbacks_)[name] = callback;
  }
}

Mutex* StaticFutureData::s_futures_mutex_ = new Mutex();
std::map<const void*, StaticFutureData*>* StaticFutureData::s_future_datas_;

// static
void StaticFutureData::CleanupFutureDataForModule(
    const void* module_identifier) {
  MutexLock lock(*s_futures_mutex_);

  if (s_future_datas_ == nullptr) return;

  auto it = s_future_datas_->find(module_identifier);
  if (it != s_future_datas_->end()) {
    StaticFutureData* existing_data = it->second;
    if (existing_data != nullptr) delete existing_data;

    s_future_datas_->erase(it);
  }
}

// static
StaticFutureData* StaticFutureData::GetFutureDataForModule(
    const void* module_identifier, int num_functions) {
  MutexLock lock(*s_futures_mutex_);

  if (s_future_datas_ == nullptr) {
    s_future_datas_ = new std::map<const void*, StaticFutureData*>;
  }

  auto found_value = s_future_datas_->find(module_identifier);
  if (found_value != s_future_datas_->end()) {
    StaticFutureData* existing_data = found_value->second;
    if (existing_data != nullptr) {
      return existing_data;
    }
  }

  StaticFutureData* new_data = CreateNewData(module_identifier, num_functions);
  (*s_future_datas_)[module_identifier] = new_data;
  return new_data;
}

// static
StaticFutureData* StaticFutureData::CreateNewData(const void* module_identifier,
                                                  int num_functions) {
  StaticFutureData* new_data = new StaticFutureData(num_functions);
  return new_data;
}

std::vector<std::string> SplitString(const std::string& s,
                                     const char delimiter) {
  size_t pos = 0;
  // This index is used as the starting index to search the delimiters from.
  size_t delimiter_search_start = 0;
  // Skip any leading delimiters
  while (s[delimiter_search_start] == delimiter) {
    delimiter_search_start++;
  }

  std::vector<std::string> split_parts;
  size_t len = s.size();
  // Can't proceed if input string consists of just delimiters
  if (pos >= len) {
    return split_parts;
  }

  while ((pos = s.find(delimiter, delimiter_search_start)) !=
         std::string::npos) {
    split_parts.push_back(
        s.substr(delimiter_search_start, pos - delimiter_search_start));

    while (s[pos] == delimiter && pos < len) {
      pos++;
      delimiter_search_start = pos;
    }
  }

  // If the input string doesn't end with a delimiter we need to push the last
  // token into our return vector
  if (delimiter_search_start != len) {
    split_parts.push_back(
        s.substr(delimiter_search_start, len - delimiter_search_start));
  }
  return split_parts;
}

// NOLINTNEXTLINE - allow namespace overridden
}  // namespace firebase
<commit_msg>Disable logging during AddCallback, which initializes too early. (#827)<commit_after>/*
 * Copyright 2016 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "app/src/include/firebase/util.h"

#include <assert.h>

#include <map>
#include <string>
#include <vector>

#include "app/src/include/firebase/internal/platform.h"

#if FIREBASE_PLATFORM_ANDROID
#include "app/src/include/google_play_services/availability.h"
#endif  // FIREBASE_PLATFORM_ANDROID
#include "app/src/include/firebase/internal/mutex.h"
#include "app/src/log.h"
#include "app/src/reference_counted_future_impl.h"
#include "app/src/util.h"

namespace firebase {

enum ModuleInitializerFn {
  kModuleInitializerInitialize,
  kModuleInitializerCount
};

struct ModuleInitializerData {
  ModuleInitializerData()
      : future_impl(kModuleInitializerCount),
        app(nullptr),
        context(nullptr),
        init_fn_idx(0) {}
  // Futures implementation.
  ReferenceCountedFutureImpl future_impl;
  // Handle to the Initialize() future.
  SafeFutureHandle<void> future_handle_init;

  // Data we will pass to the user's callbacks.
  App* app;
  void* context;

  // Initialization callbacks. These will each be called in order, but if any
  // of them returns kInitResultFailedMissingDependency, we'll stop and update,
  // then resume where we left off.
  std::vector<ModuleInitializer::InitializerFn> init_fns;

  // Where we are in the initializer function list.
  int init_fn_idx;
};

ModuleInitializer::ModuleInitializer() { data_ = new ModuleInitializerData; }

ModuleInitializer::~ModuleInitializer() {
  delete data_;
  data_ = nullptr;
}

static void PerformInitialize(ModuleInitializerData* data) {
  while (data->init_fn_idx < data->init_fns.size()) {
    InitResult init_result;
    init_result = data->init_fns[data->init_fn_idx](data->app, data->context);

#if FIREBASE_PLATFORM_ANDROID
    if (init_result == kInitResultFailedMissingDependency) {
      // On Android, we need to update or activate Google Play services
      // before we can initialize this Firebase module.
      LogWarning("Google Play services unavailable, trying to fix.");

      Future<void> make_available = google_play_services::MakeAvailable(
          data->app->GetJNIEnv(), data->app->activity());

      make_available.OnCompletion(
          [](const Future<void>& result, void* ptr) {
            ModuleInitializerData* data =
                reinterpret_cast<ModuleInitializerData*>(ptr);
            if (result.status() == kFutureStatusComplete) {
              if (result.error() == 0) {
                LogInfo("Google Play services now available, continuing.");
                PerformInitialize(data);
              } else {
                LogError("Google Play services still unavailable.");
                int num_remaining = data->init_fns.size() - data->init_fn_idx;
                data->future_impl.Complete(
                    data->future_handle_init, num_remaining,
                    "Unable to initialize due to missing Google Play services "
                    "dependency.");
              }
            }
          },
          data);
    }
#else   // !FIREBASE_PLATFORM_ANDROID
    // Outside of Android, we shouldn't get kInitResultFailedMissingDependency.
    FIREBASE_ASSERT(init_result != kInitResultFailedMissingDependency);
#endif  // FIREBASE_PLATFORM_ANDROID
    if (init_result == kInitResultSuccess) {
      data->init_fn_idx++;  // This function succeeded, move on to the next one.
    } else {
      return;  // If initialization failed, we will be trying again later, after
      // the MakeAvailable() Future completes. (or, if that future fails, then
      // we will just abort and return an error.)
    }
  }
  data->future_impl.Complete(data->future_handle_init, 0);
}

Future<void> ModuleInitializer::Initialize(
    App* app, void* context, ModuleInitializer::InitializerFn init_fn) {
  FIREBASE_ASSERT(app != nullptr);
  FIREBASE_ASSERT(init_fn != nullptr);

  return Initialize(app, context, &init_fn, 1);
}

Future<void> ModuleInitializer::Initialize(
    App* app, void* context, const ModuleInitializer::InitializerFn* init_fns,
    size_t init_fns_count) {
  FIREBASE_ASSERT(app != nullptr);
  FIREBASE_ASSERT(init_fns != nullptr);

  if (!data_->future_impl.ValidFuture(data_->future_handle_init)) {
    data_->future_handle_init =
        data_->future_impl.SafeAlloc<void>(kModuleInitializerInitialize);
    data_->app = app;
    data_->init_fn_idx = 0;
    data_->init_fns.clear();
    for (size_t i = 0; i < init_fns_count; i++) {
      data_->init_fns.push_back(init_fns[i]);
    }
    data_->context = context;
    PerformInitialize(data_);
  }
  return InitializeLastResult();
}

Future<void> ModuleInitializer::InitializeLastResult() {
  return static_cast<const Future<void>&>(
      data_->future_impl.LastResult(kModuleInitializerInitialize));
}

std::map<std::string, AppCallback*>* AppCallback::callbacks_;
Mutex* AppCallback::callbacks_mutex_ = new Mutex();

void AppCallback::NotifyAllAppCreated(
    firebase::App* app, std::map<std::string, InitResult>* results) {
  if (results) results->clear();
  MutexLock lock(*callbacks_mutex_);
  if (!callbacks_) return;
  for (std::map<std::string, AppCallback*>::const_iterator it =
           callbacks_->begin();
       it != callbacks_->end(); ++it) {
    const AppCallback* callback = it->second;
    if (callback->enabled()) {
      InitResult result = callback->NotifyAppCreated(app);
      if (results) (*results)[it->first] = result;
    }
  }
}

void AppCallback::NotifyAllAppDestroyed(firebase::App* app) {
  MutexLock lock(*callbacks_mutex_);
  if (!callbacks_) return;
  for (std::map<std::string, AppCallback*>::const_iterator it =
           callbacks_->begin();
       it != callbacks_->end(); ++it) {
    const AppCallback* callback = it->second;
    if (callback->enabled()) callback->NotifyAppDestroyed(app);
  }
}

// Determine whether a module callback is enabled, by name.
bool AppCallback::GetEnabledByName(const char* name) {
  MutexLock lock(*callbacks_mutex_);
  if (!callbacks_) return false;
  std::map<std::string, AppCallback*>::const_iterator it =
      callbacks_->find(std::string(name));
  if (it == callbacks_->end()) {
    return false;
  }
  return it->second->enabled();
}

// Enable a module callback by name.
void AppCallback::SetEnabledByName(const char* name, bool enable) {
  MutexLock lock(*callbacks_mutex_);
  if (!callbacks_) return;
  std::map<std::string, AppCallback*>::const_iterator it =
      callbacks_->find(std::string(name));
  if (it == callbacks_->end()) {
    LogDebug("App initializer %s not found, failed to enable.", name);
    return;
  }
  LogDebug("%s app initializer %s", name, enable ? "Enabling" : "Disabling");
  it->second->set_enabled(enable);
}

void AppCallback::SetEnabledAll(bool enable) {
  MutexLock lock(*callbacks_mutex_);
  if (!callbacks_) return;
  LogDebug("%s all app initializers", enable ? "Enabling" : "Disabling");
  for (std::map<std::string, AppCallback*>::const_iterator it =
           callbacks_->begin();
       it != callbacks_->end(); ++it) {
    LogDebug("%s %s", enable ? "Enable" : "Disable", it->second->module_name());
    it->second->set_enabled(enable);
  }
}

void AppCallback::AddCallback(AppCallback* callback) {
  if (!callbacks_) {
    callbacks_ = new std::map<std::string, AppCallback*>();
  }
  std::string name = callback->module_name();
  if (callbacks_->find(name) != callbacks_->end()) {
  } else {
    (*callbacks_)[name] = callback;
  }
}

Mutex* StaticFutureData::s_futures_mutex_ = new Mutex();
std::map<const void*, StaticFutureData*>* StaticFutureData::s_future_datas_;

// static
void StaticFutureData::CleanupFutureDataForModule(
    const void* module_identifier) {
  MutexLock lock(*s_futures_mutex_);

  if (s_future_datas_ == nullptr) return;

  auto it = s_future_datas_->find(module_identifier);
  if (it != s_future_datas_->end()) {
    StaticFutureData* existing_data = it->second;
    if (existing_data != nullptr) delete existing_data;

    s_future_datas_->erase(it);
  }
}

// static
StaticFutureData* StaticFutureData::GetFutureDataForModule(
    const void* module_identifier, int num_functions) {
  MutexLock lock(*s_futures_mutex_);

  if (s_future_datas_ == nullptr) {
    s_future_datas_ = new std::map<const void*, StaticFutureData*>;
  }

  auto found_value = s_future_datas_->find(module_identifier);
  if (found_value != s_future_datas_->end()) {
    StaticFutureData* existing_data = found_value->second;
    if (existing_data != nullptr) {
      return existing_data;
    }
  }

  StaticFutureData* new_data = CreateNewData(module_identifier, num_functions);
  (*s_future_datas_)[module_identifier] = new_data;
  return new_data;
}

// static
StaticFutureData* StaticFutureData::CreateNewData(const void* module_identifier,
                                                  int num_functions) {
  StaticFutureData* new_data = new StaticFutureData(num_functions);
  return new_data;
}

std::vector<std::string> SplitString(const std::string& s,
                                     const char delimiter) {
  size_t pos = 0;
  // This index is used as the starting index to search the delimiters from.
  size_t delimiter_search_start = 0;
  // Skip any leading delimiters
  while (s[delimiter_search_start] == delimiter) {
    delimiter_search_start++;
  }

  std::vector<std::string> split_parts;
  size_t len = s.size();
  // Can't proceed if input string consists of just delimiters
  if (pos >= len) {
    return split_parts;
  }

  while ((pos = s.find(delimiter, delimiter_search_start)) !=
         std::string::npos) {
    split_parts.push_back(
        s.substr(delimiter_search_start, pos - delimiter_search_start));

    while (s[pos] == delimiter && pos < len) {
      pos++;
      delimiter_search_start = pos;
    }
  }

  // If the input string doesn't end with a delimiter we need to push the last
  // token into our return vector
  if (delimiter_search_start != len) {
    split_parts.push_back(
        s.substr(delimiter_search_start, len - delimiter_search_start));
  }
  return split_parts;
}

// NOLINTNEXTLINE - allow namespace overridden
}  // namespace firebase
<|endoftext|>


<commit_before>#include "framework/trace.h"
#include <fstream>
#include <memory>
#include <mutex>
#include <sstream>
#include <thread>

namespace
{

using OpenApoc::UString;

enum class EventType
{
	Begin,
	End,
};

class TraceEvent
{
  public:
	EventType type;
	UString name;
	std::vector<std::pair<UString, UString>> args;
	uint64_t timeNS;
	TraceEvent(EventType type, const UString &name,
	           const std::vector<std::pair<UString, UString>> &args, uint64_t timeNS)
	    : type(type), name(name), args(args), timeNS(timeNS)
	{
	}
};

class EventList
{
  public:
	UString tid;
	std::vector<TraceEvent> events;
};

class TraceManager
{
  public:
	// We need a list of all the EventLists created for each thread to dump out & free at
	// TraceManager destructor time
	std::list<std::unique_ptr<EventList>> lists;
	std::mutex listMutex;
	EventList *createThreadEventList()
	{
		std::stringstream ss;
		listMutex.lock();
		EventList *list = new EventList;
		ss << std::this_thread::get_id();
		list->tid = ss.str();
		lists.emplace_back(list);
		listMutex.unlock();
		return list;
	}
	~TraceManager();
	void write();
};

static std::unique_ptr<TraceManager> trace_manager;

#if defined(PTHREADS_AVAILABLE)
#include <pthread.h>
#endif

// thread_local isn't implemented until msvc 2015 (_MSC_VER 1900)
#if defined(_MSC_VER) && _MSC_VER < 1900
static __declspec(thread) EventList *events = nullptr;
#else
#if defined(BROKEN_THREAD_LOCAL)
#warning Using pthread path

static pthread_key_t eventListKey;

#else
static thread_local EventList *events = nullptr;
#endif
#endif
static std::chrono::time_point<std::chrono::high_resolution_clock> traceStartTime;

} // anonymous namespace

TraceManager::~TraceManager()
{
	if (OpenApoc::Trace::enabled)
		this->write();
}

void TraceManager::write()
{
	assert(OpenApoc::Trace::enabled);
	OpenApoc::Trace::enabled = false;

	std::ofstream outFile("openapoc_trace.json");

	// FIXME: Use proper json parser instead of magically constructing from strings?

	outFile << "{\"traceEvents\":[\n";

	bool firstEvent = true;

	listMutex.lock();
	for (auto &eventList : lists)
	{
		for (auto &event : eventList->events)
		{
			if (!firstEvent)
				outFile << ",\n";

			firstEvent = false;

			outFile << "{"
			        << "\"pid\":1,"
			        << "\"tid\":\"" << eventList->tid.str() << "\","
			        // Time is in microseconds, not nanoseconds
			        << "\"ts\":" << event.timeNS / 1000 << ","
			        << "\"name\":\"" << event.name.str() << "\",";

			switch (event.type)
			{
				case EventType::Begin:
				{
					outFile << "\"ph\":\"B\"";

					if (!event.args.empty())
					{
						outFile << ",\"args\":{";

						bool firstArg = true;

						for (auto &arg : event.args)
						{
							if (!firstArg)
								outFile << ",";
							firstArg = false;
							outFile << "\"" << arg.first.str() << "\":\"" << arg.second.str()
							        << "\"";
						}
						outFile << "}";
					}
					break;
				}
				case EventType::End:
					outFile << "\"ph\":\"E\"";
					break;
			}
			outFile << "}";
		}
	}
	listMutex.unlock();
	outFile << "]}\n";
	outFile.flush();
}

namespace OpenApoc
{

bool Trace::enabled = false;

void Trace::enable()
{
	assert(!trace_manager);
	trace_manager.reset(new TraceManager);
#if defined(BROKEN_THREAD_LOCAL)
	pthread_key_create(&eventListKey, NULL);
#endif
	Trace::enabled = true;
	traceStartTime = std::chrono::high_resolution_clock::now();
}

void Trace::disable()
{
	assert(trace_manager);
	trace_manager->write();
	trace_manager.reset(nullptr);
#if defined(BROKEN_THREAD_LOCAL)
	pthread_key_delete(&eventListKey);
#endif
	Trace::enabled = false;
}

void Trace::setThreadName(const UString &name)
{
#if defined(PTHREADS_AVAILABLE)
	pthread_setname_np(pthread_self(), name.c_str());
#endif
	if (!Trace::enabled)
		return;

#if defined(BROKEN_THREAD_LOCAL)
	EventList *events = (EventList *)pthread_getspecific(eventListKey);
	if (!events)
	{
		events = trace_manager->createThreadEventList();
		pthread_setspecific(eventListKey, events);
	}
#else
	if (!events)
		events = trace_manager->createThreadEventList();
#endif

	events->tid = name;
}

void Trace::start(const UString &name, const std::vector<std::pair<UString, UString>> &args)
{
	if (!Trace::enabled)
		return;
#if defined(BROKEN_THREAD_LOCAL)
	EventList *events = (EventList *)pthread_getspecific(eventListKey);
	if (!events)
	{
		events = trace_manager->createThreadEventList();
		pthread_setspecific(eventListKey, events);
	}
#else
	if (!events)
		events = trace_manager->createThreadEventList();
#endif

	auto timeNow = std::chrono::high_resolution_clock::now();
	uint64_t timeNS = std::chrono::duration<uint64_t, std::nano>(timeNow - traceStartTime).count();
	events->events.emplace_back(EventType::Begin, name, args, timeNS);
}
void Trace::end(const UString &name)
{
	if (!Trace::enabled)
		return;
#if defined(BROKEN_THREAD_LOCAL)
	EventList *events = (EventList *)pthread_getspecific(eventListKey);
	if (!events)
	{
		events = trace_manager->createThreadEventList();
		pthread_setspecific(eventListKey, events);
	}
#else
	if (!events)
		events = trace_manager->createThreadEventList();
#endif
	auto timeNow = std::chrono::high_resolution_clock::now();
	uint64_t timeNS = std::chrono::duration<uint64_t, std::nano>(timeNow - traceStartTime).count();
	events->events.emplace_back(EventType::End, name, std::vector<std::pair<UString, UString>>{},
	                            timeNS);
}

} // namespace OpenApoc
<commit_msg>Fix typo in trace.cpp<commit_after>#include "framework/trace.h"
#include <fstream>
#include <memory>
#include <mutex>
#include <sstream>
#include <thread>

namespace
{

using OpenApoc::UString;

enum class EventType
{
	Begin,
	End,
};

class TraceEvent
{
  public:
	EventType type;
	UString name;
	std::vector<std::pair<UString, UString>> args;
	uint64_t timeNS;
	TraceEvent(EventType type, const UString &name,
	           const std::vector<std::pair<UString, UString>> &args, uint64_t timeNS)
	    : type(type), name(name), args(args), timeNS(timeNS)
	{
	}
};

class EventList
{
  public:
	UString tid;
	std::vector<TraceEvent> events;
};

class TraceManager
{
  public:
	// We need a list of all the EventLists created for each thread to dump out & free at
	// TraceManager destructor time
	std::list<std::unique_ptr<EventList>> lists;
	std::mutex listMutex;
	EventList *createThreadEventList()
	{
		std::stringstream ss;
		listMutex.lock();
		EventList *list = new EventList;
		ss << std::this_thread::get_id();
		list->tid = ss.str();
		lists.emplace_back(list);
		listMutex.unlock();
		return list;
	}
	~TraceManager();
	void write();
};

static std::unique_ptr<TraceManager> trace_manager;

#if defined(PTHREADS_AVAILABLE)
#include <pthread.h>
#endif

// thread_local isn't implemented until msvc 2015 (_MSC_VER 1900)
#if defined(_MSC_VER) && _MSC_VER < 1900
static __declspec(thread) EventList *events = nullptr;
#else
#if defined(BROKEN_THREAD_LOCAL)
#warning Using pthread path

static pthread_key_t eventListKey;

#else
static thread_local EventList *events = nullptr;
#endif
#endif
static std::chrono::time_point<std::chrono::high_resolution_clock> traceStartTime;

} // anonymous namespace

TraceManager::~TraceManager()
{
	if (OpenApoc::Trace::enabled)
		this->write();
}

void TraceManager::write()
{
	assert(OpenApoc::Trace::enabled);
	OpenApoc::Trace::enabled = false;

	std::ofstream outFile("openapoc_trace.json");

	// FIXME: Use proper json parser instead of magically constructing from strings?

	outFile << "{\"traceEvents\":[\n";

	bool firstEvent = true;

	listMutex.lock();
	for (auto &eventList : lists)
	{
		for (auto &event : eventList->events)
		{
			if (!firstEvent)
				outFile << ",\n";

			firstEvent = false;

			outFile << "{"
			        << "\"pid\":1,"
			        << "\"tid\":\"" << eventList->tid.str() << "\","
			        // Time is in microseconds, not nanoseconds
			        << "\"ts\":" << event.timeNS / 1000 << ","
			        << "\"name\":\"" << event.name.str() << "\",";

			switch (event.type)
			{
				case EventType::Begin:
				{
					outFile << "\"ph\":\"B\"";

					if (!event.args.empty())
					{
						outFile << ",\"args\":{";

						bool firstArg = true;

						for (auto &arg : event.args)
						{
							if (!firstArg)
								outFile << ",";
							firstArg = false;
							outFile << "\"" << arg.first.str() << "\":\"" << arg.second.str()
							        << "\"";
						}
						outFile << "}";
					}
					break;
				}
				case EventType::End:
					outFile << "\"ph\":\"E\"";
					break;
			}
			outFile << "}";
		}
	}
	listMutex.unlock();
	outFile << "]}\n";
	outFile.flush();
}

namespace OpenApoc
{

bool Trace::enabled = false;

void Trace::enable()
{
	assert(!trace_manager);
	trace_manager.reset(new TraceManager);
#if defined(BROKEN_THREAD_LOCAL)
	pthread_key_create(&eventListKey, NULL);
#endif
	Trace::enabled = true;
	traceStartTime = std::chrono::high_resolution_clock::now();
}

void Trace::disable()
{
	assert(trace_manager);
	trace_manager->write();
	trace_manager.reset(nullptr);
#if defined(BROKEN_THREAD_LOCAL)
	pthread_key_delete(eventListKey);
#endif
	Trace::enabled = false;
}

void Trace::setThreadName(const UString &name)
{
#if defined(PTHREADS_AVAILABLE)
	pthread_setname_np(pthread_self(), name.c_str());
#endif
	if (!Trace::enabled)
		return;

#if defined(BROKEN_THREAD_LOCAL)
	EventList *events = (EventList *)pthread_getspecific(eventListKey);
	if (!events)
	{
		events = trace_manager->createThreadEventList();
		pthread_setspecific(eventListKey, events);
	}
#else
	if (!events)
		events = trace_manager->createThreadEventList();
#endif

	events->tid = name;
}

void Trace::start(const UString &name, const std::vector<std::pair<UString, UString>> &args)
{
	if (!Trace::enabled)
		return;
#if defined(BROKEN_THREAD_LOCAL)
	EventList *events = (EventList *)pthread_getspecific(eventListKey);
	if (!events)
	{
		events = trace_manager->createThreadEventList();
		pthread_setspecific(eventListKey, events);
	}
#else
	if (!events)
		events = trace_manager->createThreadEventList();
#endif

	auto timeNow = std::chrono::high_resolution_clock::now();
	uint64_t timeNS = std::chrono::duration<uint64_t, std::nano>(timeNow - traceStartTime).count();
	events->events.emplace_back(EventType::Begin, name, args, timeNS);
}
void Trace::end(const UString &name)
{
	if (!Trace::enabled)
		return;
#if defined(BROKEN_THREAD_LOCAL)
	EventList *events = (EventList *)pthread_getspecific(eventListKey);
	if (!events)
	{
		events = trace_manager->createThreadEventList();
		pthread_setspecific(eventListKey, events);
	}
#else
	if (!events)
		events = trace_manager->createThreadEventList();
#endif
	auto timeNow = std::chrono::high_resolution_clock::now();
	uint64_t timeNS = std::chrono::duration<uint64_t, std::nano>(timeNow - traceStartTime).count();
	events->events.emplace_back(EventType::End, name, std::vector<std::pair<UString, UString>>{},
	                            timeNS);
}

} // namespace OpenApoc
<|endoftext|>


<commit_before>#include <iostream>

using namespace std;

class Solution {
public:
    int myAtoi(string str) {
        int begin = 0;
        while (str[begin] == ' ')
        {
            begin++;
        }

        long max_int = INT_MAX;
        long min_int = INT_MIN;

        bool negative = false;

        if (str[begin] == '-')
        {
            negative = true;
            begin++;
        }
        else if (str[begin] == '+')
        {
            begin++;
        }

        long number = 0;
        while (begin < str.size())
        {
            char c = str[begin];
            int d = c - 48;
            if (d < 0 || d > 9)
            {
                break;
            }

            number = number * 10 + d;
            begin++;

            if (negative == true)
            {
                if (-number <= min_int)
                {
                    return min_int;
                }
            }
            else
            {
                if (number >= max_int)
                {
                    return max_int;
                }
            }
        }

        if (negative == true)
        {
            number = -number;
        }

        if (number >= max_int)
        {
            return max_int;
        }

        if (number <= min_int)
        {
            return min_int;
        }

        return number;
    }
};
<commit_msg>update<commit_after>#include <iostream>

using namespace std;

class Solution {
public:
    int myAtoi(string str) {
        int begin = 0;
        while (str[begin] == ' ')
        {
            begin++;
        }

        long max_int = INT_MAX;
        long min_int = INT_MIN;

        bool negative = false;

        if (str[begin] == '-')
        {
            negative = true;
            begin++;
        }
        else if (str[begin] == '+')
        {
            begin++;
        }

        long number = 0;
        while (begin < str.size())
        {
            char c = str[begin];
            int d = c - 48;
            if (d < 0 || d > 9)
            {
                break;
            }

            number = number * 10 + d;
            begin++;

            if (negative == true)
            {
                if (-number <= min_int)
                {
                    return min_int;
                }
            }
            else
            {
                if (number >= max_int)
                {
                    return max_int;
                }
            }
        }

        if (negative == true)
        {
            number = -number;
        }

        if (number >= max_int)
        {
            return max_int;
        }

        if (number <= min_int)
        {
            return min_int;
        }

        return number;
    }
};

class Solution {
public:
    int myAtoi(string str) {
        int idx = 0;
        while (str[idx] == ' ')
        {
            idx++;
        }

        bool negative = false;

        if (str[idx] == '-')
        {
            negative = true;
            idx++;
        }
        else if (str[idx] == '+')
        {
            idx++;
        }

        int num = 0;
        while (idx < str.size())
        {
            int d = str[idx] - 48;
            if (d < 0 || d > 9)
            {
                break;
            }

            int new_num = num * 10 + d;
            if (new_num / 10 != num)
            {
                // overflow
                if (negative == true)
                {
                    return INT_MIN;
                }
                else
                {
                    return INT_MAX;
                }
            }

            num = new_num;
            idx++;
        }

        if (negative == true)
        {
            num = -num;
        }

        return num;
    }
};
<|endoftext|>


<commit_before>#include "ray/gcs/redis_context.h"

#include <unistd.h>

#include <sstream>

extern "C" {
#include "hiredis/adapters/ae.h"
#include "hiredis/async.h"
#include "hiredis/hiredis.h"
}

// TODO(pcm): Integrate into the C++ tree.
#include "state/ray_config.h"

namespace {

/// A helper function to call the callback and delete it from the callback
/// manager if necessary.
void ProcessCallback(int64_t callback_index, const std::string &data) {
  if (callback_index >= 0) {
    bool delete_callback =
        ray::gcs::RedisCallbackManager::instance().get(callback_index)(data);
    // Delete the callback if necessary.
    if (delete_callback) {
      ray::gcs::RedisCallbackManager::instance().remove(callback_index);
    }
  }
}

}  // namespace

namespace ray {

namespace gcs {

// This is a global redis callback which will be registered for every
// asynchronous redis call. It dispatches the appropriate callback
// that was registered with the RedisCallbackManager.
void GlobalRedisCallback(void *c, void *r, void *privdata) {
  if (r == nullptr) {
    return;
  }
  int64_t callback_index = reinterpret_cast<int64_t>(privdata);
  redisReply *reply = reinterpret_cast<redisReply *>(r);
  std::string data = "";
  // Parse the response.
  switch (reply->type) {
  case (REDIS_REPLY_NIL): {
    // Do not add any data for a nil response.
  } break;
  case (REDIS_REPLY_STRING): {
    data = std::string(reply->str, reply->len);
  } break;
  case (REDIS_REPLY_STATUS): {
  } break;
  case (REDIS_REPLY_ERROR): {
    RAY_LOG(ERROR) << "Redis error " << reply->str;
  } break;
  case (REDIS_REPLY_INTEGER): {
    data = std::to_string(reply->integer);
    break;
  }
  default:
    RAY_LOG(FATAL) << "Fatal redis error of type " << reply->type << " and with string "
                   << reply->str;
  }
  ProcessCallback(callback_index, data);
}

void SubscribeRedisCallback(void *c, void *r, void *privdata) {
  if (r == nullptr) {
    return;
  }
  int64_t callback_index = reinterpret_cast<int64_t>(privdata);
  redisReply *reply = reinterpret_cast<redisReply *>(r);
  std::string data = "";
  // Parse the response.
  switch (reply->type) {
  case (REDIS_REPLY_ARRAY): {
    // Parse the published message.
    redisReply *message_type = reply->element[0];
    if (strcmp(message_type->str, "subscribe") == 0) {
      // If the message is for the initial subscription call, return the empty
      // string as a response to signify that subscription was successful.
    } else if (strcmp(message_type->str, "message") == 0) {
      // If the message is from a PUBLISH, make sure the data is nonempty.
      redisReply *message = reply->element[reply->elements - 1];
      auto notification = std::string(message->str, message->len);
      RAY_CHECK(!notification.empty()) << "Empty message received on subscribe channel";
      data = notification;
    } else {
      RAY_LOG(FATAL) << "Fatal redis error during subscribe" << message_type->str;
    }

  } break;
  case (REDIS_REPLY_ERROR): {
    RAY_LOG(ERROR) << "Redis error " << reply->str;
  } break;
  default:
    RAY_LOG(FATAL) << "Fatal redis error of type " << reply->type << " and with string "
                   << reply->str;
  }
  ProcessCallback(callback_index, data);
}

int64_t RedisCallbackManager::add(const RedisCallback &function) {
  callbacks_.emplace(num_callbacks_, function);
  return num_callbacks_++;
}

RedisCallback &RedisCallbackManager::get(int64_t callback_index) {
  RAY_CHECK(callbacks_.find(callback_index) != callbacks_.end());
  return callbacks_[callback_index];
}

void RedisCallbackManager::remove(int64_t callback_index) {
  callbacks_.erase(callback_index);
}

#define REDIS_CHECK_ERROR(CONTEXT, REPLY)                     \
  if (REPLY == nullptr || REPLY->type == REDIS_REPLY_ERROR) { \
    return Status::RedisError(CONTEXT->errstr);               \
  }

RedisContext::~RedisContext() {
  if (context_) {
    redisFree(context_);
  }
  if (async_context_) {
    redisAsyncFree(async_context_);
  }
  if (subscribe_context_) {
    redisAsyncFree(subscribe_context_);
  }
}

Status AuthenticateRedis(redisContext *context, const std::string &password) {
  if (password == "") {
    return Status::OK();
  }
  redisReply *reply =
      reinterpret_cast<redisReply *>(redisCommand(context, "AUTH %s", password.c_str()));
  REDIS_CHECK_ERROR(context, reply);
  freeReplyObject(reply);
  return Status::OK();
}

Status AuthenticateRedis(redisAsyncContext *context, const std::string &password) {
  if (password == "") {
    return Status::OK();
  }
  int status = redisAsyncCommand(context, NULL, NULL, "AUTH %s", password.c_str());
  if (status == REDIS_ERR) {
    return Status::RedisError(std::string(context->errstr));
  }
  return Status::OK();
}

Status RedisContext::Connect(const std::string &address, int port, bool sharding,
                             const std::string &password = "") {
  int connection_attempts = 0;
  context_ = redisConnect(address.c_str(), port);
  while (context_ == nullptr || context_->err) {
    if (connection_attempts >= RayConfig::instance().redis_db_connect_retries()) {
      if (context_ == nullptr) {
        RAY_LOG(FATAL) << "Could not allocate redis context.";
      }
      if (context_->err) {
        RAY_LOG(FATAL) << "Could not establish connection to redis " << address << ":"
                       << port;
      }
      break;
    }
    RAY_LOG(WARNING) << "Failed to connect to Redis, retrying.";
    // Sleep for a little.
    usleep(RayConfig::instance().redis_db_connect_wait_milliseconds() * 1000);
    context_ = redisConnect(address.c_str(), port);
    connection_attempts += 1;
  }
  RAY_CHECK_OK(AuthenticateRedis(context_, password));

  redisReply *reply = reinterpret_cast<redisReply *>(
      redisCommand(context_, "CONFIG SET notify-keyspace-events Kl"));
  REDIS_CHECK_ERROR(context_, reply);
  freeReplyObject(reply);

  // Connect to async context
  async_context_ = redisAsyncConnect(address.c_str(), port);
  if (async_context_ == nullptr || async_context_->err) {
    RAY_LOG(FATAL) << "Could not establish connection to redis " << address << ":"
                   << port;
  }
  RAY_CHECK_OK(AuthenticateRedis(async_context_, password));

  // Connect to subscribe context
  subscribe_context_ = redisAsyncConnect(address.c_str(), port);
  if (subscribe_context_ == nullptr || subscribe_context_->err) {
    RAY_LOG(FATAL) << "Could not establish subscribe connection to redis " << address
                   << ":" << port;
  }
  RAY_CHECK_OK(AuthenticateRedis(subscribe_context_, password));

  return Status::OK();
}

Status RedisContext::AttachToEventLoop(aeEventLoop *loop) {
  if (redisAeAttach(loop, async_context_) != REDIS_OK ||
      redisAeAttach(loop, subscribe_context_) != REDIS_OK) {
    return Status::RedisError("could not attach redis event loop");
  } else {
    return Status::OK();
  }
}

Status RedisContext::RunAsync(const std::string &command, const UniqueID &id,
                              const uint8_t *data, int64_t length,
                              const TablePrefix prefix, const TablePubsub pubsub_channel,
                              RedisCallback redisCallback, int log_length) {
  int64_t callback_index =
      redisCallback != nullptr ? RedisCallbackManager::instance().add(redisCallback) : -1;
  if (length > 0) {
    if (log_length >= 0) {
      std::string redis_command = command + " %d %d %b %b %d";
      int status = redisAsyncCommand(
          async_context_, reinterpret_cast<redisCallbackFn *>(&GlobalRedisCallback),
          reinterpret_cast<void *>(callback_index), redis_command.c_str(), prefix,
          pubsub_channel, id.data(), id.size(), data, length, log_length);
      if (status == REDIS_ERR) {
        return Status::RedisError(std::string(async_context_->errstr));
      }
    } else {
      std::string redis_command = command + " %d %d %b %b";
      int status = redisAsyncCommand(
          async_context_, reinterpret_cast<redisCallbackFn *>(&GlobalRedisCallback),
          reinterpret_cast<void *>(callback_index), redis_command.c_str(), prefix,
          pubsub_channel, id.data(), id.size(), data, length);
      if (status == REDIS_ERR) {
        return Status::RedisError(std::string(async_context_->errstr));
      }
    }
  } else {
    RAY_CHECK(log_length == -1);
    std::string redis_command = command + " %d %d %b";
    int status = redisAsyncCommand(
        async_context_, reinterpret_cast<redisCallbackFn *>(&GlobalRedisCallback),
        reinterpret_cast<void *>(callback_index), redis_command.c_str(), prefix,
        pubsub_channel, id.data(), id.size());
    if (status == REDIS_ERR) {
      return Status::RedisError(std::string(async_context_->errstr));
    }
  }
  return Status::OK();
}

Status RedisContext::RunArgvAsync(const std::vector<std::string> &args) {
  // Build the arguments.
  std::vector<const char *> argv;
  std::vector<size_t> argc;
  for (size_t i = 0; i < args.size(); ++i) {
    argv.push_back(args[i].data());
    argc.push_back(args[i].size());
  }
  // Run the Redis command.
  int status;
  status = redisAsyncCommandArgv(async_context_, nullptr, nullptr, args.size(),
                                 argv.data(), argc.data());
  if (status == REDIS_ERR) {
    return Status::RedisError(std::string(async_context_->errstr));
  }
  return Status::OK();
}

Status RedisContext::SubscribeAsync(const ClientID &client_id,
                                    const TablePubsub pubsub_channel,
                                    const RedisCallback &redisCallback,
                                    int64_t *out_callback_index) {
  RAY_CHECK(pubsub_channel != TablePubsub::NO_PUBLISH)
      << "Client requested subscribe on a table that does not support pubsub";

  int64_t callback_index = RedisCallbackManager::instance().add(redisCallback);
  RAY_CHECK(out_callback_index != nullptr);
  *out_callback_index = callback_index;
  int status = 0;
  if (client_id.is_nil()) {
    // Subscribe to all messages.
    std::string redis_command = "SUBSCRIBE %d";
    status = redisAsyncCommand(
        subscribe_context_, reinterpret_cast<redisCallbackFn *>(&SubscribeRedisCallback),
        reinterpret_cast<void *>(callback_index), redis_command.c_str(), pubsub_channel);
  } else {
    // Subscribe only to messages sent to this client.
    std::string redis_command = "SUBSCRIBE %d:%b";
    status = redisAsyncCommand(
        subscribe_context_, reinterpret_cast<redisCallbackFn *>(&SubscribeRedisCallback),
        reinterpret_cast<void *>(callback_index), redis_command.c_str(), pubsub_channel,
        client_id.data(), client_id.size());
  }

  if (status == REDIS_ERR) {
    return Status::RedisError(std::string(subscribe_context_->errstr));
  }
  return Status::OK();
}

}  // namespace gcs

}  // namespace ray
<commit_msg>Retry connections to redis for async and subscribe contexts (#3105)<commit_after>#include "ray/gcs/redis_context.h"

#include <unistd.h>

#include <sstream>

extern "C" {
#include "hiredis/adapters/ae.h"
#include "hiredis/async.h"
#include "hiredis/hiredis.h"
}

// TODO(pcm): Integrate into the C++ tree.
#include "state/ray_config.h"

namespace {

/// A helper function to call the callback and delete it from the callback
/// manager if necessary.
void ProcessCallback(int64_t callback_index, const std::string &data) {
  if (callback_index >= 0) {
    bool delete_callback =
        ray::gcs::RedisCallbackManager::instance().get(callback_index)(data);
    // Delete the callback if necessary.
    if (delete_callback) {
      ray::gcs::RedisCallbackManager::instance().remove(callback_index);
    }
  }
}

}  // namespace

namespace ray {

namespace gcs {

// This is a global redis callback which will be registered for every
// asynchronous redis call. It dispatches the appropriate callback
// that was registered with the RedisCallbackManager.
void GlobalRedisCallback(void *c, void *r, void *privdata) {
  if (r == nullptr) {
    return;
  }
  int64_t callback_index = reinterpret_cast<int64_t>(privdata);
  redisReply *reply = reinterpret_cast<redisReply *>(r);
  std::string data = "";
  // Parse the response.
  switch (reply->type) {
  case (REDIS_REPLY_NIL): {
    // Do not add any data for a nil response.
  } break;
  case (REDIS_REPLY_STRING): {
    data = std::string(reply->str, reply->len);
  } break;
  case (REDIS_REPLY_STATUS): {
  } break;
  case (REDIS_REPLY_ERROR): {
    RAY_LOG(ERROR) << "Redis error " << reply->str;
  } break;
  case (REDIS_REPLY_INTEGER): {
    data = std::to_string(reply->integer);
    break;
  }
  default:
    RAY_LOG(FATAL) << "Fatal redis error of type " << reply->type << " and with string "
                   << reply->str;
  }
  ProcessCallback(callback_index, data);
}

void SubscribeRedisCallback(void *c, void *r, void *privdata) {
  if (r == nullptr) {
    return;
  }
  int64_t callback_index = reinterpret_cast<int64_t>(privdata);
  redisReply *reply = reinterpret_cast<redisReply *>(r);
  std::string data = "";
  // Parse the response.
  switch (reply->type) {
  case (REDIS_REPLY_ARRAY): {
    // Parse the published message.
    redisReply *message_type = reply->element[0];
    if (strcmp(message_type->str, "subscribe") == 0) {
      // If the message is for the initial subscription call, return the empty
      // string as a response to signify that subscription was successful.
    } else if (strcmp(message_type->str, "message") == 0) {
      // If the message is from a PUBLISH, make sure the data is nonempty.
      redisReply *message = reply->element[reply->elements - 1];
      auto notification = std::string(message->str, message->len);
      RAY_CHECK(!notification.empty()) << "Empty message received on subscribe channel";
      data = notification;
    } else {
      RAY_LOG(FATAL) << "Fatal redis error during subscribe" << message_type->str;
    }

  } break;
  case (REDIS_REPLY_ERROR): {
    RAY_LOG(ERROR) << "Redis error " << reply->str;
  } break;
  default:
    RAY_LOG(FATAL) << "Fatal redis error of type " << reply->type << " and with string "
                   << reply->str;
  }
  ProcessCallback(callback_index, data);
}

int64_t RedisCallbackManager::add(const RedisCallback &function) {
  callbacks_.emplace(num_callbacks_, function);
  return num_callbacks_++;
}

RedisCallback &RedisCallbackManager::get(int64_t callback_index) {
  RAY_CHECK(callbacks_.find(callback_index) != callbacks_.end());
  return callbacks_[callback_index];
}

void RedisCallbackManager::remove(int64_t callback_index) {
  callbacks_.erase(callback_index);
}

#define REDIS_CHECK_ERROR(CONTEXT, REPLY)                     \
  if (REPLY == nullptr || REPLY->type == REDIS_REPLY_ERROR) { \
    return Status::RedisError(CONTEXT->errstr);               \
  }

RedisContext::~RedisContext() {
  if (context_) {
    redisFree(context_);
  }
  if (async_context_) {
    redisAsyncFree(async_context_);
  }
  if (subscribe_context_) {
    redisAsyncFree(subscribe_context_);
  }
}

Status AuthenticateRedis(redisContext *context, const std::string &password) {
  if (password == "") {
    return Status::OK();
  }
  redisReply *reply =
      reinterpret_cast<redisReply *>(redisCommand(context, "AUTH %s", password.c_str()));
  REDIS_CHECK_ERROR(context, reply);
  freeReplyObject(reply);
  return Status::OK();
}

Status AuthenticateRedis(redisAsyncContext *context, const std::string &password) {
  if (password == "") {
    return Status::OK();
  }
  int status = redisAsyncCommand(context, NULL, NULL, "AUTH %s", password.c_str());
  if (status == REDIS_ERR) {
    return Status::RedisError(std::string(context->errstr));
  }
  return Status::OK();
}

template <typename RedisContext, typename RedisConnectFunction>
Status ConnectWithRetries(const std::string &address, int port,
                          const RedisConnectFunction &connect_function,
                          RedisContext **context) {
  int connection_attempts = 0;
  *context = connect_function(address.c_str(), port);
  while (*context == nullptr || (*context)->err) {
    if (connection_attempts >= RayConfig::instance().redis_db_connect_retries()) {
      if (*context == nullptr) {
        RAY_LOG(FATAL) << "Could not allocate redis context.";
      }
      if ((*context)->err) {
        RAY_LOG(FATAL) << "Could not establish connection to redis " << address << ":"
                       << port << " (context.err = " << (*context)->err << ")";
      }
      break;
    }
    RAY_LOG(WARNING) << "Failed to connect to Redis, retrying.";
    // Sleep for a little.
    usleep(RayConfig::instance().redis_db_connect_wait_milliseconds() * 1000);
    *context = connect_function(address.c_str(), port);
    connection_attempts += 1;
  }
  return Status::OK();
}

Status RedisContext::Connect(const std::string &address, int port, bool sharding,
                             const std::string &password = "") {
  RAY_CHECK_OK(ConnectWithRetries(address, port, redisConnect, &context_));
  RAY_CHECK_OK(AuthenticateRedis(context_, password));

  redisReply *reply = reinterpret_cast<redisReply *>(
      redisCommand(context_, "CONFIG SET notify-keyspace-events Kl"));
  REDIS_CHECK_ERROR(context_, reply);
  freeReplyObject(reply);

  // Connect to async context
  RAY_CHECK_OK(ConnectWithRetries(address, port, redisAsyncConnect, &async_context_));
  RAY_CHECK_OK(AuthenticateRedis(async_context_, password));

  // Connect to subscribe context
  RAY_CHECK_OK(ConnectWithRetries(address, port, redisAsyncConnect, &subscribe_context_));
  RAY_CHECK_OK(AuthenticateRedis(subscribe_context_, password));

  return Status::OK();
}

Status RedisContext::AttachToEventLoop(aeEventLoop *loop) {
  if (redisAeAttach(loop, async_context_) != REDIS_OK ||
      redisAeAttach(loop, subscribe_context_) != REDIS_OK) {
    return Status::RedisError("could not attach redis event loop");
  } else {
    return Status::OK();
  }
}

Status RedisContext::RunAsync(const std::string &command, const UniqueID &id,
                              const uint8_t *data, int64_t length,
                              const TablePrefix prefix, const TablePubsub pubsub_channel,
                              RedisCallback redisCallback, int log_length) {
  int64_t callback_index =
      redisCallback != nullptr ? RedisCallbackManager::instance().add(redisCallback) : -1;
  if (length > 0) {
    if (log_length >= 0) {
      std::string redis_command = command + " %d %d %b %b %d";
      int status = redisAsyncCommand(
          async_context_, reinterpret_cast<redisCallbackFn *>(&GlobalRedisCallback),
          reinterpret_cast<void *>(callback_index), redis_command.c_str(), prefix,
          pubsub_channel, id.data(), id.size(), data, length, log_length);
      if (status == REDIS_ERR) {
        return Status::RedisError(std::string(async_context_->errstr));
      }
    } else {
      std::string redis_command = command + " %d %d %b %b";
      int status = redisAsyncCommand(
          async_context_, reinterpret_cast<redisCallbackFn *>(&GlobalRedisCallback),
          reinterpret_cast<void *>(callback_index), redis_command.c_str(), prefix,
          pubsub_channel, id.data(), id.size(), data, length);
      if (status == REDIS_ERR) {
        return Status::RedisError(std::string(async_context_->errstr));
      }
    }
  } else {
    RAY_CHECK(log_length == -1);
    std::string redis_command = command + " %d %d %b";
    int status = redisAsyncCommand(
        async_context_, reinterpret_cast<redisCallbackFn *>(&GlobalRedisCallback),
        reinterpret_cast<void *>(callback_index), redis_command.c_str(), prefix,
        pubsub_channel, id.data(), id.size());
    if (status == REDIS_ERR) {
      return Status::RedisError(std::string(async_context_->errstr));
    }
  }
  return Status::OK();
}

Status RedisContext::RunArgvAsync(const std::vector<std::string> &args) {
  // Build the arguments.
  std::vector<const char *> argv;
  std::vector<size_t> argc;
  for (size_t i = 0; i < args.size(); ++i) {
    argv.push_back(args[i].data());
    argc.push_back(args[i].size());
  }
  // Run the Redis command.
  int status;
  status = redisAsyncCommandArgv(async_context_, nullptr, nullptr, args.size(),
                                 argv.data(), argc.data());
  if (status == REDIS_ERR) {
    return Status::RedisError(std::string(async_context_->errstr));
  }
  return Status::OK();
}

Status RedisContext::SubscribeAsync(const ClientID &client_id,
                                    const TablePubsub pubsub_channel,
                                    const RedisCallback &redisCallback,
                                    int64_t *out_callback_index) {
  RAY_CHECK(pubsub_channel != TablePubsub::NO_PUBLISH)
      << "Client requested subscribe on a table that does not support pubsub";

  int64_t callback_index = RedisCallbackManager::instance().add(redisCallback);
  RAY_CHECK(out_callback_index != nullptr);
  *out_callback_index = callback_index;
  int status = 0;
  if (client_id.is_nil()) {
    // Subscribe to all messages.
    std::string redis_command = "SUBSCRIBE %d";
    status = redisAsyncCommand(
        subscribe_context_, reinterpret_cast<redisCallbackFn *>(&SubscribeRedisCallback),
        reinterpret_cast<void *>(callback_index), redis_command.c_str(), pubsub_channel);
  } else {
    // Subscribe only to messages sent to this client.
    std::string redis_command = "SUBSCRIBE %d:%b";
    status = redisAsyncCommand(
        subscribe_context_, reinterpret_cast<redisCallbackFn *>(&SubscribeRedisCallback),
        reinterpret_cast<void *>(callback_index), redis_command.c_str(), pubsub_channel,
        client_id.data(), client_id.size());
  }

  if (status == REDIS_ERR) {
    return Status::RedisError(std::string(subscribe_context_->errstr));
  }
  return Status::OK();
}

}  // namespace gcs

}  // namespace ray
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2004  Justin Karneges
 * Copyright (C) 2004  Brad Hards <bradh@frogmouth.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
#include "qcaprovider.h"
#include <qstringlist.h>
#include <openssl/sha.h>
#include <openssl/md2.h>
#include <openssl/md4.h>
#include <openssl/md5.h>
#include <openssl/ripemd.h>
#include <openssl/hmac.h>

class MD2Context : public QCA::HashContext
{
public:
	MD2_CTX c;

	MD2Context(QCA::Provider *p) : HashContext(p, "md2")
	{
		clear();
	}

	Context *clone() const
	{
		return new MD2Context(*this);
	}

	void clear()
	{
		MD2_Init(&c);
	}

	void update(const QSecureArray &a)
	{
		MD2_Update(&c, (unsigned char *)a.data(), a.size());
	}

	QSecureArray final()
	{
		QSecureArray a(MD2_DIGEST_LENGTH);
		MD2_Final((unsigned char *)a.data(), &c);
		return a;
	}
};

class MD4Context : public QCA::HashContext
{
public:
	MD4_CTX c;

	MD4Context(QCA::Provider *p) : HashContext(p, "md4")
	{
		clear();
	}

	Context *clone() const
	{
		return new MD4Context(*this);
	}

	void clear()
	{
		MD4_Init(&c);
	}

	void update(const QSecureArray &a)
	{
		MD4_Update(&c, (unsigned char *)a.data(), a.size());
	}

	QSecureArray final()
	{
		QSecureArray a(MD4_DIGEST_LENGTH);
		MD4_Final((unsigned char *)a.data(), &c);
		return a;
	}
};

class MD5Context : public QCA::HashContext
{
public:
	MD5_CTX c;

	MD5Context(QCA::Provider *p) : HashContext(p, "md5")
	{
		clear();
	}

	Context *clone() const
	{
		return new MD5Context(*this);
	}

	void clear()
	{
		MD5_Init(&c);
	}

	void update(const QSecureArray &a)
	{
		MD5_Update(&c, (unsigned char *)a.data(), a.size());
	}

	QSecureArray final()
	{
		QSecureArray a(MD5_DIGEST_LENGTH);
		MD5_Final((unsigned char *)a.data(), &c);
		return a;
	}
};

class SHA0Context : public QCA::HashContext
{
public:
	SHA_CTX c;

	SHA0Context(QCA::Provider *p) : HashContext(p, "sha0")
	{
		clear();
	}

	Context *clone() const
	{
		return new SHA0Context(*this);
	}

	void clear()
	{
		SHA_Init(&c);
	}

	void update(const QSecureArray &a)
	{
		SHA_Update(&c, (unsigned char *)a.data(), a.size());
	}

	QSecureArray final()
	{
		QSecureArray a(SHA_DIGEST_LENGTH);
		SHA_Final((unsigned char *)a.data(), &c);
		return a;
	}
};

class SHA1Context : public QCA::HashContext
{
public:
	SHA_CTX c;

	SHA1Context(QCA::Provider *p) : HashContext(p, "sha1")
	{
		clear();
	}

	Context *clone() const
	{
		return new SHA1Context(*this);
	}

	void clear()
	{
		SHA1_Init(&c);
	}

	void update(const QSecureArray &a)
	{
		SHA1_Update(&c, (unsigned char *)a.data(), a.size());
	}

	QSecureArray final()
	{
		QSecureArray a(SHA_DIGEST_LENGTH);
		SHA1_Final((unsigned char *)a.data(), &c);
		return a;
	}
};

class RIPEMD160Context : public QCA::HashContext
{
public:
	RIPEMD160_CTX c;

	RIPEMD160Context(QCA::Provider *p) : HashContext(p, "ripemd160")
	{
		clear();
	}

	Context *clone() const
	{
		return new RIPEMD160Context(*this);
	}

	void clear()
	{
		RIPEMD160_Init(&c);
	}

	void update(const QSecureArray &a)
	{
		RIPEMD160_Update(&c, (unsigned char *)a.data(), a.size());
	}

	QSecureArray final()
	{
		QSecureArray result(RIPEMD160_DIGEST_LENGTH);
		RIPEMD160_Final((unsigned char *)result.data(), &c);
		return result;
	}
};

class HMACMD5Context : public QCA::MACContext
{
public:
	HMAC_CTX c;

	HMACMD5Context(QCA::Provider *p) : MACContext( p, "hmac(md5)" )
	{
		HMAC_CTX_init( &c );
	}

	Context *clone() const
	{
		return new HMACMD5Context(*this);
	}

	void setup(const QCA::SymmetricKey &key)
	{
		HMAC_Init_ex( &c, key.data(), key.size(), EVP_md5(), 0 );
	}

	QCA::KeyLength keyLength() const
	{

	}

	void update(const QSecureArray &a)
	{
		HMAC_Update( &c, (unsigned char *)a.data(), a.size() );
	}

	void final( QSecureArray *out)
	{
		unsigned int outSize;
		out->resize( MD5_DIGEST_LENGTH );
		HMAC_Final(&c, (unsigned char *)out->data(), &(outSize) );
		HMAC_CTX_cleanup(&c);
	}
};

class HMACSHA1Context : public QCA::MACContext
{
public:
	HMAC_CTX c;

	HMACSHA1Context(QCA::Provider *p) : MACContext( p, "hmac(sha1)" )
	{
		HMAC_CTX_init( &c );
	}

	Context *clone() const
	{
		return new HMACSHA1Context(*this);
	}

	void setup(const QCA::SymmetricKey &key)
	{
		HMAC_Init_ex( &c, key.data(), key.size(), EVP_sha1(), 0 );
	}

	QCA::KeyLength keyLength() const
	{

	}

	void update(const QSecureArray &a)
	{
		HMAC_Update( &c, (unsigned char *)a.data(), a.size() );
	}

	void final( QSecureArray *out)
	{
		unsigned int outSize;
		out->resize( SHA_DIGEST_LENGTH );
		HMAC_Final(&c, (unsigned char *)out->data(), &(outSize) );
		HMAC_CTX_cleanup(&c);
	}
};

class HMACRIPEMD160Context : public QCA::MACContext
{
public:
	HMAC_CTX c;

	HMACRIPEMD160Context(QCA::Provider *p) : MACContext( p, "hmac(ripemd160)" )
	{
		HMAC_CTX_init( &c );
	}

	Context *clone() const
	{
		return new HMACRIPEMD160Context(*this);
	}

	void setup(const QCA::SymmetricKey &key)
	{
		HMAC_Init_ex( &c, key.data(), key.size(), EVP_ripemd160(), 0 );
	}

	QCA::KeyLength keyLength() const
	{

	}

	void update(const QSecureArray &a)
	{
		HMAC_Update( &c, (unsigned char *)a.data(), a.size() );
	}

	void final( QSecureArray *out)
	{
		unsigned int outSize;
		out->resize( RIPEMD160_DIGEST_LENGTH );
		HMAC_Final(&c, (unsigned char *)out->data(), &(outSize) );
		HMAC_CTX_cleanup(&c);
	}
};

class opensslProvider : public QCA::Provider
{
public:
	void init()
	{
	}

	QString name() const
	{
		return "qca-openssl";
	}

	QStringList features() const
	{
		QStringList list;
		list += "sha0";
		list += "sha1";
		list += "ripemd160";
		list += "md2";
		list += "md4";
		list += "md5";
		list += "hmac(md5)";
		list += "hmac(sha1)";
		list += "hmac(ripemd160)";
		return list;
	}

	Context *createContext(const QString &type)
	{
		if ( type == "sha0" )
			return new SHA0Context( this );
		else if ( type == "sha1" )
			return new SHA1Context( this );
		else if ( type == "ripemd160" )
			return new RIPEMD160Context( this );
		else if ( type == "md2" )
			return new MD2Context( this );
		else if ( type == "md4" )
			return new MD4Context( this );
		else if ( type == "md5" )
			return new MD5Context( this );
		else if ( type == "hmac(md5)" )
			return new HMACMD5Context( this );
		else if ( type == "hmac(sha1)" )
			return new HMACSHA1Context( this );
		else if ( type == "hmac(ripemd160)" )
			return new HMACRIPEMD160Context( this );
		else
			return 0;
	}
};

QCA_EXPORT_PLUGIN(opensslProvider);
<commit_msg>KeyLength fixes.<commit_after>/*
 * Copyright (C) 2004  Justin Karneges
 * Copyright (C) 2004  Brad Hards <bradh@frogmouth.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
#include "qcaprovider.h"
#include <qstringlist.h>
#include <openssl/sha.h>
#include <openssl/md2.h>
#include <openssl/md4.h>
#include <openssl/md5.h>
#include <openssl/ripemd.h>
#include <openssl/hmac.h>

class MD2Context : public QCA::HashContext
{
public:
	MD2_CTX c;

	MD2Context(QCA::Provider *p) : HashContext(p, "md2")
	{
		clear();
	}

	Context *clone() const
	{
		return new MD2Context(*this);
	}

	void clear()
	{
		MD2_Init(&c);
	}

	void update(const QSecureArray &a)
	{
		MD2_Update(&c, (unsigned char *)a.data(), a.size());
	}

	QSecureArray final()
	{
		QSecureArray a(MD2_DIGEST_LENGTH);
		MD2_Final((unsigned char *)a.data(), &c);
		return a;
	}
};

class MD4Context : public QCA::HashContext
{
public:
	MD4_CTX c;

	MD4Context(QCA::Provider *p) : HashContext(p, "md4")
	{
		clear();
	}

	Context *clone() const
	{
		return new MD4Context(*this);
	}

	void clear()
	{
		MD4_Init(&c);
	}

	void update(const QSecureArray &a)
	{
		MD4_Update(&c, (unsigned char *)a.data(), a.size());
	}

	QSecureArray final()
	{
		QSecureArray a(MD4_DIGEST_LENGTH);
		MD4_Final((unsigned char *)a.data(), &c);
		return a;
	}
};

class MD5Context : public QCA::HashContext
{
public:
	MD5_CTX c;

	MD5Context(QCA::Provider *p) : HashContext(p, "md5")
	{
		clear();
	}

	Context *clone() const
	{
		return new MD5Context(*this);
	}

	void clear()
	{
		MD5_Init(&c);
	}

	void update(const QSecureArray &a)
	{
		MD5_Update(&c, (unsigned char *)a.data(), a.size());
	}

	QSecureArray final()
	{
		QSecureArray a(MD5_DIGEST_LENGTH);
		MD5_Final((unsigned char *)a.data(), &c);
		return a;
	}
};

class SHA0Context : public QCA::HashContext
{
public:
	SHA_CTX c;

	SHA0Context(QCA::Provider *p) : HashContext(p, "sha0")
	{
		clear();
	}

	Context *clone() const
	{
		return new SHA0Context(*this);
	}

	void clear()
	{
		SHA_Init(&c);
	}

	void update(const QSecureArray &a)
	{
		SHA_Update(&c, (unsigned char *)a.data(), a.size());
	}

	QSecureArray final()
	{
		QSecureArray a(SHA_DIGEST_LENGTH);
		SHA_Final((unsigned char *)a.data(), &c);
		return a;
	}
};

class SHA1Context : public QCA::HashContext
{
public:
	SHA_CTX c;

	SHA1Context(QCA::Provider *p) : HashContext(p, "sha1")
	{
		clear();
	}

	Context *clone() const
	{
		return new SHA1Context(*this);
	}

	void clear()
	{
		SHA1_Init(&c);
	}

	void update(const QSecureArray &a)
	{
		SHA1_Update(&c, (unsigned char *)a.data(), a.size());
	}

	QSecureArray final()
	{
		QSecureArray a(SHA_DIGEST_LENGTH);
		SHA1_Final((unsigned char *)a.data(), &c);
		return a;
	}
};

class RIPEMD160Context : public QCA::HashContext
{
public:
	RIPEMD160_CTX c;

	RIPEMD160Context(QCA::Provider *p) : HashContext(p, "ripemd160")
	{
		clear();
	}

	Context *clone() const
	{
		return new RIPEMD160Context(*this);
	}

	void clear()
	{
		RIPEMD160_Init(&c);
	}

	void update(const QSecureArray &a)
	{
		RIPEMD160_Update(&c, (unsigned char *)a.data(), a.size());
	}

	QSecureArray final()
	{
		QSecureArray result(RIPEMD160_DIGEST_LENGTH);
		RIPEMD160_Final((unsigned char *)result.data(), &c);
		return result;
	}
};

class HMACMD5Context : public QCA::MACContext
{
public:
	HMAC_CTX c;

	HMACMD5Context(QCA::Provider *p) : MACContext( p, "hmac(md5)" )
	{
		HMAC_CTX_init( &c );
	}

	Context *clone() const
	{
		return new HMACMD5Context(*this);
	}

	void setup(const QCA::SymmetricKey &key)
	{
		HMAC_Init_ex( &c, key.data(), key.size(), EVP_md5(), 0 );
	}

	QCA::KeyLength keyLength() const
	{
		return anyKeyLength();
	}

	void update(const QSecureArray &a)
	{
		HMAC_Update( &c, (unsigned char *)a.data(), a.size() );
	}

	void final( QSecureArray *out)
	{
		unsigned int outSize;
		out->resize( MD5_DIGEST_LENGTH );
		HMAC_Final(&c, (unsigned char *)out->data(), &(outSize) );
		HMAC_CTX_cleanup(&c);
	}
};

class HMACSHA1Context : public QCA::MACContext
{
public:
	HMAC_CTX c;

	HMACSHA1Context(QCA::Provider *p) : MACContext( p, "hmac(sha1)" )
	{
		HMAC_CTX_init( &c );
	}

	Context *clone() const
	{
		return new HMACSHA1Context(*this);
	}

	void setup(const QCA::SymmetricKey &key)
	{
		HMAC_Init_ex( &c, key.data(), key.size(), EVP_sha1(), 0 );
	}

	QCA::KeyLength keyLength() const
	{
		return anyKeyLength();
	}

	void update(const QSecureArray &a)
	{
		HMAC_Update( &c, (unsigned char *)a.data(), a.size() );
	}

	void final( QSecureArray *out)
	{
		unsigned int outSize;
		out->resize( SHA_DIGEST_LENGTH );
		HMAC_Final(&c, (unsigned char *)out->data(), &(outSize) );
		HMAC_CTX_cleanup(&c);
	}
};

class HMACRIPEMD160Context : public QCA::MACContext
{
public:
	HMAC_CTX c;

	HMACRIPEMD160Context(QCA::Provider *p) : MACContext( p, "hmac(ripemd160)" )
	{
		HMAC_CTX_init( &c );
	}

	Context *clone() const
	{
		return new HMACRIPEMD160Context(*this);
	}

	void setup(const QCA::SymmetricKey &key)
	{
		HMAC_Init_ex( &c, key.data(), key.size(), EVP_ripemd160(), 0 );
	}

	QCA::KeyLength keyLength() const
	{
		return anyKeyLength();
	}

	void update(const QSecureArray &a)
	{
		HMAC_Update( &c, (unsigned char *)a.data(), a.size() );
	}

	void final( QSecureArray *out)
	{
		unsigned int outSize;
		out->resize( RIPEMD160_DIGEST_LENGTH );
		HMAC_Final(&c, (unsigned char *)out->data(), &(outSize) );
		HMAC_CTX_cleanup(&c);
	}
};

class opensslProvider : public QCA::Provider
{
public:
	void init()
	{
	}

	QString name() const
	{
		return "qca-openssl";
	}

	QStringList features() const
	{
		QStringList list;
		list += "sha0";
		list += "sha1";
		list += "ripemd160";
		list += "md2";
		list += "md4";
		list += "md5";
		list += "hmac(md5)";
		list += "hmac(sha1)";
		list += "hmac(ripemd160)";
		return list;
	}

	Context *createContext(const QString &type)
	{
		if ( type == "sha0" )
			return new SHA0Context( this );
		else if ( type == "sha1" )
			return new SHA1Context( this );
		else if ( type == "ripemd160" )
			return new RIPEMD160Context( this );
		else if ( type == "md2" )
			return new MD2Context( this );
		else if ( type == "md4" )
			return new MD4Context( this );
		else if ( type == "md5" )
			return new MD5Context( this );
		else if ( type == "hmac(md5)" )
			return new HMACMD5Context( this );
		else if ( type == "hmac(sha1)" )
			return new HMACSHA1Context( this );
		else if ( type == "hmac(ripemd160)" )
			return new HMACRIPEMD160Context( this );
		else
			return 0;
	}
};

QCA_EXPORT_PLUGIN(opensslProvider);
<|endoftext|>


<commit_before>/*
 *  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 Library 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, USA.
 *
 * UsbWidget.cpp
 * Read and Write to a USB Widget.
 * Copyright (C) 2010 Simon Newton
 */

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include <string>
#include "ola/Logging.h"
#include "plugins/usbpro/UsbWidget.h"

namespace ola {
namespace plugin {
namespace usbpro {

UsbWidget::UsbWidget(ola::network::ConnectedSocket *socket)
    : m_callback(NULL),
      m_socket(socket),
      m_state(PRE_SOM),
      m_bytes_received(0) {
  m_socket->SetOnData(NewCallback(this, &UsbWidget::SocketReady));
}

UsbWidget::~UsbWidget() {
  if (m_callback)
    delete m_callback;
  // don't delete because ownership is transferred to the ss so that device
  // removal works correctly. If you delete the socket the OnClose closure will
  // be deleted, which breaks if it's already running.
  m_socket->Close();
  m_socket = NULL;
}

/**
 * Set the closure to be called when we receive a message from the widget
 * @param callback the closure to run, ownership is transferred.
 */
void UsbWidget::SetMessageHandler(
    ola::Callback3<void, uint8_t, const uint8_t*, unsigned int> *callback) {
  if (m_callback)
    delete m_callback;
  m_callback = callback;
}

/*
 * Set the onRemove handler
 */
void UsbWidget::SetOnRemove(ola::SingleUseCallback0<void> *on_close) {
  m_socket->SetOnClose(on_close);
}

/*
 * Read data from the widget
 */
void UsbWidget::SocketReady() {
  while (m_socket->DataRemaining() > 0) {
    ReceiveMessage();
  }
}

/*
 * Send the msg
 * @return true if successful, false otherwise
 */
bool UsbWidget::SendMessage(uint8_t label,
                            const uint8_t *data,
                            unsigned int length) const {
  if (length && !data)
    return false;

  message_header header;
  header.som = SOM;
  header.label = label;
  header.len = length & 0xFF;
  header.len_hi = (length & 0xFF00) >> 8;

  // should really use writev here instead
  ssize_t bytes_sent = m_socket->Send(reinterpret_cast<uint8_t*>(&header),
                                      sizeof(header));
  if (bytes_sent != sizeof(header))
    // we've probably screwed framing at this point
    return false;

  if (length) {
    unsigned int bytes_sent = m_socket->Send(data, length);
    if (bytes_sent != length)
      // we've probably screwed framing at this point
      return false;
  }

  uint8_t eom = EOM;
  bytes_sent = m_socket->Send(&eom, sizeof(EOM));
  if (bytes_sent != sizeof(EOM))
    return false;
  return true;
}

/**
 * Open a path and apply the settings required for talking to widgets.
 */
ola::network::ConnectedSocket *UsbWidget::OpenDevice(
    const string &path) {
  struct termios newtio;
  int fd = open(path.data(), O_RDWR | O_NONBLOCK | O_NOCTTY);

  if (fd == -1) {
    OLA_WARN << "Failed to open " << path << " " << strerror(errno);
    return NULL;
  }

  bzero(&newtio, sizeof(newtio));  // clear struct for new port settings
  cfsetispeed(&newtio, B115200);
  cfsetospeed(&newtio, B115200);
  tcsetattr(fd, TCSANOW, &newtio);

  return new ola::network::DeviceSocket(fd);
}

/*
 * Read the data and handle the messages.
 */
void UsbWidget::ReceiveMessage() {
  unsigned int cnt, packet_length;

  switch (m_state) {
    case PRE_SOM:
      do {
        m_socket->Receive(&m_header.som, 1, cnt);
        if (cnt != 1)
          return;
      } while (m_header.som != SOM);
      m_state = RECV_LABEL;
    case RECV_LABEL:
      m_socket->Receive(&m_header.label, 1, cnt);
      if (cnt != 1)
        return;
      m_state = RECV_SIZE_LO;
    case RECV_SIZE_LO:
      m_socket->Receive(&m_header.len, 1, cnt);
      if (cnt != 1)
        return;
      m_state = RECV_SIZE_HI;
    case RECV_SIZE_HI:
      m_socket->Receive(&m_header.len_hi, 1, cnt);
      if (cnt != 1)
        return;

      packet_length = (m_header.len_hi << 8) + m_header.len;
      if (packet_length == 0) {
        m_state = RECV_EOM;
        return;
      } else if (packet_length > MAX_DATA_SIZE) {
        m_state = PRE_SOM;
        return;
      }

      m_bytes_received = 0;
      m_state = RECV_BODY;
    case RECV_BODY:
      packet_length = (m_header.len_hi << 8) + m_header.len;
      m_socket->Receive(
          reinterpret_cast<uint8_t*>(&m_recv_buffer) + m_bytes_received,
          packet_length - m_bytes_received,
          cnt);

      if (!cnt)
        return;

      m_bytes_received += cnt;
      if (m_bytes_received != packet_length)
        return;

      m_state = RECV_EOM;
    case RECV_EOM:
      // check this is a valid frame with an end byte
      uint8_t eom;
      m_socket->Receive(&eom, 1, cnt);
      if (cnt != 1)
        return;

      packet_length = (m_header.len_hi << 8) + m_header.len;

      if (eom == EOM && m_callback)
        m_callback->Run(m_header.label,
                        packet_length ? m_recv_buffer : NULL,
                        packet_length);
      m_state = PRE_SOM;
  }
  return;
}
}  // usbpro
}  // plugin
}  // ola
<commit_msg> * fix a variable name<commit_after>/*
 *  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 Library 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, USA.
 *
 * UsbWidget.cpp
 * Read and Write to a USB Widget.
 * Copyright (C) 2010 Simon Newton
 */

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include <string>
#include "ola/Logging.h"
#include "plugins/usbpro/UsbWidget.h"

namespace ola {
namespace plugin {
namespace usbpro {

UsbWidget::UsbWidget(ola::network::ConnectedSocket *socket)
    : m_callback(NULL),
      m_socket(socket),
      m_state(PRE_SOM),
      m_bytes_received(0) {
  m_socket->SetOnData(NewCallback(this, &UsbWidget::SocketReady));
}

UsbWidget::~UsbWidget() {
  if (m_callback)
    delete m_callback;
  // don't delete because ownership is transferred to the ss so that device
  // removal works correctly. If you delete the socket the OnClose closure will
  // be deleted, which breaks if it's already running.
  m_socket->Close();
  m_socket = NULL;
}

/**
 * Set the closure to be called when we receive a message from the widget
 * @param callback the closure to run, ownership is transferred.
 */
void UsbWidget::SetMessageHandler(
    ola::Callback3<void, uint8_t, const uint8_t*, unsigned int> *callback) {
  if (m_callback)
    delete m_callback;
  m_callback = callback;
}

/*
 * Set the onRemove handler
 */
void UsbWidget::SetOnRemove(ola::SingleUseCallback0<void> *on_close) {
  m_socket->SetOnClose(on_close);
}

/*
 * Read data from the widget
 */
void UsbWidget::SocketReady() {
  while (m_socket->DataRemaining() > 0) {
    ReceiveMessage();
  }
}

/*
 * Send the msg
 * @return true if successful, false otherwise
 */
bool UsbWidget::SendMessage(uint8_t label,
                            const uint8_t *data,
                            unsigned int length) const {
  if (length && !data)
    return false;

  message_header header;
  header.som = SOM;
  header.label = label;
  header.len = length & 0xFF;
  header.len_hi = (length & 0xFF00) >> 8;

  // should really use writev here instead
  ssize_t bytes_sent = m_socket->Send(reinterpret_cast<uint8_t*>(&header),
                                      sizeof(header));
  if (bytes_sent != sizeof(header))
    // we've probably screwed framing at this point
    return false;

  if (length) {
    unsigned int bytes_sent = m_socket->Send(data, length);
    if (bytes_sent != length)
      // we've probably screwed framing at this point
      return false;
  }

  uint8_t eom = EOM;
  bytes_sent = m_socket->Send(&eom, sizeof(EOM));
  if (bytes_sent != sizeof(EOM))
    return false;
  return true;
}

/**
 * Open a path and apply the settings required for talking to widgets.
 */
ola::network::ConnectedSocket *UsbWidget::OpenDevice(
    const string &path) {
  struct termios newtio;
  int fd = open(path.data(), O_RDWR | O_NONBLOCK | O_NOCTTY);

  if (fd == -1) {
    OLA_WARN << "Failed to open " << path << " " << strerror(errno);
    return NULL;
  }

  bzero(&newtio, sizeof(newtio));  // clear struct for new port settings
  cfsetispeed(&newtio, B115200);
  cfsetospeed(&newtio, B115200);
  tcsetattr(fd, TCSANOW, &newtio);

  return new ola::network::DeviceSocket(fd);
}

/*
 * Read the data and handle the messages.
 */
void UsbWidget::ReceiveMessage() {
  unsigned int count, packet_length;

  switch (m_state) {
    case PRE_SOM:
      do {
        m_socket->Receive(&m_header.som, 1, count);
        if (count != 1)
          return;
      } while (m_header.som != SOM);
      m_state = RECV_LABEL;
    case RECV_LABEL:
      m_socket->Receive(&m_header.label, 1, count);
      if (count != 1)
        return;
      m_state = RECV_SIZE_LO;
    case RECV_SIZE_LO:
      m_socket->Receive(&m_header.len, 1, count);
      if (count != 1)
        return;
      m_state = RECV_SIZE_HI;
    case RECV_SIZE_HI:
      m_socket->Receive(&m_header.len_hi, 1, count);
      if (count != 1)
        return;

      packet_length = (m_header.len_hi << 8) + m_header.len;
      if (packet_length == 0) {
        m_state = RECV_EOM;
        return;
      } else if (packet_length > MAX_DATA_SIZE) {
        m_state = PRE_SOM;
        return;
      }

      m_bytes_received = 0;
      m_state = RECV_BODY;
    case RECV_BODY:
      packet_length = (m_header.len_hi << 8) + m_header.len;
      m_socket->Receive(
          reinterpret_cast<uint8_t*>(&m_recv_buffer) + m_bytes_received,
          packet_length - m_bytes_received,
          count);

      if (!count)
        return;

      m_bytes_received += count;
      if (m_bytes_received != packet_length)
        return;

      m_state = RECV_EOM;
    case RECV_EOM:
      // check this is a valid frame with an end byte
      uint8_t eom;
      m_socket->Receive(&eom, 1, count);
      if (count != 1)
        return;

      packet_length = (m_header.len_hi << 8) + m_header.len;

      if (eom == EOM && m_callback)
        m_callback->Run(m_header.label,
                        packet_length ? m_recv_buffer : NULL,
                        packet_length);
      m_state = PRE_SOM;
  }
  return;
}
}  // usbpro
}  // plugin
}  // ola
<|endoftext|>