<commit_before>#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-parameter"
# pragma GCC diagnostic ignored "-Wignored-qualifiers"
# pragma GCC diagnostic ignored "-Wextra"
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
# pragma GCC diagnostic ignored "-Wtype-limits"
# pragma GCC diagnostic ignored "-Wshadow"
# pragma GCC diagnostic ignored "-Wreorder"
# pragma GCC diagnostic ignored "-Wreorder"
# pragma GCC diagnostic ignored "-Wused-but-marked-unused"
#endif

/** Copyright (c) 2012, Rene Milk, Felix Schindler
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   *
   * 1. Redistributions of source code must retain the above copyright notice, this
   *    list of conditions and the following disclaimer.
   * 2. 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.
   *
   * 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.
   *
   * The views and conclusions contained in the software and documentation are those
   * of the authors and should not be interpreted as representing official policies,
   * either expressed or implied, of the FreeBSD Project.
   **/
<commit_msg>[common.disable_warnings] ignore -Wdeprecated-register<commit_after>#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-parameter"
# pragma GCC diagnostic ignored "-Wdeprecated-register"
# pragma GCC diagnostic ignored "-Wignored-qualifiers"
# pragma GCC diagnostic ignored "-Wextra"
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
# pragma GCC diagnostic ignored "-Wtype-limits"
# pragma GCC diagnostic ignored "-Wshadow"
# pragma GCC diagnostic ignored "-Wreorder"
# pragma GCC diagnostic ignored "-Wreorder"
# pragma GCC diagnostic ignored "-Wused-but-marked-unused"
#endif

/** Copyright (c) 2012, Rene Milk, Felix Schindler
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   *
   * 1. Redistributions of source code must retain the above copyright notice, this
   *    list of conditions and the following disclaimer.
   * 2. 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.
   *
   * 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.
   *
   * The views and conclusions contained in the software and documentation are those
   * of the authors and should not be interpreted as representing official policies,
   * either expressed or implied, of the FreeBSD Project.
   **/
<|endoftext|>


<commit_before><commit_msg>Fixed bug in MakeTriangle: I had assumed the 3 nodes on the nbrList were p0,p1, and p2 in that order, but actually the iterator passed to MT was already advanced, so that cn, cnn, and cnnn were NOT p0,p1,and p2 on the nbrList... Now they are (the iterator is reset to the beginning). Also fixed bug in MakeTriangleFromScratch: in the opposite sides open option, "n" wasn't recalculated for the "top" side, so that the wrong # of nodes was being assigned.<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2010 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 "webkit/glue/plugins/plugin_lib.h"

#include <dlfcn.h>
#if defined(OS_OPENBSD)
#include <sys/exec_elf.h>
#else
#include <elf.h>
#include <fcntl.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "base/eintr_wrapper.h"
#include "base/file_util.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
#include "base/utf_string_conversions.h"
#include "webkit/glue/plugins/plugin_list.h"

// These headers must be included in this order to make the declaration gods
// happy.
#include "base/third_party/nspr/prcpucfg_linux.h"

namespace {

using NPAPI::PluginList;

// Copied from nsplugindefs.h instead of including the file since it has a bunch
// of dependencies.
enum nsPluginVariable {
  nsPluginVariable_NameString        = 1,
  nsPluginVariable_DescriptionString = 2
};

// Read the ELF header and return true if it is usable on
// the current architecture (e.g. 32-bit ELF on 32-bit build).
// Returns false on other errors as well.
bool ELFMatchesCurrentArchitecture(const FilePath& filename) {
  // First make sure we can open the file and it is in fact, a regular file.
  struct stat stat_buf;
  // Open with O_NONBLOCK so we don't block on pipes.
  int fd = open(filename.value().c_str(), O_RDONLY|O_NONBLOCK);
  if (fd < 0)
    return false;
  bool ret = (fstat(fd, &stat_buf) >= 0 && S_ISREG(stat_buf.st_mode));
  if (HANDLE_EINTR(close(fd)) < 0)
    return false;
  if (!ret)
    return false;

  const size_t kELFBufferSize = 5;
  char buffer[kELFBufferSize];
  if (!file_util::ReadFile(filename, buffer, kELFBufferSize))
    return false;

  if (buffer[0] != ELFMAG0 ||
      buffer[1] != ELFMAG1 ||
      buffer[2] != ELFMAG2 ||
      buffer[3] != ELFMAG3) {
    // Not an ELF file, perhaps?
    return false;
  }

  int elf_class = buffer[EI_CLASS];
#if defined(ARCH_CPU_32_BITS)
  if (elf_class == ELFCLASS32)
    return true;
#elif defined(ARCH_CPU_64_BITS)
  if (elf_class == ELFCLASS64)
    return true;
#endif

  return false;
}

// This structure matches enough of nspluginwrapper's NPW_PluginInfo
// for us to extract the real plugin path.
struct __attribute__((packed)) NSPluginWrapperInfo {
  char ident[32];  // NSPluginWrapper magic identifier (includes version).
  char path[PATH_MAX];  // Path to wrapped plugin.
};

// Test a plugin for whether it's been wrapped by NSPluginWrapper, and
// if so attempt to unwrap it.  Pass in an opened plugin handle; on
// success, |dl| and |unwrapped_path| will be filled in with the newly
// opened plugin.  On failure, params are left unmodified.
void UnwrapNSPluginWrapper(void **dl, FilePath* unwrapped_path) {
  NSPluginWrapperInfo* info =
      reinterpret_cast<NSPluginWrapperInfo*>(dlsym(*dl, "NPW_Plugin"));
  if (!info)
    return;  // Not a NSPW plugin.

  // Here we could check the NSPW ident field for the versioning
  // information, but the path field is available in all versions
  // anyway.

  // Grab the path to the wrapped plugin.  Just in case the structure
  // format changes, protect against the path not being null-terminated.
  char* path_end = static_cast<char*>(memchr(info->path, '\0',
                                             sizeof(info->path)));
  if (!path_end)
    path_end = info->path + sizeof(info->path);
  FilePath path = FilePath(std::string(info->path, path_end - info->path));

  if (!ELFMatchesCurrentArchitecture(path)) {
    LOG(WARNING) << path.value() << " is nspluginwrapper wrapping a "
                 << "plugin for a different architecture; it will "
                 << "work better if you instead use a native plugin.";
    return;
  }

  void* newdl = base::LoadNativeLibrary(path);
  if (!newdl) {
    // We couldn't load the unwrapped plugin for some reason, despite
    // being able to load the wrapped one.  Just use the wrapped one.
    LOG_IF(ERROR, PluginList::DebugPluginLoading())
        << "Could not use unwrapped nspluginwrapper plugin "
        << unwrapped_path->value() << ", using the wrapped one.";
    return;
  }

  // Unload the wrapped plugin, and use the wrapped plugin instead.
  LOG_IF(ERROR, PluginList::DebugPluginLoading())
      << "Using unwrapped version " << unwrapped_path->value()
      << " of nspluginwrapper-wrapped plugin.";
  base::UnloadNativeLibrary(*dl);
  *dl = newdl;
  *unwrapped_path = path;
}

}  // anonymous namespace

namespace NPAPI {

bool PluginLib::ReadWebPluginInfo(const FilePath& filename,
                                  WebPluginInfo* info) {
  // The file to reference is:
  // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirUnix.cpp

  // Skip files that aren't appropriate for our architecture.
  if (!ELFMatchesCurrentArchitecture(filename)) {
    LOG_IF(ERROR, PluginList::DebugPluginLoading())
        << "Skipping plugin " << filename.value()
        << " because it doesn't match the current architecture.";
    return false;
  }

  void* dl = base::LoadNativeLibrary(filename);
  if (!dl) {
    LOG_IF(ERROR, PluginList::DebugPluginLoading())
        << "While reading plugin info, unable to load library "
        << filename.value() << ", skipping.";
    return false;
  }

  info->path = filename;
  info->enabled = true;

  // Attempt to swap in the wrapped plugin if this is nspluginwrapper.
  UnwrapNSPluginWrapper(&dl, &info->path);

  // See comments in plugin_lib_mac regarding this symbol.
  typedef const char* (*NP_GetMimeDescriptionType)();
  NP_GetMimeDescriptionType NP_GetMIMEDescription =
      reinterpret_cast<NP_GetMimeDescriptionType>(
          dlsym(dl, "NP_GetMIMEDescription"));
  const char* mime_description = NULL;
  if (NP_GetMIMEDescription)
    mime_description = NP_GetMIMEDescription();

  if (mime_description)
    ParseMIMEDescription(mime_description, &info->mime_types);

  // The plugin name and description live behind NP_GetValue calls.
  typedef NPError (*NP_GetValueType)(void* unused,
                                     nsPluginVariable variable,
                                     void* value_out);
  NP_GetValueType NP_GetValue =
      reinterpret_cast<NP_GetValueType>(dlsym(dl, "NP_GetValue"));
  if (NP_GetValue) {
    const char* name = NULL;
    NP_GetValue(NULL, nsPluginVariable_NameString, &name);
    if (name)
      info->name = UTF8ToUTF16(name);

    const char* description = NULL;
    NP_GetValue(NULL, nsPluginVariable_DescriptionString, &description);
    if (description)
      info->desc = UTF8ToUTF16(description);

    LOG_IF(ERROR, PluginList::DebugPluginLoading())
        << "Got info for plugin " << filename.value()
        << " Name = \"" << UTF16ToUTF8(info->name)
        << "\", Description = \"" << UTF16ToUTF8(info->desc) << "\".";
  } else {
    LOG_IF(ERROR, PluginList::DebugPluginLoading())
        << "Plugin " << filename.value()
        << " has no GetValue() and probably won't work.";
  }

  // Intentionally not unloading the plugin here, it can lead to crashes.

  return true;
}

// static
void PluginLib::ParseMIMEDescription(
    const std::string& description,
    std::vector<WebPluginMimeType>* mime_types) {
  // We parse the description here into WebPluginMimeType structures.
  // Naively from the NPAPI docs you'd think you could use
  // string-splitting, but the Firefox parser turns out to do something
  // different: find the first colon, then the second, then a semi.
  //
  // See ParsePluginMimeDescription near
  // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirUtils.h#53

  std::string::size_type ofs = 0;
  for (;;) {
    WebPluginMimeType mime_type;

    std::string::size_type end = description.find(':', ofs);
    if (end == std::string::npos)
      break;
    mime_type.mime_type = description.substr(ofs, end - ofs);
    ofs = end + 1;

    end = description.find(':', ofs);
    if (end == std::string::npos)
      break;
    const std::string extensions = description.substr(ofs, end - ofs);
    base::SplitString(extensions, ',', &mime_type.file_extensions);
    ofs = end + 1;

    end = description.find(';', ofs);
    // It's ok for end to run off the string here.  If there's no
    // trailing semicolon we consume the remainder of the string.
    if (end != std::string::npos) {
      mime_type.description = UTF8ToUTF16(description.substr(ofs, end - ofs));
    } else {
      mime_type.description = UTF8ToUTF16(description.substr(ofs));
    }
    mime_types->push_back(mime_type);
    if (end == std::string::npos)
      break;
    ofs = end + 1;
  }
}

}  // namespace NPAPI
<commit_msg>Linux Plugins: Add a debugging statement to help track down bug 59317.<commit_after>// Copyright (c) 2010 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 "webkit/glue/plugins/plugin_lib.h"

#include <dlfcn.h>
#if defined(OS_OPENBSD)
#include <sys/exec_elf.h>
#else
#include <elf.h>
#include <fcntl.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "base/eintr_wrapper.h"
#include "base/file_util.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
#include "base/utf_string_conversions.h"
#include "webkit/glue/plugins/plugin_list.h"

// These headers must be included in this order to make the declaration gods
// happy.
#include "base/third_party/nspr/prcpucfg_linux.h"

namespace {

using NPAPI::PluginList;

// Copied from nsplugindefs.h instead of including the file since it has a bunch
// of dependencies.
enum nsPluginVariable {
  nsPluginVariable_NameString        = 1,
  nsPluginVariable_DescriptionString = 2
};

// Read the ELF header and return true if it is usable on
// the current architecture (e.g. 32-bit ELF on 32-bit build).
// Returns false on other errors as well.
bool ELFMatchesCurrentArchitecture(const FilePath& filename) {
  // First make sure we can open the file and it is in fact, a regular file.
  struct stat stat_buf;
  // Open with O_NONBLOCK so we don't block on pipes.
  int fd = open(filename.value().c_str(), O_RDONLY|O_NONBLOCK);
  if (fd < 0)
    return false;
  bool ret = (fstat(fd, &stat_buf) >= 0 && S_ISREG(stat_buf.st_mode));
  if (HANDLE_EINTR(close(fd)) < 0)
    return false;
  if (!ret)
    return false;

  const size_t kELFBufferSize = 5;
  char buffer[kELFBufferSize];
  if (!file_util::ReadFile(filename, buffer, kELFBufferSize))
    return false;

  if (buffer[0] != ELFMAG0 ||
      buffer[1] != ELFMAG1 ||
      buffer[2] != ELFMAG2 ||
      buffer[3] != ELFMAG3) {
    // Not an ELF file, perhaps?
    return false;
  }

  int elf_class = buffer[EI_CLASS];
#if defined(ARCH_CPU_32_BITS)
  if (elf_class == ELFCLASS32)
    return true;
#elif defined(ARCH_CPU_64_BITS)
  if (elf_class == ELFCLASS64)
    return true;
#endif

  return false;
}

// This structure matches enough of nspluginwrapper's NPW_PluginInfo
// for us to extract the real plugin path.
struct __attribute__((packed)) NSPluginWrapperInfo {
  char ident[32];  // NSPluginWrapper magic identifier (includes version).
  char path[PATH_MAX];  // Path to wrapped plugin.
};

// Test a plugin for whether it's been wrapped by NSPluginWrapper, and
// if so attempt to unwrap it.  Pass in an opened plugin handle; on
// success, |dl| and |unwrapped_path| will be filled in with the newly
// opened plugin.  On failure, params are left unmodified.
void UnwrapNSPluginWrapper(void **dl, FilePath* unwrapped_path) {
  NSPluginWrapperInfo* info =
      reinterpret_cast<NSPluginWrapperInfo*>(dlsym(*dl, "NPW_Plugin"));
  if (!info)
    return;  // Not a NSPW plugin.

  // Here we could check the NSPW ident field for the versioning
  // information, but the path field is available in all versions
  // anyway.

  // Grab the path to the wrapped plugin.  Just in case the structure
  // format changes, protect against the path not being null-terminated.
  char* path_end = static_cast<char*>(memchr(info->path, '\0',
                                             sizeof(info->path)));
  if (!path_end)
    path_end = info->path + sizeof(info->path);
  FilePath path = FilePath(std::string(info->path, path_end - info->path));

  if (!ELFMatchesCurrentArchitecture(path)) {
    LOG(WARNING) << path.value() << " is nspluginwrapper wrapping a "
                 << "plugin for a different architecture; it will "
                 << "work better if you instead use a native plugin.";
    return;
  }

  void* newdl = base::LoadNativeLibrary(path);
  if (!newdl) {
    // We couldn't load the unwrapped plugin for some reason, despite
    // being able to load the wrapped one.  Just use the wrapped one.
    LOG_IF(ERROR, PluginList::DebugPluginLoading())
        << "Could not use unwrapped nspluginwrapper plugin "
        << unwrapped_path->value() << ", using the wrapped one.";
    return;
  }

  // Unload the wrapped plugin, and use the wrapped plugin instead.
  LOG_IF(ERROR, PluginList::DebugPluginLoading())
      << "Using unwrapped version " << unwrapped_path->value()
      << " of nspluginwrapper-wrapped plugin.";
  base::UnloadNativeLibrary(*dl);
  *dl = newdl;
  *unwrapped_path = path;
}

}  // anonymous namespace

namespace NPAPI {

bool PluginLib::ReadWebPluginInfo(const FilePath& filename,
                                  WebPluginInfo* info) {
  // The file to reference is:
  // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirUnix.cpp

  // Skip files that aren't appropriate for our architecture.
  if (!ELFMatchesCurrentArchitecture(filename)) {
    LOG_IF(ERROR, PluginList::DebugPluginLoading())
        << "Skipping plugin " << filename.value()
        << " because it doesn't match the current architecture.";
    return false;
  }

  // TODO(thestig) This is for debugging bug 59317. Remove this when done.
  LOG(INFO) << "PluginLib::ReadWebPluginInfo loading " << filename.value();
  void* dl = base::LoadNativeLibrary(filename);
  if (!dl) {
    LOG_IF(ERROR, PluginList::DebugPluginLoading())
        << "While reading plugin info, unable to load library "
        << filename.value() << ", skipping.";
    return false;
  }

  info->path = filename;
  info->enabled = true;

  // Attempt to swap in the wrapped plugin if this is nspluginwrapper.
  UnwrapNSPluginWrapper(&dl, &info->path);

  // See comments in plugin_lib_mac regarding this symbol.
  typedef const char* (*NP_GetMimeDescriptionType)();
  NP_GetMimeDescriptionType NP_GetMIMEDescription =
      reinterpret_cast<NP_GetMimeDescriptionType>(
          dlsym(dl, "NP_GetMIMEDescription"));
  const char* mime_description = NULL;
  if (NP_GetMIMEDescription)
    mime_description = NP_GetMIMEDescription();

  if (mime_description)
    ParseMIMEDescription(mime_description, &info->mime_types);

  // The plugin name and description live behind NP_GetValue calls.
  typedef NPError (*NP_GetValueType)(void* unused,
                                     nsPluginVariable variable,
                                     void* value_out);
  NP_GetValueType NP_GetValue =
      reinterpret_cast<NP_GetValueType>(dlsym(dl, "NP_GetValue"));
  if (NP_GetValue) {
    const char* name = NULL;
    NP_GetValue(NULL, nsPluginVariable_NameString, &name);
    if (name)
      info->name = UTF8ToUTF16(name);

    const char* description = NULL;
    NP_GetValue(NULL, nsPluginVariable_DescriptionString, &description);
    if (description)
      info->desc = UTF8ToUTF16(description);

    LOG_IF(ERROR, PluginList::DebugPluginLoading())
        << "Got info for plugin " << filename.value()
        << " Name = \"" << UTF16ToUTF8(info->name)
        << "\", Description = \"" << UTF16ToUTF8(info->desc) << "\".";
  } else {
    LOG_IF(ERROR, PluginList::DebugPluginLoading())
        << "Plugin " << filename.value()
        << " has no GetValue() and probably won't work.";
  }

  // Intentionally not unloading the plugin here, it can lead to crashes.

  return true;
}

// static
void PluginLib::ParseMIMEDescription(
    const std::string& description,
    std::vector<WebPluginMimeType>* mime_types) {
  // We parse the description here into WebPluginMimeType structures.
  // Naively from the NPAPI docs you'd think you could use
  // string-splitting, but the Firefox parser turns out to do something
  // different: find the first colon, then the second, then a semi.
  //
  // See ParsePluginMimeDescription near
  // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirUtils.h#53

  std::string::size_type ofs = 0;
  for (;;) {
    WebPluginMimeType mime_type;

    std::string::size_type end = description.find(':', ofs);
    if (end == std::string::npos)
      break;
    mime_type.mime_type = description.substr(ofs, end - ofs);
    ofs = end + 1;

    end = description.find(':', ofs);
    if (end == std::string::npos)
      break;
    const std::string extensions = description.substr(ofs, end - ofs);
    base::SplitString(extensions, ',', &mime_type.file_extensions);
    ofs = end + 1;

    end = description.find(';', ofs);
    // It's ok for end to run off the string here.  If there's no
    // trailing semicolon we consume the remainder of the string.
    if (end != std::string::npos) {
      mime_type.description = UTF8ToUTF16(description.substr(ofs, end - ofs));
    } else {
      mime_type.description = UTF8ToUTF16(description.substr(ofs));
    }
    mime_types->push_back(mime_type);
    if (end == std::string::npos)
      break;
    ofs = end + 1;
  }
}

}  // namespace NPAPI
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// http://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

#include <cmake.h>
#include <CmdLogo.h>
#include <Context.h>
#include <Filter.h>
#include <text.h>
#include <i18n.h>

extern Context context;

////////////////////////////////////////////////////////////////////////////////
CmdLogo::CmdLogo ()
{
  _keyword               = "logo";
  _usage                 = "task          logo";
  _description           = STRING_CMD_LOGO_USAGE;
  _read_only             = true;
  _displays_id           = false;
  _needs_gc              = false;
  _uses_context          = false;
  _accepts_filter        = false;
  _accepts_modifications = false;
  _accepts_miscellaneous = false;
  _category              = Command::Category::misc;
}

////////////////////////////////////////////////////////////////////////////////
// Algorithm:
//   Copy file rc.data.location/extensions
//   Generate UUID
//   Call the "install" function once, store results in rc:
//     extension.<uuid>=<JSON>
int CmdLogo::execute (std::string& output)
{
  Filter filter;
  if (filter.hasFilter ())
    throw std::string (STRING_ERROR_NO_FILTER);
  if (filter.hasModifications ())
    throw std::string (STRING_ERROR_NO_MODS);

  static const char* data[] =
  {
    ".........ABDEF",
    ".......BDILNMM",
    ".....ACJNLHFEE",
    "....AFMMFDEEEE",
    "...AFOIDEEEEDD",
    "..AFOHDEEEDIOR",
    "..DMIDEEEEOXXX",
    ".BJMDEEEDMXXXX",
    ".ENFEEEEFVXXXX",
    "BILDEEEEJXXXXX",
    "DLHEEEEDLXXXXX",
    "GMFEEEEDKXXXXX",
    "GMEEEEEEGTIKOR",
    "GMEEEEEEETODDD",
    "GMEEEEEEETXRGE",
    "GMEEEEEEFVXXSE",
    "ENFEEEEEHWXXUE",
    "CLHEEEEDLXXXUE",
    "AILDEEEDRXXXUE",
    ".DNFEEEEPXXXUE",
    ".BJMDEEEEPXXUE",
    "..DMIDEEEDLXUE",
    "..AFOHDEEEDHME",
    "...AFOIDEEEEDE",
    "....AFMMFDEEEE",
    ".....ACJNLHFEE",
    ".......BDILNMM",
    ".........ABDEF",
    ""
  };

  if (!context.color ())
    throw std::string (STRING_CMD_LOGO_COLOR_REQ);

  std::string indent (context.config.getInteger ("indent.report"), ' ');
  output += optionalBlankLine ();

  for (int line = 0; data[line][0]; ++line)
  {
    output += indent;

    for (int c = 0; c < 14; ++c)
    {
      int value = (int) data[line][c];
      if (value == '.')
        output += "  ";
      else
      {
        value += 167;
        char block [24];
        sprintf (block, "\033[48;5;%dm  \033[0m", value);
        output += block;
      }
    }

    for (int c = 13; c >= 0; --c)
    {
      int value = data[line][c];
      if (value == '.')
        output += "  ";
      else
      {
        value += 167;
        char block [24];
        sprintf (block, "\033[48;5;%dm  \033[0m", value);
        output += block;
      }
    }

    output += "\n";
  }

  output += optionalBlankLine ();
  return 0;
}

////////////////////////////////////////////////////////////////////////////////
<commit_msg>CmdLogo: Removed redundant arg checks<commit_after>////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// http://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

#include <cmake.h>
#include <CmdLogo.h>
#include <Context.h>
#include <text.h>
#include <i18n.h>

extern Context context;

////////////////////////////////////////////////////////////////////////////////
CmdLogo::CmdLogo ()
{
  _keyword               = "logo";
  _usage                 = "task          logo";
  _description           = STRING_CMD_LOGO_USAGE;
  _read_only             = true;
  _displays_id           = false;
  _needs_gc              = false;
  _uses_context          = false;
  _accepts_filter        = false;
  _accepts_modifications = false;
  _accepts_miscellaneous = false;
  _category              = Command::Category::misc;
}

////////////////////////////////////////////////////////////////////////////////
// Algorithm:
//   Copy file rc.data.location/extensions
//   Generate UUID
//   Call the "install" function once, store results in rc:
//     extension.<uuid>=<JSON>
int CmdLogo::execute (std::string& output)
{
  static const char* data[] =
  {
    ".........ABDEF",
    ".......BDILNMM",
    ".....ACJNLHFEE",
    "....AFMMFDEEEE",
    "...AFOIDEEEEDD",
    "..AFOHDEEEDIOR",
    "..DMIDEEEEOXXX",
    ".BJMDEEEDMXXXX",
    ".ENFEEEEFVXXXX",
    "BILDEEEEJXXXXX",
    "DLHEEEEDLXXXXX",
    "GMFEEEEDKXXXXX",
    "GMEEEEEEGTIKOR",
    "GMEEEEEEETODDD",
    "GMEEEEEEETXRGE",
    "GMEEEEEEFVXXSE",
    "ENFEEEEEHWXXUE",
    "CLHEEEEDLXXXUE",
    "AILDEEEDRXXXUE",
    ".DNFEEEEPXXXUE",
    ".BJMDEEEEPXXUE",
    "..DMIDEEEDLXUE",
    "..AFOHDEEEDHME",
    "...AFOIDEEEEDE",
    "....AFMMFDEEEE",
    ".....ACJNLHFEE",
    ".......BDILNMM",
    ".........ABDEF",
    ""
  };

  if (!context.color ())
    throw std::string (STRING_CMD_LOGO_COLOR_REQ);

  std::string indent (context.config.getInteger ("indent.report"), ' ');
  output += optionalBlankLine ();

  for (int line = 0; data[line][0]; ++line)
  {
    output += indent;

    for (int c = 0; c < 14; ++c)
    {
      int value = (int) data[line][c];
      if (value == '.')
        output += "  ";
      else
      {
        value += 167;
        char block [24];
        sprintf (block, "\033[48;5;%dm  \033[0m", value);
        output += block;
      }
    }

    for (int c = 13; c >= 0; --c)
    {
      int value = data[line][c];
      if (value == '.')
        output += "  ";
      else
      {
        value += 167;
        char block [24];
        sprintf (block, "\033[48;5;%dm  \033[0m", value);
        output += block;
      }
    }

    output += "\n";
  }

  output += optionalBlankLine ();
  return 0;
}

////////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <algorithm>
#include <cassert>
#include <cctype>
#include <cstddef>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <ostream>
#include <string>
#include <vector>

#include <boost/algorithm/string.hpp>

#include "pnm_image.h"

namespace visionaray
{

//-------------------------------------------------------------------------------------------------
// Helper functions
//

template <typename AssignFunc>
static void load_ascii(
        uint8_t*        dst,
        std::ifstream&  file,
        size_t          pitch,
        size_t          height,
        int             max_value,
        AssignFunc      assign_func // specifies how to handle color components
        )
{
    assert(max_value < 256);    // TODO: 16-bit

    for (size_t y = 0; y < height; ++y)
    {
        std::string line;
        std::getline(file, line);

        std::vector<std::string> tokens;
        boost::algorithm::split(
                tokens,
                line,
                boost::algorithm::is_any_of(" \t")
                );

        // Remove empty tokens and spaces
        tokens.erase(
                std::remove_if(
                        tokens.begin(),
                        tokens.end(),
                        [](std::string str) { return str.empty() || std::isspace(str[0]); }
                        ),
                tokens.end()
                );

        assert(tokens.size() == pitch);

        for (size_t x = 0; x < pitch; ++x)
        {
            int val = std::stoi(tokens[x]);
            if (max_value != 255)
            {
                double n = val / static_cast<double>(max_value); // scale down to [0..1]
                val = static_cast<int>(n * 255);                 // scale up to [0..255]
            }

            dst[y * pitch + x] = assign_func(val);
        }
    }
}

static void load_binary(
        uint8_t*        dst,
        std::ifstream&  file,
        size_t          pitch,
        size_t          height,
        int             max_value
        )
{
    assert(max_value < 256);    // TODO: 16-bit

    file.read(reinterpret_cast<char*>(dst), pitch * height);

    if (max_value != 255)
    {
        double scale = (1.0 / max_value) * 255;

        size_t size = height * pitch;
        for (size_t i = 0; i < size; ++i)
        {
            dst[i] = static_cast<uint8_t>(dst[i] * scale);
        }
    }
}

static void save_binary(
        std::ofstream&  file,
        uint8_t const*  src,
        size_t          pitch,
        size_t          height
        )
{
    file.write(reinterpret_cast<char const*>(src), pitch * height);
}

//-------------------------------------------------------------------------------------------------
// pnm_image
//

pnm_image::pnm_image(size_t width, size_t height, pixel_format format, uint8_t const* data)
    : image_base(width, height, format, data)
{
}

bool pnm_image::load(std::string const& filename)
{
    enum format { P1 = 1, P2, P3, P4, P5, P6 };

    std::ifstream file(filename);

    std::string line;

    //
    // First line determines format:
    // P1: ASCII BW         | P4: binary BW
    // P2: ASCII gray scale | P5: binary gray scale
    // P3: ASCII RGB        | P6: binary RGB
    //
    std::getline(file, line);

    if (line.size() < 2 || line[0] != 'P' || line[1] < '0' || line[1] > '6')
    {
        std::cerr << "Invalid file format: " << line << '\n';
        return false;
    }

    format fmt = static_cast<format>(line[1] - '0');

    // Header
    int header[3]; // width, height, max. value
    int index = 0;

    bool is_bitmap = (fmt == P1 || fmt == P4);

    for (;;)
    {
        std::getline(file, line);

        if (line[0] == '#')
        {
            // Skip comments
            continue;
        }
        else
        {
            std::vector<std::string> tokens;
            boost::algorithm::split(
                    tokens,
                    line,
                    boost::algorithm::is_any_of(" \t")
                    );

            if (tokens.size() > 3)
            {
                std::cerr << "Invalid pnm file header\n";
                return false;
            }

            for (auto t : tokens)
            {
                header[index++] = std::stoi(t);

                if ((is_bitmap && index > 2) || (!is_bitmap && index > 3))
                {
                    std::cerr << "Invalid pnm file header\n";
                    return false;
                }
            }

            // BitMap: width and height read ==> break
            if (is_bitmap && index == 2)
            {
                break;
            }

            // GrayMap/PixMap: width, height and max_value read ==> break
            if (!is_bitmap && index == 3)
            {
                break;
            }
        }
    }

    if (header[0] <= 0 || header[1] <= 0)
    {
        std::cerr << "Invalid image dimensions: " << header[0] << ' ' << header[1] << '\n';
        return false;
    }

    width_  = static_cast<size_t>(header[0]);
    height_ = static_cast<size_t>(header[1]);
    int max_value = header[2];

    switch (fmt)
    {
    default:
        std::cerr << "Unsupported PNM image type: P" << std::to_string(static_cast<int>(fmt)) << '\n';
        width_  = 0;
        height_ = 0;
        format_ = PF_UNSPECIFIED;
        data_.resize(0);
        return false;

    case P1:
        format_ = PF_R8;
        data_.resize(width_ * height_);

        // black or white
        load_ascii(
                data_.data(),
                file,
                width_,
                height_,
                max_value,
                [](int val)
                    {
                        assert( val == 0 || val == 1);
                        return val ? 0U : 255U;
                    }
                );
        return true;

    case P2:
        format_ = PF_R8;
        data_.resize(width_ * height_);

        // single gray component
        load_ascii(
                data_.data(),
                file,
                width_,
                height_,
                max_value,
                [](int val)
                    {
                        return static_cast<uint8_t>(val);
                    }
                );
        return true;

    case P3:
        format_ = PF_RGB8;
        data_.resize(width_ * height_ * 3);

        // RGB color components
        load_ascii(
                data_.data(),
                file,
                width_ * 3,
                height_,
                max_value,
                [](int val)
                    {
                        return static_cast<uint8_t>(val);
                    }
                );
        return true;

    case P5:
        format_ = PF_R8;
        data_.resize(width_ * height_);

        load_binary(
                data_.data(),
                file,
                width_,
                height_,
                max_value
                );
        return true;

    case P6:
        format_ = PF_RGB8;
        data_.resize(width_ * height_ * 3);

        load_binary(
                data_.data(),
                file,
                width_ * 3,
                height_,
                max_value
                );
        return true;
    }

    return false;
}

bool pnm_image::save(std::string const& filename, image_base::save_options const& options)
{
    auto it = std::find_if(
            options.begin(),
            options.end(),
            [](save_option const& opt) { return opt.first == "binary"; }
            );

    if (it == options.end())
    {
        std::cerr << "Option \"binary\" is mandatory!\n";
        return false;
    }

    bool binary = boost::any_cast<bool>(it->second);

    std::ofstream file(filename);

    if (binary && format_ == PF_RGB8)
    {
        file << "P6\n";
        file << width_ << ' ' << height_ << '\n';
        file << 255 << '\n';
        save_binary(
                file,
                data_.data(),
                width_ * 3,
                height_
                );
        return true;
    }

    std::cerr << "Unsupported image format\n";
    return false;
}

}
<commit_msg>Implement PNM image save options P2, P3, and P5<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <algorithm>
#include <cassert>
#include <cctype>
#include <cstddef>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <ostream>
#include <string>
#include <vector>

#include <boost/algorithm/string.hpp>

#include "pnm_image.h"

namespace visionaray
{

//-------------------------------------------------------------------------------------------------
// Helper functions
//

template <typename AssignFunc>
static void load_ascii(
        uint8_t*        dst,
        std::ifstream&  file,
        size_t          pitch,
        size_t          height,
        int             max_value,
        AssignFunc      assign_func // specifies how to handle color components
        )
{
    assert(max_value < 256);    // TODO: 16-bit

    for (size_t y = 0; y < height; ++y)
    {
        std::string line;
        std::getline(file, line);

        std::vector<std::string> tokens;
        boost::algorithm::split(
                tokens,
                line,
                boost::algorithm::is_any_of(" \t")
                );

        // Remove empty tokens and spaces
        tokens.erase(
                std::remove_if(
                        tokens.begin(),
                        tokens.end(),
                        [](std::string str) { return str.empty() || std::isspace(str[0]); }
                        ),
                tokens.end()
                );

        assert(tokens.size() == pitch);

        for (size_t x = 0; x < pitch; ++x)
        {
            int val = std::stoi(tokens[x]);
            if (max_value != 255)
            {
                double n = val / static_cast<double>(max_value); // scale down to [0..1]
                val = static_cast<int>(n * 255);                 // scale up to [0..255]
            }

            dst[y * pitch + x] = assign_func(val);
        }
    }
}

static void load_binary(
        uint8_t*        dst,
        std::ifstream&  file,
        size_t          pitch,
        size_t          height,
        int             max_value
        )
{
    assert(max_value < 256);    // TODO: 16-bit

    file.read(reinterpret_cast<char*>(dst), pitch * height);

    if (max_value != 255)
    {
        double scale = (1.0 / max_value) * 255;

        size_t size = height * pitch;
        for (size_t i = 0; i < size; ++i)
        {
            dst[i] = static_cast<uint8_t>(dst[i] * scale);
        }
    }
}

static void save_ascii(
        std::ofstream&  file,
        uint8_t const*  src,
        size_t          pitch,
        size_t          height
        )
{
    for (size_t y = 0; y < height; ++y)
    {
        for (size_t x = 0; x < pitch; ++x)
        {
            auto c = std::to_string(src[y * pitch + x]);

            // padding
            for (size_t i = 0; i < 3 - c.length(); ++i)
            {
                file << ' ';
            }

            // write color component
            file << c;

            // whitespace
            if (x + 1 < pitch)
            {
                file << ' ';
            }
            else
            {
                file << '\n';
            }
        }
    }
}

static void save_binary(
        std::ofstream&  file,
        uint8_t const*  src,
        size_t          pitch,
        size_t          height
        )
{
    file.write(reinterpret_cast<char const*>(src), pitch * height);
}

//-------------------------------------------------------------------------------------------------
// pnm_image
//

pnm_image::pnm_image(size_t width, size_t height, pixel_format format, uint8_t const* data)
    : image_base(width, height, format, data)
{
}

bool pnm_image::load(std::string const& filename)
{
    enum format { P1 = 1, P2, P3, P4, P5, P6 };

    std::ifstream file(filename);

    std::string line;

    //
    // First line determines format:
    // P1: ASCII BW         | P4: binary BW
    // P2: ASCII gray scale | P5: binary gray scale
    // P3: ASCII RGB        | P6: binary RGB
    //
    std::getline(file, line);

    if (line.size() < 2 || line[0] != 'P' || line[1] < '0' || line[1] > '6')
    {
        std::cerr << "Invalid file format: " << line << '\n';
        return false;
    }

    format fmt = static_cast<format>(line[1] - '0');

    // Header
    int header[3]; // width, height, max. value
    int index = 0;

    bool is_bitmap = (fmt == P1 || fmt == P4);

    for (;;)
    {
        std::getline(file, line);

        if (line[0] == '#')
        {
            // Skip comments
            continue;
        }
        else
        {
            std::vector<std::string> tokens;
            boost::algorithm::split(
                    tokens,
                    line,
                    boost::algorithm::is_any_of(" \t")
                    );

            if (tokens.size() > 3)
            {
                std::cerr << "Invalid pnm file header\n";
                return false;
            }

            for (auto t : tokens)
            {
                header[index++] = std::stoi(t);

                if ((is_bitmap && index > 2) || (!is_bitmap && index > 3))
                {
                    std::cerr << "Invalid pnm file header\n";
                    return false;
                }
            }

            // BitMap: width and height read ==> break
            if (is_bitmap && index == 2)
            {
                break;
            }

            // GrayMap/PixMap: width, height and max_value read ==> break
            if (!is_bitmap && index == 3)
            {
                break;
            }
        }
    }

    if (header[0] <= 0 || header[1] <= 0)
    {
        std::cerr << "Invalid image dimensions: " << header[0] << ' ' << header[1] << '\n';
        return false;
    }

    width_  = static_cast<size_t>(header[0]);
    height_ = static_cast<size_t>(header[1]);
    int max_value = header[2];

    switch (fmt)
    {
    default:
        std::cerr << "Unsupported PNM image type: P" << std::to_string(static_cast<int>(fmt)) << '\n';
        width_  = 0;
        height_ = 0;
        format_ = PF_UNSPECIFIED;
        data_.resize(0);
        return false;

    case P1:
        format_ = PF_R8;
        data_.resize(width_ * height_);

        // black or white
        load_ascii(
                data_.data(),
                file,
                width_,
                height_,
                max_value,
                [](int val)
                    {
                        assert( val == 0 || val == 1);
                        return val ? 0U : 255U;
                    }
                );
        return true;

    case P2:
        format_ = PF_R8;
        data_.resize(width_ * height_);

        // single gray component
        load_ascii(
                data_.data(),
                file,
                width_,
                height_,
                max_value,
                [](int val)
                    {
                        return static_cast<uint8_t>(val);
                    }
                );
        return true;

    case P3:
        format_ = PF_RGB8;
        data_.resize(width_ * height_ * 3);

        // RGB color components
        load_ascii(
                data_.data(),
                file,
                width_ * 3,
                height_,
                max_value,
                [](int val)
                    {
                        return static_cast<uint8_t>(val);
                    }
                );
        return true;

    case P5:
        format_ = PF_R8;
        data_.resize(width_ * height_);

        load_binary(
                data_.data(),
                file,
                width_,
                height_,
                max_value
                );
        return true;

    case P6:
        format_ = PF_RGB8;
        data_.resize(width_ * height_ * 3);

        load_binary(
                data_.data(),
                file,
                width_ * 3,
                height_,
                max_value
                );
        return true;
    }

    return false;
}

bool pnm_image::save(std::string const& filename, image_base::save_options const& options)
{
    auto it = std::find_if(
            options.begin(),
            options.end(),
            [](save_option const& opt) { return opt.first == "binary"; }
            );

    if (it == options.end())
    {
        std::cerr << "Option \"binary\" is mandatory!\n";
        return false;
    }

    bool binary = boost::any_cast<bool>(it->second);

    std::ofstream file(filename);

    // TODO:                                   P1
    if (!binary && format_ == PF_R8)        // P2
    {
        file << "P2\n";
        file << width_ << ' ' << height_ << '\n';
        file << 255 << '\n';
        save_ascii(
                file,
                data_.data(),
                width_,
                height_
                );
        return true;
    }
    else if (!binary && format_ == PF_RGB8) // P3
    {
        file << "P3\n";
        file << width_ << ' ' << height_ << '\n';
        file << 255 << '\n';
        save_ascii(
                file,
                data_.data(),
                width_ * 3,
                height_
                );
        return true;
    }
    // TODO:                                   P4
    else if (binary && format_ == PF_R8)    // P5
    {
        file << "P5\n";
        file << width_ << ' ' << height_ << '\n';
        file << 255 << '\n';
        save_binary(
                file,
                data_.data(),
                width_,
                height_
                );
        return true;
    }
    else if (binary && format_ == PF_RGB8)  // P6
    {
        file << "P6\n";
        file << width_ << ' ' << height_ << '\n';
        file << 255 << '\n';
        save_binary(
                file,
                data_.data(),
                width_ * 3,
                height_
                );
        return true;
    }

    std::cerr << "Unsupported image format\n";
    return false;
}

} // visionaray
<|endoftext|>


<commit_before>// Copyright (c) 2019 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "quorums.h"
#include "quorums_chainlocks.h"
#include "quorums_signing.h"
#include "quorums_utils.h"

#include "chain.h"
#include "net_processing.h"
#include "scheduler.h"
#include "validation.h"

namespace llmq
{

static const std::string CLSIG_REQUESTID_PREFIX = "clsig";

CChainLocksHandler* chainLocksHandler;

std::string CChainLockSig::ToString() const
{
    return strprintf("CChainLockSig(nHeight=%d, blockHash=%s)", nHeight, blockHash.ToString());
}

CChainLocksHandler::CChainLocksHandler(CScheduler* _scheduler) :
    scheduler(_scheduler)
{
    quorumSigningManager->RegisterRecoveredSigsListener(this);
}

CChainLocksHandler::~CChainLocksHandler()
{
    quorumSigningManager->UnregisterRecoveredSigsListener(this);
}

bool CChainLocksHandler::AlreadyHave(const CInv& inv)
{
    LOCK(cs);
    return seenChainLocks.count(inv.hash) != 0;
}

bool CChainLocksHandler::GetChainLockByHash(const uint256& hash, llmq::CChainLockSig& ret)
{
    LOCK(cs);

    if (hash != bestChainLockHash) {
        // we only propagate the best one and ditch all the old ones
        return false;
    }

    ret = bestChainLock;
    return true;
}

void CChainLocksHandler::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
{
    if (strCommand == NetMsgType::CLSIG) {
        CChainLockSig clsig;
        vRecv >> clsig;

        auto hash = ::SerializeHash(clsig);

        {
            LOCK(cs_main);
            connman.RemoveAskFor(hash);
        }

        ProcessNewChainLock(pfrom->id, clsig, hash);
    }
}

void CChainLocksHandler::ProcessNewChainLock(NodeId from, const llmq::CChainLockSig& clsig, const uint256& hash)
{
    {
        LOCK(cs);
        if (!seenChainLocks.emplace(hash, GetTimeMillis()).second) {
            return;
        }

        if (bestChainLock.nHeight != -1 && clsig.nHeight <= bestChainLock.nHeight) {
            // no need to process/relay older CLSIGs
            return;
        }
    }

    uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, clsig.nHeight));
    uint256 msgHash = clsig.blockHash;
    if (!quorumSigningManager->VerifyRecoveredSig(Params().GetConsensus().llmqChainLocks, clsig.nHeight, requestId, msgHash, clsig.sig)) {
        LogPrintf("CChainLocksHandler::%s -- invalid CLSIG (%s), peer=%d\n", __func__, clsig.ToString(), from);
        if (from != -1) {
            LOCK(cs_main);
            Misbehaving(from, 100);
        }
        return;
    }

    {
        LOCK2(cs_main, cs);

        if (InternalHasConflictingChainLock(clsig.nHeight, clsig.blockHash)) {
            // This should not happen. If it happens, it means that a malicious entity controls a large part of the MN
            // network. In this case, we don't allow him to reorg older chainlocks.
            LogPrintf("CChainLocksHandler::%s -- new CLSIG (%s) tries to reorg previous CLSIG (%s), peer=%d\n",
                      __func__, clsig.ToString(), bestChainLock.ToString(), from);
            return;
        }

        bestChainLockHash = hash;
        bestChainLock = clsig;

        CInv inv(MSG_CLSIG, hash);
        g_connman->RelayInv(inv);

        auto blockIt = mapBlockIndex.find(clsig.blockHash);
        if (blockIt == mapBlockIndex.end()) {
            // we don't know the block/header for this CLSIG yet, so bail out for now
            // when the block or the header later comes in, we will enforce the correct chain
            return;
        }

        if (blockIt->second->nHeight != clsig.nHeight) {
            // Should not happen, same as the conflict check from above.
            LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n",
                    __func__, clsig.ToString(), blockIt->second->nHeight);
            return;
        }

        const CBlockIndex* pindex = blockIt->second;
        bestChainLockWithKnownBlock = bestChainLock;
        bestChainLockBlockIndex = pindex;
    }

    EnforceBestChainLock();

    LogPrintf("CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n",
              __func__, clsig.ToString(), from);
}

void CChainLocksHandler::AcceptedBlockHeader(const CBlockIndex* pindexNew)
{
    bool doEnforce = false;
    {
        LOCK2(cs_main, cs);

        if (pindexNew->GetBlockHash() == bestChainLock.blockHash) {
            LogPrintf("CChainLocksHandler::%s -- block header %s came in late, updating and enforcing\n", __func__, pindexNew->GetBlockHash().ToString());

            if (bestChainLock.nHeight != pindexNew->nHeight) {
                // Should not happen, same as the conflict check from ProcessNewChainLock.
                LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n",
                          __func__, bestChainLock.ToString(), pindexNew->nHeight);
                return;
            }

            bestChainLockBlockIndex = pindexNew;
            doEnforce = true;
        }
    }
    if (doEnforce) {
        EnforceBestChainLock();
    }
}

void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork)
{
    if (!fMasternodeMode) {
        return;
    }
    if (!pindexNew->pprev) {
        return;
    }

    // DIP8 defines a process called "Signing attempts" which should run before the CLSIG is finalized
    // To simplify the initial implementation, we skip this process and directly try to create a CLSIG
    // This will fail when multiple blocks compete, but we accept this for the initial implementation.
    // Later, we'll add the multiple attempts process.

    uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, pindexNew->nHeight));
    uint256 msgHash = pindexNew->GetBlockHash();

    {
        LOCK(cs);

        if (InternalHasConflictingChainLock(pindexNew->nHeight, pindexNew->GetBlockHash())) {
            if (!inInvalidate) {
                // we accepted this block when there was no lock yet, but now a conflicting lock appeared. Invalidate it.
                LogPrintf("CChainLocksHandler::%s -- conflicting lock after block was accepted, invalidating now\n",
                          __func__);
                ScheduleInvalidateBlock(pindexNew);
            }
            return;
        }

        if (bestChainLock.nHeight >= pindexNew->nHeight) {
            // already got the same CLSIG or a better one
            return;
        }

        if (pindexNew->nHeight == lastSignedHeight) {
            // already signed this one
            return;
        }
        lastSignedHeight = pindexNew->nHeight;
        lastSignedRequestId = requestId;
        lastSignedMsgHash = msgHash;
    }

    quorumSigningManager->AsyncSignIfMember(Params().GetConsensus().llmqChainLocks, requestId, msgHash);

    Cleanup();
}

// WARNING: cs_main and cs should not be held!
void CChainLocksHandler::EnforceBestChainLock()
{
    CChainLockSig clsig;
    const CBlockIndex* pindex;
    {
        LOCK(cs);
        clsig = bestChainLockWithKnownBlock;
        pindex = bestChainLockBlockIndex;
    }

    {
        LOCK(cs_main);

        // Go backwards through the chain referenced by clsig until we find a block that is part of the main chain.
        // For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig
        // and invalidate each of them.
        inInvalidate = true; // avoid unnecessary ScheduleInvalidateBlock calls inside UpdatedBlockTip
        while (pindex && !chainActive.Contains(pindex)) {
            // Invalidate all blocks that have the same prevBlockHash but are not equal to blockHash
            auto itp = mapPrevBlockIndex.equal_range(pindex->pprev->GetBlockHash());
            for (auto jt = itp.first; jt != itp.second; ++jt) {
                if (jt->second == pindex) {
                    continue;
                }
                LogPrintf("CChainLocksHandler::%s -- CLSIG (%s) invalidates block %s\n",
                          __func__, bestChainLockWithKnownBlock.ToString(), jt->second->GetBlockHash().ToString());
                DoInvalidateBlock(jt->second, false);
            }

            pindex = pindex->pprev;
        }
        inInvalidate = false;
    }

    CValidationState state;
    if (!ActivateBestChain(state, Params())) {
        LogPrintf("CChainLocksHandler::UpdatedBlockTip -- ActivateBestChain failed: %s\n", FormatStateMessage(state));
        // This should not have happened and we are in a state were it's not safe to continue anymore
        assert(false);
    }
}

void CChainLocksHandler::HandleNewRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
{
    CChainLockSig clsig;
    {
        LOCK(cs);

        if (recoveredSig.id != lastSignedRequestId || recoveredSig.msgHash != lastSignedMsgHash) {
            // this is not what we signed, so lets not create a CLSIG for it
            return;
        }
        if (bestChainLock.nHeight >= lastSignedHeight) {
            // already got the same or a better CLSIG through the CLSIG message
            return;
        }

        clsig.nHeight = lastSignedHeight;
        clsig.blockHash = lastSignedMsgHash;
        clsig.sig = recoveredSig.sig;
    }
    ProcessNewChainLock(-1, clsig, ::SerializeHash(clsig));
}

void CChainLocksHandler::ScheduleInvalidateBlock(const CBlockIndex* pindex)
{
    // Calls to InvalidateBlock and ActivateBestChain might result in re-invocation of the UpdatedBlockTip and other
    // signals, so we can't directly call it from signal handlers. We solve this by doing the call from the scheduler

    scheduler->scheduleFromNow([this, pindex]() {
        DoInvalidateBlock(pindex, true);
    }, 0);
}

// WARNING, do not hold cs while calling this method as we'll otherwise run into a deadlock
void CChainLocksHandler::DoInvalidateBlock(const CBlockIndex* pindex, bool activateBestChain)
{
    auto& params = Params();

    {
        LOCK(cs_main);

        // get the non-const pointer
        CBlockIndex* pindex2 = mapBlockIndex[pindex->GetBlockHash()];

        CValidationState state;
        if (!InvalidateBlock(state, params, pindex2)) {
            LogPrintf("CChainLocksHandler::UpdatedBlockTip -- InvalidateBlock failed: %s\n", FormatStateMessage(state));
            // This should not have happened and we are in a state were it's not safe to continue anymore
            assert(false);
        }
    }

    CValidationState state;
    if (activateBestChain && !ActivateBestChain(state, params)) {
        LogPrintf("CChainLocksHandler::UpdatedBlockTip -- ActivateBestChain failed: %s\n", FormatStateMessage(state));
        // This should not have happened and we are in a state were it's not safe to continue anymore
        assert(false);
    }
}

bool CChainLocksHandler::HasChainLock(int nHeight, const uint256& blockHash)
{
    LOCK(cs);
    return InternalHasChainLock(nHeight, blockHash);
}

bool CChainLocksHandler::InternalHasChainLock(int nHeight, const uint256& blockHash)
{
    AssertLockHeld(cs);

    if (!bestChainLockBlockIndex) {
        return false;
    }

    if (nHeight > bestChainLockBlockIndex->nHeight) {
        return false;
    }

    if (nHeight == bestChainLockBlockIndex->nHeight) {
        return blockHash == bestChainLockBlockIndex->GetBlockHash();
    }

    auto pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight);
    return pAncestor && pAncestor->GetBlockHash() == blockHash;
}

bool CChainLocksHandler::HasConflictingChainLock(int nHeight, const uint256& blockHash)
{
    LOCK(cs);
    return InternalHasConflictingChainLock(nHeight, blockHash);
}

bool CChainLocksHandler::InternalHasConflictingChainLock(int nHeight, const uint256& blockHash)
{
    AssertLockHeld(cs);

    if (!bestChainLockBlockIndex) {
        return false;
    }

    if (nHeight > bestChainLockBlockIndex->nHeight) {
        return false;
    }

    if (nHeight == bestChainLockBlockIndex->nHeight) {
        return blockHash != bestChainLockBlockIndex->GetBlockHash();
    }

    auto pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight);
    assert(pAncestor);
    return pAncestor->GetBlockHash() != blockHash;
}

void CChainLocksHandler::Cleanup()
{
    {
        LOCK(cs);
        if (GetTimeMillis() - lastCleanupTime < CLEANUP_INTERVAL) {
            return;
        }
    }

    LOCK2(cs_main, cs);

    for (auto it = seenChainLocks.begin(); it != seenChainLocks.end(); ) {
        if (GetTimeMillis() - it->second >= CLEANUP_SEEN_TIMEOUT) {
            it = seenChainLocks.erase(it);
        } else {
            ++it;
        }
    }

    lastCleanupTime = GetTimeMillis();
}

}
<commit_msg>Add SPORK_19_CHAINLOCKS_ENABLED<commit_after>// Copyright (c) 2019 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "quorums.h"
#include "quorums_chainlocks.h"
#include "quorums_signing.h"
#include "quorums_utils.h"

#include "chain.h"
#include "net_processing.h"
#include "scheduler.h"
#include "spork.h"
#include "validation.h"

namespace llmq
{

static const std::string CLSIG_REQUESTID_PREFIX = "clsig";

CChainLocksHandler* chainLocksHandler;

std::string CChainLockSig::ToString() const
{
    return strprintf("CChainLockSig(nHeight=%d, blockHash=%s)", nHeight, blockHash.ToString());
}

CChainLocksHandler::CChainLocksHandler(CScheduler* _scheduler) :
    scheduler(_scheduler)
{
    quorumSigningManager->RegisterRecoveredSigsListener(this);
}

CChainLocksHandler::~CChainLocksHandler()
{
    quorumSigningManager->UnregisterRecoveredSigsListener(this);
}

bool CChainLocksHandler::AlreadyHave(const CInv& inv)
{
    LOCK(cs);
    return seenChainLocks.count(inv.hash) != 0;
}

bool CChainLocksHandler::GetChainLockByHash(const uint256& hash, llmq::CChainLockSig& ret)
{
    LOCK(cs);

    if (hash != bestChainLockHash) {
        // we only propagate the best one and ditch all the old ones
        return false;
    }

    ret = bestChainLock;
    return true;
}

void CChainLocksHandler::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
{
    if (!sporkManager.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED)) {
        return;
    }

    if (strCommand == NetMsgType::CLSIG) {
        CChainLockSig clsig;
        vRecv >> clsig;

        auto hash = ::SerializeHash(clsig);

        {
            LOCK(cs_main);
            connman.RemoveAskFor(hash);
        }

        ProcessNewChainLock(pfrom->id, clsig, hash);
    }
}

void CChainLocksHandler::ProcessNewChainLock(NodeId from, const llmq::CChainLockSig& clsig, const uint256& hash)
{
    {
        LOCK(cs);
        if (!seenChainLocks.emplace(hash, GetTimeMillis()).second) {
            return;
        }

        if (bestChainLock.nHeight != -1 && clsig.nHeight <= bestChainLock.nHeight) {
            // no need to process/relay older CLSIGs
            return;
        }
    }

    uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, clsig.nHeight));
    uint256 msgHash = clsig.blockHash;
    if (!quorumSigningManager->VerifyRecoveredSig(Params().GetConsensus().llmqChainLocks, clsig.nHeight, requestId, msgHash, clsig.sig)) {
        LogPrintf("CChainLocksHandler::%s -- invalid CLSIG (%s), peer=%d\n", __func__, clsig.ToString(), from);
        if (from != -1) {
            LOCK(cs_main);
            Misbehaving(from, 100);
        }
        return;
    }

    {
        LOCK2(cs_main, cs);

        if (InternalHasConflictingChainLock(clsig.nHeight, clsig.blockHash)) {
            // This should not happen. If it happens, it means that a malicious entity controls a large part of the MN
            // network. In this case, we don't allow him to reorg older chainlocks.
            LogPrintf("CChainLocksHandler::%s -- new CLSIG (%s) tries to reorg previous CLSIG (%s), peer=%d\n",
                      __func__, clsig.ToString(), bestChainLock.ToString(), from);
            return;
        }

        bestChainLockHash = hash;
        bestChainLock = clsig;

        CInv inv(MSG_CLSIG, hash);
        g_connman->RelayInv(inv);

        auto blockIt = mapBlockIndex.find(clsig.blockHash);
        if (blockIt == mapBlockIndex.end()) {
            // we don't know the block/header for this CLSIG yet, so bail out for now
            // when the block or the header later comes in, we will enforce the correct chain
            return;
        }

        if (blockIt->second->nHeight != clsig.nHeight) {
            // Should not happen, same as the conflict check from above.
            LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n",
                    __func__, clsig.ToString(), blockIt->second->nHeight);
            return;
        }

        const CBlockIndex* pindex = blockIt->second;
        bestChainLockWithKnownBlock = bestChainLock;
        bestChainLockBlockIndex = pindex;
    }

    EnforceBestChainLock();

    LogPrintf("CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n",
              __func__, clsig.ToString(), from);
}

void CChainLocksHandler::AcceptedBlockHeader(const CBlockIndex* pindexNew)
{
    bool doEnforce = false;
    {
        LOCK2(cs_main, cs);

        if (pindexNew->GetBlockHash() == bestChainLock.blockHash) {
            LogPrintf("CChainLocksHandler::%s -- block header %s came in late, updating and enforcing\n", __func__, pindexNew->GetBlockHash().ToString());

            if (bestChainLock.nHeight != pindexNew->nHeight) {
                // Should not happen, same as the conflict check from ProcessNewChainLock.
                LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n",
                          __func__, bestChainLock.ToString(), pindexNew->nHeight);
                return;
            }

            bestChainLockBlockIndex = pindexNew;
            doEnforce = true;
        }
    }
    if (doEnforce) {
        EnforceBestChainLock();
    }
}

void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork)
{
    if (!fMasternodeMode) {
        return;
    }
    if (!pindexNew->pprev) {
        return;
    }
    if (!sporkManager.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED)) {
        return;
    }

    // DIP8 defines a process called "Signing attempts" which should run before the CLSIG is finalized
    // To simplify the initial implementation, we skip this process and directly try to create a CLSIG
    // This will fail when multiple blocks compete, but we accept this for the initial implementation.
    // Later, we'll add the multiple attempts process.

    uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, pindexNew->nHeight));
    uint256 msgHash = pindexNew->GetBlockHash();

    {
        LOCK(cs);

        if (InternalHasConflictingChainLock(pindexNew->nHeight, pindexNew->GetBlockHash())) {
            if (!inInvalidate) {
                // we accepted this block when there was no lock yet, but now a conflicting lock appeared. Invalidate it.
                LogPrintf("CChainLocksHandler::%s -- conflicting lock after block was accepted, invalidating now\n",
                          __func__);
                ScheduleInvalidateBlock(pindexNew);
            }
            return;
        }

        if (bestChainLock.nHeight >= pindexNew->nHeight) {
            // already got the same CLSIG or a better one
            return;
        }

        if (pindexNew->nHeight == lastSignedHeight) {
            // already signed this one
            return;
        }
        lastSignedHeight = pindexNew->nHeight;
        lastSignedRequestId = requestId;
        lastSignedMsgHash = msgHash;
    }

    quorumSigningManager->AsyncSignIfMember(Params().GetConsensus().llmqChainLocks, requestId, msgHash);

    Cleanup();
}

// WARNING: cs_main and cs should not be held!
void CChainLocksHandler::EnforceBestChainLock()
{
    CChainLockSig clsig;
    const CBlockIndex* pindex;
    {
        LOCK(cs);
        clsig = bestChainLockWithKnownBlock;
        pindex = bestChainLockBlockIndex;
    }

    {
        LOCK(cs_main);

        // Go backwards through the chain referenced by clsig until we find a block that is part of the main chain.
        // For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig
        // and invalidate each of them.
        inInvalidate = true; // avoid unnecessary ScheduleInvalidateBlock calls inside UpdatedBlockTip
        while (pindex && !chainActive.Contains(pindex)) {
            // Invalidate all blocks that have the same prevBlockHash but are not equal to blockHash
            auto itp = mapPrevBlockIndex.equal_range(pindex->pprev->GetBlockHash());
            for (auto jt = itp.first; jt != itp.second; ++jt) {
                if (jt->second == pindex) {
                    continue;
                }
                LogPrintf("CChainLocksHandler::%s -- CLSIG (%s) invalidates block %s\n",
                          __func__, bestChainLockWithKnownBlock.ToString(), jt->second->GetBlockHash().ToString());
                DoInvalidateBlock(jt->second, false);
            }

            pindex = pindex->pprev;
        }
        inInvalidate = false;
    }

    CValidationState state;
    if (!ActivateBestChain(state, Params())) {
        LogPrintf("CChainLocksHandler::UpdatedBlockTip -- ActivateBestChain failed: %s\n", FormatStateMessage(state));
        // This should not have happened and we are in a state were it's not safe to continue anymore
        assert(false);
    }
}

void CChainLocksHandler::HandleNewRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
{
    if (!sporkManager.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED)) {
        return;
    }

    CChainLockSig clsig;
    {
        LOCK(cs);

        if (recoveredSig.id != lastSignedRequestId || recoveredSig.msgHash != lastSignedMsgHash) {
            // this is not what we signed, so lets not create a CLSIG for it
            return;
        }
        if (bestChainLock.nHeight >= lastSignedHeight) {
            // already got the same or a better CLSIG through the CLSIG message
            return;
        }

        clsig.nHeight = lastSignedHeight;
        clsig.blockHash = lastSignedMsgHash;
        clsig.sig = recoveredSig.sig;
    }
    ProcessNewChainLock(-1, clsig, ::SerializeHash(clsig));
}

void CChainLocksHandler::ScheduleInvalidateBlock(const CBlockIndex* pindex)
{
    // Calls to InvalidateBlock and ActivateBestChain might result in re-invocation of the UpdatedBlockTip and other
    // signals, so we can't directly call it from signal handlers. We solve this by doing the call from the scheduler

    scheduler->scheduleFromNow([this, pindex]() {
        DoInvalidateBlock(pindex, true);
    }, 0);
}

// WARNING, do not hold cs while calling this method as we'll otherwise run into a deadlock
void CChainLocksHandler::DoInvalidateBlock(const CBlockIndex* pindex, bool activateBestChain)
{
    auto& params = Params();

    {
        LOCK(cs_main);

        // get the non-const pointer
        CBlockIndex* pindex2 = mapBlockIndex[pindex->GetBlockHash()];

        CValidationState state;
        if (!InvalidateBlock(state, params, pindex2)) {
            LogPrintf("CChainLocksHandler::UpdatedBlockTip -- InvalidateBlock failed: %s\n", FormatStateMessage(state));
            // This should not have happened and we are in a state were it's not safe to continue anymore
            assert(false);
        }
    }

    CValidationState state;
    if (activateBestChain && !ActivateBestChain(state, params)) {
        LogPrintf("CChainLocksHandler::UpdatedBlockTip -- ActivateBestChain failed: %s\n", FormatStateMessage(state));
        // This should not have happened and we are in a state were it's not safe to continue anymore
        assert(false);
    }
}

bool CChainLocksHandler::HasChainLock(int nHeight, const uint256& blockHash)
{
    if (!sporkManager.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED)) {
        return false;
    }

    LOCK(cs);
    return InternalHasChainLock(nHeight, blockHash);
}

bool CChainLocksHandler::InternalHasChainLock(int nHeight, const uint256& blockHash)
{
    AssertLockHeld(cs);

    if (!bestChainLockBlockIndex) {
        return false;
    }

    if (nHeight > bestChainLockBlockIndex->nHeight) {
        return false;
    }

    if (nHeight == bestChainLockBlockIndex->nHeight) {
        return blockHash == bestChainLockBlockIndex->GetBlockHash();
    }

    auto pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight);
    return pAncestor && pAncestor->GetBlockHash() == blockHash;
}

bool CChainLocksHandler::HasConflictingChainLock(int nHeight, const uint256& blockHash)
{
    if (!sporkManager.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED)) {
        return false;
    }

    LOCK(cs);
    return InternalHasConflictingChainLock(nHeight, blockHash);
}

bool CChainLocksHandler::InternalHasConflictingChainLock(int nHeight, const uint256& blockHash)
{
    AssertLockHeld(cs);

    if (!bestChainLockBlockIndex) {
        return false;
    }

    if (nHeight > bestChainLockBlockIndex->nHeight) {
        return false;
    }

    if (nHeight == bestChainLockBlockIndex->nHeight) {
        return blockHash != bestChainLockBlockIndex->GetBlockHash();
    }

    auto pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight);
    assert(pAncestor);
    return pAncestor->GetBlockHash() != blockHash;
}

void CChainLocksHandler::Cleanup()
{
    {
        LOCK(cs);
        if (GetTimeMillis() - lastCleanupTime < CLEANUP_INTERVAL) {
            return;
        }
    }

    LOCK2(cs_main, cs);

    for (auto it = seenChainLocks.begin(); it != seenChainLocks.end(); ) {
        if (GetTimeMillis() - it->second >= CLEANUP_SEEN_TIMEOUT) {
            it = seenChainLocks.erase(it);
        } else {
            ++it;
        }
    }

    lastCleanupTime = GetTimeMillis();
}

}
<|endoftext|>


<commit_before>#include <simdee/simd_types/avx.hpp>
#include <simdee/util/allocator.hpp>

#include <limits>
#include <vector>
#include <random>
#include <chrono>
#include <iostream>
#include <functional>

const char* const hline = "===============================================================================\n";

auto now = []() {
    return std::chrono::high_resolution_clock::now();
};

template <typename Duration>
double to_ms(Duration dur) {
    return std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count() / 1.e6;
};

double benchmark_ms(std::function<void()> func) {
    double best = std::numeric_limits<double>::infinity();
    for (int i = 0; i < 24; i++) {
        auto tp1 = now();
        func();
        auto tp2 = now();
        double time = to_ms(tp2 - tp1);
        best = std::min(best, time);
    }
    return best;
}

int main() {
    std::cout << hline << "Benchmark: Ray-box intersection\n";

    // allocate data
    struct RayBoxData1 {
        float minx, miny, minz, maxx, maxy, maxz;
    };
    struct RayBoxData8 {
        float minx[8], miny[8], minz[8], maxx[8], maxy[8], maxz[8];
    };
    struct RayBoxData8S {
        sd::avxf::storage_t minx, miny, minz, maxx, maxy, maxz;
    };
    const std::size_t dataSize8 = 1024 * 1024;
    const std::size_t dataSize1 = 8 * dataSize8;
    using vec8 = std::vector<RayBoxData8, sd::allocator<RayBoxData8, sizeof(__m256)>>;
    using vec1 = std::vector<RayBoxData1>;
    using vec8S = std::vector<RayBoxData8S, sd::allocator<RayBoxData8, sizeof(__m256)>>;
    vec8 data8(dataSize8);
    vec1 data1(dataSize1);
    const vec8S& data8S = reinterpret_cast<const vec8S&>(data8);
    enum class Result : char { fail = 13, win = 42 };
    std::vector<Result> resultsNonSimd(dataSize1);
    std::vector<Result> resultsHandSimd(dataSize1);
    std::vector<Result> resultsSimdee(dataSize1);

    // fill data
    std::minstd_rand re(0x8a7ac012);
    std::normal_distribution<float> dist(0, 1);
    auto fill = [&dist, &re](float* data, std::size_t num) {
        for (size_t i = 0; i < num; ++i) {
            *(data++) = dist(re);
        }
    };
    const std::size_t numFloats = (sizeof(RayBoxData1) / sizeof(float)) * dataSize1;
    fill((float*)(data8.data()), numFloats);

    {
        RayBoxData1* ptr = data1.data();
        for (auto i = 0U; i < dataSize8; ++i) {
            const auto& el = data8[i];
            for (int j = 0; j < 8; ++j) {
                ptr->minx = el.minx[j];
                ptr->miny = el.miny[j];
                ptr->minz = el.minz[j];
                ptr->maxx = el.maxx[j];
                ptr->maxy = el.maxy[j];
                ptr->maxz = el.maxz[j];
                ptr++;
            }
        }
    }

    // common pre-calculations
    auto gamma = [](int n) {
        float eps2 = static_cast<float>(std::numeric_limits<float>::epsilon() * 0.5);
        return (n * eps2) / (1 - n * eps2);
    };
    struct Vec3f { float x, y, z; };
    const float robustFactor = 1 + 2 * gamma(3);
    const Vec3f invDir = { dist(re), dist(re), dist(re) };
    const Vec3f rayOrigin = { dist(re), dist(re), dist(re) };
    const float rayTMax = 100.f;
    const int dirIsNeg[3] = { (dist(re) > 0) ? 1 : 0, (dist(re) > 0) ? 1 : 0, (dist(re) > 0) ? 1 : 0 };

    // non-SIMD implementation (for correctness checking)
    auto nonSimd = [&]() {
        auto resIt = resultsNonSimd.begin();

        for (const auto& elem : data1) {
            float tmin = ((dirIsNeg[0] ? elem.maxx : elem.minx) - rayOrigin.x) * invDir.x;
            float tmax = ((dirIsNeg[0] ? elem.minx : elem.maxx) - rayOrigin.x) * invDir.x;
            float tminy = ((dirIsNeg[1] ? elem.maxy : elem.miny) - rayOrigin.y) * invDir.y;
            float tmaxy = ((dirIsNeg[1] ? elem.miny : elem.maxy) - rayOrigin.y) * invDir.y;
            tmax *= robustFactor;
            tmaxy *= robustFactor;
            if (tmin > tmaxy || tminy > tmax) {
                *(resIt++) = Result::fail;
                continue;
            }
            if (tminy > tmin) tmin = tminy;
            if (tmaxy < tmax) tmax = tmaxy;
            float tminz = ((dirIsNeg[2] ? elem.maxz : elem.minz) - rayOrigin.z) * invDir.z;
            float tmaxz = ((dirIsNeg[2] ? elem.minz : elem.maxz) - rayOrigin.z) * invDir.z;
            tmaxz *= robustFactor;
            if (tmin > tmaxz || tminz > tmax) {
                *(resIt++) = Result::fail;
                continue;
            }
            if (tminz > tmin) tmin = tminz;
            if (tmaxz < tmax) tmax = tmaxz;
            *(resIt++) = ((tmin < rayTMax) && (tmax > 0)) ? Result::win : Result::fail;
        }
    };

    // SIMD implementation by hand
    auto handSimd = [&]() {
        auto resIt = resultsHandSimd.begin();

        for (const auto& elem : data8) {
            uint32_t fail;
            __m256 tmin, tmax;
            {
                __m256 minx = _mm256_load_ps(elem.minx);
                __m256 maxx = _mm256_load_ps(elem.maxx);
                __m256 idirx = _mm256_broadcast_ss(&invDir.x);
                __m256 rayox = _mm256_broadcast_ss(&rayOrigin.x);
                tmin = _mm256_mul_ps(_mm256_sub_ps(dirIsNeg[0] ? maxx : minx, rayox), idirx);
                tmax = _mm256_mul_ps(_mm256_sub_ps(dirIsNeg[0] ? minx : maxx, rayox), idirx);
            }

            {
                __m256 tminy, tmaxy;
                {
                    __m256 miny = _mm256_load_ps(elem.miny);
                    __m256 maxy = _mm256_load_ps(elem.maxy);
                    __m256 idiry = _mm256_broadcast_ss(&invDir.y);
                    __m256 rayoy = _mm256_broadcast_ss(&rayOrigin.y);
                    tminy = _mm256_mul_ps(_mm256_sub_ps(dirIsNeg[1] ? maxy : miny, rayoy), idiry);
                    tmaxy = _mm256_mul_ps(_mm256_sub_ps(dirIsNeg[1] ? miny : maxy, rayoy), idiry);
                }

                __m256 factor = _mm256_broadcast_ss(&robustFactor);
                tmax = _mm256_mul_ps(tmax, factor);
                tmaxy = _mm256_mul_ps(tmaxy, factor);

                {
                    __m256 failcond = _mm256_or_ps(_mm256_cmp_ps(tmin, tmaxy, _CMP_GT_OQ), _mm256_cmp_ps(tminy, tmax, _CMP_GT_OQ));
                    fail = uint32_t(_mm256_movemask_ps(failcond));

                    if (fail == 0xFF) {
                        for (int i = 0; i < 8; ++i) {
                            *(resIt++) = Result::fail;
                        }
                        continue;
                    }
                }

                tmin = _mm256_blendv_ps(tmin, tminy, _mm256_cmp_ps(tminy, tmin, _CMP_GT_OQ));
                tmax = _mm256_blendv_ps(tmax, tmaxy, _mm256_cmp_ps(tmaxy, tmax, _CMP_LT_OQ));
            }

            {
                __m256 tminz, tmaxz;
                {
                    __m256 minz = _mm256_load_ps(elem.minz);
                    __m256 maxz = _mm256_load_ps(elem.maxz);
                    __m256 idirz = _mm256_broadcast_ss(&invDir.z);
                    __m256 rayoz = _mm256_broadcast_ss(&rayOrigin.z);
                    tminz = _mm256_mul_ps(_mm256_sub_ps(dirIsNeg[2] ? maxz : minz, rayoz), idirz);
                    tmaxz = _mm256_mul_ps(_mm256_sub_ps(dirIsNeg[2] ? minz : maxz, rayoz), idirz);
                }

                __m256 factor = _mm256_broadcast_ss(&robustFactor);
                tmaxz = _mm256_mul_ps(tmaxz, factor);

                {
                    __m256 failcond = _mm256_or_ps(_mm256_cmp_ps(tmin, tmaxz, _CMP_GT_OQ), _mm256_cmp_ps(tminz, tmax, _CMP_GT_OQ));
                    fail = fail | uint32_t(_mm256_movemask_ps(failcond));

                    if (fail == 0xFF) {
                        for (int i = 0; i < 8; ++i) {
                            *(resIt++) = Result::fail;
                        }
                        continue;
                    }
                }

                tmin = _mm256_blendv_ps(tmin, tminz, _mm256_cmp_ps(tminz, tmin, _CMP_GT_OQ));
                tmax = _mm256_blendv_ps(tmax, tmaxz, _mm256_cmp_ps(tmaxz, tmax, _CMP_LT_OQ));
            }

            __m256 wincond = _mm256_and_ps(_mm256_cmp_ps(tmin, _mm256_broadcast_ss(&rayTMax), _CMP_LT_OQ), _mm256_cmp_ps(tmax, _mm256_setzero_ps(), _CMP_GT_OQ));
            uint32_t win = ~fail & uint32_t(_mm256_movemask_ps(wincond));

            for (int i = 0; i < 8; ++i) {
                *(resIt++) = ((win >> i) & 1) ? Result::win : Result::fail;
            }
        }
    };

    // implementation using Simdee
    auto simdee = [&]() {
        auto resIt = resultsSimdee.begin();

        for (const auto& elem : data8S) {
            auto tmin = ((dirIsNeg[0] ? elem.maxx : elem.minx) - rayOrigin.x) * invDir.x;
            auto tmax = ((dirIsNeg[0] ? elem.minx : elem.maxx) - rayOrigin.x) * invDir.x;
            auto tminy = ((dirIsNeg[1] ? elem.maxy : elem.miny) - rayOrigin.y) * invDir.y;
            auto tmaxy = ((dirIsNeg[1] ? elem.miny : elem.maxy) - rayOrigin.y) * invDir.y;

            sd::avxf factor(robustFactor);
            tmax *= factor;
            tmaxy *= factor;
            auto fail = mask((tmin > tmaxy) || (tminy > tmax));

            if (all(fail)) {
                for (int i = 0; i < 8; ++i) {
                    *(resIt++) = Result::fail;
                }
                continue;
            }

            tmin = cond(tminy > tmin, tminy, tmin);
            tmax = cond(tmaxy < tmax, tmaxy, tmax);
            auto tminz = ((dirIsNeg[2] ? elem.maxz : elem.minz) - rayOrigin.z) * invDir.z;
            auto tmaxz = ((dirIsNeg[2] ? elem.minz : elem.maxz) - rayOrigin.z) * invDir.z;
            tmaxz *= factor;
            fail |= mask((tmin > tmaxz) || (tminz > tmax));

            if (all(fail)) {
                for (int i = 0; i < 8; ++i) {
                    *(resIt++) = Result::fail;
                }
                continue;
            }

            tmin = cond(tminz > tmin, tminz, tmin);
            tmax = cond(tmaxz < tmax, tmaxz, tmax);
            auto win = ~fail & mask((tmin < rayTMax) && (tmax > sd::zero()));

            for (int i = 0; i < 8; ++i) {
                *(resIt++) = win[i] ? Result::win : Result::fail;
            }
        }
    };

    // check performance
    std::cout << "non-SIMD: "  << benchmark_ms(nonSimd) << " ms\n";
    std::cout << "hand SIMD: " << benchmark_ms(handSimd) << " ms\n";
    std::cout << "Simdee: "    << benchmark_ms(simdee) << " ms\n";

    // check correctness
    if (resultsNonSimd != resultsHandSimd) std::cerr << "hand SIMD results incorrect\n";
    if (resultsNonSimd != resultsSimdee) std::cerr << "Simdee results incorrect\n";
}
<commit_msg>Enable benchmark without AVX2<commit_after>#define SIMDEE_NEED_INT 0
#include <simdee/simd_types/avx.hpp>
#include <simdee/util/allocator.hpp>

#include <limits>
#include <vector>
#include <random>
#include <chrono>
#include <iostream>
#include <functional>

const char* const hline = "===============================================================================\n";

auto now = []() {
    return std::chrono::high_resolution_clock::now();
};

template <typename Duration>
double to_ms(Duration dur) {
    return std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count() / 1.e6;
};

double benchmark_ms(std::function<void()> func) {
    double best = std::numeric_limits<double>::infinity();
    for (int i = 0; i < 24; i++) {
        auto tp1 = now();
        func();
        auto tp2 = now();
        double time = to_ms(tp2 - tp1);
        best = std::min(best, time);
    }
    return best;
}

int main() {
    std::cout << hline << "Benchmark: Ray-box intersection\n";

    // allocate data
    struct RayBoxData1 {
        float minx, miny, minz, maxx, maxy, maxz;
    };
    struct RayBoxData8 {
        float minx[8], miny[8], minz[8], maxx[8], maxy[8], maxz[8];
    };
    struct RayBoxData8S {
        sd::avxf::storage_t minx, miny, minz, maxx, maxy, maxz;
    };
    const std::size_t dataSize8 = 1024 * 1024;
    const std::size_t dataSize1 = 8 * dataSize8;
    using vec8 = std::vector<RayBoxData8, sd::allocator<RayBoxData8, sizeof(__m256)>>;
    using vec1 = std::vector<RayBoxData1>;
    using vec8S = std::vector<RayBoxData8S, sd::allocator<RayBoxData8, sizeof(__m256)>>;
    vec8 data8(dataSize8);
    vec1 data1(dataSize1);
    const vec8S& data8S = reinterpret_cast<const vec8S&>(data8);
    enum class Result : char { fail = 13, win = 42 };
    std::vector<Result> resultsNonSimd(dataSize1);
    std::vector<Result> resultsHandSimd(dataSize1);
    std::vector<Result> resultsSimdee(dataSize1);

    // fill data
    std::minstd_rand re(0x8a7ac012);
    std::normal_distribution<float> dist(0, 1);
    auto fill = [&dist, &re](float* data, std::size_t num) {
        for (size_t i = 0; i < num; ++i) {
            *(data++) = dist(re);
        }
    };
    const std::size_t numFloats = (sizeof(RayBoxData1) / sizeof(float)) * dataSize1;
    fill((float*)(data8.data()), numFloats);

    {
        RayBoxData1* ptr = data1.data();
        for (auto i = 0U; i < dataSize8; ++i) {
            const auto& el = data8[i];
            for (int j = 0; j < 8; ++j) {
                ptr->minx = el.minx[j];
                ptr->miny = el.miny[j];
                ptr->minz = el.minz[j];
                ptr->maxx = el.maxx[j];
                ptr->maxy = el.maxy[j];
                ptr->maxz = el.maxz[j];
                ptr++;
            }
        }
    }

    // common pre-calculations
    auto gamma = [](int n) {
        float eps2 = static_cast<float>(std::numeric_limits<float>::epsilon() * 0.5);
        return (n * eps2) / (1 - n * eps2);
    };
    struct Vec3f { float x, y, z; };
    const float robustFactor = 1 + 2 * gamma(3);
    const Vec3f invDir = { dist(re), dist(re), dist(re) };
    const Vec3f rayOrigin = { dist(re), dist(re), dist(re) };
    const float rayTMax = 100.f;
    const int dirIsNeg[3] = { (dist(re) > 0) ? 1 : 0, (dist(re) > 0) ? 1 : 0, (dist(re) > 0) ? 1 : 0 };

    // non-SIMD implementation (for correctness checking)
    auto nonSimd = [&]() {
        auto resIt = resultsNonSimd.begin();

        for (const auto& elem : data1) {
            float tmin = ((dirIsNeg[0] ? elem.maxx : elem.minx) - rayOrigin.x) * invDir.x;
            float tmax = ((dirIsNeg[0] ? elem.minx : elem.maxx) - rayOrigin.x) * invDir.x;
            float tminy = ((dirIsNeg[1] ? elem.maxy : elem.miny) - rayOrigin.y) * invDir.y;
            float tmaxy = ((dirIsNeg[1] ? elem.miny : elem.maxy) - rayOrigin.y) * invDir.y;
            tmax *= robustFactor;
            tmaxy *= robustFactor;
            if (tmin > tmaxy || tminy > tmax) {
                *(resIt++) = Result::fail;
                continue;
            }
            if (tminy > tmin) tmin = tminy;
            if (tmaxy < tmax) tmax = tmaxy;
            float tminz = ((dirIsNeg[2] ? elem.maxz : elem.minz) - rayOrigin.z) * invDir.z;
            float tmaxz = ((dirIsNeg[2] ? elem.minz : elem.maxz) - rayOrigin.z) * invDir.z;
            tmaxz *= robustFactor;
            if (tmin > tmaxz || tminz > tmax) {
                *(resIt++) = Result::fail;
                continue;
            }
            if (tminz > tmin) tmin = tminz;
            if (tmaxz < tmax) tmax = tmaxz;
            *(resIt++) = ((tmin < rayTMax) && (tmax > 0)) ? Result::win : Result::fail;
        }
    };

    // SIMD implementation by hand
    auto handSimd = [&]() {
        auto resIt = resultsHandSimd.begin();

        for (const auto& elem : data8) {
            uint32_t fail;
            __m256 tmin, tmax;
            {
                __m256 minx = _mm256_load_ps(elem.minx);
                __m256 maxx = _mm256_load_ps(elem.maxx);
                __m256 idirx = _mm256_broadcast_ss(&invDir.x);
                __m256 rayox = _mm256_broadcast_ss(&rayOrigin.x);
                tmin = _mm256_mul_ps(_mm256_sub_ps(dirIsNeg[0] ? maxx : minx, rayox), idirx);
                tmax = _mm256_mul_ps(_mm256_sub_ps(dirIsNeg[0] ? minx : maxx, rayox), idirx);
            }

            {
                __m256 tminy, tmaxy;
                {
                    __m256 miny = _mm256_load_ps(elem.miny);
                    __m256 maxy = _mm256_load_ps(elem.maxy);
                    __m256 idiry = _mm256_broadcast_ss(&invDir.y);
                    __m256 rayoy = _mm256_broadcast_ss(&rayOrigin.y);
                    tminy = _mm256_mul_ps(_mm256_sub_ps(dirIsNeg[1] ? maxy : miny, rayoy), idiry);
                    tmaxy = _mm256_mul_ps(_mm256_sub_ps(dirIsNeg[1] ? miny : maxy, rayoy), idiry);
                }

                __m256 factor = _mm256_broadcast_ss(&robustFactor);
                tmax = _mm256_mul_ps(tmax, factor);
                tmaxy = _mm256_mul_ps(tmaxy, factor);

                {
                    __m256 failcond = _mm256_or_ps(_mm256_cmp_ps(tmin, tmaxy, _CMP_GT_OQ), _mm256_cmp_ps(tminy, tmax, _CMP_GT_OQ));
                    fail = uint32_t(_mm256_movemask_ps(failcond));

                    if (fail == 0xFF) {
                        for (int i = 0; i < 8; ++i) {
                            *(resIt++) = Result::fail;
                        }
                        continue;
                    }
                }

                tmin = _mm256_blendv_ps(tmin, tminy, _mm256_cmp_ps(tminy, tmin, _CMP_GT_OQ));
                tmax = _mm256_blendv_ps(tmax, tmaxy, _mm256_cmp_ps(tmaxy, tmax, _CMP_LT_OQ));
            }

            {
                __m256 tminz, tmaxz;
                {
                    __m256 minz = _mm256_load_ps(elem.minz);
                    __m256 maxz = _mm256_load_ps(elem.maxz);
                    __m256 idirz = _mm256_broadcast_ss(&invDir.z);
                    __m256 rayoz = _mm256_broadcast_ss(&rayOrigin.z);
                    tminz = _mm256_mul_ps(_mm256_sub_ps(dirIsNeg[2] ? maxz : minz, rayoz), idirz);
                    tmaxz = _mm256_mul_ps(_mm256_sub_ps(dirIsNeg[2] ? minz : maxz, rayoz), idirz);
                }

                __m256 factor = _mm256_broadcast_ss(&robustFactor);
                tmaxz = _mm256_mul_ps(tmaxz, factor);

                {
                    __m256 failcond = _mm256_or_ps(_mm256_cmp_ps(tmin, tmaxz, _CMP_GT_OQ), _mm256_cmp_ps(tminz, tmax, _CMP_GT_OQ));
                    fail = fail | uint32_t(_mm256_movemask_ps(failcond));

                    if (fail == 0xFF) {
                        for (int i = 0; i < 8; ++i) {
                            *(resIt++) = Result::fail;
                        }
                        continue;
                    }
                }

                tmin = _mm256_blendv_ps(tmin, tminz, _mm256_cmp_ps(tminz, tmin, _CMP_GT_OQ));
                tmax = _mm256_blendv_ps(tmax, tmaxz, _mm256_cmp_ps(tmaxz, tmax, _CMP_LT_OQ));
            }

            __m256 wincond = _mm256_and_ps(_mm256_cmp_ps(tmin, _mm256_broadcast_ss(&rayTMax), _CMP_LT_OQ), _mm256_cmp_ps(tmax, _mm256_setzero_ps(), _CMP_GT_OQ));
            uint32_t win = ~fail & uint32_t(_mm256_movemask_ps(wincond));

            for (int i = 0; i < 8; ++i) {
                *(resIt++) = ((win >> i) & 1) ? Result::win : Result::fail;
            }
        }
    };

    // implementation using Simdee
    auto simdee = [&]() {
        auto resIt = resultsSimdee.begin();

        for (const auto& elem : data8S) {
            auto tmin = ((dirIsNeg[0] ? elem.maxx : elem.minx) - rayOrigin.x) * invDir.x;
            auto tmax = ((dirIsNeg[0] ? elem.minx : elem.maxx) - rayOrigin.x) * invDir.x;
            auto tminy = ((dirIsNeg[1] ? elem.maxy : elem.miny) - rayOrigin.y) * invDir.y;
            auto tmaxy = ((dirIsNeg[1] ? elem.miny : elem.maxy) - rayOrigin.y) * invDir.y;

            sd::avxf factor(robustFactor);
            tmax *= factor;
            tmaxy *= factor;
            auto fail = mask((tmin > tmaxy) || (tminy > tmax));

            if (all(fail)) {
                for (int i = 0; i < 8; ++i) {
                    *(resIt++) = Result::fail;
                }
                continue;
            }

            tmin = cond(tminy > tmin, tminy, tmin);
            tmax = cond(tmaxy < tmax, tmaxy, tmax);
            auto tminz = ((dirIsNeg[2] ? elem.maxz : elem.minz) - rayOrigin.z) * invDir.z;
            auto tmaxz = ((dirIsNeg[2] ? elem.minz : elem.maxz) - rayOrigin.z) * invDir.z;
            tmaxz *= factor;
            fail |= mask((tmin > tmaxz) || (tminz > tmax));

            if (all(fail)) {
                for (int i = 0; i < 8; ++i) {
                    *(resIt++) = Result::fail;
                }
                continue;
            }

            tmin = cond(tminz > tmin, tminz, tmin);
            tmax = cond(tmaxz < tmax, tmaxz, tmax);
            auto win = ~fail & mask((tmin < rayTMax) && (tmax > sd::zero()));

            for (int i = 0; i < 8; ++i) {
                *(resIt++) = win[i] ? Result::win : Result::fail;
            }
        }
    };

    // check performance
    std::cout << "non-SIMD: "  << benchmark_ms(nonSimd) << " ms\n";
    std::cout << "hand SIMD: " << benchmark_ms(handSimd) << " ms\n";
    std::cout << "Simdee: "    << benchmark_ms(simdee) << " ms\n";

    // check correctness
    if (resultsNonSimd != resultsHandSimd) std::cerr << "hand SIMD results incorrect\n";
    if (resultsNonSimd != resultsSimdee) std::cerr << "Simdee results incorrect\n";
}
<|endoftext|>