<commit_before>/*
 * Copyright 2018 Google Inc.
 *
 * 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 "blob_handler.hpp"

#include "blob_errors.hpp"
#include "crc.hpp"
#include "ipmi_errors.hpp"

#include <array>
#include <cstring>

namespace ipmiblob
{

namespace
{
const std::array<std::uint8_t, 3> ipmiPhosphorOen = {0xcf, 0xc2, 0x00};
}

std::vector<std::uint8_t>
    BlobHandler::sendIpmiPayload(BlobOEMCommands command,
                                 const std::vector<std::uint8_t>& payload)
{
    std::vector<std::uint8_t> request, reply, bytes;

    std::copy(ipmiPhosphorOen.begin(), ipmiPhosphorOen.end(),
              std::back_inserter(request));
    request.push_back(command);

    if (payload.size() > 0)
    {
        /* Grow the vector to hold the bytes. */
        request.reserve(request.size() + sizeof(std::uint16_t));

        /* CRC required. */
        std::uint16_t crc = generateCrc(payload);
        auto src = reinterpret_cast<const std::uint8_t*>(&crc);

        std::copy(src, src + sizeof(crc), std::back_inserter(request));

        /* Copy the payload. */
        std::copy(payload.begin(), payload.end(), std::back_inserter(request));
    }

    try
    {
        reply = ipmi->sendPacket(request);
    }
    catch (const IpmiException& e)
    {
        throw BlobException(e.what());
    }

    /* IPMI_CC was OK, and it returned no bytes, so let's be happy with that for
     * now.
     */
    if (reply.size() == 0)
    {
        return reply;
    }

    /* This cannot be a response because it's smaller than the smallest
     * response.
     */
    if (reply.size() < ipmiPhosphorOen.size())
    {
        throw BlobException("Invalid response length");
    }

    /* Verify the OEN. */
    if (std::memcmp(ipmiPhosphorOen.data(), reply.data(),
                    ipmiPhosphorOen.size()) != 0)
    {
        throw BlobException("Invalid OEN received");
    }

    /* In this case there was no data, as there was no CRC. */
    std::size_t headerSize = ipmiPhosphorOen.size() + sizeof(std::uint16_t);
    if (reply.size() < headerSize)
    {
        return {};
    }

    /* Validate CRC. */
    std::uint16_t crc;
    auto ptr = reinterpret_cast<std::uint8_t*>(&crc);
    std::memcpy(ptr, &reply[ipmiPhosphorOen.size()], sizeof(crc));

    for (const auto& byte : reply)
    {
        std::fprintf(stderr, "0x%02x ", byte);
    }
    std::fprintf(stderr, "\n");

    bytes.insert(bytes.begin(), reply.begin() + headerSize, reply.end());

    auto computed = generateCrc(bytes);
    if (crc != computed)
    {
        std::fprintf(stderr, "Invalid CRC, received: 0x%x, computed: 0x%x\n",
                     crc, computed);
        throw BlobException("Invalid CRC on received data.");
    }

    return bytes;
}

int BlobHandler::getBlobCount()
{
    std::uint32_t count;
    try
    {
        auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobGetCount, {});
        if (resp.size() != sizeof(count))
        {
            return 0;
        }

        /* LE to LE (need to make this portable as some point. */
        std::memcpy(&count, resp.data(), sizeof(count));
    }
    catch (const BlobException& b)
    {
        return 0;
    }

    std::fprintf(stderr, "BLOB Count: %d\n", count);
    return count;
}

std::string BlobHandler::enumerateBlob(std::uint32_t index)
{
    std::vector<std::uint8_t> payload;
    auto data = reinterpret_cast<const std::uint8_t*>(&index);

    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));

    try
    {
        auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload);
        return (resp.size() > 0) ? std::string(&resp[0], &resp[resp.size() - 1])
                                 : "";
    }
    catch (const BlobException& b)
    {
        return "";
    }
}

void BlobHandler::writeGeneric(BlobOEMCommands command, std::uint16_t session,
                               std::uint32_t offset,
                               const std::vector<std::uint8_t>& bytes)
{
    std::vector<std::uint8_t> payload;

    payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) +
                    bytes.size());

    auto data = reinterpret_cast<const std::uint8_t*>(&session);
    std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));

    data = reinterpret_cast<const std::uint8_t*>(&offset);
    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));

    std::copy(bytes.begin(), bytes.end(), std::back_inserter(payload));

    auto resp = sendIpmiPayload(command, payload);
}

void BlobHandler::writeMeta(std::uint16_t session, std::uint32_t offset,
                            const std::vector<std::uint8_t>& bytes)
{
    return writeGeneric(BlobOEMCommands::bmcBlobWriteMeta, session, offset,
                        bytes);
}

void BlobHandler::writeBytes(std::uint16_t session, std::uint32_t offset,
                             const std::vector<std::uint8_t>& bytes)
{
    return writeGeneric(BlobOEMCommands::bmcBlobWrite, session, offset, bytes);
}

std::vector<std::string> BlobHandler::getBlobList()
{
    std::vector<std::string> list;
    int blobCount = getBlobCount();

    for (int i = 0; i < blobCount; i++)
    {
        auto name = enumerateBlob(i);
        /* Currently ignore failures. */
        if (!name.empty())
        {
            list.push_back(name);
        }
    }

    return list;
}

StatResponse BlobHandler::getStat(const std::string& id)
{
    StatResponse meta;
    std::vector<std::uint8_t> name, resp;

    std::copy(id.begin(), id.end(), std::back_inserter(name));
    name.push_back(0x00); /* need to add nul-terminator. */

    try
    {
        resp = sendIpmiPayload(BlobOEMCommands::bmcBlobStat, name);
    }
    catch (const BlobException& b)
    {
        throw;
    }

    std::memcpy(&meta.blob_state, &resp[0], sizeof(meta.blob_state));
    std::memcpy(&meta.size, &resp[sizeof(meta.blob_state)], sizeof(meta.size));
    int offset = sizeof(meta.blob_state) + sizeof(meta.size);
    std::uint8_t len = resp[offset];
    if (len > 0)
    {
        std::copy(&resp[offset + 1], &resp[resp.size()],
                  std::back_inserter(meta.metadata));
    }

    return meta;
}

std::uint16_t BlobHandler::openBlob(const std::string& id,
                                    std::uint16_t handlerFlags)
{
    std::uint16_t session;
    std::vector<std::uint8_t> request, resp;
    auto addrFlags = reinterpret_cast<const std::uint8_t*>(&handlerFlags);

    std::copy(addrFlags, addrFlags + sizeof(handlerFlags),
              std::back_inserter(request));
    std::copy(id.begin(), id.end(), std::back_inserter(request));
    request.push_back(0x00); /* need to add nul-terminator. */

    try
    {
        resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request);
    }
    catch (const BlobException& b)
    {
        throw;
    }

    if (resp.size() != sizeof(session))
    {
        throw BlobException("Did not receive session.");
    }

    std::memcpy(&session, resp.data(), sizeof(session));
    return session;
}

void BlobHandler::closeBlob(std::uint16_t session)
{
    std::vector<std::uint8_t> request;
    auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
    std::copy(addrSession, addrSession + sizeof(session),
              std::back_inserter(request));

    try
    {
        sendIpmiPayload(BlobOEMCommands::bmcBlobClose, request);
    }
    catch (const BlobException& b)
    {
        std::fprintf(stderr, "Received failure on close: %s\n", b.what());
    }

    return;
}

std::vector<std::uint8_t> BlobHandler::readBytes(std::uint16_t session,
                                                 std::uint32_t offset,
                                                 std::uint32_t length)
{
    std::vector<std::uint8_t> payload;

    payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) +
                    sizeof(std::uint32_t));

    auto data = reinterpret_cast<const std::uint8_t*>(&session);
    std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));

    data = reinterpret_cast<const std::uint8_t*>(&offset);
    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));

    data = reinterpret_cast<const std::uint8_t*>(&length);
    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));

    return sendIpmiPayload(BlobOEMCommands::bmcBlobRead, payload);
}

} // namespace ipmiblob
<commit_msg>blob_handler: fprintf tweak<commit_after>/*
 * Copyright 2018 Google Inc.
 *
 * 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 "blob_handler.hpp"

#include "blob_errors.hpp"
#include "crc.hpp"
#include "ipmi_errors.hpp"

#include <array>
#include <cstring>

namespace ipmiblob
{

namespace
{
const std::array<std::uint8_t, 3> ipmiPhosphorOen = {0xcf, 0xc2, 0x00};
}

std::vector<std::uint8_t>
    BlobHandler::sendIpmiPayload(BlobOEMCommands command,
                                 const std::vector<std::uint8_t>& payload)
{
    std::vector<std::uint8_t> request, reply, bytes;

    std::copy(ipmiPhosphorOen.begin(), ipmiPhosphorOen.end(),
              std::back_inserter(request));
    request.push_back(command);

    if (payload.size() > 0)
    {
        /* Grow the vector to hold the bytes. */
        request.reserve(request.size() + sizeof(std::uint16_t));

        /* CRC required. */
        std::uint16_t crc = generateCrc(payload);
        auto src = reinterpret_cast<const std::uint8_t*>(&crc);

        std::copy(src, src + sizeof(crc), std::back_inserter(request));

        /* Copy the payload. */
        std::copy(payload.begin(), payload.end(), std::back_inserter(request));
    }

    try
    {
        reply = ipmi->sendPacket(request);
    }
    catch (const IpmiException& e)
    {
        throw BlobException(e.what());
    }

    /* IPMI_CC was OK, and it returned no bytes, so let's be happy with that for
     * now.
     */
    if (reply.size() == 0)
    {
        return reply;
    }

    /* This cannot be a response because it's smaller than the smallest
     * response.
     */
    if (reply.size() < ipmiPhosphorOen.size())
    {
        throw BlobException("Invalid response length");
    }

    /* Verify the OEN. */
    if (std::memcmp(ipmiPhosphorOen.data(), reply.data(),
                    ipmiPhosphorOen.size()) != 0)
    {
        throw BlobException("Invalid OEN received");
    }

    /* In this case there was no data, as there was no CRC. */
    std::size_t headerSize = ipmiPhosphorOen.size() + sizeof(std::uint16_t);
    if (reply.size() < headerSize)
    {
        return {};
    }

    /* Validate CRC. */
    std::uint16_t crc;
    auto ptr = reinterpret_cast<std::uint8_t*>(&crc);
    std::memcpy(ptr, &reply[ipmiPhosphorOen.size()], sizeof(crc));

    for (const auto& byte : reply)
    {
        std::fprintf(stderr, "0x%02x ", byte);
    }
    std::fprintf(stderr, "\n");

    bytes.insert(bytes.begin(), reply.begin() + headerSize, reply.end());

    auto computed = generateCrc(bytes);
    if (crc != computed)
    {
        std::fprintf(stderr, "Invalid CRC, received: 0x%x, computed: 0x%x\n",
                     crc, computed);
        throw BlobException("Invalid CRC on received data.");
    }

    return bytes;
}

int BlobHandler::getBlobCount()
{
    std::uint32_t count;
    try
    {
        auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobGetCount, {});
        if (resp.size() != sizeof(count))
        {
            return 0;
        }

        /* LE to LE (need to make this portable as some point. */
        std::memcpy(&count, resp.data(), sizeof(count));
    }
    catch (const BlobException& b)
    {
        return 0;
    }

    std::fprintf(stderr, "BLOB Count: %u\n", count);
    return count;
}

std::string BlobHandler::enumerateBlob(std::uint32_t index)
{
    std::vector<std::uint8_t> payload;
    auto data = reinterpret_cast<const std::uint8_t*>(&index);

    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));

    try
    {
        auto resp = sendIpmiPayload(BlobOEMCommands::bmcBlobEnumerate, payload);
        return (resp.size() > 0) ? std::string(&resp[0], &resp[resp.size() - 1])
                                 : "";
    }
    catch (const BlobException& b)
    {
        return "";
    }
}

void BlobHandler::writeGeneric(BlobOEMCommands command, std::uint16_t session,
                               std::uint32_t offset,
                               const std::vector<std::uint8_t>& bytes)
{
    std::vector<std::uint8_t> payload;

    payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) +
                    bytes.size());

    auto data = reinterpret_cast<const std::uint8_t*>(&session);
    std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));

    data = reinterpret_cast<const std::uint8_t*>(&offset);
    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));

    std::copy(bytes.begin(), bytes.end(), std::back_inserter(payload));

    auto resp = sendIpmiPayload(command, payload);
}

void BlobHandler::writeMeta(std::uint16_t session, std::uint32_t offset,
                            const std::vector<std::uint8_t>& bytes)
{
    return writeGeneric(BlobOEMCommands::bmcBlobWriteMeta, session, offset,
                        bytes);
}

void BlobHandler::writeBytes(std::uint16_t session, std::uint32_t offset,
                             const std::vector<std::uint8_t>& bytes)
{
    return writeGeneric(BlobOEMCommands::bmcBlobWrite, session, offset, bytes);
}

std::vector<std::string> BlobHandler::getBlobList()
{
    std::vector<std::string> list;
    int blobCount = getBlobCount();

    for (int i = 0; i < blobCount; i++)
    {
        auto name = enumerateBlob(i);
        /* Currently ignore failures. */
        if (!name.empty())
        {
            list.push_back(name);
        }
    }

    return list;
}

StatResponse BlobHandler::getStat(const std::string& id)
{
    StatResponse meta;
    std::vector<std::uint8_t> name, resp;

    std::copy(id.begin(), id.end(), std::back_inserter(name));
    name.push_back(0x00); /* need to add nul-terminator. */

    try
    {
        resp = sendIpmiPayload(BlobOEMCommands::bmcBlobStat, name);
    }
    catch (const BlobException& b)
    {
        throw;
    }

    std::memcpy(&meta.blob_state, &resp[0], sizeof(meta.blob_state));
    std::memcpy(&meta.size, &resp[sizeof(meta.blob_state)], sizeof(meta.size));
    int offset = sizeof(meta.blob_state) + sizeof(meta.size);
    std::uint8_t len = resp[offset];
    if (len > 0)
    {
        std::copy(&resp[offset + 1], &resp[resp.size()],
                  std::back_inserter(meta.metadata));
    }

    return meta;
}

std::uint16_t BlobHandler::openBlob(const std::string& id,
                                    std::uint16_t handlerFlags)
{
    std::uint16_t session;
    std::vector<std::uint8_t> request, resp;
    auto addrFlags = reinterpret_cast<const std::uint8_t*>(&handlerFlags);

    std::copy(addrFlags, addrFlags + sizeof(handlerFlags),
              std::back_inserter(request));
    std::copy(id.begin(), id.end(), std::back_inserter(request));
    request.push_back(0x00); /* need to add nul-terminator. */

    try
    {
        resp = sendIpmiPayload(BlobOEMCommands::bmcBlobOpen, request);
    }
    catch (const BlobException& b)
    {
        throw;
    }

    if (resp.size() != sizeof(session))
    {
        throw BlobException("Did not receive session.");
    }

    std::memcpy(&session, resp.data(), sizeof(session));
    return session;
}

void BlobHandler::closeBlob(std::uint16_t session)
{
    std::vector<std::uint8_t> request;
    auto addrSession = reinterpret_cast<const std::uint8_t*>(&session);
    std::copy(addrSession, addrSession + sizeof(session),
              std::back_inserter(request));

    try
    {
        sendIpmiPayload(BlobOEMCommands::bmcBlobClose, request);
    }
    catch (const BlobException& b)
    {
        std::fprintf(stderr, "Received failure on close: %s\n", b.what());
    }

    return;
}

std::vector<std::uint8_t> BlobHandler::readBytes(std::uint16_t session,
                                                 std::uint32_t offset,
                                                 std::uint32_t length)
{
    std::vector<std::uint8_t> payload;

    payload.reserve(sizeof(std::uint16_t) + sizeof(std::uint32_t) +
                    sizeof(std::uint32_t));

    auto data = reinterpret_cast<const std::uint8_t*>(&session);
    std::copy(data, data + sizeof(std::uint16_t), std::back_inserter(payload));

    data = reinterpret_cast<const std::uint8_t*>(&offset);
    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));

    data = reinterpret_cast<const std::uint8_t*>(&length);
    std::copy(data, data + sizeof(std::uint32_t), std::back_inserter(payload));

    return sendIpmiPayload(BlobOEMCommands::bmcBlobRead, payload);
}

} // namespace ipmiblob
<|endoftext|>


<commit_before>/*
    Copyright (C) 2013  Martin Klapetek <mklapetek@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "ktp-translation-proxy.h"
#include "persons-presence-model.h"
#include "persons-model.h"

#include <KTp/types.h>
#include <KDebug>

KTpTranslationProxy::KTpTranslationProxy(QObject *parent)
    : QIdentityProxyModel(parent)
{
}

KTpTranslationProxy::~KTpTranslationProxy()
{
}

QVariant KTpTranslationProxy::data(const QModelIndex &proxyIndex, int role) const
{
    switch (role) {
        case KTp::ContactPresenceTypeRole:
            if (proxyIndex.data(PersonsModel::StatusRole).toString() == "available") {
                return Tp::ConnectionPresenceTypeAvailable;
            } else if (proxyIndex.data(PersonsModel::StatusRole).toString() == "away") {
                return Tp::ConnectionPresenceTypeAway;
            } else if (proxyIndex.data(PersonsModel::StatusRole).toString() == "busy" ||
                proxyIndex.data(PersonsModel::StatusRole).toString() == "dnd") {

                return Tp::ConnectionPresenceTypeBusy;
            } else if (proxyIndex.data(PersonsModel::StatusRole).toString() == "xa") {
                return Tp::ConnectionPresenceTypeExtendedAway;
            } else {
                return Tp::ConnectionPresenceTypeOffline;
            }
            break;

        case Qt::DisplayRole:
            //this is needed to avoid infinite recursion call (proxyIndex.data(Qt::DisplayRole) would call this method)
            return mapToSource(proxyIndex).data(Qt::DisplayRole);
        case KTp::RowTypeRole:
            if (proxyIndex.data(PersonsModel::ResourceTypeRole) == PersonsModel::Contact) {
                return KTp::ContactRowType;
            } else {
                return KTp::PersonRowType;
            }
        case KTp::ContactAvatarPathRole:
            return proxyIndex.data(PersonsModel::PhotoRole);
        case KTp::ContactGroupsRole:
            return proxyIndex.data(PersonsModel::ContactGroupsRole);
        case KTp::AccountRole:
            return proxyIndex.data(PersonsModel::IMAccountRole);
        case KTp::IdRole:
            return proxyIndex.data(PersonsModel::IMRole);
        case KTp::ContactRole:
            return proxyIndex.data(PersonsModel::IMContactRole);
        case KTp::ContactIsBlockedRole:
            return proxyIndex.data(PersonsModel::BlockedRole);
    }

    return QIdentityProxyModel::data(proxyIndex, role);
}
<commit_msg>Micro-optimizations in the translation proxy<commit_after>/*
    Copyright (C) 2013  Martin Klapetek <mklapetek@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "ktp-translation-proxy.h"
#include "persons-presence-model.h"
#include "persons-model.h"

#include <KTp/types.h>
#include <KDebug>

KTpTranslationProxy::KTpTranslationProxy(QObject *parent)
    : QIdentityProxyModel(parent)
{
}

KTpTranslationProxy::~KTpTranslationProxy()
{
}

QVariant KTpTranslationProxy::data(const QModelIndex &proxyIndex, int role) const
{
    if (!proxyIndex.isValid()) {
        return QVariant();
    }

    //cache the status from our sourceModel
    QString status;

    switch (role) {
        case KTp::ContactPresenceTypeRole:
            status = mapToSource(proxyIndex).data(PersonsModel::StatusRole).toString();
            if (status == "available") {
                return Tp::ConnectionPresenceTypeAvailable;
            } else if (status == "away") {
                return Tp::ConnectionPresenceTypeAway;
            } else if (status == "busy" || status == "dnd") {
                return Tp::ConnectionPresenceTypeBusy;
            } else if (status == "xa") {
                return Tp::ConnectionPresenceTypeExtendedAway;
            } else {
                return Tp::ConnectionPresenceTypeOffline;
            }
            break;

        case Qt::DisplayRole:
            //this is needed to avoid infinite recursion call (proxyIndex.data(Qt::DisplayRole) would call this method)
            return mapToSource(proxyIndex).data(Qt::DisplayRole);
        case KTp::RowTypeRole:
            if (mapToSource(proxyIndex).data(PersonsModel::ResourceTypeRole) == PersonsModel::Contact) {
                return KTp::ContactRowType;
            } else {
                return KTp::PersonRowType;
            }
        case KTp::ContactAvatarPathRole:
            return mapToSource(proxyIndex).data(PersonsModel::PhotoRole);
        case KTp::ContactGroupsRole:
            return mapToSource(proxyIndex).data(PersonsModel::ContactGroupsRole);
        case KTp::AccountRole:
            return mapToSource(proxyIndex).data(PersonsModel::IMAccountRole);
        case KTp::IdRole:
            return mapToSource(proxyIndex).data(PersonsModel::IMRole);
        case KTp::ContactRole:
            return mapToSource(proxyIndex).data(PersonsModel::IMContactRole);
        case KTp::ContactIsBlockedRole:
            return mapToSource(proxyIndex).data(PersonsModel::BlockedRole);
    }

    return QIdentityProxyModel::data(proxyIndex, role);
}
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "qmljscheck.h"
#include "qmljsbind.h"
#include "qmljsinterpreter.h"
#include "qmljsevaluate.h"
#include "parser/qmljsast_p.h"

#include <QtCore/QDebug>
#include <QtCore/QCoreApplication>
#include <QtGui/QColor>
#include <QtGui/QApplication>

namespace QmlJS {
namespace Messages {
static const char *invalid_property_name =  QT_TRANSLATE_NOOP("QmlJS::Check", "'%1' is not a valid property name");
static const char *unknown_type = QT_TRANSLATE_NOOP("QmlJS::Check", "unknown type");
static const char *has_no_members = QT_TRANSLATE_NOOP("QmlJS::Check", "'%1' does not have members");
static const char *is_not_a_member = QT_TRANSLATE_NOOP("QmlJS::Check", "'%1' is not a member of '%2'");
static const char *easing_curve_not_a_string = QT_TRANSLATE_NOOP("QmlJS::Check", "easing-curve name is not a string");
static const char *unknown_easing_curve_name = QT_TRANSLATE_NOOP("QmlJS::Check", "unknown easing-curve name");
static const char *value_might_be_undefined = QT_TRANSLATE_NOOP("QmlJS::Check", "value might be 'undefined'");
} // namespace Messages

static inline QString tr(const char *msg)
{ return qApp->translate("QmlJS::Check", msg); }

} // namespace QmlJS

using namespace QmlJS;
using namespace QmlJS::AST;
using namespace QmlJS::Interpreter;

namespace {

class AssignmentCheck : public ValueVisitor
{
public:
    DiagnosticMessage operator()(
            const SourceLocation &location,
            const Interpreter::Value *lhsValue,
            const Interpreter::Value *rhsValue,
            ExpressionNode *ast)
    {
        _message = DiagnosticMessage(DiagnosticMessage::Error, location, QString());
        _rhsValue = rhsValue;
        _ast = ast;

        if (lhsValue)
            lhsValue->accept(this);

        return _message;
    }

    virtual void visit(const NumberValue *)
    {
        // ### Consider enums: elide: "ElideLeft" is valid, but currently elide is a NumberValue.
        if (/*cast<StringLiteral *>(_ast)
                ||*/ _ast->kind == Node::Kind_TrueLiteral
                || _ast->kind == Node::Kind_FalseLiteral) {
            _message.message = QCoreApplication::translate("QmlJS::Check", "numerical value expected");
        }
    }

    virtual void visit(const BooleanValue *)
    {
        UnaryMinusExpression *unaryMinus = cast<UnaryMinusExpression *>(_ast);

        if (cast<StringLiteral *>(_ast)
                || cast<NumericLiteral *>(_ast)
                || (unaryMinus && cast<NumericLiteral *>(unaryMinus->expression))) {
            _message.message = QCoreApplication::translate("QmlJS::Check", "boolean value expected");
        }
    }

    virtual void visit(const StringValue *)
    {
        UnaryMinusExpression *unaryMinus = cast<UnaryMinusExpression *>(_ast);

        if (cast<NumericLiteral *>(_ast)
                || (unaryMinus && cast<NumericLiteral *>(unaryMinus->expression))
                || _ast->kind == Node::Kind_TrueLiteral
                || _ast->kind == Node::Kind_FalseLiteral) {
            _message.message = QCoreApplication::translate("QmlJS::Check", "string value expected");
        }
    }

    virtual void visit(const EasingCurveNameValue *)
    {
        if (StringLiteral *stringLiteral = cast<StringLiteral *>(_ast)) {
            const QString curveName = stringLiteral->value->asString();

            if (!EasingCurveNameValue::curveNames().contains(curveName)) {
                _message.message = tr(Messages::unknown_easing_curve_name);
            }
        } else if (_rhsValue->asUndefinedValue()) {
            _message.kind = DiagnosticMessage::Warning;
            _message.message = tr(Messages::value_might_be_undefined);
        } else if (! _rhsValue->asStringValue()) {
            _message.message = tr(Messages::easing_curve_not_a_string);
        }
    }

    virtual void visit(const ColorValue *)
    {
        if (StringLiteral *stringLiteral = cast<StringLiteral *>(_ast)) {
            const QString colorString = stringLiteral->value->asString();

            bool ok = true;
            if (colorString.size() == 9 && colorString.at(0) == QLatin1Char('#')) {
                // #rgba
                for (int i = 1; i < 9; ++i) {
                    const QChar c = colorString.at(i);
                    if ((c >= QLatin1Char('0') && c <= QLatin1Char('9'))
                        || (c >= QLatin1Char('a') && c <= QLatin1Char('f'))
                        || (c >= QLatin1Char('A') && c <= QLatin1Char('F')))
                        continue;
                    ok = false;
                    break;
                }
            } else {
                ok = QColor::isValidColor(colorString);
            }
            if (!ok)
                _message.message = QCoreApplication::translate("QmlJS::Check", "not a valid color");
        } else {
            visit((StringValue *)0);
        }
    }

    virtual void visit(const AnchorLineValue *)
    {
        if (! (_rhsValue->asAnchorLineValue() || _rhsValue->asUndefinedValue()))
            _message.message = QCoreApplication::translate("QmlJS::Check", "expected anchor line");
    }

    DiagnosticMessage _message;
    const Value *_rhsValue;
    ExpressionNode *_ast;
};

} // end of anonymous namespace

Check::Check(Document::Ptr doc, const Snapshot &snapshot)
    : _doc(doc)
    , _snapshot(snapshot)
    , _context(&_engine)
    , _link(&_context, doc, snapshot)
    , _scopeBuilder(doc, &_context)
{
}

Check::~Check()
{
}

QList<DiagnosticMessage> Check::operator()()
{
    _messages.clear();
    Node::accept(_doc->ast(), this);
    return _messages;
}

bool Check::visit(UiProgram *)
{
    // build the initial scope chain
    _link.scopeChainAt(_doc);

    return true;
}

bool Check::visit(UiObjectDefinition *ast)
{
    visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
    return false;
}

bool Check::visit(UiObjectBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
    return false;
}

void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
                           UiObjectInitializer *initializer)
{
    // If the 'typeId' starts with a lower-case letter, it doesn't define
    // a new object instance. For instance: anchors { ... }
    if (typeId->name->asString().at(0).isLower() && ! typeId->next) {
        checkScopeObjectMember(typeId);
        // ### don't give up!
        return;
    }

    _scopeBuilder.push(ast);

    if (! _context.lookupType(_doc.data(), typeId)) {
        warning(typeId->identifierToken, tr(Messages::unknown_type));
        // suppress subsequent errors about scope object lookup by clearing
        // the scope object list
        // ### todo: better way?
        _context.scopeChain().qmlScopeObjects.clear();
        _context.scopeChain().update();
    }

    Node::accept(initializer, this);

    _scopeBuilder.pop();
}

bool Check::visit(UiScriptBinding *ast)
{
    // special case for id property
    if (ast->qualifiedId->name->asString() == QLatin1String("id") && ! ast->qualifiedId->next) {
        if (! ast->statement)
            return false;

        const SourceLocation loc = locationFromRange(ast->statement->firstSourceLocation(),
                                                     ast->statement->lastSourceLocation());

        ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement);
        if (!expStmt) {
            error(loc, QCoreApplication::translate("QmlJS::Check", "expected id"));
            return false;
        }

        IdentifierExpression *idExp = cast<IdentifierExpression *>(expStmt->expression);
        if (! idExp) {
            error(loc, QCoreApplication::translate("QmlJS::Check", "expected id"));
            return false;
        }

        if (! idExp->name->asString()[0].isLower()) {
            error(loc, QCoreApplication::translate("QmlJS::Check", "ids must be lower case"));
            return false;
        }
    }

    const Value *lhsValue = checkScopeObjectMember(ast->qualifiedId);
    if (lhsValue) {
        // ### Fix the evaluator to accept statements!
        if (ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement)) {
            ExpressionNode *expr = expStmt->expression;

            Evaluate evaluator(&_context);
            const Value *rhsValue = evaluator(expr);

            const SourceLocation loc = locationFromRange(expStmt->firstSourceLocation(),
                                                         expStmt->lastSourceLocation());
            AssignmentCheck assignmentCheck;
            DiagnosticMessage message = assignmentCheck(loc, lhsValue, rhsValue, expr);
            if (! message.message.isEmpty())
                _messages += message;
        }

    }

    return true;
}

bool Check::visit(UiArrayBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    return true;
}

const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
{
    QList<const ObjectValue *> scopeObjects = _context.scopeChain().qmlScopeObjects;
    if (scopeObjects.isEmpty())
        return 0;

    if (! id)
        return 0; // ### error?

    QString propertyName = id->name->asString();

    if (propertyName == QLatin1String("id") && ! id->next)
        return 0; // ### should probably be a special value

    // attached properties
    bool isAttachedProperty = false;
    if (! propertyName.isEmpty() && propertyName[0].isUpper()) {
        isAttachedProperty = true;
        scopeObjects += _context.scopeChain().qmlTypes;
    }

    if (scopeObjects.isEmpty())
        return 0;

    // global lookup for first part of id
    const Value *value = 0;
    for (int i = scopeObjects.size() - 1; i >= 0; --i) {
        value = scopeObjects[i]->lookupMember(propertyName, &_context);
        if (value)
            break;
    }
    if (!value) {
        error(id->identifierToken,
              tr(Messages::invalid_property_name).arg(propertyName));
    }

    // can't look up members for attached properties
    if (isAttachedProperty)
        return 0;

    // member lookup
    const UiQualifiedId *idPart = id;
    while (idPart->next) {
        const ObjectValue *objectValue = value_cast<const ObjectValue *>(value);
        if (! objectValue) {
            error(idPart->identifierToken,
                  tr(Messages::has_no_members).arg(propertyName));
            return 0;
        }

        if (! idPart->next->name) {
            // somebody typed "id." and error recovery still gave us a valid tree,
            // so just bail out here.
            return 0;
        }

        idPart = idPart->next;
        propertyName = idPart->name->asString();

        value = objectValue->lookupMember(propertyName, &_context);
        if (! value) {
            error(idPart->identifierToken,
                  tr(Messages::is_not_a_member).arg(propertyName,
                                               objectValue->className()));
            return 0;
        }
    }

    return value;
}

void Check::error(const AST::SourceLocation &loc, const QString &message)
{
    _messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc, message));
}

void Check::warning(const AST::SourceLocation &loc, const QString &message)
{
    _messages.append(DiagnosticMessage(DiagnosticMessage::Warning, loc, message));
}

SourceLocation Check::locationFromRange(const SourceLocation &start,
                                        const SourceLocation &end)
{
    return SourceLocation(start.offset,
                          end.end() - start.begin(),
                          start.startLine,
                          start.startColumn);
}
<commit_msg>Changed error to warning when using a string literal for an ID.<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "qmljscheck.h"
#include "qmljsbind.h"
#include "qmljsinterpreter.h"
#include "qmljsevaluate.h"
#include "parser/qmljsast_p.h"

#include <QtCore/QDebug>
#include <QtCore/QCoreApplication>
#include <QtGui/QColor>
#include <QtGui/QApplication>

namespace QmlJS {
namespace Messages {
static const char *invalid_property_name =  QT_TRANSLATE_NOOP("QmlJS::Check", "'%1' is not a valid property name");
static const char *unknown_type = QT_TRANSLATE_NOOP("QmlJS::Check", "unknown type");
static const char *has_no_members = QT_TRANSLATE_NOOP("QmlJS::Check", "'%1' does not have members");
static const char *is_not_a_member = QT_TRANSLATE_NOOP("QmlJS::Check", "'%1' is not a member of '%2'");
static const char *easing_curve_not_a_string = QT_TRANSLATE_NOOP("QmlJS::Check", "easing-curve name is not a string");
static const char *unknown_easing_curve_name = QT_TRANSLATE_NOOP("QmlJS::Check", "unknown easing-curve name");
static const char *value_might_be_undefined = QT_TRANSLATE_NOOP("QmlJS::Check", "value might be 'undefined'");
} // namespace Messages

static inline QString tr(const char *msg)
{ return qApp->translate("QmlJS::Check", msg); }

} // namespace QmlJS

using namespace QmlJS;
using namespace QmlJS::AST;
using namespace QmlJS::Interpreter;

namespace {

class AssignmentCheck : public ValueVisitor
{
public:
    DiagnosticMessage operator()(
            const SourceLocation &location,
            const Interpreter::Value *lhsValue,
            const Interpreter::Value *rhsValue,
            ExpressionNode *ast)
    {
        _message = DiagnosticMessage(DiagnosticMessage::Error, location, QString());
        _rhsValue = rhsValue;
        _ast = ast;

        if (lhsValue)
            lhsValue->accept(this);

        return _message;
    }

    virtual void visit(const NumberValue *)
    {
        // ### Consider enums: elide: "ElideLeft" is valid, but currently elide is a NumberValue.
        if (/*cast<StringLiteral *>(_ast)
                ||*/ _ast->kind == Node::Kind_TrueLiteral
                || _ast->kind == Node::Kind_FalseLiteral) {
            _message.message = QCoreApplication::translate("QmlJS::Check", "numerical value expected");
        }
    }

    virtual void visit(const BooleanValue *)
    {
        UnaryMinusExpression *unaryMinus = cast<UnaryMinusExpression *>(_ast);

        if (cast<StringLiteral *>(_ast)
                || cast<NumericLiteral *>(_ast)
                || (unaryMinus && cast<NumericLiteral *>(unaryMinus->expression))) {
            _message.message = QCoreApplication::translate("QmlJS::Check", "boolean value expected");
        }
    }

    virtual void visit(const StringValue *)
    {
        UnaryMinusExpression *unaryMinus = cast<UnaryMinusExpression *>(_ast);

        if (cast<NumericLiteral *>(_ast)
                || (unaryMinus && cast<NumericLiteral *>(unaryMinus->expression))
                || _ast->kind == Node::Kind_TrueLiteral
                || _ast->kind == Node::Kind_FalseLiteral) {
            _message.message = QCoreApplication::translate("QmlJS::Check", "string value expected");
        }
    }

    virtual void visit(const EasingCurveNameValue *)
    {
        if (StringLiteral *stringLiteral = cast<StringLiteral *>(_ast)) {
            const QString curveName = stringLiteral->value->asString();

            if (!EasingCurveNameValue::curveNames().contains(curveName)) {
                _message.message = tr(Messages::unknown_easing_curve_name);
            }
        } else if (_rhsValue->asUndefinedValue()) {
            _message.kind = DiagnosticMessage::Warning;
            _message.message = tr(Messages::value_might_be_undefined);
        } else if (! _rhsValue->asStringValue()) {
            _message.message = tr(Messages::easing_curve_not_a_string);
        }
    }

    virtual void visit(const ColorValue *)
    {
        if (StringLiteral *stringLiteral = cast<StringLiteral *>(_ast)) {
            const QString colorString = stringLiteral->value->asString();

            bool ok = true;
            if (colorString.size() == 9 && colorString.at(0) == QLatin1Char('#')) {
                // #rgba
                for (int i = 1; i < 9; ++i) {
                    const QChar c = colorString.at(i);
                    if ((c >= QLatin1Char('0') && c <= QLatin1Char('9'))
                        || (c >= QLatin1Char('a') && c <= QLatin1Char('f'))
                        || (c >= QLatin1Char('A') && c <= QLatin1Char('F')))
                        continue;
                    ok = false;
                    break;
                }
            } else {
                ok = QColor::isValidColor(colorString);
            }
            if (!ok)
                _message.message = QCoreApplication::translate("QmlJS::Check", "not a valid color");
        } else {
            visit((StringValue *)0);
        }
    }

    virtual void visit(const AnchorLineValue *)
    {
        if (! (_rhsValue->asAnchorLineValue() || _rhsValue->asUndefinedValue()))
            _message.message = QCoreApplication::translate("QmlJS::Check", "expected anchor line");
    }

    DiagnosticMessage _message;
    const Value *_rhsValue;
    ExpressionNode *_ast;
};

} // end of anonymous namespace

Check::Check(Document::Ptr doc, const Snapshot &snapshot)
    : _doc(doc)
    , _snapshot(snapshot)
    , _context(&_engine)
    , _link(&_context, doc, snapshot)
    , _scopeBuilder(doc, &_context)
{
}

Check::~Check()
{
}

QList<DiagnosticMessage> Check::operator()()
{
    _messages.clear();
    Node::accept(_doc->ast(), this);
    return _messages;
}

bool Check::visit(UiProgram *)
{
    // build the initial scope chain
    _link.scopeChainAt(_doc);

    return true;
}

bool Check::visit(UiObjectDefinition *ast)
{
    visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
    return false;
}

bool Check::visit(UiObjectBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
    return false;
}

void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
                           UiObjectInitializer *initializer)
{
    // If the 'typeId' starts with a lower-case letter, it doesn't define
    // a new object instance. For instance: anchors { ... }
    if (typeId->name->asString().at(0).isLower() && ! typeId->next) {
        checkScopeObjectMember(typeId);
        // ### don't give up!
        return;
    }

    _scopeBuilder.push(ast);

    if (! _context.lookupType(_doc.data(), typeId)) {
        warning(typeId->identifierToken, tr(Messages::unknown_type));
        // suppress subsequent errors about scope object lookup by clearing
        // the scope object list
        // ### todo: better way?
        _context.scopeChain().qmlScopeObjects.clear();
        _context.scopeChain().update();
    }

    Node::accept(initializer, this);

    _scopeBuilder.pop();
}

bool Check::visit(UiScriptBinding *ast)
{
    // special case for id property
    if (ast->qualifiedId->name->asString() == QLatin1String("id") && ! ast->qualifiedId->next) {
        if (! ast->statement)
            return false;

        const SourceLocation loc = locationFromRange(ast->statement->firstSourceLocation(),
                                                     ast->statement->lastSourceLocation());

        ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement);
        if (!expStmt) {
            error(loc, QCoreApplication::translate("QmlJS::Check", "expected id"));
            return false;
        }

        QString id;
        if (IdentifierExpression *idExp = cast<IdentifierExpression *>(expStmt->expression)) {
            id = idExp->name->asString();
        } else if (StringLiteral *strExp = cast<StringLiteral *>(expStmt->expression)) {
            id = strExp->value->asString();
            warning(loc, QCoreApplication::translate("QmlJS::Check", "using string literals for ids is discouraged"));
        } else {
            error(loc, QCoreApplication::translate("QmlJS::Check", "expected id"));
            return false;
        }

        if (id.isEmpty() || ! id[0].isLower()) {
            error(loc, QCoreApplication::translate("QmlJS::Check", "ids must be lower case"));
            return false;
        }
    }

    const Value *lhsValue = checkScopeObjectMember(ast->qualifiedId);
    if (lhsValue) {
        // ### Fix the evaluator to accept statements!
        if (ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement)) {
            ExpressionNode *expr = expStmt->expression;

            Evaluate evaluator(&_context);
            const Value *rhsValue = evaluator(expr);

            const SourceLocation loc = locationFromRange(expStmt->firstSourceLocation(),
                                                         expStmt->lastSourceLocation());
            AssignmentCheck assignmentCheck;
            DiagnosticMessage message = assignmentCheck(loc, lhsValue, rhsValue, expr);
            if (! message.message.isEmpty())
                _messages += message;
        }

    }

    return true;
}

bool Check::visit(UiArrayBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    return true;
}

const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
{
    QList<const ObjectValue *> scopeObjects = _context.scopeChain().qmlScopeObjects;
    if (scopeObjects.isEmpty())
        return 0;

    if (! id)
        return 0; // ### error?

    QString propertyName = id->name->asString();

    if (propertyName == QLatin1String("id") && ! id->next)
        return 0; // ### should probably be a special value

    // attached properties
    bool isAttachedProperty = false;
    if (! propertyName.isEmpty() && propertyName[0].isUpper()) {
        isAttachedProperty = true;
        scopeObjects += _context.scopeChain().qmlTypes;
    }

    if (scopeObjects.isEmpty())
        return 0;

    // global lookup for first part of id
    const Value *value = 0;
    for (int i = scopeObjects.size() - 1; i >= 0; --i) {
        value = scopeObjects[i]->lookupMember(propertyName, &_context);
        if (value)
            break;
    }
    if (!value) {
        error(id->identifierToken,
              tr(Messages::invalid_property_name).arg(propertyName));
    }

    // can't look up members for attached properties
    if (isAttachedProperty)
        return 0;

    // member lookup
    const UiQualifiedId *idPart = id;
    while (idPart->next) {
        const ObjectValue *objectValue = value_cast<const ObjectValue *>(value);
        if (! objectValue) {
            error(idPart->identifierToken,
                  tr(Messages::has_no_members).arg(propertyName));
            return 0;
        }

        if (! idPart->next->name) {
            // somebody typed "id." and error recovery still gave us a valid tree,
            // so just bail out here.
            return 0;
        }

        idPart = idPart->next;
        propertyName = idPart->name->asString();

        value = objectValue->lookupMember(propertyName, &_context);
        if (! value) {
            error(idPart->identifierToken,
                  tr(Messages::is_not_a_member).arg(propertyName,
                                               objectValue->className()));
            return 0;
        }
    }

    return value;
}

void Check::error(const AST::SourceLocation &loc, const QString &message)
{
    _messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc, message));
}

void Check::warning(const AST::SourceLocation &loc, const QString &message)
{
    _messages.append(DiagnosticMessage(DiagnosticMessage::Warning, loc, message));
}

SourceLocation Check::locationFromRange(const SourceLocation &start,
                                        const SourceLocation &end)
{
    return SourceLocation(start.offset,
                          end.end() - start.begin(),
                          start.startLine,
                          start.startColumn);
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Cloudius Systems, Ltd.
 */

#include "repair.hh"

#include "streaming/stream_plan.hh"
#include "streaming/stream_state.hh"
#include "gms/inet_address.hh"

static logging::logger logger("repair");

static std::vector<sstring> list_column_families(const database& db, const sstring& keyspace) {
    std::vector<sstring> ret;
    for (auto &&e : db.get_column_families_mapping()) {
        if (e.first.first == keyspace) {
            ret.push_back(e.first.second);
        }
    }
    return ret;
}

template<typename Collection, typename T>
void remove_item(Collection& c, T& item) {
    auto it = std::find(c.begin(), c.end(), item);
    if (it != c.end()) {
        c.erase(it);
    }
}

// Return all of the neighbors with whom we share the provided range.
static std::vector<gms::inet_address> get_neighbors(database& db,
        const sstring& ksname, query::range<dht::token> range
        //Collection<String> dataCenters, Collection<String> hosts)
        ) {
    keyspace& ks = db.find_keyspace(ksname);
    auto& rs = ks.get_replication_strategy();

    dht::token tok = range.end() ? range.end()->value() : dht::maximum_token();
    auto ret = rs.get_natural_endpoints(tok);
    remove_item(ret, utils::fb_utilities::get_broadcast_address());
    return ret;

#if 0
    // Origin's ActiveRepairService.getNeighbors() contains a lot of important
    // stuff we need to do, like verifying the requested range fits a local
    // range, and also taking the "datacenters" and "hosts" options.
        StorageService ss = StorageService.instance;
        Map<Range<Token>, List<InetAddress>> replicaSets = ss.getRangeToAddressMap(keyspaceName);
        Range<Token> rangeSuperSet = null;
        for (Range<Token> range : ss.getLocalRanges(keyspaceName))
        {
            if (range.contains(toRepair))
            {
                rangeSuperSet = range;
                break;
            }
            else if (range.intersects(toRepair))
            {
                throw new IllegalArgumentException("Requested range intersects a local range but is not fully contained in one; this would lead to imprecise repair");
            }
        }
        if (rangeSuperSet == null || !replicaSets.containsKey(rangeSuperSet))
            return Collections.emptySet();

        Set<InetAddress> neighbors = new HashSet<>(replicaSets.get(rangeSuperSet));
        neighbors.remove(FBUtilities.getBroadcastAddress());

        if (dataCenters != null && !dataCenters.isEmpty())
        {
            TokenMetadata.Topology topology = ss.getTokenMetadata().cloneOnlyTokenMap().getTopology();
            Set<InetAddress> dcEndpoints = Sets.newHashSet();
            Multimap<String,InetAddress> dcEndpointsMap = topology.getDatacenterEndpoints();
            for (String dc : dataCenters)
            {
                Collection<InetAddress> c = dcEndpointsMap.get(dc);
                if (c != null)
                   dcEndpoints.addAll(c);
            }
            return Sets.intersection(neighbors, dcEndpoints);
        }
        else if (hosts != null && !hosts.isEmpty())
        {
            Set<InetAddress> specifiedHost = new HashSet<>();
            for (final String host : hosts)
            {
                try
                {
                    final InetAddress endpoint = InetAddress.getByName(host.trim());
                    if (endpoint.equals(FBUtilities.getBroadcastAddress()) || neighbors.contains(endpoint))
                        specifiedHost.add(endpoint);
                }
                catch (UnknownHostException e)
                {
                    throw new IllegalArgumentException("Unknown host specified " + host, e);
                }
            }

            if (!specifiedHost.contains(FBUtilities.getBroadcastAddress()))
                throw new IllegalArgumentException("The current host must be part of the repair");

            if (specifiedHost.size() <= 1)
            {
                String msg = "Repair requires at least two endpoints that are neighbours before it can continue, the endpoint used for this repair is %s, " +
                             "other available neighbours are %s but these neighbours were not part of the supplied list of hosts to use during the repair (%s).";
                throw new IllegalArgumentException(String.format(msg, specifiedHost, neighbors, hosts));
            }

            specifiedHost.remove(FBUtilities.getBroadcastAddress());
            return specifiedHost;

        }

        return neighbors;
#endif
}

// The repair_tracker tracks ongoing repair operations and their progress.
// A repair which has already finished successfully is dropped from this
// table, but a failed repair will remain in the table forever so it can
// be queried about more than once (FIXME: reconsider this. But note that
// failed repairs should be rare anwyay).
// This object is not thread safe, and must be used by only one cpu.
static class {
private:
    // Each repair_start() call returns a unique int which the user can later
    // use to follow the status of this repair with repair_status().
    int _next_repair_command = 0;
    // Note that there are no "SUCCESSFUL" entries in the "status" map:
    // Successfully-finished repairs are those with id < _next_repair_command
    // but aren't listed as running or failed the status map.
    std::unordered_map<int, repair_status> _status;
public:
    void start(int id) {
        _status[id] = repair_status::RUNNING;
    }
    void done(int id, bool succeeded) {
        if (succeeded) {
            _status.erase(id);
        } else {
            _status[id] = repair_status::FAILED;
        }
    }
    repair_status get(int id) {
        if (id >= _next_repair_command) {
            throw std::runtime_error(sprint("unknown repair id %d", id));
        }
        auto it = _status.find(id);
        if (it == _status.end()) {
            return repair_status::SUCCESSFUL;
        } else {
            return it->second;
        }
    }
    int next_repair_command() {
        return _next_repair_command++;
    }
} repair_tracker;

// repair_start() can run on any cpu; It runs on cpu0 the function
// do_repair_start(). The benefit of always running that function on the same
// CPU is that it allows us to keep some state (like a list of ongoing
// repairs). It is fine to always do this on one CPU, because the function
// itself does very little (mainly tell other nodes and CPUs what to do).

// Repair a single range. Comparable to RepairSession in Origin
// In Origin, this is composed of several "repair jobs", each with one cf,
// but our streaming already works for several cfs.
static future<> repair_range(seastar::sharded<database>& db, sstring keyspace,
        query::range<dht::token> range, std::vector<sstring> cfs) {
    auto sp = make_lw_shared<streaming::stream_plan>("repair");
    auto id = utils::UUID_gen::get_time_UUID();

    auto neighbors = get_neighbors(db.local(), keyspace, range);
    logger.info("[repair #{}] new session: will sync {} on range {} for {}.{}", id, neighbors, range, keyspace, cfs);
    for (auto peer : neighbors) {
        // FIXME: think: if we have several neighbors, perhaps we need to
        // request ranges from all of them and only later transfer ranges to
        // all of them? Otherwise, we won't necessarily fully repair the
        // other ndoes, just this one? What does Cassandra do here?
        sp->transfer_ranges(peer, peer, keyspace, {range}, cfs);
        sp->request_ranges(peer, peer, keyspace, {range}, cfs);
    }
    return sp->execute().discard_result().then([sp, id] {
        logger.info("repair session #{} successful", id);
    }).handle_exception([id] (auto ep) {
        logger.error("repair session #{} stream failed: {}", id, ep);
        return make_exception_future(std::runtime_error("repair_range failed"));
    });
}

static std::vector<query::range<dht::token>> get_ranges_for_endpoint(
        database& db, sstring keyspace, gms::inet_address ep) {
    auto& rs = db.find_keyspace(keyspace).get_replication_strategy();
    return rs.get_ranges(ep);
}

static std::vector<query::range<dht::token>> get_local_ranges(
        database& db, sstring keyspace) {
    return get_ranges_for_endpoint(db, keyspace, utils::fb_utilities::get_broadcast_address());
}

static int do_repair_start(seastar::sharded<database>& db, sstring keyspace,
        std::unordered_map<sstring, sstring> options) {
    int id = repair_tracker.next_repair_command();
    logger.info("starting user-requested repair for keyspace {}, repair id {}", keyspace, id);

    repair_tracker.start(id);

    // If the "ranges" option is not explicitly specified, we repair all the
    // local ranges (the token ranges for which this node holds a replica of).
    // Each of these ranges may have a different set of replicas, so the
    // repair of each range is performed separately with repair_range().
    std::vector<query::range<dht::token>> ranges;
    // FIXME: if the "ranges" options exists, use that instead of
    // get_local_ranges() below. Also, translate the following Origin code:

#if 0
         if (option.isPrimaryRange())
         {
             // when repairing only primary range, neither dataCenters nor hosts can be set
             if (option.getDataCenters().isEmpty() && option.getHosts().isEmpty())
                 option.getRanges().addAll(getPrimaryRanges(keyspace));
                 // except dataCenters only contain local DC (i.e. -local)
             else if (option.getDataCenters().size() == 1 && option.getDataCenters().contains(DatabaseDescriptor.getLocalDataCenter()))
                 option.getRanges().addAll(getPrimaryRangesWithinDC(keyspace));
             else
                 throw new IllegalArgumentException("You need to run primary range repair on all nodes in the cluster.");
         }
         else
#endif
    ranges = get_local_ranges(db.local(), keyspace);

    // FIXME: let the cfs be overriden by an option
    std::vector<sstring> cfs = list_column_families(db.local(), keyspace);

    do_with(std::move(ranges), [&db, keyspace, cfs, id] (auto& ranges) {
#if 1
        // repair all the ranges in parallel
        return parallel_for_each(ranges.begin(), ranges.end(), [&db, keyspace, cfs, id] (auto&& range) {
#else
        // repair all the ranges in sequence
        return do_for_each(ranges.begin(), ranges.end(), [&db, keyspace, cfs, id] (auto&& range) {
#endif
            return repair_range(db, keyspace, range, cfs);
        }).then([id] {
            logger.info("repair {} completed sucessfully", id);
            repair_tracker.done(id, true);
        }).handle_exception([id] (std::exception_ptr eptr) {
            logger.info("repair {} failed", id);
            repair_tracker.done(id, false);
        });
    });

    return id;
}

future<int> repair_start(seastar::sharded<database>& db, sstring keyspace,
        std::unordered_map<sstring, sstring> options) {
    return db.invoke_on(0, [&db, keyspace = std::move(keyspace), options = std::move(options)] (database& localdb) {
        return do_repair_start(db, std::move(keyspace), std::move(options));
    });
}

future<repair_status> repair_get_status(seastar::sharded<database>& db, int id) {
    return db.invoke_on(0, [id] (database& localdb) {
        return repair_tracker.get(id);
    });
}
<commit_msg>repair: implement primaryRange and ranges options<commit_after>/*
 * Copyright (C) 2015 Cloudius Systems, Ltd.
 */

#include "repair.hh"

#include "streaming/stream_plan.hh"
#include "streaming/stream_state.hh"
#include "gms/inet_address.hh"

#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>

static logging::logger logger("repair");

static std::vector<sstring> list_column_families(const database& db, const sstring& keyspace) {
    std::vector<sstring> ret;
    for (auto &&e : db.get_column_families_mapping()) {
        if (e.first.first == keyspace) {
            ret.push_back(e.first.second);
        }
    }
    return ret;
}

template<typename Collection, typename T>
void remove_item(Collection& c, T& item) {
    auto it = std::find(c.begin(), c.end(), item);
    if (it != c.end()) {
        c.erase(it);
    }
}

// Return all of the neighbors with whom we share the provided range.
static std::vector<gms::inet_address> get_neighbors(database& db,
        const sstring& ksname, query::range<dht::token> range
        //Collection<String> dataCenters, Collection<String> hosts)
        ) {
    keyspace& ks = db.find_keyspace(ksname);
    auto& rs = ks.get_replication_strategy();

    dht::token tok = range.end() ? range.end()->value() : dht::maximum_token();
    auto ret = rs.get_natural_endpoints(tok);
    remove_item(ret, utils::fb_utilities::get_broadcast_address());
    return ret;

#if 0
    // Origin's ActiveRepairService.getNeighbors() contains a lot of important
    // stuff we need to do, like verifying the requested range fits a local
    // range, and also taking the "datacenters" and "hosts" options.
        StorageService ss = StorageService.instance;
        Map<Range<Token>, List<InetAddress>> replicaSets = ss.getRangeToAddressMap(keyspaceName);
        Range<Token> rangeSuperSet = null;
        for (Range<Token> range : ss.getLocalRanges(keyspaceName))
        {
            if (range.contains(toRepair))
            {
                rangeSuperSet = range;
                break;
            }
            else if (range.intersects(toRepair))
            {
                throw new IllegalArgumentException("Requested range intersects a local range but is not fully contained in one; this would lead to imprecise repair");
            }
        }
        if (rangeSuperSet == null || !replicaSets.containsKey(rangeSuperSet))
            return Collections.emptySet();

        Set<InetAddress> neighbors = new HashSet<>(replicaSets.get(rangeSuperSet));
        neighbors.remove(FBUtilities.getBroadcastAddress());

        if (dataCenters != null && !dataCenters.isEmpty())
        {
            TokenMetadata.Topology topology = ss.getTokenMetadata().cloneOnlyTokenMap().getTopology();
            Set<InetAddress> dcEndpoints = Sets.newHashSet();
            Multimap<String,InetAddress> dcEndpointsMap = topology.getDatacenterEndpoints();
            for (String dc : dataCenters)
            {
                Collection<InetAddress> c = dcEndpointsMap.get(dc);
                if (c != null)
                   dcEndpoints.addAll(c);
            }
            return Sets.intersection(neighbors, dcEndpoints);
        }
        else if (hosts != null && !hosts.isEmpty())
        {
            Set<InetAddress> specifiedHost = new HashSet<>();
            for (final String host : hosts)
            {
                try
                {
                    final InetAddress endpoint = InetAddress.getByName(host.trim());
                    if (endpoint.equals(FBUtilities.getBroadcastAddress()) || neighbors.contains(endpoint))
                        specifiedHost.add(endpoint);
                }
                catch (UnknownHostException e)
                {
                    throw new IllegalArgumentException("Unknown host specified " + host, e);
                }
            }

            if (!specifiedHost.contains(FBUtilities.getBroadcastAddress()))
                throw new IllegalArgumentException("The current host must be part of the repair");

            if (specifiedHost.size() <= 1)
            {
                String msg = "Repair requires at least two endpoints that are neighbours before it can continue, the endpoint used for this repair is %s, " +
                             "other available neighbours are %s but these neighbours were not part of the supplied list of hosts to use during the repair (%s).";
                throw new IllegalArgumentException(String.format(msg, specifiedHost, neighbors, hosts));
            }

            specifiedHost.remove(FBUtilities.getBroadcastAddress());
            return specifiedHost;

        }

        return neighbors;
#endif
}

// The repair_tracker tracks ongoing repair operations and their progress.
// A repair which has already finished successfully is dropped from this
// table, but a failed repair will remain in the table forever so it can
// be queried about more than once (FIXME: reconsider this. But note that
// failed repairs should be rare anwyay).
// This object is not thread safe, and must be used by only one cpu.
static class {
private:
    // Each repair_start() call returns a unique int which the user can later
    // use to follow the status of this repair with repair_status().
    int _next_repair_command = 0;
    // Note that there are no "SUCCESSFUL" entries in the "status" map:
    // Successfully-finished repairs are those with id < _next_repair_command
    // but aren't listed as running or failed the status map.
    std::unordered_map<int, repair_status> _status;
public:
    void start(int id) {
        _status[id] = repair_status::RUNNING;
    }
    void done(int id, bool succeeded) {
        if (succeeded) {
            _status.erase(id);
        } else {
            _status[id] = repair_status::FAILED;
        }
    }
    repair_status get(int id) {
        if (id >= _next_repair_command) {
            throw std::runtime_error(sprint("unknown repair id %d", id));
        }
        auto it = _status.find(id);
        if (it == _status.end()) {
            return repair_status::SUCCESSFUL;
        } else {
            return it->second;
        }
    }
    int next_repair_command() {
        return _next_repair_command++;
    }
} repair_tracker;

// repair_start() can run on any cpu; It runs on cpu0 the function
// do_repair_start(). The benefit of always running that function on the same
// CPU is that it allows us to keep some state (like a list of ongoing
// repairs). It is fine to always do this on one CPU, because the function
// itself does very little (mainly tell other nodes and CPUs what to do).

// Repair a single range. Comparable to RepairSession in Origin
// In Origin, this is composed of several "repair jobs", each with one cf,
// but our streaming already works for several cfs.
static future<> repair_range(seastar::sharded<database>& db, sstring keyspace,
        query::range<dht::token> range, std::vector<sstring> cfs) {
    auto sp = make_lw_shared<streaming::stream_plan>("repair");
    auto id = utils::UUID_gen::get_time_UUID();

    auto neighbors = get_neighbors(db.local(), keyspace, range);
    logger.info("[repair #{}] new session: will sync {} on range {} for {}.{}", id, neighbors, range, keyspace, cfs);
    for (auto peer : neighbors) {
        // FIXME: think: if we have several neighbors, perhaps we need to
        // request ranges from all of them and only later transfer ranges to
        // all of them? Otherwise, we won't necessarily fully repair the
        // other ndoes, just this one? What does Cassandra do here?
        sp->transfer_ranges(peer, peer, keyspace, {range}, cfs);
        sp->request_ranges(peer, peer, keyspace, {range}, cfs);
    }
    return sp->execute().discard_result().then([sp, id] {
        logger.info("repair session #{} successful", id);
    }).handle_exception([id] (auto ep) {
        logger.error("repair session #{} stream failed: {}", id, ep);
        return make_exception_future(std::runtime_error("repair_range failed"));
    });
}

static std::vector<query::range<dht::token>> get_ranges_for_endpoint(
        database& db, sstring keyspace, gms::inet_address ep) {
    auto& rs = db.find_keyspace(keyspace).get_replication_strategy();
    return rs.get_ranges(ep);
}

static std::vector<query::range<dht::token>> get_local_ranges(
        database& db, sstring keyspace) {
    return get_ranges_for_endpoint(db, keyspace, utils::fb_utilities::get_broadcast_address());
}

static std::vector<query::range<dht::token>> get_primary_ranges_for_endpoint(
        database& db, sstring keyspace, gms::inet_address ep) {
    auto& rs = db.find_keyspace(keyspace).get_replication_strategy();
    return rs.get_primary_ranges(ep);
}

static std::vector<query::range<dht::token>> get_primary_ranges(
        database& db, sstring keyspace) {
    return get_primary_ranges_for_endpoint(db, keyspace,
            utils::fb_utilities::get_broadcast_address());
}

struct repair_options {
    // If primary_range is true, we should perform repair only on this node's
    // primary ranges. The default of false means perform repair on all ranges
    // held by the node. primary_range=true is useful if the user plans to
    // repair all nodes.
    bool primary_range = false;
    // If ranges is not empty, it overrides the repair's default heuristics
    // for determining the list of ranges to repair. In particular, "ranges"
    // overrides the setting of "primary_range".
    std::vector<query::range<dht::token>> ranges;

    repair_options(const std::unordered_map<sstring, sstring>& options) {
        bool_opt(primary_range, options, PRIMARY_RANGE_KEY);
        ranges_opt(ranges, options, RANGES_KEY);
    }

    static constexpr const char* PRIMARY_RANGE_KEY = "primaryRange";
    static constexpr const char* PARALLELISM_KEY = "parallelism"; // TODO
    static constexpr const char* INCREMENTAL_KEY = "incremental"; // TODO
    static constexpr const char* JOB_THREADS_KEY = "jobThreads"; // TODO
    static constexpr const char* RANGES_KEY = "ranges";
    static constexpr const char* COLUMNFAMILIES_KEY = "columnFamilies"; // TODO
    static constexpr const char* DATACENTERS_KEY = "dataCenters"; // TODO
    static constexpr const char* HOSTS_KEY = "hosts"; // TODO
    static constexpr const char* TRACE_KEY = "trace"; // TODO

private:
    static void bool_opt(bool& var,
            const std::unordered_map<sstring, sstring>& options,
            const sstring& key) {
        auto it = options.find(key);
        if (it != options.end()) {
            // Same parsing as Boolean.parseBoolean does:
            if (boost::algorithm::iequals(it->second, "true")) {
                var = true;
            } else {
                var = false;
            }
        }
    }

    // A range is expressed as start_token:end token and multiple ranges can
    // be given as comma separated ranges(e.g. aaa:bbb,ccc:ddd).
    static void ranges_opt(std::vector<query::range<dht::token>> var,
            const std::unordered_map<sstring, sstring>& options,
                        const sstring& key) {
        auto it = options.find(key);
        if (it == options.end()) {
            return;
        }
        std::vector<sstring> range_strings;
        boost::split(range_strings, it->second, boost::algorithm::is_any_of(","));
        for (auto range : range_strings) {
            std::vector<sstring> token_strings;
            boost::split(token_strings, range, boost::algorithm::is_any_of(":"));
            if (token_strings.size() != 2) {
                throw(std::runtime_error("range must have two components "
                        "separated by ':', got '" + range + "'"));
            }
            auto tok_start = dht::global_partitioner().from_sstring(token_strings[0]);
            auto tok_end = dht::global_partitioner().from_sstring(token_strings[1]);
            var.emplace_back(
                    ::range<dht::token>::bound(tok_start, false),
                    ::range<dht::token>::bound(tok_end, true));
        }
    }
};

static int do_repair_start(seastar::sharded<database>& db, sstring keyspace,
        std::unordered_map<sstring, sstring> options_map) {

    repair_options options(options_map);

    int id = repair_tracker.next_repair_command();
    logger.info("starting user-requested repair for keyspace {}, repair id {}", keyspace, id);

    repair_tracker.start(id);

    // If the "ranges" option is not explicitly specified, we repair all the
    // local ranges (the token ranges for which this node holds a replica of).
    // Each of these ranges may have a different set of replicas, so the
    // repair of each range is performed separately with repair_range().
    std::vector<query::range<dht::token>> ranges;
    if (options.ranges.size()) {
        ranges = options.ranges;
    } else if (options.primary_range) {
        logger.info("primary-range repair");
        // when "primary_range" option is on, neither data_centers nor hosts
        // may be set, except data_centers may contain only local DC (-local)
#if 0
        if (options.data_centers.size() == 1 &&
                options.data_centers[0] == DatabaseDescriptor.getLocalDataCenter()) {
            ranges = get_primary_ranges_within_dc(db.local(), keyspace);
        } else
#endif
#if 0
        if (options.data_centers.size() > 0 || options.hosts.size() > 0) {
            throw std::runtime_error("You need to run primary range repair on all nodes in the cluster.");
        } else {
#endif
            ranges = get_primary_ranges(db.local(), keyspace);
#if 0
        }
#endif
    } else {
        ranges = get_local_ranges(db.local(), keyspace);
    }

    // FIXME: let the cfs be overriden by an option
    std::vector<sstring> cfs = list_column_families(db.local(), keyspace);

    do_with(std::move(ranges), [&db, keyspace, cfs, id] (auto& ranges) {
#if 1
        // repair all the ranges in parallel
        return parallel_for_each(ranges.begin(), ranges.end(), [&db, keyspace, cfs, id] (auto&& range) {
#else
        // repair all the ranges in sequence
        return do_for_each(ranges.begin(), ranges.end(), [&db, keyspace, cfs, id] (auto&& range) {
#endif
            return repair_range(db, keyspace, range, cfs);
        }).then([id] {
            logger.info("repair {} completed sucessfully", id);
            repair_tracker.done(id, true);
        }).handle_exception([id] (std::exception_ptr eptr) {
            logger.info("repair {} failed", id);
            repair_tracker.done(id, false);
        });
    });

    return id;
}

future<int> repair_start(seastar::sharded<database>& db, sstring keyspace,
        std::unordered_map<sstring, sstring> options) {
    return db.invoke_on(0, [&db, keyspace = std::move(keyspace), options = std::move(options)] (database& localdb) {
        return do_repair_start(db, std::move(keyspace), std::move(options));
    });
}

future<repair_status> repair_get_status(seastar::sharded<database>& db, int id) {
    return db.invoke_on(0, [id] (database& localdb) {
        return repair_tracker.get(id);
    });
}
<|endoftext|>


<commit_before><commit_msg>IMPALA-1589: allow non-codegen'd native UDFs<commit_after><|endoftext|>


<commit_before>#include "pch.h"
#include "Puck.h"

Puck::Puck(ID3D11ShaderResourceView *m_Texture, XMFLOAT2 size, XMFLOAT2 position, Windows::Foundation::Rect* movementBounds) :
Sprite(m_Texture, size, position, movementBounds, .1, 0, XMFLOAT2(size.x / 2, size.y / 2))
{
	Velocity = XMFLOAT2(0, 0);
	Speed = 1;
}

void Puck::Update(float timeTotal, float timeDelta, XMFLOAT2 currentField) {
	calculateVelocity(currentField);
	Sprite::Update(timeTotal, timeDelta);
}

void Puck::calculateVelocity(XMFLOAT2 currentField) {
	Velocity.x += currentField.x;
	Velocity.y += currentField.y;
}<commit_msg>Fixed the small bug with movement.<commit_after>#include "pch.h"
#include "Puck.h"

Puck::Puck(ID3D11ShaderResourceView *m_Texture, XMFLOAT2 size, XMFLOAT2 position, Windows::Foundation::Rect* movementBounds) :
Sprite(m_Texture, size, position, movementBounds, .1, 0, XMFLOAT2(size.x / 2, size.y / 2))
{
	Velocity = XMFLOAT2(0, 0);
	Speed = 1;
}

void Puck::Update(float timeTotal, float timeDelta, XMFLOAT2 currentField) {
	calculateVelocity(currentField);
	Sprite::Update(timeTotal, timeDelta);
}

void Puck::calculateVelocity(XMFLOAT2 currentField) {
	Velocity.x += currentField.x;
	Velocity.y -= currentField.y;	// Negative because fields are measured in what we think is intuitive, not what the screen is measured in
}<|endoftext|>


<commit_before>#include <stdint.h>
#include <RF69.h>
#include <RF69_avr.h>

#define REG_FIFO            0x00
#define REG_OPMODE          0x01
#define REG_FRFMSB          0x07
#define REG_AFCFEI          0x1E
#define REG_RSSIVALUE       0x24
#define REG_DIOMAPPING1     0x25
#define REG_IRQFLAGS1       0x27
#define REG_IRQFLAGS2       0x28
#define REG_SYNCCONFIG      0x2E
#define REG_SYNCVALUE1      0x2F
#define REG_SYNCVALUE2      0x30
#define REG_NODEADRS        0x39
#define REG_PACKETCONFIG2   0x3D
#define REG_AESKEY1         0x3E

#define MODE_SLEEP          0x00
#define MODE_STANDBY        0x04
#define MODE_RECEIVER       0x10
#define MODE_TRANSMITTER    0x0C

#define IRQ1_MODEREADY      0x80
#define IRQ1_RXREADY        0x40

#define IRQ2_FIFOFULL       0x80
#define IRQ2_FIFONOTEMPTY   0x40
#define IRQ2_FIFOOVERRUN    0x10
#define IRQ2_PACKETSENT     0x08
#define IRQ2_PAYLOADREADY   0x04

#define DMAP1_PACKETSENT    0x00
#define DMAP1_PAYLOADREADY  0x40
#define DMAP1_SYNCADDRESS   0x80

#define AFC_CLEAR           0x02

#define RF_MAX   72

// transceiver states, these determine what to do with each interrupt
enum { TXCRC1, TXCRC2, TXTAIL, TXDONE, TXIDLE, TXRECV };

namespace RF69 {
    uint32_t frf;
    uint8_t  group;
    uint8_t  node;
    uint16_t crc;
    uint8_t  rssi;
}

static volatile uint8_t rxfill;     // number of data bytes in rf12_buf
static volatile int8_t rxstate;     // current transceiver state

static ROM_UINT8 configRegs_compat [] ROM_DATA = {
  0x01, 0x04, // OpMode = standby
  0x02, 0x00, // DataModul = packet mode, fsk
  0x03, 0x02, // BitRateMsb, data rate = 49,261 khz
  0x04, 0x8A, // BitRateLsb, divider = 32 MHz / 650
  0x05, 0x05, // FdevMsb = 90 KHz
  0x06, 0xC3, // FdevLsb = 90 KHz
  // 0x07, 0xD9, // FrfMsb, freq = 868.000 MHz
  // 0x08, 0x00, // FrfMib, divider = 14221312
  // 0x09, 0x00, // FrfLsb, step = 61.03515625
  0x0B, 0x20, // AfcCtrl, afclowbetaon
  0x19, 0x42, // RxBw ...
  0x1E, 0x2C, // FeiStart, AfcAutoclearOn, AfcAutoOn
  0x25, 0x80, // DioMapping1 = SyncAddress (Rx)
  // 0x29, 0xDC, // RssiThresh ...
  0x2E, 0x88, // SyncConfig = sync on, sync size = 2
  0x2F, 0x2D, // SyncValue1 = 0x2D
  // 0x30, 0x05, // SyncValue2 = 0x05
  0x37, 0x00, // PacketConfig1 = fixed, no crc, filt off
  0x38, 0x00, // PayloadLength = 0, unlimited
  0x3C, 0x8F, // FifoTresh, not empty, level 15
  0x3D, 0x10, // PacketConfig2, interpkt = 1, autorxrestart off
  0x6F, 0x20, // TestDagc ...
  0
};

uint8_t RF69::control(uint8_t cmd, uint8_t val) {
    PreventInterrupt irq0;
    return spiTransfer(cmd, val);
}

static void writeReg (uint8_t addr, uint8_t value) {
    RF69::control(addr | 0x80, value);
}

static uint8_t readReg (uint8_t addr) {
    return RF69::control(addr, 0);
}

static void flushFifo () {
    while (readReg(REG_IRQFLAGS2) & (IRQ2_FIFONOTEMPTY | IRQ2_FIFOOVERRUN))
        readReg(REG_FIFO);
}

static void setMode (uint8_t mode) {
    writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | mode);
    // while ((readReg(REG_IRQFLAGS1) & IRQ1_MODEREADY) == 0)
    //     ;
}

static void initRadio (ROM_UINT8* init) {
    spiInit();
    do
        writeReg(REG_SYNCVALUE1, 0xAA);
    while (readReg(REG_SYNCVALUE1) != 0xAA);
    do
        writeReg(REG_SYNCVALUE1, 0x55);
    while (readReg(REG_SYNCVALUE1) != 0x55);
    for (;;) {
        uint8_t cmd = ROM_READ_UINT8(init);
        if (cmd == 0) break;
        writeReg(cmd, ROM_READ_UINT8(init+1));
        init += 2;
    }
}

void RF69::setFrequency (uint32_t freq) {
    // Frequency steps are in units of (32,000,000 >> 19) = 61.03515625 Hz
    // use multiples of 64 to avoid multi-precision arithmetic, i.e. 3906.25 Hz
    // due to this, the lower 6 bits of the calculated factor will always be 0
    // this is still 4 ppm, i.e. well below the radio's 32 MHz crystal accuracy
    // 868.0 MHz = 0xD90000, 868.3 MHz = 0xD91300, 915.0 MHz = 0xE4C000
    frf = ((freq << 2) / (32000000L >> 11)) << 6;
}

bool RF69::canSend () {
    if (rxstate == TXRECV && rxfill == 0) {
        rxstate = TXIDLE;
        setMode(MODE_STANDBY);
        return true;
    }
    return false;
}

bool RF69::sending () {
    return rxstate < TXIDLE;
}

void RF69::sleep (bool off) {
    setMode(off ? MODE_SLEEP : MODE_STANDBY);
    rxstate = TXIDLE;
}

// References to the RF12 driver above this line will generate compiler errors!
#include <RF69_compat.h>
#include <RF12.h>

void RF69::configure_compat () {
    initRadio(configRegs_compat);
    // FIXME doesn't seem to work, nothing comes in but noise for group 0
    // writeReg(REG_SYNCCONFIG, group ? 0x88 : 0x80);
    writeReg(REG_SYNCVALUE2, group);

    writeReg(REG_FRFMSB, frf >> 16);
    writeReg(REG_FRFMSB+1, frf >> 8);
    writeReg(REG_FRFMSB+2, frf);

    rxstate = TXIDLE;
}

uint8_t* recvBuf;

uint16_t RF69::recvDone_compat (uint8_t* buf) {
    switch (rxstate) {
    case TXIDLE:
        rxfill = rf12_len = 0;
        crc = _crc16_update(~0, group);
        recvBuf = buf;
        rxstate = TXRECV;
        flushFifo();
        writeReg(REG_DIOMAPPING1, DMAP1_SYNCADDRESS);    // Interrupt trigger
        setMode(MODE_RECEIVER);
        writeReg(REG_AFCFEI, AFC_CLEAR);
        break;
    case TXRECV:
        if (rxfill >= rf12_len + 5 || rxfill >= RF_MAX) {
            rxstate = TXIDLE;
            setMode(MODE_STANDBY);
            if (crc != 0 | rf12_len > RF12_MAXDATA)
                return 1; // force bad crc for invalid packet
            if (!(rf12_hdr & RF12_HDR_DST) || node == 31 ||
                    (rf12_hdr & RF12_HDR_MASK) == node)
                return 0; // it's for us, good packet received
        }
        break;
    }
    return ~0; // keep going, not done yet
}

void RF69::sendStart_compat (uint8_t hdr, const void* ptr, uint8_t len) {
    rf12_buf[2] = len;
    for (int i = 0; i < len; ++i)
        rf12_data[i] = ((const uint8_t*) ptr)[i];
    rf12_hdr = hdr & RF12_HDR_DST ? hdr : (hdr & ~RF12_HDR_MASK) + node;
    crc = _crc16_update(~0, group);
    rxstate = - (2 + rf12_len); // preamble and SYN1/SYN2 are sent by hardware
    flushFifo();
    setMode(MODE_TRANSMITTER);
    writeReg(REG_DIOMAPPING1, 0x00); // PacketSent

    // use busy polling until the last byte fits into the buffer
    // this makes sure it all happens on time, and that sendWait can sleep
    while (rxstate < TXDONE)
        if ((readReg(REG_IRQFLAGS2) & IRQ2_FIFOFULL) == 0) {
            uint8_t out = 0xAA;
            if (rxstate < 0) {
                out = recvBuf[3 + rf12_len + rxstate];
                crc = _crc16_update(crc, out);
            } else {
                switch (rxstate) {
                    case TXCRC1: out = crc; break;
                    case TXCRC2: out = crc >> 8; break;
                }
            }
            writeReg(REG_FIFO, out);
            ++rxstate;
        }
}

void RF69::interrupt_compat () {
    if (rxstate == TXRECV) {
        // The following line attempts to stop further interrupts
        writeReg(REG_DIOMAPPING1, 0x40);  // Interrupt on PayloadReady
        rssi = readReg(REG_RSSIVALUE);
        IRQ_ENABLE; // allow nested interrupts from here on
        for (;;) { // busy loop, to get each data byte as soon as it comes in
            if (readReg(REG_IRQFLAGS2) & (IRQ2_FIFONOTEMPTY|IRQ2_FIFOOVERRUN)) {
                if (rxfill == 0)
                    recvBuf[rxfill++] = group;
                uint8_t in = readReg(REG_FIFO);
                recvBuf[rxfill++] = in;
                crc = _crc16_update(crc, in);
                if (rxfill >= rf12_len + 5 || rxfill >= RF_MAX)
                    break;
            }
        }
    } else if (readReg(REG_IRQFLAGS2) & IRQ2_PACKETSENT) {
        // rxstate will be TXDONE at this point
        rxstate = TXIDLE;
        setMode(MODE_STANDBY);
        writeReg(REG_DIOMAPPING1, 0x80); // SyncAddress
    }
}
<commit_msg>see #84<commit_after>#include <stdint.h>
#include <RF69.h>
#include <RF69_avr.h>

#define REG_FIFO            0x00
#define REG_OPMODE          0x01
#define REG_FRFMSB          0x07
#define REG_AFCFEI          0x1E
#define REG_RSSIVALUE       0x24
#define REG_DIOMAPPING1     0x25
#define REG_IRQFLAGS1       0x27
#define REG_IRQFLAGS2       0x28
#define REG_SYNCCONFIG      0x2E
#define REG_SYNCVALUE1      0x2F
#define REG_SYNCVALUE2      0x30
#define REG_NODEADRS        0x39
#define REG_PACKETCONFIG2   0x3D
#define REG_AESKEY1         0x3E

#define MODE_SLEEP          0x00
#define MODE_STANDBY        0x04
#define MODE_RECEIVER       0x10
#define MODE_TRANSMITTER    0x0C

#define IRQ1_MODEREADY      0x80
#define IRQ1_RXREADY        0x40

#define IRQ2_FIFOFULL       0x80
#define IRQ2_FIFONOTEMPTY   0x40
#define IRQ2_FIFOOVERRUN    0x10
#define IRQ2_PACKETSENT     0x08
#define IRQ2_PAYLOADREADY   0x04

#define DMAP1_PACKETSENT    0x00
#define DMAP1_PAYLOADREADY  0x40
#define DMAP1_SYNCADDRESS   0x80

#define AFC_CLEAR           0x02

#define RF_MAX   72

// transceiver states, these determine what to do with each interrupt
enum { TXCRC1, TXCRC2, TXTAIL, TXDONE, TXIDLE, TXRECV };

namespace RF69 {
    uint32_t frf;
    uint8_t  group;
    uint8_t  node;
    uint16_t crc;
    uint8_t  rssi;
}

static volatile uint8_t rxfill;     // number of data bytes in rf12_buf
static volatile int8_t rxstate;     // current transceiver state

static ROM_UINT8 configRegs_compat [] ROM_DATA = {
  0x01, 0x04, // OpMode = standby
  0x02, 0x00, // DataModul = packet mode, fsk
  0x03, 0x02, // BitRateMsb, data rate = 49,261 khz
  0x04, 0x8A, // BitRateLsb, divider = 32 MHz / 650
  0x05, 0x05, // FdevMsb = 90 KHz
  0x06, 0xC3, // FdevLsb = 90 KHz
  // 0x07, 0xD9, // FrfMsb, freq = 868.000 MHz
  // 0x08, 0x00, // FrfMib, divider = 14221312
  // 0x09, 0x00, // FrfLsb, step = 61.03515625
  0x0B, 0x20, // AfcCtrl, afclowbetaon
  0x19, 0x42, // RxBw ...
  0x1E, 0x2C, // FeiStart, AfcAutoclearOn, AfcAutoOn
  0x25, 0x80, // DioMapping1 = SyncAddress (Rx)
  // 0x29, 0xDC, // RssiThresh ...
  0x2E, 0x88, // SyncConfig = sync on, sync size = 2
  0x2F, 0x2D, // SyncValue1 = 0x2D
  // 0x30, 0x05, // SyncValue2 = 0x05
  0x37, 0x00, // PacketConfig1 = fixed, no crc, filt off
  0x38, 0x00, // PayloadLength = 0, unlimited
  0x3C, 0x8F, // FifoTresh, not empty, level 15
  0x3D, 0x10, // PacketConfig2, interpkt = 1, autorxrestart off
  0x6F, 0x20, // TestDagc ...
  0
};

uint8_t RF69::control(uint8_t cmd, uint8_t val) {
    PreventInterrupt irq0;
    return spiTransfer(cmd, val);
}

static void writeReg (uint8_t addr, uint8_t value) {
    RF69::control(addr | 0x80, value);
}

static uint8_t readReg (uint8_t addr) {
    return RF69::control(addr, 0);
}

static void flushFifo () {
    while (readReg(REG_IRQFLAGS2) & (IRQ2_FIFONOTEMPTY | IRQ2_FIFOOVERRUN))
        readReg(REG_FIFO);
}

static void setMode (uint8_t mode) {
    writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | mode);
    // while ((readReg(REG_IRQFLAGS1) & IRQ1_MODEREADY) == 0)
    //     ;
}

static void initRadio (ROM_UINT8* init) {
    spiInit();
    do
        writeReg(REG_SYNCVALUE1, 0xAA);
    while (readReg(REG_SYNCVALUE1) != 0xAA);
    do
        writeReg(REG_SYNCVALUE1, 0x55);
    while (readReg(REG_SYNCVALUE1) != 0x55);
    for (;;) {
        uint8_t cmd = ROM_READ_UINT8(init);
        if (cmd == 0) break;
        writeReg(cmd, ROM_READ_UINT8(init+1));
        init += 2;
    }
}

void RF69::setFrequency (uint32_t freq) {
    // Frequency steps are in units of (32,000,000 >> 19) = 61.03515625 Hz
    // use multiples of 64 to avoid multi-precision arithmetic, i.e. 3906.25 Hz
    // due to this, the lower 6 bits of the calculated factor will always be 0
    // this is still 4 ppm, i.e. well below the radio's 32 MHz crystal accuracy
    // 868.0 MHz = 0xD90000, 868.3 MHz = 0xD91300, 915.0 MHz = 0xE4C000
    frf = ((freq << 2) / (32000000L >> 11)) << 6;
}

bool RF69::canSend () {
    if (rxstate == TXRECV && rxfill == 0) {
        rxstate = TXIDLE;
        setMode(MODE_STANDBY);
        return true;
    }
    return false;
}

bool RF69::sending () {
    return rxstate < TXIDLE;
}

void RF69::sleep (bool off) {
    setMode(off ? MODE_SLEEP : MODE_STANDBY);
    rxstate = TXIDLE;
}

// References to the RF12 driver above this line will generate compiler errors!
#include <RF69_compat.h>
#include <RF12.h>

void RF69::configure_compat () {
    initRadio(configRegs_compat);
    // FIXME doesn't seem to work, nothing comes in but noise for group 0
    // writeReg(REG_SYNCCONFIG, group ? 0x88 : 0x80);
    writeReg(REG_SYNCVALUE2, group);

    writeReg(REG_FRFMSB, frf >> 16);
    writeReg(REG_FRFMSB+1, frf >> 8);
    writeReg(REG_FRFMSB+2, frf);

    rxstate = TXIDLE;
}

uint8_t* recvBuf;

uint16_t RF69::recvDone_compat (uint8_t* buf) {
    switch (rxstate) {
    case TXIDLE:
        //rxfill = rf12_len = 0;
        rxfill = rf12_buf[2] = 0;
        crc = _crc16_update(~0, group);
        recvBuf = buf;
        rxstate = TXRECV;
        flushFifo();
        writeReg(REG_DIOMAPPING1, DMAP1_SYNCADDRESS);    // Interrupt trigger
        setMode(MODE_RECEIVER);
        writeReg(REG_AFCFEI, AFC_CLEAR);
        break;
    case TXRECV:
        if (rxfill >= rf12_len + 5 || rxfill >= RF_MAX) {
            rxstate = TXIDLE;
            setMode(MODE_STANDBY);
            if (crc != 0 | rf12_len > RF12_MAXDATA)
                return 1; // force bad crc for invalid packet
            if (!(rf12_hdr & RF12_HDR_DST) || node == 31 ||
                    (rf12_hdr & RF12_HDR_MASK) == node)
                return 0; // it's for us, good packet received
        }
        break;
    }
    return ~0; // keep going, not done yet
}

void RF69::sendStart_compat (uint8_t hdr, const void* ptr, uint8_t len) {
    rf12_buf[2] = len;
    for (int i = 0; i < len; ++i)
        rf12_data[i] = ((const uint8_t*) ptr)[i];
    rf12_hdr = hdr & RF12_HDR_DST ? hdr : (hdr & ~RF12_HDR_MASK) + node;
    crc = _crc16_update(~0, group);
    rxstate = - (2 + rf12_len); // preamble and SYN1/SYN2 are sent by hardware
    flushFifo();
    setMode(MODE_TRANSMITTER);
    writeReg(REG_DIOMAPPING1, 0x00); // PacketSent

    // use busy polling until the last byte fits into the buffer
    // this makes sure it all happens on time, and that sendWait can sleep
    while (rxstate < TXDONE)
        if ((readReg(REG_IRQFLAGS2) & IRQ2_FIFOFULL) == 0) {
            uint8_t out = 0xAA;
            if (rxstate < 0) {
                out = recvBuf[3 + rf12_len + rxstate];
                crc = _crc16_update(crc, out);
            } else {
                switch (rxstate) {
                    case TXCRC1: out = crc; break;
                    case TXCRC2: out = crc >> 8; break;
                }
            }
            writeReg(REG_FIFO, out);
            ++rxstate;
        }
}

void RF69::interrupt_compat () {
    if (rxstate == TXRECV) {
        // The following line attempts to stop further interrupts
        writeReg(REG_DIOMAPPING1, 0x40);  // Interrupt on PayloadReady
        rssi = readReg(REG_RSSIVALUE);
        IRQ_ENABLE; // allow nested interrupts from here on
        for (;;) { // busy loop, to get each data byte as soon as it comes in
            if (readReg(REG_IRQFLAGS2) & (IRQ2_FIFONOTEMPTY|IRQ2_FIFOOVERRUN)) {
                if (rxfill == 0)
                    recvBuf[rxfill++] = group;
                uint8_t in = readReg(REG_FIFO);
                recvBuf[rxfill++] = in;
                crc = _crc16_update(crc, in);
                if (rxfill >= rf12_len + 5 || rxfill >= RF_MAX)
                    break;
            }
        }
    } else if (readReg(REG_IRQFLAGS2) & IRQ2_PACKETSENT) {
        // rxstate will be TXDONE at this point
        rxstate = TXIDLE;
        setMode(MODE_STANDBY);
        writeReg(REG_DIOMAPPING1, 0x80); // SyncAddress
    }
}
<|endoftext|>