<commit_before>/*
 * Copyright 2009, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "plugin/cross/main.h"

#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "plugin/cross/config.h"
#include "plugin/cross/out_of_memory.h"
#include "plugin/cross/whitelist.h"
#ifdef OS_WIN
#include "breakpad/win/bluescreen_detector.h"
#endif

using glue::_o3d::PluginObject;
using glue::StreamManager;

#if !defined(O3D_INTERNAL_PLUGIN)

#ifdef OS_WIN
#define O3D_DEBUG_LOG_FILENAME L"debug.log"
#else
#define O3D_DEBUG_LOG_FILENAME "debug.log"
#endif

#if defined(OS_WIN) || defined(OS_MACOSX)
o3d::PluginLogging *g_logger = NULL;
static bool g_logging_initialized = false;
#ifdef OS_WIN
static o3d::BluescreenDetector *g_bluescreen_detector = NULL;
#endif  // OS_WIN
#endif  // OS_WIN || OS_MACOSX

// We would normally make this a stack variable in main(), but in a
// plugin, that's not possible, so we make it a global. When the DLL is loaded
// this it gets constructed and when it is unlooaded it is destructed. Note
// that this cannot be done in NP_Initialize and NP_Shutdown because those
// calls do not necessarily signify the DLL being loaded and unloaded. If the
// DLL is not unloaded then the values of global variables are preserved.
static base::AtExitManager g_at_exit_manager;

int BreakpadEnabler::scope_count_ = 0;

#endif  // O3D_INTERNAL_PLUGIN

namespace o3d {

NPError NP_GetValue(NPPVariable variable, void *value) {
  switch (variable) {
    case NPPVpluginNameString:
      *static_cast<char **>(value) = const_cast<char*>(O3D_PLUGIN_NAME);
      break;
    case NPPVpluginDescriptionString:
      *static_cast<char **>(value) = const_cast<char*>(O3D_PLUGIN_DESCRIPTION);
      break;
    default:
      return NPERR_INVALID_PARAM;
      break;
  }
  return NPERR_NO_ERROR;
}

int16 NPP_HandleEvent(NPP instance, void *event) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return 0;
  }

  return PlatformNPPHandleEvent(instance, obj, event);
}

NPError NPP_NewStream(NPP instance,
                      NPMIMEType type,
                      NPStream *stream,
                      NPBool seekable,
                      uint16 *stype) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return NPERR_INVALID_PARAM;
  }

  StreamManager *stream_manager = obj->stream_manager();
  if (stream_manager->NewStream(stream, stype)) {
    return NPERR_NO_ERROR;
  } else {
    // TODO: find out which error we should return
    return NPERR_INVALID_PARAM;
  }
}

NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return NPERR_NO_ERROR;
  }

  StreamManager *stream_manager = obj->stream_manager();
  if (stream_manager->DestroyStream(stream, reason)) {
    return NPERR_NO_ERROR;
  } else {
    // TODO: find out which error we should return
    return NPERR_INVALID_PARAM;
  }
}

int32 NPP_WriteReady(NPP instance, NPStream *stream) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return 0;
  }

  StreamManager *stream_manager = obj->stream_manager();
  return stream_manager->WriteReady(stream);
}

int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len,
                void *buffer) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return 0;
  }

  StreamManager *stream_manager = obj->stream_manager();
  return stream_manager->Write(stream, offset, len, buffer);
}

void NPP_Print(NPP instance, NPPrint *platformPrint) {
  HANDLE_CRASHES;
}

void NPP_URLNotify(NPP instance, const char *url, NPReason reason,
                   void *notifyData) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return;
  }

  StreamManager *stream_manager = obj->stream_manager();
  stream_manager->URLNotify(url, reason, notifyData);
}

NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) {
  HANDLE_CRASHES;
  if (!instance) {
    // Our ActiveX wrapper calls us like this as a way of saying that what it
    // really wants to call is NP_GetValue(). :/
    return o3d::NP_GetValue(variable, value);
  }
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return NPERR_INVALID_PARAM;
  }

  switch (variable) {
    case NPPVpluginScriptableNPObject: {
      void **v = static_cast<void **>(value);
      // Return value is expected to be retained
      GLUE_PROFILE_START(instance, "retainobject");
      NPN_RetainObject(obj);
      GLUE_PROFILE_STOP(instance, "retainobject");
      *v = obj;
      break;
    }
    default: {
      NPError ret = PlatformNPPGetValue(obj, variable, value);
      if (ret == NPERR_INVALID_PARAM)
        // TODO(tschmelcher): Do we still need to fall back to this now that we
        // have the ActiveX special case above?
        ret = o3d::NP_GetValue(variable, value);
      return ret;
    }
  }
  return NPERR_NO_ERROR;
}

NPError NPP_New(NPMIMEType pluginType,
                NPP instance,
                uint16 mode,
                int16 argc,
                char *argn[],
                char *argv[],
                NPSavedData *saved) {
  HANDLE_CRASHES;

#if !defined(O3D_INTERNAL_PLUGIN) && (defined(OS_WIN) || defined(OS_MACOSX))
  // TODO(tschmelcher): Support this on Linux?
  if (!g_logging_initialized) {
    // Get user config metrics. These won't be stored though unless the user
    // opts-in for usagestats logging
    GetUserAgentMetrics(instance);
    GetUserConfigMetrics();
    // Create usage stats logs object
    g_logger = o3d::PluginLogging::InitializeUsageStatsLogging();
#ifdef OS_WIN
    if (g_logger) {
      // Setup blue-screen detection
      g_bluescreen_detector = new o3d::BluescreenDetector();
      g_bluescreen_detector->Start();
    }
#endif
    g_logging_initialized = true;
  }
#endif

  if (!IsDomainAuthorized(instance)) {
    return NPERR_INVALID_URL;
  }

  PluginObject *obj = glue::_o3d::PluginObject::Create(instance);
  instance->pdata = obj;
  glue::_o3d::InitializeGlue(instance);
  obj->Init(argc, argn, argv);
  return PlatformNPPNew(instance, obj);
}

NPError NPP_Destroy(NPP instance, NPSavedData **save) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return NPERR_NO_ERROR;
  }

  NPError err = PlatformNPPDestroy(instance, obj);

  NPN_ReleaseObject(obj);
  instance->pdata = NULL;

  return err;
}

NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) {
  HANDLE_CRASHES;
  return NPERR_GENERIC_ERROR;
}

NPError NPP_SetWindow(NPP instance, NPWindow* window) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return NPERR_NO_ERROR;
  }

  return PlatformNPPSetWindow(instance, obj, window);
}

// Called when the browser has finished attempting to stream data to
// a file as requested. If fname == NULL the attempt was not successful.
void NPP_StreamAsFile(NPP instance, NPStream *stream, const char *fname) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return;
  }

  StreamManager *stream_manager = obj->stream_manager();
  PlatformNPPStreamAsFile(stream_manager, stream, fname);
}

}  // namespace o3d

#if defined(O3D_INTERNAL_PLUGIN)
namespace o3d {
#else
extern "C" {
#endif

NPError EXPORT_SYMBOL OSCALL NP_Initialize(NPNetscapeFuncs *browserFuncs
#ifdef OS_LINUX
                                           ,
                                           NPPluginFuncs *pluginFuncs
#endif
                                           ) {
  HANDLE_CRASHES;
  NPError err = InitializeNPNApi(browserFuncs);
  if (err != NPERR_NO_ERROR) {
    return err;
  }

#ifdef OS_LINUX
  NP_GetEntryPoints(pluginFuncs);
#endif  // OS_LINUX

#if !defined(O3D_INTERNAL_PLUGIN)
  if (!o3d::SetupOutOfMemoryHandler())
    return NPERR_MODULE_LOAD_FAILED_ERROR;
#endif  // O3D_INTERNAL_PLUGIN

  err = o3d::PlatformPreNPInitialize();
  if (err != NPERR_NO_ERROR) {
    return err;
  }

#if !defined(O3D_INTERNAL_PLUGIN)
  // Turn on the logging.
  CommandLine::Init(0, NULL);

  FilePath log;
  file_util::GetTempDir(&log);
  log = log.Append(O3D_DEBUG_LOG_FILENAME);

  InitLogging(log.value().c_str(),
              logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG,
              logging::DONT_LOCK_LOG_FILE,
              logging::APPEND_TO_OLD_LOG_FILE);
#endif  // O3D_INTERNAL_PLUGIN

  DLOG(INFO) << "NP_Initialize";

  return o3d::PlatformPostNPInitialize();
}

NPError EXPORT_SYMBOL OSCALL NP_Shutdown(void) {
  HANDLE_CRASHES;
  DLOG(INFO) << "NP_Shutdown";

  NPError err = o3d::PlatformPreNPShutdown();
  if (err != NPERR_NO_ERROR) {
    return err;
  }

#if !defined(O3D_INTERNAL_PLUGIN)
#if defined(OS_WIN) || defined(OS_MACOSX)
  if (g_logger) {
    // Do a last sweep to aggregate metrics before we shut down
    g_logger->ProcessMetrics(true, false, false);
    delete g_logger;
    g_logger = NULL;
    g_logging_initialized = false;
    stats_report::g_global_metrics.Uninitialize();
  }
#endif  // OS_WIN || OS_MACOSX

  CommandLine::Reset();

#ifdef OS_WIN
  // Strictly speaking, on windows, it's not really necessary to call
  // Stop(), but we do so for completeness
  if (g_bluescreen_detector) {
    g_bluescreen_detector->Stop();
    delete g_bluescreen_detector;
    g_bluescreen_detector = NULL;
  }
#endif  // OS_WIN
#endif  // O3D_INTERNAL_PLUGIN

  return o3d::PlatformPostNPShutdown();
}

NPError EXPORT_SYMBOL OSCALL NP_GetEntryPoints(NPPluginFuncs *pluginFuncs) {
  HANDLE_CRASHES;
  pluginFuncs->version = 11;
  pluginFuncs->size = sizeof(*pluginFuncs);
  pluginFuncs->newp = o3d::NPP_New;
  pluginFuncs->destroy = o3d::NPP_Destroy;
  pluginFuncs->setwindow = o3d::NPP_SetWindow;
  pluginFuncs->newstream = o3d::NPP_NewStream;
  pluginFuncs->destroystream = o3d::NPP_DestroyStream;
  pluginFuncs->asfile = o3d::NPP_StreamAsFile;
  pluginFuncs->writeready = o3d::NPP_WriteReady;
  pluginFuncs->write = o3d::NPP_Write;
  pluginFuncs->print = o3d::NPP_Print;
  pluginFuncs->event = o3d::NPP_HandleEvent;
  pluginFuncs->urlnotify = o3d::NPP_URLNotify;
  pluginFuncs->getvalue = o3d::NPP_GetValue;
  pluginFuncs->setvalue = o3d::NPP_SetValue;

  return NPERR_NO_ERROR;
}

char* NP_GetMIMEDescription(void) {
  return const_cast<char*>(O3D_PLUGIN_NPAPI_MIMETYPE "::O3D MIME");
}

}  // namespace o3d / extern "C"

#if !defined(O3D_INTERNAL_PLUGIN)
extern "C" {
NPError EXPORT_SYMBOL NP_GetValue(void *instance, NPPVariable variable,
                                  void *value) {
  return o3d::NP_GetValue(variable, value);
}
}
#endif
<commit_msg>Detect SSE2 and return if not present to avoid a crash. BUG=none TEST=run o3d on Pentium3.  Voice and Settings page should work. Review URL: http://codereview.chromium.org/6708057<commit_after>/*
 * Copyright 2009, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "plugin/cross/main.h"

#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "plugin/cross/config.h"
#include "plugin/cross/out_of_memory.h"
#include "plugin/cross/whitelist.h"
#ifdef OS_WIN
#include "breakpad/win/bluescreen_detector.h"
#include <intrin.h>
#endif

using glue::_o3d::PluginObject;
using glue::StreamManager;

#if !defined(O3D_INTERNAL_PLUGIN)

#ifdef OS_WIN
#define O3D_DEBUG_LOG_FILENAME L"debug.log"
#else
#define O3D_DEBUG_LOG_FILENAME "debug.log"
#endif

#if defined(OS_WIN) || defined(OS_MACOSX)
o3d::PluginLogging *g_logger = NULL;
static bool g_logging_initialized = false;
#ifdef OS_WIN
static o3d::BluescreenDetector *g_bluescreen_detector = NULL;
#endif  // OS_WIN
#endif  // OS_WIN || OS_MACOSX

// We would normally make this a stack variable in main(), but in a
// plugin, that's not possible, so we make it a global. When the DLL is loaded
// this it gets constructed and when it is unlooaded it is destructed. Note
// that this cannot be done in NP_Initialize and NP_Shutdown because those
// calls do not necessarily signify the DLL being loaded and unloaded. If the
// DLL is not unloaded then the values of global variables are preserved.
static base::AtExitManager g_at_exit_manager;

int BreakpadEnabler::scope_count_ = 0;

#endif  // O3D_INTERNAL_PLUGIN

namespace o3d {

NPError NP_GetValue(NPPVariable variable, void *value) {
  switch (variable) {
    case NPPVpluginNameString:
      *static_cast<char **>(value) = const_cast<char*>(O3D_PLUGIN_NAME);
      break;
    case NPPVpluginDescriptionString:
      *static_cast<char **>(value) = const_cast<char*>(O3D_PLUGIN_DESCRIPTION);
      break;
    default:
      return NPERR_INVALID_PARAM;
      break;
  }
  return NPERR_NO_ERROR;
}

int16 NPP_HandleEvent(NPP instance, void *event) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return 0;
  }

  return PlatformNPPHandleEvent(instance, obj, event);
}

NPError NPP_NewStream(NPP instance,
                      NPMIMEType type,
                      NPStream *stream,
                      NPBool seekable,
                      uint16 *stype) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return NPERR_INVALID_PARAM;
  }

  StreamManager *stream_manager = obj->stream_manager();
  if (stream_manager->NewStream(stream, stype)) {
    return NPERR_NO_ERROR;
  } else {
    // TODO: find out which error we should return
    return NPERR_INVALID_PARAM;
  }
}

NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return NPERR_NO_ERROR;
  }

  StreamManager *stream_manager = obj->stream_manager();
  if (stream_manager->DestroyStream(stream, reason)) {
    return NPERR_NO_ERROR;
  } else {
    // TODO: find out which error we should return
    return NPERR_INVALID_PARAM;
  }
}

int32 NPP_WriteReady(NPP instance, NPStream *stream) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return 0;
  }

  StreamManager *stream_manager = obj->stream_manager();
  return stream_manager->WriteReady(stream);
}

int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len,
                void *buffer) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return 0;
  }

  StreamManager *stream_manager = obj->stream_manager();
  return stream_manager->Write(stream, offset, len, buffer);
}

void NPP_Print(NPP instance, NPPrint *platformPrint) {
  HANDLE_CRASHES;
}

void NPP_URLNotify(NPP instance, const char *url, NPReason reason,
                   void *notifyData) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return;
  }

  StreamManager *stream_manager = obj->stream_manager();
  stream_manager->URLNotify(url, reason, notifyData);
}

NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) {
  HANDLE_CRASHES;
  if (!instance) {
    // Our ActiveX wrapper calls us like this as a way of saying that what it
    // really wants to call is NP_GetValue(). :/
    return o3d::NP_GetValue(variable, value);
  }
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return NPERR_INVALID_PARAM;
  }

  switch (variable) {
    case NPPVpluginScriptableNPObject: {
      void **v = static_cast<void **>(value);
      // Return value is expected to be retained
      GLUE_PROFILE_START(instance, "retainobject");
      NPN_RetainObject(obj);
      GLUE_PROFILE_STOP(instance, "retainobject");
      *v = obj;
      break;
    }
    default: {
      NPError ret = PlatformNPPGetValue(obj, variable, value);
      if (ret == NPERR_INVALID_PARAM)
        // TODO(tschmelcher): Do we still need to fall back to this now that we
        // have the ActiveX special case above?
        ret = o3d::NP_GetValue(variable, value);
      return ret;
    }
  }
  return NPERR_NO_ERROR;
}

NPError NPP_New(NPMIMEType pluginType,
                NPP instance,
                uint16 mode,
                int16 argc,
                char *argn[],
                char *argv[],
                NPSavedData *saved) {
  HANDLE_CRASHES;

#if !defined(O3D_INTERNAL_PLUGIN) && (defined(OS_WIN) || defined(OS_MACOSX))
  // TODO(tschmelcher): Support this on Linux?
  if (!g_logging_initialized) {
    // Get user config metrics. These won't be stored though unless the user
    // opts-in for usagestats logging
    GetUserAgentMetrics(instance);
    GetUserConfigMetrics();
    // Create usage stats logs object
    g_logger = o3d::PluginLogging::InitializeUsageStatsLogging();
#ifdef OS_WIN
    if (g_logger) {
      // Setup blue-screen detection
      g_bluescreen_detector = new o3d::BluescreenDetector();
      g_bluescreen_detector->Start();
    }
#endif
    g_logging_initialized = true;
  }
#endif

  if (!IsDomainAuthorized(instance)) {
    return NPERR_INVALID_URL;
  }

  PluginObject *obj = glue::_o3d::PluginObject::Create(instance);
  instance->pdata = obj;
  glue::_o3d::InitializeGlue(instance);
  obj->Init(argc, argn, argv);
  return PlatformNPPNew(instance, obj);
}

NPError NPP_Destroy(NPP instance, NPSavedData **save) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return NPERR_NO_ERROR;
  }

  NPError err = PlatformNPPDestroy(instance, obj);

  NPN_ReleaseObject(obj);
  instance->pdata = NULL;

  return err;
}

NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) {
  HANDLE_CRASHES;
  return NPERR_GENERIC_ERROR;
}

NPError NPP_SetWindow(NPP instance, NPWindow* window) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return NPERR_NO_ERROR;
  }

  return PlatformNPPSetWindow(instance, obj, window);
}

// Called when the browser has finished attempting to stream data to
// a file as requested. If fname == NULL the attempt was not successful.
void NPP_StreamAsFile(NPP instance, NPStream *stream, const char *fname) {
  HANDLE_CRASHES;
  PluginObject *obj = static_cast<PluginObject *>(instance->pdata);
  if (!obj) {
    return;
  }

  StreamManager *stream_manager = obj->stream_manager();
  PlatformNPPStreamAsFile(stream_manager, stream, fname);
}

}  // namespace o3d

#if defined(O3D_INTERNAL_PLUGIN)
namespace o3d {
#else
extern "C" {
#endif

// Detect CPU has SSE2
#if defined(__pic__) && defined(__i386__)
void __cpuid(int cpu_info[4], int info_type) {
  __asm__ volatile (
    "mov %%ebx, %%edi\n"
    "cpuid\n"
    "xchg %%edi, %%ebx\n"
    : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
    : "a"(info_type)
  );
}
#elif defined(__i386__) || defined(__x86_64__)
void __cpuid(int cpu_info[4], int info_type) {
  __asm__ volatile (
    "cpuid\n"
    : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
    : "a"(info_type)
  );
}
#endif

#if defined(__x86_64__) || defined(_M_X64) || \
    defined(__i386__) || defined(_M_IX86)
bool CpuHasSSE2() {
  int cpu_info[4];
  __cpuid(cpu_info, 1);
  return (cpu_info[3] & 0x04000000) != 0;
}
#endif

NPError EXPORT_SYMBOL OSCALL NP_Initialize(NPNetscapeFuncs *browserFuncs
#ifdef OS_LINUX
                                           ,
                                           NPPluginFuncs *pluginFuncs
#endif
                                           ) {

// On x86, detect lack of SSE2 and quit before a crash occurs.
// TODO(fbarchard): Remove this when -msse2 is removed or SSE2 is required.
#if defined(__x86_64__) || defined(_M_X64) || \
    defined(__i386__) || defined(_M_IX86)
  if (!CpuHasSSE2()) {
    return NPERR_MODULE_LOAD_FAILED_ERROR;
  }
#endif

  HANDLE_CRASHES;
  NPError err = InitializeNPNApi(browserFuncs);
  if (err != NPERR_NO_ERROR) {
    return err;
  }

#ifdef OS_LINUX
  NP_GetEntryPoints(pluginFuncs);
#endif  // OS_LINUX

#if !defined(O3D_INTERNAL_PLUGIN)
  if (!o3d::SetupOutOfMemoryHandler())
    return NPERR_MODULE_LOAD_FAILED_ERROR;
#endif  // O3D_INTERNAL_PLUGIN

  err = o3d::PlatformPreNPInitialize();
  if (err != NPERR_NO_ERROR) {
    return err;
  }

#if !defined(O3D_INTERNAL_PLUGIN)
  // Turn on the logging.
  CommandLine::Init(0, NULL);

  FilePath log;
  file_util::GetTempDir(&log);
  log = log.Append(O3D_DEBUG_LOG_FILENAME);

  InitLogging(log.value().c_str(),
              logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG,
              logging::DONT_LOCK_LOG_FILE,
              logging::APPEND_TO_OLD_LOG_FILE);
#endif  // O3D_INTERNAL_PLUGIN

  DLOG(INFO) << "NP_Initialize";

  return o3d::PlatformPostNPInitialize();
}

NPError EXPORT_SYMBOL OSCALL NP_Shutdown(void) {
  HANDLE_CRASHES;
  DLOG(INFO) << "NP_Shutdown";

  NPError err = o3d::PlatformPreNPShutdown();
  if (err != NPERR_NO_ERROR) {
    return err;
  }

#if !defined(O3D_INTERNAL_PLUGIN)
#if defined(OS_WIN) || defined(OS_MACOSX)
  if (g_logger) {
    // Do a last sweep to aggregate metrics before we shut down
    g_logger->ProcessMetrics(true, false, false);
    delete g_logger;
    g_logger = NULL;
    g_logging_initialized = false;
    stats_report::g_global_metrics.Uninitialize();
  }
#endif  // OS_WIN || OS_MACOSX

  CommandLine::Reset();

#ifdef OS_WIN
  // Strictly speaking, on windows, it's not really necessary to call
  // Stop(), but we do so for completeness
  if (g_bluescreen_detector) {
    g_bluescreen_detector->Stop();
    delete g_bluescreen_detector;
    g_bluescreen_detector = NULL;
  }
#endif  // OS_WIN
#endif  // O3D_INTERNAL_PLUGIN

  return o3d::PlatformPostNPShutdown();
}

NPError EXPORT_SYMBOL OSCALL NP_GetEntryPoints(NPPluginFuncs *pluginFuncs) {
  HANDLE_CRASHES;
  pluginFuncs->version = 11;
  pluginFuncs->size = sizeof(*pluginFuncs);
  pluginFuncs->newp = o3d::NPP_New;
  pluginFuncs->destroy = o3d::NPP_Destroy;
  pluginFuncs->setwindow = o3d::NPP_SetWindow;
  pluginFuncs->newstream = o3d::NPP_NewStream;
  pluginFuncs->destroystream = o3d::NPP_DestroyStream;
  pluginFuncs->asfile = o3d::NPP_StreamAsFile;
  pluginFuncs->writeready = o3d::NPP_WriteReady;
  pluginFuncs->write = o3d::NPP_Write;
  pluginFuncs->print = o3d::NPP_Print;
  pluginFuncs->event = o3d::NPP_HandleEvent;
  pluginFuncs->urlnotify = o3d::NPP_URLNotify;
  pluginFuncs->getvalue = o3d::NPP_GetValue;
  pluginFuncs->setvalue = o3d::NPP_SetValue;

  return NPERR_NO_ERROR;
}

char* NP_GetMIMEDescription(void) {
  return const_cast<char*>(O3D_PLUGIN_NPAPI_MIMETYPE "::O3D MIME");
}

}  // namespace o3d / extern "C"

#if !defined(O3D_INTERNAL_PLUGIN)
extern "C" {
NPError EXPORT_SYMBOL NP_GetValue(void *instance, NPPVariable variable,
                                  void *value) {
  return o3d::NP_GetValue(variable, value);
}
}
#endif
<|endoftext|>


<commit_before>
#include "Stack.h"

#include <vector>
#include <iostream>
#include <iomanip>
#include <cstdint>
#include <cassert>

#include <llvm/IR/Function.h>

#include "Utils.h"

#ifdef _MSC_VER
	#define EXPORT __declspec(dllexport)
#else
	#define EXPORT
#endif

namespace evmcc
{

using StackImpl = std::vector<i256>;

Stack::Stack(llvm::IRBuilder<>& _builder, llvm::Module* _module)
	: m_builder(_builder)
{
	// TODO: Clean up LLVM types
	auto stackPtrTy = m_builder.getInt8PtrTy();
	auto i256Ty = m_builder.getIntNTy(256);
	auto i256PtrTy = i256Ty->getPointerTo();
	auto voidTy = m_builder.getVoidTy();

	auto stackCreate = llvm::Function::Create(llvm::FunctionType::get(stackPtrTy, false),
		llvm::GlobalValue::LinkageTypes::ExternalLinkage, "evmccrt_stack_create", _module);

	llvm::Type* argsTypes[] = {stackPtrTy, i256PtrTy};
	auto funcType = llvm::FunctionType::get(voidTy, argsTypes, false);
	m_stackPush = llvm::Function::Create(funcType,
		llvm::GlobalValue::LinkageTypes::ExternalLinkage, "evmccrt_stack_push", _module);

	m_stackPop = llvm::Function::Create(funcType,
		llvm::GlobalValue::LinkageTypes::ExternalLinkage, "evmccrt_stack_pop", _module);

	llvm::Type* getArgsTypes[] = {stackPtrTy, m_builder.getInt32Ty(), i256PtrTy};
	auto getFuncType = llvm::FunctionType::get(voidTy, getArgsTypes, false);
	m_stackGet = llvm::Function::Create(getFuncType,
		llvm::GlobalValue::LinkageTypes::ExternalLinkage, "evmccrt_stack_get", _module);

	m_stackSet = llvm::Function::Create(getFuncType,
		llvm::GlobalValue::LinkageTypes::ExternalLinkage, "evmccrt_stack_set", _module);

	m_args[0] = m_builder.CreateCall(stackCreate, "stack.ptr");
	m_args[1] = m_builder.CreateAlloca(i256Ty, nullptr, "stack.val");
}

void Stack::push(llvm::Value* _value)
{
	m_builder.CreateStore(_value, m_args[1]);	  // copy value to memory
	m_builder.CreateCall(m_stackPush, m_args);
}

llvm::Value* Stack::pop()
{
	m_builder.CreateCall(m_stackPop, m_args);
	return m_builder.CreateLoad(m_args[1]);
}

llvm::Value* Stack::get(uint32_t _index)
{
	llvm::Value* args[] = {m_args[0], m_builder.getInt32(_index), m_args[1]};
	m_builder.CreateCall(m_stackGet, args);
	return m_builder.CreateLoad(m_args[1]);
}

void Stack::set(uint32_t _index, llvm::Value* _value)
{
	m_builder.CreateStore(_value, m_args[1]);	  // copy value to memory
	llvm::Value* args[] = {m_args[0], m_builder.getInt32(_index), m_args[1]};
	m_builder.CreateCall(m_stackSet, args);
}

llvm::Value* Stack::top()
{
	return get(0);
}

void debugStack(const char* _op, const i256& _word, uint32_t _index = 0)
{
	std::cerr << "STACK " << std::setw(4) << std::setfill(' ') << _op
			  << " [" << std::setw(2) << std::setfill('0') << _index << "] "
			  << std::dec << _word.a
			  << " HEX: " << std::hex;
	if (_word.b || _word.c || _word.d)
	{
		std::cerr << std::setw(16) << _word.d << " "
				  << std::setw(16) << _word.c << " "
				  << std::setw(16) << _word.b << " ";
	}
	std::cerr << std::setw(16) << _word.a << "\n";
}

}

extern "C"
{
	using namespace evmcc;

EXPORT void* evmccrt_stack_create()
{
	auto stack = new StackImpl;
	std::cerr << "STACK create\n";
	return stack;
}

EXPORT void evmccrt_stack_push(void* _stack, void* _pWord)
{
	auto stack = static_cast<StackImpl*>(_stack);
	auto word = static_cast<i256*>(_pWord);
	debugStack("push", *word);
	stack->push_back(*word);
}

EXPORT void evmccrt_stack_pop(void* _stack, void* _pWord)
{
	auto stack = static_cast<StackImpl*>(_stack);
	assert(!stack->empty());
	auto word = &stack->back();
	debugStack("pop", *word);
	auto outWord = static_cast<i256*>(_pWord);
	stack->pop_back();
	*outWord = *word;
}

EXPORT void evmccrt_stack_get(void* _stack, uint32_t _index, void* _pWord)
{
	auto stack = static_cast<StackImpl*>(_stack);
	assert(_index < stack->size());
	auto word = stack->rbegin() + _index;
	debugStack("get", *word, _index);
	auto outWord = static_cast<i256*>(_pWord);
	*outWord = *word;
}

EXPORT void evmccrt_stack_set(void* _stack, uint32_t _index, void* _pWord)
{
	auto stack = static_cast<StackImpl*>(_stack);
	auto word = static_cast<i256*>(_pWord);
	assert(_index < stack->size());
	*(stack->rbegin() + _index) = *word;
	debugStack("set", *word, _index);
}

}	// extern "C"
<commit_msg>Improve stack binary interface<commit_after>
#include "Stack.h"

#include <vector>
#include <iostream>
#include <iomanip>
#include <cstdint>
#include <cassert>

#include <llvm/IR/Function.h>

#include "Utils.h"

#ifdef _MSC_VER
	#define EXPORT __declspec(dllexport)
#else
	#define EXPORT
#endif

namespace evmcc
{

using StackImpl = std::vector<i256>;

Stack::Stack(llvm::IRBuilder<>& _builder, llvm::Module* _module)
	: m_builder(_builder)
{
	// TODO: Clean up LLVM types
	auto stackPtrTy = m_builder.getInt8PtrTy();
	auto i256Ty = m_builder.getIntNTy(256);
	auto i256PtrTy = i256Ty->getPointerTo();
	auto voidTy = m_builder.getVoidTy();

	auto stackCreate = llvm::Function::Create(llvm::FunctionType::get(stackPtrTy, false),
		llvm::GlobalValue::LinkageTypes::ExternalLinkage, "evmccrt_stack_create", _module);

	llvm::Type* argsTypes[] = {stackPtrTy, i256PtrTy};
	auto funcType = llvm::FunctionType::get(voidTy, argsTypes, false);
	m_stackPush = llvm::Function::Create(funcType,
		llvm::GlobalValue::LinkageTypes::ExternalLinkage, "evmccrt_stack_push", _module);

	m_stackPop = llvm::Function::Create(funcType,
		llvm::GlobalValue::LinkageTypes::ExternalLinkage, "evmccrt_stack_pop", _module);

	llvm::Type* getArgsTypes[] = {stackPtrTy, m_builder.getInt32Ty(), i256PtrTy};
	auto getFuncType = llvm::FunctionType::get(voidTy, getArgsTypes, false);
	m_stackGet = llvm::Function::Create(getFuncType,
		llvm::GlobalValue::LinkageTypes::ExternalLinkage, "evmccrt_stack_get", _module);

	m_stackSet = llvm::Function::Create(getFuncType,
		llvm::GlobalValue::LinkageTypes::ExternalLinkage, "evmccrt_stack_set", _module);

	m_args[0] = m_builder.CreateCall(stackCreate, "stack.ptr");
	m_args[1] = m_builder.CreateAlloca(i256Ty, nullptr, "stack.val");
}

void Stack::push(llvm::Value* _value)
{
	m_builder.CreateStore(_value, m_args[1]);	  // copy value to memory
	m_builder.CreateCall(m_stackPush, m_args);
}

llvm::Value* Stack::pop()
{
	m_builder.CreateCall(m_stackPop, m_args);
	return m_builder.CreateLoad(m_args[1]);
}

llvm::Value* Stack::get(uint32_t _index)
{
	llvm::Value* args[] = {m_args[0], m_builder.getInt32(_index), m_args[1]};
	m_builder.CreateCall(m_stackGet, args);
	return m_builder.CreateLoad(m_args[1]);
}

void Stack::set(uint32_t _index, llvm::Value* _value)
{
	m_builder.CreateStore(_value, m_args[1]);	  // copy value to memory
	llvm::Value* args[] = {m_args[0], m_builder.getInt32(_index), m_args[1]};
	m_builder.CreateCall(m_stackSet, args);
}

llvm::Value* Stack::top()
{
	return get(0);
}

void debugStack(const char* _op, const i256& _word, uint32_t _index = 0)
{
	std::cerr << "STACK " << std::setw(4) << std::setfill(' ') << _op
			  << " [" << std::setw(2) << std::setfill('0') << _index << "] "
			  << std::dec << _word.a
			  << " HEX: " << std::hex;
	if (_word.b || _word.c || _word.d)
	{
		std::cerr << std::setw(16) << _word.d << " "
				  << std::setw(16) << _word.c << " "
				  << std::setw(16) << _word.b << " ";
	}
	std::cerr << std::setw(16) << _word.a << "\n";
}

}

extern "C"
{
	using namespace evmcc;

EXPORT void* evmccrt_stack_create()
{
	auto stack = new StackImpl;
	std::cerr << "STACK create\n";
	return stack;
}

EXPORT void evmccrt_stack_push(StackImpl* _stack, i256* _word)
{
	debugStack("push", *_word);
	_stack->push_back(*_word);
}

EXPORT void evmccrt_stack_pop(StackImpl* _stack, i256* _outWord)
{
	assert(!_stack->empty());
	auto word = &_stack->back();
	debugStack("pop", *word);
	_stack->pop_back();
	*_outWord = *word;
}

EXPORT void evmccrt_stack_get(StackImpl* _stack, uint32_t _index, i256* _outWord)
{
	assert(_index < _stack->size());
	auto word = _stack->rbegin() + _index;
	debugStack("get", *word, _index);
	*_outWord = *word;
}

EXPORT void evmccrt_stack_set(StackImpl* _stack, uint32_t _index, i256* _word)
{
	assert(_index < _stack->size());
	*(_stack->rbegin() + _index) = *_word;
	debugStack("set", *_word, _index);
}

}	// extern "C"
<|endoftext|>


<commit_before>#include <glog/logging.h>
#include <gtest/gtest.h>
#include <openssl/bn.h>
#include <sys/resource.h>
#include <algorithm>
#include <map>
#include <random>
#include <string>

#include "merkletree/sparse_merkle_tree.h"
#include "util/openssl_scoped_types.h"
#include "util/testing.h"
#include "util/util.h"

namespace {

using cert_trans::ScopedBIGNUM;
using std::fill;
using std::lower_bound;
using std::map;
using std::mt19937;
using std::ostringstream;
using std::pair;
using std::random_device;
using std::reverse;
using std::string;
using std::to_string;
using std::unique_ptr;
using std::vector;
using util::ToBase64;

const char kEmptyRootHashB64[] =
    "xmifEIEqCYCXbZUz2Dh1KCFmFZVn7DUVVxbBQTr1PWo=";

struct KeyComp {
  const BIGNUM* AsBN(const ScopedBIGNUM& a) const {
    return a.get();
  }

  const BIGNUM* AsBN(const BIGNUM* a) const {
    return a;
  }

  const BIGNUM* AsBN(const pair<ScopedBIGNUM, string>& a) const {
    return a.first.get();
  }
};

struct KeyEq : public KeyComp {
  template <class A, class B>
  bool operator()(const A& a, const B& b) const {
    return BN_cmp(AsBN(a), AsBN(b)) == 0;
  }
};

struct KeyLess : public KeyComp {
  template <class A, class B>
  bool operator()(const A& a, const B& b) const {
    return BN_cmp(AsBN(a), AsBN(b)) < 0;
  }
};

typedef vector<pair<ScopedBIGNUM, string>> ValueList;

pair<ScopedBIGNUM, string> Value(uint64_t n, const string& v) {
  pair<ScopedBIGNUM, string> ret;
  ret.second = v;
  ret.first.reset(BN_new());
  BN_set_word(ret.first.get(), n);
  return ret;
}

// Implements (more-or-less) the reference python code given in the
// revocation transparency paper for calculating the root-hash of a sparse
// tree with a given set of leaf nodes.
class Reference {
 public:
  Reference(SerialHasher* hasher)
      : tree_hasher_(unique_ptr<SerialHasher>(CHECK_NOTNULL(hasher))),
        hStarEmptyCache_{tree_hasher_.HashLeaf("")} {
  }

  // Calculates the root hash of a sparse merkle tree of depth |n|, containing
  // the leaf values in |values|.
  string HStar2(size_t n, ValueList* values) {
    // values should be sorted
    std::sort(values->begin(), values->end(), KeyLess());
    // and without dupes
    values->erase(std::unique(values->begin(), values->end(), KeyEq()),
                  values->end());
    // Sounds a lot like a map to me, but I've left it as a list because:
    // a) it's just reference code, and
    // b) I want it to be as similar as possible to the code in the paper.

    ScopedBIGNUM offset(BN_new());
    BN_zero(offset.get());
    const string ret(
        HStar2b(n, *values, values->begin(), values->end(), offset.get()));
    return ret;
  }

 private:
  // Calculates & caches the 'null' node at depth |n|
  string HStarEmpty(size_t n) {
    if (hStarEmptyCache_.size() <= n) {
      const string t(
          tree_hasher_.HashChildren(HStarEmpty(n - 1), HStarEmpty(n - 1)));
      CHECK_EQ(n, hStarEmptyCache_.size());
      hStarEmptyCache_.push_back(t);
    }
    CHECK_LT(n, hStarEmptyCache_.size());
    return hStarEmptyCache_[n];
  }

  // Calculates an internal subtree.
  string HStar2b(size_t n, const ValueList& values,
                 ValueList::const_iterator lo, ValueList::const_iterator hi,
                 BIGNUM* offset) {
    if (n == 0) {
      if (lo == hi) {
        // DIFF: return the null leaf hash, rather than "0" as in the paper.
        return hStarEmptyCache_[0];
      }
      CHECK_EQ(1, hi - lo);
      // DIFF: return H(\x00||value) rather than "1" as in the paper.
      return tree_hasher_.HashLeaf(lo->second);
    }
    if (lo == hi) {
      return HStarEmpty(n);
    }

    // DIFF: use BIGNUM, 'cos we'll get to values of O(1 << 256) here (!)
    ScopedBIGNUM split(BN_new());
    CHECK_EQ(1, BN_set_word(split.get(), 1));
    CHECK_EQ(1, BN_lshift(split.get(), split.get(), n - 1));
    CHECK_EQ(1, BN_add(split.get(), split.get(), offset));

    auto i(lower_bound(lo, hi, split, KeyLess()));
    const string ret(
        tree_hasher_.HashChildren(HStar2b(n - 1, values, lo, i, offset),
                                  HStar2b(n - 1, values, i, hi, split.get())));
    return ret;
  }

  TreeHasher tree_hasher_;
  vector<string> hStarEmptyCache_;
};

class SparseMerkleTreeTest : public testing::Test {
 public:
  SparseMerkleTreeTest()
      : tree_hasher_(unique_ptr<Sha256Hasher>(new Sha256Hasher)),
        tree_(new Sha256Hasher),
        rand_({1234}) {
  }

 protected:
  // Returns a Path with the high 64 bits set to |high|
  SparseMerkleTree::Path PathHigh(uint64_t high) {
    SparseMerkleTree::Path ret;
    ret.fill(0);
    for (size_t i(0); i < 8; ++i) {
      ret[7 - i] = high & 0xff;
      high >>= 8;
    }
    return ret;
  }

  // Returns a Path with the low 64 bits set to |high|
  SparseMerkleTree::Path PathLow(uint64_t low) {
    SparseMerkleTree::Path ret;
    ret.fill(0);
    for (size_t i(0); i < 8; ++i) {
      ret[ret.size() - 1 - i] = low & 0xff;
      low >>= 8;
    }
    return ret;
  }

  // Returns a random Path.
  SparseMerkleTree::Path RandomPath() {
    SparseMerkleTree::Path ret;
    for (SparseMerkleTree::Path::size_type i(0); i < ret.size(); ++i) {
      ret[i] = rand_() & 0xff;
    }
    return ret;
  }

  SparseMerkleTree::Path PathFromString(const string& s) {
    SparseMerkleTree::Path ret;
    CHECK_LE(s.size(), ret.size());
    fill(copy(s.begin(), s.end(), ret.begin()), ret.end(), 0);
    return ret;
  }

  TreeHasher tree_hasher_;
  SparseMerkleTree tree_;
  mt19937 rand_;
};

TEST_F(SparseMerkleTreeTest, PathBitAndPathStreamOperatorAgree) {
  ostringstream os;
  const SparseMerkleTree::Path p(RandomPath());
  os << p;
  string b;
  for (size_t i(0); i < SparseMerkleTree::kDigestSizeBits; ++i) {
    b += PathBit(p, i) == 0 ? '0' : '1';
  }
  EXPECT_EQ(os.str(), b);
}

TEST_F(SparseMerkleTreeTest, ReferenceEmptyTreeRootKAT) {
  Reference ref(new Sha256Hasher);
  ValueList empty_values;
  EXPECT_EQ(kEmptyRootHashB64, ToBase64(ref.HStar2(256, &empty_values)));
}

TEST_F(SparseMerkleTreeTest, EmptyTreeRootKAT) {
  tree_.CurrentRoot();
  EXPECT_EQ(kEmptyRootHashB64, ToBase64(tree_.CurrentRoot()));
}

TEST_F(SparseMerkleTreeTest, SimpleTest) {
  Reference ref(new Sha256Hasher);
  ValueList values;
  for (auto r : vector<uint64_t>{1, 5, 10}) {
    const string value(to_string(r));
    values.emplace_back(std::move(Value(r, value)));
    SparseMerkleTree::Path p(PathLow(r));
    tree_.SetLeaf(p, value);
  }
  const string ref_root(
      ref.HStar2(SparseMerkleTree::kDigestSizeBits, &values));
  const string smt_root(tree_.CurrentRoot());
  EXPECT_EQ(ToBase64(ref_root), ToBase64(smt_root));
}

TEST_F(SparseMerkleTreeTest, RandomReferenceTest) {
  Reference ref(new Sha256Hasher);
  ValueList values;
  LOG(INFO) << "Setup";
  for (int i(0); i < 10000; ++i) {
    uint64_t r(rand_() + i);
    const string value(to_string(r));
    values.emplace_back(std::move(Value(r, value)));
    const SparseMerkleTree::Path p(PathLow(r));
    tree_.SetLeaf(p, value);
  }
  LOG(INFO) << "Calculating SMT Root";
  const string smt_root(tree_.CurrentRoot());
  LOG(INFO) << "Calculating Reference Root";
  const string ref_root(ref.HStar2(256, &values));
  LOG(INFO) << "Comparing";
  EXPECT_EQ(ToBase64(ref_root), ToBase64(smt_root));
}

TEST_F(SparseMerkleTreeTest, DISABLED_RefMemTest) {
  Reference ref(new Sha256Hasher);
  ValueList values;

  struct rusage ru;
  getrusage(RUSAGE_SELF, &ru);
  long max_rss_before = ru.ru_maxrss;
  uint64_t time_before = util::TimeInMilliseconds();
  LOG(INFO) << "Setup";

  for (int i(0); i < 10000000; ++i) {
    uint64_t r(rand_() + i);
    const string value(to_string(r));
    values.emplace_back(std::move(Value(r, value)));
  }
  LOG(INFO) << "Calculating Root";
  const string ref_root(ref.HStar2(256, &values));
  LOG(INFO) << "Done";

  uint64_t time_after = util::TimeInMilliseconds();
  getrusage(RUSAGE_SELF, &ru);
  LOG(INFO) << "Peak RSS delta (as reported by getrusage()) was "
            << ru.ru_maxrss - max_rss_before << " kB";
  LOG(INFO) << "Elapsed time: " << time_after - time_before << " ms";
}

TEST_F(SparseMerkleTreeTest, DISABLED_SMTMemTest) {
  struct rusage ru;
  getrusage(RUSAGE_SELF, &ru);
  long max_rss_before = ru.ru_maxrss;
  uint64_t time_before = util::TimeInMilliseconds();
  LOG(INFO) << "Setup";

  for (int i(0); i < 10000000; ++i) {
    uint64_t r(rand_() + i);
    const string value(to_string(r));
    const SparseMerkleTree::Path p(PathLow(r));
    tree_.SetLeaf(p, value);
  }
  LOG(INFO) << "Calculating Root";
  const string smt_root(tree_.CurrentRoot());
  LOG(INFO) << "Done";

  uint64_t time_after = util::TimeInMilliseconds();
  getrusage(RUSAGE_SELF, &ru);
  LOG(INFO) << "Peak RSS delta (as reported by getrusage()) was "
            << ru.ru_maxrss - max_rss_before << " kB";
  LOG(INFO) << "Elapsed time: " << time_after - time_before << " ms";
}

TEST_F(SparseMerkleTreeTest, TestSetLeaf) {
  tree_.CurrentRoot();
  LOG(INFO) << "Tree@0:";
  LOG(INFO) << tree_.Dump();

  tree_.SetLeaf(PathFromString("one"), "one");
  tree_.CurrentRoot();
  LOG(INFO) << "Tree@1:";
  LOG(INFO) << tree_.Dump();

  tree_.SetLeaf(PathFromString("two"), "two");
  tree_.CurrentRoot();
  LOG(INFO) << "Tree@2:";
  LOG(INFO) << tree_.Dump();

  tree_.SetLeaf(PathFromString("three"), "three");
  tree_.CurrentRoot();
  LOG(INFO) << "Tree@3:";
  LOG(INFO) << tree_.Dump();
}

// TODO(alcutter): Lots and lots more tests.

}  // namespace

int main(int argc, char** argv) {
  cert_trans::test::InitTesting(argv[0], &argc, &argv, true);
  return RUN_ALL_TESTS();
}
<commit_msg>Fix clang warnings from recent clang<commit_after>#include <glog/logging.h>
#include <gtest/gtest.h>
#include <openssl/bn.h>
#include <sys/resource.h>
#include <algorithm>
#include <map>
#include <random>
#include <string>

#include "merkletree/sparse_merkle_tree.h"
#include "util/openssl_scoped_types.h"
#include "util/testing.h"
#include "util/util.h"

namespace {

using cert_trans::ScopedBIGNUM;
using std::fill;
using std::lower_bound;
using std::map;
using std::mt19937;
using std::ostringstream;
using std::pair;
using std::random_device;
using std::reverse;
using std::string;
using std::to_string;
using std::unique_ptr;
using std::vector;
using util::ToBase64;

const char kEmptyRootHashB64[] =
    "xmifEIEqCYCXbZUz2Dh1KCFmFZVn7DUVVxbBQTr1PWo=";

struct KeyComp {
  const BIGNUM* AsBN(const ScopedBIGNUM& a) const {
    return a.get();
  }

  const BIGNUM* AsBN(const BIGNUM* a) const {
    return a;
  }

  const BIGNUM* AsBN(const pair<ScopedBIGNUM, string>& a) const {
    return a.first.get();
  }
};

struct KeyEq : public KeyComp {
  template <class A, class B>
  bool operator()(const A& a, const B& b) const {
    return BN_cmp(AsBN(a), AsBN(b)) == 0;
  }
};

struct KeyLess : public KeyComp {
  template <class A, class B>
  bool operator()(const A& a, const B& b) const {
    return BN_cmp(AsBN(a), AsBN(b)) < 0;
  }
};

typedef vector<pair<ScopedBIGNUM, string>> ValueList;

pair<ScopedBIGNUM, string> Value(uint64_t n, const string& v) {
  pair<ScopedBIGNUM, string> ret;
  ret.second = v;
  ret.first.reset(BN_new());
  BN_set_word(ret.first.get(), n);
  return ret;
}

// Implements (more-or-less) the reference python code given in the
// revocation transparency paper for calculating the root-hash of a sparse
// tree with a given set of leaf nodes.
class Reference {
 public:
  Reference(SerialHasher* hasher)
      : tree_hasher_(unique_ptr<SerialHasher>(CHECK_NOTNULL(hasher))),
        hStarEmptyCache_{tree_hasher_.HashLeaf("")} {
  }

  // Calculates the root hash of a sparse merkle tree of depth |n|, containing
  // the leaf values in |values|.
  string HStar2(size_t n, ValueList* values) {
    // values should be sorted
    std::sort(values->begin(), values->end(), KeyLess());
    // and without dupes
    values->erase(std::unique(values->begin(), values->end(), KeyEq()),
                  values->end());
    // Sounds a lot like a map to me, but I've left it as a list because:
    // a) it's just reference code, and
    // b) I want it to be as similar as possible to the code in the paper.

    ScopedBIGNUM offset(BN_new());
    BN_zero(offset.get());
    const string ret(
        HStar2b(n, *values, values->begin(), values->end(), offset.get()));
    return ret;
  }

 private:
  // Calculates & caches the 'null' node at depth |n|
  string HStarEmpty(size_t n) {
    if (hStarEmptyCache_.size() <= n) {
      const string t(
          tree_hasher_.HashChildren(HStarEmpty(n - 1), HStarEmpty(n - 1)));
      CHECK_EQ(n, hStarEmptyCache_.size());
      hStarEmptyCache_.push_back(t);
    }
    CHECK_LT(n, hStarEmptyCache_.size());
    return hStarEmptyCache_[n];
  }

  // Calculates an internal subtree.
  string HStar2b(size_t n, const ValueList& values,
                 ValueList::const_iterator lo, ValueList::const_iterator hi,
                 BIGNUM* offset) {
    if (n == 0) {
      if (lo == hi) {
        // DIFF: return the null leaf hash, rather than "0" as in the paper.
        return hStarEmptyCache_[0];
      }
      CHECK_EQ(1, hi - lo);
      // DIFF: return H(\x00||value) rather than "1" as in the paper.
      return tree_hasher_.HashLeaf(lo->second);
    }
    if (lo == hi) {
      return HStarEmpty(n);
    }

    // DIFF: use BIGNUM, 'cos we'll get to values of O(1 << 256) here (!)
    ScopedBIGNUM split(BN_new());
    CHECK_EQ(1, BN_set_word(split.get(), 1));
    CHECK_EQ(1, BN_lshift(split.get(), split.get(), n - 1));
    CHECK_EQ(1, BN_add(split.get(), split.get(), offset));

    auto i(lower_bound(lo, hi, split, KeyLess()));
    const string ret(
        tree_hasher_.HashChildren(HStar2b(n - 1, values, lo, i, offset),
                                  HStar2b(n - 1, values, i, hi, split.get())));
    return ret;
  }

  TreeHasher tree_hasher_;
  vector<string> hStarEmptyCache_;
};

class SparseMerkleTreeTest : public testing::Test {
 public:
  SparseMerkleTreeTest()
      : tree_hasher_(unique_ptr<Sha256Hasher>(new Sha256Hasher)),
        tree_(new Sha256Hasher),
        rand_(1234) {
  }

 protected:
  // Returns a Path with the high 64 bits set to |high|
  SparseMerkleTree::Path PathHigh(uint64_t high) {
    SparseMerkleTree::Path ret;
    ret.fill(0);
    for (size_t i(0); i < 8; ++i) {
      ret[7 - i] = high & 0xff;
      high >>= 8;
    }
    return ret;
  }

  // Returns a Path with the low 64 bits set to |high|
  SparseMerkleTree::Path PathLow(uint64_t low) {
    SparseMerkleTree::Path ret;
    ret.fill(0);
    for (size_t i(0); i < 8; ++i) {
      ret[ret.size() - 1 - i] = low & 0xff;
      low >>= 8;
    }
    return ret;
  }

  // Returns a random Path.
  SparseMerkleTree::Path RandomPath() {
    SparseMerkleTree::Path ret;
    for (SparseMerkleTree::Path::size_type i(0); i < ret.size(); ++i) {
      ret[i] = rand_() & 0xff;
    }
    return ret;
  }

  SparseMerkleTree::Path PathFromString(const string& s) {
    SparseMerkleTree::Path ret;
    CHECK_LE(s.size(), ret.size());
    fill(copy(s.begin(), s.end(), ret.begin()), ret.end(), 0);
    return ret;
  }

  TreeHasher tree_hasher_;
  SparseMerkleTree tree_;
  mt19937 rand_;
};

TEST_F(SparseMerkleTreeTest, PathBitAndPathStreamOperatorAgree) {
  ostringstream os;
  const SparseMerkleTree::Path p(RandomPath());
  os << p;
  string b;
  for (size_t i(0); i < SparseMerkleTree::kDigestSizeBits; ++i) {
    b += PathBit(p, i) == 0 ? '0' : '1';
  }
  EXPECT_EQ(os.str(), b);
}

TEST_F(SparseMerkleTreeTest, ReferenceEmptyTreeRootKAT) {
  Reference ref(new Sha256Hasher);
  ValueList empty_values;
  EXPECT_EQ(kEmptyRootHashB64, ToBase64(ref.HStar2(256, &empty_values)));
}

TEST_F(SparseMerkleTreeTest, EmptyTreeRootKAT) {
  tree_.CurrentRoot();
  EXPECT_EQ(kEmptyRootHashB64, ToBase64(tree_.CurrentRoot()));
}

TEST_F(SparseMerkleTreeTest, SimpleTest) {
  Reference ref(new Sha256Hasher);
  ValueList values;
  for (auto r : vector<uint64_t>{1, 5, 10}) {
    const string value(to_string(r));
    values.emplace_back(Value(r, value));
    SparseMerkleTree::Path p(PathLow(r));
    tree_.SetLeaf(p, value);
  }
  const string ref_root(
      ref.HStar2(SparseMerkleTree::kDigestSizeBits, &values));
  const string smt_root(tree_.CurrentRoot());
  EXPECT_EQ(ToBase64(ref_root), ToBase64(smt_root));
}

TEST_F(SparseMerkleTreeTest, RandomReferenceTest) {
  Reference ref(new Sha256Hasher);
  ValueList values;
  LOG(INFO) << "Setup";
  for (int i(0); i < 10000; ++i) {
    uint64_t r(rand_() + i);
    const string value(to_string(r));
    values.emplace_back(Value(r, value));
    const SparseMerkleTree::Path p(PathLow(r));
    tree_.SetLeaf(p, value);
  }
  LOG(INFO) << "Calculating SMT Root";
  const string smt_root(tree_.CurrentRoot());
  LOG(INFO) << "Calculating Reference Root";
  const string ref_root(ref.HStar2(256, &values));
  LOG(INFO) << "Comparing";
  EXPECT_EQ(ToBase64(ref_root), ToBase64(smt_root));
}

TEST_F(SparseMerkleTreeTest, DISABLED_RefMemTest) {
  Reference ref(new Sha256Hasher);
  ValueList values;

  struct rusage ru;
  getrusage(RUSAGE_SELF, &ru);
  long max_rss_before = ru.ru_maxrss;
  uint64_t time_before = util::TimeInMilliseconds();
  LOG(INFO) << "Setup";

  for (int i(0); i < 10000000; ++i) {
    uint64_t r(rand_() + i);
    const string value(to_string(r));
    values.emplace_back(Value(r, value));
  }
  LOG(INFO) << "Calculating Root";
  const string ref_root(ref.HStar2(256, &values));
  LOG(INFO) << "Done";

  uint64_t time_after = util::TimeInMilliseconds();
  getrusage(RUSAGE_SELF, &ru);
  LOG(INFO) << "Peak RSS delta (as reported by getrusage()) was "
            << ru.ru_maxrss - max_rss_before << " kB";
  LOG(INFO) << "Elapsed time: " << time_after - time_before << " ms";
}

TEST_F(SparseMerkleTreeTest, DISABLED_SMTMemTest) {
  struct rusage ru;
  getrusage(RUSAGE_SELF, &ru);
  long max_rss_before = ru.ru_maxrss;
  uint64_t time_before = util::TimeInMilliseconds();
  LOG(INFO) << "Setup";

  for (int i(0); i < 10000000; ++i) {
    uint64_t r(rand_() + i);
    const string value(to_string(r));
    const SparseMerkleTree::Path p(PathLow(r));
    tree_.SetLeaf(p, value);
  }
  LOG(INFO) << "Calculating Root";
  const string smt_root(tree_.CurrentRoot());
  LOG(INFO) << "Done";

  uint64_t time_after = util::TimeInMilliseconds();
  getrusage(RUSAGE_SELF, &ru);
  LOG(INFO) << "Peak RSS delta (as reported by getrusage()) was "
            << ru.ru_maxrss - max_rss_before << " kB";
  LOG(INFO) << "Elapsed time: " << time_after - time_before << " ms";
}

TEST_F(SparseMerkleTreeTest, TestSetLeaf) {
  tree_.CurrentRoot();
  LOG(INFO) << "Tree@0:";
  LOG(INFO) << tree_.Dump();

  tree_.SetLeaf(PathFromString("one"), "one");
  tree_.CurrentRoot();
  LOG(INFO) << "Tree@1:";
  LOG(INFO) << tree_.Dump();

  tree_.SetLeaf(PathFromString("two"), "two");
  tree_.CurrentRoot();
  LOG(INFO) << "Tree@2:";
  LOG(INFO) << tree_.Dump();

  tree_.SetLeaf(PathFromString("three"), "three");
  tree_.CurrentRoot();
  LOG(INFO) << "Tree@3:";
  LOG(INFO) << tree_.Dump();
}

// TODO(alcutter): Lots and lots more tests.

}  // namespace

int main(int argc, char** argv) {
  cert_trans::test::InitTesting(argv[0], &argc, &argv, true);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// @brief Aql, query parser
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 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 Jan Steemann
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "Aql/Parser.h"
#include "Aql/AstNode.h"
#include "Aql/QueryResult.h"

using namespace triagens::aql;

// -----------------------------------------------------------------------------
// --SECTION--                                        constructors / destructors
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief create the parser
////////////////////////////////////////////////////////////////////////////////

Parser::Parser (Query* query)
  : _query(query),
    _ast(nullptr),
    _scanner(nullptr),
    _buffer(query->queryString()),
    _remainingLength(query->queryLength()),
    _offset(0),
    _marker(nullptr),
    _stack() {

  _ast = new Ast(query, this);

  _stack.reserve(16);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief destroy the parser
////////////////////////////////////////////////////////////////////////////////

Parser::~Parser () {
  if (_ast != nullptr) {
    delete _ast;
  }
}

// -----------------------------------------------------------------------------
// --SECTION--                                                  public functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief set data for write queries
////////////////////////////////////////////////////////////////////////////////

bool Parser::configureWriteQuery (QueryType type,
                                  AstNode const* collectionNode,
                                  AstNode* optionNode) {
  TRI_ASSERT(type != AQL_QUERY_READ);

  // check if we currently are in a subquery
  if (_ast->isInSubQuery()) {
    // data modification not allowed in sub-queries
    _query->registerError(TRI_ERROR_QUERY_MODIFY_IN_SUBQUERY);
    return false;
  }

  // check current query type
  auto oldType = _query->type();

  if (oldType != AQL_QUERY_READ) {
    // already a data-modification query, cannot have two data-modification operations in one query
    _query->registerError(TRI_ERROR_QUERY_MULTI_MODIFY);
    return false;
  }

  // now track which collection is going to be modified
  _ast->setWriteCollection(collectionNode);

  if (optionNode != nullptr && ! optionNode->isConstant()) {
    _query->registerError(TRI_ERROR_QUERY_COMPILE_TIME_OPTIONS);
  }

  // register type
  _query->type(type);
  return true;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief parse the query
////////////////////////////////////////////////////////////////////////////////

QueryResult Parser::parse () {
  // start main scope
  auto scopes = _ast->scopes();
  scopes->start(AQL_SCOPE_MAIN);

  Aqllex_init(&_scanner);
  Aqlset_extra(this, _scanner);

  try {
    // parse the query string
    if (Aqlparse(this)) {
      // lexing/parsing failed
      THROW_ARANGO_EXCEPTION(TRI_ERROR_QUERY_PARSE);
    }

    Aqllex_destroy(_scanner);
  }
  catch (...) {
    Aqllex_destroy(_scanner);
    throw;
  }

  // end main scope
  scopes->endCurrent();

  TRI_ASSERT(scopes->numActive() == 0);

  QueryResult result;
  result.collectionNames = _query->collectionNames();
  result.bindParameters  = _ast->bindParameters();
  result.json            = _ast->toJson(TRI_UNKNOWN_MEM_ZONE, false);

  return result;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief register a parse error, position is specified as line / column
////////////////////////////////////////////////////////////////////////////////

void Parser::registerParseError (int errorCode,
                                 char const* format,
                                 char const* data,
                                 int line,
                                 int column) {
  char buffer[512];
  snprintf(buffer,
           sizeof(buffer),
           format,
           data);

  return registerParseError(errorCode, buffer, line, column);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief register a parse error, position is specified as line / column
////////////////////////////////////////////////////////////////////////////////

void Parser::registerParseError (int errorCode,
                                 char const* data,
                                 int line,
                                 int column) {
  TRI_ASSERT(errorCode != TRI_ERROR_NO_ERROR);
  TRI_ASSERT(data != nullptr);

  // extract the query string part where the error happened
  std::string const region(_query->extractRegion(line, column));

  // note: line numbers reported by bison/flex start at 1, columns start at 0
  char buffer[512];
  snprintf(buffer,
           sizeof(buffer),
           "%s near '%s' at position %d:%d",
           data,
           region.c_str(),
           line,
           column + 1);

  registerError(errorCode, buffer);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief register a non-parse error
////////////////////////////////////////////////////////////////////////////////

void Parser::registerError (int errorCode,
                            char const* data) {
  _query->registerError(errorCode, data);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief push an AstNode into the list element on top of the stack
////////////////////////////////////////////////////////////////////////////////

void Parser::pushList (AstNode* node) {
  auto list = static_cast<AstNode*>(peekStack());
  TRI_ASSERT(list->type == NODE_TYPE_LIST);
  list->addMember(node);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief push an AstNode into the array element on top of the stack
////////////////////////////////////////////////////////////////////////////////

void Parser::pushArray (char const* attributeName,
                        AstNode* node) {
  auto array = static_cast<AstNode*>(peekStack());
  TRI_ASSERT(array->type == NODE_TYPE_ARRAY);
  auto element = _ast->createNodeArrayElement(attributeName, node);
  array->addMember(element);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief push a temporary value on the parser's stack
////////////////////////////////////////////////////////////////////////////////

void Parser::pushStack (void* value) {
  _stack.push_back(value);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief pop a temporary value from the parser's stack
////////////////////////////////////////////////////////////////////////////////

void* Parser::popStack () {
  TRI_ASSERT(! _stack.empty());

  void* result = _stack.back();
  _stack.pop_back();
  return result;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief peek at a temporary value from the parser's stack
////////////////////////////////////////////////////////////////////////////////

void* Parser::peekStack () {
  TRI_ASSERT(! _stack.empty());

  return _stack.back();
}

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:
<commit_msg>Parser: when throwing parse exceptions add the query and a pointer to the actual indicated error to the message.<commit_after>////////////////////////////////////////////////////////////////////////////////
/// @brief Aql, query parser
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 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 Jan Steemann
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "Aql/Parser.h"
#include "Aql/AstNode.h"
#include "Aql/QueryResult.h"

using namespace triagens::aql;

// -----------------------------------------------------------------------------
// --SECTION--                                        constructors / destructors
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief create the parser
////////////////////////////////////////////////////////////////////////////////

Parser::Parser (Query* query)
  : _query(query),
    _ast(nullptr),
    _scanner(nullptr),
    _buffer(query->queryString()),
    _remainingLength(query->queryLength()),
    _offset(0),
    _marker(nullptr),
    _stack() {

  _ast = new Ast(query, this);

  _stack.reserve(16);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief destroy the parser
////////////////////////////////////////////////////////////////////////////////

Parser::~Parser () {
  if (_ast != nullptr) {
    delete _ast;
  }
}

// -----------------------------------------------------------------------------
// --SECTION--                                                  public functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief set data for write queries
////////////////////////////////////////////////////////////////////////////////

bool Parser::configureWriteQuery (QueryType type,
                                  AstNode const* collectionNode,
                                  AstNode* optionNode) {
  TRI_ASSERT(type != AQL_QUERY_READ);

  // check if we currently are in a subquery
  if (_ast->isInSubQuery()) {
    // data modification not allowed in sub-queries
    _query->registerError(TRI_ERROR_QUERY_MODIFY_IN_SUBQUERY);
    return false;
  }

  // check current query type
  auto oldType = _query->type();

  if (oldType != AQL_QUERY_READ) {
    // already a data-modification query, cannot have two data-modification operations in one query
    _query->registerError(TRI_ERROR_QUERY_MULTI_MODIFY);
    return false;
  }

  // now track which collection is going to be modified
  _ast->setWriteCollection(collectionNode);

  if (optionNode != nullptr && ! optionNode->isConstant()) {
    _query->registerError(TRI_ERROR_QUERY_COMPILE_TIME_OPTIONS);
  }

  // register type
  _query->type(type);
  return true;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief parse the query
////////////////////////////////////////////////////////////////////////////////

QueryResult Parser::parse () {
  // start main scope
  auto scopes = _ast->scopes();
  scopes->start(AQL_SCOPE_MAIN);

  Aqllex_init(&_scanner);
  Aqlset_extra(this, _scanner);

  try {
    // parse the query string
    if (Aqlparse(this)) {
      // lexing/parsing failed
      THROW_ARANGO_EXCEPTION(TRI_ERROR_QUERY_PARSE);
    }

    Aqllex_destroy(_scanner);
  }
  catch (...) {
    Aqllex_destroy(_scanner);
    throw;
  }

  // end main scope
  scopes->endCurrent();

  TRI_ASSERT(scopes->numActive() == 0);

  QueryResult result;
  result.collectionNames = _query->collectionNames();
  result.bindParameters  = _ast->bindParameters();
  result.json            = _ast->toJson(TRI_UNKNOWN_MEM_ZONE, false);

  return result;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief register a parse error, position is specified as line / column
////////////////////////////////////////////////////////////////////////////////

void Parser::registerParseError (int errorCode,
                                 char const* format,
                                 char const* data,
                                 int line,
                                 int column) {
  char buffer[512];
  snprintf(buffer,
           sizeof(buffer),
           format,
           data);

  return registerParseError(errorCode, buffer, line, column);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief register a parse error, position is specified as line / column
////////////////////////////////////////////////////////////////////////////////

void Parser::registerParseError (int errorCode,
                                 char const* data,
                                 int line,
                                 int column) {
  TRI_ASSERT(errorCode != TRI_ERROR_NO_ERROR);
  TRI_ASSERT(data != nullptr);

  // extract the query string part where the error happened
  std::string const region(_query->extractRegion(line, column));

  // create a neat pointer to the location of the error.
  auto arrowpointer = (char*) malloc (sizeof(char) * (column + 10) );
  size_t i;
  for (i = 0; i < (size_t) column; i++) {
    arrowpointer[i] = ' ';
  }
  if (i > 0) {
	i --;
	arrowpointer[i++] = '^';
  }

  arrowpointer[i++] = '^';
  arrowpointer[i++] = '^';
  arrowpointer[i++] = '\0';

  // note: line numbers reported by bison/flex start at 1, columns start at 0
  char buffer[512];
  snprintf(buffer,
           sizeof(buffer),
           "%s near '%s' at position %d:%d:\n%s\n%s\n",
           data,
           region.c_str(),
           line,
           column + 1,
           _query->queryString(),
           arrowpointer);

  free(arrowpointer);

  registerError(errorCode, buffer);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief register a non-parse error
////////////////////////////////////////////////////////////////////////////////

void Parser::registerError (int errorCode,
                            char const* data) {
  _query->registerError(errorCode, data);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief push an AstNode into the list element on top of the stack
////////////////////////////////////////////////////////////////////////////////

void Parser::pushList (AstNode* node) {
  auto list = static_cast<AstNode*>(peekStack());
  TRI_ASSERT(list->type == NODE_TYPE_LIST);
  list->addMember(node);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief push an AstNode into the array element on top of the stack
////////////////////////////////////////////////////////////////////////////////

void Parser::pushArray (char const* attributeName,
                        AstNode* node) {
  auto array = static_cast<AstNode*>(peekStack());
  TRI_ASSERT(array->type == NODE_TYPE_ARRAY);
  auto element = _ast->createNodeArrayElement(attributeName, node);
  array->addMember(element);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief push a temporary value on the parser's stack
////////////////////////////////////////////////////////////////////////////////

void Parser::pushStack (void* value) {
  _stack.push_back(value);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief pop a temporary value from the parser's stack
////////////////////////////////////////////////////////////////////////////////

void* Parser::popStack () {
  TRI_ASSERT(! _stack.empty());

  void* result = _stack.back();
  _stack.pop_back();
  return result;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief peek at a temporary value from the parser's stack
////////////////////////////////////////////////////////////////////////////////

void* Parser::peekStack () {
  TRI_ASSERT(! _stack.empty());

  return _stack.back();
}

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:
<|endoftext|>


<commit_before>#ifndef ARBITER_IS_AMALGAMATION
#include <arbiter/arbiter.hpp>
#include <arbiter/drivers/fs.hpp>
#include <arbiter/util/util.hpp>
#endif

#ifndef ARBITER_WINDOWS
#include <glob.h>
#include <sys/stat.h>
#else

#include <locale>
#include <codecvt>
#include <windows.h>
#include <direct.h>
#endif

#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <ios>
#include <istream>

#ifdef ARBITER_CUSTOM_NAMESPACE
namespace ARBITER_CUSTOM_NAMESPACE
{
#endif

namespace arbiter
{

namespace
{
    // Binary output, overwriting any existing file with a conflicting name.
    const std::ios_base::openmode binaryTruncMode(
            std::ofstream::binary |
            std::ofstream::out |
            std::ofstream::trunc);

    std::string getHome()
    {
        std::string s;

#ifndef ARBITER_WINDOWS
        if (auto home = util::env("HOME")) s = *home;
#else
        if (auto userProfile = util::env("USERPROFILE"))
        {
            s = *userProfile;
        }
        else
        {
            auto homeDrive(util::env("HOMEDRIVE"));
            auto homePath(util::env("HOMEPATH"));

            if (homeDrive && homePath) s = *homeDrive + *homePath;
        }
#endif
        if (s.empty()) std::cout << "No home directory found" << std::endl;

        return s;
    }
}

namespace drivers
{

std::unique_ptr<Fs> Fs::create(const Json::Value&)
{
    return std::unique_ptr<Fs>(new Fs());
}

std::unique_ptr<std::size_t> Fs::tryGetSize(std::string path) const
{
    std::unique_ptr<std::size_t> size;

    path = fs::expandTilde(path);

    std::ifstream stream(path, std::ios::in | std::ios::binary);

    if (stream.good())
    {
        stream.seekg(0, std::ios::end);
        size.reset(new std::size_t(stream.tellg()));
    }

    return size;
}

bool Fs::get(std::string path, std::vector<char>& data) const
{
    bool good(false);

    path = fs::expandTilde(path);
    std::ifstream stream(path, std::ios::in | std::ios::binary);

    if (stream.good())
    {
        stream.seekg(0, std::ios::end);
        data.resize(static_cast<std::size_t>(stream.tellg()));
        stream.seekg(0, std::ios::beg);
        stream.read(data.data(), data.size());
        stream.close();
        good = true;
    }

    return good;
}

void Fs::put(std::string path, const std::vector<char>& data) const
{
    path = fs::expandTilde(path);
    std::ofstream stream(path, binaryTruncMode);

    if (!stream.good())
    {
        throw ArbiterError("Could not open " + path + " for writing");
    }

    stream.write(data.data(), data.size());

    if (!stream.good())
    {
        throw ArbiterError("Error occurred while writing " + path);
    }
}

void Fs::copy(std::string src, std::string dst) const
{
    src = fs::expandTilde(src);
    dst = fs::expandTilde(dst);

    std::ifstream instream(src, std::ifstream::in | std::ifstream::binary);
    if (!instream.good())
    {
        throw ArbiterError("Could not open " + src + " for reading");
    }
    instream >> std::noskipws;

    std::ofstream outstream(dst, binaryTruncMode);
    if (!outstream.good())
    {
        throw ArbiterError("Could not open " + dst + " for writing");
    }

    outstream << instream.rdbuf();
}

std::vector<std::string> Fs::glob(std::string path, bool verbose) const
{
    return fs::glob(path);
}

} // namespace drivers

namespace fs
{

bool mkdirp(std::string raw)
{
    const std::string dir(([&raw]()
    {
        std::string s(expandTilde(raw));

        // Remove consecutive slashes.  For Windows, we'll need to be careful
        // not to remove drive letters like C:\\.
        const auto end = std::unique(s.begin(), s.end(), [](char l, char r)
        {
            return util::isSlash(l) && util::isSlash(r);
        });

        s = std::string(s.begin(), end);
        if (s.size() && util::isSlash(s.back())) s.pop_back();
        return s;
    })());

    auto it(dir.begin());
    const auto end(dir.cend());

    do
    {
        it = std::find_if(++it, end, util::isSlash);

        const std::string cur(dir.begin(), it);
#ifndef ARBITER_WINDOWS
        const bool err(::mkdir(cur.c_str(), S_IRWXU | S_IRGRP | S_IROTH));
#else
        const bool err(::_mkdir(cur.c_str()));
#endif
        if (err && errno != EEXIST) return false;
    }
    while (it != end);

    return true;

}

bool remove(std::string filename)
{
    filename = expandTilde(filename);

    return ::remove(filename.c_str()) == 0;
}

namespace
{
    struct Globs
    {
        std::vector<std::string> files;
        std::vector<std::string> dirs;
    };

    Globs globOne(std::string path)
    {
        Globs results;

#ifndef ARBITER_WINDOWS
        glob_t buffer;
        struct stat info;

        ::glob(path.c_str(), GLOB_NOSORT | GLOB_MARK, 0, &buffer);

        for (std::size_t i(0); i < buffer.gl_pathc; ++i)
        {
            const std::string val(buffer.gl_pathv[i]);

            if (stat(val.c_str(), &info) == 0)
            {
                if (S_ISREG(info.st_mode)) results.files.push_back(val);
                else if (S_ISDIR(info.st_mode)) results.dirs.push_back(val);
            }
            else
            {
                throw ArbiterError("Error globbing - POSIX stat failed");
            }
        }

        globfree(&buffer);
#else
        std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
        const std::wstring wide(converter.from_bytes(path));

        LPWIN32_FIND_DATAW data{};
        HANDLE hFind(FindFirstFileW(wide.c_str(), data));

        if (hFind != INVALID_HANDLE_VALUE)
        {
            do
            {
                if ((data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
                {
                    results.files.push_back(
                            converter.to_bytes(data->cFileName));
                }
                else
                {
                    results.dirs.push_back(converter.to_bytes(data->cFileName));
                }
            }
            while (FindNextFileW(hFind, data));
        }
#endif

        return results;
    }

    std::vector<std::string> walk(std::string dir)
    {
        std::vector<std::string> paths;
        paths.push_back(dir);

        for (const auto& d : globOne(dir + '*').dirs)
        {
            const auto next(walk(d));
            paths.insert(paths.end(), next.begin(), next.end());
        }

        return paths;
    }
}

std::vector<std::string> glob(std::string path)
{
    std::vector<std::string> results;

    path = fs::expandTilde(path);

    if (path.find('*') == std::string::npos)
    {
        results.push_back(path);
        return results;
    }

    std::vector<std::string> dirs;

    const std::size_t recPos(path.find("**"));
    if (recPos != std::string::npos)
    {
        // Convert this recursive glob into multiple non-recursive ones.
        const auto pre(path.substr(0, recPos));     // Cut off before the '*'.
        const auto post(path.substr(recPos + 1));   // Includes the second '*'.

        for (const auto d : walk(pre)) dirs.push_back(d + post);
    }
    else
    {
        dirs.push_back(path);
    }

    for (const auto& p : dirs)
    {
        Globs globs(globOne(p));
        results.insert(results.end(), globs.files.begin(), globs.files.end());
    }

    return results;
}

std::string expandTilde(std::string in)
{
    std::string out(in);
    static std::string home(getHome());
    if (!in.empty() && in.front() == '~')
    {
        if (home.empty()) throw ArbiterError("No home directory found");
        out = home + in.substr(1);
    }

    return out;
}

std::string getTempPath()
{
#ifndef ARBITER_WINDOWS
    if (const auto t = util::env("TMPDIR"))     return *t;
    if (const auto t = util::env("TMP"))        return *t;
    if (const auto t = util::env("TEMP"))       return *t;
    if (const auto t = util::env("TEMPDIR"))    return *t;
    return "/tmp";
#else
    std::vector<char> path(MAX_PATH, '\0');
    if (GetTempPath(MAX_PATH, path.data())) return path.data();
    else throw ArbiterError("Could not find a temp path.");
#endif
}

LocalHandle::LocalHandle(const std::string localPath, const bool isRemote)
    : m_localPath(expandTilde(localPath))
    , m_erase(isRemote)
{ }

LocalHandle::~LocalHandle()
{
    if (m_erase) fs::remove(fs::expandTilde(m_localPath));
}

} // namespace fs
} // namespace arbiter

#ifdef ARBITER_CUSTOM_NAMESPACE
}
#endif

<commit_msg>_mkdir returned EACCES when passing C: when the CWD was on a different drive letter. Instead of skipping this part, I changed to CreateDirectory instead; it gives more consistent results.<commit_after>#ifndef ARBITER_IS_AMALGAMATION
#include <arbiter/arbiter.hpp>
#include <arbiter/drivers/fs.hpp>
#include <arbiter/util/util.hpp>
#endif

#ifndef ARBITER_WINDOWS
#include <glob.h>
#include <sys/stat.h>
#else

#include <locale>
#include <codecvt>
#include <windows.h>
#include <direct.h>
#endif

#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <ios>
#include <istream>

#ifdef ARBITER_CUSTOM_NAMESPACE
namespace ARBITER_CUSTOM_NAMESPACE
{
#endif

namespace arbiter
{

namespace
{
    // Binary output, overwriting any existing file with a conflicting name.
    const std::ios_base::openmode binaryTruncMode(
            std::ofstream::binary |
            std::ofstream::out |
            std::ofstream::trunc);

    std::string getHome()
    {
        std::string s;

#ifndef ARBITER_WINDOWS
        if (auto home = util::env("HOME")) s = *home;
#else
        if (auto userProfile = util::env("USERPROFILE"))
        {
            s = *userProfile;
        }
        else
        {
            auto homeDrive(util::env("HOMEDRIVE"));
            auto homePath(util::env("HOMEPATH"));

            if (homeDrive && homePath) s = *homeDrive + *homePath;
        }
#endif
        if (s.empty()) std::cout << "No home directory found" << std::endl;

        return s;
    }
}

namespace drivers
{

std::unique_ptr<Fs> Fs::create(const Json::Value&)
{
    return std::unique_ptr<Fs>(new Fs());
}

std::unique_ptr<std::size_t> Fs::tryGetSize(std::string path) const
{
    std::unique_ptr<std::size_t> size;

    path = fs::expandTilde(path);

    std::ifstream stream(path, std::ios::in | std::ios::binary);

    if (stream.good())
    {
        stream.seekg(0, std::ios::end);
        size.reset(new std::size_t(stream.tellg()));
    }

    return size;
}

bool Fs::get(std::string path, std::vector<char>& data) const
{
    bool good(false);

    path = fs::expandTilde(path);
    std::ifstream stream(path, std::ios::in | std::ios::binary);

    if (stream.good())
    {
        stream.seekg(0, std::ios::end);
        data.resize(static_cast<std::size_t>(stream.tellg()));
        stream.seekg(0, std::ios::beg);
        stream.read(data.data(), data.size());
        stream.close();
        good = true;
    }

    return good;
}

void Fs::put(std::string path, const std::vector<char>& data) const
{
    path = fs::expandTilde(path);
    std::ofstream stream(path, binaryTruncMode);

    if (!stream.good())
    {
        throw ArbiterError("Could not open " + path + " for writing");
    }

    stream.write(data.data(), data.size());

    if (!stream.good())
    {
        throw ArbiterError("Error occurred while writing " + path);
    }
}

void Fs::copy(std::string src, std::string dst) const
{
    src = fs::expandTilde(src);
    dst = fs::expandTilde(dst);

    std::ifstream instream(src, std::ifstream::in | std::ifstream::binary);
    if (!instream.good())
    {
        throw ArbiterError("Could not open " + src + " for reading");
    }
    instream >> std::noskipws;

    std::ofstream outstream(dst, binaryTruncMode);
    if (!outstream.good())
    {
        throw ArbiterError("Could not open " + dst + " for writing");
    }

    outstream << instream.rdbuf();
}

std::vector<std::string> Fs::glob(std::string path, bool verbose) const
{
    return fs::glob(path);
}

} // namespace drivers

namespace fs
{

bool mkdirp(std::string raw)
{
    const std::string dir(([&raw]()
    {
        std::string s(expandTilde(raw));

        // Remove consecutive slashes.  For Windows, we'll need to be careful
        // not to remove drive letters like C:\\.
        const auto end = std::unique(s.begin(), s.end(), [](char l, char r)
        {
            return util::isSlash(l) && util::isSlash(r);
        });

        s = std::string(s.begin(), end);
        if (s.size() && util::isSlash(s.back())) s.pop_back();
        return s;
    })());

    auto it(dir.begin());
    const auto end(dir.cend());

    do
    {
        it = std::find_if(++it, end, util::isSlash);

        const std::string cur(dir.begin(), it);
#ifndef ARBITER_WINDOWS
        const bool err(::mkdir(cur.c_str(), S_IRWXU | S_IRGRP | S_IROTH));
        if (err && errno != EEXIST) return false;
#else
        // Use CreateDirectory instead of _mkdir; it is more reliable when creating directories on a drive other than the working path.
        const bool err(::CreateDirectory(cur.c_str(), NULL));
        if (err && ::GetLastError() != ERROR_ALREADY_EXISTS) return false;
#endif
    }
    while (it != end);

    return true;

}

bool remove(std::string filename)
{
    filename = expandTilde(filename);

    return ::remove(filename.c_str()) == 0;
}

namespace
{
    struct Globs
    {
        std::vector<std::string> files;
        std::vector<std::string> dirs;
    };

    Globs globOne(std::string path)
    {
        Globs results;

#ifndef ARBITER_WINDOWS
        glob_t buffer;
        struct stat info;

        ::glob(path.c_str(), GLOB_NOSORT | GLOB_MARK, 0, &buffer);

        for (std::size_t i(0); i < buffer.gl_pathc; ++i)
        {
            const std::string val(buffer.gl_pathv[i]);

            if (stat(val.c_str(), &info) == 0)
            {
                if (S_ISREG(info.st_mode)) results.files.push_back(val);
                else if (S_ISDIR(info.st_mode)) results.dirs.push_back(val);
            }
            else
            {
                throw ArbiterError("Error globbing - POSIX stat failed");
            }
        }

        globfree(&buffer);
#else
        std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
        const std::wstring wide(converter.from_bytes(path));

        LPWIN32_FIND_DATAW data{};
        HANDLE hFind(FindFirstFileW(wide.c_str(), data));

        if (hFind != INVALID_HANDLE_VALUE)
        {
            do
            {
                if ((data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
                {
                    results.files.push_back(
                            converter.to_bytes(data->cFileName));
                }
                else
                {
                    results.dirs.push_back(converter.to_bytes(data->cFileName));
                }
            }
            while (FindNextFileW(hFind, data));
        }
#endif

        return results;
    }

    std::vector<std::string> walk(std::string dir)
    {
        std::vector<std::string> paths;
        paths.push_back(dir);

        for (const auto& d : globOne(dir + '*').dirs)
        {
            const auto next(walk(d));
            paths.insert(paths.end(), next.begin(), next.end());
        }

        return paths;
    }
}

std::vector<std::string> glob(std::string path)
{
    std::vector<std::string> results;

    path = fs::expandTilde(path);

    if (path.find('*') == std::string::npos)
    {
        results.push_back(path);
        return results;
    }

    std::vector<std::string> dirs;

    const std::size_t recPos(path.find("**"));
    if (recPos != std::string::npos)
    {
        // Convert this recursive glob into multiple non-recursive ones.
        const auto pre(path.substr(0, recPos));     // Cut off before the '*'.
        const auto post(path.substr(recPos + 1));   // Includes the second '*'.

        for (const auto d : walk(pre)) dirs.push_back(d + post);
    }
    else
    {
        dirs.push_back(path);
    }

    for (const auto& p : dirs)
    {
        Globs globs(globOne(p));
        results.insert(results.end(), globs.files.begin(), globs.files.end());
    }

    return results;
}

std::string expandTilde(std::string in)
{
    std::string out(in);
    static std::string home(getHome());
    if (!in.empty() && in.front() == '~')
    {
        if (home.empty()) throw ArbiterError("No home directory found");
        out = home + in.substr(1);
    }

    return out;
}

std::string getTempPath()
{
#ifndef ARBITER_WINDOWS
    if (const auto t = util::env("TMPDIR"))     return *t;
    if (const auto t = util::env("TMP"))        return *t;
    if (const auto t = util::env("TEMP"))       return *t;
    if (const auto t = util::env("TEMPDIR"))    return *t;
    return "/tmp";
#else
    std::vector<char> path(MAX_PATH, '\0');
    if (GetTempPath(MAX_PATH, path.data())) return path.data();
    else throw ArbiterError("Could not find a temp path.");
#endif
}

LocalHandle::LocalHandle(const std::string localPath, const bool isRemote)
    : m_localPath(expandTilde(localPath))
    , m_erase(isRemote)
{ }

LocalHandle::~LocalHandle()
{
    if (m_erase) fs::remove(fs::expandTilde(m_localPath));
}

} // namespace fs
} // namespace arbiter

#ifdef ARBITER_CUSTOM_NAMESPACE
}
#endif

<|endoftext|>


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

#include "base/environment.h"
#include "base/memory/scoped_ptr.h"
#include "base/win/scoped_com_initializer.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_manager_base.h"
#if defined(OS_WIN)
#include "media/audio/win/audio_manager_win.h"
#endif
#include "testing/gtest/include/gtest/gtest.h"

using base::win::ScopedCOMInitializer;
using media::AudioDeviceNames;

// Test fixture which allows us to override the default enumeration API on
// Windows.
class AudioInputDeviceTest
    : public ::testing::Test {
 protected:
  AudioInputDeviceTest() {
    audio_manager_ = AudioManager::Create();
  }

#if defined(OS_WIN)
  bool SetMMDeviceEnumeration() {
    AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
    // Windows Wave is used as default if Windows XP was detected =>
    // return false since MMDevice is not supported on XP.
    if (amw->enumeration_type() == AudioManagerWin::kWaveEnumeration)
      return false;

    amw->SetEnumerationType(AudioManagerWin::kMMDeviceEnumeration);
    return true;
  }

  void SetWaveEnumeration() {
    AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
    amw->SetEnumerationType(AudioManagerWin::kWaveEnumeration);
  }
#endif

  scoped_refptr<AudioManager> audio_manager_;
};

// Convenience method which ensures that we are not running on the build
// bots which lacks audio device support.
static bool CanRunAudioTests() {
  scoped_ptr<base::Environment> env(base::Environment::Create());
  if (env->HasVar("CHROME_HEADLESS"))
    return false;
  return true;
}

// Helper method which verifies that the device list starts with a valid
// default record followed by non-default device names.
static void CheckDeviceNames(const AudioDeviceNames& device_names) {
  if (!device_names.empty()) {
    AudioDeviceNames::const_iterator it = device_names.begin();

    // The first device in the list should always be the default device.
    EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceName),
      it->device_name);
    EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceId), it->unique_id);
    ++it;

    // Other devices should have non-empty name and id and should not contain
    // default name or id.
    while (it != device_names.end()) {
      EXPECT_FALSE(it->device_name.empty());
      EXPECT_FALSE(it->unique_id.empty());
      EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceName),
                it->device_name);
      EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceId),
                it->unique_id);
      ++it;
    }
  }
}

// Test that devices can be enumerated.
TEST_F(AudioInputDeviceTest, EnumerateDevices) {
  if (!CanRunAudioTests())
    return;
  // The MMDevice API requires a correct COM environment.
  ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
  AudioDeviceNames device_names;
  audio_manager_->GetAudioInputDeviceNames(&device_names);
  CheckDeviceNames(device_names);
}

// Run additional tests for Windows since enumeration can be done using
// two different APIs. MMDevice is default for Vista and higher and Wave
// is default for XP and lower.
#if defined(OS_WIN)

// Override default enumeration API and force usage of Windows MMDevice.
// This test will only run on Windows Vista and higher.
TEST_F(AudioInputDeviceTest, EnumerateDevicesWinMMDevice) {
  if (!CanRunAudioTests())
    return;
  // The MMDevice API requires a correct COM environment.
  ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
  AudioDeviceNames device_names;
  if (!SetMMDeviceEnumeration()) {
    // Usage of MMDevice will fail on XP and lower.
    return;
  }
  audio_manager_->GetAudioInputDeviceNames(&device_names);
  CheckDeviceNames(device_names);
}

// Override default enumeration API and force usage of Windows Wave.
// This test will run on Windows XP, Windows Vista and Windows 7.
TEST_F(AudioInputDeviceTest, EnumerateDevicesWinWave) {
  if (!CanRunAudioTests())
    return;
  AudioDeviceNames device_names;
  SetWaveEnumeration();
  audio_manager_->GetAudioInputDeviceNames(&device_names);
  CheckDeviceNames(device_names);
}

#endif
<commit_msg>Enable the AudioInputDevice tests on all try/build bots. These tests should all succeed even though the list of enumerated input devices may be empty.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/environment.h"
#include "base/memory/scoped_ptr.h"
#include "base/win/scoped_com_initializer.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_manager_base.h"
#if defined(OS_WIN)
#include "media/audio/win/audio_manager_win.h"
#endif
#include "testing/gtest/include/gtest/gtest.h"

using base::win::ScopedCOMInitializer;
using media::AudioDeviceNames;

// Test fixture which allows us to override the default enumeration API on
// Windows.
class AudioInputDeviceTest
    : public ::testing::Test {
 protected:
  AudioInputDeviceTest() : com_init_(ScopedCOMInitializer::kMTA) {
    audio_manager_ = AudioManager::Create();
  }

#if defined(OS_WIN)
  bool SetMMDeviceEnumeration() {
    AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
    // Windows Wave is used as default if Windows XP was detected =>
    // return false since MMDevice is not supported on XP.
    if (amw->enumeration_type() == AudioManagerWin::kWaveEnumeration)
      return false;

    amw->SetEnumerationType(AudioManagerWin::kMMDeviceEnumeration);
    return true;
  }

  void SetWaveEnumeration() {
    AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
    amw->SetEnumerationType(AudioManagerWin::kWaveEnumeration);
  }
#endif

  // Helper method which verifies that the device list starts with a valid
  // default record followed by non-default device names.
  static void CheckDeviceNames(const AudioDeviceNames& device_names) {
    if (!device_names.empty()) {
      AudioDeviceNames::const_iterator it = device_names.begin();

      // The first device in the list should always be the default device.
      EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceName),
                it->device_name);
      EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceId), it->unique_id);
      ++it;

      // Other devices should have non-empty name and id and should not contain
      // default name or id.
      while (it != device_names.end()) {
        EXPECT_FALSE(it->device_name.empty());
        EXPECT_FALSE(it->unique_id.empty());
        EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceName),
                  it->device_name);
        EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceId),
                  it->unique_id);
        ++it;
      }
    } else {
      // Log a warning so we can see the status on the build bots.  No need to
      // break the test though since this does successfully test the code and
      // some failure cases.
      LOG(WARNING) << "No input devices detected";
    }
  }

  scoped_refptr<AudioManager> audio_manager_;

  // The MMDevice API requires COM to be initialized on the current thread.
  ScopedCOMInitializer com_init_;
};

// Test that devices can be enumerated.
TEST_F(AudioInputDeviceTest, EnumerateDevices) {
  AudioDeviceNames device_names;
  audio_manager_->GetAudioInputDeviceNames(&device_names);
  CheckDeviceNames(device_names);
}

// Run additional tests for Windows since enumeration can be done using
// two different APIs. MMDevice is default for Vista and higher and Wave
// is default for XP and lower.
#if defined(OS_WIN)

// Override default enumeration API and force usage of Windows MMDevice.
// This test will only run on Windows Vista and higher.
TEST_F(AudioInputDeviceTest, EnumerateDevicesWinMMDevice) {
  AudioDeviceNames device_names;
  if (!SetMMDeviceEnumeration()) {
    // Usage of MMDevice will fail on XP and lower.
    LOG(WARNING) << "MM device enumeration is not supported.";
    return;
  }
  audio_manager_->GetAudioInputDeviceNames(&device_names);
  CheckDeviceNames(device_names);
}

// Override default enumeration API and force usage of Windows Wave.
// This test will run on Windows XP, Windows Vista and Windows 7.
TEST_F(AudioInputDeviceTest, EnumerateDevicesWinWave) {
  AudioDeviceNames device_names;
  SetWaveEnumeration();
  audio_manager_->GetAudioInputDeviceNames(&device_names);
  CheckDeviceNames(device_names);
}

#endif
<|endoftext|>