<commit_before>#pragma once
#include <string>
#include <chrono>
#include <functional>
#include <deque>
#include "optional.hpp"
#include "abstbuff.hpp"
#include "serialization/chars.hpp"
#include <boost/serialization/access.hpp>
#include <boost/serialization/deque.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/base_object.hpp>

struct stat;
namespace spn {
	class PathBlock {
		public:
			enum {
				Beg = 0,
				End = 0xffff
			};
		protected:
			using Path = std::deque<char32_t>;
			using Segment = std::deque<int>;
			using OPChar = spn::Optional<char>;
			//! 絶対パスにおける先頭スラッシュを除いたパス
			Path		_path;
			//! 区切り文字の前からのオフセット
			Segment		_segment;
			const static char32_t SC,		//!< セパレート文字
								DOT,		//!< 拡張子の前の記号
								EOS,		//!< 終端記号
								CLN;		//!< コロン :
			bool		_bAbsolute;
			OPChar		_driveLetter;		//!< ドライブ文字(Windows用。Linuxでは無視)　\0=無効

			static bool _IsSC(char32_t c);
			//! パスを分解しながらセグメント長をカウントし、コールバック関数を呼ぶ
			/*! fromからtoまで1文字ずつ見ながら区切り文字を直す */
			template <class CB>
			static void _ReWriteSC(Path::iterator from, Path::iterator to, char32_t sc, CB cb);

			static int _ExtGetNum(const std::string& ext);
			static int _ExtIncNum(std::string& ext, int n=1);
			template <class CB>
			void _iterateSegment(const char32_t* c, int /*len*/, char32_t /*sc*/, CB cb) {
				char32_t tc[128];
				auto* pTc = tc;
				while(*c != EOS) {
					if(_IsSC(*c)) {
						*pTc = EOS;
						cb(tc, pTc-tc);
						pTc = tc;
					} else
						*pTc++ = *c;
					++c;
				}
			}
			template <class Itr>
			static auto _GetDriveLetter(Itr from, Itr to) -> spn::Optional<typename std::decay<decltype(*from)>::type> {
				using CH = typename std::decay<decltype(*from)>::type;
				auto cnvToCh = [](char c) {
					char32_t c2 = static_cast<char32_t>(c);
					return UTFToN<CH>(reinterpret_cast<const char*>(&c2)).code;
				};
				Itr tmp_from = from;
				if(to - from >= 3) {
					auto c = *from;
					CH cA = cnvToCh('A'),
						cZ = cnvToCh('Z'),
						ca = cnvToCh('a'),
						cz = cnvToCh('z'),
						col = cnvToCh(':');
					if((c >= cA && c <= cZ) ||
							(c >= ca && c <= cz))
					{
						if(*(++from) == col &&
							_IsSC(UTFToN<char32_t>(&*(++from)).code))
							return *tmp_from;
					}
				}
				return spn::none;
			}

			void _outputHeader(std::u32string& dst, bool bAbs) const;
        private:
            friend class boost::serialization::access;
            template <class Archive>
            void serialize(Archive& ar, const unsigned int) {
				std::vector<uint32_t> sv;
				if(typename Archive::is_saving()) {
					std::copy(_path.begin(), _path.end(), std::back_inserter(sv));
					ar & boost::serialization::make_nvp("path", sv);
				}
				if(typename Archive::is_loading()) {
					ar & boost::serialization::make_nvp("path", sv);
					_path.clear();
					std::copy(sv.begin(), sv.end(), std::back_inserter(_path));
				}
                ar & BOOST_SERIALIZATION_NVP(_segment) & BOOST_SERIALIZATION_NVP(_bAbsolute);
            }

		public:
			template <class C>
			struct StripResult {
				using OpC = spn::Optional<C>;
				bool 	bAbsolute;
				OpC		driveLetter;
				int		nread;
			};
			template <class C>
			using OPStripResult = spn::Optional<StripResult<C>>;
			//! 前後の余分な区切り文字を省く
			/*! \return [NeedOperation, AbsoluteFlag] */
			template <class Itr>
			static auto StripSC(Itr from, Itr to) -> OPStripResult<typename std::decay<decltype(*from)>::type>;
			/*! Windowsの場合は何もせずfromを返す
				Linuxの場合は先頭のドライブ文字を削った後のポインタを返す */
			static const char* RemoveDriveLetter(const char* from, const char* to);

			PathBlock();
			PathBlock(const PathBlock&) = default;
			PathBlock(PathBlock&& p);
			PathBlock(To32Str p);

			bool operator == (const PathBlock& p) const;
			bool operator != (const PathBlock& p) const;
			PathBlock& operator = (const PathBlock&) = default;
			PathBlock& operator = (PathBlock&& p);
			PathBlock& operator <<= (To32Str elem);
			PathBlock& operator <<= (const PathBlock& p);

			//! パスを後ろに追加。引数が絶対パスの時は置き換える
			void pushBack(To32Str elem);
			void pushBack(const PathBlock& p);
			void popBack();
			//! パスを前に追加。thisが絶対パスの時は置き換える
			void pushFront(To32Str elem);
			void pushFront(const PathBlock& p);
			void popFront();

			std::string plain_utf8(bool bAbs=true) const;
			std::string getFirst_utf8(bool bAbs=true) const;
			std::string getLast_utf8() const;
			std::string getSegment_utf8(int beg, int end) const;
			std::string getHeader_utf8() const;
			std::u32string plain_utf32(bool bAbs=true) const;
			std::u32string getFirst_utf32(bool bAbs=true) const;
			std::u32string getLast_utf32() const;
			std::u32string getSegment_utf32(int beg, int end) const;
			std::u32string getHeader_utf32() const;

			OPChar getDriveLetter() const;

			int size() const;
			int segments() const;
			void setPath(To32Str p);
			bool isAbsolute() const;
			bool hasExtention() const;
			void setExtension(To32Str ext);
			std::string getExtension(bool bRaw=false) const;
			int getExtNum() const;
			int addExtNum(int n=1);
			void clear();
			bool empty() const;
	};
	class URI : public PathBlock {
		const static std::string SEP;
		const static std::u32string SEP32;
		std::string		_type;

        friend class boost::serialization::access;
        template <class Archive>
        void serialize(Archive& ar, const unsigned int) {
            ar & BOOST_SERIALIZATION_NVP(_type) & BOOST_SERIALIZATION_BASE_OBJECT_NVP(PathBlock);
        }

		public:
			URI();
			URI(To8Str p);
			URI(const URI& u) = default;
			URI(URI&& u);
			URI(To8Str typ, To8Str path);
			URI(To8Str typ, const PathBlock& pb);

			URI& operator = (const URI&) = default;
			URI& operator = (URI&& u);

			void setPath(To8Str p);
			const std::string& getType_utf8() const;
			std::string plainUri_utf8() const;
			std::u32string plainUri_utf32() const;
			void setType(To8Str typ);
			const PathBlock& path() const;
			PathBlock& path();
	};

	uint64_t UnixTime2WinTime(time_t t);
	time_t WinTime2UnixTime(uint64_t ft);
	uint64_t u32_u64(uint32_t valH, uint32_t valL);

	//! ファイルアクセス時間比較用
	struct FTime {
		uint64_t	tmAccess,
					tmModify,
					tmCreated;

		bool operator == (const FTime& ft) const;
		bool operator != (const FTime& ft) const;
	};
	struct FStatus {
		enum Flag : uint32_t {
			UserRead = 0x100,
			UserWrite = 0x80,
			UserExec = 0x40,
			UserRWX = UserRead | UserWrite | UserExec,

			GroupRead = 0x20,
			GroupWrite = 0x10,
			GroupExec = 0x08,
			GroupRWX = GroupRead | GroupWrite | GroupExec,

			OtherRead = 0x04,
			OtherWrite = 0x02,
			OtherExec = 0x01,
			OtherRWX = OtherRead | OtherWrite | OtherExec,

			AllRead = UserRead | GroupRead | OtherRead,
			AllWrite = UserWrite | GroupWrite | OtherWrite,
			AllExec = UserExec | GroupExec | OtherExec,
			AllRW = AllRead | AllWrite,

			FileType = 0x200,
			DirectoryType = 0x400,
			NotAvailable = 0x800
		};

		uint32_t	flag;
		uint32_t	userId,
					groupId;
		uint64_t	size;
		FTime		ftime;

		FStatus() = default;
		FStatus(uint32_t flag);
		bool operator == (const FStatus& fs) const;
		bool operator != (const FStatus& fs) const;
	};
}
<commit_msg>PathBlock: シリアライズデータ形式を変更<commit_after>#pragma once
#include <string>
#include <chrono>
#include <functional>
#include <deque>
#include "optional.hpp"
#include "abstbuff.hpp"
#include "serialization/chars.hpp"
#include <boost/serialization/access.hpp>
#include <boost/serialization/deque.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/base_object.hpp>

struct stat;
namespace spn {
	class PathBlock {
		public:
			enum {
				Beg = 0,
				End = 0xffff
			};
		protected:
			using Path = std::deque<char32_t>;
			using Segment = std::deque<int>;
			using OPChar = spn::Optional<char>;
			//! 絶対パスにおける先頭スラッシュを除いたパス
			Path		_path;
			//! 区切り文字の前からのオフセット
			Segment		_segment;
			const static char32_t SC,		//!< セパレート文字
								DOT,		//!< 拡張子の前の記号
								EOS,		//!< 終端記号
								CLN;		//!< コロン :
			bool		_bAbsolute;
			OPChar		_driveLetter;		//!< ドライブ文字(Windows用。Linuxでは無視)　\0=無効

			static bool _IsSC(char32_t c);
			//! パスを分解しながらセグメント長をカウントし、コールバック関数を呼ぶ
			/*! fromからtoまで1文字ずつ見ながら区切り文字を直す */
			template <class CB>
			static void _ReWriteSC(Path::iterator from, Path::iterator to, char32_t sc, CB cb);

			static int _ExtGetNum(const std::string& ext);
			static int _ExtIncNum(std::string& ext, int n=1);
			template <class CB>
			void _iterateSegment(const char32_t* c, int /*len*/, char32_t /*sc*/, CB cb) {
				char32_t tc[128];
				auto* pTc = tc;
				while(*c != EOS) {
					if(_IsSC(*c)) {
						*pTc = EOS;
						cb(tc, pTc-tc);
						pTc = tc;
					} else
						*pTc++ = *c;
					++c;
				}
			}
			template <class Itr>
			static auto _GetDriveLetter(Itr from, Itr to) -> spn::Optional<typename std::decay<decltype(*from)>::type> {
				using CH = typename std::decay<decltype(*from)>::type;
				auto cnvToCh = [](char c) {
					char32_t c2 = static_cast<char32_t>(c);
					return UTFToN<CH>(reinterpret_cast<const char*>(&c2)).code;
				};
				Itr tmp_from = from;
				if(to - from >= 3) {
					auto c = *from;
					CH cA = cnvToCh('A'),
						cZ = cnvToCh('Z'),
						ca = cnvToCh('a'),
						cz = cnvToCh('z'),
						col = cnvToCh(':');
					if((c >= cA && c <= cZ) ||
							(c >= ca && c <= cz))
					{
						if(*(++from) == col &&
							_IsSC(UTFToN<char32_t>(&*(++from)).code))
							return *tmp_from;
					}
				}
				return spn::none;
			}

			void _outputHeader(std::u32string& dst, bool bAbs) const;
        private:
            friend class boost::serialization::access;
            template <class Archive>
            void serialize(Archive& ar, const unsigned int) {
				// UTF-8文字列として書き出し
				std::string path;
				if(typename Archive::is_saving()) {
					path = plain_utf8();
					ar & BOOST_SERIALIZATION_NVP(path);
				}
				if(typename Archive::is_loading()) {
					ar & BOOST_SERIALIZATION_NVP(path);
					setPath(path);
				}
            }

		public:
			template <class C>
			struct StripResult {
				using OpC = spn::Optional<C>;
				bool 	bAbsolute;
				OpC		driveLetter;
				int		nread;
			};
			template <class C>
			using OPStripResult = spn::Optional<StripResult<C>>;
			//! 前後の余分な区切り文字を省く
			/*! \return [NeedOperation, AbsoluteFlag] */
			template <class Itr>
			static auto StripSC(Itr from, Itr to) -> OPStripResult<typename std::decay<decltype(*from)>::type>;
			/*! Windowsの場合は何もせずfromを返す
				Linuxの場合は先頭のドライブ文字を削った後のポインタを返す */
			static const char* RemoveDriveLetter(const char* from, const char* to);

			PathBlock();
			PathBlock(const PathBlock&) = default;
			PathBlock(PathBlock&& p);
			PathBlock(To32Str p);

			bool operator == (const PathBlock& p) const;
			bool operator != (const PathBlock& p) const;
			PathBlock& operator = (const PathBlock&) = default;
			PathBlock& operator = (PathBlock&& p);
			PathBlock& operator <<= (To32Str elem);
			PathBlock& operator <<= (const PathBlock& p);

			//! パスを後ろに追加。引数が絶対パスの時は置き換える
			void pushBack(To32Str elem);
			void pushBack(const PathBlock& p);
			void popBack();
			//! パスを前に追加。thisが絶対パスの時は置き換える
			void pushFront(To32Str elem);
			void pushFront(const PathBlock& p);
			void popFront();

			std::string plain_utf8(bool bAbs=true) const;
			std::string getFirst_utf8(bool bAbs=true) const;
			std::string getLast_utf8() const;
			std::string getSegment_utf8(int beg, int end) const;
			std::string getHeader_utf8() const;
			std::u32string plain_utf32(bool bAbs=true) const;
			std::u32string getFirst_utf32(bool bAbs=true) const;
			std::u32string getLast_utf32() const;
			std::u32string getSegment_utf32(int beg, int end) const;
			std::u32string getHeader_utf32() const;

			OPChar getDriveLetter() const;

			int size() const;
			int segments() const;
			void setPath(To32Str p);
			bool isAbsolute() const;
			bool hasExtention() const;
			void setExtension(To32Str ext);
			std::string getExtension(bool bRaw=false) const;
			int getExtNum() const;
			int addExtNum(int n=1);
			void clear();
			bool empty() const;
	};
	class URI : public PathBlock {
		const static std::string SEP;
		const static std::u32string SEP32;
		std::string		_type;

        friend class boost::serialization::access;
        template <class Archive>
        void serialize(Archive& ar, const unsigned int) {
            ar & BOOST_SERIALIZATION_NVP(_type) & BOOST_SERIALIZATION_BASE_OBJECT_NVP(PathBlock);
        }

		public:
			URI();
			URI(To8Str p);
			URI(const URI& u) = default;
			URI(URI&& u);
			URI(To8Str typ, To8Str path);
			URI(To8Str typ, const PathBlock& pb);

			URI& operator = (const URI&) = default;
			URI& operator = (URI&& u);

			void setPath(To8Str p);
			const std::string& getType_utf8() const;
			std::string plainUri_utf8() const;
			std::u32string plainUri_utf32() const;
			void setType(To8Str typ);
			const PathBlock& path() const;
			PathBlock& path();
	};

	uint64_t UnixTime2WinTime(time_t t);
	time_t WinTime2UnixTime(uint64_t ft);
	uint64_t u32_u64(uint32_t valH, uint32_t valL);

	//! ファイルアクセス時間比較用
	struct FTime {
		uint64_t	tmAccess,
					tmModify,
					tmCreated;

		bool operator == (const FTime& ft) const;
		bool operator != (const FTime& ft) const;
	};
	struct FStatus {
		enum Flag : uint32_t {
			UserRead = 0x100,
			UserWrite = 0x80,
			UserExec = 0x40,
			UserRWX = UserRead | UserWrite | UserExec,

			GroupRead = 0x20,
			GroupWrite = 0x10,
			GroupExec = 0x08,
			GroupRWX = GroupRead | GroupWrite | GroupExec,

			OtherRead = 0x04,
			OtherWrite = 0x02,
			OtherExec = 0x01,
			OtherRWX = OtherRead | OtherWrite | OtherExec,

			AllRead = UserRead | GroupRead | OtherRead,
			AllWrite = UserWrite | GroupWrite | OtherWrite,
			AllExec = UserExec | GroupExec | OtherExec,
			AllRW = AllRead | AllWrite,

			FileType = 0x200,
			DirectoryType = 0x400,
			NotAvailable = 0x800
		};

		uint32_t	flag;
		uint32_t	userId,
					groupId;
		uint64_t	size;
		FTime		ftime;

		FStatus() = default;
		FStatus(uint32_t flag);
		bool operator == (const FStatus& fs) const;
		bool operator != (const FStatus& fs) const;
	};
}
<|endoftext|>


<commit_before>/*
  Copyright (C) 2013-2015 The Communi Project

  You may use this file under the terms of BSD license as follows:

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the copyright holder nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "bufferproxymodel.h"
#include "simplecrypt.h"
#include "zncmanager.h"
#include <QCoreApplication>
#include <QTimer>
#include <IrcBufferModel>
#include <IrcConnection>
#include <IrcBuffer>

IRC_USE_NAMESPACE

class IrcServerBuffer : public IrcBuffer
{
    Q_OBJECT

public:
    explicit IrcServerBuffer(QObject* parent = 0) : IrcBuffer(parent) { }
    ~IrcServerBuffer() { quit(tr("%1 %2").arg(qApp->applicationName(), qApp->applicationVersion())); }

public slots:
    void close(const QString& reason)
    {
        quit(reason);
        IrcBuffer::close(reason);
    }

private slots:
    void quit(const QString& reason)
    {
        IrcConnection* connection = IrcBuffer::connection();
        if (connection && connection->isActive()) {
            connection->quit(reason);
            connection->close();
        }
    }
};

BufferProxyModel::BufferProxyModel(QObject* parent) : RowsJoinerProxy(parent)
{
}

IrcBuffer* BufferProxyModel::get(int index) const
{
    foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
        IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
        if (model) {
            int count = model->count();
            if (index < count)
                return model->get(index);
            index -= count;
        }
    }
    return 0;
}

int BufferProxyModel::indexOf(IrcBuffer* buffer) const
{
    int count = 0;
    foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
        IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
        if (model) {
            int index = model->indexOf(buffer);
            if (index != -1)
                return count + index;
            count += model->count();
        }
    }
    return -1;
}

QList<QObject*> BufferProxyModel::models() const
{
    return m_models;
}

QList<QObject*> BufferProxyModel::servers() const
{
    return m_servers;
}

QList<QObject*> BufferProxyModel::connections() const
{
    return m_connections;
}

IrcBuffer* BufferProxyModel::currentBuffer() const
{
    return m_current;
}

void BufferProxyModel::setCurrentBuffer(IrcBuffer* buffer)
{
    if (m_current != buffer) {
        m_current = buffer;
        emit currentBufferChanged(buffer);
    }
}

QObject* BufferProxyModel::model(IrcConnection* connection) const
{
    if (connection)
        return connection->findChild<IrcBufferModel*>();
    return 0;
}

QObject* BufferProxyModel::server(IrcConnection* connection) const
{
    if (connection)
        return connection->findChild<IrcServerBuffer*>();
    return 0;
}

void BufferProxyModel::addConnection(IrcConnection* connection)
{
    IrcBufferModel* model = new IrcBufferModel(connection);
    model->setSortMethod(Irc::SortByTitle);
    connect(model, SIGNAL(added(IrcBuffer*)), this, SIGNAL(bufferAdded(IrcBuffer*)));
    connect(model, SIGNAL(removed(IrcBuffer*)), this, SIGNAL(bufferRemoved(IrcBuffer*)));
    connect(model, SIGNAL(aboutToBeAdded(IrcBuffer*)), this, SIGNAL(bufferAboutToBeAdded(IrcBuffer*)));
    connect(model, SIGNAL(aboutToBeRemoved(IrcBuffer*)), this, SIGNAL(bufferAboutToBeRemoved(IrcBuffer*)));

    ZncManager* znc = new ZncManager(model);
    znc->setModel(model);

    IrcServerBuffer* buffer = new IrcServerBuffer(model);
    connect(buffer, SIGNAL(destroyed(IrcBuffer*)), this, SLOT(closeConnection(IrcBuffer*)));
    buffer->setName(connection->displayName());
    buffer->setSticky(true);
    model->add(buffer);

    connection->setReconnectDelay(15); // TODO: settings?
    connect(connection, SIGNAL(displayNameChanged(QString)), buffer, SLOT(setName(QString)));
    connect(model, SIGNAL(messageIgnored(IrcMessage*)), this, SLOT(processMessage(IrcMessage*)));

    connect(connection, SIGNAL(connected()), this, SLOT(onConnected()));
    connect(connection, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
    connect(connection, SIGNAL(enabledChanged(bool)), this, SLOT(onConnectionEnabledChanged(bool)));
    connect(connection, SIGNAL(nickNameRequired(QString,QString*)), this, SLOT(onNickNameRequired(QString)));
    connect(connection, SIGNAL(channelKeyRequired(QString,QString*)), this, SLOT(onChannelKeyRequired(QString)));

    // Give bouncers a sec or two to start joining channels after getting connected.
    // If that happens, the saved buffers shouldn't be restored to avoid restoring
    // a buffer that was closed using another client meanwhile.
    QTimer* timer = new QTimer(connection);
    timer->setSingleShot(true);
    timer->setInterval(2000);
    connect(connection, SIGNAL(connected()), timer, SLOT(start()));
    QObject::connect(timer, &QTimer::timeout, [=]() -> void {
        bool hasActiveChannels = false;
        foreach (const QString& name, model->channels()) {
            IrcBuffer* channel = model->find(name);
            if (channel && channel->isActive()) {
                hasActiveChannels = true;
                break;
            }
        }
        if (!hasActiveChannels)
            model->restoreState(model->property("savedState").toByteArray());
        connect(model, SIGNAL(buffersChanged(QList<IrcBuffer*>)), this, SLOT(onModelBuffersChanged()));
    });

    m_connections.append(connection);
    m_servers.append(buffer);
    m_models.append(model);

    emit connectionAdded(connection);
    emit connectionsChanged();
    emit serversChanged();
    emit modelsChanged();

    insertSourceModel(model);
}

void BufferProxyModel::removeConnection(IrcConnection* connection)
{
    disconnect(connection, SIGNAL(connected()), this, SLOT(onConnected()));
    disconnect(connection, SIGNAL(disconnected()), this, SLOT(onDisconnected()));

    IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
    if (model) {
        disconnect(model, SIGNAL(added(IrcBuffer*)), this, SIGNAL(bufferAdded(IrcBuffer*)));
        disconnect(model, SIGNAL(removed(IrcBuffer*)), this, SIGNAL(bufferRemoved(IrcBuffer*)));
        disconnect(model, SIGNAL(aboutToBeAdded(IrcBuffer*)), this, SIGNAL(bufferAboutToBeAdded(IrcBuffer*)));
        disconnect(model, SIGNAL(aboutToBeRemoved(IrcBuffer*)), this, SIGNAL(bufferAboutToBeRemoved(IrcBuffer*)));

        int index = m_connections.indexOf(connection);
        if (index != -1) {
            if (QObject* server = m_servers.takeAt(index))
                server->disconnect(this);
            if (QObject* model = m_models.takeAt(index))
                model->deleteLater();
            if (QObject* connection = m_connections.takeAt(index))
                connection->deleteLater();

            emit connectionRemoved(connection);
            emit connectionsChanged();
            emit serversChanged();
            emit modelsChanged();

            removeSourceModel(model);

            if (m_models.isEmpty())
                emit reseted();
        }
    }
}

QHash<int, QByteArray> BufferProxyModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[Qt::DisplayRole] = "display";
    roles[Qt::UserRole] = "section";
    roles[Irc::BufferRole] = "buffer";
    roles[Irc::ChannelRole] = "channel";
    roles[Irc::NameRole] = "name";
    roles[Irc::PrefixRole] = "prefix";
    roles[Irc::TitleRole] = "title";
    return roles;
}

QVariant BufferProxyModel::data(const QModelIndex& index, int role) const
{
    if (role == Qt::UserRole) {
        IrcBuffer* buffer = data(index, Irc::BufferRole).value<IrcBuffer*>();
        if (buffer)
            return m_connections.indexOf(buffer->connection()); // TODO: optimize
    }
    return RowsJoinerProxy::data(index, role);
}

QByteArray BufferProxyModel::saveState() const
{
    QVariantList modelStates;
    QVariantList connectionStates;
    SimpleCrypt crypto(Q_UINT64_C(0xff610ed9de767b09));

    foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
        IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
        if (model) {
            IrcConnection* connection = model->connection();
            connectionStates += crypto.encryptToByteArray(connection->saveState());
            // do not save the server buffer - let addConnection() handle it when restoring
            bool wasConnected = connection->isEnabled() && connection->isConnected();
            model->remove(model->get(0));
            if (wasConnected)
                modelStates += crypto.encryptToByteArray(model->saveState());
            else
                modelStates += crypto.encryptToByteArray(model->property("savedState").toByteArray());
        }
    }

    QVariantMap state;
    state.insert("models", modelStates);
    state.insert("connections", connectionStates);

    QByteArray data;
    QDataStream out(&data, QIODevice::WriteOnly);
    out << state;
    return data;
}

bool BufferProxyModel::restoreState(const QByteArray& data)
{
    QVariantMap state;
    QDataStream in(data);
    in >> state;
    if (in.status() != QDataStream::Ok)
        return false;

    QVariantList modelStates = state.value("models").toList();
    QVariantList connectionStates = state.value("connections").toList();
    SimpleCrypt crypto(Q_UINT64_C(0xff610ed9de767b09));

    for (int i = 0; i < connectionStates.length(); ++i) {
        IrcConnection* connection = new IrcConnection(this);
        QByteArray cs = crypto.decryptToByteArray(connectionStates.at(i).toByteArray());
        connection->restoreState(!crypto.lastError() ? cs : connectionStates.at(i).toByteArray());
        addConnection(connection);
        IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
        QByteArray ms = crypto.decryptToByteArray(modelStates.value(i).toByteArray());
        model->setProperty("savedState", !crypto.lastError() ? ms : modelStates.value(i));
    }
    return true;
}

void BufferProxyModel::onConnected()
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit connected(connection);
}

void BufferProxyModel::onDisconnected()
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit disconnected(connection);
}

void BufferProxyModel::onModelBuffersChanged()
{
    IrcBufferModel* model = qobject_cast<IrcBufferModel*>(sender());
    if (model) {
        IrcConnection* connection = model->connection();
        if (connection && connection->isConnected())
            model->setProperty("savedState", model->saveState());
    }
}

void BufferProxyModel::onConnectionEnabledChanged(bool enabled)
{
    // store the model state when a connection is disabled
    // see #25: Don't save/restore buffers for disabled connections
    if (!enabled) {
        IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
        if (connection) {
            IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
            if (model && model->count() > 1)
                model->setProperty("savedState", model->saveState());
        }
    }
}

void BufferProxyModel::closeConnection(IrcBuffer* buffer)
{
    removeConnection(buffer->connection());
}

void BufferProxyModel::onChannelKeyRequired(const QString& channel)
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit channelKeyRequired(connection, channel);
}

void BufferProxyModel::onNickNameRequired(const QString& reserved)
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit nickNameRequired(connection, reserved);
}

void BufferProxyModel::processMessage(IrcMessage* message)
{
    IrcBufferModel* model = message->connection()->findChild<IrcBufferModel*>();
    if (model) {
        // deliver targeted ChanServ notices to the target channel
        // ":ChanServ!ChanServ@services. NOTICE myself :[#channel] foo bar..."
        if (message->type() == IrcMessage::Notice) {
            if (message->prefix() == "ChanServ!ChanServ@services.") {
                QString content = static_cast<IrcNoticeMessage*>(message)->content();
                if (content.startsWith("[")) {
                    int i = content.indexOf("]");
                    if (i != -1) {
                        QString title = content.mid(1, i - 1);
                        IrcBuffer* buffer = model->find(title);
                        if (buffer) {
                            message->setProperty("forwarded", true);
                            buffer->receiveMessage(message);
                            return;
                        }
                    }
                }
            }
            if (m_current && m_current->model() == model) {
                m_current->receiveMessage(message);
                return;
            }
        }
        if (IrcBuffer* buffer = model->get(0))
            buffer->receiveMessage(message);
    }
}

#include "bufferproxymodel.moc"
<commit_msg>Use IrcCommandQueue<commit_after>/*
  Copyright (C) 2013-2015 The Communi Project

  You may use this file under the terms of BSD license as follows:

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the copyright holder nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "bufferproxymodel.h"
#include "simplecrypt.h"
#include "zncmanager.h"
#include <QCoreApplication>
#include <QTimer>
#include <IrcCommandQueue>
#include <IrcBufferModel>
#include <IrcConnection>
#include <IrcBuffer>

IRC_USE_NAMESPACE

class IrcServerBuffer : public IrcBuffer
{
    Q_OBJECT

public:
    explicit IrcServerBuffer(QObject* parent = 0) : IrcBuffer(parent) { }
    ~IrcServerBuffer() { quit(tr("%1 %2").arg(qApp->applicationName(), qApp->applicationVersion())); }

public slots:
    void close(const QString& reason)
    {
        quit(reason);
        IrcBuffer::close(reason);
    }

private slots:
    void quit(const QString& reason)
    {
        IrcConnection* connection = IrcBuffer::connection();
        if (connection && connection->isActive()) {
            connection->quit(reason);
            connection->close();
        }
    }
};

BufferProxyModel::BufferProxyModel(QObject* parent) : RowsJoinerProxy(parent)
{
}

IrcBuffer* BufferProxyModel::get(int index) const
{
    foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
        IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
        if (model) {
            int count = model->count();
            if (index < count)
                return model->get(index);
            index -= count;
        }
    }
    return 0;
}

int BufferProxyModel::indexOf(IrcBuffer* buffer) const
{
    int count = 0;
    foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
        IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
        if (model) {
            int index = model->indexOf(buffer);
            if (index != -1)
                return count + index;
            count += model->count();
        }
    }
    return -1;
}

QList<QObject*> BufferProxyModel::models() const
{
    return m_models;
}

QList<QObject*> BufferProxyModel::servers() const
{
    return m_servers;
}

QList<QObject*> BufferProxyModel::connections() const
{
    return m_connections;
}

IrcBuffer* BufferProxyModel::currentBuffer() const
{
    return m_current;
}

void BufferProxyModel::setCurrentBuffer(IrcBuffer* buffer)
{
    if (m_current != buffer) {
        m_current = buffer;
        emit currentBufferChanged(buffer);
    }
}

QObject* BufferProxyModel::model(IrcConnection* connection) const
{
    if (connection)
        return connection->findChild<IrcBufferModel*>();
    return 0;
}

QObject* BufferProxyModel::server(IrcConnection* connection) const
{
    if (connection)
        return connection->findChild<IrcServerBuffer*>();
    return 0;
}

void BufferProxyModel::addConnection(IrcConnection* connection)
{
    IrcBufferModel* model = new IrcBufferModel(connection);
    model->setSortMethod(Irc::SortByTitle);
    connect(model, SIGNAL(added(IrcBuffer*)), this, SIGNAL(bufferAdded(IrcBuffer*)));
    connect(model, SIGNAL(removed(IrcBuffer*)), this, SIGNAL(bufferRemoved(IrcBuffer*)));
    connect(model, SIGNAL(aboutToBeAdded(IrcBuffer*)), this, SIGNAL(bufferAboutToBeAdded(IrcBuffer*)));
    connect(model, SIGNAL(aboutToBeRemoved(IrcBuffer*)), this, SIGNAL(bufferAboutToBeRemoved(IrcBuffer*)));

    ZncManager* znc = new ZncManager(model);
    znc->setModel(model);

    IrcCommandQueue* queue = new IrcCommandQueue(connection);
    queue->setConnection(connection);

    IrcServerBuffer* buffer = new IrcServerBuffer(model);
    connect(buffer, SIGNAL(destroyed(IrcBuffer*)), this, SLOT(closeConnection(IrcBuffer*)));
    buffer->setName(connection->displayName());
    buffer->setSticky(true);
    model->add(buffer);

    connection->setReconnectDelay(15); // TODO: settings?
    connect(connection, SIGNAL(displayNameChanged(QString)), buffer, SLOT(setName(QString)));
    connect(model, SIGNAL(messageIgnored(IrcMessage*)), this, SLOT(processMessage(IrcMessage*)));

    connect(connection, SIGNAL(connected()), this, SLOT(onConnected()));
    connect(connection, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
    connect(connection, SIGNAL(enabledChanged(bool)), this, SLOT(onConnectionEnabledChanged(bool)));
    connect(connection, SIGNAL(nickNameRequired(QString,QString*)), this, SLOT(onNickNameRequired(QString)));
    connect(connection, SIGNAL(channelKeyRequired(QString,QString*)), this, SLOT(onChannelKeyRequired(QString)));

    // Give bouncers a sec or two to start joining channels after getting connected.
    // If that happens, the saved buffers shouldn't be restored to avoid restoring
    // a buffer that was closed using another client meanwhile.
    QTimer* timer = new QTimer(connection);
    timer->setSingleShot(true);
    timer->setInterval(2000);
    connect(connection, SIGNAL(connected()), timer, SLOT(start()));
    QObject::connect(timer, &QTimer::timeout, [=]() -> void {
        bool hasActiveChannels = false;
        foreach (const QString& name, model->channels()) {
            IrcBuffer* channel = model->find(name);
            if (channel && channel->isActive()) {
                hasActiveChannels = true;
                break;
            }
        }
        if (!hasActiveChannels)
            model->restoreState(model->property("savedState").toByteArray());
        connect(model, SIGNAL(buffersChanged(QList<IrcBuffer*>)), this, SLOT(onModelBuffersChanged()));
    });

    m_connections.append(connection);
    m_servers.append(buffer);
    m_models.append(model);

    emit connectionAdded(connection);
    emit connectionsChanged();
    emit serversChanged();
    emit modelsChanged();

    insertSourceModel(model);
}

void BufferProxyModel::removeConnection(IrcConnection* connection)
{
    disconnect(connection, SIGNAL(connected()), this, SLOT(onConnected()));
    disconnect(connection, SIGNAL(disconnected()), this, SLOT(onDisconnected()));

    IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
    if (model) {
        disconnect(model, SIGNAL(added(IrcBuffer*)), this, SIGNAL(bufferAdded(IrcBuffer*)));
        disconnect(model, SIGNAL(removed(IrcBuffer*)), this, SIGNAL(bufferRemoved(IrcBuffer*)));
        disconnect(model, SIGNAL(aboutToBeAdded(IrcBuffer*)), this, SIGNAL(bufferAboutToBeAdded(IrcBuffer*)));
        disconnect(model, SIGNAL(aboutToBeRemoved(IrcBuffer*)), this, SIGNAL(bufferAboutToBeRemoved(IrcBuffer*)));

        int index = m_connections.indexOf(connection);
        if (index != -1) {
            if (QObject* server = m_servers.takeAt(index))
                server->disconnect(this);
            if (QObject* model = m_models.takeAt(index))
                model->deleteLater();
            if (QObject* connection = m_connections.takeAt(index))
                connection->deleteLater();

            emit connectionRemoved(connection);
            emit connectionsChanged();
            emit serversChanged();
            emit modelsChanged();

            removeSourceModel(model);

            if (m_models.isEmpty())
                emit reseted();
        }
    }
}

QHash<int, QByteArray> BufferProxyModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[Qt::DisplayRole] = "display";
    roles[Qt::UserRole] = "section";
    roles[Irc::BufferRole] = "buffer";
    roles[Irc::ChannelRole] = "channel";
    roles[Irc::NameRole] = "name";
    roles[Irc::PrefixRole] = "prefix";
    roles[Irc::TitleRole] = "title";
    return roles;
}

QVariant BufferProxyModel::data(const QModelIndex& index, int role) const
{
    if (role == Qt::UserRole) {
        IrcBuffer* buffer = data(index, Irc::BufferRole).value<IrcBuffer*>();
        if (buffer)
            return m_connections.indexOf(buffer->connection()); // TODO: optimize
    }
    return RowsJoinerProxy::data(index, role);
}

QByteArray BufferProxyModel::saveState() const
{
    QVariantList modelStates;
    QVariantList connectionStates;
    SimpleCrypt crypto(Q_UINT64_C(0xff610ed9de767b09));

    foreach (QAbstractItemModel* aim, RowsJoinerProxy::models()) {
        IrcBufferModel* model = qobject_cast<IrcBufferModel*>(aim);
        if (model) {
            IrcConnection* connection = model->connection();
            connectionStates += crypto.encryptToByteArray(connection->saveState());
            // do not save the server buffer - let addConnection() handle it when restoring
            bool wasConnected = connection->isEnabled() && connection->isConnected();
            model->remove(model->get(0));
            if (wasConnected)
                modelStates += crypto.encryptToByteArray(model->saveState());
            else
                modelStates += crypto.encryptToByteArray(model->property("savedState").toByteArray());
        }
    }

    QVariantMap state;
    state.insert("models", modelStates);
    state.insert("connections", connectionStates);

    QByteArray data;
    QDataStream out(&data, QIODevice::WriteOnly);
    out << state;
    return data;
}

bool BufferProxyModel::restoreState(const QByteArray& data)
{
    QVariantMap state;
    QDataStream in(data);
    in >> state;
    if (in.status() != QDataStream::Ok)
        return false;

    QVariantList modelStates = state.value("models").toList();
    QVariantList connectionStates = state.value("connections").toList();
    SimpleCrypt crypto(Q_UINT64_C(0xff610ed9de767b09));

    for (int i = 0; i < connectionStates.length(); ++i) {
        IrcConnection* connection = new IrcConnection(this);
        QByteArray cs = crypto.decryptToByteArray(connectionStates.at(i).toByteArray());
        connection->restoreState(!crypto.lastError() ? cs : connectionStates.at(i).toByteArray());
        addConnection(connection);
        IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
        QByteArray ms = crypto.decryptToByteArray(modelStates.value(i).toByteArray());
        model->setProperty("savedState", !crypto.lastError() ? ms : modelStates.value(i));
    }
    return true;
}

void BufferProxyModel::onConnected()
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit connected(connection);
}

void BufferProxyModel::onDisconnected()
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit disconnected(connection);
}

void BufferProxyModel::onModelBuffersChanged()
{
    IrcBufferModel* model = qobject_cast<IrcBufferModel*>(sender());
    if (model) {
        IrcConnection* connection = model->connection();
        if (connection && connection->isConnected())
            model->setProperty("savedState", model->saveState());
    }
}

void BufferProxyModel::onConnectionEnabledChanged(bool enabled)
{
    // store the model state when a connection is disabled
    // see #25: Don't save/restore buffers for disabled connections
    if (!enabled) {
        IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
        if (connection) {
            IrcBufferModel* model = connection->findChild<IrcBufferModel*>();
            if (model && model->count() > 1)
                model->setProperty("savedState", model->saveState());
        }
    }
}

void BufferProxyModel::closeConnection(IrcBuffer* buffer)
{
    removeConnection(buffer->connection());
}

void BufferProxyModel::onChannelKeyRequired(const QString& channel)
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit channelKeyRequired(connection, channel);
}

void BufferProxyModel::onNickNameRequired(const QString& reserved)
{
    IrcConnection* connection = qobject_cast<IrcConnection*>(sender());
    if (connection)
        emit nickNameRequired(connection, reserved);
}

void BufferProxyModel::processMessage(IrcMessage* message)
{
    IrcBufferModel* model = message->connection()->findChild<IrcBufferModel*>();
    if (model) {
        // deliver targeted ChanServ notices to the target channel
        // ":ChanServ!ChanServ@services. NOTICE myself :[#channel] foo bar..."
        if (message->type() == IrcMessage::Notice) {
            if (message->prefix() == "ChanServ!ChanServ@services.") {
                QString content = static_cast<IrcNoticeMessage*>(message)->content();
                if (content.startsWith("[")) {
                    int i = content.indexOf("]");
                    if (i != -1) {
                        QString title = content.mid(1, i - 1);
                        IrcBuffer* buffer = model->find(title);
                        if (buffer) {
                            message->setProperty("forwarded", true);
                            buffer->receiveMessage(message);
                            return;
                        }
                    }
                }
            }
            if (m_current && m_current->model() == model) {
                m_current->receiveMessage(message);
                return;
            }
        }
        if (IrcBuffer* buffer = model->get(0))
            buffer->receiveMessage(message);
    }
}

#include "bufferproxymodel.moc"
<|endoftext|>


<commit_before>#include <miopen.h>
#include "test.hpp"
#include <array>
#include <iterator>
#include <memory>
#include <utility>
#include <iostream>
#include <miopen/tensor.hpp>
#include <miopen/convolution.hpp>
#include <limits>

// #include "network_data.hpp"
#include "tensor_holder.hpp"
#include "verify.hpp"
#include "driver.hpp"
#include "get_handle.hpp"

template<class T>
tensor<T> get_output_tensor(const miopen::ConvolutionDescriptor& filter, const tensor<T>& input, const tensor<T>& weights)
{
    assert(filter.GetBackwardOutputTensor(filter.GetForwardOutputTensor(input.desc, weights.desc), weights.desc) == input.desc);
    return tensor<T>{filter.GetForwardOutputTensor(input.desc, weights.desc)};
}

template<class T>
struct conv_base
{
    tensor<T> input;
    tensor<T> weights;
    tensor<T> out;
    miopen::ConvolutionDescriptor filter;
    int bias;

    void fail(float=0)
    {
        std::cout << "Input tensor: " << input.desc.ToString() << std::endl;
        std::cout << "Weights tensor: " << weights.desc.ToString() << std::endl;
        std::cout << "Output tensor: " << out.desc.ToString() << std::endl;
        std::cout << "Filter: " << filter << std::endl;
    }
};

template<class T>
struct verify_forward_conv : conv_base<T>
{
    using conv_base<T>::input;
    using conv_base<T>::weights;
    using conv_base<T>::out;
    using conv_base<T>::filter;
    using conv_base<T>::bias;

    verify_forward_conv(const tensor<T>& pinput, const tensor<T>& pweights, const miopen::ConvolutionDescriptor& pfilter, int pbias = 0)
    {
        input = pinput;
        weights = pweights;
        filter = pfilter;
        bias = pbias;
    }

    tensor<T> cpu()
    {
        out = get_output_tensor(filter, input, weights);

        int in_h, in_w;
        std::tie(std::ignore, std::ignore, in_h, in_w) = miopen::tie4(input.desc.GetLengths());

        int wei_c, wei_h, wei_w;
        std::tie(std::ignore, wei_c, wei_h, wei_w) = miopen::tie4(weights.desc.GetLengths());

        out.par_for_each([&](int o, int w, int i, int j)
        {
            const int start_x = i * filter.v - filter.pad_h;
            const int start_y = j * filter.u - filter.pad_w;

            double acc = bias;
            ford(wei_c, wei_h, wei_w)([&](int k, int x, int y)
            {
                const int in_x = start_x + x;
                const int in_y = start_y + y;
                if(in_x >= 0 && in_x < in_h && in_y >= 0 && in_y < in_w) {
                    acc += input(o, k, in_x, in_y) * weights(w, k, x, y);
                }
            });
            out(o, w, i, j) = acc;
        });
        return out;
    }

    tensor<T> gpu()
    {
        auto&& handle = get_handle();
        out = get_output_tensor(filter, input, weights);

        auto in_dev = handle.Write(input.data);
        auto wei_dev = handle.Write(weights.data);
        auto out_dev = handle.Write(out.data);

		size_t workspace_size = filter.ForwardGetWorkSpaceSize(handle, weights.desc, input.desc, out.desc);

		std::vector<char> workspace(workspace_size);
		auto workspace_dev = workspace_size != 0 ? handle.Write(workspace) : nullptr;

        int ret_algo_count;
        miopenConvAlgoPerf_t perf;

        int alpha = 1, beta = 1;

        filter.FindConvFwdAlgorithm(handle,
            input.desc,
            in_dev.get(),
            weights.desc,
            wei_dev.get(),
            out.desc,
            out_dev.get(),
            1,
            &ret_algo_count,
            &perf,
            workspace_dev.get(),
            workspace_size,
            0); // MD: Not performing exhaustiveSearch by default for now

        filter.ConvolutionForward(handle,
            &alpha,
            input.desc,
            in_dev.get(),
            weights.desc,
            wei_dev.get(),
            perf.fwd_algo,
            &beta,
            out.desc,
            out_dev.get(),
            workspace_dev.get(),
            workspace_size);

        out.data = handle.Read<T>(out_dev, out.data.size());
        return out;
    }

    void fail(float=0)
    {
        std::cout << "Forward convolution: " << std::endl;
        this->conv_base<T>::fail();
    }

};

template<class T>
struct verify_backward_conv : conv_base<T>
{
    using conv_base<T>::input;
    using conv_base<T>::weights;
    using conv_base<T>::out;
    using conv_base<T>::filter;
    using conv_base<T>::bias;

    verify_backward_conv(const tensor<T>& pinput, const tensor<T>& pweights, const tensor<T>& pout, const miopen::ConvolutionDescriptor& pfilter, int pbias = 0)
    {
        input = pinput;
        weights = pweights;
        out = pout;
        filter = pfilter;
        bias = pbias;
    }

    tensor<T> cpu()
    {
        std::fill(input.begin(), input.end(), 0);

        int in_h, in_w;
        std::tie(std::ignore, std::ignore, in_h, in_w) = miopen::tie4(input.desc.GetLengths());

        int wei_c, wei_h, wei_w;
        std::tie(std::ignore, wei_c, wei_h, wei_w) = miopen::tie4(weights.desc.GetLengths());

        int out_n, out_c, out_h, out_w;
        std::tie(out_n, out_c, out_h, out_w) = miopen::tie4(out.desc.GetLengths());

        par_ford(out_n, wei_c)([&](int o, int k)
        {
            ford(out_c, out_h, out_w, wei_h, wei_w)([&](int w, int i, int j, int x, int y)
            {
                const int start_x = i * filter.v - filter.pad_h;
                const int start_y = j * filter.u - filter.pad_w;
                const int in_x = start_x + x;
                const int in_y = start_y + y;
                if(in_x >= 0 && in_x < in_h && in_y >= 0 && in_y < in_w) {
                    input(o, k, in_x, in_y) += out(o, w, i, j) * weights(w, k, x, y);
                }
            });
        });
        return input;
    }

    tensor<T> gpu()
    {
        auto&& handle = get_handle();
        std::fill(input.begin(), input.end(), 0);

        auto out_dev = handle.Write(out.data);
        auto wei_dev = handle.Write(weights.data);
        auto in_dev = handle.Write(input.data);

        int ret_algo_count;
        miopenConvAlgoPerf_t perf;

        int alpha = 1, beta = 1;

        filter.FindConvBwdDataAlgorithm(handle,
            out.desc,
            out_dev.get(),
            weights.desc,
            wei_dev.get(),
            input.desc,
            in_dev.get(),
            1,
            &ret_algo_count,
            &perf,
            nullptr,
            10,
            0); // MD: Not performing exhaustiveSearch by default for now

        filter.ConvolutionBackwardData(handle,
            &alpha,
            out.desc,
            out_dev.get(),
            weights.desc,
            wei_dev.get(),
            perf.bwd_data_algo,
            &beta,
            input.desc,
            in_dev.get(),
            nullptr,
            0);

        input.data = handle.Read<T>(in_dev, input.data.size());
        return input;
    }

    void fail(float)
    {
        std::cout << "Backward convolution: " << std::endl;
        this->conv_base<T>::fail();
    }

};

template<class T>
struct verify_backward_weights_conv : conv_base<T>
{
    using conv_base<T>::input;
    using conv_base<T>::weights;
    using conv_base<T>::out;
    using conv_base<T>::filter;
    using conv_base<T>::bias;

    verify_backward_weights_conv(const tensor<T>& pinput, const tensor<T>& pweights, const tensor<T>& pout, const miopen::ConvolutionDescriptor& pfilter, int pbias = 0)
    {
        input = pinput;
        weights = pweights;
        out = pout;
        filter = pfilter;
        bias = pbias;
    }

    tensor<T> cpu()
    {
        std::fill(weights.begin(), weights.end(), 0);

        int in_h, in_w;
        std::tie(std::ignore, std::ignore, in_h, in_w) = miopen::tie4(input.desc.GetLengths());

        int wei_c, wei_h, wei_w;
        std::tie(std::ignore, wei_c, wei_h, wei_w) = miopen::tie4(weights.desc.GetLengths());

        int out_n, out_c, out_h, out_w;
        std::tie(out_n, out_c, out_h, out_w) = miopen::tie4(out.desc.GetLengths());

        par_ford(out_c, wei_c, wei_h, wei_w)([&](int w, int k, int x, int y)
        {
            double acc = 0.0;
            ford(out_n, out_h, out_w)([&](int o, int i, int j)
            {
                const int start_x = i * filter.v - filter.pad_h;
                const int start_y = j * filter.u - filter.pad_w;
                const int in_x = start_x + x;
                const int in_y = start_y + y;
                if(in_x >= 0 && in_x < in_h && in_y >= 0 && in_y < in_w) {
                    acc += input(o, k, in_x, in_y) * out(o, w, i, j);
                }
            });
            weights(w, k, x, y) = acc;
        });
        return weights;
    }

    tensor<T> gpu()
    {
        auto&& handle = get_handle();
        std::fill(weights.begin(), weights.end(), 0);

        auto out_dev = handle.Write(out.data);
        auto wei_dev = handle.Write(weights.data);
        auto in_dev = handle.Write(input.data);

        std::size_t workspace_size = filter.ConvolutionBackwardWeightsGetWorkSpaceSize(handle, out.desc, input.desc, weights.desc);

        std::vector<char> workspace(workspace_size);
        auto workspace_dev = handle.Write(workspace);

        int ret_algo_count;
        miopenConvAlgoPerf_t perf;

        int alpha = 1, beta = 1;
        filter.FindConvBwdWeightsAlgorithm(handle,
            out.desc,
            out_dev.get(),
            input.desc,
            in_dev.get(),
            weights.desc,
            wei_dev.get(),
            1,
            &ret_algo_count,
            &perf,
            workspace_dev.get(),
            workspace_size,
            0); // MD: Not performing exhaustiveSearch by default for now

        filter.ConvolutionBackwardWeights(handle,
            &alpha,
            out.desc,
            out_dev.get(),
            input.desc,
            in_dev.get(),
            perf.bwd_weights_algo,
            &beta,
            weights.desc,
            wei_dev.get(),
            workspace_dev.get(),
            workspace_size);

        weights.data = handle.Read<T>(wei_dev, weights.data.size());
        return weights;
    }

    void fail(float)
    {
        std::cout << "Backward weights convolution: " << std::endl;
        this->conv_base<T>::fail();
    }

};

template<class T>
struct conv_driver : test_driver
{
    tensor<T> input;
    tensor<T> weights;
    miopen::ConvolutionDescriptor filter;
    bool enable_backward_weights = false;
    bool do_backward_data = true;

    conv_driver()
    {
        add(input, "input", get_input_tensor());
        add(weights, "weights", get_weights_tensor());
        add(filter, "filter", generate_data(get_filters()));
        add(enable_backward_weights, "enable-backward-weights", flag());
        add(do_backward_data, "disable-backward-data", set_value(false));
    }

    std::vector<miopen::ConvolutionDescriptor> get_filters()
    {
        return {
            miopen::ConvolutionDescriptor{ 0, 0, 1, 1 },
            // miopen::ConvolutionDescriptor{ 0, 0, 2, 2 },
            miopen::ConvolutionDescriptor{ 1, 1, 1, 1 },
            miopen::ConvolutionDescriptor{ 1, 1, 2, 2 },
            miopen::ConvolutionDescriptor{ 2, 2, 1, 1 },
            miopen::ConvolutionDescriptor{ 3, 3, 2, 2 }
        };
    }

    void run()
    {
        int wei_h, wei_w;
        std::tie(std::ignore, std::ignore, wei_h, wei_w) = miopen::tie4(weights.desc.GetLengths());
        if (input.desc.GetLengths().at(1) == weights.desc.GetLengths().at(1) &&
            wei_h > 2*filter.pad_h &&
            wei_w > 2*filter.pad_w
        )
        {
            auto out_p = verify(verify_forward_conv<T>{input, weights, filter});
            for(auto& x:out_p.first) x = (long(x+1)*2) % 17; // Clamp big numbers
            if (do_backward_data) verify(verify_backward_conv<T>{input, weights, out_p.first, filter});
            if(enable_backward_weights or MIOPEN_USE_TINYGEMM)
            {
                verify(verify_backward_weights_conv<T>{input, weights, out_p.first, filter});
            }
        }
    }
};

int main(int argc, const char *argv[])
{
    test_drive<conv_driver<float>>(argc, argv);
}
<commit_msg>added zero-size check<commit_after>#include <miopen.h>
#include "test.hpp"
#include <array>
#include <iterator>
#include <memory>
#include <utility>
#include <iostream>
#include <miopen/tensor.hpp>
#include <miopen/convolution.hpp>
#include <limits>

// #include "network_data.hpp"
#include "tensor_holder.hpp"
#include "verify.hpp"
#include "driver.hpp"
#include "get_handle.hpp"

template<class T>
tensor<T> get_output_tensor(const miopen::ConvolutionDescriptor& filter, const tensor<T>& input, const tensor<T>& weights)
{
    assert(filter.GetBackwardOutputTensor(filter.GetForwardOutputTensor(input.desc, weights.desc), weights.desc) == input.desc);
    return tensor<T>{filter.GetForwardOutputTensor(input.desc, weights.desc)};
}

template<class T>
struct conv_base
{
    tensor<T> input;
    tensor<T> weights;
    tensor<T> out;
    miopen::ConvolutionDescriptor filter;
    int bias;

    void fail(float=0)
    {
        std::cout << "Input tensor: " << input.desc.ToString() << std::endl;
        std::cout << "Weights tensor: " << weights.desc.ToString() << std::endl;
        std::cout << "Output tensor: " << out.desc.ToString() << std::endl;
        std::cout << "Filter: " << filter << std::endl;
    }
};

template<class T>
struct verify_forward_conv : conv_base<T>
{
    using conv_base<T>::input;
    using conv_base<T>::weights;
    using conv_base<T>::out;
    using conv_base<T>::filter;
    using conv_base<T>::bias;

    verify_forward_conv(const tensor<T>& pinput, const tensor<T>& pweights, const miopen::ConvolutionDescriptor& pfilter, int pbias = 0)
    {
        input = pinput;
        weights = pweights;
        filter = pfilter;
        bias = pbias;
    }

    tensor<T> cpu()
    {
        out = get_output_tensor(filter, input, weights);

        int in_h, in_w;
        std::tie(std::ignore, std::ignore, in_h, in_w) = miopen::tie4(input.desc.GetLengths());

        int wei_c, wei_h, wei_w;
        std::tie(std::ignore, wei_c, wei_h, wei_w) = miopen::tie4(weights.desc.GetLengths());

        out.par_for_each([&](int o, int w, int i, int j)
        {
            const int start_x = i * filter.v - filter.pad_h;
            const int start_y = j * filter.u - filter.pad_w;

            double acc = bias;
            ford(wei_c, wei_h, wei_w)([&](int k, int x, int y)
            {
                const int in_x = start_x + x;
                const int in_y = start_y + y;
                if(in_x >= 0 && in_x < in_h && in_y >= 0 && in_y < in_w) {
                    acc += input(o, k, in_x, in_y) * weights(w, k, x, y);
                }
            });
            out(o, w, i, j) = acc;
        });
        return out;
    }

    tensor<T> gpu()
    {
        auto&& handle = get_handle();
        out = get_output_tensor(filter, input, weights);

        auto in_dev = handle.Write(input.data);
        auto wei_dev = handle.Write(weights.data);
        auto out_dev = handle.Write(out.data);

		size_t workspace_size = filter.ForwardGetWorkSpaceSize(handle, weights.desc, input.desc, out.desc);

		std::vector<char> workspace(workspace_size);
		auto workspace_dev = workspace_size != 0 ? handle.Write(workspace) : nullptr;

        int ret_algo_count;
        miopenConvAlgoPerf_t perf;

        int alpha = 1, beta = 1;

        filter.FindConvFwdAlgorithm(handle,
            input.desc,
            in_dev.get(),
            weights.desc,
            wei_dev.get(),
            out.desc,
            out_dev.get(),
            1,
            &ret_algo_count,
            &perf,
            workspace_dev.get(),
            workspace_size,
            0); // MD: Not performing exhaustiveSearch by default for now

        filter.ConvolutionForward(handle,
            &alpha,
            input.desc,
            in_dev.get(),
            weights.desc,
            wei_dev.get(),
            perf.fwd_algo,
            &beta,
            out.desc,
            out_dev.get(),
            workspace_dev.get(),
            workspace_size);

        out.data = handle.Read<T>(out_dev, out.data.size());
        return out;
    }

    void fail(float=0)
    {
        std::cout << "Forward convolution: " << std::endl;
        this->conv_base<T>::fail();
    }

};

template<class T>
struct verify_backward_conv : conv_base<T>
{
    using conv_base<T>::input;
    using conv_base<T>::weights;
    using conv_base<T>::out;
    using conv_base<T>::filter;
    using conv_base<T>::bias;

    verify_backward_conv(const tensor<T>& pinput, const tensor<T>& pweights, const tensor<T>& pout, const miopen::ConvolutionDescriptor& pfilter, int pbias = 0)
    {
        input = pinput;
        weights = pweights;
        out = pout;
        filter = pfilter;
        bias = pbias;
    }

    tensor<T> cpu()
    {
        std::fill(input.begin(), input.end(), 0);

        int in_h, in_w;
        std::tie(std::ignore, std::ignore, in_h, in_w) = miopen::tie4(input.desc.GetLengths());

        int wei_c, wei_h, wei_w;
        std::tie(std::ignore, wei_c, wei_h, wei_w) = miopen::tie4(weights.desc.GetLengths());

        int out_n, out_c, out_h, out_w;
        std::tie(out_n, out_c, out_h, out_w) = miopen::tie4(out.desc.GetLengths());

        par_ford(out_n, wei_c)([&](int o, int k)
        {
            ford(out_c, out_h, out_w, wei_h, wei_w)([&](int w, int i, int j, int x, int y)
            {
                const int start_x = i * filter.v - filter.pad_h;
                const int start_y = j * filter.u - filter.pad_w;
                const int in_x = start_x + x;
                const int in_y = start_y + y;
                if(in_x >= 0 && in_x < in_h && in_y >= 0 && in_y < in_w) {
                    input(o, k, in_x, in_y) += out(o, w, i, j) * weights(w, k, x, y);
                }
            });
        });
        return input;
    }

    tensor<T> gpu()
    {
        auto&& handle = get_handle();
        std::fill(input.begin(), input.end(), 0);

        auto out_dev = handle.Write(out.data);
        auto wei_dev = handle.Write(weights.data);
        auto in_dev = handle.Write(input.data);

        int ret_algo_count;
        miopenConvAlgoPerf_t perf;

        int alpha = 1, beta = 1;

        filter.FindConvBwdDataAlgorithm(handle,
            out.desc,
            out_dev.get(),
            weights.desc,
            wei_dev.get(),
            input.desc,
            in_dev.get(),
            1,
            &ret_algo_count,
            &perf,
            nullptr,
            10,
            0); // MD: Not performing exhaustiveSearch by default for now

        filter.ConvolutionBackwardData(handle,
            &alpha,
            out.desc,
            out_dev.get(),
            weights.desc,
            wei_dev.get(),
            perf.bwd_data_algo,
            &beta,
            input.desc,
            in_dev.get(),
            nullptr,
            0);

        input.data = handle.Read<T>(in_dev, input.data.size());
        return input;
    }

    void fail(float)
    {
        std::cout << "Backward convolution: " << std::endl;
        this->conv_base<T>::fail();
    }

};

template<class T>
struct verify_backward_weights_conv : conv_base<T>
{
    using conv_base<T>::input;
    using conv_base<T>::weights;
    using conv_base<T>::out;
    using conv_base<T>::filter;
    using conv_base<T>::bias;

    verify_backward_weights_conv(const tensor<T>& pinput, const tensor<T>& pweights, const tensor<T>& pout, const miopen::ConvolutionDescriptor& pfilter, int pbias = 0)
    {
        input = pinput;
        weights = pweights;
        out = pout;
        filter = pfilter;
        bias = pbias;
    }

    tensor<T> cpu()
    {
        std::fill(weights.begin(), weights.end(), 0);

        int in_h, in_w;
        std::tie(std::ignore, std::ignore, in_h, in_w) = miopen::tie4(input.desc.GetLengths());

        int wei_c, wei_h, wei_w;
        std::tie(std::ignore, wei_c, wei_h, wei_w) = miopen::tie4(weights.desc.GetLengths());

        int out_n, out_c, out_h, out_w;
        std::tie(out_n, out_c, out_h, out_w) = miopen::tie4(out.desc.GetLengths());

        par_ford(out_c, wei_c, wei_h, wei_w)([&](int w, int k, int x, int y)
        {
            double acc = 0.0;
            ford(out_n, out_h, out_w)([&](int o, int i, int j)
            {
                const int start_x = i * filter.v - filter.pad_h;
                const int start_y = j * filter.u - filter.pad_w;
                const int in_x = start_x + x;
                const int in_y = start_y + y;
                if(in_x >= 0 && in_x < in_h && in_y >= 0 && in_y < in_w) {
                    acc += input(o, k, in_x, in_y) * out(o, w, i, j);
                }
            });
            weights(w, k, x, y) = acc;
        });
        return weights;
    }

    tensor<T> gpu()
    {
        auto&& handle = get_handle();
        std::fill(weights.begin(), weights.end(), 0);

        auto out_dev = handle.Write(out.data);
        auto wei_dev = handle.Write(weights.data);
        auto in_dev = handle.Write(input.data);

        std::size_t workspace_size = filter.ConvolutionBackwardWeightsGetWorkSpaceSize(handle, out.desc, input.desc, weights.desc);

        std::vector<char> workspace(workspace_size);
		auto workspace_dev = workspace_size != 0 ? handle.Write(workspace) : nullptr;

        int ret_algo_count;
        miopenConvAlgoPerf_t perf;

        int alpha = 1, beta = 1;
        filter.FindConvBwdWeightsAlgorithm(handle,
            out.desc,
            out_dev.get(),
            input.desc,
            in_dev.get(),
            weights.desc,
            wei_dev.get(),
            1,
            &ret_algo_count,
            &perf,
            workspace_dev.get(),
            workspace_size,
            0); // MD: Not performing exhaustiveSearch by default for now

        filter.ConvolutionBackwardWeights(handle,
            &alpha,
            out.desc,
            out_dev.get(),
            input.desc,
            in_dev.get(),
            perf.bwd_weights_algo,
            &beta,
            weights.desc,
            wei_dev.get(),
            workspace_dev.get(),
            workspace_size);

        weights.data = handle.Read<T>(wei_dev, weights.data.size());
        return weights;
    }

    void fail(float)
    {
        std::cout << "Backward weights convolution: " << std::endl;
        this->conv_base<T>::fail();
    }

};

template<class T>
struct conv_driver : test_driver
{
    tensor<T> input;
    tensor<T> weights;
    miopen::ConvolutionDescriptor filter;
    bool enable_backward_weights = false;
    bool do_backward_data = true;

    conv_driver()
    {
        add(input, "input", get_input_tensor());
        add(weights, "weights", get_weights_tensor());
        add(filter, "filter", generate_data(get_filters()));
        add(enable_backward_weights, "enable-backward-weights", flag());
        add(do_backward_data, "disable-backward-data", set_value(false));
    }

    std::vector<miopen::ConvolutionDescriptor> get_filters()
    {
        return {
            miopen::ConvolutionDescriptor{ 0, 0, 1, 1 },
            // miopen::ConvolutionDescriptor{ 0, 0, 2, 2 },
            miopen::ConvolutionDescriptor{ 1, 1, 1, 1 },
            miopen::ConvolutionDescriptor{ 1, 1, 2, 2 },
            miopen::ConvolutionDescriptor{ 2, 2, 1, 1 },
            miopen::ConvolutionDescriptor{ 3, 3, 2, 2 }
        };
    }

    void run()
    {
        int wei_h, wei_w;
        std::tie(std::ignore, std::ignore, wei_h, wei_w) = miopen::tie4(weights.desc.GetLengths());
        if (input.desc.GetLengths().at(1) == weights.desc.GetLengths().at(1) &&
            wei_h > 2*filter.pad_h &&
            wei_w > 2*filter.pad_w
        )
        {
            auto out_p = verify(verify_forward_conv<T>{input, weights, filter});
            for(auto& x:out_p.first) x = (long(x+1)*2) % 17; // Clamp big numbers
            if (do_backward_data) verify(verify_backward_conv<T>{input, weights, out_p.first, filter});
            if(enable_backward_weights or MIOPEN_USE_TINYGEMM)
            {
                verify(verify_backward_weights_conv<T>{input, weights, out_p.first, filter});
            }
        }
    }
};

int main(int argc, const char *argv[])
{
    test_drive<conv_driver<float>>(argc, argv);
}
<|endoftext|>


<commit_before>#include "urlviewformaction.h"

#include <sstream>

#include "config.h"
#include "formatstring.h"
#include "listformatter.h"
#include "strprintf.h"
#include "utils.h"
#include "view.h"

namespace newsboat {

/*
 * The UrlViewFormAction is probably the simplest dialog of all. It
 * displays a list of URLs, and makes it possible to open the URLs
 * in a browser or to bookmark them.
 */

UrlViewFormAction::UrlViewFormAction(View* vv,
	std::shared_ptr<RssFeed>& feed,
	std::string formstr,
	ConfigContainer* cfg)
	: FormAction(vv, formstr, cfg)
	, quit(false)
	, feed(feed)
{
}

UrlViewFormAction::~UrlViewFormAction() {}

void UrlViewFormAction::process_operation(Operation op,
	bool /* automatic */,
	std::vector<std::string>* /* args */)
{
	bool hardquit = false;
	switch (op) {
	case OP_OPENINBROWSER:
	case OP_OPEN: {
		std::string posstr = f->get("feedpos");
		if (posstr.length() > 0) {
			unsigned int idx = utils::to_u(posstr, 0);
			v->set_status(_("Starting browser..."));
			v->open_in_browser(links[idx].first);
			v->set_status("");
		} else {
			v->show_error(_("No link selected!"));
		}
	} break;
	case OP_BOOKMARK: {
		std::string posstr = f->get("feedpos");
		if (posstr.length() > 0) {
			unsigned int idx = utils::to_u(posstr, 0);

			this->start_bookmark_qna(
				"", links[idx].first, "", feed->title());

		} else {
			v->show_error(_("No link selected!"));
		}
	} break;
	case OP_1:
	case OP_2:
	case OP_3:
	case OP_4:
	case OP_5:
	case OP_6:
	case OP_7:
	case OP_8:
	case OP_9:
	case OP_0: {
		unsigned int idx = op - OP_1;

		if (idx < links.size()) {
			v->set_status(_("Starting browser..."));
			v->open_in_browser(links[idx].first);
			v->set_status("");
		}
	} break;
	case OP_QUIT:
		quit = true;
		break;
	case OP_HARDQUIT:
		hardquit = true;
		break;
	default: // nothing
		break;
	}
	if (hardquit) {
		while (v->formaction_stack_size() > 0) {
			v->pop_current_formaction();
		}
	} else if (quit) {
		v->pop_current_formaction();
	}
}

void UrlViewFormAction::prepare()
{
	if (do_redraw) {
		ListFormatter listfmt;
		unsigned int i = 0;
		for (const auto& link : links) {
			listfmt.add_line(
				strprintf::fmt("%2u  %s", i + 1, link.first),
				i);
			i++;
		}
		f->modify("urls", "replace_inner", listfmt.format_list());
	}
}

void UrlViewFormAction::init()
{
	v->set_status("");

	std::string viewwidth = f->get("urls:w");
	unsigned int width = utils::to_u(viewwidth, 80);

	FmtStrFormatter fmt;
	fmt.register_fmt('N', PROGRAM_NAME);
	fmt.register_fmt('V', PROGRAM_VERSION);

	f->set("head",
		fmt.do_format(
			cfg->get_configvalue("urlview-title-format"), width));
	do_redraw = true;
	quit = false;
	set_keymap_hints();
}

KeyMapHintEntry* UrlViewFormAction::get_keymap_hint()
{
	static KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")},
		{OP_OPEN, _("Open in Browser")},
		{OP_BOOKMARK, _("Save Bookmark")},
		{OP_NIL, nullptr}};
	return hints;
}

void UrlViewFormAction::handle_cmdline(const std::string& cmd)
{
	unsigned int idx = 0;
	if (1 == sscanf(cmd.c_str(), "%u", &idx)) {
		if (idx < 1 || idx > links.size()) {
			v->show_error(_("Invalid position!"));
		} else {
			f->set("feedpos", std::to_string(idx - 1));
		}
	} else {
		FormAction::handle_cmdline(cmd);
	}
}

std::string UrlViewFormAction::title()
{
	return _("URLs");
}

} // namespace newsboat
<commit_msg>Initialize `cfg` member in UrlViewFormAction<commit_after>#include "urlviewformaction.h"

#include <sstream>

#include "config.h"
#include "formatstring.h"
#include "listformatter.h"
#include "strprintf.h"
#include "utils.h"
#include "view.h"

namespace newsboat {

/*
 * The UrlViewFormAction is probably the simplest dialog of all. It
 * displays a list of URLs, and makes it possible to open the URLs
 * in a browser or to bookmark them.
 */

UrlViewFormAction::UrlViewFormAction(View* vv,
	std::shared_ptr<RssFeed>& feed,
	std::string formstr,
	ConfigContainer* cfg)
	: FormAction(vv, formstr, cfg)
	, quit(false)
	, feed(feed)
	, cfg(cfg)
{
}

UrlViewFormAction::~UrlViewFormAction() {}

void UrlViewFormAction::process_operation(Operation op,
	bool /* automatic */,
	std::vector<std::string>* /* args */)
{
	bool hardquit = false;
	switch (op) {
	case OP_OPENINBROWSER:
	case OP_OPEN: {
		std::string posstr = f->get("feedpos");
		if (posstr.length() > 0) {
			unsigned int idx = utils::to_u(posstr, 0);
			v->set_status(_("Starting browser..."));
			v->open_in_browser(links[idx].first);
			v->set_status("");
		} else {
			v->show_error(_("No link selected!"));
		}
	} break;
	case OP_BOOKMARK: {
		std::string posstr = f->get("feedpos");
		if (posstr.length() > 0) {
			unsigned int idx = utils::to_u(posstr, 0);

			this->start_bookmark_qna(
				"", links[idx].first, "", feed->title());

		} else {
			v->show_error(_("No link selected!"));
		}
	} break;
	case OP_1:
	case OP_2:
	case OP_3:
	case OP_4:
	case OP_5:
	case OP_6:
	case OP_7:
	case OP_8:
	case OP_9:
	case OP_0: {
		unsigned int idx = op - OP_1;

		if (idx < links.size()) {
			v->set_status(_("Starting browser..."));
			v->open_in_browser(links[idx].first);
			v->set_status("");
		}
	} break;
	case OP_QUIT:
		quit = true;
		break;
	case OP_HARDQUIT:
		hardquit = true;
		break;
	default: // nothing
		break;
	}
	if (hardquit) {
		while (v->formaction_stack_size() > 0) {
			v->pop_current_formaction();
		}
	} else if (quit) {
		v->pop_current_formaction();
	}
}

void UrlViewFormAction::prepare()
{
	if (do_redraw) {
		ListFormatter listfmt;
		unsigned int i = 0;
		for (const auto& link : links) {
			listfmt.add_line(
				strprintf::fmt("%2u  %s", i + 1, link.first),
				i);
			i++;
		}
		f->modify("urls", "replace_inner", listfmt.format_list());
	}
}

void UrlViewFormAction::init()
{
	v->set_status("");

	std::string viewwidth = f->get("urls:w");
	unsigned int width = utils::to_u(viewwidth, 80);

	FmtStrFormatter fmt;
	fmt.register_fmt('N', PROGRAM_NAME);
	fmt.register_fmt('V', PROGRAM_VERSION);

	f->set("head",
		fmt.do_format(
			cfg->get_configvalue("urlview-title-format"), width));
	do_redraw = true;
	quit = false;
	set_keymap_hints();
}

KeyMapHintEntry* UrlViewFormAction::get_keymap_hint()
{
	static KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")},
		{OP_OPEN, _("Open in Browser")},
		{OP_BOOKMARK, _("Save Bookmark")},
		{OP_NIL, nullptr}};
	return hints;
}

void UrlViewFormAction::handle_cmdline(const std::string& cmd)
{
	unsigned int idx = 0;
	if (1 == sscanf(cmd.c_str(), "%u", &idx)) {
		if (idx < 1 || idx > links.size()) {
			v->show_error(_("Invalid position!"));
		} else {
			f->set("feedpos", std::to_string(idx - 1));
		}
	} else {
		FormAction::handle_cmdline(cmd);
	}
}

std::string UrlViewFormAction::title()
{
	return _("URLs");
}

} // namespace newsboat
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "itkCastImageFilter.h"

#include "otbVectorRescaleIntensityImageFilter.h"
#include "itkCastImageFilter.h"
#include "otbUnaryImageFunctorWithVectorImageFilter.h"
#include "otbStreamingShrinkImageFilter.h"
#include "itkListSample.h"
#include "otbListSampleToHistogramListGenerator.h"
#include "itkImageRegionConstIterator.h"

namespace otb
{
namespace Wrapper
{

namespace Functor
{
template< class TScalar >
class ITK_EXPORT LogFunctor
{
public:
  LogFunctor(){};
  ~LogFunctor(){};
  TScalar operator() (const TScalar& v) const
  {
    return vcl_log(v);
  }
};
} // end namespace Functor

class Convert : public Application
{
public:
  /** Standard class typedefs. */
  typedef Convert                       Self;
  typedef Application                   Superclass;
  typedef itk::SmartPointer<Self>       Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;

  /** Standard macro */
  itkNewMacro(Self);

  itkTypeMacro(Convert, otb::Application);

  /** Filters typedef */
  typedef itk::Statistics::ListSample<FloatVectorImageType::PixelType> ListSampleType;
  typedef itk::Statistics::DenseFrequencyContainer2 DFContainerType;
  typedef ListSampleToHistogramListGenerator<ListSampleType, FloatVectorImageType::InternalPixelType, DFContainerType> HistogramsGeneratorType;
  typedef StreamingShrinkImageFilter<FloatVectorImageType, FloatVectorImageType> ShrinkFilterType;
  typedef Functor::LogFunctor<FloatVectorImageType::InternalPixelType> TransferLogFunctor;
  typedef UnaryImageFunctorWithVectorImageFilter<FloatVectorImageType, FloatVectorImageType, TransferLogFunctor> TransferLogType;

private:
  void DoInit() ITK_OVERRIDE
  {
    SetName("Convert");
    SetDescription("Convert an image to a different format, eventually rescaling the data"
                   " and/or changing the pixel type.");
    // Documentation
    SetDocName("Image Conversion");
    SetDocLongDescription("This application performs an image pixel type conversion (short, ushort, uchar, int, uint, float and double types are handled). The output image is written in the specified format (ie. that corresponds to the given extension).\n The conversion can include a rescale using the image 2 percent minimum and maximum values. The rescale can be linear or log2.");
    SetDocLimitations("None");
    SetDocAuthors("OTB-Team");
    SetDocSeeAlso("Rescale");

	AddDocTag(Tags::Manip);
    AddDocTag("Conversion");
    AddDocTag("Image Dynamic");

    AddParameter(ParameterType_InputImage,  "in",   "Input image");
    SetParameterDescription("in", "Input image");

    AddParameter(ParameterType_Choice, "type", "Rescale type");
    SetParameterDescription("type", "Transfer function for the rescaling");
    AddChoice("type.none", "None");
    AddChoice("type.linear", "Linear");
    AddChoice("type.log2", "Log2");
    SetParameterString("type", "none", false);

    AddParameter(ParameterType_Float,"type.linear.gamma","Gamma correction factor");
    SetParameterDescription("type.linear.gamma","Gamma correction factor");
    SetDefaultParameterFloat("type.linear.gamma",1.0);
    MandatoryOff("type.linear.gamma");

    AddParameter(ParameterType_InputImage,  "mask",   "Input mask");
    SetParameterDescription("mask", "The masked pixels won't be used to adapt the dynamic (the mask must have the same dimensions as the input image)");
    MandatoryOff("mask");
    DisableParameter("mask");

    AddParameter(ParameterType_Group,"hcp","Histogram Cutting Parameters");
    SetParameterDescription("hcp","Parameters to cut the histogram edges before rescaling");

    AddParameter(ParameterType_Float, "hcp.high", "High Cut Quantile");
    SetParameterDescription("hcp.high", "Quantiles to cut from histogram high values before computing min/max rescaling (in percent, 2 by default)");
    MandatoryOff("hcp.high");
    SetDefaultParameterFloat("hcp.high", 2.0);
    DisableParameter("hcp.high");

    AddParameter(ParameterType_Float, "hcp.low", "Low Cut Quantile");
    SetParameterDescription("hcp.low", "Quantiles to cut from histogram low values before computing min/max rescaling (in percent, 2 by default)");
    MandatoryOff("hcp.low");
    SetDefaultParameterFloat("hcp.low", 2.0);
    DisableParameter("hcp.low");

    AddParameter(ParameterType_OutputImage, "out",  "Output Image");
    SetParameterDescription("out", "Output image");

    AddRAMParameter();

    // Doc example parameter settings
    SetDocExampleParameterValue("in", "QB_Toulouse_Ortho_XS.tif");
    SetDocExampleParameterValue("out", "otbConvertWithScalingOutput.png uint8");
    SetDocExampleParameterValue("type", "linear");

    SetOfficialDocLink();
  }

  void DoUpdateParameters() ITK_OVERRIDE
  {
    // Nothing to do here for the parameters : all are independent
  }

  template<class TImageType>
  void GenericDoExecute()
  {
    typename TImageType::Pointer castIm;

    std::string rescaleType = this->GetParameterString("type");

    if( (rescaleType != "none") && (rescaleType != "linear") && (rescaleType != "log2") )
      {
      itkExceptionMacro("Unknown rescale type "<<rescaleType<<".");
      }

    if( rescaleType == "none" )
      {
      castIm = this->GetParameterImage<TImageType>("in");
      }
    else
      {
      FloatVectorImageType::Pointer input = this->GetParameterImage("in");

      FloatVectorImageType::Pointer mask;
      bool useMask = false;
      if (IsParameterEnabled("mask"))
        {
        mask = this->GetParameterImage("mask");
        useMask = true;
        }

      const unsigned int nbComp(input->GetNumberOfComponentsPerPixel());

      typedef otb::VectorRescaleIntensityImageFilter<FloatVectorImageType, TImageType> RescalerType;
      typename TImageType::PixelType minimum;
      typename TImageType::PixelType maximum;
      minimum.SetSize(nbComp);
      maximum.SetSize(nbComp);
      minimum.Fill( itk::NumericTraits<typename TImageType::InternalPixelType>::min() );
      maximum.Fill( itk::NumericTraits<typename TImageType::InternalPixelType>::max() );

      typename RescalerType::Pointer rescaler = RescalerType::New();

      rescaler->SetOutputMinimum(minimum);
      rescaler->SetOutputMaximum(maximum);

      // We need to subsample the input image in order to estimate its
      // histogram

      typename ShrinkFilterType::Pointer shrinkFilter = ShrinkFilterType::New();

      // Shrink factor is computed so as to load a quicklook of 1000
      // pixels square at most
      typename FloatVectorImageType::SizeType imageSize = input->GetLargestPossibleRegion().GetSize();
      unsigned int shrinkFactor = std::max(imageSize[0], imageSize[1]) < 1000 ? 1 : std::max(imageSize[0], imageSize[1])/1000;

      otbAppLogDEBUG( << "Shrink factor used to compute Min/Max: "<<shrinkFactor );

      otbAppLogDEBUG( << "Shrink starts..." );

      shrinkFilter->SetShrinkFactor(shrinkFactor);
      shrinkFilter->GetStreamer()->SetAutomaticAdaptativeStreaming(GetParameterInt("ram"));
      AddProcess(shrinkFilter->GetStreamer(), "Computing shrink Image for min/max estimation...");

      if ( rescaleType == "log2")
        {
        //define the transfer log
        m_TransferLog = TransferLogType::New();
        m_TransferLog->SetInput(input);
        m_TransferLog->UpdateOutputInformation();

        shrinkFilter->SetInput(m_TransferLog->GetOutput());
        rescaler->SetInput(m_TransferLog->GetOutput());
        shrinkFilter->Update();
        }
      else
        {
        shrinkFilter->SetInput(input);
        rescaler->SetInput(input);
        shrinkFilter->Update();
        }

      ShrinkFilterType::Pointer maskShrinkFilter = ShrinkFilterType::New();
      if (useMask)
        {
        maskShrinkFilter->SetShrinkFactor(shrinkFactor);
        maskShrinkFilter->SetInput(mask);
        maskShrinkFilter->GetStreamer()->SetAutomaticAdaptativeStreaming(GetParameterInt("ram"));
        maskShrinkFilter->Update();
        }

      otbAppLogDEBUG( << "Shrink done" );

      otbAppLogDEBUG( << "Evaluating input Min/Max..." );
      itk::ImageRegionConstIterator<FloatVectorImageType> it(shrinkFilter->GetOutput(), shrinkFilter->GetOutput()->GetLargestPossibleRegion());
      itk::ImageRegionConstIterator<FloatVectorImageType> itMask;
      if (useMask)
        {
        itMask = itk::ImageRegionConstIterator<FloatVectorImageType>(maskShrinkFilter->GetOutput(),maskShrinkFilter->GetOutput()->GetLargestPossibleRegion());
        }

      typename ListSampleType::Pointer listSample = ListSampleType::New();
      listSample->SetMeasurementVectorSize(input->GetNumberOfComponentsPerPixel());

      // Now we generate the list of samples
      if (useMask)
        {
        // Remove masked pixels
        it.GoToBegin();
        itMask.GoToBegin();
        while (!it.IsAtEnd())
          {
          // float values, so the threshold is set to 0.5
          if (itMask.Get()[0] < 0.5)
            {
            listSample->PushBack(it.Get());
            }
          ++it;
          ++itMask;
          }
        }
      else
        {
        for(it.GoToBegin(); !it.IsAtEnd(); ++it)
          {
          listSample->PushBack(it.Get());
          }
        }

      // if all pixels were masked, we assume a wrong mask and then include all image
      if (listSample->Size() == 0)
        {
        otbAppLogINFO( << "All pixels were masked, the application assume a wrong mask and include all the image");
        for(it.GoToBegin(); !it.IsAtEnd(); ++it)
          {
          listSample->PushBack(it.Get());
          }
        }

      // And then the histogram
      typename HistogramsGeneratorType::Pointer histogramsGenerator = HistogramsGeneratorType::New();
      histogramsGenerator->SetListSample(listSample);
      histogramsGenerator->SetNumberOfBins(255);
      histogramsGenerator->NoDataFlagOn();
      histogramsGenerator->Update();

      // And extract the lower and upper quantile
      typename FloatVectorImageType::PixelType inputMin(nbComp), inputMax(nbComp);

      for(unsigned int i = 0; i < nbComp; ++i)
        {
        inputMin[i] = histogramsGenerator->GetOutput()->GetNthElement(i)->Quantile(0, 0.01 * GetParameterFloat("hcp.low"));
        inputMax[i] = histogramsGenerator->GetOutput()->GetNthElement(i)->Quantile(0, 1.0 - 0.01 * GetParameterFloat("hcp.high"));
        }

      otbAppLogDEBUG( << std::setprecision(5) << "Min/Max computation done : min=" << inputMin
                      << " max=" << inputMax );

      rescaler->AutomaticInputMinMaxComputationOff();
      rescaler->SetInputMinimum(inputMin);
      rescaler->SetInputMaximum(inputMax);

      if ( rescaleType == "linear")
        {
        rescaler->SetGamma(GetParameterFloat("type.linear.gamma"));
        }

      m_TmpFilter = rescaler;
      castIm = rescaler->GetOutput();
      }

    SetParameterOutputImage<TImageType>("out", castIm);
  }

 void DoExecute() ITK_OVERRIDE
  {
    switch ( this->GetParameterOutputImagePixelType("out") )
      {
      case ImagePixelType_uint8:
        GenericDoExecute<UInt8VectorImageType>();
        break;
      case ImagePixelType_int16:
        GenericDoExecute<Int16VectorImageType>();
        break;
      case ImagePixelType_uint16:
        GenericDoExecute<UInt16VectorImageType>();
        break;
      case ImagePixelType_int32:
        GenericDoExecute<Int32VectorImageType>();
        break;
      case ImagePixelType_uint32:
        GenericDoExecute<UInt32VectorImageType>();
        break;
      case ImagePixelType_float:
        GenericDoExecute<FloatVectorImageType>();
        break;
      case ImagePixelType_double:
        GenericDoExecute<DoubleVectorImageType>();
        break;
      default:
        itkExceptionMacro("Unknown pixel type "<<this->GetParameterOutputImagePixelType("out")<<".");
        break;
      }
  }

  itk::ProcessObject::Pointer m_TmpFilter;
  TransferLogType::Pointer m_TransferLog;
};

}
}

OTB_APPLICATION_EXPORT(otb::Wrapper::Convert)

<commit_msg>BUG: Typo in Convert app description<commit_after>/*
 * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "itkCastImageFilter.h"

#include "otbVectorRescaleIntensityImageFilter.h"
#include "itkCastImageFilter.h"
#include "otbUnaryImageFunctorWithVectorImageFilter.h"
#include "otbStreamingShrinkImageFilter.h"
#include "itkListSample.h"
#include "otbListSampleToHistogramListGenerator.h"
#include "itkImageRegionConstIterator.h"

namespace otb
{
namespace Wrapper
{

namespace Functor
{
template< class TScalar >
class ITK_EXPORT LogFunctor
{
public:
  LogFunctor(){};
  ~LogFunctor(){};
  TScalar operator() (const TScalar& v) const
  {
    return vcl_log(v);
  }
};
} // end namespace Functor

class Convert : public Application
{
public:
  /** Standard class typedefs. */
  typedef Convert                       Self;
  typedef Application                   Superclass;
  typedef itk::SmartPointer<Self>       Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;

  /** Standard macro */
  itkNewMacro(Self);

  itkTypeMacro(Convert, otb::Application);

  /** Filters typedef */
  typedef itk::Statistics::ListSample<FloatVectorImageType::PixelType> ListSampleType;
  typedef itk::Statistics::DenseFrequencyContainer2 DFContainerType;
  typedef ListSampleToHistogramListGenerator<ListSampleType, FloatVectorImageType::InternalPixelType, DFContainerType> HistogramsGeneratorType;
  typedef StreamingShrinkImageFilter<FloatVectorImageType, FloatVectorImageType> ShrinkFilterType;
  typedef Functor::LogFunctor<FloatVectorImageType::InternalPixelType> TransferLogFunctor;
  typedef UnaryImageFunctorWithVectorImageFilter<FloatVectorImageType, FloatVectorImageType, TransferLogFunctor> TransferLogType;

private:
  void DoInit() ITK_OVERRIDE
  {
    SetName("Convert");
    SetDescription("Convert an image to a different format, optionally rescaling the data"
                   " and/or changing the pixel type.");
    // Documentation
    SetDocName("Image Conversion");
    SetDocLongDescription("This application performs an image pixel type conversion (short, ushort, uchar, int, uint, float and double types are handled). The output image is written in the specified format (ie. that corresponds to the given extension).\n The conversion can include a rescale using the image 2 percent minimum and maximum values. The rescale can be linear or log2.");
    SetDocLimitations("None");
    SetDocAuthors("OTB-Team");
    SetDocSeeAlso("Rescale");

	AddDocTag(Tags::Manip);
    AddDocTag("Conversion");
    AddDocTag("Image Dynamic");

    AddParameter(ParameterType_InputImage,  "in",   "Input image");
    SetParameterDescription("in", "Input image");

    AddParameter(ParameterType_Choice, "type", "Rescale type");
    SetParameterDescription("type", "Transfer function for the rescaling");
    AddChoice("type.none", "None");
    AddChoice("type.linear", "Linear");
    AddChoice("type.log2", "Log2");
    SetParameterString("type", "none", false);

    AddParameter(ParameterType_Float,"type.linear.gamma","Gamma correction factor");
    SetParameterDescription("type.linear.gamma","Gamma correction factor");
    SetDefaultParameterFloat("type.linear.gamma",1.0);
    MandatoryOff("type.linear.gamma");

    AddParameter(ParameterType_InputImage,  "mask",   "Input mask");
    SetParameterDescription("mask", "The masked pixels won't be used to adapt the dynamic (the mask must have the same dimensions as the input image)");
    MandatoryOff("mask");
    DisableParameter("mask");

    AddParameter(ParameterType_Group,"hcp","Histogram Cutting Parameters");
    SetParameterDescription("hcp","Parameters to cut the histogram edges before rescaling");

    AddParameter(ParameterType_Float, "hcp.high", "High Cut Quantile");
    SetParameterDescription("hcp.high", "Quantiles to cut from histogram high values before computing min/max rescaling (in percent, 2 by default)");
    MandatoryOff("hcp.high");
    SetDefaultParameterFloat("hcp.high", 2.0);
    DisableParameter("hcp.high");

    AddParameter(ParameterType_Float, "hcp.low", "Low Cut Quantile");
    SetParameterDescription("hcp.low", "Quantiles to cut from histogram low values before computing min/max rescaling (in percent, 2 by default)");
    MandatoryOff("hcp.low");
    SetDefaultParameterFloat("hcp.low", 2.0);
    DisableParameter("hcp.low");

    AddParameter(ParameterType_OutputImage, "out",  "Output Image");
    SetParameterDescription("out", "Output image");

    AddRAMParameter();

    // Doc example parameter settings
    SetDocExampleParameterValue("in", "QB_Toulouse_Ortho_XS.tif");
    SetDocExampleParameterValue("out", "otbConvertWithScalingOutput.png uint8");
    SetDocExampleParameterValue("type", "linear");

    SetOfficialDocLink();
  }

  void DoUpdateParameters() ITK_OVERRIDE
  {
    // Nothing to do here for the parameters : all are independent
  }

  template<class TImageType>
  void GenericDoExecute()
  {
    typename TImageType::Pointer castIm;

    std::string rescaleType = this->GetParameterString("type");

    if( (rescaleType != "none") && (rescaleType != "linear") && (rescaleType != "log2") )
      {
      itkExceptionMacro("Unknown rescale type "<<rescaleType<<".");
      }

    if( rescaleType == "none" )
      {
      castIm = this->GetParameterImage<TImageType>("in");
      }
    else
      {
      FloatVectorImageType::Pointer input = this->GetParameterImage("in");

      FloatVectorImageType::Pointer mask;
      bool useMask = false;
      if (IsParameterEnabled("mask"))
        {
        mask = this->GetParameterImage("mask");
        useMask = true;
        }

      const unsigned int nbComp(input->GetNumberOfComponentsPerPixel());

      typedef otb::VectorRescaleIntensityImageFilter<FloatVectorImageType, TImageType> RescalerType;
      typename TImageType::PixelType minimum;
      typename TImageType::PixelType maximum;
      minimum.SetSize(nbComp);
      maximum.SetSize(nbComp);
      minimum.Fill( itk::NumericTraits<typename TImageType::InternalPixelType>::min() );
      maximum.Fill( itk::NumericTraits<typename TImageType::InternalPixelType>::max() );

      typename RescalerType::Pointer rescaler = RescalerType::New();

      rescaler->SetOutputMinimum(minimum);
      rescaler->SetOutputMaximum(maximum);

      // We need to subsample the input image in order to estimate its
      // histogram

      typename ShrinkFilterType::Pointer shrinkFilter = ShrinkFilterType::New();

      // Shrink factor is computed so as to load a quicklook of 1000
      // pixels square at most
      typename FloatVectorImageType::SizeType imageSize = input->GetLargestPossibleRegion().GetSize();
      unsigned int shrinkFactor = std::max(imageSize[0], imageSize[1]) < 1000 ? 1 : std::max(imageSize[0], imageSize[1])/1000;

      otbAppLogDEBUG( << "Shrink factor used to compute Min/Max: "<<shrinkFactor );

      otbAppLogDEBUG( << "Shrink starts..." );

      shrinkFilter->SetShrinkFactor(shrinkFactor);
      shrinkFilter->GetStreamer()->SetAutomaticAdaptativeStreaming(GetParameterInt("ram"));
      AddProcess(shrinkFilter->GetStreamer(), "Computing shrink Image for min/max estimation...");

      if ( rescaleType == "log2")
        {
        //define the transfer log
        m_TransferLog = TransferLogType::New();
        m_TransferLog->SetInput(input);
        m_TransferLog->UpdateOutputInformation();

        shrinkFilter->SetInput(m_TransferLog->GetOutput());
        rescaler->SetInput(m_TransferLog->GetOutput());
        shrinkFilter->Update();
        }
      else
        {
        shrinkFilter->SetInput(input);
        rescaler->SetInput(input);
        shrinkFilter->Update();
        }

      ShrinkFilterType::Pointer maskShrinkFilter = ShrinkFilterType::New();
      if (useMask)
        {
        maskShrinkFilter->SetShrinkFactor(shrinkFactor);
        maskShrinkFilter->SetInput(mask);
        maskShrinkFilter->GetStreamer()->SetAutomaticAdaptativeStreaming(GetParameterInt("ram"));
        maskShrinkFilter->Update();
        }

      otbAppLogDEBUG( << "Shrink done" );

      otbAppLogDEBUG( << "Evaluating input Min/Max..." );
      itk::ImageRegionConstIterator<FloatVectorImageType> it(shrinkFilter->GetOutput(), shrinkFilter->GetOutput()->GetLargestPossibleRegion());
      itk::ImageRegionConstIterator<FloatVectorImageType> itMask;
      if (useMask)
        {
        itMask = itk::ImageRegionConstIterator<FloatVectorImageType>(maskShrinkFilter->GetOutput(),maskShrinkFilter->GetOutput()->GetLargestPossibleRegion());
        }

      typename ListSampleType::Pointer listSample = ListSampleType::New();
      listSample->SetMeasurementVectorSize(input->GetNumberOfComponentsPerPixel());

      // Now we generate the list of samples
      if (useMask)
        {
        // Remove masked pixels
        it.GoToBegin();
        itMask.GoToBegin();
        while (!it.IsAtEnd())
          {
          // float values, so the threshold is set to 0.5
          if (itMask.Get()[0] < 0.5)
            {
            listSample->PushBack(it.Get());
            }
          ++it;
          ++itMask;
          }
        }
      else
        {
        for(it.GoToBegin(); !it.IsAtEnd(); ++it)
          {
          listSample->PushBack(it.Get());
          }
        }

      // if all pixels were masked, we assume a wrong mask and then include all image
      if (listSample->Size() == 0)
        {
        otbAppLogINFO( << "All pixels were masked, the application assume a wrong mask and include all the image");
        for(it.GoToBegin(); !it.IsAtEnd(); ++it)
          {
          listSample->PushBack(it.Get());
          }
        }

      // And then the histogram
      typename HistogramsGeneratorType::Pointer histogramsGenerator = HistogramsGeneratorType::New();
      histogramsGenerator->SetListSample(listSample);
      histogramsGenerator->SetNumberOfBins(255);
      histogramsGenerator->NoDataFlagOn();
      histogramsGenerator->Update();

      // And extract the lower and upper quantile
      typename FloatVectorImageType::PixelType inputMin(nbComp), inputMax(nbComp);

      for(unsigned int i = 0; i < nbComp; ++i)
        {
        inputMin[i] = histogramsGenerator->GetOutput()->GetNthElement(i)->Quantile(0, 0.01 * GetParameterFloat("hcp.low"));
        inputMax[i] = histogramsGenerator->GetOutput()->GetNthElement(i)->Quantile(0, 1.0 - 0.01 * GetParameterFloat("hcp.high"));
        }

      otbAppLogDEBUG( << std::setprecision(5) << "Min/Max computation done : min=" << inputMin
                      << " max=" << inputMax );

      rescaler->AutomaticInputMinMaxComputationOff();
      rescaler->SetInputMinimum(inputMin);
      rescaler->SetInputMaximum(inputMax);

      if ( rescaleType == "linear")
        {
        rescaler->SetGamma(GetParameterFloat("type.linear.gamma"));
        }

      m_TmpFilter = rescaler;
      castIm = rescaler->GetOutput();
      }

    SetParameterOutputImage<TImageType>("out", castIm);
  }

 void DoExecute() ITK_OVERRIDE
  {
    switch ( this->GetParameterOutputImagePixelType("out") )
      {
      case ImagePixelType_uint8:
        GenericDoExecute<UInt8VectorImageType>();
        break;
      case ImagePixelType_int16:
        GenericDoExecute<Int16VectorImageType>();
        break;
      case ImagePixelType_uint16:
        GenericDoExecute<UInt16VectorImageType>();
        break;
      case ImagePixelType_int32:
        GenericDoExecute<Int32VectorImageType>();
        break;
      case ImagePixelType_uint32:
        GenericDoExecute<UInt32VectorImageType>();
        break;
      case ImagePixelType_float:
        GenericDoExecute<FloatVectorImageType>();
        break;
      case ImagePixelType_double:
        GenericDoExecute<DoubleVectorImageType>();
        break;
      default:
        itkExceptionMacro("Unknown pixel type "<<this->GetParameterOutputImagePixelType("out")<<".");
        break;
      }
  }

  itk::ProcessObject::Pointer m_TmpFilter;
  TransferLogType::Pointer m_TransferLog;
};

}
}

OTB_APPLICATION_EXPORT(otb::Wrapper::Convert)

<|endoftext|>