<commit_before>/**
 * Copyright (c) 2016 zScale Technology GmbH <legal@zscale.io>
 * Authors:
 *   - Paul Asmuth <paul@zscale.io>
 *   - Laura Schlimmer <laura@zscale.io>
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License ("the license") as
 * published by the Free Software Foundation, either version 3 of the License,
 * or any later version.
 *
 * In accordance with Section 7(e) of the license, the licensing of the Program
 * under the license does not imply a trademark license. Therefore any rights,
 * title and interest in our trademarks remain entirely with us.
 *
 * This program 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 license for more details.
 *
 * You can be released from the requirements of the license by purchasing a
 * commercial license. Buying such a license is mandatory as soon as you develop
 * commercial activities involving this program without disclosing the source
 * code of your own applications
 */
#include "eventql/server/sql/pipelined_expression.h"
#include "eventql/server/sql/codec/binary_codec.h"

namespace eventql {

PipelinedExpression::PipelinedExpression(
    csql::Transaction* txn,
    csql::ExecutionContext* ctx,
    const String& db_namespace,
    AnalyticsAuth* auth,
    size_t max_concurrency) :
    txn_(txn),
    ctx_(ctx),
    db_namespace_(db_namespace),
    auth_(auth),
    max_concurrency_(max_concurrency),
    num_columns_(0),
    eof_(false),
    error_(false),
    cancelled_(false),
    queries_started_(0),
    queries_finished_(0) {};

PipelinedExpression::~PipelinedExpression() {
  cancelled_ = true;
  cv_.notify_all();
  for (size_t i = 0; i < threads_.size(); ++i) {
    threads_[i].join();
  }
}

void PipelinedExpression::addLocalQuery(ScopedPtr<csql::TableExpression> expr) {
  num_columns_ = std::max(num_columns_, expr->getNumColumns());
  ctx_->incrementNumTasks();

  queries_.emplace_back(QuerySpec {
    .is_local = true,
    .expr = std::move(expr)
  });
}

void PipelinedExpression::addRemoteQuery(
    RefPtr<csql::TableExpressionNode> qtree,
    Vector<ReplicaRef> hosts) {
  num_columns_ = std::max(num_columns_, qtree->getNumComputedColumns());
  ctx_->incrementNumTasks();

  queries_.emplace_back(QuerySpec {
    .is_local = false,
    .qtree = qtree,
    .hosts = hosts
  });
}

ScopedPtr<csql::ResultCursor> PipelinedExpression::execute() {
  size_t num_threads = std::min(queries_.size(), max_concurrency_);
  for (size_t i = 0; i < num_threads; ++i) {
    threads_.emplace_back(
        std::thread(std::bind(&PipelinedExpression::executeAsync, this)));
  }

  return mkScoped(
    new csql::DefaultResultCursor(
        num_columns_,
        std::bind(
            &PipelinedExpression::next,
            this,
            std::placeholders::_1,
            std::placeholders::_2)));
}

size_t PipelinedExpression::getNumColumns() const {
  return num_columns_;
}

void PipelinedExpression::executeAsync() {
  while (!cancelled_) {
    std::unique_lock<std::mutex> lk(mutex_);
    auto query_idx = queries_started_++;
    lk.unlock();

    if (query_idx >= queries_.size()) {
      break;
    }

    ctx_->incrementNumTasksRunning();

    const auto& query = queries_[query_idx];
    String error;
    try {
      if (query.is_local) {
        executeLocal(query);
      } else {
        executeRemote(query);
      }
    } catch (const StandardException& e) {
      error = e.what();
    }

    ctx_->incrementNumTasksCompleted();

    lk.lock();
    error_ = !error.empty();
    error_str_ += error;
    eof_ = ++queries_finished_ == queries_.size();
    cv_.notify_all();

    if (cancelled_ || error_) {
      break;
    }
  }
}

void PipelinedExpression::executeLocal(const QuerySpec& query) {
  auto cursor = query.expr->execute();
  Vector<csql::SValue> row(cursor->getNumColumns());
  while (cursor->next(row.data(), row.size())) {
    if (!returnRow(&*row.cbegin(), &*row.cend())) {
      return;
    }
  }
}

void PipelinedExpression::executeRemote(const QuerySpec& query) {
  size_t row_ctr = 0;

  for (size_t i = 0; i < query.hosts.size(); ++i) {
    try {
      executeOnHost(query.qtree, query.hosts[i].addr, &row_ctr);
      break;
    } catch (const StandardException& e) {
      logError(
          "eventql",
          e,
          "PipelinedExpression::executeOnHost failed @ $0",
          query.hosts[i].addr.hostAndPort());

      if (row_ctr > 0 || i + 1 == query.hosts.size()) {
        throw e;
      }
    }
  }
}

void PipelinedExpression::executeOnHost(
    RefPtr<csql::TableExpressionNode> qtree,
    const InetAddr& host,
    size_t* row_ctr) {
  Buffer req_body;
  auto req_body_os = BufferOutputStream::fromBuffer(&req_body);
  csql::QueryTreeCoder qtree_coder(txn_);
  qtree_coder.encode(qtree.get(), req_body_os.get());

  bool error = false;
  csql::BinaryResultParser res_parser;

  res_parser.onRow([this, row_ctr] (int argc, const csql::SValue* argv) {
    if (returnRow(argv, argv + argc)) {
      ++(*row_ctr);
    } else {
      RAISE(kCancelledError);
    }
  });

  res_parser.onError([this, &error] (const String& error_str) {
    error = true;
    RAISE(kRuntimeError, error_str);
  });

  auto url = StringUtil::format(
      "http://$0/api/v1/sql/execute_qtree",
      host.ipAndPort());

  AnalyticsPrivileges privileges;
  privileges.set_allow_private_api_read_access(true);
  auto api_token = auth_->getPrivateAPIToken(db_namespace_, privileges);

  http::HTTPMessage::HeaderList auth_headers;
  auth_headers.emplace_back(
      "Authorization",
      StringUtil::format("Token $0", api_token));

  http::HTTPClient http_client(&z1stats()->http_client_stats);
  auto req = http::HTTPRequest::mkPost(url, req_body, auth_headers);
  auto res = http_client.executeRequest(
      req,
      http::StreamingResponseHandler::getFactory(
          std::bind(
              &csql::BinaryResultParser::parse,
              &res_parser,
              std::placeholders::_1,
              std::placeholders::_2)));

  if (!res_parser.eof() || error) {
    RAISE(kRuntimeError, "lost connection to upstream server");
  }

  if (res.statusCode() != 200) {
    RAISEF(
        kRuntimeError,
        "HTTP Error: $0",
        res.statusCode());
  }
}

bool PipelinedExpression::returnRow(
    const csql::SValue* begin,
    const csql::SValue* end) {
  std::unique_lock<std::mutex> lk(mutex_);

  while (!cancelled_ && !error_ && buf_.size() > kMaxBufferSize) {
    cv_.wait(lk);
  }

  if (cancelled_ || error_) {
    return false;
  }

  buf_.emplace_back(begin, end);
  cv_.notify_all();
  return true;
}

bool PipelinedExpression::next(csql::SValue* out_row, size_t out_row_len) {
  std::unique_lock<std::mutex> lk(mutex_);

  while (buf_.size() == 0 && !eof_ && !error_ && !cancelled_) {
    cv_.wait(lk);
  }

  if (cancelled_) {
    return false;
  }

  if (error_) {
    RAISEF(kIOError, "ERROR: $0", error_str_);
  }

  if (eof_ && buf_.size() == 0) {
    return false;
  }

  const auto& row = buf_.front();
  for (size_t i = 0; i < row.size() && i < out_row_len; ++i) {
    out_row[i] = row[i];
  }

  buf_.pop_front();
  cv_.notify_all();
  return true;
}

} // namespace eventql
<commit_msg>gcc doesn't support c-style struct initialization<commit_after>/**
 * Copyright (c) 2016 zScale Technology GmbH <legal@zscale.io>
 * Authors:
 *   - Paul Asmuth <paul@zscale.io>
 *   - Laura Schlimmer <laura@zscale.io>
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License ("the license") as
 * published by the Free Software Foundation, either version 3 of the License,
 * or any later version.
 *
 * In accordance with Section 7(e) of the license, the licensing of the Program
 * under the license does not imply a trademark license. Therefore any rights,
 * title and interest in our trademarks remain entirely with us.
 *
 * This program 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 license for more details.
 *
 * You can be released from the requirements of the license by purchasing a
 * commercial license. Buying such a license is mandatory as soon as you develop
 * commercial activities involving this program without disclosing the source
 * code of your own applications
 */
#include "eventql/server/sql/pipelined_expression.h"
#include "eventql/server/sql/codec/binary_codec.h"

namespace eventql {

PipelinedExpression::PipelinedExpression(
    csql::Transaction* txn,
    csql::ExecutionContext* ctx,
    const String& db_namespace,
    AnalyticsAuth* auth,
    size_t max_concurrency) :
    txn_(txn),
    ctx_(ctx),
    db_namespace_(db_namespace),
    auth_(auth),
    max_concurrency_(max_concurrency),
    num_columns_(0),
    eof_(false),
    error_(false),
    cancelled_(false),
    queries_started_(0),
    queries_finished_(0) {};

PipelinedExpression::~PipelinedExpression() {
  cancelled_ = true;
  cv_.notify_all();
  for (size_t i = 0; i < threads_.size(); ++i) {
    threads_[i].join();
  }
}

void PipelinedExpression::addLocalQuery(ScopedPtr<csql::TableExpression> expr) {
  num_columns_ = std::max(num_columns_, expr->getNumColumns());
  ctx_->incrementNumTasks();

  QuerySpec query_spec;
  query_spec.is_local = true;
  query_spec.expr = std::move(expr);
  queries_.emplace_back(std::move(query_spec));
}

void PipelinedExpression::addRemoteQuery(
    RefPtr<csql::TableExpressionNode> qtree,
    Vector<ReplicaRef> hosts) {
  num_columns_ = std::max(num_columns_, qtree->getNumComputedColumns());
  ctx_->incrementNumTasks();

  QuerySpec query_spec;
  query_spec.is_local = false;
  query_spec.qtree = qtree;
  query_spec.hosts = hosts;
  queries_.emplace_back(std::move(query_spec));
}

ScopedPtr<csql::ResultCursor> PipelinedExpression::execute() {
  size_t num_threads = std::min(queries_.size(), max_concurrency_);
  for (size_t i = 0; i < num_threads; ++i) {
    threads_.emplace_back(
        std::thread(std::bind(&PipelinedExpression::executeAsync, this)));
  }

  return mkScoped(
    new csql::DefaultResultCursor(
        num_columns_,
        std::bind(
            &PipelinedExpression::next,
            this,
            std::placeholders::_1,
            std::placeholders::_2)));
}

size_t PipelinedExpression::getNumColumns() const {
  return num_columns_;
}

void PipelinedExpression::executeAsync() {
  while (!cancelled_) {
    std::unique_lock<std::mutex> lk(mutex_);
    auto query_idx = queries_started_++;
    lk.unlock();

    if (query_idx >= queries_.size()) {
      break;
    }

    ctx_->incrementNumTasksRunning();

    const auto& query = queries_[query_idx];
    String error;
    try {
      if (query.is_local) {
        executeLocal(query);
      } else {
        executeRemote(query);
      }
    } catch (const StandardException& e) {
      error = e.what();
    }

    ctx_->incrementNumTasksCompleted();

    lk.lock();
    error_ = !error.empty();
    error_str_ += error;
    eof_ = ++queries_finished_ == queries_.size();
    cv_.notify_all();

    if (cancelled_ || error_) {
      break;
    }
  }
}

void PipelinedExpression::executeLocal(const QuerySpec& query) {
  auto cursor = query.expr->execute();
  Vector<csql::SValue> row(cursor->getNumColumns());
  while (cursor->next(row.data(), row.size())) {
    if (!returnRow(&*row.cbegin(), &*row.cend())) {
      return;
    }
  }
}

void PipelinedExpression::executeRemote(const QuerySpec& query) {
  size_t row_ctr = 0;

  for (size_t i = 0; i < query.hosts.size(); ++i) {
    try {
      executeOnHost(query.qtree, query.hosts[i].addr, &row_ctr);
      break;
    } catch (const StandardException& e) {
      logError(
          "eventql",
          e,
          "PipelinedExpression::executeOnHost failed @ $0",
          query.hosts[i].addr.hostAndPort());

      if (row_ctr > 0 || i + 1 == query.hosts.size()) {
        throw e;
      }
    }
  }
}

void PipelinedExpression::executeOnHost(
    RefPtr<csql::TableExpressionNode> qtree,
    const InetAddr& host,
    size_t* row_ctr) {
  Buffer req_body;
  auto req_body_os = BufferOutputStream::fromBuffer(&req_body);
  csql::QueryTreeCoder qtree_coder(txn_);
  qtree_coder.encode(qtree.get(), req_body_os.get());

  bool error = false;
  csql::BinaryResultParser res_parser;

  res_parser.onRow([this, row_ctr] (int argc, const csql::SValue* argv) {
    if (returnRow(argv, argv + argc)) {
      ++(*row_ctr);
    } else {
      RAISE(kCancelledError);
    }
  });

  res_parser.onError([this, &error] (const String& error_str) {
    error = true;
    RAISE(kRuntimeError, error_str);
  });

  auto url = StringUtil::format(
      "http://$0/api/v1/sql/execute_qtree",
      host.ipAndPort());

  AnalyticsPrivileges privileges;
  privileges.set_allow_private_api_read_access(true);
  auto api_token = auth_->getPrivateAPIToken(db_namespace_, privileges);

  http::HTTPMessage::HeaderList auth_headers;
  auth_headers.emplace_back(
      "Authorization",
      StringUtil::format("Token $0", api_token));

  http::HTTPClient http_client(&z1stats()->http_client_stats);
  auto req = http::HTTPRequest::mkPost(url, req_body, auth_headers);
  auto res = http_client.executeRequest(
      req,
      http::StreamingResponseHandler::getFactory(
          std::bind(
              &csql::BinaryResultParser::parse,
              &res_parser,
              std::placeholders::_1,
              std::placeholders::_2)));

  if (!res_parser.eof() || error) {
    RAISE(kRuntimeError, "lost connection to upstream server");
  }

  if (res.statusCode() != 200) {
    RAISEF(
        kRuntimeError,
        "HTTP Error: $0",
        res.statusCode());
  }
}

bool PipelinedExpression::returnRow(
    const csql::SValue* begin,
    const csql::SValue* end) {
  std::unique_lock<std::mutex> lk(mutex_);

  while (!cancelled_ && !error_ && buf_.size() > kMaxBufferSize) {
    cv_.wait(lk);
  }

  if (cancelled_ || error_) {
    return false;
  }

  buf_.emplace_back(begin, end);
  cv_.notify_all();
  return true;
}

bool PipelinedExpression::next(csql::SValue* out_row, size_t out_row_len) {
  std::unique_lock<std::mutex> lk(mutex_);

  while (buf_.size() == 0 && !eof_ && !error_ && !cancelled_) {
    cv_.wait(lk);
  }

  if (cancelled_) {
    return false;
  }

  if (error_) {
    RAISEF(kIOError, "ERROR: $0", error_str_);
  }

  if (eof_ && buf_.size() == 0) {
    return false;
  }

  const auto& row = buf_.front();
  for (size_t i = 0; i < row.size() && i < out_row_len; ++i) {
    out_row[i] = row[i];
  }

  buf_.pop_front();
  cv_.notify_all();
  return true;
}

} // namespace eventql
<|endoftext|>


<commit_before>#include <QFileInfo>
#include <QDir>
#include <QFile>
#include <QStringList>
#include <QDirIterator>
#include <QCoreApplication>
#include "debug.h"
#include "fileutils.h"
#include "settingsmodel.h"

QStringList FileUtils::safeSubpaths_;

//https://qt.gitorious.org/qt-creator/qt-creator/source/1a37da73abb60ad06b7e33983ca51b266be5910e:src/app/main.cpp#L13-189
// taken from utils/fileutils.cpp. We can not use utils here since that depends app_version.h.
bool FileUtils::copy(const QString& srcPath, const QString& dstPath)
{
    QFileInfo srcFi(srcPath);
    //Directory
    if (srcFi.isDir())
    {
        QDir().mkpath(dstPath);
        if (!QFileInfo(dstPath).exists())
        {
            DBG << "ERROR: Failed to create directory" << dstPath;
            return false;
        }
        QDir srcDir(srcPath);
        QStringList fileNames = srcDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
        for (const QString& fileName : fileNames)
        {
            const QString newSrcPath
                    = srcPath + QLatin1Char('/') + fileName;
            const QString newDstPath
                    = dstPath + QLatin1Char('/') + fileName;
            if (!copy(newSrcPath, newDstPath))
                return false;
        }
    }
    //File
    else if (srcFi.isFile())
    {
        QFile dstFile(dstPath);
        DBG << "Copying" << srcPath << "to" << dstPath;
        FileUtils::safeRemove(dstFile);
        if (dstFile.exists())
        {
            DBG << "Cannot overwrite file" << dstPath;
            return false;
        }
        if (!QFile::copy(srcPath, dstPath))
        {
            DBG << "Failure to copy " << srcPath << "to" << dstPath;
            return false;
        }
    }
    else
    {
        DBG << srcPath << "does not exist";
        return false;
    }
    return true;
}

bool FileUtils::move(const QString& srcPath, const QString& dstPath)
{
    QFileInfo srcFi(srcPath);
    //Directory
    if (srcFi.isDir())
    {
        QDir().mkpath(dstPath);
        if (!QFileInfo(dstPath).exists())
        {
            DBG << "ERROR: Failed to create directory" << dstPath;
            return false;
        }
        QDir srcDir(srcPath);
        QStringList fileNames = srcDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
        for (const QString& fileName : fileNames)
        {
            const QString newSrcPath
                    = srcPath + QLatin1Char('/') + fileName;
            const QString newDstPath
                    = dstPath + QLatin1Char('/') + fileName;
            if (!move(newSrcPath, newDstPath))
                return false;
        }
    }
    //File
    else if (srcFi.isFile())
    {
        QFile dstFile(dstPath);
        DBG << "Moving" << srcPath << "to" << dstPath;
        FileUtils::safeRemove(dstFile);
        if (dstFile.exists())
        {
            DBG << "Cannot overwrite file" << dstPath;
            return false;
        }
        if (!safeRename(srcPath, dstPath))
        {
            DBG << "Failure to copy " << srcPath << "to" << dstPath;
            return false;
        }
    }
    else
    {
        DBG << srcPath << "does not exist";
        return false;
    }
    return true;
}

qint64 FileUtils::dirSize(const QString& path)
{
    qint64 rVal = 0;
    QDirIterator it(path, QDir::Files, QDirIterator::Subdirectories);
    while (it.hasNext())
    {
        rVal += it.fileInfo().size();
        it.next();
    }

    return rVal;
}

bool FileUtils::rmCi(QString path)
{
    QString cPath = casedPath(path);
    if (cPath.length() <= 3) //D:/
        return false;

    return safeRemove(cPath);
}

QByteArray FileUtils::readFile(const QString& path)
{
    QByteArray rVal;
    QFile file(path);

    file.open(QFile::ReadOnly);
    if (!file.isReadable())
    {
        DBG << "Warning: unable to read file" << path;
        return rVal;
    }

    rVal = file.readAll();
    file.close();
    return rVal;
}

//Writes a new file
bool FileUtils::writeFile(const QByteArray& data, const QString& path)
{
    QFile file(path);
    QDir parentDir = QFileInfo(path).dir();
    parentDir.mkpath(".");
    safeRemove(file);
    file.open(QFile::WriteOnly);

    if (!file.isWritable())
    {
        DBG << "ERROR: file" << path << " is not writable.";
        return false;
    }
    DBG << "Writing file:" << path;
    file.write(data);
    file.close();
    return true;
}

QString FileUtils::casedPath(const QString& path)
{
    DBG << "Input:" << path;
    QString pathCi = QFileInfo(path).absoluteFilePath();
    //Construct case sentive path by comparing file or dir names to case sentive ones.
    QStringList ciNames = pathCi.split("/");
    QString casedPath = ciNames.first().endsWith(":") ? ciNames.first() + "/" : "/";
    ciNames.removeFirst(); //Drive letter on windows or "" on linux.

    for (const QString& ciName : ciNames)
    {
        QDirIterator it(casedPath);
        while (true)
        {
            if (!it.hasNext())
            {
                DBG << "ERROR: Unable to construct case sensitive path from" << path;
                return QString();
            }
            QFileInfo fi = it.next();
            if (fi.fileName().toUpper() == ciName.toUpper())
            {
                casedPath += (casedPath.endsWith("/") ? "" : "/") + fi.fileName();
                DBG << casedPath;
                break;
            }
        }
    }
    DBG << "Output:" << casedPath;
    return casedPath;
}

bool FileUtils::safeRename(const QString& srcPath, const QString& dstPath)
{
    if (!pathIsSafe(srcPath))
        return false;

    DBG << "Rename" << srcPath << "to"  << dstPath;
    return QFile::rename(srcPath, dstPath);
}

bool FileUtils::safeRemove(const QString& filePath)
{
    QFile file(filePath);
    return safeRemove(file);
}

bool FileUtils::safeRemove(QFile& file)
{
    QString path = QFileInfo(file).absoluteFilePath();
    if (!pathIsSafe(path))
        return false;

    DBG << "Removing" << path;
    return file.remove();
}

bool FileUtils::safeRemoveRecursively(QDir& dir)
{
    QString path = dir.absolutePath();
    if (!pathIsSafe(path))
        return false;

    DBG << "Removing" << path;
    return dir.removeRecursively();
}

bool FileUtils::safeRemoveRecursively(const QString& path)
{
    QDir dir = QDir(path);
    return safeRemoveRecursively(dir);
}

bool FileUtils::pathIsSafe(const QString& path)
{
    QString pathFull = QFileInfo(path).absoluteFilePath();
    QString pathUpper = pathFull.toUpper();
    QStringList safeSubpaths;
    safeSubpaths.append(SettingsModel::modDownloadPath());
    safeSubpaths.append(SettingsModel::arma3Path());
    safeSubpaths.append(SettingsModel::teamSpeak3Path());
    safeSubpaths.append(QCoreApplication::applicationDirPath());
    safeSubpaths.append(".");
    safeSubpaths.append(safeSubpaths_);

    for (QString safeSubpath : safeSubpaths)
    {
        QString safeUpper = QFileInfo(safeSubpath).absoluteFilePath().toUpper();
        //Shortest possible save path: C:/d (4 characters)
        if (safeUpper.length() >= 4 && pathUpper.startsWith(safeUpper))
            return true;
    }

    DBG << "ERROR: " << pathFull << "not in safe subpaths" << safeSubpaths << ". Operation aborted!";
    return false;
}

void FileUtils::appendSafePath(const QString& path)
{
    safeSubpaths_.append(path);
}
<commit_msg>More specific logging.<commit_after>#include <QFileInfo>
#include <QDir>
#include <QFile>
#include <QStringList>
#include <QDirIterator>
#include <QCoreApplication>
#include "debug.h"
#include "fileutils.h"
#include "settingsmodel.h"

QStringList FileUtils::safeSubpaths_;

//https://qt.gitorious.org/qt-creator/qt-creator/source/1a37da73abb60ad06b7e33983ca51b266be5910e:src/app/main.cpp#L13-189
// taken from utils/fileutils.cpp. We can not use utils here since that depends app_version.h.
bool FileUtils::copy(const QString& srcPath, const QString& dstPath)
{
    QFileInfo srcFi(srcPath);
    //Directory
    if (srcFi.isDir())
    {
        QDir().mkpath(dstPath);
        if (!QFileInfo(dstPath).exists())
        {
            DBG << "ERROR: Failed to create directory" << dstPath;
            return false;
        }
        QDir srcDir(srcPath);
        QStringList fileNames = srcDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
        for (const QString& fileName : fileNames)
        {
            const QString newSrcPath
                    = srcPath + QLatin1Char('/') + fileName;
            const QString newDstPath
                    = dstPath + QLatin1Char('/') + fileName;
            if (!copy(newSrcPath, newDstPath))
                return false;
        }
    }
    //File
    else if (srcFi.isFile())
    {
        QFile dstFile(dstPath);
        DBG << "Copying" << srcPath << "to" << dstPath;
        FileUtils::safeRemove(dstFile);
        if (dstFile.exists())
        {
            DBG << "Cannot overwrite file" << dstPath;
            return false;
        }
        if (!QFile::copy(srcPath, dstPath))
        {
            DBG << "Failure to copy " << srcPath << "to" << dstPath;
            return false;
        }
    }
    else
    {
        DBG << srcPath << "does not exist";
        return false;
    }
    return true;
}

bool FileUtils::move(const QString& srcPath, const QString& dstPath)
{
    QFileInfo srcFi(srcPath);
    //Directory
    if (srcFi.isDir())
    {
        QDir().mkpath(dstPath);
        if (!QFileInfo(dstPath).exists())
        {
            DBG << "ERROR: Failed to create directory" << dstPath;
            return false;
        }
        QDir srcDir(srcPath);
        QStringList fileNames = srcDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
        for (const QString& fileName : fileNames)
        {
            const QString newSrcPath
                    = srcPath + QLatin1Char('/') + fileName;
            const QString newDstPath
                    = dstPath + QLatin1Char('/') + fileName;
            if (!move(newSrcPath, newDstPath))
                return false;
        }
    }
    //File
    else if (srcFi.isFile())
    {
        QFile dstFile(dstPath);
        DBG << "Moving" << srcPath << "to" << dstPath;
        FileUtils::safeRemove(dstFile);
        if (dstFile.exists())
        {
            DBG << "Cannot overwrite file" << dstPath;
            return false;
        }
        if (!safeRename(srcPath, dstPath))
        {
            DBG << "Failure to copy " << srcPath << "to" << dstPath;
            return false;
        }
    }
    else
    {
        DBG << srcPath << "does not exist";
        return false;
    }
    return true;
}

qint64 FileUtils::dirSize(const QString& path)
{
    qint64 rVal = 0;
    QDirIterator it(path, QDir::Files, QDirIterator::Subdirectories);
    while (it.hasNext())
    {
        rVal += it.fileInfo().size();
        it.next();
    }

    return rVal;
}

bool FileUtils::rmCi(QString path)
{
    QString cPath = casedPath(path);
    if (cPath.length() <= 3) //D:/
        return false;

    return safeRemove(cPath);
}

QByteArray FileUtils::readFile(const QString& path)
{
    QByteArray rVal;
    QFile file(path);

    file.open(QFile::ReadOnly);
    if (!file.isReadable())
    {
        DBG << "Warning: unable to read file" << path;
        return rVal;
    }

    rVal = file.readAll();
    file.close();
    return rVal;
}

//Writes a new file
bool FileUtils::writeFile(const QByteArray& data, const QString& path)
{
    QFile file(path);
    QDir parentDir = QFileInfo(path).dir();
    parentDir.mkpath(".");
    safeRemove(file);
    file.open(QFile::WriteOnly);

    if (!file.isWritable())
    {
        DBG << "ERROR: file" << path << " is not writable.";
        return false;
    }
    DBG << "Writing file:" << path;
    file.write(data);
    file.close();
    return true;
}

QString FileUtils::casedPath(const QString& path)
{
    DBG << "Input:" << path;
    QString pathCi = QFileInfo(path).absoluteFilePath();
    //Construct case sentive path by comparing file or dir names to case sentive ones.
    QStringList ciNames = pathCi.split("/");
    QString casedPath = ciNames.first().endsWith(":") ? ciNames.first() + "/" : "/";
    ciNames.removeFirst(); //Drive letter on windows or "" on linux.

    for (const QString& ciName : ciNames)
    {
        QDirIterator it(casedPath);
        while (true)
        {
            if (!it.hasNext())
            {
                DBG << "ERROR: Unable to construct case sensitive path from" << path;
                return QString();
            }
            QFileInfo fi = it.next();
            if (fi.fileName().toUpper() == ciName.toUpper())
            {
                casedPath += (casedPath.endsWith("/") ? "" : "/") + fi.fileName();
                DBG << casedPath;
                break;
            }
        }
    }
    DBG << "Output:" << casedPath;
    return casedPath;
}

bool FileUtils::safeRename(const QString& srcPath, const QString& dstPath)
{
    if (!pathIsSafe(srcPath))
        return false;

    DBG << "Rename" << srcPath << "to"  << dstPath;
    return QFile::rename(srcPath, dstPath);
}

bool FileUtils::safeRemove(const QString& filePath)
{
    QFile file(filePath);
    return safeRemove(file);
}

bool FileUtils::safeRemove(QFile& file)
{
    QString path = QFileInfo(file).absoluteFilePath();

    if (pathIsSafe(path) && file.remove())
    {
        DBG << "Removed" << path;
        return true;
    }
    return false;
}

bool FileUtils::safeRemoveRecursively(QDir& dir)
{
    QString path = dir.absolutePath();

    if (pathIsSafe(path) && dir.removeRecursively())
    {
        DBG << "Removed" << path;
        return true;
    }
    return false;
}

bool FileUtils::safeRemoveRecursively(const QString& path)
{
    QDir dir = QDir(path);
    return safeRemoveRecursively(dir);
}

bool FileUtils::pathIsSafe(const QString& path)
{
    QString pathFull = QFileInfo(path).absoluteFilePath();
    QString pathUpper = pathFull.toUpper();
    QStringList safeSubpaths;
    safeSubpaths.append(SettingsModel::modDownloadPath());
    safeSubpaths.append(SettingsModel::arma3Path());
    safeSubpaths.append(SettingsModel::teamSpeak3Path());
    safeSubpaths.append(QCoreApplication::applicationDirPath());
    safeSubpaths.append(".");
    safeSubpaths.append(safeSubpaths_);

    for (QString safeSubpath : safeSubpaths)
    {
        QString safeUpper = QFileInfo(safeSubpath).absoluteFilePath().toUpper();
        //Shortest possible save path: C:/d (4 characters)
        if (safeUpper.length() >= 4 && pathUpper.startsWith(safeUpper))
            return true;
    }

    DBG << "ERROR: " << pathFull << "not in safe subpaths" << safeSubpaths << ". Operation aborted!";
    return false;
}

void FileUtils::appendSafePath(const QString& path)
{
    safeSubpaths_.append(path);
}
<|endoftext|>


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

#include "QUECTEL/BG96/QUECTEL_BG96_CellularStack.h"
#include "CellularLog.h"

using namespace mbed;

QUECTEL_BG96_CellularStack::QUECTEL_BG96_CellularStack(ATHandler &atHandler, int cid, nsapi_ip_stack_t stack_type) : AT_CellularStack(atHandler, cid, stack_type)
{
    _at.set_urc_handler("+QIURC:", mbed::Callback<void()>(this, &QUECTEL_BG96_CellularStack::urc_qiurc));
}

QUECTEL_BG96_CellularStack::~QUECTEL_BG96_CellularStack()
{
}

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

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

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

    int modem_connect_id = -1;
    int request_connect_id = socket->id;
    int err = -1;

    _at.lock();
    if (socket->proto == NSAPI_TCP) {
        _at.cmd_start("AT+QIOPEN=");
        _at.write_int(_cid);
        _at.write_int(request_connect_id);
        _at.write_string("TCP");
        _at.write_string(address.get_ip_address());
        _at.write_int(address.get_port());
        _at.write_int(socket->localAddress.get_port());
        _at.write_int(0);
        _at.cmd_stop();

        handle_open_socket_response(modem_connect_id, err);

        if ((_at.get_last_error() == NSAPI_ERROR_OK) && err) {
            if (err == BG96_SOCKET_BIND_FAIL) {
                socket->created = false;
                return NSAPI_ERROR_PARAMETER;
            }
            _at.cmd_start("AT+QICLOSE=");
            _at.write_int(modem_connect_id);
            _at.cmd_stop();
            _at.resp_start();
            _at.resp_stop();

            _at.cmd_start("AT+QIOPEN=");
            _at.write_int(_cid);
            _at.write_int(request_connect_id);
            _at.write_string("TCP");
            _at.write_string(address.get_ip_address());
            _at.write_int(address.get_port());
            _at.write_int(socket->localAddress.get_port());
            _at.write_int(0);
            _at.cmd_stop();

            handle_open_socket_response(modem_connect_id, err);
        }
    }

    // If opened successfully BUT not requested one, close it
    if (!err && (modem_connect_id != request_connect_id)) {
        _at.cmd_start("AT+QICLOSE=");
        _at.write_int(modem_connect_id);
        _at.cmd_stop();
        _at.resp_start();
        _at.resp_stop();
    }

    nsapi_error_t ret_val = _at.get_last_error();
    _at.unlock();

    if ((ret_val == NSAPI_ERROR_OK) && (modem_connect_id == request_connect_id)) {
        socket->created = true;
        socket->remoteAddress = address;
        socket->connected = true;
        return NSAPI_ERROR_OK;
    }

    return NSAPI_ERROR_NO_CONNECTION;
}

void QUECTEL_BG96_CellularStack::urc_qiurc()
{
    int sock_id = 0;

    _at.lock();
    (void) _at.skip_param();
    sock_id = _at.read_int();
    _at.unlock();

    for (int i = 0; i < get_max_socket_count(); i++) {
        CellularSocket *sock = _socket[i];
        if (sock && sock->id == sock_id) {
            if (sock->_cb) {
                sock->_cb(sock->_data);
            }
            break;
        }
    }
}

int QUECTEL_BG96_CellularStack::get_max_socket_count()
{
    return BG96_SOCKET_MAX;
}

bool QUECTEL_BG96_CellularStack::is_protocol_supported(nsapi_protocol_t protocol)
{
    return (protocol == NSAPI_UDP || protocol == NSAPI_TCP);
}

nsapi_error_t QUECTEL_BG96_CellularStack::socket_close_impl(int sock_id)
{
    _at.set_at_timeout(BG96_CLOSE_SOCKET_TIMEOUT);
    _at.cmd_start("AT+QICLOSE=");
    _at.write_int(sock_id);
    _at.cmd_stop_read_resp();
    _at.restore_at_timeout();

    return _at.get_last_error();
}

void QUECTEL_BG96_CellularStack::handle_open_socket_response(int &modem_connect_id, int &err)
{
    // OK
    _at.resp_start();
    _at.resp_stop();
    // QIOPEN -> should be handled as URC?
    _at.set_at_timeout(BG96_CREATE_SOCKET_TIMEOUT);
    _at.resp_start("+QIOPEN:");
    _at.restore_at_timeout();
    modem_connect_id = _at.read_int();
    err = _at.read_int();
}
nsapi_error_t QUECTEL_BG96_CellularStack::create_socket_impl(CellularSocket *socket)
{
    int modem_connect_id = -1;
    int request_connect_id = socket->id;
    int remote_port = 0;
    int err = -1;

    if (socket->proto == NSAPI_UDP && !socket->connected) {
        _at.cmd_start("AT+QIOPEN=");
        _at.write_int(_cid);
        _at.write_int(request_connect_id);
        _at.write_string("UDP SERVICE");
        _at.write_string("127.0.0.1");
        _at.write_int(remote_port);
        _at.write_int(socket->localAddress.get_port());
        _at.write_int(0);
        _at.cmd_stop();

        handle_open_socket_response(modem_connect_id, err);

        if ((_at.get_last_error() == NSAPI_ERROR_OK) && err) {
            if (err == BG96_SOCKET_BIND_FAIL) {
                socket->created = false;
                return NSAPI_ERROR_PARAMETER;
            }
            _at.cmd_start("AT+QICLOSE=");
            _at.write_int(modem_connect_id);
            _at.cmd_stop_read_resp();

            _at.cmd_start("AT+QIOPEN=");
            _at.write_int(_cid);
            _at.write_int(request_connect_id);
            _at.write_string("UDP SERVICE");
            _at.write_string("127.0.0.1");
            _at.write_int(remote_port);
            _at.write_int(socket->localAddress.get_port());
            _at.write_int(0);
            _at.cmd_stop();

            handle_open_socket_response(modem_connect_id, err);
        }
    } else if (socket->proto == NSAPI_UDP && socket->connected) {
        _at.cmd_start("AT+QIOPEN=");
        _at.write_int(_cid);
        _at.write_int(request_connect_id);
        _at.write_string("UDP");
        _at.write_string(socket->remoteAddress.get_ip_address());
        _at.write_int(socket->remoteAddress.get_port());
        _at.cmd_stop();

        handle_open_socket_response(modem_connect_id, err);

        if ((_at.get_last_error() == NSAPI_ERROR_OK) && err) {
            if (err == BG96_SOCKET_BIND_FAIL) {
                socket->created = false;
                return NSAPI_ERROR_PARAMETER;
            }
            _at.cmd_start("AT+QICLOSE=");
            _at.write_int(modem_connect_id);
            _at.cmd_stop_read_resp();

            _at.cmd_start("AT+QIOPEN=");
            _at.write_int(_cid);
            _at.write_int(request_connect_id);
            _at.write_string("UDP");
            _at.write_string(socket->remoteAddress.get_ip_address());
            _at.write_int(socket->remoteAddress.get_port());
            _at.cmd_stop();

            handle_open_socket_response(modem_connect_id, err);
        }
    }

    // If opened successfully BUT not requested one, close it
    if (!err && (modem_connect_id != request_connect_id)) {
        _at.cmd_start("AT+QICLOSE=");
        _at.write_int(modem_connect_id);
        _at.cmd_stop_read_resp();
    }

    nsapi_error_t ret_val = _at.get_last_error();

    socket->created = ((ret_val == NSAPI_ERROR_OK) && (modem_connect_id == request_connect_id));

    return ret_val;
}

nsapi_size_or_error_t QUECTEL_BG96_CellularStack::socket_sendto_impl(CellularSocket *socket, const SocketAddress &address,
                                                                     const void *data, nsapi_size_t size)
{
    if (size > BG96_MAX_SEND_SIZE) {
        return NSAPI_ERROR_PARAMETER;
    }

    int sent_len = 0;
    int sent_len_before = 0;
    int sent_len_after = 0;

    // Get the sent count before sending
    _at.cmd_start("AT+QISEND=");
    _at.write_int(socket->id);
    _at.write_int(0);
    _at.cmd_stop();

    _at.resp_start("+QISEND:");
    sent_len_before = _at.read_int();
    _at.resp_stop();

    // Send
    _at.cmd_start("AT+QISEND=");
    _at.write_int(socket->id);
    _at.write_int(size);
    if (socket->proto == NSAPI_UDP) {
        _at.write_string(address.get_ip_address());
        _at.write_int(address.get_port());
    }
    _at.cmd_stop();

    _at.resp_start(">");
    _at.write_bytes((uint8_t *)data, size);
    _at.resp_start();
    _at.set_stop_tag("\r\n");
    _at.resp_stop();

    // Get the sent count after sending
    _at.cmd_start("AT+QISEND=");
    _at.write_int(socket->id);
    _at.write_int(0);
    _at.cmd_stop();

    _at.resp_start("+QISEND:");
    sent_len_after = _at.read_int();
    _at.resp_stop();

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

    return _at.get_last_error();
}

nsapi_size_or_error_t QUECTEL_BG96_CellularStack::socket_recvfrom_impl(CellularSocket *socket, SocketAddress *address,
                                                                       void *buffer, nsapi_size_t size)
{
    nsapi_size_or_error_t recv_len = 0;
    int port;
    char ip_address[NSAPI_IP_SIZE + 1];

    _at.cmd_start("AT+QIRD=");
    _at.write_int(socket->id);
    if (socket->proto == NSAPI_TCP) {
        // do not read more than max size
        size = size > BG96_MAX_RECV_SIZE ? BG96_MAX_RECV_SIZE : size;
        _at.write_int(size);
    }
    _at.cmd_stop();

    _at.resp_start("+QIRD:");
    recv_len = _at.read_int();
    _at.read_string(ip_address, sizeof(ip_address));
    port = _at.read_int();
    if (recv_len > 0) {
        // do not read more than buffer size
        recv_len = recv_len > (nsapi_size_or_error_t)size ? size : recv_len;
        _at.read_bytes((uint8_t *)buffer, recv_len);
    }
    _at.resp_stop();

    // We block only if 0 recv length really means no data.
    // If 0 is followed by ip address and port can be an UDP 0 length packet
    if (!recv_len && port < 0) {
        return NSAPI_ERROR_WOULD_BLOCK;
    }

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

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

#include "QUECTEL/BG96/QUECTEL_BG96_CellularStack.h"
#include "CellularLog.h"

using namespace mbed;

const char *QIURC_RECV = "recv";
const uint8_t QIURC_RECV_LENGTH = 4;
const char *QIURC_CLOSED = "closed";
const uint8_t QIURC_CLOSED_LENGTH = 6;
const uint8_t MAX_QIURC_LENGTH = QIURC_CLOSED_LENGTH;

QUECTEL_BG96_CellularStack::QUECTEL_BG96_CellularStack(ATHandler &atHandler, int cid, nsapi_ip_stack_t stack_type) : AT_CellularStack(atHandler, cid, stack_type)
{
    _at.set_urc_handler("+QIURC:", mbed::Callback<void()>(this, &QUECTEL_BG96_CellularStack::urc_qiurc));
}

QUECTEL_BG96_CellularStack::~QUECTEL_BG96_CellularStack()
{
}

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

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

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

    int modem_connect_id = -1;
    int request_connect_id = socket->id;
    int err = -1;

    _at.lock();
    if (socket->proto == NSAPI_TCP) {
        _at.cmd_start("AT+QIOPEN=");
        _at.write_int(_cid);
        _at.write_int(request_connect_id);
        _at.write_string("TCP");
        _at.write_string(address.get_ip_address());
        _at.write_int(address.get_port());
        _at.write_int(socket->localAddress.get_port());
        _at.write_int(0);
        _at.cmd_stop();

        handle_open_socket_response(modem_connect_id, err);

        if ((_at.get_last_error() == NSAPI_ERROR_OK) && err) {
            if (err == BG96_SOCKET_BIND_FAIL) {
                socket->created = false;
                return NSAPI_ERROR_PARAMETER;
            }
            _at.cmd_start("AT+QICLOSE=");
            _at.write_int(modem_connect_id);
            _at.cmd_stop();
            _at.resp_start();
            _at.resp_stop();

            _at.cmd_start("AT+QIOPEN=");
            _at.write_int(_cid);
            _at.write_int(request_connect_id);
            _at.write_string("TCP");
            _at.write_string(address.get_ip_address());
            _at.write_int(address.get_port());
            _at.write_int(socket->localAddress.get_port());
            _at.write_int(0);
            _at.cmd_stop();

            handle_open_socket_response(modem_connect_id, err);
        }
    }

    // If opened successfully BUT not requested one, close it
    if (!err && (modem_connect_id != request_connect_id)) {
        _at.cmd_start("AT+QICLOSE=");
        _at.write_int(modem_connect_id);
        _at.cmd_stop();
        _at.resp_start();
        _at.resp_stop();
    }

    nsapi_error_t ret_val = _at.get_last_error();
    _at.unlock();

    if ((ret_val == NSAPI_ERROR_OK) && (modem_connect_id == request_connect_id)) {
        socket->created = true;
        socket->remoteAddress = address;
        socket->connected = true;
        return NSAPI_ERROR_OK;
    }

    return NSAPI_ERROR_NO_CONNECTION;
}

void QUECTEL_BG96_CellularStack::urc_qiurc()
{
    char urc_string[MAX_QIURC_LENGTH + 1];
    _at.lock();
    const int urc_string_length = _at.read_string(urc_string, sizeof(urc_string));
    const int sock_id = _at.read_int();
    const nsapi_error_t err = _at.unlock_return_error();

    if (err != NSAPI_ERROR_OK) {
        return;
    }

    bool recv = strcmp(urc_string, "recv") == 0;
    bool closed = strcmp(urc_string, "closed") == 0;

    CellularSocket *sock = find_socket(sock_id);
    if (sock) {
        if (closed) {
            tr_error("Socket closed %d", sock_id);
            sock->closed = true;
        }

        if (recv || closed) {
            if (sock->_cb) {
                sock->_cb(sock->_data);
            }
        }
    }
}

int QUECTEL_BG96_CellularStack::get_max_socket_count()
{
    return BG96_SOCKET_MAX;
}

bool QUECTEL_BG96_CellularStack::is_protocol_supported(nsapi_protocol_t protocol)
{
    return (protocol == NSAPI_UDP || protocol == NSAPI_TCP);
}

nsapi_error_t QUECTEL_BG96_CellularStack::socket_close_impl(int sock_id)
{
    _at.set_at_timeout(BG96_CLOSE_SOCKET_TIMEOUT);
    _at.cmd_start("AT+QICLOSE=");
    _at.write_int(sock_id);
    _at.cmd_stop_read_resp();
    _at.restore_at_timeout();

    return _at.get_last_error();
}

void QUECTEL_BG96_CellularStack::handle_open_socket_response(int &modem_connect_id, int &err)
{
    // OK
    _at.resp_start();
    _at.resp_stop();
    // QIOPEN -> should be handled as URC?
    _at.set_at_timeout(BG96_CREATE_SOCKET_TIMEOUT);
    _at.resp_start("+QIOPEN:");
    _at.restore_at_timeout();
    modem_connect_id = _at.read_int();
    err = _at.read_int();
}
nsapi_error_t QUECTEL_BG96_CellularStack::create_socket_impl(CellularSocket *socket)
{
    int modem_connect_id = -1;
    int request_connect_id = socket->id;
    int remote_port = 0;
    int err = -1;

    if (socket->proto == NSAPI_UDP && !socket->connected) {
        _at.cmd_start("AT+QIOPEN=");
        _at.write_int(_cid);
        _at.write_int(request_connect_id);
        _at.write_string("UDP SERVICE");
        _at.write_string("127.0.0.1");
        _at.write_int(remote_port);
        _at.write_int(socket->localAddress.get_port());
        _at.write_int(0);
        _at.cmd_stop();

        handle_open_socket_response(modem_connect_id, err);

        if ((_at.get_last_error() == NSAPI_ERROR_OK) && err) {
            if (err == BG96_SOCKET_BIND_FAIL) {
                socket->created = false;
                return NSAPI_ERROR_PARAMETER;
            }
            _at.cmd_start("AT+QICLOSE=");
            _at.write_int(modem_connect_id);
            _at.cmd_stop_read_resp();

            _at.cmd_start("AT+QIOPEN=");
            _at.write_int(_cid);
            _at.write_int(request_connect_id);
            _at.write_string("UDP SERVICE");
            _at.write_string("127.0.0.1");
            _at.write_int(remote_port);
            _at.write_int(socket->localAddress.get_port());
            _at.write_int(0);
            _at.cmd_stop();

            handle_open_socket_response(modem_connect_id, err);
        }
    } else if (socket->proto == NSAPI_UDP && socket->connected) {
        _at.cmd_start("AT+QIOPEN=");
        _at.write_int(_cid);
        _at.write_int(request_connect_id);
        _at.write_string("UDP");
        _at.write_string(socket->remoteAddress.get_ip_address());
        _at.write_int(socket->remoteAddress.get_port());
        _at.cmd_stop();

        handle_open_socket_response(modem_connect_id, err);

        if ((_at.get_last_error() == NSAPI_ERROR_OK) && err) {
            if (err == BG96_SOCKET_BIND_FAIL) {
                socket->created = false;
                return NSAPI_ERROR_PARAMETER;
            }
            _at.cmd_start("AT+QICLOSE=");
            _at.write_int(modem_connect_id);
            _at.cmd_stop_read_resp();

            _at.cmd_start("AT+QIOPEN=");
            _at.write_int(_cid);
            _at.write_int(request_connect_id);
            _at.write_string("UDP");
            _at.write_string(socket->remoteAddress.get_ip_address());
            _at.write_int(socket->remoteAddress.get_port());
            _at.cmd_stop();

            handle_open_socket_response(modem_connect_id, err);
        }
    }

    // If opened successfully BUT not requested one, close it
    if (!err && (modem_connect_id != request_connect_id)) {
        _at.cmd_start("AT+QICLOSE=");
        _at.write_int(modem_connect_id);
        _at.cmd_stop_read_resp();
    }

    nsapi_error_t ret_val = _at.get_last_error();

    socket->created = ((ret_val == NSAPI_ERROR_OK) && (modem_connect_id == request_connect_id));

    return ret_val;
}

nsapi_size_or_error_t QUECTEL_BG96_CellularStack::socket_sendto_impl(CellularSocket *socket, const SocketAddress &address,
                                                                     const void *data, nsapi_size_t size)
{
    if (size > BG96_MAX_SEND_SIZE) {
        return NSAPI_ERROR_PARAMETER;
    }

    int sent_len = 0;
    int sent_len_before = 0;
    int sent_len_after = 0;

    // Get the sent count before sending
    _at.cmd_start("AT+QISEND=");
    _at.write_int(socket->id);
    _at.write_int(0);
    _at.cmd_stop();

    _at.resp_start("+QISEND:");
    sent_len_before = _at.read_int();
    _at.resp_stop();

    // Send
    _at.cmd_start("AT+QISEND=");
    _at.write_int(socket->id);
    _at.write_int(size);
    if (socket->proto == NSAPI_UDP) {
        _at.write_string(address.get_ip_address());
        _at.write_int(address.get_port());
    }
    _at.cmd_stop();

    _at.resp_start(">");
    _at.write_bytes((uint8_t *)data, size);
    _at.resp_start();
    _at.set_stop_tag("\r\n");
    _at.resp_stop();

    // Get the sent count after sending
    _at.cmd_start("AT+QISEND=");
    _at.write_int(socket->id);
    _at.write_int(0);
    _at.cmd_stop();

    _at.resp_start("+QISEND:");
    sent_len_after = _at.read_int();
    _at.resp_stop();

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

    return _at.get_last_error();
}

nsapi_size_or_error_t QUECTEL_BG96_CellularStack::socket_recvfrom_impl(CellularSocket *socket, SocketAddress *address,
                                                                       void *buffer, nsapi_size_t size)
{
    nsapi_size_or_error_t recv_len = 0;
    int port;
    char ip_address[NSAPI_IP_SIZE + 1];

    _at.cmd_start("AT+QIRD=");
    _at.write_int(socket->id);
    if (socket->proto == NSAPI_TCP) {
        // do not read more than max size
        size = size > BG96_MAX_RECV_SIZE ? BG96_MAX_RECV_SIZE : size;
        _at.write_int(size);
    }
    _at.cmd_stop();

    _at.resp_start("+QIRD:");
    recv_len = _at.read_int();
    _at.read_string(ip_address, sizeof(ip_address));
    port = _at.read_int();
    if (recv_len > 0) {
        // do not read more than buffer size
        recv_len = recv_len > (nsapi_size_or_error_t)size ? size : recv_len;
        _at.read_bytes((uint8_t *)buffer, recv_len);
    }
    _at.resp_stop();

    // We block only if 0 recv length really means no data.
    // If 0 is followed by ip address and port can be an UDP 0 length packet
    if (!recv_len && port < 0) {
        return NSAPI_ERROR_WOULD_BLOCK;
    }

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

    return recv_len;
}
<|endoftext|>


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

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

#ifndef _OSL_DIAGNOSE_H_
#include <osl/diagnose.h>
#endif

#ifndef INCLUDED_RTL_INSTANCE_HXX
#include <rtl/instance.hxx>
#endif

#ifndef _BGFX_MATRIX_B2DHOMMATRIX_HXX
#include <basegfx/matrix/b2dhommatrix.hxx>
#endif

#ifndef _HOMMATRIX_TEMPLATE_HXX
#include <hommatrixtemplate.hxx>
#endif

#ifndef _BGFX_MATRIX_B3DHOMMATRIX_HXX
#include <basegfx/matrix/b3dhommatrix.hxx>
#endif

#ifndef _BGFX_TUPLE_B3DTUPLE_HXX
#include <basegfx/tuple/b3dtuple.hxx>
#endif

#ifndef _BGFX_TUPLE_B2DTUPLE_HXX
#include <basegfx/tuple/b2dtuple.hxx>
#endif

#ifndef _BGFX_VECTOR_B2DVECTOR_HXX
#include <basegfx/vector/b2dvector.hxx>
#endif

namespace basegfx
{
    class Impl2DHomMatrix : public ::basegfx::internal::ImplHomMatrixTemplate< 3 >
    {
    };

    namespace { struct IdentityMatrix : public rtl::Static< B2DHomMatrix::ImplType,
                                                            IdentityMatrix > {}; }

    B2DHomMatrix::B2DHomMatrix() :
        mpImpl( IdentityMatrix::get() ) // use common identity matrix
    {
    }

    B2DHomMatrix::B2DHomMatrix(const B2DHomMatrix& rMat) :
        mpImpl(rMat.mpImpl)
    {
    }

    B2DHomMatrix::~B2DHomMatrix()
    {
    }

    B2DHomMatrix& B2DHomMatrix::operator=(const B2DHomMatrix& rMat)
    {
        mpImpl = rMat.mpImpl;
        return *this;
    }

    void B2DHomMatrix::makeUnique()
    {
        mpImpl.make_unique();
    }

    double B2DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const
    {
        return mpImpl->get(nRow, nColumn);
    }

    void B2DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
    {
        mpImpl->set(nRow, nColumn, fValue);
    }

    bool B2DHomMatrix::isLastLineDefault() const
    {
        return mpImpl->isLastLineDefault();
    }

    bool B2DHomMatrix::isIdentity() const
    {
        if(mpImpl.same_object(IdentityMatrix::get()))
            return true;

        return mpImpl->isIdentity();
    }

    void B2DHomMatrix::identity()
    {
        mpImpl = IdentityMatrix::get();
    }

    bool B2DHomMatrix::isInvertible() const
    {
        return mpImpl->isInvertible();
    }

    bool B2DHomMatrix::invert()
    {
        Impl2DHomMatrix aWork(*mpImpl);
        sal_uInt16* pIndex = new sal_uInt16[mpImpl->getEdgeLength()];
        sal_Int16 nParity;

        if(aWork.ludcmp(pIndex, nParity))
        {
            mpImpl->doInvert(aWork, pIndex);
            delete[] pIndex;

            return true;
        }

        delete[] pIndex;
        return false;
    }

    bool B2DHomMatrix::isNormalized() const
    {
        return mpImpl->isNormalized();
    }

    void B2DHomMatrix::normalize()
    {
        if(!const_cast<const B2DHomMatrix*>(this)->mpImpl->isNormalized())
            mpImpl->doNormalize();
    }

    double B2DHomMatrix::determinant() const
    {
        return mpImpl->doDeterminant();
    }

    double B2DHomMatrix::trace() const
    {
        return mpImpl->doTrace();
    }

    void B2DHomMatrix::transpose()
    {
        mpImpl->doTranspose();
    }

    B2DHomMatrix& B2DHomMatrix::operator+=(const B2DHomMatrix& rMat)
    {
        mpImpl->doAddMatrix(*rMat.mpImpl);
        return *this;
    }

    B2DHomMatrix& B2DHomMatrix::operator-=(const B2DHomMatrix& rMat)
    {
        mpImpl->doSubMatrix(*rMat.mpImpl);
        return *this;
    }

    B2DHomMatrix& B2DHomMatrix::operator*=(double fValue)
    {
        const double fOne(1.0);

        if(!fTools::equal(fOne, fValue))
            mpImpl->doMulMatrix(fValue);

        return *this;
    }

    B2DHomMatrix& B2DHomMatrix::operator/=(double fValue)
    {
        const double fOne(1.0);

        if(!fTools::equal(fOne, fValue))
            mpImpl->doMulMatrix(1.0 / fValue);

        return *this;
    }

    B2DHomMatrix& B2DHomMatrix::operator*=(const B2DHomMatrix& rMat)
    {
        if(!rMat.isIdentity())
            mpImpl->doMulMatrix(*rMat.mpImpl);

        return *this;
    }

    bool B2DHomMatrix::operator==(const B2DHomMatrix& rMat) const
    {
        if(mpImpl.same_object(rMat.mpImpl))
            return true;

        return mpImpl->isEqual(*rMat.mpImpl);
    }

    bool B2DHomMatrix::operator!=(const B2DHomMatrix& rMat) const
    {
        return !(*this == rMat);
    }

    void B2DHomMatrix::rotate(double fRadiant)
    {
        if(!fTools::equalZero(fRadiant))
        {
            double fSin;
            double fCos;

            // is the rotation angle an approximate multiple of pi/2?
            // If yes, force fSin/fCos to -1/0/1, to maintain
            // orthogonality (which might also be advantageous for the
            // other cases, but: for multiples of pi/2, the exact
            // values _can_ be attained. It would be largely
            // unintuitive, if a 180 degrees rotation would introduce
            // slight roundoff errors, instead of exactly mirroring
            // the coordinate system).
            if( fTools::equalZero( fmod( fRadiant, F_PI2 ) ) )
            {
                // determine quadrant
                const sal_Int32 nQuad(
                    (4 + fround( 4/F_2PI*fmod( fRadiant, F_2PI ) )) % 4 );
                switch( nQuad )
                {
                    case 0: // -2pi,0,2pi
                        fSin = 0.0;
                        fCos = 1.0;
                        break;

                    case 1: // -3/2pi,1/2pi
                        fSin = 1.0;
                        fCos = 0.0;
                        break;

                    case 2: // -pi,pi
                        fSin = 0.0;
                        fCos = -1.0;
                        break;

                    case 3: // -1/2pi,3/2pi
                        fSin = -1.0;
                        fCos = 0.0;
                        break;

                    default:
                        OSL_ENSURE( false,
                                    "B2DHomMatrix::rotate(): Impossible case reached" );
                }
            }
            else
            {
                // TODO(P1): Maybe use glibc's sincos here (though
                // that's kinda non-portable...)
                fSin = sin(fRadiant);
                fCos = cos(fRadiant);
            }

            Impl2DHomMatrix aRotMat;

            aRotMat.set(0, 0, fCos);
            aRotMat.set(1, 1, fCos);
            aRotMat.set(1, 0, fSin);
            aRotMat.set(0, 1, -fSin);

            mpImpl->doMulMatrix(aRotMat);
        }
    }

    void B2DHomMatrix::translate(double fX, double fY)
    {
        if(!fTools::equalZero(fX) || !fTools::equalZero(fY))
        {
            Impl2DHomMatrix aTransMat;

            aTransMat.set(0, 2, fX);
            aTransMat.set(1, 2, fY);

            mpImpl->doMulMatrix(aTransMat);
        }
    }

    void B2DHomMatrix::scale(double fX, double fY)
    {
        const double fOne(1.0);

        if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY))
        {
            Impl2DHomMatrix aScaleMat;

            aScaleMat.set(0, 0, fX);
            aScaleMat.set(1, 1, fY);

            mpImpl->doMulMatrix(aScaleMat);
        }
    }

    void B2DHomMatrix::shearX(double fSx)
    {
        const double fOne(1.0);

        if(!fTools::equal(fOne, fSx))
        {
            Impl2DHomMatrix aShearXMat;

            aShearXMat.set(0, 1, fSx);

            mpImpl->doMulMatrix(aShearXMat);
        }
    }

    void B2DHomMatrix::shearY(double fSy)
    {
        const double fOne(1.0);

        if(!fTools::equal(fOne, fSy))
        {
            Impl2DHomMatrix aShearYMat;

            aShearYMat.set(1, 0, fSy);

            mpImpl->doMulMatrix(aShearYMat);
        }
    }

    // Decomposition
    bool B2DHomMatrix::decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const
    {
        // when perspective is used, decompose is not made here
        if(!mpImpl->isLastLineDefault())
            return false;

        // test for rotation and shear
        if(fTools::equalZero(get(0, 1))
           && fTools::equalZero(get(1, 0))
           && fTools::moreOrEqual(get(0,0), 0.0)
           && fTools::moreOrEqual(get(1,1), 0.0))
        {
            // no rotation and shear, direct value extraction
            rRotate = rShearX = 0.0;

            // copy scale values
            rScale.setX(get(0, 0));
            rScale.setY(get(1, 1));

            // copy translation values
            rTranslate.setX(get(0, 2));
            rTranslate.setY(get(1, 2));

            return true;
        }
        else
        {
            // test if shear is zero. That's the case, if the unit vectors in the matrix
            // are perpendicular -> scalar is zero
            const ::basegfx::B2DVector aUnitVecX(get(0, 0), get(1, 0));
            const ::basegfx::B2DVector aUnitVecY(get(0, 1), get(1, 1));

            if(fTools::equalZero(aUnitVecX.scalar(aUnitVecY)))
            {
                // no shear, direct value extraction
                rShearX = 0.0;

                // calculate rotation
                rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());

                // calculate scale values
                rScale.setX(aUnitVecX.getLength());
                rScale.setY(aUnitVecY.getLength());

                // copy translation values
                rTranslate.setX(get(0, 2));
                rTranslate.setY(get(1, 2));

                return true;
            }
            else
            {
                // If determinant is zero, decomposition is not possible
                if(0.0 == determinant())
                    return false;

                // copy 2x2 matrix and translate vector to 3x3 matrix
                ::basegfx::B3DHomMatrix a3DHomMat;

                a3DHomMat.set(0, 0, get(0, 0));
                a3DHomMat.set(0, 1, get(0, 1));
                a3DHomMat.set(1, 0, get(1, 0));
                a3DHomMat.set(1, 1, get(1, 1));
                a3DHomMat.set(0, 3, get(0, 2));
                a3DHomMat.set(1, 3, get(1, 2));

                ::basegfx::B3DTuple r3DScale, r3DTranslate, r3DRotate, r3DShear;

                if(a3DHomMat.decompose(r3DScale, r3DTranslate, r3DRotate, r3DShear))
                {
                    // copy scale values
                    rScale.setX(r3DScale.getX());
                    rScale.setY(r3DScale.getY());

                    // copy shear
                    rShearX = r3DShear.getX();

                    // copy rotate
                    rRotate = r3DRotate.getZ();

                    // copy translate
                    rTranslate.setX(r3DTranslate.getX());
                    rTranslate.setY(r3DTranslate.getY());

                    return true;
                }
            }
        }

        return false;
    }
} // end of namespace basegfx

// eof
<commit_msg>INTEGRATION: CWS thbpp6v2 (1.15.2); FILE MERGED 2007/01/27 22:23:21 thb 1.15.2.1: #i73942# Reverted change for i72417. This change potentially spoils a bunch of  optimizations at other places<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: b2dhommatrix.cxx,v $
 *
 *  $Revision: 1.16 $
 *
 *  last change: $Author: vg $ $Date: 2007-02-05 12:50:26 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

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

#ifndef _OSL_DIAGNOSE_H_
#include <osl/diagnose.h>
#endif

#ifndef INCLUDED_RTL_INSTANCE_HXX
#include <rtl/instance.hxx>
#endif

#ifndef _BGFX_MATRIX_B2DHOMMATRIX_HXX
#include <basegfx/matrix/b2dhommatrix.hxx>
#endif

#ifndef _HOMMATRIX_TEMPLATE_HXX
#include <hommatrixtemplate.hxx>
#endif

#ifndef _BGFX_MATRIX_B3DHOMMATRIX_HXX
#include <basegfx/matrix/b3dhommatrix.hxx>
#endif

#ifndef _BGFX_TUPLE_B3DTUPLE_HXX
#include <basegfx/tuple/b3dtuple.hxx>
#endif

#ifndef _BGFX_TUPLE_B2DTUPLE_HXX
#include <basegfx/tuple/b2dtuple.hxx>
#endif

#ifndef _BGFX_VECTOR_B2DVECTOR_HXX
#include <basegfx/vector/b2dvector.hxx>
#endif

namespace basegfx
{
    class Impl2DHomMatrix : public ::basegfx::internal::ImplHomMatrixTemplate< 3 >
    {
    };

    namespace { struct IdentityMatrix : public rtl::Static< B2DHomMatrix::ImplType,
                                                            IdentityMatrix > {}; }

    B2DHomMatrix::B2DHomMatrix() :
        mpImpl( IdentityMatrix::get() ) // use common identity matrix
    {
    }

    B2DHomMatrix::B2DHomMatrix(const B2DHomMatrix& rMat) :
        mpImpl(rMat.mpImpl)
    {
    }

    B2DHomMatrix::~B2DHomMatrix()
    {
    }

    B2DHomMatrix& B2DHomMatrix::operator=(const B2DHomMatrix& rMat)
    {
        mpImpl = rMat.mpImpl;
        return *this;
    }

    void B2DHomMatrix::makeUnique()
    {
        mpImpl.make_unique();
    }

    double B2DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const
    {
        return mpImpl->get(nRow, nColumn);
    }

    void B2DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
    {
        mpImpl->set(nRow, nColumn, fValue);
    }

    bool B2DHomMatrix::isLastLineDefault() const
    {
        return mpImpl->isLastLineDefault();
    }

    bool B2DHomMatrix::isIdentity() const
    {
        if(mpImpl.same_object(IdentityMatrix::get()))
            return true;

        return mpImpl->isIdentity();
    }

    void B2DHomMatrix::identity()
    {
        mpImpl = IdentityMatrix::get();
    }

    bool B2DHomMatrix::isInvertible() const
    {
        return mpImpl->isInvertible();
    }

    bool B2DHomMatrix::invert()
    {
        Impl2DHomMatrix aWork(*mpImpl);
        sal_uInt16* pIndex = new sal_uInt16[mpImpl->getEdgeLength()];
        sal_Int16 nParity;

        if(aWork.ludcmp(pIndex, nParity))
        {
            mpImpl->doInvert(aWork, pIndex);
            delete[] pIndex;

            return true;
        }

        delete[] pIndex;
        return false;
    }

    bool B2DHomMatrix::isNormalized() const
    {
        return mpImpl->isNormalized();
    }

    void B2DHomMatrix::normalize()
    {
        if(!const_cast<const B2DHomMatrix*>(this)->mpImpl->isNormalized())
            mpImpl->doNormalize();
    }

    double B2DHomMatrix::determinant() const
    {
        return mpImpl->doDeterminant();
    }

    double B2DHomMatrix::trace() const
    {
        return mpImpl->doTrace();
    }

    void B2DHomMatrix::transpose()
    {
        mpImpl->doTranspose();
    }

    B2DHomMatrix& B2DHomMatrix::operator+=(const B2DHomMatrix& rMat)
    {
        mpImpl->doAddMatrix(*rMat.mpImpl);
        return *this;
    }

    B2DHomMatrix& B2DHomMatrix::operator-=(const B2DHomMatrix& rMat)
    {
        mpImpl->doSubMatrix(*rMat.mpImpl);
        return *this;
    }

    B2DHomMatrix& B2DHomMatrix::operator*=(double fValue)
    {
        const double fOne(1.0);

        if(!fTools::equal(fOne, fValue))
            mpImpl->doMulMatrix(fValue);

        return *this;
    }

    B2DHomMatrix& B2DHomMatrix::operator/=(double fValue)
    {
        const double fOne(1.0);

        if(!fTools::equal(fOne, fValue))
            mpImpl->doMulMatrix(1.0 / fValue);

        return *this;
    }

    B2DHomMatrix& B2DHomMatrix::operator*=(const B2DHomMatrix& rMat)
    {
        if(!rMat.isIdentity())
            mpImpl->doMulMatrix(*rMat.mpImpl);

        return *this;
    }

    bool B2DHomMatrix::operator==(const B2DHomMatrix& rMat) const
    {
        if(mpImpl.same_object(rMat.mpImpl))
            return true;

        return mpImpl->isEqual(*rMat.mpImpl);
    }

    bool B2DHomMatrix::operator!=(const B2DHomMatrix& rMat) const
    {
        return !(*this == rMat);
    }

    void B2DHomMatrix::rotate(double fRadiant)
    {
        if(!fTools::equalZero(fRadiant))
        {
            double fSin;
            double fCos;

            // is the rotation angle an approximate multiple of pi/2?
            // If yes, force fSin/fCos to -1/0/1, to maintain
            // orthogonality (which might also be advantageous for the
            // other cases, but: for multiples of pi/2, the exact
            // values _can_ be attained. It would be largely
            // unintuitive, if a 180 degrees rotation would introduce
            // slight roundoff errors, instead of exactly mirroring
            // the coordinate system).
            if( fTools::equalZero( fmod( fRadiant, F_PI2 ) ) )
            {
                // determine quadrant
                const sal_Int32 nQuad(
                    (4 + fround( 4/F_2PI*fmod( fRadiant, F_2PI ) )) % 4 );
                switch( nQuad )
                {
                    case 0: // -2pi,0,2pi
                        fSin = 0.0;
                        fCos = 1.0;
                        break;

                    case 1: // -3/2pi,1/2pi
                        fSin = 1.0;
                        fCos = 0.0;
                        break;

                    case 2: // -pi,pi
                        fSin = 0.0;
                        fCos = -1.0;
                        break;

                    case 3: // -1/2pi,3/2pi
                        fSin = -1.0;
                        fCos = 0.0;
                        break;

                    default:
                        OSL_ENSURE( false,
                                    "B2DHomMatrix::rotate(): Impossible case reached" );
                }
            }
            else
            {
                // TODO(P1): Maybe use glibc's sincos here (though
                // that's kinda non-portable...)
                fSin = sin(fRadiant);
                fCos = cos(fRadiant);
            }

            Impl2DHomMatrix aRotMat;

            aRotMat.set(0, 0, fCos);
            aRotMat.set(1, 1, fCos);
            aRotMat.set(1, 0, fSin);
            aRotMat.set(0, 1, -fSin);

            mpImpl->doMulMatrix(aRotMat);
        }
    }

    void B2DHomMatrix::translate(double fX, double fY)
    {
        if(!fTools::equalZero(fX) || !fTools::equalZero(fY))
        {
            Impl2DHomMatrix aTransMat;

            aTransMat.set(0, 2, fX);
            aTransMat.set(1, 2, fY);

            mpImpl->doMulMatrix(aTransMat);
        }
    }

    void B2DHomMatrix::scale(double fX, double fY)
    {
        const double fOne(1.0);

        if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY))
        {
            Impl2DHomMatrix aScaleMat;

            aScaleMat.set(0, 0, fX);
            aScaleMat.set(1, 1, fY);

            mpImpl->doMulMatrix(aScaleMat);
        }
    }

    void B2DHomMatrix::shearX(double fSx)
    {
        const double fOne(1.0);

        if(!fTools::equal(fOne, fSx))
        {
            Impl2DHomMatrix aShearXMat;

            aShearXMat.set(0, 1, fSx);

            mpImpl->doMulMatrix(aShearXMat);
        }
    }

    void B2DHomMatrix::shearY(double fSy)
    {
        const double fOne(1.0);

        if(!fTools::equal(fOne, fSy))
        {
            Impl2DHomMatrix aShearYMat;

            aShearYMat.set(1, 0, fSy);

            mpImpl->doMulMatrix(aShearYMat);
        }
    }

    // Decomposition
    bool B2DHomMatrix::decompose(B2DTuple& rScale, B2DTuple& rTranslate, double& rRotate, double& rShearX) const
    {
        // when perspective is used, decompose is not made here
        if(!mpImpl->isLastLineDefault())
            return false;

        // test for rotation and shear
        if(fTools::equalZero(get(0, 1))
           && fTools::equalZero(get(1, 0)))
        {
            // no rotation and shear, direct value extraction
            rRotate = rShearX = 0.0;

            // copy scale values
            rScale.setX(get(0, 0));
            rScale.setY(get(1, 1));

            // copy translation values
            rTranslate.setX(get(0, 2));
            rTranslate.setY(get(1, 2));

            return true;
        }
        else
        {
            // test if shear is zero. That's the case, if the unit vectors in the matrix
            // are perpendicular -> scalar is zero
            const ::basegfx::B2DVector aUnitVecX(get(0, 0), get(1, 0));
            const ::basegfx::B2DVector aUnitVecY(get(0, 1), get(1, 1));

            if(fTools::equalZero(aUnitVecX.scalar(aUnitVecY)))
            {
                // no shear, direct value extraction
                rShearX = 0.0;

                // calculate rotation
                rRotate = atan2(aUnitVecX.getY(), aUnitVecX.getX());

                // calculate scale values
                rScale.setX(aUnitVecX.getLength());
                rScale.setY(aUnitVecY.getLength());

                // copy translation values
                rTranslate.setX(get(0, 2));
                rTranslate.setY(get(1, 2));

                return true;
            }
            else
            {
                // If determinant is zero, decomposition is not possible
                if(0.0 == determinant())
                    return false;

                // copy 2x2 matrix and translate vector to 3x3 matrix
                ::basegfx::B3DHomMatrix a3DHomMat;

                a3DHomMat.set(0, 0, get(0, 0));
                a3DHomMat.set(0, 1, get(0, 1));
                a3DHomMat.set(1, 0, get(1, 0));
                a3DHomMat.set(1, 1, get(1, 1));
                a3DHomMat.set(0, 3, get(0, 2));
                a3DHomMat.set(1, 3, get(1, 2));

                ::basegfx::B3DTuple r3DScale, r3DTranslate, r3DRotate, r3DShear;

                if(a3DHomMat.decompose(r3DScale, r3DTranslate, r3DRotate, r3DShear))
                {
                    // copy scale values
                    rScale.setX(r3DScale.getX());
                    rScale.setY(r3DScale.getY());

                    // copy shear
                    rShearX = r3DShear.getX();

                    // copy rotate
                    rRotate = r3DRotate.getZ();

                    // copy translate
                    rTranslate.setX(r3DTranslate.getX());
                    rTranslate.setY(r3DTranslate.getY());

                    return true;
                }
            }
        }

        return false;
    }
} // end of namespace basegfx

// eof
<|endoftext|>


<commit_before>#ifndef MSTD_CONTAINER_HASH_ITERATOR_IMP_HPP_
#define MSTD_CONTAINER_HASH_ITERATOR_IMP_HPP_

#include <mstd/iterator/base.hpp>
#include <mstd/container/hash/hash_table_decl.hpp>

namespace mstd {
    namespace detail {
        template <class Derived, class ValueType>
        struct hash_iterator_mixin : public forward_iterator<ValueType> {
        private:
            using child_type = Derived;

        protected:
            ~hash_iterator_mixin() = default;

        private:
            child_type& child()
            {
                return static_cast<child_type&>(*this);
            }

            const child_type& child() const
            {
                return static_cast<const child_type&>(*this);
            }

        public:  // mixins
            //! \fix find the cause of the error
            auto& operator*() const
            {
                return *child().iter_;
            }

            auto operator->() const
            {
                return std::addressof(**this);
            }

            child_type& operator++()
            {
                auto& self = this->child();
                auto& iter = self.iter_;
                auto& obj = *iter;
                auto& nodes = self.table_.get_bucket_(obj);

                ++iter;

                //! if the current node chain is exhausted, find the next
                //! non-exhausted chain.
                if (iter == nodes.end()) {
                    const auto next_bnum = self.table_.bucket_num_(obj) + 1;

                    auto first = self.table_.buckets_.begin() + next_bnum;
                    auto last = self.table_.buckets_.end();

                    if (first < last) {
                        using ref_type = decltype(nodes);

                        auto i = std::find_if(first, last, [](ref_type v){
                            return !v.empty();
                        });

                        if (i != last) {
                            iter = i->begin();
                        }
                    }
                }

                return self;
            }

            child_type operator++(int)
            {
                child_type old = child();
                ++*this;
                return old;
            }

        protected:
            bool is_equal_with(const child_type& other) const
            {
                auto& self = this->child();
                return self.iter_ == other.iter_ &&
                       std::addressof(self.table_) == std::addressof(other.table_);
            }
        };

        template <class Key, class Value,
                  class ExtractKey,
                  class Hash,
                  class EqualObj,
                  class Allocator>
        struct hash_table<
                Key, Value, ExtractKey, Hash, EqualObj, Allocator
            >::iterator_ : public hash_iterator_mixin<iterator_, Value> {
            friend struct hash_iterator_mixin<iterator_, Value>;
            friend struct hash_table::const_iterator_;
            friend class hash_table;

            using self_type = iterator_;

        public:
            friend bool operator==(const self_type& x, const self_type& y)
            {
                return x.is_equal_with(y);
            }

        private:
            using node_iterator = typename node_list::iterator;

            explicit iterator_(hash_table &table)
                : iterator_(table, {})
            {}

            iterator_(hash_table& table, node_iterator iter)
                : table_(table),
                  iter_{iter} // no std::make_unique now :(
            {
            }

        private:
            hash_table& table_;
            node_iterator iter_;
        };

        template <class Key, class Value,
                  class ExtractKey,
                  class Hash,
                  class EqualObj,
                  class Allocator>
        struct hash_table<
                Key, Value, ExtractKey, Hash, EqualObj, Allocator
            >::const_iterator_ :
                public hash_iterator_mixin<const_iterator_, const Value> {
            friend struct hash_iterator_mixin<const_iterator_, const Value>;
            friend class hash_table;

            using self_type = const_iterator_;
            using const_node_iterator = typename node_list::const_iterator;

        public:
            friend bool operator==(const self_type& x, const self_type& y)
            {
                return x.is_equal_with(y);
            }

        public:
            const_iterator_(const iterator_& iter)
                : const_iterator_{iter.table_, iter.iter_}
            {
            }

        private:
            explicit const_iterator_(const hash_table &table)
                : const_iterator_(table, {})
            {}

            const_iterator_(
                    const hash_table& table,
                    const_node_iterator iter)
                : table_(table), iter_{iter}
            {
            }

        private:
            const hash_table& table_;
            const_node_iterator iter_;
        };
    }  // of namespace detail
}  // of namespace mstd

#endif //! MSTD_CONTAINER_HASH_ITERATOR_IMP_HPP_
<commit_msg>review template name binding<commit_after>#ifndef MSTD_CONTAINER_HASH_ITERATOR_IMP_HPP_
#define MSTD_CONTAINER_HASH_ITERATOR_IMP_HPP_

#include <mstd/iterator/base.hpp>
#include <mstd/container/hash/hash_table_decl.hpp>

namespace mstd {
    namespace detail {
        template <class Derived, class ValueType>
        struct hash_iterator_mixin : public forward_iterator<ValueType> {
        private:
            using child_type = Derived;

        protected:
            ~hash_iterator_mixin() = default;

        private:
            child_type& child()
            {
                return static_cast<child_type&>(*this);
            }

            const child_type& child() const
            {
                return static_cast<const child_type&>(*this);
            }

        public:  // mixins
            //! \fix find the cause of the error
            //! where the error comes from?
            //!     Because the base class is a dependent name, so unqualified
            //!     identifiers will be resolved at definition time.
            auto& operator*() const
            {
                return *child().iter_;
            }

            auto operator->() const
            {
                return std::addressof(**this);
            }

            child_type& operator++()
            {
                auto& self = this->child();
                auto& iter = self.iter_;
                auto& obj = *iter;
                auto& nodes = self.table_.get_bucket_(obj);

                ++iter;

                //! if the current node chain is exhausted, find the next
                //! non-exhausted chain.
                if (iter == nodes.end()) {
                    const auto next_bnum = self.table_.bucket_num_(obj) + 1;

                    auto first = self.table_.buckets_.begin() + next_bnum;
                    auto last = self.table_.buckets_.end();

                    if (first < last) {
                        using ref_type = decltype(nodes);

                        auto i = std::find_if(first, last, [](ref_type v){
                            return !v.empty();
                        });

                        if (i != last) {
                            iter = i->begin();
                        }
                    }
                }

                return self;
            }

            child_type operator++(int)
            {
                child_type old = child();
                ++*this;
                return old;
            }

        protected:
            bool is_equal_with(const child_type& other) const
            {
                auto& self = this->child();
                return self.iter_ == other.iter_ &&
                       std::addressof(self.table_) == std::addressof(other.table_);
            }
        };

        template <class Key, class Value,
                  class ExtractKey,
                  class Hash,
                  class EqualObj,
                  class Allocator>
        struct hash_table<
                Key, Value, ExtractKey, Hash, EqualObj, Allocator
            >::iterator_ : public hash_iterator_mixin<iterator_, Value> {
            friend struct hash_iterator_mixin<iterator_, Value>;
            friend struct hash_table::const_iterator_;
            friend class hash_table;

            using self_type = iterator_;

        public:
            friend bool operator==(const self_type& x, const self_type& y)
            {
                return x.is_equal_with(y);
            }

        private:
            using node_iterator = typename node_list::iterator;

            explicit iterator_(hash_table &table)
                : iterator_(table, {})
            {}

            iterator_(hash_table& table, node_iterator iter)
                : table_(table),
                  iter_{iter} // no std::make_unique now :(
            {
            }

        private:
            hash_table& table_;
            node_iterator iter_;
        };

        template <class Key, class Value,
                  class ExtractKey,
                  class Hash,
                  class EqualObj,
                  class Allocator>
        struct hash_table<
                Key, Value, ExtractKey, Hash, EqualObj, Allocator
            >::const_iterator_ :
                public hash_iterator_mixin<const_iterator_, const Value> {
            friend struct hash_iterator_mixin<const_iterator_, const Value>;
            friend class hash_table;

            using self_type = const_iterator_;
            using const_node_iterator = typename node_list::const_iterator;

        public:
            friend bool operator==(const self_type& x, const self_type& y)
            {
                return x.is_equal_with(y);
            }

        public:
            const_iterator_(const iterator_& iter)
                : const_iterator_{iter.table_, iter.iter_}
            {
            }

        private:
            explicit const_iterator_(const hash_table &table)
                : const_iterator_(table, {})
            {}

            const_iterator_(
                    const hash_table& table,
                    const_node_iterator iter)
                : table_(table), iter_{iter}
            {
            }

        private:
            const hash_table& table_;
            const_node_iterator iter_;
        };
    }  // of namespace detail
}  // of namespace mstd

#endif //! MSTD_CONTAINER_HASH_ITERATOR_IMP_HPP_
<|endoftext|>