<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2019.  All rights reserved
 */
#ifndef _Stroika_Foundation_IO_Network_InternetAddress_inl_
#define _Stroika_Foundation_IO_Network_InternetAddress_inl_ 1

/*
 ********************************************************************************
 ***************************** Implementation Details ***************************
 ********************************************************************************
 */
#include "../../Configuration/Endian.h"
#include "../../Memory/Bits.h"

namespace Stroika::Foundation::IO::Network {

    /*
     ********************************************************************************
     *********************** IO::Network::InternetAddress ***************************
     ********************************************************************************
     */
    constexpr InternetAddress::InternetAddress ()
        : fAddressFamily_{AddressFamily::UNKNOWN}
        , fV4_{}
    {
    }
    constexpr InternetAddress::InternetAddress (const in_addr_t& i)
        : fAddressFamily_ (AddressFamily::V4)
#if qPlatform_POSIX
        , fV4_
    {
        i
    }
#elif qPlatform_Windows
        , fV4_
    {
        in_addr
        {
            {
                static_cast<uint8_t> (Memory::BitSubstring (i, 0, 8)),
                    static_cast<uint8_t> (Memory::BitSubstring (i, 8, 16)),
                    static_cast<uint8_t> (Memory::BitSubstring (i, 16, 24)),
                    static_cast<uint8_t> (Memory::BitSubstring (i, 24, 32))
            }
        }
    }
#endif
    {
#if qPlatform_Windows
        Assert (fV4_.s_addr == i);
#endif
    }
    inline InternetAddress::InternetAddress (const in_addr_t& i, ByteOrder byteOrder)
        : fAddressFamily_ (AddressFamily::V4)
#if qPlatform_POSIX
        , fV4_
    {
        i
    }
#endif
    {
#if qPlatform_Windows
        fV4_.s_addr = i;
#endif
        if (byteOrder == ByteOrder::Host) {
            fV4_.s_addr = htonl (fV4_.s_addr); //NB no ':' cuz some systems use macro
        }
    }
    inline constexpr InternetAddress::InternetAddress (const in_addr& i)
        : fAddressFamily_{AddressFamily::V4}
        , fV4_{i}
    {
    }
    inline InternetAddress::InternetAddress (const in_addr& i, ByteOrder byteOrder)
        : fAddressFamily_{AddressFamily::V4}
        , fV4_{i}
    {
        if (byteOrder == ByteOrder::Host) {
            fV4_.s_addr = htonl (fV4_.s_addr); //NB no ':' cuz some systems use macro
        }
    }
    constexpr InternetAddress::InternetAddress (byte octet1, byte octet2, byte octet3, byte octet4)
        : InternetAddress (array<byte, 4>{octet1, octet2, octet3, octet4})
    {
    }
    constexpr InternetAddress::InternetAddress (uint8_t octet1, uint8_t octet2, uint8_t octet3, uint8_t octet4)
        : InternetAddress (array<uint8_t, 4>{octet1, octet2, octet3, octet4})
    {
    }
    constexpr InternetAddress::InternetAddress (tuple<uint8_t, uint8_t, uint8_t, uint8_t> octets)
        : InternetAddress (array<uint8_t, 4>{get<0> (octets), get<1> (octets), get<2> (octets), get<3> (octets)})
    {
    }
    constexpr InternetAddress::InternetAddress (array<uint8_t, 4> octets, AddressFamily af)
        : fAddressFamily_{af}
        , fArray_4_uint_{octets}
    {
    }
    constexpr InternetAddress::InternetAddress (array<byte, 4> octets, AddressFamily af)
        : fAddressFamily_{af}
        , fArray_4_byte_{octets}
    {
    }
    constexpr InternetAddress::InternetAddress (const in6_addr& i)
        : fAddressFamily_ (AddressFamily::V6)
        , fV6_{i}
    {
    }
    constexpr InternetAddress::InternetAddress (array<uint8_t, 16> octets, AddressFamily af)
        : fAddressFamily_{af}
        , fArray_16_uint_{octets}
    {
    }
    constexpr InternetAddress::InternetAddress (array<byte, 16> octets, AddressFamily af)
        : fAddressFamily_{af}
        , fArray_16_byte_{octets}
    {
    }
    template <typename ITERABLE_OF_UINT8OrByte, enable_if_t<Configuration::IsIterableOfT_v<ITERABLE_OF_UINT8OrByte, byte> or Configuration::IsIterableOfT_v<ITERABLE_OF_UINT8OrByte, uint8_t>>*>
    inline InternetAddress::InternetAddress (ITERABLE_OF_UINT8OrByte octets, AddressFamily af)
        : fAddressFamily_{af}
    {
        Require (af != AddressFamily::V4 or octets.size () == 4);
        Require (af != AddressFamily::V6 or octets.size () == 16);
        size_t i = 0;
        for (auto b : octets) {
            fArray_16_uint_[i++] = static_cast<uint8_t> (b);
        }
    }
    constexpr bool InternetAddress::empty () const
    {
        return fAddressFamily_ == AddressFamily::UNKNOWN;
    }
    inline void InternetAddress::clear ()
    {
        fAddressFamily_ = AddressFamily::UNKNOWN;
    }
    constexpr InternetAddress::AddressFamily InternetAddress::GetAddressFamily () const
    {
        return fAddressFamily_;
    }
    constexpr optional<size_t> InternetAddress::GetAddressSize () const
    {
        switch (GetAddressFamily ()) {
            case AddressFamily::V4:
                return 4;
            case AddressFamily::V6:
                return 16;
            default:
                return nullopt;
        }
    }
    template <>
    String InternetAddress::As<String> () const;
#if qPlatform_POSIX
    template <>
    inline in_addr_t InternetAddress::As<in_addr_t> () const
    {
        Require (fAddressFamily_ == AddressFamily::V4);
        return fV4_.s_addr;
    }
#endif
    template <>
    constexpr in_addr InternetAddress::As<in_addr> () const
    {
        Require (fAddressFamily_ == AddressFamily::V4);
        return fV4_;
    }
    template <>
    constexpr array<uint8_t, 4> InternetAddress::As<array<uint8_t, 4>> () const
    {
        Require (GetAddressSize () == 4u);
        return fArray_4_uint_;
    }
    template <>
    constexpr array<byte, 4> InternetAddress::As<array<byte, 4>> () const
    {
        Require (GetAddressSize () == 4u);
        return fArray_4_byte_;
    }
    template <>
    constexpr array<uint8_t, 16> InternetAddress::As<array<uint8_t, 16>> () const
    {
        Require (GetAddressSize () == 16u);
        return fArray_16_uint_;
    }
    template <>
    constexpr array<byte, 16> InternetAddress::As<array<byte, 16>> () const
    {
        Require (GetAddressSize () == 16u);
        return fArray_16_byte_;
    }
    template <>
    inline vector<byte> InternetAddress::As<vector<byte>> () const
    {
        Require (GetAddressSize ().has_value ());
        Assert (*GetAddressSize () <= 16);
        return vector<byte>{fArray_16_byte_.begin (), fArray_16_byte_.begin () + *GetAddressSize ()};
    }
    template <>
    inline vector<uint8_t> InternetAddress::As<vector<uint8_t>> () const
    {
        Require (GetAddressSize ().has_value ());
        Assert (*GetAddressSize () <= 16);
        return vector<uint8_t>{fArray_16_uint_.begin (), fArray_16_uint_.begin () + *GetAddressSize ()};
    }
    template <>
    [[deprecated ("array<byte,4> works better so this is deprecated since Stroika v2.1d13")]] inline tuple<uint8_t, uint8_t, uint8_t, uint8_t> InternetAddress::As<tuple<uint8_t, uint8_t, uint8_t, uint8_t>> () const
    {
        Require (fAddressFamily_ == AddressFamily::V4);
        switch (Configuration::GetEndianness ()) {
            case Configuration::Endian::eLittleByte:
                return make_tuple<uint8_t, uint8_t, uint8_t, uint8_t> (
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 0, 8)),
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 8, 16)),
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 16, 24)),
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 24, 32)));
            case Configuration::Endian::eBigByte:
                return make_tuple<uint8_t, uint8_t, uint8_t, uint8_t> (
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 24, 32)),
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 16, 24)),
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 8, 16)),
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 0, 8)));
            default:
                AssertNotImplemented ();
                return tuple<uint8_t, uint8_t, uint8_t, uint8_t>{};
        }
    }
    template <>
    constexpr in6_addr InternetAddress::As<in6_addr> () const
    {
        Require (fAddressFamily_ == AddressFamily::V6);
        return fV6_;
    }
    template <>
    inline in_addr InternetAddress::As<in_addr> (ByteOrder byteOrder) const
    {
        Require (fAddressFamily_ == AddressFamily::V4);
        if (byteOrder == ByteOrder::Network) {
            return fV4_;
        }
        else {
            in_addr tmp = fV4_;
            tmp.s_addr  = ntohl (tmp.s_addr);
            return tmp;
        }
    }

    /*
     ********************************************************************************
     ********************** InternetAddress::ThreeWayComparer ***********************
     ********************************************************************************
     */
    constexpr int InternetAddress::ThreeWayComparer::operator() (const InternetAddress& lhs, const InternetAddress& rhs) const
    {
        if (int cmp = Common::ThreeWayCompare (lhs.fAddressFamily_, rhs.fAddressFamily_)) {
            return cmp;
        }
        switch (lhs.fAddressFamily_) {
            case AddressFamily::UNKNOWN: {
                return 0;
            } break;
            case AddressFamily::V4: {
                return Common::COMPARE_EQUAL (lhs.fArray_4_uint_.begin (), lhs.fArray_4_uint_.end (), rhs.fArray_4_uint_.begin ());
            } break;
            case AddressFamily::V6: {
                return Common::COMPARE_EQUAL (lhs.fArray_16_uint_.begin (), lhs.fArray_16_uint_.end (), rhs.fArray_16_uint_.begin ());
            } break;
        }
        //AssertNotReached ();  @todo - this really should be an assertion failure, but tricky cuz constexpr function could fix with template)
        return 0;
    }

    /*
     ********************************************************************************
     ************************* InternetAddress operators ****************************
     ********************************************************************************
     */
    inline bool operator< (const InternetAddress& lhs, const InternetAddress& rhs)
    {
        return Common::ThreeWayCompare (lhs, rhs) < 0;
    }
    inline bool operator<= (const InternetAddress& lhs, const InternetAddress& rhs)
    {
        return Common::ThreeWayCompare (lhs, rhs) <= 0;
    }
    inline bool operator== (const InternetAddress& lhs, const InternetAddress& rhs)
    {
        return Common::ThreeWayCompare (lhs, rhs) == 0;
    }
    inline bool operator!= (const InternetAddress& lhs, const InternetAddress& rhs)
    {
        return Common::ThreeWayCompare (lhs, rhs) != 0;
    }
    inline bool operator>= (const InternetAddress& lhs, const InternetAddress& rhs)
    {
        return Common::ThreeWayCompare (lhs, rhs) >= 0;
    }
    inline bool operator> (const InternetAddress& lhs, const InternetAddress& rhs)
    {
        return Common::ThreeWayCompare (lhs, rhs) > 0;
    }

    namespace V4 {
        constexpr InternetAddress kAddrAny{in_addr{}};
    }
    namespace V6 {
        constexpr InternetAddress kAddrAny{in6_addr{}};
    }
    namespace V4 {
        constexpr InternetAddress kLocalhost{0x7f, 0x0, 0x0, 0x1};
    }
    namespace V6 {
        constexpr InternetAddress kLocalhost{in6_addr{{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}}};
    }
    namespace V6 {
        constexpr InternetAddress kV4MappedLocalhost{in6_addr{{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1}}}};
    }

}

#endif /*_Stroika_Foundation_IO_Network_InternetAddress_inl_*/
<commit_msg>use Memory::MemCmp not Common::COMPARE_EQUAL () - was wrong anyhow<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2019.  All rights reserved
 */
#ifndef _Stroika_Foundation_IO_Network_InternetAddress_inl_
#define _Stroika_Foundation_IO_Network_InternetAddress_inl_ 1

/*
 ********************************************************************************
 ***************************** Implementation Details ***************************
 ********************************************************************************
 */
#include "../../Configuration/Endian.h"
#include "../../Memory/Bits.h"
#include "../../Memory/Common.h"

namespace Stroika::Foundation::IO::Network {

    /*
     ********************************************************************************
     *********************** IO::Network::InternetAddress ***************************
     ********************************************************************************
     */
    constexpr InternetAddress::InternetAddress ()
        : fAddressFamily_{AddressFamily::UNKNOWN}
        , fV4_{}
    {
    }
    constexpr InternetAddress::InternetAddress (const in_addr_t& i)
        : fAddressFamily_ (AddressFamily::V4)
#if qPlatform_POSIX
        , fV4_
    {
        i
    }
#elif qPlatform_Windows
        , fV4_
    {
        in_addr
        {
            {
                static_cast<uint8_t> (Memory::BitSubstring (i, 0, 8)),
                    static_cast<uint8_t> (Memory::BitSubstring (i, 8, 16)),
                    static_cast<uint8_t> (Memory::BitSubstring (i, 16, 24)),
                    static_cast<uint8_t> (Memory::BitSubstring (i, 24, 32))
            }
        }
    }
#endif
    {
#if qPlatform_Windows
        Assert (fV4_.s_addr == i);
#endif
    }
    inline InternetAddress::InternetAddress (const in_addr_t& i, ByteOrder byteOrder)
        : fAddressFamily_ (AddressFamily::V4)
#if qPlatform_POSIX
        , fV4_
    {
        i
    }
#endif
    {
#if qPlatform_Windows
        fV4_.s_addr = i;
#endif
        if (byteOrder == ByteOrder::Host) {
            fV4_.s_addr = htonl (fV4_.s_addr); //NB no ':' cuz some systems use macro
        }
    }
    inline constexpr InternetAddress::InternetAddress (const in_addr& i)
        : fAddressFamily_{AddressFamily::V4}
        , fV4_{i}
    {
    }
    inline InternetAddress::InternetAddress (const in_addr& i, ByteOrder byteOrder)
        : fAddressFamily_{AddressFamily::V4}
        , fV4_{i}
    {
        if (byteOrder == ByteOrder::Host) {
            fV4_.s_addr = htonl (fV4_.s_addr); //NB no ':' cuz some systems use macro
        }
    }
    constexpr InternetAddress::InternetAddress (byte octet1, byte octet2, byte octet3, byte octet4)
        : InternetAddress (array<byte, 4>{octet1, octet2, octet3, octet4})
    {
    }
    constexpr InternetAddress::InternetAddress (uint8_t octet1, uint8_t octet2, uint8_t octet3, uint8_t octet4)
        : InternetAddress (array<uint8_t, 4>{octet1, octet2, octet3, octet4})
    {
    }
    constexpr InternetAddress::InternetAddress (tuple<uint8_t, uint8_t, uint8_t, uint8_t> octets)
        : InternetAddress (array<uint8_t, 4>{get<0> (octets), get<1> (octets), get<2> (octets), get<3> (octets)})
    {
    }
    constexpr InternetAddress::InternetAddress (array<uint8_t, 4> octets, AddressFamily af)
        : fAddressFamily_{af}
        , fArray_4_uint_{octets}
    {
    }
    constexpr InternetAddress::InternetAddress (array<byte, 4> octets, AddressFamily af)
        : fAddressFamily_{af}
        , fArray_4_byte_{octets}
    {
    }
    constexpr InternetAddress::InternetAddress (const in6_addr& i)
        : fAddressFamily_ (AddressFamily::V6)
        , fV6_{i}
    {
    }
    constexpr InternetAddress::InternetAddress (array<uint8_t, 16> octets, AddressFamily af)
        : fAddressFamily_{af}
        , fArray_16_uint_{octets}
    {
    }
    constexpr InternetAddress::InternetAddress (array<byte, 16> octets, AddressFamily af)
        : fAddressFamily_{af}
        , fArray_16_byte_{octets}
    {
    }
    template <typename ITERABLE_OF_UINT8OrByte, enable_if_t<Configuration::IsIterableOfT_v<ITERABLE_OF_UINT8OrByte, byte> or Configuration::IsIterableOfT_v<ITERABLE_OF_UINT8OrByte, uint8_t>>*>
    inline InternetAddress::InternetAddress (ITERABLE_OF_UINT8OrByte octets, AddressFamily af)
        : fAddressFamily_{af}
    {
        Require (af != AddressFamily::V4 or octets.size () == 4);
        Require (af != AddressFamily::V6 or octets.size () == 16);
        size_t i = 0;
        for (auto b : octets) {
            fArray_16_uint_[i++] = static_cast<uint8_t> (b);
        }
    }
    constexpr bool InternetAddress::empty () const
    {
        return fAddressFamily_ == AddressFamily::UNKNOWN;
    }
    inline void InternetAddress::clear ()
    {
        fAddressFamily_ = AddressFamily::UNKNOWN;
    }
    constexpr InternetAddress::AddressFamily InternetAddress::GetAddressFamily () const
    {
        return fAddressFamily_;
    }
    constexpr optional<size_t> InternetAddress::GetAddressSize () const
    {
        switch (GetAddressFamily ()) {
            case AddressFamily::V4:
                return 4;
            case AddressFamily::V6:
                return 16;
            default:
                return nullopt;
        }
    }
    template <>
    String InternetAddress::As<String> () const;
#if qPlatform_POSIX
    template <>
    inline in_addr_t InternetAddress::As<in_addr_t> () const
    {
        Require (fAddressFamily_ == AddressFamily::V4);
        return fV4_.s_addr;
    }
#endif
    template <>
    constexpr in_addr InternetAddress::As<in_addr> () const
    {
        Require (fAddressFamily_ == AddressFamily::V4);
        return fV4_;
    }
    template <>
    constexpr array<uint8_t, 4> InternetAddress::As<array<uint8_t, 4>> () const
    {
        Require (GetAddressSize () == 4u);
        return fArray_4_uint_;
    }
    template <>
    constexpr array<byte, 4> InternetAddress::As<array<byte, 4>> () const
    {
        Require (GetAddressSize () == 4u);
        return fArray_4_byte_;
    }
    template <>
    constexpr array<uint8_t, 16> InternetAddress::As<array<uint8_t, 16>> () const
    {
        Require (GetAddressSize () == 16u);
        return fArray_16_uint_;
    }
    template <>
    constexpr array<byte, 16> InternetAddress::As<array<byte, 16>> () const
    {
        Require (GetAddressSize () == 16u);
        return fArray_16_byte_;
    }
    template <>
    inline vector<byte> InternetAddress::As<vector<byte>> () const
    {
        Require (GetAddressSize ().has_value ());
        Assert (*GetAddressSize () <= 16);
        return vector<byte>{fArray_16_byte_.begin (), fArray_16_byte_.begin () + *GetAddressSize ()};
    }
    template <>
    inline vector<uint8_t> InternetAddress::As<vector<uint8_t>> () const
    {
        Require (GetAddressSize ().has_value ());
        Assert (*GetAddressSize () <= 16);
        return vector<uint8_t>{fArray_16_uint_.begin (), fArray_16_uint_.begin () + *GetAddressSize ()};
    }
    template <>
    [[deprecated ("array<byte,4> works better so this is deprecated since Stroika v2.1d13")]] inline tuple<uint8_t, uint8_t, uint8_t, uint8_t> InternetAddress::As<tuple<uint8_t, uint8_t, uint8_t, uint8_t>> () const
    {
        Require (fAddressFamily_ == AddressFamily::V4);
        switch (Configuration::GetEndianness ()) {
            case Configuration::Endian::eLittleByte:
                return make_tuple<uint8_t, uint8_t, uint8_t, uint8_t> (
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 0, 8)),
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 8, 16)),
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 16, 24)),
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 24, 32)));
            case Configuration::Endian::eBigByte:
                return make_tuple<uint8_t, uint8_t, uint8_t, uint8_t> (
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 24, 32)),
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 16, 24)),
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 8, 16)),
                    static_cast<uint8_t> (Memory::BitSubstring (fV4_.s_addr, 0, 8)));
            default:
                AssertNotImplemented ();
                return tuple<uint8_t, uint8_t, uint8_t, uint8_t>{};
        }
    }
    template <>
    constexpr in6_addr InternetAddress::As<in6_addr> () const
    {
        Require (fAddressFamily_ == AddressFamily::V6);
        return fV6_;
    }
    template <>
    inline in_addr InternetAddress::As<in_addr> (ByteOrder byteOrder) const
    {
        Require (fAddressFamily_ == AddressFamily::V4);
        if (byteOrder == ByteOrder::Network) {
            return fV4_;
        }
        else {
            in_addr tmp = fV4_;
            tmp.s_addr  = ntohl (tmp.s_addr);
            return tmp;
        }
    }

    /*
     ********************************************************************************
     ********************** InternetAddress::ThreeWayComparer ***********************
     ********************************************************************************
     */
    constexpr int InternetAddress::ThreeWayComparer::operator() (const InternetAddress& lhs, const InternetAddress& rhs) const
    {
        if (int cmp = Common::ThreeWayCompare (lhs.fAddressFamily_, rhs.fAddressFamily_)) {
            return cmp;
        }
        switch (lhs.fAddressFamily_) {
            case AddressFamily::UNKNOWN: {
                return 0;
            } break;
            case AddressFamily::V4: {
                return Memory::MemCmp (&*lhs.fArray_4_uint_.begin (), &*rhs.fArray_4_uint_.begin (), 4);
            } break;
            case AddressFamily::V6: {
                return Memory::MemCmp (&*lhs.fArray_16_uint_.begin (), &*rhs.fArray_16_uint_.begin (), 16);
            } break;
        }
        //AssertNotReached ();  @todo - this really should be an assertion failure, but tricky cuz constexpr function could fix with template)
        return 0;
    }

    /*
     ********************************************************************************
     ************************* InternetAddress operators ****************************
     ********************************************************************************
     */
    inline bool operator< (const InternetAddress& lhs, const InternetAddress& rhs)
    {
        return Common::ThreeWayCompare (lhs, rhs) < 0;
    }
    inline bool operator<= (const InternetAddress& lhs, const InternetAddress& rhs)
    {
        return Common::ThreeWayCompare (lhs, rhs) <= 0;
    }
    inline bool operator== (const InternetAddress& lhs, const InternetAddress& rhs)
    {
        return Common::ThreeWayCompare (lhs, rhs) == 0;
    }
    inline bool operator!= (const InternetAddress& lhs, const InternetAddress& rhs)
    {
        return Common::ThreeWayCompare (lhs, rhs) != 0;
    }
    inline bool operator>= (const InternetAddress& lhs, const InternetAddress& rhs)
    {
        return Common::ThreeWayCompare (lhs, rhs) >= 0;
    }
    inline bool operator> (const InternetAddress& lhs, const InternetAddress& rhs)
    {
        return Common::ThreeWayCompare (lhs, rhs) > 0;
    }

    namespace V4 {
        constexpr InternetAddress kAddrAny{in_addr{}};
    }
    namespace V6 {
        constexpr InternetAddress kAddrAny{in6_addr{}};
    }
    namespace V4 {
        constexpr InternetAddress kLocalhost{0x7f, 0x0, 0x0, 0x1};
    }
    namespace V6 {
        constexpr InternetAddress kLocalhost{in6_addr{{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}}};
    }
    namespace V6 {
        constexpr InternetAddress kV4MappedLocalhost{in6_addr{{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1}}}};
    }

}

#endif /*_Stroika_Foundation_IO_Network_InternetAddress_inl_*/
<|endoftext|>


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

 Program:   BlueBerry Platform
 Language:  C++
 Date:      $Date$
 Version:   $Revision$

 Copyright (c) German Cancer Research Center, Division of Medical and
 Biological Informatics. All rights reserved.
 See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

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

 =========================================================================*/

#include "berryQtAssistantUtil.h"
#include <berryPlatformUI.h>
#include <berryConfig.h>
#include <berryLog.h>
#include <berryIBundleStorage.h>
#include <berryIWorkbench.h>
#include <berryIWorkbenchPage.h>
#include <berryIWorkbenchPart.h>

#include <QFileInfo>
#include <QProgressDialog>
#include <QMessageBox>
#include <QDir>

namespace berry
{

QProcess* QtAssistantUtil::assistantProcess = 0;
QString QtAssistantUtil::helpCollectionFile;
QString QtAssistantUtil::defaultHelpUrl;
QStringList QtAssistantUtil::registeredBundles;

void QtAssistantUtil::SetHelpColletionFile(const QString& file)
{
  helpCollectionFile = file;
}

void QtAssistantUtil::OpenActivePartHelp()
{
  //Get Plugin-ID
  QString pluginID;
  berry::IWorkbench* currentWorkbench = berry::PlatformUI::GetWorkbench();
  if (currentWorkbench)
  {
    berry::IWorkbenchWindow::Pointer currentWorkbenchWindow = currentWorkbench->GetActiveWorkbenchWindow();
    if (currentWorkbenchWindow)
    {
      berry::IWorkbenchPage::Pointer currentPage = currentWorkbenchWindow->GetActivePage();
      if (currentPage)
      {
        berry::IWorkbenchPart::Pointer currentPart = currentPage->GetActivePart();
        if (currentPart) pluginID = QString::fromStdString(currentPart->GetSite()->GetPluginId());
      }
    }
  }
  //End get Plugin-ID

  QString helpUrl = defaultHelpUrl;
  if (!pluginID.isEmpty() && registeredBundles.contains(pluginID))
    helpUrl = "qthelp://"+pluginID+"/bundle/index.html";

  OpenAssistant(helpUrl);
}

void QtAssistantUtil::OpenAssistant(const QString& startPage)
{
  QString startUrl = startPage;
  if (startUrl.isEmpty()) startUrl = defaultHelpUrl;

  if (assistantProcess == 0)
  {
    assistantProcess = new QProcess;
  }

  if (assistantProcess->state() == QProcess::NotRunning)
  {
    QStringList assistantArgs;
    if (!helpCollectionFile.isEmpty())
    {
      assistantArgs << QLatin1String("-collectionFile")
                     << QLatin1String(helpCollectionFile.toLatin1());
    }

    assistantArgs << QLatin1String("-enableRemoteControl")
                   << QLatin1String("-showUrl")
                   << QLatin1String(startUrl.toLatin1());
    assistantProcess->start(GetAssistantExecutable(), assistantArgs);
  }
  else
  {
    QByteArray ba;
    ba.append("setSource ").append(startUrl.toLatin1()).append('\0');
    assistantProcess->write(ba);
  }

}

void QtAssistantUtil::CloseAssistant()
{
  if (assistantProcess && (assistantProcess->state() != QProcess::NotRunning))
  {
    assistantProcess->close();
  }
  delete assistantProcess;
}

bool QtAssistantUtil::RegisterQCHFiles(const QString& collectionFile,
    const std::vector<IBundle::Pointer>& bundles)
{
  QString assistantExec = GetAssistantExecutable();

  QList<QStringList> argsVector;

  for (std::size_t i = 0; i < bundles.size(); ++i)
  {
    std::vector<std::string> resourceFiles;
    bundles[i]->GetStorage().List("resources", resourceFiles);
    bool qchFileFound = false;
    for (std::size_t j = 0; j < resourceFiles.size(); ++j)
    {
      QString resource = QString::fromStdString(resourceFiles[j]);
      if (resource.endsWith(".qch"))
      {
        qchFileFound = true;
        QStringList args;
        args << QLatin1String("-collectionFile") << collectionFile;
        Poco::Path qchPath = bundles[i]->GetPath();
        qchPath.pushDirectory("resources");
        qchPath.setFileName(resourceFiles[j]);
        args << QLatin1String("-register") << QString::fromStdString(qchPath.toString());
        args << QLatin1String("-quiet");
      //BERRY_INFO << "Registering " << qchPath.toString() << " with " << collectionFile.toStdString();
        argsVector.push_back(args);
      }
    }

    if (qchFileFound)
    {
      registeredBundles.push_back(QString::fromStdString(bundles[i]->GetSymbolicName()));
    }
  }

  bool success = true;
  QProgressDialog progress("Registering help files...", "Abort Registration", 0, argsVector.size());
  progress.setWindowModality(Qt::WindowModal);

  if (argsVector.isEmpty())
  {
    BERRY_WARN << "No .qch files found. Help contents will not be available.";
  }

  QString errorString;
  int exitCode = 0;
  for (int i = 0; i < argsVector.size(); ++i)
  {
    const QStringList& args = argsVector[i];
    progress.setValue(i);
    QString labelText = QString("Registering ") + args[3];
    progress.setLabelText(labelText);

    if (progress.wasCanceled())
    {
      success = false;
      break;
    }

    QProcess* process = new QProcess;
    process->start(assistantExec, args);
    if (!process->waitForStarted())
    {
      success = false;
      BERRY_ERROR << "Registering compressed help file" << args[3].toStdString() << " failed";
    }

    if (process->error() != QProcess::UnknownError)
    {
      errorString = process->errorString();
      success = false;
    }

    if (process->exitCode() != 0)
      exitCode = process->exitCode();
  }
  progress.setValue(argsVector.size());

  if (!errorString.isEmpty() || exitCode)
  {
    QString errText = "Registering one or more help files failed.";
    if (errorString.isEmpty())
    {
      errText += "\nYou may not have write permissions in " + QDir::toNativeSeparators(QDir::homePath());
    }
    else
    {
      errText += " The last error was: " + errorString;
    }
    QMessageBox::warning(0, "Help System Error", errText);
  }

  return success;
}

QString QtAssistantUtil::GetAssistantExecutable()
{
  QFileInfo assistantFile(QT_ASSISTANT_EXECUTABLE);
  return assistantFile.fileName();
}

void QtAssistantUtil::SetDefaultHelpUrl(const QString& defaultUrl)
{
  defaultHelpUrl = defaultUrl;
}

}
<commit_msg>FIX (#4432): improved QAssistant exe search logic<commit_after>/*=========================================================================

 Program:   BlueBerry Platform
 Language:  C++
 Date:      $Date$
 Version:   $Revision$

 Copyright (c) German Cancer Research Center, Division of Medical and
 Biological Informatics. All rights reserved.
 See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

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

 =========================================================================*/

#include "berryQtAssistantUtil.h"
#include <berryPlatformUI.h>
#include <berryConfig.h>
#include <berryLog.h>
#include <berryIBundleStorage.h>
#include <berryIWorkbench.h>
#include <berryIWorkbenchPage.h>
#include <berryIWorkbenchPart.h>

#include <QFileInfo>
#include <QProgressDialog>
#include <QMessageBox>
#include <QDir>

namespace berry
{

QProcess* QtAssistantUtil::assistantProcess = 0;
QString QtAssistantUtil::helpCollectionFile;
QString QtAssistantUtil::defaultHelpUrl;
QStringList QtAssistantUtil::registeredBundles;

void QtAssistantUtil::SetHelpColletionFile(const QString& file)
{
  helpCollectionFile = file;
}

void QtAssistantUtil::OpenActivePartHelp()
{
  //Get Plugin-ID
  QString pluginID;
  berry::IWorkbench* currentWorkbench = berry::PlatformUI::GetWorkbench();
  if (currentWorkbench)
  {
    berry::IWorkbenchWindow::Pointer currentWorkbenchWindow = currentWorkbench->GetActiveWorkbenchWindow();
    if (currentWorkbenchWindow)
    {
      berry::IWorkbenchPage::Pointer currentPage = currentWorkbenchWindow->GetActivePage();
      if (currentPage)
      {
        berry::IWorkbenchPart::Pointer currentPart = currentPage->GetActivePart();
        if (currentPart) pluginID = QString::fromStdString(currentPart->GetSite()->GetPluginId());
      }
    }
  }
  //End get Plugin-ID

  QString helpUrl = defaultHelpUrl;
  if (!pluginID.isEmpty() && registeredBundles.contains(pluginID))
    helpUrl = "qthelp://"+pluginID+"/bundle/index.html";

  OpenAssistant(helpUrl);
}

void QtAssistantUtil::OpenAssistant(const QString& startPage)
{
  QString startUrl = startPage;
  if (startUrl.isEmpty()) startUrl = defaultHelpUrl;

  if (assistantProcess == 0)
  {
    assistantProcess = new QProcess;
  }

  if (assistantProcess->state() == QProcess::NotRunning)
  {
    QStringList assistantArgs;
    if (!helpCollectionFile.isEmpty())
    {
      assistantArgs << QLatin1String("-collectionFile")
                     << QLatin1String(helpCollectionFile.toLatin1());
    }

    assistantArgs << QLatin1String("-enableRemoteControl")
                   << QLatin1String("-showUrl")
                   << QLatin1String(startUrl.toLatin1());
    assistantProcess->start(GetAssistantExecutable(), assistantArgs);
  }
  else
  {
    QByteArray ba;
    ba.append("setSource ").append(startUrl.toLatin1()).append('\0');
    assistantProcess->write(ba);
  }

}

void QtAssistantUtil::CloseAssistant()
{
  if (assistantProcess && (assistantProcess->state() != QProcess::NotRunning))
  {
    assistantProcess->close();
  }
  delete assistantProcess;
}

bool QtAssistantUtil::RegisterQCHFiles(const QString& collectionFile,
    const std::vector<IBundle::Pointer>& bundles)
{
  QString assistantExec = GetAssistantExecutable();

  QList<QStringList> argsVector;

  for (std::size_t i = 0; i < bundles.size(); ++i)
  {
    std::vector<std::string> resourceFiles;
    bundles[i]->GetStorage().List("resources", resourceFiles);
    bool qchFileFound = false;
    for (std::size_t j = 0; j < resourceFiles.size(); ++j)
    {
      QString resource = QString::fromStdString(resourceFiles[j]);
      if (resource.endsWith(".qch"))
      {
        qchFileFound = true;
        QStringList args;
        args << QLatin1String("-collectionFile") << collectionFile;
        Poco::Path qchPath = bundles[i]->GetPath();
        qchPath.pushDirectory("resources");
        qchPath.setFileName(resourceFiles[j]);
        args << QLatin1String("-register") << QString::fromStdString(qchPath.toString());
        args << QLatin1String("-quiet");
      //BERRY_INFO << "Registering " << qchPath.toString() << " with " << collectionFile.toStdString();
        argsVector.push_back(args);
      }
    }

    if (qchFileFound)
    {
      registeredBundles.push_back(QString::fromStdString(bundles[i]->GetSymbolicName()));
    }
  }

  bool success = true;
  QProgressDialog progress("Registering help files...", "Abort Registration", 0, argsVector.size());
  progress.setWindowModality(Qt::WindowModal);

  if (argsVector.isEmpty())
  {
    BERRY_WARN << "No .qch files found. Help contents will not be available.";
  }

  QString errorString;
  int exitCode = 0;
  for (int i = 0; i < argsVector.size(); ++i)
  {
    const QStringList& args = argsVector[i];
    progress.setValue(i);
    QString labelText = QString("Registering ") + args[3];
    progress.setLabelText(labelText);

    if (progress.wasCanceled())
    {
      success = false;
      break;
    }

    QProcess* process = new QProcess;
    process->start(assistantExec, args);
    if (!process->waitForStarted())
    {
      success = false;
      BERRY_ERROR << "Registering compressed help file" << args[3].toStdString() << " failed";
    }

    if (process->error() != QProcess::UnknownError)
    {
      errorString = process->errorString();
      success = false;
    }

    if (process->exitCode() != 0)
      exitCode = process->exitCode();
  }
  progress.setValue(argsVector.size());

  if (!errorString.isEmpty() || exitCode)
  {
    QString errText = "Registering one or more help files failed.";
    if (errorString.isEmpty())
    {
      errText += "\nYou may not have write permissions in " + QDir::toNativeSeparators(QDir::homePath());
    }
    else
    {
      errText += " The last error was: " + errorString;
    }
    QMessageBox::warning(0, "Help System Error", errText);
  }

  return success;
}

QString QtAssistantUtil::GetAssistantExecutable()
{
  QFileInfo assistantFile(QT_ASSISTANT_EXECUTABLE);
  QFileInfo localAssistant(QCoreApplication::applicationDirPath() + "/" + assistantFile.fileName() );

  if (localAssistant.isExecutable())
  {
    return localAssistant.absoluteFilePath();
  }
  else
  {
    return assistantFile.absoluteFilePath();
  }
}

void QtAssistantUtil::SetDefaultHelpUrl(const QString& defaultUrl)
{
  defaultHelpUrl = defaultUrl;
}

}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2018 Dubalu LLC. All rights reserved.
 *
 * 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 "base_client.h"

#include <errno.h>                  // for errno
#include <memory>                   // for std::shared_ptr
#include <sys/socket.h>             // for SHUT_RDWR
#include <sysexits.h>               // for EX_SOFTWARE
#include <type_traits>              // for remove_reference<>::type
#include <utility>                  // for std::move
#include <xapian.h>                 // for SerialisationError

#include "cassert.h"                // for ASSERT
#include "error.hh"                 // for error:name, error::description
#include "ev/ev++.h"                // for ::EV_ERROR, ::EV_READ, ::EV_WRITE
#include "ignore_unused.h"          // for ignore_unused
#include "io.hh"                    // for io::read, io::close, io::lseek, io::write
#include "length.h"                 // for serialise_length, unserialise_length
#include "likely.h"                 // for likely, unlikely
#include "log.h"                    // for L_CALL, L_ERR, L_EV, L_CONN, L_OBJ
#include "manager.h"                // for sig_exit
#include "readable_revents.hh"      // for readable_revents
#include "repr.hh"                  // for repr
#include "thread.hh"                // for get_thread_name

// #undef L_DEBUG
// #define L_DEBUG L_GREY
// #undef L_CALL
// #define L_CALL L_STACKED_DIM_GREY
// #undef L_CONN
// #define L_CONN L_GREEN
// #undef L_TCP_ENQUEUE
// #define L_TCP_ENQUEUE L_GREEN
// #undef L_TCP_WIRE
// #define L_TCP_WIRE L_WHITE
// #undef L_EV
// #define L_EV L_MEDIUM_PURPLE
// #undef L_EV_BEGIN
// #define L_EV_BEGIN L_DELAYED_200
// #undef L_EV_END
// #define L_EV_END L_DELAYED_N_UNLOG

constexpr int WRITE_QUEUE_LIMIT = 10;
constexpr int WRITE_QUEUE_THRESHOLD = WRITE_QUEUE_LIMIT * 2 / 3;

BaseClient::BaseClient(const std::shared_ptr<Worker>& parent_, ev::loop_ref* ev_loop_, unsigned int ev_flags_, int sock_)
	: Worker(std::move(parent_), ev_loop_, ev_flags_),
	  io_read(*ev_loop),
	  io_write(*ev_loop),
	  write_start_async(*ev_loop),
	  read_start_async(*ev_loop),
	  waiting(false),
	  running(false),
	  shutting_down(false),
	  sock(sock_),
	  closed(false),
	  writes(0),
	  total_received_bytes(0),
	  total_sent_bytes(0),
	  mode(MODE::READ_BUF),
	  write_queue(WRITE_QUEUE_LIMIT, -1, WRITE_QUEUE_THRESHOLD)
{
	if (sock == -1) {
		throw std::invalid_argument("Invalid socket");
	}

	write_start_async.set<BaseClient, &BaseClient::write_start_async_cb>(this);
	read_start_async.set<BaseClient, &BaseClient::read_start_async_cb>(this);

	io_write.set<BaseClient, &BaseClient::io_cb_write>(this);
	io_write.set(sock, ev::WRITE);

	++XapiandManager::manager->total_clients;
}

BaseClient::~BaseClient()
{
	if (XapiandManager::manager->total_clients.fetch_sub(1) == 0) {
		L_CRIT("Inconsistency in number of binary clients");
		sig_exit(-EX_SOFTWARE);
	}

	// If shutting down and there are no more clients connected,
	// continue shutdown.
	if (XapiandManager::manager->shutdown_asap.load() != 0) {
		if (XapiandManager::manager->total_clients == 0) {
			XapiandManager::manager->shutdown_sig(0);
		}
	}

	io::close(sock);

	Worker::deinit();

	if (get_thread_name()[0] != 'S') {
		L_CRIT("BaseClient destroyed from %s!" + TRACEBACK(), repr(get_thread_name()));
		// sig_exit(-EX_SOFTWARE);
	}
}

void
BaseClient::close()
{
	L_CALL("BaseClient::close()");

	if (!closed.exchange(true)) {
		io::shutdown(sock, SHUT_RDWR);
	}
}

void
BaseClient::destroy_impl()
{
	L_CALL("BaseClient::destroy_impl()");

	Worker::destroy_impl();

	close();
}

void
BaseClient::start_impl()
{
	L_CALL("BaseClient::start_impl()");

	Worker::start_impl();

	write_start_async.start();
	L_EV("Start client's async update event");

	read_start_async.start();
	L_EV("Start client's async read start event");

	io_read.start();
	L_EV("Start client's read event (sock=%d)", sock);
}

void
BaseClient::stop_impl()
{
	L_CALL("BaseClient::stop_impl()");

	Worker::stop_impl();

	write_start_async.stop();
	L_EV("Stop client's async update event");

	read_start_async.stop();
	L_EV("Stop client's async read start event");

	io_write.stop();
	L_EV("Stop client's write event");

	io_read.stop();
	L_EV("Stop client's read event");

	write_queue.finish();
	write_queue.clear();
}

WR
BaseClient::write_from_queue()
{
	L_CALL("BaseClient::write_from_queue()");

	if (closed) {
		L_ERR("ERROR: write error {sock:%d}: Socket already closed!", sock);
		L_CONN("WR:ERR.1: {sock:%d}", sock);
		return WR::ERROR;
	}

	std::lock_guard<std::mutex> lk(_mutex);

	std::shared_ptr<Buffer> buffer;
	if (write_queue.front(buffer)) {
		size_t buf_size = buffer->size();
		const char *buf_data = buffer->data();

#ifdef MSG_NOSIGNAL
		ssize_t sent = io::send(sock, buf_data, buf_size, MSG_NOSIGNAL);
#else
		ssize_t sent = io::write(sock, buf_data, buf_size);
#endif

		if (sent < 0) {
			if (io::ignored_errno(errno, true, true, false)) {
				L_CONN("WR:RETRY: {sock:%d} - %s (%d): %s", sock, error::name(errno), errno, error::description(errno));
				return WR::RETRY;
			}

			L_ERR("ERROR: write error {sock:%d} - %s (%d): %s", sock, error::name(errno), errno, error::description(errno));
			L_CONN("WR:ERR.2: {sock:%d}", sock);
			close();
			return WR::ERROR;
		}

		total_sent_bytes += sent;
		L_TCP_WIRE("{sock:%d} <<-- %s (%zu bytes)", sock, repr(buf_data, sent, true, true, 500), sent);

		buffer->remove_prefix(sent);
		if (buffer->size() == 0) {
			if (write_queue.pop(buffer)) {
				if (write_queue.empty()) {
					L_CONN("WR:OK: {sock:%d}", sock);
					return WR::OK;
				}
			}
		}

		L_CONN("WR:PENDING: {sock:%d}", sock);
		return WR::PENDING;
	}

	L_CONN("WR:OK.2: {sock:%d}", sock);
	return WR::OK;
}

WR
BaseClient::write_from_queue(int max)
{
	L_CALL("BaseClient::write_from_queue(%d)", max);

	WR status = WR::PENDING;

	for (int i = 0; max < 0 || i < max; ++i) {
		status = write_from_queue();
		if (status != WR::PENDING) {
			return status;
		}
	}

	return status;
}

bool
BaseClient::write(const char *buf, size_t buf_size)
{
	L_CALL("BaseClient::write(<buf>, %zu)", buf_size);

	return write_buffer(std::make_shared<Buffer>('\0', buf, buf_size));
}

bool
BaseClient::write_file(std::string_view path, bool unlink)
{
	L_CALL("BaseClient::write_file(<path>, <unlink>)");

	return write_buffer(std::make_shared<Buffer>(path, unlink));
}

bool
BaseClient::write_buffer(const std::shared_ptr<Buffer>& buffer)
{
	L_CALL("BaseClient::write_buffer(<buffer>)");

	do {
		if (closed) {
			return false;
		}
	} while (!write_queue.push(buffer, 1));

	writes += 1;
	L_TCP_ENQUEUE("{sock:%d} <ENQUEUE> buffer (%zu bytes)", sock, buffer->full_size());

	switch (write_from_queue(-1)) {
		case WR::RETRY:
		case WR::PENDING:
			write_start_async.send();
			/* FALLTHROUGH */
		case WR::OK:
			return true;
		default:
			return false;
	}
}

void
BaseClient::_io_cb_write(ev::io &watcher, int revents)
{
	L_CALL("BaseClient::io_cb_write(<watcher>, 0x%x (%s)) {sock:%d}", revents, readable_revents(revents), watcher.fd);

	L_EV_BEGIN("BaseClient::io_cb_write:BEGIN");
	L_EV_END("BaseClient::io_cb_write:END");

	ASSERT(sock == -1 || sock == watcher.fd);
	ignore_unused(watcher);

	L_DEBUG_HOOK("BaseClient::io_cb_write", "BaseClient::io_cb_write(<watcher>, 0x%x (%s)) {sock:%d}", revents, readable_revents(revents), watcher.fd);

	if (closed) {
		stop();
		destroy();
		detach();
		return;
	}

	if ((revents & EV_ERROR) != 0) {
		L_ERR("ERROR: got invalid event {sock:%d} - %s (%d): %s", watcher.fd, error::name(errno), errno, error::description(errno));
		stop();
		destroy();
		detach();
		return;
	}

	switch (write_from_queue(10)) {
		case WR::RETRY:
		case WR::PENDING:
			break;
		case WR::ERROR:
		case WR::OK:
			write_queue.empty([&](bool empty) {
				if (empty) {
					io_write.stop();
					L_EV("Disable write event");
					if (shutting_down) {
						detach();
					}
				}
			});
			break;
	}

	if (closed) {
		detach();
	}
}

void
BaseClient::write_start_async_cb(ev::async& /*unused*/, int revents)
{
	L_CALL("BaseClient::write_start_async_cb(<watcher>, 0x%x (%s))", revents, readable_revents(revents));

	L_EV_BEGIN("BaseClient::write_start_async_cb:BEGIN");
	L_EV_END("BaseClient::write_start_async_cb:END");

	ignore_unused(revents);

	if (!closed) {
		io_write.start();
		L_EV("Enable write event [%d]", io_write.is_active());
	}
}

void
BaseClient::read_start_async_cb(ev::async& /*unused*/, int revents)
{
	L_CALL("BaseClient::read_start_async_cb(<watcher>, 0x%x (%s))", revents, readable_revents(revents));

	L_EV_BEGIN("BaseClient::read_start_async_cb:BEGIN");
	L_EV_END("BaseClient::read_start_async_cb:END");

	ignore_unused(revents);

	if (!closed) {
		io_read.start();
		L_EV("Enable read event [%d]", io_read.is_active());
	}
}

void
BaseClient::read_file()
{
	L_CALL("BaseClient::read_file()");

	mode = MODE::READ_FILE_TYPE;
	file_size = -1;
	receive_checksum = false;
}
<commit_msg>Clients: Does it matter where clients are destroyed?<commit_after>/*
 * Copyright (C) 2015-2018 Dubalu LLC. All rights reserved.
 *
 * 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 "base_client.h"

#include <errno.h>                  // for errno
#include <memory>                   // for std::shared_ptr
#include <sys/socket.h>             // for SHUT_RDWR
#include <sysexits.h>               // for EX_SOFTWARE
#include <type_traits>              // for remove_reference<>::type
#include <utility>                  // for std::move
#include <xapian.h>                 // for SerialisationError

#include "cassert.h"                // for ASSERT
#include "error.hh"                 // for error:name, error::description
#include "ev/ev++.h"                // for ::EV_ERROR, ::EV_READ, ::EV_WRITE
#include "ignore_unused.h"          // for ignore_unused
#include "io.hh"                    // for io::read, io::close, io::lseek, io::write
#include "length.h"                 // for serialise_length, unserialise_length
#include "likely.h"                 // for likely, unlikely
#include "log.h"                    // for L_CALL, L_ERR, L_EV, L_CONN, L_OBJ
#include "manager.h"                // for sig_exit
#include "readable_revents.hh"      // for readable_revents
#include "repr.hh"                  // for repr
#include "thread.hh"                // for get_thread_name

// #undef L_DEBUG
// #define L_DEBUG L_GREY
// #undef L_CALL
// #define L_CALL L_STACKED_DIM_GREY
// #undef L_CONN
// #define L_CONN L_GREEN
// #undef L_TCP_ENQUEUE
// #define L_TCP_ENQUEUE L_GREEN
// #undef L_TCP_WIRE
// #define L_TCP_WIRE L_WHITE
// #undef L_EV
// #define L_EV L_MEDIUM_PURPLE
// #undef L_EV_BEGIN
// #define L_EV_BEGIN L_DELAYED_200
// #undef L_EV_END
// #define L_EV_END L_DELAYED_N_UNLOG

constexpr int WRITE_QUEUE_LIMIT = 10;
constexpr int WRITE_QUEUE_THRESHOLD = WRITE_QUEUE_LIMIT * 2 / 3;

BaseClient::BaseClient(const std::shared_ptr<Worker>& parent_, ev::loop_ref* ev_loop_, unsigned int ev_flags_, int sock_)
	: Worker(std::move(parent_), ev_loop_, ev_flags_),
	  io_read(*ev_loop),
	  io_write(*ev_loop),
	  write_start_async(*ev_loop),
	  read_start_async(*ev_loop),
	  waiting(false),
	  running(false),
	  shutting_down(false),
	  sock(sock_),
	  closed(false),
	  writes(0),
	  total_received_bytes(0),
	  total_sent_bytes(0),
	  mode(MODE::READ_BUF),
	  write_queue(WRITE_QUEUE_LIMIT, -1, WRITE_QUEUE_THRESHOLD)
{
	if (sock == -1) {
		throw std::invalid_argument("Invalid socket");
	}

	write_start_async.set<BaseClient, &BaseClient::write_start_async_cb>(this);
	read_start_async.set<BaseClient, &BaseClient::read_start_async_cb>(this);

	io_write.set<BaseClient, &BaseClient::io_cb_write>(this);
	io_write.set(sock, ev::WRITE);

	++XapiandManager::manager->total_clients;
}

BaseClient::~BaseClient()
{
	if (XapiandManager::manager->total_clients.fetch_sub(1) == 0) {
		L_CRIT("Inconsistency in number of binary clients");
		sig_exit(-EX_SOFTWARE);
	}

	// If shutting down and there are no more clients connected,
	// continue shutdown.
	if (XapiandManager::manager->shutdown_asap.load() != 0) {
		if (XapiandManager::manager->total_clients == 0) {
			XapiandManager::manager->shutdown_sig(0);
		}
	}

	io::close(sock);

	Worker::deinit();
}

void
BaseClient::close()
{
	L_CALL("BaseClient::close()");

	if (!closed.exchange(true)) {
		io::shutdown(sock, SHUT_RDWR);
	}
}

void
BaseClient::destroy_impl()
{
	L_CALL("BaseClient::destroy_impl()");

	Worker::destroy_impl();

	close();
}

void
BaseClient::start_impl()
{
	L_CALL("BaseClient::start_impl()");

	Worker::start_impl();

	write_start_async.start();
	L_EV("Start client's async update event");

	read_start_async.start();
	L_EV("Start client's async read start event");

	io_read.start();
	L_EV("Start client's read event (sock=%d)", sock);
}

void
BaseClient::stop_impl()
{
	L_CALL("BaseClient::stop_impl()");

	Worker::stop_impl();

	write_start_async.stop();
	L_EV("Stop client's async update event");

	read_start_async.stop();
	L_EV("Stop client's async read start event");

	io_write.stop();
	L_EV("Stop client's write event");

	io_read.stop();
	L_EV("Stop client's read event");

	write_queue.finish();
	write_queue.clear();
}

WR
BaseClient::write_from_queue()
{
	L_CALL("BaseClient::write_from_queue()");

	if (closed) {
		L_ERR("ERROR: write error {sock:%d}: Socket already closed!", sock);
		L_CONN("WR:ERR.1: {sock:%d}", sock);
		return WR::ERROR;
	}

	std::lock_guard<std::mutex> lk(_mutex);

	std::shared_ptr<Buffer> buffer;
	if (write_queue.front(buffer)) {
		size_t buf_size = buffer->size();
		const char *buf_data = buffer->data();

#ifdef MSG_NOSIGNAL
		ssize_t sent = io::send(sock, buf_data, buf_size, MSG_NOSIGNAL);
#else
		ssize_t sent = io::write(sock, buf_data, buf_size);
#endif

		if (sent < 0) {
			if (io::ignored_errno(errno, true, true, false)) {
				L_CONN("WR:RETRY: {sock:%d} - %s (%d): %s", sock, error::name(errno), errno, error::description(errno));
				return WR::RETRY;
			}

			L_ERR("ERROR: write error {sock:%d} - %s (%d): %s", sock, error::name(errno), errno, error::description(errno));
			L_CONN("WR:ERR.2: {sock:%d}", sock);
			close();
			return WR::ERROR;
		}

		total_sent_bytes += sent;
		L_TCP_WIRE("{sock:%d} <<-- %s (%zu bytes)", sock, repr(buf_data, sent, true, true, 500), sent);

		buffer->remove_prefix(sent);
		if (buffer->size() == 0) {
			if (write_queue.pop(buffer)) {
				if (write_queue.empty()) {
					L_CONN("WR:OK: {sock:%d}", sock);
					return WR::OK;
				}
			}
		}

		L_CONN("WR:PENDING: {sock:%d}", sock);
		return WR::PENDING;
	}

	L_CONN("WR:OK.2: {sock:%d}", sock);
	return WR::OK;
}

WR
BaseClient::write_from_queue(int max)
{
	L_CALL("BaseClient::write_from_queue(%d)", max);

	WR status = WR::PENDING;

	for (int i = 0; max < 0 || i < max; ++i) {
		status = write_from_queue();
		if (status != WR::PENDING) {
			return status;
		}
	}

	return status;
}

bool
BaseClient::write(const char *buf, size_t buf_size)
{
	L_CALL("BaseClient::write(<buf>, %zu)", buf_size);

	return write_buffer(std::make_shared<Buffer>('\0', buf, buf_size));
}

bool
BaseClient::write_file(std::string_view path, bool unlink)
{
	L_CALL("BaseClient::write_file(<path>, <unlink>)");

	return write_buffer(std::make_shared<Buffer>(path, unlink));
}

bool
BaseClient::write_buffer(const std::shared_ptr<Buffer>& buffer)
{
	L_CALL("BaseClient::write_buffer(<buffer>)");

	do {
		if (closed) {
			return false;
		}
	} while (!write_queue.push(buffer, 1));

	writes += 1;
	L_TCP_ENQUEUE("{sock:%d} <ENQUEUE> buffer (%zu bytes)", sock, buffer->full_size());

	switch (write_from_queue(-1)) {
		case WR::RETRY:
		case WR::PENDING:
			write_start_async.send();
			/* FALLTHROUGH */
		case WR::OK:
			return true;
		default:
			return false;
	}
}

void
BaseClient::_io_cb_write(ev::io &watcher, int revents)
{
	L_CALL("BaseClient::io_cb_write(<watcher>, 0x%x (%s)) {sock:%d}", revents, readable_revents(revents), watcher.fd);

	L_EV_BEGIN("BaseClient::io_cb_write:BEGIN");
	L_EV_END("BaseClient::io_cb_write:END");

	ASSERT(sock == -1 || sock == watcher.fd);
	ignore_unused(watcher);

	L_DEBUG_HOOK("BaseClient::io_cb_write", "BaseClient::io_cb_write(<watcher>, 0x%x (%s)) {sock:%d}", revents, readable_revents(revents), watcher.fd);

	if (closed) {
		stop();
		destroy();
		detach();
		return;
	}

	if ((revents & EV_ERROR) != 0) {
		L_ERR("ERROR: got invalid event {sock:%d} - %s (%d): %s", watcher.fd, error::name(errno), errno, error::description(errno));
		stop();
		destroy();
		detach();
		return;
	}

	switch (write_from_queue(10)) {
		case WR::RETRY:
		case WR::PENDING:
			break;
		case WR::ERROR:
		case WR::OK:
			write_queue.empty([&](bool empty) {
				if (empty) {
					io_write.stop();
					L_EV("Disable write event");
					if (shutting_down) {
						detach();
					}
				}
			});
			break;
	}

	if (closed) {
		detach();
	}
}

void
BaseClient::write_start_async_cb(ev::async& /*unused*/, int revents)
{
	L_CALL("BaseClient::write_start_async_cb(<watcher>, 0x%x (%s))", revents, readable_revents(revents));

	L_EV_BEGIN("BaseClient::write_start_async_cb:BEGIN");
	L_EV_END("BaseClient::write_start_async_cb:END");

	ignore_unused(revents);

	if (!closed) {
		io_write.start();
		L_EV("Enable write event [%d]", io_write.is_active());
	}
}

void
BaseClient::read_start_async_cb(ev::async& /*unused*/, int revents)
{
	L_CALL("BaseClient::read_start_async_cb(<watcher>, 0x%x (%s))", revents, readable_revents(revents));

	L_EV_BEGIN("BaseClient::read_start_async_cb:BEGIN");
	L_EV_END("BaseClient::read_start_async_cb:END");

	ignore_unused(revents);

	if (!closed) {
		io_read.start();
		L_EV("Enable read event [%d]", io_read.is_active());
	}
}

void
BaseClient::read_file()
{
	L_CALL("BaseClient::read_file()");

	mode = MODE::READ_FILE_TYPE;
	file_size = -1;
	receive_checksum = false;
}
<|endoftext|>


<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2012 Francois Beaune, Jupiter Jazz
//
// 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.
//

// Interface header.
#include "renderingmanager.h"

// appleseed.studio headers.
#include "mainwindow/rendering/renderwidget.h"
#include "mainwindow/statusbar.h"

// appleseed.shared headers.
#include "application/application.h"

// appleseed.renderer headers.
#include "renderer/api/aov.h"
#include "renderer/api/camera.h"
#include "renderer/api/frame.h"
#include "renderer/api/project.h"
#include "renderer/api/scene.h"

// appleseed.foundation headers.
#include "foundation/image/analysis.h"
#include "foundation/image/image.h"
#include "foundation/math/transform.h"
#include "foundation/utility/string.h"

// boost headers.
#include "boost/filesystem/path.hpp"

// Qt headers.
#include <QAction>
#include <QApplication>
#include <QTimerEvent>

using namespace appleseed::shared;
using namespace boost;
using namespace foundation;
using namespace renderer;
using namespace std;

namespace appleseed {
namespace studio {

//
// RenderingManager class implementation.
//

namespace
{
    class MasterRendererThread
      : public QThread
    {
      public:
        // Constructor.
        explicit MasterRendererThread(MasterRenderer* master_renderer)
          : m_master_renderer(master_renderer)
        {
        }

      private:
        MasterRenderer* m_master_renderer;

        // The starting point for the thread.
        virtual void run()
        {
            m_master_renderer->render();
        }
    };
}

RenderingManager::RenderingManager(StatusBar& status_bar)
  : m_status_bar(status_bar)
  , m_project(0)
  , m_render_widget(0)
  , m_override_shading(false)
{
    //
    // The connections below are using the Qt::BlockingQueuedConnection connection type.
    //
    // They are using a queued connection because the emitting thread is different from
    // the receiving thread (the emitting thread is the master renderer thread, and the
    // receiving thread is the UI thread of the main window (presumably).
    //
    // They are using a blocking queue connection because we need the receiving slot to
    // have returned in the receiving thread before the emitting thread can continue.
    //
    // See http://doc.trolltech.com/4.6/qt.html#ConnectionType-enum for more details.
    //

    connect(
        &m_renderer_controller, SIGNAL(signal_frame_begin()),
        this, SLOT(slot_frame_begin()),
        Qt::BlockingQueuedConnection);

    connect(
        &m_renderer_controller, SIGNAL(signal_frame_end()),
        this, SLOT(slot_frame_end()),
        Qt::BlockingQueuedConnection);

    connect(
        &m_renderer_controller, SIGNAL(signal_rendering_begin()),
        this, SLOT(slot_rendering_begin()),
        Qt::BlockingQueuedConnection);

    connect(
        &m_renderer_controller, SIGNAL(signal_rendering_success()),
        this, SLOT(slot_rendering_end()),
        Qt::BlockingQueuedConnection);

    connect(
        &m_renderer_controller, SIGNAL(signal_rendering_abort()),
        this, SLOT(slot_rendering_end()),
        Qt::BlockingQueuedConnection);

    connect(
        &m_renderer_controller, SIGNAL(signal_rendering_success()),
        this, SIGNAL(signal_rendering_end()));

    connect(
        &m_renderer_controller, SIGNAL(signal_rendering_abort()),
        this, SIGNAL(signal_rendering_end()));
}

void RenderingManager::start_rendering(
    Project*                    project,
    const ParamArray&           params,
    const bool                  highlight_tiles,
    RenderWidget*               render_widget)
{
    m_project = project;
    m_render_widget = render_widget;

    m_camera_controller.reset(
        new CameraController(
            m_render_widget,
            m_project->get_scene()));

    connect(
        m_camera_controller.get(), SIGNAL(signal_camera_changed()),
        this, SLOT(slot_camera_changed()));

    connect(
        m_camera_controller.get(), SIGNAL(signal_camera_changed()),
        this, SIGNAL(signal_camera_changed()));

    m_tile_callback_factory.reset(
        new QtTileCallbackFactory(
            m_render_widget,
            highlight_tiles));

    m_master_renderer.reset(
        new MasterRenderer(
            *m_project,
            params,
            &m_renderer_controller,
            m_tile_callback_factory.get()));

    m_master_renderer_thread.reset(
        new MasterRendererThread(m_master_renderer.get()));

    m_master_renderer_thread->start();
}

bool RenderingManager::is_rendering() const
{
    return m_master_renderer_thread.get() && m_master_renderer_thread->isRunning();
}

void RenderingManager::wait_until_rendering_end()
{
    while (is_rendering())
    {
        QApplication::processEvents();
    }
}

void RenderingManager::abort_rendering()
{
    RENDERER_LOG_DEBUG("aborting rendering...");

    m_renderer_controller.set_status(IRendererController::AbortRendering);
}

void RenderingManager::restart_rendering()
{
    m_renderer_controller.set_status(IRendererController::RestartRendering);
}

void RenderingManager::reinitialize_rendering()
{
    m_renderer_controller.set_status(IRendererController::ReinitializeRendering);
}

void RenderingManager::timerEvent(QTimerEvent* event)
{
    if (event->timerId() == m_render_widget_update_timer.timerId())
        m_render_widget->update();
    else QObject::timerEvent(event);
}

void RenderingManager::print_final_rendering_time()
{
    const double rendering_time = m_rendering_timer.get_seconds();
    const string rendering_time_string = pretty_time(rendering_time, 3);

    RENDERER_LOG_INFO("rendering finished in %s.", rendering_time_string.c_str());

    m_status_bar.set_text("Rendering finished in " + rendering_time_string);
}

void RenderingManager::print_average_luminance()
{
    Image final_image(m_project->get_frame()->image());
    m_project->get_frame()->transform_to_output_color_space(final_image);

    const double average_luminance = compute_average_luminance(final_image);

    RENDERER_LOG_DEBUG(
        "final average luminance is %s.",
        pretty_scalar(average_luminance, 6).c_str());
}

void RenderingManager::archive_frame_to_disk()
{
    RENDERER_LOG_INFO("archiving frame to disk...");

    const filesystem::path autosave_path =
          filesystem::path(Application::get_root_path())
        / "images/autosave/";

    m_project->get_frame()->archive(
        autosave_path.directory_string().c_str());
}

void RenderingManager::slot_rendering_begin()
{
    assert(m_master_renderer.get());

    if (m_override_shading)
    {
        m_master_renderer->get_parameters()
            .push("shading_engine")
            .push("override_shading")
            .insert("mode", m_override_shading_mode);
    }
    else
    {
        m_master_renderer->get_parameters()
            .push("shading_engine")
            .dictionaries().remove("override_shading");
    }

    const int UpdateRate = 5;
    m_render_widget_update_timer.start(1000 / UpdateRate, this);
}

void RenderingManager::slot_rendering_end()
{
    m_render_widget_update_timer.stop();

    m_render_widget->update();

    print_final_rendering_time();
    print_average_luminance();

    archive_frame_to_disk();

    // Prevent manipulation of the camera after rendering has ended.
    m_camera_controller.reset();
}

void RenderingManager::slot_frame_begin()
{
    m_renderer_controller.set_status(IRendererController::ContinueRendering);

    m_camera_controller->update_camera_transform();

    m_rendering_timer.start();
    m_status_bar.start_rendering_time_display(&m_rendering_timer);
}

void RenderingManager::slot_frame_end()
{
    m_status_bar.stop_rendering_time_display();
}

void RenderingManager::slot_clear_shading_override()
{
    m_override_shading = false;

    reinitialize_rendering();
}

void RenderingManager::slot_set_shading_override()
{
    QAction* action = qobject_cast<QAction*>(sender());
    const string shading_mode = action->data().toString().toStdString();

    m_override_shading = true;
    m_override_shading_mode = shading_mode;

    reinitialize_rendering();
}

void RenderingManager::slot_camera_changed()
{
    restart_rendering();
}

}   // namespace studio
}   // namespace appleseed
<commit_msg>trigger a render widget update when the view changes.<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2012 Francois Beaune, Jupiter Jazz
//
// 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.
//

// Interface header.
#include "renderingmanager.h"

// appleseed.studio headers.
#include "mainwindow/rendering/renderwidget.h"
#include "mainwindow/statusbar.h"

// appleseed.shared headers.
#include "application/application.h"

// appleseed.renderer headers.
#include "renderer/api/aov.h"
#include "renderer/api/camera.h"
#include "renderer/api/frame.h"
#include "renderer/api/project.h"
#include "renderer/api/scene.h"

// appleseed.foundation headers.
#include "foundation/image/analysis.h"
#include "foundation/image/image.h"
#include "foundation/math/transform.h"
#include "foundation/utility/string.h"

// boost headers.
#include "boost/filesystem/path.hpp"

// Qt headers.
#include <QAction>
#include <QApplication>
#include <QTimerEvent>

using namespace appleseed::shared;
using namespace boost;
using namespace foundation;
using namespace renderer;
using namespace std;

namespace appleseed {
namespace studio {

//
// RenderingManager class implementation.
//

namespace
{
    class MasterRendererThread
      : public QThread
    {
      public:
        // Constructor.
        explicit MasterRendererThread(MasterRenderer* master_renderer)
          : m_master_renderer(master_renderer)
        {
        }

      private:
        MasterRenderer* m_master_renderer;

        // The starting point for the thread.
        virtual void run()
        {
            m_master_renderer->render();
        }
    };
}

RenderingManager::RenderingManager(StatusBar& status_bar)
  : m_status_bar(status_bar)
  , m_project(0)
  , m_render_widget(0)
  , m_override_shading(false)
{
    //
    // The connections below are using the Qt::BlockingQueuedConnection connection type.
    //
    // They are using a queued connection because the emitting thread is different from
    // the receiving thread (the emitting thread is the master renderer thread, and the
    // receiving thread is the UI thread of the main window (presumably).
    //
    // They are using a blocking queue connection because we need the receiving slot to
    // have returned in the receiving thread before the emitting thread can continue.
    //
    // See http://doc.trolltech.com/4.6/qt.html#ConnectionType-enum for more details.
    //

    connect(
        &m_renderer_controller, SIGNAL(signal_frame_begin()),
        this, SLOT(slot_frame_begin()),
        Qt::BlockingQueuedConnection);

    connect(
        &m_renderer_controller, SIGNAL(signal_frame_end()),
        this, SLOT(slot_frame_end()),
        Qt::BlockingQueuedConnection);

    connect(
        &m_renderer_controller, SIGNAL(signal_rendering_begin()),
        this, SLOT(slot_rendering_begin()),
        Qt::BlockingQueuedConnection);

    connect(
        &m_renderer_controller, SIGNAL(signal_rendering_success()),
        this, SLOT(slot_rendering_end()),
        Qt::BlockingQueuedConnection);

    connect(
        &m_renderer_controller, SIGNAL(signal_rendering_abort()),
        this, SLOT(slot_rendering_end()),
        Qt::BlockingQueuedConnection);

    connect(
        &m_renderer_controller, SIGNAL(signal_rendering_success()),
        this, SIGNAL(signal_rendering_end()));

    connect(
        &m_renderer_controller, SIGNAL(signal_rendering_abort()),
        this, SIGNAL(signal_rendering_end()));
}

void RenderingManager::start_rendering(
    Project*                    project,
    const ParamArray&           params,
    const bool                  highlight_tiles,
    RenderWidget*               render_widget)
{
    m_project = project;
    m_render_widget = render_widget;

    m_camera_controller.reset(
        new CameraController(
            m_render_widget,
            m_project->get_scene()));

    connect(
        m_camera_controller.get(), SIGNAL(signal_camera_changed()),
        this, SLOT(slot_camera_changed()));

    connect(
        m_camera_controller.get(), SIGNAL(signal_camera_changed()),
        this, SIGNAL(signal_camera_changed()));

    m_tile_callback_factory.reset(
        new QtTileCallbackFactory(
            m_render_widget,
            highlight_tiles));

    m_master_renderer.reset(
        new MasterRenderer(
            *m_project,
            params,
            &m_renderer_controller,
            m_tile_callback_factory.get()));

    m_master_renderer_thread.reset(
        new MasterRendererThread(m_master_renderer.get()));

    m_master_renderer_thread->start();
}

bool RenderingManager::is_rendering() const
{
    return m_master_renderer_thread.get() && m_master_renderer_thread->isRunning();
}

void RenderingManager::wait_until_rendering_end()
{
    while (is_rendering())
    {
        QApplication::processEvents();
    }
}

void RenderingManager::abort_rendering()
{
    RENDERER_LOG_DEBUG("aborting rendering...");

    m_renderer_controller.set_status(IRendererController::AbortRendering);
}

void RenderingManager::restart_rendering()
{
    m_renderer_controller.set_status(IRendererController::RestartRendering);
}

void RenderingManager::reinitialize_rendering()
{
    m_renderer_controller.set_status(IRendererController::ReinitializeRendering);
}

void RenderingManager::timerEvent(QTimerEvent* event)
{
    if (event->timerId() == m_render_widget_update_timer.timerId())
        m_render_widget->update();
    else QObject::timerEvent(event);
}

void RenderingManager::print_final_rendering_time()
{
    const double rendering_time = m_rendering_timer.get_seconds();
    const string rendering_time_string = pretty_time(rendering_time, 3);

    RENDERER_LOG_INFO("rendering finished in %s.", rendering_time_string.c_str());

    m_status_bar.set_text("Rendering finished in " + rendering_time_string);
}

void RenderingManager::print_average_luminance()
{
    Image final_image(m_project->get_frame()->image());
    m_project->get_frame()->transform_to_output_color_space(final_image);

    const double average_luminance = compute_average_luminance(final_image);

    RENDERER_LOG_DEBUG(
        "final average luminance is %s.",
        pretty_scalar(average_luminance, 6).c_str());
}

void RenderingManager::archive_frame_to_disk()
{
    RENDERER_LOG_INFO("archiving frame to disk...");

    const filesystem::path autosave_path =
          filesystem::path(Application::get_root_path())
        / "images/autosave/";

    m_project->get_frame()->archive(
        autosave_path.directory_string().c_str());
}

void RenderingManager::slot_rendering_begin()
{
    assert(m_master_renderer.get());

    if (m_override_shading)
    {
        m_master_renderer->get_parameters()
            .push("shading_engine")
            .push("override_shading")
            .insert("mode", m_override_shading_mode);
    }
    else
    {
        m_master_renderer->get_parameters()
            .push("shading_engine")
            .dictionaries().remove("override_shading");
    }

    const int UpdateRate = 5;
    m_render_widget_update_timer.start(1000 / UpdateRate, this);
}

void RenderingManager::slot_rendering_end()
{
    m_render_widget_update_timer.stop();

    m_render_widget->update();

    print_final_rendering_time();
    print_average_luminance();

    archive_frame_to_disk();

    // Prevent manipulation of the camera after rendering has ended.
    m_camera_controller.reset();
}

void RenderingManager::slot_frame_begin()
{
    m_renderer_controller.set_status(IRendererController::ContinueRendering);

    m_camera_controller->update_camera_transform();

    m_rendering_timer.start();
    m_status_bar.start_rendering_time_display(&m_rendering_timer);
}

void RenderingManager::slot_frame_end()
{
    m_status_bar.stop_rendering_time_display();

    m_render_widget->update();
}

void RenderingManager::slot_clear_shading_override()
{
    m_override_shading = false;

    reinitialize_rendering();
}

void RenderingManager::slot_set_shading_override()
{
    QAction* action = qobject_cast<QAction*>(sender());
    const string shading_mode = action->data().toString().toStdString();

    m_override_shading = true;
    m_override_shading_mode = shading_mode;

    reinitialize_rendering();
}

void RenderingManager::slot_camera_changed()
{
    restart_rendering();
}

}   // namespace studio
}   // namespace appleseed
<|endoftext|>


<commit_before>//UPCOMING BOOST LOCALE!
//http://cppcms.sourceforge.net/boost_locale/html/index.html

//#include <unicode/ustring.h>
//#include <boost/regex/icu.hpp>
/*
http://www.unicode.org/versions/Unicode5.0.0/ch02.pdf#G13708

boost reg expressions and icu
http://www.boost.org/libs/regex/doc/icu_strings.html

http://en.wikipedia.org/wiki/UTF-8
http://unicode.org/faq/utf_bom.html

Test words (this document is saved as UTF8 unicode)
Iñtërnâtiônàlizætiøn windows cp1252
 في اليونا

 web page to try out multilingual
 http://www.unicode.org/iuc/iuc10/x-utf8.html

notes:

0. bytes c0 c1 f5-ff never appear in utf8
1. postgres bytea can be treated as UTF8 for comparisons/indexing etc without conversion
2. icu likes everything to be in utf16
3. icu offers a fast conversion between utf8 and utf16 but can only replace illegal bytes with one character fast
(neosys uses a modified one that converts between F8-FF and u2558-u255F)

icu conversion functions allow custom function be convert illegal UTF8 pick field character bytes with some arbitrary unicode code points but what speed? this was not found/used ... maybe better than the custom function mentioned above

Strategy 1:

Store utf16 in postgres bytea and provide convertion to bytea(utf16)->utf8->text/number/date etc for sql comparisons/indexing etc

pros:
	no codeconversion during read/write/calculate
cons:
	continuous conversion on database operations with/by clauses
	double database size
	double data traffic read/write/calculate

Strategy 2:

Store utf8 in postgres bytea and convert to/from utf16 in the client program on read/write/calculate

pros:
	no code conversion during database operations with/by clauses
	smallest database size
	smallest data traffic read/write/calculate
pros:
	codeconversion during every read/write/calculate

UTF8 versus UTF16 implementation discussion
-------------------------------------------
this 2005 document advocates UTF8 but more for documents/interchange than implementations
http://www-128.ibm.com/developerworks/xml/library/x-utf8/

size issue is NOT the main thing - compatibility with api is the main problem
size issue is important for memory throughput and therefore processing speed
UTF16 is 100% larger for non ascii (0-127) characters
UTF8 is 50% larger than UTF16 for asian and CJK characters (but one CJK char = one word = many characters)
UTF8 can be thought of as a non-adaptive huffman compression format(ie "common" ascii requires fewer bits)
UTF8 is the only format not to have the endian issue
ORACLE/DB2/JBASE/POSTGRES is UTF8, SAP is UTF32, Windows/MSSQL is UTF16

UTF16 is very poorly supported on std Linux libraries since wchar_t is 4 bytes (gcc has compiler option for 2 bytes)
icu offers UTF16 string replacement but then we are intimately tied to icu library
UTF8 is supported very broadly in unix/linux libraries
windows/java/icu/iisjavascript is UTF16 (unavoidable conversion into UTF16 in IIS even if web page is sent as UTF8!)
i18n web pages are UTF8 in the end

UTF8 negatives
character indexing/iteration  using integers is slow (until code migrates to proper iterators)
slower for asian and CJK
complicated bit pattern
doesnt handle binary (lots of illegal characters/UTF16 doesnt have this problem IF you ignore surrogates as Java does)

UTF16 negatives
probably become highly dependent on icu (string instead of std::wstring)
slower for ascii
doesnt work with c std strings (unless you use java style encoding c0 80 to represent nulls)
endian issues not a problem for implementations only for transmissions

so in the end this is a compatibility issue

Unicode in DB2 version 9 (UTF8)
http://publib.boulder.ibm.com/infocenter/db2luw/v9/topic/com.ibm.db2.udb.admin.doc/doc/c0004821.htm
lots of good explanation including
- during utf16->utf8 conversion surrogate pairs used to be converted to two x three byte sequences but no more.
- they treat combining characters separately
- implements UCA for collation

qt strings are unicode

*/

//#include <boost/algorithm/string.hpp>
#include <wctype.h>

#if defined(_MSC_VER)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <string.h>
#endif

#include <boost/scoped_array.hpp>
#include <locale.h>
#define MV_NO_NARROW
#include <exodus/mv.h>
#include <exodus/mvexceptions.h>

namespace exodus {

int var::localeAwareCompare(const std::wstring& str1, const std::wstring& str2) const
{
	if (str1.length()==0&&str2.length()==0)
		return 0;

#if defined(_MSC_VER) && defined(UNICODE)

	/*
	Now of course this points to what may be the best solution for a single function that will let you pass string length, ignore case, choose an appropriate locale, and work in different versions of Windows -- the master NLS collation function, CompareStringW!
	*/

	int comparison;

	//CompareStringW
	//comparison=CompareStringW(GetUserDefaultLCID(), 0,
	comparison=CompareStringW(GetThreadLocale(), 0,
		(TCHAR*)str1.data(), int(str1.length()),
		(TCHAR*)str2.data(), int(str2.length()));
	switch (comparison) {
	case CSTR_LESS_THAN:
		return -1;
	case CSTR_GREATER_THAN:
		return 1;
	default:
		return 0;
	}

#elif defined(_MACOS)
    // order strings the same way as native applications do, and also respects
    // the "Order for sorted lists" setting in the International preferences panel
	const CFStringRef thisString=CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
		reinterpret_cast<const UniChar *>(str1.data()),str1.length(),kCFAllocatorNull);
	const CFStringRef otherString=CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
		reinterpret_cast<const UniChar *>(data2), length2, kCFAllocatorNull);

	const int result = CFStringCompare(thisString, otherString, kCFCompareLocalized);
	CFRelease(thisString);
	CFRelease(otherString);
	return result;

//why not collate::compare?
//#elseif defined(_UNIX)
#elif defined(strcoll)
#elif !defined(XYZZZZ)
//	setlocale (LC_COLLATE, "en_US.UTF-8");
	return wcscoll(str1.c_str(),str2.c_str());
#else
#error stop here
	return str1.compare(str2);
#endif
}

var& var::localeAwareChangeCase(const int lowerupper)
{
	if (var_mvstr.length()==0)
		return *this;

#if defined(_MSC_VER) && defined(UNICODE)

	//TODO http://stackoverflow.com/questions/1767946/getthreadlocale-returns-different-value-than-getuserdefaultlcid
	//Microsoft is deprecating the Locale ID in favor of the Locale Name from Vista onwards

	/*
	http://msdn.microsoft.com/en-us/library/dd318700%28v=VS.85%29.aspx
	int LCMapString(
		__in   LCID Locale,
		__in   DWORD dwMapFlags,
		__in   LPCTSTR lpSrcStr,
		__in   int cchSrc,
		__out  LPTSTR lpDestStr,
		__in   int cchDest
	);
	return 0 if it DOESNT succeed
	GetLastError()
    * ERROR_INSUFFICIENT_BUFFER. A supplied buffer size was not large enough, or it was incorrectly set to NULL.
    * ERROR_INVALID_FLAGS. The values supplied for flags were not valid.
    * ERROR_INVALID_PARAMETER. Any of the parameter values was invalid.
	*/

	//uppercasing can double the number of characters (eg german b to SS) can it triple?
	int buffersize=(int) var_mvstr.length()*2+2;
	boost::scoped_array<TCHAR> buffer( new TCHAR [buffersize]);
	if (buffer==0)
		throw MVException(var(L"Out of memory in changecase(). Need ")^int(buffersize)^L" characters");

	int tolowerupper=LCMAP_LINGUISTIC_CASING;
	if (lowerupper==1)
		tolowerupper+=LCMAP_LOWERCASE;
	else if (lowerupper==2)
		tolowerupper+=LCMAP_UPPERCASE;

	//LCMapStringW
	int outputsize=LCMapStringW(
		GetThreadLocale(),
		tolowerupper,
		(TCHAR*)var_mvstr.data(),
		(int) var_mvstr.length(),
		buffer.get(),
		(int) buffersize);

	//cant convert for some reason. see above
	if (!outputsize)
	{
		//for now only ascii conversion
		if (lowerupper==1)
			return converter(UPPERCASE_,LOWERCASE_);
		else if (lowerupper==2)
			return converter(LOWERCASE_, UPPERCASE_);
	}

	var_mvstr=std::wstring(buffer.get(),outputsize);

	return *this;

#elif 1
	//for now only ascii conversion
	//if (lowerupper==1)
	//	converter(UPPERCASE_,LOWERCASE_);
	//else if (lowerupper==2)
	//	converter(LOWERCASE_, UPPERCASE_);

	//for now only fairly simple one for one conversion

	//if (lowerupper==1)
	//	boost::to_lower(var_mvstr);
	//else if (lowerupper==2)
	//	boost::to_upper(var_mvstr);

	size_t  length=var_mvstr.length();
	if (lowerupper==1)
		for (size_t ptr=0; ptr<length; ++ptr)
			var_mvstr[ptr]=towlower(var_mvstr[ptr]);
	else if (lowerupper==2)
		for (size_t ptr=0; ptr<length; ++ptr)
			var_mvstr[ptr]=towupper(var_mvstr[ptr]);

	return *this;

#elif defined(_MACOS)

//why not collate::compare?
//#elseif defined(_UNIX)
#elif defined(strcoll)
#elif !defined(XYZZZZ)
////	setlocale (LC_COLLATE, "en_US.UTF-8");
//	return wcscoll(str1.c_str(),str2.c_str());
	//TODO unicode/native conversion
	converter(LOWERCASE_, UPPERCASE_);
#else
#error stop here
	return str1.compare(str2);
#endif
}

bool var::setxlocale() const
{

#if defined(_MSC_VER) && defined(UNICODE)

	/* http://msdn.microsoft.com/en-us/library/dd374051%28v=VS.85%29.aspx
	//locale list
	//XP 2003 http://msdn.microsoft.com/en-us/goglobal/bb895996.aspx
	//http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx
	BOOL SetThreadLocale(
		__in  LCID Locale
	);
	*/
	//GetSystemDefaultLCID()
	//GetUserDefaultLCID()
	//LCID locale_lcid=1031;//German Standard
	//LCID locale_lcid=1032;//Greek
	//LCID locale_lcid=1055;//Turkish

	return SetThreadLocale((*this).toInt())!=NULL;

//#elif defined(_MACOS)
#else
	THISIS(L"bool var::setxlocale() const")
	THISISSTRING()

	//make a thread local locale if not done already
	//TODO do this in thread creation
	//TODO destroy threalocale in thread destruction *OTHERWISE MEMORY LEAK
	//to avoid checking on every setxlocale
	if (uselocale(NULL)==uselocale(LC_GLOBAL_LOCALE))
		uselocale(duplocale(uselocale(LC_GLOBAL_LOCALE)));

	return setlocale(LC_ALL,(*this).tostring().c_str())!=NULL;

#endif

}

var& var::getxlocale()
{
#if defined(_MSC_VER) && defined(UNICODE)
	*this=(int)GetThreadLocale();
	return *this;
//#elif defined(_MACOS)
#else
	//return "";
	*this=var(setlocale(LC_ALL,NULL));
	return *this;
#endif
}

} // namespace exodus

<commit_msg>minor update of localeAwareCompare()<commit_after>//UPCOMING BOOST LOCALE!
//http://cppcms.sourceforge.net/boost_locale/html/index.html

//#include <unicode/ustring.h>
//#include <boost/regex/icu.hpp>
/*
http://www.unicode.org/versions/Unicode5.0.0/ch02.pdf#G13708

boost reg expressions and icu
http://www.boost.org/libs/regex/doc/icu_strings.html

http://en.wikipedia.org/wiki/UTF-8
http://unicode.org/faq/utf_bom.html

Test words (this document is saved as UTF8 unicode)
Iñtërnâtiônàlizætiøn windows cp1252
 في اليونا

 web page to try out multilingual
 http://www.unicode.org/iuc/iuc10/x-utf8.html

notes:

0. bytes c0 c1 f5-ff never appear in utf8
1. postgres bytea can be treated as UTF8 for comparisons/indexing etc without conversion
2. icu likes everything to be in utf16
3. icu offers a fast conversion between utf8 and utf16 but can only replace illegal bytes with one character fast
(neosys uses a modified one that converts between F8-FF and u2558-u255F)

icu conversion functions allow custom function be convert illegal UTF8 pick field character bytes with some arbitrary unicode code points but what speed? this was not found/used ... maybe better than the custom function mentioned above

Strategy 1:

Store utf16 in postgres bytea and provide convertion to bytea(utf16)->utf8->text/number/date etc for sql comparisons/indexing etc

pros:
	no codeconversion during read/write/calculate
cons:
	continuous conversion on database operations with/by clauses
	double database size
	double data traffic read/write/calculate

Strategy 2:

Store utf8 in postgres bytea and convert to/from utf16 in the client program on read/write/calculate

pros:
	no code conversion during database operations with/by clauses
	smallest database size
	smallest data traffic read/write/calculate
pros:
	codeconversion during every read/write/calculate

UTF8 versus UTF16 implementation discussion
-------------------------------------------
this 2005 document advocates UTF8 but more for documents/interchange than implementations
http://www-128.ibm.com/developerworks/xml/library/x-utf8/

size issue is NOT the main thing - compatibility with api is the main problem
size issue is important for memory throughput and therefore processing speed
UTF16 is 100% larger for non ascii (0-127) characters
UTF8 is 50% larger than UTF16 for asian and CJK characters (but one CJK char = one word = many characters)
UTF8 can be thought of as a non-adaptive huffman compression format(ie "common" ascii requires fewer bits)
UTF8 is the only format not to have the endian issue
ORACLE/DB2/JBASE/POSTGRES is UTF8, SAP is UTF32, Windows/MSSQL is UTF16

UTF16 is very poorly supported on std Linux libraries since wchar_t is 4 bytes (gcc has compiler option for 2 bytes)
icu offers UTF16 string replacement but then we are intimately tied to icu library
UTF8 is supported very broadly in unix/linux libraries
windows/java/icu/iisjavascript is UTF16 (unavoidable conversion into UTF16 in IIS even if web page is sent as UTF8!)
i18n web pages are UTF8 in the end

UTF8 negatives
character indexing/iteration  using integers is slow (until code migrates to proper iterators)
slower for asian and CJK
complicated bit pattern
doesnt handle binary (lots of illegal characters/UTF16 doesnt have this problem IF you ignore surrogates as Java does)

UTF16 negatives
probably become highly dependent on icu (string instead of std::wstring)
slower for ascii
doesnt work with c std strings (unless you use java style encoding c0 80 to represent nulls)
endian issues not a problem for implementations only for transmissions

so in the end this is a compatibility issue

Unicode in DB2 version 9 (UTF8)
http://publib.boulder.ibm.com/infocenter/db2luw/v9/topic/com.ibm.db2.udb.admin.doc/doc/c0004821.htm
lots of good explanation including
- during utf16->utf8 conversion surrogate pairs used to be converted to two x three byte sequences but no more.
- they treat combining characters separately
- implements UCA for collation

qt strings are unicode

*/

//#include <boost/algorithm/string.hpp>
#include <wctype.h>

#if defined(_MSC_VER)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <string.h>
#endif

#include <boost/scoped_array.hpp>
#include <locale.h>
#define MV_NO_NARROW
#include <exodus/mv.h>
#include <exodus/mvexceptions.h>

namespace exodus {

int var::localeAwareCompare(const std::wstring& str1, const std::wstring& str2) const
{
	if (str1.length()==0&&str2.length()==0)
		return 0;

#if defined(_MSC_VER) && defined(UNICODE)

	/*
	Now of course this points to what may be the best solution for a single function
    that will let you pass string length, ignore case, choose an appropriate locale,
    and work in different versions of Windows -- the master NLS collation function,
	CompareStringW !
	*/

	int comparison;

	//CompareStringW
	//comparison=CompareStringW(GetUserDefaultLCID(), 0,

	// UNICODE is always defined so CompareString is CompareStringW.
	comparison=CompareString(GetThreadLocale(), 0,
		(TCHAR*)str1.data(), int(str1.length()),
		(TCHAR*)str2.data(), int(str2.length()));
	switch (comparison) {
	case CSTR_LESS_THAN:
		return -1;
	case CSTR_GREATER_THAN:
		return 1;
	case CSTR_EQUAL:
		return 0;
	default:
		throw MVException( L"localeAwareCompare(" ^ str1 ^ L", " ^ str2 ^ L")\n");
	}

#elif defined(_MACOS)
    // order strings the same way as native applications do, and also respects
    // the "Order for sorted lists" setting in the International preferences panel
	const CFStringRef thisString=CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
		reinterpret_cast<const UniChar *>(str1.data()),str1.length(),kCFAllocatorNull);
	const CFStringRef otherString=CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
		reinterpret_cast<const UniChar *>(data2), length2, kCFAllocatorNull);

	const int result = CFStringCompare(thisString, otherString, kCFCompareLocalized);
	CFRelease(thisString);
	CFRelease(otherString);
	return result;

//why not collate::compare?
//#elseif defined(_UNIX)
#elif defined(strcoll)
#elif !defined(XYZZZZ)
//	setlocale (LC_COLLATE, "en_US.UTF-8");
	return wcscoll(str1.c_str(),str2.c_str());
#else
#error stop here
	return str1.compare(str2);
#endif
}

var& var::localeAwareChangeCase(const int lowerupper)
{
	if (var_mvstr.length()==0)
		return *this;

#if defined(_MSC_VER) && defined(UNICODE)

	//TODO http://stackoverflow.com/questions/1767946/getthreadlocale-returns-different-value-than-getuserdefaultlcid
	//Microsoft is deprecating the Locale ID in favor of the Locale Name from Vista onwards

	/*
	http://msdn.microsoft.com/en-us/library/dd318700%28v=VS.85%29.aspx
	int LCMapString(
		__in   LCID Locale,
		__in   DWORD dwMapFlags,
		__in   LPCTSTR lpSrcStr,
		__in   int cchSrc,
		__out  LPTSTR lpDestStr,
		__in   int cchDest
	);
	return 0 if it DOESNT succeed
	GetLastError()
    * ERROR_INSUFFICIENT_BUFFER. A supplied buffer size was not large enough, or it was incorrectly set to NULL.
    * ERROR_INVALID_FLAGS. The values supplied for flags were not valid.
    * ERROR_INVALID_PARAMETER. Any of the parameter values was invalid.
	*/

	//uppercasing can double the number of characters (eg german b to SS) can it triple?
	int buffersize=(int) var_mvstr.length()*2+2;
	boost::scoped_array<TCHAR> buffer( new TCHAR [buffersize]);
	if (buffer==0)
		throw MVException(var(L"Out of memory in changecase(). Need ")^int(buffersize)^L" characters");

	int tolowerupper=LCMAP_LINGUISTIC_CASING;
	if (lowerupper==1)
		tolowerupper+=LCMAP_LOWERCASE;
	else if (lowerupper==2)
		tolowerupper+=LCMAP_UPPERCASE;

	//LCMapStringW
	int outputsize=LCMapStringW(
		GetThreadLocale(),
		tolowerupper,
		(TCHAR*)var_mvstr.data(),
		(int) var_mvstr.length(),
		buffer.get(),
		(int) buffersize);

	//cant convert for some reason. see above
	if (!outputsize)
	{
		//for now only ascii conversion
		if (lowerupper==1)
			return converter(UPPERCASE_,LOWERCASE_);
		else if (lowerupper==2)
			return converter(LOWERCASE_, UPPERCASE_);
	}

	var_mvstr=std::wstring(buffer.get(),outputsize);

	return *this;

#elif 1
	//for now only ascii conversion
	//if (lowerupper==1)
	//	converter(UPPERCASE_,LOWERCASE_);
	//else if (lowerupper==2)
	//	converter(LOWERCASE_, UPPERCASE_);

	//for now only fairly simple one for one conversion

	//if (lowerupper==1)
	//	boost::to_lower(var_mvstr);
	//else if (lowerupper==2)
	//	boost::to_upper(var_mvstr);

	size_t  length=var_mvstr.length();
	if (lowerupper==1)
		for (size_t ptr=0; ptr<length; ++ptr)
			var_mvstr[ptr]=towlower(var_mvstr[ptr]);
	else if (lowerupper==2)
		for (size_t ptr=0; ptr<length; ++ptr)
			var_mvstr[ptr]=towupper(var_mvstr[ptr]);

	return *this;

#elif defined(_MACOS)

//why not collate::compare?
//#elseif defined(_UNIX)
#elif defined(strcoll)
#elif !defined(XYZZZZ)
////	setlocale (LC_COLLATE, "en_US.UTF-8");
//	return wcscoll(str1.c_str(),str2.c_str());
	//TODO unicode/native conversion
	converter(LOWERCASE_, UPPERCASE_);
#else
#error stop here
	return str1.compare(str2);
#endif
}

bool var::setxlocale() const
{

#if defined(_MSC_VER) && defined(UNICODE)

	/* http://msdn.microsoft.com/en-us/library/dd374051%28v=VS.85%29.aspx
	//locale list
	//XP 2003 http://msdn.microsoft.com/en-us/goglobal/bb895996.aspx
	//http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx
	BOOL SetThreadLocale(
		__in  LCID Locale
	);
	*/
	//GetSystemDefaultLCID()
	//GetUserDefaultLCID()
	//LCID locale_lcid=1031;//German Standard
	//LCID locale_lcid=1032;//Greek
	//LCID locale_lcid=1055;//Turkish

	return SetThreadLocale((*this).toInt())!=NULL;

//#elif defined(_MACOS)
#else
	THISIS(L"bool var::setxlocale() const")
	THISISSTRING()

	//make a thread local locale if not done already
	//TODO do this in thread creation
	//TODO destroy threalocale in thread destruction *OTHERWISE MEMORY LEAK
	//to avoid checking on every setxlocale
	if (uselocale(NULL)==uselocale(LC_GLOBAL_LOCALE))
		uselocale(duplocale(uselocale(LC_GLOBAL_LOCALE)));

	return setlocale(LC_ALL,(*this).tostring().c_str())!=NULL;

#endif

}

var& var::getxlocale()
{
#if defined(_MSC_VER) && defined(UNICODE)
	*this=(int)GetThreadLocale();
	return *this;
//#elif defined(_MACOS)
#else
	//return "";
	*this=var(setlocale(LC_ALL,NULL));
	return *this;
#endif
}

} // namespace exodus

<|endoftext|>