<commit_before>#include "connection.hpp"
#include <boost/log/trivial.hpp>
#include <boost/throw_exception.hpp>
#include <tmsp/types.pb.cc>

namespace tmsp
{

namespace wire
{

/*
	Original tendermint implementation:

	func ReadVarint(r io.Reader, n *int, err *error) int {
		var size = ReadUint8(r, n, err)
		var negate = false
		if (size >> 4) == 0xF {
			negate = true
			size = size & 0x0F
		}
		if size > 8 {
			setFirstErr(err, errors.New("Varint overflow"))
			return 0
		}
		if size == 0 {
			if negate {
				setFirstErr(err, errors.New("Varint does not allow negative zero"))
			}
			return 0
		}
		buf := make([]byte, 8)
		ReadFull(buf[(8-size):], r, n, err)
		var i = int(binary.BigEndian.Uint64(buf))
		if negate {
			return -i
		} else {
			return i
		}
	}
*/

template<typename Integer>
bool read_variable_integer(buffer_type::pointer& iter, buffer_type::pointer end, Integer& result)
{
	auto size(*iter & char(0x0F));
	auto const negate(*iter & char(0xF0));

	if(size == 0 && negate)
	{
		BOOST_THROW_EXCEPTION(std::range_error("varint does not allow negative zero"));
	}
	else if(size > 8)
	{
		BOOST_THROW_EXCEPTION(std::range_error("varint overflow"));
	}
	else if(++ iter + size > end)
	{
		return false;
	}

	result = 0;
	for(; size; -- size, ++ iter)
	{
		result <<= 8;
		result += static_cast<std::uint8_t>(*iter);
	}

	if(negate)
	{
		result = - result;
	}

	return true;
}

/*
	Original tendermint implementation:

	func WriteVarint(i int, w io.Writer, n *int, err *error) {
		var negate = false
		if i < 0 {
			negate = true
			i = -i
		}
		var size = uvarintSize(uint64(i))
		if negate {
			// e.g. 0xF1 for a single negative byte
			WriteUint8(uint8(size+0xF0), w, n, err)
		} else {
			WriteUint8(uint8(size), w, n, err)
		}
		if size > 0 {
			buf := make([]byte, 8)
			binary.BigEndian.PutUint64(buf, uint64(i))
			WriteTo(buf[(8-size):], w, n, err)
		}
	}
*/

template<typename Integer>
bool write_variable_integer(buffer_type::pointer& iter, buffer_type::pointer end, Integer value)
{
	if(iter == end)
	{
		return false;
	}

	auto& size(*iter);
	if(value < 0)
	{
		size = char(0xF0);
		value = - value;
	}
	else
	{
		size = char(0x00);
	}

	for(++ iter; value; ++ size, ++ iter)
	{
		if(iter == end)
		{
			return false;
		}

		*iter = static_cast<std::uint8_t>(value);
		value >>= 8;
	}

	return true;
}

}	// namespace wire

connection_type::connection_type(boost::asio::ip::tcp::socket socket, application_ptr_type const& application) :
	socket_(std::move(socket)),
	read_head_(read_buffer_.data()),
	read_tail_(read_head_),
	write_head_(write_buffer_.data()),
	write_tail_(write_head_),
	application_(application)
{
}

void connection_type::start()
{
	async_read_();
}

void connection_type::async_read_()
{
	auto const self(shared_from_this());
	auto const remaining(read_buffer_.data() + read_buffer_.size() - read_tail_);
	boost::asio::async_read(socket_, boost::asio::buffer(read_tail_, remaining), boost::asio::transfer_at_least(1),
		[self, this](boost::system::error_code const& error, std::size_t const& transferred)
		{
			if(error)
			{
				return;
			}

			read_tail_ += transferred;
			try_advance_read_buffer_();
			async_read_();
		});
}

void connection_type::async_write_()
{
	auto const self(shared_from_this());
	auto const remaining(write_tail_ - write_head_);
	if(remaining)
	{
		boost::asio::async_write(socket_, boost::asio::buffer(write_head_, remaining), boost::asio::transfer_all(),
			[self, this](boost::system::error_code const& error, std::size_t const& transferred)
			{
				if(error)
				{
					BOOST_LOG_TRIVIAL(error) << socket_.remote_endpoint() << ": Write error: " << error;
					return;
				}

				async_write_();
			});

		write_head_ = write_tail_;
	}
	else
	{
		write_head_ = write_buffer_.data();
		write_tail_ = write_head_;

		// Now that the write buffer is empty, see if there are any unprocessed requests available
		try_advance_read_buffer_();
	}
}

void connection_type::try_advance_read_buffer_()
{
	while(read_head_ != read_tail_)
	{
		auto iter(read_head_);
		std::int32_t message_size(0);
		if(!wire::read_variable_integer(iter, read_tail_, message_size))
		{
			break;
		}

		if(message_size <= 0)
		{
			BOOST_THROW_EXCEPTION(std::range_error("invalid message size"));
		}

		if(iter + message_size > read_tail_)
		{
			// Buffer does not contain the full message
			break;
		}

		types::Request request;
		request.ParseFromArray(iter, message_size);
		if(!request.IsInitialized())
		{
			BOOST_THROW_EXCEPTION(std::runtime_error("unable to parse request"));
		}

		// Only advance read head (i.e. treat request as completed) if a response could be prepared and written
		types::Response response;
		if(prepare_response_(request, response))
		{
			if(!write_message_(response))
			{
				BOOST_LOG_TRIVIAL(warning) << "Unable to write response - buffer full";
				break;
			}
		}
		read_head_ = iter + message_size;
	}

	if(read_head_ == read_tail_)
	{
		// All data consumed
		read_head_ = read_buffer_.data();
		read_tail_ = read_head_;
	}
	else
	{
		// Copy remaining data to front of buffer
		read_tail_ = std::copy(read_head_, read_tail_, read_buffer_.data());
		read_head_ = read_buffer_.data();
	}
}

bool connection_type::prepare_response_(types::Request const& request, types::Response& response)
{
	if(request.type() == types::Echo)
	{
		response.set_type(types::Echo);
		response.set_data(request.data());
		response.set_code(types::OK);
		return true;
	}
	else if(request.type() == types::Flush)
	{
		response.set_type(types::Flush);
		response.set_code(types::OK);
		return true;
	}
	else if(request.type() == types::Info)
	{
		std::string out;
		application_->info(out);
		response.set_type(types::Info);
		response.set_data(out);
		return true;
	}
	else if(request.type() == types::SetOption)
	{
		std::string log;
		application_->set_option(request.key(), request.value(), log);
		response.set_type(types::SetOption);
		response.set_log(log);
		return true;
	}
	else if(request.type() == types::Query)
	{
		std::string out, log;
		response.set_code(application_->query(request.data(), out, log));
		response.set_type(types::CheckTx);
		response.set_data(out);
		response.set_log(log);
		return true;
	}
	else if(request.type() == types::BeginBlock)
	{
		application_->begin_block(request.height());
		return false;
	}
	else if(request.type() == types::EndBlock)
	{
		response.set_type(types::EndBlock);
		response.set_code(application_->end_block(request.height()));
		return true;
	}
	else if(request.type() == types::CheckTx)
	{
		std::string out, log;
		response.set_code(application_->check_tx(request.data(), out, log));
		response.set_type(types::CheckTx);
		response.set_data(out);
		response.set_log(log);
		return true;
	}
	else if(request.type() == types::AppendTx)
	{
		std::string out, log;
		response.set_code(application_->append_tx(request.data(), out, log));
		response.set_type(types::AppendTx);
		response.set_data(out);
		response.set_log(log);
		return true;
	}
	else if(request.type() == types::Commit)
	{
		std::string out, log;
		application_->commit(out, log);
		response.set_type(types::Commit);
		response.set_data(out);
		response.set_log(log);
		return true;
	}

	BOOST_THROW_EXCEPTION(std::logic_error("unsupported TMSP request: " + request.DebugString()));
}

bool connection_type::write_message_(google::protobuf::MessageLite const& message)
{
	auto iter(write_tail_);
	if(!wire::write_variable_integer(iter, write_buffer_.data() + write_buffer_.size(), message.ByteSize()))
	{
		// Not enough room for message size
		return false;
	}

	if(write_tail_ + message.ByteSize() > write_buffer_.data() + write_buffer_.size())
	{
		// Not enough room for message content
		return false;
	}

	if(!message.SerializeToArray(iter, message.ByteSize()))
	{
		BOOST_THROW_EXCEPTION(std::runtime_error("unable to serialize message"));
	}
	write_tail_ = iter + message.ByteSize();

	// Start async write if the write head is at the start of the buffer
	if(write_head_ == write_buffer_.data())
	{
		async_write_();
	}

	return true;
}

}	// namespace tmsp<commit_msg>Set proper response type for "Query" requests<commit_after>#include "connection.hpp"
#include <boost/log/trivial.hpp>
#include <boost/throw_exception.hpp>
#include <tmsp/types.pb.cc>

namespace tmsp
{

namespace wire
{

/*
	Original tendermint implementation:

	func ReadVarint(r io.Reader, n *int, err *error) int {
		var size = ReadUint8(r, n, err)
		var negate = false
		if (size >> 4) == 0xF {
			negate = true
			size = size & 0x0F
		}
		if size > 8 {
			setFirstErr(err, errors.New("Varint overflow"))
			return 0
		}
		if size == 0 {
			if negate {
				setFirstErr(err, errors.New("Varint does not allow negative zero"))
			}
			return 0
		}
		buf := make([]byte, 8)
		ReadFull(buf[(8-size):], r, n, err)
		var i = int(binary.BigEndian.Uint64(buf))
		if negate {
			return -i
		} else {
			return i
		}
	}
*/

template<typename Integer>
bool read_variable_integer(buffer_type::pointer& iter, buffer_type::pointer end, Integer& result)
{
	auto size(*iter & char(0x0F));
	auto const negate(*iter & char(0xF0));

	if(size == 0 && negate)
	{
		BOOST_THROW_EXCEPTION(std::range_error("varint does not allow negative zero"));
	}
	else if(size > 8)
	{
		BOOST_THROW_EXCEPTION(std::range_error("varint overflow"));
	}
	else if(++ iter + size > end)
	{
		return false;
	}

	result = 0;
	for(; size; -- size, ++ iter)
	{
		result <<= 8;
		result += static_cast<std::uint8_t>(*iter);
	}

	if(negate)
	{
		result = - result;
	}

	return true;
}

/*
	Original tendermint implementation:

	func WriteVarint(i int, w io.Writer, n *int, err *error) {
		var negate = false
		if i < 0 {
			negate = true
			i = -i
		}
		var size = uvarintSize(uint64(i))
		if negate {
			// e.g. 0xF1 for a single negative byte
			WriteUint8(uint8(size+0xF0), w, n, err)
		} else {
			WriteUint8(uint8(size), w, n, err)
		}
		if size > 0 {
			buf := make([]byte, 8)
			binary.BigEndian.PutUint64(buf, uint64(i))
			WriteTo(buf[(8-size):], w, n, err)
		}
	}
*/

template<typename Integer>
bool write_variable_integer(buffer_type::pointer& iter, buffer_type::pointer end, Integer value)
{
	if(iter == end)
	{
		return false;
	}

	auto& size(*iter);
	if(value < 0)
	{
		size = char(0xF0);
		value = - value;
	}
	else
	{
		size = char(0x00);
	}

	for(++ iter; value; ++ size, ++ iter)
	{
		if(iter == end)
		{
			return false;
		}

		*iter = static_cast<std::uint8_t>(value);
		value >>= 8;
	}

	return true;
}

}	// namespace wire

connection_type::connection_type(boost::asio::ip::tcp::socket socket, application_ptr_type const& application) :
	socket_(std::move(socket)),
	read_head_(read_buffer_.data()),
	read_tail_(read_head_),
	write_head_(write_buffer_.data()),
	write_tail_(write_head_),
	application_(application)
{
}

void connection_type::start()
{
	async_read_();
}

void connection_type::async_read_()
{
	auto const self(shared_from_this());
	auto const remaining(read_buffer_.data() + read_buffer_.size() - read_tail_);
	boost::asio::async_read(socket_, boost::asio::buffer(read_tail_, remaining), boost::asio::transfer_at_least(1),
		[self, this](boost::system::error_code const& error, std::size_t const& transferred)
		{
			if(error)
			{
				return;
			}

			read_tail_ += transferred;
			try_advance_read_buffer_();
			async_read_();
		});
}

void connection_type::async_write_()
{
	auto const self(shared_from_this());
	auto const remaining(write_tail_ - write_head_);
	if(remaining)
	{
		boost::asio::async_write(socket_, boost::asio::buffer(write_head_, remaining), boost::asio::transfer_all(),
			[self, this](boost::system::error_code const& error, std::size_t const& transferred)
			{
				if(error)
				{
					BOOST_LOG_TRIVIAL(error) << socket_.remote_endpoint() << ": Write error: " << error;
					return;
				}

				async_write_();
			});

		write_head_ = write_tail_;
	}
	else
	{
		write_head_ = write_buffer_.data();
		write_tail_ = write_head_;

		// Now that the write buffer is empty, see if there are any unprocessed requests available
		try_advance_read_buffer_();
	}
}

void connection_type::try_advance_read_buffer_()
{
	while(read_head_ != read_tail_)
	{
		auto iter(read_head_);
		std::int32_t message_size(0);
		if(!wire::read_variable_integer(iter, read_tail_, message_size))
		{
			break;
		}

		if(message_size <= 0)
		{
			BOOST_THROW_EXCEPTION(std::range_error("invalid message size"));
		}

		if(iter + message_size > read_tail_)
		{
			// Buffer does not contain the full message
			break;
		}

		types::Request request;
		request.ParseFromArray(iter, message_size);
		if(!request.IsInitialized())
		{
			BOOST_THROW_EXCEPTION(std::runtime_error("unable to parse request"));
		}

		// Only advance read head (i.e. treat request as completed) if a response could be prepared and written
		types::Response response;
		if(prepare_response_(request, response))
		{
			if(!write_message_(response))
			{
				BOOST_LOG_TRIVIAL(warning) << "Unable to write response - buffer full";
				break;
			}
		}
		read_head_ = iter + message_size;
	}

	if(read_head_ == read_tail_)
	{
		// All data consumed
		read_head_ = read_buffer_.data();
		read_tail_ = read_head_;
	}
	else
	{
		// Copy remaining data to front of buffer
		read_tail_ = std::copy(read_head_, read_tail_, read_buffer_.data());
		read_head_ = read_buffer_.data();
	}
}

bool connection_type::prepare_response_(types::Request const& request, types::Response& response)
{
	if(request.type() == types::Echo)
	{
		response.set_type(types::Echo);
		response.set_data(request.data());
		response.set_code(types::OK);
		return true;
	}
	else if(request.type() == types::Flush)
	{
		response.set_type(types::Flush);
		response.set_code(types::OK);
		return true;
	}
	else if(request.type() == types::Info)
	{
		std::string out;
		application_->info(out);
		response.set_type(types::Info);
		response.set_data(out);
		return true;
	}
	else if(request.type() == types::SetOption)
	{
		std::string log;
		application_->set_option(request.key(), request.value(), log);
		response.set_type(types::SetOption);
		response.set_log(log);
		return true;
	}
	else if(request.type() == types::Query)
	{
		std::string out, log;
		response.set_code(application_->query(request.data(), out, log));
		response.set_type(types::Query);
		response.set_data(out);
		response.set_log(log);
		return true;
	}
	else if(request.type() == types::BeginBlock)
	{
		application_->begin_block(request.height());
		return false;
	}
	else if(request.type() == types::EndBlock)
	{
		response.set_type(types::EndBlock);
		response.set_code(application_->end_block(request.height()));
		return true;
	}
	else if(request.type() == types::CheckTx)
	{
		std::string out, log;
		response.set_code(application_->check_tx(request.data(), out, log));
		response.set_type(types::CheckTx);
		response.set_data(out);
		response.set_log(log);
		return true;
	}
	else if(request.type() == types::AppendTx)
	{
		std::string out, log;
		response.set_code(application_->append_tx(request.data(), out, log));
		response.set_type(types::AppendTx);
		response.set_data(out);
		response.set_log(log);
		return true;
	}
	else if(request.type() == types::Commit)
	{
		std::string out, log;
		application_->commit(out, log);
		response.set_type(types::Commit);
		response.set_data(out);
		response.set_log(log);
		return true;
	}

	BOOST_THROW_EXCEPTION(std::logic_error("unsupported TMSP request: " + request.DebugString()));
}

bool connection_type::write_message_(google::protobuf::MessageLite const& message)
{
	auto iter(write_tail_);
	if(!wire::write_variable_integer(iter, write_buffer_.data() + write_buffer_.size(), message.ByteSize()))
	{
		// Not enough room for message size
		return false;
	}

	if(write_tail_ + message.ByteSize() > write_buffer_.data() + write_buffer_.size())
	{
		// Not enough room for message content
		return false;
	}

	if(!message.SerializeToArray(iter, message.ByteSize()))
	{
		BOOST_THROW_EXCEPTION(std::runtime_error("unable to serialize message"));
	}
	write_tail_ = iter + message.ByteSize();

	// Start async write if the write head is at the start of the buffer
	if(write_head_ == write_buffer_.data())
	{
		async_write_();
	}

	return true;
}

}	// namespace tmsp<|endoftext|>


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

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you 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.

*******************************************************************************/

//==============================================================================
// CellML Model Repository window
//==============================================================================

#include "cellmlmodelrepositorywindowwindow.h"
#include "cellmlmodelrepositorywindowwidget.h"
#include "cliutils.h"
#include "guiutils.h"

//==============================================================================

#include "ui_cellmlmodelrepositorywindowwindow.h"

//==============================================================================

#include <Qt>

//==============================================================================

#include <QClipboard>
#include <QJsonDocument>
#include <QJsonObject>
#include <QMenu>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QPoint>
#include <QRegularExpression>

//==============================================================================

namespace OpenCOR {
namespace CellMLModelRepositoryWindow {

//==============================================================================

CellmlModelRepositoryWindowWindow::CellmlModelRepositoryWindowWindow(QWidget *pParent) :
    Core::OrganisationWidget(pParent),
    mGui(new Ui::CellmlModelRepositoryWindowWindow),
    mModelListRequested(false)
{
    // Set up the GUI

    mGui->setupUi(this);

#ifdef Q_OS_MAC
    mGui->filterValue->setAttribute(Qt::WA_MacShowFocusRect, false);
    // Note: the above removes the focus border since it messes up the look of
    //       our filter value widget...
#endif

    // Make the name value our focus proxy

    setFocusProxy(mGui->filterValue);

    // Create and add the CellML Model Repository widget

    mCellmlModelRepositoryWidget = new CellmlModelRepositoryWindowWidget(this);

    mGui->dockWidgetContents->layout()->addWidget(mCellmlModelRepositoryWidget);

    // Create and populate our context menu

    mContextMenu = new QMenu(this);

    mContextMenu->addAction(mGui->actionCopy);

    // We want our own context menu for the help widget (indeed, we don't want
    // the default one which has the reload menu item and not the other actions
    // that we have in our tool bar widget, so...)

    mCellmlModelRepositoryWidget->setContextMenuPolicy(Qt::CustomContextMenu);

    connect(mCellmlModelRepositoryWidget, SIGNAL(customContextMenuRequested(const QPoint &)),
            this, SLOT(showCustomContextMenu(const QPoint &)));

    // Keep track of the window's visibility, so that we can request the list of
    // models, if necessary

    connect(this, SIGNAL(visibilityChanged(bool)),
            this, SLOT(retrieveModelList(const bool &)));

    // Create a network access manager so that we can then retrieve a list of
    // CellML models from the CellML Model Repository

    mNetworkAccessManager = new QNetworkAccessManager(this);

    // Make sure that we get told when the download of our Internet file is
    // complete, as well as if there are SSL errors (which would happen if the
    // website's certificate is invalid, e.g. it has expired)

    connect(mNetworkAccessManager, SIGNAL(finished(QNetworkReply *)),
            this, SLOT(finished(QNetworkReply *)) );
    connect(mNetworkAccessManager, SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &)),
            this, SLOT(sslErrors(QNetworkReply *, const QList<QSslError> &)) );

    // Connection to update the enabled state of our copy action

    connect(mCellmlModelRepositoryWidget, SIGNAL(copyTextEnabled(const bool &)),
            mGui->actionCopy, SLOT(setEnabled(bool)));
}

//==============================================================================

CellmlModelRepositoryWindowWindow::~CellmlModelRepositoryWindowWindow()
{
    // Delete the GUI

    delete mGui;
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::retranslateUi()
{
    // Retranslate the whole window

    mGui->retranslateUi(this);

    // Retranslate our list of models

    outputModelList(mModelList);
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::outputModelList(const QStringList &pModelList)
{
    // Output a given list of models

    mModelList = pModelList;

    QString contents = QString();
    static const QString leadingSpaces = "        ";

    if (mModelList.count()) {
        // We have models to list, so...

        if (mModelList.count() == 1)
            contents = tr("1 CellML model was found:")+"\n";
        else
            contents = tr("%1 CellML models were found:").arg(mModelList.count())+"\n";

        contents += leadingSpaces+"
\n";

        foreach (const QString &model, mModelList)
            contents += leadingSpaces+"
"+model+"
\n";

        contents += leadingSpaces+"
";
    } else if (mModelNames.empty()) {
        if (mErrorMessage.count())
            // Something went wrong while trying to retrieve the list of models,
            // so...

            contents = leadingSpaces+tr("Error: ")+Core::formatErrorMessage(mErrorMessage);
    } else {
        // No model could be found, so...

        contents = leadingSpaces+tr("No CellML model matches your criteria");
    }

    // Show/hide our busy widget

    if (mModelListRequested)
        showBusyWidget(mCellmlModelRepositoryWidget);
    else
        hideBusyWidget();

    // Output the list matching the search criteria, or a message telling the
    // user what went wrong, if anything and if needed

    if (!mModelListRequested)
        mCellmlModelRepositoryWidget->output(contents);
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::on_filterValue_textChanged(const QString &text)
{
    // Generate a Web page that contains all the models that match our search
    // criteria

    outputModelList(mModelNames.filter(QRegularExpression(text, QRegularExpression::CaseInsensitiveOption)));
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::on_actionCopy_triggered()
{
    // Copy the current selection to the clipboard

    QApplication::clipboard()->setText(mCellmlModelRepositoryWidget->selectedText());
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::on_refreshButton_clicked()
{
    // Output the message telling the user that the list is being downloaded
    // Note: to clear mModelNames ensures that we get the correct message from
    //       outputModelList...

    mModelListRequested = true;

    mModelNames.clear();

    outputModelList(QStringList());

    // Disable the GUI side, so that the user doesn't get confused and ask to
    // refresh over and over again while he should just be patient

    mGui->dockWidgetContents->setEnabled(false);

    // Get the list of CellML models

    mNetworkAccessManager->get(QNetworkRequest(QUrl("https://models.physiomeproject.org/cellml/workspace/rest/contents.json")));
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::finished(QNetworkReply *pNetworkReply)
{
    // Clear some properties

    mModelNames.clear();
    mModelUrls.clear();

    mModelListRequested = false;

    // Output the list of models, should we have retrieved it without any
    // problem

    if (pNetworkReply->error() == QNetworkReply::NoError) {
        // Parse the JSON code

        QJsonParseError jsonParseError;
        QJsonDocument jsonDocument = QJsonDocument::fromJson(pNetworkReply->readAll(), &jsonParseError);

        if (jsonParseError.error == QJsonParseError::NoError) {
            // Retrieve the list of CellML models

            QVariantMap resultMap = jsonDocument.object().toVariantMap();

            foreach (const QVariant &modelVariant, resultMap["values"].toList()) {
                QVariantList modelDetailsVariant = modelVariant.toList();

                mModelNames << modelDetailsVariant[0].toString();
                mModelUrls << modelDetailsVariant[1].toString();
            }

            // Everything went fine, so...

            mErrorMessage = QString();
        } else {
            // Something went wrong, so...

            mErrorMessage = jsonParseError.errorString();
        }
    } else {
        // Something went wrong, so...

        mErrorMessage = pNetworkReply->errorString();
    }

    // Initialise the output using whatever search criteria is present

    on_filterValue_textChanged(mGui->filterValue->text());

    // Re-enable the GUI side

    mGui->dockWidgetContents->setEnabled(true);

    // Give, within the current window, the focus to mGui->filterValue, but
    // only if the current window already has the focus

    Core::setFocusTo(mGui->filterValue);

    // Delete (later) the network reply

    pNetworkReply->deleteLater();
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::sslErrors(QNetworkReply *pNetworkReply,
                                                  const QList<QSslError> &pSslErrors)
{
    // Ignore the SSL errors since we trust the website and therefore its
    // certificate (even if it is invalid, e.g. it has expired)

    pNetworkReply->ignoreSslErrors(pSslErrors);
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::showCustomContextMenu(const QPoint &pPosition) const
{
    Q_UNUSED(pPosition);

    // Show our context menu for our CellML Models Repository widget

    mContextMenu->exec(QCursor::pos());
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::retrieveModelList(const bool &pVisible)
{
    // Retrieve the list of models, if we are becoming visible and the list of
    // models has never been requested before

    static bool firstTime = true;

    if (pVisible && firstTime) {
        firstTime = false;

        on_refreshButton_clicked();
    }
}

//==============================================================================

}   // namespace CellMLModelRepositoryWindow
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Some minor cleaning up [ci skip].<commit_after>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you 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.

*******************************************************************************/

//==============================================================================
// CellML Model Repository window
//==============================================================================

#include "cellmlmodelrepositorywindowwindow.h"
#include "cellmlmodelrepositorywindowwidget.h"
#include "cliutils.h"
#include "guiutils.h"

//==============================================================================

#include "ui_cellmlmodelrepositorywindowwindow.h"

//==============================================================================

#include <Qt>

//==============================================================================

#include <QClipboard>
#include <QJsonDocument>
#include <QJsonObject>
#include <QMenu>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QPoint>
#include <QRegularExpression>

//==============================================================================

namespace OpenCOR {
namespace CellMLModelRepositoryWindow {

//==============================================================================

CellmlModelRepositoryWindowWindow::CellmlModelRepositoryWindowWindow(QWidget *pParent) :
    Core::OrganisationWidget(pParent),
    mGui(new Ui::CellmlModelRepositoryWindowWindow),
    mModelListRequested(false)
{
    // Set up the GUI

    mGui->setupUi(this);

#ifdef Q_OS_MAC
    mGui->filterValue->setAttribute(Qt::WA_MacShowFocusRect, false);
    // Note: the above removes the focus border since it messes up the look of
    //       our filter value widget...
#endif

    // Make the name value our focus proxy

    setFocusProxy(mGui->filterValue);

    // Create and add the CellML Model Repository widget

    mCellmlModelRepositoryWidget = new CellmlModelRepositoryWindowWidget(this);

    mGui->dockWidgetContents->layout()->addWidget(mCellmlModelRepositoryWidget);

    // Create and populate our context menu

    mContextMenu = new QMenu(this);

    mContextMenu->addAction(mGui->actionCopy);

    // We want our own context menu for the help widget (indeed, we don't want
    // the default one which has the reload menu item and not the other actions
    // that we have in our tool bar widget, so...)

    mCellmlModelRepositoryWidget->setContextMenuPolicy(Qt::CustomContextMenu);

    connect(mCellmlModelRepositoryWidget, SIGNAL(customContextMenuRequested(const QPoint &)),
            this, SLOT(showCustomContextMenu(const QPoint &)));

    // Keep track of the window's visibility, so that we can request the list of
    // models, if necessary

    connect(this, SIGNAL(visibilityChanged(bool)),
            this, SLOT(retrieveModelList(const bool &)));

    // Create a network access manager so that we can then retrieve a list of
    // CellML models from the CellML Model Repository

    mNetworkAccessManager = new QNetworkAccessManager(this);

    // Make sure that we get told when the download of our Internet file is
    // complete, as well as if there are SSL errors (which would happen if the
    // website's certificate is invalid, e.g. it has expired)

    connect(mNetworkAccessManager, SIGNAL(finished(QNetworkReply *)),
            this, SLOT(finished(QNetworkReply *)) );
    connect(mNetworkAccessManager, SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &)),
            this, SLOT(sslErrors(QNetworkReply *, const QList<QSslError> &)) );

    // Connection to update the enabled state of our copy action

    connect(mCellmlModelRepositoryWidget, SIGNAL(copyTextEnabled(const bool &)),
            mGui->actionCopy, SLOT(setEnabled(bool)));
}

//==============================================================================

CellmlModelRepositoryWindowWindow::~CellmlModelRepositoryWindowWindow()
{
    // Delete the GUI

    delete mGui;
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::retranslateUi()
{
    // Retranslate the whole window

    mGui->retranslateUi(this);

    // Retranslate our list of models

    outputModelList(mModelList);
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::outputModelList(const QStringList &pModelList)
{
    // Output a given list of models

    mModelList = pModelList;

    QString contents = QString();
    static const QString leadingSpaces = "        ";

    if (mModelList.count()) {
        // We have models to list, so...

        if (mModelList.count() == 1)
            contents = tr("1 CellML model was found:")+"\n";
        else
            contents = tr("%1 CellML models were found:").arg(mModelList.count())+"\n";

        contents += leadingSpaces+"
\n";

        foreach (const QString &model, mModelList)
            contents += leadingSpaces+"
"+model+"
\n";

        contents += leadingSpaces+"
";
    } else if (mModelNames.empty()) {
        if (mErrorMessage.count())
            // Something went wrong while trying to retrieve the list of models,
            // so...

            contents = leadingSpaces+tr("Error: ")+Core::formatErrorMessage(mErrorMessage);
    } else {
        // No model could be found, so...

        contents = leadingSpaces+tr("No CellML model matches your criteria");
    }

    // Show/hide our busy widget and output the list matching the search
    // criteria, or a message telling the user what went wrong, if anything and
    // if needed

    if (mModelListRequested) {
        showBusyWidget(mCellmlModelRepositoryWidget);
    } else {
        hideBusyWidget();

        mCellmlModelRepositoryWidget->output(contents);
    }
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::on_filterValue_textChanged(const QString &text)
{
    // Generate a Web page that contains all the models that match our search
    // criteria

    outputModelList(mModelNames.filter(QRegularExpression(text, QRegularExpression::CaseInsensitiveOption)));
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::on_actionCopy_triggered()
{
    // Copy the current selection to the clipboard

    QApplication::clipboard()->setText(mCellmlModelRepositoryWidget->selectedText());
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::on_refreshButton_clicked()
{
    // Output the message telling the user that the list is being downloaded
    // Note: to clear mModelNames ensures that we get the correct message from
    //       outputModelList...

    mModelListRequested = true;

    mModelNames.clear();

    outputModelList(QStringList());

    // Disable the GUI side, so that the user doesn't get confused and ask to
    // refresh over and over again while he should just be patient

    mGui->dockWidgetContents->setEnabled(false);

    // Get the list of CellML models

    mNetworkAccessManager->get(QNetworkRequest(QUrl("https://models.physiomeproject.org/cellml/workspace/rest/contents.json")));
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::finished(QNetworkReply *pNetworkReply)
{
    // Clear some properties

    mModelNames.clear();
    mModelUrls.clear();

    mModelListRequested = false;

    // Output the list of models, should we have retrieved it without any
    // problem

    if (pNetworkReply->error() == QNetworkReply::NoError) {
        // Parse the JSON code

        QJsonParseError jsonParseError;
        QJsonDocument jsonDocument = QJsonDocument::fromJson(pNetworkReply->readAll(), &jsonParseError);

        if (jsonParseError.error == QJsonParseError::NoError) {
            // Retrieve the list of CellML models

            QVariantMap resultMap = jsonDocument.object().toVariantMap();

            foreach (const QVariant &modelVariant, resultMap["values"].toList()) {
                QVariantList modelDetailsVariant = modelVariant.toList();

                mModelNames << modelDetailsVariant[0].toString();
                mModelUrls << modelDetailsVariant[1].toString();
            }

            // Everything went fine, so...

            mErrorMessage = QString();
        } else {
            // Something went wrong, so...

            mErrorMessage = jsonParseError.errorString();
        }
    } else {
        // Something went wrong, so...

        mErrorMessage = pNetworkReply->errorString();
    }

    // Initialise the output using whatever search criteria is present

    on_filterValue_textChanged(mGui->filterValue->text());

    // Re-enable the GUI side

    mGui->dockWidgetContents->setEnabled(true);

    // Give, within the current window, the focus to mGui->filterValue, but
    // only if the current window already has the focus

    Core::setFocusTo(mGui->filterValue);

    // Delete (later) the network reply

    pNetworkReply->deleteLater();
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::sslErrors(QNetworkReply *pNetworkReply,
                                                  const QList<QSslError> &pSslErrors)
{
    // Ignore the SSL errors since we trust the website and therefore its
    // certificate (even if it is invalid, e.g. it has expired)

    pNetworkReply->ignoreSslErrors(pSslErrors);
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::showCustomContextMenu(const QPoint &pPosition) const
{
    Q_UNUSED(pPosition);

    // Show our context menu for our CellML Models Repository widget

    mContextMenu->exec(QCursor::pos());
}

//==============================================================================

void CellmlModelRepositoryWindowWindow::retrieveModelList(const bool &pVisible)
{
    // Retrieve the list of models, if we are becoming visible and the list of
    // models has never been requested before

    static bool firstTime = true;

    if (pVisible && firstTime) {
        firstTime = false;

        on_refreshButton_clicked();
    }
}

//==============================================================================

}   // namespace CellMLModelRepositoryWindow
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>/**
  MQTT433gateway - MQTT 433.92 MHz radio gateway utilizing ESPiLight
  Project home: https://github.com/puuu/MQTT433gateway/

  The MIT License (MIT)

  Copyright (c) 2017 Jan Losinski

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation files
  (the "Software"), to deal in the Software without restriction,
  including without limitation the rights to use, copy, modify, merge,
  publish, distribute, sublicense, and/or sell copies of the Software,
  and to permit persons to whom the Software is furnished to do so,
  subject to the following conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

#include <ArduinoSimpleLogging.h>

#include <Version.h>

#include "MqttClient.h"

class PayloadString : public String {
 public:
  PayloadString(const uint8_t *data, unsigned int length) : String() {
    if (reserve(length)) {
      memcpy(buffer, data, length);
      len = length;
    }
  }
};

MqttClient::MqttClient(const Settings &settings, WiFiClient &client)
    : settings(settings), mqttClient(client), lastConnectAttempt(0) {}

MqttClient::~MqttClient() { mqttClient.disconnect(); }

void MqttClient::begin() {
  using namespace std::placeholders;
  mqttClient.setServer(settings.mqttBroker.c_str(), settings.mqttBrokerPort);

  mqttClient.setCallback(std::bind(&MqttClient::onMessage, this, _1, _2, _3));

  reconnect();
}

static String stateMessage(const bool online) {
  return String(F("{\"chipId\":\"")) + chipId() +
         String(F("\",\"firmware\":\"")) + fwVersion() + F("\",\"state\":\"") +
         String(online ? F("online") : F("offline")) + String(F("\"}"));
}

bool MqttClient::connect() {
  if (0 == settings.mqttUser.length()) {
    return mqttClient.connect(settings.deviceName.c_str(),
                              settings.mqttStateTopic.c_str(), 0, true,
                              stateMessage(false).c_str());
  } else {
    return mqttClient.connect(
        settings.deviceName.c_str(), settings.mqttUser.c_str(),
        settings.mqttPassword.c_str(), settings.mqttStateTopic.c_str(), 0, true,
        stateMessage(false).c_str());
  }
}

void MqttClient::reconnect() {
  if (lastConnectAttempt > 0 &&
      (millis() - lastConnectAttempt) < MQTT_CONNECTION_ATTEMPT_DELAY) {
    return;
  }

  if (!mqttClient.connected()) {
    Logger.debug.println(F("Try to (re)connect to MQTT broker"));
    if (connect()) {
      Logger.info.println(F("MQTT connected."));
      if (subsrcibe()) {
        mqttClient.publish(settings.mqttStateTopic.c_str(),
                           stateMessage(true).c_str(), true);
        mqttClient.publish(settings.mqttVersionTopic.c_str(),
                           fwJsonVersion(false).c_str(), true);
        Logger.info.println(F("MQTT subscribed."));
      } else {
        Logger.error.println(F("MQTT subsrcibe failed!"));
      }
    } else {
      Logger.error.println(F("MQTT connect failed!"));
    }
  }

  lastConnectAttempt = millis();
}

bool MqttClient::subsrcibe() {
  String topic = settings.mqttSendTopic + "+";

  Logger.debug.print(F("MQTT subscribe to topic: "));
  Logger.debug.println(topic);

  return mqttClient.subscribe(topic.c_str());
}

void MqttClient::loop() {
  reconnect();
  mqttClient.loop();
}

void MqttClient::onMessage(char *topic, uint8_t *payload, unsigned int length) {
  PayloadString strPayload(payload, length);
  String strTopic(topic);

  Logger.debug.print(F("New MQTT message: "));
  Logger.debug.print(strTopic);
  Logger.debug.print(F(" .. "));
  Logger.debug.println(strPayload);

  if (strTopic.startsWith(settings.mqttSendTopic)) {
    if (onRfDataCallback) {
      onRfDataCallback(String(topic + settings.mqttSendTopic.length()),
                       strPayload);
    }
  }
}

void MqttClient::registerRfDataHandler(const MqttClient::RfDataCb &cb) {
  onRfDataCallback = cb;
}

void MqttClient::publishCode(const String &protocol, const String &payload) {
  String topic = settings.mqttReceiveTopic + protocol;

  Logger.debug.print(F("Publish MQTT message: "));
  Logger.debug.print(topic);
  Logger.debug.print(F(" retain="));
  Logger.debug.print(settings.mqttRetain);
  Logger.debug.print(F(" .. "));
  Logger.debug.println(payload);
  mqttClient.publish(topic.c_str(), payload.c_str(), settings.mqttRetain);
}

bool MqttClient::isConnected() { return mqttClient.connected(); }
<commit_msg>lib/MQTT/MqttClient.cpp: let state topic only be 'online' or 'offline'<commit_after>/**
  MQTT433gateway - MQTT 433.92 MHz radio gateway utilizing ESPiLight
  Project home: https://github.com/puuu/MQTT433gateway/

  The MIT License (MIT)

  Copyright (c) 2017 Jan Losinski

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation files
  (the "Software"), to deal in the Software without restriction,
  including without limitation the rights to use, copy, modify, merge,
  publish, distribute, sublicense, and/or sell copies of the Software,
  and to permit persons to whom the Software is furnished to do so,
  subject to the following conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

#include <ArduinoSimpleLogging.h>

#include <Version.h>

#include "MqttClient.h"

class PayloadString : public String {
 public:
  PayloadString(const uint8_t *data, unsigned int length) : String() {
    if (reserve(length)) {
      memcpy(buffer, data, length);
      len = length;
    }
  }
};

MqttClient::MqttClient(const Settings &settings, WiFiClient &client)
    : settings(settings), mqttClient(client), lastConnectAttempt(0) {}

MqttClient::~MqttClient() { mqttClient.disconnect(); }

void MqttClient::begin() {
  using namespace std::placeholders;
  mqttClient.setServer(settings.mqttBroker.c_str(), settings.mqttBrokerPort);

  mqttClient.setCallback(std::bind(&MqttClient::onMessage, this, _1, _2, _3));

  reconnect();
}

static String stateMessage(const bool online) {
  return String(online ? F("online") : F("offline"));
}

bool MqttClient::connect() {
  if (0 == settings.mqttUser.length()) {
    return mqttClient.connect(settings.deviceName.c_str(),
                              settings.mqttStateTopic.c_str(), 0, true,
                              stateMessage(false).c_str());
  } else {
    return mqttClient.connect(
        settings.deviceName.c_str(), settings.mqttUser.c_str(),
        settings.mqttPassword.c_str(), settings.mqttStateTopic.c_str(), 0, true,
        stateMessage(false).c_str());
  }
}

void MqttClient::reconnect() {
  if (lastConnectAttempt > 0 &&
      (millis() - lastConnectAttempt) < MQTT_CONNECTION_ATTEMPT_DELAY) {
    return;
  }

  if (!mqttClient.connected()) {
    Logger.debug.println(F("Try to (re)connect to MQTT broker"));
    if (connect()) {
      Logger.info.println(F("MQTT connected."));
      if (subsrcibe()) {
        mqttClient.publish(settings.mqttStateTopic.c_str(),
                           stateMessage(true).c_str(), true);
        mqttClient.publish(settings.mqttVersionTopic.c_str(),
                           fwJsonVersion(false).c_str(), true);
        Logger.info.println(F("MQTT subscribed."));
      } else {
        Logger.error.println(F("MQTT subsrcibe failed!"));
      }
    } else {
      Logger.error.println(F("MQTT connect failed!"));
    }
  }

  lastConnectAttempt = millis();
}

bool MqttClient::subsrcibe() {
  String topic = settings.mqttSendTopic + "+";

  Logger.debug.print(F("MQTT subscribe to topic: "));
  Logger.debug.println(topic);

  return mqttClient.subscribe(topic.c_str());
}

void MqttClient::loop() {
  reconnect();
  mqttClient.loop();
}

void MqttClient::onMessage(char *topic, uint8_t *payload, unsigned int length) {
  PayloadString strPayload(payload, length);
  String strTopic(topic);

  Logger.debug.print(F("New MQTT message: "));
  Logger.debug.print(strTopic);
  Logger.debug.print(F(" .. "));
  Logger.debug.println(strPayload);

  if (strTopic.startsWith(settings.mqttSendTopic)) {
    if (onRfDataCallback) {
      onRfDataCallback(String(topic + settings.mqttSendTopic.length()),
                       strPayload);
    }
  }
}

void MqttClient::registerRfDataHandler(const MqttClient::RfDataCb &cb) {
  onRfDataCallback = cb;
}

void MqttClient::publishCode(const String &protocol, const String &payload) {
  String topic = settings.mqttReceiveTopic + protocol;

  Logger.debug.print(F("Publish MQTT message: "));
  Logger.debug.print(topic);
  Logger.debug.print(F(" retain="));
  Logger.debug.print(settings.mqttRetain);
  Logger.debug.print(F(" .. "));
  Logger.debug.println(payload);
  mqttClient.publish(topic.c_str(), payload.c_str(), settings.mqttRetain);
}

bool MqttClient::isConnected() { return mqttClient.connected(); }
<|endoftext|>


<commit_before>//===------------------------- MemLocation.cpp ----------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sil-memlocation"
#include "swift/SIL/MemLocation.h"
#include "llvm/Support/Debug.h"

using namespace swift;

//===----------------------------------------------------------------------===//
//                              Utility Functions
//===----------------------------------------------------------------------===//

static inline void removeMemLocations(MemLocationValueMap &Values,
                                      MemLocationList &FirstLevel) {
  for (auto &X : FirstLevel)
    Values.erase(X);
}

//===----------------------------------------------------------------------===//
//                              SILValue Projection
//===----------------------------------------------------------------------===//

void SILValueProjection::print() const {
  llvm::outs() << Base;
  llvm::outs() << Path.getValue();
}

//===----------------------------------------------------------------------===//
//                              Load Store Value
//===----------------------------------------------------------------------===//

SILValue LoadStoreValue::createExtract(SILValue Base,
                                       Optional<ProjectionPath> &Path,
                                       SILInstruction *Inst) {
  // If we found a projection path, but there are no projections, then the two
  // loads must be the same, return PrevLI.
  if (!Path || Path->empty())
    return Base;

  // Ok, at this point we know that we can construct our aggregate projections
  // from our list of address projections.
  SILValue LastExtract = Base;
  SILBuilder Builder(Inst);

  // Construct the path!
  for (auto PI = Path->rbegin(), PE = Path->rend(); PI != PE; ++PI) {
    LastExtract =
        PI->createValueProjection(Builder, Inst->getLoc(), LastExtract).get();
    continue;
  }
  // Return the last extract we created.
  return LastExtract;
}

//===----------------------------------------------------------------------===//
//                                  Memory Location
//===----------------------------------------------------------------------===//

void MemLocation::initialize(SILValue Dest) {
  Base = getUnderlyingObject(Dest);
  Path = ProjectionPath::getAddrProjectionPath(Base, Dest);
}

bool MemLocation::isMustAliasMemLocation(const MemLocation &RHS,
                                         AliasAnalysis *AA) {
  // If the bases are not must-alias, the locations may not alias.
  if (!AA->isMustAlias(Base, RHS.getBase()))
    return false;
  // If projection paths are different, then the locations can not alias.
  if (!hasIdenticalProjectionPath(RHS))
    return false;
  return true;
}

bool MemLocation::isMayAliasMemLocation(const MemLocation &RHS,
                                        AliasAnalysis *AA) {
  // If the bases do not alias, then the locations can not alias.
  if (AA->isNoAlias(Base, RHS.getBase()))
    return false;
  // If one projection path is a prefix of another, then the locations
  // could alias.
  if (hasNonEmptySymmetricPathDifference(RHS))
    return false;
  return true;
}

MemLocation MemLocation::createMemLocation(SILValue Base, ProjectionPath &P1,
                                           ProjectionPath &P2) {
  ProjectionPath T;
  T.append(P1);
  T.append(P2);
  return MemLocation(Base, T);
}

void MemLocation::getFirstLevelMemLocations(MemLocationList &Locs,
                                            SILModule *Mod) {
  SILType Ty = getType();
  llvm::SmallVector<Projection, 8> Out;
  Projection::getFirstLevelAddrProjections(Ty, *Mod, Out);
  for (auto &X : Out) {
    ProjectionPath P;
    P.append(X);
    P.append(Path.getValue());
    Locs.push_back(MemLocation(Base, P));
  }
}

void MemLocation::expand(MemLocation &Base, SILModule *Mod,
                         MemLocationList &Locs,
                         TypeExpansionMap &TypeExpansionVault) {
  // To expand a memory location to its indivisible parts, we first get the
  // address projection paths from the accessed type to each indivisible field,
  // i.e. leaf nodes, then we append these projection paths to the Base.
  //
  // NOTE: we get the address projection because the Base memory location is
  // initialized with address projection paths. By keeping it consistent makes
  // it easier to implement the getType function for MemLocation.
  //
  SILType BaseType = Base.getType();
  if (TypeExpansionVault.find(BaseType) == TypeExpansionVault.end()) {
    // There is no cached expansion for this type, build and cache it now.
    ProjectionPathList Paths;
    ProjectionPath::expandTypeIntoLeafProjectionPaths(BaseType, Mod, Paths,
                                                      true);
    for (auto &X : Paths) {
      TypeExpansionVault[Base.getType()].push_back(std::move(X.getValue()));
    }
  }

  // Construct the MemLocation by appending the projection path from the
  // accessed node to the leaf nodes.
  for (auto &X : TypeExpansionVault[BaseType]) {
    Locs.push_back(MemLocation::createMemLocation(Base.getBase(), X.getValue(),
                                                  Base.getPath().getValue()));
  }
}

void MemLocation::reduce(MemLocation &Base, SILModule *Mod,
                         MemLocationSet &Locs) {
  // First, construct the MemLocation by appending the projection path from the
  // accessed node to the leaf nodes.
  MemLocationList ALocs;
  ProjectionPathList Paths;
  ProjectionPath::expandTypeIntoLeafProjectionPaths(Base.getType(), Mod, Paths,
                                                    false);
  for (auto &X : Paths) {
    ALocs.push_back(MemLocation::createMemLocation(Base.getBase(), X.getValue(),
                                                   Base.getPath().getValue()));
  }

  // Second, go from leaf nodes to their parents. This guarantees that at the
  // point the parent is processed, its children have been processed already.
  for (auto I = ALocs.rbegin(), E = ALocs.rend(); I != E; ++I) {
    MemLocationList FirstLevel;
    I->getFirstLevelMemLocations(FirstLevel, Mod);
    // Reached the end of the projection tree, this is a leaf node.
    if (FirstLevel.empty())
      continue;

    // If this is a class reference type, we have reached end of the type tree.
    if (I->getType().getClassOrBoundGenericClass())
      continue;

    // This is NOT a leaf node, check whether all its first level children are
    // alive.
    bool Alive = true;
    for (auto &X : FirstLevel) {
      if (Locs.find(X) != Locs.end())
        continue;
      Alive = false;
    }

    // All first level locations are alive, create the new aggregated location.
    if (Alive) {
      for (auto &X : FirstLevel)
        Locs.erase(X);
      Locs.insert(*I);
    }
  }
}

void MemLocation::expandWithValues(MemLocation &Base, SILValue &Val,
                                   SILModule *Mod, MemLocationList &Locs,
                                   LoadStoreValueList &Vals) {
  // To expand a memory location to its indivisible parts, we first get the
  // projection paths from the accessed type to each indivisible field, i.e.
  // leaf nodes, then we append these projection paths to the Base.
  ProjectionPathList Paths;
  ProjectionPath::expandTypeIntoLeafProjectionPaths(Base.getType(), Mod, Paths,
                                                    true);

  // Construct the MemLocation and LoadStoreValues by appending the projection
  // path
  // from the accessed node to the leaf nodes.
  for (auto &X : Paths) {
    Locs.push_back(MemLocation::createMemLocation(Base.getBase(), X.getValue(),
                                                  Base.getPath().getValue()));
    Vals.push_back(LoadStoreValue(Val, X.getValue()));
  }
}

SILValue MemLocation::reduceWithValues(MemLocation &Base, SILModule *Mod,
                                       MemLocationValueMap &Values,
                                       SILInstruction *InsertPt) {
  // Walk bottom up the projection tree, try to reason about how to construct
  // a single SILValue out of all the available values for all the memory
  // locations.
  //
  // First, get a list of all the leaf nodes and intermediate nodes for the
  // Base memory location.
  MemLocationList ALocs;
  ProjectionPathList Paths;
  ProjectionPath::expandTypeIntoLeafProjectionPaths(Base.getType(), Mod, Paths,
                                                    false);
  for (auto &X : Paths) {
    ALocs.push_back(MemLocation::createMemLocation(Base.getBase(), X.getValue(),
                                                   Base.getPath().getValue()));
  }

  // Second, go from leaf nodes to their parents. This guarantees that at the
  // point the parent is processed, its children have been processed already.
  for (auto I = ALocs.rbegin(), E = ALocs.rend(); I != E; ++I) {
    // This is a leaf node, we have a value for it.
    //
    // Reached the end of the projection tree, this is a leaf node.
    MemLocationList FirstLevel;
    I->getFirstLevelMemLocations(FirstLevel, Mod);
    if (FirstLevel.empty())
      continue;

    // If this is a class reference type, we have reached end of the type tree.
    if (I->getType().getClassOrBoundGenericClass())
      continue;

    // This is NOT a leaf node, we need to construct a value for it.
    //
    // If there are more than 1 children and all the children nodes have
    // LoadStoreValues with the same base. we can get away by not extracting
    // value
    // for every single field.
    //
    // Simply create a new node with all the aggregated base value, i.e.
    // stripping off the last level projection.
    //
    bool HasIdenticalValueBase = true;
    auto Iter = FirstLevel.begin();
    LoadStoreValue &FirstVal = Values[*Iter];
    SILValue FirstBase = FirstVal.getBase();
    Iter = std::next(Iter);
    for (auto EndIter = FirstLevel.end(); Iter != EndIter; ++Iter) {
      LoadStoreValue &V = Values[*Iter];
      HasIdenticalValueBase &= (FirstBase == V.getBase());
    }

    if (HasIdenticalValueBase &&
        (FirstLevel.size() > 1 || !FirstVal.hasEmptyProjectionPath())) {
      Values[*I] = FirstVal.stripLastLevelProjection();
      // We have a value for the parent, remove all the values for children.
      removeMemLocations(Values, FirstLevel);
      continue;
    }

    // In 2 cases do we need aggregation.
    //
    // 1. If there is only 1 child and we can not strip off any projections,
    // that means we need to create an aggregation.
    //
    // 2. Children have values from different bases, We need to create
    // extractions and aggregation in this case.
    //
    llvm::SmallVector<SILValue, 8> Vals;
    for (auto &X : FirstLevel) {
      Vals.push_back(Values[X].materialize(InsertPt));
    }
    SILBuilder Builder(InsertPt);
    NullablePtr<swift::SILInstruction> AI =
        Projection::createAggFromFirstLevelProjections(
            Builder, InsertPt->getLoc(), I->getType(), Vals);
    // This is the Value for the current node.
    ProjectionPath P;
    Values[*I] = LoadStoreValue(SILValue(AI.get()), P);
    removeMemLocations(Values, FirstLevel);

    // Keep iterating until we have reach the top-most level of the projection
    // tree.
    // i.e. the memory location represented by the Base.
  }

  assert(Values.size() == 1 && "Should have a single location this point");

  // Finally materialize and return the forwarding SILValue.
  return Values.begin()->second.materialize(InsertPt);
}

void MemLocation::enumerateMemLocation(SILModule *M, SILValue Mem,
                                       std::vector<MemLocation> &LV,
                                       MemLocationIndexMap &BM,
                                       TypeExpansionMap &TV) {
  // Construct a Location to represent the memory written by this instruction.
  MemLocation L(Mem);

  // If we cant figure out the Base or Projection Path for the memory location,
  // simply ignore it for now.
  if (!L.isValid())
    return;

  // Expand the given Mem into individual fields and add them to the
  // locationvault.
  MemLocationList Locs;
  MemLocation::expand(L, M, Locs, TV);
  for (auto &Loc : Locs) {
    BM[Loc] = LV.size();
    LV.push_back(Loc);
  }
}

void MemLocation::enumerateMemLocations(SILFunction &F,
                                        std::vector<MemLocation> &LV,
                                        MemLocationIndexMap &BM,
                                        TypeExpansionMap &TV) {
  // Enumerate all locations accessed by the loads or stores.
  //
  // TODO: process more instructions as we process more instructions in
  // processInstruction.
  //
  SILValue Op;
  for (auto &B : F) {
    for (auto &I : B) {
      if (auto *LI = dyn_cast<LoadInst>(&I)) {
        enumerateMemLocation(&I.getModule(), LI->getOperand(), LV, BM, TV);
      } else if (auto *SI = dyn_cast<StoreInst>(&I)) {
        enumerateMemLocation(&I.getModule(), SI->getDest(), LV, BM, TV);
      }
    }
  }
}
<commit_msg>Refactor in MemLocation. NFC<commit_after>//===------------------------- MemLocation.cpp ----------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sil-memlocation"
#include "swift/SIL/MemLocation.h"
#include "llvm/Support/Debug.h"

using namespace swift;

//===----------------------------------------------------------------------===//
//                              Utility Functions
//===----------------------------------------------------------------------===//

static inline void removeMemLocations(MemLocationValueMap &Values,
                                      MemLocationList &FirstLevel) {
  for (auto &X : FirstLevel)
    Values.erase(X);
}

//===----------------------------------------------------------------------===//
//                              SILValue Projection
//===----------------------------------------------------------------------===//

void SILValueProjection::print() const {
  llvm::outs() << Base;
  llvm::outs() << Path.getValue();
}

//===----------------------------------------------------------------------===//
//                              Load Store Value
//===----------------------------------------------------------------------===//

SILValue LoadStoreValue::createExtract(SILValue Base,
                                       Optional<ProjectionPath> &Path,
                                       SILInstruction *Inst) {
  // If we found a projection path, but there are no projections, then the two
  // loads must be the same, return PrevLI.
  if (!Path || Path->empty())
    return Base;

  // Ok, at this point we know that we can construct our aggregate projections
  // from our list of address projections.
  SILValue LastExtract = Base;
  SILBuilder Builder(Inst);

  // Construct the path!
  for (auto PI = Path->rbegin(), PE = Path->rend(); PI != PE; ++PI) {
    LastExtract =
        PI->createValueProjection(Builder, Inst->getLoc(), LastExtract).get();
    continue;
  }
  // Return the last extract we created.
  return LastExtract;
}

//===----------------------------------------------------------------------===//
//                                  Memory Location
//===----------------------------------------------------------------------===//

void MemLocation::initialize(SILValue Dest) {
  Base = getUnderlyingObject(Dest);
  Path = ProjectionPath::getAddrProjectionPath(Base, Dest);
}

bool MemLocation::isMustAliasMemLocation(const MemLocation &RHS,
                                         AliasAnalysis *AA) {
  // If the bases are not must-alias, the locations may not alias.
  if (!AA->isMustAlias(Base, RHS.getBase()))
    return false;
  // If projection paths are different, then the locations can not alias.
  if (!hasIdenticalProjectionPath(RHS))
    return false;
  return true;
}

bool MemLocation::isMayAliasMemLocation(const MemLocation &RHS,
                                        AliasAnalysis *AA) {
  // If the bases do not alias, then the locations can not alias.
  if (AA->isNoAlias(Base, RHS.getBase()))
    return false;
  // If one projection path is a prefix of another, then the locations
  // could alias.
  if (hasNonEmptySymmetricPathDifference(RHS))
    return false;
  return true;
}

MemLocation MemLocation::createMemLocation(SILValue Base, ProjectionPath &P1,
                                           ProjectionPath &P2) {
  ProjectionPath T;
  T.append(P1);
  T.append(P2);
  return MemLocation(Base, T);
}

void MemLocation::getFirstLevelMemLocations(MemLocationList &Locs,
                                            SILModule *Mod) {
  SILType Ty = getType();
  llvm::SmallVector<Projection, 8> Out;
  Projection::getFirstLevelAddrProjections(Ty, *Mod, Out);
  for (auto &X : Out) {
    ProjectionPath P;
    P.append(X);
    P.append(Path.getValue());
    Locs.push_back(MemLocation(Base, P));
  }
}

void MemLocation::expand(MemLocation &Base, SILModule *Mod,
                         MemLocationList &Locs,
                         TypeExpansionMap &TECache) {
  // To expand a memory location to its indivisible parts, we first get the
  // address projection paths from the accessed type to each indivisible field,
  // i.e. leaf nodes, then we append these projection paths to the Base.
  //
  // NOTE: we get the address projection because the Base memory location is
  // initialized with address projection paths. By keeping it consistent makes
  // it easier to implement the getType function for MemLocation.
  //
  SILType BaseType = Base.getType();
  if (TECache.find(BaseType) == TECache.end()) {
    // There is no cached expansion for this type, build and cache it now.
    ProjectionPathList Paths;
    ProjectionPath::expandTypeIntoLeafProjectionPaths(BaseType, Mod, Paths,
                                                      true);
    for (auto &P : Paths) {
      TECache[BaseType()].push_back(std::move(P.getValue()));
    }
  }

  // Construct the MemLocation by appending the projection path from the
  // accessed node to the leaf nodes.
  for (auto &X : TECache[BaseType]) {
    Locs.push_back(MemLocation::createMemLocation(Base.getBase(), X.getValue(),
                                                  Base.getPath().getValue()));
  }
}

void MemLocation::reduce(MemLocation &Base, SILModule *Mod,
                         MemLocationSet &Locs) {
  // First, construct the MemLocation by appending the projection path from the
  // accessed node to the leaf nodes.
  MemLocationList ALocs;
  ProjectionPathList Paths;
  ProjectionPath::expandTypeIntoLeafProjectionPaths(Base.getType(), Mod, Paths,
                                                    false);
  for (auto &X : Paths) {
    ALocs.push_back(MemLocation::createMemLocation(Base.getBase(), X.getValue(),
                                                   Base.getPath().getValue()));
  }

  // Second, go from leaf nodes to their parents. This guarantees that at the
  // point the parent is processed, its children have been processed already.
  for (auto I = ALocs.rbegin(), E = ALocs.rend(); I != E; ++I) {
    MemLocationList FirstLevel;
    I->getFirstLevelMemLocations(FirstLevel, Mod);
    // Reached the end of the projection tree, this is a leaf node.
    if (FirstLevel.empty())
      continue;

    // If this is a class reference type, we have reached end of the type tree.
    if (I->getType().getClassOrBoundGenericClass())
      continue;

    // This is NOT a leaf node, check whether all its first level children are
    // alive.
    bool Alive = true;
    for (auto &X : FirstLevel) {
      if (Locs.find(X) != Locs.end())
        continue;
      Alive = false;
    }

    // All first level locations are alive, create the new aggregated location.
    if (Alive) {
      for (auto &X : FirstLevel)
        Locs.erase(X);
      Locs.insert(*I);
    }
  }
}

void MemLocation::expandWithValues(MemLocation &Base, SILValue &Val,
                                   SILModule *Mod, MemLocationList &Locs,
                                   LoadStoreValueList &Vals) {
  // To expand a memory location to its indivisible parts, we first get the
  // projection paths from the accessed type to each indivisible field, i.e.
  // leaf nodes, then we append these projection paths to the Base.
  ProjectionPathList Paths;
  ProjectionPath::expandTypeIntoLeafProjectionPaths(Base.getType(), Mod, Paths,
                                                    true);

  // Construct the MemLocation and LoadStoreValues by appending the projection
  // path
  // from the accessed node to the leaf nodes.
  for (auto &X : Paths) {
    Locs.push_back(MemLocation::createMemLocation(Base.getBase(), X.getValue(),
                                                  Base.getPath().getValue()));
    Vals.push_back(LoadStoreValue(Val, X.getValue()));
  }
}

SILValue MemLocation::reduceWithValues(MemLocation &Base, SILModule *Mod,
                                       MemLocationValueMap &Values,
                                       SILInstruction *InsertPt) {
  // Walk bottom up the projection tree, try to reason about how to construct
  // a single SILValue out of all the available values for all the memory
  // locations.
  //
  // First, get a list of all the leaf nodes and intermediate nodes for the
  // Base memory location.
  MemLocationList ALocs;
  ProjectionPathList Paths;
  ProjectionPath::expandTypeIntoLeafProjectionPaths(Base.getType(), Mod, Paths,
                                                    false);
  for (auto &X : Paths) {
    ALocs.push_back(MemLocation::createMemLocation(Base.getBase(), X.getValue(),
                                                   Base.getPath().getValue()));
  }

  // Second, go from leaf nodes to their parents. This guarantees that at the
  // point the parent is processed, its children have been processed already.
  for (auto I = ALocs.rbegin(), E = ALocs.rend(); I != E; ++I) {
    // This is a leaf node, we have a value for it.
    //
    // Reached the end of the projection tree, this is a leaf node.
    MemLocationList FirstLevel;
    I->getFirstLevelMemLocations(FirstLevel, Mod);
    if (FirstLevel.empty())
      continue;

    // If this is a class reference type, we have reached end of the type tree.
    if (I->getType().getClassOrBoundGenericClass())
      continue;

    // This is NOT a leaf node, we need to construct a value for it.
    //
    // If there are more than 1 children and all the children nodes have
    // LoadStoreValues with the same base. we can get away by not extracting
    // value
    // for every single field.
    //
    // Simply create a new node with all the aggregated base value, i.e.
    // stripping off the last level projection.
    //
    bool HasIdenticalValueBase = true;
    auto Iter = FirstLevel.begin();
    LoadStoreValue &FirstVal = Values[*Iter];
    SILValue FirstBase = FirstVal.getBase();
    Iter = std::next(Iter);
    for (auto EndIter = FirstLevel.end(); Iter != EndIter; ++Iter) {
      LoadStoreValue &V = Values[*Iter];
      HasIdenticalValueBase &= (FirstBase == V.getBase());
    }

    if (HasIdenticalValueBase &&
        (FirstLevel.size() > 1 || !FirstVal.hasEmptyProjectionPath())) {
      Values[*I] = FirstVal.stripLastLevelProjection();
      // We have a value for the parent, remove all the values for children.
      removeMemLocations(Values, FirstLevel);
      continue;
    }

    // In 2 cases do we need aggregation.
    //
    // 1. If there is only 1 child and we can not strip off any projections,
    // that means we need to create an aggregation.
    //
    // 2. Children have values from different bases, We need to create
    // extractions and aggregation in this case.
    //
    llvm::SmallVector<SILValue, 8> Vals;
    for (auto &X : FirstLevel) {
      Vals.push_back(Values[X].materialize(InsertPt));
    }
    SILBuilder Builder(InsertPt);
    NullablePtr<swift::SILInstruction> AI =
        Projection::createAggFromFirstLevelProjections(
            Builder, InsertPt->getLoc(), I->getType(), Vals);
    // This is the Value for the current node.
    ProjectionPath P;
    Values[*I] = LoadStoreValue(SILValue(AI.get()), P);
    removeMemLocations(Values, FirstLevel);

    // Keep iterating until we have reach the top-most level of the projection
    // tree.
    // i.e. the memory location represented by the Base.
  }

  assert(Values.size() == 1 && "Should have a single location this point");

  // Finally materialize and return the forwarding SILValue.
  return Values.begin()->second.materialize(InsertPt);
}

void MemLocation::enumerateMemLocation(SILModule *M, SILValue Mem,
                                       std::vector<MemLocation> &LV,
                                       MemLocationIndexMap &BM,
                                       TypeExpansionMap &TV) {
  // Construct a Location to represent the memory written by this instruction.
  MemLocation L(Mem);

  // If we cant figure out the Base or Projection Path for the memory location,
  // simply ignore it for now.
  if (!L.isValid())
    return;

  // Expand the given Mem into individual fields and add them to the
  // locationvault.
  MemLocationList Locs;
  MemLocation::expand(L, M, Locs, TV);
  for (auto &Loc : Locs) {
    BM[Loc] = LV.size();
    LV.push_back(Loc);
  }
}

void MemLocation::enumerateMemLocations(SILFunction &F,
                                        std::vector<MemLocation> &LV,
                                        MemLocationIndexMap &BM,
                                        TypeExpansionMap &TV) {
  // Enumerate all locations accessed by the loads or stores.
  //
  // TODO: process more instructions as we process more instructions in
  // processInstruction.
  //
  SILValue Op;
  for (auto &B : F) {
    for (auto &I : B) {
      if (auto *LI = dyn_cast<LoadInst>(&I)) {
        enumerateMemLocation(&I.getModule(), LI->getOperand(), LV, BM, TV);
      } else if (auto *SI = dyn_cast<StoreInst>(&I)) {
        enumerateMemLocation(&I.getModule(), SI->getDest(), LV, BM, TV);
      }
    }
  }
}
<|endoftext|>