<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <glog/logging.h>

#include <iostream>
#include <string>

#include <mesos/resources.hpp>
#include <mesos/scheduler.hpp>

#include <stout/flags.hpp>
#include <stout/foreach.hpp>
#include <stout/option.hpp>
#include <stout/os.hpp>
#include <stout/path.hpp>
#include <stout/stringify.hpp>

using namespace mesos;

using std::string;
using std::vector;

// NOTE: Per-task resources are nominal because all of the resources for the
// container are provisioned when the executor is created. The executor can
// run multiple tasks at once, but uses a constant amount of resources
// regardless of the number of tasks.
const double CPUS_PER_TASK = 0.001;
const int32_t MEM_PER_TASK = 1;

const double CPUS_PER_EXECUTOR = 0.1;
const int32_t MEM_PER_EXECUTOR = 32;

// This scheduler picks one slave and repeatedly launches sleep tasks on it,
// using a single multi-task executor. If the slave or executor fails, the
// scheduler will pick another slave and continue launching sleep tasks.
class LongLivedScheduler : public Scheduler
{
public:
  explicit LongLivedScheduler(const ExecutorInfo& _executor)
    : executor(_executor),
      taskResources(Resources::parse(
          "cpus:" + stringify(CPUS_PER_TASK) +
          ";mem:" + stringify(MEM_PER_TASK)).get()),
      tasksLaunched(0) {}

  virtual ~LongLivedScheduler() {}

  virtual void registered(SchedulerDriver*,
                          const FrameworkID&,
                          const MasterInfo&)
  {
    LOG(INFO) << "Registered!";
  }

  virtual void reregistered(SchedulerDriver*, const MasterInfo& masterInfo)
  {
    LOG(INFO) << "Re-registered!";
  }

  virtual void disconnected(SchedulerDriver* driver)
  {
    LOG(INFO) << "Disconnected!";
  }

  virtual void resourceOffers(SchedulerDriver* driver,
                              const vector<Offer>& offers)
  {
    static const Resources EXECUTOR_RESOURCES = Resources(executor.resources());

    foreach (const Offer& offer, offers) {
      if (slaveId.isNone()) {
        // No active executor running in the cluster.
        // Launch a new task with executor.

        if (Resources(offer.resources()).flatten()
            .contains(EXECUTOR_RESOURCES + taskResources)) {
          LOG(INFO)
            << "Starting executor and task " << tasksLaunched
            << " on " << offer.hostname();

          launchTask(driver, offer);

          slaveId = offer.slave_id();
        } else {
          declineOffer(driver, offer);
        }
      } else if (slaveId == offer.slave_id()) {
        // Offer from the same slave that has an active executor.
        // Launch more tasks on that executor.

        if (Resources(offer.resources()).flatten().contains(taskResources)) {
          LOG(INFO)
            << "Starting task " << tasksLaunched << " on " << offer.hostname();

          launchTask(driver, offer);
        } else {
          declineOffer(driver, offer);
        }
      } else {
        // We have an active executor but this offer comes from a
        // different slave; decline the offer.
        declineOffer(driver, offer);
      }
    }
  }

  virtual void offerRescinded(SchedulerDriver* driver,
                              const OfferID& offerId) {}

  virtual void statusUpdate(SchedulerDriver* driver, const TaskStatus& status)
  {
    LOG(INFO)
      << "Task " << status.task_id().value()
      << " is in state " << TaskState_Name(status.state())
      << (status.has_message() ? " with message: " + status.message() : "");
  }

  virtual void frameworkMessage(SchedulerDriver* driver,
                                const ExecutorID& executorId,
                                const SlaveID& slaveId,
                                const string& data) {}

  virtual void slaveLost(SchedulerDriver* driver, const SlaveID& _slaveId)
  {
    LOG(INFO) << "Slave lost: " << _slaveId;

    if (slaveId == _slaveId) {
      slaveId = None();
    }
  }

  virtual void executorLost(SchedulerDriver* driver,
                            const ExecutorID& executorId,
                            const SlaveID& _slaveId,
                            int status)
  {
    LOG(INFO)
      << "Executor '" << executorId << "' lost on slave "
      << _slaveId << " with status: " << status;

    slaveId = None();
  }

  virtual void error(SchedulerDriver* driver, const string& message) {}

private:
  // Helper to decline an offer.
  void declineOffer(SchedulerDriver* driver, const Offer& offer)
  {
    Filters filters;
    filters.set_refuse_seconds(600);

    driver->declineOffer(offer.id(), filters);
  }

  // Helper to launch a task using an offer.
  void launchTask(SchedulerDriver* driver, const Offer& offer)
  {
    int taskId = tasksLaunched++;

    TaskInfo task;
    task.set_name("Task " + stringify(taskId));
    task.mutable_task_id()->set_value(stringify(taskId));
    task.mutable_slave_id()->MergeFrom(offer.slave_id());
    task.mutable_resources()->CopyFrom(taskResources);
    task.mutable_executor()->CopyFrom(executor);

    driver->launchTasks(offer.id(), {task});
  }

  const ExecutorInfo executor;
  const Resources taskResources;
  string uri;
  int tasksLaunched;

  // The slave that is running the long-lived-executor.
  // Unless that slave/executor dies, this framework will not launch
  // an executor on any other slave.
  Option<SlaveID> slaveId;
};

class Flags : public flags::FlagsBase
{
public:
  Flags()
  {
    add(&master,
        "master",
        "Master to connect to.",
        [](const Option<string>& value) -> Option<Error> {
          if (value.isNone()) {
            return Error("Missing --master");
          }

          return None();
        });

    add(&build_dir,
        "build_dir",
        "The build directory of Mesos. If set, the framework will assume\n"
        "that the executor, framework, and agent(s) all live on the same\n"
        "machine.");

    add(&executor_uri,
        "executor_uri",
        "URI the fetcher should use to get the executor.");

    add(&executor_command,
        "executor_command",
        "The command that should be used to start the executor.\n"
        "This will override the value set by `--build_dir`.");

    add(&checkpoint,
        "checkpoint",
        "Whether this framework should be checkpointed.",
        false);
  }

  Option<string> master;

  // Flags for specifying the executor binary.
  Option<string> build_dir;
  Option<string> executor_uri;
  Option<string> executor_command;

  bool checkpoint;
};

int main(int argc, char** argv)
{
  Flags flags;
  Try<Nothing> load = flags.load("MESOS_", argc, argv);

  if (load.isError()) {
    EXIT(EXIT_FAILURE) << flags.usage(load.error());
  }

  const Resources resources = Resources::parse(
      "cpus:" + stringify(CPUS_PER_EXECUTOR) +
      ";mem:" + stringify(MEM_PER_EXECUTOR)).get();

  ExecutorInfo executor;
  executor.mutable_executor_id()->set_value("default");
  executor.mutable_resources()->CopyFrom(resources);
  executor.set_name("Long Lived Executor (C++)");
  executor.set_source("cpp_long_lived_framework");

  // Determine the command to run the executor based on three possibilities:
  //   1) `--executor_command` was set, which overrides the below cases.
  //   2) We are in the Mesos build directory, so the targeted executable
  //      is actually a libtool wrapper script.
  //   3) We have not detected the Mesos build directory, so assume the
  //      executor is in the same directory as the framework.
  string command;

  // Find this executable's directory to locate executor.
  if (flags.executor_command.isSome()) {
    command = flags.executor_command.get();
  } else if (flags.build_dir.isSome()) {
    command = path::join(
        flags.build_dir.get(), "src", "long-lived-executor");
  } else {
    command = path::join(
        os::realpath(Path(argv[0]).dirname()).get(),
        "long-lived-executor");
  }

  executor.mutable_command()->set_value(command);

  // Copy `--executor_uri` into the command.
  if (flags.executor_uri.isSome()) {
    mesos::CommandInfo::URI* uri = executor.mutable_command()->add_uris();
    uri->set_value(flags.executor_uri.get());
    uri->set_executable(true);
  }

  LongLivedScheduler scheduler(executor);

  FrameworkInfo framework;
  framework.set_user(os::user().get());
  framework.set_name("Long Lived Framework (C++)");
  framework.set_checkpoint(flags.checkpoint);

  MesosSchedulerDriver* driver;

  // TODO(josephw): Refactor these into a common set of flags.
  if (os::getenv("MESOS_AUTHENTICATE").isSome()) {
    LOG(INFO) << "Enabling authentication for the framework";

    Option<string> value = os::getenv("DEFAULT_PRINCIPAL");
    if (value.isNone()) {
      EXIT(EXIT_FAILURE)
        << "Expecting authentication principal in the environment";
    }

    Credential credential;
    credential.set_principal(value.get());

    framework.set_principal(value.get());

    value = os::getenv("DEFAULT_SECRET");
    if (value.isNone()) {
      EXIT(EXIT_FAILURE)
        << "Expecting authentication secret in the environment";
    }

    credential.set_secret(value.get());

    driver = new MesosSchedulerDriver(
        &scheduler, framework, flags.master.get(), credential);
  } else {
    framework.set_principal("long-lived-framework-cpp");

    driver = new MesosSchedulerDriver(
        &scheduler, framework, flags.master.get());
  }

  int status = driver->run() == DRIVER_STOPPED ? 0 : 1;

  // Ensure that the driver process terminates.
  driver->stop();

  delete driver;
  return status;
}
<commit_msg>Added some metrics to the long-lived-framework example.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <glog/logging.h>

#include <iostream>
#include <string>

#include <mesos/resources.hpp>
#include <mesos/scheduler.hpp>

#include <process/clock.hpp>
#include <process/defer.hpp>
#include <process/help.hpp>
#include <process/http.hpp>
#include <process/process.hpp>
#include <process/protobuf.hpp>
#include <process/time.hpp>

#include <process/metrics/counter.hpp>
#include <process/metrics/gauge.hpp>
#include <process/metrics/metrics.hpp>

#include <stout/flags.hpp>
#include <stout/foreach.hpp>
#include <stout/option.hpp>
#include <stout/os.hpp>
#include <stout/path.hpp>
#include <stout/stringify.hpp>

using namespace mesos;

using std::string;
using std::vector;

using process::AUTHENTICATION;
using process::Clock;
using process::defer;
using process::DESCRIPTION;
using process::HELP;
using process::TLDR;

using process::http::OK;

using process::metrics::Gauge;
using process::metrics::Counter;

// NOTE: Per-task resources are nominal because all of the resources for the
// container are provisioned when the executor is created. The executor can
// run multiple tasks at once, but uses a constant amount of resources
// regardless of the number of tasks.
const double CPUS_PER_TASK = 0.001;
const int32_t MEM_PER_TASK = 1;

const double CPUS_PER_EXECUTOR = 0.1;
const int32_t MEM_PER_EXECUTOR = 32;

// This scheduler picks one slave and repeatedly launches sleep tasks on it,
// using a single multi-task executor. If the slave or executor fails, the
// scheduler will pick another slave and continue launching sleep tasks.
class LongLivedScheduler : public Scheduler
{
public:
  explicit LongLivedScheduler(const ExecutorInfo& _executor)
    : executor(_executor),
      taskResources(Resources::parse(
          "cpus:" + stringify(CPUS_PER_TASK) +
          ";mem:" + stringify(MEM_PER_TASK)).get()),
      tasksLaunched(0),
      metrics(*this)
  {
    process::spawn(metrics);
  }

  virtual ~LongLivedScheduler()
  {
    process::terminate(metrics);
    process::wait(metrics);
  }

  virtual void registered(SchedulerDriver*,
                          const FrameworkID&,
                          const MasterInfo&)
  {
    LOG(INFO) << "Registered!";

    metrics.isRegistered = true;
  }

  virtual void reregistered(SchedulerDriver*, const MasterInfo& masterInfo)
  {
    LOG(INFO) << "Re-registered!";

    metrics.isRegistered = true;
  }

  virtual void disconnected(SchedulerDriver* driver)
  {
    LOG(INFO) << "Disconnected!";

    metrics.isRegistered = false;
  }

  virtual void resourceOffers(SchedulerDriver* driver,
                              const vector<Offer>& offers)
  {
    static const Resources EXECUTOR_RESOURCES = Resources(executor.resources());

    metrics.offers_received += offers.size();

    foreach (const Offer& offer, offers) {
      if (slaveId.isNone()) {
        // No active executor running in the cluster.
        // Launch a new task with executor.

        if (Resources(offer.resources()).flatten()
            .contains(EXECUTOR_RESOURCES + taskResources)) {
          LOG(INFO)
            << "Starting executor and task " << tasksLaunched
            << " on " << offer.hostname();

          launchTask(driver, offer);

          slaveId = offer.slave_id();
        } else {
          declineOffer(driver, offer);
        }
      } else if (slaveId == offer.slave_id()) {
        // Offer from the same slave that has an active executor.
        // Launch more tasks on that executor.

        if (Resources(offer.resources()).flatten().contains(taskResources)) {
          LOG(INFO)
            << "Starting task " << tasksLaunched << " on " << offer.hostname();

          launchTask(driver, offer);
        } else {
          declineOffer(driver, offer);
        }
      } else {
        // We have an active executor but this offer comes from a
        // different slave; decline the offer.
        declineOffer(driver, offer);
      }
    }
  }

  virtual void offerRescinded(SchedulerDriver* driver,
                              const OfferID& offerId) {}

  virtual void statusUpdate(SchedulerDriver* driver, const TaskStatus& status)
  {
    LOG(INFO)
      << "Task " << status.task_id().value()
      << " is in state " << TaskState_Name(status.state())
      << (status.has_message() ? " with message: " + status.message() : "");

    if (status.state() == TASK_KILLED ||
        status.state() == TASK_LOST ||
        status.state() == TASK_FAILED ||
        status.state() == TASK_ERROR) {
      ++metrics.abnormal_terminations;
    }
  }

  virtual void frameworkMessage(SchedulerDriver* driver,
                                const ExecutorID& executorId,
                                const SlaveID& slaveId,
                                const string& data) {}

  virtual void slaveLost(SchedulerDriver* driver, const SlaveID& _slaveId)
  {
    LOG(INFO) << "Slave lost: " << _slaveId;

    if (slaveId == _slaveId) {
      slaveId = None();
    }
  }

  virtual void executorLost(SchedulerDriver* driver,
                            const ExecutorID& executorId,
                            const SlaveID& _slaveId,
                            int status)
  {
    LOG(INFO)
      << "Executor '" << executorId << "' lost on slave "
      << _slaveId << " with status: " << status;

    slaveId = None();
  }

  virtual void error(SchedulerDriver* driver, const string& message) {}

private:
  // Helper to decline an offer.
  void declineOffer(SchedulerDriver* driver, const Offer& offer)
  {
    Filters filters;
    filters.set_refuse_seconds(600);

    driver->declineOffer(offer.id(), filters);
  }

  // Helper to launch a task using an offer.
  void launchTask(SchedulerDriver* driver, const Offer& offer)
  {
    int taskId = tasksLaunched++;

    TaskInfo task;
    task.set_name("Task " + stringify(taskId));
    task.mutable_task_id()->set_value(stringify(taskId));
    task.mutable_slave_id()->MergeFrom(offer.slave_id());
    task.mutable_resources()->CopyFrom(taskResources);
    task.mutable_executor()->CopyFrom(executor);

    driver->launchTasks(offer.id(), {task});
  }

  const ExecutorInfo executor;
  const Resources taskResources;
  string uri;
  int tasksLaunched;

  // The slave that is running the long-lived-executor.
  // Unless that slave/executor dies, this framework will not launch
  // an executor on any other slave.
  Option<SlaveID> slaveId;

  struct Metrics : process::Process<Metrics>
  {
    Metrics(const LongLivedScheduler& _scheduler)
      : ProcessBase("framework"),
        scheduler(_scheduler),
        isRegistered(false),
        uptime_secs(
            "long_lived_framework/uptime_secs",
            defer(this, &Self::_uptime_secs)),
        registered(
            "long_lived_framework/registered",
            defer(this, &Self::_registered)),
        offers_received("long_lived_framework/offers_received"),
        tasks_launched(
            "long_lived_framework/tasks_launched",
            defer(this, &Self::_tasksLaunched)),
        abnormal_terminations("long_lived_framework/abnormal_terminations")
    {
      start_time = Clock::now();

      process::metrics::add(uptime_secs);
      process::metrics::add(registered);
      process::metrics::add(offers_received);
      process::metrics::add(tasks_launched);
      process::metrics::add(abnormal_terminations);
    }

    virtual void initialize()
    {
      // Special route for metric metadata.
      route(
          "/counters",
          HELP(
              TLDR("List of counter-type metrics."),
              DESCRIPTION("Returns 200 OK iff the request is accepted."),
              AUTHENTICATION(false)),
          [this](const process::http::Request& request) {
            JSON::Array array;
            array.values.push_back("long_lived_framework/offers_received");
            array.values.push_back(
                "long_lived_framework/abnormal_terminations");

            return OK(array, request.url.query.get("jsonp"));
          });
    }

    ~Metrics()
    {
      process::metrics::remove(uptime_secs);
      process::metrics::remove(registered);
      process::metrics::remove(offers_received);
      process::metrics::remove(tasks_launched);
      process::metrics::remove(abnormal_terminations);
    }

    const LongLivedScheduler& scheduler;

    process::Time start_time;
    double _uptime_secs()
    {
      return (Clock::now() - start_time).secs();
    }

    bool isRegistered;
    double _registered()
    {
      return isRegistered ? 1 : 0;
    }

    double _tasksLaunched()
    {
      return scheduler.tasksLaunched;
    }

    process::metrics::Gauge uptime_secs;
    process::metrics::Gauge registered;

    process::metrics::Counter offers_received;
    process::metrics::Gauge tasks_launched;

    // The only expected terminal state is TASK_FINISHED.
    // Other terminal states are considered incorrect.
    process::metrics::Counter abnormal_terminations;
  } metrics;
};

class Flags : public flags::FlagsBase
{
public:
  Flags()
  {
    add(&master,
        "master",
        "Master to connect to.",
        [](const Option<string>& value) -> Option<Error> {
          if (value.isNone()) {
            return Error("Missing --master");
          }

          return None();
        });

    add(&build_dir,
        "build_dir",
        "The build directory of Mesos. If set, the framework will assume\n"
        "that the executor, framework, and agent(s) all live on the same\n"
        "machine.");

    add(&executor_uri,
        "executor_uri",
        "URI the fetcher should use to get the executor.");

    add(&executor_command,
        "executor_command",
        "The command that should be used to start the executor.\n"
        "This will override the value set by `--build_dir`.");

    add(&checkpoint,
        "checkpoint",
        "Whether this framework should be checkpointed.",
        false);
  }

  Option<string> master;

  // Flags for specifying the executor binary.
  Option<string> build_dir;
  Option<string> executor_uri;
  Option<string> executor_command;

  bool checkpoint;
};

int main(int argc, char** argv)
{
  Flags flags;
  Try<Nothing> load = flags.load("MESOS_", argc, argv);

  if (load.isError()) {
    EXIT(EXIT_FAILURE) << flags.usage(load.error());
  }

  const Resources resources = Resources::parse(
      "cpus:" + stringify(CPUS_PER_EXECUTOR) +
      ";mem:" + stringify(MEM_PER_EXECUTOR)).get();

  ExecutorInfo executor;
  executor.mutable_executor_id()->set_value("default");
  executor.mutable_resources()->CopyFrom(resources);
  executor.set_name("Long Lived Executor (C++)");
  executor.set_source("cpp_long_lived_framework");

  // Determine the command to run the executor based on three possibilities:
  //   1) `--executor_command` was set, which overrides the below cases.
  //   2) We are in the Mesos build directory, so the targeted executable
  //      is actually a libtool wrapper script.
  //   3) We have not detected the Mesos build directory, so assume the
  //      executor is in the same directory as the framework.
  string command;

  // Find this executable's directory to locate executor.
  if (flags.executor_command.isSome()) {
    command = flags.executor_command.get();
  } else if (flags.build_dir.isSome()) {
    command = path::join(
        flags.build_dir.get(), "src", "long-lived-executor");
  } else {
    command = path::join(
        os::realpath(Path(argv[0]).dirname()).get(),
        "long-lived-executor");
  }

  executor.mutable_command()->set_value(command);

  // Copy `--executor_uri` into the command.
  if (flags.executor_uri.isSome()) {
    mesos::CommandInfo::URI* uri = executor.mutable_command()->add_uris();
    uri->set_value(flags.executor_uri.get());
    uri->set_executable(true);
  }

  LongLivedScheduler scheduler(executor);

  FrameworkInfo framework;
  framework.set_user(os::user().get());
  framework.set_name("Long Lived Framework (C++)");
  framework.set_checkpoint(flags.checkpoint);

  MesosSchedulerDriver* driver;

  // TODO(josephw): Refactor these into a common set of flags.
  if (os::getenv("MESOS_AUTHENTICATE").isSome()) {
    LOG(INFO) << "Enabling authentication for the framework";

    Option<string> value = os::getenv("DEFAULT_PRINCIPAL");
    if (value.isNone()) {
      EXIT(EXIT_FAILURE)
        << "Expecting authentication principal in the environment";
    }

    Credential credential;
    credential.set_principal(value.get());

    framework.set_principal(value.get());

    value = os::getenv("DEFAULT_SECRET");
    if (value.isNone()) {
      EXIT(EXIT_FAILURE)
        << "Expecting authentication secret in the environment";
    }

    credential.set_secret(value.get());

    driver = new MesosSchedulerDriver(
        &scheduler, framework, flags.master.get(), credential);
  } else {
    framework.set_principal("long-lived-framework-cpp");

    driver = new MesosSchedulerDriver(
        &scheduler, framework, flags.master.get());
  }

  int status = driver->run() == DRIVER_STOPPED ? 0 : 1;

  // Ensure that the driver process terminates.
  driver->stop();

  delete driver;
  return status;
}
<|endoftext|>


<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "DeviceLister.h"
#include "medialibrary/filesystem/Errors.h"

#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)

#include "logging/Logger.h"
#include "utils/Charsets.h"
#include "utils/Filename.h"

#include <memory>
#include <sstream>

#include <windows.h>

namespace medialibrary
{
namespace fs
{

std::vector<CommonDeviceLister::Device> DeviceLister::devices() const
{
    wchar_t volumeName[MAX_PATH];
    auto handle = FindFirstVolume( volumeName, sizeof(volumeName)/sizeof(volumeName[0]) );
    if ( handle == INVALID_HANDLE_VALUE )
    {
        std::stringstream ss;
        ss << "error code" << GetLastError();
        throw fs::errors::DeviceListing{ ss.str() };
    }
    std::unique_ptr<typename std::remove_pointer<HANDLE>::type, decltype(&FindVolumeClose)>
            uh( handle, &FindVolumeClose );
    std::vector<Device> res;
    for ( BOOL success =  TRUE; ; success = FindNextVolume( handle, volumeName, sizeof( volumeName )/sizeof(volumeName[0]) ) )
    {
        if ( success == FALSE )
        {
            auto err = GetLastError();
            if ( err == ERROR_NO_MORE_FILES )
                break;
            std::stringstream ss;
            ss << "error code" << err;
            throw fs::errors::DeviceListing{ ss.str() };
        }

        auto lastChar = wcslen( volumeName ) - 1;
        if ( volumeName[0] != L'\\' || volumeName[1] != L'\\' || volumeName[2] != L'?' ||
             volumeName[3] != L'\\' || volumeName[lastChar] != L'\\' )
            continue;

        wchar_t buffer[MAX_PATH + 1];
        DWORD buffLength = sizeof( buffer ) / sizeof( wchar_t );

        if ( GetVolumePathNamesForVolumeName( volumeName, buffer, buffLength, &buffLength ) == 0 )
            continue;
        std::string mountpoint = charset::FromWide( buffer ).get();

        // Filter out anything which isn't a removable or fixed drive. We don't care about network
        // drive here.
        auto type = GetDriveType( buffer );
        if ( type != DRIVE_REMOVABLE && type != DRIVE_FIXED )
            continue;

        std::string uuid =  charset::FromWide( volumeName ).get();

        LOG_INFO( "Discovered device ", uuid, "; mounted on ", mountpoint, "; removable: ",
                  type == DRIVE_REMOVABLE ? "yes" : "no" );
        res.emplace_back( uuid,
                          std::vector<std::string>{ utils::file::toMrl( mountpoint ) },
                          type == DRIVE_REMOVABLE );
    }
    return res;
}

}
}

#endif
<commit_msg>DeviceLister: win32: Accept remote drives<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "DeviceLister.h"
#include "medialibrary/filesystem/Errors.h"

#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)

#include "logging/Logger.h"
#include "utils/Charsets.h"
#include "utils/Filename.h"

#include <memory>
#include <sstream>

#include <windows.h>

namespace medialibrary
{
namespace fs
{

std::vector<CommonDeviceLister::Device> DeviceLister::devices() const
{
    wchar_t volumeName[MAX_PATH];
    auto handle = FindFirstVolume( volumeName, sizeof(volumeName)/sizeof(volumeName[0]) );
    if ( handle == INVALID_HANDLE_VALUE )
    {
        std::stringstream ss;
        ss << "error code" << GetLastError();
        throw fs::errors::DeviceListing{ ss.str() };
    }
    std::unique_ptr<typename std::remove_pointer<HANDLE>::type, decltype(&FindVolumeClose)>
            uh( handle, &FindVolumeClose );
    std::vector<Device> res;
    for ( BOOL success =  TRUE; ; success = FindNextVolume( handle, volumeName, sizeof( volumeName )/sizeof(volumeName[0]) ) )
    {
        if ( success == FALSE )
        {
            auto err = GetLastError();
            if ( err == ERROR_NO_MORE_FILES )
                break;
            std::stringstream ss;
            ss << "error code" << err;
            throw fs::errors::DeviceListing{ ss.str() };
        }

        auto lastChar = wcslen( volumeName ) - 1;
        if ( volumeName[0] != L'\\' || volumeName[1] != L'\\' || volumeName[2] != L'?' ||
             volumeName[3] != L'\\' || volumeName[lastChar] != L'\\' )
            continue;

        wchar_t buffer[MAX_PATH + 1];
        DWORD buffLength = sizeof( buffer ) / sizeof( wchar_t );

        if ( GetVolumePathNamesForVolumeName( volumeName, buffer, buffLength, &buffLength ) == 0 )
            continue;
        std::string mountpoint = charset::FromWide( buffer ).get();

        // Filter out anything which isn't a removable or fixed drive. We don't care about network
        // drive here.
        auto type = GetDriveType( buffer );
        if ( type != DRIVE_REMOVABLE && type != DRIVE_FIXED && type != DRIVE_REMOTE )
            continue;

        std::string uuid =  charset::FromWide( volumeName ).get();

        LOG_INFO( "Discovered device ", uuid, "; mounted on ", mountpoint, "; removable: ",
                  type == DRIVE_REMOVABLE ? "yes" : "no" );
        res.emplace_back( uuid,
                          std::vector<std::string>{ utils::file::toMrl( mountpoint ) },
                          type == DRIVE_REMOVABLE );
    }
    return res;
}

}
}

#endif
<|endoftext|>


<commit_before>#include "formulation/stamper.h"

#include "domain/tests/definition_mock.h"
#include "test_helpers/gmock_wrapper.h"

namespace {

using namespace bart;

template <typename DimensionWrapper>
class FormulationStamperTest : public ::testing::Test {
 public:
  static constexpr int dim = DimensionWrapper::value;
  using DomainDefinitionType = domain::DefinitionMock<dim>;

  std::shared_ptr<DomainDefinitionType> domain_ptr_;

  void SetUp() override;
};

template <typename DimensionWrapper>
void FormulationStamperTest<DimensionWrapper>::SetUp() {
  domain_ptr_ = std::make_shared<DomainDefinitionType>();
}

TYPED_TEST_SUITE(FormulationStamperTest, bart::testing::AllDimensions);

TYPED_TEST(FormulationStamperTest, Constructor) {
  using StamperType = formulation::Stamper<this->dim>;
  std::shared_ptr<StamperType> stamper_ptr;
  EXPECT_NO_THROW({
    stamper_ptr = std::make_shared<StamperType>(this->domain_ptr_);
  });
  ASSERT_NE(stamper_ptr->domain_ptr(), nullptr);
  EXPECT_EQ(stamper_ptr->domain_ptr(), this->domain_ptr_.get());
}

TYPED_TEST(FormulationStamperTest, BadDependency) {
  using StamperType = formulation::Stamper<this->dim>;
  std::shared_ptr<StamperType> stamper_ptr;
  EXPECT_ANY_THROW({
    stamper_ptr = std::make_shared<StamperType>(nullptr);
  });
}

} // namespace
<commit_msg>added dealii domain tests<commit_after>#include "formulation/stamper.h"

#include "domain/tests/definition_mock.h"
#include "test_helpers/dealii_test_domain.h"
#include "test_helpers/gmock_wrapper.h"

namespace {

using namespace bart;

using ::testing::ReturnRef, ::testing::Return;

/* ===== BASIC TESTS ===========================================================
 * These tests verify basic functionality of formulation::Stamper. */
template <typename DimensionWrapper>
class FormulationStamperTest : public ::testing::Test {
 public:
  static constexpr int dim = DimensionWrapper::value;
  using DomainDefinitionType = domain::DefinitionMock<dim>;

  std::shared_ptr<DomainDefinitionType> domain_ptr_;

  void SetUp() override;
};

template <typename DimensionWrapper>
void FormulationStamperTest<DimensionWrapper>::SetUp() {
  domain_ptr_ = std::make_shared<DomainDefinitionType>();
}

TYPED_TEST_SUITE(FormulationStamperTest, bart::testing::AllDimensions);

// Constructor should set dependency correct, getter should return correct value
TYPED_TEST(FormulationStamperTest, Constructor) {
  using StamperType = formulation::Stamper<this->dim>;
  std::shared_ptr<StamperType> stamper_ptr;
  EXPECT_NO_THROW({
    stamper_ptr = std::make_shared<StamperType>(this->domain_ptr_);
  });
  ASSERT_NE(stamper_ptr->domain_ptr(), nullptr);
  EXPECT_EQ(stamper_ptr->domain_ptr(), this->domain_ptr_.get());
}
// Constructor should throw if a nullptr dependency is passed
TYPED_TEST(FormulationStamperTest, BadDependency) {
  using StamperType = formulation::Stamper<this->dim>;
  std::shared_ptr<StamperType> stamper_ptr;
  EXPECT_ANY_THROW({
    stamper_ptr = std::make_shared<StamperType>(nullptr);
  });
}

/* ===== DEAL.II DOMAIN TESTS ==================================================
 * These tests verify operation of the stamper on a real dealii domain in both
 * serial and MPI */
template <typename DimensionWrapper>
class FormulationStamperTestDealiiDomain
    : public ::testing::Test,
      public bart::testing::DealiiTestDomain<DimensionWrapper::value> {
 public:
  static constexpr int dim = DimensionWrapper::value;
  using DomainDefinitionType = domain::DefinitionMock<dim>;
  using StamperType = formulation::Stamper<dim>;

  // Test object
  std::unique_ptr<StamperType> test_stamper_ptr_;

  // Depdendency
  std::shared_ptr<DomainDefinitionType> domain_ptr_;

  // Test parameters
  system::MPISparseMatrix& system_matrix = this->matrix_1;
  system::MPISparseMatrix& expected_matrix = this->matrix_2;
  std::function<void(formulation::FullMatrix&,
                     const domain::CellPtr<dim>&)> stamp_function;

  void SetUp() override;
};

void SetMatrixToOne(formulation::FullMatrix& to_stamp) {
  for (int i = 0; i < static_cast<int>(to_stamp.n_rows()); ++i) {
    for (int j = 0; j < static_cast<int>(to_stamp.n_cols()); ++j) {
      to_stamp(i,j) = 1;
    }
  }
}

template <typename DimensionWrapper>
void FormulationStamperTestDealiiDomain<DimensionWrapper>::SetUp() {
  this->SetUpDealii();
  domain_ptr_ = std::make_shared<DomainDefinitionType>();
  test_stamper_ptr_ = std::make_unique<StamperType>(domain_ptr_);

  /* Set up expected result of matrix stamping, we will return a matrix of all
   * ones for each cell. These should be stamped onto the degrees of freedom
   * for each cell. */
  int cell_dofs = this->fe_.dofs_per_cell;
  formulation::FullMatrix ones_matrix(cell_dofs, cell_dofs);
  SetMatrixToOne(ones_matrix);

  for (const auto& cell : this->cells_) {
    std::vector<dealii::types::global_dof_index> local_dof_indices(cell_dofs);
    cell->get_dof_indices(local_dof_indices);
    expected_matrix.add(local_dof_indices, local_dof_indices, ones_matrix);
  }

  // Set up the stamp function that just sets the provided matrix to all 1
  stamp_function = [](formulation::FullMatrix& to_stamp,
                      const domain::CellPtr<dim>&) -> void {
    SetMatrixToOne(to_stamp);
  };

  // Set up expected calls for the definition object
  ON_CALL(*domain_ptr_, Cells()).WillByDefault(Return(this->cells_));
  ON_CALL(*domain_ptr_, GetCellMatrix())
      .WillByDefault(Return(dealii::FullMatrix<double>(cell_dofs, cell_dofs)));
}

TYPED_TEST_SUITE(FormulationStamperTestDealiiDomain,
                 bart::testing::AllDimensions);

} // namespace
<|endoftext|>


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

// Author: laszlocsomor@google.com (Laszlo Csomor)
//
// Implementation for long-path-aware open/mkdir/access/etc. on Windows, as well
// as for the supporting utility functions.
//
// These functions convert the input path to an absolute Windows path
// with "\\?\" prefix, then pass that to _wopen/_wmkdir/_waccess/etc.
// (declared in <io.h>) respectively. This allows working with files/directories
// whose paths are longer than MAX_PATH (260 chars).
//
// This file is only used on Windows, it's empty on other platforms.

#if defined(_WIN32)

// Comment this out to fall back to using the ANSI versions (open, mkdir, ...)
// instead of the Unicode ones (_wopen, _wmkdir, ...). Doing so can be useful to
// debug failing tests if that's caused by the long path support.
#define SUPPORT_LONGPATHS

#include <ctype.h>
#include <direct.h>
#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <memory>
#include <sys/stat.h>
#include <sys/types.h>
#include <wctype.h>
#include <windows.h>

#include <google/protobuf/stubs/io_win32.h>

#include <memory>
#include <sstream>
#include <string>
#include <vector>

namespace google {
namespace protobuf {
namespace internal {
namespace win32 {
namespace {

using std::string;
using std::wstring;

template <typename char_type>
struct CharTraits {
  static bool is_alpha(char_type ch);
};

template <>
struct CharTraits<char> {
  static bool is_alpha(char ch) { return isalpha(ch); }
};

template <>
struct CharTraits<wchar_t> {
  static bool is_alpha(wchar_t ch) { return iswalpha(ch); }
};

template <typename char_type>
bool null_or_empty(const char_type* s) {
  return s == nullptr || *s == 0;
}

// Returns true if the path starts with a drive letter, e.g. "c:".
// Note that this won't check for the "\" after the drive letter, so this also
// returns true for "c:foo" (which is "c:\${PWD}\foo").
// This check requires that a path not have a longpath prefix ("\\?\").
template <typename char_type>
bool has_drive_letter(const char_type* ch) {
  return CharTraits<char_type>::is_alpha(ch[0]) && ch[1] == ':';
}

// Returns true if the path starts with a longpath prefix ("\\?\").
template <typename char_type>
bool has_longpath_prefix(const char_type* path) {
  return path[0] == '\\' && path[1] == '\\' && path[2] == '?' &&
         path[3] == '\\';
}

template <typename char_type>
bool is_separator(char_type c) {
  return c == '/' || c == '\\';
}

// Returns true if the path starts with a drive specifier (e.g. "c:\").
template <typename char_type>
bool is_path_absolute(const char_type* path) {
  return has_drive_letter(path) && is_separator(path[2]);
}

template <typename char_type>
bool is_drive_relative(const char_type* path) {
  return has_drive_letter(path) && (path[2] == 0 || !is_separator(path[2]));
}

wstring join_paths(const wstring& path1, const wstring& path2) {
  if (path1.empty() || is_path_absolute(path2.c_str()) ||
      has_longpath_prefix(path2.c_str())) {
    return path2;
  }
  if (path2.empty()) {
    return path1;
  }

  if (is_separator(path1[path1.size() - 1])) {
    return is_separator(path2[0]) ? (path1 + path2.substr(1))
                                       : (path1 + path2);
  } else {
    return is_separator(path2[0]) ? (path1 + path2)
                                       : (path1 + L'\\' + path2);
  }
}

wstring normalize(wstring path) {
  if (has_longpath_prefix(path.c_str())) {
    path = path.substr(4);
  }

  static const wstring dot(L".");
  static const wstring dotdot(L"..");
  const WCHAR* p = path.c_str();

  std::vector<wstring> segments;
  int segment_start = -1;
  // Find the path segments in `path` (separated by "/").
  for (int i = 0;; ++i) {
    if (!is_separator(p[i]) && p[i] != L'\0') {
      // The current character does not end a segment, so start one unless it's
      // already started.
      if (segment_start < 0) {
        segment_start = i;
      }
    } else if (segment_start >= 0 && i > segment_start) {
      // The current character is "/" or "\0", so this ends a segment.
      // Add that to `segments` if there's anything to add; handle "." and "..".
      wstring segment(p, segment_start, i - segment_start);
      segment_start = -1;
      if (segment == dotdot) {
        if (!segments.empty() &&
            (!has_drive_letter(segments[0].c_str()) || segments.size() > 1)) {
          segments.pop_back();
        }
      } else if (segment != dot && !segment.empty()) {
        segments.push_back(segment);
      }
    }
    if (p[i] == L'\0') {
      break;
    }
  }

  // Handle the case when `path` is just a drive specifier (or some degenerate
  // form of it, e.g. "c:\..").
  if (segments.size() == 1 && segments[0].size() == 2 &&
      has_drive_letter(segments[0].c_str())) {
    return segments[0] + L'\\';
  }

  // Join all segments.
  bool first = true;
  std::wstringstream result;
  for (int i = 0; i < segments.size(); ++i) {
    if (!first) {
      result << L'\\';
    }
    first = false;
    result << segments[i];
  }
  // Preserve trailing separator if the input contained it.
  if (!path.empty() && is_separator(p[path.size() - 1])) {
    result << L'\\';
  }
  return result.str();
}

bool as_windows_path(const char* path, wstring* result) {
  if (null_or_empty(path)) {
    result->clear();
    return true;
  }
  wstring wpath;
  if (!strings::utf8_to_wcs(path, &wpath)) {
    return false;
  }
  if (has_longpath_prefix(wpath.c_str())) {
    *result = wpath;
    return true;
  }
  if (is_separator(path[0]) || is_drive_relative(path)) {
    return false;
  }

  if (!is_path_absolute(wpath.c_str())) {
    int size = ::GetCurrentDirectoryW(0, nullptr);
    if (size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
      return false;
    }
    std::unique_ptr<WCHAR[]> wcwd(new WCHAR[size]);
    ::GetCurrentDirectoryW(size, wcwd.get());
    wpath = join_paths(wcwd.get(), wpath);
  }
  wpath = normalize(wpath);
  if (!has_longpath_prefix(wpath.c_str())) {
    // Add the "\\?\" prefix unconditionally. This way we prevent the Win32 API
    // from processing the path and "helpfully" removing trailing dots from the
    // path, for example.
    // See https://github.com/bazelbuild/bazel/issues/2935
    wpath = wstring(L"\\\\?\\") + wpath;
  }
  *result = wpath;
  return true;
}

}  // namespace

int open(const char* path, int flags, int mode) {
#ifdef SUPPORT_LONGPATHS
  wstring wpath;
  if (!as_windows_path(path, &wpath)) {
    errno = ENOENT;
    return -1;
  }
  return ::_wopen(wpath.c_str(), flags, mode);
#else
  return ::_open(path, flags, mode);
#endif
}

int mkdir(const char* path, int _mode) {
#ifdef SUPPORT_LONGPATHS
  wstring wpath;
  if (!as_windows_path(path, &wpath)) {
    errno = ENOENT;
    return -1;
  }
  return ::_wmkdir(wpath.c_str());
#else   // not SUPPORT_LONGPATHS
  return ::_mkdir(path);
#endif  // not SUPPORT_LONGPATHS
}

int access(const char* path, int mode) {
#ifdef SUPPORT_LONGPATHS
  wstring wpath;
  if (!as_windows_path(path, &wpath)) {
    errno = ENOENT;
    return -1;
  }
  return ::_waccess(wpath.c_str(), mode);
#else
  return ::_access(path, mode);
#endif
}

int chdir(const char* path) {
#ifdef SUPPORT_LONGPATHS
  wstring wpath;
  if (!as_windows_path(path, &wpath)) {
    errno = ENOENT;
    return -1;
  }
  return ::_wchdir(wpath.c_str());
#else
  return ::_chdir(path);
#endif
}

int stat(const char* path, struct _stat* buffer) {
#ifdef SUPPORT_LONGPATHS
  wstring wpath;
  if (!as_windows_path(path, &wpath)) {
    errno = ENOENT;
    return -1;
  }
  return ::_wstat(wpath.c_str(), buffer);
#else   // not SUPPORT_LONGPATHS
  return ::_stat(path, buffer);
#endif  // not SUPPORT_LONGPATHS
}

FILE* fopen(const char* path, const char* mode) {
#ifdef SUPPORT_LONGPATHS
  if (null_or_empty(path)) {
    errno = EINVAL;
    return nullptr;
  }
  wstring wpath;
  if (!as_windows_path(path, &wpath)) {
    errno = ENOENT;
    return nullptr;
  }
  wstring wmode;
  if (!strings::utf8_to_wcs(mode, &wmode)) {
    errno = EINVAL;
    return nullptr;
  }
  return ::_wfopen(wpath.c_str(), wmode.c_str());
#else
  return ::fopen(path, mode);
#endif
}

int close(int fd) { return ::close(fd); }

int dup(int fd) { return ::_dup(fd); }

int dup2(int fd1, int fd2) { return ::_dup2(fd1, fd2); }

int read(int fd, void* buffer, size_t size) {
  return ::_read(fd, buffer, size);
}

int setmode(int fd, int mode) { return ::_setmode(fd, mode); }

int write(int fd, const void* buffer, size_t size) {
  return ::_write(fd, buffer, size);
}

wstring testonly_utf8_to_winpath(const char* path) {
  wstring wpath;
  return as_windows_path(path, &wpath) ? wpath : wstring();
}

namespace strings {

bool wcs_to_mbs(const WCHAR* s, string* out, bool outUtf8) {
  if (null_or_empty(s)) {
    out->clear();
    return true;
  }
  BOOL usedDefaultChar = FALSE;
  SetLastError(0);
  int size = WideCharToMultiByte(
      outUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, nullptr, 0, nullptr,
      outUtf8 ? nullptr : &usedDefaultChar);
  if ((size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
      || usedDefaultChar) {
    return false;
  }
  std::unique_ptr<CHAR[]> astr(new CHAR[size]);
  WideCharToMultiByte(
      outUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, astr.get(), size, nullptr, nullptr);
  out->assign(astr.get());
  return true;
}

bool mbs_to_wcs(const char* s, wstring* out, bool inUtf8) {
  if (null_or_empty(s)) {
    out->clear();
    return true;
  }

  SetLastError(0);
  int size =
      MultiByteToWideChar(inUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, nullptr, 0);
  if (size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
    return false;
  }
  std::unique_ptr<WCHAR[]> wstr(new WCHAR[size]);
  MultiByteToWideChar(
      inUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, wstr.get(), size + 1);
  out->assign(wstr.get());
  return true;
}

bool utf8_to_wcs(const char* input, wstring* out) {
  return mbs_to_wcs(input, out, true);
}

bool wcs_to_utf8(const wchar_t* input, string* out) {
  return wcs_to_mbs(input, out, true);
}

}  // namespace strings
}  // namespace win32
}  // namespace internal
}  // namespace protobuf
}  // namespace google

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

// Author: laszlocsomor@google.com (Laszlo Csomor)
//
// Implementation for long-path-aware open/mkdir/access/etc. on Windows, as well
// as for the supporting utility functions.
//
// These functions convert the input path to an absolute Windows path
// with "\\?\" prefix, then pass that to _wopen/_wmkdir/_waccess/etc.
// (declared in <io.h>) respectively. This allows working with files/directories
// whose paths are longer than MAX_PATH (260 chars).
//
// This file is only used on Windows, it's empty on other platforms.

#if defined(_WIN32)

// Comment this out to fall back to using the ANSI versions (open, mkdir, ...)
// instead of the Unicode ones (_wopen, _wmkdir, ...). Doing so can be useful to
// debug failing tests if that's caused by the long path support.
#define SUPPORT_LONGPATHS

#include <ctype.h>
#include <direct.h>
#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <memory>
#include <sys/stat.h>
#include <sys/types.h>
#include <wctype.h>
#include <windows.h>

#include <google/protobuf/stubs/io_win32.h>

#include <memory>
#include <sstream>
#include <string>
#include <vector>

namespace google {
namespace protobuf {
namespace internal {
namespace win32 {
namespace {

using std::string;
using std::wstring;

template <typename char_type>
struct CharTraits {
  static bool is_alpha(char_type ch);
};

template <>
struct CharTraits<char> {
  static bool is_alpha(char ch) { return isalpha(ch); }
};

template <>
struct CharTraits<wchar_t> {
  static bool is_alpha(wchar_t ch) { return iswalpha(ch); }
};

template <typename char_type>
bool null_or_empty(const char_type* s) {
  return s == nullptr || *s == 0;
}

// Returns true if the path starts with a drive letter, e.g. "c:".
// Note that this won't check for the "\" after the drive letter, so this also
// returns true for "c:foo" (which is "c:\${PWD}\foo").
// This check requires that a path not have a longpath prefix ("\\?\").
template <typename char_type>
bool has_drive_letter(const char_type* ch) {
  return CharTraits<char_type>::is_alpha(ch[0]) && ch[1] == ':';
}

// Returns true if the path starts with a longpath prefix ("\\?\").
template <typename char_type>
bool has_longpath_prefix(const char_type* path) {
  return path[0] == '\\' && path[1] == '\\' && path[2] == '?' &&
         path[3] == '\\';
}

template <typename char_type>
bool is_separator(char_type c) {
  return c == '/' || c == '\\';
}

// Returns true if the path starts with a drive specifier (e.g. "c:\").
template <typename char_type>
bool is_path_absolute(const char_type* path) {
  return has_drive_letter(path) && is_separator(path[2]);
}

template <typename char_type>
bool is_drive_relative(const char_type* path) {
  return has_drive_letter(path) && (path[2] == 0 || !is_separator(path[2]));
}

wstring join_paths(const wstring& path1, const wstring& path2) {
  if (path1.empty() || is_path_absolute(path2.c_str()) ||
      has_longpath_prefix(path2.c_str())) {
    return path2;
  }
  if (path2.empty()) {
    return path1;
  }

  if (is_separator(path1[path1.size() - 1])) {
    return is_separator(path2[0]) ? (path1 + path2.substr(1))
                                       : (path1 + path2);
  } else {
    return is_separator(path2[0]) ? (path1 + path2)
                                       : (path1 + L'\\' + path2);
  }
}

wstring normalize(wstring path) {
  if (has_longpath_prefix(path.c_str())) {
    path = path.substr(4);
  }

  static const wstring dot(L".");
  static const wstring dotdot(L"..");
  const WCHAR* p = path.c_str();

  std::vector<wstring> segments;
  int segment_start = -1;
  // Find the path segments in `path` (separated by "/").
  for (int i = 0;; ++i) {
    if (!is_separator(p[i]) && p[i] != L'\0') {
      // The current character does not end a segment, so start one unless it's
      // already started.
      if (segment_start < 0) {
        segment_start = i;
      }
    } else if (segment_start >= 0 && i > segment_start) {
      // The current character is "/" or "\0", so this ends a segment.
      // Add that to `segments` if there's anything to add; handle "." and "..".
      wstring segment(p, segment_start, i - segment_start);
      segment_start = -1;
      if (segment == dotdot) {
        if (!segments.empty() &&
            (!has_drive_letter(segments[0].c_str()) || segments.size() > 1)) {
          segments.pop_back();
        }
      } else if (segment != dot && !segment.empty()) {
        segments.push_back(segment);
      }
    }
    if (p[i] == L'\0') {
      break;
    }
  }

  // Handle the case when `path` is just a drive specifier (or some degenerate
  // form of it, e.g. "c:\..").
  if (segments.size() == 1 && segments[0].size() == 2 &&
      has_drive_letter(segments[0].c_str())) {
    return segments[0] + L'\\';
  }

  // Join all segments.
  bool first = true;
  std::wstringstream result;
  for (int i = 0; i < segments.size(); ++i) {
    if (!first) {
      result << L'\\';
    }
    first = false;
    result << segments[i];
  }
  // Preserve trailing separator if the input contained it.
  if (!path.empty() && is_separator(p[path.size() - 1])) {
    result << L'\\';
  }
  return result.str();
}

bool as_windows_path(const char* path, wstring* result) {
  if (null_or_empty(path)) {
    result->clear();
    return true;
  }
  wstring wpath;
  if (!strings::utf8_to_wcs(path, &wpath)) {
    return false;
  }
  if (has_longpath_prefix(wpath.c_str())) {
    *result = wpath;
    return true;
  }
  if (is_separator(path[0]) || is_drive_relative(path)) {
    return false;
  }

  if (!is_path_absolute(wpath.c_str())) {
    int size = ::GetCurrentDirectoryW(0, nullptr);
    if (size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
      return false;
    }
    std::unique_ptr<WCHAR[]> wcwd(new WCHAR[size]);
    ::GetCurrentDirectoryW(size, wcwd.get());
    wpath = join_paths(wcwd.get(), wpath);
  }
  wpath = normalize(wpath);
  if (!has_longpath_prefix(wpath.c_str())) {
    // Add the "\\?\" prefix unconditionally. This way we prevent the Win32 API
    // from processing the path and "helpfully" removing trailing dots from the
    // path, for example.
    // See https://github.com/bazelbuild/bazel/issues/2935
    wpath = wstring(L"\\\\?\\") + wpath;
  }
  *result = wpath;
  return true;
}

}  // namespace

int open(const char* path, int flags, int mode) {
#ifdef SUPPORT_LONGPATHS
  wstring wpath;
  if (!as_windows_path(path, &wpath)) {
    errno = ENOENT;
    return -1;
  }
  return ::_wopen(wpath.c_str(), flags, mode);
#else
  return ::_open(path, flags, mode);
#endif
}

int mkdir(const char* path, int _mode) {
#ifdef SUPPORT_LONGPATHS
  wstring wpath;
  if (!as_windows_path(path, &wpath)) {
    errno = ENOENT;
    return -1;
  }
  return ::_wmkdir(wpath.c_str());
#else   // not SUPPORT_LONGPATHS
  return ::_mkdir(path);
#endif  // not SUPPORT_LONGPATHS
}

int access(const char* path, int mode) {
#ifdef SUPPORT_LONGPATHS
  wstring wpath;
  if (!as_windows_path(path, &wpath)) {
    errno = ENOENT;
    return -1;
  }
  return ::_waccess(wpath.c_str(), mode);
#else
  return ::_access(path, mode);
#endif
}

int chdir(const char* path) {
#ifdef SUPPORT_LONGPATHS
  wstring wpath;
  if (!as_windows_path(path, &wpath)) {
    errno = ENOENT;
    return -1;
  }
  return ::_wchdir(wpath.c_str());
#else
  return ::_chdir(path);
#endif
}

int stat(const char* path, struct _stat* buffer) {
#ifdef SUPPORT_LONGPATHS
  wstring wpath;
  if (!as_windows_path(path, &wpath)) {
    errno = ENOENT;
    return -1;
  }
  return ::_wstat(wpath.c_str(), buffer);
#else   // not SUPPORT_LONGPATHS
  return ::_stat(path, buffer);
#endif  // not SUPPORT_LONGPATHS
}

FILE* fopen(const char* path, const char* mode) {
#ifdef SUPPORT_LONGPATHS
  if (null_or_empty(path)) {
    errno = EINVAL;
    return nullptr;
  }
  wstring wpath;
  if (!as_windows_path(path, &wpath)) {
    errno = ENOENT;
    return nullptr;
  }
  wstring wmode;
  if (!strings::utf8_to_wcs(mode, &wmode)) {
    errno = EINVAL;
    return nullptr;
  }
  return ::_wfopen(wpath.c_str(), wmode.c_str());
#else
  return ::fopen(path, mode);
#endif
}

int close(int fd) { return ::_close(fd); }

int dup(int fd) { return ::_dup(fd); }

int dup2(int fd1, int fd2) { return ::_dup2(fd1, fd2); }

int read(int fd, void* buffer, size_t size) {
  return ::_read(fd, buffer, size);
}

int setmode(int fd, int mode) { return ::_setmode(fd, mode); }

int write(int fd, const void* buffer, size_t size) {
  return ::_write(fd, buffer, size);
}

wstring testonly_utf8_to_winpath(const char* path) {
  wstring wpath;
  return as_windows_path(path, &wpath) ? wpath : wstring();
}

namespace strings {

bool wcs_to_mbs(const WCHAR* s, string* out, bool outUtf8) {
  if (null_or_empty(s)) {
    out->clear();
    return true;
  }
  BOOL usedDefaultChar = FALSE;
  SetLastError(0);
  int size = WideCharToMultiByte(
      outUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, nullptr, 0, nullptr,
      outUtf8 ? nullptr : &usedDefaultChar);
  if ((size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
      || usedDefaultChar) {
    return false;
  }
  std::unique_ptr<CHAR[]> astr(new CHAR[size]);
  WideCharToMultiByte(
      outUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, astr.get(), size, nullptr, nullptr);
  out->assign(astr.get());
  return true;
}

bool mbs_to_wcs(const char* s, wstring* out, bool inUtf8) {
  if (null_or_empty(s)) {
    out->clear();
    return true;
  }

  SetLastError(0);
  int size =
      MultiByteToWideChar(inUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, nullptr, 0);
  if (size == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
    return false;
  }
  std::unique_ptr<WCHAR[]> wstr(new WCHAR[size]);
  MultiByteToWideChar(
      inUtf8 ? CP_UTF8 : CP_ACP, 0, s, -1, wstr.get(), size + 1);
  out->assign(wstr.get());
  return true;
}

bool utf8_to_wcs(const char* input, wstring* out) {
  return mbs_to_wcs(input, out, true);
}

bool wcs_to_utf8(const wchar_t* input, string* out) {
  return wcs_to_mbs(input, out, true);
}

}  // namespace strings
}  // namespace win32
}  // namespace internal
}  // namespace protobuf
}  // namespace google

#endif  // defined(_WIN32)
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2014 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

// mapnik
#include <mapnik/group/group_symbolizer_helper.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/value_types.hpp>
#include <mapnik/text/placements/base.hpp>
#include <mapnik/text/placements/dummy.hpp>
#include <mapnik/vertex_cache.hpp>
#include <mapnik/tolerance_iterator.hpp>

//agg
#include "agg_conv_clip_polyline.h"

namespace mapnik {

group_symbolizer_helper::group_symbolizer_helper(
        group_symbolizer const& sym, feature_impl const& feature,
        attributes const& vars,
        proj_transform const& prj_trans,
        unsigned width, unsigned height, double scale_factor,
        view_transform const& t, DetectorType & detector,
        box2d<double> const& query_extent)
    : base_symbolizer_helper(sym, feature, vars, prj_trans, width, height, scale_factor, t, query_extent),
      detector_(detector)
{}

pixel_position_list const& group_symbolizer_helper::get()
{
    results_.clear();

    if (point_placement_)
    {
        for (pixel_position const& point : points_)
        {
            check_point_placement(point);
        }
    }
    else
    {
        for (auto const* geom : geometries_to_process_)
        {
            // TODO to support clipped geometries this needs to use
            // vertex_converters
            using path_type = transform_path_adapter<view_transform,vertex_adapter>;
            vertex_adapter va(*geom);
            path_type path(t_, va, prj_trans_);
            find_line_placements(path);
        }
    }

    return results_;
}

template <typename T>
bool group_symbolizer_helper::find_line_placements(T & path)
{
    if (box_elements_.empty()) return true;

    vertex_cache pp(path);

    bool success = false;
    while (pp.next_subpath())
    {
        if (pp.length() <= 0.001)
        {
            success = check_point_placement(pp.current_position()) || success;
            continue;
        }

        double spacing = get_spacing(pp.length());

        pp.forward(spacing/2.0);
        do
        {
            tolerance_iterator tolerance_offset(text_props_->label_position_tolerance * scale_factor_, spacing); //TODO: Handle halign
            while (tolerance_offset.next())
            {
                vertex_cache::scoped_state state(pp);
                if (pp.move(tolerance_offset.get()) && check_point_placement(pp.current_position()))
                {
                    success = true;
                    break;
                }
            }
        } while (pp.forward(spacing));
    }
    return success;
}

bool group_symbolizer_helper::check_point_placement(pixel_position const& pos)
{
    if (box_elements_.empty()) return false;

    // offset boxes and check collision
    std::list< box2d<double> > real_boxes;
    for (auto const& box_elem : box_elements_)
    {
        box2d<double> real_box = box2d<double>(box_elem.box_);
        real_box.move(pos.x, pos.y);
        if (collision(real_box, box_elem.repeat_key_))
        {
            return false;
        }
        real_boxes.push_back(real_box);
    }

    // add boxes to collision detector
    std::list<box_element>::iterator elem_itr = box_elements_.begin();
    std::list< box2d<double> >::iterator real_itr = real_boxes.begin();
    while (elem_itr != box_elements_.end() && real_itr != real_boxes.end())
    {
        detector_.insert(*real_itr, elem_itr->repeat_key_);
        elem_itr++;
        real_itr++;
    }

    results_.push_back(pos);

    return true;
}

bool group_symbolizer_helper::collision(box2d<double> const& box, value_unicode_string const& repeat_key) const
{
    if (!detector_.extent().intersects(box)
            ||
        (text_props_->avoid_edges && !query_extent_.contains(box))
            ||
        (text_props_->minimum_padding > 0 &&
         !query_extent_.contains(box + (scale_factor_ * text_props_->minimum_padding)))
            ||
        (!text_props_->allow_overlap &&
            ((repeat_key.length() == 0 && !detector_.has_placement(box, text_props_->margin * scale_factor_))
                ||
             (repeat_key.length() > 0  && !detector_.has_placement(box, text_props_->margin * scale_factor_,
                                                                   repeat_key, text_props_->repeat_distance * scale_factor_))))
        )
    {
        return true;
    }
    return false;
}

double group_symbolizer_helper::get_spacing(double path_length) const
{
    int num_labels = 1;
    if (text_props_->label_spacing > 0)
    {
        num_labels = static_cast<int>(std::floor(
            path_length / (text_props_->label_spacing * scale_factor_)));
    }
    if (num_labels <= 0)
    {
        num_labels = 1;
    }
    return path_length / num_labels;
}

} //namespace
<commit_msg>Use correct bounding box for avoid-edges check.<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2014 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

// mapnik
#include <mapnik/group/group_symbolizer_helper.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/value_types.hpp>
#include <mapnik/text/placements/base.hpp>
#include <mapnik/text/placements/dummy.hpp>
#include <mapnik/vertex_cache.hpp>
#include <mapnik/tolerance_iterator.hpp>

//agg
#include "agg_conv_clip_polyline.h"

namespace mapnik {

group_symbolizer_helper::group_symbolizer_helper(
        group_symbolizer const& sym, feature_impl const& feature,
        attributes const& vars,
        proj_transform const& prj_trans,
        unsigned width, unsigned height, double scale_factor,
        view_transform const& t, DetectorType & detector,
        box2d<double> const& query_extent)
    : base_symbolizer_helper(sym, feature, vars, prj_trans, width, height, scale_factor, t, query_extent),
      detector_(detector)
{}

pixel_position_list const& group_symbolizer_helper::get()
{
    results_.clear();

    if (point_placement_)
    {
        for (pixel_position const& point : points_)
        {
            check_point_placement(point);
        }
    }
    else
    {
        for (auto const* geom : geometries_to_process_)
        {
            // TODO to support clipped geometries this needs to use
            // vertex_converters
            using path_type = transform_path_adapter<view_transform,vertex_adapter>;
            vertex_adapter va(*geom);
            path_type path(t_, va, prj_trans_);
            find_line_placements(path);
        }
    }

    return results_;
}

template <typename T>
bool group_symbolizer_helper::find_line_placements(T & path)
{
    if (box_elements_.empty()) return true;

    vertex_cache pp(path);

    bool success = false;
    while (pp.next_subpath())
    {
        if (pp.length() <= 0.001)
        {
            success = check_point_placement(pp.current_position()) || success;
            continue;
        }

        double spacing = get_spacing(pp.length());

        pp.forward(spacing/2.0);
        do
        {
            tolerance_iterator tolerance_offset(text_props_->label_position_tolerance * scale_factor_, spacing); //TODO: Handle halign
            while (tolerance_offset.next())
            {
                vertex_cache::scoped_state state(pp);
                if (pp.move(tolerance_offset.get()) && check_point_placement(pp.current_position()))
                {
                    success = true;
                    break;
                }
            }
        } while (pp.forward(spacing));
    }
    return success;
}

bool group_symbolizer_helper::check_point_placement(pixel_position const& pos)
{
    if (box_elements_.empty()) return false;

    // offset boxes and check collision
    std::list< box2d<double> > real_boxes;
    for (auto const& box_elem : box_elements_)
    {
        box2d<double> real_box = box2d<double>(box_elem.box_);
        real_box.move(pos.x, pos.y);
        if (collision(real_box, box_elem.repeat_key_))
        {
            return false;
        }
        real_boxes.push_back(real_box);
    }

    // add boxes to collision detector
    std::list<box_element>::iterator elem_itr = box_elements_.begin();
    std::list< box2d<double> >::iterator real_itr = real_boxes.begin();
    while (elem_itr != box_elements_.end() && real_itr != real_boxes.end())
    {
        detector_.insert(*real_itr, elem_itr->repeat_key_);
        elem_itr++;
        real_itr++;
    }

    results_.push_back(pos);

    return true;
}

bool group_symbolizer_helper::collision(box2d<double> const& box, value_unicode_string const& repeat_key) const
{
    if (!detector_.extent().intersects(box)
            ||
        (text_props_->avoid_edges && !dims_.contains(box))
            ||
        (text_props_->minimum_padding > 0 &&
         !dims_.contains(box + (scale_factor_ * text_props_->minimum_padding)))
            ||
        (!text_props_->allow_overlap &&
            ((repeat_key.length() == 0 && !detector_.has_placement(box, text_props_->margin * scale_factor_))
                ||
             (repeat_key.length() > 0  && !detector_.has_placement(box, text_props_->margin * scale_factor_,
                                                                   repeat_key, text_props_->repeat_distance * scale_factor_))))
        )
    {
        return true;
    }
    return false;
}

double group_symbolizer_helper::get_spacing(double path_length) const
{
    int num_labels = 1;
    if (text_props_->label_spacing > 0)
    {
        num_labels = static_cast<int>(std::floor(
            path_length / (text_props_->label_spacing * scale_factor_)));
    }
    if (num_labels <= 0)
    {
        num_labels = 1;
    }
    return path_length / num_labels;
}

} //namespace
<|endoftext|>