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

#include "src/gpu/dawn/GrDawnCaps.h"

#include "src/gpu/GrProgramDesc.h"
#include "src/gpu/GrProgramInfo.h"
#include "src/gpu/GrRenderTarget.h"
#include "src/gpu/GrRenderTargetPriv.h"
#include "src/gpu/GrStencilSettings.h"

GrDawnCaps::GrDawnCaps(const GrContextOptions& contextOptions) : INHERITED(contextOptions) {
    fMipMapSupport = true;
    fBufferMapThreshold = SK_MaxS32;  // FIXME: get this from Dawn?
    fShaderCaps.reset(new GrShaderCaps(contextOptions));
    fMaxTextureSize = fMaxRenderTargetSize = 4096; // FIXME
    fMaxVertexAttributes = 16; // FIXME
    fClampToBorderSupport = false;
    fPerformPartialClearsAsDraws = true;
    fDynamicStateArrayGeometryProcessorTextureSupport = true;

    fShaderCaps->fFlatInterpolationSupport = true;
    fShaderCaps->fIntegerSupport = true;
    // FIXME: each fragment sampler takes two binding slots in Dawn (sampler + texture). Limit to
    // 6 * 2 = 12, since kMaxBindingsPerGroup is 16 in Dawn, and we need to keep a few for
    // non-texture bindings. Eventually, we may be able to increase kMaxBindingsPerGroup in Dawn.
    fShaderCaps->fMaxFragmentSamplers = 6;
    fShaderCaps->fShaderDerivativeSupport = true;

    this->finishInitialization(contextOptions);
}

bool GrDawnCaps::isFormatSRGB(const GrBackendFormat& format) const {
    return false;
}

SkImage::CompressionType GrDawnCaps::compressionType(const GrBackendFormat& format) const {
    return SkImage::CompressionType::kNone;
}

bool GrDawnCaps::isFormatTexturable(const GrBackendFormat& format) const {
    // Currently, all the formats in GrDawnFormatToPixelConfig are texturable.
    wgpu::TextureFormat dawnFormat;
    return format.asDawnFormat(&dawnFormat);
}

static GrSwizzle get_swizzle(const GrBackendFormat& format, GrColorType colorType,
                             bool forOutput) {
    switch (colorType) {
        case GrColorType::kAlpha_8: // fall through
        case GrColorType::kAlpha_F16:
            if (forOutput) {
                return GrSwizzle::AAAA();
            } else {
                return GrSwizzle::RRRR();
            }
        case GrColorType::kGray_8:
            if (!forOutput) {
                return GrSwizzle::RRRA();
            }
            break;
        case GrColorType::kRGB_888x:
            if (!forOutput) {
                return GrSwizzle::RGB1();
            }
        default:
            return GrSwizzle::RGBA();
    }
    return GrSwizzle::RGBA();
}

bool GrDawnCaps::isFormatTexturableAndUploadable(GrColorType ct,
                                                 const GrBackendFormat& format) const {
    wgpu::TextureFormat dawnFormat;
    if (!format.asDawnFormat(&dawnFormat)) {
        return false;
    }
    switch (ct) {
        case GrColorType::kAlpha_8:
            return wgpu::TextureFormat::R8Unorm == dawnFormat;
        case GrColorType::kRGBA_8888:
        case GrColorType::kRGB_888x:
        case GrColorType::kBGRA_8888:
            return wgpu::TextureFormat::RGBA8Unorm == dawnFormat ||
                   wgpu::TextureFormat::BGRA8Unorm == dawnFormat;
        default:
            return false;
    }
}

bool GrDawnCaps::isFormatRenderable(const GrBackendFormat& format,
                                    int sampleCount) const {
    wgpu::TextureFormat dawnFormat;
    if (!format.isValid() || sampleCount > 1 || !format.asDawnFormat(&dawnFormat)) {
        return false;
    }

    return GrDawnFormatIsRenderable(dawnFormat);
}

bool GrDawnCaps::isFormatAsColorTypeRenderable(GrColorType ct, const GrBackendFormat& format,
                                               int sampleCount) const {
    return isFormatRenderable(format, sampleCount);
}

size_t GrDawnCaps::bytesPerPixel(const GrBackendFormat& backendFormat) const {
    wgpu::TextureFormat dawnFormat;
    if (!backendFormat.asDawnFormat(&dawnFormat)) {
        return 0;
    }
    return GrDawnBytesPerPixel(dawnFormat);
}

int GrDawnCaps::getRenderTargetSampleCount(int requestedCount,
                                           const GrBackendFormat& backendFormat) const {
    wgpu::TextureFormat dawnFormat;
    if (!backendFormat.asDawnFormat(&dawnFormat)) {
        return 0;
    }
    return GrDawnFormatIsRenderable(dawnFormat) ? 1 : 0;
}

int GrDawnCaps::maxRenderTargetSampleCount(const GrBackendFormat& format) const {
    return format.isValid() ? 1 : 0;
}

GrBackendFormat GrDawnCaps::onGetDefaultBackendFormat(GrColorType ct,
                                                      GrRenderable renderable) const {
    wgpu::TextureFormat format;
    if (!GrColorTypeToDawnFormat(ct, &format)) {
        return GrBackendFormat();
    }
    return GrBackendFormat::MakeDawn(format);
}

GrBackendFormat GrDawnCaps::getBackendFormatFromCompressionType(SkImage::CompressionType type) const
{
    return GrBackendFormat();
}

GrSwizzle GrDawnCaps::getReadSwizzle(const GrBackendFormat& format, GrColorType colorType) const
{
    return get_swizzle(format, colorType, false);
}

GrSwizzle GrDawnCaps::getOutputSwizzle(const GrBackendFormat& format, GrColorType colorType) const
{
    return get_swizzle(format, colorType, true);
}

uint64_t GrDawnCaps::computeFormatKey(const GrBackendFormat& format) const {
    wgpu::TextureFormat dawnFormat;
    SkAssertResult(format.asDawnFormat(&dawnFormat));

    // Dawn max enum value should always fit in 32 bits.

    // disabled: no member named 'WGPUTextureFormat_Force32' in namespace 'wgpu'
    //SkASSERT(dawnFormat <= wgpu::WGPUTextureFormat_Force32);
    return (uint64_t)dawnFormat;
}

bool GrDawnCaps::onAreColorTypeAndFormatCompatible(GrColorType ct,
                                                   const GrBackendFormat& format) const {
    return true;
}

GrColorType GrDawnCaps::getYUVAColorTypeFromBackendFormat(const GrBackendFormat& backendFormat,
                                                          bool isAlphaChannel) const {
    wgpu::TextureFormat textureFormat;
    if (!backendFormat.asDawnFormat(&textureFormat)) {
        return GrColorType::kUnknown;
    }
    switch (textureFormat) {
        case wgpu::TextureFormat::R8Unorm:     return isAlphaChannel ? GrColorType::kAlpha_8
                                                                     : GrColorType::kGray_8;
        case wgpu::TextureFormat::RGBA8Unorm:  return GrColorType::kRGBA_8888;
        case wgpu::TextureFormat::BGRA8Unorm:  return GrColorType::kBGRA_8888;
        default:                               return GrColorType::kUnknown;
    }
}

// FIXME: taken from GrVkPipelineState; refactor.
static uint32_t get_blend_info_key(const GrPipeline& pipeline) {
    GrXferProcessor::BlendInfo blendInfo = pipeline.getXferProcessor().getBlendInfo();

    static const uint32_t kBlendWriteShift = 1;
    static const uint32_t kBlendCoeffShift = 5;
    static_assert(kLast_GrBlendCoeff < (1 << kBlendCoeffShift));
    static_assert(kFirstAdvancedGrBlendEquation - 1 < 4);

    uint32_t key = blendInfo.fWriteColor;
    key |= (blendInfo.fSrcBlend << kBlendWriteShift);
    key |= (blendInfo.fDstBlend << (kBlendWriteShift + kBlendCoeffShift));
    key |= (blendInfo.fEquation << (kBlendWriteShift + 2 * kBlendCoeffShift));

    return key;
}

GrProgramDesc GrDawnCaps::makeDesc(const GrRenderTarget* rt,
                                   const GrProgramInfo& programInfo) const {
    GrProgramDesc desc;
    if (!GrProgramDesc::Build(&desc, rt, programInfo, *this)) {
        SkASSERT(!desc.isValid());
        return desc;
    }

    wgpu::TextureFormat format;
    if (!programInfo.backendFormat().asDawnFormat(&format)) {
        desc.key().reset();
        SkASSERT(!desc.isValid());
        return desc;
    }

    GrProcessorKeyBuilder b(&desc.key());

    GrStencilSettings stencil = programInfo.nonGLStencilSettings();
    stencil.genKey(&b);

    // TODO: remove this reliance on the renderTarget
    bool hasDepthStencil = rt->renderTargetPriv().getStencilAttachment() != nullptr;

    b.add32(static_cast<uint32_t>(format));
    b.add32(static_cast<int32_t>(hasDepthStencil));
    b.add32(get_blend_info_key(programInfo.pipeline()));
    b.add32(programInfo.primitiveTypeKey());
    return desc;
}

#if GR_TEST_UTILS
std::vector<GrCaps::TestFormatColorTypeCombination> GrDawnCaps::getTestingCombinations() const {
    std::vector<GrCaps::TestFormatColorTypeCombination> combos = {
        { GrColorType::kAlpha_8,   GrBackendFormat::MakeDawn(wgpu::TextureFormat::R8Unorm)    },
        { GrColorType::kRGBA_8888, GrBackendFormat::MakeDawn(wgpu::TextureFormat::RGBA8Unorm) },
        { GrColorType::kRGBA_8888, GrBackendFormat::MakeDawn(wgpu::TextureFormat::BGRA8Unorm) },
        { GrColorType::kRGB_888x,  GrBackendFormat::MakeDawn(wgpu::TextureFormat::RGBA8Unorm) },
        { GrColorType::kRGB_888x,  GrBackendFormat::MakeDawn(wgpu::TextureFormat::BGRA8Unorm) },
        { GrColorType::kBGRA_8888, GrBackendFormat::MakeDawn(wgpu::TextureFormat::BGRA8Unorm) },
        { GrColorType::kBGRA_8888, GrBackendFormat::MakeDawn(wgpu::TextureFormat::RGBA8Unorm) },
    };

#ifdef SK_DEBUG
    for (auto combo : combos) {
        SkASSERT(this->onAreColorTypeAndFormatCompatible(combo.fColorType, combo.fFormat));
    }
#endif
    return combos;
}
#endif
<commit_msg>Turn off mip map support in GrDawnCaps<commit_after>/*
 * Copyright 2019 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/gpu/dawn/GrDawnCaps.h"

#include "src/gpu/GrProgramDesc.h"
#include "src/gpu/GrProgramInfo.h"
#include "src/gpu/GrRenderTarget.h"
#include "src/gpu/GrRenderTargetPriv.h"
#include "src/gpu/GrStencilSettings.h"

GrDawnCaps::GrDawnCaps(const GrContextOptions& contextOptions) : INHERITED(contextOptions) {
    fMipMapSupport = false;  // FIXME: implement onRegenerateMipMapLevels in GrDawnGpu.
    fBufferMapThreshold = SK_MaxS32;  // FIXME: get this from Dawn?
    fShaderCaps.reset(new GrShaderCaps(contextOptions));
    fMaxTextureSize = fMaxRenderTargetSize = 4096; // FIXME
    fMaxVertexAttributes = 16; // FIXME
    fClampToBorderSupport = false;
    fPerformPartialClearsAsDraws = true;
    fDynamicStateArrayGeometryProcessorTextureSupport = true;

    fShaderCaps->fFlatInterpolationSupport = true;
    fShaderCaps->fIntegerSupport = true;
    // FIXME: each fragment sampler takes two binding slots in Dawn (sampler + texture). Limit to
    // 6 * 2 = 12, since kMaxBindingsPerGroup is 16 in Dawn, and we need to keep a few for
    // non-texture bindings. Eventually, we may be able to increase kMaxBindingsPerGroup in Dawn.
    fShaderCaps->fMaxFragmentSamplers = 6;
    fShaderCaps->fShaderDerivativeSupport = true;

    this->finishInitialization(contextOptions);
}

bool GrDawnCaps::isFormatSRGB(const GrBackendFormat& format) const {
    return false;
}

SkImage::CompressionType GrDawnCaps::compressionType(const GrBackendFormat& format) const {
    return SkImage::CompressionType::kNone;
}

bool GrDawnCaps::isFormatTexturable(const GrBackendFormat& format) const {
    // Currently, all the formats in GrDawnFormatToPixelConfig are texturable.
    wgpu::TextureFormat dawnFormat;
    return format.asDawnFormat(&dawnFormat);
}

static GrSwizzle get_swizzle(const GrBackendFormat& format, GrColorType colorType,
                             bool forOutput) {
    switch (colorType) {
        case GrColorType::kAlpha_8: // fall through
        case GrColorType::kAlpha_F16:
            if (forOutput) {
                return GrSwizzle::AAAA();
            } else {
                return GrSwizzle::RRRR();
            }
        case GrColorType::kGray_8:
            if (!forOutput) {
                return GrSwizzle::RRRA();
            }
            break;
        case GrColorType::kRGB_888x:
            if (!forOutput) {
                return GrSwizzle::RGB1();
            }
        default:
            return GrSwizzle::RGBA();
    }
    return GrSwizzle::RGBA();
}

bool GrDawnCaps::isFormatTexturableAndUploadable(GrColorType ct,
                                                 const GrBackendFormat& format) const {
    wgpu::TextureFormat dawnFormat;
    if (!format.asDawnFormat(&dawnFormat)) {
        return false;
    }
    switch (ct) {
        case GrColorType::kAlpha_8:
            return wgpu::TextureFormat::R8Unorm == dawnFormat;
        case GrColorType::kRGBA_8888:
        case GrColorType::kRGB_888x:
        case GrColorType::kBGRA_8888:
            return wgpu::TextureFormat::RGBA8Unorm == dawnFormat ||
                   wgpu::TextureFormat::BGRA8Unorm == dawnFormat;
        default:
            return false;
    }
}

bool GrDawnCaps::isFormatRenderable(const GrBackendFormat& format,
                                    int sampleCount) const {
    wgpu::TextureFormat dawnFormat;
    if (!format.isValid() || sampleCount > 1 || !format.asDawnFormat(&dawnFormat)) {
        return false;
    }

    return GrDawnFormatIsRenderable(dawnFormat);
}

bool GrDawnCaps::isFormatAsColorTypeRenderable(GrColorType ct, const GrBackendFormat& format,
                                               int sampleCount) const {
    return isFormatRenderable(format, sampleCount);
}

size_t GrDawnCaps::bytesPerPixel(const GrBackendFormat& backendFormat) const {
    wgpu::TextureFormat dawnFormat;
    if (!backendFormat.asDawnFormat(&dawnFormat)) {
        return 0;
    }
    return GrDawnBytesPerPixel(dawnFormat);
}

int GrDawnCaps::getRenderTargetSampleCount(int requestedCount,
                                           const GrBackendFormat& backendFormat) const {
    wgpu::TextureFormat dawnFormat;
    if (!backendFormat.asDawnFormat(&dawnFormat)) {
        return 0;
    }
    return GrDawnFormatIsRenderable(dawnFormat) ? 1 : 0;
}

int GrDawnCaps::maxRenderTargetSampleCount(const GrBackendFormat& format) const {
    return format.isValid() ? 1 : 0;
}

GrBackendFormat GrDawnCaps::onGetDefaultBackendFormat(GrColorType ct,
                                                      GrRenderable renderable) const {
    wgpu::TextureFormat format;
    if (!GrColorTypeToDawnFormat(ct, &format)) {
        return GrBackendFormat();
    }
    return GrBackendFormat::MakeDawn(format);
}

GrBackendFormat GrDawnCaps::getBackendFormatFromCompressionType(SkImage::CompressionType type) const
{
    return GrBackendFormat();
}

GrSwizzle GrDawnCaps::getReadSwizzle(const GrBackendFormat& format, GrColorType colorType) const
{
    return get_swizzle(format, colorType, false);
}

GrSwizzle GrDawnCaps::getOutputSwizzle(const GrBackendFormat& format, GrColorType colorType) const
{
    return get_swizzle(format, colorType, true);
}

uint64_t GrDawnCaps::computeFormatKey(const GrBackendFormat& format) const {
    wgpu::TextureFormat dawnFormat;
    SkAssertResult(format.asDawnFormat(&dawnFormat));

    // Dawn max enum value should always fit in 32 bits.

    // disabled: no member named 'WGPUTextureFormat_Force32' in namespace 'wgpu'
    //SkASSERT(dawnFormat <= wgpu::WGPUTextureFormat_Force32);
    return (uint64_t)dawnFormat;
}

bool GrDawnCaps::onAreColorTypeAndFormatCompatible(GrColorType ct,
                                                   const GrBackendFormat& format) const {
    return true;
}

GrColorType GrDawnCaps::getYUVAColorTypeFromBackendFormat(const GrBackendFormat& backendFormat,
                                                          bool isAlphaChannel) const {
    wgpu::TextureFormat textureFormat;
    if (!backendFormat.asDawnFormat(&textureFormat)) {
        return GrColorType::kUnknown;
    }
    switch (textureFormat) {
        case wgpu::TextureFormat::R8Unorm:     return isAlphaChannel ? GrColorType::kAlpha_8
                                                                     : GrColorType::kGray_8;
        case wgpu::TextureFormat::RGBA8Unorm:  return GrColorType::kRGBA_8888;
        case wgpu::TextureFormat::BGRA8Unorm:  return GrColorType::kBGRA_8888;
        default:                               return GrColorType::kUnknown;
    }
}

// FIXME: taken from GrVkPipelineState; refactor.
static uint32_t get_blend_info_key(const GrPipeline& pipeline) {
    GrXferProcessor::BlendInfo blendInfo = pipeline.getXferProcessor().getBlendInfo();

    static const uint32_t kBlendWriteShift = 1;
    static const uint32_t kBlendCoeffShift = 5;
    static_assert(kLast_GrBlendCoeff < (1 << kBlendCoeffShift));
    static_assert(kFirstAdvancedGrBlendEquation - 1 < 4);

    uint32_t key = blendInfo.fWriteColor;
    key |= (blendInfo.fSrcBlend << kBlendWriteShift);
    key |= (blendInfo.fDstBlend << (kBlendWriteShift + kBlendCoeffShift));
    key |= (blendInfo.fEquation << (kBlendWriteShift + 2 * kBlendCoeffShift));

    return key;
}

GrProgramDesc GrDawnCaps::makeDesc(const GrRenderTarget* rt,
                                   const GrProgramInfo& programInfo) const {
    GrProgramDesc desc;
    if (!GrProgramDesc::Build(&desc, rt, programInfo, *this)) {
        SkASSERT(!desc.isValid());
        return desc;
    }

    wgpu::TextureFormat format;
    if (!programInfo.backendFormat().asDawnFormat(&format)) {
        desc.key().reset();
        SkASSERT(!desc.isValid());
        return desc;
    }

    GrProcessorKeyBuilder b(&desc.key());

    GrStencilSettings stencil = programInfo.nonGLStencilSettings();
    stencil.genKey(&b);

    // TODO: remove this reliance on the renderTarget
    bool hasDepthStencil = rt->renderTargetPriv().getStencilAttachment() != nullptr;

    b.add32(static_cast<uint32_t>(format));
    b.add32(static_cast<int32_t>(hasDepthStencil));
    b.add32(get_blend_info_key(programInfo.pipeline()));
    b.add32(programInfo.primitiveTypeKey());
    return desc;
}

#if GR_TEST_UTILS
std::vector<GrCaps::TestFormatColorTypeCombination> GrDawnCaps::getTestingCombinations() const {
    std::vector<GrCaps::TestFormatColorTypeCombination> combos = {
        { GrColorType::kAlpha_8,   GrBackendFormat::MakeDawn(wgpu::TextureFormat::R8Unorm)    },
        { GrColorType::kRGBA_8888, GrBackendFormat::MakeDawn(wgpu::TextureFormat::RGBA8Unorm) },
        { GrColorType::kRGBA_8888, GrBackendFormat::MakeDawn(wgpu::TextureFormat::BGRA8Unorm) },
        { GrColorType::kRGB_888x,  GrBackendFormat::MakeDawn(wgpu::TextureFormat::RGBA8Unorm) },
        { GrColorType::kRGB_888x,  GrBackendFormat::MakeDawn(wgpu::TextureFormat::BGRA8Unorm) },
        { GrColorType::kBGRA_8888, GrBackendFormat::MakeDawn(wgpu::TextureFormat::BGRA8Unorm) },
        { GrColorType::kBGRA_8888, GrBackendFormat::MakeDawn(wgpu::TextureFormat::RGBA8Unorm) },
    };

#ifdef SK_DEBUG
    for (auto combo : combos) {
        SkASSERT(this->onAreColorTypeAndFormatCompatible(combo.fColorType, combo.fFormat));
    }
#endif
    return combos;
}
#endif
<|endoftext|>


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

#include <miopen/hip_build_utils.hpp>
#include <miopen/stringutils.hpp>
#include <miopen/exec_utils.hpp>
#include <miopen/logger.hpp>
#include <miopen/env.hpp>
#include <boost/optional.hpp>
#include <sstream>
#include <string>

MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_HIP_ENFORCE_COV3)

namespace miopen {

namespace {

inline bool IsHccCompiler()
{
    static const auto isHcc = EndsWith(MIOPEN_HIP_COMPILER, "hcc");
    return isHcc;
}

inline bool ProduceCoV3()
{
    // If env.var is set, then let's follow it.
    if(IsEnabled(MIOPEN_DEBUG_HIP_ENFORCE_COV3{}))
        return true;
    if(IsDisabled(MIOPEN_DEBUG_HIP_ENFORCE_COV3{}))
        return false;
    // Otherwise, let's enable CO v3 for HIP kernels since ROCm 3.0.
    return (HipGetHccVersion() >= external_tool_version_t{3, 0, -1});
}

/// Returns option for enabling/disabling CO v3 generation for the compiler
/// that builds HIP kernels, depending on compiler version etc.
inline const std::string& GetCoV3Option(const bool enable)
{
    /// \note PR #2166 uses the "--hcc-cov3" option when isHCC is true.
    /// It's unclear why... HCC included in ROCm 2.8 does not support it,
    /// perhaps it suits for some older HCC?
    ///
    /// These options are Ok for ROCm 3.0:
    static const std::string option_enable{"-mcode-object-v3"};
    static const std::string no_option{};
    if(enable)
        return option_enable;
    else
        return no_option;
}
} // namespace

boost::filesystem::path HipBuild(boost::optional<TmpDir>& tmp_dir,
                                 const std::string& filename,
                                 std::string src,
                                 std::string params,
                                 const std::string& dev_name)
{
#ifdef __linux__
    // write out the include files
    auto inc_list = GetKernelIncList();
    auto inc_path = tmp_dir->path;
    boost::filesystem::create_directories(inc_path);
    for(auto inc_file : inc_list)
    {
        auto inc_src = GetKernelInc(inc_file);
        WriteFile(inc_src, inc_path / inc_file);
    }
    src += "\nint main() {}\n";
    WriteFile(src, tmp_dir->path / filename);
    if(IsHccCompiler())
    {
        params += " -amdgpu-target=" + dev_name;
    }
    else
    {
        if(params.find("-std=") == std::string::npos)
            params += " --std=c++11";
        params += " --cuda-gpu-arch=" + dev_name;
        params += " --cuda-device-only -c";
        params += " -O3 -mllvm -amdgpu-early-inline-all=true -mllvm -amdgpu-function-calls=false";
    }
    params += " " + GetCoV3Option(ProduceCoV3());

    // params += " -Wno-unused-command-line-argument -c -fno-gpu-rdc -I. ";
    params += " -Wno-unused-command-line-argument -I. ";
    params += MIOPEN_STRINGIZE(HIP_COMPILER_FLAGS);
    params += " ";
    auto bin_file = tmp_dir->path / (filename + ".o");
    // compile
    auto env = std::string("KMOPTLLC=\"-mattr=+enable-ds128 -amdgpu-enable-global-sgpr-addr");
    if(miopen::HipGetHccVersion() >= external_tool_version_t{2, 8, 0})
        env += " --amdgpu-spill-vgpr-to-agpr=0";
    env += '\"';
    tmp_dir->Execute(env + std::string(" ") + MIOPEN_HIP_COMPILER,
                     params + filename + " -o " + bin_file.string());
    if(!boost::filesystem::exists(bin_file))
        MIOPEN_THROW(filename + " failed to compile");
#ifdef EXTRACTKERNEL_BIN
    if(IsHccCompiler())
    {
        // call extract kernel
        tmp_dir->Execute(EXTRACTKERNEL_BIN, " -i " + bin_file.string());
        auto hsaco =
            std::find_if(boost::filesystem::directory_iterator{tmp_dir->path},
                         {},
                         [](auto entry) { return (entry.path().extension() == ".hsaco"); });

        if(hsaco == boost::filesystem::directory_iterator{})
        {
            MIOPEN_LOG_E("failed to find *.hsaco in " << hsaco->path().string());
        }

        return hsaco->path();
    }
    else
#endif
    {
        return bin_file;
    }
#else
    (void)filename;
    (void)params;
    MIOPEN_THROW("HIP kernels are only supported in Linux");
#endif
}

void bin_file_to_str(const boost::filesystem::path& file, std::string& buf)
{
    std::ifstream bin_file_ptr(file.string().c_str(), std::ios::binary);
    std::ostringstream bin_file_strm;
    bin_file_strm << bin_file_ptr.rdbuf();
    buf = bin_file_strm.str();
}

static external_tool_version_t HipGetHccVersionImpl()
{
    external_tool_version_t hcc_version;
    const std::string path(MIOPEN_HIP_COMPILER);
    const std::string mandatory_prefix("(based on HCC ");
    do
    {
        if(path.empty() || !std::ifstream(path).good())
            break;

        std::stringstream out;
        MIOPEN_LOG_NQI2("Running: " << '\'' << path << " --version" << '\'');
        if(miopen::exec::Run(path + " --version", nullptr, &out) != 0)
            break;

        std::string line;
        while(!out.eof())
        {
            std::getline(out, line);
            MIOPEN_LOG_NQI2(line);
            auto begin = line.find(mandatory_prefix);
            if(begin == std::string::npos)
                continue;

            begin += mandatory_prefix.size();
            int v3, v2, v1 = v2 = v3 = -1;
            char c2, c1 = c2 = 'X';
            std::istringstream iss(line.substr(begin));
            iss >> v1 >> c1 >> v2 >> c2 >> v3;
            if(!iss.fail() && v1 >= 0)
            {
                hcc_version.major = v1;
                if(c1 == '.' && v2 >= 0)
                {
                    hcc_version.minor = v2;
                    if(c2 == '.' && v3 >= 0)
                        hcc_version.patch = v3;
                }
            }
            break;
        }
    } while(false);
    MIOPEN_LOG_NQI("HCC base: " << hcc_version.major << '.' << hcc_version.minor << '.'
                                << hcc_version.patch);
    return hcc_version;
}

external_tool_version_t HipGetHccVersion()
{
    static auto once = HipGetHccVersionImpl();
    return once;
}

bool external_tool_version_t::operator>=(const external_tool_version_t& rhs) const
{
    if(major > rhs.major)
        return true;
    else if(major == rhs.major)
    {
        if(minor > rhs.minor)
            return true;
        else if(minor == rhs.minor)
            return (patch >= rhs.patch);
        else
            return false;
    }
    else
        return false;
}

} // namespace miopen
<commit_msg>Remove hip-clang internal flags (#2508)<commit_after>/*******************************************************************************
*
* MIT License
*
* Copyright (c) 2019 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*******************************************************************************/

#include <miopen/hip_build_utils.hpp>
#include <miopen/stringutils.hpp>
#include <miopen/exec_utils.hpp>
#include <miopen/logger.hpp>
#include <miopen/env.hpp>
#include <boost/optional.hpp>
#include <sstream>
#include <string>

MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_HIP_ENFORCE_COV3)

namespace miopen {

namespace {

inline bool IsHccCompiler()
{
    static const auto isHcc = EndsWith(MIOPEN_HIP_COMPILER, "hcc");
    return isHcc;
}

inline bool ProduceCoV3()
{
    // If env.var is set, then let's follow it.
    if(IsEnabled(MIOPEN_DEBUG_HIP_ENFORCE_COV3{}))
        return true;
    if(IsDisabled(MIOPEN_DEBUG_HIP_ENFORCE_COV3{}))
        return false;
    // Otherwise, let's enable CO v3 for HIP kernels since ROCm 3.0.
    return (HipGetHccVersion() >= external_tool_version_t{3, 0, -1});
}

/// Returns option for enabling/disabling CO v3 generation for the compiler
/// that builds HIP kernels, depending on compiler version etc.
inline const std::string& GetCoV3Option(const bool enable)
{
    /// \note PR #2166 uses the "--hcc-cov3" option when isHCC is true.
    /// It's unclear why... HCC included in ROCm 2.8 does not support it,
    /// perhaps it suits for some older HCC?
    ///
    /// These options are Ok for ROCm 3.0:
    static const std::string option_enable{"-mcode-object-v3"};
    static const std::string no_option{};
    if(enable)
        return option_enable;
    else
        return no_option;
}
} // namespace

boost::filesystem::path HipBuild(boost::optional<TmpDir>& tmp_dir,
                                 const std::string& filename,
                                 std::string src,
                                 std::string params,
                                 const std::string& dev_name)
{
#ifdef __linux__
    // write out the include files
    auto inc_list = GetKernelIncList();
    auto inc_path = tmp_dir->path;
    boost::filesystem::create_directories(inc_path);
    for(auto inc_file : inc_list)
    {
        auto inc_src = GetKernelInc(inc_file);
        WriteFile(inc_src, inc_path / inc_file);
    }
    src += "\nint main() {}\n";
    WriteFile(src, tmp_dir->path / filename);
    if(IsHccCompiler())
    {
        params += " -amdgpu-target=" + dev_name;
    }
    else
    {
        if(params.find("-std=") == std::string::npos)
            params += " --std=c++11";
        params += " --cuda-gpu-arch=" + dev_name;
        params += " --cuda-device-only -c";
        params += " -O3 ";
    }
    params += " " + GetCoV3Option(ProduceCoV3());

    // params += " -Wno-unused-command-line-argument -c -fno-gpu-rdc -I. ";
    params += " -Wno-unused-command-line-argument -I. ";
    params += MIOPEN_STRINGIZE(HIP_COMPILER_FLAGS);
    params += " ";
    auto bin_file = tmp_dir->path / (filename + ".o");
    // compile
    auto env = std::string("KMOPTLLC=\"-mattr=+enable-ds128 -amdgpu-enable-global-sgpr-addr");
    if(miopen::HipGetHccVersion() >= external_tool_version_t{2, 8, 0})
        env += " --amdgpu-spill-vgpr-to-agpr=0";
    env += '\"';
    tmp_dir->Execute(env + std::string(" ") + MIOPEN_HIP_COMPILER,
                     params + filename + " -o " + bin_file.string());
    if(!boost::filesystem::exists(bin_file))
        MIOPEN_THROW(filename + " failed to compile");
#ifdef EXTRACTKERNEL_BIN
    if(IsHccCompiler())
    {
        // call extract kernel
        tmp_dir->Execute(EXTRACTKERNEL_BIN, " -i " + bin_file.string());
        auto hsaco =
            std::find_if(boost::filesystem::directory_iterator{tmp_dir->path},
                         {},
                         [](auto entry) { return (entry.path().extension() == ".hsaco"); });

        if(hsaco == boost::filesystem::directory_iterator{})
        {
            MIOPEN_LOG_E("failed to find *.hsaco in " << hsaco->path().string());
        }

        return hsaco->path();
    }
    else
#endif
    {
        return bin_file;
    }
#else
    (void)filename;
    (void)params;
    MIOPEN_THROW("HIP kernels are only supported in Linux");
#endif
}

void bin_file_to_str(const boost::filesystem::path& file, std::string& buf)
{
    std::ifstream bin_file_ptr(file.string().c_str(), std::ios::binary);
    std::ostringstream bin_file_strm;
    bin_file_strm << bin_file_ptr.rdbuf();
    buf = bin_file_strm.str();
}

static external_tool_version_t HipGetHccVersionImpl()
{
    external_tool_version_t hcc_version;
    const std::string path(MIOPEN_HIP_COMPILER);
    const std::string mandatory_prefix("(based on HCC ");
    do
    {
        if(path.empty() || !std::ifstream(path).good())
            break;

        std::stringstream out;
        MIOPEN_LOG_NQI2("Running: " << '\'' << path << " --version" << '\'');
        if(miopen::exec::Run(path + " --version", nullptr, &out) != 0)
            break;

        std::string line;
        while(!out.eof())
        {
            std::getline(out, line);
            MIOPEN_LOG_NQI2(line);
            auto begin = line.find(mandatory_prefix);
            if(begin == std::string::npos)
                continue;

            begin += mandatory_prefix.size();
            int v3, v2, v1 = v2 = v3 = -1;
            char c2, c1 = c2 = 'X';
            std::istringstream iss(line.substr(begin));
            iss >> v1 >> c1 >> v2 >> c2 >> v3;
            if(!iss.fail() && v1 >= 0)
            {
                hcc_version.major = v1;
                if(c1 == '.' && v2 >= 0)
                {
                    hcc_version.minor = v2;
                    if(c2 == '.' && v3 >= 0)
                        hcc_version.patch = v3;
                }
            }
            break;
        }
    } while(false);
    MIOPEN_LOG_NQI("HCC base: " << hcc_version.major << '.' << hcc_version.minor << '.'
                                << hcc_version.patch);
    return hcc_version;
}

external_tool_version_t HipGetHccVersion()
{
    static auto once = HipGetHccVersionImpl();
    return once;
}

bool external_tool_version_t::operator>=(const external_tool_version_t& rhs) const
{
    if(major > rhs.major)
        return true;
    else if(major == rhs.major)
    {
        if(minor > rhs.minor)
            return true;
        else if(minor == rhs.minor)
            return (patch >= rhs.patch);
        else
            return false;
    }
    else
        return false;
}

} // namespace miopen
<|endoftext|>


<commit_before>#pragma once

#include <stdint.h>

/* Comment out this line for a non-test network */
// #define BTS_TEST_NETWORK
// #define BTS_TEST_NETWORK_VERSION                            10 // autogenerated

/** @file bts/blockchain/config.hpp
 *  @brief Defines global constants that determine blockchain behavior
 */
#define BTS_TEST_NETWORK_VERSION                            21 // autogenerated
#define BTS_BLOCKCHAIN_DATABASE_VERSION                     11

/**
 *  The address prepended to string representation of
 *  addresses.
 *
 *  Changing these parameters will result in a hard fork.
 */
#define BTS_ADDRESS_PREFIX                                  "PLS"
#define BTS_BLOCKCHAIN_SYMBOL                               "PLS"
#define BTS_BLOCKCHAIN_NAME                                 "BitShares PLAY"
#define BTS_BLOCKCHAIN_DESCRIPTION                          "BitShares PLAY DryRun Network"

#define BTS_BLOCKCHAIN_PRECISION                            100000

#define BTS_BLOCKCHAIN_MAX_UIA_MARKET_FEE                   10000
#define BTS_BLOCKCHAIN_BLOCK_INTERVAL_SEC                   int64_t(10)
#define BTS_BLOCKCHAIN_BLOCKS_PER_HOUR                      ((60*60)/BTS_BLOCKCHAIN_BLOCK_INTERVAL_SEC)
#define BTS_BLOCKCHAIN_BLOCKS_PER_DAY                       (BTS_BLOCKCHAIN_BLOCKS_PER_HOUR*int64_t(24))
#define BTS_BLOCKCHAIN_BLOCKS_PER_YEAR                      (BTS_BLOCKCHAIN_BLOCKS_PER_DAY*int64_t(365))

#define BTS_BLOCKCHAIN_NUM_DELEGATES                        uint32_t(101)
#define BTS_MAX_DELEGATE_PAY_PER_BLOCK                      int64_t( 50 * BTS_BLOCKCHAIN_PRECISION ) // 50 XTS
#define BTS_BLOCKCHAIN_MAX_UNDO_HISTORY                     BTS_BLOCKCHAIN_BLOCKS_PER_HOUR

#define BTS_BLOCKCHAIN_MAX_SLATE_SIZE                       (BTS_BLOCKCHAIN_NUM_DELEGATES + (BTS_BLOCKCHAIN_NUM_DELEGATES/10))
#define BTS_BLOCKCHAIN_MAX_TRANSACTION_EXPIRATION_SEC       (60*60*24*2)
#define BTS_BLOCKCHAIN_MAX_MEMO_SIZE                        19 // bytes
#define BTS_BLOCKCHAIN_EXTENDED_MEMO_SIZE                   32 // bytes
#define BTS_BLOCKCHAIN_MAX_EXTENDED_MEMO_SIZE               (BTS_BLOCKCHAIN_MAX_MEMO_SIZE + BTS_BLOCKCHAIN_EXTENDED_MEMO_SIZE)

/**
 *  The maximum amount that can be issued for user assets.
 *
 *  10^18 / 2^63 < 1  however, to support representing all share values as a double in
 *  languages like java script, we must stay within the epsilon so
 *
 *  10^15 / 2^53 < 1 allows all values to be represented as a double or an int64
 */
#define BTS_BLOCKCHAIN_MAX_SHARES                           (1000*1000*int64_t(1000)*1000*int64_t(1000))

#define BTS_BLOCKCHAIN_MIN_NAME_SIZE                        1
#define BTS_BLOCKCHAIN_MAX_NAME_SIZE                        63
#define BTS_BLOCKCHAIN_MAX_NAME_DATA_SIZE                   (1024*64)

#define BTS_BLOCKCHAIN_MAX_SUB_SYMBOL_SIZE                  8 // characters
#define BTS_BLOCKCHAIN_MIN_SYMBOL_SIZE                      3 // characters
#define BTS_BLOCKCHAIN_MAX_SYMBOL_SIZE                      12 // characters

#define BTS_BLOCKCHAIN_MIN_BURN_FEE                         BTS_BLOCKCHAIN_PRECISION * 1 // 1 XTS

#ifdef BTS_TEST_NETWORK
#define BTS_BLOCKCHAIN_VOTE_UPDATE_PERIOD_SEC               10
#else
#define BTS_BLOCKCHAIN_VOTE_UPDATE_PERIOD_SEC               (60*60) // 1 hour
#endif

#define BTS_BLOCKCHAIN_MIN_FEEDS                            ((BTS_BLOCKCHAIN_NUM_DELEGATES/2) + 1)
#define BTS_BLOCKCHAIN_MINIMUM_SHORT_ORDER_SIZE             (BTS_BLOCKCHAIN_PRECISION*100)
#define BTS_BLOCKCHAIN_MIN_YIELD_PERIOD_SEC                 (60*60*24) // 24 hours
#define BTS_BLOCKCHAIN_MAX_YIELD_PERIOD_SEC                 (BTS_BLOCKCHAIN_BLOCKS_PER_YEAR * BTS_BLOCKCHAIN_BLOCK_INTERVAL_SEC) // 1 year

#ifdef BTS_TEST_NETWORK
#define BTS_BLOCKCHAIN_MAX_SHORT_PERIOD_SEC                 (2*60*60) // 2 hours
#else
#define BTS_BLOCKCHAIN_MAX_SHORT_PERIOD_SEC                 (30*24*60*60) // 1 month
#endif

#define BTS_BLOCKCHAIN_MCALL_D2C_NUMERATOR                  1
#define BTS_BLOCKCHAIN_MCALL_D2C_DENOMINATOR                2

// TODO: This stuff only matters for propagation throttling; should go somewhere else
#define BTS_BLOCKCHAIN_DEFAULT_RELAY_FEE                    10000 // XTS
#define BTS_BLOCKCHAIN_MAX_TRX_PER_SECOND                   1  // (10)
#define BTS_BLOCKCHAIN_MAX_PENDING_QUEUE_SIZE               10 // (BTS_BLOCKCHAIN_MAX_TRX_PER_SECOND * BTS_BLOCKCHAIN_BLOCK_INTERVAL_SEC)
<commit_msg>update BTS_BLOCKCHAIN_VERSION<commit_after>#pragma once

#include <stdint.h>

/* Comment out this line for a non-test network */
// #define BTS_TEST_NETWORK
// #define BTS_TEST_NETWORK_VERSION                            10 // autogenerated

/** @file bts/blockchain/config.hpp
 *  @brief Defines global constants that determine blockchain behavior
 */
#define BTS_BLOCKCHAIN_VERSION                            21 // autogenerated
#define BTS_BLOCKCHAIN_DATABASE_VERSION                     11

/**
 *  The address prepended to string representation of
 *  addresses.
 *
 *  Changing these parameters will result in a hard fork.
 */
#define BTS_ADDRESS_PREFIX                                  "PLS"
#define BTS_BLOCKCHAIN_SYMBOL                               "PLS"
#define BTS_BLOCKCHAIN_NAME                                 "BitShares PLAY"
#define BTS_BLOCKCHAIN_DESCRIPTION                          "BitShares PLAY DryRun Network"

#define BTS_BLOCKCHAIN_PRECISION                            100000

#define BTS_BLOCKCHAIN_MAX_UIA_MARKET_FEE                   10000
#define BTS_BLOCKCHAIN_BLOCK_INTERVAL_SEC                   int64_t(10)
#define BTS_BLOCKCHAIN_BLOCKS_PER_HOUR                      ((60*60)/BTS_BLOCKCHAIN_BLOCK_INTERVAL_SEC)
#define BTS_BLOCKCHAIN_BLOCKS_PER_DAY                       (BTS_BLOCKCHAIN_BLOCKS_PER_HOUR*int64_t(24))
#define BTS_BLOCKCHAIN_BLOCKS_PER_YEAR                      (BTS_BLOCKCHAIN_BLOCKS_PER_DAY*int64_t(365))

#define BTS_BLOCKCHAIN_NUM_DELEGATES                        uint32_t(101)
#define BTS_MAX_DELEGATE_PAY_PER_BLOCK                      int64_t( 50 * BTS_BLOCKCHAIN_PRECISION ) // 50 XTS
#define BTS_BLOCKCHAIN_MAX_UNDO_HISTORY                     BTS_BLOCKCHAIN_BLOCKS_PER_HOUR

#define BTS_BLOCKCHAIN_MAX_SLATE_SIZE                       (BTS_BLOCKCHAIN_NUM_DELEGATES + (BTS_BLOCKCHAIN_NUM_DELEGATES/10))
#define BTS_BLOCKCHAIN_MAX_TRANSACTION_EXPIRATION_SEC       (60*60*24*2)
#define BTS_BLOCKCHAIN_MAX_MEMO_SIZE                        19 // bytes
#define BTS_BLOCKCHAIN_EXTENDED_MEMO_SIZE                   32 // bytes
#define BTS_BLOCKCHAIN_MAX_EXTENDED_MEMO_SIZE               (BTS_BLOCKCHAIN_MAX_MEMO_SIZE + BTS_BLOCKCHAIN_EXTENDED_MEMO_SIZE)

/**
 *  The maximum amount that can be issued for user assets.
 *
 *  10^18 / 2^63 < 1  however, to support representing all share values as a double in
 *  languages like java script, we must stay within the epsilon so
 *
 *  10^15 / 2^53 < 1 allows all values to be represented as a double or an int64
 */
#define BTS_BLOCKCHAIN_MAX_SHARES                           (1000*1000*int64_t(1000)*1000*int64_t(1000))

#define BTS_BLOCKCHAIN_MIN_NAME_SIZE                        1
#define BTS_BLOCKCHAIN_MAX_NAME_SIZE                        63
#define BTS_BLOCKCHAIN_MAX_NAME_DATA_SIZE                   (1024*64)

#define BTS_BLOCKCHAIN_MAX_SUB_SYMBOL_SIZE                  8 // characters
#define BTS_BLOCKCHAIN_MIN_SYMBOL_SIZE                      3 // characters
#define BTS_BLOCKCHAIN_MAX_SYMBOL_SIZE                      12 // characters

#define BTS_BLOCKCHAIN_MIN_BURN_FEE                         BTS_BLOCKCHAIN_PRECISION * 1 // 1 XTS

#ifdef BTS_TEST_NETWORK
#define BTS_BLOCKCHAIN_VOTE_UPDATE_PERIOD_SEC               10
#else
#define BTS_BLOCKCHAIN_VOTE_UPDATE_PERIOD_SEC               (60*60) // 1 hour
#endif

#define BTS_BLOCKCHAIN_MIN_FEEDS                            ((BTS_BLOCKCHAIN_NUM_DELEGATES/2) + 1)
#define BTS_BLOCKCHAIN_MINIMUM_SHORT_ORDER_SIZE             (BTS_BLOCKCHAIN_PRECISION*100)
#define BTS_BLOCKCHAIN_MIN_YIELD_PERIOD_SEC                 (60*60*24) // 24 hours
#define BTS_BLOCKCHAIN_MAX_YIELD_PERIOD_SEC                 (BTS_BLOCKCHAIN_BLOCKS_PER_YEAR * BTS_BLOCKCHAIN_BLOCK_INTERVAL_SEC) // 1 year

#ifdef BTS_TEST_NETWORK
#define BTS_BLOCKCHAIN_MAX_SHORT_PERIOD_SEC                 (2*60*60) // 2 hours
#else
#define BTS_BLOCKCHAIN_MAX_SHORT_PERIOD_SEC                 (30*24*60*60) // 1 month
#endif

#define BTS_BLOCKCHAIN_MCALL_D2C_NUMERATOR                  1
#define BTS_BLOCKCHAIN_MCALL_D2C_DENOMINATOR                2

// TODO: This stuff only matters for propagation throttling; should go somewhere else
#define BTS_BLOCKCHAIN_DEFAULT_RELAY_FEE                    10000 // XTS
#define BTS_BLOCKCHAIN_MAX_TRX_PER_SECOND                   1  // (10)
#define BTS_BLOCKCHAIN_MAX_PENDING_QUEUE_SIZE               10 // (BTS_BLOCKCHAIN_MAX_TRX_PER_SECOND * BTS_BLOCKCHAIN_BLOCK_INTERVAL_SEC)
<|endoftext|>


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

// CLASS  HEADER
#include <dali/internal/text/text-abstraction/bidirectional-support-impl.h>

// INTERNAL INCLUDES
#include <dali/internal/system/common/singleton-service-impl.h>

// EXTERNAL INCLUDES
#include <memory.h>
#include <fribidi/fribidi.h>
#include <dali/integration-api/debug.h>

namespace Dali
{

namespace TextAbstraction
{

namespace Internal
{

namespace
{
  typedef unsigned char BidiDirection;

  // Internal charcter's direction.
  const BidiDirection LEFT_TO_RIGHT = 0u;
  const BidiDirection NEUTRAL = 1u;
  const BidiDirection RIGHT_TO_LEFT = 2u;

  /**
   * @param[in] paragraphDirection The FriBiDi paragraph's direction.
   *
   * @return Whether the paragraph is right to left.
   */
  bool GetBidiParagraphDirection( FriBidiParType paragraphDirection )
  {
    switch( paragraphDirection )
    {
      case FRIBIDI_PAR_RTL:  // Right-To-Left paragraph.
      case FRIBIDI_PAR_WRTL: // Weak Right To Left paragraph.
      {
        return true;
      }
      case FRIBIDI_PAR_LTR:  // Left-To-Right paragraph.
      case FRIBIDI_PAR_ON:   // DirectiOn-Neutral paragraph.
      case FRIBIDI_PAR_WLTR: // Weak Left To Right paragraph.
      {
        return false;
      }
    }

    return false;
  }

  BidiDirection GetBidiCharacterDirection( FriBidiCharType characterDirection )
  {
    switch( characterDirection )
    {
      case FRIBIDI_TYPE_LTR: // Left-To-Right letter.
      case FRIBIDI_TYPE_EN:  // European Numeral.
      case FRIBIDI_TYPE_AN:  // Arabic Numeral.
      case FRIBIDI_TYPE_ES:  // European number Separator.
      case FRIBIDI_TYPE_ET:  // European number Terminator.
      {
        return LEFT_TO_RIGHT;
      }
      case FRIBIDI_TYPE_RTL: // Right-To-Left letter.
      case FRIBIDI_TYPE_AL:  // Arabic Letter.
      {
        return RIGHT_TO_LEFT;
      }
    }

    return NEUTRAL;
  }
}

struct BidirectionalSupport::Plugin
{
  /**
   * Stores bidirectional info per paragraph.
   */
  struct BidirectionalInfo
  {
    FriBidiCharType* characterTypes;      ///< The type of each character (right, left, neutral, ...)
    FriBidiLevel*    embeddedLevels;      ///< Embedded levels.
    FriBidiParType   paragraphDirection;  ///< The paragraph's direction.
  };

  Plugin()
  : mParagraphBidirectionalInfo(),
    mFreeIndices()
  {}

  ~Plugin()
  {
    // free all resources.
    for( Vector<BidirectionalInfo*>::Iterator it = mParagraphBidirectionalInfo.Begin(),
           endIt = mParagraphBidirectionalInfo.End();
         it != endIt;
         ++it )
    {
      BidirectionalInfo* info = *it;

      free( info->embeddedLevels );
      free( info->characterTypes );
      delete info;
    }
  }

  BidiInfoIndex CreateInfo( const Character* const paragraph,
                            Length numberOfCharacters )
  {
    // Reserve memory for the paragraph's bidirectional info.
    BidirectionalInfo* bidirectionalInfo = new BidirectionalInfo();

    bidirectionalInfo->characterTypes = reinterpret_cast<FriBidiCharType*>( malloc( numberOfCharacters * sizeof( FriBidiCharType ) ) );
    if( !bidirectionalInfo->characterTypes )
    {
      delete bidirectionalInfo;
      return 0;
    }

    bidirectionalInfo->embeddedLevels = reinterpret_cast<FriBidiLevel*>( malloc( numberOfCharacters * sizeof( FriBidiLevel ) ) );
    if( !bidirectionalInfo->embeddedLevels )
    {
      free( bidirectionalInfo->characterTypes );
      delete bidirectionalInfo;
      return 0;
    }

    // Retrieve the type of each character..
    fribidi_get_bidi_types( paragraph, numberOfCharacters, bidirectionalInfo->characterTypes );

    // Retrieve the paragraph's direction.
    bidirectionalInfo->paragraphDirection = fribidi_get_par_direction( bidirectionalInfo->characterTypes, numberOfCharacters );

    // Retrieve the embedding levels.
    if (fribidi_get_par_embedding_levels( bidirectionalInfo->characterTypes, numberOfCharacters, &bidirectionalInfo->paragraphDirection, bidirectionalInfo->embeddedLevels ) == 0)
    {
      free( bidirectionalInfo->characterTypes );
      delete bidirectionalInfo;
      return 0;
    }

    // Store the bidirectional info and return the index.
    BidiInfoIndex index = 0u;
    if( 0u != mFreeIndices.Count() )
    {
      Vector<BidiInfoIndex>::Iterator it = mFreeIndices.End() - 1u;

      index = *it;

      mFreeIndices.Remove( it );

      *( mParagraphBidirectionalInfo.Begin() + index ) = bidirectionalInfo;
    }
    else
    {
      index = static_cast<BidiInfoIndex>( mParagraphBidirectionalInfo.Count() );

      mParagraphBidirectionalInfo.PushBack( bidirectionalInfo );
    }

    return index;
  }

  void DestroyInfo( BidiInfoIndex bidiInfoIndex )
  {
    if( bidiInfoIndex >= mParagraphBidirectionalInfo.Count() )
    {
      return;
    }

    // Retrieve the paragraph's bidirectional info.
    Vector<BidirectionalInfo*>::Iterator it = mParagraphBidirectionalInfo.Begin() + bidiInfoIndex;
    BidirectionalInfo* bidirectionalInfo = *it;

    if( NULL != bidirectionalInfo )
    {
      // Free resources and destroy the container.
      free( bidirectionalInfo->embeddedLevels );
      free( bidirectionalInfo->characterTypes );
      delete bidirectionalInfo;

      *it = NULL;
    }

    // Add the index to the free indices vector.
    mFreeIndices.PushBack( bidiInfoIndex );
  }

  void Reorder( BidiInfoIndex bidiInfoIndex,
                CharacterIndex firstCharacterIndex,
                Length numberOfCharacters,
                CharacterIndex* visualToLogicalMap )
  {
    const FriBidiFlags flags = FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC;

    // Retrieve the paragraph's bidirectional info.
    const BidirectionalInfo* const bidirectionalInfo = *( mParagraphBidirectionalInfo.Begin() + bidiInfoIndex );

    // Initialize the visual to logical mapping table to the identity. Otherwise fribidi_reorder_line fails to retrieve a valid mapping table.
    for( CharacterIndex index = 0u; index < numberOfCharacters; ++index )
    {
      visualToLogicalMap[ index ] = index;
    }

    // Copy embedded levels as fribidi_reorder_line() may change them.
    const uint32_t embeddedLevelsSize = numberOfCharacters * sizeof( FriBidiLevel );
    FriBidiLevel* embeddedLevels = reinterpret_cast<FriBidiLevel*>( malloc( embeddedLevelsSize ) );
    if( embeddedLevels )
    {
      memcpy( embeddedLevels, bidirectionalInfo->embeddedLevels + firstCharacterIndex,  embeddedLevelsSize );

      // Reorder the line.
      if (fribidi_reorder_line( flags,
                                bidirectionalInfo->characterTypes + firstCharacterIndex,
                                numberOfCharacters,
                                0u,
                                bidirectionalInfo->paragraphDirection,
                                embeddedLevels,
                                NULL,
                                reinterpret_cast<FriBidiStrIndex*>( visualToLogicalMap ) ) == 0)
      {
        DALI_LOG_ERROR("fribidi_reorder_line is failed\n");
      }

      // Free resources.
      free( embeddedLevels );
    }
  }

  bool GetMirroredText( Character* text,
                        CharacterDirection* directions,
                        Length numberOfCharacters ) const
  {
    bool updated = false;

    for( CharacterIndex index = 0u; index < numberOfCharacters; ++index )
    {
      // Get a reference to the character inside the text.
      Character& character = *( text + index );

      // Retrieve the mirrored character.
      FriBidiChar mirroredCharacter = character;
      bool mirrored = false;
      if( *( directions + index ) )
      {
        mirrored = fribidi_get_mirror_char( character, &mirroredCharacter );
      }
      updated = updated || mirrored;

      // Update the character inside the text.
      character = mirroredCharacter;
    }

    return updated;
  }

  bool GetParagraphDirection( BidiInfoIndex bidiInfoIndex ) const
  {
    // Retrieve the paragraph's bidirectional info.
    const BidirectionalInfo* const bidirectionalInfo = *( mParagraphBidirectionalInfo.Begin() + bidiInfoIndex );

    return GetBidiParagraphDirection( bidirectionalInfo->paragraphDirection );
  }

  void GetCharactersDirection( BidiInfoIndex bidiInfoIndex,
                               CharacterDirection* directions,
                               Length numberOfCharacters )
  {
    const BidirectionalInfo* const bidirectionalInfo = *( mParagraphBidirectionalInfo.Begin() + bidiInfoIndex );

    const CharacterDirection paragraphDirection = GetBidiParagraphDirection( bidirectionalInfo->paragraphDirection );
    CharacterDirection previousDirection = paragraphDirection;

    for( CharacterIndex index = 0u; index < numberOfCharacters; ++index )
    {
      CharacterDirection& characterDirection = *( directions + index );
      characterDirection = false;

      // Get the bidi direction.
      const BidiDirection bidiDirection = GetBidiCharacterDirection( *( bidirectionalInfo->characterTypes + index ) );

      if( RIGHT_TO_LEFT == bidiDirection )
      {
        characterDirection = true;
      }
      else if( NEUTRAL == bidiDirection )
      {
        // For neutral characters it check's the next and previous directions.
        // If they are equals set that direction. If they are not, sets the paragraph's direction.
        // If there is no next, sets the paragraph's direction.

        CharacterDirection nextDirection = paragraphDirection;

        // Look for the next non-neutral character.
        Length nextIndex = index + 1u;
        for( ; nextIndex < numberOfCharacters; ++nextIndex )
        {
          BidiDirection nextBidiDirection = GetBidiCharacterDirection( *( bidirectionalInfo->characterTypes + nextIndex ) );
          if( nextBidiDirection != NEUTRAL )
          {
            nextDirection = RIGHT_TO_LEFT == nextBidiDirection;
            break;
          }
        }

        // Calculate the direction for all the neutral characters.
        characterDirection = previousDirection == nextDirection ? previousDirection : paragraphDirection;

        // Set the direction to all the neutral characters.
        ++index;
        for( ; index < nextIndex; ++index )
        {
          CharacterDirection& nextCharacterDirection = *( directions + index );
          nextCharacterDirection = characterDirection;
        }

        // Set the direction of the next non-neutral character.
        if( nextIndex < numberOfCharacters )
        {
          *( directions + nextIndex ) = nextDirection;
        }
      }

      previousDirection = characterDirection;
    }
  }

  Vector<BidirectionalInfo*> mParagraphBidirectionalInfo; ///< Stores the bidirectional info per paragraph.
  Vector<BidiInfoIndex>      mFreeIndices;                ///< Stores indices of free positions in the bidirectional info vector.
};

BidirectionalSupport::BidirectionalSupport()
: mPlugin( NULL )
{
}

BidirectionalSupport::~BidirectionalSupport()
{
  delete mPlugin;
}

TextAbstraction::BidirectionalSupport BidirectionalSupport::Get()
{
  TextAbstraction::BidirectionalSupport bidirectionalSupportHandle;

  SingletonService service( SingletonService::Get() );
  if( service )
  {
    // Check whether the singleton is already created
    BaseHandle handle = service.GetSingleton( typeid( TextAbstraction::BidirectionalSupport ) );
    if(handle)
    {
      // If so, downcast the handle
      BidirectionalSupport* impl = dynamic_cast< Internal::BidirectionalSupport* >( handle.GetObjectPtr() );
      bidirectionalSupportHandle = TextAbstraction::BidirectionalSupport( impl );
    }
    else // create and register the object
    {
      bidirectionalSupportHandle = TextAbstraction::BidirectionalSupport( new BidirectionalSupport );
      service.Register( typeid( bidirectionalSupportHandle ), bidirectionalSupportHandle );
    }
  }

  return bidirectionalSupportHandle;
}

BidiInfoIndex BidirectionalSupport::CreateInfo( const Character* const paragraph,
                                                Length numberOfCharacters )
{
  CreatePlugin();

  return mPlugin->CreateInfo( paragraph,
                              numberOfCharacters );
}

void BidirectionalSupport::DestroyInfo( BidiInfoIndex bidiInfoIndex )
{
  CreatePlugin();

  mPlugin->DestroyInfo( bidiInfoIndex );
}

void BidirectionalSupport::Reorder( BidiInfoIndex bidiInfoIndex,
                                    CharacterIndex firstCharacterIndex,
                                    Length numberOfCharacters,
                                    CharacterIndex* visualToLogicalMap )
{
  CreatePlugin();

  mPlugin->Reorder( bidiInfoIndex,
                    firstCharacterIndex,
                    numberOfCharacters,
                    visualToLogicalMap );
}

bool BidirectionalSupport::GetMirroredText( Character* text,
                                            CharacterDirection* directions,
                                            Length numberOfCharacters )
{
  CreatePlugin();

  return mPlugin->GetMirroredText( text, directions, numberOfCharacters );
}

bool BidirectionalSupport::GetParagraphDirection( BidiInfoIndex bidiInfoIndex ) const
{
  if( !mPlugin )
  {
    return false;
  }

  return mPlugin->GetParagraphDirection( bidiInfoIndex );
}

void BidirectionalSupport::GetCharactersDirection( BidiInfoIndex bidiInfoIndex,
                                                   CharacterDirection* directions,
                                                   Length numberOfCharacters )
{
  CreatePlugin();

  mPlugin->GetCharactersDirection( bidiInfoIndex,
                                   directions,
                                   numberOfCharacters );
}

void BidirectionalSupport::CreatePlugin()
{
  if( !mPlugin )
  {
    mPlugin = new Plugin();
  }
}

} // namespace Internal

} // namespace TextAbstraction

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

// CLASS  HEADER
#include <dali/internal/text/text-abstraction/bidirectional-support-impl.h>

// INTERNAL INCLUDES
#include <dali/internal/system/common/singleton-service-impl.h>

// EXTERNAL INCLUDES
#include <memory.h>
#include <fribidi/fribidi.h>
#include <dali/integration-api/debug.h>

namespace Dali
{

namespace TextAbstraction
{

namespace Internal
{

namespace
{
  typedef unsigned char BidiDirection;

  // Internal charcter's direction.
  const BidiDirection LEFT_TO_RIGHT = 0u;
  const BidiDirection NEUTRAL = 1u;
  const BidiDirection RIGHT_TO_LEFT = 2u;

  /**
   * @param[in] paragraphDirection The FriBiDi paragraph's direction.
   *
   * @return Whether the paragraph is right to left.
   */
  bool GetBidiParagraphDirection( FriBidiParType paragraphDirection )
  {
    switch( paragraphDirection )
    {
      case FRIBIDI_PAR_RTL:  // Right-To-Left paragraph.
      case FRIBIDI_PAR_WRTL: // Weak Right To Left paragraph.
      {
        return true;
      }
      case FRIBIDI_PAR_LTR:  // Left-To-Right paragraph.
      case FRIBIDI_PAR_ON:   // DirectiOn-Neutral paragraph.
      case FRIBIDI_PAR_WLTR: // Weak Left To Right paragraph.
      {
        return false;
      }
    }

    return false;
  }

  BidiDirection GetBidiCharacterDirection( FriBidiCharType characterDirection )
  {
    switch( characterDirection )
    {
      case FRIBIDI_TYPE_LTR: // Left-To-Right letter.
      case FRIBIDI_TYPE_EN:  // European Numeral.
      case FRIBIDI_TYPE_AN:  // Arabic Numeral.
      case FRIBIDI_TYPE_ES:  // European number Separator.
      case FRIBIDI_TYPE_ET:  // European number Terminator.
      {
        return LEFT_TO_RIGHT;
      }
      case FRIBIDI_TYPE_RTL: // Right-To-Left letter.
      case FRIBIDI_TYPE_AL:  // Arabic Letter.
      {
        return RIGHT_TO_LEFT;
      }
    }

    return NEUTRAL;
  }
}

struct BidirectionalSupport::Plugin
{
  /**
   * Stores bidirectional info per paragraph.
   */
  struct BidirectionalInfo
  {
    FriBidiCharType* characterTypes;      ///< The type of each character (right, left, neutral, ...)
    FriBidiLevel*    embeddedLevels;      ///< Embedded levels.
    FriBidiParType   paragraphDirection;  ///< The paragraph's direction.
  };

  Plugin()
  : mParagraphBidirectionalInfo(),
    mFreeIndices()
  {}

  ~Plugin()
  {
    // free all resources.
    for( Vector<BidirectionalInfo*>::Iterator it = mParagraphBidirectionalInfo.Begin(),
           endIt = mParagraphBidirectionalInfo.End();
         it != endIt;
         ++it )
    {
      BidirectionalInfo* info = *it;

      free( info->embeddedLevels );
      free( info->characterTypes );
      delete info;
    }
  }

  BidiInfoIndex CreateInfo( const Character* const paragraph,
                            Length numberOfCharacters )
  {
    // Reserve memory for the paragraph's bidirectional info.
    BidirectionalInfo* bidirectionalInfo = new BidirectionalInfo();

    bidirectionalInfo->characterTypes = reinterpret_cast<FriBidiCharType*>( malloc( numberOfCharacters * sizeof( FriBidiCharType ) ) );
    if( !bidirectionalInfo->characterTypes )
    {
      delete bidirectionalInfo;
      return 0;
    }

    bidirectionalInfo->embeddedLevels = reinterpret_cast<FriBidiLevel*>( malloc( numberOfCharacters * sizeof( FriBidiLevel ) ) );
    if( !bidirectionalInfo->embeddedLevels )
    {
      free( bidirectionalInfo->characterTypes );
      delete bidirectionalInfo;
      return 0;
    }

    // Retrieve the type of each character..
    fribidi_get_bidi_types( paragraph, numberOfCharacters, bidirectionalInfo->characterTypes );

    // Retrieve the paragraph's direction.
    bidirectionalInfo->paragraphDirection = fribidi_get_par_direction( bidirectionalInfo->characterTypes, numberOfCharacters );

    // Retrieve the embedding levels.
    if (fribidi_get_par_embedding_levels( bidirectionalInfo->characterTypes, numberOfCharacters, &bidirectionalInfo->paragraphDirection, bidirectionalInfo->embeddedLevels ) == 0)
    {
      free( bidirectionalInfo->characterTypes );
      delete bidirectionalInfo;
      return 0;
    }

    // Store the bidirectional info and return the index.
    BidiInfoIndex index = 0u;
    if( 0u != mFreeIndices.Count() )
    {
      Vector<BidiInfoIndex>::Iterator it = mFreeIndices.End() - 1u;

      index = *it;

      mFreeIndices.Remove( it );

      *( mParagraphBidirectionalInfo.Begin() + index ) = bidirectionalInfo;
    }
    else
    {
      index = static_cast<BidiInfoIndex>( mParagraphBidirectionalInfo.Count() );

      mParagraphBidirectionalInfo.PushBack( bidirectionalInfo );
    }

    return index;
  }

  void DestroyInfo( BidiInfoIndex bidiInfoIndex )
  {
    if( bidiInfoIndex >= mParagraphBidirectionalInfo.Count() )
    {
      return;
    }

    // Retrieve the paragraph's bidirectional info.
    Vector<BidirectionalInfo*>::Iterator it = mParagraphBidirectionalInfo.Begin() + bidiInfoIndex;
    BidirectionalInfo* bidirectionalInfo = *it;

    if( NULL != bidirectionalInfo )
    {
      // Free resources and destroy the container.
      free( bidirectionalInfo->embeddedLevels );
      free( bidirectionalInfo->characterTypes );
      delete bidirectionalInfo;

      *it = NULL;
    }

    // Add the index to the free indices vector.
    mFreeIndices.PushBack( bidiInfoIndex );
  }

  void Reorder( BidiInfoIndex bidiInfoIndex,
                CharacterIndex firstCharacterIndex,
                Length numberOfCharacters,
                CharacterIndex* visualToLogicalMap )
  {
    const FriBidiFlags flags = FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC;

    // Retrieve the paragraph's bidirectional info.
    const BidirectionalInfo* const bidirectionalInfo = *( mParagraphBidirectionalInfo.Begin() + bidiInfoIndex );

    // Initialize the visual to logical mapping table to the identity. Otherwise fribidi_reorder_line fails to retrieve a valid mapping table.
    for( CharacterIndex index = 0u; index < numberOfCharacters; ++index )
    {
      visualToLogicalMap[ index ] = index;
    }

    // Copy embedded levels as fribidi_reorder_line() may change them.
    const uint32_t embeddedLevelsSize = numberOfCharacters * sizeof( FriBidiLevel );
    FriBidiLevel* embeddedLevels = reinterpret_cast<FriBidiLevel*>( malloc( embeddedLevelsSize ) );
    if( embeddedLevels )
    {
      memcpy( embeddedLevels, bidirectionalInfo->embeddedLevels + firstCharacterIndex,  embeddedLevelsSize );

      // Reorder the line.
      if (fribidi_reorder_line( flags,
                                bidirectionalInfo->characterTypes + firstCharacterIndex,
                                numberOfCharacters,
                                0u,
                                bidirectionalInfo->paragraphDirection,
                                embeddedLevels,
                                NULL,
                                reinterpret_cast<FriBidiStrIndex*>( visualToLogicalMap ) ) == 0)
      {
        DALI_LOG_ERROR("fribidi_reorder_line is failed\n");
      }

      // Free resources.
      free( embeddedLevels );
    }
  }

  bool GetMirroredText( Character* text,
                        CharacterDirection* directions,
                        Length numberOfCharacters ) const
  {
    bool updated = false;

    for( CharacterIndex index = 0u; index < numberOfCharacters; ++index )
    {
      // Get a reference to the character inside the text.
      Character& character = *( text + index );

      // Retrieve the mirrored character.
      FriBidiChar mirroredCharacter = character;
      bool mirrored = false;
      if( *( directions + index ) )
      {
        mirrored = fribidi_get_mirror_char( character, &mirroredCharacter );
      }
      updated = updated || mirrored;

      // Update the character inside the text.
      character = mirroredCharacter;
    }

    return updated;
  }

  bool GetParagraphDirection( BidiInfoIndex bidiInfoIndex ) const
  {
    // Retrieve the paragraph's bidirectional info.
    const BidirectionalInfo* const bidirectionalInfo = *( mParagraphBidirectionalInfo.Begin() + bidiInfoIndex );

    return GetBidiParagraphDirection( bidirectionalInfo->paragraphDirection );
  }

  void GetCharactersDirection( BidiInfoIndex bidiInfoIndex,
                               CharacterDirection* directions,
                               Length numberOfCharacters )
  {
    const BidirectionalInfo* const bidirectionalInfo = *( mParagraphBidirectionalInfo.Begin() + bidiInfoIndex );

    const CharacterDirection paragraphDirection = GetBidiParagraphDirection( bidirectionalInfo->paragraphDirection );
    CharacterDirection previousDirection = paragraphDirection;

    for( CharacterIndex index = 0u; index < numberOfCharacters; ++index )
    {
      CharacterDirection& characterDirection = *( directions + index );
      CharacterDirection nextDirection = false;

      characterDirection = false;

      // Get the bidi direction.
      const BidiDirection bidiDirection = GetBidiCharacterDirection( *( bidirectionalInfo->characterTypes + index ) );

      if( RIGHT_TO_LEFT == bidiDirection )
      {
        characterDirection = true;
        nextDirection = true;
      }
      else if( NEUTRAL == bidiDirection )
      {
        // For neutral characters it check's the next and previous directions.
        // If they are equals set that direction. If they are not, sets the paragraph's direction.
        // If there is no next, sets the paragraph's direction.
        nextDirection = paragraphDirection;

        // Look for the next non-neutral character.
        Length nextIndex = index + 1u;
        for( ; nextIndex < numberOfCharacters; ++nextIndex )
        {
          BidiDirection nextBidiDirection = GetBidiCharacterDirection( *( bidirectionalInfo->characterTypes + nextIndex ) );
          if( nextBidiDirection != NEUTRAL )
          {
            nextDirection = RIGHT_TO_LEFT == nextBidiDirection;
            break;
          }
        }

        // Calculate the direction for all the neutral characters.
        characterDirection = previousDirection == nextDirection ? previousDirection : paragraphDirection;

        // Set the direction to all the neutral characters.
        // The indices from currentIndex + 1u to nextIndex - 1u are neutral characters.
        ++index;

        for( ; index < nextIndex; ++index )
        {
          CharacterDirection& nextCharacterDirection = *( directions + index );
          nextCharacterDirection = characterDirection;
        }

        // Set the direction of the next non-neutral character.
        if( nextIndex < numberOfCharacters )
        {
          *( directions + nextIndex ) = nextDirection;
        }
      }

      previousDirection = nextDirection;
    }
  }

  Vector<BidirectionalInfo*> mParagraphBidirectionalInfo; ///< Stores the bidirectional info per paragraph.
  Vector<BidiInfoIndex>      mFreeIndices;                ///< Stores indices of free positions in the bidirectional info vector.
};

BidirectionalSupport::BidirectionalSupport()
: mPlugin( NULL )
{
}

BidirectionalSupport::~BidirectionalSupport()
{
  delete mPlugin;
}

TextAbstraction::BidirectionalSupport BidirectionalSupport::Get()
{
  TextAbstraction::BidirectionalSupport bidirectionalSupportHandle;

  SingletonService service( SingletonService::Get() );
  if( service )
  {
    // Check whether the singleton is already created
    BaseHandle handle = service.GetSingleton( typeid( TextAbstraction::BidirectionalSupport ) );
    if(handle)
    {
      // If so, downcast the handle
      BidirectionalSupport* impl = dynamic_cast< Internal::BidirectionalSupport* >( handle.GetObjectPtr() );
      bidirectionalSupportHandle = TextAbstraction::BidirectionalSupport( impl );
    }
    else // create and register the object
    {
      bidirectionalSupportHandle = TextAbstraction::BidirectionalSupport( new BidirectionalSupport );
      service.Register( typeid( bidirectionalSupportHandle ), bidirectionalSupportHandle );
    }
  }

  return bidirectionalSupportHandle;
}

BidiInfoIndex BidirectionalSupport::CreateInfo( const Character* const paragraph,
                                                Length numberOfCharacters )
{
  CreatePlugin();

  return mPlugin->CreateInfo( paragraph,
                              numberOfCharacters );
}

void BidirectionalSupport::DestroyInfo( BidiInfoIndex bidiInfoIndex )
{
  CreatePlugin();

  mPlugin->DestroyInfo( bidiInfoIndex );
}

void BidirectionalSupport::Reorder( BidiInfoIndex bidiInfoIndex,
                                    CharacterIndex firstCharacterIndex,
                                    Length numberOfCharacters,
                                    CharacterIndex* visualToLogicalMap )
{
  CreatePlugin();

  mPlugin->Reorder( bidiInfoIndex,
                    firstCharacterIndex,
                    numberOfCharacters,
                    visualToLogicalMap );
}

bool BidirectionalSupport::GetMirroredText( Character* text,
                                            CharacterDirection* directions,
                                            Length numberOfCharacters )
{
  CreatePlugin();

  return mPlugin->GetMirroredText( text, directions, numberOfCharacters );
}

bool BidirectionalSupport::GetParagraphDirection( BidiInfoIndex bidiInfoIndex ) const
{
  if( !mPlugin )
  {
    return false;
  }

  return mPlugin->GetParagraphDirection( bidiInfoIndex );
}

void BidirectionalSupport::GetCharactersDirection( BidiInfoIndex bidiInfoIndex,
                                                   CharacterDirection* directions,
                                                   Length numberOfCharacters )
{
  CreatePlugin();

  mPlugin->GetCharactersDirection( bidiInfoIndex,
                                   directions,
                                   numberOfCharacters );
}

void BidirectionalSupport::CreatePlugin()
{
  if( !mPlugin )
  {
    mPlugin = new Plugin();
  }
}

} // namespace Internal

} // namespace TextAbstraction

} // namespace Dali
<|endoftext|>


<commit_before>#include <vector>
#include <sstream> // std::ostringstream
#include <boost/test/unit_test.hpp>
#include <boost/test/parameterized_test.hpp>
#include <boost/program_options.hpp>
#include <boost/foreach.hpp>

#include <glog/logging.h>

#include "IndexerTestFixture.h"
#include "t_IndexReader.h"
#include "t_BarrelsInfo.h"
#include "t_TermDocFreqs.h"

using namespace std;
using namespace boost::unit_test;
namespace po = boost::program_options;

namespace {
/** the parameters for each test case */
const IndexerTestConfig INDEXER_TEST_CONFIGS[] = {
    {0, 1, 1, "default", true},
    {1, 1, 1, "realtime", true},
    {2, 5, 3, "default", true},
    {3, 5, 3, "realtime", true},
    {4, 10, 6, "default", true},
    {5, 10, 6, "realtime", true},
    {6, 100, 23, "default", true},
    {7, 100, 23, "realtime", true},
    {8, 1000, 10, "default", true},
    {9, 1000, 10, "realtime", true},
};

/** the parameter number */
const int INDEXER_TEST_CONFIG_NUM = sizeof(INDEXER_TEST_CONFIGS) / sizeof(IndexerTestConfig);

/** if the command options of "--run_config_list" or "--run_config_range" are not specified, run configs of range [0, DEFAULT_CONFIG_RANGE] */
const int DEFAULT_CONFIG_RANGE = 5;

/** the command option to specify a list of config numbers, such as "--run_config_list 0 1 2 3" */
const char* OPTION_CONFIG_LIST = "run_config_list";

/** the command option to specify a range of config numbers, such as "--run_config_range 4 7" */
const char* OPTION_CONFIG_RANGE = "run_config_range";
}

/**
 * Load command line option "run_config_list" or "run_config_range" to decide which configs to run.
 * @param configVec the loaded configs to run
 * @return true for load success, false for fail
 */
bool loadConfigOption(vector<IndexerTestConfig>& configVec)
{
    try
    {
        vector<int> runConfigListVec;
        vector<int> runConfigRangeVec;
        po::options_description desp("Allowed options");
        desp.add_options()
            (OPTION_CONFIG_LIST, po::value<std::vector<int> >(&runConfigListVec)->multitoken(), "specify the list of config parameters to run")
            (OPTION_CONFIG_RANGE, po::value<std::vector<int> >(&runConfigRangeVec)->multitoken(), "specify the range of config parameters to run")
            ;

        po::options_description cmdline_options;
        cmdline_options.add(desp);

        po::variables_map vm;
        store(po::command_line_parser(framework::master_test_suite().argc, framework::master_test_suite().argv).options(cmdline_options).run(), vm);
        po::notify(vm);

        if(vm.count(OPTION_CONFIG_LIST))
        {
            cout << OPTION_CONFIG_LIST << ": ";
            BOOST_FOREACH(int i, runConfigListVec)
            {
                cout << i << ", ";
                if(i >= 0 && i < INDEXER_TEST_CONFIG_NUM)
                    configVec.push_back(INDEXER_TEST_CONFIGS[i]);
                else
                {
                    cout << endl;
                    ostringstream oss;
                    oss << "unknown config number " << i;
                    throw std::runtime_error(oss.str());
                }
            }
            cout << endl;
        }
        else if(vm.count(OPTION_CONFIG_RANGE))
        {
            cout << OPTION_CONFIG_RANGE << ": ";
            BOOST_FOREACH(int i, runConfigRangeVec)
                cout << i << ", ";
            cout << endl;

            if(runConfigRangeVec.size() == 2
                    && runConfigRangeVec[0] >= 0
                    && runConfigRangeVec[0] <= runConfigRangeVec[1]
                    && runConfigRangeVec[1] < INDEXER_TEST_CONFIG_NUM)
                configVec.insert(configVec.begin(), INDEXER_TEST_CONFIGS + runConfigRangeVec[0],
                                                    INDEXER_TEST_CONFIGS + runConfigRangeVec[1] + 1);
            else
            {
                ostringstream oss;
                oss << "the config range is invalid";
                throw std::runtime_error(oss.str());
            }
        }
        else
        {
            cout << "run default configs range: [0, " << DEFAULT_CONFIG_RANGE << "]" << endl;
            configVec.insert(configVec.begin(), INDEXER_TEST_CONFIGS,
                                                INDEXER_TEST_CONFIGS + DEFAULT_CONFIG_RANGE + 1);
        }
    }
    catch(std::exception& e)
    {
        cerr << "error: " << e.what() << endl;
        return false;
    }

    return true;
}

/**
 * Boost test module initialize function, it regsiters the test cases to run.
 * @return true for success, false for failure
 */
bool my_init_unit_test()
{
    vector<IndexerTestConfig> configVec;
    if(! loadConfigOption(configVec))
        return false;

    const IndexerTestConfig* const pConfigStart = &configVec[0];
    const IndexerTestConfig* const pConfigEnd = pConfigStart + configVec.size();

    test_suite* tsIndexManager = BOOST_TEST_SUITE("index_manager");

    test_suite* tsIndexReader = BOOST_TEST_SUITE("t_IndexReader");
    tsIndexReader->add(BOOST_PARAM_TEST_CASE(&t_IndexReader::index, pConfigStart, pConfigEnd));
    tsIndexReader->add(BOOST_PARAM_TEST_CASE(&t_IndexReader::update, pConfigStart, pConfigEnd));
    tsIndexReader->add(BOOST_PARAM_TEST_CASE(&t_IndexReader::remove, pConfigStart, pConfigEnd));
    tsIndexManager->add(tsIndexReader);

    test_suite* tsBarrelsInfo = BOOST_TEST_SUITE("t_BarrelsInfo");
    tsBarrelsInfo->add(BOOST_PARAM_TEST_CASE(&t_BarrelsInfo::index, pConfigStart, pConfigEnd));
    tsBarrelsInfo->add(BOOST_PARAM_TEST_CASE(&t_BarrelsInfo::optimize, pConfigStart, pConfigEnd));
    tsBarrelsInfo->add(BOOST_PARAM_TEST_CASE(&t_BarrelsInfo::createAfterOptimize, pConfigStart, pConfigEnd));
    tsBarrelsInfo->add(BOOST_PARAM_TEST_CASE(&t_BarrelsInfo::pauseResumeMerge, pConfigStart, pConfigEnd));
    tsIndexManager->add(tsBarrelsInfo);

    test_suite* tsTermDocFreqs = BOOST_TEST_SUITE("t_TermDocFreqs");
    tsTermDocFreqs->add(BOOST_PARAM_TEST_CASE(&t_TermDocFreqs::index, pConfigStart, pConfigEnd));
    tsTermDocFreqs->add(BOOST_PARAM_TEST_CASE(&t_TermDocFreqs::update, pConfigStart, pConfigEnd));
    tsTermDocFreqs->add(BOOST_PARAM_TEST_CASE(&t_TermDocFreqs::remove, pConfigStart, pConfigEnd));
    tsIndexManager->add(tsTermDocFreqs);

    framework::master_test_suite().add(tsIndexManager);
    return true;
}

int main( int argc, char* argv[] )
{
    google::InitGoogleLogging(argv[0]);

    return ::boost::unit_test::unit_test_main( &my_init_unit_test, argc, argv );
}
<commit_msg>in "t_integration_Indexer", update master test suite name to "index_manager".<commit_after>#include <vector>
#include <sstream> // std::ostringstream
#include <boost/test/unit_test.hpp>
#include <boost/test/parameterized_test.hpp>
#include <boost/program_options.hpp>
#include <boost/foreach.hpp>

#include <glog/logging.h>

#include "IndexerTestFixture.h"
#include "t_IndexReader.h"
#include "t_BarrelsInfo.h"
#include "t_TermDocFreqs.h"

using namespace std;
using namespace boost::unit_test;
namespace po = boost::program_options;

namespace {
/** the parameters for each test case */
const IndexerTestConfig INDEXER_TEST_CONFIGS[] = {
    {0, 1, 1, "default", true},
    {1, 1, 1, "realtime", true},
    {2, 5, 3, "default", true},
    {3, 5, 3, "realtime", true},
    {4, 10, 6, "default", true},
    {5, 10, 6, "realtime", true},
    {6, 100, 23, "default", true},
    {7, 100, 23, "realtime", true},
    {8, 1000, 10, "default", true},
    {9, 1000, 10, "realtime", true},
};

/** the parameter number */
const int INDEXER_TEST_CONFIG_NUM = sizeof(INDEXER_TEST_CONFIGS) / sizeof(IndexerTestConfig);

/** if the command options of "--run_config_list" or "--run_config_range" are not specified, run configs of range [0, DEFAULT_CONFIG_RANGE] */
const int DEFAULT_CONFIG_RANGE = 5;

/** the command option to specify a list of config numbers, such as "--run_config_list 0 1 2 3" */
const char* OPTION_CONFIG_LIST = "run_config_list";

/** the command option to specify a range of config numbers, such as "--run_config_range 4 7" */
const char* OPTION_CONFIG_RANGE = "run_config_range";
}

/**
 * Load command line option "run_config_list" or "run_config_range" to decide which configs to run.
 * @param configVec the loaded configs to run
 * @return true for load success, false for fail
 */
bool loadConfigOption(vector<IndexerTestConfig>& configVec)
{
    try
    {
        vector<int> runConfigListVec;
        vector<int> runConfigRangeVec;
        po::options_description desp("Allowed options");
        desp.add_options()
            (OPTION_CONFIG_LIST, po::value<std::vector<int> >(&runConfigListVec)->multitoken(), "specify the list of config parameters to run")
            (OPTION_CONFIG_RANGE, po::value<std::vector<int> >(&runConfigRangeVec)->multitoken(), "specify the range of config parameters to run")
            ;

        po::options_description cmdline_options;
        cmdline_options.add(desp);

        po::variables_map vm;
        store(po::command_line_parser(framework::master_test_suite().argc, framework::master_test_suite().argv).options(cmdline_options).run(), vm);
        po::notify(vm);

        if(vm.count(OPTION_CONFIG_LIST))
        {
            cout << OPTION_CONFIG_LIST << ": ";
            BOOST_FOREACH(int i, runConfigListVec)
            {
                cout << i << ", ";
                if(i >= 0 && i < INDEXER_TEST_CONFIG_NUM)
                    configVec.push_back(INDEXER_TEST_CONFIGS[i]);
                else
                {
                    cout << endl;
                    ostringstream oss;
                    oss << "unknown config number " << i;
                    throw std::runtime_error(oss.str());
                }
            }
            cout << endl;
        }
        else if(vm.count(OPTION_CONFIG_RANGE))
        {
            cout << OPTION_CONFIG_RANGE << ": ";
            BOOST_FOREACH(int i, runConfigRangeVec)
                cout << i << ", ";
            cout << endl;

            if(runConfigRangeVec.size() == 2
                    && runConfigRangeVec[0] >= 0
                    && runConfigRangeVec[0] <= runConfigRangeVec[1]
                    && runConfigRangeVec[1] < INDEXER_TEST_CONFIG_NUM)
                configVec.insert(configVec.begin(), INDEXER_TEST_CONFIGS + runConfigRangeVec[0],
                                                    INDEXER_TEST_CONFIGS + runConfigRangeVec[1] + 1);
            else
            {
                ostringstream oss;
                oss << "the config range is invalid";
                throw std::runtime_error(oss.str());
            }
        }
        else
        {
            cout << "run default configs range: [0, " << DEFAULT_CONFIG_RANGE << "]" << endl;
            configVec.insert(configVec.begin(), INDEXER_TEST_CONFIGS,
                                                INDEXER_TEST_CONFIGS + DEFAULT_CONFIG_RANGE + 1);
        }
    }
    catch(std::exception& e)
    {
        cerr << "error: " << e.what() << endl;
        return false;
    }

    return true;
}

/**
 * Boost test module initialize function, it regsiters the test cases to run.
 * @return true for success, false for failure
 */
bool my_init_unit_test()
{
    vector<IndexerTestConfig> configVec;
    if(! loadConfigOption(configVec))
        return false;

    framework::master_test_suite().p_name.value = "index_manager";

    const IndexerTestConfig* const pConfigStart = &configVec[0];
    const IndexerTestConfig* const pConfigEnd = pConfigStart + configVec.size();

    test_suite* tsIndexReader = BOOST_TEST_SUITE("t_IndexReader");
    tsIndexReader->add(BOOST_PARAM_TEST_CASE(&t_IndexReader::index, pConfigStart, pConfigEnd));
    tsIndexReader->add(BOOST_PARAM_TEST_CASE(&t_IndexReader::update, pConfigStart, pConfigEnd));
    tsIndexReader->add(BOOST_PARAM_TEST_CASE(&t_IndexReader::remove, pConfigStart, pConfigEnd));
    framework::master_test_suite().add(tsIndexReader);

    test_suite* tsBarrelsInfo = BOOST_TEST_SUITE("t_BarrelsInfo");
    tsBarrelsInfo->add(BOOST_PARAM_TEST_CASE(&t_BarrelsInfo::index, pConfigStart, pConfigEnd));
    tsBarrelsInfo->add(BOOST_PARAM_TEST_CASE(&t_BarrelsInfo::optimize, pConfigStart, pConfigEnd));
    tsBarrelsInfo->add(BOOST_PARAM_TEST_CASE(&t_BarrelsInfo::createAfterOptimize, pConfigStart, pConfigEnd));
    tsBarrelsInfo->add(BOOST_PARAM_TEST_CASE(&t_BarrelsInfo::pauseResumeMerge, pConfigStart, pConfigEnd));
    framework::master_test_suite().add(tsBarrelsInfo);

    test_suite* tsTermDocFreqs = BOOST_TEST_SUITE("t_TermDocFreqs");
    tsTermDocFreqs->add(BOOST_PARAM_TEST_CASE(&t_TermDocFreqs::index, pConfigStart, pConfigEnd));
    tsTermDocFreqs->add(BOOST_PARAM_TEST_CASE(&t_TermDocFreqs::update, pConfigStart, pConfigEnd));
    tsTermDocFreqs->add(BOOST_PARAM_TEST_CASE(&t_TermDocFreqs::remove, pConfigStart, pConfigEnd));
    framework::master_test_suite().add(tsTermDocFreqs);

    return true;
}

int main( int argc, char* argv[] )
{
    google::InitGoogleLogging(argv[0]);

    return ::boost::unit_test::unit_test_main( &my_init_unit_test, argc, argv );
}
<|endoftext|>