<commit_before>// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include "precomp.h"

namespace Dml
{

constexpr NameAndIndex coordinateTransformationModes[] =
{
    {"half_pixel", 0},
    {"pytorch_half_pixel", 1},
    {"align_corners", 2},
    {"asymmetric", 3},
    {"tf_half_pixel_for_nn", 4},
    {"tf_crop_and_resize", 5},
};

constexpr NameAndIndex nearestNeighborRoundingModes[] =
{
    {"", 0},
    {"round_prefer_floor", 0},
    {"round_prefer_ceil", 1},
    {"floor", 2},
};

void ComputePixelOffsetsAndScales(
    const MLOperatorKernelCreationContext& kernelCreationContext,
    gsl::span<const float> regionOfInterest, // May be empty depending on mode.
    gsl::span<const uint32_t> inputDimensions,
    gsl::span<const uint32_t> outputDimensions,
    /*inout*/ gsl::span<float> scales,
    /*out*/   gsl::span<float> inputPixelOffsets,
    /*out*/   gsl::span<float> outputPixelOffsets
    )
{
    assert(inputDimensions.size() == outputDimensions.size());
    assert(inputPixelOffsets.size() == outputPixelOffsets.size());
    assert(inputPixelOffsets.size() == scales.size());
    assert(inputPixelOffsets.size() == inputDimensions.size());
    assert(regionOfInterest.empty() || regionOfInterest.size() == inputDimensions.size() * 2);

    std::string coordinateTransformationMode = kernelCreationContext.GetOptionalAttribute<std::string>(AttrName::CoordinateTransformationMode, "half_pixel");
    auto optionalCoordinateTransformationModeValue = TryMapStringToIndex(coordinateTransformationMode, coordinateTransformationModes);
    if (!optionalCoordinateTransformationModeValue)
    {
        ML_INVALID_ARGUMENT("Unsupported 'coordinate_transformation_mode'");
    }
    uint32_t coordinateTransformationModeValue = *optionalCoordinateTransformationModeValue;

    ML_CHECK_VALID_ARGUMENT(
        !regionOfInterest.empty() || coordinateTransformationModeValue != 5 /*tf_crop_and_resize*/,
        "Resize expects 'roi' tensor for 'tf_crop_and_resize' mode."
    );

    const uint32_t rank = gsl::narrow_cast<uint32_t>(inputDimensions.size());

    // Fill in all the input/output pixel offset for each axis,
    // and recompute the scale for certain modes.

    for (uint32_t i = 0; i < rank; ++i)
    {
        float inputPixelOffset = 0;
        float outputPixelOffset = 0;

        // All these mapping modes can be generalized to the equations:
        //
        // output_coordinate = (input_coordinate  + input_offset ) * scale + output_offset
        // input_coordinate  = (output_coordinate - output_offset) / scale - input_offset
        //
        // With DML, a scale > 1 maps input to an upsampled output, and a positive pixel
        // offset shifts the input contents to the right/down in the output.
        //
        // Since the equations from ONNX are in terms of mapping the output coordinate back
        // to the input coordinate, any offsets need their signs flipped. e.g. For "half_pixel",
        // the "x_resized" is the output coordinate, and the "+ 0.5" is the output coordinate
        // adjustment which needs to be -0.5 when passed to DML.

        switch (coordinateTransformationModeValue)
        {
        case 0:
            // coordinate_transformation_mode is "half_pixel",
            // x_original = (x_resized + 0.5) / scale - 0.5
            inputPixelOffset = 0.5;
            outputPixelOffset = -0.5;
            // Keep existing scales.
            break;

        case 1:
            // if coordinate_transformation_mode is "pytorch_half_pixel",
            // x_original = length_resized > 1 ? (x_resized + 0.5) / scale - 0.5 : 0
            if (inputDimensions[i] <= 1)
            {
                inputPixelOffset = 0.0;
                outputPixelOffset = 0.0;
                scales[i] = FLT_MAX; // Set large scale so all output pixels map to 0th input pixel.
            }
            else
            {
                inputPixelOffset = 0.5;
                outputPixelOffset = -0.5;
                // Keep existing scales.
            }
            break;

        case 2:
            // if coordinate_transformation_mode is "align_corners",
            // x_original = x_resized * (length_original - 1) / (length_resized - 1)
            inputPixelOffset = 0.0;
            outputPixelOffset = 0.0;
            if (outputDimensions[i] <= 1 || inputDimensions[i] <= 1)
            {
                // Protect against division by zero when either input/output is a single pixel.
                scales[i] = FLT_MAX;
            }
            else
            {
                // Recalcalculate scale, ignoring existing one (only used to determine output size).
                scales[i] = float(outputDimensions[i] - 1) / (inputDimensions[i] - 1);
            }
            break;

        case 3:
            // if coordinate_transformation_mode is "asymmetric",
            // x_original = x_resized / scale
            inputPixelOffset = 0.0;
            outputPixelOffset = 0.0;
            // Keep existing scales.
            break;

        case 4:
            // if coordinate_transformation_mode is "tf_half_pixel_for_nn",
            // x_original = (x_resized + 0.5) / scale
            inputPixelOffset = 0.0;
            outputPixelOffset = -0.5;
            // Keep existing scales.
            break;

        case 5:
            // if coordinate_transformation_mode is "tf_crop_and_resize",
            // x_original = length_resized > 1 ? start_x * (length_original - 1) + x_resized * (end_x - start_x) * (length_original - 1) / (length_resized - 1)
            //                                 : 0.5 * (start_x + end_x) * (length_original - 1)
            if (inputDimensions[i] > 1)
            {
                assert(regionOfInterest.size() == rank * 2);

                // Fold this part of the equation into the input offset: start_x * (length_original - 1)
                inputPixelOffset = -(regionOfInterest[i] * (inputDimensions[i] - 1));
                outputPixelOffset = 0.0;

                // Fold this part to scale: (end_x - start_x) * (length_original - 1) / (length_resized - 1)
                float computedScale = float(outputDimensions[i] - 1)
                                    / std::max((regionOfInterest[i + rank] - regionOfInterest[i]) * (inputDimensions[i] - 1), 1.0f);
                scales[i] = computedScale;
            }
            else // inputDimensions[i] <= 1
            {
                // 0.5 * (start_x + end_x) * (length_original - 1)
                inputPixelOffset = -0.5f * (regionOfInterest[i] + regionOfInterest[i + rank]) * (inputDimensions[i] - 1);
                outputPixelOffset = 0.0;
                scales[i] = 1;
            }
            break;

        default:
            assert(false); // TryMapStringToIndex would have already bailed above.
        }

        inputPixelOffsets[i] = inputPixelOffset;
        outputPixelOffsets[i] = outputPixelOffset;
    }
}

class DmlOperatorResize : public DmlOperator, public ResizeHelper
{
public:
    // Resample a multidimensional image to a new size.
    DmlOperatorResize(const MLOperatorKernelCreationContext& kernelCreationContext, uint32_t opsetVersion)
    :   DmlOperator(kernelCreationContext),
        ResizeHelper(kernelCreationContext, kernelCreationContext.GetTensorShapeDescription(), opsetVersion)
    {
        ML_CHECK_VALID_ARGUMENT(!m_scales.empty(), "Resize/Upsample expect scales, either a 2nd input tensors or 'scales' attribute.");
        ML_CHECK_VALID_ARGUMENT(kernelCreationContext.GetOutputCount() == 1, "Resize/Upsample expect 1 output tensor.");

        // Use only the first input tensor. In the case of Resize or the later Upsample-v9,
        // the second tensor is CPU based and should not be passed to Resize.
        std::vector<std::optional<uint32_t>> inputIndices = { 0 };
        std::vector<std::optional<uint32_t>> outputIndices = { 0 };
        DmlOperator::Initialize(kernelCreationContext, inputIndices, outputIndices);

        // Because DirectML supports a limited number of dimensions, try to squeeze the dimension count
        // to only those which actually matter. Models sometimes use a greater number of dimensions,
        // even though those dimensions have no significance and can be elided (nop 1's), coercing the
        // total dimension count back down to a supported value.

        std::vector<uint32_t> squeezedInputShape = m_inputDimensions;
        std::vector<uint32_t> squeezedOutputShape = m_outputDimensions;
        std::vector<uint32_t> squeezableDimensionIndices;
        std::vector<float> paddedScales = m_scales;
        std::vector<float> inputPixelOffsets(paddedScales.size());
        std::vector<float> outputPixelOffsets(paddedScales.size());

        ComputePixelOffsetsAndScales(
            kernelCreationContext,
            m_regionOfInterest, // May be empty depending on mode.
            m_inputDimensions,
            m_outputDimensions,
            /*inout*/ paddedScales,
            /*out*/ inputPixelOffsets,
            /*out*/ outputPixelOffsets
        );

        // Find any useless dimensions of size 1 that occur in both input and output.
        for (size_t i = 0, rank = m_outputDimensions.size(); i < rank; ++i)
        {
            if (m_inputDimensions[i] = 1 && m_outputDimensions[i] == 1)
            {
                squeezableDimensionIndices.push_back(gsl::narrow_cast<uint32_t>(i));
            }
        }
        RemoveValuesByIndex(squeezableDimensionIndices, /*keepOneValue*/ true, /*inout*/ squeezedInputShape);
        RemoveValuesByIndex(squeezableDimensionIndices, /*keepOneValue*/ true, /*inout*/ paddedScales);
        RemoveValuesByIndex(squeezableDimensionIndices, /*keepOneValue*/ true, /*inout*/ inputPixelOffsets);
        RemoveValuesByIndex(squeezableDimensionIndices, /*keepOneValue*/ true, /*inout*/ outputPixelOffsets);
        RemoveValuesByIndex(squeezableDimensionIndices, /*keepOneValue*/ true, /*inout*/ squeezedOutputShape);

        // Update the tensor descriptions.
        MLOperatorTensorDataType inputTensorDataType = kernelCreationContext.GetInputEdgeDescription(0).tensorDataType;
        auto inputTensorDesc = TensorDesc(inputTensorDataType, squeezedInputShape, squeezedInputShape, TensorAxis::DoNotCoerce, TensorAxis::W, TensorAxis::RightAligned, NchwDimensionCount, 0);
        auto outputTensorDesc = TensorDesc(inputTensorDataType, squeezedOutputShape, squeezedOutputShape, TensorAxis::DoNotCoerce, TensorAxis::W, TensorAxis::RightAligned, NchwDimensionCount, 0);
        m_inputTensorDescs[0] = inputTensorDesc;
        m_outputTensorDescs[0] = outputTensorDesc;

        // If the output tensor dimension count was right-aligned to a larger size,
        // then ensure that scales has the same count as the tensor rank by inserting
        // leading ones, since DirectML requires the scales to have the same count.
        const uint32_t squeezedDimCount = gsl::narrow_cast<uint32_t>(squeezedOutputShape.size());
        const uint32_t dmlCompatibleDimCount = outputTensorDesc.GetDimensionCount();
        if (dmlCompatibleDimCount > squeezedDimCount)
        {
            paddedScales.insert(paddedScales.begin(), dmlCompatibleDimCount - squeezedDimCount, 1.0f);
            inputPixelOffsets.insert(inputPixelOffsets.begin(), dmlCompatibleDimCount - squeezedDimCount, 0.5f);
            outputPixelOffsets.insert(outputPixelOffsets.begin(), dmlCompatibleDimCount - squeezedDimCount, -0.5f);
        }

        std::string mode = kernelCreationContext.GetOptionalAttribute<std::string>(AttrName::Mode, "NEAREST");
        DML_INTERPOLATION_MODE interpolationMode = Dml::MapStringToInteropolationMode(mode);

        // DML's nearest neighbor mode uses round-halves-up (or round_prefer_ceil) via floor(input.x + 0.5).
        // So to support floor, adjust the input by half a pixel.
        // round_prefer_floor is not supported without an API extension,
        // but existing code already default to treating it as round_prefer_ceil.
        // So continue that.
        if (interpolationMode == DML_INTERPOLATION_MODE_NEAREST_NEIGHBOR)
        {
            std::string nearestMode = kernelCreationContext.GetOptionalAttribute<std::string>(AttrName::NearestMode, "round_prefer_floor");
            auto optionalNearestModeValue = TryMapStringToIndex(nearestMode, nearestNeighborRoundingModes);
            if (optionalNearestModeValue)
            {
                switch (*optionalNearestModeValue)
                {
                case 0: // round_prefer_floor
                case 1: // round_prefer_ceil
                    break;
                case 2: // floor
                    for (auto& offset : inputPixelOffsets)
                    {
                        offset += 0.5;
                    }
                    break;
                default:
                    assert(false);
                }
            }
        }

        // Create the operator description.
        std::vector<DML_TENSOR_DESC> inputDescs = GetDmlInputDescs();
        std::vector<DML_TENSOR_DESC> outputDescs = GetDmlOutputDescs();

        DML_RESAMPLE1_OPERATOR_DESC operatorDesc = {};
        operatorDesc.InputTensor = inputDescs.data();
        operatorDesc.OutputTensor = outputDescs.data();
        operatorDesc.InterpolationMode = interpolationMode;
        operatorDesc.Scales = paddedScales.data();
        operatorDesc.DimensionCount = gsl::narrow_cast<uint32_t>(paddedScales.size());
        operatorDesc.InputPixelOffsets = inputPixelOffsets.data();
        operatorDesc.OutputPixelOffsets = outputPixelOffsets.data();

        DML_OPERATOR_DESC opDesc = { DML_OPERATOR_RESAMPLE1, &operatorDesc };
        SetDmlOperatorDesc(opDesc, kernelCreationContext);
    }
};

void CALLBACK QueryResize(IMLOperatorSupportQueryContextPrivate* context, bool* isSupported)
{
    *isSupported = false;

    MLOperatorAttributes attributes(context);

    // DML does not support cubic.
    std::string mode = attributes.GetOptionalAttribute<std::string>(AttrName::Mode, "nearest");
    if (mode == "cubic")
    {
        return;
    }

    // DML clamps the input coordinates to the edges and essentially repeats the last pixel.
    // So rescaling the input kernel total denominator is not supported.
    int32_t excludeOutside = attributes.GetOptionalAttribute<int32_t>(AttrName::ExcludeOutside, 0);
    if (excludeOutside != 0)
    {
        return;
    }

    // DML does not support specifying a specific element value for reading outside the edges.
    // Note the extrapolation value is only pertinent for "tf_crop_and_resize" mode.
    float extrapolationValue = attributes.GetOptionalAttribute<float>(AttrName::ExtrapolationValue, 0.0);
    if (extrapolationValue != 0.0)
    {
        return;
    }

    // DML's nearest neighbor mode uses half pixels rounded down.
    std::string nearestMode = attributes.GetOptionalAttribute<std::string>(AttrName::NearestMode, "round_prefer_floor");
    auto optionalNearestModeValue = TryMapStringToIndex(nearestMode, nearestNeighborRoundingModes);
    if (!optionalNearestModeValue)
    {
        return;
    }

    // Ignore parameter "cubic_coeff_a" since Cubic interpolation unsupported in DML.
    // Ignore parameter "extrapolation_value" as DML clamps to the input rather than reading black pixels.

    *isSupported = true;
}

DML_OP_DEFINE_CREATION_FUNCTION(Resize10, VersionedKernel<DmlOperatorResize, 10>);
DML_OP_DEFINE_CREATION_FUNCTION(Resize11, VersionedKernel<DmlOperatorResize, 11>);
DML_OP_DEFINE_CREATION_FUNCTION(Upsample7, VersionedKernel<DmlOperatorResize, 7>);
DML_OP_DEFINE_CREATION_FUNCTION(Upsample9, VersionedKernel<DmlOperatorResize, 9>);
DML_OP_DEFINE_CREATION_FUNCTION(Upsample10, VersionedKernel<DmlOperatorResize, 10>);

} // namespace Dml
<commit_msg>Bug in DmlOperatorResize.cpp with m_inputDimensions (#9456)<commit_after>// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include "precomp.h"

namespace Dml
{

constexpr NameAndIndex coordinateTransformationModes[] =
{
    {"half_pixel", 0},
    {"pytorch_half_pixel", 1},
    {"align_corners", 2},
    {"asymmetric", 3},
    {"tf_half_pixel_for_nn", 4},
    {"tf_crop_and_resize", 5},
};

constexpr NameAndIndex nearestNeighborRoundingModes[] =
{
    {"", 0},
    {"round_prefer_floor", 0},
    {"round_prefer_ceil", 1},
    {"floor", 2},
};

void ComputePixelOffsetsAndScales(
    const MLOperatorKernelCreationContext& kernelCreationContext,
    gsl::span<const float> regionOfInterest, // May be empty depending on mode.
    gsl::span<const uint32_t> inputDimensions,
    gsl::span<const uint32_t> outputDimensions,
    /*inout*/ gsl::span<float> scales,
    /*out*/   gsl::span<float> inputPixelOffsets,
    /*out*/   gsl::span<float> outputPixelOffsets
    )
{
    assert(inputDimensions.size() == outputDimensions.size());
    assert(inputPixelOffsets.size() == outputPixelOffsets.size());
    assert(inputPixelOffsets.size() == scales.size());
    assert(inputPixelOffsets.size() == inputDimensions.size());
    assert(regionOfInterest.empty() || regionOfInterest.size() == inputDimensions.size() * 2);

    std::string coordinateTransformationMode = kernelCreationContext.GetOptionalAttribute<std::string>(AttrName::CoordinateTransformationMode, "half_pixel");
    auto optionalCoordinateTransformationModeValue = TryMapStringToIndex(coordinateTransformationMode, coordinateTransformationModes);
    if (!optionalCoordinateTransformationModeValue)
    {
        ML_INVALID_ARGUMENT("Unsupported 'coordinate_transformation_mode'");
    }
    uint32_t coordinateTransformationModeValue = *optionalCoordinateTransformationModeValue;

    ML_CHECK_VALID_ARGUMENT(
        !regionOfInterest.empty() || coordinateTransformationModeValue != 5 /*tf_crop_and_resize*/,
        "Resize expects 'roi' tensor for 'tf_crop_and_resize' mode."
    );

    const uint32_t rank = gsl::narrow_cast<uint32_t>(inputDimensions.size());

    // Fill in all the input/output pixel offset for each axis,
    // and recompute the scale for certain modes.

    for (uint32_t i = 0; i < rank; ++i)
    {
        float inputPixelOffset = 0;
        float outputPixelOffset = 0;

        // All these mapping modes can be generalized to the equations:
        //
        // output_coordinate = (input_coordinate  + input_offset ) * scale + output_offset
        // input_coordinate  = (output_coordinate - output_offset) / scale - input_offset
        //
        // With DML, a scale > 1 maps input to an upsampled output, and a positive pixel
        // offset shifts the input contents to the right/down in the output.
        //
        // Since the equations from ONNX are in terms of mapping the output coordinate back
        // to the input coordinate, any offsets need their signs flipped. e.g. For "half_pixel",
        // the "x_resized" is the output coordinate, and the "+ 0.5" is the output coordinate
        // adjustment which needs to be -0.5 when passed to DML.

        switch (coordinateTransformationModeValue)
        {
        case 0:
            // coordinate_transformation_mode is "half_pixel",
            // x_original = (x_resized + 0.5) / scale - 0.5
            inputPixelOffset = 0.5;
            outputPixelOffset = -0.5;
            // Keep existing scales.
            break;

        case 1:
            // if coordinate_transformation_mode is "pytorch_half_pixel",
            // x_original = length_resized > 1 ? (x_resized + 0.5) / scale - 0.5 : 0
            if (inputDimensions[i] <= 1)
            {
                inputPixelOffset = 0.0;
                outputPixelOffset = 0.0;
                scales[i] = FLT_MAX; // Set large scale so all output pixels map to 0th input pixel.
            }
            else
            {
                inputPixelOffset = 0.5;
                outputPixelOffset = -0.5;
                // Keep existing scales.
            }
            break;

        case 2:
            // if coordinate_transformation_mode is "align_corners",
            // x_original = x_resized * (length_original - 1) / (length_resized - 1)
            inputPixelOffset = 0.0;
            outputPixelOffset = 0.0;
            if (outputDimensions[i] <= 1 || inputDimensions[i] <= 1)
            {
                // Protect against division by zero when either input/output is a single pixel.
                scales[i] = FLT_MAX;
            }
            else
            {
                // Recalcalculate scale, ignoring existing one (only used to determine output size).
                scales[i] = float(outputDimensions[i] - 1) / (inputDimensions[i] - 1);
            }
            break;

        case 3:
            // if coordinate_transformation_mode is "asymmetric",
            // x_original = x_resized / scale
            inputPixelOffset = 0.0;
            outputPixelOffset = 0.0;
            // Keep existing scales.
            break;

        case 4:
            // if coordinate_transformation_mode is "tf_half_pixel_for_nn",
            // x_original = (x_resized + 0.5) / scale
            inputPixelOffset = 0.0;
            outputPixelOffset = -0.5;
            // Keep existing scales.
            break;

        case 5:
            // if coordinate_transformation_mode is "tf_crop_and_resize",
            // x_original = length_resized > 1 ? start_x * (length_original - 1) + x_resized * (end_x - start_x) * (length_original - 1) / (length_resized - 1)
            //                                 : 0.5 * (start_x + end_x) * (length_original - 1)
            if (inputDimensions[i] > 1)
            {
                assert(regionOfInterest.size() == rank * 2);

                // Fold this part of the equation into the input offset: start_x * (length_original - 1)
                inputPixelOffset = -(regionOfInterest[i] * (inputDimensions[i] - 1));
                outputPixelOffset = 0.0;

                // Fold this part to scale: (end_x - start_x) * (length_original - 1) / (length_resized - 1)
                float computedScale = float(outputDimensions[i] - 1)
                                    / std::max((regionOfInterest[i + rank] - regionOfInterest[i]) * (inputDimensions[i] - 1), 1.0f);
                scales[i] = computedScale;
            }
            else // inputDimensions[i] <= 1
            {
                // 0.5 * (start_x + end_x) * (length_original - 1)
                inputPixelOffset = -0.5f * (regionOfInterest[i] + regionOfInterest[i + rank]) * (inputDimensions[i] - 1);
                outputPixelOffset = 0.0;
                scales[i] = 1;
            }
            break;

        default:
            assert(false); // TryMapStringToIndex would have already bailed above.
        }

        inputPixelOffsets[i] = inputPixelOffset;
        outputPixelOffsets[i] = outputPixelOffset;
    }
}

class DmlOperatorResize : public DmlOperator, public ResizeHelper
{
public:
    // Resample a multidimensional image to a new size.
    DmlOperatorResize(const MLOperatorKernelCreationContext& kernelCreationContext, uint32_t opsetVersion)
    :   DmlOperator(kernelCreationContext),
        ResizeHelper(kernelCreationContext, kernelCreationContext.GetTensorShapeDescription(), opsetVersion)
    {
        ML_CHECK_VALID_ARGUMENT(!m_scales.empty(), "Resize/Upsample expect scales, either a 2nd input tensors or 'scales' attribute.");
        ML_CHECK_VALID_ARGUMENT(kernelCreationContext.GetOutputCount() == 1, "Resize/Upsample expect 1 output tensor.");

        // Use only the first input tensor. In the case of Resize or the later Upsample-v9,
        // the second tensor is CPU based and should not be passed to Resize.
        std::vector<std::optional<uint32_t>> inputIndices = { 0 };
        std::vector<std::optional<uint32_t>> outputIndices = { 0 };
        DmlOperator::Initialize(kernelCreationContext, inputIndices, outputIndices);

        // Because DirectML supports a limited number of dimensions, try to squeeze the dimension count
        // to only those which actually matter. Models sometimes use a greater number of dimensions,
        // even though those dimensions have no significance and can be elided (nop 1's), coercing the
        // total dimension count back down to a supported value.

        std::vector<uint32_t> squeezedInputShape = m_inputDimensions;
        std::vector<uint32_t> squeezedOutputShape = m_outputDimensions;
        std::vector<uint32_t> squeezableDimensionIndices;
        std::vector<float> paddedScales = m_scales;
        std::vector<float> inputPixelOffsets(paddedScales.size());
        std::vector<float> outputPixelOffsets(paddedScales.size());

        ComputePixelOffsetsAndScales(
            kernelCreationContext,
            m_regionOfInterest, // May be empty depending on mode.
            m_inputDimensions,
            m_outputDimensions,
            /*inout*/ paddedScales,
            /*out*/ inputPixelOffsets,
            /*out*/ outputPixelOffsets
        );

        // Find any useless dimensions of size 1 that occur in both input and output.
        for (size_t i = 0, rank = m_outputDimensions.size(); i < rank; ++i)
        {
            if (m_inputDimensions[i] == 1 && m_outputDimensions[i] == 1)
            {
                squeezableDimensionIndices.push_back(gsl::narrow_cast<uint32_t>(i));
            }
        }
        RemoveValuesByIndex(squeezableDimensionIndices, /*keepOneValue*/ true, /*inout*/ squeezedInputShape);
        RemoveValuesByIndex(squeezableDimensionIndices, /*keepOneValue*/ true, /*inout*/ paddedScales);
        RemoveValuesByIndex(squeezableDimensionIndices, /*keepOneValue*/ true, /*inout*/ inputPixelOffsets);
        RemoveValuesByIndex(squeezableDimensionIndices, /*keepOneValue*/ true, /*inout*/ outputPixelOffsets);
        RemoveValuesByIndex(squeezableDimensionIndices, /*keepOneValue*/ true, /*inout*/ squeezedOutputShape);

        // Update the tensor descriptions.
        MLOperatorTensorDataType inputTensorDataType = kernelCreationContext.GetInputEdgeDescription(0).tensorDataType;
        auto inputTensorDesc = TensorDesc(inputTensorDataType, squeezedInputShape, squeezedInputShape, TensorAxis::DoNotCoerce, TensorAxis::W, TensorAxis::RightAligned, NchwDimensionCount, 0);
        auto outputTensorDesc = TensorDesc(inputTensorDataType, squeezedOutputShape, squeezedOutputShape, TensorAxis::DoNotCoerce, TensorAxis::W, TensorAxis::RightAligned, NchwDimensionCount, 0);
        m_inputTensorDescs[0] = inputTensorDesc;
        m_outputTensorDescs[0] = outputTensorDesc;

        // If the output tensor dimension count was right-aligned to a larger size,
        // then ensure that scales has the same count as the tensor rank by inserting
        // leading ones, since DirectML requires the scales to have the same count.
        const uint32_t squeezedDimCount = gsl::narrow_cast<uint32_t>(squeezedOutputShape.size());
        const uint32_t dmlCompatibleDimCount = outputTensorDesc.GetDimensionCount();
        if (dmlCompatibleDimCount > squeezedDimCount)
        {
            paddedScales.insert(paddedScales.begin(), dmlCompatibleDimCount - squeezedDimCount, 1.0f);
            inputPixelOffsets.insert(inputPixelOffsets.begin(), dmlCompatibleDimCount - squeezedDimCount, 0.5f);
            outputPixelOffsets.insert(outputPixelOffsets.begin(), dmlCompatibleDimCount - squeezedDimCount, -0.5f);
        }

        std::string mode = kernelCreationContext.GetOptionalAttribute<std::string>(AttrName::Mode, "NEAREST");
        DML_INTERPOLATION_MODE interpolationMode = Dml::MapStringToInteropolationMode(mode);

        // DML's nearest neighbor mode uses round-halves-up (or round_prefer_ceil) via floor(input.x + 0.5).
        // So to support floor, adjust the input by half a pixel.
        // round_prefer_floor is not supported without an API extension,
        // but existing code already default to treating it as round_prefer_ceil.
        // So continue that.
        if (interpolationMode == DML_INTERPOLATION_MODE_NEAREST_NEIGHBOR)
        {
            std::string nearestMode = kernelCreationContext.GetOptionalAttribute<std::string>(AttrName::NearestMode, "round_prefer_floor");
            auto optionalNearestModeValue = TryMapStringToIndex(nearestMode, nearestNeighborRoundingModes);
            if (optionalNearestModeValue)
            {
                switch (*optionalNearestModeValue)
                {
                case 0: // round_prefer_floor
                case 1: // round_prefer_ceil
                    break;
                case 2: // floor
                    for (auto& offset : inputPixelOffsets)
                    {
                        offset += 0.5;
                    }
                    break;
                default:
                    assert(false);
                }
            }
        }

        // Create the operator description.
        std::vector<DML_TENSOR_DESC> inputDescs = GetDmlInputDescs();
        std::vector<DML_TENSOR_DESC> outputDescs = GetDmlOutputDescs();

        DML_RESAMPLE1_OPERATOR_DESC operatorDesc = {};
        operatorDesc.InputTensor = inputDescs.data();
        operatorDesc.OutputTensor = outputDescs.data();
        operatorDesc.InterpolationMode = interpolationMode;
        operatorDesc.Scales = paddedScales.data();
        operatorDesc.DimensionCount = gsl::narrow_cast<uint32_t>(paddedScales.size());
        operatorDesc.InputPixelOffsets = inputPixelOffsets.data();
        operatorDesc.OutputPixelOffsets = outputPixelOffsets.data();

        DML_OPERATOR_DESC opDesc = { DML_OPERATOR_RESAMPLE1, &operatorDesc };
        SetDmlOperatorDesc(opDesc, kernelCreationContext);
    }
};

void CALLBACK QueryResize(IMLOperatorSupportQueryContextPrivate* context, bool* isSupported)
{
    *isSupported = false;

    MLOperatorAttributes attributes(context);

    // DML does not support cubic.
    std::string mode = attributes.GetOptionalAttribute<std::string>(AttrName::Mode, "nearest");
    if (mode == "cubic")
    {
        return;
    }

    // DML clamps the input coordinates to the edges and essentially repeats the last pixel.
    // So rescaling the input kernel total denominator is not supported.
    int32_t excludeOutside = attributes.GetOptionalAttribute<int32_t>(AttrName::ExcludeOutside, 0);
    if (excludeOutside != 0)
    {
        return;
    }

    // DML does not support specifying a specific element value for reading outside the edges.
    // Note the extrapolation value is only pertinent for "tf_crop_and_resize" mode.
    float extrapolationValue = attributes.GetOptionalAttribute<float>(AttrName::ExtrapolationValue, 0.0);
    if (extrapolationValue != 0.0)
    {
        return;
    }

    // DML's nearest neighbor mode uses half pixels rounded down.
    std::string nearestMode = attributes.GetOptionalAttribute<std::string>(AttrName::NearestMode, "round_prefer_floor");
    auto optionalNearestModeValue = TryMapStringToIndex(nearestMode, nearestNeighborRoundingModes);
    if (!optionalNearestModeValue)
    {
        return;
    }

    // Ignore parameter "cubic_coeff_a" since Cubic interpolation unsupported in DML.
    // Ignore parameter "extrapolation_value" as DML clamps to the input rather than reading black pixels.

    *isSupported = true;
}

DML_OP_DEFINE_CREATION_FUNCTION(Resize10, VersionedKernel<DmlOperatorResize, 10>);
DML_OP_DEFINE_CREATION_FUNCTION(Resize11, VersionedKernel<DmlOperatorResize, 11>);
DML_OP_DEFINE_CREATION_FUNCTION(Upsample7, VersionedKernel<DmlOperatorResize, 7>);
DML_OP_DEFINE_CREATION_FUNCTION(Upsample9, VersionedKernel<DmlOperatorResize, 9>);
DML_OP_DEFINE_CREATION_FUNCTION(Upsample10, VersionedKernel<DmlOperatorResize, 10>);

} // namespace Dml
<|endoftext|>


<commit_before>/**
 * \file
 * \brief PoolAllocator class header
 *
 * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2015-01-11
 */

#ifndef INCLUDE_DISTORTOS_ALLOCATORS_POOLALLOCATOR_HPP_
#define INCLUDE_DISTORTOS_ALLOCATORS_POOLALLOCATOR_HPP_

#include "distortos/allocators/AllocatorBase.hpp"

namespace distortos
{

namespace allocators
{

/**
 * \brief PoolAllocator class is a generic allocator which uses pool.
 *
 * \param T is the allocated type
 * \param PoolType is the type of pool used by allocator
 */

template<typename T, typename PoolType>
class PoolAllocator : public AllocatorBase<T>
{
public:

	/// alias of PoolType template parameter
	using Pool = PoolType;

	/// base of PoolAllocator
	using Base = AllocatorBase<T>;

	using typename Base::value_type;
	using typename Base::pointer;
	using typename Base::const_pointer;
	using typename Base::reference;
	using typename Base::const_reference;
	using typename Base::size_type;

	/**
	 * \brief Rebinds allocator to different type.
	 *
	 * \param U is the allocated type
	 */

	template<typename U>
	struct rebind
	{
		/// type of rebound allocator
		using other = PoolAllocator<U, Pool>;
	};

	/**
	 * \brief PoolAllocator's constructor
	 *
	 * \param [in] pool is a reference to pool used by allocator
	 */

	constexpr explicit PoolAllocator(Pool& pool) :
			pool_(pool)
	{

	}

	/**
	 * \brief PoolAllocator's copy constructor
	 *
	 * \param U is the allocated type
	 *
	 * \param [in] other is a reference to PoolAllocator which will be copied
	 */

	template<typename U>
	constexpr explicit PoolAllocator(const PoolAllocator<U, Pool>& other) :
			pool_(other.pool_)
	{

	}

	/**
	 * \brief Allocates raw storage.
	 *
	 * \param [in] size is the number of elements (each of size sizeof(value_type)) to be allocated
	 * \param [in] hint is the hint that may be used to improve locality
	 *
	 * \return pointer to allocated storage
	 */

	pointer allocate(const size_type size, const void* = nullptr)
	{
		return static_cast<T*>(pool_.allocate(size * sizeof(T)));
	}

	/**
	 * \brief Deallocates raw storage.
	 *
	 * \param [in] storage is the pointer to deallocated storage
	 * \param [in] size is the number of elements (each of size sizeof(value_type)) to be deallocated
	 */

	void deallocate(const pointer storage, const size_type size)
	{
		pool_.deallocate(storage, size * sizeof(T));
	}

private:

	/// reference to pool used by allocator
	Pool& pool_;

	template<typename U, typename OtherPoolType>
	friend class PoolAllocator;

	template<typename T1, typename T2, typename OtherPoolType>
	friend bool operator==(const PoolAllocator<T1, OtherPoolType>& left, const PoolAllocator<T2, OtherPoolType>& right);
};

/**
 * \brief PoolAllocator operator==
 *
 * \param T1 is the type allocated by left PoolAllocator
 * \param T2 is the type allocated by right PoolAllocator
 * \param OtherPoolType is the type of pool used by both allocators
 *
 * \param [in] left is a reference to left PoolAllocator
 * \param [in] right is a reference to right PoolAllocator
 *
 * \return true if the allocators are equal, false otherwise
 */

template<typename T1, typename T2, typename OtherPoolType>
inline bool operator==(const PoolAllocator<T1, OtherPoolType>& left, const PoolAllocator<T2, OtherPoolType>& right)
{
	return &left.pool_ == &right.pool_;
}

/**
 * \brief PoolAllocator operator!=
 *
 * \param T1 is the type allocated by left PoolAllocator
 * \param T2 is the type allocated by right PoolAllocator
 * \param OtherPoolType is the type of pool used by both allocators
 *
 * \param [in] left is a reference to left PoolAllocator
 * \param [in] right is a reference to right PoolAllocator
 *
 * \return true if the allocators are not equal, false otherwise
 */

template<typename T1, typename T2, typename OtherPoolType>
inline bool operator!=(const PoolAllocator<T1, OtherPoolType>& left, const PoolAllocator<T2, OtherPoolType>& right)
{
	return !(left == right);
}

}	// namespace allocators

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_ALLOCATORS_POOLALLOCATOR_HPP_
<commit_msg>PoolAllocator: minor style improvements<commit_after>/**
 * \file
 * \brief PoolAllocator class header
 *
 * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2015-10-18
 */

#ifndef INCLUDE_DISTORTOS_ALLOCATORS_POOLALLOCATOR_HPP_
#define INCLUDE_DISTORTOS_ALLOCATORS_POOLALLOCATOR_HPP_

#include "distortos/allocators/AllocatorBase.hpp"

namespace distortos
{

namespace allocators
{

/**
 * \brief PoolAllocator class is a generic allocator which uses pool.
 *
 * \param T is the allocated type
 * \param PoolType is the type of pool used by allocator
 */

template<typename T, typename PoolType>
class PoolAllocator : public AllocatorBase<T>
{
public:

	/// alias of PoolType template parameter
	using Pool = PoolType;

	/// base of PoolAllocator
	using Base = AllocatorBase<T>;

	using typename Base::value_type;
	using typename Base::pointer;
	using typename Base::const_pointer;
	using typename Base::reference;
	using typename Base::const_reference;
	using typename Base::size_type;

	/**
	 * \brief Rebinds allocator to different type.
	 *
	 * \param U is the allocated type
	 */

	template<typename U>
	struct rebind
	{
		/// type of rebound allocator
		using other = PoolAllocator<U, Pool>;
	};

	/**
	 * \brief PoolAllocator's constructor
	 *
	 * \param [in] pool is a reference to pool used by allocator
	 */

	constexpr explicit PoolAllocator(Pool& pool) :
			pool_{pool}
	{

	}

	/**
	 * \brief PoolAllocator's copy constructor
	 *
	 * \param U is the allocated type
	 *
	 * \param [in] other is a reference to PoolAllocator which will be copied
	 */

	template<typename U>
	constexpr explicit PoolAllocator(const PoolAllocator<U, Pool>& other) :
			pool_{other.pool_}
	{

	}

	/**
	 * \brief Allocates raw storage.
	 *
	 * \param [in] size is the number of elements (each of size sizeof(value_type)) to be allocated
	 * \param [in] hint is the hint that may be used to improve locality
	 *
	 * \return pointer to allocated storage
	 */

	pointer allocate(const size_type size, const void* = {})
	{
		return static_cast<T*>(pool_.allocate(size * sizeof(T)));
	}

	/**
	 * \brief Deallocates raw storage.
	 *
	 * \param [in] storage is the pointer to deallocated storage
	 * \param [in] size is the number of elements (each of size sizeof(value_type)) to be deallocated
	 */

	void deallocate(const pointer storage, const size_type size)
	{
		pool_.deallocate(storage, size * sizeof(T));
	}

private:

	/// reference to pool used by allocator
	Pool& pool_;

	template<typename U, typename OtherPoolType>
	friend class PoolAllocator;

	template<typename T1, typename T2, typename OtherPoolType>
	friend bool operator==(const PoolAllocator<T1, OtherPoolType>& left, const PoolAllocator<T2, OtherPoolType>& right);
};

/**
 * \brief PoolAllocator operator==
 *
 * \param T1 is the type allocated by left PoolAllocator
 * \param T2 is the type allocated by right PoolAllocator
 * \param OtherPoolType is the type of pool used by both allocators
 *
 * \param [in] left is a reference to left PoolAllocator
 * \param [in] right is a reference to right PoolAllocator
 *
 * \return true if the allocators are equal, false otherwise
 */

template<typename T1, typename T2, typename OtherPoolType>
inline bool operator==(const PoolAllocator<T1, OtherPoolType>& left, const PoolAllocator<T2, OtherPoolType>& right)
{
	return &left.pool_ == &right.pool_;
}

/**
 * \brief PoolAllocator operator!=
 *
 * \param T1 is the type allocated by left PoolAllocator
 * \param T2 is the type allocated by right PoolAllocator
 * \param OtherPoolType is the type of pool used by both allocators
 *
 * \param [in] left is a reference to left PoolAllocator
 * \param [in] right is a reference to right PoolAllocator
 *
 * \return true if the allocators are not equal, false otherwise
 */

template<typename T1, typename T2, typename OtherPoolType>
inline bool operator!=(const PoolAllocator<T1, OtherPoolType>& left, const PoolAllocator<T2, OtherPoolType>& right)
{
	return (left == right) == false;
}

}	// namespace allocators

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_ALLOCATORS_POOLALLOCATOR_HPP_
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

  Some parts of this code are derived from ITK. See ITKCopyright.txt
  for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif

//  Software Guide : BeginLatex
//
//  The \doxygen{itk}{PointSet} class uses an internal container to manage the storage of
//  \doxygen{itk}{Point}s. It is more efficient, in general, to manage points by using the
//  access methods provided directly on the points container. The following
//  example illustrates how to interact with the point container and how to use
//  point iterators.
//
//  Software Guide : EndLatex

#include "itkPointSet.h"

int main(int, char *[])
{
  typedef itk::PointSet< unsigned short, 3 > PointSetType;

  //  Software Guide : BeginLatex
  //
  //  The type is defined by the \emph{traits} of the PointSet
  //  class. The following line conveniently takes the PointsContainer type
  //  from the PointSet traits and declare it in the global namespace.
  //
  //  \index{itk::PointSet!PointsContainer}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef PointSetType::PointsContainer      PointsContainer;
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The actual type of the PointsContainer depends on what style of
  //  PointSet is being used. The dynamic PointSet use the
  //  \doxygen{itk}{MapContainer} while the static PointSet uses the
  //  \doxygen{itk}{VectorContainer}. The vector and map containers are basically
  //  ITK wrappers around the \href{http://www.sgi.com/tech/stl/}{STL}
  //  classes \href{http://www.sgi.com/tech/stl/Map.html}{\code{std::map}}
  //  and \href{http://www.sgi.com/tech/stl/Vector.html}{\code{std::vector}}.
  //  By default, the PointSet uses a static style, hence the default
  //  type of point container is an VectorContainer.  Both the map
  //  and vector container are templated over the type of the elements they
  //  contain. In this case they are templated over PointType.
  //  Containers are reference counted object. They are then created with the
  //  \code{New()} method and assigned to a \doxygen{itk}{SmartPointer} after
  //  creation.  The following line creates a point container compatible with
  //  the type of the PointSet from which the trait has been taken.
  //
  //  \index{PointsContainer!New()}
  //  \index{PointsContainer!Pointer}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  PointsContainer::Pointer points = PointsContainer::New();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  Points can now be defined using the \code{PointType} trait from the
  //  PointSet.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef PointSetType::PointType   PointType;
  PointType p0;
  PointType p1;
  p0[0] = -1.0; p0[1] = 0.0;  // Point 0 = {-1,0 }
  p1[0] =  1.0; p1[1] = 0.0;  // Point 1 = { 1,0 }
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The created points can be inserted in the PointsContainer using the
  //  generic method \code{InsertElement()} which requires an identifier to
  //  be provided for each point.
  //
  //  \index{PointsContainer!InsertElement()}
  //  \index{PointsContainer!InsertElement()}
  //  \index{itk::VectorContainer!InsertElement()}
  //  \index{itk::MapContainer!InsertElement()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  unsigned int pointId = 0;
  points->InsertElement( pointId++ , p0 );
  points->InsertElement( pointId++ , p1 );
  // Software Guide : EndCodeSnippet

  PointSetType::Pointer  pointSet = PointSetType::New();

  //  Software Guide : BeginLatex
  //
  //  Finally the PointsContainer can be assigned to the PointSet. This will
  //  substitute any previously existing PointsContainer on the PointSet. The
  //  assignment is done using the \code{SetPoints()} method.
  //
  //  \index{itk::PointSet!SetPoints()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  pointSet->SetPoints( points );
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The PointsContainer object can be obtained from the PointSet using the
  //  \code{GetPoints()} method.  This method returns a pointer
  //  to the actual container owned by the PointSet which is then assigned to
  //  a SmartPointer.
  //
  //  \index{itk::PointSet!GetPoints()}
  //  \index{PointsContainer!Pointer}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  PointsContainer::Pointer  points2 = pointSet->GetPoints();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The most efficient way to sequentially visit the points is to use the
  //  iterators provided by PointsContainer. The \code{Iterator} type belongs
  //  to the traits of the PointsContainer classes. It behaves pretty much like
  //  the STL iterators.\footnote{If you dig deep enough into the code, you
  //  will discover that these iterators are actually ITK wrappers around STL
  //  iterators.}  The Points iterator is not a reference counted class, so it
  //  is created directly from the traits without using SmartPointers.
  //
  //  \index{PointsContainer!Iterator}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef PointsContainer::Iterator     PointsIterator;
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The subsequent use of the iterator follows what you may expect from a STL
  //  iterator. The iterator to the first point is obtained from the container
  //  with the \code{Begin()} method and assigned to another iterator.
  //
  //  \index{PointsContainer!Begin()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  PointsIterator  pointIterator = points->Begin();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The \code{++} operator on the iterator can be used to advance from one
  //  point to the next. The actual value of the Point to which the iterator is
  //  pointing can be obtained with the \code{Value()} method. The loop for
  //  walking through all the points can be controlled by comparing the current
  //  iterator with the iterator returned by the \code{End()} method of the
  //  PointsContainer. The following lines illustrate the typical loop for
  //  walking through the points.
  //
  //  \index{PointsContainer!End()}
  //  \index{PointsContainer!Iterator}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  PointsIterator end = points->End();
  while( pointIterator != end )
    {
    PointType p = pointIterator.Value();   // access the point
    std::cout << p << std::endl;           // print the point
    ++pointIterator;                       // advance to next point
    }
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  Note that as in STL, the iterator returned by the \code{End()} method is
  //  not a valid iterator. This is called a past-end iterator in order to
  //  indicate that it is the value resulting from advancing one step after
  //  visiting the last element in the container.
  //
  //  The number of elements stored in a container can be queried with the
  //  \code{Size()} method. In the case of the PointSet, the following two
  //  lines of code are equivalent, both of them returning the number of points
  //  in the PointSet.
  //
  //  \index{itk::PointSet!GetNumberOfPoints()}
  //  \index{itk::PointSet!GetPoints()}
  //  \index{PointsContainer!Size()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  std::cout << pointSet->GetNumberOfPoints() << std::endl;
  std::cout << pointSet->GetPoints()->Size() << std::endl;
  // Software Guide : EndCodeSnippet

  return EXIT_SUCCESS;
}

<commit_msg>COMP: Change PointSet dimension value 3 to 2<commit_after>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

  Some parts of this code are derived from ITK. See ITKCopyright.txt
  for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif

//  Software Guide : BeginLatex
//
//  The \doxygen{itk}{PointSet} class uses an internal container to manage the storage of
//  \doxygen{itk}{Point}s. It is more efficient, in general, to manage points by using the
//  access methods provided directly on the points container. The following
//  example illustrates how to interact with the point container and how to use
//  point iterators.
//
//  Software Guide : EndLatex

#include "itkPointSet.h"

int main(int, char *[])
{
  typedef itk::PointSet< unsigned short, 2 > PointSetType;

  //  Software Guide : BeginLatex
  //
  //  The type is defined by the \emph{traits} of the PointSet
  //  class. The following line conveniently takes the PointsContainer type
  //  from the PointSet traits and declare it in the global namespace.
  //
  //  \index{itk::PointSet!PointsContainer}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef PointSetType::PointsContainer      PointsContainer;
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The actual type of the PointsContainer depends on what style of
  //  PointSet is being used. The dynamic PointSet use the
  //  \doxygen{itk}{MapContainer} while the static PointSet uses the
  //  \doxygen{itk}{VectorContainer}. The vector and map containers are basically
  //  ITK wrappers around the \href{http://www.sgi.com/tech/stl/}{STL}
  //  classes \href{http://www.sgi.com/tech/stl/Map.html}{\code{std::map}}
  //  and \href{http://www.sgi.com/tech/stl/Vector.html}{\code{std::vector}}.
  //  By default, the PointSet uses a static style, hence the default
  //  type of point container is an VectorContainer.  Both the map
  //  and vector container are templated over the type of the elements they
  //  contain. In this case they are templated over PointType.
  //  Containers are reference counted object. They are then created with the
  //  \code{New()} method and assigned to a \doxygen{itk}{SmartPointer} after
  //  creation.  The following line creates a point container compatible with
  //  the type of the PointSet from which the trait has been taken.
  //
  //  \index{PointsContainer!New()}
  //  \index{PointsContainer!Pointer}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  PointsContainer::Pointer points = PointsContainer::New();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  Points can now be defined using the \code{PointType} trait from the
  //  PointSet.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef PointSetType::PointType   PointType;
  PointType p0;
  PointType p1;
  p0[0] = -1.0; p0[1] = 0.0;  // Point 0 = {-1,0 }
  p1[0] =  1.0; p1[1] = 0.0;  // Point 1 = { 1,0 }
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The created points can be inserted in the PointsContainer using the
  //  generic method \code{InsertElement()} which requires an identifier to
  //  be provided for each point.
  //
  //  \index{PointsContainer!InsertElement()}
  //  \index{PointsContainer!InsertElement()}
  //  \index{itk::VectorContainer!InsertElement()}
  //  \index{itk::MapContainer!InsertElement()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  unsigned int pointId = 0;
  points->InsertElement( pointId++ , p0 );
  points->InsertElement( pointId++ , p1 );
  // Software Guide : EndCodeSnippet

  PointSetType::Pointer  pointSet = PointSetType::New();

  //  Software Guide : BeginLatex
  //
  //  Finally the PointsContainer can be assigned to the PointSet. This will
  //  substitute any previously existing PointsContainer on the PointSet. The
  //  assignment is done using the \code{SetPoints()} method.
  //
  //  \index{itk::PointSet!SetPoints()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  pointSet->SetPoints( points );
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The PointsContainer object can be obtained from the PointSet using the
  //  \code{GetPoints()} method.  This method returns a pointer
  //  to the actual container owned by the PointSet which is then assigned to
  //  a SmartPointer.
  //
  //  \index{itk::PointSet!GetPoints()}
  //  \index{PointsContainer!Pointer}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  PointsContainer::Pointer  points2 = pointSet->GetPoints();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The most efficient way to sequentially visit the points is to use the
  //  iterators provided by PointsContainer. The \code{Iterator} type belongs
  //  to the traits of the PointsContainer classes. It behaves pretty much like
  //  the STL iterators.\footnote{If you dig deep enough into the code, you
  //  will discover that these iterators are actually ITK wrappers around STL
  //  iterators.}  The Points iterator is not a reference counted class, so it
  //  is created directly from the traits without using SmartPointers.
  //
  //  \index{PointsContainer!Iterator}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  typedef PointsContainer::Iterator     PointsIterator;
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The subsequent use of the iterator follows what you may expect from a STL
  //  iterator. The iterator to the first point is obtained from the container
  //  with the \code{Begin()} method and assigned to another iterator.
  //
  //  \index{PointsContainer!Begin()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  PointsIterator  pointIterator = points->Begin();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  The \code{++} operator on the iterator can be used to advance from one
  //  point to the next. The actual value of the Point to which the iterator is
  //  pointing can be obtained with the \code{Value()} method. The loop for
  //  walking through all the points can be controlled by comparing the current
  //  iterator with the iterator returned by the \code{End()} method of the
  //  PointsContainer. The following lines illustrate the typical loop for
  //  walking through the points.
  //
  //  \index{PointsContainer!End()}
  //  \index{PointsContainer!Iterator}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  PointsIterator end = points->End();
  while( pointIterator != end )
    {
    PointType p = pointIterator.Value();   // access the point
    std::cout << p << std::endl;           // print the point
    ++pointIterator;                       // advance to next point
    }
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  Note that as in STL, the iterator returned by the \code{End()} method is
  //  not a valid iterator. This is called a past-end iterator in order to
  //  indicate that it is the value resulting from advancing one step after
  //  visiting the last element in the container.
  //
  //  The number of elements stored in a container can be queried with the
  //  \code{Size()} method. In the case of the PointSet, the following two
  //  lines of code are equivalent, both of them returning the number of points
  //  in the PointSet.
  //
  //  \index{itk::PointSet!GetNumberOfPoints()}
  //  \index{itk::PointSet!GetPoints()}
  //  \index{PointsContainer!Size()}
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  std::cout << pointSet->GetNumberOfPoints() << std::endl;
  std::cout << pointSet->GetPoints()->Size() << std::endl;
  // Software Guide : EndCodeSnippet

  return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>#include "../base/SRC_FIRST.hpp"

#include "framebuffer.hpp"
#include "render_target.hpp"
#include "internal/opengl.hpp"
#include "utils.hpp"

#include "../base/logging.hpp"
#include "../std/list.hpp"

namespace yg
{
  namespace gl
  {
    list<unsigned int> frameBufferStack;

    unsigned FrameBuffer::current()
    {
      int id;
#ifdef OMIM_GL_ES
      OGLCHECK(glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &id));
#else
      OGLCHECK(glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &id));
#endif
      return id;
    }

    void FrameBuffer::pushCurrent()
    {
      frameBufferStack.push_back(current());
    }

    void FrameBuffer::popCurrent()
    {
#ifdef OMIM_GL_ES
        OGLCHECK(glBindFramebufferOES(GL_FRAMEBUFFER_OES, frameBufferStack.back()));
#else
        OGLCHECK(glBindFramebuffer(GL_FRAMEBUFFER_EXT, frameBufferStack.back()));
#endif
        frameBufferStack.pop_back();
    }

    FrameBuffer::FrameBuffer(bool defaultFB /*= false*/) : m_width(0), m_height(0)
    {
      if (defaultFB)
        m_id = 0;
      else
      {
#ifdef OMIM_GL_ES
        OGLCHECK(glGenFramebuffersOES(1, &m_id));
#else
        OGLCHECK(glGenFramebuffers(1, &m_id));
#endif
      }
    }

    FrameBuffer::~FrameBuffer()
    {
      if (m_id != 0)
      {
#ifdef OMIM_GL_ES
        OGLCHECK(glDeleteFramebuffersOES(1, &m_id));
#else
        OGLCHECK(glDeleteFramebuffers(1, &m_id));
#endif
      }
    }

    void FrameBuffer::makeCurrent()
    {
      if (m_id != current())
      {
#ifdef OMIM_GL_ES
        OGLCHECK(glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_id));
#else
        OGLCHECK(glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_id));
#endif
//        LOG(LINFO, ("FrameBuffer::makeCurrent", m_id));
      }

      if (m_renderTarget)
        m_renderTarget->attachToFrameBuffer();
      else
        utils::setupCoordinates(width(), height(), true);
      if (m_depthBuffer)
        m_depthBuffer->attachToFrameBuffer();

      /// !!! it's a must for a correct work.
      checkStatus();
    }

    void FrameBuffer::setRenderTarget(shared_ptr<RenderTarget> const & renderTarget)
    {
      m_renderTarget = renderTarget;
    }

    shared_ptr<RenderTarget> const & FrameBuffer::renderTarget() const
    {
      return m_renderTarget;
    }

    void FrameBuffer::resetRenderTarget()
    {
      m_renderTarget.reset();
    }

    void FrameBuffer::setDepthBuffer(shared_ptr<RenderTarget> const & depthBuffer)
    {
      m_depthBuffer = depthBuffer;
    }

    shared_ptr<RenderTarget> const & FrameBuffer::depthBuffer() const
    {
      return m_depthBuffer;
    }

    void FrameBuffer::resetDepthBuffer()
    {
      m_depthBuffer.reset();
    }

    int FrameBuffer::id() const
    {
      return m_id;
    }

    unsigned FrameBuffer::width() const
    {
      return m_width;
    }

    unsigned FrameBuffer::height() const
    {
      return m_height;
    }

    void FrameBuffer::onSize(unsigned width, unsigned height)
    {
      m_width = width;
      m_height = height;
    }

    void FrameBuffer::checkStatus()
    {
#ifdef OMIM_GL_ES
      GLenum res = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
      if (res != GL_FRAMEBUFFER_COMPLETE_OES)
        LOG(LERROR, ("incomplete framebuffer"));
#else
      GLenum res = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
      if (res == GL_FRAMEBUFFER_UNSUPPORTED)
        LOG(LINFO, ("unsupported combination of attached target formats. could be possibly skipped"));
      else if (res != GL_FRAMEBUFFER_COMPLETE_EXT)
        LOG(LERROR, ("incomplete framebuffer"));
#endif
    }
  }
}
<commit_msg>Fixed EGL logging<commit_after>#include "../base/SRC_FIRST.hpp"

#include "framebuffer.hpp"
#include "render_target.hpp"
#include "internal/opengl.hpp"
#include "utils.hpp"

#include "../base/logging.hpp"
#include "../std/list.hpp"

namespace yg
{
  namespace gl
  {
    list<unsigned int> frameBufferStack;

    unsigned FrameBuffer::current()
    {
      int id;
#ifdef OMIM_GL_ES
      OGLCHECK(glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &id));
#else
      OGLCHECK(glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &id));
#endif
      return id;
    }

    void FrameBuffer::pushCurrent()
    {
      frameBufferStack.push_back(current());
    }

    void FrameBuffer::popCurrent()
    {
#ifdef OMIM_GL_ES
        OGLCHECK(glBindFramebufferOES(GL_FRAMEBUFFER_OES, frameBufferStack.back()));
#else
        OGLCHECK(glBindFramebuffer(GL_FRAMEBUFFER_EXT, frameBufferStack.back()));
#endif
        frameBufferStack.pop_back();
    }

    FrameBuffer::FrameBuffer(bool defaultFB /*= false*/) : m_width(0), m_height(0)
    {
      if (defaultFB)
        m_id = 0;
      else
      {
#ifdef OMIM_GL_ES
        OGLCHECK(glGenFramebuffersOES(1, &m_id));
#else
        OGLCHECK(glGenFramebuffers(1, &m_id));
#endif
      }
    }

    FrameBuffer::~FrameBuffer()
    {
      if (m_id != 0)
      {
#ifdef OMIM_GL_ES
        OGLCHECK(glDeleteFramebuffersOES(1, &m_id));
#else
        OGLCHECK(glDeleteFramebuffers(1, &m_id));
#endif
      }
    }

    void FrameBuffer::makeCurrent()
    {
      if (m_id != current())
      {
#ifdef OMIM_GL_ES
        OGLCHECK(glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_id));
#else
        OGLCHECK(glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_id));
#endif
//        LOG(LINFO, ("FrameBuffer::makeCurrent", m_id));
      }

      if (m_renderTarget)
        m_renderTarget->attachToFrameBuffer();
      else
        utils::setupCoordinates(width(), height(), true);
      if (m_depthBuffer)
        m_depthBuffer->attachToFrameBuffer();

      /// !!! it's a must for a correct work.
      checkStatus();
    }

    void FrameBuffer::setRenderTarget(shared_ptr<RenderTarget> const & renderTarget)
    {
      m_renderTarget = renderTarget;
    }

    shared_ptr<RenderTarget> const & FrameBuffer::renderTarget() const
    {
      return m_renderTarget;
    }

    void FrameBuffer::resetRenderTarget()
    {
      m_renderTarget.reset();
    }

    void FrameBuffer::setDepthBuffer(shared_ptr<RenderTarget> const & depthBuffer)
    {
      m_depthBuffer = depthBuffer;
    }

    shared_ptr<RenderTarget> const & FrameBuffer::depthBuffer() const
    {
      return m_depthBuffer;
    }

    void FrameBuffer::resetDepthBuffer()
    {
      m_depthBuffer.reset();
    }

    int FrameBuffer::id() const
    {
      return m_id;
    }

    unsigned FrameBuffer::width() const
    {
      return m_width;
    }

    unsigned FrameBuffer::height() const
    {
      return m_height;
    }

    void FrameBuffer::onSize(unsigned width, unsigned height)
    {
      m_width = width;
      m_height = height;
    }

    void FrameBuffer::checkStatus()
    {
#ifdef OMIM_GL_ES
      GLenum res = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
      if (res == GL_FRAMEBUFFER_UNSUPPORTED_OES)
        LOG(LINFO, ("unsupported combination of attached target formats. could be possibly skipped"));
      else if (res != GL_FRAMEBUFFER_COMPLETE_OES)
        LOG(LERROR, ("incomplete framebuffer"));
#else
      GLenum res = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
      if (res == GL_FRAMEBUFFER_UNSUPPORTED)
        LOG(LINFO, ("unsupported combination of attached target formats. could be possibly skipped"));
      else if (res != GL_FRAMEBUFFER_COMPLETE_EXT)
        LOG(LERROR, ("incomplete framebuffer"));
#endif
    }
  }
}
<|endoftext|>


<commit_before>#include "com/mapswithme/maps/Framework.hpp"
#include "com/mapswithme/core/jni_helper.hpp"

#include "map/place_page_info.hpp"

#include "ugc/api.hpp"
#include "ugc/types.hpp"

#include "indexer/feature_decl.hpp"

#include "platform/preferred_languages.hpp"

#include "coding/multilang_utf8_string.hpp"

#include "base/logging.hpp"

#include <utility>

namespace
{
class FeatureIdBuilder
{
public:
  FeatureID Build(JNIEnv * env, jobject obj)
  {
    Init(env);

    jstring jcountryName = static_cast<jstring>(env->GetObjectField(obj, m_countryName));
    jlong jversion = env->GetLongField(obj, m_version);
    jint jindex = env->GetIntField(obj, m_index);

    auto const countryName = jni::ToNativeString(env, jcountryName);
    auto const version = static_cast<int64_t>(jversion);
    auto const index = static_cast<uint32_t>(jindex);

    auto const & ix = g_framework->GetIndex();
    auto const id = ix.GetMwmIdByCountryFile(platform::CountryFile(countryName));
    return FeatureID(id, index);
  }

private:
  void Init(JNIEnv * env)
  {
    if (m_initialized)
      return;

    m_class = jni::GetGlobalClassRef(env, "com/mapswithme/maps/bookmarks/data/FeatureId");
    m_countryName = env->GetFieldID(m_class, "mMwmName", "Ljava/lang/String;");
    m_version = env->GetFieldID(m_class, "mMwmVersion", "J");
    m_index = env->GetFieldID(m_class, "mFeatureIndex", "I");

    m_initialized = true;
  }

  bool m_initialized = false;

  jclass m_class;
  jfieldID m_countryName;
  jfieldID m_version;
  jfieldID m_index;
} g_builder;

class JavaBridge
{
public:
  void OnResult(JNIEnv * env, ugc::UGC const & ugc, ugc::UGCUpdate const & ugcUpdate)
  {
    Init(env);
    jni::TScopedLocalRef ugcResult(env, ToJavaUGC(env, ugc));
    jni::TScopedLocalRef ugcUpdateResult(env, ToJavaUGCUpdate(env, ugcUpdate));

    std::string formattedRating = place_page::rating::GetRatingFormatted(ugc.m_totalRating);
    jni::TScopedLocalRef jrating(env, jni::ToJavaString(env, formattedRating));

    env->CallStaticVoidMethod(m_ugcClass, m_onResult, ugcResult.get(), ugcUpdateResult.get(),
                              ToImpress(ugc.m_totalRating), jrating.get());
  }

  const int ToImpress(float const rating)
  {
    return static_cast<int>(place_page::rating::GetImpress(rating));
  }

  ugc::UGCUpdate ToNativeUGCUpdate(JNIEnv * env, jobject ugcUpdate)
  {
    Init(env);

    jobjectArray jratings = static_cast<jobjectArray>(env->GetObjectField(ugcUpdate, m_ratingArrayFieldId));
    int const length = env->GetArrayLength(jratings);
    std::vector<ugc::RatingRecord> records(length);
    for (int i = 0; i < length; i++)
    {
      jobject jrating = env->GetObjectArrayElement(jratings, i);

      jstring name = static_cast<jstring>(env->GetObjectField(jrating, m_ratingNameFieldId));
      ugc::TranslationKey key(jni::ToNativeString(env, name));

      jfloat value = env->GetFloatField(jrating, m_ratingValueFieldId);
      auto const ratingValue = static_cast<float>(value);

      records.emplace_back(std::move(key), std::move(ratingValue));
    }
    jstring jtext = static_cast<jstring>(env->GetObjectField(ugcUpdate, m_ratingTextFieldId));
    ugc::Text text(jni::ToNativeString(env, jtext),
                   StringUtf8Multilang::GetLangIndex(languages::GetCurrentNorm()));
    jlong jtime = env->GetLongField(ugcUpdate, m_updateTimeFieldId);
    uint64_t timeSec = static_cast<uint64_t>(jtime / 1000);
    return ugc::UGCUpdate(records, text, std::chrono::system_clock::from_time_t(timeSec));
  }

private:
  jobject ToJavaUGC(JNIEnv * env, ugc::UGC const & ugc)
  {
    jni::TScopedLocalObjectArrayRef ratings(env, ToJavaRatings(env, ugc.m_ratings));
    jni::TScopedLocalObjectArrayRef reviews(env, ToJavaReviews(env, ugc.m_reviews));

    jobject result = nullptr;
    if (!ugc.IsEmpty())
      result = env->NewObject(m_ugcClass, m_ugcCtor, ratings.get(), ugc.m_totalRating,
                              reviews.get(), ugc.m_basedOn);
    return result;
  }

  jobject ToJavaUGCUpdate(JNIEnv * env, ugc::UGCUpdate const & ugcUpdate)
  {
    jni::TScopedLocalObjectArrayRef ratings(env, ToJavaRatings(env, ugcUpdate.m_ratings));
    jni::TScopedLocalRef text(env, jni::ToJavaString(env, ugcUpdate.m_text.m_text));

    jobject result = nullptr;
    if (!ugcUpdate.IsEmpty())
      result = env->NewObject(m_ugcUpdateClass, m_ugcUpdateCtor, ratings.get(),
                              text.get(), ugc::ToMillisecondsSinceEpoch(ugcUpdate.m_time));
    return result;
  }

  jobjectArray ToJavaRatings(JNIEnv * env, std::vector<ugc::RatingRecord> const & ratings)
  {
    size_t const n = ratings.size();
    jobjectArray result = env->NewObjectArray(n, g_ratingClazz, nullptr);
    for (size_t i = 0; i < n; ++i)
    {
      jni::TScopedLocalRef rating(env, ToJavaRating(env, ratings[i]));
      env->SetObjectArrayElement(result, i, rating.get());
    }
    return result;
  }

  jobjectArray ToJavaReviews(JNIEnv * env, std::vector<ugc::Review> const & reviews)
  {
    size_t const n = reviews.size();
    jobjectArray result = env->NewObjectArray(n, m_reviewClass, nullptr);
    for (size_t i = 0; i < n; ++i)
    {
      jni::TScopedLocalRef review(env, ToJavaReview(env, reviews[i]));
      env->SetObjectArrayElement(result, i, review.get());
    }
    return result;
  }

  jobject ToJavaRating(JNIEnv * env, ugc::RatingRecord const & ratingRecord)
  {
    jni::TScopedLocalRef name(env, jni::ToJavaString(env, ratingRecord.m_key.m_key));
    jobject result = env->NewObject(g_ratingClazz, m_ratingCtor, name.get(), ratingRecord.m_value);
    ASSERT(result, ());
    return result;
  }

  jobject ToJavaReview(JNIEnv * env, ugc::Review const & review)
  {
    jni::TScopedLocalRef text(env, jni::ToJavaString(env, review.m_text.m_text));
    jni::TScopedLocalRef author(env, jni::ToJavaString(env, review.m_author));
    jobject result = env->NewObject(m_reviewClass, m_reviewCtor, text.get(), author.get(),
                                    ugc::ToMillisecondsSinceEpoch(review.m_time), review.m_rating,
                                    ToImpress(review.m_rating));
    ASSERT(result, ());
    return result;
  }

  void Init(JNIEnv * env)
  {
    if (m_initialized)
      return;

    m_ugcClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/ugc/UGC");
    m_ugcCtor = jni::GetConstructorID(
        env, m_ugcClass,
        "([Lcom/mapswithme/maps/ugc/UGC$Rating;F[Lcom/mapswithme/maps/ugc/UGC$Review;I)V");
    m_onResult = jni::GetStaticMethodID(env, m_ugcClass, "onUGCReceived",
                                        "(Lcom/mapswithme/maps/ugc/UGC;Lcom/mapswithme/maps/ugc/UGCUpdate;ILjava/lang/String;)V");

    m_ratingCtor = jni::GetConstructorID(env, g_ratingClazz, "(Ljava/lang/String;F)V");

    m_reviewClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/ugc/UGC$Review");
    m_reviewCtor =
        jni::GetConstructorID(env, m_reviewClass, "(Ljava/lang/String;Ljava/lang/String;JFI)V");

    m_ugcUpdateClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/ugc/UGCUpdate");
    m_ugcUpdateCtor = jni::GetConstructorID(
        env, m_ugcUpdateClass, "([Lcom/mapswithme/maps/ugc/UGC$Rating;Ljava/lang/String;J)V");
    m_ratingArrayFieldId = env->GetFieldID(m_ugcUpdateClass, "mRatings", "[Lcom/mapswithme/maps/ugc/UGC$Rating;");
    m_ratingTextFieldId = env->GetFieldID(m_ugcUpdateClass, "mText", "Ljava/lang/String;");
    m_updateTimeFieldId = env->GetFieldID(m_ugcUpdateClass, "mTimeMillis", "J");
    m_ratingNameFieldId = env->GetFieldID(g_ratingClazz, "mName", "Ljava/lang/String;");
    m_ratingValueFieldId = env->GetFieldID(g_ratingClazz, "mValue", "F");
    m_initialized = true;
  }

  bool m_initialized = false;

  jclass m_ugcClass;
  jmethodID m_ugcCtor;

  jclass m_ugcUpdateClass;
  jmethodID m_ugcUpdateCtor;
  jfieldID m_ratingArrayFieldId;
  jfieldID m_ratingTextFieldId;
  jfieldID m_updateTimeFieldId;
  jfieldID m_ratingNameFieldId;
  jfieldID m_ratingValueFieldId;

  jmethodID m_onResult;

  jmethodID m_ratingCtor;

  jclass m_reviewClass;
  jmethodID m_reviewCtor;
} g_bridge;
}  // namespace

extern "C" {
JNIEXPORT
void JNICALL Java_com_mapswithme_maps_ugc_UGC_requestUGC(JNIEnv * env, jclass /* clazz */,
                                                                   jobject featureId)
{
  auto const fid = g_builder.Build(env, featureId);
  g_framework->RequestUGC(fid, [&](ugc::UGC const & ugc, ugc::UGCUpdate const & update) {
    JNIEnv * e = jni::GetEnv();
    g_bridge.OnResult(e, ugc, update);
  });
}

JNIEXPORT
void JNICALL Java_com_mapswithme_maps_ugc_UGC_setUGCUpdate(JNIEnv * env, jclass /* clazz */,
                                                           jobject featureId, jobject ugcUpdate)
{
  auto const fid = g_builder.Build(env, featureId);
  ugc::UGCUpdate update = g_bridge.ToNativeUGCUpdate(env, ugcUpdate);
  g_framework->SetUGCUpdate(fid, update);
}

JNIEXPORT
void JNICALL Java_com_mapswithme_maps_ugc_UGC_nativeUploadUGC(JNIEnv * env, jclass /* clazz */)
{
  g_framework->UploadUGC();
}
}
<commit_msg>[android] Fixed rating duplication<commit_after>#include "com/mapswithme/maps/Framework.hpp"
#include "com/mapswithme/core/jni_helper.hpp"

#include "map/place_page_info.hpp"

#include "ugc/api.hpp"
#include "ugc/types.hpp"

#include "indexer/feature_decl.hpp"

#include "platform/preferred_languages.hpp"

#include "coding/multilang_utf8_string.hpp"

#include "base/logging.hpp"

#include <utility>

namespace
{
class FeatureIdBuilder
{
public:
  FeatureID Build(JNIEnv * env, jobject obj)
  {
    Init(env);

    jstring jcountryName = static_cast<jstring>(env->GetObjectField(obj, m_countryName));
    jlong jversion = env->GetLongField(obj, m_version);
    jint jindex = env->GetIntField(obj, m_index);

    auto const countryName = jni::ToNativeString(env, jcountryName);
    auto const version = static_cast<int64_t>(jversion);
    auto const index = static_cast<uint32_t>(jindex);

    auto const & ix = g_framework->GetIndex();
    auto const id = ix.GetMwmIdByCountryFile(platform::CountryFile(countryName));
    return FeatureID(id, index);
  }

private:
  void Init(JNIEnv * env)
  {
    if (m_initialized)
      return;

    m_class = jni::GetGlobalClassRef(env, "com/mapswithme/maps/bookmarks/data/FeatureId");
    m_countryName = env->GetFieldID(m_class, "mMwmName", "Ljava/lang/String;");
    m_version = env->GetFieldID(m_class, "mMwmVersion", "J");
    m_index = env->GetFieldID(m_class, "mFeatureIndex", "I");

    m_initialized = true;
  }

  bool m_initialized = false;

  jclass m_class;
  jfieldID m_countryName;
  jfieldID m_version;
  jfieldID m_index;
} g_builder;

class JavaBridge
{
public:
  void OnResult(JNIEnv * env, ugc::UGC const & ugc, ugc::UGCUpdate const & ugcUpdate)
  {
    Init(env);
    jni::TScopedLocalRef ugcResult(env, ToJavaUGC(env, ugc));
    jni::TScopedLocalRef ugcUpdateResult(env, ToJavaUGCUpdate(env, ugcUpdate));

    std::string formattedRating = place_page::rating::GetRatingFormatted(ugc.m_totalRating);
    jni::TScopedLocalRef jrating(env, jni::ToJavaString(env, formattedRating));

    env->CallStaticVoidMethod(m_ugcClass, m_onResult, ugcResult.get(), ugcUpdateResult.get(),
                              ToImpress(ugc.m_totalRating), jrating.get());
  }

  const int ToImpress(float const rating)
  {
    return static_cast<int>(place_page::rating::GetImpress(rating));
  }

  ugc::UGCUpdate ToNativeUGCUpdate(JNIEnv * env, jobject ugcUpdate)
  {
    Init(env);

    jobjectArray jratings = static_cast<jobjectArray>(env->GetObjectField(ugcUpdate, m_ratingArrayFieldId));
    int const length = env->GetArrayLength(jratings);
    std::vector<ugc::RatingRecord> records;
    records.reserve(length);
    for (int i = 0; i < length; i++)
    {
      jobject jrating = env->GetObjectArrayElement(jratings, i);

      jstring name = static_cast<jstring>(env->GetObjectField(jrating, m_ratingNameFieldId));
      ugc::TranslationKey key(jni::ToNativeString(env, name));

      jfloat value = env->GetFloatField(jrating, m_ratingValueFieldId);
      auto const ratingValue = static_cast<float>(value);

      records.emplace_back(std::move(key), std::move(ratingValue));
    }
    jstring jtext = static_cast<jstring>(env->GetObjectField(ugcUpdate, m_ratingTextFieldId));
    ugc::Text text(jni::ToNativeString(env, jtext),
                   StringUtf8Multilang::GetLangIndex(languages::GetCurrentNorm()));
    jlong jtime = env->GetLongField(ugcUpdate, m_updateTimeFieldId);
    uint64_t timeSec = static_cast<uint64_t>(jtime / 1000);
    return ugc::UGCUpdate(records, text, std::chrono::system_clock::from_time_t(timeSec));
  }

private:
  jobject ToJavaUGC(JNIEnv * env, ugc::UGC const & ugc)
  {
    jni::TScopedLocalObjectArrayRef ratings(env, ToJavaRatings(env, ugc.m_ratings));
    jni::TScopedLocalObjectArrayRef reviews(env, ToJavaReviews(env, ugc.m_reviews));

    jobject result = nullptr;
    if (!ugc.IsEmpty())
      result = env->NewObject(m_ugcClass, m_ugcCtor, ratings.get(), ugc.m_totalRating,
                              reviews.get(), ugc.m_basedOn);
    return result;
  }

  jobject ToJavaUGCUpdate(JNIEnv * env, ugc::UGCUpdate const & ugcUpdate)
  {
    jni::TScopedLocalObjectArrayRef ratings(env, ToJavaRatings(env, ugcUpdate.m_ratings));
    jni::TScopedLocalRef text(env, jni::ToJavaString(env, ugcUpdate.m_text.m_text));

    jobject result = nullptr;
    if (!ugcUpdate.IsEmpty())
      result = env->NewObject(m_ugcUpdateClass, m_ugcUpdateCtor, ratings.get(),
                              text.get(), ugc::ToMillisecondsSinceEpoch(ugcUpdate.m_time));
    return result;
  }

  jobjectArray ToJavaRatings(JNIEnv * env, std::vector<ugc::RatingRecord> const & ratings)
  {
    size_t const n = ratings.size();
    jobjectArray result = env->NewObjectArray(n, g_ratingClazz, nullptr);
    for (size_t i = 0; i < n; ++i)
    {
      jni::TScopedLocalRef rating(env, ToJavaRating(env, ratings[i]));
      env->SetObjectArrayElement(result, i, rating.get());
    }
    return result;
  }

  jobjectArray ToJavaReviews(JNIEnv * env, std::vector<ugc::Review> const & reviews)
  {
    size_t const n = reviews.size();
    jobjectArray result = env->NewObjectArray(n, m_reviewClass, nullptr);
    for (size_t i = 0; i < n; ++i)
    {
      jni::TScopedLocalRef review(env, ToJavaReview(env, reviews[i]));
      env->SetObjectArrayElement(result, i, review.get());
    }
    return result;
  }

  jobject ToJavaRating(JNIEnv * env, ugc::RatingRecord const & ratingRecord)
  {
    jni::TScopedLocalRef name(env, jni::ToJavaString(env, ratingRecord.m_key.m_key));
    jobject result = env->NewObject(g_ratingClazz, m_ratingCtor, name.get(), ratingRecord.m_value);
    ASSERT(result, ());
    return result;
  }

  jobject ToJavaReview(JNIEnv * env, ugc::Review const & review)
  {
    jni::TScopedLocalRef text(env, jni::ToJavaString(env, review.m_text.m_text));
    jni::TScopedLocalRef author(env, jni::ToJavaString(env, review.m_author));
    jobject result = env->NewObject(m_reviewClass, m_reviewCtor, text.get(), author.get(),
                                    ugc::ToMillisecondsSinceEpoch(review.m_time), review.m_rating,
                                    ToImpress(review.m_rating));
    ASSERT(result, ());
    return result;
  }

  void Init(JNIEnv * env)
  {
    if (m_initialized)
      return;

    m_ugcClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/ugc/UGC");
    m_ugcCtor = jni::GetConstructorID(
        env, m_ugcClass,
        "([Lcom/mapswithme/maps/ugc/UGC$Rating;F[Lcom/mapswithme/maps/ugc/UGC$Review;I)V");
    m_onResult = jni::GetStaticMethodID(env, m_ugcClass, "onUGCReceived",
                                        "(Lcom/mapswithme/maps/ugc/UGC;Lcom/mapswithme/maps/ugc/UGCUpdate;ILjava/lang/String;)V");

    m_ratingCtor = jni::GetConstructorID(env, g_ratingClazz, "(Ljava/lang/String;F)V");

    m_reviewClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/ugc/UGC$Review");
    m_reviewCtor =
        jni::GetConstructorID(env, m_reviewClass, "(Ljava/lang/String;Ljava/lang/String;JFI)V");

    m_ugcUpdateClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/ugc/UGCUpdate");
    m_ugcUpdateCtor = jni::GetConstructorID(
        env, m_ugcUpdateClass, "([Lcom/mapswithme/maps/ugc/UGC$Rating;Ljava/lang/String;J)V");
    m_ratingArrayFieldId = env->GetFieldID(m_ugcUpdateClass, "mRatings", "[Lcom/mapswithme/maps/ugc/UGC$Rating;");
    m_ratingTextFieldId = env->GetFieldID(m_ugcUpdateClass, "mText", "Ljava/lang/String;");
    m_updateTimeFieldId = env->GetFieldID(m_ugcUpdateClass, "mTimeMillis", "J");
    m_ratingNameFieldId = env->GetFieldID(g_ratingClazz, "mName", "Ljava/lang/String;");
    m_ratingValueFieldId = env->GetFieldID(g_ratingClazz, "mValue", "F");
    m_initialized = true;
  }

  bool m_initialized = false;

  jclass m_ugcClass;
  jmethodID m_ugcCtor;

  jclass m_ugcUpdateClass;
  jmethodID m_ugcUpdateCtor;
  jfieldID m_ratingArrayFieldId;
  jfieldID m_ratingTextFieldId;
  jfieldID m_updateTimeFieldId;
  jfieldID m_ratingNameFieldId;
  jfieldID m_ratingValueFieldId;

  jmethodID m_onResult;

  jmethodID m_ratingCtor;

  jclass m_reviewClass;
  jmethodID m_reviewCtor;
} g_bridge;
}  // namespace

extern "C" {
JNIEXPORT
void JNICALL Java_com_mapswithme_maps_ugc_UGC_requestUGC(JNIEnv * env, jclass /* clazz */,
                                                                   jobject featureId)
{
  auto const fid = g_builder.Build(env, featureId);
  g_framework->RequestUGC(fid, [&](ugc::UGC const & ugc, ugc::UGCUpdate const & update) {
    JNIEnv * e = jni::GetEnv();
    g_bridge.OnResult(e, ugc, update);
  });
}

JNIEXPORT
void JNICALL Java_com_mapswithme_maps_ugc_UGC_setUGCUpdate(JNIEnv * env, jclass /* clazz */,
                                                           jobject featureId, jobject ugcUpdate)
{
  auto const fid = g_builder.Build(env, featureId);
  ugc::UGCUpdate update = g_bridge.ToNativeUGCUpdate(env, ugcUpdate);
  g_framework->SetUGCUpdate(fid, update);
}

JNIEXPORT
void JNICALL Java_com_mapswithme_maps_ugc_UGC_nativeUploadUGC(JNIEnv * env, jclass /* clazz */)
{
  g_framework->UploadUGC();
}
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <map>
#include <memory>
#include "database_fwd.hh"
#include "dht/i_partitioner.hh"
#include "schema.hh"
#include "mutation_reader.hh"
#include "db/commitlog/replay_position.hh"
#include "db/commitlog/rp_set.hh"
#include "utils/logalloc.hh"
#include "partition_version.hh"

class frozen_mutation;

namespace bi = boost::intrusive;

class memtable_entry {
    bi::set_member_hook<> _link;
    schema_ptr _schema;
    dht::decorated_key _key;
    partition_entry _pe;
public:
    friend class memtable;

    memtable_entry(schema_ptr s, dht::decorated_key key, mutation_partition p)
        : _schema(std::move(s))
        , _key(std::move(key))
        , _pe(std::move(p))
    { }

    memtable_entry(memtable_entry&& o) noexcept;

    const dht::decorated_key& key() const { return _key; }
    dht::decorated_key& key() { return _key; }
    const partition_entry& partition() const { return _pe; }
    partition_entry& partition() { return _pe; }
    const schema_ptr& schema() const { return _schema; }
    schema_ptr& schema() { return _schema; }
    streamed_mutation read(lw_shared_ptr<memtable> mtbl, const schema_ptr&, const query::partition_slice&, streamed_mutation::forwarding);

    size_t external_memory_usage_without_rows() const {
        return _key.key().external_memory_usage();
    }

    struct compare {
        dht::decorated_key::less_comparator _c;

        compare(schema_ptr s)
            : _c(std::move(s))
        {}

        bool operator()(const dht::decorated_key& k1, const memtable_entry& k2) const {
            return _c(k1, k2._key);
        }

        bool operator()(const memtable_entry& k1, const memtable_entry& k2) const {
            return _c(k1._key, k2._key);
        }

        bool operator()(const memtable_entry& k1, const dht::decorated_key& k2) const {
            return _c(k1._key, k2);
        }

        bool operator()(const memtable_entry& k1, const dht::ring_position& k2) const {
            return _c(k1._key, k2);
        }

        bool operator()(const dht::ring_position& k1, const memtable_entry& k2) const {
            return _c(k1, k2._key);
        }
    };
};

class dirty_memory_manager;

// Managed by lw_shared_ptr<>.
class memtable final : public enable_lw_shared_from_this<memtable>, private logalloc::region {
public:
    using partitions_type = bi::set<memtable_entry,
        bi::member_hook<memtable_entry, bi::set_member_hook<>, &memtable_entry::_link>,
        bi::compare<memtable_entry::compare>>;
private:
    dirty_memory_manager& _dirty_mgr;
    memtable_list *_memtable_list;
    schema_ptr _schema;
    logalloc::allocating_section _read_section;
    logalloc::allocating_section _allocating_section;
    partitions_type partitions;
    db::replay_position _replay_position;
    db::rp_set _rp_set;
    // mutation source to which reads fall-back after mark_flushed()
    // so that memtable contents can be moved away while there are
    // still active readers. This is needed for this mutation_source
    // to be monotonic (not loose writes). Monotonicity of each
    // mutation_source is necessary for the combined mutation source to be
    // monotonic. That combined source in this case is cache + memtable.
    mutation_source_opt _underlying;
    uint64_t _flushed_memory = 0;
    void update(db::rp_handle&&);
    friend class row_cache;
    friend class memtable_entry;
    friend class flush_reader;
    friend class flush_memory_accounter;
private:
    boost::iterator_range<partitions_type::const_iterator> slice(const dht::partition_range& r) const;
    partition_entry& find_or_create_partition(const dht::decorated_key& key);
    partition_entry& find_or_create_partition_slow(partition_key_view key);
    void upgrade_entry(memtable_entry&);
    void add_flushed_memory(uint64_t);
    void remove_flushed_memory(uint64_t);
    void clear() noexcept;
    uint64_t dirty_size() const;
public:
    explicit memtable(schema_ptr schema, dirty_memory_manager&, memtable_list *memtable_list = nullptr);
    // Used for testing that want to control the flush process.
    explicit memtable(schema_ptr schema);
    ~memtable();
    // Clears this memtable gradually without consuming the whole CPU.
    // Never resolves with a failed future.
    future<> clear_gently() noexcept;
    schema_ptr schema() const { return _schema; }
    void set_schema(schema_ptr) noexcept;
    future<> apply(memtable&);
    // Applies mutation to this memtable.
    // The mutation is upgraded to current schema.
    void apply(const mutation& m, db::rp_handle&& = {});
    // The mutation is upgraded to current schema.
    void apply(const frozen_mutation& m, const schema_ptr& m_schema, db::rp_handle&& = {});

    static memtable& from_region(logalloc::region& r) {
        return static_cast<memtable&>(r);
    }

    const logalloc::region& region() const {
        return *this;
    }

    logalloc::region_group* region_group() {
        return group();
    }
public:
    memtable_list* get_memtable_list() {
        return _memtable_list;
    }

    size_t partition_count() const;
    logalloc::occupancy_stats occupancy() const;

    // Creates a reader of data in this memtable for given partition range.
    //
    // Live readers share ownership of the memtable instance, so caller
    // doesn't need to ensure that memtable remains live.
    //
    // The 'range' parameter must be live as long as the reader is being used
    //
    // Mutations returned by the reader will all have given schema.
    mutation_reader make_reader(schema_ptr,
                                const dht::partition_range& range = query::full_partition_range,
                                const query::partition_slice& slice = query::full_slice,
                                const io_priority_class& pc = default_priority_class(),
                                tracing::trace_state_ptr trace_state_ptr = nullptr,
                                streamed_mutation::forwarding fwd = streamed_mutation::forwarding::no,
                                mutation_reader::forwarding fwd_mr = mutation_reader::forwarding::no);

    mutation_reader make_flush_reader(schema_ptr, const io_priority_class& pc);

    mutation_source as_data_source();

    bool empty() const { return partitions.empty(); }
    void mark_flushed(mutation_source);
    bool is_flushed() const;
    void on_detach_from_region_group() noexcept;
    void revert_flushed_memory() noexcept;

    const db::replay_position& replay_position() const {
        return _replay_position;
    }
    const db::rp_set& rp_set() const {
        return _rp_set;
    }
    friend class iterator_reader;
};
<commit_msg>memtable: Created readers should be fast forwardable by default<commit_after>/*
 * Copyright (C) 2015 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <map>
#include <memory>
#include "database_fwd.hh"
#include "dht/i_partitioner.hh"
#include "schema.hh"
#include "mutation_reader.hh"
#include "db/commitlog/replay_position.hh"
#include "db/commitlog/rp_set.hh"
#include "utils/logalloc.hh"
#include "partition_version.hh"

class frozen_mutation;

namespace bi = boost::intrusive;

class memtable_entry {
    bi::set_member_hook<> _link;
    schema_ptr _schema;
    dht::decorated_key _key;
    partition_entry _pe;
public:
    friend class memtable;

    memtable_entry(schema_ptr s, dht::decorated_key key, mutation_partition p)
        : _schema(std::move(s))
        , _key(std::move(key))
        , _pe(std::move(p))
    { }

    memtable_entry(memtable_entry&& o) noexcept;

    const dht::decorated_key& key() const { return _key; }
    dht::decorated_key& key() { return _key; }
    const partition_entry& partition() const { return _pe; }
    partition_entry& partition() { return _pe; }
    const schema_ptr& schema() const { return _schema; }
    schema_ptr& schema() { return _schema; }
    streamed_mutation read(lw_shared_ptr<memtable> mtbl, const schema_ptr&, const query::partition_slice&, streamed_mutation::forwarding);

    size_t external_memory_usage_without_rows() const {
        return _key.key().external_memory_usage();
    }

    struct compare {
        dht::decorated_key::less_comparator _c;

        compare(schema_ptr s)
            : _c(std::move(s))
        {}

        bool operator()(const dht::decorated_key& k1, const memtable_entry& k2) const {
            return _c(k1, k2._key);
        }

        bool operator()(const memtable_entry& k1, const memtable_entry& k2) const {
            return _c(k1._key, k2._key);
        }

        bool operator()(const memtable_entry& k1, const dht::decorated_key& k2) const {
            return _c(k1._key, k2);
        }

        bool operator()(const memtable_entry& k1, const dht::ring_position& k2) const {
            return _c(k1._key, k2);
        }

        bool operator()(const dht::ring_position& k1, const memtable_entry& k2) const {
            return _c(k1, k2._key);
        }
    };
};

class dirty_memory_manager;

// Managed by lw_shared_ptr<>.
class memtable final : public enable_lw_shared_from_this<memtable>, private logalloc::region {
public:
    using partitions_type = bi::set<memtable_entry,
        bi::member_hook<memtable_entry, bi::set_member_hook<>, &memtable_entry::_link>,
        bi::compare<memtable_entry::compare>>;
private:
    dirty_memory_manager& _dirty_mgr;
    memtable_list *_memtable_list;
    schema_ptr _schema;
    logalloc::allocating_section _read_section;
    logalloc::allocating_section _allocating_section;
    partitions_type partitions;
    db::replay_position _replay_position;
    db::rp_set _rp_set;
    // mutation source to which reads fall-back after mark_flushed()
    // so that memtable contents can be moved away while there are
    // still active readers. This is needed for this mutation_source
    // to be monotonic (not loose writes). Monotonicity of each
    // mutation_source is necessary for the combined mutation source to be
    // monotonic. That combined source in this case is cache + memtable.
    mutation_source_opt _underlying;
    uint64_t _flushed_memory = 0;
    void update(db::rp_handle&&);
    friend class row_cache;
    friend class memtable_entry;
    friend class flush_reader;
    friend class flush_memory_accounter;
private:
    boost::iterator_range<partitions_type::const_iterator> slice(const dht::partition_range& r) const;
    partition_entry& find_or_create_partition(const dht::decorated_key& key);
    partition_entry& find_or_create_partition_slow(partition_key_view key);
    void upgrade_entry(memtable_entry&);
    void add_flushed_memory(uint64_t);
    void remove_flushed_memory(uint64_t);
    void clear() noexcept;
    uint64_t dirty_size() const;
public:
    explicit memtable(schema_ptr schema, dirty_memory_manager&, memtable_list *memtable_list = nullptr);
    // Used for testing that want to control the flush process.
    explicit memtable(schema_ptr schema);
    ~memtable();
    // Clears this memtable gradually without consuming the whole CPU.
    // Never resolves with a failed future.
    future<> clear_gently() noexcept;
    schema_ptr schema() const { return _schema; }
    void set_schema(schema_ptr) noexcept;
    future<> apply(memtable&);
    // Applies mutation to this memtable.
    // The mutation is upgraded to current schema.
    void apply(const mutation& m, db::rp_handle&& = {});
    // The mutation is upgraded to current schema.
    void apply(const frozen_mutation& m, const schema_ptr& m_schema, db::rp_handle&& = {});

    static memtable& from_region(logalloc::region& r) {
        return static_cast<memtable&>(r);
    }

    const logalloc::region& region() const {
        return *this;
    }

    logalloc::region_group* region_group() {
        return group();
    }
public:
    memtable_list* get_memtable_list() {
        return _memtable_list;
    }

    size_t partition_count() const;
    logalloc::occupancy_stats occupancy() const;

    // Creates a reader of data in this memtable for given partition range.
    //
    // Live readers share ownership of the memtable instance, so caller
    // doesn't need to ensure that memtable remains live.
    //
    // The 'range' parameter must be live as long as the reader is being used
    //
    // Mutations returned by the reader will all have given schema.
    mutation_reader make_reader(schema_ptr,
                                const dht::partition_range& range = query::full_partition_range,
                                const query::partition_slice& slice = query::full_slice,
                                const io_priority_class& pc = default_priority_class(),
                                tracing::trace_state_ptr trace_state_ptr = nullptr,
                                streamed_mutation::forwarding fwd = streamed_mutation::forwarding::no,
                                mutation_reader::forwarding fwd_mr = mutation_reader::forwarding::yes);

    mutation_reader make_flush_reader(schema_ptr, const io_priority_class& pc);

    mutation_source as_data_source();

    bool empty() const { return partitions.empty(); }
    void mark_flushed(mutation_source);
    bool is_flushed() const;
    void on_detach_from_region_group() noexcept;
    void revert_flushed_memory() noexcept;

    const db::replay_position& replay_position() const {
        return _replay_position;
    }
    const db::rp_set& rp_set() const {
        return _rp_set;
    }
    friend class iterator_reader;
};
<|endoftext|>