<commit_before>#include <babylon/misc/file_tools.h>

#define STB_IMAGE_IMPLEMENTATION
#if defined(__GNUC__) || defined(__MINGW32__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual"
#pragma GCC diagnostic ignored "-Wconversion"
#if __GNUC__ > 5
#pragma GCC diagnostic ignored "-Wmisleading-indentation"
#pragma GCC diagnostic ignored "-Wshift-negative-value"
#endif
#if __GNUC__ > 6
// Use of GNU statement expression extension
#endif
#pragma GCC diagnostic ignored "-Wswitch-default"
#endif
#if _MSC_VER && !__INTEL_COMPILER
#ifndef NOMINMAX
#define NOMINMAX
#endif
#pragma warning(push)
#pragma warning(disable : 4244)
#endif
#include <stb_image/stb_image.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#if defined(__GNUC__) || defined(__MINGW32__)
#pragma GCC diagnostic pop
#endif

#include <babylon/core/array_buffer_view.h>
#include <babylon/core/filesystem.h>
#include <babylon/core/logging.h>
#include <babylon/core/string.h>
#include <babylon/interfaces/igl_rendering_context.h>
#include <babylon/loading/progress_event.h>
#include <babylon/utils/base64.h>

namespace BABYLON {

std::function<std::string(std::string url)> FileTools::PreprocessUrl = [](std::string url) {
  if (String::startsWith(url, "file:")) {
    url = url.substr(5);
  }
  // Check if the file is locally available
  // - Check in local folder
  auto absolutePath = Filesystem::absolutePath(url);
  if (Filesystem::exists(absolutePath)) {
    return String::concat("file:", absolutePath);
  }
  // - Check in assets folder
  absolutePath = Filesystem::absolutePath(BABYLON::assets_folder() + url);
  if (Filesystem::exists(absolutePath)) {
    return String::concat("file:", absolutePath);
  }
  return url;
};

std::string FileTools::_CleanUrl(std::string url)
{
  String::replaceInPlace(url, "#", "%23");
  return url;
}

void FileTools::LoadImageFromUrl(
  std::string url, const std::function<void(const Image& img)>& onLoad,
  const std::function<void(const std::string& message, const std::string& exception)>& onError,
  bool flipVertically)
{
  url = FileTools::_CleanUrl(url);
  url = FileTools::PreprocessUrl(url);

  if (String::startsWith(url, "file:")) {
    using stbi_ptr = std::unique_ptr<unsigned char, std::function<void(unsigned char*)>>;

    for (auto req_comp : {STBI_rgb_alpha, STBI_rgb}) {
      int w = -1, h = -1, n = -1;
      stbi_set_flip_vertically_on_load(flipVertically);
      stbi_ptr data(stbi_load(url.substr(5).c_str(), &w, &h, &n, req_comp),
                    [](unsigned char* _data) {
                      if (_data) {
                        stbi_image_free(_data);
                      }
                    });
      stbi_set_flip_vertically_on_load(false);

      if (data) {
        Image image(data.get(), w * h * req_comp, w, h, req_comp,
                    (req_comp == 3) ? GL::RGB : GL::RGBA);
        onLoad(image);
        return;
      }
    }

    if (onError) {
      onError("Error loading image from file " + url, "");
      return;
    }
  }
}

void FileTools::LoadImageFromBuffer(
  const std::variant<std::string, ArrayBuffer, ArrayBufferView, Image>& input, bool invertY,
  const std::function<void(const Image& img)>& onLoad,
  const std::function<void(const std::string& message, const std::string& exception)>& onError)
{
  if (!onLoad) {
    return;
  }

  if (std::holds_alternative<std::string>(input)) {
    onLoad(FileTools::StringToImage(std::get<std::string>(input), invertY));
  }
  else if (std::holds_alternative<ArrayBuffer>(input)) {
    onLoad(FileTools::ArrayBufferToImage(std::get<ArrayBuffer>(input), invertY));
  }
  else if (std::holds_alternative<ArrayBufferView>(input)) {
    onLoad(FileTools::ArrayBufferToImage(std::get<ArrayBufferView>(input).uint8Array, invertY));
  }
  else if (std::holds_alternative<Image>(input)) {
    onLoad(std::get<Image>(input));
  }
  else {
    auto errorMessage = "Loading image from url not supported";
    if (onError) {
      onError(errorMessage, "");
    }
    else {
      throw std::runtime_error(errorMessage);
    }
  }
}

Image FileTools::ArrayBufferToImage(const ArrayBuffer& buffer, bool flipVertically)
{
  if (buffer.empty()) {
    return Image();
  }

  using stbi_ptr = std::unique_ptr<unsigned char, std::function<void(unsigned char*)>>;

  auto bufferSize = static_cast<int>(buffer.size());
  auto w = -1, h = -1, n = -1;
  auto req_comp = STBI_rgb_alpha;
  stbi_set_flip_vertically_on_load(flipVertically);
  stbi_ptr data(stbi_load_from_memory(buffer.data(), bufferSize, &w, &h, &n, req_comp),
                [](unsigned char* _data) {
                  if (_data) {
                    stbi_image_free(_data);
                  }
                });
  stbi_set_flip_vertically_on_load(false);

  if (!data) {
    return Image();
  }

  n = STBI_rgb_alpha;
  return Image(data.get(), w * h * n, w, h, n, (n == 3) ? GL::RGB : GL::RGBA);
}

Image FileTools::StringToImage(const std::string& uri, bool flipVertically)
{
  const auto IsDataURI = [](const std::string& in) -> bool {
    std::string header = "data:application/octet-stream;base64,";
    if (in.find(header) == 0) {
      return true;
    }

    header = "data:image/jpeg;base64,";
    if (in.find(header) == 0) {
      return true;
    }

    header = "data:image/png;base64,";
    if (in.find(header) == 0) {
      return true;
    }

    header = "data:image/bmp;base64,";
    if (in.find(header) == 0) {
      return true;
    }

    header = "data:image/gif;base64,";
    if (in.find(header) == 0) {
      return true;
    }

    header = "data:text/plain;base64,";
    if (in.find(header) == 0) {
      return true;
    }

    header = "data:application/gltf-buffer;base64,";
    return in.find(header) == 0;
  };

  const auto DecodeDataURI = [](std::vector<unsigned char>* out, std::string& mime_type,
                                const std::string& in, size_t reqBytes, bool checkSize) -> bool {
    std::string header = "data:application/octet-stream;base64,";
    std::string data;
    if (in.find(header) == 0) {
      data = Base64::decode(in.substr(header.size())); // cut mime string.
    }

    if (data.empty()) {
      header = "data:image/jpeg;base64,";
      if (in.find(header) == 0) {
        mime_type = "image/jpeg";
        data      = Base64::decode(in.substr(header.size())); // cut mime string.
      }
    }

    if (data.empty()) {
      header = "data:image/png;base64,";
      if (in.find(header) == 0) {
        mime_type = "image/png";
        data      = Base64::decode(in.substr(header.size())); // cut mime string.
      }
    }

    if (data.empty()) {
      header = "data:image/bmp;base64,";
      if (in.find(header) == 0) {
        mime_type = "image/bmp";
        data      = Base64::decode(in.substr(header.size())); // cut mime string.
      }
    }

    if (data.empty()) {
      header = "data:image/gif;base64,";
      if (in.find(header) == 0) {
        mime_type = "image/gif";
        data      = Base64::decode(in.substr(header.size())); // cut mime string.
      }
    }

    if (data.empty()) {
      header = "data:text/plain;base64,";
      if (in.find(header) == 0) {
        mime_type = "text/plain";
        data      = Base64::decode(in.substr(header.size()));
      }
    }

    if (data.empty()) {
      header = "data:application/gltf-buffer;base64,";
      if (in.find(header) == 0) {
        data = Base64::decode(in.substr(header.size()));
      }
    }

    if (data.empty()) {
      return false;
    }

    if (checkSize) {
      if (data.size() != reqBytes) {
        return false;
      }
      out->resize(reqBytes);
    }
    else {
      out->resize(data.size());
    }
    std::copy(data.begin(), data.end(), out->begin());
    return true;
  };

  const auto LoadImageData = [](Image& image, int req_width, int req_height,
                                const unsigned char* bytes, int size, bool flipVertically) -> bool {
    int w = 0, h = 0, comp = 0, req_comp = 0;

    unsigned char* data = nullptr;

    // force 32-bit textures for common Vulkan compatibility. It appears that
    // some GPU drivers do not support 24-bit images for Vulkan
    req_comp = 4;
    int bits = 8;

    stbi_set_flip_vertically_on_load(flipVertically);

    // It is possible that the image we want to load is a 16bit per channel
    // image We are going to attempt to load it as 16bit per channel, and if it
    // worked, set the image data accodingly. We are casting the returned
    // pointer into unsigned char, because we are representing "bytes". But we
    // are updating the Image metadata to signal that this image uses 2 bytes
    // (16bits) per channel:
    if (stbi_is_16_bit_from_memory(bytes, size)) {
      data = reinterpret_cast<unsigned char*>(
        stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
      if (data) {
        bits = 16;
      }
    }

    // at this point, if data is still NULL, it means that the image wasn't
    // 16bit per channel, we are going to load it as a normal 8bit per channel
    // mage as we used to do:
    // if image cannot be decoded, ignore parsing and keep it by its path
    // don't break in this case
    // FIXME we should only enter this function if the image is embedded. If
    // image->uri references
    // an image file, it should be left as it is. Image loading should not be
    // mandatory (to support other formats)
    if (!data)
      data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
    if (!data) {
      BABYLON_LOG_WARN("StringToImage",
                       "Unknown image format. STB cannot decode image data for image")
      return false;
    }

    stbi_set_flip_vertically_on_load(false);

    if ((w < 1) || (h < 1)) {
      stbi_image_free(data);
      BABYLON_LOG_ERROR("StringToImage", "Invalid image data for image")
      return false;
    }

    if (req_width > 0) {
      if (req_width != w) {
        stbi_image_free(data);
        BABYLON_LOG_ERROR("StringToImage", "Image width mismatch for image")
        return false;
      }
    }

    if (req_height > 0) {
      if (req_height != h) {
        stbi_image_free(data);
        BABYLON_LOG_ERROR("StringToImage", "Image height mismatch. for image")
        return false;
      }
    }

    image.width  = w;
    image.height = h;
    image.depth  = req_comp;
    image.mode   = (req_comp == 3) ? GL::RGB : GL::RGBA;
    image.data.resize(static_cast<size_t>(w * h * req_comp) * size_t(bits / 8));
    std::copy(data, data + w * h * req_comp * (bits / 8), image.data.begin());
    stbi_image_free(data);

    return true;
  };

  Image image;
  std::vector<unsigned char> img;
  std::string mimeType;
  if (IsDataURI(uri)) {
    if (!DecodeDataURI(&img, mimeType, uri, 0, false)) {
      BABYLON_LOG_ERROR("Failed to decode image from uri: %s", uri.c_str())
      return Image();
    }
  }

  if (LoadImageData(image, 0, 0, &img.at(0), static_cast<int>(img.size()), flipVertically)) {
    return image;
  }

  return Image();
}

void FileTools::ReadFile(
  std::string fileToLoad,
  const std::function<void(const std::variant<std::string, ArrayBuffer>& data,
                           const std::string& responseURL)>& callback,
  const std::function<void(const ProgressEvent& event)>& onProgress, bool useArrayBuffer)
{
  if (!Filesystem::exists(fileToLoad)) {
    BABYLON_LOGF_ERROR("Tools", "Error while reading file: %s", fileToLoad.c_str())
    if (callback) {
      callback("", "");
    }
    if (onProgress) {
      onProgress(ProgressEvent{"ReadFileEvent", true, 100, 100});
    }
    return;
  }

  if (!useArrayBuffer) {
    // Read file contents
    if (callback) {
      callback(Filesystem::readFileContents(fileToLoad.c_str()), "");
    }
    if (onProgress) {
      onProgress(ProgressEvent{"ReadFileEvent", true, 100, 100});
    }
    return;
  }
  else {
    // Read file contents
    if (callback) {
      callback(Filesystem::readBinaryFile(fileToLoad.c_str()), "");
    }
    if (onProgress) {
      onProgress(ProgressEvent{"ReadFileEvent", true, 100, 100});
    }
    return;
  }
}

void FileTools::LoadFile(
  std::string url,
  const std::function<void(const std::variant<std::string, ArrayBuffer>& data,
                           const std::string& responseURL)>& onSuccess,
  const std::function<void(const ProgressEvent& event)>& onProgress, bool useArrayBuffer,
  const std::function<void(const std::string& message, const std::string& exception)>& onError)
{
  url = FileTools::_CleanUrl(url);

  url = FileTools::PreprocessUrl(url);

  // If file and file input are set
  if (String::startsWith(url, "file:")) {
    const auto fileName = url.substr(5);
    if (!fileName.empty()) {
      FileTools::ReadFile(fileName, onSuccess, onProgress, useArrayBuffer);
      return;
    }
  }

  // Report error
  if (onError) {
    onError("Unable to load file from location " + url, "");
  }
}

} // end of namespace BABYLON
<commit_msg>FileTools::LoadImageFromUrl/ArrayBufferToImage simplify<commit_after>#include <babylon/misc/file_tools.h>
#include <future>

#define STB_IMAGE_IMPLEMENTATION
#if defined(__GNUC__) || defined(__MINGW32__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual"
#pragma GCC diagnostic ignored "-Wconversion"
#if __GNUC__ > 5
#pragma GCC diagnostic ignored "-Wmisleading-indentation"
#pragma GCC diagnostic ignored "-Wshift-negative-value"
#endif
#if __GNUC__ > 6
// Use of GNU statement expression extension
#endif
#pragma GCC diagnostic ignored "-Wswitch-default"
#endif
#if _MSC_VER && !__INTEL_COMPILER
#ifndef NOMINMAX
#define NOMINMAX
#endif
#pragma warning(push)
#pragma warning(disable : 4244)
#endif
#include <stb_image/stb_image.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#if defined(__GNUC__) || defined(__MINGW32__)
#pragma GCC diagnostic pop
#endif

#include <babylon/core/array_buffer_view.h>
#include <babylon/core/filesystem.h>
#include <babylon/core/logging.h>
#include <babylon/core/string.h>
#include <babylon/interfaces/igl_rendering_context.h>
#include <babylon/loading/progress_event.h>
#include <babylon/utils/base64.h>

namespace BABYLON {

std::function<std::string(std::string url)> FileTools::PreprocessUrl = [](std::string url) {
  if (String::startsWith(url, "file:")) {
    url = url.substr(5);
  }
  // Check if the file is locally available
  // - Check in local folder
  auto absolutePath = Filesystem::absolutePath(url);
  if (Filesystem::exists(absolutePath)) {
    return String::concat("file:", absolutePath);
  }
  // - Check in assets folder
  absolutePath = Filesystem::absolutePath(BABYLON::assets_folder() + url);
  if (Filesystem::exists(absolutePath)) {
    return String::concat("file:", absolutePath);
  }
  return url;
};

std::string FileTools::_CleanUrl(std::string url)
{
  String::replaceInPlace(url, "#", "%23");
  return url;
}

std::optional<Image> LoadImage_Stbi_Impl(
  const char *filename,
  int nb_desired_channels,
  bool flipVertically)
{

  // Basic usage (see HDR discussion below for HDR usage):
  int w, h, nb_channels_real;
  stbi_set_flip_vertically_on_load(flipVertically);
  unsigned char *data = stbi_load(filename, &w, &h, &nb_channels_real, nb_desired_channels);
  stbi_set_flip_vertically_on_load(false);
  if (data)
  {
    unsigned int glColorMode = (nb_desired_channels == 3) ? GL::RGB : GL::RGBA;
    Image image(data, w * h * nb_desired_channels, w, h, nb_desired_channels, glColorMode);
    stbi_image_free(data);
    return image;
  }
  return std::nullopt;
}

void FileTools::LoadImageFromUrl(
  std::string url, const std::function<void(const Image& img)>& onLoad,
  const std::function<void(const std::string& message, const std::string& exception)>& onError,
  bool flipVertically)
{
  url = FileTools::_CleanUrl(url);
  url = FileTools::PreprocessUrl(url);

  if (!String::startsWith(url, "file:"))
    throw std::runtime_error("FileTools::LoadImageFromUrl only supports file urls");

  const char *filename = url.substr(5).c_str();
  std::optional<Image> image;

  // Try to load RGBA, then load RGB on fail (is this really required? we could instead trust stbi)
  image = LoadImage_Stbi_Impl(filename, STBI_rgb_alpha, flipVertically);
  if (!image)
    image = LoadImage_Stbi_Impl(filename, STBI_rgb, flipVertically);

  if (image)
      onLoad(*image);
  else if (onError)
    onError("Error loading image from file " + url, "");
}

void FileTools::LoadImageFromBuffer(
  const std::variant<std::string, ArrayBuffer, ArrayBufferView, Image>& input, bool invertY,
  const std::function<void(const Image& img)>& onLoad,
  const std::function<void(const std::string& message, const std::string& exception)>& onError)
{
  if (!onLoad) {
    return;
  }

  if (std::holds_alternative<std::string>(input)) {
    onLoad(FileTools::StringToImage(std::get<std::string>(input), invertY));
  }
  else if (std::holds_alternative<ArrayBuffer>(input)) {
    onLoad(FileTools::ArrayBufferToImage(std::get<ArrayBuffer>(input), invertY));
  }
  else if (std::holds_alternative<ArrayBufferView>(input)) {
    onLoad(FileTools::ArrayBufferToImage(std::get<ArrayBufferView>(input).uint8Array, invertY));
  }
  else if (std::holds_alternative<Image>(input)) {
    onLoad(std::get<Image>(input));
  }
  else {
    auto errorMessage = "Loading image from url not supported";
    if (onError) {
      onError(errorMessage, "");
    }
    else {
      throw std::runtime_error(errorMessage);
    }
  }
}

Image FileTools::ArrayBufferToImage(const ArrayBuffer& buffer, bool flipVertically)
{
  if (buffer.empty()) {
    return Image();
  }
  auto bufferSize = static_cast<int>(buffer.size());
  int w = -1, h = -1, n = -1;
  int req_comp = STBI_rgb_alpha;

  stbi_set_flip_vertically_on_load(flipVertically);
  unsigned char * ucharBuffer = stbi_load_from_memory(buffer.data(), bufferSize, &w, &h, &n, req_comp);
  stbi_set_flip_vertically_on_load(false);

  if (!ucharBuffer)
    return Image();

  n = STBI_rgb_alpha;
  Image image (ucharBuffer, w * h * n, w, h, n, (n == 3) ? GL::RGB : GL::RGBA);
  stbi_image_free(ucharBuffer);
  return image;
}

Image FileTools::StringToImage(const std::string& uri, bool flipVertically)
{
  const auto IsDataURI = [](const std::string& in) -> bool {
    std::string header = "data:application/octet-stream;base64,";
    if (in.find(header) == 0) {
      return true;
    }

    header = "data:image/jpeg;base64,";
    if (in.find(header) == 0) {
      return true;
    }

    header = "data:image/png;base64,";
    if (in.find(header) == 0) {
      return true;
    }

    header = "data:image/bmp;base64,";
    if (in.find(header) == 0) {
      return true;
    }

    header = "data:image/gif;base64,";
    if (in.find(header) == 0) {
      return true;
    }

    header = "data:text/plain;base64,";
    if (in.find(header) == 0) {
      return true;
    }

    header = "data:application/gltf-buffer;base64,";
    return in.find(header) == 0;
  };

  const auto DecodeDataURI = [](std::vector<unsigned char>* out, std::string& mime_type,
                                const std::string& in, size_t reqBytes, bool checkSize) -> bool {
    std::string header = "data:application/octet-stream;base64,";
    std::string data;
    if (in.find(header) == 0) {
      data = Base64::decode(in.substr(header.size())); // cut mime string.
    }

    if (data.empty()) {
      header = "data:image/jpeg;base64,";
      if (in.find(header) == 0) {
        mime_type = "image/jpeg";
        data      = Base64::decode(in.substr(header.size())); // cut mime string.
      }
    }

    if (data.empty()) {
      header = "data:image/png;base64,";
      if (in.find(header) == 0) {
        mime_type = "image/png";
        data      = Base64::decode(in.substr(header.size())); // cut mime string.
      }
    }

    if (data.empty()) {
      header = "data:image/bmp;base64,";
      if (in.find(header) == 0) {
        mime_type = "image/bmp";
        data      = Base64::decode(in.substr(header.size())); // cut mime string.
      }
    }

    if (data.empty()) {
      header = "data:image/gif;base64,";
      if (in.find(header) == 0) {
        mime_type = "image/gif";
        data      = Base64::decode(in.substr(header.size())); // cut mime string.
      }
    }

    if (data.empty()) {
      header = "data:text/plain;base64,";
      if (in.find(header) == 0) {
        mime_type = "text/plain";
        data      = Base64::decode(in.substr(header.size()));
      }
    }

    if (data.empty()) {
      header = "data:application/gltf-buffer;base64,";
      if (in.find(header) == 0) {
        data = Base64::decode(in.substr(header.size()));
      }
    }

    if (data.empty()) {
      return false;
    }

    if (checkSize) {
      if (data.size() != reqBytes) {
        return false;
      }
      out->resize(reqBytes);
    }
    else {
      out->resize(data.size());
    }
    std::copy(data.begin(), data.end(), out->begin());
    return true;
  };

  const auto LoadImageData = [](Image& image, int req_width, int req_height,
                                const unsigned char* bytes, int size, bool flipVertically) -> bool {
    int w = 0, h = 0, comp = 0, req_comp = 0;

    unsigned char* data = nullptr;

    // force 32-bit textures for common Vulkan compatibility. It appears that
    // some GPU drivers do not support 24-bit images for Vulkan
    req_comp = 4;
    int bits = 8;

    stbi_set_flip_vertically_on_load(flipVertically);

    // It is possible that the image we want to load is a 16bit per channel
    // image We are going to attempt to load it as 16bit per channel, and if it
    // worked, set the image data accodingly. We are casting the returned
    // pointer into unsigned char, because we are representing "bytes". But we
    // are updating the Image metadata to signal that this image uses 2 bytes
    // (16bits) per channel:
    if (stbi_is_16_bit_from_memory(bytes, size)) {
      data = reinterpret_cast<unsigned char*>(
        stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
      if (data) {
        bits = 16;
      }
    }

    // at this point, if data is still NULL, it means that the image wasn't
    // 16bit per channel, we are going to load it as a normal 8bit per channel
    // mage as we used to do:
    // if image cannot be decoded, ignore parsing and keep it by its path
    // don't break in this case
    // FIXME we should only enter this function if the image is embedded. If
    // image->uri references
    // an image file, it should be left as it is. Image loading should not be
    // mandatory (to support other formats)
    if (!data)
      data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
    if (!data) {
      BABYLON_LOG_WARN("StringToImage",
                       "Unknown image format. STB cannot decode image data for image")
      return false;
    }

    stbi_set_flip_vertically_on_load(false);

    if ((w < 1) || (h < 1)) {
      stbi_image_free(data);
      BABYLON_LOG_ERROR("StringToImage", "Invalid image data for image")
      return false;
    }

    if (req_width > 0) {
      if (req_width != w) {
        stbi_image_free(data);
        BABYLON_LOG_ERROR("StringToImage", "Image width mismatch for image")
        return false;
      }
    }

    if (req_height > 0) {
      if (req_height != h) {
        stbi_image_free(data);
        BABYLON_LOG_ERROR("StringToImage", "Image height mismatch. for image")
        return false;
      }
    }

    image.width  = w;
    image.height = h;
    image.depth  = req_comp;
    image.mode   = (req_comp == 3) ? GL::RGB : GL::RGBA;
    image.data.resize(static_cast<size_t>(w * h * req_comp) * size_t(bits / 8));
    std::copy(data, data + w * h * req_comp * (bits / 8), image.data.begin());
    stbi_image_free(data);

    return true;
  };

  Image image;
  std::vector<unsigned char> img;
  std::string mimeType;
  if (IsDataURI(uri)) {
    if (!DecodeDataURI(&img, mimeType, uri, 0, false)) {
      BABYLON_LOG_ERROR("Failed to decode image from uri: %s", uri.c_str())
      return Image();
    }
  }

  if (LoadImageData(image, 0, 0, &img.at(0), static_cast<int>(img.size()), flipVertically)) {
    return image;
  }

  return Image();
}

void FileTools::ReadFile(
  std::string fileToLoad,
  const std::function<void(const std::variant<std::string, ArrayBuffer>& data,
                           const std::string& responseURL)>& callback,
  const std::function<void(const ProgressEvent& event)>& onProgress, bool useArrayBuffer)
{
  if (!Filesystem::exists(fileToLoad)) {
    BABYLON_LOGF_ERROR("Tools", "Error while reading file: %s", fileToLoad.c_str())
    if (callback) {
      callback("", "");
    }
    if (onProgress) {
      onProgress(ProgressEvent{"ReadFileEvent", true, 100, 100});
    }
    return;
  }

  if (!useArrayBuffer) {
    // Read file contents
    if (callback) {
      callback(Filesystem::readFileContents(fileToLoad.c_str()), "");
    }
    if (onProgress) {
      onProgress(ProgressEvent{"ReadFileEvent", true, 100, 100});
    }
    return;
  }
  else {
    // Read file contents
    if (callback) {
      callback(Filesystem::readBinaryFile(fileToLoad.c_str()), "");
    }
    if (onProgress) {
      onProgress(ProgressEvent{"ReadFileEvent", true, 100, 100});
    }
    return;
  }
}

void FileTools::LoadFile(
  std::string url,
  const std::function<void(const std::variant<std::string, ArrayBuffer>& data,
                           const std::string& responseURL)>& onSuccess,
  const std::function<void(const ProgressEvent& event)>& onProgress, bool useArrayBuffer,
  const std::function<void(const std::string& message, const std::string& exception)>& onError)
{
  url = FileTools::_CleanUrl(url);

  url = FileTools::PreprocessUrl(url);

  // If file and file input are set
  if (String::startsWith(url, "file:")) {
    const auto fileName = url.substr(5);
    if (!fileName.empty()) {
      FileTools::ReadFile(fileName, onSuccess, onProgress, useArrayBuffer);
      return;
    }
  }

  // Report error
  if (onError) {
    onError("Unable to load file from location " + url, "");
  }
}

} // end of namespace BABYLON
<|endoftext|>


<commit_before>/*
A C++ header for Roaring Bitmaps.
*/
#ifndef INCLUDE_ROARING_HH_
#define INCLUDE_ROARING_HH_

#include <stdarg.h>

#include <algorithm>
#include <new>
#include <stdexcept>
#include <roaring/roaring.h>

class RoaringSetBitForwardIterator;

class Roaring {
   public:
    /**
     * Create an empty bitmap
     */
    Roaring() : roaring(NULL) {
        roaring = roaring_bitmap_create();
        if (roaring == NULL) {
            throw std::runtime_error("failed memory alloc in constructor");
        }
    }

    /**
     * Construct a bitmap from a list of integer values.
     */
    Roaring(size_t n, const uint32_t *data) {
        roaring = roaring_bitmap_of_ptr(n, data);
        if (roaring == NULL) {
            throw std::runtime_error("failed memory alloc in constructor");
        }
    }
    /**
     * Copy constructor
     */
    Roaring(const Roaring &r) : roaring(NULL) {
        roaring = roaring_bitmap_copy(r.roaring);
        if (roaring == NULL) {
            throw std::runtime_error("failed memory alloc in constructor");
        }
    }

    /**
     * Construct a roaring object from the C struct.
     *
     * Passing a NULL point is unsafe.
     */
    Roaring(roaring_bitmap_t *s) : roaring(s) {}

    /**
     * Construct a bitmap from a list of integer values.
     */
    static Roaring bitmapOf(size_t n, ...) {
        Roaring ans;
        va_list vl;
        va_start(vl, n);
        for (size_t i = 0; i < n; i++) {
            ans.add(va_arg(vl, uint32_t));
        }
        va_end(vl);
        return ans;
    }

    /**
     * Add value x
     *
     */
    void add(uint32_t x) { roaring_bitmap_add(roaring, x); }

    /**
     * Add value n_args from pointer vals
     *
     */
    void addMany(size_t n_args, const uint32_t *vals) {
        roaring_bitmap_add_many(roaring, n_args, vals);
    }

    /**
     * Remove value x
     *
     */
    void remove(uint32_t x) { roaring_bitmap_remove(roaring, x); }

    /**
     * Check if value x is present
     */
    bool contains(uint32_t x) const {
        return roaring_bitmap_contains(roaring, x);
    }

    /**
     * Destructor
     */
    ~Roaring() { roaring_bitmap_free(roaring); }

    /**
     * Copies the content of the provided bitmap, and
     * discard the current content.
     */
    Roaring &operator=(const Roaring &r) {
        roaring_bitmap_free(roaring);
        roaring = roaring_bitmap_copy(r.roaring);
        if (roaring == NULL) {
            throw std::runtime_error("failed memory alloc in assignement");
        }
        return *this;
    }

    /**
     * Compute the intersection between the current bitmap and the provided
     * bitmap,
     * writing the result in the current bitmap. The provided bitmap is not
     * modified.
     */
    Roaring &operator&=(const Roaring &r) {
        roaring_bitmap_and_inplace(roaring, r.roaring);
        return *this;
    }

    /**
     * Compute the union between the current bitmap and the provided bitmap,
     * writing the result in the current bitmap. The provided bitmap is not
     * modified.
     *
     * See also the fastunion function to aggregate many bitmaps more quickly.
     */
    Roaring &operator|=(const Roaring &r) {
        roaring_bitmap_or_inplace(roaring, r.roaring);
        return *this;
    }

    /**
     * Compute the symmetric union between the current bitmap and the provided
     * bitmap,
     * writing the result in the current bitmap. The provided bitmap is not
     * modified.
     */
    Roaring &operator^=(const Roaring &r) {
        roaring_bitmap_xor_inplace(roaring, r.roaring);
        return *this;
    }

    /**
     * Exchange the content of this bitmap with another.
     */
    void swap(Roaring &r) { std::swap(r.roaring, roaring); }

    /**
     * Get the cardinality of the bitmap (number of elements).
     */
    uint64_t cardinality() const {
        return roaring_bitmap_get_cardinality(roaring);
    }

    /**
    * Returns true if the bitmap is empty (cardinality is zero).
    */
    bool isEmpty() const { return roaring_bitmap_is_empty(roaring); }

    /**
    * Returns true if the bitmap is subset of the other.
    */
    bool isSubset(const Roaring &r) const { return roaring_bitmap_is_subset(roaring, r.roaring); }

    /**
    * Returns true if the bitmap is strict subset of the other.
    */
    bool isStrictSubset(const Roaring &r) const { return roaring_bitmap_is_strict_subset(roaring, r.roaring); }

    /**
     * Convert the bitmap to an array. Write the output to "ans",
     * caller is responsible to ensure that there is enough memory
     * allocated
     * (e.g., ans = new uint32[mybitmap.cardinality()];)
     */
    void toUint32Array(uint32_t *ans) const {
        roaring_bitmap_to_uint32_array(roaring, ans);
    }

    /**
     * Return true if the two bitmaps contain the same elements.
     */
    bool operator==(const Roaring &r) const {
        return roaring_bitmap_equals(roaring, r.roaring);
    }

    /**
     * compute the negation of the roaring bitmap within a specified interval.
     * areas outside the range are passed through unchanged.
     */
    void flip(uint64_t range_start, uint64_t range_end) {
        roaring_bitmap_flip_inplace(roaring, range_start, range_end);
    }

    /**
     *  Remove run-length encoding even when it is more space efficient
     *  return whether a change was applied
     */
    bool removeRunCompression() {
        return roaring_bitmap_remove_run_compression(roaring);
    }

    /** convert array and bitmap containers to run containers when it is more
     * efficient;
     * also convert from run containers when more space efficient.  Returns
     * true if the result has at least one run container.
     * Additional savings might be possible by calling shrinkToFit().
     */
    bool runOptimize() { return roaring_bitmap_run_optimize(roaring); }

    /**
     * If needed, reallocate memory to shrink the memory usage. Returns
     * the number of bytes saved.
    */
    size_t shrinkToFit() { return roaring_bitmap_shrink_to_fit(roaring); }

    /**
     * Iterate over the bitmap elements. The function iterator is called once
     * for
     *  all the values with ptr (can be NULL) as the second parameter of each
     * call.
     *
     *  roaring_iterator is simply a pointer to a function that returns void,
     *  and takes (uint32_t,void*) as inputs.
     */
    void iterate(roaring_iterator iterator, void *ptr) const {
        roaring_iterate(roaring, iterator, ptr);
    }

    /**
     * If the size of the roaring bitmap is strictly greater than rank, then
     this
       function returns true and set element to the element of given rank.
       Otherwise, it returns false.
     */
    bool select(uint32_t rank, uint32_t *element) const {
        return roaring_bitmap_select(roaring, rank, element);
    }

    /**
     * write a bitmap to a char buffer. This is meant to be compatible with
     * the
     * Java and Go versions. Returns how many bytes were written which should be
     * getSizeInBytes().
     *
     * Setting the portable flag to false enable a custom format that
     * can save space compared to the portable format (e.g., for very
     * sparse bitmaps).
     */
    size_t write(char *buf, bool portable = true) const {
        if (portable)
            return roaring_bitmap_portable_serialize(roaring, buf);
        else
            return roaring_bitmap_serialize(roaring, buf);
    }

    /**
     * read a bitmap from a serialized version. This is meant to be compatible
     * with
     * the
     * Java and Go versions.
     *
     * Setting the portable flag to false enable a custom format that
     * can save space compared to the portable format (e.g., for very
     * sparse bitmaps).
     */
    static Roaring read(const char *buf, bool portable = true) {
        Roaring ans(NULL);
        if (portable)
            ans.roaring = roaring_bitmap_portable_deserialize(buf);
        else
            ans.roaring = roaring_bitmap_deserialize(buf);
        if (ans.roaring == NULL) {
            throw std::runtime_error("failed memory alloc while reading");
        }
        return ans;
    }

    /**
     * How many bytes are required to serialize this bitmap (meant to be
     * compatible
     * with Java and Go versions)
     *
     * Setting the portable flag to false enable a custom format that
     * can save space compared to the portable format (e.g., for very
     * sparse bitmaps).
     */
    size_t getSizeInBytes(bool portable = true) const {
        if (portable)
            return roaring_bitmap_portable_size_in_bytes(roaring);
        else
            return roaring_bitmap_size_in_bytes(roaring);
    }

    /**
     * Computes the intersection between two bitmaps and returns new bitmap.
     * The current bitmap and the provided bitmap are unchanged.
     */
    Roaring operator&(const Roaring &o) const {
        roaring_bitmap_t *r = roaring_bitmap_and(roaring, o.roaring);
        if (r == NULL) {
            throw std::runtime_error("failed materalization in and");
        }
        return Roaring(r);
    }

    /**
     * Computes the union between two bitmaps and returns new bitmap.
     * The current bitmap and the provided bitmap are unchanged.
     */
    Roaring operator|(const Roaring &o) const {
        roaring_bitmap_t *r = roaring_bitmap_or(roaring, o.roaring);
        if (r == NULL) {
            throw std::runtime_error("failed materalization in or");
        }
        return Roaring(r);
    }

    /**
     * Computes the symmetric union between two bitmaps and returns new bitmap.
     * The current bitmap and the provided bitmap are unchanged.
     */
    Roaring operator^(const Roaring &o) const {
        roaring_bitmap_t *r = roaring_bitmap_xor(roaring, o.roaring);
        if (r == NULL) {
            throw std::runtime_error("failed materalization in xor");
        }
        return Roaring(r);
    }

    /**
     * Whether or not we apply copy and write.
     */
    void setCopyOnWrite(bool val) { roaring->copy_on_write = val; }

    /**
     * Print the content of the bitmap
     */
    void printf() { roaring_bitmap_printf(roaring); }

    /**
     * Whether or not copy and write is active.
     */
    bool getCopyOnWrite() const { return roaring->copy_on_write; }

    /**
     * computes the logical or (union) between "n" bitmaps (referenced by a
     * pointer).
     */
    static Roaring fastunion(size_t n, const Roaring **inputs) {
        const roaring_bitmap_t **x =
            (const roaring_bitmap_t **)malloc(n * sizeof(roaring_bitmap_t *));
        if (x == NULL) {
            throw std::runtime_error("failed memory alloc in fastunion");
        }
        for (size_t k = 0; k < n; ++k) x[k] = inputs[k]->roaring;

        Roaring ans(NULL);
        ans.roaring = roaring_bitmap_or_many(n, x);
        if (ans.roaring == NULL) {
            throw std::runtime_error("failed memory alloc in fastunion");
        }
        free(x);
        return ans;
    }

    typedef  RoaringSetBitForwardIterator const_iterator;

    /**
    * Returns an iterator that can be used to access the position of the
    * set bits. The running time complexity of a full scan is proportional to the
    * number
    * of set bits: be aware that if you have long strings of 1s, this can be
    * very inefficient.
    *
    * It can be much faster to use the toArray method if you want to
    * retrieve the set bits.
    */
    const_iterator begin() const ;
    /*{
      return RoaringSetBitForwardIterator(*this);
    }*/

    /**
    * A bogus iterator that can be used together with begin()
    * for constructions such as for(auto i = b.begin();
    * i!=b.end(); ++i) {}
    */
    const_iterator end() const ; /*{
      return RoaringSetBitForwardIterator(*this, true);
    }*/

    roaring_bitmap_t *roaring;
};

/**
 * Used to go through the set bits. Not optimally fast, but convenient.
 */
class RoaringSetBitForwardIterator {
public:
  typedef std::forward_iterator_tag iterator_category;
  typedef uint32_t *pointer;
  typedef uint32_t &reference_type;
  typedef uint32_t value_type;
  typedef int32_t difference_type;
  typedef RoaringSetBitForwardIterator type_of_iterator;

  /**
   * Provides the location of the set bit.
   */
  value_type operator*() const {
    return i->current_value;
  }

  bool operator<(const type_of_iterator &o) {
    return i->current_value < *o;
  }

  bool operator<=(const type_of_iterator &o) {
    return i->current_value <= *o;
  }

  bool operator>(const type_of_iterator &o) {
    return i->current_value > *o;
  }

  bool operator>=(const type_of_iterator &o) {
    return i->current_value >= *o;
  }

  type_of_iterator &operator++() {// ++i, must returned inc. value
    roaring_advance_uint32_iterator(i);
    return *this;
  }

  type_of_iterator operator++(int) {// i++, must return orig. value
    RoaringSetBitForwardIterator orig(*this);
    roaring_advance_uint32_iterator(i);
    return orig;
  }

  bool operator==(const RoaringSetBitForwardIterator &o) {
    return i->current_value == *o;
  }

  bool operator!=(const RoaringSetBitForwardIterator &o) {
    return i->current_value != *o;
  }

  RoaringSetBitForwardIterator(const Roaring & parent, bool exhausted = false) : i(NULL) {
    if(exhausted) {
        i = (roaring_uint32_iterator_t *) malloc(sizeof(roaring_uint32_iterator_t));
        i->parent = parent.roaring;
        i->container_index = INT32_MAX;
        i->has_value = false;
        i->current_value = UINT32_MAX;
    } else {
      i = roaring_create_iterator(parent.roaring);
    }
  }

  virtual ~RoaringSetBitForwardIterator() {
    roaring_free_uint32_iterator(i);
    i = NULL;
  }

  RoaringSetBitForwardIterator(
      const RoaringSetBitForwardIterator &o)
      : i(NULL) {
    i = roaring_copy_uint32_iterator (o.i);
  }

  roaring_uint32_iterator_t *  i;
};

RoaringSetBitForwardIterator Roaring::begin() const {
      return RoaringSetBitForwardIterator(*this);
}

RoaringSetBitForwardIterator Roaring::end() const {
      return RoaringSetBitForwardIterator(*this, true);
}

#endif /* INCLUDE_ROARING_HH_ */
<commit_msg>Adding missing difference operator to C++ API<commit_after>/*
A C++ header for Roaring Bitmaps.
*/
#ifndef INCLUDE_ROARING_HH_
#define INCLUDE_ROARING_HH_

#include <stdarg.h>

#include <algorithm>
#include <new>
#include <stdexcept>
#include <roaring/roaring.h>

class RoaringSetBitForwardIterator;

class Roaring {
   public:
    /**
     * Create an empty bitmap
     */
    Roaring() : roaring(NULL) {
        roaring = roaring_bitmap_create();
        if (roaring == NULL) {
            throw std::runtime_error("failed memory alloc in constructor");
        }
    }

    /**
     * Construct a bitmap from a list of integer values.
     */
    Roaring(size_t n, const uint32_t *data) {
        roaring = roaring_bitmap_of_ptr(n, data);
        if (roaring == NULL) {
            throw std::runtime_error("failed memory alloc in constructor");
        }
    }
    /**
     * Copy constructor
     */
    Roaring(const Roaring &r) : roaring(NULL) {
        roaring = roaring_bitmap_copy(r.roaring);
        if (roaring == NULL) {
            throw std::runtime_error("failed memory alloc in constructor");
        }
    }

    /**
     * Construct a roaring object from the C struct.
     *
     * Passing a NULL point is unsafe.
     */
    Roaring(roaring_bitmap_t *s) : roaring(s) {}

    /**
     * Construct a bitmap from a list of integer values.
     */
    static Roaring bitmapOf(size_t n, ...) {
        Roaring ans;
        va_list vl;
        va_start(vl, n);
        for (size_t i = 0; i < n; i++) {
            ans.add(va_arg(vl, uint32_t));
        }
        va_end(vl);
        return ans;
    }

    /**
     * Add value x
     *
     */
    void add(uint32_t x) { roaring_bitmap_add(roaring, x); }

    /**
     * Add value n_args from pointer vals
     *
     */
    void addMany(size_t n_args, const uint32_t *vals) {
        roaring_bitmap_add_many(roaring, n_args, vals);
    }

    /**
     * Remove value x
     *
     */
    void remove(uint32_t x) { roaring_bitmap_remove(roaring, x); }

    /**
     * Check if value x is present
     */
    bool contains(uint32_t x) const {
        return roaring_bitmap_contains(roaring, x);
    }

    /**
     * Destructor
     */
    ~Roaring() { roaring_bitmap_free(roaring); }

    /**
     * Copies the content of the provided bitmap, and
     * discard the current content.
     */
    Roaring &operator=(const Roaring &r) {
        roaring_bitmap_free(roaring);
        roaring = roaring_bitmap_copy(r.roaring);
        if (roaring == NULL) {
            throw std::runtime_error("failed memory alloc in assignement");
        }
        return *this;
    }

    /**
     * Compute the intersection between the current bitmap and the provided
     * bitmap,
     * writing the result in the current bitmap. The provided bitmap is not
     * modified.
     */
    Roaring &operator&=(const Roaring &r) {
        roaring_bitmap_and_inplace(roaring, r.roaring);
        return *this;
    }

    /**
     * Compute the difference between the current bitmap and the provided
     * bitmap,
     * writing the result in the current bitmap. The provided bitmap is not
     * modified.
     */
    Roaring &operator-=(const Roaring &r) {
        roaring_bitmap_andnot_inplace(roaring, r.roaring);
        return *this;
    }

    /**
     * Compute the union between the current bitmap and the provided bitmap,
     * writing the result in the current bitmap. The provided bitmap is not
     * modified.
     *
     * See also the fastunion function to aggregate many bitmaps more quickly.
     */
    Roaring &operator|=(const Roaring &r) {
        roaring_bitmap_or_inplace(roaring, r.roaring);
        return *this;
    }

    /**
     * Compute the symmetric union between the current bitmap and the provided
     * bitmap,
     * writing the result in the current bitmap. The provided bitmap is not
     * modified.
     */
    Roaring &operator^=(const Roaring &r) {
        roaring_bitmap_xor_inplace(roaring, r.roaring);
        return *this;
    }

    /**
     * Exchange the content of this bitmap with another.
     */
    void swap(Roaring &r) { std::swap(r.roaring, roaring); }

    /**
     * Get the cardinality of the bitmap (number of elements).
     */
    uint64_t cardinality() const {
        return roaring_bitmap_get_cardinality(roaring);
    }

    /**
    * Returns true if the bitmap is empty (cardinality is zero).
    */
    bool isEmpty() const { return roaring_bitmap_is_empty(roaring); }

    /**
    * Returns true if the bitmap is subset of the other.
    */
    bool isSubset(const Roaring &r) const { return roaring_bitmap_is_subset(roaring, r.roaring); }

    /**
    * Returns true if the bitmap is strict subset of the other.
    */
    bool isStrictSubset(const Roaring &r) const { return roaring_bitmap_is_strict_subset(roaring, r.roaring); }

    /**
     * Convert the bitmap to an array. Write the output to "ans",
     * caller is responsible to ensure that there is enough memory
     * allocated
     * (e.g., ans = new uint32[mybitmap.cardinality()];)
     */
    void toUint32Array(uint32_t *ans) const {
        roaring_bitmap_to_uint32_array(roaring, ans);
    }

    /**
     * Return true if the two bitmaps contain the same elements.
     */
    bool operator==(const Roaring &r) const {
        return roaring_bitmap_equals(roaring, r.roaring);
    }

    /**
     * compute the negation of the roaring bitmap within a specified interval.
     * areas outside the range are passed through unchanged.
     */
    void flip(uint64_t range_start, uint64_t range_end) {
        roaring_bitmap_flip_inplace(roaring, range_start, range_end);
    }

    /**
     *  Remove run-length encoding even when it is more space efficient
     *  return whether a change was applied
     */
    bool removeRunCompression() {
        return roaring_bitmap_remove_run_compression(roaring);
    }

    /** convert array and bitmap containers to run containers when it is more
     * efficient;
     * also convert from run containers when more space efficient.  Returns
     * true if the result has at least one run container.
     * Additional savings might be possible by calling shrinkToFit().
     */
    bool runOptimize() { return roaring_bitmap_run_optimize(roaring); }

    /**
     * If needed, reallocate memory to shrink the memory usage. Returns
     * the number of bytes saved.
    */
    size_t shrinkToFit() { return roaring_bitmap_shrink_to_fit(roaring); }

    /**
     * Iterate over the bitmap elements. The function iterator is called once
     * for
     *  all the values with ptr (can be NULL) as the second parameter of each
     * call.
     *
     *  roaring_iterator is simply a pointer to a function that returns void,
     *  and takes (uint32_t,void*) as inputs.
     */
    void iterate(roaring_iterator iterator, void *ptr) const {
        roaring_iterate(roaring, iterator, ptr);
    }

    /**
     * If the size of the roaring bitmap is strictly greater than rank, then
     this
       function returns true and set element to the element of given rank.
       Otherwise, it returns false.
     */
    bool select(uint32_t rank, uint32_t *element) const {
        return roaring_bitmap_select(roaring, rank, element);
    }

    /**
     * write a bitmap to a char buffer. This is meant to be compatible with
     * the
     * Java and Go versions. Returns how many bytes were written which should be
     * getSizeInBytes().
     *
     * Setting the portable flag to false enable a custom format that
     * can save space compared to the portable format (e.g., for very
     * sparse bitmaps).
     */
    size_t write(char *buf, bool portable = true) const {
        if (portable)
            return roaring_bitmap_portable_serialize(roaring, buf);
        else
            return roaring_bitmap_serialize(roaring, buf);
    }

    /**
     * read a bitmap from a serialized version. This is meant to be compatible
     * with
     * the
     * Java and Go versions.
     *
     * Setting the portable flag to false enable a custom format that
     * can save space compared to the portable format (e.g., for very
     * sparse bitmaps).
     */
    static Roaring read(const char *buf, bool portable = true) {
        Roaring ans(NULL);
        if (portable)
            ans.roaring = roaring_bitmap_portable_deserialize(buf);
        else
            ans.roaring = roaring_bitmap_deserialize(buf);
        if (ans.roaring == NULL) {
            throw std::runtime_error("failed memory alloc while reading");
        }
        return ans;
    }

    /**
     * How many bytes are required to serialize this bitmap (meant to be
     * compatible
     * with Java and Go versions)
     *
     * Setting the portable flag to false enable a custom format that
     * can save space compared to the portable format (e.g., for very
     * sparse bitmaps).
     */
    size_t getSizeInBytes(bool portable = true) const {
        if (portable)
            return roaring_bitmap_portable_size_in_bytes(roaring);
        else
            return roaring_bitmap_size_in_bytes(roaring);
    }

    /**
     * Computes the intersection between two bitmaps and returns new bitmap.
     * The current bitmap and the provided bitmap are unchanged.
     */
    Roaring operator&(const Roaring &o) const {
        roaring_bitmap_t *r = roaring_bitmap_and(roaring, o.roaring);
        if (r == NULL) {
            throw std::runtime_error("failed materalization in and");
        }
        return Roaring(r);
    }

    /**
     * Computes the difference between two bitmaps and returns new bitmap.
     * The current bitmap and the provided bitmap are unchanged.
     */
    Roaring operator-(const Roaring &o) const {
        roaring_bitmap_t *r = roaring_bitmap_andnot(roaring, o.roaring);
        if (r == NULL) {
            throw std::runtime_error("failed materalization in andnot");
        }
        return Roaring(r);
    }

    /**
     * Computes the union between two bitmaps and returns new bitmap.
     * The current bitmap and the provided bitmap are unchanged.
     */
    Roaring operator|(const Roaring &o) const {
        roaring_bitmap_t *r = roaring_bitmap_or(roaring, o.roaring);
        if (r == NULL) {
            throw std::runtime_error("failed materalization in or");
        }
        return Roaring(r);
    }

    /**
     * Computes the symmetric union between two bitmaps and returns new bitmap.
     * The current bitmap and the provided bitmap are unchanged.
     */
    Roaring operator^(const Roaring &o) const {
        roaring_bitmap_t *r = roaring_bitmap_xor(roaring, o.roaring);
        if (r == NULL) {
            throw std::runtime_error("failed materalization in xor");
        }
        return Roaring(r);
    }

    /**
     * Whether or not we apply copy and write.
     */
    void setCopyOnWrite(bool val) { roaring->copy_on_write = val; }

    /**
     * Print the content of the bitmap
     */
    void printf() { roaring_bitmap_printf(roaring); }

    /**
     * Whether or not copy and write is active.
     */
    bool getCopyOnWrite() const { return roaring->copy_on_write; }

    /**
     * computes the logical or (union) between "n" bitmaps (referenced by a
     * pointer).
     */
    static Roaring fastunion(size_t n, const Roaring **inputs) {
        const roaring_bitmap_t **x =
            (const roaring_bitmap_t **)malloc(n * sizeof(roaring_bitmap_t *));
        if (x == NULL) {
            throw std::runtime_error("failed memory alloc in fastunion");
        }
        for (size_t k = 0; k < n; ++k) x[k] = inputs[k]->roaring;

        Roaring ans(NULL);
        ans.roaring = roaring_bitmap_or_many(n, x);
        if (ans.roaring == NULL) {
            throw std::runtime_error("failed memory alloc in fastunion");
        }
        free(x);
        return ans;
    }

    typedef  RoaringSetBitForwardIterator const_iterator;

    /**
    * Returns an iterator that can be used to access the position of the
    * set bits. The running time complexity of a full scan is proportional to the
    * number
    * of set bits: be aware that if you have long strings of 1s, this can be
    * very inefficient.
    *
    * It can be much faster to use the toArray method if you want to
    * retrieve the set bits.
    */
    const_iterator begin() const ;
    /*{
      return RoaringSetBitForwardIterator(*this);
    }*/

    /**
    * A bogus iterator that can be used together with begin()
    * for constructions such as for(auto i = b.begin();
    * i!=b.end(); ++i) {}
    */
    const_iterator end() const ; /*{
      return RoaringSetBitForwardIterator(*this, true);
    }*/

    roaring_bitmap_t *roaring;
};

/**
 * Used to go through the set bits. Not optimally fast, but convenient.
 */
class RoaringSetBitForwardIterator {
public:
  typedef std::forward_iterator_tag iterator_category;
  typedef uint32_t *pointer;
  typedef uint32_t &reference_type;
  typedef uint32_t value_type;
  typedef int32_t difference_type;
  typedef RoaringSetBitForwardIterator type_of_iterator;

  /**
   * Provides the location of the set bit.
   */
  value_type operator*() const {
    return i->current_value;
  }

  bool operator<(const type_of_iterator &o) {
    return i->current_value < *o;
  }

  bool operator<=(const type_of_iterator &o) {
    return i->current_value <= *o;
  }

  bool operator>(const type_of_iterator &o) {
    return i->current_value > *o;
  }

  bool operator>=(const type_of_iterator &o) {
    return i->current_value >= *o;
  }

  type_of_iterator &operator++() {// ++i, must returned inc. value
    roaring_advance_uint32_iterator(i);
    return *this;
  }

  type_of_iterator operator++(int) {// i++, must return orig. value
    RoaringSetBitForwardIterator orig(*this);
    roaring_advance_uint32_iterator(i);
    return orig;
  }

  bool operator==(const RoaringSetBitForwardIterator &o) {
    return i->current_value == *o;
  }

  bool operator!=(const RoaringSetBitForwardIterator &o) {
    return i->current_value != *o;
  }

  RoaringSetBitForwardIterator(const Roaring & parent, bool exhausted = false) : i(NULL) {
    if(exhausted) {
        i = (roaring_uint32_iterator_t *) malloc(sizeof(roaring_uint32_iterator_t));
        i->parent = parent.roaring;
        i->container_index = INT32_MAX;
        i->has_value = false;
        i->current_value = UINT32_MAX;
    } else {
      i = roaring_create_iterator(parent.roaring);
    }
  }

  virtual ~RoaringSetBitForwardIterator() {
    roaring_free_uint32_iterator(i);
    i = NULL;
  }

  RoaringSetBitForwardIterator(
      const RoaringSetBitForwardIterator &o)
      : i(NULL) {
    i = roaring_copy_uint32_iterator (o.i);
  }

  roaring_uint32_iterator_t *  i;
};

RoaringSetBitForwardIterator Roaring::begin() const {
      return RoaringSetBitForwardIterator(*this);
}

RoaringSetBitForwardIterator Roaring::end() const {
      return RoaringSetBitForwardIterator(*this, true);
}

#endif /* INCLUDE_ROARING_HH_ */
<|endoftext|>


<commit_before>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

// XLA-specific Shape Ops.

#include "tensorflow/compiler/tf2xla/kernels/shape_util.h"
#include "tensorflow/compiler/tf2xla/type_util.h"
#include "tensorflow/compiler/tf2xla/xla_helpers.h"
#include "tensorflow/compiler/tf2xla/xla_op_kernel.h"
#include "tensorflow/compiler/tf2xla/xla_op_registry.h"
#include "tensorflow/compiler/xla/client/xla_builder.h"
#include "tensorflow/core/framework/kernel_def_builder.h"
#include "tensorflow/core/framework/tensor_shape.h"
#include "tensorflow/core/kernels/bounds_check.h"

namespace tensorflow {
namespace {

class ShapeOp : public XlaOpKernel {
 public:
  explicit ShapeOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {
    OP_REQUIRES_OK(ctx, ctx->GetAttr("out_type", &out_dtype_));
  }

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape(0);
    Tensor shape_constant(out_dtype_, TensorShape({input_shape.dims()}));
    OP_REQUIRES_OK(ctx, TensorShapeToConstant(input_shape, &shape_constant));
    ctx->SetConstantOutput(0, shape_constant);
  }

 private:
  DataType out_dtype_;
};

REGISTER_XLA_OP(Name("Shape").CompilationOnly().IsMetadataOp(), ShapeOp);

class ShapeNOp : public XlaOpKernel {
 public:
  explicit ShapeNOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {
    OP_REQUIRES_OK(ctx, ctx->GetAttr("out_type", &out_dtype_));
  }

  void Compile(XlaOpKernelContext* ctx) override {
    for (int i = 0; i < ctx->num_inputs(); ++i) {
      const TensorShape input_shape = ctx->InputShape(i);
      Tensor shape_constant(out_dtype_, TensorShape({input_shape.dims()}));
      OP_REQUIRES_OK(ctx, TensorShapeToConstant(input_shape, &shape_constant));
      ctx->SetConstantOutput(i, shape_constant);
    }
  }

  bool IsExpensive() override { return false; }

 private:
  DataType out_dtype_;
};
REGISTER_XLA_OP(Name("ShapeN").CompilationOnly().IsMetadataOp(), ShapeNOp);

class RankOp : public XlaOpKernel {
 public:
  explicit RankOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {}

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape(0);
    const int rank = input_shape.dims();
    Tensor rank_constant(DT_INT32, TensorShape({}));
    rank_constant.scalar<int32>()() = rank;

    ctx->SetConstantOutput(0, rank_constant);
  }
};

REGISTER_XLA_OP(Name("Rank").CompilationOnly().IsMetadataOp(), RankOp);

class SizeOp : public XlaOpKernel {
 public:
  explicit SizeOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {}

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape(0);
    const int64 size = input_shape.num_elements();
    OP_REQUIRES(ctx, FastBoundsCheck(size, std::numeric_limits<int32>::max()),
                errors::InvalidArgument("Size does not work for tensors > "
                                        "int32 max."));
    Tensor size_constant(DT_INT32, TensorShape({}));
    size_constant.scalar<int32>()() = static_cast<int32>(size);

    ctx->SetConstantOutput(0, size_constant);
  }
};

REGISTER_XLA_OP(Name("Size").CompilationOnly().IsMetadataOp(), SizeOp);

class ExpandDimsOp : public XlaOpKernel {
 public:
  explicit ExpandDimsOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {}

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape("input");
    const TensorShape dim_shape = ctx->InputShape("dim");

    std::vector<int64> dims;
    OP_REQUIRES_OK(ctx, ctx->ConstantInputReshapedToIntVector("dim", &dims));
    OP_REQUIRES(ctx, dims.size() == 1,
                errors::InvalidArgument(absl::StrCat(
                    "dim input to ExpandDims must be a scalar; got ",
                    dim_shape.DebugString())));
    int dim = dims[0];

    OP_REQUIRES(ctx,
                (dim >= -1 - input_shape.dims() && dim <= input_shape.dims()),
                errors::InvalidArgument("Tried to expand dim index ", dim,
                                        " for tensor with ", input_shape.dims(),
                                        " dimensions."));

    auto existing_dims = input_shape.dim_sizes();
    // Safe - # elements in tensor dims bounded.
    const int existing_dims_size = static_cast<int>(existing_dims.size());
    std::vector<int64> new_shape(existing_dims_size);
    for (size_t i = 0; i < new_shape.size(); ++i) {
      new_shape[i] = existing_dims[i];
    }

    // We emulate numpy's interpretation of the dim axis when
    // -input.dims() >= dim <= input.dims().
    if (dim < 0) {
      dim += existing_dims.size() + 1;
    }

    // Clamp to the end if needed.
    dim = std::min<int32>(dim, existing_dims_size);
    new_shape.emplace(new_shape.begin() + dim, 1);

    ctx->SetOutput(0, xla::Reshape(ctx->Input("input"), new_shape));
  }
};
REGISTER_XLA_OP(Name("ExpandDims").CompileTimeConstantInput("dim"),
                ExpandDimsOp);

class SqueezeOp : public XlaOpKernel {
 public:
  explicit SqueezeOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {
    std::vector<int32> squeeze_dims;
    OP_REQUIRES_OK(ctx, ctx->GetAttr("squeeze_dims", &squeeze_dims));
    squeeze_dims_.insert(squeeze_dims.begin(), squeeze_dims.end());
  }

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape(0);
    auto existing_dims = input_shape.dim_sizes();
    int existing_dims_size = input_shape.dims();
    std::vector<int64> new_shape;

    std::unordered_set<int32> wrapped_squeeze_dims;
    wrapped_squeeze_dims.reserve(squeeze_dims_.size());
    // Validate squeeze dims against the input.
    for (int32 dim : squeeze_dims_) {
      OP_REQUIRES(ctx, (dim >= -input_shape.dims() && dim < input_shape.dims()),
                  errors::InvalidArgument("Tried to squeeze dim index ", dim,
                                          " for tensor with ",
                                          input_shape.dims(), " dimensions."));
      // If dim is < 0, we wrap around (-1 means the last element).
      if (dim < 0) {
        dim = existing_dims_size + dim;
      }

      wrapped_squeeze_dims.insert(dim);
    }

    for (int i = 0; i < existing_dims_size; ++i) {
      auto existing_dim = existing_dims[i];

      // If squeeze_set is non-empty, only squeeze those dimensions.
      if (!wrapped_squeeze_dims.empty()) {
        if (wrapped_squeeze_dims.count(i) > 0) {
          OP_REQUIRES(ctx, existing_dim == 1,
                      errors::InvalidArgument(
                          "Tried to explicitly squeeze dimension ", i,
                          " but dimension was not 1: ", existing_dim));
        } else {
          // This dimension is not being squeezed.
          new_shape.push_back(existing_dim);
        }
      } else {
        // Copy over all non-1-length dimensions.
        if (existing_dim != 1) {
          new_shape.push_back(existing_dim);
        }
      }
    }

    ctx->SetOutput(0, xla::Reshape(ctx->Input(0), new_shape));
  }

 private:
  std::unordered_set<int32> squeeze_dims_;
};

REGISTER_XLA_OP(Name("Squeeze"), SqueezeOp);

class ZerosLikeOp : public XlaOpKernel {
 public:
  explicit ZerosLikeOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {}

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape(0);

    auto zero = XlaHelpers::Zero(ctx->builder(), input_type(0));
    ctx->SetOutput(0, xla::Broadcast(zero, input_shape.dim_sizes()));
  }
};

REGISTER_XLA_OP(Name("ZerosLike"), ZerosLikeOp);

class OnesLikeOp : public XlaOpKernel {
 public:
  explicit OnesLikeOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {}

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape(0);

    auto one = XlaHelpers::One(ctx->builder(), input_type(0));
    ctx->SetOutput(0, xla::Broadcast(one, input_shape.dim_sizes()));
  }
};

REGISTER_XLA_OP(Name("OnesLike"), OnesLikeOp);

}  // namespace
}  // namespace tensorflow
<commit_msg>[Resubmit][TF2XLA] Use dynamic dimension in SizeOp.<commit_after>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

// XLA-specific Shape Ops.

#include "tensorflow/compiler/tf2xla/kernels/shape_util.h"
#include "tensorflow/compiler/tf2xla/type_util.h"
#include "tensorflow/compiler/tf2xla/xla_helpers.h"
#include "tensorflow/compiler/tf2xla/xla_op_kernel.h"
#include "tensorflow/compiler/tf2xla/xla_op_registry.h"
#include "tensorflow/compiler/xla/client/lib/constants.h"
#include "tensorflow/compiler/xla/client/xla_builder.h"
#include "tensorflow/core/framework/kernel_def_builder.h"
#include "tensorflow/core/framework/tensor_shape.h"
#include "tensorflow/core/kernels/bounds_check.h"

namespace tensorflow {
namespace {

class ShapeOp : public XlaOpKernel {
 public:
  explicit ShapeOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {
    OP_REQUIRES_OK(ctx, ctx->GetAttr("out_type", &out_dtype_));
  }

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape(0);
    Tensor shape_constant(out_dtype_, TensorShape({input_shape.dims()}));
    OP_REQUIRES_OK(ctx, TensorShapeToConstant(input_shape, &shape_constant));
    ctx->SetConstantOutput(0, shape_constant);
  }

 private:
  DataType out_dtype_;
};

REGISTER_XLA_OP(Name("Shape").CompilationOnly().IsMetadataOp(), ShapeOp);

class ShapeNOp : public XlaOpKernel {
 public:
  explicit ShapeNOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {
    OP_REQUIRES_OK(ctx, ctx->GetAttr("out_type", &out_dtype_));
  }

  void Compile(XlaOpKernelContext* ctx) override {
    for (int i = 0; i < ctx->num_inputs(); ++i) {
      const TensorShape input_shape = ctx->InputShape(i);
      Tensor shape_constant(out_dtype_, TensorShape({input_shape.dims()}));
      OP_REQUIRES_OK(ctx, TensorShapeToConstant(input_shape, &shape_constant));
      ctx->SetConstantOutput(i, shape_constant);
    }
  }

  bool IsExpensive() override { return false; }

 private:
  DataType out_dtype_;
};
REGISTER_XLA_OP(Name("ShapeN").CompilationOnly().IsMetadataOp(), ShapeNOp);

class RankOp : public XlaOpKernel {
 public:
  explicit RankOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {}

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape(0);
    const int rank = input_shape.dims();
    Tensor rank_constant(DT_INT32, TensorShape({}));
    rank_constant.scalar<int32>()() = rank;

    ctx->SetConstantOutput(0, rank_constant);
  }
};

REGISTER_XLA_OP(Name("Rank").CompilationOnly().IsMetadataOp(), RankOp);

class SizeOp : public XlaOpKernel {
 public:
  explicit SizeOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {}

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape(0);
    OP_REQUIRES(ctx,
                FastBoundsCheck(input_shape.num_elements(),
                                std::numeric_limits<int32>::max()),
                errors::InvalidArgument("Size does not work for tensors > "
                                        "int32 max."));
    Tensor size_constant(DT_INT32, TensorShape({}));
    const int rank = input_shape.dims();
    xla::XlaBuilder* builder = ctx->builder();
    auto size = xla::One(builder, xla::U32);
    for (int64 i = 0; i < rank; ++i) {
      size = xla::Mul(size, xla::GetDimensionSize(ctx->Input(0), i));
    }
    size = xla::ConvertElementType(size, xla::S32);
    ctx->SetOutput(0, size);
  }
};

REGISTER_XLA_OP(Name("Size").CompilationOnly().IsMetadataOp(), SizeOp);

class ExpandDimsOp : public XlaOpKernel {
 public:
  explicit ExpandDimsOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {}

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape("input");
    const TensorShape dim_shape = ctx->InputShape("dim");

    std::vector<int64> dims;
    OP_REQUIRES_OK(ctx, ctx->ConstantInputReshapedToIntVector("dim", &dims));
    OP_REQUIRES(ctx, dims.size() == 1,
                errors::InvalidArgument(absl::StrCat(
                    "dim input to ExpandDims must be a scalar; got ",
                    dim_shape.DebugString())));
    int dim = dims[0];

    OP_REQUIRES(ctx,
                (dim >= -1 - input_shape.dims() && dim <= input_shape.dims()),
                errors::InvalidArgument("Tried to expand dim index ", dim,
                                        " for tensor with ", input_shape.dims(),
                                        " dimensions."));

    auto existing_dims = input_shape.dim_sizes();
    // Safe - # elements in tensor dims bounded.
    const int existing_dims_size = static_cast<int>(existing_dims.size());
    std::vector<int64> new_shape(existing_dims_size);
    for (size_t i = 0; i < new_shape.size(); ++i) {
      new_shape[i] = existing_dims[i];
    }

    // We emulate numpy's interpretation of the dim axis when
    // -input.dims() >= dim <= input.dims().
    if (dim < 0) {
      dim += existing_dims.size() + 1;
    }

    // Clamp to the end if needed.
    dim = std::min<int32>(dim, existing_dims_size);
    new_shape.emplace(new_shape.begin() + dim, 1);

    ctx->SetOutput(0, xla::Reshape(ctx->Input("input"), new_shape));
  }
};
REGISTER_XLA_OP(Name("ExpandDims").CompileTimeConstantInput("dim"),
                ExpandDimsOp);

class SqueezeOp : public XlaOpKernel {
 public:
  explicit SqueezeOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {
    std::vector<int32> squeeze_dims;
    OP_REQUIRES_OK(ctx, ctx->GetAttr("squeeze_dims", &squeeze_dims));
    squeeze_dims_.insert(squeeze_dims.begin(), squeeze_dims.end());
  }

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape(0);
    auto existing_dims = input_shape.dim_sizes();
    int existing_dims_size = input_shape.dims();
    std::vector<int64> new_shape;

    std::unordered_set<int32> wrapped_squeeze_dims;
    wrapped_squeeze_dims.reserve(squeeze_dims_.size());
    // Validate squeeze dims against the input.
    for (int32 dim : squeeze_dims_) {
      OP_REQUIRES(ctx, (dim >= -input_shape.dims() && dim < input_shape.dims()),
                  errors::InvalidArgument("Tried to squeeze dim index ", dim,
                                          " for tensor with ",
                                          input_shape.dims(), " dimensions."));
      // If dim is < 0, we wrap around (-1 means the last element).
      if (dim < 0) {
        dim = existing_dims_size + dim;
      }

      wrapped_squeeze_dims.insert(dim);
    }

    for (int i = 0; i < existing_dims_size; ++i) {
      auto existing_dim = existing_dims[i];

      // If squeeze_set is non-empty, only squeeze those dimensions.
      if (!wrapped_squeeze_dims.empty()) {
        if (wrapped_squeeze_dims.count(i) > 0) {
          OP_REQUIRES(ctx, existing_dim == 1,
                      errors::InvalidArgument(
                          "Tried to explicitly squeeze dimension ", i,
                          " but dimension was not 1: ", existing_dim));
        } else {
          // This dimension is not being squeezed.
          new_shape.push_back(existing_dim);
        }
      } else {
        // Copy over all non-1-length dimensions.
        if (existing_dim != 1) {
          new_shape.push_back(existing_dim);
        }
      }
    }

    ctx->SetOutput(0, xla::Reshape(ctx->Input(0), new_shape));
  }

 private:
  std::unordered_set<int32> squeeze_dims_;
};

REGISTER_XLA_OP(Name("Squeeze"), SqueezeOp);

class ZerosLikeOp : public XlaOpKernel {
 public:
  explicit ZerosLikeOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {}

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape(0);

    auto zero = XlaHelpers::Zero(ctx->builder(), input_type(0));
    ctx->SetOutput(0, xla::Broadcast(zero, input_shape.dim_sizes()));
  }
};

REGISTER_XLA_OP(Name("ZerosLike"), ZerosLikeOp);

class OnesLikeOp : public XlaOpKernel {
 public:
  explicit OnesLikeOp(OpKernelConstruction* ctx) : XlaOpKernel(ctx) {}

  void Compile(XlaOpKernelContext* ctx) override {
    const TensorShape input_shape = ctx->InputShape(0);

    auto one = XlaHelpers::One(ctx->builder(), input_type(0));
    ctx->SetOutput(0, xla::Broadcast(one, input_shape.dim_sizes()));
  }
};

REGISTER_XLA_OP(Name("OnesLike"), OnesLikeOp);

}  // namespace
}  // namespace tensorflow
<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <libtorrent/natpmp.hpp>
#include <libtorrent/io.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <asio/ip/host_name.hpp>

using boost::bind;
using namespace libtorrent;
using boost::posix_time::microsec_clock;

natpmp::natpmp(io_service& ios, portmap_callback_t const& cb)
	: m_callback(cb)
	, m_currently_mapping(-1)
	, m_retry_count(0)
	, m_socket(ios)
	, m_send_timer(ios)
	, m_refresh_timer(ios)
	, m_disabled(false)
{
	m_mappings[0].protocol = 2; // tcp
	m_mappings[1].protocol = 1; // udp

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log.open("natpmp.log", std::ios::in | std::ios::out | std::ios::trunc);
#endif

	udp::resolver r(ios);
	udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(), "0"));
	for (;i != udp::resolver_iterator(); ++i)
	{
		if (i->endpoint().address().is_v4()) break;
	}

	if (i == udp::resolver_iterator())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << "local host name did not resolve to an IPv4 address. "
			"disabling NAT-PMP" << std::endl;
#endif
		m_disabled = true;
		return;
	}

	address_v4 local = i->endpoint().address().to_v4();
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << to_simple_string(microsec_clock::universal_time())
		<< " local ip: " << local.to_string() << std::endl;
#endif

	if ((local.to_ulong() & 0xff000000) != 0x0a000000
		&& (local.to_ulong() & 0xfff00000) != 0xac100000
		&& (local.to_ulong() & 0xffff0000) != 0xaca80000)
	{
		// the local address seems to be an external
		// internet address. Assume it is not behind a NAT
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << "not on a NAT. disabling NAT-PMP" << std::endl;
#endif
		m_disabled = true;
		return;
	}

	// assume the router is located on the local
	// network as x.x.x.1
	// TODO: find a better way to figure out the router IP
	m_nat_endpoint = udp::endpoint(
		address_v4((local.to_ulong() & 0xffffff00) | 1), 5351);

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << "assuming router is at: " << m_nat_endpoint.address().to_string() << std::endl;
#endif

	m_socket.open(udp::v4());
	m_socket.bind(udp::endpoint());
}

void natpmp::set_mappings(int tcp, int udp)
{
	if (m_disabled) return;
	update_mapping(0, tcp);
	update_mapping(1, udp);
}

void natpmp::update_mapping(int i, int port)
{
	natpmp::mapping& m = m_mappings[i];
	if (port <= 0) return;
	if (m.local_port != port)
		m.need_update = true;

	m.local_port = port;
	// prefer the same external port as the local port
	if (m.external_port == 0) m.external_port = port;

	if (m_currently_mapping == -1)
	{
		// the socket is not currently in use
		// send out a mapping request
		m_retry_count = 0;
		send_map_request(i);
		m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16)
			, m_remote, bind(&natpmp::on_reply, this, _1, _2));
	}
}

void natpmp::send_map_request(int i) try
{
	using namespace libtorrent::detail;
	using boost::posix_time::milliseconds;

	assert(m_currently_mapping == -1
		|| m_currently_mapping == i);
	m_currently_mapping = i;
	mapping& m = m_mappings[i];
	char buf[12];
	char* out = buf;
	write_uint8(0, out); // NAT-PMP version
	write_uint8(m.protocol, out); // map "protocol"
	write_uint16(0, out); // reserved
	write_uint16(m.local_port, out); // private port
	write_uint16(m.external_port, out); // requested public port
	int ttl = m.external_port == 0 ? 0 : 3600;
	write_uint32(ttl, out); // port mapping lifetime

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << to_simple_string(microsec_clock::universal_time())
		<< " ==> port map request: " << (m.protocol == 1 ? "udp" : "tcp")
		<< " local: " << m.local_port << " external: " << m.external_port
		<< " ttl: " << ttl << std::endl;
#endif

	m_socket.send_to(asio::buffer(buf, 12), m_nat_endpoint);
	// linear back-off instead of exponential
	++m_retry_count;
	m_send_timer.expires_from_now(milliseconds(250 * m_retry_count));
	m_send_timer.async_wait(bind(&natpmp::resend_request, this, i, _1));
}
catch (std::exception& e)
{
	std::string err = e.what();
}

void natpmp::resend_request(int i, asio::error_code const& e)
{
	using boost::posix_time::hours;
	if (e) return;
	if (m_retry_count >= 9)
	{
		m_mappings[i].need_update = false;
		// try again in two hours
		m_mappings[i].expires
			= boost::posix_time::second_clock::universal_time() + hours(2);
		return;
	}
	send_map_request(i);
}

void natpmp::on_reply(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	using namespace libtorrent::detail;
	using boost::posix_time::seconds;
	if (e) return;

	try
	{

		if (m_remote != m_nat_endpoint)
		{
			m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16)
				, m_remote, bind(&natpmp::on_reply, this, _1, _2));
			return;
		}

		m_send_timer.cancel();

		assert(m_currently_mapping >= 0);
		int i = m_currently_mapping;
		mapping& m = m_mappings[i];

		char* in = m_response_buffer;
		int version = read_uint8(in);
		int cmd = read_uint8(in);
		int result = read_uint16(in);
		int time = read_uint32(in);
		int private_port = read_uint16(in);
		int public_port = read_uint16(in);
		int lifetime = read_uint32(in);

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << to_simple_string(microsec_clock::universal_time())
			<< " <== port map response: " << (cmd - 128 == 1 ? "udp" : "tcp")
			<< " local: " << private_port << " external: " << public_port
			<< " ttl: " << lifetime << std::endl;
#endif

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		if (version != 0)
		{
			m_log << "*** unexpected version: " << version << std::endl;
		}
#endif

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		if (private_port != m.local_port)
		{
			m_log << "*** unexpected local port: " << private_port << std::endl;
		}
#endif

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		if (cmd != 128 + m.protocol)
		{
			m_log << "*** unexpected protocol: " << (cmd - 128) << std::endl;
		}
#endif

		if (public_port == 0 || lifetime == 0)
		{
			// this means the mapping was
			// successfully closed
			m.local_port = 0;
		}
		else
		{
			m.expires = boost::posix_time::second_clock::universal_time()
				+ seconds(int(lifetime * 0.7f));
			m.external_port = public_port;
		}

		if (result != 0)
		{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
			m_log << "*** ERROR: " << result << std::endl;
#endif
			std::stringstream errmsg;
			errmsg << "NAT router reports error (" << result << ") ";
			switch (result)
			{
				case 1: errmsg << "Unsupported protocol version"; break;
				case 2: errmsg << "Not authorized to create port map (enable NAT-PMP on your router)"; break;
				case 3: errmsg << "Network failure"; break;
				case 4: errmsg << "Out of resources"; break;
				case 5: errmsg << "Unsupported opcpde"; break;
			}
			throw std::runtime_error(errmsg.str());
		}

		int tcp_port = 0;
		int udp_port = 0;
		if (m.protocol == 1) udp_port = m.external_port;
		else tcp_port = public_port;
		m_callback(tcp_port, udp_port, "");
	}
	catch (std::exception& e)
	{
		using boost::posix_time::hours;
		// try again in two hours
		m_mappings[m_currently_mapping].expires
			= boost::posix_time::second_clock::universal_time() + hours(2);
		m_callback(0, 0, e.what());
	}
	int i = m_currently_mapping;
	m_currently_mapping = -1;
	m_mappings[i].need_update = false;
	update_expiration_timer();
	try_next_mapping(i);
}

void natpmp::update_expiration_timer()
{
	using boost::posix_time::seconds;
	boost::posix_time::ptime now = boost::posix_time::second_clock::universal_time();
	boost::posix_time::ptime min_expire = now + seconds(3600);
	int min_index = -1;
	for (int i = 0; i < 2; ++i)
		if (m_mappings[i].expires < min_expire
			&& m_mappings[i].local_port != 0)
		{
			min_expire = m_mappings[i].expires;
			min_index = i;
		}

	if (min_index >= 0)
	{
		m_refresh_timer.expires_from_now(min_expire - now);
		m_refresh_timer.async_wait(bind(&natpmp::mapping_expired, this, _1, min_index));
	}
}

void natpmp::mapping_expired(asio::error_code const& e, int i)
{
	if (e) return;
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << "*** mapping " << i << " expired, updating" << std::endl;
#endif
	refresh_mapping(i);
}

void natpmp::refresh_mapping(int i)
{
	m_mappings[i].need_update = true;
	if (m_currently_mapping == -1)
	{
		// the socket is not currently in use
		// send out a mapping request
		m_retry_count = 0;
		send_map_request(i);
		m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16)
			, m_remote, bind(&natpmp::on_reply, this, _1, _2));
	}
}

void natpmp::try_next_mapping(int i)
{
	++i;
	if (i >= 2) i = 0;
	if (m_mappings[i].need_update)
		refresh_mapping(i);
}

void natpmp::close()
{
	if (m_disabled) return;
	for (int i = 0; i < 2; ++i)
	{
		if (m_mappings[i].local_port == 0)
			continue;
		m_mappings[i].external_port = 0;
		refresh_mapping(i);
	}
}

<commit_msg>fixes incorrect local IP address check<commit_after>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <libtorrent/natpmp.hpp>
#include <libtorrent/io.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <asio/ip/host_name.hpp>

using boost::bind;
using namespace libtorrent;
using boost::posix_time::microsec_clock;

natpmp::natpmp(io_service& ios, portmap_callback_t const& cb)
	: m_callback(cb)
	, m_currently_mapping(-1)
	, m_retry_count(0)
	, m_socket(ios)
	, m_send_timer(ios)
	, m_refresh_timer(ios)
	, m_disabled(false)
{
	m_mappings[0].protocol = 2; // tcp
	m_mappings[1].protocol = 1; // udp

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log.open("natpmp.log", std::ios::in | std::ios::out | std::ios::trunc);
#endif

	udp::resolver r(ios);
	udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(), "0"));
	for (;i != udp::resolver_iterator(); ++i)
	{
		if (i->endpoint().address().is_v4()) break;
	}

	if (i == udp::resolver_iterator())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << "local host name did not resolve to an IPv4 address. "
			"disabling NAT-PMP" << std::endl;
#endif
		m_disabled = true;
		return;
	}

	address_v4 local = i->endpoint().address().to_v4();
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << to_simple_string(microsec_clock::universal_time())
		<< " local ip: " << local.to_string() << std::endl;
#endif

	if ((local.to_ulong() & 0xff000000) != 0x0a000000
		&& (local.to_ulong() & 0xfff00000) != 0xac100000
		&& (local.to_ulong() & 0xffff0000) != 0xc0a80000)
	{
		// the local address seems to be an external
		// internet address. Assume it is not behind a NAT
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << "not on a NAT. disabling NAT-PMP" << std::endl;
#endif
		m_disabled = true;
		return;
	}

	// assume the router is located on the local
	// network as x.x.x.1
	// TODO: find a better way to figure out the router IP
	m_nat_endpoint = udp::endpoint(
		address_v4((local.to_ulong() & 0xffffff00) | 1), 5351);

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << "assuming router is at: " << m_nat_endpoint.address().to_string() << std::endl;
#endif

	m_socket.open(udp::v4());
	m_socket.bind(udp::endpoint());
}

void natpmp::set_mappings(int tcp, int udp)
{
	if (m_disabled) return;
	update_mapping(0, tcp);
	update_mapping(1, udp);
}

void natpmp::update_mapping(int i, int port)
{
	natpmp::mapping& m = m_mappings[i];
	if (port <= 0) return;
	if (m.local_port != port)
		m.need_update = true;

	m.local_port = port;
	// prefer the same external port as the local port
	if (m.external_port == 0) m.external_port = port;

	if (m_currently_mapping == -1)
	{
		// the socket is not currently in use
		// send out a mapping request
		m_retry_count = 0;
		send_map_request(i);
		m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16)
			, m_remote, bind(&natpmp::on_reply, this, _1, _2));
	}
}

void natpmp::send_map_request(int i) try
{
	using namespace libtorrent::detail;
	using boost::posix_time::milliseconds;

	assert(m_currently_mapping == -1
		|| m_currently_mapping == i);
	m_currently_mapping = i;
	mapping& m = m_mappings[i];
	char buf[12];
	char* out = buf;
	write_uint8(0, out); // NAT-PMP version
	write_uint8(m.protocol, out); // map "protocol"
	write_uint16(0, out); // reserved
	write_uint16(m.local_port, out); // private port
	write_uint16(m.external_port, out); // requested public port
	int ttl = m.external_port == 0 ? 0 : 3600;
	write_uint32(ttl, out); // port mapping lifetime

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << to_simple_string(microsec_clock::universal_time())
		<< " ==> port map request: " << (m.protocol == 1 ? "udp" : "tcp")
		<< " local: " << m.local_port << " external: " << m.external_port
		<< " ttl: " << ttl << std::endl;
#endif

	m_socket.send_to(asio::buffer(buf, 12), m_nat_endpoint);
	// linear back-off instead of exponential
	++m_retry_count;
	m_send_timer.expires_from_now(milliseconds(250 * m_retry_count));
	m_send_timer.async_wait(bind(&natpmp::resend_request, this, i, _1));
}
catch (std::exception& e)
{
	std::string err = e.what();
}

void natpmp::resend_request(int i, asio::error_code const& e)
{
	using boost::posix_time::hours;
	if (e) return;
	if (m_retry_count >= 9)
	{
		m_mappings[i].need_update = false;
		// try again in two hours
		m_mappings[i].expires
			= boost::posix_time::second_clock::universal_time() + hours(2);
		return;
	}
	send_map_request(i);
}

void natpmp::on_reply(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	using namespace libtorrent::detail;
	using boost::posix_time::seconds;
	if (e) return;

	try
	{

		if (m_remote != m_nat_endpoint)
		{
			m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16)
				, m_remote, bind(&natpmp::on_reply, this, _1, _2));
			return;
		}

		m_send_timer.cancel();

		assert(m_currently_mapping >= 0);
		int i = m_currently_mapping;
		mapping& m = m_mappings[i];

		char* in = m_response_buffer;
		int version = read_uint8(in);
		int cmd = read_uint8(in);
		int result = read_uint16(in);
		int time = read_uint32(in);
		int private_port = read_uint16(in);
		int public_port = read_uint16(in);
		int lifetime = read_uint32(in);

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << to_simple_string(microsec_clock::universal_time())
			<< " <== port map response: " << (cmd - 128 == 1 ? "udp" : "tcp")
			<< " local: " << private_port << " external: " << public_port
			<< " ttl: " << lifetime << std::endl;
#endif

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		if (version != 0)
		{
			m_log << "*** unexpected version: " << version << std::endl;
		}
#endif

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		if (private_port != m.local_port)
		{
			m_log << "*** unexpected local port: " << private_port << std::endl;
		}
#endif

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		if (cmd != 128 + m.protocol)
		{
			m_log << "*** unexpected protocol: " << (cmd - 128) << std::endl;
		}
#endif

		if (public_port == 0 || lifetime == 0)
		{
			// this means the mapping was
			// successfully closed
			m.local_port = 0;
		}
		else
		{
			m.expires = boost::posix_time::second_clock::universal_time()
				+ seconds(int(lifetime * 0.7f));
			m.external_port = public_port;
		}

		if (result != 0)
		{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
			m_log << "*** ERROR: " << result << std::endl;
#endif
			std::stringstream errmsg;
			errmsg << "NAT router reports error (" << result << ") ";
			switch (result)
			{
				case 1: errmsg << "Unsupported protocol version"; break;
				case 2: errmsg << "Not authorized to create port map (enable NAT-PMP on your router)"; break;
				case 3: errmsg << "Network failure"; break;
				case 4: errmsg << "Out of resources"; break;
				case 5: errmsg << "Unsupported opcpde"; break;
			}
			throw std::runtime_error(errmsg.str());
		}

		int tcp_port = 0;
		int udp_port = 0;
		if (m.protocol == 1) udp_port = m.external_port;
		else tcp_port = public_port;
		m_callback(tcp_port, udp_port, "");
	}
	catch (std::exception& e)
	{
		using boost::posix_time::hours;
		// try again in two hours
		m_mappings[m_currently_mapping].expires
			= boost::posix_time::second_clock::universal_time() + hours(2);
		m_callback(0, 0, e.what());
	}
	int i = m_currently_mapping;
	m_currently_mapping = -1;
	m_mappings[i].need_update = false;
	update_expiration_timer();
	try_next_mapping(i);
}

void natpmp::update_expiration_timer()
{
	using boost::posix_time::seconds;
	boost::posix_time::ptime now = boost::posix_time::second_clock::universal_time();
	boost::posix_time::ptime min_expire = now + seconds(3600);
	int min_index = -1;
	for (int i = 0; i < 2; ++i)
		if (m_mappings[i].expires < min_expire
			&& m_mappings[i].local_port != 0)
		{
			min_expire = m_mappings[i].expires;
			min_index = i;
		}

	if (min_index >= 0)
	{
		m_refresh_timer.expires_from_now(min_expire - now);
		m_refresh_timer.async_wait(bind(&natpmp::mapping_expired, this, _1, min_index));
	}
}

void natpmp::mapping_expired(asio::error_code const& e, int i)
{
	if (e) return;
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << "*** mapping " << i << " expired, updating" << std::endl;
#endif
	refresh_mapping(i);
}

void natpmp::refresh_mapping(int i)
{
	m_mappings[i].need_update = true;
	if (m_currently_mapping == -1)
	{
		// the socket is not currently in use
		// send out a mapping request
		m_retry_count = 0;
		send_map_request(i);
		m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16)
			, m_remote, bind(&natpmp::on_reply, this, _1, _2));
	}
}

void natpmp::try_next_mapping(int i)
{
	++i;
	if (i >= 2) i = 0;
	if (m_mappings[i].need_update)
		refresh_mapping(i);
}

void natpmp::close()
{
	if (m_disabled) return;
	for (int i = 0; i < 2; ++i)
	{
		if (m_mappings[i].local_port == 0)
			continue;
		m_mappings[i].external_port = 0;
		refresh_mapping(i);
	}
}

<|endoftext|>