<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.

#ifndef __PROTOBUF_UTILS_HPP__
#define __PROTOBUF_UTILS_HPP__

#include <initializer_list>
#include <ostream>
#include <set>
#include <string>

#include <sys/stat.h>

#include <mesos/mesos.hpp>

#include <mesos/maintenance/maintenance.hpp>

#include <mesos/master/master.hpp>

#include <mesos/slave/isolator.hpp>

#include <process/time.hpp>

#include <stout/duration.hpp>
#include <stout/ip.hpp>
#include <stout/none.hpp>
#include <stout/option.hpp>
#include <stout/try.hpp>
#include <stout/uuid.hpp>

#include "messages/messages.hpp"

// Forward declaration (in lieu of an include).
namespace process {
struct UPID;
}

namespace mesos {

class AuthorizationAcceptor;

namespace internal {

namespace master {
// Forward declaration (in lieu of an include).
struct Framework;
struct Slave;
} // namespace master {

namespace protobuf {

bool frameworkHasCapability(
    const FrameworkInfo& framework,
    FrameworkInfo::Capability::Type capability);

// Returns whether the task state is terminal. Terminal states
// mean that the resources are released and the task cannot
// transition back to a non-terminal state. Note that
// `TASK_UNREACHABLE` is not a terminal state, but still
// releases the resources.
bool isTerminalState(const TaskState& state);

// See TaskStatus for more information about these fields. Note
// that the 'uuid' must be provided for updates that need
// acknowledgement. Currently, all slave and executor generated
// updates require acknowledgement, whereas master generated
// and scheduler driver generated updates do not.
StatusUpdate createStatusUpdate(
    const FrameworkID& frameworkId,
    const Option<SlaveID>& slaveId,
    const TaskID& taskId,
    const TaskState& state,
    const TaskStatus::Source& source,
    const Option<id::UUID>& uuid,
    const std::string& message = "",
    const Option<TaskStatus::Reason>& reason = None(),
    const Option<ExecutorID>& executorId = None(),
    const Option<bool>& healthy = None(),
    const Option<CheckStatusInfo>& checkStatus = None(),
    const Option<Labels>& labels = None(),
    const Option<ContainerStatus>& containerStatus = None(),
    const Option<TimeInfo>& unreachableTime = None(),
    const Option<Resources>& limitedResources = None());

StatusUpdate createStatusUpdate(
    const FrameworkID& frameworkId,
    const TaskStatus& status,
    const Option<SlaveID>& slaveId);

// Helper function that creates a new task status from scratch with
// obligatory fields set.
TaskStatus createTaskStatus(
    const TaskID& taskId,
    const TaskState& state,
    const id::UUID& uuid,
    double timestamp);

// Helper function that creates a new task status from the given task
// status. Specific fields in `status` can be overridden in the new
// status by specifying the appropriate argument. Fields `task_id`,
// `slave_id`, `executor_id`, cannot be changed; while `timestamp`
// and `uuid` cannot be preserved.
//
// NOTE: A task status update may be used for guaranteed delivery of
// some task-related information, e.g., task's health update. In this
// case, it is often desirable to preserve specific fields from the
// previous status update to avoid shadowing information that was
// delivered previously.
TaskStatus createTaskStatus(
    TaskStatus status,
    const id::UUID& uuid,
    double timestamp,
    const Option<TaskState>& state = None(),
    const Option<std::string>& message = None(),
    const Option<TaskStatus::Source>& source = None(),
    const Option<TaskStatus::Reason>& reason = None(),
    const Option<std::string>& data = None(),
    const Option<bool>& healthy = None(),
    const Option<CheckStatusInfo>& checkStatus = None(),
    const Option<Labels>& labels = None(),
    const Option<ContainerStatus>& containerStatus = None(),
    const Option<TimeInfo>& unreachableTime = None());

Task createTask(
    const TaskInfo& task,
    const TaskState& state,
    const FrameworkID& frameworkId);

Option<bool> getTaskHealth(const Task& task);

Option<CheckStatusInfo> getTaskCheckStatus(const Task& task);

Option<ContainerStatus> getTaskContainerStatus(const Task& task);

bool isTerminalState(const OperationState& state);

OperationStatus createOperationStatus(
    const OperationState& state,
    const Option<OperationID>& operationId = None(),
    const Option<std::string>& message = None(),
    const Option<Resources>& convertedResources = None(),
    const Option<id::UUID>& statusUUID = None());

Operation createOperation(
    const Offer::Operation& info,
    const OperationStatus& latestStatus,
    const Option<FrameworkID>& frameworkId,
    const Option<SlaveID>& slaveId,
    const Option<id::UUID>& operationUUID = None());

UpdateOperationStatusMessage createUpdateOperationStatusMessage(
    const id::UUID& operationUUID,
    const OperationStatus& status,
    const Option<OperationStatus>& latestStatus = None(),
    const Option<FrameworkID>& frameworkId = None(),
    const Option<SlaveID>& slaveId = None());

// Helper function that creates a MasterInfo from UPID.
MasterInfo createMasterInfo(const process::UPID& pid);

Label createLabel(
    const std::string& key,
    const Option<std::string>& value = None());

// Helper function to convert a protobuf string map to `Labels`.
Labels convertStringMapToLabels(
    const google::protobuf::Map<std::string, std::string>& map);

// Helper function to convert a `Labels` to a protobuf string map.
Try<google::protobuf::Map<std::string, std::string>> convertLabelsToStringMap(
    const Labels& labels);

// Previously, `Resource` did not contain `AllocationInfo`.
// So for backwards compatibility with old schedulers and
// tooling, we must allow operations to contain `Resource`s
// without an `AllocationInfo`. This allows the master to
// inject the offer's `AllocationInfo` into the operation's
// resources.
void injectAllocationInfo(
    Offer::Operation* operation,
    const Resource::AllocationInfo& allocationInfo);

// This strips the Resource::AllocationInfo from all
// Resource objects contained within the operation.
void stripAllocationInfo(Offer::Operation* operation);

bool isSpeculativeOperation(const Offer::Operation& operation);

// Helper function to pack a protobuf list of resource versions.
google::protobuf::RepeatedPtrField<ResourceVersionUUID> createResourceVersions(
    const hashmap<Option<ResourceProviderID>, id::UUID>& resourceVersions);

// Helper function to unpack a protobuf list of resource versions.
hashmap<Option<ResourceProviderID>, id::UUID> parseResourceVersions(
    const google::protobuf::RepeatedPtrField<ResourceVersionUUID>&
      resourceVersionUUIDs);

// Helper function that fills in a TimeInfo from the current time.
TimeInfo getCurrentTime();

// Helper function that creates a `FileInfo` from data returned by `stat()`.
FileInfo createFileInfo(const std::string& path, const struct stat& s);

ContainerID getRootContainerId(const ContainerID& containerId);

Try<Resources> getConsumedResources(const Offer::Operation& operation);

namespace slave {

// TODO(bmahler): Store the repeated field within this so that we
// don't drop unknown capabilities.
struct Capabilities
{
  Capabilities() = default;

  template <typename Iterable>
  Capabilities(const Iterable& capabilities)
  {
    foreach (const SlaveInfo::Capability& capability, capabilities) {
      switch (capability.type()) {
        case SlaveInfo::Capability::UNKNOWN:
          break;
        case SlaveInfo::Capability::MULTI_ROLE:
          multiRole = true;
          break;
        case SlaveInfo::Capability::HIERARCHICAL_ROLE:
          hierarchicalRole = true;
          break;
        case SlaveInfo::Capability::RESERVATION_REFINEMENT:
          reservationRefinement = true;
          break;
        case SlaveInfo::Capability::RESOURCE_PROVIDER:
          resourceProvider = true;
          break;
        // If adding another case here be sure to update the
        // equality operator.
      }
    }
  }

  // See mesos.proto for the meaning of agent capabilities.
  bool multiRole = false;
  bool hierarchicalRole = false;
  bool reservationRefinement = false;
  bool resourceProvider = false;

  google::protobuf::RepeatedPtrField<SlaveInfo::Capability>
  toRepeatedPtrField() const
  {
    google::protobuf::RepeatedPtrField<SlaveInfo::Capability> result;
    if (multiRole) {
      result.Add()->set_type(SlaveInfo::Capability::MULTI_ROLE);
    }
    if (hierarchicalRole) {
      result.Add()->set_type(SlaveInfo::Capability::HIERARCHICAL_ROLE);
    }
    if (reservationRefinement) {
      result.Add()->set_type(SlaveInfo::Capability::RESERVATION_REFINEMENT);
    }
    if (resourceProvider) {
      result.Add()->set_type(SlaveInfo::Capability::RESOURCE_PROVIDER);
    }

    return result;
  }
};

bool operator==(const Capabilities& left, const Capabilities& right);
bool operator!=(const Capabilities& left, const Capabilities& right);
std::ostream& operator<<(std::ostream& stream, const Capabilities& c);

mesos::slave::ContainerLimitation createContainerLimitation(
    const Resources& resources,
    const std::string& message,
    const TaskStatus::Reason& reason);

mesos::slave::ContainerState createContainerState(
    const Option<ExecutorInfo>& executorInfo,
    const ContainerID& id,
    pid_t pid,
    const std::string& directory);

} // namespace slave {

namespace maintenance {

/**
 * Helper for constructing an unavailability from a `Time` and `Duration`.
 */
Unavailability createUnavailability(
    const process::Time& start,
    const Option<Duration>& duration = None());

/**
 * Helper for constructing a list of `MachineID`.
 */
google::protobuf::RepeatedPtrField<MachineID> createMachineList(
    std::initializer_list<MachineID> ids);

/**
 * Helper for constructing a maintenance `Window`.
 * See `createUnavailability` above.
 */
mesos::maintenance::Window createWindow(
    std::initializer_list<MachineID> ids,
    const Unavailability& unavailability);

/**
 * Helper for constructing a maintenance `Schedule`.
 * See `createWindow` above.
 */
mesos::maintenance::Schedule createSchedule(
    std::initializer_list<mesos::maintenance::Window> windows);

} // namespace maintenance {

namespace master {

// TODO(bmahler): Store the repeated field within this so that we
// don't drop unknown capabilities.
struct Capabilities
{
  Capabilities() = default;

  template <typename Iterable>
  Capabilities(const Iterable& capabilities)
  {
    foreach (const MasterInfo::Capability& capability, capabilities) {
      switch (capability.type()) {
        case MasterInfo::Capability::UNKNOWN:
          break;
        case MasterInfo::Capability::AGENT_UPDATE:
          agentUpdate = true;
          break;
      }
    }
  }

  bool agentUpdate = false;
};

namespace event {

// Helper for creating a `TASK_UPDATED` event from a `Task`, its
// latest state according to the agent, and its status corresponding
// to the last status update acknowledged from the scheduler.
mesos::master::Event createTaskUpdated(
    const Task& task,
    const TaskState& state,
    const TaskStatus& status);

// Helper for creating a `TASK_ADDED` event from a `Task`.
mesos::master::Event createTaskAdded(const Task& task);

// Helper for creating a 'FRAMEWORK_ADDED' event from a `Framework`.
mesos::master::Event createFrameworkAdded(
    const mesos::internal::master::Framework& framework);

// Helper for creating a 'FRAMEWORK_UPDATED' event from a `Framework`.
mesos::master::Event createFrameworkUpdated(
    const mesos::internal::master::Framework& framework);

// Helper for creating a 'FRAMEWORK_REMOVED' event from a `FrameworkInfo`.
mesos::master::Event createFrameworkRemoved(const FrameworkInfo& frameworkInfo);

// Helper for creating an `Agent` response.
mesos::master::Response::GetAgents::Agent createAgentResponse(
    const mesos::internal::master::Slave& slave,
    const Option<process::Owned<AuthorizationAcceptor>>& rolesAcceptor =
      None());

// Helper for creating an `AGENT_ADDED` event from a `Slave`.
mesos::master::Event createAgentAdded(
    const mesos::internal::master::Slave& slave);

// Helper for creating an `AGENT_REMOVED` event from a `SlaveID`.
mesos::master::Event createAgentRemoved(const SlaveID& slaveId);

} // namespace event {
} // namespace master {

namespace framework {

// TODO(bmahler): Store the repeated field within this so that we
// don't drop unknown capabilities.
struct Capabilities
{
  Capabilities() = default;

  template <typename Iterable>
  Capabilities(const Iterable& capabilities)
  {
    foreach (const FrameworkInfo::Capability& capability, capabilities) {
      switch (capability.type()) {
        case FrameworkInfo::Capability::UNKNOWN:
          break;
        case FrameworkInfo::Capability::REVOCABLE_RESOURCES:
          revocableResources = true;
          break;
        case FrameworkInfo::Capability::TASK_KILLING_STATE:
          taskKillingState = true;
          break;
        case FrameworkInfo::Capability::GPU_RESOURCES:
          gpuResources = true;
          break;
        case FrameworkInfo::Capability::SHARED_RESOURCES:
          sharedResources = true;
          break;
        case FrameworkInfo::Capability::PARTITION_AWARE:
          partitionAware = true;
          break;
        case FrameworkInfo::Capability::MULTI_ROLE:
          multiRole = true;
          break;
        case FrameworkInfo::Capability::RESERVATION_REFINEMENT:
          reservationRefinement = true;
          break;
        case FrameworkInfo::Capability::REGION_AWARE:
          regionAware = true;
          break;
      }
    }
  }

  // See mesos.proto for the meaning of these capabilities.
  bool revocableResources = false;
  bool taskKillingState = false;
  bool gpuResources = false;
  bool sharedResources = false;
  bool partitionAware = false;
  bool multiRole = false;
  bool reservationRefinement = false;
  bool regionAware = false;
};

// Helper to get roles from FrameworkInfo based on the
// presence of the MULTI_ROLE capability.
std::set<std::string> getRoles(const FrameworkInfo& frameworkInfo);

} // namespace framework {

} // namespace protobuf {
} // namespace internal {
} // namespace mesos {

#endif // __PROTOBUF_UTILS_HPP__
<commit_msg>Added missing protobuf include.<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.

#ifndef __PROTOBUF_UTILS_HPP__
#define __PROTOBUF_UTILS_HPP__

#include <initializer_list>
#include <ostream>
#include <set>
#include <string>

#include <sys/stat.h>

#include <google/protobuf/map.h>

#include <mesos/mesos.hpp>

#include <mesos/maintenance/maintenance.hpp>

#include <mesos/master/master.hpp>

#include <mesos/slave/isolator.hpp>

#include <process/time.hpp>

#include <stout/duration.hpp>
#include <stout/ip.hpp>
#include <stout/none.hpp>
#include <stout/option.hpp>
#include <stout/try.hpp>
#include <stout/uuid.hpp>

#include "messages/messages.hpp"

// Forward declaration (in lieu of an include).
namespace process {
struct UPID;
}

namespace mesos {

class AuthorizationAcceptor;

namespace internal {

namespace master {
// Forward declaration (in lieu of an include).
struct Framework;
struct Slave;
} // namespace master {

namespace protobuf {

bool frameworkHasCapability(
    const FrameworkInfo& framework,
    FrameworkInfo::Capability::Type capability);

// Returns whether the task state is terminal. Terminal states
// mean that the resources are released and the task cannot
// transition back to a non-terminal state. Note that
// `TASK_UNREACHABLE` is not a terminal state, but still
// releases the resources.
bool isTerminalState(const TaskState& state);

// See TaskStatus for more information about these fields. Note
// that the 'uuid' must be provided for updates that need
// acknowledgement. Currently, all slave and executor generated
// updates require acknowledgement, whereas master generated
// and scheduler driver generated updates do not.
StatusUpdate createStatusUpdate(
    const FrameworkID& frameworkId,
    const Option<SlaveID>& slaveId,
    const TaskID& taskId,
    const TaskState& state,
    const TaskStatus::Source& source,
    const Option<id::UUID>& uuid,
    const std::string& message = "",
    const Option<TaskStatus::Reason>& reason = None(),
    const Option<ExecutorID>& executorId = None(),
    const Option<bool>& healthy = None(),
    const Option<CheckStatusInfo>& checkStatus = None(),
    const Option<Labels>& labels = None(),
    const Option<ContainerStatus>& containerStatus = None(),
    const Option<TimeInfo>& unreachableTime = None(),
    const Option<Resources>& limitedResources = None());

StatusUpdate createStatusUpdate(
    const FrameworkID& frameworkId,
    const TaskStatus& status,
    const Option<SlaveID>& slaveId);

// Helper function that creates a new task status from scratch with
// obligatory fields set.
TaskStatus createTaskStatus(
    const TaskID& taskId,
    const TaskState& state,
    const id::UUID& uuid,
    double timestamp);

// Helper function that creates a new task status from the given task
// status. Specific fields in `status` can be overridden in the new
// status by specifying the appropriate argument. Fields `task_id`,
// `slave_id`, `executor_id`, cannot be changed; while `timestamp`
// and `uuid` cannot be preserved.
//
// NOTE: A task status update may be used for guaranteed delivery of
// some task-related information, e.g., task's health update. In this
// case, it is often desirable to preserve specific fields from the
// previous status update to avoid shadowing information that was
// delivered previously.
TaskStatus createTaskStatus(
    TaskStatus status,
    const id::UUID& uuid,
    double timestamp,
    const Option<TaskState>& state = None(),
    const Option<std::string>& message = None(),
    const Option<TaskStatus::Source>& source = None(),
    const Option<TaskStatus::Reason>& reason = None(),
    const Option<std::string>& data = None(),
    const Option<bool>& healthy = None(),
    const Option<CheckStatusInfo>& checkStatus = None(),
    const Option<Labels>& labels = None(),
    const Option<ContainerStatus>& containerStatus = None(),
    const Option<TimeInfo>& unreachableTime = None());

Task createTask(
    const TaskInfo& task,
    const TaskState& state,
    const FrameworkID& frameworkId);

Option<bool> getTaskHealth(const Task& task);

Option<CheckStatusInfo> getTaskCheckStatus(const Task& task);

Option<ContainerStatus> getTaskContainerStatus(const Task& task);

bool isTerminalState(const OperationState& state);

OperationStatus createOperationStatus(
    const OperationState& state,
    const Option<OperationID>& operationId = None(),
    const Option<std::string>& message = None(),
    const Option<Resources>& convertedResources = None(),
    const Option<id::UUID>& statusUUID = None());

Operation createOperation(
    const Offer::Operation& info,
    const OperationStatus& latestStatus,
    const Option<FrameworkID>& frameworkId,
    const Option<SlaveID>& slaveId,
    const Option<id::UUID>& operationUUID = None());

UpdateOperationStatusMessage createUpdateOperationStatusMessage(
    const id::UUID& operationUUID,
    const OperationStatus& status,
    const Option<OperationStatus>& latestStatus = None(),
    const Option<FrameworkID>& frameworkId = None(),
    const Option<SlaveID>& slaveId = None());

// Helper function that creates a MasterInfo from UPID.
MasterInfo createMasterInfo(const process::UPID& pid);

Label createLabel(
    const std::string& key,
    const Option<std::string>& value = None());

// Helper function to convert a protobuf string map to `Labels`.
Labels convertStringMapToLabels(
    const google::protobuf::Map<std::string, std::string>& map);

// Helper function to convert a `Labels` to a protobuf string map.
Try<google::protobuf::Map<std::string, std::string>> convertLabelsToStringMap(
    const Labels& labels);

// Previously, `Resource` did not contain `AllocationInfo`.
// So for backwards compatibility with old schedulers and
// tooling, we must allow operations to contain `Resource`s
// without an `AllocationInfo`. This allows the master to
// inject the offer's `AllocationInfo` into the operation's
// resources.
void injectAllocationInfo(
    Offer::Operation* operation,
    const Resource::AllocationInfo& allocationInfo);

// This strips the Resource::AllocationInfo from all
// Resource objects contained within the operation.
void stripAllocationInfo(Offer::Operation* operation);

bool isSpeculativeOperation(const Offer::Operation& operation);

// Helper function to pack a protobuf list of resource versions.
google::protobuf::RepeatedPtrField<ResourceVersionUUID> createResourceVersions(
    const hashmap<Option<ResourceProviderID>, id::UUID>& resourceVersions);

// Helper function to unpack a protobuf list of resource versions.
hashmap<Option<ResourceProviderID>, id::UUID> parseResourceVersions(
    const google::protobuf::RepeatedPtrField<ResourceVersionUUID>&
      resourceVersionUUIDs);

// Helper function that fills in a TimeInfo from the current time.
TimeInfo getCurrentTime();

// Helper function that creates a `FileInfo` from data returned by `stat()`.
FileInfo createFileInfo(const std::string& path, const struct stat& s);

ContainerID getRootContainerId(const ContainerID& containerId);

Try<Resources> getConsumedResources(const Offer::Operation& operation);

namespace slave {

// TODO(bmahler): Store the repeated field within this so that we
// don't drop unknown capabilities.
struct Capabilities
{
  Capabilities() = default;

  template <typename Iterable>
  Capabilities(const Iterable& capabilities)
  {
    foreach (const SlaveInfo::Capability& capability, capabilities) {
      switch (capability.type()) {
        case SlaveInfo::Capability::UNKNOWN:
          break;
        case SlaveInfo::Capability::MULTI_ROLE:
          multiRole = true;
          break;
        case SlaveInfo::Capability::HIERARCHICAL_ROLE:
          hierarchicalRole = true;
          break;
        case SlaveInfo::Capability::RESERVATION_REFINEMENT:
          reservationRefinement = true;
          break;
        case SlaveInfo::Capability::RESOURCE_PROVIDER:
          resourceProvider = true;
          break;
        // If adding another case here be sure to update the
        // equality operator.
      }
    }
  }

  // See mesos.proto for the meaning of agent capabilities.
  bool multiRole = false;
  bool hierarchicalRole = false;
  bool reservationRefinement = false;
  bool resourceProvider = false;

  google::protobuf::RepeatedPtrField<SlaveInfo::Capability>
  toRepeatedPtrField() const
  {
    google::protobuf::RepeatedPtrField<SlaveInfo::Capability> result;
    if (multiRole) {
      result.Add()->set_type(SlaveInfo::Capability::MULTI_ROLE);
    }
    if (hierarchicalRole) {
      result.Add()->set_type(SlaveInfo::Capability::HIERARCHICAL_ROLE);
    }
    if (reservationRefinement) {
      result.Add()->set_type(SlaveInfo::Capability::RESERVATION_REFINEMENT);
    }
    if (resourceProvider) {
      result.Add()->set_type(SlaveInfo::Capability::RESOURCE_PROVIDER);
    }

    return result;
  }
};

bool operator==(const Capabilities& left, const Capabilities& right);
bool operator!=(const Capabilities& left, const Capabilities& right);
std::ostream& operator<<(std::ostream& stream, const Capabilities& c);

mesos::slave::ContainerLimitation createContainerLimitation(
    const Resources& resources,
    const std::string& message,
    const TaskStatus::Reason& reason);

mesos::slave::ContainerState createContainerState(
    const Option<ExecutorInfo>& executorInfo,
    const ContainerID& id,
    pid_t pid,
    const std::string& directory);

} // namespace slave {

namespace maintenance {

/**
 * Helper for constructing an unavailability from a `Time` and `Duration`.
 */
Unavailability createUnavailability(
    const process::Time& start,
    const Option<Duration>& duration = None());

/**
 * Helper for constructing a list of `MachineID`.
 */
google::protobuf::RepeatedPtrField<MachineID> createMachineList(
    std::initializer_list<MachineID> ids);

/**
 * Helper for constructing a maintenance `Window`.
 * See `createUnavailability` above.
 */
mesos::maintenance::Window createWindow(
    std::initializer_list<MachineID> ids,
    const Unavailability& unavailability);

/**
 * Helper for constructing a maintenance `Schedule`.
 * See `createWindow` above.
 */
mesos::maintenance::Schedule createSchedule(
    std::initializer_list<mesos::maintenance::Window> windows);

} // namespace maintenance {

namespace master {

// TODO(bmahler): Store the repeated field within this so that we
// don't drop unknown capabilities.
struct Capabilities
{
  Capabilities() = default;

  template <typename Iterable>
  Capabilities(const Iterable& capabilities)
  {
    foreach (const MasterInfo::Capability& capability, capabilities) {
      switch (capability.type()) {
        case MasterInfo::Capability::UNKNOWN:
          break;
        case MasterInfo::Capability::AGENT_UPDATE:
          agentUpdate = true;
          break;
      }
    }
  }

  bool agentUpdate = false;
};

namespace event {

// Helper for creating a `TASK_UPDATED` event from a `Task`, its
// latest state according to the agent, and its status corresponding
// to the last status update acknowledged from the scheduler.
mesos::master::Event createTaskUpdated(
    const Task& task,
    const TaskState& state,
    const TaskStatus& status);

// Helper for creating a `TASK_ADDED` event from a `Task`.
mesos::master::Event createTaskAdded(const Task& task);

// Helper for creating a 'FRAMEWORK_ADDED' event from a `Framework`.
mesos::master::Event createFrameworkAdded(
    const mesos::internal::master::Framework& framework);

// Helper for creating a 'FRAMEWORK_UPDATED' event from a `Framework`.
mesos::master::Event createFrameworkUpdated(
    const mesos::internal::master::Framework& framework);

// Helper for creating a 'FRAMEWORK_REMOVED' event from a `FrameworkInfo`.
mesos::master::Event createFrameworkRemoved(const FrameworkInfo& frameworkInfo);

// Helper for creating an `Agent` response.
mesos::master::Response::GetAgents::Agent createAgentResponse(
    const mesos::internal::master::Slave& slave,
    const Option<process::Owned<AuthorizationAcceptor>>& rolesAcceptor =
      None());

// Helper for creating an `AGENT_ADDED` event from a `Slave`.
mesos::master::Event createAgentAdded(
    const mesos::internal::master::Slave& slave);

// Helper for creating an `AGENT_REMOVED` event from a `SlaveID`.
mesos::master::Event createAgentRemoved(const SlaveID& slaveId);

} // namespace event {
} // namespace master {

namespace framework {

// TODO(bmahler): Store the repeated field within this so that we
// don't drop unknown capabilities.
struct Capabilities
{
  Capabilities() = default;

  template <typename Iterable>
  Capabilities(const Iterable& capabilities)
  {
    foreach (const FrameworkInfo::Capability& capability, capabilities) {
      switch (capability.type()) {
        case FrameworkInfo::Capability::UNKNOWN:
          break;
        case FrameworkInfo::Capability::REVOCABLE_RESOURCES:
          revocableResources = true;
          break;
        case FrameworkInfo::Capability::TASK_KILLING_STATE:
          taskKillingState = true;
          break;
        case FrameworkInfo::Capability::GPU_RESOURCES:
          gpuResources = true;
          break;
        case FrameworkInfo::Capability::SHARED_RESOURCES:
          sharedResources = true;
          break;
        case FrameworkInfo::Capability::PARTITION_AWARE:
          partitionAware = true;
          break;
        case FrameworkInfo::Capability::MULTI_ROLE:
          multiRole = true;
          break;
        case FrameworkInfo::Capability::RESERVATION_REFINEMENT:
          reservationRefinement = true;
          break;
        case FrameworkInfo::Capability::REGION_AWARE:
          regionAware = true;
          break;
      }
    }
  }

  // See mesos.proto for the meaning of these capabilities.
  bool revocableResources = false;
  bool taskKillingState = false;
  bool gpuResources = false;
  bool sharedResources = false;
  bool partitionAware = false;
  bool multiRole = false;
  bool reservationRefinement = false;
  bool regionAware = false;
};

// Helper to get roles from FrameworkInfo based on the
// presence of the MULTI_ROLE capability.
std::set<std::string> getRoles(const FrameworkInfo& frameworkInfo);

} // namespace framework {

} // namespace protobuf {
} // namespace internal {
} // namespace mesos {

#endif // __PROTOBUF_UTILS_HPP__
<|endoftext|>


<commit_before>// Macro MUONrecoNtuple.C (TO BE COMPILED)
// for testing the C++ reconstruction code
// and producing an Ntuple with reconstructed tracks
// in output file "MUONtrackReco.root".
// An example for using the Ntuple is in the macro MUONmassPlot.C

// Arguments:
//   FirstEvent (default 0)
//   LastEvent (default 0)
//   RecGeantHits (1 to reconstruct GEANT hits) (default 0)
//   FileName (for signal) (default "galice.root")
//   BkgGeantFileName (for background),
//      needed only if RecGeantHits = 1 and background to be added

// IMPORTANT NOTICE FOR USERS:
// under "root" or "root.exe", execute the following commands:
// 1. "gSystem->SetIncludePath("-I$ALICE_ROOT/MUON -I$ALICE_ROOT/STEER -I$ROOTSYS/include")" to get the right path at compilation time
// 2. ".x loadlibs.C" to load the shared libraries
// 3. ".x MUONrecoNtuple.C+()" with the right arguments, without forgetting the "+" which implies the compilation of the macro before its execution

#include <iostream.h>

#include <TClassTable.h>
#include <TClonesArray.h>
#include <TFile.h>
#include <TParticle.h>
#include <TROOT.h>
#include <TTree.h>

#include "AliHeader.h"
#include "AliRun.h"

#include "AliMUONEventReconstructor.h"
#include "AliMUONTrack.h"
#include "AliMUONTrackHit.h"
#include "AliMUONTrackParam.h"

// Classes for Ntuple ////////////////////////////////////////////////////

class AliMUONTrackRecNtuple : public TObject {
 public:
  // for direct access to members
  Int_t fCharge; // charge of reconstructed track (+/- 1)
  Float_t fPxRec; // Px of reconstructed track at vertex (GeV/c)
  Float_t fPyRec; // Py of reconstructed track at vertex (GeV/c)
  Float_t fPzRec; // Pz of reconstructed track at vertex (GeV/c)
  Float_t fZRec; // Z of reconstructed track at vertex (cm)
  Float_t fZRec1; // Z of reconstructed track at first hit (cm)
  Int_t fNHits; // number of hits
  Float_t fChi2; // chi2 of fit
  Float_t fPxGen; // Px of best compatible generated track at vertex (GeV/c)
  Float_t fPyGen; // Py of best compatible generated track at vertex (GeV/c)
  Float_t fPzGen; // Pz of best compatible generated track at vertex (GeV/c)
  AliMUONTrackRecNtuple(){;} // Constructor
  virtual ~AliMUONTrackRecNtuple(){;} // Destructor
 protected:
 private:
  ClassDef(AliMUONTrackRecNtuple, 1) // AliMUONTrackRecNtuple
    };

class AliMUONHeaderRecNtuple : public TObject {
 public:
  // for direct access to members
  Int_t fEvent; // event number
  AliMUONHeaderRecNtuple(){;} // Constructor
  virtual ~AliMUONHeaderRecNtuple(){;} // Destructor
 protected:
 private:
  ClassDef(AliMUONHeaderRecNtuple, 1) // AliMUONHeaderRecNtuple
    };

ClassImp(AliMUONTrackRecNtuple) // Class implementation in ROOT context
ClassImp(AliMUONHeaderRecNtuple) // Class implementation in ROOT context

  //__________________________________________________________________________
void AliMUONEventRecNtupleFill(AliMUONEventReconstructor *Reco, Int_t FillWrite = 0)
{
  // Fill Ntuple for reconstructed event pointed to by "Reco"
  // if "FillWrite" different from -1.
  // Ntuple is created automatically before filling first event.
  // If "FillWrite" = -1, write and close the file

  static Bool_t firstTime = kTRUE;
  static TTree *ntuple;
  static AliMUONHeaderRecNtuple *header = new AliMUONHeaderRecNtuple();
  static TClonesArray *recTracks = new TClonesArray("AliMUONTrackRecNtuple",5);

  Int_t trackIndex;
  AliMUONTrack *track;
  AliMUONTrackParam *trackParam;
  AliMUONTrackRecNtuple *recTrackNt;
  Double_t bendingSlope, nonBendingSlope, pYZ;

  if (FillWrite == -1) {
    // better to create the file before the Ntuple ????
    TFile *file = new TFile("MUONtrackReco.root","recreate");
    ntuple->Write();
    file->Write();
    file->Close();
  }

  if (firstTime) {
    firstTime = kFALSE;
    // first call: create tree for Ntuple...
    ntuple = new TTree("MUONtrackReco", "MUONtrackReco");
    ntuple->Branch("Header","AliMUONHeaderRecNtuple", &header);
    ntuple->Branch("Tracks", &recTracks);
  }

  // header
  header->fEvent = gAlice->GetHeader()->GetEvent();

  TClonesArray *recoTracksPtr = Reco->GetRecTracksPtr();
  recoTracksPtr->Compress(); // for simple loop without "Next" since no hole
  recTracks->Clear(); // to reset the TClonesArray of tracks to be put in the ntuple
  // Loop over reconstructed tracks
  for (trackIndex = 0; trackIndex < Reco->GetNRecTracks(); trackIndex++) {
    track = (AliMUONTrack*) ((*recoTracksPtr)[trackIndex]);
    recTrackNt = (AliMUONTrackRecNtuple*)
      new ((*recTracks)[trackIndex]) AliMUONTrackRecNtuple();
    // track parameters at Vertex
    trackParam = track->GetTrackParamAtVertex();
    recTrackNt->fCharge =
      Int_t(TMath::Sign(1., trackParam->GetInverseBendingMomentum()));
    bendingSlope = trackParam->GetBendingSlope();
    nonBendingSlope = trackParam->GetNonBendingSlope();
    pYZ = 1/TMath::Abs(trackParam->GetInverseBendingMomentum());
    recTrackNt->fPzRec = pYZ / TMath::Sqrt(1.0 + bendingSlope * bendingSlope);
    recTrackNt->fPxRec = recTrackNt->fPzRec * nonBendingSlope;
    recTrackNt->fPyRec = recTrackNt->fPzRec * bendingSlope;
    recTrackNt->fZRec = trackParam->GetZ();
    // track parameters at first hit
    trackParam = ((AliMUONTrackHit*)
		  (track->GetTrackHitsPtr()->First()))->GetTrackParam();
    recTrackNt->fZRec1 = trackParam->GetZ();
    // chi2
    recTrackNt->fChi2 = track->GetFitFMin();
    // number of hits
    recTrackNt->fNHits = track->GetNTrackHits();
    // track parameters at vertex of best compatible generated track:
    // in fact muon with the right charge
    for (int iPart = 0; iPart < gAlice->Particles()->GetEntriesFast(); iPart++) {
      TParticle *particle = (TParticle*) gAlice->Particles()->UncheckedAt(iPart);
      if ((particle->GetPdgCode() * recTrackNt->fCharge) == -13) {
	recTrackNt->fPxGen = particle->Px();
	recTrackNt->fPyGen = particle->Py();
	recTrackNt->fPzGen = particle->Pz();
      }
    }
  } // for (trackIndex = 0;...

  ntuple->Fill();

  return;
}

void MUONrecoNtuple (Int_t FirstEvent = 0, Int_t LastEvent = 0, Int_t RecGeantHits = 0, Text_t *FileName = "galice.root", Text_t *BkgGeantFileName = "")
{
  //
  cout << "MUON_recoNtuple" << endl;
  cout << "FirstEvent " << FirstEvent << endl;
  cout << "LastEvent " << LastEvent << endl;
  cout << "RecGeantHits " << RecGeantHits << endl;
  cout << "FileName ``" << FileName << "''" << endl;
  cout << "BkgGeantFileName ``" << BkgGeantFileName << "''" << endl;
//   // Dynamically link some shared libs
//   if (gClassTable->GetID("AliRun") < 0) {
//     gROOT->LoadMacro("loadlibs.C");
//     loadlibs();
//   }

  // Connect the Root Galice file containing Geometry, Kine, Hits
  // and eventually RawClusters
  TFile *file = (TFile*) gROOT->GetListOfFiles()->FindObject(FileName);
  if (!file) {
    printf("\n Creating file %s\n", FileName);
    file = new TFile(FileName);
  }
  else printf("\n File %s found in file list\n", FileName);

  // Get AliRun object from file or create it if not on file
  if (!gAlice) {
    gAlice = (AliRun*) file->Get("gAlice");
    if (gAlice) printf("AliRun object found on file\n");
    if (!gAlice) {
      printf("\n Create new gAlice object");
      gAlice = new AliRun("gAlice","Alice test program");
    }
  }

  // Initializations
  // AliMUON *MUON  = (AliMUON*) gAlice->GetModule("MUON"); // necessary ????
  AliMUONEventReconstructor *Reco = new AliMUONEventReconstructor();

  Reco->SetRecGeantHits(RecGeantHits);

  // The right place for changing AliMUONEventReconstructor parameters
  // with respect to the default ones
//   Reco->SetMaxSigma2Distance(100.0);
  Reco->SetPrintLevel(10);
//   Reco->SetPrintLevel(1);
//   Reco->SetBendingResolution(0.0);
//   Reco->SetNonBendingResolution(0.0);
  cout << "AliMUONEventReconstructor: actual parameters" << endl;
  Reco->Dump();
//   gObjectTable->Print();

  // Loop over events
  for (Int_t event = FirstEvent; event <= LastEvent; event++) {
    cout << "Event: " << event << endl;
//     AliMUON *MUON  = (AliMUON*) gAlice->GetModule("MUON"); // necessary ????
    Int_t nparticles = gAlice->GetEvent(event);
    cout << "nparticles: " << nparticles << endl;
    // prepare background file and/or event if necessary
    if (RecGeantHits == 1) {
      if (event == FirstEvent) Reco->SetBkgGeantFile(BkgGeantFileName);
      if (Reco->GetBkgGeantFile())Reco->NextBkgGeantEvent();
    }
    Reco->EventReconstruct();
    // Dump current event
    Reco->EventDump();
    // Fill Ntuple
    AliMUONEventRecNtupleFill(Reco, 0);
//     gObjectTable->Print();

  } // Event loop
    // Write Ntuple
    AliMUONEventRecNtupleFill(Reco, -1);
}
<commit_msg>Set print level to 10.<commit_after>// Macro MUONrecoNtuple.C (TO BE COMPILED)
// for testing the C++ reconstruction code
// and producing an Ntuple with reconstructed tracks
// in output file "MUONtrackReco.root".
// An example for using the Ntuple is in the macro MUONmassPlot.C

// Arguments:
//   FirstEvent (default 0)
//   LastEvent (default 0)
//   RecGeantHits (1 to reconstruct GEANT hits) (default 0)
//   FileName (for signal) (default "galice.root")
//   BkgGeantFileName (for background),
//      needed only if RecGeantHits = 1 and background to be added

// IMPORTANT NOTICE FOR USERS:
// under "root" or "root.exe", execute the following commands:
// 1. "gSystem->SetIncludePath("-I$ALICE_ROOT/MUON -I$ALICE_ROOT/STEER -I$ROOTSYS/include")" to get the right path at compilation time
// 2. ".x loadlibs.C" to load the shared libraries
// 3. ".x MUONrecoNtuple.C+()" with the right arguments, without forgetting the "+" which implies the compilation of the macro before its execution

#include <iostream.h>

#include <TClassTable.h>
#include <TClonesArray.h>
#include <TFile.h>
#include <TParticle.h>
#include <TROOT.h>
#include <TTree.h>

#include "AliHeader.h"
#include "AliRun.h"

#include "AliMUONEventReconstructor.h"
#include "AliMUONTrack.h"
#include "AliMUONTrackHit.h"
#include "AliMUONTrackParam.h"

// Classes for Ntuple ////////////////////////////////////////////////////

class AliMUONTrackRecNtuple : public TObject {
 public:
  // for direct access to members
  Int_t fCharge; // charge of reconstructed track (+/- 1)
  Float_t fPxRec; // Px of reconstructed track at vertex (GeV/c)
  Float_t fPyRec; // Py of reconstructed track at vertex (GeV/c)
  Float_t fPzRec; // Pz of reconstructed track at vertex (GeV/c)
  Float_t fZRec; // Z of reconstructed track at vertex (cm)
  Float_t fZRec1; // Z of reconstructed track at first hit (cm)
  Int_t fNHits; // number of hits
  Float_t fChi2; // chi2 of fit
  Float_t fPxGen; // Px of best compatible generated track at vertex (GeV/c)
  Float_t fPyGen; // Py of best compatible generated track at vertex (GeV/c)
  Float_t fPzGen; // Pz of best compatible generated track at vertex (GeV/c)
  AliMUONTrackRecNtuple(){;} // Constructor
  virtual ~AliMUONTrackRecNtuple(){;} // Destructor
 protected:
 private:
  ClassDef(AliMUONTrackRecNtuple, 1) // AliMUONTrackRecNtuple
    };

class AliMUONHeaderRecNtuple : public TObject {
 public:
  // for direct access to members
  Int_t fEvent; // event number
  AliMUONHeaderRecNtuple(){;} // Constructor
  virtual ~AliMUONHeaderRecNtuple(){;} // Destructor
 protected:
 private:
  ClassDef(AliMUONHeaderRecNtuple, 1) // AliMUONHeaderRecNtuple
    };

ClassImp(AliMUONTrackRecNtuple) // Class implementation in ROOT context
ClassImp(AliMUONHeaderRecNtuple) // Class implementation in ROOT context

  //__________________________________________________________________________
void AliMUONEventRecNtupleFill(AliMUONEventReconstructor *Reco, Int_t FillWrite = 0)
{
  // Fill Ntuple for reconstructed event pointed to by "Reco"
  // if "FillWrite" different from -1.
  // Ntuple is created automatically before filling first event.
  // If "FillWrite" = -1, write and close the file

  static Bool_t firstTime = kTRUE;
  static TTree *ntuple;
  static AliMUONHeaderRecNtuple *header = new AliMUONHeaderRecNtuple();
  static TClonesArray *recTracks = new TClonesArray("AliMUONTrackRecNtuple",5);

  Int_t trackIndex;
  AliMUONTrack *track;
  AliMUONTrackParam *trackParam;
  AliMUONTrackRecNtuple *recTrackNt;
  Double_t bendingSlope, nonBendingSlope, pYZ;

  if (FillWrite == -1) {
    // better to create the file before the Ntuple ????
    TFile *file = new TFile("MUONtrackReco.root","recreate");
    ntuple->Write();
    file->Write();
    file->Close();
  }

  if (firstTime) {
    firstTime = kFALSE;
    // first call: create tree for Ntuple...
    ntuple = new TTree("MUONtrackReco", "MUONtrackReco");
    ntuple->Branch("Header","AliMUONHeaderRecNtuple", &header);
    ntuple->Branch("Tracks", &recTracks);
  }

  // header
  header->fEvent = gAlice->GetHeader()->GetEvent();

  TClonesArray *recoTracksPtr = Reco->GetRecTracksPtr();
  recoTracksPtr->Compress(); // for simple loop without "Next" since no hole
  recTracks->Clear(); // to reset the TClonesArray of tracks to be put in the ntuple
  // Loop over reconstructed tracks
  for (trackIndex = 0; trackIndex < Reco->GetNRecTracks(); trackIndex++) {
    track = (AliMUONTrack*) ((*recoTracksPtr)[trackIndex]);
    recTrackNt = (AliMUONTrackRecNtuple*)
      new ((*recTracks)[trackIndex]) AliMUONTrackRecNtuple();
    // track parameters at Vertex
    trackParam = track->GetTrackParamAtVertex();
    recTrackNt->fCharge =
      Int_t(TMath::Sign(1., trackParam->GetInverseBendingMomentum()));
    bendingSlope = trackParam->GetBendingSlope();
    nonBendingSlope = trackParam->GetNonBendingSlope();
    pYZ = 1/TMath::Abs(trackParam->GetInverseBendingMomentum());
    recTrackNt->fPzRec = pYZ / TMath::Sqrt(1.0 + bendingSlope * bendingSlope);
    recTrackNt->fPxRec = recTrackNt->fPzRec * nonBendingSlope;
    recTrackNt->fPyRec = recTrackNt->fPzRec * bendingSlope;
    recTrackNt->fZRec = trackParam->GetZ();
    // track parameters at first hit
    trackParam = ((AliMUONTrackHit*)
		  (track->GetTrackHitsPtr()->First()))->GetTrackParam();
    recTrackNt->fZRec1 = trackParam->GetZ();
    // chi2
    recTrackNt->fChi2 = track->GetFitFMin();
    // number of hits
    recTrackNt->fNHits = track->GetNTrackHits();
    // track parameters at vertex of best compatible generated track:
    // in fact muon with the right charge
    for (int iPart = 0; iPart < gAlice->Particles()->GetEntriesFast(); iPart++) {
      TParticle *particle = (TParticle*) gAlice->Particles()->UncheckedAt(iPart);
      if ((particle->GetPdgCode() * recTrackNt->fCharge) == -13) {
	recTrackNt->fPxGen = particle->Px();
	recTrackNt->fPyGen = particle->Py();
	recTrackNt->fPzGen = particle->Pz();
      }
    }
  } // for (trackIndex = 0;...

  ntuple->Fill();

  return;
}

void MUONrecoNtuple (Int_t FirstEvent = 0, Int_t LastEvent = 0, Int_t RecGeantHits = 0, Text_t *FileName = "galice.root", Text_t *BkgGeantFileName = "")
{
  //
  cout << "MUON_recoNtuple" << endl;
  cout << "FirstEvent " << FirstEvent << endl;
  cout << "LastEvent " << LastEvent << endl;
  cout << "RecGeantHits " << RecGeantHits << endl;
  cout << "FileName ``" << FileName << "''" << endl;
  cout << "BkgGeantFileName ``" << BkgGeantFileName << "''" << endl;
//   // Dynamically link some shared libs
//   if (gClassTable->GetID("AliRun") < 0) {
//     gROOT->LoadMacro("loadlibs.C");
//     loadlibs();
//   }

  // Connect the Root Galice file containing Geometry, Kine, Hits
  // and eventually RawClusters
  TFile *file = (TFile*) gROOT->GetListOfFiles()->FindObject(FileName);
  if (!file) {
    printf("\n Creating file %s\n", FileName);
    file = new TFile(FileName);
  }
  else printf("\n File %s found in file list\n", FileName);

  // Get AliRun object from file or create it if not on file
  if (!gAlice) {
    gAlice = (AliRun*) file->Get("gAlice");
    if (gAlice) printf("AliRun object found on file\n");
    if (!gAlice) {
      printf("\n Create new gAlice object");
      gAlice = new AliRun("gAlice","Alice test program");
    }
  }

  // Initializations
  // AliMUON *MUON  = (AliMUON*) gAlice->GetModule("MUON"); // necessary ????
  AliMUONEventReconstructor *Reco = new AliMUONEventReconstructor();

  Reco->SetRecGeantHits(RecGeantHits);

  // The right place for changing AliMUONEventReconstructor parameters
  // with respect to the default ones
//   Reco->SetMaxSigma2Distance(100.0);
  Reco->SetPrintLevel(0);
//   Reco->SetPrintLevel(1);
//   Reco->SetBendingResolution(0.0);
//   Reco->SetNonBendingResolution(0.0);
  cout << "AliMUONEventReconstructor: actual parameters" << endl;
  Reco->Dump();
//   gObjectTable->Print();

  // Loop over events
  for (Int_t event = FirstEvent; event <= LastEvent; event++) {
    cout << "Event: " << event << endl;
//     AliMUON *MUON  = (AliMUON*) gAlice->GetModule("MUON"); // necessary ????
    Int_t nparticles = gAlice->GetEvent(event);
    cout << "nparticles: " << nparticles << endl;
    // prepare background file and/or event if necessary
    if (RecGeantHits == 1) {
      if (event == FirstEvent) Reco->SetBkgGeantFile(BkgGeantFileName);
      if (Reco->GetBkgGeantFile())Reco->NextBkgGeantEvent();
    }
    Reco->EventReconstruct();
    // Dump current event
    Reco->EventDump();
    // Fill Ntuple
    AliMUONEventRecNtupleFill(Reco, 0);
//     gObjectTable->Print();

  } // Event loop
    // Write Ntuple
    AliMUONEventRecNtupleFill(Reco, -1);
}
<|endoftext|>


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

#include "riegeli/base/options_parser.h"

#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <cmath>
#include <functional>
#include <limits>
#include <string>
#include <utility>
#include <vector>

#include "absl/base/optimization.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "riegeli/base/base.h"

namespace riegeli {

namespace {

// This is a struct rather than a lambda to capture function1 and function2 by
// move.
struct OrFunction {
  bool operator()(ValueParser* value_parser) const {
    return function1(value_parser) || function2(value_parser);
  }

  ValueParser::Function function1;
  ValueParser::Function function2;
};

// This is a struct rather than a lambda to capture function1 and function2 by
// move.
struct AndFunction {
  bool operator()(ValueParser* value_parser) const {
    return function1(value_parser) && function2(value_parser);
  }

  ValueParser::Function function1;
  ValueParser::Function function2;
};

}  // namespace

ValueParser::ValueParser(OptionsParser* options_parser, absl::string_view key,
                         absl::string_view value)
    : Object(State::kOpen),
      options_parser_(RIEGELI_ASSERT_NOTNULL(options_parser)),
      key_(key),
      value_(value) {}

ValueParser::Function ValueParser::Int(int* out, int min_value, int max_value) {
  RIEGELI_ASSERT_LE(min_value, max_value)
      << "Failed precondition of OptionsParser::IntOption(): "
         "bounds in the wrong order";
  return [out, min_value, max_value](ValueParser* value_parser) {
    int int_value;
    if (ABSL_PREDICT_TRUE(absl::SimpleAtoi(value_parser->value(), &int_value) &&
                          int_value >= min_value && int_value <= max_value)) {
      *out = int_value;
      return true;
    }
    return value_parser->InvalidValue(
        absl::StrCat("integers ", min_value, "..", max_value));
  };
}

ValueParser::Function ValueParser::Bytes(uint64_t* out, uint64_t min_value,
                                         uint64_t max_value) {
  RIEGELI_ASSERT_LE(min_value, max_value)
      << "Failed precondition of BytesOption(): bounds in the wrong order";
  return [out, min_value, max_value](ValueParser* value_parser) {
    absl::string_view value = value_parser->value();
    double scale = 1.0;
    if (ABSL_PREDICT_TRUE(!value.empty())) {
      switch (value.back()) {
        case 'B':
          break;
        case 'k':
        case 'K':
          scale = static_cast<double>(uint64_t{1} << 10);
          break;
        case 'M':
          scale = static_cast<double>(uint64_t{1} << 20);
          break;
        case 'G':
          scale = static_cast<double>(uint64_t{1} << 30);
          break;
        case 'T':
          scale = static_cast<double>(uint64_t{1} << 40);
          break;
        case 'P':
          scale = static_cast<double>(uint64_t{1} << 50);
          break;
        case 'E':
          scale = static_cast<double>(uint64_t{1} << 60);
          break;
        default:
          goto no_scale;
      }
      value.remove_suffix(1);
    }
  no_scale:
    double double_value;
    if (ABSL_PREDICT_TRUE(absl::SimpleAtod(value, &double_value) &&
                          double_value >= 0.0)) {
      double_value = std::round(double_value * scale);
      const uint64_t uint64_value =
          ABSL_PREDICT_FALSE(
              double_value >=
              static_cast<double>(std::numeric_limits<uint64_t>::max()))
              ? std::numeric_limits<uint64_t>::max()
              : static_cast<uint64_t>(double_value);
      if (ABSL_PREDICT_TRUE(uint64_value >= min_value &&
                            uint64_value <= max_value)) {
        *out = uint64_value;
        return true;
      }
    }
    return value_parser->InvalidValue(
        absl::StrCat("integers expressed as reals with "
                     "optional suffix [BkKMGTPE], ",
                     min_value, "..", max_value));
  };
}

ValueParser::Function ValueParser::Real(double* out, double min_value,
                                        double max_value) {
  RIEGELI_ASSERT_LE(min_value, max_value)
      << "Failed precondition of IntOption(): bounds in the wrong order";
  return [out, min_value, max_value](ValueParser* value_parser) {
    double double_value;
    if (ABSL_PREDICT_TRUE(
            absl::SimpleAtod(value_parser->value(), &double_value) &&
            double_value >= min_value && double_value <= max_value)) {
      *out = double_value;
      return true;
    }
    return value_parser->InvalidValue(
        absl::StrCat("reals ", min_value, "..", max_value));
  };
}

ValueParser::Function ValueParser::Or(Function function1, Function function2) {
  return OrFunction{std::move(function1), std::move(function2)};
}

ValueParser::Function ValueParser::And(Function function1, Function function2) {
  return AndFunction{std::move(function1), std::move(function2)};
}

ValueParser::Function ValueParser::CopyTo(std::string* text) {
  return [text](ValueParser* value_parser) {
    StrAppend(text, text->empty() ? "" : ",", value_parser->key(),
              value_parser->value().empty() ? "" : ":", value_parser->value());
    return true;
  };
}

ValueParser::Function ValueParser::FailIfSeen(absl::string_view key) {
  return [key](ValueParser* value_parser) {
    for (const OptionsParser::Option& option :
         value_parser->options_parser_->options_) {
      if (option.key == key) {
        if (ABSL_PREDICT_FALSE(option.seen)) {
          return value_parser->Fail(absl::StrCat(
              "Option ", value_parser->key(), " conflicts with option ", key));
        }
        return true;
      }
    }
    RIEGELI_ASSERT_UNREACHABLE() << "Unknown option " << key;
  };
}

ValueParser::Function ValueParser::FailIfAnySeen() {
  return [](ValueParser* value_parser) {
    for (const OptionsParser::Option& option :
         value_parser->options_parser_->options_) {
      if (ABSL_PREDICT_FALSE(option.seen)) {
        return value_parser->Fail(
            absl::StrCat("Option ", value_parser->key(), " must be first"));
      }
    }
    return true;
  };
}

bool ValueParser::InvalidValue(absl::string_view valid_values) {
  RIEGELI_ASSERT(!valid_values.empty())
      << "Failed precondition of OptionsParser::InvalidValue(): "
         "empty valid values";
  StrAppend(&valid_values_, valid_values_.empty() ? "" : ", ", valid_values);
  return false;
}

void OptionsParser::Done() { options_ = std::vector<Option>(); }

bool OptionsParser::Parse(absl::string_view text) {
  if (ABSL_PREDICT_FALSE(!healthy())) return false;
  size_t option_begin = 0;
  for (;;) {
    size_t option_end = text.find(',', option_begin);
    if (option_end == absl::string_view::npos) option_end = text.size();
    if (option_begin != option_end) {
      const absl::string_view key_value =
          text.substr(option_begin, option_end - option_begin);
      absl::string_view key;
      absl::string_view value;
      const size_t colon = key_value.find(':');
      if (colon == absl::string_view::npos) {
        key = key_value;
      } else {
        key = key_value.substr(0, colon);
        value = key_value.substr(colon + 1);
      }
      const std::vector<Option>::iterator option = std::find_if(
          options_.begin(), options_.end(),
          [key](const Option& option) { return option.key == key; });
      if (ABSL_PREDICT_FALSE(option == options_.end())) {
        std::string message =
            absl::StrCat("Unknown option ", key, ", valid options: ");
        std::vector<Option>::const_iterator iter = options_.cbegin();
        if (iter != options_.cend()) {
          absl::StrAppend(&message, iter->key);
          for (++iter; iter != options_.cend(); ++iter) {
            absl::StrAppend(&message, ", ", iter->key);
          }
        }
        return Fail(message);
      }
      if (ABSL_PREDICT_FALSE(option->seen)) {
        return Fail(absl::StrCat("Option ", key, " is present more than once"));
      }
      ValueParser value_parser(this, key, value);
      if (ABSL_PREDICT_FALSE(!option->function(&value_parser))) {
        if (!value_parser.healthy()) return Fail(value_parser);
        return Fail(absl::StrCat(
            "Option ", key, ": ",
            "invalid value: ", value.empty() ? "(empty)" : value,
            value_parser.valid_values_.empty() ? "" : ", valid values: ",
            value_parser.valid_values_));
      }
      RIEGELI_ASSERT(value_parser.healthy())
          << "Value parser of option " << key
          << " returned true but failed the ValueParser: "
          << value_parser.message();
      option->seen = true;
    }
    if (option_end == text.size()) break;
    option_begin = option_end + 1;
  }
  return true;
}

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

#include "riegeli/base/options_parser.h"

#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <cmath>
#include <functional>
#include <limits>
#include <string>
#include <utility>
#include <vector>

#include "absl/base/optimization.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "riegeli/base/base.h"

namespace riegeli {

namespace {

// This is a struct rather than a lambda to capture function1 and function2 by
// move.
struct OrFunction {
  bool operator()(ValueParser* value_parser) const {
    return function1(value_parser) || function2(value_parser);
  }

  ValueParser::Function function1;
  ValueParser::Function function2;
};

// This is a struct rather than a lambda to capture function1 and function2 by
// move.
struct AndFunction {
  bool operator()(ValueParser* value_parser) const {
    return function1(value_parser) && function2(value_parser);
  }

  ValueParser::Function function1;
  ValueParser::Function function2;
};

}  // namespace

ValueParser::ValueParser(OptionsParser* options_parser, absl::string_view key,
                         absl::string_view value)
    : Object(State::kOpen),
      options_parser_(RIEGELI_ASSERT_NOTNULL(options_parser)),
      key_(key),
      value_(value) {}

ValueParser::Function ValueParser::Int(int* out, int min_value, int max_value) {
  RIEGELI_ASSERT_LE(min_value, max_value)
      << "Failed precondition of OptionsParser::IntOption(): "
         "bounds in the wrong order";
  return [out, min_value, max_value](ValueParser* value_parser) {
    int int_value;
    if (ABSL_PREDICT_TRUE(absl::SimpleAtoi(value_parser->value(), &int_value) &&
                          int_value >= min_value && int_value <= max_value)) {
      *out = int_value;
      return true;
    }
    return value_parser->InvalidValue(
        absl::StrCat("integers ", min_value, "..", max_value));
  };
}

ValueParser::Function ValueParser::Bytes(uint64_t* out, uint64_t min_value,
                                         uint64_t max_value) {
  RIEGELI_ASSERT_LE(min_value, max_value)
      << "Failed precondition of BytesOption(): bounds in the wrong order";
  return [out, min_value, max_value](ValueParser* value_parser) {
    absl::string_view value = value_parser->value();
    double scale = 1.0;
    if (ABSL_PREDICT_TRUE(!value.empty())) {
      switch (value.back()) {
        case 'B':
          break;
        case 'k':
        case 'K':
          scale = static_cast<double>(uint64_t{1} << 10);
          break;
        case 'M':
          scale = static_cast<double>(uint64_t{1} << 20);
          break;
        case 'G':
          scale = static_cast<double>(uint64_t{1} << 30);
          break;
        case 'T':
          scale = static_cast<double>(uint64_t{1} << 40);
          break;
        case 'P':
          scale = static_cast<double>(uint64_t{1} << 50);
          break;
        case 'E':
          scale = static_cast<double>(uint64_t{1} << 60);
          break;
        default:
          goto no_scale;
      }
      value.remove_suffix(1);
    }
  no_scale:
    double double_value;
    if (ABSL_PREDICT_TRUE(absl::SimpleAtod(value, &double_value) &&
                          double_value >= 0.0)) {
      double_value = std::round(double_value * scale);
      const uint64_t uint64_value =
          ABSL_PREDICT_FALSE(
              double_value >=
              static_cast<double>(std::numeric_limits<uint64_t>::max()))
              ? std::numeric_limits<uint64_t>::max()
              : static_cast<uint64_t>(double_value);
      if (ABSL_PREDICT_TRUE(uint64_value >= min_value &&
                            uint64_value <= max_value)) {
        *out = uint64_value;
        return true;
      }
    }
    return value_parser->InvalidValue(
        absl::StrCat("integers expressed as reals with "
                     "optional suffix [BkKMGTPE], ",
                     min_value, "..", max_value));
  };
}

ValueParser::Function ValueParser::Real(double* out, double min_value,
                                        double max_value) {
  RIEGELI_ASSERT_LE(min_value, max_value)
      << "Failed precondition of IntOption(): bounds in the wrong order";
  return [out, min_value, max_value](ValueParser* value_parser) {
    double double_value;
    if (ABSL_PREDICT_TRUE(
            absl::SimpleAtod(value_parser->value(), &double_value) &&
            double_value >= min_value && double_value <= max_value)) {
      *out = double_value;
      return true;
    }
    return value_parser->InvalidValue(
        absl::StrCat("reals ", min_value, "..", max_value));
  };
}

ValueParser::Function ValueParser::Or(Function function1, Function function2) {
  return OrFunction{std::move(function1), std::move(function2)};
}

ValueParser::Function ValueParser::And(Function function1, Function function2) {
  return AndFunction{std::move(function1), std::move(function2)};
}

ValueParser::Function ValueParser::CopyTo(std::string* text) {
  return [text](ValueParser* value_parser) {
    absl::StrAppend(text, text->empty() ? "" : ",", value_parser->key(),
                    value_parser->value().empty() ? "" : ":",
                    value_parser->value());
    return true;
  };
}

ValueParser::Function ValueParser::FailIfSeen(absl::string_view key) {
  return [key](ValueParser* value_parser) {
    for (const OptionsParser::Option& option :
         value_parser->options_parser_->options_) {
      if (option.key == key) {
        if (ABSL_PREDICT_FALSE(option.seen)) {
          return value_parser->Fail(absl::StrCat(
              "Option ", value_parser->key(), " conflicts with option ", key));
        }
        return true;
      }
    }
    RIEGELI_ASSERT_UNREACHABLE() << "Unknown option " << key;
  };
}

ValueParser::Function ValueParser::FailIfAnySeen() {
  return [](ValueParser* value_parser) {
    for (const OptionsParser::Option& option :
         value_parser->options_parser_->options_) {
      if (ABSL_PREDICT_FALSE(option.seen)) {
        return value_parser->Fail(
            absl::StrCat("Option ", value_parser->key(), " must be first"));
      }
    }
    return true;
  };
}

bool ValueParser::InvalidValue(absl::string_view valid_values) {
  RIEGELI_ASSERT(!valid_values.empty())
      << "Failed precondition of OptionsParser::InvalidValue(): "
         "empty valid values";
  absl::StrAppend(&valid_values_, valid_values_.empty() ? "" : ", ",
                  valid_values);
  return false;
}

void OptionsParser::Done() { options_ = std::vector<Option>(); }

bool OptionsParser::Parse(absl::string_view text) {
  if (ABSL_PREDICT_FALSE(!healthy())) return false;
  size_t option_begin = 0;
  for (;;) {
    size_t option_end = text.find(',', option_begin);
    if (option_end == absl::string_view::npos) option_end = text.size();
    if (option_begin != option_end) {
      const absl::string_view key_value =
          text.substr(option_begin, option_end - option_begin);
      absl::string_view key;
      absl::string_view value;
      const size_t colon = key_value.find(':');
      if (colon == absl::string_view::npos) {
        key = key_value;
      } else {
        key = key_value.substr(0, colon);
        value = key_value.substr(colon + 1);
      }
      const std::vector<Option>::iterator option = std::find_if(
          options_.begin(), options_.end(),
          [key](const Option& option) { return option.key == key; });
      if (ABSL_PREDICT_FALSE(option == options_.end())) {
        std::string message =
            absl::StrCat("Unknown option ", key, ", valid options: ");
        std::vector<Option>::const_iterator iter = options_.cbegin();
        if (iter != options_.cend()) {
          absl::StrAppend(&message, iter->key);
          for (++iter; iter != options_.cend(); ++iter) {
            absl::StrAppend(&message, ", ", iter->key);
          }
        }
        return Fail(message);
      }
      if (ABSL_PREDICT_FALSE(option->seen)) {
        return Fail(absl::StrCat("Option ", key, " is present more than once"));
      }
      ValueParser value_parser(this, key, value);
      if (ABSL_PREDICT_FALSE(!option->function(&value_parser))) {
        if (!value_parser.healthy()) return Fail(value_parser);
        return Fail(absl::StrCat(
            "Option ", key, ": ",
            "invalid value: ", value.empty() ? "(empty)" : value,
            value_parser.valid_values_.empty() ? "" : ", valid values: ",
            value_parser.valid_values_));
      }
      RIEGELI_ASSERT(value_parser.healthy())
          << "Value parser of option " << key
          << " returned true but failed the ValueParser: "
          << value_parser.message();
      option->seen = true;
    }
    if (option_end == text.size()) break;
    option_begin = option_end + 1;
  }
  return true;
}

}  // namespace riegeli
<|endoftext|>


<commit_before><commit_msg>Re-enabled jump<commit_after><|endoftext|>


<commit_before>/*
* Copyright 2015, Delft University of Technology
*
* This software is licensed under the terms of the MIT license.
* See http://opensource.org/licenses/MIT for the full license.
*
*
*/

#include <iostream>
#include <thread>
#include <chrono>
using std::chrono::duration_cast;
using std::chrono::high_resolution_clock;
using std::chrono::milliseconds;

#include <opencv2/highgui/highgui.hpp>

#include "detector.hpp"
#include "serversocket.h"
using namespace mirrors;

/// The port used for the server socket.
const uint16_t SERVER_PORT = 23369;

/**
 * Dummy function for handling SIGPIPE, required
 * to stop server crashes on a broken pipe (non-Windows).
 */
void handle_sigpipe(int) {
#ifdef DEBUG
    clog << "DEBUG: Received SIGPIPE: " << endl;
#endif
}

/**
* @brief Entry point of this application.
*
* The first argument to the application indicates the
* camera device ID. If omitted, 0 is used as device ID.
*
* The second argument indicates if frames are displayed
* or not. A non-zero value indicates frames should be
* displayed, while a value of 0 skips showing the frames.
* The default value is to show the frames.
*
* @param argc - The amount of command-line arguments.
* @param argv - The command-line argument values.
* @return The exit code of this application.
*/
int main(int argc, char **argv) {
    signal(SIGPIPE, handle_sigpipe);

    int deviceID = 0;
    bool showFrames = true;
    if (argc > 1) {
        deviceID = atoi(argv[1]);
        std::clog << "Using camera device #" << deviceID << std::endl;
    }
    if (argc > 2) {
        showFrames = atoi(argv[2]) != 0;
        std::clog << "UI Enabled: " << showFrames << std::endl;
    }

    detector cameraDetector(deviceID);

    ServerSocket::initialize();
    ServerSocket server(SERVER_PORT);
    std::thread serverThread([&]() {
        try {
            server.run();
        } catch (const NL::Exception &ex) {
            std::cerr << "Uncaught exception in server: " << ex.what() << std::endl;
            cameraDetector.stop();
        }
    });

    if (showFrames) {
        cv::startWindowThread();
        cv::namedWindow("Camera", CV_WINDOW_AUTOSIZE);
    }

    std::vector<Mat> markerPatterns;

    for (int i = 0; i < 8; i++) {
        markerPatterns.push_back(cv::imread("markers/" + std::to_string(i) + ".png", CV_LOAD_IMAGE_GRAYSCALE));
    }

    bool markersLoaded = cameraDetector.registerMarkers(markerPatterns);

    if (!markersLoaded) {
        std::cerr << "Failed to load markers!" << std::endl;
        return 1;
    }

    // Start detection loop
    cameraDetector.loop([&](const Mat& processedFrame, vector<detected_marker> markers) {
        auto time = duration_cast<milliseconds>(high_resolution_clock::now().time_since_epoch()).count();

        for (auto& marker : markers) {
            server.broadcastPositionUpdate(marker.id, marker.position.x, marker.position.y, marker.rotation, time);
        }

        if (showFrames) {
            cv::imshow("Camera", processedFrame);
        }
    });

    // Disconnect the Socket and wait for the server and detector to stop.
    server.disconnect();
    serverThread.join();

    return EXIT_SUCCESS;
}
<commit_msg>Logging of some server messages are now only enabled in Debug builds.<commit_after>/*
* Copyright 2015, Delft University of Technology
*
* This software is licensed under the terms of the MIT license.
* See http://opensource.org/licenses/MIT for the full license.
*
*
*/

#include <iostream>
#include <thread>
#include <chrono>
using std::chrono::duration_cast;
using std::chrono::high_resolution_clock;
using std::chrono::milliseconds;

#include <opencv2/highgui/highgui.hpp>

#include "detector.hpp"
#include "serversocket.h"
using namespace mirrors;

/// The port used for the server socket.
const uint16_t SERVER_PORT = 23369;

/**
 * Dummy function for handling SIGPIPE, required
 * to stop server crashes on a broken pipe (non-Windows).
 */
void handle_sigpipe(int) {
#ifdef DEBUG
    std::clog << "DEBUG: Received SIGPIPE: " << std::endl;
#endif
}

/**
* @brief Entry point of this application.
*
* The first argument to the application indicates the
* camera device ID. If omitted, 0 is used as device ID.
*
* The second argument indicates if frames are displayed
* or not. A non-zero value indicates frames should be
* displayed, while a value of 0 skips showing the frames.
* The default value is to show the frames.
*
* @param argc - The amount of command-line arguments.
* @param argv - The command-line argument values.
* @return The exit code of this application.
*/
int main(int argc, char **argv) {
    signal(SIGPIPE, handle_sigpipe);

    int deviceID = 0;
    bool showFrames = true;
    if (argc > 1) {
        deviceID = atoi(argv[1]);
    }
    if (argc > 2) {
        showFrames = atoi(argv[2]) != 0;
    }

#ifdef DEBUG
    std::clog << "Using camera device #" << deviceID << std::endl;
    std::clog << "UI Enabled: " << showFrames << std::endl;
#endif

    detector cameraDetector(deviceID);

    ServerSocket::initialize();
    ServerSocket server(SERVER_PORT);
    std::thread serverThread([&]() {
        try {
            server.run();
        } catch (const NL::Exception &ex) {
            std::cerr << "Uncaught exception in server: " << ex.what() << std::endl;
            cameraDetector.stop();
        }
    });

    if (showFrames) {
        cv::startWindowThread();
        cv::namedWindow("Camera", CV_WINDOW_AUTOSIZE);
    }

    std::vector<Mat> markerPatterns;

    for (int i = 0; i < 8; i++) {
        markerPatterns.push_back(cv::imread("markers/" + std::to_string(i) + ".png", CV_LOAD_IMAGE_GRAYSCALE));
    }

    bool markersLoaded = cameraDetector.registerMarkers(markerPatterns);

    if (!markersLoaded) {
        std::cerr << "Failed to load markers!" << std::endl;
        return 1;
    }
#ifdef DEBUG
    std::clog << "Detector initialized (loaded " << markerPatterns.size() << " markers)" << std::endl;
#endif

    // Start detection loop
    cameraDetector.loop([&](const Mat& processedFrame, vector<detected_marker> markers) {
        auto time = duration_cast<milliseconds>(high_resolution_clock::now().time_since_epoch()).count();

        for (auto& marker : markers) {
            server.broadcastPositionUpdate(marker.id, marker.position.x, marker.position.y, marker.rotation, time);
        }

        if (showFrames) {
            cv::imshow("Camera", processedFrame);
        }
    });

    // Disconnect the Socket and wait for the server and detector to stop.
    server.disconnect();
    serverThread.join();

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before><commit_msg>plugins: tester: fix crash<commit_after><|endoftext|>


<commit_before>/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkBmpCodec.h"
#include "SkCodec.h"
#include "SkData.h"
#include "SkCodec_libgif.h"
#include "SkCodec_libico.h"
#include "SkCodec_libpng.h"
#include "SkCodec_wbmp.h"
#include "SkCodecPriv.h"
#if !defined(GOOGLE3)
#include "SkJpegCodec.h"
#endif
#include "SkStream.h"
#include "SkWebpCodec.h"

struct DecoderProc {
    bool (*IsFormat)(const void*, size_t);
    SkCodec* (*NewFromStream)(SkStream*);
};

static const DecoderProc gDecoderProcs[] = {
#if !defined(GOOGLE3)
    { SkJpegCodec::IsJpeg, SkJpegCodec::NewFromStream },
#endif
    { SkWebpCodec::IsWebp, SkWebpCodec::NewFromStream },
    { SkGifCodec::IsGif, SkGifCodec::NewFromStream },
    { SkIcoCodec::IsIco, SkIcoCodec::NewFromStream },
    { SkBmpCodec::IsBmp, SkBmpCodec::NewFromStream },
    { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewFromStream }
};

size_t SkCodec::MinBufferedBytesNeeded() {
    return WEBP_VP8_HEADER_SIZE;
}

SkCodec* SkCodec::NewFromStream(SkStream* stream,
                                SkPngChunkReader* chunkReader) {
    if (!stream) {
        return nullptr;
    }

    SkAutoTDelete<SkStream> streamDeleter(stream);

    // 14 is enough to read all of the supported types.
    const size_t bytesToRead = 14;
    SkASSERT(bytesToRead <= MinBufferedBytesNeeded());

    char buffer[bytesToRead];
    size_t bytesRead = stream->peek(buffer, bytesToRead);

    // It is also possible to have a complete image less than bytesToRead bytes
    // (e.g. a 1 x 1 wbmp), meaning peek() would return less than bytesToRead.
    // Assume that if bytesRead < bytesToRead, but > 0, the stream is shorter
    // than bytesToRead, so pass that directly to the decoder.
    // It also is possible the stream uses too small a buffer for peeking, but
    // we trust the caller to use a large enough buffer.

    if (0 == bytesRead) {
        SkCodecPrintf("Could not peek!\n");
        // It is possible the stream does not support peeking, but does support
        // rewinding.
        // Attempt to read() and pass the actual amount read to the decoder.
        bytesRead = stream->read(buffer, bytesToRead);
        if (!stream->rewind()) {
            SkCodecPrintf("Could not rewind!\n");
            return nullptr;
        }
    }

    SkAutoTDelete<SkCodec> codec(nullptr);
    // PNG is special, since we want to be able to supply an SkPngChunkReader.
    // But this code follows the same pattern as the loop.
    if (SkPngCodec::IsPng(buffer, bytesRead)) {
        codec.reset(SkPngCodec::NewFromStream(streamDeleter.detach(), chunkReader));
    } else {
        for (DecoderProc proc : gDecoderProcs) {
            if (proc.IsFormat(buffer, bytesRead)) {
                codec.reset(proc.NewFromStream(streamDeleter.detach()));
                break;
            }
        }
    }

    // Set the max size at 128 megapixels (512 MB for kN32).
    // This is about 4x smaller than a test image that takes a few minutes for
    // dm to decode and draw.
    const int32_t maxSize = 1 << 27;
    if (codec && codec->getInfo().width() * codec->getInfo().height() > maxSize) {
        SkCodecPrintf("Error: Image size too large, cannot decode.\n");
        return nullptr;
    } else {
        return codec.detach();
    }
}

SkCodec* SkCodec::NewFromData(SkData* data, SkPngChunkReader* reader) {
    if (!data) {
        return nullptr;
    }
    return NewFromStream(new SkMemoryStream(data), reader);
}

SkCodec::SkCodec(const SkImageInfo& info, SkStream* stream)
    : fSrcInfo(info)
    , fStream(stream)
    , fNeedsRewind(false)
    , fDstInfo()
    , fOptions()
    , fCurrScanline(-1)
{}

SkCodec::~SkCodec() {}

bool SkCodec::rewindIfNeeded() {
    if (!fStream) {
        // Some codecs do not have a stream, but they hold others that do. They
        // must handle rewinding themselves.
        return true;
    }

    // Store the value of fNeedsRewind so we can update it. Next read will
    // require a rewind.
    const bool needsRewind = fNeedsRewind;
    fNeedsRewind = true;
    if (!needsRewind) {
        return true;
    }

    // startScanlineDecode will need to be called before decoding scanlines.
    fCurrScanline = -1;

    if (!fStream->rewind()) {
        return false;
    }

    return this->onRewind();
}

SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
                                   const Options* options, SkPMColor ctable[], int* ctableCount) {
    if (kUnknown_SkColorType == info.colorType()) {
        return kInvalidConversion;
    }
    if (nullptr == pixels) {
        return kInvalidParameters;
    }
    if (rowBytes < info.minRowBytes()) {
        return kInvalidParameters;
    }

    if (kIndex_8_SkColorType == info.colorType()) {
        if (nullptr == ctable || nullptr == ctableCount) {
            return kInvalidParameters;
        }
    } else {
        if (ctableCount) {
            *ctableCount = 0;
        }
        ctableCount = nullptr;
        ctable = nullptr;
    }

    {
        SkAlphaType canonical;
        if (!SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &canonical)
            || canonical != info.alphaType())
        {
            return kInvalidConversion;
        }
    }

    if (!this->rewindIfNeeded()) {
        return kCouldNotRewind;
    }

    // Default options.
    Options optsStorage;
    if (nullptr == options) {
        options = &optsStorage;
    } else if (options->fSubset) {
        SkIRect subset(*options->fSubset);
        if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) {
            // FIXME: How to differentiate between not supporting subset at all
            // and not supporting this particular subset?
            return kUnimplemented;
        }
    }

    // FIXME: Support subsets somehow? Note that this works for SkWebpCodec
    // because it supports arbitrary scaling/subset combinations.
    if (!this->dimensionsSupported(info.dimensions())) {
        return kInvalidScale;
    }

    // On an incomplete decode, the subclass will specify the number of scanlines that it decoded
    // successfully.
    int rowsDecoded = 0;
    const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount,
            &rowsDecoded);

    if ((kIncompleteInput == result || kSuccess == result) && ctableCount) {
        SkASSERT(*ctableCount >= 0 && *ctableCount <= 256);
    }

    // A return value of kIncompleteInput indicates a truncated image stream.
    // In this case, we will fill any uninitialized memory with a default value.
    // Some subclasses will take care of filling any uninitialized memory on
    // their own.  They indicate that all of the memory has been filled by
    // setting rowsDecoded equal to the height.
    if (kIncompleteInput == result && rowsDecoded != info.height()) {
        this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitialized, info.height(),
                rowsDecoded);
    }

    return result;
}

SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
    return this->getPixels(info, pixels, rowBytes, nullptr, nullptr, nullptr);
}

SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo,
        const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) {
    // Reset fCurrScanline in case of failure.
    fCurrScanline = -1;
    // Ensure that valid color ptrs are passed in for kIndex8 color type
    if (kIndex_8_SkColorType == dstInfo.colorType()) {
        if (nullptr == ctable || nullptr == ctableCount) {
            return SkCodec::kInvalidParameters;
        }
    } else {
        if (ctableCount) {
            *ctableCount = 0;
        }
        ctableCount = nullptr;
        ctable = nullptr;
    }

    if (!this->rewindIfNeeded()) {
        return kCouldNotRewind;
    }

    // Set options.
    Options optsStorage;
    if (nullptr == options) {
        options = &optsStorage;
    } else if (options->fSubset) {
        SkIRect size = SkIRect::MakeSize(dstInfo.dimensions());
        if (!size.contains(*options->fSubset)) {
            return kInvalidInput;
        }

        // We only support subsetting in the x-dimension for scanline decoder.
        // Subsetting in the y-dimension can be accomplished using skipScanlines().
        if (options->fSubset->top() != 0 || options->fSubset->height() != dstInfo.height()) {
            return kInvalidInput;
        }
    }

    // FIXME: Support subsets somehow?
    if (!this->dimensionsSupported(dstInfo.dimensions())) {
        return kInvalidScale;
    }

    const Result result = this->onStartScanlineDecode(dstInfo, *options, ctable, ctableCount);
    if (result != SkCodec::kSuccess) {
        return result;
    }

    fCurrScanline = 0;
    fDstInfo = dstInfo;
    fOptions = *options;
    return kSuccess;
}

SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo) {
    return this->startScanlineDecode(dstInfo, nullptr, nullptr, nullptr);
}

int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) {
    if (fCurrScanline < 0) {
        return 0;
    }

    SkASSERT(!fDstInfo.isEmpty());
    if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) {
        return 0;
    }

    const int linesDecoded = this->onGetScanlines(dst, countLines, rowBytes);
    if (linesDecoded < countLines) {
        this->fillIncompleteImage(this->dstInfo(), dst, rowBytes, this->options().fZeroInitialized,
                countLines, linesDecoded);
    }
    fCurrScanline += countLines;
    return linesDecoded;
}

bool SkCodec::skipScanlines(int countLines) {
    if (fCurrScanline < 0) {
        return false;
    }

    SkASSERT(!fDstInfo.isEmpty());
    if (countLines < 0 || fCurrScanline + countLines > fDstInfo.height()) {
        // Arguably, we could just skip the scanlines which are remaining,
        // and return true. We choose to return false so the client
        // can catch their bug.
        return false;
    }

    bool result = this->onSkipScanlines(countLines);
    fCurrScanline += countLines;
    return result;
}

int SkCodec::outputScanline(int inputScanline) const {
    SkASSERT(0 <= inputScanline && inputScanline < this->getInfo().height());
    return this->onOutputScanline(inputScanline);
}

int SkCodec::onOutputScanline(int inputScanline) const {
    switch (this->getScanlineOrder()) {
        case kTopDown_SkScanlineOrder:
        case kNone_SkScanlineOrder:
            return inputScanline;
        case kBottomUp_SkScanlineOrder:
            return this->getInfo().height() - inputScanline - 1;
        default:
            // This case indicates an interlaced gif and is implemented by SkGifCodec.
            SkASSERT(false);
            return 0;
    }
}

static void fill_proc(const SkImageInfo& info, void* dst, size_t rowBytes,
        uint32_t colorOrIndex, SkCodec::ZeroInitialized zeroInit, SkSampler* sampler) {
    if (sampler) {
        sampler->fill(info, dst, rowBytes, colorOrIndex, zeroInit);
    } else {
        SkSampler::Fill(info, dst, rowBytes, colorOrIndex, zeroInit);
    }
}

void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t rowBytes,
        ZeroInitialized zeroInit, int linesRequested, int linesDecoded) {

    void* fillDst;
    const uint32_t fillValue = this->getFillValue(info.colorType(), info.alphaType());
    const int linesRemaining = linesRequested - linesDecoded;
    SkSampler* sampler = this->getSampler(false);

    switch (this->getScanlineOrder()) {
        case kTopDown_SkScanlineOrder:
        case kNone_SkScanlineOrder: {
            const SkImageInfo fillInfo = info.makeWH(info.width(), linesRemaining);
            fillDst = SkTAddOffset<void>(dst, linesDecoded * rowBytes);
            fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
            break;
        }
        case kBottomUp_SkScanlineOrder: {
            fillDst = dst;
            const SkImageInfo fillInfo = info.makeWH(info.width(), linesRemaining);
            fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
            break;
        }
        case kOutOfOrder_SkScanlineOrder: {
            SkASSERT(1 == linesRequested || this->getInfo().height() == linesRequested);
            const SkImageInfo fillInfo = info.makeWH(info.width(), 1);
            for (int srcY = linesDecoded; srcY < linesRequested; srcY++) {
                fillDst = SkTAddOffset<void>(dst, this->outputScanline(srcY) * rowBytes);
                fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
            }
            break;
        }
    }
}
<commit_msg>Fix some SkCodecPrintfs<commit_after>/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkBmpCodec.h"
#include "SkCodec.h"
#include "SkData.h"
#include "SkCodec_libgif.h"
#include "SkCodec_libico.h"
#include "SkCodec_libpng.h"
#include "SkCodec_wbmp.h"
#include "SkCodecPriv.h"
#if !defined(GOOGLE3)
#include "SkJpegCodec.h"
#endif
#include "SkStream.h"
#include "SkWebpCodec.h"

struct DecoderProc {
    bool (*IsFormat)(const void*, size_t);
    SkCodec* (*NewFromStream)(SkStream*);
};

static const DecoderProc gDecoderProcs[] = {
#if !defined(GOOGLE3)
    { SkJpegCodec::IsJpeg, SkJpegCodec::NewFromStream },
#endif
    { SkWebpCodec::IsWebp, SkWebpCodec::NewFromStream },
    { SkGifCodec::IsGif, SkGifCodec::NewFromStream },
    { SkIcoCodec::IsIco, SkIcoCodec::NewFromStream },
    { SkBmpCodec::IsBmp, SkBmpCodec::NewFromStream },
    { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewFromStream }
};

size_t SkCodec::MinBufferedBytesNeeded() {
    return WEBP_VP8_HEADER_SIZE;
}

SkCodec* SkCodec::NewFromStream(SkStream* stream,
                                SkPngChunkReader* chunkReader) {
    if (!stream) {
        return nullptr;
    }

    SkAutoTDelete<SkStream> streamDeleter(stream);

    // 14 is enough to read all of the supported types.
    const size_t bytesToRead = 14;
    SkASSERT(bytesToRead <= MinBufferedBytesNeeded());

    char buffer[bytesToRead];
    size_t bytesRead = stream->peek(buffer, bytesToRead);

    // It is also possible to have a complete image less than bytesToRead bytes
    // (e.g. a 1 x 1 wbmp), meaning peek() would return less than bytesToRead.
    // Assume that if bytesRead < bytesToRead, but > 0, the stream is shorter
    // than bytesToRead, so pass that directly to the decoder.
    // It also is possible the stream uses too small a buffer for peeking, but
    // we trust the caller to use a large enough buffer.

    if (0 == bytesRead) {
        // TODO: After implementing peek in CreateJavaOutputStreamAdaptor.cpp, this
        // printf could be useful to notice failures.
        // SkCodecPrintf("Encoded image data failed to peek!\n");

        // It is possible the stream does not support peeking, but does support
        // rewinding.
        // Attempt to read() and pass the actual amount read to the decoder.
        bytesRead = stream->read(buffer, bytesToRead);
        if (!stream->rewind()) {
            SkCodecPrintf("Encoded image data could not peek or rewind to determine format!\n");
            return nullptr;
        }
    }

    SkAutoTDelete<SkCodec> codec(nullptr);
    // PNG is special, since we want to be able to supply an SkPngChunkReader.
    // But this code follows the same pattern as the loop.
    if (SkPngCodec::IsPng(buffer, bytesRead)) {
        codec.reset(SkPngCodec::NewFromStream(streamDeleter.detach(), chunkReader));
    } else {
        for (DecoderProc proc : gDecoderProcs) {
            if (proc.IsFormat(buffer, bytesRead)) {
                codec.reset(proc.NewFromStream(streamDeleter.detach()));
                break;
            }
        }
    }

    // Set the max size at 128 megapixels (512 MB for kN32).
    // This is about 4x smaller than a test image that takes a few minutes for
    // dm to decode and draw.
    const int32_t maxSize = 1 << 27;
    if (codec && codec->getInfo().width() * codec->getInfo().height() > maxSize) {
        SkCodecPrintf("Error: Image size too large, cannot decode.\n");
        return nullptr;
    } else {
        return codec.detach();
    }
}

SkCodec* SkCodec::NewFromData(SkData* data, SkPngChunkReader* reader) {
    if (!data) {
        return nullptr;
    }
    return NewFromStream(new SkMemoryStream(data), reader);
}

SkCodec::SkCodec(const SkImageInfo& info, SkStream* stream)
    : fSrcInfo(info)
    , fStream(stream)
    , fNeedsRewind(false)
    , fDstInfo()
    , fOptions()
    , fCurrScanline(-1)
{}

SkCodec::~SkCodec() {}

bool SkCodec::rewindIfNeeded() {
    if (!fStream) {
        // Some codecs do not have a stream, but they hold others that do. They
        // must handle rewinding themselves.
        return true;
    }

    // Store the value of fNeedsRewind so we can update it. Next read will
    // require a rewind.
    const bool needsRewind = fNeedsRewind;
    fNeedsRewind = true;
    if (!needsRewind) {
        return true;
    }

    // startScanlineDecode will need to be called before decoding scanlines.
    fCurrScanline = -1;

    if (!fStream->rewind()) {
        return false;
    }

    return this->onRewind();
}

SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
                                   const Options* options, SkPMColor ctable[], int* ctableCount) {
    if (kUnknown_SkColorType == info.colorType()) {
        return kInvalidConversion;
    }
    if (nullptr == pixels) {
        return kInvalidParameters;
    }
    if (rowBytes < info.minRowBytes()) {
        return kInvalidParameters;
    }

    if (kIndex_8_SkColorType == info.colorType()) {
        if (nullptr == ctable || nullptr == ctableCount) {
            return kInvalidParameters;
        }
    } else {
        if (ctableCount) {
            *ctableCount = 0;
        }
        ctableCount = nullptr;
        ctable = nullptr;
    }

    {
        SkAlphaType canonical;
        if (!SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &canonical)
            || canonical != info.alphaType())
        {
            return kInvalidConversion;
        }
    }

    if (!this->rewindIfNeeded()) {
        return kCouldNotRewind;
    }

    // Default options.
    Options optsStorage;
    if (nullptr == options) {
        options = &optsStorage;
    } else if (options->fSubset) {
        SkIRect subset(*options->fSubset);
        if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) {
            // FIXME: How to differentiate between not supporting subset at all
            // and not supporting this particular subset?
            return kUnimplemented;
        }
    }

    // FIXME: Support subsets somehow? Note that this works for SkWebpCodec
    // because it supports arbitrary scaling/subset combinations.
    if (!this->dimensionsSupported(info.dimensions())) {
        return kInvalidScale;
    }

    // On an incomplete decode, the subclass will specify the number of scanlines that it decoded
    // successfully.
    int rowsDecoded = 0;
    const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount,
            &rowsDecoded);

    if ((kIncompleteInput == result || kSuccess == result) && ctableCount) {
        SkASSERT(*ctableCount >= 0 && *ctableCount <= 256);
    }

    // A return value of kIncompleteInput indicates a truncated image stream.
    // In this case, we will fill any uninitialized memory with a default value.
    // Some subclasses will take care of filling any uninitialized memory on
    // their own.  They indicate that all of the memory has been filled by
    // setting rowsDecoded equal to the height.
    if (kIncompleteInput == result && rowsDecoded != info.height()) {
        this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitialized, info.height(),
                rowsDecoded);
    }

    return result;
}

SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
    return this->getPixels(info, pixels, rowBytes, nullptr, nullptr, nullptr);
}

SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo,
        const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) {
    // Reset fCurrScanline in case of failure.
    fCurrScanline = -1;
    // Ensure that valid color ptrs are passed in for kIndex8 color type
    if (kIndex_8_SkColorType == dstInfo.colorType()) {
        if (nullptr == ctable || nullptr == ctableCount) {
            return SkCodec::kInvalidParameters;
        }
    } else {
        if (ctableCount) {
            *ctableCount = 0;
        }
        ctableCount = nullptr;
        ctable = nullptr;
    }

    if (!this->rewindIfNeeded()) {
        return kCouldNotRewind;
    }

    // Set options.
    Options optsStorage;
    if (nullptr == options) {
        options = &optsStorage;
    } else if (options->fSubset) {
        SkIRect size = SkIRect::MakeSize(dstInfo.dimensions());
        if (!size.contains(*options->fSubset)) {
            return kInvalidInput;
        }

        // We only support subsetting in the x-dimension for scanline decoder.
        // Subsetting in the y-dimension can be accomplished using skipScanlines().
        if (options->fSubset->top() != 0 || options->fSubset->height() != dstInfo.height()) {
            return kInvalidInput;
        }
    }

    // FIXME: Support subsets somehow?
    if (!this->dimensionsSupported(dstInfo.dimensions())) {
        return kInvalidScale;
    }

    const Result result = this->onStartScanlineDecode(dstInfo, *options, ctable, ctableCount);
    if (result != SkCodec::kSuccess) {
        return result;
    }

    fCurrScanline = 0;
    fDstInfo = dstInfo;
    fOptions = *options;
    return kSuccess;
}

SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo) {
    return this->startScanlineDecode(dstInfo, nullptr, nullptr, nullptr);
}

int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) {
    if (fCurrScanline < 0) {
        return 0;
    }

    SkASSERT(!fDstInfo.isEmpty());
    if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) {
        return 0;
    }

    const int linesDecoded = this->onGetScanlines(dst, countLines, rowBytes);
    if (linesDecoded < countLines) {
        this->fillIncompleteImage(this->dstInfo(), dst, rowBytes, this->options().fZeroInitialized,
                countLines, linesDecoded);
    }
    fCurrScanline += countLines;
    return linesDecoded;
}

bool SkCodec::skipScanlines(int countLines) {
    if (fCurrScanline < 0) {
        return false;
    }

    SkASSERT(!fDstInfo.isEmpty());
    if (countLines < 0 || fCurrScanline + countLines > fDstInfo.height()) {
        // Arguably, we could just skip the scanlines which are remaining,
        // and return true. We choose to return false so the client
        // can catch their bug.
        return false;
    }

    bool result = this->onSkipScanlines(countLines);
    fCurrScanline += countLines;
    return result;
}

int SkCodec::outputScanline(int inputScanline) const {
    SkASSERT(0 <= inputScanline && inputScanline < this->getInfo().height());
    return this->onOutputScanline(inputScanline);
}

int SkCodec::onOutputScanline(int inputScanline) const {
    switch (this->getScanlineOrder()) {
        case kTopDown_SkScanlineOrder:
        case kNone_SkScanlineOrder:
            return inputScanline;
        case kBottomUp_SkScanlineOrder:
            return this->getInfo().height() - inputScanline - 1;
        default:
            // This case indicates an interlaced gif and is implemented by SkGifCodec.
            SkASSERT(false);
            return 0;
    }
}

static void fill_proc(const SkImageInfo& info, void* dst, size_t rowBytes,
        uint32_t colorOrIndex, SkCodec::ZeroInitialized zeroInit, SkSampler* sampler) {
    if (sampler) {
        sampler->fill(info, dst, rowBytes, colorOrIndex, zeroInit);
    } else {
        SkSampler::Fill(info, dst, rowBytes, colorOrIndex, zeroInit);
    }
}

void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t rowBytes,
        ZeroInitialized zeroInit, int linesRequested, int linesDecoded) {

    void* fillDst;
    const uint32_t fillValue = this->getFillValue(info.colorType(), info.alphaType());
    const int linesRemaining = linesRequested - linesDecoded;
    SkSampler* sampler = this->getSampler(false);

    switch (this->getScanlineOrder()) {
        case kTopDown_SkScanlineOrder:
        case kNone_SkScanlineOrder: {
            const SkImageInfo fillInfo = info.makeWH(info.width(), linesRemaining);
            fillDst = SkTAddOffset<void>(dst, linesDecoded * rowBytes);
            fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
            break;
        }
        case kBottomUp_SkScanlineOrder: {
            fillDst = dst;
            const SkImageInfo fillInfo = info.makeWH(info.width(), linesRemaining);
            fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
            break;
        }
        case kOutOfOrder_SkScanlineOrder: {
            SkASSERT(1 == linesRequested || this->getInfo().height() == linesRequested);
            const SkImageInfo fillInfo = info.makeWH(info.width(), 1);
            for (int srcY = linesDecoded; srcY < linesRequested; srcY++) {
                fillDst = SkTAddOffset<void>(dst, this->outputScanline(srcY) * rowBytes);
                fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
            }
            break;
        }
    }
}
<|endoftext|>