<commit_before>//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

#include "RuntimePlatformAgnosticPch.h"

#ifdef INTL_ICU

#include "Common.h"
#include "ChakraPlatform.h"
#include "Intl.h"

#define U_STATIC_IMPLEMENTATION
#define U_SHOW_CPLUSPLUS_API 0
#pragma warning(push)
#pragma warning(disable:4995) // deprecation warning
#include <unicode/uloc.h>
#include <unicode/numfmt.h>
//#include <unicode/decimfmt.h>
#pragma warning(pop)

namespace PlatformAgnostic
{
namespace Intl
{
    using namespace PlatformAgnostic::Resource;

    /*
    // NOTE: cannot return non-pointer instance of a pure virtual class, so move ctor and move asgn will not be applicable.

    // TODO (doilij): Remove if unused
    template <typename T>
    inline PlatformAgnosticIntlObject<T> PlatformAgnosticIntlObject<T>::operator=(PlatformAgnosticIntlObject<T> &&other)
    {
        // Only move if the object is different.
        if (this != &other)
        {
            if (this->intlObject)
            {
                // REVIEW (doilij): Ensure that this statement is true in practice, or delete this assert.
                AssertMsg(false, "FinalizableIntlObject move assignment shouldn't need to delete other->intlObject because it should be initialized to nullptr: check usage.");
                delete this->intlObject;
            }

            this->move(other);
        }

        return *this;
    }
    */

    bool IsWellFormedLanguageTag(_In_z_ const char16 *languageTag, _In_ const charcount_t cch)
    {
        // Allocate memory for the UTF8 output buffer. Need 3 bytes for each (code point + null) to satisfy SAL.
        const size_t inputLangTagUtf8SizeAllocated = AllocSizeMath::Mul(AllocSizeMath::Add(cch, 1), 3);
        // REVIEW (doilij): not perf critical so I used HeapNewArrayZ to zero-out the allocated array
        utf8char_t *inputLangTagUtf8 = HeapNewArrayZ(utf8char_t, inputLangTagUtf8SizeAllocated);
        if (!inputLangTagUtf8)
        {
            AssertOrFailFastMsg(false, "OOM: HeapNewArrayZ failed to allocate.");
        }
        const size_t inputLangTagUtf8SizeActual = utf8::EncodeIntoAndNullTerminate(inputLangTagUtf8, languageTag, cch);

        bool success = false;
        UErrorCode error = UErrorCode::U_ZERO_ERROR;

        // REVIEW (doilij): should we / do we need to zero these stack-allocated arrays?
        char icuLocaleId[ULOC_FULLNAME_CAPACITY] = { 0 };
        char icuLangTag[ULOC_FULLNAME_CAPACITY] = { 0 };
        int32_t parsedLength = 0;
        int32_t forLangTagResultLength = 0;
        int32_t toLangTagResultLength = 0;

        // Convert input language tag to a locale ID for use in uloc_toLanguageTag API.
        forLangTagResultLength = uloc_forLanguageTag(reinterpret_cast<const char *>(inputLangTagUtf8),
            icuLocaleId, ULOC_FULLNAME_CAPACITY, &parsedLength, &error);
        success = (forLangTagResultLength > 0) && (parsedLength > 0) &&
            U_SUCCESS(error) && ((size_t)parsedLength == inputLangTagUtf8SizeActual);
        if (!success)
        {
            goto cleanup;
        }

        toLangTagResultLength = uloc_toLanguageTag(icuLocaleId, icuLangTag, ULOC_FULLNAME_CAPACITY, TRUE, &error);
        success = toLangTagResultLength && U_SUCCESS(error);
        if (!success)
        {
            if (error == UErrorCode::U_ILLEGAL_ARGUMENT_ERROR)
            {
                AssertMsg(false, "uloc_toLanguageTag: error U_ILLEGAL_ARGUMENT_ERROR");
            }
            else
            {
                AssertMsg(false, "uloc_toLanguageTag: unexpected error (besides U_ILLEGAL_ARGUMENT_ERROR)");
            }

            goto cleanup;
        }

    cleanup:
        HeapDeleteArray(inputLangTagUtf8SizeAllocated, inputLangTagUtf8);
        inputLangTagUtf8 = nullptr;
        return success;
    }

    HRESULT NormalizeLanguageTag(_In_z_ const char16 *languageTag, _In_ const charcount_t cch,
        _Out_ char16 *normalized, _Out_ size_t *normalizedLength)
    {
        // Allocate memory for the UTF8 output buffer. Need 3 bytes for each (code point + null) to satisfy SAL.
        const size_t inputLangTagUtf8SizeAllocated = AllocSizeMath::Mul(AllocSizeMath::Add(cch, 1), 3);
        // REVIEW (doilij): not perf critical so I used HeapNewArrayZ to zero-out the allocated array
        utf8char_t *inputLangTagUtf8 = HeapNewArrayZ(utf8char_t, inputLangTagUtf8SizeAllocated);
        if (!inputLangTagUtf8)
        {
            AssertOrFailFastMsg(false, "OOM: HeapNewArrayZ failed to allocate.");
        }
        const size_t inputLangTagUtf8SizeActual = utf8::EncodeIntoAndNullTerminate(inputLangTagUtf8, languageTag, cch);

        // REVIEW (doilij): should we / do we need to zero these stack-allocated arrays?
        char icuLocaleId[ULOC_FULLNAME_CAPACITY] = { 0 };
        char icuLangTag[ULOC_FULLNAME_CAPACITY] = { 0 };
        bool success = false;
        UErrorCode error = UErrorCode::U_ZERO_ERROR;
        int32_t parsedLength = 0;
        int32_t forLangTagResultLength = 0;
        int32_t toLangTagResultLength = 0;

        // Convert input language tag to a locale ID for use in uloc_toLanguageTag API.
        forLangTagResultLength = uloc_forLanguageTag(reinterpret_cast<const char *>(inputLangTagUtf8), icuLocaleId, ULOC_FULLNAME_CAPACITY, &parsedLength, &error);
        success = forLangTagResultLength && parsedLength && U_SUCCESS(error);
        if (!success)
        {
            AssertMsg(false, "uloc_forLanguageTag failed");
            goto cleanup;
        }

        // Try to convert icuLocaleId (locale ID version of input locale string) to BCP47 language tag, using strict checks
        toLangTagResultLength = uloc_toLanguageTag(icuLocaleId, icuLangTag, ULOC_FULLNAME_CAPACITY, TRUE, &error);
        success = toLangTagResultLength && U_SUCCESS(error);
        if (!success)
        {
            if (error == UErrorCode::U_ILLEGAL_ARGUMENT_ERROR)
            {
                AssertMsg(false, "uloc_toLanguageTag: error U_ILLEGAL_ARGUMENT_ERROR");
            }
            else
            {
                AssertMsg(false, "uloc_toLanguageTag: unexpected error (besides U_ILLEGAL_ARGUMENT_ERROR)");
            }

            goto cleanup;
        }

        *normalizedLength = utf8::DecodeUnitsIntoAndNullTerminateNoAdvance(normalized,
            reinterpret_cast<const utf8char_t *>(icuLangTag), reinterpret_cast<utf8char_t *>(icuLangTag + toLangTagResultLength), utf8::doDefault);

    cleanup:
        HeapDeleteArray(inputLangTagUtf8SizeAllocated, inputLangTagUtf8);
        inputLangTagUtf8 = nullptr;
        return success ? S_OK : E_INVALIDARG;
    }

    // REVIEW (doilij): Is scriptContext needed as a param here?
    int32_t GetCurrencyFractionDigits(_In_z_ const char16 * currencyCode)
    {
        UErrorCode error = UErrorCode::U_ZERO_ERROR;
        const UChar *uCurrencyCode = (const UChar *)currencyCode; // UChar, like char16, is guaranteed to be 2 bytes on all platforms.
        int32_t minFracDigits = 2; // REVIEW: fallback value is a good starting value here

                                   // Note: The number of fractional digits specified for a currency is not locale-dependent.
        icu::NumberFormat *nf = icu::NumberFormat::createCurrencyInstance(error); // using default locale
        if (U_FAILURE(error))
        {
#ifdef INTL_ICU_DEBUG
            if (error == UErrorCode::U_MISSING_RESOURCE_ERROR)
            {
                Output::Print(_u("EntryIntl_CurrencyDigits > icu::NumberFormat::createCurrencyInstance(error) > U_MISSING_RESOURCE_ERROR (%d)\n"), error);
            }
            else
            {
                Output::Print(_u("EntryIntl_CurrencyDigits > icu::NumberFormat::createCurrencyInstance(error) > UErrorCode (%d)\n"), error);
            }
#endif
            goto LReturn;
        }

        nf->setCurrency(uCurrencyCode, error);
        if (U_FAILURE(error))
        {
#ifdef INTL_ICU_DEBUG
            if (error == UErrorCode::U_MISSING_RESOURCE_ERROR)
            {
                Output::Print(_u("EntryIntl_CurrencyDigits > nf->setCurrency(uCurrencyCode (%s), error) > U_MISSING_RESOURCE_ERROR (%d)\n"), currencyCode, error);
            }
            else
            {
                Output::Print(_u("EntryIntl_CurrencyDigits > nf->setCurrency(uCurrencyCode (%s), error) > UErrorCode (%d)\n"), currencyCode, error);
            }
#endif
            goto LReturn;
        }

        minFracDigits = nf->getMinimumFractionDigits();

#ifdef INTL_ICU_DEBUG
        Output::Print(_u("EntryIntl_CurrencyDigits > nf->getMinimumFractionDigits() successful > returned (%d)\n"), minFracDigits);
#endif

    LReturn:
        // Since something failed, return "reasonable" default of 2 fractional digits (obviously won't be the case for some currencies like JPY).
        // REVIEW (doilij): What does the spec say to do if a currency is not supported?

#ifdef INTL_ICU_DEBUG
        Output::Print(_u("EntryIntl_CurrencyDigits > returning (%d)\n"), minFracDigits);
#endif

        return minFracDigits;
    }

    IPlatformAgnosticResource *CreateNumberFormat()
    {
        UErrorCode error = UErrorCode::U_ZERO_ERROR;
        // REVIEW (doilij): is allocating with `new` here okay?
        icu::NumberFormat *nf = icu::NumberFormat::createInstance(error);
        IPlatformAgnosticResource *obj = new PlatformAgnosticIntlObject<icu::NumberFormat>(nf);
        AssertMsg(U_SUCCESS(error), "Creating icu::NumberFormat failed");
        return obj;
    }

    template const char16 *FormatNumber<>(IPlatformAgnosticResource *formatter, int32 val);
    template const char16 *FormatNumber<>(IPlatformAgnosticResource *formatter, double val);

    template <typename T>
    const char16 *FormatNumber(IPlatformAgnosticResource *formatter, T val)
    {
        icu::UnicodeString result;

        auto *formatterHolder = (PlatformAgnosticIntlObject<icu::NumberFormat> *)formatter;
        AssertOrFailFastMsg(formatterHolder, "Formatter did not hold an object of type `FinalizableIntlObject<icu::NumberFormat>`");
        icu::NumberFormat *numberFormatter = formatterHolder->GetInstance();

        numberFormatter->format(val, result);
        int32_t length = result.length();
        char16 *ret = new char16[length + 1];
        result.extract(0, length, ret);
        ret[length] = 0;
        return ret;
    }
} // namespace Intl
} // namespace PlatformAgnostic

#endif // INTL_ICU
<commit_msg>Some xplat fixes.<commit_after>//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

#include "RuntimePlatformAgnosticPch.h"

#ifdef INTL_ICU

// REVIEW (doilij): Where are these defined that is safe to include in PlatformAgnostic?
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
#include "Codex/Utf8Codex.h"

#ifndef _WIN32
// REVIEW (doilij): The PCH allegedly defines enough stuff to get AssertMsg to work -- why was compile failing for this file?
#ifdef AssertMsg
#undef AssertMsg
#define AssertMsg(test, message)
#endif
#ifdef AssertOrFailFastMessage
#undef AssertOrFailFastMessage
#define AssertOrFailFastMessage(test, message)
#endif
#define DECLSPEC_GUARD_OVERFLOW
#endif // !_WIN32

#include "Common/MathUtil.h"
#include "Core/AllocSizeMath.h"

#include "Intl.h"

#define U_STATIC_IMPLEMENTATION
#define U_SHOW_CPLUSPLUS_API 0
#pragma warning(push)
#pragma warning(disable:4995) // deprecation warning
#include <unicode/uloc.h>
#include <unicode/numfmt.h>
//#include <unicode/decimfmt.h>
#pragma warning(pop)

namespace PlatformAgnostic
{
namespace Intl
{
    using namespace PlatformAgnostic::Resource;

    /*
    // NOTE: cannot return non-pointer instance of a pure virtual class, so move ctor and move asgn will not be applicable.

    // TODO (doilij): Remove if unused
    template <typename T>
    inline PlatformAgnosticIntlObject<T> PlatformAgnosticIntlObject<T>::operator=(PlatformAgnosticIntlObject<T> &&other)
    {
        // Only move if the object is different.
        if (this != &other)
        {
            if (this->intlObject)
            {
                // REVIEW (doilij): Ensure that this statement is true in practice, or delete this assert.
                AssertMsg(false, "FinalizableIntlObject move assignment shouldn't need to delete other->intlObject because it should be initialized to nullptr: check usage.");
                delete this->intlObject;
            }

            this->move(other);
        }

        return *this;
    }
    */

    bool IsWellFormedLanguageTag(_In_z_ const char16 *languageTag, _In_ const charcount_t cch)
    {
        // Allocate memory for the UTF8 output buffer. Need 3 bytes for each (code point + null) to satisfy SAL.
        const size_t inputLangTagUtf8SizeAllocated = AllocSizeMath::Mul(AllocSizeMath::Add(cch, 1), 3);
        // REVIEW (doilij): not perf critical so I used HeapNewArrayZ to zero-out the allocated array
        unsigned char *inputLangTagUtf8 = new unsigned char[inputLangTagUtf8SizeAllocated];
        if (!inputLangTagUtf8)
        {
            AssertOrFailFastMsg(false, "OOM: HeapNewArrayZ failed to allocate.");
        }
        const size_t inputLangTagUtf8SizeActual = utf8::EncodeIntoAndNullTerminate(inputLangTagUtf8, languageTag, cch);

        bool success = false;
        UErrorCode error = UErrorCode::U_ZERO_ERROR;

        // REVIEW (doilij): should we / do we need to zero these stack-allocated arrays?
        char icuLocaleId[ULOC_FULLNAME_CAPACITY] = { 0 };
        char icuLangTag[ULOC_FULLNAME_CAPACITY] = { 0 };
        int32_t parsedLength = 0;
        int32_t forLangTagResultLength = 0;
        int32_t toLangTagResultLength = 0;

        // Convert input language tag to a locale ID for use in uloc_toLanguageTag API.
        forLangTagResultLength = uloc_forLanguageTag(reinterpret_cast<const char *>(inputLangTagUtf8),
            icuLocaleId, ULOC_FULLNAME_CAPACITY, &parsedLength, &error);
        success = (forLangTagResultLength > 0) && (parsedLength > 0) &&
            U_SUCCESS(error) && ((size_t)parsedLength == inputLangTagUtf8SizeActual);
        if (!success)
        {
            goto cleanup;
        }

        toLangTagResultLength = uloc_toLanguageTag(icuLocaleId, icuLangTag, ULOC_FULLNAME_CAPACITY, TRUE, &error);
        success = toLangTagResultLength && U_SUCCESS(error);
        if (!success)
        {
            if (error == UErrorCode::U_ILLEGAL_ARGUMENT_ERROR)
            {
                AssertMsg(false, "uloc_toLanguageTag: error U_ILLEGAL_ARGUMENT_ERROR");
            }
            else
            {
                AssertMsg(false, "uloc_toLanguageTag: unexpected error (besides U_ILLEGAL_ARGUMENT_ERROR)");
            }

            goto cleanup;
        }

    cleanup:
        delete[] inputLangTagUtf8;
        inputLangTagUtf8 = nullptr;
        return success;
    }

    HRESULT NormalizeLanguageTag(_In_z_ const char16 *languageTag, _In_ const charcount_t cch,
        _Out_ char16 *normalized, _Out_ size_t *normalizedLength)
    {
        // Allocate memory for the UTF8 output buffer. Need 3 bytes for each (code point + null) to satisfy SAL.
        const size_t inputLangTagUtf8SizeAllocated = AllocSizeMath::Mul(AllocSizeMath::Add(cch, 1), 3);
        // REVIEW (doilij): not perf critical so I used HeapNewArrayZ to zero-out the allocated array
        unsigned char *inputLangTagUtf8 = new unsigned char[inputLangTagUtf8SizeAllocated];
        if (!inputLangTagUtf8)
        {
            AssertOrFailFastMsg(false, "OOM: HeapNewArrayZ failed to allocate.");
        }
        const size_t inputLangTagUtf8SizeActual = utf8::EncodeIntoAndNullTerminate(inputLangTagUtf8, languageTag, cch);

        // REVIEW (doilij): should we / do we need to zero these stack-allocated arrays?
        char icuLocaleId[ULOC_FULLNAME_CAPACITY] = { 0 };
        char icuLangTag[ULOC_FULLNAME_CAPACITY] = { 0 };
        bool success = false;
        UErrorCode error = UErrorCode::U_ZERO_ERROR;
        int32_t parsedLength = 0;
        int32_t forLangTagResultLength = 0;
        int32_t toLangTagResultLength = 0;

        // Convert input language tag to a locale ID for use in uloc_toLanguageTag API.
        forLangTagResultLength = uloc_forLanguageTag(reinterpret_cast<const char *>(inputLangTagUtf8), icuLocaleId, ULOC_FULLNAME_CAPACITY, &parsedLength, &error);
        success = forLangTagResultLength && parsedLength && U_SUCCESS(error);
        if (!success)
        {
            AssertMsg(false, "uloc_forLanguageTag failed");
            goto cleanup;
        }

        // Try to convert icuLocaleId (locale ID version of input locale string) to BCP47 language tag, using strict checks
        toLangTagResultLength = uloc_toLanguageTag(icuLocaleId, icuLangTag, ULOC_FULLNAME_CAPACITY, TRUE, &error);
        success = toLangTagResultLength && U_SUCCESS(error);
        if (!success)
        {
            if (error == UErrorCode::U_ILLEGAL_ARGUMENT_ERROR)
            {
                AssertMsg(false, "uloc_toLanguageTag: error U_ILLEGAL_ARGUMENT_ERROR");
            }
            else
            {
                AssertMsg(false, "uloc_toLanguageTag: unexpected error (besides U_ILLEGAL_ARGUMENT_ERROR)");
            }

            goto cleanup;
        }

        *normalizedLength = utf8::DecodeUnitsIntoAndNullTerminateNoAdvance(normalized,
            reinterpret_cast<const utf8char_t *>(icuLangTag), reinterpret_cast<utf8char_t *>(icuLangTag + toLangTagResultLength), utf8::doDefault);

    cleanup:
        delete[] inputLangTagUtf8;
        inputLangTagUtf8 = nullptr;
        return success ? S_OK : E_INVALIDARG;
    }

    // REVIEW (doilij): Is scriptContext needed as a param here?
    int32_t GetCurrencyFractionDigits(_In_z_ const char16 * currencyCode)
    {
        UErrorCode error = UErrorCode::U_ZERO_ERROR;
        const UChar *uCurrencyCode = (const UChar *)currencyCode; // UChar, like char16, is guaranteed to be 2 bytes on all platforms.
        int32_t minFracDigits = 2; // REVIEW: fallback value is a good starting value here

                                   // Note: The number of fractional digits specified for a currency is not locale-dependent.
        icu::NumberFormat *nf = icu::NumberFormat::createCurrencyInstance(error); // using default locale
        if (U_FAILURE(error))
        {
#ifdef INTL_ICU_DEBUG
            if (error == UErrorCode::U_MISSING_RESOURCE_ERROR)
            {
                Output::Print(_u("EntryIntl_CurrencyDigits > icu::NumberFormat::createCurrencyInstance(error) > U_MISSING_RESOURCE_ERROR (%d)\n"), error);
            }
            else
            {
                Output::Print(_u("EntryIntl_CurrencyDigits > icu::NumberFormat::createCurrencyInstance(error) > UErrorCode (%d)\n"), error);
            }
#endif
            goto LReturn;
        }

        nf->setCurrency(uCurrencyCode, error);
        if (U_FAILURE(error))
        {
#ifdef INTL_ICU_DEBUG
            if (error == UErrorCode::U_MISSING_RESOURCE_ERROR)
            {
                Output::Print(_u("EntryIntl_CurrencyDigits > nf->setCurrency(uCurrencyCode (%s), error) > U_MISSING_RESOURCE_ERROR (%d)\n"), currencyCode, error);
            }
            else
            {
                Output::Print(_u("EntryIntl_CurrencyDigits > nf->setCurrency(uCurrencyCode (%s), error) > UErrorCode (%d)\n"), currencyCode, error);
            }
#endif
            goto LReturn;
        }

        minFracDigits = nf->getMinimumFractionDigits();

#ifdef INTL_ICU_DEBUG
        Output::Print(_u("EntryIntl_CurrencyDigits > nf->getMinimumFractionDigits() successful > returned (%d)\n"), minFracDigits);
#endif

    LReturn:
        // Since something failed, return "reasonable" default of 2 fractional digits (obviously won't be the case for some currencies like JPY).
        // REVIEW (doilij): What does the spec say to do if a currency is not supported?

#ifdef INTL_ICU_DEBUG
        Output::Print(_u("EntryIntl_CurrencyDigits > returning (%d)\n"), minFracDigits);
#endif

        return minFracDigits;
    }

    IPlatformAgnosticResource *CreateNumberFormat()
    {
        UErrorCode error = UErrorCode::U_ZERO_ERROR;
        // REVIEW (doilij): is allocating with `new` here okay?
        icu::NumberFormat *nf = icu::NumberFormat::createInstance(error);
        IPlatformAgnosticResource *obj = new PlatformAgnosticIntlObject<icu::NumberFormat>(nf);
        AssertMsg(U_SUCCESS(error), "Creating icu::NumberFormat failed");
        return obj;
    }

    template const char16 *FormatNumber<>(IPlatformAgnosticResource *formatter, int32_t val);
    template const char16 *FormatNumber<>(IPlatformAgnosticResource *formatter, double val);

    template <typename T>
    const char16 *FormatNumber(IPlatformAgnosticResource *formatter, T val)
    {
        icu::UnicodeString result;

        auto *formatterHolder = (PlatformAgnosticIntlObject<icu::NumberFormat> *)formatter;
        AssertOrFailFastMsg(formatterHolder, "Formatter did not hold an object of type `FinalizableIntlObject<icu::NumberFormat>`");
        icu::NumberFormat *numberFormatter = formatterHolder->GetInstance();

        numberFormatter->format(val, result);
        int32_t length = result.length();
        char16 *ret = new char16[length + 1];
        result.extract(0, length, (UChar *)ret);
        ret[length] = 0;
        return ret;
    }
} // namespace Intl
} // namespace PlatformAgnostic

#endif // INTL_ICU
<|endoftext|>


<commit_before>// Copyright (c) 2019 Chris Ohk, Youngjoong Kim, SeungHyun Jeon

// We are making my contributions/submissions to this project solely in our
// personal capacity and are not conveying any rights to any intellectual
// property of any third parties.

#include <Rosetta/Games/Game.hpp>
#include <Rosetta/Models/Entity.hpp>
#include <Rosetta/Models/Player.hpp>
#include <Rosetta/Models/Spell.hpp>

namespace RosettaStone
{
Entity::Entity(Player& _owner, Card& _card) : card(_card), owner(&_owner)
{
    auraEffects = new AuraEffects(this);

    for (auto& gameTag : _card.gameTags)
    {
        Entity::SetGameTag(gameTag.first, gameTag.second);
    }
}

Entity::Entity(const Entity& ent)
{
    FreeMemory();

    owner = ent.owner;
    card = ent.card;

    auraEffects = ent.auraEffects;
    onGoingEffect = ent.onGoingEffect;
    m_gameTags = ent.m_gameTags;
}

Entity::Entity(Entity&& ent) noexcept
{
    FreeMemory();

    owner = ent.owner;
    card = ent.card;

    auraEffects = ent.auraEffects;
    onGoingEffect = ent.onGoingEffect;
    m_gameTags = ent.m_gameTags;
}

Entity::~Entity()
{
    FreeMemory();
}

Entity& Entity::operator=(const Entity& ent)
{
    if (this == &ent)
    {
        return *this;
    }

    FreeMemory();

    owner = ent.owner;
    card = ent.card;

    auraEffects = ent.auraEffects;
    onGoingEffect = ent.onGoingEffect;
    m_gameTags = ent.m_gameTags;

    return *this;
}

Entity& Entity::operator=(Entity&& ent) noexcept
{
    if (this == &ent)
    {
        return *this;
    }

    FreeMemory();

    owner = ent.owner;
    card = ent.card;

    auraEffects = ent.auraEffects;
    onGoingEffect = ent.onGoingEffect;
    m_gameTags = ent.m_gameTags;

    return *this;
}

void Entity::Reset()
{
    SetGameTag(GameTag::DAMAGE, 0);
    SetGameTag(GameTag::EXHAUSTED, 0);
    SetGameTag(GameTag::ATK, 0);
    SetGameTag(GameTag::HEALTH, 0);
    SetGameTag(GameTag::COST, 0);
    SetGameTag(GameTag::TAUNT, 0);
    SetGameTag(GameTag::FROZEN, 0);
    SetGameTag(GameTag::CHARGE, 0);
    SetGameTag(GameTag::WINDFURY, 0);
    SetGameTag(GameTag::DIVINE_SHIELD, 0);
    SetGameTag(GameTag::STEALTH, 0);
    SetGameTag(GameTag::NUM_ATTACKS_THIS_TURN, 0);
}

int Entity::GetGameTag(GameTag tag) const
{
    if (m_gameTags.find(tag) == m_gameTags.end())
    {
        return 0;
    }

    if (auraEffects != nullptr)
    {
        return m_gameTags.at(tag) + auraEffects->GetGameTag(tag);
    }

    return m_gameTags.at(tag);
}

void Entity::SetGameTag(GameTag tag, int value)
{
    m_gameTags.insert_or_assign(tag, value);
}

int Entity::GetCost() const
{
    return GetGameTag(GameTag::COST);
}

void Entity::SetCost(int cost)
{
    SetGameTag(GameTag::COST, cost);
}

void Entity::Destroy()
{
    isDestroyed = true;
}

Entity* Entity::GetFromCard(Player& player, Card&& card)
{
    Entity* result;

    switch (card.GetCardType())
    {
        case CardType::HERO:
            result = new Hero(player, card);
            break;
        case CardType::HERO_POWER:
            result = new HeroPower(player, card);
            break;
        case CardType::MINION:
            result = new Minion(player, card);
            break;
        case CardType::SPELL:
            result = new Spell(player, card);
            break;
        case CardType::WEAPON:
            result = new Weapon(player, card);
            break;
        default:
            throw std::invalid_argument(
                "Generic::DrawCard() - Invalid card type!");
    }

    // Set entity ID
    result->id = player.GetGame()->GetNextID();

    return result;
}

void Entity::FreeMemory()
{
    delete auraEffects;
    delete onGoingEffect;

    m_gameTags.clear();
}
}  // namespace RosettaStone
<commit_msg>fix: Correct initialization order error<commit_after>// Copyright (c) 2019 Chris Ohk, Youngjoong Kim, SeungHyun Jeon

// We are making my contributions/submissions to this project solely in our
// personal capacity and are not conveying any rights to any intellectual
// property of any third parties.

#include <Rosetta/Games/Game.hpp>
#include <Rosetta/Models/Entity.hpp>
#include <Rosetta/Models/Player.hpp>
#include <Rosetta/Models/Spell.hpp>

namespace RosettaStone
{
Entity::Entity(Player& _owner, Card& _card) : owner(&_owner), card(_card)
{
    auraEffects = new AuraEffects(this);

    for (auto& gameTag : _card.gameTags)
    {
        Entity::SetGameTag(gameTag.first, gameTag.second);
    }
}

Entity::Entity(const Entity& ent)
{
    FreeMemory();

    owner = ent.owner;
    card = ent.card;

    auraEffects = ent.auraEffects;
    onGoingEffect = ent.onGoingEffect;
    m_gameTags = ent.m_gameTags;
}

Entity::Entity(Entity&& ent) noexcept
{
    FreeMemory();

    owner = ent.owner;
    card = ent.card;

    auraEffects = ent.auraEffects;
    onGoingEffect = ent.onGoingEffect;
    m_gameTags = ent.m_gameTags;
}

Entity::~Entity()
{
    FreeMemory();
}

Entity& Entity::operator=(const Entity& ent)
{
    if (this == &ent)
    {
        return *this;
    }

    FreeMemory();

    owner = ent.owner;
    card = ent.card;

    auraEffects = ent.auraEffects;
    onGoingEffect = ent.onGoingEffect;
    m_gameTags = ent.m_gameTags;

    return *this;
}

Entity& Entity::operator=(Entity&& ent) noexcept
{
    if (this == &ent)
    {
        return *this;
    }

    FreeMemory();

    owner = ent.owner;
    card = ent.card;

    auraEffects = ent.auraEffects;
    onGoingEffect = ent.onGoingEffect;
    m_gameTags = ent.m_gameTags;

    return *this;
}

void Entity::Reset()
{
    SetGameTag(GameTag::DAMAGE, 0);
    SetGameTag(GameTag::EXHAUSTED, 0);
    SetGameTag(GameTag::ATK, 0);
    SetGameTag(GameTag::HEALTH, 0);
    SetGameTag(GameTag::COST, 0);
    SetGameTag(GameTag::TAUNT, 0);
    SetGameTag(GameTag::FROZEN, 0);
    SetGameTag(GameTag::CHARGE, 0);
    SetGameTag(GameTag::WINDFURY, 0);
    SetGameTag(GameTag::DIVINE_SHIELD, 0);
    SetGameTag(GameTag::STEALTH, 0);
    SetGameTag(GameTag::NUM_ATTACKS_THIS_TURN, 0);
}

int Entity::GetGameTag(GameTag tag) const
{
    if (m_gameTags.find(tag) == m_gameTags.end())
    {
        return 0;
    }

    if (auraEffects != nullptr)
    {
        return m_gameTags.at(tag) + auraEffects->GetGameTag(tag);
    }

    return m_gameTags.at(tag);
}

void Entity::SetGameTag(GameTag tag, int value)
{
    m_gameTags.insert_or_assign(tag, value);
}

int Entity::GetCost() const
{
    return GetGameTag(GameTag::COST);
}

void Entity::SetCost(int cost)
{
    SetGameTag(GameTag::COST, cost);
}

void Entity::Destroy()
{
    isDestroyed = true;
}

Entity* Entity::GetFromCard(Player& player, Card&& card)
{
    Entity* result;

    switch (card.GetCardType())
    {
        case CardType::HERO:
            result = new Hero(player, card);
            break;
        case CardType::HERO_POWER:
            result = new HeroPower(player, card);
            break;
        case CardType::MINION:
            result = new Minion(player, card);
            break;
        case CardType::SPELL:
            result = new Spell(player, card);
            break;
        case CardType::WEAPON:
            result = new Weapon(player, card);
            break;
        default:
            throw std::invalid_argument(
                "Generic::DrawCard() - Invalid card type!");
    }

    // Set entity ID
    result->id = player.GetGame()->GetNextID();

    return result;
}

void Entity::FreeMemory()
{
    delete auraEffects;
    delete onGoingEffect;

    m_gameTags.clear();
}
}  // namespace RosettaStone
<|endoftext|>


<commit_before><commit_msg>fix comments.<commit_after><|endoftext|>


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

#include "rtos/ThisThread.h"
#include "mbed_error.h"
#include "platform/mbed_atomic.h"
#include "events/EventQueue.h"
#include "events/mbed_shared_queues.h"

#include "QUECTEL_BC95_CellularStack.h"
#include "CellularUtil.h"
#include "CellularLog.h"

#define PACKET_SIZE_MAX 1358
#define TXFULL_EVENT_TIMEOUT 1s

#define AT_UPLINK_BUSY          159
#define AT_UART_BUFFER_ERROR    536
#define AT_BACK_OFF_TIMER       537

using namespace std::chrono;
using namespace mbed;
using namespace mbed_cellular_util;

QUECTEL_BC95_CellularStack::QUECTEL_BC95_CellularStack(ATHandler &atHandler, int cid, nsapi_ip_stack_t stack_type, AT_CellularDevice &device) :
    AT_CellularStack(atHandler, cid, stack_type, device), _event_queue(mbed_event_queue()), _txfull_event_id(0)
{
    _at.set_urc_handler("+NSONMI:", mbed::Callback<void()>(this, &QUECTEL_BC95_CellularStack::urc_nsonmi));
    _at.set_urc_handler("+NSOCLI:", mbed::Callback<void()>(this, &QUECTEL_BC95_CellularStack::urc_nsocli));
}

QUECTEL_BC95_CellularStack::~QUECTEL_BC95_CellularStack()
{
    if (_txfull_event_id) {
        _event_queue->cancel(_txfull_event_id);
    }

    _at.set_urc_handler("+NSONMI:", nullptr);
    _at.set_urc_handler("+NSOCLI:", nullptr);
}

nsapi_error_t QUECTEL_BC95_CellularStack::socket_listen(nsapi_socket_t handle, int backlog)
{
    return NSAPI_ERROR_UNSUPPORTED;
}

nsapi_error_t QUECTEL_BC95_CellularStack::socket_accept(void *server, void **socket, SocketAddress *addr)
{
    return NSAPI_ERROR_UNSUPPORTED;
}

nsapi_error_t QUECTEL_BC95_CellularStack::socket_connect(nsapi_socket_t handle, const SocketAddress &address)
{
    CellularSocket *socket = (CellularSocket *)handle;

    _at.lock();
    if (socket->id == -1) {
        const nsapi_error_t error_create = create_socket_impl(socket);
        if (error_create != NSAPI_ERROR_OK) {
            return error_create;
        }
    }

    _at.cmd_start("AT+NSOCO=");
    _at.write_int(socket->id);
    _at.write_string(address.get_ip_address(), false);
    _at.write_int(address.get_port());
    _at.cmd_stop_read_resp();

    _at.unlock();

    if (_at.get_last_error() == NSAPI_ERROR_OK) {
        socket->remoteAddress = address;
        socket->connected = true;
        return NSAPI_ERROR_OK;
    }

    return NSAPI_ERROR_NO_CONNECTION;
}

void QUECTEL_BC95_CellularStack::urc_nsonmi()
{
    int sock_id = _at.read_int();

    for (int i = 0; i < _device.get_property(AT_CellularDevice::PROPERTY_SOCKET_COUNT); i++) {
        CellularSocket *sock = _socket[i];
        if (sock && sock->id == sock_id) {
            if (sock->_cb) {
                sock->_cb(sock->_data);
            }
            break;
        }
    }
}

void QUECTEL_BC95_CellularStack::urc_nsocli()
{
    int sock_id = _at.read_int();

    const nsapi_error_t err = _at.get_last_error();

    if (err != NSAPI_ERROR_OK) {
        return;
    }

    CellularSocket *sock = find_socket(sock_id);

    if (sock) {
        sock->closed = true;
        if (sock->_cb) {
            sock->_cb(sock->_data);
        }
        tr_info("Socket closed %d", sock_id);
    }
}

nsapi_error_t QUECTEL_BC95_CellularStack::socket_close_impl(int sock_id)
{
    CellularSocket *sock = find_socket(sock_id);

    if (sock && sock->closed) {
        return NSAPI_ERROR_OK;
    }

    sock->txfull_event = false;

    nsapi_error_t err = _at.at_cmd_discard("+NSOCL", "=", "%d", sock_id);

    tr_info("Close socket: %d error: %d", sock_id, err);

    return err;
}

nsapi_error_t QUECTEL_BC95_CellularStack::create_socket_impl(CellularSocket *socket)
{
    int sock_id = -1;
    bool socketOpenWorking = false;

    if (socket->proto == NSAPI_UDP) {
        _at.cmd_start_stop("+NSOCR", "=DGRAM,", "%d%d%d%s", 17, socket->localAddress.get_port(), 1, ((_ip_ver_sendto == NSAPI_IPv4) ? "AF_INET" : "AF_INET6"));
    } else if (socket->proto == NSAPI_TCP) {
        _at.cmd_start_stop("+NSOCR", "=STREAM,", "%d%d%d%s", 6, socket->localAddress.get_port(), 1, ((_ip_ver_sendto == NSAPI_IPv4) ? "AF_INET" : "AF_INET6"));
    } else {
        return NSAPI_ERROR_PARAMETER;
    }
    _at.resp_start();
    sock_id = _at.read_int();
    _at.resp_stop();

    socketOpenWorking = (_at.get_last_error() == NSAPI_ERROR_OK);

    if (!socketOpenWorking || (sock_id == -1)) {
        tr_error("Socket create failed!");
        return NSAPI_ERROR_NO_SOCKET;
    }

    tr_info("Socket create id: %d", sock_id);

    socket->id = sock_id;

    return NSAPI_ERROR_OK;
}

nsapi_size_or_error_t QUECTEL_BC95_CellularStack::socket_sendto_impl(CellularSocket *socket, const SocketAddress &address,
                                                                     const void *data, nsapi_size_t size)
{
    //AT_CellularStack::socket_sendto(...) will create a socket on modem if it wasn't
    // open already.
    MBED_ASSERT(socket->id != -1);

    if (_ip_ver_sendto != address.get_ip_version()) {
        _ip_ver_sendto =  address.get_ip_version();
        socket_close_impl(socket->id);
        create_socket_impl(socket);
    }

    int sent_len = 0;

    if (size > PACKET_SIZE_MAX) {
        return NSAPI_ERROR_PARAMETER;
    }

    int retry = 0;
retry_send:
    if (socket->proto == NSAPI_UDP) {
        _at.cmd_start("AT+NSOST=");
        _at.write_int(socket->id);
        _at.write_string(address.get_ip_address(), false);
        _at.write_int(address.get_port());
        _at.write_int(size);
    } else if (socket->proto == NSAPI_TCP) {
        _at.cmd_start("AT+NSOSD=");
        _at.write_int(socket->id);
        _at.write_int(size);
    } else {
        return NSAPI_ERROR_PARAMETER;
    }

    _at.write_hex_string((char *)data, size);
    _at.cmd_stop();
    _at.resp_start();
    // skip socket id
    _at.skip_param();
    sent_len = _at.read_int();
    _at.resp_stop();

    if (_at.get_last_error() == NSAPI_ERROR_OK) {
        return sent_len;
    }

    // check for network congestion
    device_err_t err = _at.get_last_device_error();
    if ((err.errType == DeviceErrorTypeErrorCME &&
            (err.errCode == AT_UART_BUFFER_ERROR || err.errCode == AT_BACK_OFF_TIMER)) || err.errCode == AT_UPLINK_BUSY) {
        if (socket->proto == NSAPI_UDP) {
            if (retry < 3) {
                retry++;
                tr_warn("Socket %d sendto EAGAIN", socket->id);
                rtos::ThisThread::sleep_for(30ms);
                _at.clear_error();
                goto retry_send;
            }
            return NSAPI_ERROR_NO_MEMORY;
        }
        _socket_mutex.lock();
        if (!socket->txfull_event && !_txfull_event_id) {
            tr_warn("socket %d tx full", socket->id);
            socket->txfull_event = true;
            _txfull_event_id = _event_queue->call_in(TXFULL_EVENT_TIMEOUT, callback(this, &QUECTEL_BC95_CellularStack::txfull_event_timeout));
            if (!_txfull_event_id) {
                MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_ENOMEM), \
                           "QUECTEL_BC95_CellularStack::socket_sendto_impl(): unable to add event to queue. Increase \"events.shared-eventsize\"\n");
                _socket_mutex.unlock();
                return NSAPI_ERROR_NO_MEMORY;
            }
        }
        _socket_mutex.unlock();
        return NSAPI_ERROR_WOULD_BLOCK;
    }

    return _at.get_last_error();
}

nsapi_size_or_error_t QUECTEL_BC95_CellularStack::socket_recvfrom_impl(CellularSocket *socket, SocketAddress *address,
                                                                       void *buffer, nsapi_size_t size)
{
    //AT_CellularStack::socket_recvfrom(...) will create a socket on modem if it wasn't
    // open already.
    MBED_ASSERT(socket->id != -1);

    nsapi_size_or_error_t recv_len = 0;
    int port;
    char ip_address[NSAPI_IP_SIZE];

    _at.cmd_start_stop("+NSORF", "=", "%d%d", socket->id, size < PACKET_SIZE_MAX ? size : PACKET_SIZE_MAX);

    _at.resp_start();
    // receiving socket id
    _at.skip_param();
    _at.read_string(ip_address, sizeof(ip_address));
    port = _at.read_int();
    recv_len = _at.read_int();
    int hexlen = _at.read_hex_string((char *)buffer, recv_len);
    // remaining length
    _at.skip_param();
    _at.resp_stop();

    if (!recv_len || (recv_len == -1) || (_at.get_last_error() != NSAPI_ERROR_OK)) {
        return NSAPI_ERROR_WOULD_BLOCK;
    }

    if (address) {
        address->set_ip_address(ip_address);
        address->set_port(port);
    }

    if (recv_len != hexlen) {
        tr_error("Not received as much data as expected. Should receive: %d bytes, received: %d bytes", recv_len, hexlen);
    }
    return recv_len;
}

void QUECTEL_BC95_CellularStack::txfull_event_timeout()
{
    _socket_mutex.lock();
    _txfull_event_id = 0;
    for (int i = 0; i < _device.get_property(AT_CellularDevice::PROPERTY_SOCKET_COUNT); i++) {
        CellularSocket *sock = _socket[i];
        if (sock && sock->_cb && sock->txfull_event) {
            sock->txfull_event = false;
            sock->_cb(sock->_data);
        }
    }
    _socket_mutex.unlock();
}
<commit_msg>Update connectivity/drivers/cellular/QUECTEL/BC95/QUECTEL_BC95_CellularStack.cpp<commit_after>/*
 * Copyright (c) 2017, Arm Limited and affiliates.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "rtos/ThisThread.h"
#include "mbed_error.h"
#include "platform/mbed_atomic.h"
#include "events/EventQueue.h"
#include "events/mbed_shared_queues.h"

#include "QUECTEL_BC95_CellularStack.h"
#include "CellularUtil.h"
#include "CellularLog.h"

#define PACKET_SIZE_MAX 1358
#define TXFULL_EVENT_TIMEOUT 1s

#define AT_UPLINK_BUSY          159
#define AT_UART_BUFFER_ERROR    536
#define AT_BACK_OFF_TIMER       537

using namespace std::chrono;
using namespace mbed;
using namespace mbed_cellular_util;

QUECTEL_BC95_CellularStack::QUECTEL_BC95_CellularStack(ATHandler &atHandler, int cid, nsapi_ip_stack_t stack_type, AT_CellularDevice &device) :
    AT_CellularStack(atHandler, cid, stack_type, device), _event_queue(mbed_event_queue()), _txfull_event_id(0)
{
    _at.set_urc_handler("+NSONMI:", mbed::Callback<void()>(this, &QUECTEL_BC95_CellularStack::urc_nsonmi));
    _at.set_urc_handler("+NSOCLI:", mbed::Callback<void()>(this, &QUECTEL_BC95_CellularStack::urc_nsocli));
}

QUECTEL_BC95_CellularStack::~QUECTEL_BC95_CellularStack()
{
    if (_txfull_event_id) {
        _event_queue->cancel(_txfull_event_id);
    }

    _at.set_urc_handler("+NSONMI:", nullptr);
    _at.set_urc_handler("+NSOCLI:", nullptr);
}

nsapi_error_t QUECTEL_BC95_CellularStack::socket_listen(nsapi_socket_t handle, int backlog)
{
    return NSAPI_ERROR_UNSUPPORTED;
}

nsapi_error_t QUECTEL_BC95_CellularStack::socket_accept(void *server, void **socket, SocketAddress *addr)
{
    return NSAPI_ERROR_UNSUPPORTED;
}

nsapi_error_t QUECTEL_BC95_CellularStack::socket_connect(nsapi_socket_t handle, const SocketAddress &address)
{
    CellularSocket *socket = (CellularSocket *)handle;

    _at.lock();
    if (socket->id == -1) {
        const nsapi_error_t error_create = create_socket_impl(socket);
        if (error_create != NSAPI_ERROR_OK) {
            return error_create;
        }
    }

    _at.cmd_start("AT+NSOCO=");
    _at.write_int(socket->id);
    _at.write_string(address.get_ip_address(), false);
    _at.write_int(address.get_port());
    _at.cmd_stop_read_resp();

    _at.unlock();

    if (_at.get_last_error() == NSAPI_ERROR_OK) {
        socket->remoteAddress = address;
        socket->connected = true;
        return NSAPI_ERROR_OK;
    }

    return NSAPI_ERROR_NO_CONNECTION;
}

void QUECTEL_BC95_CellularStack::urc_nsonmi()
{
    int sock_id = _at.read_int();

    for (int i = 0; i < _device.get_property(AT_CellularDevice::PROPERTY_SOCKET_COUNT); i++) {
        CellularSocket *sock = _socket[i];
        if (sock && sock->id == sock_id) {
            if (sock->_cb) {
                sock->_cb(sock->_data);
            }
            break;
        }
    }
}

void QUECTEL_BC95_CellularStack::urc_nsocli()
{
    int sock_id = _at.read_int();

    const nsapi_error_t err = _at.get_last_error();

    if (err != NSAPI_ERROR_OK) {
        return;
    }

    CellularSocket *sock = find_socket(sock_id);

    if (sock) {
        sock->closed = true;
        if (sock->_cb) {
            sock->_cb(sock->_data);
        }
        tr_info("Socket closed %d", sock_id);
    }
}

nsapi_error_t QUECTEL_BC95_CellularStack::socket_close_impl(int sock_id)
{
    CellularSocket *sock = find_socket(sock_id);

    if (sock && sock->closed) {
        return NSAPI_ERROR_OK;
    }

    sock->txfull_event = false;

    nsapi_error_t err = _at.at_cmd_discard("+NSOCL", "=", "%d", sock_id);

    tr_info("Close socket: %d error: %d", sock_id, err);

    return err;
}

nsapi_error_t QUECTEL_BC95_CellularStack::create_socket_impl(CellularSocket *socket)
{
    int sock_id = -1;
    bool socketOpenWorking = false;

    if (socket->proto == NSAPI_UDP) {
        _at.cmd_start_stop("+NSOCR", "=DGRAM,", "%d%d%d%s", 17, socket->localAddress.get_port(), 1, ((_ip_ver_sendto == NSAPI_IPv4) ? "AF_INET" : "AF_INET6"));
    } else if (socket->proto == NSAPI_TCP) {
        _at.cmd_start_stop("+NSOCR", "=STREAM,", "%d%d%d%s", 6, socket->localAddress.get_port(), 1, ((_ip_ver_sendto == NSAPI_IPv4) ? "AF_INET" : "AF_INET6"));
    } else {
        return NSAPI_ERROR_PARAMETER;
    }
    _at.resp_start();
    sock_id = _at.read_int();
    _at.resp_stop();

    socketOpenWorking = (_at.get_last_error() == NSAPI_ERROR_OK);

    if (!socketOpenWorking || (sock_id == -1)) {
        tr_error("Socket create failed!");
        return NSAPI_ERROR_NO_SOCKET;
    }

    tr_info("Socket create id: %d", sock_id);

    socket->id = sock_id;

    return NSAPI_ERROR_OK;
}

nsapi_size_or_error_t QUECTEL_BC95_CellularStack::socket_sendto_impl(CellularSocket *socket, const SocketAddress &address,
                                                                     const void *data, nsapi_size_t size)
{
    //AT_CellularStack::socket_sendto(...) will create a socket on modem if it wasn't
    // open already.
    MBED_ASSERT(socket->id != -1);

    if (_ip_ver_sendto != address.get_ip_version()) {
        _ip_ver_sendto =  address.get_ip_version();
        socket_close_impl(socket->id);
        create_socket_impl(socket);
    }

    int sent_len = 0;

    if (size > PACKET_SIZE_MAX) {
        return NSAPI_ERROR_PARAMETER;
    }

    int retry = 0;
retry_send:
    if (socket->proto == NSAPI_UDP) {
        _at.cmd_start("AT+NSOST=");
        _at.write_int(socket->id);
        _at.write_string(address.get_ip_address(), false);
        _at.write_int(address.get_port());
        _at.write_int(size);
    } else if (socket->proto == NSAPI_TCP) {
        _at.cmd_start("AT+NSOSD=");
        _at.write_int(socket->id);
        _at.write_int(size);
    } else {
        return NSAPI_ERROR_PARAMETER;
    }

    _at.write_hex_string((char *)data, size);
    _at.cmd_stop();
    _at.resp_start();
    // skip socket id
    _at.skip_param();
    sent_len = _at.read_int();
    _at.resp_stop();

    if (_at.get_last_error() == NSAPI_ERROR_OK) {
        return sent_len;
    }

    // check for network congestion
    device_err_t err = _at.get_last_device_error();
    if (
        (
            err.errType == DeviceErrorTypeErrorCME
            && (err.errCode == AT_UART_BUFFER_ERROR || err.errCode == AT_BACK_OFF_TIMER)
        )
        || err.errCode == AT_UPLINK_BUSY
    ) {
        if (socket->proto == NSAPI_UDP) {
            if (retry < 3) {
                retry++;
                tr_warn("Socket %d sendto EAGAIN", socket->id);
                rtos::ThisThread::sleep_for(30ms);
                _at.clear_error();
                goto retry_send;
            }
            return NSAPI_ERROR_NO_MEMORY;
        }
        _socket_mutex.lock();
        if (!socket->txfull_event && !_txfull_event_id) {
            tr_warn("socket %d tx full", socket->id);
            socket->txfull_event = true;
            _txfull_event_id = _event_queue->call_in(TXFULL_EVENT_TIMEOUT, callback(this, &QUECTEL_BC95_CellularStack::txfull_event_timeout));
            if (!_txfull_event_id) {
                MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_ENOMEM), \
                           "QUECTEL_BC95_CellularStack::socket_sendto_impl(): unable to add event to queue. Increase \"events.shared-eventsize\"\n");
                _socket_mutex.unlock();
                return NSAPI_ERROR_NO_MEMORY;
            }
        }
        _socket_mutex.unlock();
        return NSAPI_ERROR_WOULD_BLOCK;
    }

    return _at.get_last_error();
}

nsapi_size_or_error_t QUECTEL_BC95_CellularStack::socket_recvfrom_impl(CellularSocket *socket, SocketAddress *address,
                                                                       void *buffer, nsapi_size_t size)
{
    //AT_CellularStack::socket_recvfrom(...) will create a socket on modem if it wasn't
    // open already.
    MBED_ASSERT(socket->id != -1);

    nsapi_size_or_error_t recv_len = 0;
    int port;
    char ip_address[NSAPI_IP_SIZE];

    _at.cmd_start_stop("+NSORF", "=", "%d%d", socket->id, size < PACKET_SIZE_MAX ? size : PACKET_SIZE_MAX);

    _at.resp_start();
    // receiving socket id
    _at.skip_param();
    _at.read_string(ip_address, sizeof(ip_address));
    port = _at.read_int();
    recv_len = _at.read_int();
    int hexlen = _at.read_hex_string((char *)buffer, recv_len);
    // remaining length
    _at.skip_param();
    _at.resp_stop();

    if (!recv_len || (recv_len == -1) || (_at.get_last_error() != NSAPI_ERROR_OK)) {
        return NSAPI_ERROR_WOULD_BLOCK;
    }

    if (address) {
        address->set_ip_address(ip_address);
        address->set_port(port);
    }

    if (recv_len != hexlen) {
        tr_error("Not received as much data as expected. Should receive: %d bytes, received: %d bytes", recv_len, hexlen);
    }
    return recv_len;
}

void QUECTEL_BC95_CellularStack::txfull_event_timeout()
{
    _socket_mutex.lock();
    _txfull_event_id = 0;
    for (int i = 0; i < _device.get_property(AT_CellularDevice::PROPERTY_SOCKET_COUNT); i++) {
        CellularSocket *sock = _socket[i];
        if (sock && sock->_cb && sock->txfull_event) {
            sock->txfull_event = false;
            sock->_cb(sock->_data);
        }
    }
    _socket_mutex.unlock();
}
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2017                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_DETAIL_INTRUSIVE_PARTITIONED_LIST_HPP
#define CAF_DETAIL_INTRUSIVE_PARTITIONED_LIST_HPP

#include "caf/config.hpp"

#include <memory>
#include <iterator>
#include <algorithm>

#include "caf/behavior.hpp"
#include "caf/invoke_message_result.hpp"

namespace caf {
namespace detail {

/// Describes a partitioned list of elements. The first part
/// of the list is described by the iterator pair `[begin, separator)`.
/// The second part by `[continuation, end)`. Actors use the second half
/// of the list to store previously skipped elements. Priority-aware actors
/// also use the first half of the list to sort messages by priority.
template <class T, class Delete = std::default_delete<T>>
class intrusive_partitioned_list {
public:
  using value_type = T;
  using pointer = value_type*;
  using deleter_type = Delete;

  struct iterator : std::iterator<std::bidirectional_iterator_tag, value_type> {
    pointer ptr;

    iterator(pointer init = nullptr) : ptr(init) {
      // nop
    }

    iterator(const iterator&) = default;
    iterator& operator=(const iterator&) = default;

    iterator& operator++() {
      ptr = ptr->next;
      return *this;
    }

    iterator operator++(int) {
      iterator res = *this;
      ptr = ptr->next;
      return res;
    }

    iterator& operator--() {
      ptr = ptr->prev;
      return *this;
    }

    iterator operator--(int) {
      iterator res = *this;
      ptr = ptr->prev;
      return res;
    }

    value_type& operator*() {
      return *ptr;
    }

    pointer operator->() {
      return ptr;
    }

    bool operator==(const iterator& other) const {
      return ptr == other.ptr;
    }

    bool operator!=(const iterator& other) const {
      return ptr != other.ptr;
    }

    iterator next() const {
      return ptr->next;
    }
  };

  intrusive_partitioned_list() {
    head_.next = &separator_;
    separator_.prev = &head_;
    separator_.next = &tail_;
    tail_.prev = &separator_;
  }

  ~intrusive_partitioned_list() {
    clear();
  }

  iterator begin() {
    return head_.next;
  }

  iterator separator() {
    return &separator_;
  }

  iterator continuation() {
    return separator_.next;
  }

  iterator end() {
    return &tail_;
  }

  using range = std::pair<iterator, iterator>;

  /// Returns the two iterator pairs describing the first and second part
  /// of the partitioned list.
  std::array<range, 2> ranges() {
    return {{range{begin(), separator()}, range{continuation(), end()}}};
  }

  template <class F>
  void clear(F f) {
    for (auto& range : ranges()) {
      auto i = range.first;
      auto e = range.second;
      while (i != e) {
        auto ptr = i.ptr;
        ++i;
        f(*ptr);
        delete_(ptr);
      }
    }
    if (head_.next != &separator_) {
      head_.next = &separator_;
      separator_.prev = &head_;
    }
    if (separator_.next != &tail_) {
      separator_.next = &tail_;
      tail_.prev = &separator_;
    }
  }

  void clear() {
    auto nop = [](value_type&) {};
    clear(nop);
  }

  iterator insert(iterator next, pointer val) {
    auto prev = next->prev;
    val->prev = prev;
    val->next = next.ptr;
    prev->next = val;
    next->prev = val;
    return val;
  }

  bool empty() const {
    return head_.next == &separator_ && separator_.next == &tail_;
  }

  pointer take(iterator pos) {
    auto res = pos.ptr;
    auto next = res->next;
    auto prev = res->prev;
    prev->next = next;
    next->prev = prev;
    return res;
  }

  iterator erase(iterator pos) {
    auto next = pos->next;
    delete_(take(pos));
    return next;
  }

  size_t count(size_t max_count = std::numeric_limits<size_t>::max()) {
    size_t result = 0;
    for (auto& range : ranges())
      for (auto i = range.first; i != range.second; ++i)
        if (++result == max_count)
          return max_count;
    return result;
  }

private:
  value_type head_;
  value_type separator_;
  value_type tail_;
  deleter_type delete_;
};

} // namespace detail
} // namespace caf

#endif // CAF_DETAIL_INTRUSIVE_PARTITIONED_LIST_HPP
<commit_msg>Fix build with MSVC<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2017                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_DETAIL_INTRUSIVE_PARTITIONED_LIST_HPP
#define CAF_DETAIL_INTRUSIVE_PARTITIONED_LIST_HPP

#include "caf/config.hpp"

#include <memory>
#include <iterator>
#include <algorithm>

#include "caf/behavior.hpp"
#include "caf/invoke_message_result.hpp"

namespace caf {
namespace detail {

/// Describes a partitioned list of elements. The first part
/// of the list is described by the iterator pair `[begin, separator)`.
/// The second part by `[continuation, end)`. Actors use the second half
/// of the list to store previously skipped elements. Priority-aware actors
/// also use the first half of the list to sort messages by priority.
template <class T, class Delete = std::default_delete<T>>
class intrusive_partitioned_list {
public:
  using value_type = T;
  using pointer = value_type*;
  using deleter_type = Delete;

  struct iterator : std::iterator<std::bidirectional_iterator_tag, value_type> {
    pointer ptr;

    iterator(pointer init = nullptr) : ptr(init) {
      // nop
    }

    iterator(const iterator&) = default;
    iterator& operator=(const iterator&) = default;

    iterator& operator++() {
      ptr = ptr->next;
      return *this;
    }

    iterator operator++(int) {
      iterator res = *this;
      ptr = ptr->next;
      return res;
    }

    iterator& operator--() {
      ptr = ptr->prev;
      return *this;
    }

    iterator operator--(int) {
      iterator res = *this;
      ptr = ptr->prev;
      return res;
    }

    const value_type& operator*() const {
      return *ptr;
    }

    value_type& operator*() {
      return *ptr;
    }

    pointer operator->() {
      return ptr;
    }

    bool operator==(const iterator& other) const {
      return ptr == other.ptr;
    }

    bool operator!=(const iterator& other) const {
      return ptr != other.ptr;
    }

    iterator next() const {
      return ptr->next;
    }
  };

  intrusive_partitioned_list() {
    head_.next = &separator_;
    separator_.prev = &head_;
    separator_.next = &tail_;
    tail_.prev = &separator_;
  }

  ~intrusive_partitioned_list() {
    clear();
  }

  iterator begin() {
    return head_.next;
  }

  iterator separator() {
    return &separator_;
  }

  iterator continuation() {
    return separator_.next;
  }

  iterator end() {
    return &tail_;
  }

  using range = std::pair<iterator, iterator>;

  /// Returns the two iterator pairs describing the first and second part
  /// of the partitioned list.
  std::array<range, 2> ranges() {
    return {{range{begin(), separator()}, range{continuation(), end()}}};
  }

  template <class F>
  void clear(F f) {
    for (auto& range : ranges()) {
      auto i = range.first;
      auto e = range.second;
      while (i != e) {
        auto ptr = i.ptr;
        ++i;
        f(*ptr);
        delete_(ptr);
      }
    }
    if (head_.next != &separator_) {
      head_.next = &separator_;
      separator_.prev = &head_;
    }
    if (separator_.next != &tail_) {
      separator_.next = &tail_;
      tail_.prev = &separator_;
    }
  }

  void clear() {
    auto nop = [](value_type&) {};
    clear(nop);
  }

  iterator insert(iterator next, pointer val) {
    auto prev = next->prev;
    val->prev = prev;
    val->next = next.ptr;
    prev->next = val;
    next->prev = val;
    return val;
  }

  bool empty() const {
    return head_.next == &separator_ && separator_.next == &tail_;
  }

  pointer take(iterator pos) {
    auto res = pos.ptr;
    auto next = res->next;
    auto prev = res->prev;
    prev->next = next;
    next->prev = prev;
    return res;
  }

  iterator erase(iterator pos) {
    auto next = pos->next;
    delete_(take(pos));
    return next;
  }

  size_t count(size_t max_count = std::numeric_limits<size_t>::max()) {
    size_t result = 0;
    for (auto& range : ranges())
      for (auto i = range.first; i != range.second; ++i)
        if (++result == max_count)
          return max_count;
    return result;
  }

private:
  value_type head_;
  value_type separator_;
  value_type tail_;
  deleter_type delete_;
};

} // namespace detail
} // namespace caf

#endif // CAF_DETAIL_INTRUSIVE_PARTITIONED_LIST_HPP
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2009 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "commandwatcher.h"
#include "sconnect.h"
#include <QTextStream>
#include <QFile>
#include <QSocketNotifier>
#include <QStringList>
#include <QCoreApplication>
#include <QDebug>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <QMap>

CommandWatcher::CommandWatcher(int commandfd, QObject *parent) :
    QObject(parent), commandfd(commandfd)
{
    commandNotifier = new QSocketNotifier(commandfd, QSocketNotifier::Read, this);
    sconnect(commandNotifier, SIGNAL(activated(int)), this, SLOT(onActivated()));
    help();
}

void CommandWatcher::onActivated()
{
    // read all available input to commandBuffer
    static QByteArray commandBuffer = "";
    static char buf[1024];
    int readSize;
    fcntl(commandfd, F_SETFL, O_NONBLOCK);
    while ((readSize = read(commandfd, &buf, 1024)) > 0)
        commandBuffer += QByteArray(buf, readSize);

    // handle all available whole input lines as commands
    int nextSeparator;
    while ((nextSeparator = commandBuffer.indexOf('\n')) != -1) {
        // split lines to separate commands by semicolons
        QStringList commands = QString::fromUtf8(commandBuffer.constData()).left(nextSeparator).split(";");
        foreach (QString command, commands)
            interpret(command.trimmed());
        commandBuffer.remove(0, nextSeparator + 1);
    }

    if (readSize == 0) // EOF
        QCoreApplication::exit(0);
}

void CommandWatcher::help()
{
    qDebug() << "Available commands:";
    qDebug() << "  add KEY TYPE                    - create new key with the given type";
    qDebug() << "  KEY=VALUE                       - set KEY to the given VALUE";
    qDebug() << "  flush                           - write FLUSHED to stderr and stdout";
    qDebug() << "Any prefix of a command can be used as an abbreviation";
}

void CommandWatcher::interpret(const QString& command)
{
    QTextStream out(stdout);
    QTextStream err(stderr);
    if (command == "") {
        // Show help
        help();
    } else if (command.contains('=')) {
        // Setter command
        setCommand(command);
    } else {
        QStringList args = command.split(" ");
        QString commandName = args[0];
        args.pop_front();

        // Interpret commands
        if (QString("add").startsWith(commandName)) {
            addCommand(args);
        }
   }
}

void CommandWatcher::addCommand(const QStringList& args)
{
    if (args.count() < 2) {
        qDebug() << "ERROR: need to specify both KEY and TYPE";
        return;
    }

    const QString keyName = args.at(0);
    const QString keyType = args.at(1);

    properties.insert(keyName, keyType);
    qDebug() << "Added key:" << keyName << "with type:" << keyType;
}

void CommandWatcher::setCommand(const QString& command)
{
    QStringList parts = command.split("=");

    const QString keyName = parts.at(0).trimmed();
    const QString value = parts.at(1).trimmed();

    qDebug() << "Setting key:" << keyName << "to value:" << value;
    Property(keyName).setValue(value);
}
<commit_msg>Actually do set the props.<commit_after>/*
 * Copyright (C) 2009 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "commandwatcher.h"
#include "sconnect.h"
#include <QTextStream>
#include <QFile>
#include <QSocketNotifier>
#include <QStringList>
#include <QCoreApplication>
#include <QDebug>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <QMap>

CommandWatcher::CommandWatcher(int commandfd, QObject *parent) :
    QObject(parent), commandfd(commandfd)
{
    commandNotifier = new QSocketNotifier(commandfd, QSocketNotifier::Read, this);
    sconnect(commandNotifier, SIGNAL(activated(int)), this, SLOT(onActivated()));
    help();
}

void CommandWatcher::onActivated()
{
    // read all available input to commandBuffer
    static QByteArray commandBuffer = "";
    static char buf[1024];
    int readSize;
    fcntl(commandfd, F_SETFL, O_NONBLOCK);
    while ((readSize = read(commandfd, &buf, 1024)) > 0)
        commandBuffer += QByteArray(buf, readSize);

    // handle all available whole input lines as commands
    int nextSeparator;
    while ((nextSeparator = commandBuffer.indexOf('\n')) != -1) {
        // split lines to separate commands by semicolons
        QStringList commands = QString::fromUtf8(commandBuffer.constData()).left(nextSeparator).split(";");
        foreach (QString command, commands)
            interpret(command.trimmed());
        commandBuffer.remove(0, nextSeparator + 1);
    }

    if (readSize == 0) // EOF
        QCoreApplication::exit(0);
}

void CommandWatcher::help()
{
    qDebug() << "Available commands:";
    qDebug() << "  add KEY TYPE                    - create new key with the given type";
    qDebug() << "  KEY=VALUE                       - set KEY to the given VALUE";
    qDebug() << "  flush                           - write FLUSHED to stderr and stdout";
    qDebug() << "Any prefix of a command can be used as an abbreviation";
}

void CommandWatcher::interpret(const QString& command)
{
    QTextStream out(stdout);
    QTextStream err(stderr);
    if (command == "") {
        // Show help
        help();
    } else if (command.contains('=')) {
        // Setter command
        setCommand(command);
    } else {
        QStringList args = command.split(" ");
        QString commandName = args[0];
        args.pop_front();

        // Interpret commands
        if (QString("add").startsWith(commandName)) {
            addCommand(args);
        }
   }
}

void CommandWatcher::addCommand(const QStringList& args)
{
    if (args.count() < 2) {
        qDebug() << "ERROR: need to specify both KEY and TYPE";
        return;
    }

    const QString keyName = args.at(0);
    const QString keyType = args.at(1);

    if (keyType != "integer" && keyType != "string" &&
        keyType != "double") {
        qDebug() << "Unknown type";
        return;
    }

    properties.insert(keyName, keyType);
    qDebug() << "Added key:" << keyName << "with type:" << keyType;
}

void CommandWatcher::setCommand(const QString& command)
{
    QStringList parts = command.split("=");

    const QString keyName = parts.at(0).trimmed();
    const QString value = parts.at(1).trimmed();

    if (! properties.contains(keyName)) {
        qDebug() << "ERROR: key" << keyName << "not known/added";
        return;
    }

    const QString keyType = properties.value(keyName);
    QVariant v;

    if (keyType == "integer")
        v = QVariant(value.toInt());
    else if (keyType == "string")
        v = QVariant(value);
    else if (keyType == "double")
        v = QVariant(value.toDouble());

    qDebug() << "Setting key:" << keyName << "to value:" << value << QString("(" + keyType + ")");
    Property(keyName).setValue(v);
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: precompiled_lingucomponent.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: kz $ $Date: 2007-05-10 15:14:04 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): Generated on 2006-09-01 17:49:51.030070

#ifdef PRECOMPILED_HEADERS
#include <tools/debug.hxx>
#endif

<commit_msg>INTEGRATION: CWS changefileheader (1.3.68); FILE MERGED 2008/03/31 16:25:02 rt 1.3.68.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: precompiled_lingucomponent.hxx,v $
 * $Revision: 1.4 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): Generated on 2006-09-01 17:49:51.030070

#ifdef PRECOMPILED_HEADERS
#include <tools/debug.hxx>
#endif

<|endoftext|>


<commit_before>/***************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: altstrfunc.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: rt $ $Date: 2007-06-22 08:32:42 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_lingucomponent.hxx"

#ifndef _ALT_STRFUNC_HXX_
#include "altstrfunc.hxx"
#endif

#include <sal/types.h>

std::string upperCase(const std::string &s) {
    std::string upper(s);
    for(size_t i = 0; i < s.length(); i++)
        upper[i] = sal::static_int_cast< char >( toupper(upper[i]) );
    return upper;
}

int start(const std::string &s1, const std::string &s2){
    int i;
    int ret = 0;

    for(i = 0; s2[i] && s1[i] && !ret; i++){
        ret = toupper(s1[i]) - toupper(s2[i]);
        if(s1[i] == '.' || s2[i] == '.'){ret = 0;}//. is a neutral character
    }
    return ret;
}

<commit_msg>INTEGRATION: CWS tl48_SRC680 (1.4.36); FILE MERGED 2007/10/22 11:07:01 tl 1.4.36.3: warning fixed 2007/10/22 11:05:15 tl 1.4.36.2: warning fixed 2007/10/17 15:05:18 tl 1.4.36.1: #i81311# fix warning with non-pro build and a possible crash<commit_after>/***************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: altstrfunc.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: hr $ $Date: 2007-11-01 10:54:22 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_lingucomponent.hxx"

#ifndef _ALT_STRFUNC_HXX_
#include "altstrfunc.hxx"
#endif

#include <sal/types.h>

std::string upperCase(const std::string &s) {
    std::string upper(s);
    for(size_t i = 0; i < s.length(); i++)
        upper[i] = sal::static_int_cast< char >( toupper(upper[i]) );
    return upper;
}

int start( const std::string &s1, const std::string &s2 ){
    size_t i;
    int ret = 0;

    size_t min = s1.length();
    if (min > s2.length())
        min = s2.length();

    for(i = 0; i < min && s2[i] && s1[i] && !ret; i++){
        ret = toupper(s1[i]) - toupper(s2[i]);
        if(s1[i] == '.' || s2[i] == '.'){ret = 0;}//. is a neutral character
    }
    return ret;
}

<|endoftext|>


<commit_before>/*
 *
 * BitcoinLikeTransactionDatabaseHelper
 * ledger-core
 *
 * Created by Pierre Pollastri on 02/06/2017.
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Ledger
 *
 * 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 <crypto/SHA256.hpp>
#include "BitcoinLikeTransactionDatabaseHelper.h"
#include <wallet/common/database/BlockDatabaseHelper.h>
#include <database/soci-option.h>
#include <database/soci-date.h>
#include <database/soci-number.h>

#include <iostream>
using namespace std;

using namespace soci;

namespace ledger {
    namespace core {

        bool BitcoinLikeTransactionDatabaseHelper::transactionExists(soci::session &sql, const std::string &btcTxUid) {
            int32_t count = 0;
            sql << "SELECT COUNT(*) FROM bitcoin_transactions WHERE transaction_uid = :btcTxUid", use(btcTxUid), into(count);
            return count == 1;
        }

        std::string BitcoinLikeTransactionDatabaseHelper::createBitcoinTransactionUid(const std::string& accountUid, const std::string& txHash) {
            auto result = SHA256::stringToHexHash(fmt::format("uid:{}+{}", accountUid, txHash));
            return result;
        }

        std::string BitcoinLikeTransactionDatabaseHelper::createInputUid(const std::string& accountUid,
                                                                         int32_t previousOutputIndex,
                                                                         const std::string &previousTxHash,
                                                                         const std::string &coinbase) {
            return SHA256::stringToHexHash(fmt::format("uid:{}+{}+{}+{}", accountUid, previousOutputIndex, previousTxHash, coinbase));
        }

        bool BitcoinLikeTransactionDatabaseHelper::getTransactionByHash(soci::session &sql,
                                                                        const std::string &hash,
                                                                        const std::string &accountUid,
                                                                        BitcoinLikeBlockchainExplorerTransaction &out) {
            rowset<row> rows = (sql.prepare <<
                    "SELECT  tx.hash, tx.version, tx.time, tx.locktime, "
                            "block.hash, block.height, block.time, block.currency_name "
                            "FROM bitcoin_transactions AS tx "
                            "LEFT JOIN blocks AS block ON tx.block_uid = block.uid "
                            "WHERE tx.hash = :hash", use(hash)
            );
            for (auto& row : rows) {
                inflateTransaction(sql, row, accountUid, out);
                return true;
            }
            return false;
        }

        bool BitcoinLikeTransactionDatabaseHelper::inflateTransaction(soci::session &sql,
                                                                      const soci::row &row,
                                                                      const std::string &accountUid,
                                                                      BitcoinLikeBlockchainExplorerTransaction &out) {
            out.hash = row.get<std::string>(0);
            out.version = (uint32_t) row.get<int32_t>(1);
            out.receivedAt = row.get<std::chrono::system_clock::time_point>(2);
            out.lockTime = (uint64_t) row.get<int>(3);
            if (row.get_indicator(4) != i_null) {
                BitcoinLikeBlockchainExplorer::Block block;
                block.hash = row.get<std::string>(4);
                block.height = get_number<uint64_t>(row, 5);
                block.time = row.get<std::chrono::system_clock::time_point>(6);
                block.currencyName = row.get<std::string>(7);
                out.block = block;
            }
            // Fetch inputs
            rowset<soci::row> inputRows = (sql.prepare <<
                "SELECT DISTINCT ON(ti.input_idx) ti.input_idx, i.previous_output_idx, i.previous_tx_hash, i.amount, i.address, i.coinbase,"
                        "i.sequence "
                "FROM bitcoin_transaction_inputs AS ti "
                "INNER JOIN bitcoin_inputs AS i ON ti.input_uid = i.uid "
                "WHERE ti.transaction_hash = :hash ORDER BY ti.input_idx", use(out.hash)
            );
            for (auto& inputRow : inputRows) {
                BitcoinLikeBlockchainExplorerInput input;
                input.index = get_number<uint64_t>(inputRow, 0);
                input.previousTxOutputIndex = inputRow.get<Option<int>>(1).map<uint32_t>([] (const int& v) {
                    return (uint32_t) v;
                });
                input.previousTxHash = inputRow.get<Option<std::string>>(2);
                input.value = inputRow.get<Option<long long>>(3).map<BigInt>([] (const unsigned long long& v) {
                    return BigInt(v);
                });
                input.address = inputRow.get<Option<std::string>>(4);
                input.coinbase = inputRow.get<Option<std::string>>(5);
                input.sequence = get_number<uint32_t>(inputRow, 6);
                out.inputs.push_back(input);
            }

            // Fetch outputs
            //Check if the output belongs to this account
            //solve case of 2 accounts in DB one as sender and one as receiver
            //of same transaction (filter on account_uid won't solve the issue because
            //bitcoin_outputs going to external accounts have NULL account_uid)
            auto btcTxUid = BitcoinLikeTransactionDatabaseHelper::createBitcoinTransactionUid(accountUid, out.hash);
            rowset<soci::row> outputRows = (sql.prepare <<
                    "SELECT idx, amount, script, address, block_height, replaceable "
                    "FROM bitcoin_outputs WHERE transaction_hash = :hash AND transaction_uid = :tx_uid "
                    "ORDER BY idx", use(out.hash), use(btcTxUid)
            );

            for (auto& outputRow : outputRows) {
                BitcoinLikeBlockchainExplorerOutput output;
                output.index = (uint64_t) outputRow.get<int>(0);
                output.value.assignScalar(outputRow.get<long long>(1));
                output.script = outputRow.get<std::string>(2);
                output.address = outputRow.get<Option<std::string>>(3);
                if (outputRow.get_indicator(4) != i_null) {
                    output.blockHeight = soci::get_number<uint64_t>(outputRow, 4);
                }
                output.replaceable = soci::get_number<int>(outputRow, 5) == 1;
                out.outputs.push_back(std::move(output));
            }

            // Enjoy the silence.
            return true;
        }

        void
        BitcoinLikeTransactionDatabaseHelper::getMempoolTransactions(soci::session &sql, const std::string &accountUid,
                                                                     std::vector<BitcoinLikeBlockchainExplorerTransaction> &out) {
            // Query all transaction
            rowset<row> txRows = (sql.prepare <<
                    "SELECT  tx.hash, tx.version, tx.time, tx.locktime, "
                    "block.hash, block.height, block.time, block.currency_name "
                    "FROM bitcoin_transactions AS tx "
                    "LEFT JOIN blocks AS block ON tx.block_uid = block.uid "
                    "WHERE tx.hash IN ("
                      "SELECT tx.hash FROM operations AS op "
                      "JOIN bitcoin_operations AS bop ON bop.uid = op.uid  "
                      "JOIN bitcoin_transactions AS tx ON tx.hash = bop.transaction_hash "
                      "WHERE op.account_uid = :uid AND op.block_uid IS NULL"
                    ")", use(accountUid));
            // Inflate all transactions
            for (const auto& txRow : txRows) {
                BitcoinLikeBlockchainExplorerTransaction tx;
                inflateTransaction(sql, txRow, accountUid, tx);
                out.emplace_back(std::move(tx));
            }
        }

        void BitcoinLikeTransactionDatabaseHelper::removeAllMempoolOperation(soci::session &sql,
                                                                             const std::string &accountUid) {
            rowset<std::string> rows = (sql.prepare <<
                    "SELECT transaction_uid FROM bitcoin_operations AS bop "
                    "JOIN operations AS op ON bop.uid = op.uid "
                    "WHERE op.account_uid = :uid AND op.block_uid IS NULL", use(accountUid)
            );
            std::vector<std::string> txToDelete(rows.begin(), rows.end());
            if (!txToDelete.empty()) {
                sql << "DELETE FROM bitcoin_inputs WHERE uid IN ("
                       "SELECT input_uid FROM bitcoin_transaction_inputs "
                       "WHERE transaction_uid IN(:uids)"
                       ")", use(txToDelete);
                sql << "DELETE FROM operations WHERE account_uid = :uid AND block_uid is NULL", use(accountUid);
                sql << "DELETE FROM bitcoin_transactions "
                       "WHERE transaction_uid IN (:uids)", use(txToDelete);
            }
        }

        void BitcoinLikeTransactionDatabaseHelper::eraseDataSince(
                soci::session &sql,
                const std::string &accountUid,
                const std::chrono::system_clock::time_point & date) {

            rowset<std::string> rows = (sql.prepare <<
                "SELECT transaction_uid FROM bitcoin_operations AS bop "
                "JOIN operations AS op ON bop.uid = op.uid "
                "WHERE op.account_uid = :uid AND op.date >= :date",
                use(accountUid), use(date)
            );
            std::vector<std::string> txToDelete(rows.begin(), rows.end());
            if (!txToDelete.empty()) {
                sql << "DELETE FROM operations WHERE account_uid = :account_uid AND date >= :date",
                    use(accountUid), use(date);
                sql << "DELETE FROM bitcoin_inputs WHERE uid IN ("
                       "SELECT input_uid FROM bitcoin_transaction_inputs "
                       "WHERE transaction_uid IN(:uids)"
                       ")", use(txToDelete);
                sql << "DELETE FROM bitcoin_transactions "
                       "WHERE transaction_uid IN (:uids)", use(txToDelete);
            }
        }

    }
}
<commit_msg>VG-2191: deduplicate inputs with txuid instead of DISTINCT ON clause<commit_after>/*
 *
 * BitcoinLikeTransactionDatabaseHelper
 * ledger-core
 *
 * Created by Pierre Pollastri on 02/06/2017.
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Ledger
 *
 * 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 <crypto/SHA256.hpp>
#include "BitcoinLikeTransactionDatabaseHelper.h"
#include <wallet/common/database/BlockDatabaseHelper.h>
#include <database/soci-option.h>
#include <database/soci-date.h>
#include <database/soci-number.h>

#include <iostream>
using namespace std;

using namespace soci;

namespace ledger {
    namespace core {

        bool BitcoinLikeTransactionDatabaseHelper::transactionExists(soci::session &sql, const std::string &btcTxUid) {
            int32_t count = 0;
            sql << "SELECT COUNT(*) FROM bitcoin_transactions WHERE transaction_uid = :btcTxUid", use(btcTxUid), into(count);
            return count == 1;
        }

        std::string BitcoinLikeTransactionDatabaseHelper::createBitcoinTransactionUid(const std::string& accountUid, const std::string& txHash) {
            auto result = SHA256::stringToHexHash(fmt::format("uid:{}+{}", accountUid, txHash));
            return result;
        }

        std::string BitcoinLikeTransactionDatabaseHelper::createInputUid(const std::string& accountUid,
                                                                         int32_t previousOutputIndex,
                                                                         const std::string &previousTxHash,
                                                                         const std::string &coinbase) {
            return SHA256::stringToHexHash(fmt::format("uid:{}+{}+{}+{}", accountUid, previousOutputIndex, previousTxHash, coinbase));
        }

        bool BitcoinLikeTransactionDatabaseHelper::getTransactionByHash(soci::session &sql,
                                                                        const std::string &hash,
                                                                        const std::string &accountUid,
                                                                        BitcoinLikeBlockchainExplorerTransaction &out) {
            rowset<row> rows = (sql.prepare <<
                    "SELECT  tx.hash, tx.version, tx.time, tx.locktime, "
                            "block.hash, block.height, block.time, block.currency_name "
                            "FROM bitcoin_transactions AS tx "
                            "LEFT JOIN blocks AS block ON tx.block_uid = block.uid "
                            "WHERE tx.hash = :hash", use(hash)
            );
            for (auto& row : rows) {
                inflateTransaction(sql, row, accountUid, out);
                return true;
            }
            return false;
        }

        bool BitcoinLikeTransactionDatabaseHelper::inflateTransaction(soci::session &sql,
                                                                      const soci::row &row,
                                                                      const std::string &accountUid,
                                                                      BitcoinLikeBlockchainExplorerTransaction &out) {
            out.hash = row.get<std::string>(0);
            out.version = (uint32_t) row.get<int32_t>(1);
            out.receivedAt = row.get<std::chrono::system_clock::time_point>(2);
            out.lockTime = (uint64_t) row.get<int>(3);
            if (row.get_indicator(4) != i_null) {
                BitcoinLikeBlockchainExplorer::Block block;
                block.hash = row.get<std::string>(4);
                block.height = get_number<uint64_t>(row, 5);
                block.time = row.get<std::chrono::system_clock::time_point>(6);
                block.currencyName = row.get<std::string>(7);
                out.block = block;
            }

            auto btcTxUid = BitcoinLikeTransactionDatabaseHelper::createBitcoinTransactionUid(accountUid, out.hash);

            // Fetch inputs
            rowset<soci::row> inputRows = (sql.prepare <<
                "SELECT ti.input_idx, i.previous_output_idx, i.previous_tx_hash, i.amount, i.address, i.coinbase,"
                        "i.sequence "
                "FROM bitcoin_transaction_inputs AS ti "
                "INNER JOIN bitcoin_inputs AS i ON ti.input_uid = i.uid "
                "WHERE ti.transaction_uid = :txuid ORDER BY ti.input_idx", use(btcTxUid));
            for (auto& inputRow : inputRows) {
                BitcoinLikeBlockchainExplorerInput input;
                input.index = get_number<uint64_t>(inputRow, 0);
                input.previousTxOutputIndex = inputRow.get<Option<int>>(1).map<uint32_t>([] (const int& v) {
                    return (uint32_t) v;
                });
                input.previousTxHash = inputRow.get<Option<std::string>>(2);
                input.value = inputRow.get<Option<long long>>(3).map<BigInt>([] (const unsigned long long& v) {
                    return BigInt(v);
                });
                input.address = inputRow.get<Option<std::string>>(4);
                input.coinbase = inputRow.get<Option<std::string>>(5);
                input.sequence = get_number<uint32_t>(inputRow, 6);
                out.inputs.push_back(input);
            }

            // Fetch outputs
            //Check if the output belongs to this account
            //solve case of 2 accounts in DB one as sender and one as receiver
            //of same transaction (filter on account_uid won't solve the issue because
            //bitcoin_outputs going to external accounts have NULL account_uid)
            rowset<soci::row> outputRows = (sql.prepare <<
                    "SELECT idx, amount, script, address, block_height, replaceable "
                    "FROM bitcoin_outputs WHERE transaction_hash = :hash AND transaction_uid = :tx_uid "
                    "ORDER BY idx", use(out.hash), use(btcTxUid)
            );

            for (auto& outputRow : outputRows) {
                BitcoinLikeBlockchainExplorerOutput output;
                output.index = (uint64_t) outputRow.get<int>(0);
                output.value.assignScalar(outputRow.get<long long>(1));
                output.script = outputRow.get<std::string>(2);
                output.address = outputRow.get<Option<std::string>>(3);
                if (outputRow.get_indicator(4) != i_null) {
                    output.blockHeight = soci::get_number<uint64_t>(outputRow, 4);
                }
                output.replaceable = soci::get_number<int>(outputRow, 5) == 1;
                out.outputs.push_back(std::move(output));
            }

            // Enjoy the silence.
            return true;
        }

        void
        BitcoinLikeTransactionDatabaseHelper::getMempoolTransactions(soci::session &sql, const std::string &accountUid,
                                                                     std::vector<BitcoinLikeBlockchainExplorerTransaction> &out) {
            // Query all transaction
            rowset<row> txRows = (sql.prepare <<
                    "SELECT  tx.hash, tx.version, tx.time, tx.locktime, "
                    "block.hash, block.height, block.time, block.currency_name "
                    "FROM bitcoin_transactions AS tx "
                    "LEFT JOIN blocks AS block ON tx.block_uid = block.uid "
                    "WHERE tx.hash IN ("
                      "SELECT tx.hash FROM operations AS op "
                      "JOIN bitcoin_operations AS bop ON bop.uid = op.uid  "
                      "JOIN bitcoin_transactions AS tx ON tx.hash = bop.transaction_hash "
                      "WHERE op.account_uid = :uid AND op.block_uid IS NULL"
                    ")", use(accountUid));
            // Inflate all transactions
            for (const auto& txRow : txRows) {
                BitcoinLikeBlockchainExplorerTransaction tx;
                inflateTransaction(sql, txRow, accountUid, tx);
                out.emplace_back(std::move(tx));
            }
        }

        void BitcoinLikeTransactionDatabaseHelper::removeAllMempoolOperation(soci::session &sql,
                                                                             const std::string &accountUid) {
            rowset<std::string> rows = (sql.prepare <<
                    "SELECT transaction_uid FROM bitcoin_operations AS bop "
                    "JOIN operations AS op ON bop.uid = op.uid "
                    "WHERE op.account_uid = :uid AND op.block_uid IS NULL", use(accountUid)
            );
            std::vector<std::string> txToDelete(rows.begin(), rows.end());
            if (!txToDelete.empty()) {
                sql << "DELETE FROM bitcoin_inputs WHERE uid IN ("
                       "SELECT input_uid FROM bitcoin_transaction_inputs "
                       "WHERE transaction_uid IN(:uids)"
                       ")", use(txToDelete);
                sql << "DELETE FROM operations WHERE account_uid = :uid AND block_uid is NULL", use(accountUid);
                sql << "DELETE FROM bitcoin_transactions "
                       "WHERE transaction_uid IN (:uids)", use(txToDelete);
            }
        }

        void BitcoinLikeTransactionDatabaseHelper::eraseDataSince(
                soci::session &sql,
                const std::string &accountUid,
                const std::chrono::system_clock::time_point & date) {

            rowset<std::string> rows = (sql.prepare <<
                "SELECT transaction_uid FROM bitcoin_operations AS bop "
                "JOIN operations AS op ON bop.uid = op.uid "
                "WHERE op.account_uid = :uid AND op.date >= :date",
                use(accountUid), use(date)
            );
            std::vector<std::string> txToDelete(rows.begin(), rows.end());
            if (!txToDelete.empty()) {
                sql << "DELETE FROM operations WHERE account_uid = :account_uid AND date >= :date",
                    use(accountUid), use(date);
                sql << "DELETE FROM bitcoin_inputs WHERE uid IN ("
                       "SELECT input_uid FROM bitcoin_transaction_inputs "
                       "WHERE transaction_uid IN(:uids)"
                       ")", use(txToDelete);
                sql << "DELETE FROM bitcoin_transactions "
                       "WHERE transaction_uid IN (:uids)", use(txToDelete);
            }
        }

    }
}
<|endoftext|>